OLD | NEW |
(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_view.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "chrome/browser/extensions/api/commands/command_service.h" |
| 9 #include "chrome/browser/extensions/extension_action.h" |
| 10 #include "chrome/browser/extensions/extension_toolbar_model.h" |
| 11 #include "chrome/browser/extensions/location_bar_controller.h" |
| 12 #include "chrome/browser/extensions/tab_helper.h" |
| 13 #include "chrome/browser/profiles/profile.h" |
| 14 #include "chrome/browser/sessions/session_id.h" |
| 15 #include "chrome/browser/ui/browser.h" |
| 16 #include "chrome/browser/ui/extensions/accelerator_priority.h" |
| 17 #include "chrome/common/extensions/api/extension_action/action_info.h" |
| 18 #include "extensions/common/extension.h" |
| 19 #include "extensions/common/manifest_constants.h" |
| 20 #include "ui/views/controls/menu/menu_runner.h" |
| 21 #include "ui/views/view.h" |
| 22 #include "ui/views/widget/widget.h" |
| 23 |
| 24 using extensions::ActionInfo; |
| 25 using extensions::CommandService; |
| 26 |
| 27 ExtensionActionView::ExtensionActionView( |
| 28 const extensions::Extension* extension, |
| 29 Browser* browser, |
| 30 ExtensionAction* extension_action, |
| 31 ViewDelegate* delegate) |
| 32 : extension_(extension), |
| 33 browser_(browser), |
| 34 extension_action_(extension_action), |
| 35 delegate_(delegate), |
| 36 icon_factory_(browser->profile(), extension, extension_action, this), |
| 37 popup_(NULL) { |
| 38 DCHECK(extension_action->action_type() == ActionInfo::TYPE_PAGE || |
| 39 extension_action->action_type() == ActionInfo::TYPE_BROWSER); |
| 40 DCHECK(extension); |
| 41 |
| 42 } |
| 43 |
| 44 ExtensionActionView::~ExtensionActionView() { |
| 45 HidePopup(); |
| 46 UnregisterCommand(false); |
| 47 } |
| 48 |
| 49 void ExtensionActionView::InspectPopup() { |
| 50 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT, true); |
| 51 } |
| 52 |
| 53 void ExtensionActionView::ExecuteActionByUser() { |
| 54 ExecuteAction(ExtensionPopup::SHOW, true); |
| 55 } |
| 56 |
| 57 bool ExtensionActionView::ExecuteAction(ExtensionPopup::ShowAction show_action, |
| 58 bool grant_tab_permissions) { |
| 59 GURL popup_url; |
| 60 bool show_popup = false; |
| 61 if (extension_action_->action_type() == ActionInfo::TYPE_BROWSER) { |
| 62 extensions::ExtensionToolbarModel* toolbar_model = |
| 63 extensions::ExtensionToolbarModel::Get(browser_->profile()); |
| 64 show_popup = toolbar_model->ExecuteBrowserAction( |
| 65 extension_, browser_, &popup_url, grant_tab_permissions) == |
| 66 extensions::ExtensionToolbarModel::ACTION_SHOW_POPUP; |
| 67 } else { // PageAction |
| 68 content::WebContents* web_contents = delegate_->GetCurrentWebContents(); |
| 69 if (!web_contents) |
| 70 return false; |
| 71 extensions::LocationBarController* controller = |
| 72 extensions::TabHelper::FromWebContents(web_contents)-> |
| 73 location_bar_controller(); |
| 74 switch (controller->OnClicked(extension_action_)) { |
| 75 case extensions::LocationBarController::ACTION_NONE: |
| 76 break; |
| 77 case extensions::LocationBarController::ACTION_SHOW_POPUP: |
| 78 popup_url = extension_action_->GetPopupUrl(GetCurrentTabId()); |
| 79 show_popup = true; |
| 80 break; |
| 81 case extensions::LocationBarController::ACTION_SHOW_CONTEXT_MENU: |
| 82 // We are never passing OnClicked a right-click button, so assume that |
| 83 // we're never going to be asked to show a context menu. |
| 84 // TODO(kalman): if this changes, update this class to pass the real |
| 85 // mouse button through to the LocationBarController. |
| 86 NOTREACHED(); |
| 87 break; |
| 88 } |
| 89 } |
| 90 |
| 91 if (show_popup && ShowPopupWithUrl(show_action, popup_url)) { |
| 92 delegate_->OnPopupShown(grant_tab_permissions); |
| 93 return true; |
| 94 } |
| 95 |
| 96 return false; |
| 97 } |
| 98 |
| 99 void ExtensionActionView::HidePopup() { |
| 100 if (popup_) |
| 101 CleanupPopup(true); |
| 102 } |
| 103 |
| 104 gfx::Image ExtensionActionView::GetIcon(int tab_id) { |
| 105 return icon_factory_.GetIcon(tab_id); |
| 106 } |
| 107 |
| 108 int ExtensionActionView::GetCurrentTabId() const { |
| 109 content::WebContents* web_contents = delegate_->GetCurrentWebContents(); |
| 110 return web_contents ? SessionID::IdForTab(web_contents) : -1; |
| 111 } |
| 112 |
| 113 void ExtensionActionView::RegisterCommand() { |
| 114 // If we've already registered, do nothing. |
| 115 if (action_keybinding_.get()) |
| 116 return; |
| 117 |
| 118 extensions::Command extension_command; |
| 119 views::FocusManager* focus_manager = |
| 120 delegate_->GetFocusManagerForAccelerator(); |
| 121 if (focus_manager && GetExtensionCommand(&extension_command)) { |
| 122 action_keybinding_.reset( |
| 123 new ui::Accelerator(extension_command.accelerator())); |
| 124 focus_manager->RegisterAccelerator( |
| 125 *action_keybinding_, |
| 126 GetAcceleratorPriority(extension_command.accelerator(), extension_), |
| 127 delegate_->GetAsView()); |
| 128 } |
| 129 } |
| 130 |
| 131 void ExtensionActionView::UnregisterCommand(bool only_if_removed) { |
| 132 views::FocusManager* focus_manager = |
| 133 delegate_->GetFocusManagerForAccelerator(); |
| 134 if (!focus_manager || !action_keybinding_.get()) |
| 135 return; |
| 136 |
| 137 // If |only_if_removed| is true, it means that we only need to unregister |
| 138 // ourselves as an accelerator if the command was removed. Otherwise, we need |
| 139 // to unregister ourselves no matter what (likely because we are shutting |
| 140 // down). |
| 141 extensions::Command extension_command; |
| 142 if (!only_if_removed || !GetExtensionCommand(&extension_command)) { |
| 143 focus_manager->UnregisterAccelerator(*action_keybinding_, |
| 144 delegate_->GetAsView()); |
| 145 action_keybinding_.reset(); |
| 146 } |
| 147 } |
| 148 |
| 149 bool ExtensionActionView::AcceleratorPressed( |
| 150 const ui::Accelerator& accelerator) { |
| 151 // Normal priority shortcuts must be handled via standard browser commands to |
| 152 // be processed at the proper time. |
| 153 if (GetAcceleratorPriority(accelerator, extension()) == |
| 154 ui::AcceleratorManager::kNormalPriority) |
| 155 return false; |
| 156 |
| 157 ExecuteActionByUser(); |
| 158 return true; |
| 159 } |
| 160 |
| 161 void ExtensionActionView::OnIconUpdated() { |
| 162 delegate_->OnIconUpdated(); |
| 163 } |
| 164 |
| 165 void ExtensionActionView::OnWidgetDestroying(views::Widget* widget) { |
| 166 DCHECK(popup_); |
| 167 DCHECK_EQ(popup_->GetWidget(), widget); |
| 168 CleanupPopup(false); |
| 169 } |
| 170 |
| 171 void ExtensionActionView::ShowContextMenuForView( |
| 172 views::View* source, |
| 173 const gfx::Point& point, |
| 174 ui::MenuSourceType source_type) { |
| 175 if (!extension_->ShowConfigureContextMenus()) |
| 176 return; |
| 177 |
| 178 delegate_->OnWillShowContextMenus(); |
| 179 |
| 180 // Reconstructs the menu every time because the menu's contents are dynamic. |
| 181 scoped_refptr<ExtensionContextMenuModel> context_menu_model( |
| 182 new ExtensionContextMenuModel(extension_, browser_, this)); |
| 183 |
| 184 gfx::Point screen_loc; |
| 185 views::View::ConvertPointToScreen(delegate_->GetAsView(), &screen_loc); |
| 186 |
| 187 int run_types = views::MenuRunner::HAS_MNEMONICS | |
| 188 views::MenuRunner::CONTEXT_MENU; |
| 189 if (delegate_->IsNestedView()) |
| 190 run_types |= views::MenuRunner::IS_NESTED; |
| 191 |
| 192 views::Widget* parent = delegate_->GetParentForContextMenu(); |
| 193 |
| 194 menu_runner_.reset( |
| 195 new views::MenuRunner(context_menu_model.get(), run_types)); |
| 196 |
| 197 if (menu_runner_->RunMenuAt( |
| 198 parent, |
| 199 NULL, |
| 200 gfx::Rect(screen_loc, delegate_->GetAsView()->size()), |
| 201 views::MENU_ANCHOR_TOPLEFT, |
| 202 source_type) == views::MenuRunner::MENU_DELETED) { |
| 203 return; |
| 204 } |
| 205 |
| 206 menu_runner_.reset(); |
| 207 delegate_->OnContextMenuDone(); |
| 208 } |
| 209 |
| 210 bool ExtensionActionView::ShowPopupWithUrl( |
| 211 ExtensionPopup::ShowAction show_action, const GURL& popup_url) { |
| 212 // If we're already showing the popup for this browser action, just hide it |
| 213 // and return. |
| 214 bool already_showing = popup_ != NULL; |
| 215 |
| 216 // Always hide the current popup, even if it's not the same. |
| 217 // Only one popup should be visible at a time. |
| 218 delegate_->HideActivePopup(); |
| 219 if (already_showing) |
| 220 return false; |
| 221 |
| 222 views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ? |
| 223 views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT; |
| 224 |
| 225 views::View* reference_view = delegate_->GetReferenceViewForPopup(); |
| 226 |
| 227 popup_ = ExtensionPopup::ShowPopup( |
| 228 popup_url, browser_, reference_view, arrow, show_action); |
| 229 popup_->GetWidget()->AddObserver(this); |
| 230 |
| 231 return true; |
| 232 } |
| 233 |
| 234 bool ExtensionActionView::GetExtensionCommand(extensions::Command* command) { |
| 235 DCHECK(command); |
| 236 CommandService* command_service = CommandService::Get(browser_->profile()); |
| 237 if (extension_action_->action_type() == ActionInfo::TYPE_PAGE) { |
| 238 return command_service->GetPageActionCommand( |
| 239 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); |
| 240 } |
| 241 return command_service->GetBrowserActionCommand( |
| 242 extension_->id(), CommandService::ACTIVE_ONLY, command, NULL); |
| 243 } |
| 244 |
| 245 void ExtensionActionView::CleanupPopup(bool close_widget) { |
| 246 DCHECK(popup_); |
| 247 delegate_->CleanupPopup(); |
| 248 popup_->GetWidget()->RemoveObserver(this); |
| 249 if (close_widget) |
| 250 popup_->GetWidget()->Close(); |
| 251 popup_ = NULL; |
| 252 } |
OLD | NEW |