1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_ 6 #define COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_ 7 8 #include <memory> 9 10 #include "base/compiler_specific.h" 11 #include "base/macros.h" 12 #include "base/memory/ref_counted.h" 13 #include "base/observer_list.h" 14 #include "components/prefs/pref_member.h" 15 #include "content/public/browser/host_zoom_map.h" 16 #include "content/public/browser/web_contents_observer.h" 17 #include "content/public/browser/web_contents_user_data.h" 18 19 class ZoomControllerTest; 20 21 namespace content { 22 class WebContents; 23 } 24 25 namespace zoom { 26 class ZoomObserver; 27 28 class ZoomRequestClient : public base::RefCounted<ZoomRequestClient> { 29 public: ZoomRequestClient()30 ZoomRequestClient() {} 31 virtual bool ShouldSuppressBubble() const = 0; 32 33 protected: ~ZoomRequestClient()34 virtual ~ZoomRequestClient() {} 35 36 private: 37 friend class base::RefCounted<ZoomRequestClient>; 38 39 DISALLOW_COPY_AND_ASSIGN(ZoomRequestClient); 40 }; 41 42 // Per-tab class to manage zoom changes and the Omnibox zoom icon. Lives on the 43 // UI thread. 44 class ZoomController : public content::WebContentsObserver, 45 public content::WebContentsUserData<ZoomController> { 46 public: 47 // Defines how zoom changes are handled. 48 enum ZoomMode { 49 // Results in default zoom behavior, i.e. zoom changes are handled 50 // automatically and on a per-origin basis, meaning that other tabs 51 // navigated to the same origin will also zoom. 52 ZOOM_MODE_DEFAULT, 53 // Results in zoom changes being handled automatically, but on a per-tab 54 // basis. Tabs in this zoom mode will not be affected by zoom changes in 55 // other tabs, and vice versa. 56 ZOOM_MODE_ISOLATED, 57 // Overrides the automatic handling of zoom changes. The |onZoomChange| 58 // event will still be dispatched, but the page will not actually be zoomed. 59 // These zoom changes can be handled manually by listening for the 60 // |onZoomChange| event. Zooming in this mode is also on a per-tab basis. 61 ZOOM_MODE_MANUAL, 62 // Disables all zooming in this tab. The tab will revert to the default 63 // zoom level, and all attempted zoom changes will be ignored. 64 ZOOM_MODE_DISABLED, 65 }; 66 67 enum RelativeZoom { 68 ZOOM_BELOW_DEFAULT_ZOOM, 69 ZOOM_AT_DEFAULT_ZOOM, 70 ZOOM_ABOVE_DEFAULT_ZOOM 71 }; 72 73 struct ZoomChangedEventData { ZoomChangedEventDataZoomChangedEventData74 ZoomChangedEventData(content::WebContents* web_contents, 75 double old_zoom_level, 76 double new_zoom_level, 77 ZoomController::ZoomMode zoom_mode, 78 bool can_show_bubble) 79 : web_contents(web_contents), 80 old_zoom_level(old_zoom_level), 81 new_zoom_level(new_zoom_level), 82 zoom_mode(zoom_mode), 83 can_show_bubble(can_show_bubble) {} 84 content::WebContents* web_contents; 85 double old_zoom_level; 86 double new_zoom_level; 87 ZoomController::ZoomMode zoom_mode; 88 bool can_show_bubble; 89 }; 90 91 // Since it's possible for a WebContents to not have a ZoomController, provide 92 // a simple, safe and reliable method to find the current zoom level for a 93 // given WebContents*. 94 static double GetZoomLevelForWebContents(content::WebContents* web_contents); 95 96 ~ZoomController() override; 97 zoom_mode()98 ZoomMode zoom_mode() const { return zoom_mode_; } 99 100 // Convenience method to get default zoom level. Implemented here for 101 // inlining. GetDefaultZoomLevel()102 double GetDefaultZoomLevel() const { 103 return content::HostZoomMap::GetForWebContents(web_contents()) 104 ->GetDefaultZoomLevel(); 105 } 106 107 // Convenience method to quickly check if the tab's at default zoom. 108 // Virtual for testing. 109 virtual bool IsAtDefaultZoom() const; 110 111 // Returns which image should be loaded for the current zoom level. 112 RelativeZoom GetZoomRelativeToDefault() const; 113 last_client()114 const ZoomRequestClient* last_client() const { return last_client_.get(); } 115 116 void AddObserver(ZoomObserver* observer); 117 void RemoveObserver(ZoomObserver* observer); 118 119 // Used to set whether the zoom notification bubble can be shown when the 120 // zoom level is changed for this controller. Default behavior is to show 121 // the bubble. SetShowsNotificationBubble(bool can_show_bubble)122 void SetShowsNotificationBubble(bool can_show_bubble) { 123 can_show_bubble_ = can_show_bubble; 124 } 125 126 // Gets the current zoom level by querying HostZoomMap (if not in manual zoom 127 // mode) or from the ZoomController local value otherwise. 128 double GetZoomLevel() const; 129 // Calls GetZoomLevel() then converts the returned value to a percentage 130 // zoom factor. 131 // Virtual for testing. 132 virtual int GetZoomPercent() const; 133 134 // Sets the zoom level through HostZoomMap. 135 // Returns true on success. 136 bool SetZoomLevel(double zoom_level); 137 138 // Sets the zoom level via HostZoomMap (or stores it locally if in manual zoom 139 // mode), and attributes the zoom to |client|. Returns true on success. 140 bool SetZoomLevelByClient( 141 double zoom_level, 142 const scoped_refptr<const ZoomRequestClient>& client); 143 144 // Sets the zoom mode, which defines zoom behavior (see enum ZoomMode). 145 void SetZoomMode(ZoomMode zoom_mode); 146 147 // Set and query whether or not the page scale factor is one. 148 void SetPageScaleFactorIsOneForTesting(bool is_one); 149 bool PageScaleFactorIsOne() const; 150 151 // content::WebContentsObserver overrides: 152 void DidFinishNavigation( 153 content::NavigationHandle* navigation_handle) override; 154 void WebContentsDestroyed() override; 155 void RenderFrameHostChanged(content::RenderFrameHost* old_host, 156 content::RenderFrameHost* new_host) override; 157 158 protected: 159 // Protected for testing. 160 explicit ZoomController(content::WebContents* web_contents); 161 162 private: 163 friend class content::WebContentsUserData<ZoomController>; 164 friend class ::ZoomControllerTest; 165 166 void ResetZoomModeOnNavigationIfNeeded(const GURL& url); 167 void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); 168 169 // Updates the zoom icon and zoom percentage based on current values and 170 // notifies the observer if changes have occurred. |host| may be empty, 171 // meaning the change should apply to ~all sites. If it is not empty, the 172 // change only affects sites with the given host. 173 void UpdateState(const std::string& host); 174 175 // True if changes to zoom level can trigger the zoom notification bubble. 176 bool can_show_bubble_; 177 178 // The current zoom mode. 179 ZoomMode zoom_mode_; 180 181 // Current zoom level. 182 double zoom_level_; 183 184 std::unique_ptr<ZoomChangedEventData> event_data_; 185 186 // Keeps track of the extension (if any) that initiated the last zoom change 187 // that took effect. 188 scoped_refptr<const ZoomRequestClient> last_client_; 189 190 // Observer receiving notifications on state changes. 191 base::ObserverList<ZoomObserver>::Unchecked observers_; 192 193 content::BrowserContext* browser_context_; 194 // Keep track of the HostZoomMap we're currently subscribed to. 195 content::HostZoomMap* host_zoom_map_; 196 197 std::unique_ptr<content::HostZoomMap::Subscription> zoom_subscription_; 198 199 WEB_CONTENTS_USER_DATA_KEY_DECL(); 200 201 DISALLOW_COPY_AND_ASSIGN(ZoomController); 202 }; 203 204 } // namespace zoom 205 206 #endif // COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_ 207