Home | History | Annotate | Download | only in location_bar
      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 
     31 using content::WebContents;
     32 using extensions::LocationBarController;
     33 using extensions::Extension;
     34 
     35 PageActionImageView::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 
     72 PageActionImageView::~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 
     85 void 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 
    114 void PageActionImageView::GetAccessibleState(ui::AXViewState* state) {
    115   state->role = ui::AX_ROLE_BUTTON;
    116   state->name = base::UTF8ToUTF16(tooltip_);
    117 }
    118 
    119 bool 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 
    126 void 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 
    139 bool 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 
    148 void 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 
    174 bool 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 
    182 bool 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 
    188 void 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 
    214 void PageActionImageView::InspectPopup(ExtensionAction* action) {
    215   ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT);
    216 }
    217 
    218 void PageActionImageView::OnWidgetDestroying(views::Widget* widget) {
    219   DCHECK_EQ(popup_->GetWidget(), widget);
    220   popup_->GetWidget()->RemoveObserver(this);
    221   popup_ = NULL;
    222 }
    223 
    224 void PageActionImageView::OnIconUpdated() {
    225   WebContents* web_contents = owner_->GetWebContents();
    226   if (web_contents)
    227     UpdateVisibility(web_contents, current_url_);
    228 }
    229 
    230 void 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 
    237 void 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 
    257 void PageActionImageView::HidePopup() {
    258   if (popup_)
    259     popup_->GetWidget()->Close();
    260 }
    261