blob: 95693c253420b9305e64a2a2d847f75150bc2440 [file] [log] [blame]
// Copyright 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/gtk/location_bar_view_gtk.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/accessibility/accessibility_events.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/content_settings/tab_specific_content_settings.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/extensions/api/commands/command_service.h"
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
#include "chrome/browser/extensions/extension_action.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/script_bubble_controller.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/browser_instant_controller.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
#include "chrome/browser/ui/content_settings/content_setting_image_model.h"
#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
#include "chrome/browser/ui/gtk/browser_window_gtk.h"
#include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h"
#include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h"
#include "chrome/browser/ui/gtk/first_run_bubble.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/nine_box.h"
#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
#include "chrome/browser/ui/gtk/rounded_window.h"
#include "chrome/browser/ui/gtk/script_bubble_gtk.h"
#include "chrome/browser/ui/gtk/view_id_util.h"
#include "chrome/browser/ui/gtk/zoom_bubble_gtk.h"
#include "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.h"
#include "chrome/browser/ui/omnibox/location_bar_util.h"
#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/extensions/extension_info_ui.h"
#include "chrome/browser/ui/zoom/zoom_controller.h"
#include "chrome/common/badge_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_manifest_constants.h"
#include "chrome/common/extensions/feature_switch.h"
#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/net_util.h"
#include "ui/base/accelerators/platform_accelerator_gtk.h"
#include "ui/base/dragdrop/gtk_dnd_util.h"
#include "ui/base/gtk/gtk_hig_constants.h"
#include "ui/base/gtk/gtk_signal_registrar.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/canvas_skia_paint.h"
#include "ui/gfx/font.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/image.h"
using content::NavigationEntry;
using content::OpenURLParams;
using content::WebContents;
using extensions::LocationBarController;
using extensions::Extension;
namespace {
// We are positioned with a little bit of extra space that we don't use now.
const int kTopMargin = 1;
const int kBottomMargin = 1;
// We draw a border on the top and bottom (but not on left or right).
const int kBorderThickness = 1;
const int kPopupEdgeThickness = 1;
const int kNormalEdgeThickness = 2;
// Spacing needed to align the bubble with the left side of the omnibox.
const int kFirstRunBubbleLeftSpacing = 4;
// The padding around the top, bottom, and sides of the location bar hbox.
// We don't want to edit control's text to be right against the edge,
// as well the tab to search box and other widgets need to have the padding on
// top and bottom to avoid drawing larger than the location bar space.
const int kHboxBorder = 2;
// Padding between the elements in the bar.
const int kInnerPadding = 2;
const int kScriptBadgeInnerPadding = 9;
// Colors used to draw the EV certificate rounded bubble.
const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
// Colors used to draw the Tab to Search rounded bubble.
const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
// Use weak gray for showing search and keyword hint text.
const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
// Size of the rounding of the "Search site for:" box.
const int kCornerSize = 3;
// Default page tool animation time (open and close). In ms.
const int kPageToolAnimationTime = 150;
// The time, in ms, that the content setting label is fully displayed, for the
// cases where we animate it into and out of view.
const int kContentSettingImageDisplayTime = 3200;
// The time, in ms, of the animation (open and close).
const int kContentSettingImageAnimationTime = 150;
// Color of border of content setting area (icon/label).
const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
// Colors for the background gradient.
const GdkColor kContentSettingTopColor = GDK_COLOR_RGB(0xff, 0xf8, 0xd4);
const GdkColor kContentSettingBottomColor = GDK_COLOR_RGB(0xff, 0xe6, 0xaf);
// Styling for gray button.
const GdkColor kGrayBorderColor = GDK_COLOR_RGB(0xa0, 0xa0, 0xa0);
const GdkColor kTopColorGray = GDK_COLOR_RGB(0xe5, 0xe5, 0xe5);
const GdkColor kBottomColorGray = GDK_COLOR_RGB(0xd0, 0xd0, 0xd0);
inline int InnerPadding() {
return extensions::FeatureSwitch::script_badges()->IsEnabled() ?
kScriptBadgeInnerPadding : kInnerPadding;
}
// If widget is visible, increment the int pointed to by count.
// Suitible for use with gtk_container_foreach.
void CountVisibleWidgets(GtkWidget* widget, gpointer count) {
if (gtk_widget_get_visible(widget))
*static_cast<int*>(count) += 1;
}
class ContentSettingImageViewGtk : public LocationBarViewGtk::PageToolViewGtk,
public BubbleDelegateGtk {
public:
ContentSettingImageViewGtk(ContentSettingsType content_type,
const LocationBarViewGtk* parent);
virtual ~ContentSettingImageViewGtk();
// PageToolViewGtk
virtual void Update(WebContents* web_contents) OVERRIDE;
// ui::AnimationDelegate
virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
private:
// PageToolViewGtk
virtual GdkColor button_border_color() const OVERRIDE;
virtual GdkColor gradient_top_color() const OVERRIDE;
virtual GdkColor gradient_bottom_color() const OVERRIDE;
virtual void OnClick(GtkWidget* sender) OVERRIDE;
// BubbleDelegateGtk
virtual void BubbleClosing(BubbleGtk* bubble,
bool closed_by_escape) OVERRIDE;
scoped_ptr<ContentSettingImageModel> content_setting_image_model_;
// The currently shown bubble if any.
ContentSettingBubbleGtk* content_setting_bubble_;
DISALLOW_COPY_AND_ASSIGN(ContentSettingImageViewGtk);
};
ContentSettingImageViewGtk::ContentSettingImageViewGtk(
ContentSettingsType content_type,
const LocationBarViewGtk* parent)
: PageToolViewGtk(parent),
content_setting_image_model_(
ContentSettingImageModel::CreateContentSettingImageModel(
content_type)),
content_setting_bubble_(NULL) {
animation_.SetSlideDuration(kContentSettingImageAnimationTime);
}
ContentSettingImageViewGtk::~ContentSettingImageViewGtk() {
if (content_setting_bubble_)
content_setting_bubble_->Close();
}
void ContentSettingImageViewGtk::Update(WebContents* web_contents) {
if (web_contents)
content_setting_image_model_->UpdateFromWebContents(web_contents);
if (!content_setting_image_model_->is_visible()) {
gtk_widget_hide(widget());
return;
}
gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()),
GtkThemeService::GetFrom(parent_->browser()->profile())->GetImageNamed(
content_setting_image_model_->get_icon()).ToGdkPixbuf());
gtk_widget_set_tooltip_text(widget(),
content_setting_image_model_->get_tooltip().c_str());
gtk_widget_show_all(widget());
if (!web_contents)
return;
TabSpecificContentSettings* content_settings =
TabSpecificContentSettings::FromWebContents(web_contents);
if (!content_settings || content_settings->IsBlockageIndicated(
content_setting_image_model_->get_content_settings_type()))
return;
// The content blockage was not yet indicated to the user. Start indication
// animation and clear "not yet shown" flag.
content_settings->SetBlockageHasBeenIndicated(
content_setting_image_model_->get_content_settings_type());
int label_string_id =
content_setting_image_model_->explanatory_string_id();
// If there's no string for the content type, we don't animate.
if (!label_string_id)
return;
gtk_label_set_text(GTK_LABEL(label_.get()),
l10n_util::GetStringUTF8(label_string_id).c_str());
StartAnimating();
}
void ContentSettingImageViewGtk::AnimationEnded(
const ui::Animation* animation) {
if (animation_.IsShowing()) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&ContentSettingImageViewGtk::CloseAnimation,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kContentSettingImageDisplayTime));
} else {
gtk_widget_hide(label_.get());
gtk_util::StopActingAsRoundedWindow(event_box_.get());
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
}
}
GdkColor ContentSettingImageViewGtk::
button_border_color() const {
return kContentSettingBorderColor;
}
GdkColor ContentSettingImageViewGtk::
gradient_top_color() const {
return kContentSettingTopColor;
}
GdkColor ContentSettingImageViewGtk::
gradient_bottom_color() const {
return kContentSettingBottomColor;
}
void ContentSettingImageViewGtk::OnClick(
GtkWidget* sender) {
WebContents* web_contents = parent_->GetWebContents();
if (!web_contents)
return;
Profile* profile = parent_->browser()->profile();
content_setting_bubble_ = new ContentSettingBubbleGtk(
sender, this,
ContentSettingBubbleModel::CreateContentSettingBubbleModel(
parent_->browser()->content_setting_bubble_model_delegate(),
web_contents,
profile,
content_setting_image_model_->get_content_settings_type()),
profile, web_contents);
return;
}
void ContentSettingImageViewGtk::BubbleClosing(
BubbleGtk* bubble,
bool closed_by_escape) {
content_setting_bubble_ = NULL;
}
gfx::Rect AllocationToRect(const GtkAllocation& allocation) {
return gfx::Rect(allocation.x, allocation.y,
allocation.width, allocation.height);
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// LocationBarViewGtk
// static
const GdkColor LocationBarViewGtk::kBackgroundColor =
GDK_COLOR_RGB(255, 255, 255);
LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
: zoom_image_(NULL),
script_bubble_button_image_(NULL),
num_running_scripts_(0u),
star_image_(NULL),
starred_(false),
star_sized_(false),
site_type_alignment_(NULL),
site_type_event_box_(NULL),
location_icon_image_(NULL),
drag_icon_(NULL),
enable_location_drag_(false),
security_info_label_(NULL),
tab_to_search_alignment_(NULL),
tab_to_search_box_(NULL),
tab_to_search_full_label_(NULL),
tab_to_search_partial_label_(NULL),
tab_to_search_hint_(NULL),
tab_to_search_hint_leading_label_(NULL),
tab_to_search_hint_icon_(NULL),
tab_to_search_hint_trailing_label_(NULL),
command_updater_(browser->command_controller()->command_updater()),
toolbar_model_(browser->toolbar_model()),
browser_(browser),
disposition_(CURRENT_TAB),
transition_(content::PageTransitionFromInt(
content::PAGE_TRANSITION_TYPED |
content::PAGE_TRANSITION_FROM_ADDRESS_BAR)),
weak_ptr_factory_(this),
popup_window_mode_(false),
theme_service_(NULL),
hbox_width_(0),
entry_box_width_(0),
show_selected_keyword_(false),
show_keyword_hint_(false) {
}
LocationBarViewGtk::~LocationBarViewGtk() {
// All of our widgets should be children of / owned by the alignment.
zoom_.Destroy();
script_bubble_button_.Destroy();
star_.Destroy();
hbox_.Destroy();
content_setting_hbox_.Destroy();
page_action_hbox_.Destroy();
}
void LocationBarViewGtk::Init(bool popup_window_mode) {
popup_window_mode_ = popup_window_mode;
Profile* profile = browser_->profile();
theme_service_ = GtkThemeService::GetFrom(profile);
// Create the widget first, so we can pass it to the OmniboxViewGtk.
hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
// We will paint for the alignment, to paint the background and border.
gtk_widget_set_app_paintable(hbox_.get(), TRUE);
// Redraw the whole location bar when it changes size (e.g., when toggling
// the home button on/off.
gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
// Now initialize the OmniboxViewGtk.
location_entry_.reset(new OmniboxViewGtk(this, toolbar_model_, browser_,
browser_->profile(), command_updater_, popup_window_mode_, hbox_.get()));
location_entry_->Init();
g_signal_connect(hbox_.get(), "expose-event",
G_CALLBACK(&HandleExposeThunk), this);
BuildSiteTypeArea();
// Put |tab_to_search_box_|, |location_entry_|, and |tab_to_search_hint_| into
// a sub hbox, so that we can make this part horizontally shrinkable without
// affecting other elements in the location bar.
entry_box_ = gtk_hbox_new(FALSE, InnerPadding());
gtk_widget_show(entry_box_);
gtk_widget_set_size_request(entry_box_, 0, -1);
gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0);
// We need to adjust the visibility of the search hint widgets according to
// the horizontal space in the |entry_box_|.
g_signal_connect(entry_box_, "size-allocate",
G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this);
// Tab to search (the keyword box on the left hand side).
tab_to_search_full_label_ =
theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
tab_to_search_partial_label_ =
theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
tab_to_search_full_label_, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
tab_to_search_partial_label_, FALSE, FALSE, 0);
GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0);
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
tab_to_search_magnifier_ = gtk_image_new_from_pixbuf(
rb.GetNativeImageNamed(IDR_KEYWORD_SEARCH_MAGNIFIER).ToGdkPixbuf());
gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_,
FALSE, FALSE, 0);
gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox,
false, 0);
// This creates a box around the keyword text with a border, background color,
// and padding around the text.
tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
tab_to_search_hbox, NULL, 1, 1, 1, 3);
gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box");
gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor,
kCornerSize,
gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
// Put the event box in an alignment to get the padding correct.
tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1);
gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_),
tab_to_search_box_);
gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_,
FALSE, FALSE, 0);
// Show all children widgets of |tab_to_search_box_| initially, except
// |tab_to_search_partial_label_|.
gtk_widget_show_all(tab_to_search_box_);
gtk_widget_hide(tab_to_search_partial_label_);
location_entry_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
gtk_container_add(GTK_CONTAINER(location_entry_alignment_),
location_entry_->GetNativeView());
gtk_box_pack_start(GTK_BOX(entry_box_), location_entry_alignment_,
TRUE, TRUE, 0);
// Tab to search notification (the hint on the right hand side).
tab_to_search_hint_ = gtk_hbox_new(FALSE, 0);
gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint");
tab_to_search_hint_leading_label_ =
theme_service_->BuildLabel(std::string(), kHintTextColor);
gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE);
tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf(
rb.GetNativeImageNamed(IDR_OMNIBOX_KEYWORD_HINT_TAB).ToGdkPixbuf());
tab_to_search_hint_trailing_label_ =
theme_service_->BuildLabel(std::string(), kHintTextColor);
gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE);
gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
tab_to_search_hint_leading_label_,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
tab_to_search_hint_icon_,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
tab_to_search_hint_trailing_label_,
FALSE, FALSE, 0);
// Show all children widgets of |tab_to_search_hint_| initially.
gtk_widget_show_all(tab_to_search_hint_);
gtk_widget_hide(tab_to_search_hint_);
// tab_to_search_hint_ gets hidden initially in OnChanged. Hiding it here
// doesn't work, someone is probably calling show_all on our parent box.
gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
// Hide the star icon in popups, app windows, etc.
CreateStarButton();
gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
}
CreateScriptBubbleButton();
gtk_box_pack_end(GTK_BOX(hbox_.get()), script_bubble_button_.get(), FALSE,
FALSE, 0);
CreateZoomButton();
gtk_box_pack_end(GTK_BOX(hbox_.get()), zoom_.get(), FALSE, FALSE, 0);
content_setting_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding() + 1));
gtk_widget_set_name(content_setting_hbox_.get(),
"chrome-content-setting-hbox");
gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(),
FALSE, FALSE, 1);
for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
ContentSettingImageViewGtk* content_setting_view =
new ContentSettingImageViewGtk(
static_cast<ContentSettingsType>(i), this);
content_setting_views_.push_back(content_setting_view);
gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()),
content_setting_view->widget(), FALSE, FALSE, 0);
}
page_action_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
gtk_widget_set_name(page_action_hbox_.get(),
"chrome-page-action-hbox");
gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(),
FALSE, FALSE, 0);
// Now that we've created the widget hierarchy, connect to the main |hbox_|'s
// size-allocate so we can do proper resizing and eliding on
// |security_info_label_|.
g_signal_connect(hbox_.get(), "size-allocate",
G_CALLBACK(&OnHboxSizeAllocateThunk), this);
registrar_.Add(this,
chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
content::Source<ThemeService>(theme_service_));
registrar_.Add(this,
chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
content::Source<Profile>(browser()->profile()));
edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
profile->GetPrefs(),
base::Bind(&LocationBarViewGtk::UpdateStarIcon,
base::Unretained(this)));
theme_service_->InitThemesFor(this);
}
void LocationBarViewGtk::BuildSiteTypeArea() {
location_icon_image_ = gtk_image_new();
gtk_widget_set_name(location_icon_image_, "chrome-location-icon");
GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1);
gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0);
gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_);
gtk_widget_show_all(icon_alignment);
security_info_label_ = gtk_label_new(NULL);
gtk_label_set_ellipsize(GTK_LABEL(security_info_label_),
PANGO_ELLIPSIZE_MIDDLE);
gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL,
&kEvSecureTextColor);
gtk_widget_set_name(security_info_label_,
"chrome-location-bar-security-info-label");
GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1);
gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_,
FALSE, FALSE, 2);
site_type_event_box_ = gtk_event_box_new();
gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL,
&kEvSecureBackgroundColor);
g_signal_connect(site_type_event_box_, "drag-data-get",
G_CALLBACK(&OnIconDragDataThunk), this);
g_signal_connect(site_type_event_box_, "drag-begin",
G_CALLBACK(&OnIconDragBeginThunk), this);
g_signal_connect(site_type_event_box_, "drag-end",
G_CALLBACK(&OnIconDragEndThunk), this);
// Make the event box not visible so it does not paint a background.
gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
FALSE);
gtk_widget_set_name(site_type_event_box_,
"chrome-location-icon-eventbox");
gtk_container_add(GTK_CONTAINER(site_type_event_box_),
site_type_hbox);
// Put the event box in an alignment to get the padding correct.
site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1);
gtk_container_add(GTK_CONTAINER(site_type_alignment_),
site_type_event_box_);
gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_,
FALSE, FALSE, 0);
gtk_widget_set_tooltip_text(location_icon_image_,
l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
g_signal_connect(site_type_event_box_, "button-release-event",
G_CALLBACK(&OnIconReleasedThunk), this);
}
void LocationBarViewGtk::SetSiteTypeDragSource() {
bool enable = !GetLocationEntry()->IsEditingOrEmpty();
if (enable_location_drag_ == enable)
return;
enable_location_drag_ = enable;
if (!enable) {
gtk_drag_source_unset(site_type_event_box_);
return;
}
gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK,
NULL, 0, GDK_ACTION_COPY);
ui::SetSourceTargetListFromCodeMask(site_type_event_box_,
ui::TEXT_PLAIN |
ui::TEXT_URI_LIST |
ui::CHROME_NAMED_URL);
}
WebContents* LocationBarViewGtk::GetWebContents() const {
return browser_->tab_strip_model()->GetActiveWebContents();
}
void LocationBarViewGtk::SetPreviewEnabledPageAction(
ExtensionAction* page_action,
bool preview_enabled) {
DCHECK(page_action);
for (ScopedVector<PageActionViewGtk>::iterator iter =
page_action_views_.begin(); iter != page_action_views_.end();
++iter) {
if ((*iter)->page_action() == page_action) {
(*iter)->set_preview_enabled(preview_enabled);
UpdatePageActions();
return;
}
}
}
GtkWidget* LocationBarViewGtk::GetPageActionWidget(
ExtensionAction* page_action) {
DCHECK(page_action);
for (ScopedVector<PageActionViewGtk>::iterator iter =
page_action_views_.begin();
iter != page_action_views_.end();
++iter) {
if ((*iter)->page_action() == page_action)
return (*iter)->widget();
}
return NULL;
}
void LocationBarViewGtk::Update(const WebContents* contents) {
UpdateZoomIcon();
UpdateScriptBubbleIcon();
UpdateStarIcon();
UpdateSiteTypeArea();
UpdateContentSettingsIcons();
UpdatePageActions();
location_entry_->Update(contents);
// The security level (background color) could have changed, etc.
if (theme_service_->UsingNativeTheme()) {
// In GTK mode, we need our parent to redraw, as it draws the text entry
// border.
gtk_widget_queue_draw(gtk_widget_get_parent(widget()));
} else {
gtk_widget_queue_draw(widget());
}
ZoomBubbleGtk::CloseBubble();
}
void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url,
WindowOpenDisposition disposition,
content::PageTransition transition,
const GURL& alternate_nav_url) {
if (url.is_valid()) {
location_input_ = UTF8ToUTF16(url.spec());
disposition_ = disposition;
transition_ = content::PageTransitionFromInt(
transition | content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
if (command_updater_) {
if (!alternate_nav_url.is_valid()) {
command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
} else {
AlternateNavURLFetcher* fetcher =
new AlternateNavURLFetcher(alternate_nav_url);
// The AlternateNavURLFetcher will listen for the pending navigation
// notification that will be issued as a result of the "open URL." It
// will automatically install itself into that navigation controller.
command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
// I'm not sure this should be reachable, but I'm not also sure enough
// that it shouldn't to stick in a NOTREACHED(). In any case, this is
// harmless.
delete fetcher;
} else {
// The navigation controller will delete the fetcher.
}
}
}
}
}
void LocationBarViewGtk::OnChanged() {
UpdateSiteTypeArea();
const string16 keyword(location_entry_->model()->keyword());
const bool is_keyword_hint = location_entry_->model()->is_keyword_hint();
show_selected_keyword_ = !keyword.empty() && !is_keyword_hint;
show_keyword_hint_ = !keyword.empty() && is_keyword_hint;
if (show_selected_keyword_)
SetKeywordLabel(keyword);
if (show_keyword_hint_)
SetKeywordHintLabel(keyword);
AdjustChildrenVisibility();
}
void LocationBarViewGtk::OnSelectionBoundsChanged() {
NOTIMPLEMENTED();
}
GtkWidget* LocationBarViewGtk::CreateIconButton(
GtkWidget** image,
int image_id,
ViewID debug_id,
int tooltip_id,
gboolean (click_callback)(GtkWidget*, GdkEventButton*, gpointer)) {
*image = image_id ?
gtk_image_new_from_pixbuf(
theme_service_->GetImageNamed(image_id).ToGdkPixbuf()) :
gtk_image_new();
GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
0, InnerPadding());
gtk_container_add(GTK_CONTAINER(alignment), *image);
GtkWidget* result = gtk_event_box_new();
gtk_event_box_set_visible_window(GTK_EVENT_BOX(result), FALSE);
gtk_container_add(GTK_CONTAINER(result), alignment);
gtk_widget_show_all(result);
if (debug_id != VIEW_ID_NONE)
ViewIDUtil::SetID(result, debug_id);
if (tooltip_id) {
gtk_widget_set_tooltip_text(result,
l10n_util::GetStringUTF8(tooltip_id).c_str());
}
g_signal_connect(result, "button-press-event",
G_CALLBACK(click_callback), this);
return result;
}
void LocationBarViewGtk::CreateZoomButton() {
zoom_.Own(CreateIconButton(&zoom_image_,
0,
VIEW_ID_ZOOM_BUTTON,
0,
OnZoomButtonPressThunk));
}
void LocationBarViewGtk::CreateScriptBubbleButton() {
script_bubble_button_.Own(CreateIconButton(&script_bubble_button_image_,
0,
VIEW_ID_SCRIPT_BUBBLE,
IDS_TOOLTIP_SCRIPT_BUBBLE,
OnScriptBubbleButtonPressThunk));
gtk_image_set_from_pixbuf(
GTK_IMAGE(script_bubble_button_image_),
theme_service_->GetImageNamed(
IDR_EXTENSIONS_SCRIPT_BUBBLE).ToGdkPixbuf());
g_signal_connect_after(script_bubble_button_image_, "expose-event",
G_CALLBACK(&OnScriptBubbleButtonExposeThunk), this);
}
void LocationBarViewGtk::CreateStarButton() {
star_.Own(CreateIconButton(&star_image_,
0,
VIEW_ID_STAR_BUTTON,
IDS_TOOLTIP_STAR,
OnStarButtonPressThunk));
// We need to track when the star button is resized to show any bubble
// attached to it at this time.
g_signal_connect(star_image_, "size-allocate",
G_CALLBACK(&OnStarButtonSizeAllocateThunk), this);
}
void LocationBarViewGtk::OnInputInProgress(bool in_progress) {
// This is identical to the Windows code, except that we don't proxy the call
// back through the Toolbar, and just access the model here.
// The edit should make sure we're only notified when something changes.
DCHECK(toolbar_model_->GetInputInProgress() != in_progress);
toolbar_model_->SetInputInProgress(in_progress);
Update(NULL);
}
void LocationBarViewGtk::OnKillFocus() {
}
void LocationBarViewGtk::OnSetFocus() {
Profile* profile = browser_->profile();
AccessibilityTextBoxInfo info(
profile,
l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION),
std::string(),
false);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_FOCUSED,
content::Source<Profile>(profile),
content::Details<AccessibilityTextBoxInfo>(&info));
// Update the keyword and search hint states.
OnChanged();
}
gfx::Image LocationBarViewGtk::GetFavicon() const {
return FaviconTabHelper::FromWebContents(GetWebContents())->GetFavicon();
}
string16 LocationBarViewGtk::GetTitle() const {
return GetWebContents()->GetTitle();
}
InstantController* LocationBarViewGtk::GetInstant() {
return browser_->instant_controller() ?
browser_->instant_controller()->instant() : NULL;
}
void LocationBarViewGtk::ShowFirstRunBubble() {
// We need the browser window to be shown before we can show the bubble, but
// we get called before that's happened.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&LocationBarViewGtk::ShowFirstRunBubbleInternal,
weak_ptr_factory_.GetWeakPtr()));
}
string16 LocationBarViewGtk::GetInputString() const {
return location_input_;
}
WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
return disposition_;
}
content::PageTransition LocationBarViewGtk::GetPageTransition() const {
return transition_;
}
void LocationBarViewGtk::AcceptInput() {
location_entry_->model()->AcceptInput(CURRENT_TAB, false);
}
void LocationBarViewGtk::FocusLocation(bool select_all) {
location_entry_->SetFocus();
if (select_all)
location_entry_->SelectAll(true);
}
void LocationBarViewGtk::FocusSearch() {
location_entry_->SetFocus();
location_entry_->SetForcedQuery();
}
void LocationBarViewGtk::UpdateContentSettingsIcons() {
bool any_visible = false;
for (ScopedVector<PageToolViewGtk>::iterator i(
content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
(*i)->Update(
toolbar_model_->GetInputInProgress() ? NULL : GetWebContents());
any_visible = (*i)->IsVisible() || any_visible;
}
// If there are no visible content things, hide the top level box so it
// doesn't mess with padding.
gtk_widget_set_visible(content_setting_hbox_.get(), any_visible);
}
void LocationBarViewGtk::UpdatePageActions() {
UpdateScriptBubbleIcon();
std::vector<ExtensionAction*> new_page_actions;
WebContents* contents = GetWebContents();
if (contents) {
LocationBarController* location_bar_controller =
extensions::TabHelper::FromWebContents(contents)->
location_bar_controller();
new_page_actions = location_bar_controller->GetCurrentActions();
}
// Initialize on the first call, or re-initialize if more extensions have been
// loaded or added after startup.
if (new_page_actions != page_actions_) {
page_actions_.swap(new_page_actions);
page_action_views_.clear();
for (size_t i = 0; i < page_actions_.size(); ++i) {
page_action_views_.push_back(
new PageActionViewGtk(this, page_actions_[i]));
gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()),
page_action_views_[i]->widget(), FALSE, FALSE, 0);
}
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
content::Source<LocationBar>(this),
content::NotificationService::NoDetails());
}
if (!page_action_views_.empty() && contents) {
GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
for (size_t i = 0; i < page_action_views_.size(); i++) {
page_action_views_[i]->UpdateVisibility(
toolbar_model_->GetInputInProgress() ? NULL : contents, url);
}
gtk_widget_queue_draw(hbox_.get());
}
// If there are no visible page actions, hide the hbox too, so that it does
// not affect the padding in the location bar.
gtk_widget_set_visible(page_action_hbox_.get(),
PageActionVisibleCount() && !ShouldOnlyShowLocation());
}
void LocationBarViewGtk::InvalidatePageActions() {
size_t count_before = page_action_views_.size();
page_action_views_.clear();
if (page_action_views_.size() != count_before) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
content::Source<LocationBar>(this),
content::NotificationService::NoDetails());
}
}
void LocationBarViewGtk::UpdateOpenPDFInReaderPrompt() {
// Not implemented on Gtk.
}
void LocationBarViewGtk::UpdateGeneratedCreditCardView() {
NOTIMPLEMENTED();
}
void LocationBarViewGtk::SaveStateToContents(WebContents* contents) {
location_entry_->SaveStateToTab(contents);
}
void LocationBarViewGtk::Revert() {
location_entry_->RevertAll();
}
const OmniboxView* LocationBarViewGtk::GetLocationEntry() const {
return location_entry_.get();
}
OmniboxView* LocationBarViewGtk::GetLocationEntry() {
return location_entry_.get();
}
LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
return this;
}
int LocationBarViewGtk::PageActionCount() {
return page_action_views_.size();
}
int LocationBarViewGtk::PageActionVisibleCount() {
int count = 0;
gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
CountVisibleWidgets, &count);
return count;
}
ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) {
if (index >= page_action_views_.size()) {
NOTREACHED();
return NULL;
}
return page_action_views_[index]->page_action();
}
ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) {
size_t visible_index = 0;
for (size_t i = 0; i < page_action_views_.size(); ++i) {
if (page_action_views_[i]->IsVisible()) {
if (index == visible_index++)
return page_action_views_[i]->page_action();
}
}
NOTREACHED();
return NULL;
}
void LocationBarViewGtk::TestPageActionPressed(size_t index) {
if (index >= page_action_views_.size()) {
NOTREACHED();
return;
}
page_action_views_[index]->TestActivatePageAction();
}
bool LocationBarViewGtk::GetBookmarkStarVisibility() {
return starred_;
}
void LocationBarViewGtk::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: {
// Only update if the updated action box was for the active tab contents.
WebContents* target_tab = content::Details<WebContents>(details).ptr();
if (target_tab == GetWebContents())
UpdatePageActions();
break;
}
case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
if (theme_service_->UsingNativeTheme()) {
gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL);
GdkColor border_color = theme_service_->GetGdkColor(
ThemeProperties::COLOR_FRAME);
gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
gtk_util::UndoForceFontSize(security_info_label_);
gtk_util::UndoForceFontSize(tab_to_search_full_label_);
gtk_util::UndoForceFontSize(tab_to_search_partial_label_);
gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_);
gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_);
gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
0, 0, 0, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
1, 1, 1, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
1, 1, 1, 0);
} else {
gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
&kKeywordBackgroundColor);
gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
kKeywordBorderColor);
// Until we switch to vector graphics, force the font size of labels.
// 12.1px = 9pt @ 96dpi
gtk_util::ForceFontSizePixels(security_info_label_, 12.1);
gtk_util::ForceFontSizePixels(tab_to_search_full_label_,
browser_defaults::kOmniboxFontPixelSize);
gtk_util::ForceFontSizePixels(tab_to_search_partial_label_,
browser_defaults::kOmniboxFontPixelSize);
gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_,
browser_defaults::kOmniboxFontPixelSize);
gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_,
browser_defaults::kOmniboxFontPixelSize);
const int top_bottom = popup_window_mode_ ? kPopupEdgeThickness : 0;
gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
kTopMargin + kBorderThickness,
kBottomMargin + kBorderThickness,
top_bottom, top_bottom);
gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
1, 1, 0, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
1, 1, 0, 0);
}
UpdateZoomIcon();
UpdateScriptBubbleIcon();
UpdateStarIcon();
UpdateSiteTypeArea();
UpdateContentSettingsIcons();
break;
}
default:
NOTREACHED();
}
}
gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
GdkEventExpose* event) {
// If we're not using GTK theming, draw our own border over the edge pixels
// of the background.
GtkThemeService* theme_service =
GtkThemeService::GetFrom(browser_->profile());
if (!theme_service->UsingNativeTheme()) {
// Perform a scoped paint to fill in the background color.
{
gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
int thickness = popup_window_mode_ ?
kPopupEdgeThickness : kNormalEdgeThickness;
gfx::Rect bounds(allocation);
bounds.Inset(thickness, thickness);
const SkColor color = SK_ColorWHITE;
if (popup_window_mode_) {
canvas.FillRect(bounds, color);
} else {
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(color);
const int kBorderCornerRadius = 2;
canvas.DrawRoundRect(bounds, kBorderCornerRadius, paint);
}
}
if (popup_window_mode_) {
NineBox(IDR_OMNIBOX_POPUP_BORDER_TOP_LEFT,
IDR_OMNIBOX_POPUP_BORDER_TOP,
IDR_OMNIBOX_POPUP_BORDER_TOP_RIGHT,
IDR_OMNIBOX_POPUP_BORDER_LEFT,
IDR_OMNIBOX_POPUP_BORDER_CENTER,
IDR_OMNIBOX_POPUP_BORDER_RIGHT,
IDR_OMNIBOX_POPUP_BORDER_BOTTOM_LEFT,
IDR_OMNIBOX_POPUP_BORDER_BOTTOM,
IDR_OMNIBOX_POPUP_BORDER_BOTTOM_RIGHT).RenderToWidget(widget);
} else {
NineBox(IDR_OMNIBOX_BORDER_TOP_LEFT,
IDR_OMNIBOX_BORDER_TOP,
IDR_OMNIBOX_BORDER_TOP_RIGHT,
IDR_OMNIBOX_BORDER_LEFT,
IDR_OMNIBOX_BORDER_CENTER,
IDR_OMNIBOX_BORDER_RIGHT,
IDR_OMNIBOX_BORDER_BOTTOM_LEFT,
IDR_OMNIBOX_BORDER_BOTTOM,
IDR_OMNIBOX_BORDER_BOTTOM_RIGHT).RenderToWidget(widget);
}
}
// Draw ExtensionAction backgrounds and borders, if necessary. The borders
// appear exactly between the elements, so they can't draw the borders
// themselves.
gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
for (ScopedVector<PageActionViewGtk>::const_iterator
page_action_view = page_action_views_.begin();
page_action_view != page_action_views_.end();
++page_action_view) {
if ((*page_action_view)->IsVisible()) {
// Figure out where the page action is drawn so we can draw
// borders to its left and right.
GtkAllocation allocation;
gtk_widget_get_allocation((*page_action_view)->widget(), &allocation);
ExtensionAction* action = (*page_action_view)->page_action();
gfx::Rect bounds(allocation);
// Make the bounding rectangle include the whole vertical range of the
// location bar, and the mid-point pixels between adjacent page actions.
//
// For odd InnerPadding()s, "InnerPadding() + 1" includes the mid-point
// between two page actions in the bounding rectangle. For even paddings,
// the +1 is dropped, which is right since there is no pixel at the
// mid-point.
bounds.Inset(-(InnerPadding() + 1) / 2,
theme_service_->UsingNativeTheme() ? -1 : 0);
location_bar_util::PaintExtensionActionBackground(
*action, SessionID::IdForTab(GetWebContents()),
&canvas, bounds,
theme_service_->get_location_bar_text_color(),
theme_service_->get_location_bar_bg_color());
}
}
// Destroying |canvas| draws the background.
return FALSE; // Continue propagating the expose.
}
void LocationBarViewGtk::UpdateSiteTypeArea() {
// The icon is always visible except when the |tab_to_search_alignment_| is
// visible.
if (!location_entry_->model()->keyword().empty() &&
!location_entry_->model()->is_keyword_hint()) {
gtk_widget_hide(site_type_area());
return;
}
int resource_id = location_entry_->GetIcon();
gtk_image_set_from_pixbuf(
GTK_IMAGE(location_icon_image_),
theme_service_->GetImageNamed(resource_id).ToGdkPixbuf());
if (toolbar_model_->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) {
if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
// Fun fact: If wee try to make |site_type_event_box_| act as a
// rounded window while it doesn't have a visible window, GTK interprets
// this as a sign that it should paint the skyline texture into the
// omnibox.
gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
TRUE);
gtk_util::ActAsRoundedWindow(site_type_event_box_,
kEvSecureBorderColor,
kCornerSize,
gtk_util::ROUNDED_ALL,
gtk_util::BORDER_ALL);
}
string16 info_text = toolbar_model_->GetEVCertName();
gtk_label_set_text(GTK_LABEL(security_info_label_),
UTF16ToUTF8(info_text).c_str());
UpdateEVCertificateLabelSize();
gtk_widget_show(GTK_WIDGET(security_info_label_));
} else {
if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
gtk_util::StopActingAsRoundedWindow(site_type_event_box_);
gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
FALSE);
}
gtk_widget_hide(GTK_WIDGET(security_info_label_));
}
if (GetLocationEntry()->IsEditingOrEmpty()) {
// Do not show the tooltip if the user has been editing the location
// bar, or the location bar is at the NTP.
gtk_widget_set_tooltip_text(location_icon_image_, "");
} else {
gtk_widget_set_tooltip_text(location_icon_image_,
l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
}
gtk_widget_show(site_type_area());
SetSiteTypeDragSource();
}
void LocationBarViewGtk::UpdateEVCertificateLabelSize() {
// Figure out the width of the average character.
PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_));
PangoContext* context = pango_layout_get_context(layout);
PangoFontMetrics* metrics = pango_context_get_metrics(
context,
gtk_widget_get_style(security_info_label_)->font_desc,
pango_context_get_language(context));
int char_width =
pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE;
// The EV label should never take up more than half the hbox. We try to
// correct our inaccurate measurement units ("the average character width")
// by dividing more than an even 2.
GtkAllocation security_label_allocation;
gtk_widget_get_allocation(security_info_label_, &security_label_allocation);
GtkAllocation entry_box_allocation;
gtk_widget_get_allocation(entry_box_, &entry_box_allocation);
int text_area = security_label_allocation.width +
entry_box_allocation.width;
int max_chars = static_cast<int>(static_cast<float>(text_area) /
static_cast<float>(char_width) / 2.75);
// Don't let the label be smaller than 10 characters so that the country
// code is always visible.
gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_),
std::max(10, max_chars));
pango_font_metrics_unref(metrics);
}
void LocationBarViewGtk::SetKeywordLabel(const string16& keyword) {
if (keyword.empty())
return;
Profile* profile = browser_->profile();
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
if (!template_url_service)
return;
bool is_extension_keyword;
const string16 short_name = template_url_service->GetKeywordShortName(
keyword, &is_extension_keyword);
const string16 min_string = location_bar_util::CalculateMinString(short_name);
const string16 full_name = is_extension_keyword ?
short_name :
l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, short_name);
const string16 partial_name = is_extension_keyword ?
min_string :
l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, min_string);
gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_),
UTF16ToUTF8(full_name).c_str());
gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_),
UTF16ToUTF8(partial_name).c_str());
if (last_keyword_ != keyword) {
last_keyword_ = keyword;
if (is_extension_keyword) {
const TemplateURL* template_url =
template_url_service->GetTemplateURLForKeyword(keyword);
gfx::Image image = extensions::OmniboxAPI::Get(profile)->
GetOmniboxIcon(template_url->GetExtensionId());
gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
image.ToGdkPixbuf());
} else {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
rb.GetNativeImageNamed(IDR_OMNIBOX_SEARCH).ToGdkPixbuf());
}
}
}
void LocationBarViewGtk::SetKeywordHintLabel(const string16& keyword) {
if (keyword.empty())
return;
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(browser_->profile());
if (!template_url_service)
return;
bool is_extension_keyword;
const string16 short_name = template_url_service->
GetKeywordShortName(keyword, &is_extension_keyword);
int message_id = is_extension_keyword ?
IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT;
std::vector<size_t> content_param_offsets;
const string16 keyword_hint = l10n_util::GetStringFUTF16(
message_id,
string16(),
short_name,
&content_param_offsets);
if (content_param_offsets.size() != 2) {
// See comments on an identical NOTREACHED() in search_provider.cc.
NOTREACHED();
return;
}
std::string leading(UTF16ToUTF8(
keyword_hint.substr(0, content_param_offsets.front())));
std::string trailing(UTF16ToUTF8(
keyword_hint.substr(content_param_offsets.front())));
gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_),
leading.c_str());
gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_),
trailing.c_str());
}
void LocationBarViewGtk::ShowFirstRunBubbleInternal() {
if (!location_entry_.get() || !gtk_widget_get_window(widget()))
return;
gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_);
bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing);
FirstRunBubble::Show(browser_, location_icon_image_, bounds);
}
gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender,
GdkEventButton* event) {
WebContents* tab = GetWebContents();
if (event->button == 1) {
// Do not show page info if the user has been editing the location
// bar, or the location bar is at the NTP.
if (GetLocationEntry()->IsEditingOrEmpty())
return FALSE;
// (0,0) event coordinates indicates that the release came at the end of
// a drag.
if (event->x == 0 && event->y == 0)
return FALSE;
NavigationEntry* nav_entry = tab->GetController().GetActiveEntry();
if (!nav_entry) {
NOTREACHED();
return FALSE;
}
chrome::ShowWebsiteSettings(browser_, tab, nav_entry->GetURL(),
nav_entry->GetSSL());
return TRUE;
} else if (event->button == 2) {
// When the user middle clicks on the location icon, try to open the
// contents of the PRIMARY selection in the current tab.
// If the click was outside our bounds, do nothing.
if (!gtk_util::WidgetBounds(sender).Contains(
gfx::Point(event->x, event->y))) {
return FALSE;
}
GURL url;
if (!gtk_util::URLFromPrimarySelection(browser_->profile(), &url))
return FALSE;
tab->OpenURL(OpenURLParams(
url, content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
false));
return TRUE;
}
return FALSE;
}
void LocationBarViewGtk::OnIconDragData(GtkWidget* sender,
GdkDragContext* context,
GtkSelectionData* data,
guint info, guint time) {
ui::WriteURLWithName(data, drag_url_, drag_title_, info);
}
void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender,
GdkDragContext* context) {
gfx::Image favicon = GetFavicon();
if (favicon.IsEmpty())
return;
drag_icon_ = bookmark_utils::GetDragRepresentation(favicon.ToGdkPixbuf(),
GetTitle(), theme_service_);
gtk_drag_set_icon_widget(context, drag_icon_, 0, 0);
WebContents* tab = GetWebContents();
if (!tab)
return;
drag_url_ = tab->GetURL();
drag_title_ = tab->GetTitle();
}
void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
GdkDragContext* context) {
DCHECK(drag_icon_);
gtk_widget_destroy(drag_icon_);
drag_icon_ = NULL;
drag_url_ = GURL::EmptyGURL();
drag_title_.clear();
}
void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
GtkAllocation* allocation) {
if (hbox_width_ != allocation->width) {
hbox_width_ = allocation->width;
UpdateEVCertificateLabelSize();
}
if (browser_ && browser_->instant_controller()) {
browser_->instant_controller()->
SetOmniboxBounds(AllocationToRect(*allocation));
}
}
void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender,
GtkAllocation* allocation) {
if (entry_box_width_ != allocation->width) {
entry_box_width_ = allocation->width;
AdjustChildrenVisibility();
}
}
gboolean LocationBarViewGtk::OnZoomButtonPress(GtkWidget* widget,
GdkEventButton* event) {
if (event->button == 1 && GetWebContents()) {
// If the zoom icon is clicked, show the zoom bubble and keep it open until
// it loses focus.
ZoomBubbleGtk::ShowBubble(GetWebContents(), false);
return TRUE;
}
return FALSE;
}
gboolean LocationBarViewGtk::OnScriptBubbleButtonPress(GtkWidget* widget,
GdkEventButton* event) {
if (event->button == 1 && GetWebContents()) {
ScriptBubbleGtk::Show(script_bubble_button_image_, GetWebContents());
return TRUE;
}
return FALSE;
}
gboolean LocationBarViewGtk::OnScriptBubbleButtonExpose(GtkWidget* widget,
GdkEventExpose* event) {
gfx::CanvasSkiaPaint canvas(event, false);
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
badge_util::PaintBadge(&canvas,
gfx::Rect(allocation),
base::UintToString(num_running_scripts_),
SK_ColorWHITE,
SkColorSetRGB(0, 170, 0),
allocation.width,
extensions::ActionInfo::TYPE_PAGE);
return FALSE;
}
void LocationBarViewGtk::OnStarButtonSizeAllocate(GtkWidget* sender,
GtkAllocation* allocation) {
if (!on_star_sized_.is_null()) {
on_star_sized_.Run();
on_star_sized_.Reset();
}
star_sized_ = true;
}
gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
GdkEventButton* event) {
if (event->button == 1) {
chrome::ExecuteCommand(browser_, IDC_BOOKMARK_PAGE);
return TRUE;
}
return FALSE;
}
void LocationBarViewGtk::ShowZoomBubble() {
if (toolbar_model_->GetInputInProgress() || !GetWebContents())
return;
ZoomBubbleGtk::ShowBubble(GetWebContents(), true);
}
void LocationBarViewGtk::ShowStarBubble(const GURL& url,
bool newly_bookmarked) {
if (!star_.get())
return;
if (star_sized_) {
BookmarkBubbleGtk::Show(star_.get(), browser_->profile(), url,
newly_bookmarked);
} else {
on_star_sized_ = base::Bind(&BookmarkBubbleGtk::Show,
star_.get(), browser_->profile(),
url, newly_bookmarked);
}
}
void LocationBarViewGtk::SetStarred(bool starred) {
if (starred == starred_)
return;
starred_ = starred;
UpdateStarIcon();
}
void LocationBarViewGtk::ZoomChangedForActiveTab(bool can_show_bubble) {
UpdateZoomIcon();
if (can_show_bubble && gtk_widget_get_visible(zoom_.get()))
ShowZoomBubble();
}
void LocationBarViewGtk::UpdateZoomIcon() {
WebContents* web_contents = GetWebContents();
if (!zoom_.get() || !web_contents)
return;
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
if (!zoom_controller || zoom_controller->IsAtDefaultZoom() ||
toolbar_model_->GetInputInProgress()) {
gtk_widget_hide(zoom_.get());
ZoomBubbleGtk::CloseBubble();
return;
}
const int zoom_resource = zoom_controller->GetResourceForZoomLevel();
gtk_image_set_from_pixbuf(GTK_IMAGE(zoom_image_),
theme_service_->GetImageNamed(zoom_resource).ToGdkPixbuf());
string16 tooltip = l10n_util::GetStringFUTF16Int(
IDS_TOOLTIP_ZOOM, zoom_controller->zoom_percent());
gtk_widget_set_tooltip_text(zoom_.get(), UTF16ToUTF8(tooltip).c_str());
gtk_widget_show(zoom_.get());
}
void LocationBarViewGtk::UpdateScriptBubbleIcon() {
num_running_scripts_ = 0;
if (GetWebContents()) {
extensions::TabHelper* tab_helper =
extensions::TabHelper::FromWebContents(GetWebContents());
if (tab_helper && tab_helper->script_bubble_controller()) {
num_running_scripts_ = tab_helper->script_bubble_controller()->
extensions_running_scripts().size();
}
}
if (num_running_scripts_ == 0u)
gtk_widget_hide(script_bubble_button_.get());
else
gtk_widget_show(script_bubble_button_.get());
}
void LocationBarViewGtk::UpdateStarIcon() {
if (!star_.get())
return;
// Indicate the star icon is not correctly sized. It will be marked as sized
// when the next size-allocate signal is received by the star widget.
star_sized_ = false;
bool star_enabled = !toolbar_model_->GetInputInProgress() &&
edit_bookmarks_enabled_.GetValue();
command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE_FROM_STAR,
star_enabled);
if (star_enabled) {
gtk_widget_show_all(star_.get());
int id = starred_ ? IDR_STAR_LIT : IDR_STAR;
gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_),
theme_service_->GetImageNamed(id).ToGdkPixbuf());
gtk_widget_set_tooltip_text(star_.get(), l10n_util::GetStringUTF8(
starred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR).c_str());
} else {
gtk_widget_hide_all(star_.get());
}
}
bool LocationBarViewGtk::ShouldOnlyShowLocation() {
return !browser_->is_type_tabbed();
}
void LocationBarViewGtk::AdjustChildrenVisibility() {
int text_width = location_entry_->TextWidth();
int available_width = entry_box_width_ - text_width - InnerPadding();
// Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be
// visible at the same time.
if (!show_selected_keyword_ &&
gtk_widget_get_visible(tab_to_search_alignment_)) {
gtk_widget_hide(tab_to_search_alignment_);
} else if (!show_keyword_hint_ &&
gtk_widget_get_visible(tab_to_search_hint_)) {
gtk_widget_hide(tab_to_search_hint_);
}
if (show_selected_keyword_) {
GtkRequisition box, full_label, partial_label;
gtk_widget_size_request(tab_to_search_box_, &box);
gtk_widget_size_request(tab_to_search_full_label_, &full_label);
gtk_widget_size_request(tab_to_search_partial_label_, &partial_label);
int full_partial_width_diff = full_label.width - partial_label.width;
int full_box_width;
int partial_box_width;
if (gtk_widget_get_visible(tab_to_search_full_label_)) {
full_box_width = box.width;
partial_box_width = full_box_width - full_partial_width_diff;
} else {
partial_box_width = box.width;
full_box_width = partial_box_width + full_partial_width_diff;
}
if (partial_box_width >= entry_box_width_ - InnerPadding()) {
gtk_widget_hide(tab_to_search_alignment_);
} else if (full_box_width >= available_width) {
gtk_widget_hide(tab_to_search_full_label_);
gtk_widget_show(tab_to_search_partial_label_);
gtk_widget_show(tab_to_search_alignment_);
} else if (full_box_width < available_width) {
gtk_widget_hide(tab_to_search_partial_label_);
gtk_widget_show(tab_to_search_full_label_);
gtk_widget_show(tab_to_search_alignment_);
}
} else if (show_keyword_hint_) {
GtkRequisition leading, icon, trailing;
gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading);
gtk_widget_size_request(tab_to_search_hint_icon_, &icon);
gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing);
int full_width = leading.width + icon.width + trailing.width;
if (icon.width >= entry_box_width_ - InnerPadding()) {
gtk_widget_hide(tab_to_search_hint_);
} else if (full_width >= available_width) {
gtk_widget_hide(tab_to_search_hint_leading_label_);
gtk_widget_hide(tab_to_search_hint_trailing_label_);
gtk_widget_show(tab_to_search_hint_);
} else if (full_width < available_width) {
gtk_widget_show(tab_to_search_hint_leading_label_);
gtk_widget_show(tab_to_search_hint_trailing_label_);
gtk_widget_show(tab_to_search_hint_);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarViewGtk::PageToolViewGtk
LocationBarViewGtk::PageToolViewGtk::PageToolViewGtk(
const LocationBarViewGtk* parent)
: alignment_(gtk_alignment_new(0, 0, 1, 1)),
event_box_(gtk_event_box_new()),
hbox_(gtk_hbox_new(FALSE, InnerPadding())),
image_(gtk_image_new()),
label_(gtk_label_new(NULL)),
parent_(parent),
animation_(this),
weak_factory_(this) {
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get());
// Make the event box not visible so it does not paint a background.
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
g_signal_connect(event_box_.get(), "button-press-event",
G_CALLBACK(&OnButtonPressedThunk), this);
g_signal_connect(event_box_.get(), "expose-event",
G_CALLBACK(&OnExposeThunk), this);
gtk_widget_set_no_show_all(label_.get(), TRUE);
gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE);
gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder);
gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_);
gtk_widget_hide(widget());
animation_.SetSlideDuration(kPageToolAnimationTime);
}
LocationBarViewGtk::PageToolViewGtk::~PageToolViewGtk() {
image_.Destroy();
label_.Destroy();
event_box_.Destroy();
alignment_.Destroy();
}
GtkWidget* LocationBarViewGtk::PageToolViewGtk::widget() {
return alignment_.get();
}
bool LocationBarViewGtk::PageToolViewGtk::IsVisible() {
return gtk_widget_get_visible(widget());
}
void LocationBarViewGtk::PageToolViewGtk::StartAnimating() {
if (animation_.IsShowing() || animation_.IsClosing())
return;
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE);
GdkColor border_color = button_border_color();
gtk_util::ActAsRoundedWindow(event_box_.get(), border_color,
kCornerSize,
gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
gtk_widget_set_size_request(label_.get(), -1, -1);
gtk_widget_size_request(label_.get(), &label_req_);
gtk_widget_set_size_request(label_.get(), 0, -1);
gtk_widget_show(label_.get());
animation_.Show();
}
void LocationBarViewGtk::PageToolViewGtk::CloseAnimation() {
animation_.Hide();
}
void LocationBarViewGtk::PageToolViewGtk::AnimationProgressed(
const ui::Animation* animation) {
gtk_widget_set_size_request(
label_.get(),
animation->GetCurrentValue() * label_req_.width,
-1);
}
void LocationBarViewGtk::PageToolViewGtk::AnimationEnded(
const ui::Animation* animation) {
}
void LocationBarViewGtk::PageToolViewGtk::AnimationCanceled(
const ui::Animation* animation) {
}
gboolean LocationBarViewGtk::PageToolViewGtk::OnButtonPressed(
GtkWidget* sender, GdkEvent* event) {
OnClick(sender);
return TRUE;
}
gboolean LocationBarViewGtk::PageToolViewGtk::OnExpose(
GtkWidget* sender, GdkEventExpose* event) {
TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageToolViewGtk::OnExpose");
if (!(animation_.IsShowing() || animation_.IsClosing()))
return FALSE;
GtkAllocation allocation;
gtk_widget_get_allocation(sender, &allocation);
const int height = allocation.height;
cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
gdk_cairo_rectangle(cr, &event->area);
cairo_clip(cr);
cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
const GdkColor top_color = gradient_top_color();
const GdkColor bottom_color = gradient_bottom_color();
cairo_pattern_add_color_stop_rgb(
pattern, 0.0,
top_color.red/255.0,
top_color.blue/255.0,
top_color.green/255.0);
cairo_pattern_add_color_stop_rgb(
pattern, 1.0,
bottom_color.red/255.0,
bottom_color.blue/255.0,
bottom_color.green/255.0);
cairo_set_source(cr, pattern);
cairo_paint(cr);
cairo_pattern_destroy(pattern);
cairo_destroy(cr);
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarViewGtk::PageActionViewGtk
LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
LocationBarViewGtk* owner,
ExtensionAction* page_action)
: owner_(NULL),
page_action_(page_action),
current_tab_id_(-1),
window_(NULL),
accel_group_(NULL),
preview_enabled_(false),
scoped_icon_animation_observer_(
page_action->GetIconAnimation(
SessionID::IdForTab(owner->GetWebContents())),
this) {
event_box_.Own(gtk_event_box_new());
gtk_widget_set_size_request(event_box_.get(),
extensions::IconsInfo::kPageActionIconMaxSize,
extensions::IconsInfo::kPageActionIconMaxSize);
// Make the event box not visible so it does not paint a background.
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
g_signal_connect(event_box_.get(), "button-press-event",
G_CALLBACK(&OnButtonPressedThunk), this);
g_signal_connect_after(event_box_.get(), "expose-event",
G_CALLBACK(OnExposeEventThunk), this);
g_signal_connect(event_box_.get(), "realize",
G_CALLBACK(OnRealizeThunk), this);
image_.Own(gtk_image_new());
gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get());
const Extension* extension = owner->browser()->profile()->
GetExtensionService()->GetExtensionById(page_action->extension_id(),
false);
DCHECK(extension);
icon_factory_.reset(
new ExtensionActionIconFactory(
owner->browser()->profile(), extension, page_action, this));
// We set the owner last of all so that we can determine whether we are in
// the process of initializing this class or not.
owner_ = owner;
}
LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
DisconnectPageActionAccelerator();
image_.Destroy();
event_box_.Destroy();
}
bool LocationBarViewGtk::PageActionViewGtk::IsVisible() {
return gtk_widget_get_visible(widget());
}
void LocationBarViewGtk::PageActionViewGtk::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;
bool visible = contents &&
(preview_enabled_ || page_action_->GetIsVisible(current_tab_id_));
if (visible) {
// Set the tooltip.
gtk_widget_set_tooltip_text(event_box_.get(),
page_action_->GetTitle(current_tab_id_).c_str());
// Set the image.
gfx::Image icon = icon_factory_->GetIcon(current_tab_id_);
if (!icon.IsEmpty()) {
GdkPixbuf* pixbuf = icon.ToGdkPixbuf();
DCHECK(pixbuf);
gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
}
}
bool old_visible = IsVisible();
if (visible)
gtk_widget_show_all(event_box_.get());
else
gtk_widget_hide_all(event_box_.get());
if (visible != old_visible) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionAction>(page_action_),
content::Details<WebContents>(contents));
}
}
void LocationBarViewGtk::PageActionViewGtk::OnIconUpdated() {
// If we have no owner, that means this class is still being constructed.
WebContents* web_contents = owner_ ? owner_->GetWebContents() : NULL;
if (web_contents)
UpdateVisibility(web_contents, current_url_);
}
void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
GdkEventButton event = {};
event.type = GDK_BUTTON_PRESS;
event.button = 1;
OnButtonPressed(widget(), &event);
}
void LocationBarViewGtk::PageActionViewGtk::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(type, chrome::NOTIFICATION_WINDOW_CLOSED);
DisconnectPageActionAccelerator();
}
void LocationBarViewGtk::PageActionViewGtk::ConnectPageActionAccelerator() {
const ExtensionSet* extensions = owner_->browser()->profile()->
GetExtensionService()->extensions();
const Extension* extension =
extensions->GetByID(page_action_->extension_id());
window_ = owner_->browser()->window()->GetNativeWindow();
extensions::CommandService* command_service =
extensions::CommandService::Get(owner_->browser()->profile());
extensions::Command command_page_action;
if (command_service->GetPageActionCommand(
extension->id(),
extensions::CommandService::ACTIVE_ONLY,
&command_page_action,
NULL)) {
// Found the page action shortcut command, register it.
page_action_keybinding_.reset(
new ui::Accelerator(command_page_action.accelerator()));
}
extensions::Command command_script_badge;
if (command_service->GetScriptBadgeCommand(
extension->id(),
extensions::CommandService::ACTIVE_ONLY,
&command_script_badge,
NULL)) {
// Found the script badge shortcut command, register it.
script_badge_keybinding_.reset(
new ui::Accelerator(command_script_badge.accelerator()));
}
if (page_action_keybinding_.get() || script_badge_keybinding_.get()) {
accel_group_ = gtk_accel_group_new();
gtk_window_add_accel_group(window_, accel_group_);
if (page_action_keybinding_.get()) {
gtk_accel_group_connect(
accel_group_,
ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
ui::GetGdkModifierForAccelerator(*page_action_keybinding_),
GtkAccelFlags(0),
g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
}
if (script_badge_keybinding_.get()) {
gtk_accel_group_connect(
accel_group_,
ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
ui::GetGdkModifierForAccelerator(*script_badge_keybinding_),
GtkAccelFlags(0),
g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
}
// Since we've added an accelerator, we'll need to unregister it before
// the window is closed, so we listen for the window being closed.
registrar_.Add(this,
chrome::NOTIFICATION_WINDOW_CLOSED,
content::Source<GtkWindow>(window_));
}
}
void LocationBarViewGtk::PageActionViewGtk::OnIconChanged() {
UpdateVisibility(owner_->GetWebContents(), current_url_);
}
void LocationBarViewGtk::PageActionViewGtk::DisconnectPageActionAccelerator() {
if (accel_group_) {
if (page_action_keybinding_.get()) {
gtk_accel_group_disconnect_key(
accel_group_,
ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
ui::GetGdkModifierForAccelerator(*page_action_keybinding_));
}
if (script_badge_keybinding_.get()) {
gtk_accel_group_disconnect_key(
accel_group_,
ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
ui::GetGdkModifierForAccelerator(*script_badge_keybinding_));
}
gtk_window_remove_accel_group(window_, accel_group_);
g_object_unref(accel_group_);
accel_group_ = NULL;
page_action_keybinding_.reset(NULL);
script_badge_keybinding_.reset(NULL);
}
}
gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed(
GtkWidget* sender,
GdkEventButton* event) {
// Double and triple-clicks generate both a GDK_BUTTON_PRESS and a
// GDK_[23]BUTTON_PRESS event. We don't want to double-trigger by acting on
// both.
if (event->type != GDK_BUTTON_PRESS)
return TRUE;
WebContents* web_contents = owner_->GetWebContents();
if (!web_contents)
return TRUE;
ExtensionService* extension_service =
owner_->browser()->profile()->GetExtensionService();
if (!extension_service)
return TRUE;
const Extension* extension =
extension_service->extensions()->GetByID(page_action()->extension_id());
if (!extension)
return TRUE;
LocationBarController* controller =
extensions::TabHelper::FromWebContents(web_contents)->
location_bar_controller();
switch (controller->OnClicked(extension->id(), event->button)) {
case LocationBarController::ACTION_NONE:
break;
case LocationBarController::ACTION_SHOW_POPUP:
ExtensionPopupGtk::Show(
page_action_->GetPopupUrl(current_tab_id_),
owner_->browser_,
event_box_.get(),
ExtensionPopupGtk::SHOW);
break;
case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
context_menu_model_ =
new ExtensionContextMenuModel(extension, owner_->browser_, this);
context_menu_.reset(
new MenuGtk(NULL, context_menu_model_.get()));
context_menu_->PopupForWidget(sender, event->button, event->time);
break;
case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
ExtensionPopupGtk::Show(
extensions::ExtensionInfoUI::GetURL(extension->id()),
owner_->browser_,
event_box_.get(),
ExtensionPopupGtk::SHOW);
break;
}
return TRUE;
}
gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
GtkWidget* widget,
GdkEventExpose* event) {
TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageActionViewGtk::OnExpose");
WebContents* contents = owner_->GetWebContents();
if (!contents)
return FALSE;
int tab_id = ExtensionTabUtil::GetTabId(contents);
if (tab_id < 0)
return FALSE;
std::string badge_text = page_action_->GetBadgeText(tab_id);
if (badge_text.empty())
return FALSE;
gfx::CanvasSkiaPaint canvas(event, false);
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
page_action_->PaintBadge(&canvas, gfx::Rect(allocation), tab_id);
return FALSE;
}
void LocationBarViewGtk::PageActionViewGtk::OnRealize(GtkWidget* widget) {
ConnectPageActionAccelerator();
}
// static
gboolean LocationBarViewGtk::PageActionViewGtk::OnGtkAccelerator(
GtkAccelGroup* accel_group,
GObject* acceleratable,
guint keyval,
GdkModifierType modifier,
void* user_data) {
PageActionViewGtk* view = static_cast<PageActionViewGtk*>(user_data);
if (!gtk_widget_get_visible(view->widget()))
return FALSE;
GdkEventButton event = {};
event.type = GDK_BUTTON_PRESS;
event.button = 1;
return view->OnButtonPressed(view->widget(), &event);
}
void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
ExtensionAction* action) {
ExtensionPopupGtk::Show(
action->GetPopupUrl(current_tab_id_),
owner_->browser_,
event_box_.get(),
ExtensionPopupGtk::SHOW_AND_INSPECT);
}