File: zoom_controller.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (289 lines) | stat: -rw-r--r-- 10,909 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_
#define COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_

#include <memory>
#include <optional>

#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "components/prefs/pref_member.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"

class ZoomControllerTest;

namespace content {
class WebContents;
}

namespace zoom {
class ZoomObserver;

class ZoomRequestClient : public base::RefCounted<ZoomRequestClient> {
 public:
  ZoomRequestClient() = default;

  ZoomRequestClient(const ZoomRequestClient&) = delete;
  ZoomRequestClient& operator=(const ZoomRequestClient&) = delete;

  virtual bool ShouldSuppressBubble() const = 0;

 protected:
  virtual ~ZoomRequestClient() = default;

 private:
  friend class base::RefCounted<ZoomRequestClient>;
};

// ZoomController manages zoom changes and the Omnibox zoom icon. It can be
// created for main frames and subframes. Creating for subframes allows those
// frames to have zoom behavior independent from the main frame's. Lives on the
// UI thread.
class ZoomController : public content::WebContentsObserver {
 public:
  // 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,
  };

  enum RelativeZoom {
    ZOOM_BELOW_DEFAULT_ZOOM,
    ZOOM_AT_DEFAULT_ZOOM,
    ZOOM_ABOVE_DEFAULT_ZOOM
  };

  struct ZoomChangedEventData {
    ZoomChangedEventData(content::WebContents* web_contents,
                         content::FrameTreeNodeId ftn_id,
                         double old_zoom_level,
                         double new_zoom_level,
                         ZoomController::ZoomMode zoom_mode,
                         bool can_show_bubble)
        : web_contents(web_contents),
          frame_tree_node_id(ftn_id),
          old_zoom_level(old_zoom_level),
          new_zoom_level(new_zoom_level),
          zoom_mode(zoom_mode),
          can_show_bubble(can_show_bubble) {}
    raw_ptr<content::WebContents> web_contents;
    // Since there can be multiple ZoomControllers for a given WebContents,
    // include the FrameTreeNodeId to uniquely identify which one created this
    // struct. One case where a single WebContents will have multiple
    // ZoomControllers is in a GuestView with features::kGuestViewMPArch
    // enabled.
    content::FrameTreeNodeId frame_tree_node_id;
    double old_zoom_level;
    double new_zoom_level;
    ZoomController::ZoomMode zoom_mode;
    bool can_show_bubble;
  };

  // Since it's possible for a WebContents to not have a ZoomController, provide
  // a simple, safe and reliable method to find the current zoom level for a
  // given WebContents*.
  static double GetZoomLevelForWebContents(content::WebContents* web_contents);

  // Used to create a ZoomController for the primary mainframe of
  // `web_contents`.
  static ZoomController* CreateForWebContents(
      content::WebContents* web_contents);

  // Use to create a ZoomController for a subframe in `web_contents`. The
  // specified `rfh_id` must be for a local-root RenderFrameHost.
  static ZoomController* CreateForWebContentsAndRenderFrameHost(
      content::WebContents* web_contents,
      content::GlobalRenderFrameHostId rfh_id);

  // Retrieves the ZoomController for `web_contents` primary mainframe if it
  // exists, otherwise returns nullptr.
  static ZoomController* FromWebContents(
      const content::WebContents* web_contents);

  // Retrieves the ZoomController for `web_contents` and the specified `rfh_id`
  // if it exists, otherwise returns nullptr.
  static ZoomController* FromWebContentsAndRenderFrameHost(
      const content::WebContents* web_contents,
      content::GlobalRenderFrameHostId rfh_id);

  ZoomController(const ZoomController&) = delete;
  ZoomController& operator=(const ZoomController&) = delete;

  ~ZoomController() override;

  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();
  }

  // Convenience method to quickly check if the tab's at default zoom.
  // Virtual for testing.
  virtual bool IsAtDefaultZoom() const;

  // Returns which image should be loaded for the current zoom level.
  RelativeZoom GetZoomRelativeToDefault() const;

  const ZoomRequestClient* last_client() const { return last_client_.get(); }

  void AddObserver(ZoomObserver* observer);
  void RemoveObserver(ZoomObserver* observer);

  // Used to set whether the zoom notification bubble can be shown when the
  // zoom level is changed for this controller. Default behavior is to show
  // the bubble.
  void SetShowsNotificationBubble(bool can_show_bubble) {
    can_show_bubble_ = can_show_bubble;
  }

  // Gets the current zoom level by querying HostZoomMap (if not in manual zoom
  // mode) or from the ZoomController local value otherwise.
  double GetZoomLevel() const;
  // Calls GetZoomLevel() then converts the returned value to a percentage
  // zoom factor.
  // Virtual for testing.
  virtual int GetZoomPercent() const;

  // Sets the zoom level through HostZoomMap.
  // Returns true on success.
  bool SetZoomLevel(double zoom_level);

  // Sets the zoom level via HostZoomMap (or stores it locally if in manual zoom
  // mode), and attributes the zoom to |client|. Returns true on success.
  bool SetZoomLevelByClient(
      double zoom_level,
      const scoped_refptr<const ZoomRequestClient>& client);

  // Sets the zoom mode, which defines zoom behavior (see enum ZoomMode).
  void SetZoomMode(ZoomMode zoom_mode);

  // Set and query whether or not the page scale factor is one.
  void SetPageScaleFactorIsOneForTesting(bool is_one);
  bool PageScaleFactorIsOne() const;

  // content::WebContentsObserver overrides:
  void DidFinishNavigation(
      content::NavigationHandle* navigation_handle) override;
  void WebContentsDestroyed() override;
  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
                              content::RenderFrameHost* new_host) override;
  void OnPageScaleFactorChanged(float page_scale_factor) override;
  void FrameDeleted(content::FrameTreeNodeId ftn_id) override;

 protected:
  // Protected for testing.
  explicit ZoomController(content::WebContents* web_contents,
                          content::RenderFrameHost* rfh);

 private:
  friend class ::ZoomControllerTest;

  // A class to (i) be owned by WebContents as UserData, and (ii) own and manage
  // all the ZoomControllers in that WebContents.
  class Manager : public content::WebContentsUserData<Manager> {
   public:
    explicit Manager(content::WebContents* web_contents);
    ~Manager() override;

    ZoomController* GetZoomController(
        const content::GlobalRenderFrameHostId& rfh_id) const;

    void AddZoomControllerIfNecessary(
        content::WebContents* web_contents,
        const content::GlobalRenderFrameHostId& rfh_id);

    // Called from ZoomController to notify that one of the ZoomControllers has
    // had its frame deleted, meaning the ZoomCOntroller itself should be
    // deleted.
    void FrameDeleted(content::FrameTreeNodeId ftn_id);

   private:
    friend class content::WebContentsUserData<Manager>;

    // The map is keyed on FrameTreeNodeId, but this class will
    // have to update the map to account for frames being deleted. The
    // ZoomController object will call out to the manager to provide the
    // details.
    base::flat_map<content::FrameTreeNodeId, std::unique_ptr<ZoomController>>
        zoom_controller_map_;

    WEB_CONTENTS_USER_DATA_KEY_DECL();
  };

  // Note: this function uses WebContents::UnsafeFindFrameByFrameTreeNodeId,
  // so the RenderFrameHost* returned should be used immediately, and not
  // stored.
  content::RenderFrameHost* GetRenderFrameHost() const;
  void ResetZoomModeOnNavigationIfNeeded(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);

  // Stores the FrameTreeNodeId of the RenderFrameHost this ZoomController was
  // created with.
  const content::FrameTreeNodeId frame_tree_node_id_;

  // True if changes to zoom level can trigger the zoom notification bubble.
  bool can_show_bubble_ = true;

  // The current zoom mode.
  ZoomMode zoom_mode_ = ZOOM_MODE_DEFAULT;

  // Current zoom level.
  double zoom_level_;

  std::unique_ptr<ZoomChangedEventData> event_data_;

  // Keeps track of the extension (if any) that initiated the last zoom change
  // that took effect.
  scoped_refptr<const ZoomRequestClient> last_client_;

  // Observer receiving notifications on state changes.
  base::ObserverList<ZoomObserver> observers_;

  raw_ptr<content::BrowserContext> browser_context_;
  // Keep track of the HostZoomMap we're currently subscribed to.
  raw_ptr<content::HostZoomMap> host_zoom_map_;

  base::CallbackListSubscription zoom_subscription_;

  // Whether the page scale factor was one the last time we notified our
  // observers of a change to PageScaleFactorIsOne.
  bool last_page_scale_factor_was_one_ = true;

  // If set, this value is returned in PageScaleFactorIsOne.
  std::optional<bool> page_scale_factor_is_one_for_testing_;
};

}  // namespace zoom

#endif  // COMPONENTS_ZOOM_ZOOM_CONTROLLER_H_