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 CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ZOOM_BUBBLE_VIEW_H_
6 #define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ZOOM_BUBBLE_VIEW_H_
7 
8 #include "base/gtest_prod_util.h"
9 #include "base/macros.h"
10 #include "base/timer/timer.h"
11 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
12 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
13 #include "components/sessions/core/session_id.h"
14 #include "content/public/browser/notification_observer.h"
15 #include "content/public/browser/notification_registrar.h"
16 #include "content/public/browser/web_contents_observer.h"
17 #include "extensions/browser/extension_icon_image.h"
18 #include "ui/views/controls/label.h"
19 
20 namespace content {
21 class WebContents;
22 }
23 
24 namespace views {
25 class AXVirtualView;
26 class Button;
27 class ImageButton;
28 }  // namespace views
29 
30 // View used to display the zoom percentage when it has changed.
31 class ZoomBubbleView : public LocationBarBubbleDelegateView,
32                        public ImmersiveModeController::Observer,
33                        public extensions::IconImage::Observer {
34  public:
35   // Shows the bubble and automatically closes it after a short time period if
36   // |reason| is AUTOMATIC.
37   static void ShowBubble(content::WebContents* web_contents,
38                          DisplayReason reason);
39 
40   // If the bubble is being shown for the given |web_contents|, refreshes it.
41   static bool RefreshBubbleIfShowing(const content::WebContents* web_contents);
42 
43   // Closes the showing bubble (if one exists).
44   static void CloseCurrentBubble();
45 
46   // Returns the zoom bubble if the zoom bubble is showing. Returns NULL
47   // otherwise.
48   static ZoomBubbleView* GetZoomBubble();
49 
50   // Refreshes the bubble by changing the zoom percentage appropriately and
51   // resetting the timer if necessary.
52   void Refresh();
53 
54  private:
55   FRIEND_TEST_ALL_PREFIXES(ZoomBubbleBrowserTest, ImmersiveFullscreen);
56   FRIEND_TEST_ALL_PREFIXES(ZoomBubbleBrowserTest,
57                            BubbleSuppressingExtensionRefreshesExistingBubble);
58   FRIEND_TEST_ALL_PREFIXES(ZoomBubbleBrowserTest, FocusPreventsClose);
59   FRIEND_TEST_ALL_PREFIXES(ZoomBubbleBrowserTest, AnchorPositionsInFullscreen);
60 
61   // Returns true if we can reuse the existing bubble for the given
62   // |web_contents|.
63   static bool CanRefresh(const content::WebContents* web_contents);
64 
65   // Stores information about the extension that initiated the zoom change, if
66   // any.
67   struct ZoomBubbleExtensionInfo {
68     ZoomBubbleExtensionInfo();
69     ~ZoomBubbleExtensionInfo();
70 
71     // The unique id of the extension, which is used to find the correct
72     // extension after clicking on the image button in the zoom bubble.
73     std::string id;
74 
75     // The name of the extension, which appears in the tooltip of the image
76     // button in the zoom bubble.
77     std::string name;
78 
79     // An image of the extension's icon, which appears in the zoom bubble as an
80     // image button.
81     std::unique_ptr<const extensions::IconImage> icon_image;
82   };
83 
84   // Constructs ZoomBubbleView. Anchors the bubble to |anchor_view|, which must
85   // not be nullptr. The bubble will auto-close when |reason| is AUTOMATIC. If
86   // |immersive_mode_controller_| is present, the bubble will auto-close when
87   // the top-of-window views are revealed.
88   ZoomBubbleView(views::View* anchor_view,
89                  content::WebContents* web_contents,
90                  DisplayReason reason,
91                  ImmersiveModeController* immersive_mode_controller);
92   ~ZoomBubbleView() override;
93 
94   // LocationBarBubbleDelegateView:
95   base::string16 GetAccessibleWindowTitle() const override;
96   void OnFocus() override;
97   void OnBlur() override;
98   void OnGestureEvent(ui::GestureEvent* event) override;
99   void OnKeyEvent(ui::KeyEvent* event) override;
100   void OnMouseEntered(const ui::MouseEvent& event) override;
101   void OnMouseExited(const ui::MouseEvent& event) override;
102   void Init() override;
103   void WindowClosing() override;
104   void CloseBubble() override;
105 
106   // ImmersiveModeController::Observer
107   void OnImmersiveRevealStarted() override;
108   void OnImmersiveModeControllerDestroyed() override;
109 
110   // extensions::IconImage::Observer
111   void OnExtensionIconImageChanged(extensions::IconImage* /* image */) override;
112 
113   // Sets information about the extension that initiated the zoom change.
114   // Calling this method asserts that the extension |extension| did initiate
115   // the zoom change.
116   void SetExtensionInfo(const extensions::Extension* extension);
117 
118   // Updates |label_| with the up to date zoom.
119   void UpdateZoomPercent();
120 
121   // Updates visibility of the zoom icon.
122   void UpdateZoomIconVisibility();
123 
124   // Starts a timer which will close the bubble if |auto_close_| is true.
125   void StartTimerIfNecessary();
126 
127   // Stops the auto-close timer.
128   void StopTimer();
129 
130   // Called when any button is pressed; does common logic, then runs |closure|.
131   void ButtonPressed(base::RepeatingClosure closure);
132 
133   // Called by ButtonPressed() when |image_button_| is pressed.
134   void ImageButtonPressed();
135 
136   ZoomBubbleExtensionInfo extension_info_;
137 
138   // Singleton instance of the zoom bubble. The zoom bubble can only be shown on
139   // the active browser window, so there is no case in which it will be shown
140   // twice at the same time.
141   static ZoomBubbleView* zoom_bubble_;
142 
143   // Timer used to auto close the bubble.
144   base::OneShotTimer auto_close_timer_;
145 
146   // Timer duration that is made longer if a user presses + or - buttons.
147   base::TimeDelta auto_close_duration_;
148 
149   // Image button in the zoom bubble that will show the |extension_icon_| image
150   // if an extension initiated the zoom change, and links to that extension at
151   // "chrome://extensions".
152   views::ImageButton* image_button_ = nullptr;
153 
154   // Label displaying the zoom percentage.
155   views::Label* label_ = nullptr;
156 
157   // Action buttons that can change zoom.
158   views::Button* zoom_out_button_ = nullptr;
159   views::Button* zoom_in_button_ = nullptr;
160   views::Button* reset_button_ = nullptr;
161 
162   // Virtual view used to announce zoom level changes.
163   views::AXVirtualView* zoom_level_alert_ = nullptr;
164 
165   // Whether the currently displayed bubble will automatically close.
166   bool auto_close_;
167 
168   // Used to ignore close requests generated automatically in response to
169   // button presses, since pressing a button in the bubble should not trigger
170   // closing.
171   bool ignore_close_bubble_ = false;
172 
173   // The immersive mode controller for the BrowserView containing
174   // |web_contents_|.
175   // Not owned.
176   ImmersiveModeController* immersive_mode_controller_;
177 
178   // The session of the Browser that triggered the bubble. This allows the zoom
179   // icon to be updated even if the WebContents is destroyed.
180   const SessionID session_id_;
181 
182   DISALLOW_COPY_AND_ASSIGN(ZoomBubbleView);
183 };
184 
185 #endif  // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ZOOM_BUBBLE_VIEW_H_
186