1 // Copyright 2014 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_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_
6 #define CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_
7 
8 #include "base/macros.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/scoped_observer.h"
11 #include "chrome/browser/extensions/extension_action_icon_factory.h"
12 #include "chrome/browser/extensions/extension_context_menu_model.h"
13 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
14 #include "extensions/browser/extension_host.h"
15 #include "extensions/browser/extension_host_observer.h"
16 #include "ui/gfx/image/image.h"
17 
18 class Browser;
19 class ExtensionActionPlatformDelegate;
20 class GURL;
21 class IconWithBadgeImageSource;
22 class ExtensionsContainer;
23 
24 namespace extensions {
25 class Command;
26 class Extension;
27 class ExtensionAction;
28 class ExtensionRegistry;
29 class ExtensionViewHost;
30 }
31 
32 // The platform-independent controller for an ExtensionAction that is shown on
33 // the toolbar (such as a page or browser action).
34 // Since this class doesn't own the extension or extension action in question,
35 // be sure to check for validity using ExtensionIsValid() before using those
36 // members (see also comments above ExtensionIsValid()).
37 class ExtensionActionViewController
38     : public ToolbarActionViewController,
39       public ExtensionActionIconFactory::Observer,
40       public extensions::ExtensionContextMenuModel::PopupDelegate,
41       public extensions::ExtensionHostObserver {
42  public:
43   // The different options for showing a popup.
44   enum PopupShowAction { SHOW_POPUP, SHOW_POPUP_AND_INSPECT };
45 
46   ExtensionActionViewController(const extensions::Extension* extension,
47                                 Browser* browser,
48                                 extensions::ExtensionAction* extension_action,
49                                 ExtensionsContainer* extensions_container,
50                                 bool in_overflow_mode);
51   ~ExtensionActionViewController() override;
52 
53   // ToolbarActionViewController:
54   std::string GetId() const override;
55   void SetDelegate(ToolbarActionViewDelegate* delegate) override;
56   gfx::Image GetIcon(content::WebContents* web_contents,
57                      const gfx::Size& size) override;
58   base::string16 GetActionName() const override;
59   base::string16 GetAccessibleName(content::WebContents* web_contents) const
60       override;
61   base::string16 GetTooltip(content::WebContents* web_contents) const override;
62   PageInteractionStatus GetPageInteractionStatus(
63       content::WebContents* web_contents) const override;
64   bool IsEnabled(content::WebContents* web_contents) const override;
65   bool HasPopup(content::WebContents* web_contents) const override;
66   bool IsShowingPopup() const override;
67   void HidePopup() override;
68   gfx::NativeView GetPopupNativeView() override;
69   ui::MenuModel* GetContextMenu() override;
70   void OnContextMenuShown() override;
71   void OnContextMenuClosed() override;
72   bool ExecuteAction(bool by_user, InvocationSource source) override;
73   void UpdateState() override;
74   void RegisterCommand() override;
75   void UnregisterCommand() override;
76   bool DisabledClickOpensMenu() const override;
77 
78   // ExtensionContextMenuModel::PopupDelegate:
79   void InspectPopup() override;
80 
81   // Populates |command| with the command associated with |extension|, if one
82   // exists. Returns true if |command| was populated.
83   bool GetExtensionCommand(extensions::Command* command) const;
84 
85   // Returns true if this controller can handle accelerators (i.e., keyboard
86   // commands) on the currently-active WebContents.
87   // This must only be called if the extension has an associated command.
88   // TODO(devlin): Move accelerator logic out of the platform delegate and into
89   // this class.
90   bool CanHandleAccelerators() const;
91 
extension()92   const extensions::Extension* extension() const { return extension_.get(); }
browser()93   Browser* browser() { return browser_; }
extension_action()94   extensions::ExtensionAction* extension_action() { return extension_action_; }
extension_action()95   const extensions::ExtensionAction* extension_action() const {
96     return extension_action_;
97   }
view_delegate()98   ToolbarActionViewDelegate* view_delegate() { return view_delegate_; }
99 
100   std::unique_ptr<IconWithBadgeImageSource> GetIconImageSourceForTesting(
101       content::WebContents* web_contents,
102       const gfx::Size& size);
103   bool HasBeenBlockedForTesting(content::WebContents* web_contents) const;
104 
105  private:
106   // ExtensionActionIconFactory::Observer:
107   void OnIconUpdated() override;
108 
109   // ExtensionHostObserver:
110   void OnExtensionHostDestroyed(extensions::ExtensionHost* host) override;
111 
112   // Checks if the associated |extension| is still valid by checking its
113   // status in the registry. Since the OnExtensionUnloaded() notifications are
114   // not in a deterministic order, it's possible that the view tries to refresh
115   // itself before we're notified to remove it.
116   bool ExtensionIsValid() const;
117 
118   // In some cases (such as when an action is shown in a menu), a substitute
119   // ToolbarActionViewController should be used for showing popups. This
120   // returns the preferred controller.
121   ExtensionActionViewController* GetPreferredPopupViewController();
122 
123   // Executes the extension action with |show_action|. If
124   // |grant_tab_permissions| is true, this will grant the extension active tab
125   // permissions. Only do this if this was done through a user action (and not
126   // e.g. an API). Returns true if a popup is shown.
127   bool ExecuteAction(PopupShowAction show_action, bool grant_tab_permissions);
128 
129   // Begins the process of showing the popup for the extension action, given the
130   // associated |popup_url|. |grant_tab_permissions| is true if active tab
131   // permissions should be given to the extension; this is only true if the
132   // popup is opened through a user action.
133   // The popup may not be shown synchronously if the extension is hidden and
134   // first needs to slide itself out.
135   // Returns true if a popup will be shown.
136   bool TriggerPopupWithUrl(PopupShowAction show_action,
137                            const GURL& popup_url,
138                            bool grant_tab_permissions);
139 
140   // Shows the popup with the given |host|.
141   void ShowPopup(std::unique_ptr<extensions::ExtensionViewHost> host,
142                  bool grant_tab_permissions,
143                  PopupShowAction show_action);
144 
145   // Handles cleanup after the popup closes.
146   void OnPopupClosed();
147 
148   // Returns the image source for the icon.
149   std::unique_ptr<IconWithBadgeImageSource> GetIconImageSource(
150       content::WebContents* web_contents,
151       const gfx::Size& size);
152 
153   // Returns true if this extension has a page action and that page action wants
154   // to run on the given |web_contents|.
155   bool PageActionWantsToRun(content::WebContents* web_contents) const;
156 
157   // Returns true if this extension uses the activeTab permission and would
158   // probably be able to to access the given |url|. The actual checks when an
159   // activeTab extension tries to run are a little more complicated and can be
160   // seen in ExtensionActionRunner and ActiveTabPermissionGranter.
161   // Note: The rare cases where this gets it wrong should only be for false
162   // positives, where it reports that the extension wants access but it can't
163   // actually be given access when it tries to run.
164   bool HasActiveTabAndCanAccess(const GURL& url) const;
165 
166   // Returns true if this extension has been blocked on the given
167   // |web_contents|.
168   bool HasBeenBlocked(content::WebContents* web_contents) const;
169 
170   // The extension associated with the action we're displaying.
171   scoped_refptr<const extensions::Extension> extension_;
172 
173   // The corresponding browser.
174   Browser* const browser_;
175 
176   // Whether we are displayed in the 3-dot menu or not.
177   // TODO(pbos): Remove when 3-dot menu no longer contains extensions.
178   const bool in_overflow_mode_;
179 
180   // The browser action this view represents. The ExtensionAction is not owned
181   // by this class.
182   extensions::ExtensionAction* const extension_action_;
183 
184   // The corresponding ExtensionsContainer on the toolbar.
185   ExtensionsContainer* const extensions_container_;
186 
187   // The extension popup's host if the popup is visible; null otherwise.
188   extensions::ExtensionViewHost* popup_host_;
189 
190   // The context menu model for the extension.
191   std::unique_ptr<extensions::ExtensionContextMenuModel> context_menu_model_;
192 
193   // Our view delegate.
194   ToolbarActionViewDelegate* view_delegate_;
195 
196   // The delegate to handle platform-specific implementations.
197   std::unique_ptr<ExtensionActionPlatformDelegate> platform_delegate_;
198 
199   // The object that will be used to get the browser action icon for us.
200   // It may load the icon asynchronously (in which case the initial icon
201   // returned by the factory will be transparent), so we have to observe it for
202   // updates to the icon.
203   ExtensionActionIconFactory icon_factory_;
204 
205   // The associated ExtensionRegistry; cached for quick checking.
206   extensions::ExtensionRegistry* extension_registry_;
207 
208   ScopedObserver<extensions::ExtensionHost, extensions::ExtensionHostObserver>
209       popup_host_observer_{this};
210 
211   base::WeakPtrFactory<ExtensionActionViewController> weak_factory_{this};
212 
213   DISALLOW_COPY_AND_ASSIGN(ExtensionActionViewController);
214 };
215 
216 #endif  // CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_
217