Home | History | Annotate | Download | only in location_bar
      1 // Copyright (c) 2011 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/utf_string_conversions.h"
      8 #include "chrome/browser/extensions/extension_browser_event_router.h"
      9 #include "chrome/browser/extensions/extension_service.h"
     10 #include "chrome/browser/platform_util.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/ui/browser_list.h"
     13 #include "chrome/browser/ui/views/frame/browser_view.h"
     14 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
     15 #include "chrome/common/extensions/extension_action.h"
     16 #include "chrome/common/extensions/extension_resource.h"
     17 #include "ui/base/accessibility/accessible_view_state.h"
     18 #include "views/controls/menu/menu_2.h"
     19 
     20 PageActionImageView::PageActionImageView(LocationBarView* owner,
     21                                          Profile* profile,
     22                                          ExtensionAction* page_action)
     23     : owner_(owner),
     24       profile_(profile),
     25       page_action_(page_action),
     26       ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)),
     27       current_tab_id_(-1),
     28       preview_enabled_(false),
     29       popup_(NULL) {
     30   const Extension* extension = profile->GetExtensionService()->
     31       GetExtensionById(page_action->extension_id(), false);
     32   DCHECK(extension);
     33 
     34   // Load all the icons declared in the manifest. This is the contents of the
     35   // icons array, plus the default_icon property, if any.
     36   std::vector<std::string> icon_paths(*page_action->icon_paths());
     37   if (!page_action_->default_icon_path().empty())
     38     icon_paths.push_back(page_action_->default_icon_path());
     39 
     40   for (std::vector<std::string>::iterator iter = icon_paths.begin();
     41        iter != icon_paths.end(); ++iter) {
     42     tracker_.LoadImage(extension, extension->GetResource(*iter),
     43                        gfx::Size(Extension::kPageActionIconMaxSize,
     44                                  Extension::kPageActionIconMaxSize),
     45                        ImageLoadingTracker::DONT_CACHE);
     46   }
     47 
     48   set_accessibility_focusable(true);
     49 }
     50 
     51 PageActionImageView::~PageActionImageView() {
     52   if (popup_)
     53     HidePopup();
     54 }
     55 
     56 void PageActionImageView::ExecuteAction(int button,
     57                                         bool inspect_with_devtools) {
     58   if (current_tab_id_ < 0) {
     59     NOTREACHED() << "No current tab.";
     60     return;
     61   }
     62 
     63   if (page_action_->HasPopup(current_tab_id_)) {
     64     // In tests, GetLastActive could return NULL, so we need to have
     65     // a fallback.
     66     // TODO(erikkay): Find a better way to get the Browser that this
     67     // button is in.
     68     Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
     69     if (!browser)
     70       browser = BrowserList::FindBrowserWithProfile(profile_);
     71     DCHECK(browser);
     72 
     73     bool popup_showing = popup_ != NULL;
     74 
     75     // Always hide the current popup. Only one popup at a time.
     76     HidePopup();
     77 
     78     // If we were already showing, then treat this click as a dismiss.
     79     if (popup_showing)
     80       return;
     81 
     82     gfx::Rect screen_bounds(GetImageBounds());
     83     gfx::Point origin(screen_bounds.origin());
     84     View::ConvertPointToScreen(this, &origin);
     85     screen_bounds.set_origin(origin);
     86 
     87     BubbleBorder::ArrowLocation arrow_location = base::i18n::IsRTL() ?
     88         BubbleBorder::TOP_LEFT : BubbleBorder::TOP_RIGHT;
     89 
     90     popup_ = ExtensionPopup::Show(
     91         page_action_->GetPopupUrl(current_tab_id_),
     92         browser,
     93         screen_bounds,
     94         arrow_location,
     95         inspect_with_devtools,
     96         this);  // ExtensionPopup::Observer
     97   } else {
     98     ExtensionService* service = profile_->GetExtensionService();
     99     service->browser_event_router()->PageActionExecuted(
    100         profile_, page_action_->extension_id(), page_action_->id(),
    101         current_tab_id_, current_url_.spec(), button);
    102   }
    103 }
    104 
    105 void PageActionImageView::GetAccessibleState(ui::AccessibleViewState* state) {
    106   state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
    107 }
    108 
    109 bool PageActionImageView::OnMousePressed(const views::MouseEvent& event) {
    110   // We want to show the bubble on mouse release; that is the standard behavior
    111   // for buttons.  (Also, triggering on mouse press causes bugs like
    112   // http://crbug.com/33155.)
    113   return true;
    114 }
    115 
    116 void PageActionImageView::OnMouseReleased(const views::MouseEvent& event) {
    117   if (!HitTest(event.location()))
    118     return;
    119 
    120   int button = -1;
    121   if (event.IsLeftMouseButton()) {
    122     button = 1;
    123   } else if (event.IsMiddleMouseButton()) {
    124     button = 2;
    125   } else if (event.IsRightMouseButton()) {
    126     // Get the top left point of this button in screen coordinates.
    127     gfx::Point menu_origin;
    128     ConvertPointToScreen(this, &menu_origin);
    129     // Make the menu appear below the button.
    130     menu_origin.Offset(0, height());
    131     ShowContextMenu(menu_origin, true);
    132     return;
    133   }
    134 
    135   ExecuteAction(button, false);  // inspect_with_devtools
    136 }
    137 
    138 bool PageActionImageView::OnKeyPressed(const views::KeyEvent& event) {
    139   if (event.key_code() == ui::VKEY_SPACE ||
    140       event.key_code() == ui::VKEY_RETURN) {
    141     ExecuteAction(1, false);
    142     return true;
    143   }
    144   return false;
    145 }
    146 
    147 void PageActionImageView::ShowContextMenu(const gfx::Point& p,
    148                                           bool is_mouse_gesture) {
    149   const Extension* extension = profile_->GetExtensionService()->
    150       GetExtensionById(page_action()->extension_id(), false);
    151   if (!extension->ShowConfigureContextMenus())
    152     return;
    153 
    154   Browser* browser = BrowserView::GetBrowserViewForNativeWindow(
    155       platform_util::GetTopLevel(GetWidget()->GetNativeView()))->browser();
    156   context_menu_contents_ =
    157       new ExtensionContextMenuModel(extension, browser, this);
    158   context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get()));
    159   context_menu_menu_->RunContextMenuAt(p);
    160 }
    161 
    162 void PageActionImageView::OnImageLoaded(
    163     SkBitmap* image, const ExtensionResource& resource, int index) {
    164   // We loaded icons()->size() icons, plus one extra if the page action had
    165   // a default icon.
    166   int total_icons = static_cast<int>(page_action_->icon_paths()->size());
    167   if (!page_action_->default_icon_path().empty())
    168     total_icons++;
    169   DCHECK(index < total_icons);
    170 
    171   // Map the index of the loaded image back to its name. If we ever get an
    172   // index greater than the number of icons, it must be the default icon.
    173   if (image) {
    174     if (index < static_cast<int>(page_action_->icon_paths()->size()))
    175       page_action_icons_[page_action_->icon_paths()->at(index)] = *image;
    176     else
    177       page_action_icons_[page_action_->default_icon_path()] = *image;
    178   }
    179 
    180   // During object construction (before the parent has been set) we are already
    181   // in a UpdatePageActions call, so we don't need to start another one (and
    182   // doing so causes crash described in http://crbug.com/57333).
    183   if (parent())
    184     owner_->UpdatePageActions();
    185 }
    186 
    187 void PageActionImageView::UpdateVisibility(TabContents* contents,
    188                                            const GURL& url) {
    189   // Save this off so we can pass it back to the extension when the action gets
    190   // executed. See PageActionImageView::OnMousePressed.
    191   current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1;
    192   current_url_ = url;
    193 
    194   if (!contents ||
    195       (!preview_enabled_ && !page_action_->GetIsVisible(current_tab_id_))) {
    196     SetVisible(false);
    197     return;
    198   }
    199 
    200   // Set the tooltip.
    201   tooltip_ = page_action_->GetTitle(current_tab_id_);
    202   SetTooltipText(UTF8ToWide(tooltip_));
    203 
    204   // Set the image.
    205   // It can come from three places. In descending order of priority:
    206   // - The developer can set it dynamically by path or bitmap. It will be in
    207   //   page_action_->GetIcon().
    208   // - The developer can set it dynamically by index. It will be in
    209   //   page_action_->GetIconIndex().
    210   // - It can be set in the manifest by path. It will be in
    211   //   page_action_->default_icon_path().
    212 
    213   // First look for a dynamically set bitmap.
    214   SkBitmap icon = page_action_->GetIcon(current_tab_id_);
    215   if (icon.isNull()) {
    216     int icon_index = page_action_->GetIconIndex(current_tab_id_);
    217     std::string icon_path = (icon_index < 0) ?
    218         page_action_->default_icon_path() :
    219         page_action_->icon_paths()->at(icon_index);
    220     if (!icon_path.empty()) {
    221       PageActionMap::iterator iter = page_action_icons_.find(icon_path);
    222       if (iter != page_action_icons_.end())
    223         icon = iter->second;
    224     }
    225   }
    226   if (!icon.isNull())
    227     SetImage(&icon);
    228 
    229   SetVisible(true);
    230 }
    231 
    232 void PageActionImageView::InspectPopup(ExtensionAction* action) {
    233   ExecuteAction(1,  // left-click
    234                 true);  // inspect_with_devtools
    235 }
    236 
    237 void PageActionImageView::ExtensionPopupIsClosing(ExtensionPopup* popup) {
    238   DCHECK_EQ(popup_, popup);
    239   // ExtensionPopup is ref-counted, so we don't need to delete it.
    240   popup_ = NULL;
    241 }
    242 
    243 void PageActionImageView::HidePopup() {
    244   if (popup_)
    245     popup_->Close();
    246 }
    247