From 8e3dcc8b179c0cd38dbd367b123e31af0ba3cfb9 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Tue, 15 Aug 2023 03:33:30 +0200 Subject: [PATCH] refactor: update `WebContentsZoomController` (#39428) refactor: update WebContentsZoomController --- filenames.gni | 1 + shell/browser/extensions/api/tabs/tabs_api.cc | 18 +- shell/browser/web_contents_zoom_controller.cc | 251 +++++++++++++----- shell/browser/web_contents_zoom_controller.h | 94 ++++--- shell/browser/web_contents_zoom_observer.h | 29 ++ shell/browser/web_view_guest_delegate.cc | 20 +- shell/browser/web_view_guest_delegate.h | 13 +- 7 files changed, 305 insertions(+), 121 deletions(-) create mode 100644 shell/browser/web_contents_zoom_observer.h diff --git a/filenames.gni b/filenames.gni index b4b01068e8f5..c06f6ba484ba 100644 --- a/filenames.gni +++ b/filenames.gni @@ -494,6 +494,7 @@ filenames = { "shell/browser/web_contents_preferences.h", "shell/browser/web_contents_zoom_controller.cc", "shell/browser/web_contents_zoom_controller.h", + "shell/browser/web_contents_zoom_observer.h", "shell/browser/web_view_guest_delegate.cc", "shell/browser/web_view_guest_delegate.h", "shell/browser/web_view_manager.cc", diff --git a/shell/browser/extensions/api/tabs/tabs_api.cc b/shell/browser/extensions/api/tabs/tabs_api.cc index 79f595121b6f..0cdc96819f70 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.cc +++ b/shell/browser/extensions/api/tabs/tabs_api.cc @@ -42,19 +42,19 @@ void ZoomModeToZoomSettings(WebContentsZoomController::ZoomMode zoom_mode, api::tabs::ZoomSettings* zoom_settings) { DCHECK(zoom_settings); switch (zoom_mode) { - case WebContentsZoomController::ZoomMode::kDefault: + case WebContentsZoomController::ZOOM_MODE_DEFAULT: zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC; zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN; break; - case WebContentsZoomController::ZoomMode::kIsolated: + case WebContentsZoomController::ZOOM_MODE_ISOLATED: zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC; zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; break; - case WebContentsZoomController::ZoomMode::kManual: + case WebContentsZoomController::ZOOM_MODE_MANUAL: zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL; zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; break; - case WebContentsZoomController::ZoomMode::kDisabled: + case WebContentsZoomController::ZOOM_MODE_DISABLED: zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED; zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; break; @@ -427,24 +427,24 @@ ExtensionFunction::ResponseAction TabsSetZoomSettingsFunction::Run() { // Determine the correct internal zoom mode to set |web_contents| to from the // user-specified |zoom_settings|. WebContentsZoomController::ZoomMode zoom_mode = - WebContentsZoomController::ZoomMode::kDefault; + WebContentsZoomController::ZOOM_MODE_DEFAULT; switch (params->zoom_settings.mode) { case tabs::ZOOM_SETTINGS_MODE_NONE: case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC: switch (params->zoom_settings.scope) { case tabs::ZOOM_SETTINGS_SCOPE_NONE: case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN: - zoom_mode = WebContentsZoomController::ZoomMode::kDefault; + zoom_mode = WebContentsZoomController::ZOOM_MODE_DEFAULT; break; case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB: - zoom_mode = WebContentsZoomController::ZoomMode::kIsolated; + zoom_mode = WebContentsZoomController::ZOOM_MODE_ISOLATED; } break; case tabs::ZOOM_SETTINGS_MODE_MANUAL: - zoom_mode = WebContentsZoomController::ZoomMode::kManual; + zoom_mode = WebContentsZoomController::ZOOM_MODE_MANUAL; break; case tabs::ZOOM_SETTINGS_MODE_DISABLED: - zoom_mode = WebContentsZoomController::ZoomMode::kDisabled; + zoom_mode = WebContentsZoomController::ZOOM_MODE_DISABLED; } contents->GetZoomController()->SetZoomMode(zoom_mode); diff --git a/shell/browser/web_contents_zoom_controller.cc b/shell/browser/web_contents_zoom_controller.cc index 6562122fe95b..b0fb6bee9d93 100644 --- a/shell/browser/web_contents_zoom_controller.cc +++ b/shell/browser/web_contents_zoom_controller.cc @@ -6,6 +6,7 @@ #include +#include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" @@ -16,72 +17,122 @@ #include "content/public/browser/web_contents_user_data.h" #include "content/public/common/page_type.h" #include "net/base/url_util.h" +#include "shell/browser/web_contents_zoom_observer.h" #include "third_party/blink/public/common/page/page_zoom.h" +using content::BrowserThread; + namespace electron { +namespace { + +const double kPageZoomEpsilon = 0.001; + +} // namespace + WebContentsZoomController::WebContentsZoomController( content::WebContents* web_contents) : content::WebContentsObserver(web_contents), content::WebContentsUserData(*web_contents) { - default_zoom_factor_ = kPageZoomEpsilon; + DCHECK_CURRENTLY_ON(BrowserThread::UI); host_zoom_map_ = content::HostZoomMap::GetForWebContents(web_contents); + zoom_level_ = host_zoom_map_->GetDefaultZoomLevel(); + default_zoom_factor_ = kPageZoomEpsilon; + + zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback( + base::BindRepeating(&WebContentsZoomController::OnZoomLevelChanged, + base::Unretained(this))); + + UpdateState(std::string()); } -WebContentsZoomController::~WebContentsZoomController() = default; +WebContentsZoomController::~WebContentsZoomController() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + for (auto& observer : observers_) { + observer.OnZoomControllerDestroyed(this); + } +} -void WebContentsZoomController::AddObserver( - WebContentsZoomController::Observer* observer) { +void WebContentsZoomController::AddObserver(WebContentsZoomObserver* observer) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); observers_.AddObserver(observer); } void WebContentsZoomController::RemoveObserver( - WebContentsZoomController::Observer* observer) { + WebContentsZoomObserver* observer) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); observers_.RemoveObserver(observer); } void WebContentsZoomController::SetEmbedderZoomController( WebContentsZoomController* controller) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); embedder_zoom_controller_ = controller; } -void WebContentsZoomController::SetZoomLevel(double level) { - if (!web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive() || - blink::PageZoomValuesEqual(GetZoomLevel(), level) || - zoom_mode_ == ZoomMode::kDisabled) - return; +bool WebContentsZoomController::SetZoomLevel(double level) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::NavigationEntry* entry = + web_contents()->GetController().GetLastCommittedEntry(); + // Cannot zoom in disabled mode. Also, don't allow changing zoom level on + // a crashed tab, an error page or an interstitial page. + if (zoom_mode_ == ZOOM_MODE_DISABLED || + !web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive()) + return false; - content::GlobalRenderFrameHostId rfh_id = - web_contents()->GetPrimaryMainFrame()->GetGlobalId(); + // Do not actually rescale the page in manual mode. + if (zoom_mode_ == ZOOM_MODE_MANUAL) { + // If the zoom level hasn't changed, early out to avoid sending an event. + if (blink::PageZoomValuesEqual(zoom_level_, level)) + return true; - if (zoom_mode_ == ZoomMode::kManual) { + double old_zoom_level = zoom_level_; zoom_level_ = level; - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), level, true); + ZoomChangedEventData zoom_change_data(web_contents(), old_zoom_level, + zoom_level_, false /* temporary */, + zoom_mode_); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); - return; + return true; } content::HostZoomMap* zoom_map = content::HostZoomMap::GetForWebContents(web_contents()); - if (zoom_mode_ == ZoomMode::kIsolated || + DCHECK(zoom_map); + DCHECK(!event_data_); + event_data_ = std::make_unique( + web_contents(), GetZoomLevel(), level, false /* temporary */, zoom_mode_); + + content::GlobalRenderFrameHostId rfh_id = + web_contents()->GetPrimaryMainFrame()->GetGlobalId(); + if (zoom_mode_ == ZOOM_MODE_ISOLATED || zoom_map->UsesTemporaryZoomLevel(rfh_id)) { zoom_map->SetTemporaryZoomLevel(rfh_id, level); - // Notify observers of zoom level changes. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), level, true); + ZoomChangedEventData zoom_change_data(web_contents(), zoom_level_, level, + true /* temporary */, zoom_mode_); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); } else { - content::HostZoomMap::SetZoomLevel(web_contents(), level); - - // Notify observers of zoom level changes. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), level, false); + if (!entry) { + // If we exit without triggering an update, we should clear event_data_, + // else we may later trigger a DCHECK(event_data_). + event_data_.reset(); + return false; + } + std::string host = + net::GetHostOrSpecFromURL(content::HostZoomMap::GetURLFromEntry(entry)); + zoom_map->SetZoomLevelForHost(host, level); } + + DCHECK(!event_data_); + return true; } -double WebContentsZoomController::GetZoomLevel() { - return zoom_mode_ == ZoomMode::kManual +double WebContentsZoomController::GetZoomLevel() const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return zoom_mode_ == ZOOM_MODE_MANUAL ? zoom_level_ : content::HostZoomMap::GetZoomLevel(web_contents()); } @@ -95,32 +146,44 @@ double WebContentsZoomController::GetDefaultZoomFactor() { } void WebContentsZoomController::SetTemporaryZoomLevel(double level) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::GlobalRenderFrameHostId old_rfh_id_ = web_contents()->GetPrimaryMainFrame()->GetGlobalId(); host_zoom_map_->SetTemporaryZoomLevel(old_rfh_id_, level); + // Notify observers of zoom level changes. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), level, true); + ZoomChangedEventData zoom_change_data(web_contents(), zoom_level_, level, + true /* temporary */, zoom_mode_); + for (WebContentsZoomObserver& observer : observers_) + observer.OnZoomChanged(zoom_change_data); } bool WebContentsZoomController::UsesTemporaryZoomLevel() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::GlobalRenderFrameHostId rfh_id = web_contents()->GetPrimaryMainFrame()->GetGlobalId(); return host_zoom_map_->UsesTemporaryZoomLevel(rfh_id); } void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (new_mode == zoom_mode_) return; content::HostZoomMap* zoom_map = content::HostZoomMap::GetForWebContents(web_contents()); + DCHECK(zoom_map); content::GlobalRenderFrameHostId rfh_id = web_contents()->GetPrimaryMainFrame()->GetGlobalId(); double original_zoom_level = GetZoomLevel(); + DCHECK(!event_data_); + event_data_ = std::make_unique( + web_contents(), original_zoom_level, original_zoom_level, + false /* temporary */, new_mode); + switch (new_mode) { - case ZoomMode::kDefault: { + case ZOOM_MODE_DEFAULT: { content::NavigationEntry* entry = web_contents()->GetController().GetLastCommittedEntry(); @@ -135,6 +198,7 @@ void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { // the correct zoom level. double origin_zoom_level = zoom_map->GetZoomLevelForHostAndScheme(url.scheme(), host); + event_data_->new_zoom_level = origin_zoom_level; zoom_map->SetTemporaryZoomLevel(rfh_id, origin_zoom_level); } else { // The host will need a level prior to removing the temporary level. @@ -147,101 +211,128 @@ void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { zoom_map->ClearTemporaryZoomLevel(rfh_id); break; } - case ZoomMode::kIsolated: { - // Unless the zoom mode was |ZoomMode::kDisabled| before this call, the + case ZOOM_MODE_ISOLATED: { + // Unless the zoom mode was |ZOOM_MODE_DISABLED| before this call, the // page needs an initial isolated zoom back to the same level it was at // in the other mode. - if (zoom_mode_ != ZoomMode::kDisabled) { + if (zoom_mode_ != ZOOM_MODE_DISABLED) { zoom_map->SetTemporaryZoomLevel(rfh_id, original_zoom_level); } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), original_zoom_level, - false); + for (auto& observer : observers_) + observer.OnZoomChanged(*event_data_); + event_data_.reset(); } break; } - case ZoomMode::kManual: { - // Unless the zoom mode was |ZoomMode::kDisabled| before this call, the + case ZOOM_MODE_MANUAL: { + // Unless the zoom mode was |ZOOM_MODE_DISABLED| before this call, the // page needs to be resized to the default zoom. While in manual mode, // the zoom level is handled independently. - if (zoom_mode_ != ZoomMode::kDisabled) { + if (zoom_mode_ != ZOOM_MODE_DISABLED) { zoom_map->SetTemporaryZoomLevel(rfh_id, GetDefaultZoomLevel()); zoom_level_ = original_zoom_level; } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), original_zoom_level, - false); + for (auto& observer : observers_) + observer.OnZoomChanged(*event_data_); + event_data_.reset(); } break; } - case ZoomMode::kDisabled: { + case ZOOM_MODE_DISABLED: { // The page needs to be zoomed back to default before disabling the zoom - zoom_map->SetTemporaryZoomLevel(rfh_id, GetDefaultZoomLevel()); + double new_zoom_level = GetDefaultZoomLevel(); + event_data_->new_zoom_level = new_zoom_level; + zoom_map->SetTemporaryZoomLevel(rfh_id, new_zoom_level); break; } } + // Any event data we've stored should have been consumed by this point. + DCHECK(!event_data_); zoom_mode_ = new_mode; } void WebContentsZoomController::ResetZoomModeOnNavigationIfNeeded( const GURL& url) { - if (zoom_mode_ != ZoomMode::kIsolated && zoom_mode_ != ZoomMode::kManual) + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (zoom_mode_ != ZOOM_MODE_ISOLATED && zoom_mode_ != ZOOM_MODE_MANUAL) return; - content::GlobalRenderFrameHostId rfh_id = - web_contents()->GetPrimaryMainFrame()->GetGlobalId(); content::HostZoomMap* zoom_map = content::HostZoomMap::GetForWebContents(web_contents()); 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)); - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), new_zoom_level, false); - zoom_map->ClearTemporaryZoomLevel(rfh_id); - zoom_mode_ = ZoomMode::kDefault; + event_data_ = std::make_unique( + web_contents(), old_zoom_level, new_zoom_level, false, ZOOM_MODE_DEFAULT); + // The call to ClearTemporaryZoomLevel() doesn't generate any events from + // HostZoomMap, but the call to UpdateState() at the end of + // DidFinishNavigation will notify our observers. + // Note: it's possible the render_process/frame 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( + web_contents()->GetPrimaryMainFrame()->GetGlobalId()); + zoom_mode_ = ZOOM_MODE_DEFAULT; } void WebContentsZoomController::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted()) - return; - - if (navigation_handle->IsErrorPage()) { - content::HostZoomMap::SendErrorPageZoomLevelRefresh(web_contents()); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!navigation_handle->IsInPrimaryMainFrame() || + !navigation_handle->HasCommitted()) { return; } - ResetZoomModeOnNavigationIfNeeded(navigation_handle->GetURL()); - SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL()); + if (navigation_handle->IsErrorPage()) + content::HostZoomMap::SendErrorPageZoomLevelRefresh(web_contents()); + + if (!navigation_handle->IsSameDocument()) { + ResetZoomModeOnNavigationIfNeeded(navigation_handle->GetURL()); + SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL()); + } + + // If the main frame's content has changed, the new page may have a different + // zoom level from the old one. + UpdateState(std::string()); + DCHECK(!event_data_); } void WebContentsZoomController::WebContentsDestroyed() { - for (Observer& observer : observers_) - observer.OnZoomControllerWebContentsDestroyed(); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // At this point we should no longer be sending any zoom events with this + // WebContents. + for (auto& observer : observers_) { + observer.OnZoomControllerDestroyed(this); + } - observers_.Clear(); embedder_zoom_controller_ = nullptr; } void WebContentsZoomController::RenderFrameHostChanged( content::RenderFrameHost* old_host, content::RenderFrameHost* new_host) { - // If our associated HostZoomMap changes, update our event subscription. + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // If our associated HostZoomMap changes, update our subscription. content::HostZoomMap* new_host_zoom_map = content::HostZoomMap::GetForWebContents(web_contents()); if (new_host_zoom_map == host_zoom_map_) return; host_zoom_map_ = new_host_zoom_map; + zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback( + base::BindRepeating(&WebContentsZoomController::OnZoomLevelChanged, + base::Unretained(this))); } void WebContentsZoomController::SetZoomFactorOnNavigationIfNeeded( const GURL& url) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (blink::PageZoomValuesEqual(GetDefaultZoomFactor(), kPageZoomEpsilon)) return; @@ -276,6 +367,42 @@ void WebContentsZoomController::SetZoomFactorOnNavigationIfNeeded( SetZoomLevel(zoom_level); } +void WebContentsZoomController::OnZoomLevelChanged( + const content::HostZoomMap::ZoomLevelChange& change) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + UpdateState(change.host); +} + +void WebContentsZoomController::UpdateState(const std::string& host) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // If |host| is empty, all observers should be updated. + if (!host.empty()) { + // Use the navigation entry's URL instead of the WebContents' so virtual + // URLs work (e.g. chrome://settings). http://crbug.com/153950 + content::NavigationEntry* entry = + web_contents()->GetController().GetLastCommittedEntry(); + if (!entry || host != net::GetHostOrSpecFromURL( + content::HostZoomMap::GetURLFromEntry(entry))) { + return; + } + } + + if (event_data_) { + // For state changes initiated within the ZoomController, information about + // the change should be sent. + ZoomChangedEventData zoom_change_data = *event_data_; + event_data_.reset(); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); + } else { + double zoom_level = GetZoomLevel(); + ZoomChangedEventData zoom_change_data(web_contents(), zoom_level, + zoom_level, false, zoom_mode_); + for (auto& observer : observers_) + observer.OnZoomChanged(zoom_change_data); + } +} + WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsZoomController); } // namespace electron diff --git a/shell/browser/web_contents_zoom_controller.h b/shell/browser/web_contents_zoom_controller.h index c8e912464857..dcfe20b27da9 100644 --- a/shell/browser/web_contents_zoom_controller.h +++ b/shell/browser/web_contents_zoom_controller.h @@ -14,40 +14,49 @@ namespace electron { +class WebContentsZoomObserver; + // Manages the zoom changes of WebContents. class WebContentsZoomController : public content::WebContentsObserver, public content::WebContentsUserData { public: - class Observer : public base::CheckedObserver { - public: - virtual void OnZoomLevelChanged(content::WebContents* web_contents, - double level, - bool is_temporary) {} - virtual void OnZoomControllerWebContentsDestroyed() {} - - protected: - ~Observer() override {} - }; - // Defines how zoom changes are handled. - enum class ZoomMode { + enum ZoomMode { // Results in default zoom behavior, i.e. zoom changes are handled // automatically and on a per-origin basis, meaning that other tabs // navigated to the same origin will also zoom. - kDefault, + ZOOM_MODE_DEFAULT, // Results in zoom changes being handled automatically, but on a per-tab // basis. Tabs in this zoom mode will not be affected by zoom changes in // other tabs, and vice versa. - kIsolated, + ZOOM_MODE_ISOLATED, // Overrides the automatic handling of zoom changes. The |onZoomChange| // event will still be dispatched, but the page will not actually be zoomed. // These zoom changes can be handled manually by listening for the // |onZoomChange| event. Zooming in this mode is also on a per-tab basis. - kManual, + ZOOM_MODE_MANUAL, // Disables all zooming in this tab. The tab will revert to the default // zoom level, and all attempted zoom changes will be ignored. - kDisabled, + ZOOM_MODE_DISABLED, + }; + + struct ZoomChangedEventData { + ZoomChangedEventData(content::WebContents* web_contents, + double old_zoom_level, + double new_zoom_level, + bool temporary, + WebContentsZoomController::ZoomMode zoom_mode) + : web_contents(web_contents), + old_zoom_level(old_zoom_level), + new_zoom_level(new_zoom_level), + temporary(temporary), + zoom_mode(zoom_mode) {} + raw_ptr web_contents; + double old_zoom_level; + double new_zoom_level; + bool temporary; + WebContentsZoomController::ZoomMode zoom_mode; }; explicit WebContentsZoomController(content::WebContents* web_contents); @@ -58,24 +67,29 @@ class WebContentsZoomController WebContentsZoomController& operator=(const WebContentsZoomController&) = delete; - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); + void AddObserver(WebContentsZoomObserver* observer); + void RemoveObserver(WebContentsZoomObserver* observer); void SetEmbedderZoomController(WebContentsZoomController* controller); - // Methods for managing zoom levels. - void SetZoomLevel(double level); - double GetZoomLevel(); + // Gets the current zoom level by querying HostZoomMap (if not in manual zoom + // mode) or from the ZoomController local value otherwise. + double GetZoomLevel() const; + + // Sets the zoom level through HostZoomMap. + // Returns true on success. + bool SetZoomLevel(double zoom_level); + void SetDefaultZoomFactor(double factor); double GetDefaultZoomFactor(); + + // Sets the temporary zoom level through HostZoomMap. void SetTemporaryZoomLevel(double level); bool UsesTemporaryZoomLevel(); // Sets the zoom mode, which defines zoom behavior (see enum ZoomMode). void SetZoomMode(ZoomMode zoom_mode); - void ResetZoomModeOnNavigationIfNeeded(const GURL& url); - ZoomMode zoom_mode() const { return zoom_mode_; } // Convenience method to get default zoom level. Implemented here for @@ -86,8 +100,9 @@ class WebContentsZoomController } protected: - // content::WebContentsObserver: - void DidFinishNavigation(content::NavigationHandle* handle) override; + // content::WebContentsObserver overrides: + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; void WebContentsDestroyed() override; void RenderFrameHostChanged(content::RenderFrameHost* old_host, content::RenderFrameHost* new_host) override; @@ -95,29 +110,40 @@ class WebContentsZoomController private: friend class content::WebContentsUserData; - // Called after a navigation has committed to set default zoom factor. + void ResetZoomModeOnNavigationIfNeeded(const GURL& url); void SetZoomFactorOnNavigationIfNeeded(const GURL& url); + void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); + + // Updates the zoom icon and zoom percentage based on current values and + // notifies the observer if changes have occurred. |host| may be empty, + // meaning the change should apply to ~all sites. If it is not empty, the + // change only affects sites with the given host. + void UpdateState(const std::string& host); // The current zoom mode. - ZoomMode zoom_mode_ = ZoomMode::kDefault; + ZoomMode zoom_mode_ = ZOOM_MODE_DEFAULT; - // Current zoom level. - double zoom_level_ = 1.0; + // The current zoom level. + double zoom_level_; - // kZoomFactor. - double default_zoom_factor_ = 0; - - const double kPageZoomEpsilon = 0.001; + // The current default zoom factor. + double default_zoom_factor_; int old_process_id_ = -1; int old_view_id_ = -1; + std::unique_ptr event_data_; + raw_ptr embedder_zoom_controller_ = nullptr; - base::ObserverList observers_; + // Observer receiving notifications on state changes. + base::ObserverList observers_; + // Keep track of the HostZoomMap we're currently subscribed to. raw_ptr host_zoom_map_; + base::CallbackListSubscription zoom_subscription_; + WEB_CONTENTS_USER_DATA_KEY_DECL(); }; diff --git a/shell/browser/web_contents_zoom_observer.h b/shell/browser/web_contents_zoom_observer.h new file mode 100644 index 000000000000..fe95e06f1d5b --- /dev/null +++ b/shell/browser/web_contents_zoom_observer.h @@ -0,0 +1,29 @@ +// Copyright (c) 2023 Microsoft, GmbH +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ELECTRON_SHELL_BROWSER_WEB_CONTENTS_ZOOM_OBSERVER_H_ +#define ELECTRON_SHELL_BROWSER_WEB_CONTENTS_ZOOM_OBSERVER_H_ + +#include "shell/browser/web_contents_zoom_controller.h" + +namespace electron { + +// Interface for objects that wish to be notified of changes in +// WebContentsZoomController. +class WebContentsZoomObserver : public base::CheckedObserver { + public: + // Fired when the ZoomController is destructed. Observers should deregister + // themselves from the ZoomObserver in this event handler. Note that + // ZoomController::FromWebContents() returns nullptr at this point already. + virtual void OnZoomControllerDestroyed( + WebContentsZoomController* zoom_controller) = 0; + + // Notification that the zoom percentage has changed. + virtual void OnZoomChanged( + const WebContentsZoomController::ZoomChangedEventData& data) {} +}; + +} // namespace electron + +#endif // ELECTRON_SHELL_BROWSER_WEB_CONTENTS_ZOOM_OBSERVER_H_ diff --git a/shell/browser/web_view_guest_delegate.cc b/shell/browser/web_view_guest_delegate.cc index e67c84f1b7fa..686095bc2ee4 100644 --- a/shell/browser/web_view_guest_delegate.cc +++ b/shell/browser/web_view_guest_delegate.cc @@ -73,23 +73,23 @@ content::WebContents* WebViewGuestDelegate::GetOwnerWebContents() { return embedder_web_contents_; } -void WebViewGuestDelegate::OnZoomLevelChanged( - content::WebContents* web_contents, - double level, - bool is_temporary) { - if (web_contents == GetOwnerWebContents()) { - if (is_temporary) { - api_web_contents_->GetZoomController()->SetTemporaryZoomLevel(level); +void WebViewGuestDelegate::OnZoomChanged( + const WebContentsZoomController::ZoomChangedEventData& data) { + if (data.web_contents == GetOwnerWebContents()) { + if (data.temporary) { + api_web_contents_->GetZoomController()->SetTemporaryZoomLevel( + data.new_zoom_level); } else { - api_web_contents_->GetZoomController()->SetZoomLevel(level); + api_web_contents_->GetZoomController()->SetZoomLevel(data.new_zoom_level); } // Change the default zoom factor to match the embedders' new zoom level. - double zoom_factor = blink::PageZoomLevelToZoomFactor(level); + double zoom_factor = blink::PageZoomLevelToZoomFactor(data.new_zoom_level); api_web_contents_->GetZoomController()->SetDefaultZoomFactor(zoom_factor); } } -void WebViewGuestDelegate::OnZoomControllerWebContentsDestroyed() { +void WebViewGuestDelegate::OnZoomControllerDestroyed( + WebContentsZoomController* zoom_controller) { ResetZoomController(); } diff --git a/shell/browser/web_view_guest_delegate.h b/shell/browser/web_view_guest_delegate.h index 0fcde0083100..9d7a968e8369 100644 --- a/shell/browser/web_view_guest_delegate.h +++ b/shell/browser/web_view_guest_delegate.h @@ -10,6 +10,7 @@ #include "base/memory/raw_ptr.h" #include "content/public/browser/browser_plugin_guest_delegate.h" #include "shell/browser/web_contents_zoom_controller.h" +#include "shell/browser/web_contents_zoom_observer.h" namespace electron { @@ -18,7 +19,7 @@ class WebContents; } class WebViewGuestDelegate : public content::BrowserPluginGuestDelegate, - public WebContentsZoomController::Observer { + public WebContentsZoomObserver { public: WebViewGuestDelegate(content::WebContents* embedder, api::WebContents* api_web_contents); @@ -41,11 +42,11 @@ class WebViewGuestDelegate : public content::BrowserPluginGuestDelegate, base::WeakPtr GetGuestDelegateWeakPtr() final; - // WebContentsZoomController::Observer: - void OnZoomLevelChanged(content::WebContents* web_contents, - double level, - bool is_temporary) override; - void OnZoomControllerWebContentsDestroyed() override; + // WebContentsZoomObserver: + void OnZoomControllerDestroyed( + WebContentsZoomController* zoom_controller) override; + void OnZoomChanged( + const WebContentsZoomController::ZoomChangedEventData& data) override; private: void ResetZoomController();