Chromium Code Reviews| Index: components/zoom/zoom_controller.cc |
| diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc |
| index 8d258a5d7adb3ddaf7c36df2848d1be6af680252..3a61df0627b2ca86ef8942176bb7bdac597480d1 100644 |
| --- a/components/zoom/zoom_controller.cc |
| +++ b/components/zoom/zoom_controller.cc |
| @@ -6,12 +6,15 @@ |
| #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/navigation_handle.h" |
| +#include "content/public/browser/render_frame_host.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" |
| @@ -36,12 +39,40 @@ double ZoomController::GetZoomLevelForWebContents( |
| ZoomController::ZoomController(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| can_show_bubble_(true), |
| - zoom_mode_(ZOOM_MODE_DEFAULT), |
| + default_scope_is_per_origin_(ZoomPrefsDelegate::kZoomScopeSettingDefault), |
| zoom_level_(1.0), |
| browser_context_(web_contents->GetBrowserContext()) { |
| host_zoom_map_ = content::HostZoomMap::GetForWebContents(web_contents); |
| zoom_level_ = host_zoom_map_->GetDefaultZoomLevel(); |
| + ZoomPrefsDelegate* zoom_prefs_delegate = static_cast<ZoomPrefsDelegate*>( |
| + content::BrowserContext::GetStoragePartition( |
| + browser_context_, web_contents->GetSiteInstance()) |
| + ->GetZoomLevelDelegate()); |
| + if (zoom_prefs_delegate) { |
| + default_scope_is_per_origin_ = |
| + zoom_prefs_delegate->GetZoomScopeIsPerOriginPref(); |
| + default_zoom_scope_subscription_ = |
| + zoom_prefs_delegate->RegisterDefaultZoomScopeCallback( |
| + base::Bind(&ZoomController::OnDefaultZoomScopeChanged, |
| + base::Unretained(this))); |
| + } |
| + zoom_mode_ = |
| + default_scope_is_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))); |
| @@ -180,7 +211,9 @@ 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_scope_is_per_origin_ |
| + ? ZOOM_MODE_DEFAULT : ZOOM_MODE_ISOLATED))); |
| switch (new_mode) { |
| case ZOOM_MODE_DEFAULT: { |
| @@ -261,28 +294,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_scope_is_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_scope_is_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_scope_is_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. |
|
Kevin McNee
2017/02/03 23:17:20
Is this acceptable behaviour?
wjmaclean
2017/02/06 16:09:07
I don't think I understand the comment. I would ha
Kevin McNee
2017/02/06 18:26:12
The problem with deferring the change in zoom mode
wjmaclean
2017/02/06 18:48:38
Ok. In that case I'd clarify the comment, since it
|
| + SetZoomMode( |
| + default_scope_is_per_origin_ ? ZOOM_MODE_DEFAULT : ZOOM_MODE_ISOLATED); |
| } |
| void ZoomController::DidFinishNavigation( |
| @@ -303,6 +366,9 @@ void ZoomController::DidFinishNavigation( |
| } |
| void ZoomController::WebContentsDestroyed() { |
| + // Once our WebContents is destroyed, we can no longer respond to zoom scope |
| + // changes. |
| + default_zoom_scope_subscription_.reset(); |
| // At this point we should no longer be sending any zoom events with this |
| // WebContents. |
| observers_.Clear(); |
| @@ -311,9 +377,19 @@ 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_scope_is_per_origin_ && |
| + zoom_mode_ == ZOOM_MODE_ISOLATED) { |
| + PreserveTemporaryZoomLevel( |
| + host_zoom_map_, new_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; |
| @@ -322,11 +398,67 @@ void ZoomController::RenderFrameHostChanged( |
| base::Bind(&ZoomController::OnZoomLevelChanged, base::Unretained(this))); |
| } |
| +void ZoomController::PreserveTemporaryZoomLevel( |
| + const content::HostZoomMap* old_zoom_map, |
| + content::HostZoomMap* new_zoom_map, |
| + content::RenderFrameHost* old_host, |
| + content::RenderFrameHost* new_host) const { |
| + const content::RenderViewHost* old_view_host = old_host->GetRenderViewHost(); |
| + const content::RenderViewHost* new_view_host = new_host->GetRenderViewHost(); |
| + |
| + DCHECK(old_zoom_map->UsesTemporaryZoomLevel( |
| + old_view_host->GetProcess()->GetID(), old_view_host->GetRoutingID())); |
| + |
| + const double level = old_zoom_map->GetTemporaryZoomLevel( |
| + old_view_host->GetProcess()->GetID(), old_view_host->GetRoutingID()); |
| + new_zoom_map->SetTemporaryZoomLevel( |
| + new_view_host->GetProcess()->GetID(), new_view_host->GetRoutingID(), |
| + level); |
| +} |
| + |
| +void ZoomController::PreserveTemporaryZoomLevel( |
| + content::WebContents* old_web_contents, |
| + content::WebContents* new_web_contents) const { |
| + if (default_scope_is_per_origin_ || zoom_mode_ != ZOOM_MODE_ISOLATED) |
| + 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); |
| + |
| + PreserveTemporaryZoomLevel( |
| + old_host_zoom_map, new_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* 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(web_contents(), new_web_contents); |
| +} |
| + |
| void ZoomController::OnZoomLevelChanged( |
| const content::HostZoomMap::ZoomLevelChange& change) { |
| UpdateState(change.host); |
| } |
| +void ZoomController::OnDefaultZoomScopeChanged() { |
| + default_scope_is_per_origin_ = !default_scope_is_per_origin_; |
| + UpdateZoomModeOnScopeChangeIfNeeded(); |
| +} |
| + |
| void ZoomController::UpdateState(const std::string& host) { |
| // If |host| is empty, all observers should be updated. |
| if (!host.empty()) { |