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