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

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: 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/ui/browser.h" 13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/extensions/accelerator_priority.h" 14 #include "chrome/browser/ui/extensions/accelerator_priority.h"
15 #include "chrome/browser/ui/views/extensions/extension_action_view_delegate.h" 15 #include "chrome/browser/ui/views/toolbar/toolbar_action_view_delegate.h"
16 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
16 #include "chrome/common/extensions/api/extension_action/action_info.h" 17 #include "chrome/common/extensions/api/extension_action/action_info.h"
18 #include "content/public/browser/notification_details.h"
19 #include "content/public/browser/notification_source.h"
20 #include "extensions/browser/notification_types.h"
17 #include "extensions/common/extension.h" 21 #include "extensions/common/extension.h"
22 #include "extensions/common/manifest_constants.h"
23 #include "ui/gfx/image/image_skia.h"
24 #include "ui/gfx/image/image_skia_operations.h"
18 #include "ui/views/controls/menu/menu_controller.h" 25 #include "ui/views/controls/menu/menu_controller.h"
19 #include "ui/views/controls/menu/menu_runner.h" 26 #include "ui/views/controls/menu/menu_runner.h"
20 #include "ui/views/view.h" 27 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h" 28 #include "ui/views/widget/widget.h"
22 29
23 using extensions::ActionInfo; 30 using extensions::ActionInfo;
24 using extensions::CommandService; 31 using extensions::CommandService;
25 32
26 namespace { 33 namespace {
27 34
28 // The ExtensionActionViewController which is currently showing its context 35 // The ExtensionActionViewController which is currently showing its context
29 // menu, if any. 36 // menu, if any.
30 // Since only one context menu can be shown (even across browser windows), it's 37 // Since only one context menu can be shown (even across browser windows), it's
31 // safe to have this be a global singleton. 38 // safe to have this be a global singleton.
32 ExtensionActionViewController* context_menu_owner = NULL; 39 ExtensionActionViewController* context_menu_owner = NULL;
33 40
34 } // namespace 41 } // namespace
35 42
36 ExtensionActionViewController::ExtensionActionViewController( 43 ExtensionActionViewController::ExtensionActionViewController(
37 const extensions::Extension* extension, 44 const extensions::Extension* extension,
38 Browser* browser, 45 Browser* browser,
39 ExtensionAction* extension_action, 46 ExtensionAction* extension_action,
40 ExtensionActionViewDelegate* delegate) 47 ToolbarActionViewDelegate* 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_(delegate),
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 void ExtensionActionViewController::SetDelegate(
62 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT, true); 79 ToolbarActionViewDelegate* delegate) {
80 delegate_ = delegate;
63 } 81 }
64 82
65 void ExtensionActionViewController::ExecuteActionByUser() { 83 gfx::Image ExtensionActionViewController::GetIcon(int tab_id) {
66 ExecuteAction(ExtensionPopup::SHOW, true); 84 return icon_factory_.GetIcon(tab_id);
67 } 85 }
68 86
69 bool ExtensionActionViewController::ExecuteAction( 87 base::string16 ExtensionActionViewController::GetAccessibleName(int tab_id)
70 ExtensionPopup::ShowAction show_action, bool grant_tab_permissions) { 88 const {
71 if (extensions::ExtensionActionAPI::Get(browser_->profile())-> 89 std::string title = extension_action()->GetTitle(tab_id);
72 ExecuteExtensionAction(extension_, browser_, grant_tab_permissions) == 90 return base::UTF8ToUTF16(title.empty() ? extension()->name() : title);
73 ExtensionAction::ACTION_SHOW_POPUP) { 91 }
74 GURL popup_url = extension_action_->GetPopupUrl(GetCurrentTabId()); 92
75 return delegate_->GetPreferredPopupViewController()->ShowPopupWithUrl( 93 base::string16 ExtensionActionViewController::GetTooltip(int tab_id) const {
76 show_action, popup_url, grant_tab_permissions); 94 return GetAccessibleName(tab_id);
77 } 95 }
78 return false; 96
97 bool ExtensionActionViewController::IsEnabled(int tab_id) const {
98 return extension_action_->GetIsVisible(tab_id);
99 }
100
101 bool ExtensionActionViewController::HasPopup(int tab_id) const {
102 return (tab_id < 0) ? false : extension_action_->HasPopup(tab_id);
79 } 103 }
80 104
81 void ExtensionActionViewController::HidePopup() { 105 void ExtensionActionViewController::HidePopup() {
82 if (popup_) 106 if (popup_)
83 CleanupPopup(true); 107 CleanupPopup(true);
84 } 108 }
85 109
86 gfx::Image ExtensionActionViewController::GetIcon(int tab_id) { 110 bool ExtensionActionViewController::IsMenuRunning() const {
87 return icon_factory_.GetIcon(tab_id); 111 return menu_runner_.get() != NULL;
88 } 112 }
89 113
90 int ExtensionActionViewController::GetCurrentTabId() const { 114 void ExtensionActionViewController::ExecuteActionByUser() {
91 content::WebContents* web_contents = delegate_->GetCurrentWebContents(); 115 ExecuteAction(ExtensionPopup::SHOW, true);
92 return web_contents ? SessionTabHelper::IdForTab(web_contents) : -1; 116 }
117
118 void ExtensionActionViewController::PaintExtra(gfx::Canvas* canvas,
119 const gfx::Rect& bounds,
120 int tab_id) const {
121 if (tab_id >= 0)
122 extension_action_->PaintBadge(canvas, bounds, tab_id);
93 } 123 }
94 124
95 void ExtensionActionViewController::RegisterCommand() { 125 void ExtensionActionViewController::RegisterCommand() {
96 // If we've already registered, do nothing. 126 // If we've already registered, do nothing.
97 if (action_keybinding_.get()) 127 if (action_keybinding_.get())
98 return; 128 return;
99 129
100 extensions::Command extension_command; 130 extensions::Command extension_command;
101 views::FocusManager* focus_manager = 131 views::FocusManager* focus_manager =
102 delegate_->GetFocusManagerForAccelerator(); 132 delegate_->GetFocusManagerForAccelerator();
103 if (focus_manager && GetExtensionCommand(&extension_command)) { 133 if (focus_manager && GetExtensionCommand(&extension_command)) {
104 action_keybinding_.reset( 134 action_keybinding_.reset(
105 new ui::Accelerator(extension_command.accelerator())); 135 new ui::Accelerator(extension_command.accelerator()));
106 focus_manager->RegisterAccelerator( 136 focus_manager->RegisterAccelerator(
107 *action_keybinding_, 137 *action_keybinding_,
108 GetAcceleratorPriority(extension_command.accelerator(), extension_), 138 GetAcceleratorPriority(extension_command.accelerator(), extension_),
109 this); 139 this);
110 } 140 }
111 } 141 }
112 142
113 void ExtensionActionViewController::UnregisterCommand(bool only_if_removed) { 143 void ExtensionActionViewController::InspectPopup() {
114 views::FocusManager* focus_manager = 144 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT, true);
115 delegate_->GetFocusManagerForAccelerator(); 145 }
116 if (!focus_manager || !action_keybinding_.get())
117 return;
118 146
119 // If |only_if_removed| is true, it means that we only need to unregister 147 bool ExtensionActionViewController::ExecuteAction(
120 // ourselves as an accelerator if the command was removed. Otherwise, we need 148 ExtensionPopup::ShowAction show_action, bool grant_tab_permissions) {
121 // to unregister ourselves no matter what (likely because we are shutting 149 if (extensions::ExtensionActionAPI::Get(browser_->profile())->
122 // down). 150 ExecuteExtensionAction(extension_, browser_, grant_tab_permissions) ==
123 extensions::Command extension_command; 151 ExtensionAction::ACTION_SHOW_POPUP) {
124 if (!only_if_removed || !GetExtensionCommand(&extension_command)) { 152 GURL popup_url =
125 focus_manager->UnregisterAccelerator(*action_keybinding_, this); 153 extension_action_->GetPopupUrl(delegate_->GetCurrentTabId());
126 action_keybinding_.reset(); 154 return static_cast<ExtensionActionViewController*>(
155 delegate_->GetPreferredPopupViewController())->ShowPopupWithUrl(
156 show_action, popup_url, grant_tab_permissions);
127 } 157 }
158 return false;
159 }
160
161 gfx::ImageSkia ExtensionActionViewController::GetIconWithBadge() {
162 int tab_id = delegate_->GetCurrentTabId();
163 gfx::Size spacing(0, ToolbarView::kVertSpacing);
164 gfx::ImageSkia icon = *GetIcon(tab_id).ToImageSkia();
165 if (!IsEnabled(tab_id))
166 icon = gfx::ImageSkiaOperations::CreateTransparentImage(icon, .25);
167 return extension_action_->GetIconWithBadge(icon, tab_id, spacing);
128 } 168 }
129 169
130 void ExtensionActionViewController::OnIconUpdated() { 170 void ExtensionActionViewController::OnIconUpdated() {
131 delegate_->OnIconUpdated(); 171 delegate_->OnIconUpdated();
172 if (icon_observer_)
173 icon_observer_->OnIconUpdated();
132 } 174 }
133 175
134 bool ExtensionActionViewController::AcceleratorPressed( 176 bool ExtensionActionViewController::AcceleratorPressed(
135 const ui::Accelerator& accelerator) { 177 const ui::Accelerator& accelerator) {
136 // We shouldn't be handling any accelerators if the view is hidden, unless 178 // We shouldn't be handling any accelerators if the view is hidden, unless
137 // this is a browser action. 179 // this is a browser action.
138 DCHECK(extension_action_->action_type() == ActionInfo::TYPE_BROWSER || 180 DCHECK(extension_action_->action_type() == ActionInfo::TYPE_BROWSER ||
139 delegate_->GetAsView()->visible()); 181 delegate_->GetAsView()->visible());
140 182
141 // Normal priority shortcuts must be handled via standard browser commands to 183 // Normal priority shortcuts must be handled via standard browser commands to
(...skipping 17 matching lines...) Expand all
159 void ExtensionActionViewController::OnWidgetDestroying(views::Widget* widget) { 201 void ExtensionActionViewController::OnWidgetDestroying(views::Widget* widget) {
160 DCHECK(popup_); 202 DCHECK(popup_);
161 DCHECK_EQ(popup_->GetWidget(), widget); 203 DCHECK_EQ(popup_->GetWidget(), widget);
162 CleanupPopup(false); 204 CleanupPopup(false);
163 } 205 }
164 206
165 void ExtensionActionViewController::ShowContextMenuForView( 207 void ExtensionActionViewController::ShowContextMenuForView(
166 views::View* source, 208 views::View* source,
167 const gfx::Point& point, 209 const gfx::Point& point,
168 ui::MenuSourceType source_type) { 210 ui::MenuSourceType source_type) {
169
170 // If there's another active menu that won't be dismissed by opening this one, 211 // 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 212 // then we can't show this one right away, since we can only show one nested
172 // menu at a time. 213 // menu at a time.
173 // If the other menu is an extension action's context menu, then we'll run 214 // 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 215 // 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 216 // close it and give up, for want of a better solution. (Luckily, this is
176 // rare). 217 // rare).
177 // TODO(devlin): Update this when views code no longer runs menus in a nested 218 // TODO(devlin): Update this when views code no longer runs menus in a nested
178 // loop. 219 // loop.
179 if (context_menu_owner) { 220 if (context_menu_owner) {
180 context_menu_owner->followup_context_menu_task_ = 221 context_menu_owner->followup_context_menu_task_ =
181 base::Bind(&ExtensionActionViewController::DoShowContextMenu, 222 base::Bind(&ExtensionActionViewController::DoShowContextMenu,
182 weak_factory_.GetWeakPtr(), 223 weak_factory_.GetWeakPtr(),
183 source_type); 224 source_type);
184 } 225 }
185 if (CloseActiveMenuIfNeeded()) 226 if (CloseActiveMenuIfNeeded())
186 return; 227 return;
187 228
188 // Otherwise, no other menu is showing, and we can proceed normally. 229 // Otherwise, no other menu is showing, and we can proceed normally.
189 DoShowContextMenu(source_type); 230 DoShowContextMenu(source_type);
190 } 231 }
191 232
233 void ExtensionActionViewController::Observe(
234 int type,
235 const content::NotificationSource& source,
236 const content::NotificationDetails& details) {
237 DCHECK(type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED ||
238 type == extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED);
239 std::pair<const std::string, const std::string>* payload =
240 content::Details<std::pair<const std::string, const std::string> >(
241 details).ptr();
242 if (extension_->id() == payload->first &&
243 payload->second ==
244 extensions::manifest_values::kBrowserActionCommandEvent) {
245 if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED)
246 RegisterCommand();
247 else
248 UnregisterCommand(true);
249 }
250 }
251
192 void ExtensionActionViewController::DoShowContextMenu( 252 void ExtensionActionViewController::DoShowContextMenu(
193 ui::MenuSourceType source_type) { 253 ui::MenuSourceType source_type) {
194 if (!extension_->ShowConfigureContextMenus()) 254 if (!extension_->ShowConfigureContextMenus())
195 return; 255 return;
196 256
197 DCHECK(!context_menu_owner); 257 DCHECK(!context_menu_owner);
198 context_menu_owner = this; 258 context_menu_owner = this;
199 259
200 // We shouldn't have both a popup and a context menu showing. 260 // We shouldn't have both a popup and a context menu showing.
201 delegate_->HideActivePopup(); 261 delegate_->HideActivePopup();
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 DCHECK(command); 335 DCHECK(command);
276 CommandService* command_service = CommandService::Get(browser_->profile()); 336 CommandService* command_service = CommandService::Get(browser_->profile());
277 if (extension_action_->action_type() == ActionInfo::TYPE_PAGE) { 337 if (extension_action_->action_type() == ActionInfo::TYPE_PAGE) {
278 return command_service->GetPageActionCommand( 338 return command_service->GetPageActionCommand(
279 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); 339 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL);
280 } 340 }
281 return command_service->GetBrowserActionCommand( 341 return command_service->GetBrowserActionCommand(
282 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); 342 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL);
283 } 343 }
284 344
345 void ExtensionActionViewController::UnregisterCommand(bool only_if_removed) {
346 views::FocusManager* focus_manager =
347 delegate_->GetFocusManagerForAccelerator();
348 if (!focus_manager || !action_keybinding_.get())
349 return;
350
351 // If |only_if_removed| is true, it means that we only need to unregister
352 // ourselves as an accelerator if the command was removed. Otherwise, we need
353 // to unregister ourselves no matter what (likely because we are shutting
354 // down).
355 extensions::Command extension_command;
356 if (!only_if_removed || !GetExtensionCommand(&extension_command)) {
357 focus_manager->UnregisterAccelerator(*action_keybinding_, this);
358 action_keybinding_.reset();
359 }
360 }
361
285 bool ExtensionActionViewController::CloseActiveMenuIfNeeded() { 362 bool ExtensionActionViewController::CloseActiveMenuIfNeeded() {
286 // If this view is shown inside another menu, there's a possibility that there 363 // 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 364 // is another context menu showing that we have to close before we can
288 // activate a different menu. 365 // activate a different menu.
289 if (delegate_->IsShownInMenu()) { 366 if (delegate_->IsShownInMenu()) {
290 views::MenuController* menu_controller = 367 views::MenuController* menu_controller =
291 views::MenuController::GetActiveInstance(); 368 views::MenuController::GetActiveInstance();
292 // If this is shown inside a menu, then there should always be an active 369 // If this is shown inside a menu, then there should always be an active
293 // menu controller. 370 // menu controller.
294 DCHECK(menu_controller); 371 DCHECK(menu_controller);
295 if (menu_controller->in_nested_run()) { 372 if (menu_controller->in_nested_run()) {
296 // There is another menu showing. Close the outermost menu (since we are 373 // 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). 374 // shown in the same menu, we don't want to close the whole thing).
298 menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST); 375 menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST);
299 return true; 376 return true;
300 } 377 }
301 } 378 }
302 379
303 return false; 380 return false;
304 } 381 }
305 382
306 void ExtensionActionViewController::CleanupPopup(bool close_widget) { 383 void ExtensionActionViewController::CleanupPopup(bool close_widget) {
307 DCHECK(popup_); 384 DCHECK(popup_);
308 delegate_->CleanupPopup(); 385 delegate_->CleanupPopup();
309 popup_->GetWidget()->RemoveObserver(this); 386 popup_->GetWidget()->RemoveObserver(this);
310 if (close_widget) 387 if (close_widget)
311 popup_->GetWidget()->Close(); 388 popup_->GetWidget()->Close();
312 popup_ = NULL; 389 popup_ = NULL;
313 } 390 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698