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.
5 #include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
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/base/accessibility/accessible_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_item_view.h"
30 #include "ui/views/controls/menu/menu_runner.h"
32 using content::WebContents
;
33 using extensions::LocationBarController
;
34 using extensions::Extension
;
36 PageActionImageView::PageActionImageView(LocationBarView
* owner
,
37 ExtensionAction
* page_action
,
40 page_action_(page_action
),
43 preview_enabled_(false),
45 const Extension
* extension
= owner_
->profile()->GetExtensionService()->
46 GetExtensionById(page_action
->extension_id(), false);
50 new ExtensionActionIconFactory(
51 owner_
->profile(), extension
, page_action
, this));
53 SetAccessibilityFocusable(true);
54 set_context_menu_controller(this);
56 extensions::CommandService
* command_service
=
57 extensions::CommandService::Get(browser_
->profile());
58 extensions::Command page_action_command
;
59 if (command_service
->GetPageActionCommand(
61 extensions::CommandService::ACTIVE_ONLY
,
64 page_action_keybinding_
.reset(
65 new ui::Accelerator(page_action_command
.accelerator()));
66 owner_
->GetFocusManager()->RegisterAccelerator(
67 *page_action_keybinding_
.get(),
68 ui::AcceleratorManager::kHighPriority
,
73 PageActionImageView::~PageActionImageView() {
74 if (owner_
->GetFocusManager()) {
75 if (page_action_keybinding_
.get()) {
76 owner_
->GetFocusManager()->UnregisterAccelerator(
77 *page_action_keybinding_
.get(), this);
82 popup_
->GetWidget()->RemoveObserver(this);
86 void PageActionImageView::ExecuteAction(
87 ExtensionPopup::ShowAction show_action
) {
88 WebContents
* web_contents
= owner_
->GetWebContents();
92 extensions::TabHelper
* extensions_tab_helper
=
93 extensions::TabHelper::FromWebContents(web_contents
);
94 LocationBarController
* controller
=
95 extensions_tab_helper
->location_bar_controller();
97 switch (controller
->OnClicked(page_action_
->extension_id(), 1)) {
98 case LocationBarController::ACTION_NONE
:
101 case LocationBarController::ACTION_SHOW_POPUP
:
102 ShowPopupWithURL(page_action_
->GetPopupUrl(current_tab_id_
), show_action
);
105 case LocationBarController::ACTION_SHOW_CONTEXT_MENU
:
106 // We are never passing OnClicked a right-click button, so assume that
107 // we're never going to be asked to show a context menu.
108 // TODO(kalman): if this changes, update this class to pass the real
109 // mouse button through to the LocationBarController.
115 void PageActionImageView::GetAccessibleState(ui::AccessibleViewState
* state
) {
116 state
->role
= ui::AccessibilityTypes::ROLE_PUSHBUTTON
;
117 state
->name
= base::UTF8ToUTF16(tooltip_
);
120 bool PageActionImageView::OnMousePressed(const ui::MouseEvent
& event
) {
121 // We want to show the bubble on mouse release; that is the standard behavior
122 // for buttons. (Also, triggering on mouse press causes bugs like
123 // http://crbug.com/33155.)
127 void PageActionImageView::OnMouseReleased(const ui::MouseEvent
& event
) {
128 if (!HitTestPoint(event
.location()))
131 if (event
.IsRightMouseButton()) {
132 // Don't show a menu here, its handled in View::ProcessMouseReleased. We
133 // show the context menu by way of being the ContextMenuController.
137 ExecuteAction(ExtensionPopup::SHOW
);
140 bool PageActionImageView::OnKeyPressed(const ui::KeyEvent
& event
) {
141 if (event
.key_code() == ui::VKEY_SPACE
||
142 event
.key_code() == ui::VKEY_RETURN
) {
143 ExecuteAction(ExtensionPopup::SHOW
);
149 void PageActionImageView::ShowContextMenuForView(
151 const gfx::Point
& point
,
152 ui::MenuSourceType source_type
) {
153 const Extension
* extension
= owner_
->profile()->GetExtensionService()->
154 GetExtensionById(page_action()->extension_id(), false);
155 if (!extension
->ShowConfigureContextMenus())
158 scoped_refptr
<ExtensionContextMenuModel
> context_menu_model(
159 new ExtensionContextMenuModel(extension
, browser_
, this));
160 menu_runner_
.reset(new views::MenuRunner(context_menu_model
.get()));
161 gfx::Point screen_loc
;
162 views::View::ConvertPointToScreen(this, &screen_loc
);
163 if (menu_runner_
->RunMenuAt(GetWidget(), NULL
, gfx::Rect(screen_loc
, size()),
164 views::MenuItemView::TOPLEFT
, source_type
,
165 views::MenuRunner::HAS_MNEMONICS
| views::MenuRunner::CONTEXT_MENU
) ==
166 views::MenuRunner::MENU_DELETED
)
170 bool PageActionImageView::AcceleratorPressed(
171 const ui::Accelerator
& accelerator
) {
172 DCHECK(visible()); // Should not have happened due to CanHandleAccelerator.
174 ExecuteAction(ExtensionPopup::SHOW
);
178 bool PageActionImageView::CanHandleAccelerators() const {
179 // While visible, we don't handle accelerators and while so we also don't
180 // count as a priority accelerator handler.
184 void PageActionImageView::UpdateVisibility(WebContents
* contents
,
186 // Save this off so we can pass it back to the extension when the action gets
187 // executed. See PageActionImageView::OnMousePressed.
189 contents
? extensions::ExtensionTabUtil::GetTabId(contents
) : -1;
193 (!preview_enabled_
&& !page_action_
->GetIsVisible(current_tab_id_
))) {
199 tooltip_
= page_action_
->GetTitle(current_tab_id_
);
200 SetTooltipText(base::UTF8ToUTF16(tooltip_
));
203 gfx::Image icon
= icon_factory_
->GetIcon(current_tab_id_
);
205 SetImage(*icon
.ToImageSkia());
210 void PageActionImageView::InspectPopup(ExtensionAction
* action
) {
211 ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT
);
214 void PageActionImageView::OnWidgetDestroying(views::Widget
* widget
) {
215 DCHECK_EQ(popup_
->GetWidget(), widget
);
216 popup_
->GetWidget()->RemoveObserver(this);
220 void PageActionImageView::OnIconUpdated() {
221 WebContents
* web_contents
= owner_
->GetWebContents();
223 UpdateVisibility(web_contents
, current_url_
);
226 void PageActionImageView::PaintChildren(gfx::Canvas
* canvas
) {
227 View::PaintChildren(canvas
);
228 if (current_tab_id_
>= 0)
229 page_action_
->PaintBadge(canvas
, GetLocalBounds(), current_tab_id_
);
232 void PageActionImageView::ShowPopupWithURL(
233 const GURL
& popup_url
,
234 ExtensionPopup::ShowAction show_action
) {
235 bool popup_showing
= popup_
!= NULL
;
237 // Always hide the current popup. Only one popup at a time.
240 // If we were already showing, then treat this click as a dismiss.
244 views::BubbleBorder::Arrow arrow
= base::i18n::IsRTL() ?
245 views::BubbleBorder::TOP_LEFT
: views::BubbleBorder::TOP_RIGHT
;
247 popup_
= ExtensionPopup::ShowPopup(popup_url
, browser_
, this, arrow
,
249 popup_
->GetWidget()->AddObserver(this);
252 void PageActionImageView::HidePopup() {
254 popup_
->GetWidget()->Close();