Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / ui / gtk / location_bar_view_gtk.cc
blob9f9e14842f4d2b1c3aa549ed83338bad14216bc5
1 // Copyright 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.
5 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/debug/trace_event.h"
15 #include "base/i18n/rtl.h"
16 #include "base/logging.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/app/chrome_command_ids.h"
23 #include "chrome/browser/accessibility/accessibility_events.h"
24 #include "chrome/browser/accessibility/accessibility_extension_api.h"
25 #include "chrome/browser/chrome_notification_types.h"
26 #include "chrome/browser/command_updater.h"
27 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
28 #include "chrome/browser/defaults.h"
29 #include "chrome/browser/extensions/api/commands/command_service.h"
30 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
31 #include "chrome/browser/extensions/extension_action.h"
32 #include "chrome/browser/extensions/extension_service.h"
33 #include "chrome/browser/extensions/extension_tab_util.h"
34 #include "chrome/browser/extensions/location_bar_controller.h"
35 #include "chrome/browser/extensions/script_bubble_controller.h"
36 #include "chrome/browser/extensions/tab_helper.h"
37 #include "chrome/browser/favicon/favicon_tab_helper.h"
38 #include "chrome/browser/search_engines/template_url.h"
39 #include "chrome/browser/search_engines/template_url_service.h"
40 #include "chrome/browser/search_engines/template_url_service_factory.h"
41 #include "chrome/browser/themes/theme_properties.h"
42 #include "chrome/browser/ui/browser.h"
43 #include "chrome/browser/ui/browser_command_controller.h"
44 #include "chrome/browser/ui/browser_commands.h"
45 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
46 #include "chrome/browser/ui/browser_instant_controller.h"
47 #include "chrome/browser/ui/browser_list.h"
48 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
49 #include "chrome/browser/ui/content_settings/content_setting_image_model.h"
50 #include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
51 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
52 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
53 #include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h"
54 #include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h"
55 #include "chrome/browser/ui/gtk/first_run_bubble.h"
56 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
57 #include "chrome/browser/ui/gtk/gtk_util.h"
58 #include "chrome/browser/ui/gtk/manage_passwords_bubble_gtk.h"
59 #include "chrome/browser/ui/gtk/nine_box.h"
60 #include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
61 #include "chrome/browser/ui/gtk/rounded_window.h"
62 #include "chrome/browser/ui/gtk/script_bubble_gtk.h"
63 #include "chrome/browser/ui/gtk/view_id_util.h"
64 #include "chrome/browser/ui/gtk/zoom_bubble_gtk.h"
65 #include "chrome/browser/ui/omnibox/location_bar_util.h"
66 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
67 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
68 #include "chrome/browser/ui/passwords/manage_passwords_bubble_ui_controller.h"
69 #include "chrome/browser/ui/tabs/tab_strip_model.h"
70 #include "chrome/browser/ui/webui/extensions/extension_info_ui.h"
71 #include "chrome/browser/ui/zoom/zoom_controller.h"
72 #include "chrome/common/badge_util.h"
73 #include "chrome/common/chrome_switches.h"
74 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
75 #include "chrome/common/pref_names.h"
76 #include "content/public/browser/navigation_entry.h"
77 #include "content/public/browser/notification_service.h"
78 #include "content/public/browser/web_contents.h"
79 #include "extensions/common/extension.h"
80 #include "extensions/common/feature_switch.h"
81 #include "grit/generated_resources.h"
82 #include "grit/theme_resources.h"
83 #include "net/base/net_util.h"
84 #include "ui/base/accelerators/platform_accelerator_gtk.h"
85 #include "ui/base/accessibility/accessibility_types.h"
86 #include "ui/base/dragdrop/gtk_dnd_util.h"
87 #include "ui/base/gtk/gtk_hig_constants.h"
88 #include "ui/base/gtk/gtk_signal_registrar.h"
89 #include "ui/base/l10n/l10n_util.h"
90 #include "ui/base/resource/resource_bundle.h"
91 #include "ui/base/window_open_disposition.h"
92 #include "ui/gfx/canvas_skia_paint.h"
93 #include "ui/gfx/font.h"
94 #include "ui/gfx/gtk_util.h"
95 #include "ui/gfx/image/image.h"
97 using content::NavigationEntry;
98 using content::OpenURLParams;
99 using content::WebContents;
100 using extensions::LocationBarController;
101 using extensions::Extension;
103 namespace {
105 // We are positioned with a little bit of extra space that we don't use now.
106 const int kTopMargin = 1;
107 const int kBottomMargin = 1;
108 // We draw a border on the top and bottom (but not on left or right).
109 const int kBorderThickness = 1;
111 const int kPopupEdgeThickness = 1;
112 const int kNormalEdgeThickness = 2;
114 // Spacing needed to align the bubble with the left side of the omnibox.
115 const int kFirstRunBubbleLeftSpacing = 4;
117 // The padding around the top, bottom, and sides of the location bar hbox.
118 // We don't want to edit control's text to be right against the edge,
119 // as well the tab to search box and other widgets need to have the padding on
120 // top and bottom to avoid drawing larger than the location bar space.
121 const int kHboxBorder = 2;
123 // Padding between the elements in the bar.
124 const int kInnerPadding = 2;
125 const int kScriptBadgeInnerPadding = 9;
127 // Colors used to draw the EV certificate rounded bubble.
128 const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
129 const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
130 const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
132 // Colors used to draw the Tab to Search rounded bubble.
133 const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
134 const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
136 // Use weak gray for showing search and keyword hint text.
137 const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
139 // Size of the rounding of the "Search site for:" box.
140 const int kCornerSize = 3;
142 // Default page tool animation time (open and close). In ms.
143 const int kPageToolAnimationTime = 150;
145 // The time, in ms, that the content setting label is fully displayed, for the
146 // cases where we animate it into and out of view.
147 const int kContentSettingImageDisplayTime = 3200;
148 // The time, in ms, of the animation (open and close).
149 const int kContentSettingImageAnimationTime = 150;
151 // Color of border of content setting area (icon/label).
152 const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
153 // Colors for the background gradient.
154 const GdkColor kContentSettingTopColor = GDK_COLOR_RGB(0xff, 0xf8, 0xd4);
155 const GdkColor kContentSettingBottomColor = GDK_COLOR_RGB(0xff, 0xe6, 0xaf);
157 inline int InnerPadding() {
158 return extensions::FeatureSwitch::script_badges()->IsEnabled() ?
159 kScriptBadgeInnerPadding : kInnerPadding;
162 // If widget is visible, increment the int pointed to by count.
163 // Suitible for use with gtk_container_foreach.
164 void CountVisibleWidgets(GtkWidget* widget, gpointer count) {
165 if (gtk_widget_get_visible(widget))
166 *static_cast<int*>(count) += 1;
169 class ContentSettingImageViewGtk : public LocationBarViewGtk::PageToolViewGtk,
170 public BubbleDelegateGtk {
171 public:
172 ContentSettingImageViewGtk(ContentSettingsType content_type,
173 LocationBarViewGtk* parent);
174 virtual ~ContentSettingImageViewGtk();
176 // PageToolViewGtk
177 virtual void Update(WebContents* web_contents) OVERRIDE;
179 // gfx::AnimationDelegate
180 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
182 private:
183 // PageToolViewGtk
184 virtual GdkColor GetButtonBorderColor() const OVERRIDE;
185 virtual GdkColor GetGradientTopColor() const OVERRIDE;
186 virtual GdkColor GetGradientBottomColor() const OVERRIDE;
187 virtual void OnClick(GtkWidget* sender) OVERRIDE;
189 // BubbleDelegateGtk
190 virtual void BubbleClosing(BubbleGtk* bubble,
191 bool closed_by_escape) OVERRIDE;
193 // The owning LocationBarViewGtk.
194 LocationBarViewGtk* parent_;
196 scoped_ptr<ContentSettingImageModel> content_setting_image_model_;
198 // The currently shown bubble if any.
199 ContentSettingBubbleGtk* content_setting_bubble_;
201 DISALLOW_COPY_AND_ASSIGN(ContentSettingImageViewGtk);
204 ContentSettingImageViewGtk::ContentSettingImageViewGtk(
205 ContentSettingsType content_type,
206 LocationBarViewGtk* parent)
207 : PageToolViewGtk(),
208 parent_(parent),
209 content_setting_image_model_(
210 ContentSettingImageModel::CreateContentSettingImageModel(
211 content_type)),
212 content_setting_bubble_(NULL) {
213 animation_.SetSlideDuration(kContentSettingImageAnimationTime);
216 ContentSettingImageViewGtk::~ContentSettingImageViewGtk() {
217 if (content_setting_bubble_)
218 content_setting_bubble_->Close();
221 void ContentSettingImageViewGtk::Update(WebContents* web_contents) {
222 if (web_contents)
223 content_setting_image_model_->UpdateFromWebContents(web_contents);
225 if (!content_setting_image_model_->is_visible()) {
226 gtk_widget_hide(widget());
227 return;
230 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()),
231 GtkThemeService::GetFrom(parent_->browser()->profile())->GetImageNamed(
232 content_setting_image_model_->get_icon()).ToGdkPixbuf());
234 gtk_widget_set_tooltip_text(widget(),
235 content_setting_image_model_->get_tooltip().c_str());
236 gtk_widget_show_all(widget());
238 if (!web_contents)
239 return;
241 TabSpecificContentSettings* content_settings =
242 TabSpecificContentSettings::FromWebContents(web_contents);
243 if (!content_settings || content_settings->IsBlockageIndicated(
244 content_setting_image_model_->get_content_settings_type()))
245 return;
247 // The content blockage was not yet indicated to the user. Start indication
248 // animation and clear "not yet shown" flag.
249 content_settings->SetBlockageHasBeenIndicated(
250 content_setting_image_model_->get_content_settings_type());
252 int label_string_id =
253 content_setting_image_model_->explanatory_string_id();
254 // If there's no string for the content type, we don't animate.
255 if (!label_string_id)
256 return;
258 gtk_label_set_text(GTK_LABEL(label_.get()),
259 l10n_util::GetStringUTF8(label_string_id).c_str());
260 StartAnimating();
263 void ContentSettingImageViewGtk::AnimationEnded(
264 const gfx::Animation* animation) {
265 if (animation_.IsShowing()) {
266 base::MessageLoop::current()->PostDelayedTask(
267 FROM_HERE,
268 base::Bind(&ContentSettingImageViewGtk::CloseAnimation,
269 weak_factory_.GetWeakPtr()),
270 base::TimeDelta::FromMilliseconds(kContentSettingImageDisplayTime));
271 } else {
272 gtk_widget_hide(label_.get());
273 gtk_util::StopActingAsRoundedWindow(event_box_.get());
274 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
278 GdkColor ContentSettingImageViewGtk::GetButtonBorderColor() const {
279 return kContentSettingBorderColor;
282 GdkColor ContentSettingImageViewGtk::GetGradientTopColor() const {
283 return kContentSettingTopColor;
286 GdkColor ContentSettingImageViewGtk::GetGradientBottomColor() const {
287 return kContentSettingBottomColor;
290 void ContentSettingImageViewGtk::OnClick(
291 GtkWidget* sender) {
292 WebContents* web_contents = parent_->GetWebContents();
293 if (!web_contents)
294 return;
295 Profile* profile = parent_->browser()->profile();
296 content_setting_bubble_ = new ContentSettingBubbleGtk(
297 sender, this,
298 ContentSettingBubbleModel::CreateContentSettingBubbleModel(
299 parent_->browser()->content_setting_bubble_model_delegate(),
300 web_contents,
301 profile,
302 content_setting_image_model_->get_content_settings_type()),
303 profile);
304 return;
307 void ContentSettingImageViewGtk::BubbleClosing(
308 BubbleGtk* bubble,
309 bool closed_by_escape) {
310 content_setting_bubble_ = NULL;
313 gfx::Rect AllocationToRect(const GtkAllocation& allocation) {
314 return gfx::Rect(allocation.x, allocation.y,
315 allocation.width, allocation.height);
318 } // namespace
320 ////////////////////////////////////////////////////////////////////////////////
321 // LocationBarViewGtk
323 // static
324 const GdkColor LocationBarViewGtk::kBackgroundColor =
325 GDK_COLOR_RGB(255, 255, 255);
327 LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
328 : OmniboxEditController(browser->command_controller()->command_updater()),
329 LocationBar(browser->profile()),
330 zoom_image_(NULL),
331 manage_passwords_icon_image_(NULL),
332 script_bubble_button_image_(NULL),
333 num_running_scripts_(0u),
334 star_image_(NULL),
335 starred_(false),
336 star_sized_(false),
337 site_type_alignment_(NULL),
338 site_type_event_box_(NULL),
339 location_icon_image_(NULL),
340 drag_icon_(NULL),
341 enable_location_drag_(false),
342 security_info_label_(NULL),
343 tab_to_search_alignment_(NULL),
344 tab_to_search_box_(NULL),
345 tab_to_search_full_label_(NULL),
346 tab_to_search_partial_label_(NULL),
347 tab_to_search_hint_(NULL),
348 tab_to_search_hint_leading_label_(NULL),
349 tab_to_search_hint_icon_(NULL),
350 tab_to_search_hint_trailing_label_(NULL),
351 browser_(browser),
352 popup_window_mode_(false),
353 theme_service_(NULL),
354 hbox_width_(0),
355 entry_box_width_(0),
356 show_selected_keyword_(false),
357 show_keyword_hint_(false),
358 weak_ptr_factory_(this) {
361 LocationBarViewGtk::~LocationBarViewGtk() {
362 // All of our widgets should be children of / owned by the alignment.
363 zoom_.Destroy();
364 manage_passwords_icon_.Destroy();
365 script_bubble_button_.Destroy();
366 star_.Destroy();
367 hbox_.Destroy();
368 content_setting_hbox_.Destroy();
369 page_action_hbox_.Destroy();
372 void LocationBarViewGtk::Init(bool popup_window_mode) {
373 popup_window_mode_ = popup_window_mode;
375 theme_service_ = GtkThemeService::GetFrom(profile());
377 // Create the widget first, so we can pass it to the OmniboxViewGtk.
378 hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
379 gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
380 // We will paint for the alignment, to paint the background and border.
381 gtk_widget_set_app_paintable(hbox_.get(), TRUE);
382 // Redraw the whole location bar when it changes size (e.g., when toggling
383 // the home button on/off.
384 gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
386 // Now initialize the OmniboxViewGtk.
387 omnibox_view_.reset(new OmniboxViewGtk(this, browser_, profile(),
388 command_updater(),
389 popup_window_mode_, hbox_.get()));
390 omnibox_view_->Init();
392 g_signal_connect(hbox_.get(), "expose-event",
393 G_CALLBACK(&HandleExposeThunk), this);
395 BuildSiteTypeArea();
397 // Put |tab_to_search_box_|, |omnibox_view_|, and |tab_to_search_hint_| into
398 // a sub hbox, so that we can make this part horizontally shrinkable without
399 // affecting other elements in the location bar.
400 entry_box_ = gtk_hbox_new(FALSE, InnerPadding());
401 gtk_widget_show(entry_box_);
402 gtk_widget_set_size_request(entry_box_, 0, -1);
403 gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0);
405 // We need to adjust the visibility of the search hint widgets according to
406 // the horizontal space in the |entry_box_|.
407 g_signal_connect(entry_box_, "size-allocate",
408 G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this);
410 // Tab to search (the keyword box on the left hand side).
411 tab_to_search_full_label_ =
412 theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
413 tab_to_search_partial_label_ =
414 theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
415 GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0);
416 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
417 tab_to_search_full_label_, FALSE, FALSE, 0);
418 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
419 tab_to_search_partial_label_, FALSE, FALSE, 0);
420 GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0);
421 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
422 tab_to_search_magnifier_ = gtk_image_new_from_pixbuf(
423 rb.GetNativeImageNamed(IDR_KEYWORD_SEARCH_MAGNIFIER).ToGdkPixbuf());
424 gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_,
425 FALSE, FALSE, 0);
426 gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox,
427 false, 0);
429 // This creates a box around the keyword text with a border, background color,
430 // and padding around the text.
431 tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
432 tab_to_search_hbox, NULL, 1, 1, 1, 3);
433 gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box");
434 gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor,
435 kCornerSize,
436 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
438 // Put the event box in an alignment to get the padding correct.
439 tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1);
440 gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_),
441 tab_to_search_box_);
442 gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_,
443 FALSE, FALSE, 0);
445 // Show all children widgets of |tab_to_search_box_| initially, except
446 // |tab_to_search_partial_label_|.
447 gtk_widget_show_all(tab_to_search_box_);
448 gtk_widget_hide(tab_to_search_partial_label_);
450 omnibox_view_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
451 gtk_container_add(GTK_CONTAINER(omnibox_view_alignment_),
452 omnibox_view_->GetNativeView());
453 gtk_box_pack_start(GTK_BOX(entry_box_), omnibox_view_alignment_,
454 TRUE, TRUE, 0);
456 // Tab to search notification (the hint on the right hand side).
457 tab_to_search_hint_ = gtk_hbox_new(FALSE, 0);
458 gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint");
459 tab_to_search_hint_leading_label_ =
460 theme_service_->BuildLabel(std::string(), kHintTextColor);
461 gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE);
462 tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf(
463 rb.GetNativeImageNamed(IDR_OMNIBOX_KEYWORD_HINT_TAB).ToGdkPixbuf());
464 tab_to_search_hint_trailing_label_ =
465 theme_service_->BuildLabel(std::string(), kHintTextColor);
466 gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE);
467 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
468 tab_to_search_hint_leading_label_,
469 FALSE, FALSE, 0);
470 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
471 tab_to_search_hint_icon_,
472 FALSE, FALSE, 0);
473 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
474 tab_to_search_hint_trailing_label_,
475 FALSE, FALSE, 0);
476 // Show all children widgets of |tab_to_search_hint_| initially.
477 gtk_widget_show_all(tab_to_search_hint_);
478 gtk_widget_hide(tab_to_search_hint_);
479 // tab_to_search_hint_ gets hidden initially in OnChanged. Hiding it here
480 // doesn't work, someone is probably calling show_all on our parent box.
481 gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
483 if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
484 // Hide the star icon in popups, app windows, etc.
485 CreateStarButton();
486 gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
489 CreateScriptBubbleButton();
490 gtk_box_pack_end(GTK_BOX(hbox_.get()), script_bubble_button_.get(), FALSE,
491 FALSE, 0);
493 CreateZoomButton();
494 gtk_box_pack_end(GTK_BOX(hbox_.get()), zoom_.get(), FALSE, FALSE, 0);
496 CreateManagePasswordsIconButton();
497 gtk_box_pack_end(GTK_BOX(hbox_.get()), manage_passwords_icon_.get(), FALSE,
498 FALSE, 0);
500 content_setting_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding() + 1));
501 gtk_widget_set_name(content_setting_hbox_.get(),
502 "chrome-content-setting-hbox");
503 gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(),
504 FALSE, FALSE, 1);
506 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
507 ContentSettingImageViewGtk* content_setting_view =
508 new ContentSettingImageViewGtk(
509 static_cast<ContentSettingsType>(i), this);
510 content_setting_views_.push_back(content_setting_view);
511 gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()),
512 content_setting_view->widget(), FALSE, FALSE, 0);
515 page_action_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
516 gtk_widget_set_name(page_action_hbox_.get(),
517 "chrome-page-action-hbox");
518 gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(),
519 FALSE, FALSE, 0);
521 // Now that we've created the widget hierarchy, connect to the main |hbox_|'s
522 // size-allocate so we can do proper resizing and eliding on
523 // |security_info_label_|.
524 g_signal_connect(hbox_.get(), "size-allocate",
525 G_CALLBACK(&OnHboxSizeAllocateThunk), this);
527 registrar_.Add(this,
528 chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
529 content::Source<ThemeService>(theme_service_));
530 registrar_.Add(this,
531 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
532 content::Source<Profile>(profile()));
533 edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
534 profile()->GetPrefs(),
535 base::Bind(&LocationBarViewGtk::UpdateStarIcon,
536 base::Unretained(this)));
538 theme_service_->InitThemesFor(this);
541 void LocationBarViewGtk::SetPreviewEnabledPageAction(
542 ExtensionAction* page_action,
543 bool preview_enabled) {
544 DCHECK(page_action);
545 for (ScopedVector<PageActionViewGtk>::iterator iter =
546 page_action_views_.begin(); iter != page_action_views_.end();
547 ++iter) {
548 if ((*iter)->page_action() == page_action) {
549 (*iter)->set_preview_enabled(preview_enabled);
550 UpdatePageActions();
551 return;
556 GtkWidget* LocationBarViewGtk::GetPageActionWidget(
557 ExtensionAction* page_action) {
558 DCHECK(page_action);
559 for (ScopedVector<PageActionViewGtk>::iterator iter =
560 page_action_views_.begin();
561 iter != page_action_views_.end();
562 ++iter) {
563 if ((*iter)->page_action() == page_action)
564 return (*iter)->widget();
566 return NULL;
569 void LocationBarViewGtk::ShowStarBubble(const GURL& url,
570 bool newly_bookmarked) {
571 if (!star_.get())
572 return;
574 if (star_sized_) {
575 BookmarkBubbleGtk::Show(star_.get(), profile(), url, newly_bookmarked);
576 } else {
577 on_star_sized_ = base::Bind(&BookmarkBubbleGtk::Show, star_.get(),
578 profile(), url, newly_bookmarked);
582 void LocationBarViewGtk::ShowManagePasswordsBubble() {
583 if (GetToolbarModel()->input_in_progress() || !GetWebContents())
584 return;
586 ManagePasswordsBubbleGtk::ShowBubble(GetWebContents());
589 void LocationBarViewGtk::ZoomChangedForActiveTab(bool can_show_bubble) {
590 UpdateZoomIcon();
592 if (can_show_bubble && gtk_widget_get_visible(zoom_.get()))
593 ShowZoomBubble();
596 void LocationBarViewGtk::SetStarred(bool starred) {
597 if (starred == starred_)
598 return;
600 starred_ = starred;
601 UpdateStarIcon();
604 void LocationBarViewGtk::Update(const WebContents* contents) {
605 UpdateZoomIcon();
606 UpdateScriptBubbleIcon();
607 UpdateStarIcon();
608 UpdateSiteTypeArea();
609 UpdateContentSettingsIcons();
610 UpdatePageActions();
611 if (contents)
612 omnibox_view_->OnTabChanged(contents);
613 else
614 omnibox_view_->Update();
615 // The security level (background color) could have changed, etc.
616 if (theme_service_->UsingNativeTheme()) {
617 // In GTK mode, we need our parent to redraw, as it draws the text entry
618 // border.
619 gtk_widget_queue_draw(gtk_widget_get_parent(widget()));
620 } else {
621 gtk_widget_queue_draw(widget());
623 ZoomBubbleGtk::CloseBubble();
626 void LocationBarViewGtk::OnChanged() {
627 UpdateSiteTypeArea();
629 const base::string16 keyword(omnibox_view_->model()->keyword());
630 const bool is_keyword_hint = omnibox_view_->model()->is_keyword_hint();
631 show_selected_keyword_ = !keyword.empty() && !is_keyword_hint;
632 show_keyword_hint_ = !keyword.empty() && is_keyword_hint;
634 if (show_selected_keyword_)
635 SetKeywordLabel(keyword);
637 if (show_keyword_hint_)
638 SetKeywordHintLabel(keyword);
640 AdjustChildrenVisibility();
643 void LocationBarViewGtk::OnSetFocus() {
644 AccessibilityTextBoxInfo info(profile(),
645 l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION),
646 std::string(), false);
647 ExtensionAccessibilityEventRouter::GetInstance()->HandleControlEvent(
648 ui::AccessibilityTypes::EVENT_FOCUS, &info);
650 // Update the keyword and search hint states.
651 OnChanged();
654 InstantController* LocationBarViewGtk::GetInstant() {
655 return browser_->instant_controller() ?
656 browser_->instant_controller()->instant() : NULL;
659 WebContents* LocationBarViewGtk::GetWebContents() {
660 return browser_->tab_strip_model()->GetActiveWebContents();
663 ToolbarModel* LocationBarViewGtk::GetToolbarModel() {
664 return browser_->toolbar_model();
667 const ToolbarModel* LocationBarViewGtk::GetToolbarModel() const {
668 return browser_->toolbar_model();
671 void LocationBarViewGtk::ShowFirstRunBubble() {
672 // We need the browser window to be shown before we can show the bubble, but
673 // we get called before that's happened.
674 base::MessageLoop::current()->PostTask(
675 FROM_HERE,
676 base::Bind(&LocationBarViewGtk::ShowFirstRunBubbleInternal,
677 weak_ptr_factory_.GetWeakPtr()));
680 GURL LocationBarViewGtk::GetDestinationURL() const {
681 return destination_url();
684 WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
685 return disposition();
688 content::PageTransition LocationBarViewGtk::GetPageTransition() const {
689 return transition();
692 void LocationBarViewGtk::AcceptInput() {
693 omnibox_view_->model()->AcceptInput(CURRENT_TAB, false);
696 void LocationBarViewGtk::FocusLocation(bool select_all) {
697 omnibox_view_->SetFocus();
698 if (select_all)
699 omnibox_view_->SelectAll(true);
702 void LocationBarViewGtk::FocusSearch() {
703 omnibox_view_->SetFocus();
704 omnibox_view_->SetForcedQuery();
707 void LocationBarViewGtk::UpdateContentSettingsIcons() {
708 bool any_visible = false;
709 for (ScopedVector<PageToolViewGtk>::iterator i(
710 content_setting_views_.begin());
711 i != content_setting_views_.end(); ++i) {
712 (*i)->Update(GetToolbarModel()->input_in_progress() ?
713 NULL : GetWebContents());
714 any_visible = (*i)->IsVisible() || any_visible;
716 // If there are no visible content things, hide the top level box so it
717 // doesn't mess with padding.
718 gtk_widget_set_visible(content_setting_hbox_.get(), any_visible);
721 void LocationBarViewGtk::UpdateManagePasswordsIconAndBubble() {
722 UpdateManagePasswordsIcon();
725 void LocationBarViewGtk::UpdatePageActions() {
726 UpdateScriptBubbleIcon();
728 std::vector<ExtensionAction*> new_page_actions;
730 WebContents* contents = GetWebContents();
731 if (contents) {
732 LocationBarController* location_bar_controller =
733 extensions::TabHelper::FromWebContents(contents)->
734 location_bar_controller();
735 new_page_actions = location_bar_controller->GetCurrentActions();
738 // Initialize on the first call, or re-initialize if more extensions have been
739 // loaded or added after startup.
740 if (new_page_actions != page_actions_) {
741 page_actions_.swap(new_page_actions);
742 page_action_views_.clear();
744 for (size_t i = 0; i < page_actions_.size(); ++i) {
745 page_action_views_.push_back(
746 new PageActionViewGtk(this, page_actions_[i]));
747 gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()),
748 page_action_views_[i]->widget(), FALSE, FALSE, 0);
750 content::NotificationService::current()->Notify(
751 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
752 content::Source<LocationBar>(this),
753 content::NotificationService::NoDetails());
756 if (!page_action_views_.empty() && contents) {
757 GURL url = GetWebContents()->GetURL();
759 for (size_t i = 0; i < page_action_views_.size(); i++) {
760 page_action_views_[i]->UpdateVisibility(
761 GetToolbarModel()->input_in_progress() ? NULL : contents, url);
763 gtk_widget_queue_draw(hbox_.get());
766 // If there are no visible page actions, hide the hbox too, so that it does
767 // not affect the padding in the location bar.
768 gtk_widget_set_visible(page_action_hbox_.get(),
769 PageActionVisibleCount() && !ShouldOnlyShowLocation());
772 void LocationBarViewGtk::InvalidatePageActions() {
773 size_t count_before = page_action_views_.size();
774 page_action_views_.clear();
775 if (page_action_views_.size() != count_before) {
776 content::NotificationService::current()->Notify(
777 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
778 content::Source<LocationBar>(this),
779 content::NotificationService::NoDetails());
783 void LocationBarViewGtk::UpdateOpenPDFInReaderPrompt() {
784 // Not implemented on Gtk.
787 void LocationBarViewGtk::UpdateGeneratedCreditCardView() {
788 NOTIMPLEMENTED();
791 void LocationBarViewGtk::SaveStateToContents(WebContents* contents) {
792 omnibox_view_->SaveStateToTab(contents);
795 void LocationBarViewGtk::Revert() {
796 omnibox_view_->RevertAll();
799 const OmniboxView* LocationBarViewGtk::GetOmniboxView() const {
800 return omnibox_view_.get();
803 OmniboxView* LocationBarViewGtk::GetOmniboxView() {
804 return omnibox_view_.get();
807 LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
808 return this;
811 int LocationBarViewGtk::PageActionCount() {
812 return page_action_views_.size();
815 int LocationBarViewGtk::PageActionVisibleCount() {
816 int count = 0;
817 gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
818 CountVisibleWidgets, &count);
819 return count;
822 ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) {
823 if (index >= page_action_views_.size()) {
824 NOTREACHED();
825 return NULL;
828 return page_action_views_[index]->page_action();
831 ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) {
832 size_t visible_index = 0;
833 for (size_t i = 0; i < page_action_views_.size(); ++i) {
834 if (page_action_views_[i]->IsVisible()) {
835 if (index == visible_index++)
836 return page_action_views_[i]->page_action();
840 NOTREACHED();
841 return NULL;
844 void LocationBarViewGtk::TestPageActionPressed(size_t index) {
845 if (index >= page_action_views_.size()) {
846 NOTREACHED();
847 return;
850 page_action_views_[index]->TestActivatePageAction();
853 bool LocationBarViewGtk::GetBookmarkStarVisibility() {
854 return starred_;
857 void LocationBarViewGtk::Observe(int type,
858 const content::NotificationSource& source,
859 const content::NotificationDetails& details) {
860 switch (type) {
861 case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: {
862 // Only update if the updated action box was for the active tab contents.
863 WebContents* target_tab = content::Details<WebContents>(details).ptr();
864 if (target_tab == GetWebContents())
865 UpdatePageActions();
866 break;
869 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
870 if (theme_service_->UsingNativeTheme()) {
871 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL);
873 GdkColor border_color = theme_service_->GetGdkColor(
874 ThemeProperties::COLOR_FRAME);
875 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
877 gtk_util::UndoForceFontSize(security_info_label_);
878 gtk_util::UndoForceFontSize(tab_to_search_full_label_);
879 gtk_util::UndoForceFontSize(tab_to_search_partial_label_);
880 gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_);
881 gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_);
883 gtk_alignment_set_padding(GTK_ALIGNMENT(omnibox_view_alignment_),
884 0, 0, 0, 0);
885 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
886 1, 1, 1, 0);
887 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
888 1, 1, 1, 0);
889 } else {
890 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
891 &kKeywordBackgroundColor);
892 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
893 kKeywordBorderColor);
895 // Until we switch to vector graphics, force the font size of labels.
896 // 12.1px = 9pt @ 96dpi
897 gtk_util::ForceFontSizePixels(security_info_label_, 12.1);
898 gtk_util::ForceFontSizePixels(tab_to_search_full_label_,
899 browser_defaults::kOmniboxFontPixelSize);
900 gtk_util::ForceFontSizePixels(tab_to_search_partial_label_,
901 browser_defaults::kOmniboxFontPixelSize);
902 gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_,
903 browser_defaults::kOmniboxFontPixelSize);
904 gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_,
905 browser_defaults::kOmniboxFontPixelSize);
907 const int top_bottom = popup_window_mode_ ? kPopupEdgeThickness : 0;
908 gtk_alignment_set_padding(GTK_ALIGNMENT(omnibox_view_alignment_),
909 kTopMargin + kBorderThickness,
910 kBottomMargin + kBorderThickness,
911 top_bottom, top_bottom);
912 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
913 1, 1, 0, 0);
914 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
915 1, 1, 0, 0);
918 UpdateZoomIcon();
919 UpdateManagePasswordsIcon();
920 UpdateScriptBubbleIcon();
921 UpdateStarIcon();
922 UpdateSiteTypeArea();
923 UpdateContentSettingsIcons();
924 break;
927 default:
928 NOTREACHED();
932 void LocationBarViewGtk::BuildSiteTypeArea() {
933 location_icon_image_ = gtk_image_new();
934 gtk_widget_set_name(location_icon_image_, "chrome-location-icon");
936 GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1);
937 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0);
938 gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_);
939 gtk_widget_show_all(icon_alignment);
941 security_info_label_ = gtk_label_new(NULL);
942 gtk_label_set_ellipsize(GTK_LABEL(security_info_label_),
943 PANGO_ELLIPSIZE_MIDDLE);
944 gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL,
945 &kEvSecureTextColor);
946 gtk_widget_set_name(security_info_label_,
947 "chrome-location-bar-security-info-label");
949 GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1);
950 gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment,
951 FALSE, FALSE, 0);
952 gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_,
953 FALSE, FALSE, 2);
955 site_type_event_box_ = gtk_event_box_new();
956 gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL,
957 &kEvSecureBackgroundColor);
958 g_signal_connect(site_type_event_box_, "drag-data-get",
959 G_CALLBACK(&OnIconDragDataThunk), this);
960 g_signal_connect(site_type_event_box_, "drag-begin",
961 G_CALLBACK(&OnIconDragBeginThunk), this);
962 g_signal_connect(site_type_event_box_, "drag-end",
963 G_CALLBACK(&OnIconDragEndThunk), this);
965 // Make the event box not visible so it does not paint a background.
966 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
967 FALSE);
968 gtk_widget_set_name(site_type_event_box_,
969 "chrome-location-icon-eventbox");
970 gtk_container_add(GTK_CONTAINER(site_type_event_box_),
971 site_type_hbox);
973 // Put the event box in an alignment to get the padding correct.
974 site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1);
975 gtk_container_add(GTK_CONTAINER(site_type_alignment_),
976 site_type_event_box_);
977 gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_,
978 FALSE, FALSE, 0);
980 gtk_widget_set_tooltip_text(location_icon_image_,
981 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
983 g_signal_connect(site_type_event_box_, "button-release-event",
984 G_CALLBACK(&OnIconReleasedThunk), this);
987 void LocationBarViewGtk::SetSiteTypeDragSource() {
988 bool enable = !GetOmniboxView()->IsEditingOrEmpty();
989 if (enable_location_drag_ == enable)
990 return;
991 enable_location_drag_ = enable;
993 if (!enable) {
994 gtk_drag_source_unset(site_type_event_box_);
995 return;
998 gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK,
999 NULL, 0, GDK_ACTION_COPY);
1000 ui::SetSourceTargetListFromCodeMask(site_type_event_box_,
1001 ui::TEXT_PLAIN |
1002 ui::TEXT_URI_LIST |
1003 ui::CHROME_NAMED_URL);
1006 gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
1007 GdkEventExpose* event) {
1008 // If we're not using GTK theming, draw our own border over the edge pixels
1009 // of the background.
1010 GtkThemeService* theme_service = GtkThemeService::GetFrom(profile());
1011 if (!theme_service->UsingNativeTheme()) {
1012 // Perform a scoped paint to fill in the background color.
1014 gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
1016 GtkAllocation allocation;
1017 gtk_widget_get_allocation(widget, &allocation);
1019 int thickness = popup_window_mode_ ?
1020 kPopupEdgeThickness : kNormalEdgeThickness;
1021 gfx::Rect bounds(allocation);
1022 bounds.Inset(thickness, thickness);
1024 const SkColor color = SK_ColorWHITE;
1025 if (popup_window_mode_) {
1026 canvas.FillRect(bounds, color);
1027 } else {
1028 SkPaint paint;
1029 paint.setStyle(SkPaint::kFill_Style);
1030 paint.setColor(color);
1031 const int kBorderCornerRadius = 2;
1032 canvas.DrawRoundRect(bounds, kBorderCornerRadius, paint);
1036 if (popup_window_mode_) {
1037 NineBox(IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP_LEFT,
1038 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP,
1039 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP_RIGHT,
1040 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_LEFT,
1041 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_CENTER,
1042 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_RIGHT,
1043 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM_LEFT,
1044 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM,
1045 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM_RIGHT).
1046 RenderToWidget(widget);
1047 } else {
1048 NineBox(IDR_OMNIBOX_BORDER_AND_SHADOW_TOP_LEFT,
1049 IDR_OMNIBOX_BORDER_AND_SHADOW_TOP,
1050 IDR_OMNIBOX_BORDER_AND_SHADOW_TOP_RIGHT,
1051 IDR_OMNIBOX_BORDER_AND_SHADOW_LEFT,
1052 IDR_OMNIBOX_BORDER_AND_SHADOW_CENTER,
1053 IDR_OMNIBOX_BORDER_AND_SHADOW_RIGHT,
1054 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM_LEFT,
1055 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM,
1056 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM_RIGHT).
1057 RenderToWidget(widget);
1061 // Draw ExtensionAction backgrounds and borders, if necessary. The borders
1062 // appear exactly between the elements, so they can't draw the borders
1063 // themselves.
1064 gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
1065 for (ScopedVector<PageActionViewGtk>::const_iterator
1066 page_action_view = page_action_views_.begin();
1067 page_action_view != page_action_views_.end();
1068 ++page_action_view) {
1069 if ((*page_action_view)->IsVisible()) {
1070 // Figure out where the page action is drawn so we can draw
1071 // borders to its left and right.
1072 GtkAllocation allocation;
1073 gtk_widget_get_allocation((*page_action_view)->widget(), &allocation);
1074 ExtensionAction* action = (*page_action_view)->page_action();
1075 gfx::Rect bounds(allocation);
1076 // Make the bounding rectangle include the whole vertical range of the
1077 // location bar, and the mid-point pixels between adjacent page actions.
1079 // For odd InnerPadding()s, "InnerPadding() + 1" includes the mid-point
1080 // between two page actions in the bounding rectangle. For even paddings,
1081 // the +1 is dropped, which is right since there is no pixel at the
1082 // mid-point.
1083 bounds.Inset(-(InnerPadding() + 1) / 2,
1084 theme_service_->UsingNativeTheme() ? -1 : 0);
1085 location_bar_util::PaintExtensionActionBackground(
1086 *action, SessionID::IdForTab(GetWebContents()),
1087 &canvas, bounds,
1088 theme_service_->get_location_bar_text_color(),
1089 theme_service_->get_location_bar_bg_color());
1092 // Destroying |canvas| draws the background.
1094 return FALSE; // Continue propagating the expose.
1097 gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender,
1098 GdkEventButton* event) {
1099 WebContents* tab = GetWebContents();
1101 if (event->button == 1) {
1102 // Do not show page info if the user has been editing the location
1103 // bar, or the location bar is at the NTP.
1104 if (GetOmniboxView()->IsEditingOrEmpty())
1105 return FALSE;
1107 // (0,0) event coordinates indicates that the release came at the end of
1108 // a drag.
1109 if (event->x == 0 && event->y == 0)
1110 return FALSE;
1112 // Important to use GetVisibleEntry to match what's showing in the omnibox.
1113 NavigationEntry* nav_entry = tab->GetController().GetVisibleEntry();
1114 if (!nav_entry) {
1115 NOTREACHED();
1116 return FALSE;
1118 chrome::ShowWebsiteSettings(browser_, tab, nav_entry->GetURL(),
1119 nav_entry->GetSSL());
1120 return TRUE;
1121 } else if (event->button == 2) {
1122 // When the user middle clicks on the location icon, try to open the
1123 // contents of the PRIMARY selection in the current tab.
1124 // If the click was outside our bounds, do nothing.
1125 if (!gtk_util::WidgetBounds(sender).Contains(
1126 gfx::Point(event->x, event->y))) {
1127 return FALSE;
1130 GURL url;
1131 if (!gtk_util::URLFromPrimarySelection(profile(), &url))
1132 return FALSE;
1134 tab->OpenURL(OpenURLParams(
1135 url, content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1136 false));
1137 return TRUE;
1140 return FALSE;
1143 void LocationBarViewGtk::OnIconDragData(GtkWidget* sender,
1144 GdkDragContext* context,
1145 GtkSelectionData* data,
1146 guint info, guint time) {
1147 ui::WriteURLWithName(data, drag_url_, drag_title_, info);
1150 void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender,
1151 GdkDragContext* context) {
1152 content::WebContents* web_contents = GetWebContents();
1153 gfx::Image favicon =
1154 FaviconTabHelper::FromWebContents(web_contents)->GetFavicon();
1155 if (favicon.IsEmpty())
1156 return;
1157 drag_url_ = web_contents->GetURL();
1158 drag_title_ = web_contents->GetTitle();
1159 drag_icon_ = GetDragRepresentation(favicon.ToGdkPixbuf(), drag_title_,
1160 theme_service_);
1161 gtk_drag_set_icon_widget(context, drag_icon_, 0, 0);
1164 void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
1165 GdkDragContext* context) {
1166 DCHECK(drag_icon_);
1167 gtk_widget_destroy(drag_icon_);
1168 drag_icon_ = NULL;
1169 drag_url_ = GURL::EmptyGURL();
1170 drag_title_.clear();
1173 void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
1174 GtkAllocation* allocation) {
1175 if (hbox_width_ != allocation->width) {
1176 hbox_width_ = allocation->width;
1177 UpdateEVCertificateLabelSize();
1179 if (browser_ && browser_->instant_controller()) {
1180 browser_->instant_controller()->
1181 SetOmniboxBounds(AllocationToRect(*allocation));
1185 void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender,
1186 GtkAllocation* allocation) {
1187 if (entry_box_width_ != allocation->width) {
1188 entry_box_width_ = allocation->width;
1189 AdjustChildrenVisibility();
1193 gboolean LocationBarViewGtk::OnZoomButtonPress(GtkWidget* widget,
1194 GdkEventButton* event) {
1195 if (event->button == 1 && GetWebContents()) {
1196 // If the zoom icon is clicked, show the zoom bubble and keep it open until
1197 // it loses focus.
1198 ZoomBubbleGtk::ShowBubble(GetWebContents(), false);
1199 return TRUE;
1201 return FALSE;
1204 gboolean LocationBarViewGtk::OnManagePasswordsIconButtonPress(
1205 GtkWidget* widget, GdkEventButton* event) {
1206 if (event->button == 1 && GetWebContents()) {
1207 // If the manage passwords icon is clicked, show the manage passwords bubble
1208 // and keep it open until the user makes a choice or clicks outside the
1209 // bubble.
1210 ManagePasswordsBubbleGtk::ShowBubble(GetWebContents());
1211 return TRUE;
1213 return FALSE;
1216 gboolean LocationBarViewGtk::OnScriptBubbleButtonPress(GtkWidget* widget,
1217 GdkEventButton* event) {
1218 if (event->button == 1 && GetWebContents()) {
1219 ScriptBubbleGtk::Show(script_bubble_button_image_, GetWebContents());
1220 return TRUE;
1222 return FALSE;
1225 void LocationBarViewGtk::OnStarButtonSizeAllocate(GtkWidget* sender,
1226 GtkAllocation* allocation) {
1227 if (!on_star_sized_.is_null()) {
1228 on_star_sized_.Run();
1229 on_star_sized_.Reset();
1231 star_sized_ = true;
1234 gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
1235 GdkEventButton* event) {
1236 if (event->button != 1)
1237 return FALSE;
1238 chrome::ExecuteCommand(browser_, IDC_BOOKMARK_PAGE_FROM_STAR);
1239 return TRUE;
1242 gboolean LocationBarViewGtk::OnScriptBubbleButtonExpose(GtkWidget* widget,
1243 GdkEventExpose* event) {
1244 gfx::CanvasSkiaPaint canvas(event, false);
1245 GtkAllocation allocation;
1246 gtk_widget_get_allocation(widget, &allocation);
1247 badge_util::PaintBadge(&canvas,
1248 gfx::Rect(allocation),
1249 base::UintToString(num_running_scripts_),
1250 SK_ColorWHITE,
1251 SkColorSetRGB(0, 170, 0),
1252 allocation.width,
1253 extensions::ActionInfo::TYPE_PAGE);
1254 return FALSE;
1257 void LocationBarViewGtk::UpdateSiteTypeArea() {
1258 // The icon is always visible except when the |tab_to_search_alignment_| is
1259 // visible.
1260 if (!omnibox_view_->model()->keyword().empty() &&
1261 !omnibox_view_->model()->is_keyword_hint()) {
1262 gtk_widget_hide(site_type_area());
1263 return;
1266 int resource_id = omnibox_view_->GetIcon();
1267 gtk_image_set_from_pixbuf(
1268 GTK_IMAGE(location_icon_image_),
1269 theme_service_->GetImageNamed(resource_id).ToGdkPixbuf());
1271 if (GetToolbarModel()->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) {
1272 if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1273 // Fun fact: If wee try to make |site_type_event_box_| act as a
1274 // rounded window while it doesn't have a visible window, GTK interprets
1275 // this as a sign that it should paint the skyline texture into the
1276 // omnibox.
1277 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1278 TRUE);
1280 gtk_util::ActAsRoundedWindow(site_type_event_box_,
1281 kEvSecureBorderColor,
1282 kCornerSize,
1283 gtk_util::ROUNDED_ALL,
1284 gtk_util::BORDER_ALL);
1287 base::string16 info_text = GetToolbarModel()->GetEVCertName();
1288 gtk_label_set_text(GTK_LABEL(security_info_label_),
1289 base::UTF16ToUTF8(info_text).c_str());
1291 UpdateEVCertificateLabelSize();
1293 gtk_widget_show(GTK_WIDGET(security_info_label_));
1294 } else {
1295 if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1296 gtk_util::StopActingAsRoundedWindow(site_type_event_box_);
1298 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1299 FALSE);
1302 gtk_widget_hide(GTK_WIDGET(security_info_label_));
1305 if (GetOmniboxView()->IsEditingOrEmpty()) {
1306 // Do not show the tooltip if the user has been editing the location
1307 // bar, or the location bar is at the NTP.
1308 gtk_widget_set_tooltip_text(location_icon_image_, "");
1309 } else {
1310 gtk_widget_set_tooltip_text(location_icon_image_,
1311 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
1314 gtk_widget_show(site_type_area());
1316 SetSiteTypeDragSource();
1319 void LocationBarViewGtk::UpdateEVCertificateLabelSize() {
1320 // Figure out the width of the average character.
1321 PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_));
1322 PangoContext* context = pango_layout_get_context(layout);
1323 PangoFontMetrics* metrics = pango_context_get_metrics(
1324 context,
1325 gtk_widget_get_style(security_info_label_)->font_desc,
1326 pango_context_get_language(context));
1327 int char_width =
1328 pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE;
1330 // The EV label should never take up more than half the hbox. We try to
1331 // correct our inaccurate measurement units ("the average character width")
1332 // by dividing more than an even 2.
1333 GtkAllocation security_label_allocation;
1334 gtk_widget_get_allocation(security_info_label_, &security_label_allocation);
1335 GtkAllocation entry_box_allocation;
1336 gtk_widget_get_allocation(entry_box_, &entry_box_allocation);
1337 int text_area = security_label_allocation.width +
1338 entry_box_allocation.width;
1339 int max_chars = static_cast<int>(static_cast<float>(text_area) /
1340 static_cast<float>(char_width) / 2.75);
1341 // Don't let the label be smaller than 10 characters so that the country
1342 // code is always visible.
1343 gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_),
1344 std::max(10, max_chars));
1346 pango_font_metrics_unref(metrics);
1349 void LocationBarViewGtk::SetKeywordLabel(const base::string16& keyword) {
1350 if (keyword.empty())
1351 return;
1353 TemplateURLService* template_url_service =
1354 TemplateURLServiceFactory::GetForProfile(profile());
1355 if (!template_url_service)
1356 return;
1358 bool is_extension_keyword;
1359 const base::string16 short_name = template_url_service->GetKeywordShortName(
1360 keyword, &is_extension_keyword);
1361 const base::string16 min_string =
1362 location_bar_util::CalculateMinString(short_name);
1363 const base::string16 full_name = is_extension_keyword ?
1364 short_name :
1365 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, short_name);
1366 const base::string16 partial_name = is_extension_keyword ?
1367 min_string :
1368 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, min_string);
1369 gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_),
1370 base::UTF16ToUTF8(full_name).c_str());
1371 gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_),
1372 base::UTF16ToUTF8(partial_name).c_str());
1374 if (last_keyword_ != keyword) {
1375 last_keyword_ = keyword;
1377 if (is_extension_keyword) {
1378 const TemplateURL* template_url =
1379 template_url_service->GetTemplateURLForKeyword(keyword);
1380 gfx::Image image = extensions::OmniboxAPI::Get(profile())->
1381 GetOmniboxIcon(template_url->GetExtensionId());
1382 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1383 image.ToGdkPixbuf());
1384 } else {
1385 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1386 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1387 rb.GetNativeImageNamed(IDR_OMNIBOX_SEARCH).ToGdkPixbuf());
1392 void LocationBarViewGtk::SetKeywordHintLabel(const base::string16& keyword) {
1393 if (keyword.empty())
1394 return;
1396 TemplateURLService* template_url_service =
1397 TemplateURLServiceFactory::GetForProfile(profile());
1398 if (!template_url_service)
1399 return;
1401 bool is_extension_keyword;
1402 const base::string16 short_name = template_url_service->
1403 GetKeywordShortName(keyword, &is_extension_keyword);
1404 int message_id = is_extension_keyword ?
1405 IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT;
1406 std::vector<size_t> content_param_offsets;
1407 const base::string16 keyword_hint = l10n_util::GetStringFUTF16(
1408 message_id,
1409 base::string16(),
1410 short_name,
1411 &content_param_offsets);
1412 if (content_param_offsets.size() != 2) {
1413 // See comments on an identical NOTREACHED() in search_provider.cc.
1414 NOTREACHED();
1415 return;
1418 std::string leading(base::UTF16ToUTF8(
1419 keyword_hint.substr(0, content_param_offsets.front())));
1420 std::string trailing(base::UTF16ToUTF8(
1421 keyword_hint.substr(content_param_offsets.front())));
1422 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_),
1423 leading.c_str());
1424 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_),
1425 trailing.c_str());
1428 void LocationBarViewGtk::ShowFirstRunBubbleInternal() {
1429 if (!omnibox_view_.get() || !gtk_widget_get_window(widget()))
1430 return;
1432 gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_);
1433 bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing);
1434 FirstRunBubble::Show(browser_, location_icon_image_, bounds);
1437 void LocationBarViewGtk::ShowZoomBubble() {
1438 if (GetToolbarModel()->input_in_progress() || !GetWebContents())
1439 return;
1441 ZoomBubbleGtk::ShowBubble(GetWebContents(), true);
1444 void LocationBarViewGtk::AdjustChildrenVisibility() {
1445 int text_width = omnibox_view_->GetTextWidth();
1446 int available_width = entry_box_width_ - text_width - InnerPadding();
1448 // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be
1449 // visible at the same time.
1450 if (!show_selected_keyword_ &&
1451 gtk_widget_get_visible(tab_to_search_alignment_)) {
1452 gtk_widget_hide(tab_to_search_alignment_);
1453 } else if (!show_keyword_hint_ &&
1454 gtk_widget_get_visible(tab_to_search_hint_)) {
1455 gtk_widget_hide(tab_to_search_hint_);
1458 if (show_selected_keyword_) {
1459 GtkRequisition box, full_label, partial_label;
1460 gtk_widget_size_request(tab_to_search_box_, &box);
1461 gtk_widget_size_request(tab_to_search_full_label_, &full_label);
1462 gtk_widget_size_request(tab_to_search_partial_label_, &partial_label);
1463 int full_partial_width_diff = full_label.width - partial_label.width;
1464 int full_box_width;
1465 int partial_box_width;
1466 if (gtk_widget_get_visible(tab_to_search_full_label_)) {
1467 full_box_width = box.width;
1468 partial_box_width = full_box_width - full_partial_width_diff;
1469 } else {
1470 partial_box_width = box.width;
1471 full_box_width = partial_box_width + full_partial_width_diff;
1474 if (partial_box_width >= entry_box_width_ - InnerPadding()) {
1475 gtk_widget_hide(tab_to_search_alignment_);
1476 } else if (full_box_width >= available_width) {
1477 gtk_widget_hide(tab_to_search_full_label_);
1478 gtk_widget_show(tab_to_search_partial_label_);
1479 gtk_widget_show(tab_to_search_alignment_);
1480 } else if (full_box_width < available_width) {
1481 gtk_widget_hide(tab_to_search_partial_label_);
1482 gtk_widget_show(tab_to_search_full_label_);
1483 gtk_widget_show(tab_to_search_alignment_);
1485 } else if (show_keyword_hint_) {
1486 GtkRequisition leading, icon, trailing;
1487 gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading);
1488 gtk_widget_size_request(tab_to_search_hint_icon_, &icon);
1489 gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing);
1490 int full_width = leading.width + icon.width + trailing.width;
1492 if (icon.width >= entry_box_width_ - InnerPadding()) {
1493 gtk_widget_hide(tab_to_search_hint_);
1494 } else if (full_width >= available_width) {
1495 gtk_widget_hide(tab_to_search_hint_leading_label_);
1496 gtk_widget_hide(tab_to_search_hint_trailing_label_);
1497 gtk_widget_show(tab_to_search_hint_);
1498 } else if (full_width < available_width) {
1499 gtk_widget_show(tab_to_search_hint_leading_label_);
1500 gtk_widget_show(tab_to_search_hint_trailing_label_);
1501 gtk_widget_show(tab_to_search_hint_);
1506 GtkWidget* LocationBarViewGtk::CreateIconButton(
1507 GtkWidget** image,
1508 int image_id,
1509 ViewID debug_id,
1510 int tooltip_id,
1511 gboolean (click_callback)(GtkWidget*, GdkEventButton*, gpointer)) {
1512 *image = image_id ?
1513 gtk_image_new_from_pixbuf(
1514 theme_service_->GetImageNamed(image_id).ToGdkPixbuf()) :
1515 gtk_image_new();
1517 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
1518 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
1519 0, InnerPadding());
1520 gtk_container_add(GTK_CONTAINER(alignment), *image);
1522 GtkWidget* result = gtk_event_box_new();
1523 gtk_event_box_set_visible_window(GTK_EVENT_BOX(result), FALSE);
1524 gtk_container_add(GTK_CONTAINER(result), alignment);
1525 gtk_widget_show_all(result);
1527 if (debug_id != VIEW_ID_NONE)
1528 ViewIDUtil::SetID(result, debug_id);
1530 if (tooltip_id) {
1531 gtk_widget_set_tooltip_text(result,
1532 l10n_util::GetStringUTF8(tooltip_id).c_str());
1535 g_signal_connect(result, "button-press-event",
1536 G_CALLBACK(click_callback), this);
1538 return result;
1541 void LocationBarViewGtk::CreateZoomButton() {
1542 zoom_.Own(CreateIconButton(&zoom_image_,
1544 VIEW_ID_ZOOM_BUTTON,
1546 OnZoomButtonPressThunk));
1549 void LocationBarViewGtk::CreateManagePasswordsIconButton() {
1550 manage_passwords_icon_.Own(CreateIconButton(
1551 &manage_passwords_icon_image_, 0, VIEW_ID_MANAGE_PASSWORDS_ICON_BUTTON, 0,
1552 OnManagePasswordsIconButtonPressThunk));
1555 void LocationBarViewGtk::CreateScriptBubbleButton() {
1556 script_bubble_button_.Own(CreateIconButton(&script_bubble_button_image_,
1558 VIEW_ID_SCRIPT_BUBBLE,
1559 IDS_TOOLTIP_SCRIPT_BUBBLE,
1560 OnScriptBubbleButtonPressThunk));
1561 gtk_image_set_from_pixbuf(
1562 GTK_IMAGE(script_bubble_button_image_),
1563 theme_service_->GetImageNamed(
1564 IDR_EXTENSIONS_SCRIPT_BUBBLE).ToGdkPixbuf());
1565 g_signal_connect_after(script_bubble_button_image_, "expose-event",
1566 G_CALLBACK(&OnScriptBubbleButtonExposeThunk), this);
1569 void LocationBarViewGtk::CreateStarButton() {
1570 star_.Own(CreateIconButton(&star_image_,
1572 VIEW_ID_STAR_BUTTON,
1573 IDS_TOOLTIP_STAR,
1574 OnStarButtonPressThunk));
1575 // We need to track when the star button is resized to show any bubble
1576 // attached to it at this time.
1577 g_signal_connect(star_image_, "size-allocate",
1578 G_CALLBACK(&OnStarButtonSizeAllocateThunk), this);
1581 void LocationBarViewGtk::UpdateZoomIcon() {
1582 WebContents* web_contents = GetWebContents();
1583 if (!zoom_.get() || !web_contents)
1584 return;
1586 ZoomController* zoom_controller =
1587 ZoomController::FromWebContents(web_contents);
1588 if (!zoom_controller || zoom_controller->IsAtDefaultZoom() ||
1589 GetToolbarModel()->input_in_progress()) {
1590 gtk_widget_hide(zoom_.get());
1591 ZoomBubbleGtk::CloseBubble();
1592 return;
1595 const int zoom_resource = zoom_controller->GetResourceForZoomLevel();
1596 gtk_image_set_from_pixbuf(GTK_IMAGE(zoom_image_),
1597 theme_service_->GetImageNamed(zoom_resource).ToGdkPixbuf());
1599 base::string16 tooltip = l10n_util::GetStringFUTF16Int(
1600 IDS_TOOLTIP_ZOOM, zoom_controller->zoom_percent());
1601 gtk_widget_set_tooltip_text(zoom_.get(), base::UTF16ToUTF8(tooltip).c_str());
1603 gtk_widget_show(zoom_.get());
1606 void LocationBarViewGtk::UpdateManagePasswordsIcon() {
1607 WebContents* web_contents = GetWebContents();
1608 if (!manage_passwords_icon_.get() || !web_contents)
1609 return;
1611 ManagePasswordsBubbleUIController* manage_passwords_bubble_ui_controller =
1612 ManagePasswordsBubbleUIController::FromWebContents(web_contents);
1613 if (!manage_passwords_bubble_ui_controller ||
1614 !manage_passwords_bubble_ui_controller->password_to_be_saved() ||
1615 GetToolbarModel()->input_in_progress()) {
1616 gtk_widget_hide(manage_passwords_icon_.get());
1617 ManagePasswordsBubbleGtk::CloseBubble();
1618 return;
1621 gtk_image_set_from_pixbuf(
1622 GTK_IMAGE(manage_passwords_icon_image_),
1623 theme_service_->GetImageNamed(IDR_SAVE_PASSWORD).ToGdkPixbuf());
1625 base::string16 tooltip =
1626 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_TOOLTIP_SAVE);
1627 gtk_widget_set_tooltip_text(manage_passwords_icon_.get(),
1628 base::UTF16ToUTF8(tooltip).c_str());
1630 gtk_widget_show(manage_passwords_icon_.get());
1631 if (manage_passwords_bubble_ui_controller->
1632 manage_passwords_bubble_needs_showing()) {
1633 ShowManagePasswordsBubble();
1634 manage_passwords_bubble_ui_controller->OnBubbleShown();
1638 void LocationBarViewGtk::UpdateScriptBubbleIcon() {
1639 num_running_scripts_ = 0;
1640 if (GetWebContents()) {
1641 extensions::TabHelper* tab_helper =
1642 extensions::TabHelper::FromWebContents(GetWebContents());
1643 if (tab_helper && tab_helper->script_bubble_controller()) {
1644 num_running_scripts_ = tab_helper->script_bubble_controller()->
1645 extensions_running_scripts().size();
1649 if (num_running_scripts_ == 0u)
1650 gtk_widget_hide(script_bubble_button_.get());
1651 else
1652 gtk_widget_show(script_bubble_button_.get());
1655 void LocationBarViewGtk::UpdateStarIcon() {
1656 if (!star_.get())
1657 return;
1658 // Indicate the star icon is not correctly sized. It will be marked as sized
1659 // when the next size-allocate signal is received by the star widget.
1660 star_sized_ = false;
1661 if (browser_defaults::bookmarks_enabled && !popup_window_mode_ &&
1662 !GetToolbarModel()->input_in_progress() &&
1663 edit_bookmarks_enabled_.GetValue() &&
1664 !IsBookmarkStarHiddenByExtension()) {
1665 gtk_widget_show_all(star_.get());
1666 int id = starred_ ? IDR_STAR_LIT : IDR_STAR;
1667 gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_),
1668 theme_service_->GetImageNamed(id).ToGdkPixbuf());
1669 gtk_widget_set_tooltip_text(star_.get(), l10n_util::GetStringUTF8(
1670 starred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR).c_str());
1671 } else {
1672 gtk_widget_hide_all(star_.get());
1676 bool LocationBarViewGtk::ShouldOnlyShowLocation() {
1677 return !browser_->is_type_tabbed();
1680 ////////////////////////////////////////////////////////////////////////////////
1681 // LocationBarViewGtk::PageToolViewGtk
1683 LocationBarViewGtk::PageToolViewGtk::PageToolViewGtk()
1684 : alignment_(gtk_alignment_new(0, 0, 1, 1)),
1685 event_box_(gtk_event_box_new()),
1686 hbox_(gtk_hbox_new(FALSE, InnerPadding())),
1687 image_(gtk_image_new()),
1688 label_(gtk_label_new(NULL)),
1689 animation_(this),
1690 weak_factory_(this) {
1691 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0);
1692 gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get());
1694 // Make the event box not visible so it does not paint a background.
1695 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1696 g_signal_connect(event_box_.get(), "button-press-event",
1697 G_CALLBACK(&OnButtonPressedThunk), this);
1698 g_signal_connect(event_box_.get(), "expose-event",
1699 G_CALLBACK(&OnExposeThunk), this);
1701 gtk_widget_set_no_show_all(label_.get(), TRUE);
1702 gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE);
1704 gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0);
1705 gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0);
1707 gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder);
1709 gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_);
1710 gtk_widget_hide(widget());
1712 animation_.SetSlideDuration(kPageToolAnimationTime);
1715 LocationBarViewGtk::PageToolViewGtk::~PageToolViewGtk() {
1716 image_.Destroy();
1717 label_.Destroy();
1718 event_box_.Destroy();
1719 alignment_.Destroy();
1722 bool LocationBarViewGtk::PageToolViewGtk::IsVisible() {
1723 return gtk_widget_get_visible(widget());
1726 void LocationBarViewGtk::PageToolViewGtk::AnimationProgressed(
1727 const gfx::Animation* animation) {
1728 gtk_widget_set_size_request(
1729 label_.get(),
1730 animation->GetCurrentValue() * label_req_.width,
1731 -1);
1734 void LocationBarViewGtk::PageToolViewGtk::AnimationEnded(
1735 const gfx::Animation* animation) {
1738 void LocationBarViewGtk::PageToolViewGtk::AnimationCanceled(
1739 const gfx::Animation* animation) {
1742 void LocationBarViewGtk::PageToolViewGtk::StartAnimating() {
1743 if (animation_.IsShowing() || animation_.IsClosing())
1744 return;
1746 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE);
1747 GdkColor border_color = GetButtonBorderColor();
1748 gtk_util::ActAsRoundedWindow(event_box_.get(), border_color,
1749 kCornerSize,
1750 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
1752 gtk_widget_set_size_request(label_.get(), -1, -1);
1753 gtk_widget_size_request(label_.get(), &label_req_);
1754 gtk_widget_set_size_request(label_.get(), 0, -1);
1755 gtk_widget_show(label_.get());
1757 animation_.Show();
1760 void LocationBarViewGtk::PageToolViewGtk::CloseAnimation() {
1761 animation_.Hide();
1764 gboolean LocationBarViewGtk::PageToolViewGtk::OnButtonPressed(
1765 GtkWidget* sender, GdkEvent* event) {
1766 OnClick(sender);
1767 return TRUE;
1770 gboolean LocationBarViewGtk::PageToolViewGtk::OnExpose(
1771 GtkWidget* sender, GdkEventExpose* event) {
1772 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageToolViewGtk::OnExpose");
1774 if (!(animation_.IsShowing() || animation_.IsClosing()))
1775 return FALSE;
1777 GtkAllocation allocation;
1778 gtk_widget_get_allocation(sender, &allocation);
1779 const int height = allocation.height;
1781 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
1782 gdk_cairo_rectangle(cr, &event->area);
1783 cairo_clip(cr);
1785 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
1787 const GdkColor top_color = GetGradientTopColor();
1788 const GdkColor bottom_color = GetGradientBottomColor();
1789 cairo_pattern_add_color_stop_rgb(
1790 pattern, 0.0,
1791 top_color.red/255.0,
1792 top_color.blue/255.0,
1793 top_color.green/255.0);
1794 cairo_pattern_add_color_stop_rgb(
1795 pattern, 1.0,
1796 bottom_color.red/255.0,
1797 bottom_color.blue/255.0,
1798 bottom_color.green/255.0);
1800 cairo_set_source(cr, pattern);
1801 cairo_paint(cr);
1802 cairo_pattern_destroy(pattern);
1803 cairo_destroy(cr);
1805 return FALSE;
1808 ////////////////////////////////////////////////////////////////////////////////
1809 // LocationBarViewGtk::PageActionViewGtk
1811 LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
1812 LocationBarViewGtk* owner,
1813 ExtensionAction* page_action)
1814 : owner_(NULL),
1815 page_action_(page_action),
1816 current_tab_id_(-1),
1817 window_(NULL),
1818 accel_group_(NULL),
1819 preview_enabled_(false),
1820 scoped_icon_animation_observer_(
1821 page_action->GetIconAnimation(
1822 SessionID::IdForTab(owner->GetWebContents())),
1823 this) {
1824 event_box_.Own(gtk_event_box_new());
1825 gtk_widget_set_size_request(event_box_.get(),
1826 extensions::IconsInfo::kPageActionIconMaxSize,
1827 extensions::IconsInfo::kPageActionIconMaxSize);
1829 // Make the event box not visible so it does not paint a background.
1830 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1831 g_signal_connect(event_box_.get(), "button-press-event",
1832 G_CALLBACK(&OnButtonPressedThunk), this);
1833 g_signal_connect_after(event_box_.get(), "expose-event",
1834 G_CALLBACK(OnExposeEventThunk), this);
1835 g_signal_connect(event_box_.get(), "realize",
1836 G_CALLBACK(OnRealizeThunk), this);
1838 image_.Own(gtk_image_new());
1839 gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get());
1841 const Extension* extension =
1842 owner->profile()->GetExtensionService()->GetExtensionById(
1843 page_action->extension_id(), false);
1844 DCHECK(extension);
1846 icon_factory_.reset(new ExtensionActionIconFactory(
1847 owner->profile(), extension, page_action, this));
1849 // We set the owner last of all so that we can determine whether we are in
1850 // the process of initializing this class or not.
1851 owner_ = owner;
1854 LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
1855 DisconnectPageActionAccelerator();
1857 image_.Destroy();
1858 event_box_.Destroy();
1861 bool LocationBarViewGtk::PageActionViewGtk::IsVisible() {
1862 return gtk_widget_get_visible(widget());
1865 void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
1866 WebContents* contents, const GURL& url) {
1867 // Save this off so we can pass it back to the extension when the action gets
1868 // executed. See PageActionImageView::OnMousePressed.
1869 current_tab_id_ =
1870 contents ? extensions::ExtensionTabUtil::GetTabId(contents) : -1;
1871 current_url_ = url;
1873 bool visible = contents &&
1874 (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_));
1875 if (visible) {
1876 // Set the tooltip.
1877 gtk_widget_set_tooltip_text(event_box_.get(),
1878 page_action_->GetTitle(current_tab_id_).c_str());
1880 // Set the image.
1881 gfx::Image icon = icon_factory_->GetIcon(current_tab_id_);
1882 if (!icon.IsEmpty()) {
1883 GdkPixbuf* pixbuf = icon.ToGdkPixbuf();
1884 DCHECK(pixbuf);
1885 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
1889 bool old_visible = IsVisible();
1890 if (visible)
1891 gtk_widget_show_all(event_box_.get());
1892 else
1893 gtk_widget_hide_all(event_box_.get());
1895 if (visible != old_visible) {
1896 content::NotificationService::current()->Notify(
1897 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
1898 content::Source<ExtensionAction>(page_action_),
1899 content::Details<WebContents>(contents));
1903 void LocationBarViewGtk::PageActionViewGtk::OnIconUpdated() {
1904 // If we have no owner, that means this class is still being constructed.
1905 WebContents* web_contents = owner_ ? owner_->GetWebContents() : NULL;
1906 if (web_contents)
1907 UpdateVisibility(web_contents, current_url_);
1910 void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
1911 GdkEventButton event = {};
1912 event.type = GDK_BUTTON_PRESS;
1913 event.button = 1;
1914 OnButtonPressed(widget(), &event);
1917 void LocationBarViewGtk::PageActionViewGtk::Observe(
1918 int type,
1919 const content::NotificationSource& source,
1920 const content::NotificationDetails& details) {
1921 DCHECK_EQ(type, chrome::NOTIFICATION_WINDOW_CLOSED);
1922 DisconnectPageActionAccelerator();
1925 void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
1926 ExtensionAction* action) {
1927 ExtensionPopupGtk::Show(
1928 action->GetPopupUrl(current_tab_id_),
1929 owner_->browser_,
1930 event_box_.get(),
1931 ExtensionPopupGtk::SHOW_AND_INSPECT);
1934 void LocationBarViewGtk::PageActionViewGtk::ConnectPageActionAccelerator() {
1935 const extensions::ExtensionSet* extensions =
1936 owner_->profile()->GetExtensionService()->extensions();
1937 const Extension* extension =
1938 extensions->GetByID(page_action_->extension_id());
1939 window_ = owner_->browser()->window()->GetNativeWindow();
1941 extensions::CommandService* command_service =
1942 extensions::CommandService::Get(owner_->profile());
1944 extensions::Command command_page_action;
1945 if (command_service->GetPageActionCommand(
1946 extension->id(),
1947 extensions::CommandService::ACTIVE_ONLY,
1948 &command_page_action,
1949 NULL)) {
1950 // Found the page action shortcut command, register it.
1951 page_action_keybinding_.reset(
1952 new ui::Accelerator(command_page_action.accelerator()));
1955 extensions::Command command_script_badge;
1956 if (command_service->GetScriptBadgeCommand(
1957 extension->id(),
1958 extensions::CommandService::ACTIVE_ONLY,
1959 &command_script_badge,
1960 NULL)) {
1961 // Found the script badge shortcut command, register it.
1962 script_badge_keybinding_.reset(
1963 new ui::Accelerator(command_script_badge.accelerator()));
1966 if (page_action_keybinding_.get() || script_badge_keybinding_.get()) {
1967 accel_group_ = gtk_accel_group_new();
1968 gtk_window_add_accel_group(window_, accel_group_);
1970 if (page_action_keybinding_.get()) {
1971 gtk_accel_group_connect(
1972 accel_group_,
1973 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
1974 ui::GetGdkModifierForAccelerator(*page_action_keybinding_),
1975 GtkAccelFlags(0),
1976 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
1978 if (script_badge_keybinding_.get()) {
1979 gtk_accel_group_connect(
1980 accel_group_,
1981 ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
1982 ui::GetGdkModifierForAccelerator(*script_badge_keybinding_),
1983 GtkAccelFlags(0),
1984 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
1987 // Since we've added an accelerator, we'll need to unregister it before
1988 // the window is closed, so we listen for the window being closed.
1989 registrar_.Add(this,
1990 chrome::NOTIFICATION_WINDOW_CLOSED,
1991 content::Source<GtkWindow>(window_));
1995 void LocationBarViewGtk::PageActionViewGtk::DisconnectPageActionAccelerator() {
1996 if (accel_group_) {
1997 if (page_action_keybinding_.get()) {
1998 gtk_accel_group_disconnect_key(
1999 accel_group_,
2000 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
2001 ui::GetGdkModifierForAccelerator(*page_action_keybinding_));
2003 if (script_badge_keybinding_.get()) {
2004 gtk_accel_group_disconnect_key(
2005 accel_group_,
2006 ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
2007 ui::GetGdkModifierForAccelerator(*script_badge_keybinding_));
2009 gtk_window_remove_accel_group(window_, accel_group_);
2010 g_object_unref(accel_group_);
2011 accel_group_ = NULL;
2012 page_action_keybinding_.reset(NULL);
2013 script_badge_keybinding_.reset(NULL);
2017 gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed(
2018 GtkWidget* sender,
2019 GdkEventButton* event) {
2020 // Double and triple-clicks generate both a GDK_BUTTON_PRESS and a
2021 // GDK_[23]BUTTON_PRESS event. We don't want to double-trigger by acting on
2022 // both.
2023 if (event->type != GDK_BUTTON_PRESS)
2024 return TRUE;
2026 WebContents* web_contents = owner_->GetWebContents();
2027 if (!web_contents)
2028 return TRUE;
2030 ExtensionService* extension_service =
2031 owner_->profile()->GetExtensionService();
2032 if (!extension_service)
2033 return TRUE;
2035 const Extension* extension =
2036 extension_service->extensions()->GetByID(page_action()->extension_id());
2037 if (!extension)
2038 return TRUE;
2040 LocationBarController* controller =
2041 extensions::TabHelper::FromWebContents(web_contents)->
2042 location_bar_controller();
2044 switch (controller->OnClicked(extension->id(), event->button)) {
2045 case LocationBarController::ACTION_NONE:
2046 break;
2048 case LocationBarController::ACTION_SHOW_POPUP:
2049 ExtensionPopupGtk::Show(
2050 page_action_->GetPopupUrl(current_tab_id_),
2051 owner_->browser_,
2052 event_box_.get(),
2053 ExtensionPopupGtk::SHOW);
2054 break;
2056 case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
2057 context_menu_model_ =
2058 new ExtensionContextMenuModel(extension, owner_->browser_, this);
2059 context_menu_.reset(
2060 new MenuGtk(NULL, context_menu_model_.get()));
2061 context_menu_->PopupForWidget(sender, event->button, event->time);
2062 break;
2064 case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
2065 ExtensionPopupGtk::Show(
2066 extensions::ExtensionInfoUI::GetURL(extension->id()),
2067 owner_->browser_,
2068 event_box_.get(),
2069 ExtensionPopupGtk::SHOW);
2070 break;
2073 return TRUE;
2076 gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
2077 GtkWidget* widget,
2078 GdkEventExpose* event) {
2079 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageActionViewGtk::OnExpose");
2080 WebContents* contents = owner_->GetWebContents();
2081 if (!contents)
2082 return FALSE;
2084 int tab_id = extensions::ExtensionTabUtil::GetTabId(contents);
2085 if (tab_id < 0)
2086 return FALSE;
2088 std::string badge_text = page_action_->GetBadgeText(tab_id);
2089 if (badge_text.empty())
2090 return FALSE;
2092 gfx::CanvasSkiaPaint canvas(event, false);
2093 GtkAllocation allocation;
2094 gtk_widget_get_allocation(widget, &allocation);
2095 page_action_->PaintBadge(&canvas, gfx::Rect(allocation), tab_id);
2096 return FALSE;
2099 void LocationBarViewGtk::PageActionViewGtk::OnRealize(GtkWidget* widget) {
2100 ConnectPageActionAccelerator();
2103 // static
2104 gboolean LocationBarViewGtk::PageActionViewGtk::OnGtkAccelerator(
2105 GtkAccelGroup* accel_group,
2106 GObject* acceleratable,
2107 guint keyval,
2108 GdkModifierType modifier,
2109 void* user_data) {
2110 PageActionViewGtk* view = static_cast<PageActionViewGtk*>(user_data);
2111 if (!gtk_widget_get_visible(view->widget()))
2112 return FALSE;
2114 GdkEventButton event = {};
2115 event.type = GDK_BUTTON_PRESS;
2116 event.button = 1;
2117 return view->OnButtonPressed(view->widget(), &event);
2120 void LocationBarViewGtk::PageActionViewGtk::OnIconChanged() {
2121 UpdateVisibility(owner_->GetWebContents(), current_url_);