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