Index: components/zoom/zoom_controller.cc |
diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc |
index 8d258a5d7adb3ddaf7c36df2848d1be6af680252..dd0231226f9bf9a0488b38421ed6aacd17f47cd8 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,43 @@ 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(); |
+ // TODO(mcnee) If the need to navigate between StoragePartitions is |
+ // introduced by a new feature, we will need to add logic to update this |
+ // subscription. |
+ 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 +214,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_scope_is_per_origin_ ? ZOOM_MODE_DEFAULT |
+ : ZOOM_MODE_ISOLATED))); |
switch (new_mode) { |
case ZOOM_MODE_DEFAULT: { |
@@ -261,28 +296,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 (zoom_mode_ != (default_scope_is_per_origin_ ? ZOOM_MODE_ISOLATED |
+ : ZOOM_MODE_DEFAULT) && |
+ 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 and we have a non-default temporary zoom level, this |
+ // ZoomController will set the per-host zoom level. |
+ SetZoomMode(default_scope_is_per_origin_ ? ZOOM_MODE_DEFAULT |
+ : ZOOM_MODE_ISOLATED); |
} |
void ZoomController::DidFinishNavigation( |
@@ -303,6 +368,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 +379,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 +400,66 @@ 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()) { |