- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / location_bar / page_action_image_view.cc
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 "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"
31
32 using content::WebContents;
33 using extensions::LocationBarController;
34 using extensions::Extension;
35
36 PageActionImageView::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       scoped_icon_animation_observer_(
46           page_action->GetIconAnimation(
47               SessionID::IdForTab(owner->GetWebContents())),
48           this) {
49   const Extension* extension = owner_->profile()->GetExtensionService()->
50       GetExtensionById(page_action->extension_id(), false);
51   DCHECK(extension);
52
53   icon_factory_.reset(
54       new ExtensionActionIconFactory(
55           owner_->profile(), extension, page_action, this));
56
57   set_accessibility_focusable(true);
58   set_context_menu_controller(this);
59
60   extensions::CommandService* command_service =
61       extensions::CommandService::Get(browser_->profile());
62   extensions::Command page_action_command;
63   if (command_service->GetPageActionCommand(
64           extension->id(),
65           extensions::CommandService::ACTIVE_ONLY,
66           &page_action_command,
67           NULL)) {
68     page_action_keybinding_.reset(
69         new ui::Accelerator(page_action_command.accelerator()));
70     owner_->GetFocusManager()->RegisterAccelerator(
71         *page_action_keybinding_.get(),
72         ui::AcceleratorManager::kHighPriority,
73         this);
74   }
75
76   extensions::Command script_badge_command;
77   if (command_service->GetScriptBadgeCommand(
78           extension->id(),
79           extensions::CommandService::ACTIVE_ONLY,
80           &script_badge_command,
81           NULL)) {
82     script_badge_keybinding_.reset(
83         new ui::Accelerator(script_badge_command.accelerator()));
84     owner_->GetFocusManager()->RegisterAccelerator(
85         *script_badge_keybinding_.get(),
86         ui::AcceleratorManager::kHighPriority,
87         this);
88   }
89 }
90
91 PageActionImageView::~PageActionImageView() {
92   if (owner_->GetFocusManager()) {
93     if (page_action_keybinding_.get()) {
94       owner_->GetFocusManager()->UnregisterAccelerator(
95           *page_action_keybinding_.get(), this);
96     }
97
98     if (script_badge_keybinding_.get()) {
99       owner_->GetFocusManager()->UnregisterAccelerator(
100           *script_badge_keybinding_.get(), this);
101     }
102   }
103
104   if (popup_)
105     popup_->GetWidget()->RemoveObserver(this);
106   HidePopup();
107 }
108
109 void PageActionImageView::ExecuteAction(
110     ExtensionPopup::ShowAction show_action) {
111   WebContents* web_contents = owner_->GetWebContents();
112   if (!web_contents)
113     return;
114
115   extensions::TabHelper* extensions_tab_helper =
116       extensions::TabHelper::FromWebContents(web_contents);
117   LocationBarController* controller =
118       extensions_tab_helper->location_bar_controller();
119
120   switch (controller->OnClicked(page_action_->extension_id(), 1)) {
121     case LocationBarController::ACTION_NONE:
122       break;
123
124     case LocationBarController::ACTION_SHOW_POPUP:
125       ShowPopupWithURL(page_action_->GetPopupUrl(current_tab_id_), show_action);
126       break;
127
128     case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
129       // We are never passing OnClicked a right-click button, so assume that
130       // we're never going to be asked to show a context menu.
131       // TODO(kalman): if this changes, update this class to pass the real
132       // mouse button through to the LocationBarController.
133       NOTREACHED();
134       break;
135
136     case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
137       ShowPopupWithURL(
138           extensions::ExtensionInfoUI::GetURL(page_action_->extension_id()),
139           show_action);
140       break;
141   }
142 }
143
144 void PageActionImageView::GetAccessibleState(ui::AccessibleViewState* state) {
145   state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
146   state->name = UTF8ToUTF16(tooltip_);
147 }
148
149 bool 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
156 void 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
169 bool 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
178 void 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
199 bool 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
207 bool 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
213 void 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
238 void PageActionImageView::InspectPopup(ExtensionAction* action) {
239   ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT);
240 }
241
242 void PageActionImageView::OnWidgetDestroying(views::Widget* widget) {
243   DCHECK_EQ(popup_->GetWidget(), widget);
244   popup_->GetWidget()->RemoveObserver(this);
245   popup_ = NULL;
246 }
247
248 void PageActionImageView::OnIconUpdated() {
249   WebContents* web_contents = owner_->GetWebContents();
250   if (web_contents)
251     UpdateVisibility(web_contents, current_url_);
252 }
253
254 void PageActionImageView::OnIconChanged() {
255   OnIconUpdated();
256 }
257
258 void 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
264 void 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
284 void PageActionImageView::HidePopup() {
285   if (popup_)
286     popup_->GetWidget()->Close();
287 }