Chromium Code Reviews| Index: components/zoom/zoom_controller.cc |
| diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc |
| index a28a01dcef71663f17c63d79aeb66ae9d0ecd8bc..ee3cecbc093094b21b7a302501204465f8a5d277 100644 |
| --- a/components/zoom/zoom_controller.cc |
| +++ b/components/zoom/zoom_controller.cc |
| @@ -6,11 +6,13 @@ |
| #include "components/zoom/zoom_event_manager.h" |
| #include "components/zoom/zoom_observer.h" |
| +#include "content/public/browser/browser_context.h" |
| #include "content/public/browser/host_zoom_map.h" |
| #include "content/public/browser/navigation_details.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| +#include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/page_type.h" |
| #include "content/public/common/page_zoom.h" |
| @@ -35,12 +37,38 @@ double ZoomController::GetZoomLevelForWebContents( |
| ZoomController::ZoomController(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| can_show_bubble_(true), |
| - zoom_mode_(ZOOM_MODE_DEFAULT), |
| + default_per_origin_(true), |
| zoom_level_(1.0), |
| browser_context_(web_contents->GetBrowserContext()) { |
| host_zoom_map_ = content::HostZoomMap::GetForWebContents(web_contents); |
| zoom_level_ = host_zoom_map_->GetDefaultZoomLevel(); |
| + // TODO Should we use GetStoragePartition with the SiteInstance? |
|
wjmaclean
2017/01/19 21:09:05
If we have the SiteInstance, then it's preferable
Kevin McNee
2017/02/03 20:11:54
Done.
|
| + content::ZoomLevelDelegate* zoom_level_delegate = |
| + content::BrowserContext::GetDefaultStoragePartition(browser_context_) |
| + ->GetZoomLevelDelegate(); |
| + if (zoom_level_delegate) { |
| + default_per_origin_ = zoom_level_delegate->GetIsOriginScopePref(); |
| + default_zoom_scope_subscription_ = |
| + zoom_level_delegate->RegisterDefaultZoomScopeCallback( |
| + base::Bind(&ZoomController::OnDefaultZoomScopeChanged, |
| + base::Unretained(this))); |
|
wjmaclean
2017/01/19 21:09:05
Is this safe? What if the WebContents that owns us
Kevin McNee
2017/02/01 20:29:40
Good point. Once this ZoomController is destroyed
Kevin McNee
2017/02/03 20:11:54
Done.
|
| + } |
| + zoom_mode_ = default_per_origin_ ? ZOOM_MODE_DEFAULT : ZOOM_MODE_ISOLATED; |
| + |
| + // When in isolated zoom, a temporary zoom level needs to be set to override |
| + // any existing per-host zoom levels. |
| + // If this WebContents is a clone of another, don't overwrite the temporary |
| + // level we've inherited. |
| + int render_process_id = web_contents->GetRenderProcessHost()->GetID(); |
| + int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); |
| + if (zoom_mode_ == ZOOM_MODE_ISOLATED && |
| + !host_zoom_map_->UsesTemporaryZoomLevel(render_process_id, |
| + render_view_id)) { |
| + host_zoom_map_->SetTemporaryZoomLevel(render_process_id, render_view_id, |
| + zoom_level_); |
| + } |
| + |
| zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback( |
| base::Bind(&ZoomController::OnZoomLevelChanged, base::Unretained(this))); |
| @@ -179,7 +207,8 @@ void ZoomController::SetZoomMode(ZoomMode new_mode) { |
| DCHECK(!event_data_); |
| event_data_.reset(new ZoomChangedEventData( |
| web_contents(), original_zoom_level, original_zoom_level, new_mode, |
| - new_mode != ZOOM_MODE_DEFAULT)); |
| + new_mode != |
| + (default_per_origin_ ? ZOOM_MODE_DEFAULT : ZOOM_MODE_ISOLATED))); |
| switch (new_mode) { |
| case ZOOM_MODE_DEFAULT: { |
| @@ -260,28 +289,58 @@ void ZoomController::SetZoomMode(ZoomMode new_mode) { |
| } |
| void ZoomController::ResetZoomModeOnNavigationIfNeeded(const GURL& url) { |
| - if (zoom_mode_ != ZOOM_MODE_ISOLATED && zoom_mode_ != ZOOM_MODE_MANUAL) |
| + if ((!default_per_origin_ || zoom_mode_ != ZOOM_MODE_ISOLATED) && |
| + zoom_mode_ != ZOOM_MODE_MANUAL) |
| return; |
| int render_process_id = web_contents()->GetRenderProcessHost()->GetID(); |
| int render_view_id = web_contents()->GetRenderViewHost()->GetRoutingID(); |
| content::HostZoomMap* zoom_map = |
| content::HostZoomMap::GetForWebContents(web_contents()); |
| + double old_zoom_level = GetZoomLevel(); |
| + // Set |zoom_level_| after calling GetZoomLevel() so we get the correct old |
| + // zoom level if we're resetting from manual. |
| zoom_level_ = zoom_map->GetDefaultZoomLevel(); |
| - double old_zoom_level = zoom_map->GetZoomLevel(web_contents()); |
| - double new_zoom_level = zoom_map->GetZoomLevelForHostAndScheme( |
| - url.scheme(), net::GetHostOrSpecFromURL(url)); |
| + ZoomMode new_mode; |
| + double new_zoom_level; |
| + if (default_per_origin_) { |
| + new_mode = ZOOM_MODE_DEFAULT; |
| + new_zoom_level = zoom_map->GetZoomLevelForHostAndScheme( |
| + url.scheme(), net::GetHostOrSpecFromURL(url)); |
| + } else { |
| + new_mode = ZOOM_MODE_ISOLATED; |
| + new_zoom_level = zoom_level_; |
| + } |
| event_data_.reset(new ZoomChangedEventData(web_contents(), old_zoom_level, |
| - new_zoom_level, ZOOM_MODE_DEFAULT, |
| + new_zoom_level, new_mode, |
| false /* can_show_bubble */)); |
| - // The call to ClearTemporaryZoomLevel() doesn't generate any events from |
| - // HostZoomMap, but the call to UpdateState() at the end of this function |
| - // will notify our observers. |
| - // Note: it's possible the render_process/view ids have disappeared (e.g. |
| - // if we navigated to a new origin), but this won't cause a problem in the |
| - // call below. |
| - zoom_map->ClearTemporaryZoomLevel(render_process_id, render_view_id); |
| - zoom_mode_ = ZOOM_MODE_DEFAULT; |
| + if (default_per_origin_) { |
| + // The call to ClearTemporaryZoomLevel() doesn't generate any events from |
| + // HostZoomMap, but the call to UpdateState() at the end of this function |
| + // will notify our observers. |
| + // Note: it's possible the render_process/view ids have disappeared (e.g. |
| + // if we navigated to a new origin), but this won't cause a problem in the |
| + // call below. |
| + zoom_map->ClearTemporaryZoomLevel(render_process_id, render_view_id); |
| + } else { |
| + // If we're resetting to isolated zoom, a temporary zoom level needs to |
| + // be set to override any existing per-host zoom levels. |
| + zoom_map->SetTemporaryZoomLevel(render_process_id, render_view_id, |
| + new_zoom_level); |
| + } |
| + zoom_mode_ = new_mode; |
| +} |
| + |
| +void ZoomController::UpdateZoomModeOnScopeChangeIfNeeded() { |
| + if (zoom_mode_ != ZOOM_MODE_DEFAULT && zoom_mode_ != ZOOM_MODE_ISOLATED) |
| + return; |
| + |
| + // When going from per-tab to per-host, if there isn't an existing per-host |
| + // zoom level set, the first ZoomController (that has set a non-default |
| + // temporary zoom level) to be notified of the scope change will set the |
| + // per-host zoom level. |
| + // TODO Is this acceptable behaviour? |
| + SetZoomMode(default_per_origin_ ? ZOOM_MODE_DEFAULT : ZOOM_MODE_ISOLATED); |
| } |
| void ZoomController::DidNavigateMainFrame( |
| @@ -308,9 +367,18 @@ void ZoomController::WebContentsDestroyed() { |
| void ZoomController::RenderFrameHostChanged( |
| content::RenderFrameHost* old_host, |
| content::RenderFrameHost* new_host) { |
| - // If our associated HostZoomMap changes, update our event subscription. |
| content::HostZoomMap* new_host_zoom_map = |
| content::HostZoomMap::GetForWebContents(web_contents()); |
| + |
| + // The HostZoomMap records temporary zoom levels per RenderView. When zooming |
| + // is per-tab and our RenderView changes, we need to copy the level for our |
| + // new RenderView so that the tab's zoom level is preserved. |
| + if (old_host && !default_per_origin_ && zoom_mode_ == ZOOM_MODE_ISOLATED) { |
| + new_host_zoom_map->PreserveTemporaryZoomLevelFrom( |
| + host_zoom_map_, old_host, new_host); |
| + } |
| + |
| + // If our associated HostZoomMap changes, update our event subscription. |
| if (new_host_zoom_map == host_zoom_map_) |
| return; |
| @@ -319,11 +387,50 @@ void ZoomController::RenderFrameHostChanged( |
| base::Bind(&ZoomController::OnZoomLevelChanged, base::Unretained(this))); |
| } |
| +void ZoomController::PreserveTemporaryZoomLevel( |
| + content::WebContents* old_web_contents, |
| + content::WebContents* new_web_contents) const { |
| + if (default_per_origin_ || zoom_mode_ != ZOOM_MODE_ISOLATED) |
|
wjmaclean
2017/01/19 21:09:05
Again, something like |is_per_origin| or |zoom_is_
Kevin McNee
2017/02/03 22:51:34
Done.
|
| + return; |
| + |
| + content::RenderFrameHost* old_host = old_web_contents->GetMainFrame(); |
| + content::RenderFrameHost* new_host = new_web_contents->GetMainFrame(); |
| + const content::HostZoomMap* old_host_zoom_map = |
| + content::HostZoomMap::GetForWebContents(old_web_contents); |
| + content::HostZoomMap* new_host_zoom_map = |
| + content::HostZoomMap::GetForWebContents(new_web_contents); |
| + |
| + new_host_zoom_map->PreserveTemporaryZoomLevelFrom( |
| + old_host_zoom_map, old_host, new_host); |
| +} |
| + |
| +void ZoomController::DidCloneToNewWebContents( |
| + content::WebContents* old_web_contents, |
| + content::WebContents* new_web_contents) { |
| + // If a WebContents is cloned when zooming is per-tab, have the clone inherit |
| + // the original's zoom level. |
| + PreserveTemporaryZoomLevel(old_web_contents, new_web_contents); |
| +} |
| + |
| +void ZoomController::WebContentsReplaced( |
| + content::WebContents* old_web_contents, |
| + content::WebContents* new_web_contents) { |
| + // A tab may replace its WebContents with another, but from the user's |
| + // perspective, this is still the same tab, so we need to preserve the |
| + // per-tab zoom. |
| + PreserveTemporaryZoomLevel(old_web_contents, new_web_contents); |
| +} |
| + |
| void ZoomController::OnZoomLevelChanged( |
| const content::HostZoomMap::ZoomLevelChange& change) { |
| UpdateState(change.host); |
| } |
| +void ZoomController::OnDefaultZoomScopeChanged() { |
| + default_per_origin_ = !default_per_origin_; |
| + UpdateZoomModeOnScopeChangeIfNeeded(); |
| +} |
| + |
| void ZoomController::UpdateState(const std::string& host) { |
| // If |host| is empty, all observers should be updated. |
| if (!host.empty()) { |