1 // Copyright 2019 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_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_
6 #define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_
7 
8 #include <map>
9 #include <memory>
10 #include <set>
11 #include <string>
12 #include <vector>
13 
14 #include "base/optional.h"
15 #include "chrome/browser/ui/extensions/extensions_container.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
17 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
18 #include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
19 #include "chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h"
20 #include "ui/views/widget/widget_observer.h"
21 
22 class Browser;
23 class ExtensionsToolbarButton;
24 class ToolbarActionViewController;
25 
26 // Container for extensions shown in the toolbar. These include pinned
27 // extensions and extensions that are 'popped out' transitively to show dialogs
28 // or be called out to the user.
29 // This container is used when the extension-menu experiment is active as a
30 // replacement for BrowserActionsContainer and ToolbarActionsBar which are
31 // intended to be removed.
32 // TODO(crbug.com/943702): Remove note related to extensions menu when cleaning
33 // up after the experiment.
34 class ExtensionsToolbarContainer : public ToolbarIconContainerView,
35                                    public ExtensionsContainer,
36                                    public TabStripModelObserver,
37                                    public ToolbarActionsModel::Observer,
38                                    public ToolbarActionView::Delegate,
39                                    public views::WidgetObserver {
40  public:
41   using ToolbarIcons =
42       std::map<ToolbarActionsModel::ActionId, ToolbarActionView*>;
43 
44   // Determines how the container displays - specifically whether the menu and
45   // popped out action can be hidden.
46   enum class DisplayMode {
47     // In normal mode, the menu icon and popped-out action is always visible.
48     // Normal mode is used for the main toolbar and in windows where there is
49     // always enough space to show at least two icons.
50     kNormal,
51     // In compact mode, one or both of the menu icon and popped-out action may
52     // be hidden. Compact mode is used in smaller windows (e.g. webapps) where
53     // there may not be enough space to display the buttons.
54     kCompact,
55   };
56 
57   explicit ExtensionsToolbarContainer(
58       Browser* browser,
59       DisplayMode display_mode = DisplayMode::kNormal);
60   ExtensionsToolbarContainer(const ExtensionsToolbarContainer&) = delete;
61   ExtensionsToolbarContainer& operator=(const ExtensionsToolbarContainer&) =
62       delete;
63   ~ExtensionsToolbarContainer() override;
64 
extensions_button()65   ExtensionsToolbarButton* extensions_button() const {
66     return extensions_button_;
67   }
icons_for_testing()68   const ToolbarIcons& icons_for_testing() const { return icons_; }
popup_owner_for_testing()69   ToolbarActionViewController* popup_owner_for_testing() {
70     return popup_owner_;
71   }
72 
73   // Get the view corresponding to the extension |id|, if any.
74   ToolbarActionView* GetViewForId(const std::string& id);
75 
76   // Pop out and show the extension corresponding to |extension_id|, then show
77   // the Widget when the icon is visible. If the icon is already visible the
78   // action will be posted immediately (not run synchronously).
79   void ShowWidgetForExtension(views::Widget* widget,
80                               const std::string& extension_id);
81 
82   // Gets the widget that anchors to the extension (or is about to anchor to the
83   // extension, pending pop-out).
84   views::Widget* GetAnchoredWidgetForExtensionForTesting(
85       const std::string& extension_id);
86 
87   base::Optional<extensions::ExtensionId>
GetExtensionWithOpenContextMenuForTesting()88   GetExtensionWithOpenContextMenuForTesting() {
89     return extension_with_open_context_menu_id_;
90   }
91 
92   // ToolbarIconContainerView:
93   void UpdateAllIcons() override;
94   bool GetDropFormats(int* formats,
95                       std::set<ui::ClipboardFormatType>* format_types) override;
96   bool AreDropTypesRequired() override;
97   bool CanDrop(const ui::OSExchangeData& data) override;
98   int OnDragUpdated(const ui::DropTargetEvent& event) override;
99   void OnDragExited() override;
100   int OnPerformDrop(const ui::DropTargetEvent& event) override;
101   const char* GetClassName() const override;
102 
103   // ExtensionsContainer:
104   ToolbarActionViewController* GetActionForId(
105       const std::string& action_id) override;
106   ToolbarActionViewController* GetPoppedOutAction() const override;
107   void OnContextMenuShown(ToolbarActionViewController* extension) override;
108   void OnContextMenuClosed(ToolbarActionViewController* extension) override;
109   bool IsActionVisibleOnToolbar(
110       const ToolbarActionViewController* action) const override;
111   extensions::ExtensionContextMenuModel::ButtonVisibility GetActionVisibility(
112       const ToolbarActionViewController* action) const override;
113   void UndoPopOut() override;
114   void SetPopupOwner(ToolbarActionViewController* popup_owner) override;
115   void HideActivePopup() override;
116   bool CloseOverflowMenuIfOpen() override;
117   void PopOutAction(ToolbarActionViewController* action,
118                     bool is_sticky,
119                     const base::Closure& closure) override;
120   bool ShowToolbarActionPopupForAPICall(const std::string& action_id) override;
121   void ShowToolbarActionBubble(
122       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
123   void ShowToolbarActionBubbleAsync(
124       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
125 
126   // ToolbarActionView::Delegate:
127   content::WebContents* GetCurrentWebContents() override;
128   bool ShownInsideMenu() const override;
129   bool CanShowIconInToolbar() const override;
130   void OnToolbarActionViewDragDone() override;
131   views::LabelButton* GetOverflowReferenceView() const override;
132   gfx::Size GetToolbarActionSize() override;
133   void WriteDragDataForView(View* sender,
134                             const gfx::Point& press_pt,
135                             ui::OSExchangeData* data) override;
136   int GetDragOperationsForView(View* sender, const gfx::Point& p) override;
137   bool CanStartDragForView(View* sender,
138                            const gfx::Point& press_pt,
139                            const gfx::Point& p) override;
140 
141  private:
142   // A struct representing the position and action being dragged.
143   struct DropInfo;
144 
145   // Pairing of widgets associated with this container and the extension they
146   // are associated with. This is used to keep track of icons that are popped
147   // out due to a widget showing (or being queued to show).
148   struct AnchoredWidget {
149     views::Widget* widget;
150     std::string extension_id;
151   };
152 
153   // Determines whether an action must be visible (i.e. cannot be hidden for any
154   // reason). Returns true if the action is popped out or has an attached
155   // bubble.
156   bool ShouldForceVisibility(const std::string& extension_id) const;
157 
158   // Updates the view's visibility state according to
159   // IsActionVisibleOnToolbar(). Note that IsActionVisibleOnToolbar() does not
160   // return View visibility but whether the action should be visible or not
161   // (according to pin and pop-out state).
162   void UpdateIconVisibility(const std::string& extension_id);
163 
164   // Set |widget|'s anchor (to the corresponding extension) and then show it.
165   // Posted from |ShowWidgetForExtension|.
166   void AnchorAndShowWidgetImmediately(views::Widget* widget);
167 
168   // Creates toolbar actions and icons corresponding to the model. This is only
169   // called in the constructor or when the model initializes and should not be
170   // called for subsequent changes to the model.
171   void CreateActions();
172 
173   // Creates an action and toolbar button for the corresponding ID.
174   void CreateActionForId(const ToolbarActionsModel::ActionId& action_id);
175 
176   // Sorts child views to display them in the correct order (pinned actions,
177   // popped out actions, extensions button).
178   void ReorderViews();
179 
180   // Utility function for going from width to icon counts.
181   size_t WidthToIconCount(int x_offset);
182 
183   gfx::ImageSkia GetExtensionIcon(ToolbarActionView* extension_view);
184 
185   // Sets a pinned extension button's image to be shown/hidden.
186   void SetExtensionIconVisibility(ToolbarActionsModel::ActionId id,
187                                   bool visible);
188 
189   // Calls SetVisible to make sure that the container is showing only when there
190   // are extensions available.
191   void UpdateContainerVisibility();
192 
193   // TabStripModelObserver:
194   void OnTabStripModelChanged(
195       TabStripModel* tab_strip_model,
196       const TabStripModelChange& change,
197       const TabStripSelectionChange& selection) override;
198 
199   // ToolbarActionsModel::Observer:
200   void OnToolbarActionAdded(const ToolbarActionsModel::ActionId& action_id,
201                             int index) override;
202   void OnToolbarActionRemoved(
203       const ToolbarActionsModel::ActionId& action_id) override;
204   void OnToolbarActionMoved(const ToolbarActionsModel::ActionId& action_id,
205                             int index) override;
206   void OnToolbarActionLoadFailed() override;
207   void OnToolbarActionUpdated(
208       const ToolbarActionsModel::ActionId& action_id) override;
209   void OnToolbarVisibleCountChanged() override;
210   void OnToolbarHighlightModeChanged(bool is_highlighting) override;
211   void OnToolbarModelInitialized() override;
212   void OnToolbarPinnedActionsChanged() override;
213 
214   // views::WidgetObserver:
215   void OnWidgetClosing(views::Widget* widget) override;
216   void OnWidgetDestroying(views::Widget* widget) override;
217 
218   Browser* const browser_;
219   ToolbarActionsModel* const model_;
220   ScopedObserver<ToolbarActionsModel, ToolbarActionsModel::Observer>
221       model_observer_;
222   ExtensionsToolbarButton* const extensions_button_;
223   DisplayMode display_mode_;
224 
225   // TODO(pbos): Create actions and icons only for pinned pinned / popped out
226   // actions (lazily). Currently code expects GetActionForId() to return
227   // actions for extensions that aren't visible.
228   // Actions for all extensions.
229   std::vector<std::unique_ptr<ToolbarActionViewController>> actions_;
230   // View for every action, does not imply pinned or currently shown.
231   ToolbarIcons icons_;
232   // Popped-out extension, if any.
233   ToolbarActionViewController* popped_out_action_ = nullptr;
234   // The action that triggered the current popup, if any.
235   ToolbarActionViewController* popup_owner_ = nullptr;
236   // Extension with an open context menu, if any.
237   base::Optional<extensions::ExtensionId> extension_with_open_context_menu_id_;
238 
239   // The widgets currently popped out and, for each, the extension it is
240   // associated with. See AnchoredWidget.
241   std::vector<AnchoredWidget> anchored_widgets_;
242 
243   // The DropInfo for the current drag-and-drop operation, or a null pointer if
244   // there is none.
245   std::unique_ptr<DropInfo> drop_info_;
246 
247   base::WeakPtrFactory<ExtensionsToolbarContainer> weak_ptr_factory_{this};
248 };
249 
250 #endif  // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_
251