page_action_image_view.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright (c) 2012 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/location_bar/page_action_image_view.h" 6 7#include "base/strings/utf_string_conversions.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_action_icon_factory.h" 11#include "chrome/browser/extensions/extension_action_manager.h" 12#include "chrome/browser/extensions/extension_context_menu_model.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/extensions/extension_tab_util.h" 15#include "chrome/browser/extensions/location_bar_controller.h" 16#include "chrome/browser/extensions/tab_helper.h" 17#include "chrome/browser/platform_util.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/browser/sessions/session_id.h" 20#include "chrome/browser/ui/browser_list.h" 21#include "chrome/browser/ui/views/frame/browser_view.h" 22#include "chrome/browser/ui/views/location_bar/location_bar_view.h" 23#include "chrome/browser/ui/webui/extensions/extension_info_ui.h" 24#include "chrome/common/extensions/extension.h" 25#include "chrome/common/extensions/extension_manifest_constants.h" 26#include "ui/base/accessibility/accessible_view_state.h" 27#include "ui/base/events/event.h" 28#include "ui/gfx/canvas.h" 29#include "ui/gfx/image/image.h" 30#include "ui/views/controls/menu/menu_item_view.h" 31#include "ui/views/controls/menu/menu_runner.h" 32 33using content::WebContents; 34using extensions::LocationBarController; 35using extensions::Extension; 36 37PageActionImageView::PageActionImageView(LocationBarView* owner, 38 ExtensionAction* page_action, 39 Browser* browser) 40 : owner_(owner), 41 page_action_(page_action), 42 browser_(browser), 43 current_tab_id_(-1), 44 preview_enabled_(false), 45 popup_(NULL), 46 scoped_icon_animation_observer_( 47 page_action->GetIconAnimation( 48 SessionID::IdForTab(owner->GetWebContents())), 49 this) { 50 const Extension* extension = owner_->profile()->GetExtensionService()-> 51 GetExtensionById(page_action->extension_id(), false); 52 DCHECK(extension); 53 54 icon_factory_.reset( 55 new ExtensionActionIconFactory( 56 owner_->profile(), extension, page_action, this)); 57 58 set_accessibility_focusable(true); 59 set_context_menu_controller(this); 60 61 extensions::CommandService* command_service = 62 extensions::CommandService::Get(browser_->profile()); 63 extensions::Command page_action_command; 64 if (command_service->GetPageActionCommand( 65 extension->id(), 66 extensions::CommandService::ACTIVE_ONLY, 67 &page_action_command, 68 NULL)) { 69 page_action_keybinding_.reset( 70 new ui::Accelerator(page_action_command.accelerator())); 71 owner_->GetFocusManager()->RegisterAccelerator( 72 *page_action_keybinding_.get(), 73 ui::AcceleratorManager::kHighPriority, 74 this); 75 } 76 77 extensions::Command script_badge_command; 78 if (command_service->GetScriptBadgeCommand( 79 extension->id(), 80 extensions::CommandService::ACTIVE_ONLY, 81 &script_badge_command, 82 NULL)) { 83 script_badge_keybinding_.reset( 84 new ui::Accelerator(script_badge_command.accelerator())); 85 owner_->GetFocusManager()->RegisterAccelerator( 86 *script_badge_keybinding_.get(), 87 ui::AcceleratorManager::kHighPriority, 88 this); 89 } 90} 91 92PageActionImageView::~PageActionImageView() { 93 if (owner_->GetFocusManager()) { 94 if (page_action_keybinding_.get()) { 95 owner_->GetFocusManager()->UnregisterAccelerator( 96 *page_action_keybinding_.get(), this); 97 } 98 99 if (script_badge_keybinding_.get()) { 100 owner_->GetFocusManager()->UnregisterAccelerator( 101 *script_badge_keybinding_.get(), this); 102 } 103 } 104 105 if (popup_) 106 popup_->GetWidget()->RemoveObserver(this); 107 HidePopup(); 108} 109 110void PageActionImageView::ExecuteAction( 111 ExtensionPopup::ShowAction show_action) { 112 WebContents* web_contents = owner_->GetWebContents(); 113 if (!web_contents) 114 return; 115 116 extensions::TabHelper* extensions_tab_helper = 117 extensions::TabHelper::FromWebContents(web_contents); 118 LocationBarController* controller = 119 extensions_tab_helper->location_bar_controller(); 120 121 switch (controller->OnClicked(page_action_->extension_id(), 1)) { 122 case LocationBarController::ACTION_NONE: 123 break; 124 125 case LocationBarController::ACTION_SHOW_POPUP: 126 ShowPopupWithURL(page_action_->GetPopupUrl(current_tab_id_), show_action); 127 break; 128 129 case LocationBarController::ACTION_SHOW_CONTEXT_MENU: 130 // We are never passing OnClicked a right-click button, so assume that 131 // we're never going to be asked to show a context menu. 132 // TODO(kalman): if this changes, update this class to pass the real 133 // mouse button through to the LocationBarController. 134 NOTREACHED(); 135 break; 136 137 case LocationBarController::ACTION_SHOW_SCRIPT_POPUP: 138 ShowPopupWithURL(ExtensionInfoUI::GetURL(page_action_->extension_id()), 139 show_action); 140 break; 141 } 142} 143 144void PageActionImageView::GetAccessibleState(ui::AccessibleViewState* state) { 145 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; 146 state->name = UTF8ToUTF16(tooltip_); 147} 148 149bool PageActionImageView::OnMousePressed(const ui::MouseEvent& event) { 150 // We want to show the bubble on mouse release; that is the standard behavior 151 // for buttons. (Also, triggering on mouse press causes bugs like 152 // http://crbug.com/33155.) 153 return true; 154} 155 156void PageActionImageView::OnMouseReleased(const ui::MouseEvent& event) { 157 if (!HitTestPoint(event.location())) 158 return; 159 160 if (event.IsRightMouseButton()) { 161 // Don't show a menu here, its handled in View::ProcessMouseReleased. We 162 // show the context menu by way of being the ContextMenuController. 163 return; 164 } 165 166 ExecuteAction(ExtensionPopup::SHOW); 167} 168 169bool PageActionImageView::OnKeyPressed(const ui::KeyEvent& event) { 170 if (event.key_code() == ui::VKEY_SPACE || 171 event.key_code() == ui::VKEY_RETURN) { 172 ExecuteAction(ExtensionPopup::SHOW); 173 return true; 174 } 175 return false; 176} 177 178void PageActionImageView::ShowContextMenuForView( 179 View* source, 180 const gfx::Point& point, 181 ui::MenuSourceType source_type) { 182 const Extension* extension = owner_->profile()->GetExtensionService()-> 183 GetExtensionById(page_action()->extension_id(), false); 184 if (!extension->ShowConfigureContextMenus()) 185 return; 186 187 scoped_refptr<ExtensionContextMenuModel> context_menu_model( 188 new ExtensionContextMenuModel(extension, browser_, this)); 189 menu_runner_.reset(new views::MenuRunner(context_menu_model.get())); 190 gfx::Point screen_loc; 191 views::View::ConvertPointToScreen(this, &screen_loc); 192 if (menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()), 193 views::MenuItemView::TOPLEFT, source_type, 194 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == 195 views::MenuRunner::MENU_DELETED) 196 return; 197} 198 199bool PageActionImageView::AcceleratorPressed( 200 const ui::Accelerator& accelerator) { 201 DCHECK(visible()); // Should not have happened due to CanHandleAccelerator. 202 203 ExecuteAction(ExtensionPopup::SHOW); 204 return true; 205} 206 207bool PageActionImageView::CanHandleAccelerators() const { 208 // While visible, we don't handle accelerators and while so we also don't 209 // count as a priority accelerator handler. 210 return visible(); 211} 212 213void PageActionImageView::UpdateVisibility(WebContents* contents, 214 const GURL& url) { 215 // Save this off so we can pass it back to the extension when the action gets 216 // executed. See PageActionImageView::OnMousePressed. 217 current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1; 218 current_url_ = url; 219 220 if (!contents || 221 (!preview_enabled_ && !page_action_->GetIsVisible(current_tab_id_))) { 222 SetVisible(false); 223 return; 224 } 225 226 // Set the tooltip. 227 tooltip_ = page_action_->GetTitle(current_tab_id_); 228 SetTooltipText(UTF8ToUTF16(tooltip_)); 229 230 // Set the image. 231 gfx::Image icon = icon_factory_->GetIcon(current_tab_id_); 232 if (!icon.IsEmpty()) 233 SetImage(*icon.ToImageSkia()); 234 235 SetVisible(true); 236} 237 238void PageActionImageView::InspectPopup(ExtensionAction* action) { 239 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT); 240} 241 242void PageActionImageView::OnWidgetDestroying(views::Widget* widget) { 243 DCHECK_EQ(popup_->GetWidget(), widget); 244 popup_->GetWidget()->RemoveObserver(this); 245 popup_ = NULL; 246} 247 248void PageActionImageView::OnIconUpdated() { 249 WebContents* web_contents = owner_->GetWebContents(); 250 if (web_contents) 251 UpdateVisibility(web_contents, current_url_); 252} 253 254void PageActionImageView::OnIconChanged() { 255 OnIconUpdated(); 256} 257 258void PageActionImageView::PaintChildren(gfx::Canvas* canvas) { 259 View::PaintChildren(canvas); 260 if (current_tab_id_ >= 0) 261 page_action_->PaintBadge(canvas, GetLocalBounds(), current_tab_id_); 262} 263 264void PageActionImageView::ShowPopupWithURL( 265 const GURL& popup_url, 266 ExtensionPopup::ShowAction show_action) { 267 bool popup_showing = popup_ != NULL; 268 269 // Always hide the current popup. Only one popup at a time. 270 HidePopup(); 271 272 // If we were already showing, then treat this click as a dismiss. 273 if (popup_showing) 274 return; 275 276 views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ? 277 views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT; 278 279 popup_ = ExtensionPopup::ShowPopup(popup_url, browser_, this, arrow, 280 show_action); 281 popup_->GetWidget()->AddObserver(this); 282} 283 284void PageActionImageView::HidePopup() { 285 if (popup_) 286 popup_->GetWidget()->Close(); 287} 288