Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(789)

Side by Side Diff: chrome/browser/ui/views/extensions/extension_action_view_controller.cc

Issue 661493004: Add infrastructure for Chrome Actions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sky's I Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/extensions/extension_action_view_controller.h" 5 #include "chrome/browser/ui/views/extensions/extension_action_view_controller.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/api/commands/command_service.h" 9 #include "chrome/browser/extensions/api/commands/command_service.h"
9 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" 10 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
10 #include "chrome/browser/extensions/extension_action.h" 11 #include "chrome/browser/extensions/extension_action.h"
11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/sessions/session_tab_helper.h" 13 #include "chrome/browser/sessions/session_tab_helper.h"
13 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/extensions/accelerator_priority.h" 15 #include "chrome/browser/ui/extensions/accelerator_priority.h"
15 #include "chrome/browser/ui/views/extensions/extension_action_view_delegate.h" 16 #include "chrome/browser/ui/views/toolbar/toolbar_action_view_delegate.h"
17 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
16 #include "chrome/common/extensions/api/extension_action/action_info.h" 18 #include "chrome/common/extensions/api/extension_action/action_info.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_source.h"
21 #include "extensions/browser/notification_types.h"
17 #include "extensions/common/extension.h" 22 #include "extensions/common/extension.h"
23 #include "extensions/common/manifest_constants.h"
24 #include "ui/gfx/image/image_skia.h"
25 #include "ui/gfx/image/image_skia_operations.h"
18 #include "ui/views/controls/menu/menu_controller.h" 26 #include "ui/views/controls/menu/menu_controller.h"
19 #include "ui/views/controls/menu/menu_runner.h" 27 #include "ui/views/controls/menu/menu_runner.h"
20 #include "ui/views/view.h" 28 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h" 29 #include "ui/views/widget/widget.h"
22 30
23 using extensions::ActionInfo; 31 using extensions::ActionInfo;
24 using extensions::CommandService; 32 using extensions::CommandService;
25 33
26 namespace { 34 namespace {
27 35
28 // The ExtensionActionViewController which is currently showing its context 36 // The ExtensionActionViewController which is currently showing its context
29 // menu, if any. 37 // menu, if any.
30 // Since only one context menu can be shown (even across browser windows), it's 38 // Since only one context menu can be shown (even across browser windows), it's
31 // safe to have this be a global singleton. 39 // safe to have this be a global singleton.
32 ExtensionActionViewController* context_menu_owner = NULL; 40 ExtensionActionViewController* context_menu_owner = NULL;
33 41
34 } // namespace 42 } // namespace
35 43
36 ExtensionActionViewController::ExtensionActionViewController( 44 ExtensionActionViewController::ExtensionActionViewController(
37 const extensions::Extension* extension, 45 const extensions::Extension* extension,
38 Browser* browser, 46 Browser* browser,
39 ExtensionAction* extension_action, 47 ExtensionAction* extension_action)
40 ExtensionActionViewDelegate* delegate)
41 : extension_(extension), 48 : extension_(extension),
42 browser_(browser), 49 browser_(browser),
43 extension_action_(extension_action), 50 extension_action_(extension_action),
44 delegate_(delegate), 51 delegate_(NULL),
Finnur 2014/10/17 09:57:02 nit: nullptr, same on line 53 and 54.
Devlin 2014/10/17 16:33:33 Ah, right. Old habits and all. :)
45 icon_factory_(browser->profile(), extension, extension_action, this), 52 icon_factory_(browser->profile(), extension, extension_action, this),
53 icon_observer_(NULL),
46 popup_(NULL), 54 popup_(NULL),
47 weak_factory_(this) { 55 weak_factory_(this) {
48 DCHECK(extension_action); 56 DCHECK(extension_action);
49 DCHECK(extension_action->action_type() == ActionInfo::TYPE_PAGE || 57 DCHECK(extension_action->action_type() == ActionInfo::TYPE_PAGE ||
50 extension_action->action_type() == ActionInfo::TYPE_BROWSER); 58 extension_action->action_type() == ActionInfo::TYPE_BROWSER);
51 DCHECK(extension); 59 DCHECK(extension);
60
61 content::NotificationSource notification_source =
62 content::Source<Profile>(browser->profile()->GetOriginalProfile());
63 registrar_.Add(this,
64 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED,
65 notification_source);
66 registrar_.Add(this,
67 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
68 notification_source);
52 } 69 }
53 70
54 ExtensionActionViewController::~ExtensionActionViewController() { 71 ExtensionActionViewController::~ExtensionActionViewController() {
55 if (context_menu_owner == this) 72 if (context_menu_owner == this)
56 context_menu_owner = NULL; 73 context_menu_owner = NULL;
57 HidePopup(); 74 HidePopup();
58 UnregisterCommand(false); 75 UnregisterCommand(false);
59 } 76 }
60 77
61 void ExtensionActionViewController::InspectPopup() { 78 ToolbarActionViewController::Type ExtensionActionViewController::GetType()
62 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT, true); 79 const {
80 return TYPE_EXTENSION_ACTION;
63 } 81 }
64 82
65 void ExtensionActionViewController::ExecuteActionByUser() { 83 void ExtensionActionViewController::SetDelegate(
66 ExecuteAction(ExtensionPopup::SHOW, true); 84 ToolbarActionViewDelegate* delegate) {
85 delegate_ = delegate;
86 delegate_->GetAsView()->set_context_menu_controller(this);
67 } 87 }
68 88
69 bool ExtensionActionViewController::ExecuteAction( 89 gfx::Image ExtensionActionViewController::GetIcon(
70 ExtensionPopup::ShowAction show_action, bool grant_tab_permissions) { 90 content::WebContents* web_contents) {
71 if (extensions::ExtensionActionAPI::Get(browser_->profile())-> 91 return icon_factory_.GetIcon(SessionTabHelper::IdForTab(web_contents));
72 ExecuteExtensionAction(extension_, browser_, grant_tab_permissions) == 92 }
73 ExtensionAction::ACTION_SHOW_POPUP) { 93
74 GURL popup_url = extension_action_->GetPopupUrl(GetCurrentTabId()); 94 base::string16 ExtensionActionViewController::GetAccessibleName(
75 return delegate_->GetPreferredPopupViewController()->ShowPopupWithUrl( 95 content::WebContents* web_contents) const {
76 show_action, popup_url, grant_tab_permissions); 96 std::string title =
77 } 97 extension_action()->GetTitle(SessionTabHelper::IdForTab(web_contents));
78 return false; 98 return base::UTF8ToUTF16(title.empty() ? extension()->name() : title);
99 }
100
101 base::string16 ExtensionActionViewController::GetTooltip(
102 content::WebContents* web_contents) const {
103 return GetAccessibleName(web_contents);
104 }
105
106 bool ExtensionActionViewController::IsEnabled(
107 content::WebContents* web_contents) const {
108 return extension_action_->GetIsVisible(
109 SessionTabHelper::IdForTab(web_contents));
110 }
111
112 bool ExtensionActionViewController::HasPopup(
113 content::WebContents* web_contents) const {
114 int tab_id = SessionTabHelper::IdForTab(web_contents);
115 return (tab_id < 0) ? false : extension_action_->HasPopup(tab_id);
79 } 116 }
80 117
81 void ExtensionActionViewController::HidePopup() { 118 void ExtensionActionViewController::HidePopup() {
82 if (popup_) 119 if (popup_)
83 CleanupPopup(true); 120 CleanupPopup(true);
84 } 121 }
85 122
86 gfx::Image ExtensionActionViewController::GetIcon(int tab_id) { 123 bool ExtensionActionViewController::IsMenuRunning() const {
87 return icon_factory_.GetIcon(tab_id); 124 return menu_runner_.get() != NULL;
88 } 125 }
89 126
90 int ExtensionActionViewController::GetCurrentTabId() const { 127 void ExtensionActionViewController::ExecuteActionByUser() {
91 content::WebContents* web_contents = delegate_->GetCurrentWebContents(); 128 ExecuteAction(ExtensionPopup::SHOW, true);
92 return web_contents ? SessionTabHelper::IdForTab(web_contents) : -1; 129 }
130
131 void ExtensionActionViewController::PaintExtra(
132 gfx::Canvas* canvas,
133 const gfx::Rect& bounds,
134 content::WebContents* web_contents) const {
135 int tab_id = SessionTabHelper::IdForTab(web_contents);
136 if (tab_id >= 0)
137 extension_action_->PaintBadge(canvas, bounds, tab_id);
93 } 138 }
94 139
95 void ExtensionActionViewController::RegisterCommand() { 140 void ExtensionActionViewController::RegisterCommand() {
96 // If we've already registered, do nothing. 141 // If we've already registered, do nothing.
97 if (action_keybinding_.get()) 142 if (action_keybinding_.get())
98 return; 143 return;
99 144
100 extensions::Command extension_command; 145 extensions::Command extension_command;
101 views::FocusManager* focus_manager = 146 views::FocusManager* focus_manager =
102 delegate_->GetFocusManagerForAccelerator(); 147 delegate_->GetFocusManagerForAccelerator();
103 if (focus_manager && GetExtensionCommand(&extension_command)) { 148 if (focus_manager && GetExtensionCommand(&extension_command)) {
104 action_keybinding_.reset( 149 action_keybinding_.reset(
105 new ui::Accelerator(extension_command.accelerator())); 150 new ui::Accelerator(extension_command.accelerator()));
106 focus_manager->RegisterAccelerator( 151 focus_manager->RegisterAccelerator(
107 *action_keybinding_, 152 *action_keybinding_,
108 GetAcceleratorPriority(extension_command.accelerator(), extension_), 153 GetAcceleratorPriority(extension_command.accelerator(), extension_),
109 this); 154 this);
110 } 155 }
111 } 156 }
112 157
113 void ExtensionActionViewController::UnregisterCommand(bool only_if_removed) { 158 void ExtensionActionViewController::InspectPopup() {
114 views::FocusManager* focus_manager = 159 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT, true);
115 delegate_->GetFocusManagerForAccelerator(); 160 }
116 if (!focus_manager || !action_keybinding_.get())
117 return;
118 161
119 // If |only_if_removed| is true, it means that we only need to unregister 162 bool ExtensionActionViewController::ExecuteAction(
120 // ourselves as an accelerator if the command was removed. Otherwise, we need 163 ExtensionPopup::ShowAction show_action, bool grant_tab_permissions) {
121 // to unregister ourselves no matter what (likely because we are shutting 164 if (extensions::ExtensionActionAPI::Get(browser_->profile())->
122 // down). 165 ExecuteExtensionAction(extension_, browser_, grant_tab_permissions) ==
123 extensions::Command extension_command; 166 ExtensionAction::ACTION_SHOW_POPUP) {
124 if (!only_if_removed || !GetExtensionCommand(&extension_command)) { 167 GURL popup_url = extension_action_->GetPopupUrl(
125 focus_manager->UnregisterAccelerator(*action_keybinding_, this); 168 SessionTabHelper::IdForTab(delegate_->GetCurrentWebContents()));
126 action_keybinding_.reset(); 169 return static_cast<ExtensionActionViewController*>(
170 delegate_->GetPreferredPopupViewController())->ShowPopupWithUrl(
171 show_action, popup_url, grant_tab_permissions);
127 } 172 }
173 return false;
174 }
175
176 gfx::ImageSkia ExtensionActionViewController::GetIconWithBadge() {
177 content::WebContents* web_contents = delegate_->GetCurrentWebContents();
178 gfx::Size spacing(0, ToolbarView::kVertSpacing);
179 gfx::ImageSkia icon = *GetIcon(web_contents).ToImageSkia();
180 if (!IsEnabled(web_contents))
181 icon = gfx::ImageSkiaOperations::CreateTransparentImage(icon, .25);
182 return extension_action_->GetIconWithBadge(
183 icon, SessionTabHelper::IdForTab(web_contents), spacing);
128 } 184 }
129 185
130 void ExtensionActionViewController::OnIconUpdated() { 186 void ExtensionActionViewController::OnIconUpdated() {
131 delegate_->OnIconUpdated(); 187 delegate_->OnIconUpdated();
188 if (icon_observer_)
189 icon_observer_->OnIconUpdated();
132 } 190 }
133 191
134 bool ExtensionActionViewController::AcceleratorPressed( 192 bool ExtensionActionViewController::AcceleratorPressed(
135 const ui::Accelerator& accelerator) { 193 const ui::Accelerator& accelerator) {
136 // We shouldn't be handling any accelerators if the view is hidden, unless 194 // We shouldn't be handling any accelerators if the view is hidden, unless
137 // this is a browser action. 195 // this is a browser action.
138 DCHECK(extension_action_->action_type() == ActionInfo::TYPE_BROWSER || 196 DCHECK(extension_action_->action_type() == ActionInfo::TYPE_BROWSER ||
139 delegate_->GetAsView()->visible()); 197 delegate_->GetAsView()->visible());
140 198
141 // Normal priority shortcuts must be handled via standard browser commands to 199 // Normal priority shortcuts must be handled via standard browser commands to
(...skipping 17 matching lines...) Expand all
159 void ExtensionActionViewController::OnWidgetDestroying(views::Widget* widget) { 217 void ExtensionActionViewController::OnWidgetDestroying(views::Widget* widget) {
160 DCHECK(popup_); 218 DCHECK(popup_);
161 DCHECK_EQ(popup_->GetWidget(), widget); 219 DCHECK_EQ(popup_->GetWidget(), widget);
162 CleanupPopup(false); 220 CleanupPopup(false);
163 } 221 }
164 222
165 void ExtensionActionViewController::ShowContextMenuForView( 223 void ExtensionActionViewController::ShowContextMenuForView(
166 views::View* source, 224 views::View* source,
167 const gfx::Point& point, 225 const gfx::Point& point,
168 ui::MenuSourceType source_type) { 226 ui::MenuSourceType source_type) {
169
170 // If there's another active menu that won't be dismissed by opening this one, 227 // If there's another active menu that won't be dismissed by opening this one,
171 // then we can't show this one right away, since we can only show one nested 228 // then we can't show this one right away, since we can only show one nested
172 // menu at a time. 229 // menu at a time.
173 // If the other menu is an extension action's context menu, then we'll run 230 // If the other menu is an extension action's context menu, then we'll run
174 // this one after that one closes. If it's a different type of menu, then we 231 // this one after that one closes. If it's a different type of menu, then we
175 // close it and give up, for want of a better solution. (Luckily, this is 232 // close it and give up, for want of a better solution. (Luckily, this is
176 // rare). 233 // rare).
177 // TODO(devlin): Update this when views code no longer runs menus in a nested 234 // TODO(devlin): Update this when views code no longer runs menus in a nested
178 // loop. 235 // loop.
179 if (context_menu_owner) { 236 if (context_menu_owner) {
180 context_menu_owner->followup_context_menu_task_ = 237 context_menu_owner->followup_context_menu_task_ =
181 base::Bind(&ExtensionActionViewController::DoShowContextMenu, 238 base::Bind(&ExtensionActionViewController::DoShowContextMenu,
182 weak_factory_.GetWeakPtr(), 239 weak_factory_.GetWeakPtr(),
183 source_type); 240 source_type);
184 } 241 }
185 if (CloseActiveMenuIfNeeded()) 242 if (CloseActiveMenuIfNeeded())
186 return; 243 return;
187 244
188 // Otherwise, no other menu is showing, and we can proceed normally. 245 // Otherwise, no other menu is showing, and we can proceed normally.
189 DoShowContextMenu(source_type); 246 DoShowContextMenu(source_type);
190 } 247 }
191 248
249 void ExtensionActionViewController::Observe(
250 int type,
251 const content::NotificationSource& source,
252 const content::NotificationDetails& details) {
253 DCHECK(type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED ||
254 type == extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED);
255 std::pair<const std::string, const std::string>* payload =
256 content::Details<std::pair<const std::string, const std::string> >(
257 details).ptr();
258 if (extension_->id() == payload->first &&
259 payload->second ==
260 extensions::manifest_values::kBrowserActionCommandEvent) {
261 if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED)
262 RegisterCommand();
263 else
264 UnregisterCommand(true);
265 }
266 }
267
192 void ExtensionActionViewController::DoShowContextMenu( 268 void ExtensionActionViewController::DoShowContextMenu(
193 ui::MenuSourceType source_type) { 269 ui::MenuSourceType source_type) {
194 if (!extension_->ShowConfigureContextMenus()) 270 if (!extension_->ShowConfigureContextMenus())
195 return; 271 return;
196 272
197 DCHECK(!context_menu_owner); 273 DCHECK(!context_menu_owner);
198 context_menu_owner = this; 274 context_menu_owner = this;
199 275
200 // We shouldn't have both a popup and a context menu showing. 276 // We shouldn't have both a popup and a context menu showing.
201 delegate_->HideActivePopup(); 277 delegate_->HideActivePopup();
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 DCHECK(command); 351 DCHECK(command);
276 CommandService* command_service = CommandService::Get(browser_->profile()); 352 CommandService* command_service = CommandService::Get(browser_->profile());
277 if (extension_action_->action_type() == ActionInfo::TYPE_PAGE) { 353 if (extension_action_->action_type() == ActionInfo::TYPE_PAGE) {
278 return command_service->GetPageActionCommand( 354 return command_service->GetPageActionCommand(
279 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); 355 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL);
280 } 356 }
281 return command_service->GetBrowserActionCommand( 357 return command_service->GetBrowserActionCommand(
282 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); 358 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL);
283 } 359 }
284 360
361 void ExtensionActionViewController::UnregisterCommand(bool only_if_removed) {
362 views::FocusManager* focus_manager =
363 delegate_->GetFocusManagerForAccelerator();
364 if (!focus_manager || !action_keybinding_.get())
365 return;
366
367 // If |only_if_removed| is true, it means that we only need to unregister
368 // ourselves as an accelerator if the command was removed. Otherwise, we need
369 // to unregister ourselves no matter what (likely because we are shutting
370 // down).
371 extensions::Command extension_command;
372 if (!only_if_removed || !GetExtensionCommand(&extension_command)) {
373 focus_manager->UnregisterAccelerator(*action_keybinding_, this);
374 action_keybinding_.reset();
375 }
376 }
377
285 bool ExtensionActionViewController::CloseActiveMenuIfNeeded() { 378 bool ExtensionActionViewController::CloseActiveMenuIfNeeded() {
286 // If this view is shown inside another menu, there's a possibility that there 379 // If this view is shown inside another menu, there's a possibility that there
287 // is another context menu showing that we have to close before we can 380 // is another context menu showing that we have to close before we can
288 // activate a different menu. 381 // activate a different menu.
289 if (delegate_->IsShownInMenu()) { 382 if (delegate_->IsShownInMenu()) {
290 views::MenuController* menu_controller = 383 views::MenuController* menu_controller =
291 views::MenuController::GetActiveInstance(); 384 views::MenuController::GetActiveInstance();
292 // If this is shown inside a menu, then there should always be an active 385 // If this is shown inside a menu, then there should always be an active
293 // menu controller. 386 // menu controller.
294 DCHECK(menu_controller); 387 DCHECK(menu_controller);
295 if (menu_controller->in_nested_run()) { 388 if (menu_controller->in_nested_run()) {
296 // There is another menu showing. Close the outermost menu (since we are 389 // There is another menu showing. Close the outermost menu (since we are
297 // shown in the same menu, we don't want to close the whole thing). 390 // shown in the same menu, we don't want to close the whole thing).
298 menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST); 391 menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST);
299 return true; 392 return true;
300 } 393 }
301 } 394 }
302 395
303 return false; 396 return false;
304 } 397 }
305 398
306 void ExtensionActionViewController::CleanupPopup(bool close_widget) { 399 void ExtensionActionViewController::CleanupPopup(bool close_widget) {
307 DCHECK(popup_); 400 DCHECK(popup_);
308 delegate_->CleanupPopup(); 401 delegate_->CleanupPopup();
309 popup_->GetWidget()->RemoveObserver(this); 402 popup_->GetWidget()->RemoveObserver(this);
310 if (close_widget) 403 if (close_widget)
311 popup_->GetWidget()->Close(); 404 popup_->GetWidget()->Close();
312 popup_ = NULL; 405 popup_ = NULL;
313 } 406 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698