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.
5 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
7 #include "base/logging.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/api/commands/command_service.h"
10 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
11 #include "chrome/browser/extensions/extension_action.h"
12 #include "chrome/browser/extensions/extension_view.h"
13 #include "chrome/browser/extensions/extension_view_host.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sessions/session_tab_helper.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/extensions/accelerator_priority.h"
18 #include "chrome/browser/ui/extensions/extension_action_platform_delegate.h"
19 #include "chrome/browser/ui/toolbar/toolbar_action_view_delegate.h"
20 #include "chrome/common/extensions/api/extension_action/action_info.h"
21 #include "extensions/browser/extension_host.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/manifest_constants.h"
25 #include "ui/gfx/image/image_skia.h"
26 #include "ui/gfx/image/image_skia_operations.h"
28 using extensions::ActionInfo
;
29 using extensions::CommandService
;
31 ExtensionActionViewController::ExtensionActionViewController(
32 const extensions::Extension
* extension
,
34 ExtensionAction
* extension_action
)
35 : extension_(extension
),
37 extension_action_(extension_action
),
39 view_delegate_(nullptr),
40 platform_delegate_(ExtensionActionPlatformDelegate::Create(this)),
41 icon_factory_(browser
->profile(), extension
, extension_action
, this),
42 icon_observer_(nullptr),
44 extensions::ExtensionRegistry::Get(browser_
->profile())),
45 popup_host_observer_(this) {
46 DCHECK(extension_action
);
47 DCHECK(extension_action
->action_type() == ActionInfo::TYPE_PAGE
||
48 extension_action
->action_type() == ActionInfo::TYPE_BROWSER
);
52 ExtensionActionViewController::~ExtensionActionViewController() {
53 DCHECK(!is_showing_popup());
56 const std::string
& ExtensionActionViewController::GetId() const {
57 return extension_
->id();
60 void ExtensionActionViewController::SetDelegate(
61 ToolbarActionViewDelegate
* delegate
) {
62 DCHECK((delegate
== nullptr) ^ (view_delegate_
== nullptr));
64 view_delegate_
= delegate
;
65 platform_delegate_
->OnDelegateSet();
67 if (is_showing_popup())
69 platform_delegate_
.reset();
70 view_delegate_
= nullptr;
74 gfx::Image
ExtensionActionViewController::GetIcon(
75 content::WebContents
* web_contents
) {
76 if (!ExtensionIsValid())
79 return icon_factory_
.GetIcon(SessionTabHelper::IdForTab(web_contents
));
82 gfx::ImageSkia
ExtensionActionViewController::GetIconWithBadge() {
83 if (!ExtensionIsValid())
84 return gfx::ImageSkia();
86 content::WebContents
* web_contents
= view_delegate_
->GetCurrentWebContents();
87 gfx::Size
spacing(0, 3);
88 gfx::ImageSkia icon
= *GetIcon(web_contents
).ToImageSkia();
89 if (!IsEnabled(web_contents
))
90 icon
= gfx::ImageSkiaOperations::CreateTransparentImage(icon
, .25);
91 return extension_action_
->GetIconWithBadge(
92 icon
, SessionTabHelper::IdForTab(web_contents
), spacing
);
95 base::string16
ExtensionActionViewController::GetActionName() const {
96 if (!ExtensionIsValid())
97 return base::string16();
99 return base::UTF8ToUTF16(extension_
->name());
102 base::string16
ExtensionActionViewController::GetAccessibleName(
103 content::WebContents
* web_contents
) const {
104 if (!ExtensionIsValid())
105 return base::string16();
108 extension_action()->GetTitle(SessionTabHelper::IdForTab(web_contents
));
109 return base::UTF8ToUTF16(title
.empty() ? extension()->name() : title
);
112 base::string16
ExtensionActionViewController::GetTooltip(
113 content::WebContents
* web_contents
) const {
114 return GetAccessibleName(web_contents
);
117 bool ExtensionActionViewController::IsEnabled(
118 content::WebContents
* web_contents
) const {
119 if (!ExtensionIsValid())
122 return extension_action_
->GetIsVisible(
123 SessionTabHelper::IdForTab(web_contents
)) ||
124 extensions::ExtensionActionAPI::Get(browser_
->profile())->
125 ExtensionWantsToRun(extension(), web_contents
);
128 bool ExtensionActionViewController::WantsToRun(
129 content::WebContents
* web_contents
) const {
130 return extensions::ExtensionActionAPI::Get(browser_
->profile())->
131 ExtensionWantsToRun(extension(), web_contents
);
134 bool ExtensionActionViewController::HasPopup(
135 content::WebContents
* web_contents
) const {
136 if (!ExtensionIsValid())
139 int tab_id
= SessionTabHelper::IdForTab(web_contents
);
140 return (tab_id
< 0) ? false : extension_action_
->HasPopup(tab_id
);
143 void ExtensionActionViewController::HidePopup() {
144 if (is_showing_popup()) {
145 popup_host_
->Close();
146 // We need to do these actions synchronously (instead of closing and then
147 // performing the rest of the cleanup in OnExtensionHostDestroyed()) because
148 // the extension host can close asynchronously, and we need to keep the view
149 // delegate up-to-date.
154 gfx::NativeView
ExtensionActionViewController::GetPopupNativeView() {
155 return popup_host_
? popup_host_
->view()->GetNativeView() : nullptr;
158 ui::MenuModel
* ExtensionActionViewController::GetContextMenu() {
159 if (!ExtensionIsValid() || !extension()->ShowConfigureContextMenus())
162 // Reconstruct the menu every time because the menu's contents are dynamic.
163 context_menu_model_
= make_scoped_refptr(new ExtensionContextMenuModel(
164 extension(), browser_
, this));
165 return context_menu_model_
.get();
168 bool ExtensionActionViewController::IsMenuRunning() const {
169 return platform_delegate_
->IsMenuRunning();
172 bool ExtensionActionViewController::CanDrag() const {
176 bool ExtensionActionViewController::ExecuteAction(bool by_user
) {
177 return ExecuteAction(SHOW_POPUP
, by_user
);
180 void ExtensionActionViewController::UpdateState() {
181 if (!ExtensionIsValid())
184 view_delegate_
->UpdateState();
187 bool ExtensionActionViewController::ExecuteAction(PopupShowAction show_action
,
188 bool grant_tab_permissions
) {
189 if (!ExtensionIsValid())
192 if (extensions::ExtensionActionAPI::Get(browser_
->profile())
193 ->ExecuteExtensionAction(
194 extension_
, browser_
, grant_tab_permissions
) ==
195 ExtensionAction::ACTION_SHOW_POPUP
) {
196 GURL popup_url
= extension_action_
->GetPopupUrl(
197 SessionTabHelper::IdForTab(view_delegate_
->GetCurrentWebContents()));
198 return static_cast<ExtensionActionViewController
*>(
199 view_delegate_
->GetPreferredPopupViewController())
200 ->ShowPopupWithUrl(show_action
, popup_url
, grant_tab_permissions
);
205 void ExtensionActionViewController::PaintExtra(
207 const gfx::Rect
& bounds
,
208 content::WebContents
* web_contents
) const {
209 if (!ExtensionIsValid())
212 int tab_id
= SessionTabHelper::IdForTab(web_contents
);
214 extension_action_
->PaintBadge(canvas
, bounds
, tab_id
);
217 void ExtensionActionViewController::RegisterCommand() {
218 if (!ExtensionIsValid())
221 platform_delegate_
->RegisterCommand();
224 void ExtensionActionViewController::InspectPopup() {
225 ExecuteAction(SHOW_POPUP_AND_INSPECT
, true);
228 void ExtensionActionViewController::OnIconUpdated() {
230 icon_observer_
->OnIconUpdated();
232 view_delegate_
->UpdateState();
235 void ExtensionActionViewController::OnExtensionHostDestroyed(
236 const extensions::ExtensionHost
* host
) {
240 bool ExtensionActionViewController::ExtensionIsValid() const {
241 return extension_registry_
->enabled_extensions().Contains(extension_
->id());
244 bool ExtensionActionViewController::GetExtensionCommand(
245 extensions::Command
* command
) {
247 if (!ExtensionIsValid())
250 CommandService
* command_service
= CommandService::Get(browser_
->profile());
251 if (extension_action_
->action_type() == ActionInfo::TYPE_PAGE
) {
252 return command_service
->GetPageActionCommand(
253 extension_
->id(), CommandService::ACTIVE
, command
, NULL
);
255 return command_service
->GetBrowserActionCommand(
256 extension_
->id(), CommandService::ACTIVE
, command
, NULL
);
259 bool ExtensionActionViewController::ShowPopupWithUrl(
260 PopupShowAction show_action
,
261 const GURL
& popup_url
,
262 bool grant_tab_permissions
) {
263 if (!ExtensionIsValid())
266 bool already_showing
= is_showing_popup();
268 // Always hide the current popup, even if it's not owned by this extension.
269 // Only one popup should be visible at a time.
270 platform_delegate_
->CloseActivePopup();
272 // If we were showing a popup already, then we treat the action to open the
273 // same one as a desire to close it (like clicking a menu button that was
278 popup_host_
= platform_delegate_
->ShowPopupWithUrl(
279 show_action
, popup_url
, grant_tab_permissions
);
281 popup_host_observer_
.Add(popup_host_
);
282 view_delegate_
->OnPopupShown(grant_tab_permissions
);
284 return is_showing_popup();
287 void ExtensionActionViewController::OnPopupClosed() {
288 popup_host_observer_
.Remove(popup_host_
);
289 popup_host_
= nullptr;
290 view_delegate_
->OnPopupClosed();