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

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

Issue 670463004: Make a platform-independent ToolbarActionViewController (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month 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
(Empty)
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 #include "chrome/browser/ui/views/extensions/extension_action_platform_delegate_ views.h"
6
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/profiles/profile.h"
13 #include "chrome/browser/sessions/session_tab_helper.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/extensions/accelerator_priority.h"
16 #include "chrome/browser/ui/views/toolbar/toolbar_action_view_delegate_views.h"
17 #include "chrome/browser/ui/views/toolbar/toolbar_view.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"
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"
26 #include "ui/views/controls/menu/menu_controller.h"
27 #include "ui/views/controls/menu/menu_runner.h"
28 #include "ui/views/view.h"
29 #include "ui/views/widget/widget.h"
30
31 using extensions::ActionInfo;
32 using extensions::CommandService;
33
34 namespace {
35
36 // The ExtensionActionPlatformDelegateViews which is currently showing its
37 // context menu, if any.
38 // Since only one context menu can be shown (even across browser windows), it's
39 // safe to have this be a global singleton.
40 ExtensionActionPlatformDelegateViews* context_menu_owner = NULL;
41
42 } // namespace
43
44 // static
45 scoped_ptr<ExtensionActionPlatformDelegate>
46 ExtensionActionPlatformDelegate::Create(
47 ExtensionActionViewController* controller) {
48 return make_scoped_ptr(new ExtensionActionPlatformDelegateViews(controller));
49 }
50
51 ExtensionActionPlatformDelegateViews::ExtensionActionPlatformDelegateViews(
52 ExtensionActionViewController* controller)
53 : controller_(controller),
54 popup_(nullptr),
55 weak_factory_(this) {
56 content::NotificationSource notification_source = content::Source<Profile>(
57 controller_->browser()->profile()->GetOriginalProfile());
58 registrar_.Add(this,
59 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED,
60 notification_source);
61 registrar_.Add(this,
62 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
63 notification_source);
64 }
65
66 ExtensionActionPlatformDelegateViews::~ExtensionActionPlatformDelegateViews() {
67 if (context_menu_owner == this)
68 context_menu_owner = NULL;
69 controller_->HidePopup();
70 UnregisterCommand(false);
71 }
72
73 gfx::NativeView ExtensionActionPlatformDelegateViews::GetPopupNativeView() {
74 return popup_ ? popup_->GetWidget()->GetNativeView() : nullptr;
75 }
76
77 bool ExtensionActionPlatformDelegateViews::IsMenuRunning() const {
78 return menu_runner_.get() != NULL;
79 }
80
81 void ExtensionActionPlatformDelegateViews::RegisterCommand() {
82 // If we've already registered, do nothing.
83 if (action_keybinding_.get())
84 return;
85
86 extensions::Command extension_command;
87 views::FocusManager* focus_manager =
88 GetDelegateViews()->GetFocusManagerForAccelerator();
89 if (focus_manager && controller_->GetExtensionCommand(&extension_command)) {
90 action_keybinding_.reset(
91 new ui::Accelerator(extension_command.accelerator()));
92 focus_manager->RegisterAccelerator(
93 *action_keybinding_,
94 GetAcceleratorPriority(extension_command.accelerator(),
95 controller_->extension()),
96 this);
97 }
98 }
99
100 void ExtensionActionPlatformDelegateViews::OnDelegateSet() {
101 GetDelegateViews()->GetAsView()->set_context_menu_controller(this);
102 }
103
104 bool ExtensionActionPlatformDelegateViews::IsShowingPopup() const {
105 return popup_ != nullptr;
106 }
107
108 void ExtensionActionPlatformDelegateViews::CloseActivePopup() {
109 GetDelegateViews()->HideActivePopup();
110 }
111
112 void ExtensionActionPlatformDelegateViews::CloseOwnPopup() {
113 // We should only be asked to close the popup if we're showing one.
114 DCHECK(popup_);
115 CleanupPopup(true);
116 }
117
118 bool ExtensionActionPlatformDelegateViews::ShowPopupWithUrl(
119 ExtensionActionViewController::PopupShowAction show_action,
120 const GURL& popup_url,
121 bool grant_tab_permissions) {
122 views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ?
123 views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT;
124
125 views::View* reference_view = GetDelegateViews()->GetReferenceViewForPopup();
126
127 ExtensionPopup::ShowAction popup_show_action =
128 show_action == ExtensionActionViewController::SHOW_POPUP ?
129 ExtensionPopup::SHOW : ExtensionPopup::SHOW_AND_INSPECT;
130 popup_ = ExtensionPopup::ShowPopup(popup_url,
131 controller_->browser(),
132 reference_view,
133 arrow,
134 popup_show_action);
135 popup_->GetWidget()->AddObserver(this);
136
137 GetDelegateViews()->OnPopupShown(grant_tab_permissions);
138
139 return true;
140 }
141
142 void ExtensionActionPlatformDelegateViews::Observe(
143 int type,
144 const content::NotificationSource& source,
145 const content::NotificationDetails& details) {
146 DCHECK(type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED ||
147 type == extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED);
148 std::pair<const std::string, const std::string>* payload =
149 content::Details<std::pair<const std::string, const std::string> >(
150 details).ptr();
151 if (controller_->extension()->id() == payload->first &&
152 payload->second ==
153 extensions::manifest_values::kBrowserActionCommandEvent) {
154 if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED)
155 RegisterCommand();
156 else
157 UnregisterCommand(true);
158 }
159 }
160
161 bool ExtensionActionPlatformDelegateViews::AcceleratorPressed(
162 const ui::Accelerator& accelerator) {
163 // We shouldn't be handling any accelerators if the view is hidden, unless
164 // this is a browser action.
165 DCHECK(controller_->extension_action()->action_type() ==
166 ActionInfo::TYPE_BROWSER ||
167 GetDelegateViews()->GetAsView()->visible());
168
169 // Normal priority shortcuts must be handled via standard browser commands to
170 // be processed at the proper time.
171 if (GetAcceleratorPriority(accelerator, controller_->extension()) ==
172 ui::AcceleratorManager::kNormalPriority)
173 return false;
174
175 controller_->ExecuteAction(true);
176 return true;
177 }
178
179 bool ExtensionActionPlatformDelegateViews::CanHandleAccelerators() const {
180 // Page actions can only handle accelerators when they are visible.
181 // Browser actions can handle accelerators even when not visible, since they
182 // might be hidden in an overflow menu.
183 return controller_->extension_action()->action_type() ==
184 ActionInfo::TYPE_PAGE ? GetDelegateViews()->GetAsView()->visible() :
185 true;
186 }
187
188 void ExtensionActionPlatformDelegateViews::OnWidgetDestroying(
189 views::Widget* widget) {
190 DCHECK(popup_);
191 DCHECK_EQ(popup_->GetWidget(), widget);
192 CleanupPopup(false);
193 }
194
195 void ExtensionActionPlatformDelegateViews::ShowContextMenuForView(
196 views::View* source,
197 const gfx::Point& point,
198 ui::MenuSourceType source_type) {
199 // If there's another active menu that won't be dismissed by opening this one,
200 // then we can't show this one right away, since we can only show one nested
201 // menu at a time.
202 // If the other menu is an extension action's context menu, then we'll run
203 // this one after that one closes. If it's a different type of menu, then we
204 // close it and give up, for want of a better solution. (Luckily, this is
205 // rare).
206 // TODO(devlin): Update this when views code no longer runs menus in a nested
207 // loop.
208 if (context_menu_owner) {
209 context_menu_owner->followup_context_menu_task_ =
210 base::Bind(&ExtensionActionPlatformDelegateViews::DoShowContextMenu,
211 weak_factory_.GetWeakPtr(),
212 source_type);
213 }
214 if (CloseActiveMenuIfNeeded())
215 return;
216
217 // Otherwise, no other menu is showing, and we can proceed normally.
218 DoShowContextMenu(source_type);
219 }
220
221 void ExtensionActionPlatformDelegateViews::DoShowContextMenu(
222 ui::MenuSourceType source_type) {
223 if (!controller_->extension()->ShowConfigureContextMenus())
224 return;
225
226 DCHECK(!context_menu_owner);
227 context_menu_owner = this;
228
229 // We shouldn't have both a popup and a context menu showing.
230 GetDelegateViews()->HideActivePopup();
231
232 // Reconstructs the menu every time because the menu's contents are dynamic.
233 scoped_refptr<ExtensionContextMenuModel> context_menu_model(
234 new ExtensionContextMenuModel(
235 controller_->extension(), controller_->browser(), controller_));
236
237 gfx::Point screen_loc;
238 views::View::ConvertPointToScreen(GetDelegateViews()->GetAsView(),
239 &screen_loc);
240
241 int run_types =
242 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU;
243 if (GetDelegateViews()->IsShownInMenu())
244 run_types |= views::MenuRunner::IS_NESTED;
245
246 views::Widget* parent = GetDelegateViews()->GetParentForContextMenu();
247
248 menu_runner_.reset(
249 new views::MenuRunner(context_menu_model.get(), run_types));
250
251 if (menu_runner_->RunMenuAt(
252 parent,
253 GetDelegateViews()->GetContextMenuButton(),
254 gfx::Rect(screen_loc, GetDelegateViews()->GetAsView()->size()),
255 views::MENU_ANCHOR_TOPLEFT,
256 source_type) == views::MenuRunner::MENU_DELETED) {
257 return;
258 }
259
260 context_menu_owner = NULL;
261 menu_runner_.reset();
262
263 // If another extension action wants to show its context menu, allow it to.
264 if (!followup_context_menu_task_.is_null()) {
265 base::Closure task = followup_context_menu_task_;
266 followup_context_menu_task_ = base::Closure();
267 task.Run();
268 }
269 }
270
271 void ExtensionActionPlatformDelegateViews::UnregisterCommand(
272 bool only_if_removed) {
273 views::FocusManager* focus_manager =
274 GetDelegateViews()->GetFocusManagerForAccelerator();
275 if (!focus_manager || !action_keybinding_.get())
276 return;
277
278 // If |only_if_removed| is true, it means that we only need to unregister
279 // ourselves as an accelerator if the command was removed. Otherwise, we need
280 // to unregister ourselves no matter what (likely because we are shutting
281 // down).
282 extensions::Command extension_command;
283 if (!only_if_removed ||
284 !controller_->GetExtensionCommand(&extension_command)) {
285 focus_manager->UnregisterAccelerator(*action_keybinding_, this);
286 action_keybinding_.reset();
287 }
288 }
289
290 bool ExtensionActionPlatformDelegateViews::CloseActiveMenuIfNeeded() {
291 // If this view is shown inside another menu, there's a possibility that there
292 // is another context menu showing that we have to close before we can
293 // activate a different menu.
294 if (GetDelegateViews()->IsShownInMenu()) {
295 views::MenuController* menu_controller =
296 views::MenuController::GetActiveInstance();
297 // If this is shown inside a menu, then there should always be an active
298 // menu controller.
299 DCHECK(menu_controller);
300 if (menu_controller->in_nested_run()) {
301 // There is another menu showing. Close the outermost menu (since we are
302 // shown in the same menu, we don't want to close the whole thing).
303 menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST);
304 return true;
305 }
306 }
307
308 return false;
309 }
310
311 void ExtensionActionPlatformDelegateViews::CleanupPopup(bool close_widget) {
312 DCHECK(popup_);
313 GetDelegateViews()->CleanupPopup();
314 popup_->GetWidget()->RemoveObserver(this);
315 if (close_widget)
316 popup_->GetWidget()->Close();
317 popup_ = NULL;
318 }
319
320 ToolbarActionViewDelegateViews*
321 ExtensionActionPlatformDelegateViews::GetDelegateViews() const {
322 return static_cast<ToolbarActionViewDelegateViews*>(
323 controller_->view_delegate());
324 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698