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