blob: bd2d06b32bce88076c7e4d5a89c21d3bf630a993 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/api/commands/command_service.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_icon_factory.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/location_bar_controller.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/webui/extensions/extension_info_ui.h"
#include "chrome/common/extensions/extension.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
using content::WebContents;
using extensions::LocationBarController;
using extensions::Extension;
PageActionImageView::PageActionImageView(LocationBarView* owner,
ExtensionAction* page_action,
Browser* browser)
: owner_(owner),
page_action_(page_action),
browser_(browser),
current_tab_id_(-1),
preview_enabled_(false),
popup_(NULL),
scoped_icon_animation_observer_(
page_action->GetIconAnimation(
SessionID::IdForTab(owner->GetWebContents())),
this) {
const Extension* extension = owner_->profile()->GetExtensionService()->
GetExtensionById(page_action->extension_id(), false);
DCHECK(extension);
icon_factory_.reset(
new ExtensionActionIconFactory(
owner_->profile(), extension, page_action, this));
set_accessibility_focusable(true);
set_context_menu_controller(this);
extensions::CommandService* command_service =
extensions::CommandService::Get(browser_->profile());
extensions::Command page_action_command;
if (command_service->GetPageActionCommand(
extension->id(),
extensions::CommandService::ACTIVE_ONLY,
&page_action_command,
NULL)) {
page_action_keybinding_.reset(
new ui::Accelerator(page_action_command.accelerator()));
owner_->GetFocusManager()->RegisterAccelerator(
*page_action_keybinding_.get(),
ui::AcceleratorManager::kHighPriority,
this);
}
extensions::Command script_badge_command;
if (command_service->GetScriptBadgeCommand(
extension->id(),
extensions::CommandService::ACTIVE_ONLY,
&script_badge_command,
NULL)) {
script_badge_keybinding_.reset(
new ui::Accelerator(script_badge_command.accelerator()));
owner_->GetFocusManager()->RegisterAccelerator(
*script_badge_keybinding_.get(),
ui::AcceleratorManager::kHighPriority,
this);
}
}
PageActionImageView::~PageActionImageView() {
if (owner_->GetFocusManager()) {
if (page_action_keybinding_.get()) {
owner_->GetFocusManager()->UnregisterAccelerator(
*page_action_keybinding_.get(), this);
}
if (script_badge_keybinding_.get()) {
owner_->GetFocusManager()->UnregisterAccelerator(
*script_badge_keybinding_.get(), this);
}
}
if (popup_)
popup_->GetWidget()->RemoveObserver(this);
HidePopup();
}
void PageActionImageView::ExecuteAction(
ExtensionPopup::ShowAction show_action) {
WebContents* web_contents = owner_->GetWebContents();
if (!web_contents)
return;
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(web_contents);
LocationBarController* controller =
extensions_tab_helper->location_bar_controller();
switch (controller->OnClicked(page_action_->extension_id(), 1)) {
case LocationBarController::ACTION_NONE:
break;
case LocationBarController::ACTION_SHOW_POPUP:
ShowPopupWithURL(page_action_->GetPopupUrl(current_tab_id_), show_action);
break;
case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
// We are never passing OnClicked a right-click button, so assume that
// we're never going to be asked to show a context menu.
// TODO(kalman): if this changes, update this class to pass the real
// mouse button through to the LocationBarController.
NOTREACHED();
break;
case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
ShowPopupWithURL(
extensions::ExtensionInfoUI::GetURL(page_action_->extension_id()),
show_action);
break;
}
}
void PageActionImageView::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
state->name = UTF8ToUTF16(tooltip_);
}
bool PageActionImageView::OnMousePressed(const ui::MouseEvent& event) {
// We want to show the bubble on mouse release; that is the standard behavior
// for buttons. (Also, triggering on mouse press causes bugs like
// http://crbug.com/33155.)
return true;
}
void PageActionImageView::OnMouseReleased(const ui::MouseEvent& event) {
if (!HitTestPoint(event.location()))
return;
if (event.IsRightMouseButton()) {
// Don't show a menu here, its handled in View::ProcessMouseReleased. We
// show the context menu by way of being the ContextMenuController.
return;
}
ExecuteAction(ExtensionPopup::SHOW);
}
bool PageActionImageView::OnKeyPressed(const ui::KeyEvent& event) {
if (event.key_code() == ui::VKEY_SPACE ||
event.key_code() == ui::VKEY_RETURN) {
ExecuteAction(ExtensionPopup::SHOW);
return true;
}
return false;
}
void PageActionImageView::ShowContextMenuForView(
View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) {
const Extension* extension = owner_->profile()->GetExtensionService()->
GetExtensionById(page_action()->extension_id(), false);
if (!extension->ShowConfigureContextMenus())
return;
scoped_refptr<ExtensionContextMenuModel> context_menu_model(
new ExtensionContextMenuModel(extension, browser_, this));
menu_runner_.reset(new views::MenuRunner(context_menu_model.get()));
gfx::Point screen_loc;
views::View::ConvertPointToScreen(this, &screen_loc);
if (menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()),
views::MenuItemView::TOPLEFT, source_type,
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
views::MenuRunner::MENU_DELETED)
return;
}
bool PageActionImageView::AcceleratorPressed(
const ui::Accelerator& accelerator) {
DCHECK(visible()); // Should not have happened due to CanHandleAccelerator.
ExecuteAction(ExtensionPopup::SHOW);
return true;
}
bool PageActionImageView::CanHandleAccelerators() const {
// While visible, we don't handle accelerators and while so we also don't
// count as a priority accelerator handler.
return visible();
}
void PageActionImageView::UpdateVisibility(WebContents* contents,
const GURL& url) {
// Save this off so we can pass it back to the extension when the action gets
// executed. See PageActionImageView::OnMousePressed.
current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1;
current_url_ = url;
if (!contents ||
(!preview_enabled_ && !page_action_->GetIsVisible(current_tab_id_))) {
SetVisible(false);
return;
}
// Set the tooltip.
tooltip_ = page_action_->GetTitle(current_tab_id_);
SetTooltipText(UTF8ToUTF16(tooltip_));
// Set the image.
gfx::Image icon = icon_factory_->GetIcon(current_tab_id_);
if (!icon.IsEmpty())
SetImage(*icon.ToImageSkia());
SetVisible(true);
}
void PageActionImageView::InspectPopup(ExtensionAction* action) {
ExecuteAction(ExtensionPopup::SHOW_AND_INSPECT);
}
void PageActionImageView::OnWidgetDestroying(views::Widget* widget) {
DCHECK_EQ(popup_->GetWidget(), widget);
popup_->GetWidget()->RemoveObserver(this);
popup_ = NULL;
}
void PageActionImageView::OnIconUpdated() {
WebContents* web_contents = owner_->GetWebContents();
if (web_contents)
UpdateVisibility(web_contents, current_url_);
}
void PageActionImageView::OnIconChanged() {
OnIconUpdated();
}
void PageActionImageView::PaintChildren(gfx::Canvas* canvas) {
View::PaintChildren(canvas);
if (current_tab_id_ >= 0)
page_action_->PaintBadge(canvas, GetLocalBounds(), current_tab_id_);
}
void PageActionImageView::ShowPopupWithURL(
const GURL& popup_url,
ExtensionPopup::ShowAction show_action) {
bool popup_showing = popup_ != NULL;
// Always hide the current popup. Only one popup at a time.
HidePopup();
// If we were already showing, then treat this click as a dismiss.
if (popup_showing)
return;
views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ?
views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT;
popup_ = ExtensionPopup::ShowPopup(popup_url, browser_, this, arrow,
show_action);
popup_->GetWidget()->AddObserver(this);
}
void PageActionImageView::HidePopup() {
if (popup_)
popup_->GetWidget()->Close();
}