diff --git a/atom/browser/atom_web_ui_controller_factory.cc b/atom/browser/atom_web_ui_controller_factory.cc index a73eed19a11..ef8b2785ae5 100644 --- a/atom/browser/atom_web_ui_controller_factory.cc +++ b/atom/browser/atom_web_ui_controller_factory.cc @@ -57,6 +57,9 @@ AtomWebUIControllerFactory::CreateWebUIControllerForURL(content::WebUI* web_ui, src = param.second; } } + if (url.has_ref()) { + src = src + '#' + url.ref(); + } auto browser_context = web_ui->GetWebContents()->GetBrowserContext(); return new PdfViewerUI(browser_context, web_ui, src); } diff --git a/atom/browser/ui/webui/pdf_viewer_handler.cc b/atom/browser/ui/webui/pdf_viewer_handler.cc index 5829b59f6d3..e00ab9d4d93 100644 --- a/atom/browser/ui/webui/pdf_viewer_handler.cc +++ b/atom/browser/ui/webui/pdf_viewer_handler.cc @@ -90,15 +90,15 @@ void PdfViewerHandler::RegisterMessages() { } void PdfViewerHandler::OnJavascriptAllowed() { - auto host_zoom_map = - content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents()); - host_zoom_map_subscription_ = - host_zoom_map->AddZoomLevelChangedCallback(base::Bind( - &PdfViewerHandler::OnZoomLevelChanged, base::Unretained(this))); + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + zoom_controller->AddObserver(this); } void PdfViewerHandler::OnJavascriptDisallowed() { - host_zoom_map_subscription_.reset(); + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + zoom_controller->RemoveObserver(this); } void PdfViewerHandler::Initialize(const base::ListValue* args) { @@ -116,6 +116,11 @@ void PdfViewerHandler::Initialize(const base::ListValue* args) { } else { initialize_callback_id_ = callback_id->CreateDeepCopy(); } + + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + zoom_controller->SetZoomMode(WebContentsZoomController::ZOOM_MODE_MANUAL); + zoom_controller->SetZoomLevel(0); } void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) { @@ -125,9 +130,9 @@ void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) { const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); - auto host_zoom_map = - content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents()); - double zoom_level = host_zoom_map->GetDefaultZoomLevel(); + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + double zoom_level = zoom_controller->GetDefaultZoomLevel(); ResolveJavascriptCallback( *callback_id, base::Value(content::ZoomLevelToZoomFactor(zoom_level))); @@ -140,8 +145,9 @@ void PdfViewerHandler::GetInitialZoom(const base::ListValue* args) { const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); - double zoom_level = - content::HostZoomMap::GetZoomLevel(web_ui()->GetWebContents()); + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + double zoom_level = zoom_controller->GetZoomLevel(); ResolveJavascriptCallback( *callback_id, base::Value(content::ZoomLevelToZoomFactor(zoom_level))); @@ -156,8 +162,9 @@ void PdfViewerHandler::SetZoom(const base::ListValue* args) { double zoom_level = 0.0; CHECK(args->GetDouble(1, &zoom_level)); - content::HostZoomMap::SetZoomLevel(web_ui()->GetWebContents(), - zoom_level); + auto zoom_controller = WebContentsZoomController::FromWebContents( + web_ui()->GetWebContents()); + zoom_controller->SetZoomLevel(zoom_level); ResolveJavascriptCallback(*callback_id, base::Value(zoom_level)); } @@ -198,13 +205,12 @@ void PdfViewerHandler::Reload(const base::ListValue* args) { web_ui()->GetWebContents()->ReloadFocusedFrame(false); } -void PdfViewerHandler::OnZoomLevelChanged( - const content::HostZoomMap::ZoomLevelChange& change) { - if (change.host == kPdfViewerUIHost) { - CallJavascriptFunction( - "cr.webUIListenerCallback", base::StringValue("onZoomLevelChanged"), - base::Value( - content::ZoomLevelToZoomFactor(change.zoom_level))); +void PdfViewerHandler::OnZoomLevelChanged(content::WebContents* web_contents, + double level, bool is_temporary) { + if (web_ui()->GetWebContents() == web_contents) { + CallJavascriptFunction("cr.webUIListenerCallback", + base::StringValue("onZoomLevelChanged"), + base::Value(content::ZoomLevelToZoomFactor(level))); } } diff --git a/atom/browser/ui/webui/pdf_viewer_handler.h b/atom/browser/ui/webui/pdf_viewer_handler.h index 7576b06c851..e6ae315e876 100644 --- a/atom/browser/ui/webui/pdf_viewer_handler.h +++ b/atom/browser/ui/webui/pdf_viewer_handler.h @@ -7,6 +7,7 @@ #include +#include "atom/browser/web_contents_zoom_controller.h" #include "base/macros.h" #include "content/public/browser/host_zoom_map.h" #include "content/public/browser/web_ui_message_handler.h" @@ -21,7 +22,8 @@ struct StreamInfo; namespace atom { -class PdfViewerHandler : public content::WebUIMessageHandler { +class PdfViewerHandler : public content::WebUIMessageHandler, + public WebContentsZoomController::Observer { public: explicit PdfViewerHandler(const std::string& original_url); ~PdfViewerHandler() override; @@ -41,11 +43,9 @@ class PdfViewerHandler : public content::WebUIMessageHandler { void SetZoom(const base::ListValue* args); void GetStrings(const base::ListValue* args); void Reload(const base::ListValue* args); - void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); + void OnZoomLevelChanged(content::WebContents* web_contents, double level, + bool is_temporary); - // Keeps track of events related to zooming. - std::unique_ptr - host_zoom_map_subscription_; std::unique_ptr initialize_callback_id_; content::StreamInfo* stream_; std::string original_url_; diff --git a/atom/browser/web_contents_zoom_controller.cc b/atom/browser/web_contents_zoom_controller.cc index f48e643d96a..3c8d512b6bb 100644 --- a/atom/browser/web_contents_zoom_controller.cc +++ b/atom/browser/web_contents_zoom_controller.cc @@ -21,6 +21,8 @@ namespace atom { WebContentsZoomController::WebContentsZoomController( content::WebContents* web_contents) : content::WebContentsObserver(web_contents), + zoom_mode_(ZOOM_MODE_DEFAULT), + zoom_level_(1.0), old_process_id_(-1), old_view_id_(-1), embedder_zoom_controller_(nullptr) { @@ -47,24 +49,44 @@ void WebContentsZoomController::SetEmbedderZoomController( void WebContentsZoomController::SetZoomLevel(double level) { if (!web_contents()->GetRenderViewHost()->IsRenderViewLive() || - content::ZoomValuesEqual(GetZoomLevel(), level)) + content::ZoomValuesEqual(GetZoomLevel(), level) || + zoom_mode_ == ZOOM_MODE_DISABLED) return; int render_process_id = web_contents()->GetRenderProcessHost()->GetID(); int render_view_id = web_contents()->GetRenderViewHost()->GetRoutingID(); - if (host_zoom_map_->UsesTemporaryZoomLevel(render_process_id, - render_view_id)) { - host_zoom_map_->ClearTemporaryZoomLevel(render_process_id, render_view_id); + + if (zoom_mode_ == ZOOM_MODE_MANUAL) { + zoom_level_ = level; + + for (Observer& observer : observers_) + observer.OnZoomLevelChanged(web_contents(), level, true); + + return; } - content::HostZoomMap::SetZoomLevel(web_contents(), level); - // Notify observers of zoom level changes. - for (Observer& observer : observers_) - observer.OnZoomLevelChanged(web_contents(), level, false); + content::HostZoomMap* zoom_map = + content::HostZoomMap::GetForWebContents(web_contents()); + if (zoom_mode_ == ZOOM_MODE_ISOLATED || + zoom_map->UsesTemporaryZoomLevel(render_process_id, render_view_id)) { + zoom_map->SetTemporaryZoomLevel( + render_process_id, render_view_id, level); + // Notify observers of zoom level changes. + for (Observer& observer : observers_) + observer.OnZoomLevelChanged(web_contents(), level, true); + } else { + content::HostZoomMap::SetZoomLevel(web_contents(), level); + + // Notify observers of zoom level changes. + for (Observer& observer : observers_) + observer.OnZoomLevelChanged(web_contents(), level, false); + } } double WebContentsZoomController::GetZoomLevel() { - return content::HostZoomMap::GetZoomLevel(web_contents()); + return zoom_mode_ == ZOOM_MODE_MANUAL + ? zoom_level_ + : content::HostZoomMap::GetZoomLevel(web_contents()); } void WebContentsZoomController::SetDefaultZoomFactor(double factor) { @@ -91,6 +113,110 @@ bool WebContentsZoomController::UsesTemporaryZoomLevel() { render_view_id); } + +void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { + if (new_mode == zoom_mode_) + return; + + content::HostZoomMap* zoom_map = + content::HostZoomMap::GetForWebContents(web_contents()); + int render_process_id = web_contents()->GetRenderProcessHost()->GetID(); + int render_view_id = web_contents()->GetRenderViewHost()->GetRoutingID(); + double original_zoom_level = GetZoomLevel(); + + switch (new_mode) { + case ZOOM_MODE_DEFAULT: { + content::NavigationEntry* entry = + web_contents()->GetController().GetLastCommittedEntry(); + + if (entry) { + GURL url = content::HostZoomMap::GetURLFromEntry(entry); + std::string host = net::GetHostOrSpecFromURL(url); + + if (zoom_map->HasZoomLevel(url.scheme(), host)) { + // If there are other tabs with the same origin, then set this tab's + // zoom level to match theirs. The temporary zoom level will be + // cleared below, but this call will make sure this tab re-draws at + // the correct zoom level. + double origin_zoom_level = + zoom_map->GetZoomLevelForHostAndScheme(url.scheme(), host); + zoom_map->SetTemporaryZoomLevel(render_process_id, render_view_id, + origin_zoom_level); + } else { + // The host will need a level prior to removing the temporary level. + // We don't want the zoom level to change just because we entered + // default mode. + zoom_map->SetZoomLevelForHost(host, original_zoom_level); + } + } + // Remove per-tab zoom data for this tab. No event callback expected. + zoom_map->ClearTemporaryZoomLevel(render_process_id, render_view_id); + break; + } + 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_ != ZOOM_MODE_DISABLED) { + zoom_map->SetTemporaryZoomLevel(render_process_id, render_view_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); + } + break; + } + 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_ != ZOOM_MODE_DISABLED) { + zoom_map->SetTemporaryZoomLevel( + render_process_id, render_view_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); + } + break; + } + case ZOOM_MODE_DISABLED: { + // The page needs to be zoomed back to default before disabling the zoom + zoom_map->SetTemporaryZoomLevel( + render_process_id, render_view_id, GetDefaultZoomLevel()); + break; + } + } + + zoom_mode_ = new_mode; +} + + +void WebContentsZoomController::ResetZoomModeOnNavigationIfNeeded( + const GURL& url) { + if (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()); + zoom_level_ = zoom_map->GetDefaultZoomLevel(); + 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(render_process_id, render_view_id); + zoom_mode_ = ZOOM_MODE_DEFAULT; +} + void WebContentsZoomController::DidFinishNavigation( content::NavigationHandle* navigation_handle) { if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted()) @@ -101,6 +227,7 @@ void WebContentsZoomController::DidFinishNavigation( return; } + ResetZoomModeOnNavigationIfNeeded(navigation_handle->GetURL()); SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL()); } diff --git a/atom/browser/web_contents_zoom_controller.h b/atom/browser/web_contents_zoom_controller.h index a0d28ad9b99..220d5c45bfa 100644 --- a/atom/browser/web_contents_zoom_controller.h +++ b/atom/browser/web_contents_zoom_controller.h @@ -29,6 +29,26 @@ class WebContentsZoomController virtual ~Observer() {} }; + // Defines how zoom changes are handled. + 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. + 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. + 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. + 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. + ZOOM_MODE_DISABLED, + }; + explicit WebContentsZoomController(content::WebContents* web_contents); ~WebContentsZoomController() override; @@ -45,6 +65,20 @@ class WebContentsZoomController 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 + // inlining. + double GetDefaultZoomLevel() const { + return content::HostZoomMap::GetForWebContents(web_contents()) + ->GetDefaultZoomLevel(); + } + protected: // content::WebContentsObserver: void DidFinishNavigation(content::NavigationHandle* handle) override; @@ -58,6 +92,12 @@ class WebContentsZoomController // Called after a navigation has committed to set default zoom factor. void SetZoomFactorOnNavigationIfNeeded(const GURL& url); + // The current zoom mode. + ZoomMode zoom_mode_; + + // Current zoom level. + double zoom_level_; + // kZoomFactor. double default_zoom_factor_; double temporary_zoom_level_;