NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / gtk / location_bar_view_gtk.cc
blob71d80fd9b8429446900c3d3078dadbe23a6ceb51
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/tab_helper.h"
36 #include "chrome/browser/favicon/favicon_tab_helper.h"
37 #include "chrome/browser/search/instant_service.h"
38 #include "chrome/browser/search/instant_service_factory.h"
39 #include "chrome/browser/search_engines/template_url.h"
40 #include "chrome/browser/search_engines/template_url_service.h"
41 #include "chrome/browser/search_engines/template_url_service_factory.h"
42 #include "chrome/browser/themes/theme_properties.h"
43 #include "chrome/browser/ui/browser.h"
44 #include "chrome/browser/ui/browser_command_controller.h"
45 #include "chrome/browser/ui/browser_commands.h"
46 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
47 #include "chrome/browser/ui/browser_instant_controller.h"
48 #include "chrome/browser/ui/browser_list.h"
49 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
50 #include "chrome/browser/ui/content_settings/content_setting_image_model.h"
51 #include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
52 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
53 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
54 #include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h"
55 #include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h"
56 #include "chrome/browser/ui/gtk/first_run_bubble.h"
57 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
58 #include "chrome/browser/ui/gtk/gtk_util.h"
59 #include "chrome/browser/ui/gtk/manage_passwords_bubble_gtk.h"
60 #include "chrome/browser/ui/gtk/nine_box.h"
61 #include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
62 #include "chrome/browser/ui/gtk/rounded_window.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 draw a border on the top and bottom (but not on left or right).
106 const int kBorderThickness = 1;
108 const int kPopupEdgeThickness = 1;
109 const int kNormalEdgeThickness = 2;
111 // Spacing needed to align the bubble with the left side of the omnibox.
112 const int kFirstRunBubbleLeftSpacing = 4;
114 // The padding around the top, bottom, and sides of the location bar hbox.
115 // We don't want to edit control's text to be right against the edge,
116 // as well the tab to search box and other widgets need to have the padding on
117 // top and bottom to avoid drawing larger than the location bar space.
118 const int kHboxBorder = 2;
120 // Padding between the elements in the bar.
121 const int kInnerPadding = 2;
123 // Colors used to draw the EV certificate rounded bubble.
124 const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
125 const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
126 const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
128 // Colors used to draw the Tab to Search rounded bubble.
129 const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
130 const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
132 // Use weak gray for showing search and keyword hint text.
133 const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
135 // Size of the rounding of the "Search site for:" box.
136 const int kCornerSize = 3;
138 // Default page tool animation time (open and close). In ms.
139 const int kPageToolAnimationTime = 150;
141 // The time, in ms, that the content setting label is fully displayed, for the
142 // cases where we animate it into and out of view.
143 const int kContentSettingImageDisplayTime = 3200;
144 // The time, in ms, of the animation (open and close).
145 const int kContentSettingImageAnimationTime = 150;
147 // Color of border of content setting area (icon/label).
148 const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
149 // Colors for the background gradient.
150 const GdkColor kContentSettingTopColor = GDK_COLOR_RGB(0xff, 0xf8, 0xd4);
151 const GdkColor kContentSettingBottomColor = GDK_COLOR_RGB(0xff, 0xe6, 0xaf);
153 // If widget is visible, increment the int pointed to by count.
154 // Suitible for use with gtk_container_foreach.
155 void CountVisibleWidgets(GtkWidget* widget, gpointer count) {
156 if (gtk_widget_get_visible(widget))
157 *static_cast<int*>(count) += 1;
160 class ContentSettingImageViewGtk : public LocationBarViewGtk::PageToolViewGtk,
161 public BubbleDelegateGtk {
162 public:
163 ContentSettingImageViewGtk(ContentSettingsType content_type,
164 LocationBarViewGtk* parent);
165 virtual ~ContentSettingImageViewGtk();
167 // PageToolViewGtk
168 virtual void Update(WebContents* web_contents) OVERRIDE;
170 // gfx::AnimationDelegate
171 virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
173 private:
174 // PageToolViewGtk
175 virtual GdkColor GetButtonBorderColor() const OVERRIDE;
176 virtual GdkColor GetGradientTopColor() const OVERRIDE;
177 virtual GdkColor GetGradientBottomColor() const OVERRIDE;
178 virtual void OnClick(GtkWidget* sender) OVERRIDE;
180 // BubbleDelegateGtk
181 virtual void BubbleClosing(BubbleGtk* bubble,
182 bool closed_by_escape) OVERRIDE;
184 // The owning LocationBarViewGtk.
185 LocationBarViewGtk* parent_;
187 scoped_ptr<ContentSettingImageModel> content_setting_image_model_;
189 // The currently shown bubble if any.
190 ContentSettingBubbleGtk* content_setting_bubble_;
192 DISALLOW_COPY_AND_ASSIGN(ContentSettingImageViewGtk);
195 ContentSettingImageViewGtk::ContentSettingImageViewGtk(
196 ContentSettingsType content_type,
197 LocationBarViewGtk* parent)
198 : PageToolViewGtk(),
199 parent_(parent),
200 content_setting_image_model_(
201 ContentSettingImageModel::CreateContentSettingImageModel(
202 content_type)),
203 content_setting_bubble_(NULL) {
204 animation_.SetSlideDuration(kContentSettingImageAnimationTime);
207 ContentSettingImageViewGtk::~ContentSettingImageViewGtk() {
208 if (content_setting_bubble_)
209 content_setting_bubble_->Close();
212 void ContentSettingImageViewGtk::Update(WebContents* web_contents) {
213 if (web_contents)
214 content_setting_image_model_->UpdateFromWebContents(web_contents);
216 if (!content_setting_image_model_->is_visible()) {
217 gtk_widget_hide(widget());
218 return;
221 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()),
222 GtkThemeService::GetFrom(parent_->browser()->profile())->GetImageNamed(
223 content_setting_image_model_->get_icon()).ToGdkPixbuf());
225 gtk_widget_set_tooltip_text(widget(),
226 content_setting_image_model_->get_tooltip().c_str());
227 gtk_widget_show_all(widget());
229 if (!web_contents)
230 return;
232 TabSpecificContentSettings* content_settings =
233 TabSpecificContentSettings::FromWebContents(web_contents);
234 if (!content_settings || content_settings->IsBlockageIndicated(
235 content_setting_image_model_->get_content_settings_type()))
236 return;
238 // The content blockage was not yet indicated to the user. Start indication
239 // animation and clear "not yet shown" flag.
240 content_settings->SetBlockageHasBeenIndicated(
241 content_setting_image_model_->get_content_settings_type());
243 int label_string_id =
244 content_setting_image_model_->explanatory_string_id();
245 // If there's no string for the content type, we don't animate.
246 if (!label_string_id)
247 return;
249 gtk_label_set_text(GTK_LABEL(label_.get()),
250 l10n_util::GetStringUTF8(label_string_id).c_str());
251 StartAnimating();
254 void ContentSettingImageViewGtk::AnimationEnded(
255 const gfx::Animation* animation) {
256 if (animation_.IsShowing()) {
257 base::MessageLoop::current()->PostDelayedTask(
258 FROM_HERE,
259 base::Bind(&ContentSettingImageViewGtk::CloseAnimation,
260 weak_factory_.GetWeakPtr()),
261 base::TimeDelta::FromMilliseconds(kContentSettingImageDisplayTime));
262 } else {
263 gtk_widget_hide(label_.get());
264 gtk_util::StopActingAsRoundedWindow(event_box_.get());
265 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
269 GdkColor ContentSettingImageViewGtk::GetButtonBorderColor() const {
270 return kContentSettingBorderColor;
273 GdkColor ContentSettingImageViewGtk::GetGradientTopColor() const {
274 return kContentSettingTopColor;
277 GdkColor ContentSettingImageViewGtk::GetGradientBottomColor() const {
278 return kContentSettingBottomColor;
281 void ContentSettingImageViewGtk::OnClick(
282 GtkWidget* sender) {
283 WebContents* web_contents = parent_->GetWebContents();
284 if (!web_contents)
285 return;
286 Profile* profile = parent_->browser()->profile();
287 content_setting_bubble_ = new ContentSettingBubbleGtk(
288 sender, this,
289 ContentSettingBubbleModel::CreateContentSettingBubbleModel(
290 parent_->browser()->content_setting_bubble_model_delegate(),
291 web_contents,
292 profile,
293 content_setting_image_model_->get_content_settings_type()),
294 profile);
295 return;
298 void ContentSettingImageViewGtk::BubbleClosing(
299 BubbleGtk* bubble,
300 bool closed_by_escape) {
301 content_setting_bubble_ = NULL;
304 gfx::Rect AllocationToRect(const GtkAllocation& allocation) {
305 return gfx::Rect(allocation.x, allocation.y,
306 allocation.width, allocation.height);
309 } // namespace
311 ////////////////////////////////////////////////////////////////////////////////
312 // LocationBarViewGtk
314 // static
315 const GdkColor LocationBarViewGtk::kBackgroundColor =
316 GDK_COLOR_RGB(255, 255, 255);
318 LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
319 : OmniboxEditController(browser->command_controller()->command_updater()),
320 LocationBar(browser->profile()),
321 zoom_image_(NULL),
322 manage_passwords_icon_image_(NULL),
323 star_image_(NULL),
324 starred_(false),
325 star_sized_(false),
326 site_type_alignment_(NULL),
327 site_type_event_box_(NULL),
328 location_icon_image_(NULL),
329 drag_icon_(NULL),
330 enable_location_drag_(false),
331 security_info_label_(NULL),
332 tab_to_search_alignment_(NULL),
333 tab_to_search_box_(NULL),
334 tab_to_search_full_label_(NULL),
335 tab_to_search_partial_label_(NULL),
336 tab_to_search_hint_(NULL),
337 tab_to_search_hint_leading_label_(NULL),
338 tab_to_search_hint_icon_(NULL),
339 tab_to_search_hint_trailing_label_(NULL),
340 browser_(browser),
341 popup_window_mode_(false),
342 theme_service_(NULL),
343 hbox_width_(0),
344 entry_box_width_(0),
345 show_selected_keyword_(false),
346 show_keyword_hint_(false),
347 weak_ptr_factory_(this) {
350 LocationBarViewGtk::~LocationBarViewGtk() {
351 // All of our widgets should be children of / owned by the alignment.
352 zoom_.Destroy();
353 manage_passwords_icon_.Destroy();
354 star_.Destroy();
355 hbox_.Destroy();
356 content_setting_hbox_.Destroy();
357 page_action_hbox_.Destroy();
360 void LocationBarViewGtk::Init(bool popup_window_mode) {
361 popup_window_mode_ = popup_window_mode;
363 theme_service_ = GtkThemeService::GetFrom(profile());
365 // Create the widget first, so we can pass it to the OmniboxViewGtk.
366 hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding));
367 gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
368 // We will paint for the alignment, to paint the background and border.
369 gtk_widget_set_app_paintable(hbox_.get(), TRUE);
370 // Redraw the whole location bar when it changes size (e.g., when toggling
371 // the home button on/off.
372 gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
374 // Now initialize the OmniboxViewGtk.
375 omnibox_view_.reset(new OmniboxViewGtk(this, browser_, profile(),
376 command_updater(),
377 popup_window_mode_, hbox_.get()));
378 omnibox_view_->Init();
380 g_signal_connect(hbox_.get(), "expose-event",
381 G_CALLBACK(&HandleExposeThunk), this);
383 BuildSiteTypeArea();
385 // Put |tab_to_search_box_|, |omnibox_view_|, and |tab_to_search_hint_| into
386 // a sub hbox, so that we can make this part horizontally shrinkable without
387 // affecting other elements in the location bar.
388 entry_box_ = gtk_hbox_new(FALSE, kInnerPadding);
389 gtk_widget_show(entry_box_);
390 gtk_widget_set_size_request(entry_box_, 0, -1);
391 gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0);
393 // We need to adjust the visibility of the search hint widgets according to
394 // the horizontal space in the |entry_box_|.
395 g_signal_connect(entry_box_, "size-allocate",
396 G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this);
398 // Tab to search (the keyword box on the left hand side).
399 tab_to_search_full_label_ =
400 theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
401 tab_to_search_partial_label_ =
402 theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
403 GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0);
404 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
405 tab_to_search_full_label_, FALSE, FALSE, 0);
406 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
407 tab_to_search_partial_label_, FALSE, FALSE, 0);
408 GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0);
409 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
410 tab_to_search_magnifier_ = gtk_image_new_from_pixbuf(
411 rb.GetNativeImageNamed(IDR_KEYWORD_SEARCH_MAGNIFIER).ToGdkPixbuf());
412 gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_,
413 FALSE, FALSE, 0);
414 gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox,
415 false, 0);
417 // This creates a box around the keyword text with a border, background color,
418 // and padding around the text.
419 tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
420 tab_to_search_hbox, NULL, 1, 1, 1, 3);
421 gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box");
422 gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor,
423 kCornerSize,
424 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
426 // Put the event box in an alignment to get the padding correct.
427 tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1);
428 gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_),
429 tab_to_search_box_);
430 gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_,
431 FALSE, FALSE, 0);
433 // Show all children widgets of |tab_to_search_box_| initially, except
434 // |tab_to_search_partial_label_|.
435 gtk_widget_show_all(tab_to_search_box_);
436 gtk_widget_hide(tab_to_search_partial_label_);
438 omnibox_view_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
439 gtk_container_add(GTK_CONTAINER(omnibox_view_alignment_),
440 omnibox_view_->GetNativeView());
441 gtk_box_pack_start(GTK_BOX(entry_box_), omnibox_view_alignment_,
442 TRUE, TRUE, 0);
444 // Tab to search notification (the hint on the right hand side).
445 tab_to_search_hint_ = gtk_hbox_new(FALSE, 0);
446 gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint");
447 tab_to_search_hint_leading_label_ =
448 theme_service_->BuildLabel(std::string(), kHintTextColor);
449 gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE);
450 tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf(
451 rb.GetNativeImageNamed(IDR_OMNIBOX_KEYWORD_HINT_TAB).ToGdkPixbuf());
452 tab_to_search_hint_trailing_label_ =
453 theme_service_->BuildLabel(std::string(), kHintTextColor);
454 gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE);
455 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
456 tab_to_search_hint_leading_label_,
457 FALSE, FALSE, 0);
458 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
459 tab_to_search_hint_icon_,
460 FALSE, FALSE, 0);
461 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
462 tab_to_search_hint_trailing_label_,
463 FALSE, FALSE, 0);
464 // Show all children widgets of |tab_to_search_hint_| initially.
465 gtk_widget_show_all(tab_to_search_hint_);
466 gtk_widget_hide(tab_to_search_hint_);
467 // tab_to_search_hint_ gets hidden initially in OnChanged. Hiding it here
468 // doesn't work, someone is probably calling show_all on our parent box.
469 gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
471 if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
472 // Hide the star icon in popups, app windows, etc.
473 CreateStarButton();
474 gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
477 CreateZoomButton();
478 gtk_box_pack_end(GTK_BOX(hbox_.get()), zoom_.get(), FALSE, FALSE, 0);
480 CreateManagePasswordsIconButton();
481 gtk_box_pack_end(GTK_BOX(hbox_.get()), manage_passwords_icon_.get(), FALSE,
482 FALSE, 0);
484 content_setting_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding + 1));
485 gtk_widget_set_name(content_setting_hbox_.get(),
486 "chrome-content-setting-hbox");
487 gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(),
488 FALSE, FALSE, 1);
490 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
491 ContentSettingImageViewGtk* content_setting_view =
492 new ContentSettingImageViewGtk(
493 static_cast<ContentSettingsType>(i), this);
494 content_setting_views_.push_back(content_setting_view);
495 gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()),
496 content_setting_view->widget(), FALSE, FALSE, 0);
499 page_action_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding));
500 gtk_widget_set_name(page_action_hbox_.get(),
501 "chrome-page-action-hbox");
502 gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(),
503 FALSE, FALSE, 0);
505 // Now that we've created the widget hierarchy, connect to the main |hbox_|'s
506 // size-allocate so we can do proper resizing and eliding on
507 // |security_info_label_|.
508 g_signal_connect(hbox_.get(), "size-allocate",
509 G_CALLBACK(&OnHboxSizeAllocateThunk), this);
511 registrar_.Add(this,
512 chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
513 content::Source<ThemeService>(theme_service_));
514 registrar_.Add(this,
515 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
516 content::Source<Profile>(profile()));
517 edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
518 profile()->GetPrefs(),
519 base::Bind(&LocationBarViewGtk::UpdateStarIcon,
520 base::Unretained(this)));
522 theme_service_->InitThemesFor(this);
525 void LocationBarViewGtk::SetPreviewEnabledPageAction(
526 ExtensionAction* page_action,
527 bool preview_enabled) {
528 DCHECK(page_action);
529 for (ScopedVector<PageActionViewGtk>::iterator iter =
530 page_action_views_.begin(); iter != page_action_views_.end();
531 ++iter) {
532 if ((*iter)->page_action() == page_action) {
533 (*iter)->set_preview_enabled(preview_enabled);
534 UpdatePageActions();
535 return;
540 GtkWidget* LocationBarViewGtk::GetPageActionWidget(
541 ExtensionAction* page_action) {
542 DCHECK(page_action);
543 for (ScopedVector<PageActionViewGtk>::iterator iter =
544 page_action_views_.begin();
545 iter != page_action_views_.end();
546 ++iter) {
547 if ((*iter)->page_action() == page_action)
548 return (*iter)->widget();
550 return NULL;
553 void LocationBarViewGtk::ShowStarBubble(const GURL& url,
554 bool newly_bookmarked) {
555 if (!star_.get())
556 return;
558 if (star_sized_) {
559 BookmarkBubbleGtk::Show(star_.get(), profile(), url, newly_bookmarked);
560 } else {
561 on_star_sized_ = base::Bind(&BookmarkBubbleGtk::Show, star_.get(),
562 profile(), url, newly_bookmarked);
566 void LocationBarViewGtk::ShowManagePasswordsBubble() {
567 if (GetToolbarModel()->input_in_progress() || !GetWebContents())
568 return;
570 ManagePasswordsBubbleGtk::ShowBubble(GetWebContents());
573 void LocationBarViewGtk::ZoomChangedForActiveTab(bool can_show_bubble) {
574 UpdateZoomIcon();
576 if (can_show_bubble && gtk_widget_get_visible(zoom_.get()))
577 ShowZoomBubble();
580 void LocationBarViewGtk::SetStarred(bool starred) {
581 if (starred == starred_)
582 return;
584 starred_ = starred;
585 UpdateStarIcon();
588 void LocationBarViewGtk::Update(const WebContents* contents) {
589 UpdateZoomIcon();
590 UpdateStarIcon();
591 UpdateSiteTypeArea();
592 UpdateContentSettingsIcons();
593 UpdatePageActions();
594 if (contents)
595 omnibox_view_->OnTabChanged(contents);
596 else
597 omnibox_view_->Update();
598 // The security level (background color) could have changed, etc.
599 if (theme_service_->UsingNativeTheme()) {
600 // In GTK mode, we need our parent to redraw, as it draws the text entry
601 // border.
602 gtk_widget_queue_draw(gtk_widget_get_parent(widget()));
603 } else {
604 gtk_widget_queue_draw(widget());
606 ZoomBubbleGtk::CloseBubble();
609 void LocationBarViewGtk::OnChanged() {
610 UpdateSiteTypeArea();
612 const base::string16 keyword(omnibox_view_->model()->keyword());
613 const bool is_keyword_hint = omnibox_view_->model()->is_keyword_hint();
614 show_selected_keyword_ = !keyword.empty() && !is_keyword_hint;
615 show_keyword_hint_ = !keyword.empty() && is_keyword_hint;
617 if (show_selected_keyword_)
618 SetKeywordLabel(keyword);
620 if (show_keyword_hint_)
621 SetKeywordHintLabel(keyword);
623 AdjustChildrenVisibility();
626 void LocationBarViewGtk::OnSetFocus() {
627 AccessibilityTextBoxInfo info(profile(),
628 l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION),
629 std::string(), false);
630 ExtensionAccessibilityEventRouter::GetInstance()->HandleControlEvent(
631 ui::AccessibilityTypes::EVENT_FOCUS, &info);
633 // Update the keyword and search hint states.
634 OnChanged();
637 InstantController* LocationBarViewGtk::GetInstant() {
638 return browser_->instant_controller() ?
639 browser_->instant_controller()->instant() : NULL;
642 WebContents* LocationBarViewGtk::GetWebContents() {
643 return browser_->tab_strip_model()->GetActiveWebContents();
646 ToolbarModel* LocationBarViewGtk::GetToolbarModel() {
647 return browser_->toolbar_model();
650 const ToolbarModel* LocationBarViewGtk::GetToolbarModel() const {
651 return browser_->toolbar_model();
654 void LocationBarViewGtk::ShowFirstRunBubble() {
655 // We need the browser window to be shown before we can show the bubble, but
656 // we get called before that's happened.
657 base::MessageLoop::current()->PostTask(
658 FROM_HERE,
659 base::Bind(&LocationBarViewGtk::ShowFirstRunBubbleInternal,
660 weak_ptr_factory_.GetWeakPtr()));
663 GURL LocationBarViewGtk::GetDestinationURL() const {
664 return destination_url();
667 WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
668 return disposition();
671 content::PageTransition LocationBarViewGtk::GetPageTransition() const {
672 return transition();
675 void LocationBarViewGtk::AcceptInput() {
676 omnibox_view_->model()->AcceptInput(CURRENT_TAB, false);
679 void LocationBarViewGtk::FocusLocation(bool select_all) {
680 omnibox_view_->SetFocus();
681 if (select_all)
682 omnibox_view_->SelectAll(true);
685 void LocationBarViewGtk::FocusSearch() {
686 omnibox_view_->SetFocus();
687 omnibox_view_->SetForcedQuery();
690 void LocationBarViewGtk::UpdateContentSettingsIcons() {
691 bool any_visible = false;
692 for (ScopedVector<PageToolViewGtk>::iterator i(
693 content_setting_views_.begin());
694 i != content_setting_views_.end(); ++i) {
695 (*i)->Update(GetToolbarModel()->input_in_progress() ?
696 NULL : GetWebContents());
697 any_visible = (*i)->IsVisible() || any_visible;
699 // If there are no visible content things, hide the top level box so it
700 // doesn't mess with padding.
701 gtk_widget_set_visible(content_setting_hbox_.get(), any_visible);
704 void LocationBarViewGtk::UpdateManagePasswordsIconAndBubble() {
705 UpdateManagePasswordsIcon();
708 void LocationBarViewGtk::UpdatePageActions() {
709 std::vector<ExtensionAction*> new_page_actions;
711 WebContents* contents = GetWebContents();
712 if (contents) {
713 LocationBarController* location_bar_controller =
714 extensions::TabHelper::FromWebContents(contents)->
715 location_bar_controller();
716 new_page_actions = location_bar_controller->GetCurrentActions();
719 // Initialize on the first call, or re-initialize if more extensions have been
720 // loaded or added after startup.
721 if (new_page_actions != page_actions_) {
722 page_actions_.swap(new_page_actions);
723 page_action_views_.clear();
725 for (size_t i = 0; i < page_actions_.size(); ++i) {
726 page_action_views_.push_back(
727 new PageActionViewGtk(this, page_actions_[i]));
728 gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()),
729 page_action_views_[i]->widget(), FALSE, FALSE, 0);
731 content::NotificationService::current()->Notify(
732 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
733 content::Source<LocationBar>(this),
734 content::NotificationService::NoDetails());
737 if (!page_action_views_.empty() && contents) {
738 GURL url = GetWebContents()->GetURL();
740 for (size_t i = 0; i < page_action_views_.size(); i++) {
741 page_action_views_[i]->UpdateVisibility(
742 GetToolbarModel()->input_in_progress() ? NULL : contents, url);
744 gtk_widget_queue_draw(hbox_.get());
747 // If there are no visible page actions, hide the hbox too, so that it does
748 // not affect the padding in the location bar.
749 gtk_widget_set_visible(page_action_hbox_.get(),
750 PageActionVisibleCount() && !ShouldOnlyShowLocation());
753 void LocationBarViewGtk::InvalidatePageActions() {
754 size_t count_before = page_action_views_.size();
755 page_action_views_.clear();
756 if (page_action_views_.size() != count_before) {
757 content::NotificationService::current()->Notify(
758 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
759 content::Source<LocationBar>(this),
760 content::NotificationService::NoDetails());
764 void LocationBarViewGtk::UpdateOpenPDFInReaderPrompt() {
765 // Not implemented on Gtk.
768 void LocationBarViewGtk::UpdateGeneratedCreditCardView() {
769 NOTIMPLEMENTED();
772 void LocationBarViewGtk::SaveStateToContents(WebContents* contents) {
773 omnibox_view_->SaveStateToTab(contents);
776 void LocationBarViewGtk::Revert() {
777 omnibox_view_->RevertAll();
780 const OmniboxView* LocationBarViewGtk::GetOmniboxView() const {
781 return omnibox_view_.get();
784 OmniboxView* LocationBarViewGtk::GetOmniboxView() {
785 return omnibox_view_.get();
788 LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
789 return this;
792 int LocationBarViewGtk::PageActionCount() {
793 return page_action_views_.size();
796 int LocationBarViewGtk::PageActionVisibleCount() {
797 int count = 0;
798 gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
799 CountVisibleWidgets, &count);
800 return count;
803 ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) {
804 if (index >= page_action_views_.size()) {
805 NOTREACHED();
806 return NULL;
809 return page_action_views_[index]->page_action();
812 ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) {
813 size_t visible_index = 0;
814 for (size_t i = 0; i < page_action_views_.size(); ++i) {
815 if (page_action_views_[i]->IsVisible()) {
816 if (index == visible_index++)
817 return page_action_views_[i]->page_action();
821 NOTREACHED();
822 return NULL;
825 void LocationBarViewGtk::TestPageActionPressed(size_t index) {
826 if (index >= page_action_views_.size()) {
827 NOTREACHED();
828 return;
831 page_action_views_[index]->TestActivatePageAction();
834 bool LocationBarViewGtk::GetBookmarkStarVisibility() {
835 return starred_;
838 void LocationBarViewGtk::Observe(int type,
839 const content::NotificationSource& source,
840 const content::NotificationDetails& details) {
841 switch (type) {
842 case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: {
843 // Only update if the updated action box was for the active tab contents.
844 WebContents* target_tab = content::Details<WebContents>(details).ptr();
845 if (target_tab == GetWebContents())
846 UpdatePageActions();
847 break;
850 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
851 if (theme_service_->UsingNativeTheme()) {
852 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL);
854 GdkColor border_color = theme_service_->GetGdkColor(
855 ThemeProperties::COLOR_FRAME);
856 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
858 gtk_util::UndoForceFontSize(security_info_label_);
859 gtk_util::UndoForceFontSize(tab_to_search_full_label_);
860 gtk_util::UndoForceFontSize(tab_to_search_partial_label_);
861 gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_);
862 gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_);
864 gtk_alignment_set_padding(GTK_ALIGNMENT(omnibox_view_alignment_),
865 0, 0, 0, 0);
866 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
867 1, 1, 1, 0);
868 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
869 1, 1, 1, 0);
870 } else {
871 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
872 &kKeywordBackgroundColor);
873 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
874 kKeywordBorderColor);
876 // Until we switch to vector graphics, force the font size of labels.
877 // 12.1px = 9pt @ 96dpi
878 gtk_util::ForceFontSizePixels(security_info_label_, 12.1);
879 gtk_util::ForceFontSizePixels(tab_to_search_full_label_,
880 browser_defaults::kOmniboxFontPixelSize);
881 gtk_util::ForceFontSizePixels(tab_to_search_partial_label_,
882 browser_defaults::kOmniboxFontPixelSize);
883 gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_,
884 browser_defaults::kOmniboxFontPixelSize);
885 gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_,
886 browser_defaults::kOmniboxFontPixelSize);
888 const int left_right = popup_window_mode_ ? kPopupEdgeThickness : 0;
889 gtk_alignment_set_padding(GTK_ALIGNMENT(omnibox_view_alignment_),
890 kBorderThickness, kBorderThickness,
891 left_right, left_right);
892 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
893 1, 1, 0, 0);
894 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
895 1, 1, 0, 0);
898 UpdateZoomIcon();
899 UpdateManagePasswordsIcon();
900 UpdateStarIcon();
901 UpdateSiteTypeArea();
902 UpdateContentSettingsIcons();
903 break;
906 default:
907 NOTREACHED();
911 void LocationBarViewGtk::BuildSiteTypeArea() {
912 location_icon_image_ = gtk_image_new();
913 gtk_widget_set_name(location_icon_image_, "chrome-location-icon");
915 GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1);
916 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0);
917 gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_);
918 gtk_widget_show_all(icon_alignment);
920 security_info_label_ = gtk_label_new(NULL);
921 gtk_label_set_ellipsize(GTK_LABEL(security_info_label_),
922 PANGO_ELLIPSIZE_MIDDLE);
923 gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL,
924 &kEvSecureTextColor);
925 gtk_widget_set_name(security_info_label_,
926 "chrome-location-bar-security-info-label");
928 GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1);
929 gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment,
930 FALSE, FALSE, 0);
931 gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_,
932 FALSE, FALSE, 2);
934 site_type_event_box_ = gtk_event_box_new();
935 gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL,
936 &kEvSecureBackgroundColor);
937 g_signal_connect(site_type_event_box_, "drag-data-get",
938 G_CALLBACK(&OnIconDragDataThunk), this);
939 g_signal_connect(site_type_event_box_, "drag-begin",
940 G_CALLBACK(&OnIconDragBeginThunk), this);
941 g_signal_connect(site_type_event_box_, "drag-end",
942 G_CALLBACK(&OnIconDragEndThunk), this);
944 // Make the event box not visible so it does not paint a background.
945 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
946 FALSE);
947 gtk_widget_set_name(site_type_event_box_,
948 "chrome-location-icon-eventbox");
949 gtk_container_add(GTK_CONTAINER(site_type_event_box_),
950 site_type_hbox);
952 // Put the event box in an alignment to get the padding correct.
953 site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1);
954 gtk_container_add(GTK_CONTAINER(site_type_alignment_),
955 site_type_event_box_);
956 gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_,
957 FALSE, FALSE, 0);
959 gtk_widget_set_tooltip_text(location_icon_image_,
960 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
962 g_signal_connect(site_type_event_box_, "button-release-event",
963 G_CALLBACK(&OnIconReleasedThunk), this);
966 void LocationBarViewGtk::SetSiteTypeDragSource() {
967 bool enable = !GetOmniboxView()->IsEditingOrEmpty();
968 if (enable_location_drag_ == enable)
969 return;
970 enable_location_drag_ = enable;
972 if (!enable) {
973 gtk_drag_source_unset(site_type_event_box_);
974 return;
977 gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK,
978 NULL, 0, GDK_ACTION_COPY);
979 ui::SetSourceTargetListFromCodeMask(site_type_event_box_,
980 ui::TEXT_PLAIN |
981 ui::TEXT_URI_LIST |
982 ui::CHROME_NAMED_URL);
985 gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
986 GdkEventExpose* event) {
987 // If we're not using GTK theming, draw our own border over the edge pixels
988 // of the background.
989 GtkThemeService* theme_service = GtkThemeService::GetFrom(profile());
990 if (!theme_service->UsingNativeTheme()) {
991 // Perform a scoped paint to fill in the background color.
993 gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
995 GtkAllocation allocation;
996 gtk_widget_get_allocation(widget, &allocation);
998 int thickness = popup_window_mode_ ?
999 kPopupEdgeThickness : kNormalEdgeThickness;
1000 gfx::Rect bounds(allocation);
1001 bounds.Inset(thickness, thickness);
1003 const SkColor color = SK_ColorWHITE;
1004 if (popup_window_mode_) {
1005 canvas.FillRect(bounds, color);
1006 } else {
1007 SkPaint paint;
1008 paint.setStyle(SkPaint::kFill_Style);
1009 paint.setColor(color);
1010 const int kBorderCornerRadius = 2;
1011 canvas.DrawRoundRect(bounds, kBorderCornerRadius, paint);
1015 if (popup_window_mode_) {
1016 NineBox(IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP_LEFT,
1017 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP,
1018 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_TOP_RIGHT,
1019 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_LEFT,
1020 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_CENTER,
1021 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_RIGHT,
1022 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM_LEFT,
1023 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM,
1024 IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM_RIGHT).
1025 RenderToWidget(widget);
1026 } else {
1027 NineBox(IDR_OMNIBOX_BORDER_AND_SHADOW_TOP_LEFT,
1028 IDR_OMNIBOX_BORDER_AND_SHADOW_TOP,
1029 IDR_OMNIBOX_BORDER_AND_SHADOW_TOP_RIGHT,
1030 IDR_OMNIBOX_BORDER_AND_SHADOW_LEFT,
1031 IDR_OMNIBOX_BORDER_AND_SHADOW_CENTER,
1032 IDR_OMNIBOX_BORDER_AND_SHADOW_RIGHT,
1033 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM_LEFT,
1034 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM,
1035 IDR_OMNIBOX_BORDER_AND_SHADOW_BOTTOM_RIGHT).
1036 RenderToWidget(widget);
1040 return FALSE; // Continue propagating the expose.
1043 gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender,
1044 GdkEventButton* event) {
1045 WebContents* tab = GetWebContents();
1047 if (event->button == 1) {
1048 // Do not show page info if the user has been editing the location
1049 // bar, or the location bar is at the NTP.
1050 if (GetOmniboxView()->IsEditingOrEmpty())
1051 return FALSE;
1053 // (0,0) event coordinates indicates that the release came at the end of
1054 // a drag.
1055 if (event->x == 0 && event->y == 0)
1056 return FALSE;
1058 // Important to use GetVisibleEntry to match what's showing in the omnibox.
1059 NavigationEntry* nav_entry = tab->GetController().GetVisibleEntry();
1060 if (!nav_entry) {
1061 NOTREACHED();
1062 return FALSE;
1064 chrome::ShowWebsiteSettings(browser_, tab, nav_entry->GetURL(),
1065 nav_entry->GetSSL());
1066 return TRUE;
1067 } else if (event->button == 2) {
1068 // When the user middle clicks on the location icon, try to open the
1069 // contents of the PRIMARY selection in the current tab.
1070 // If the click was outside our bounds, do nothing.
1071 if (!gtk_util::WidgetBounds(sender).Contains(
1072 gfx::Point(event->x, event->y))) {
1073 return FALSE;
1076 GURL url;
1077 if (!gtk_util::URLFromPrimarySelection(profile(), &url))
1078 return FALSE;
1080 tab->OpenURL(OpenURLParams(
1081 url, content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1082 false));
1083 return TRUE;
1086 return FALSE;
1089 void LocationBarViewGtk::OnIconDragData(GtkWidget* sender,
1090 GdkDragContext* context,
1091 GtkSelectionData* data,
1092 guint info, guint time) {
1093 ui::WriteURLWithName(data, drag_url_, drag_title_, info);
1096 void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender,
1097 GdkDragContext* context) {
1098 content::WebContents* web_contents = GetWebContents();
1099 gfx::Image favicon =
1100 FaviconTabHelper::FromWebContents(web_contents)->GetFavicon();
1101 if (favicon.IsEmpty())
1102 return;
1103 drag_url_ = web_contents->GetURL();
1104 drag_title_ = web_contents->GetTitle();
1105 drag_icon_ = GetDragRepresentation(favicon.ToGdkPixbuf(), drag_title_,
1106 theme_service_);
1107 gtk_drag_set_icon_widget(context, drag_icon_, 0, 0);
1110 void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
1111 GdkDragContext* context) {
1112 DCHECK(drag_icon_);
1113 gtk_widget_destroy(drag_icon_);
1114 drag_icon_ = NULL;
1115 drag_url_ = GURL::EmptyGURL();
1116 drag_title_.clear();
1119 void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
1120 GtkAllocation* allocation) {
1121 if (hbox_width_ != allocation->width) {
1122 hbox_width_ = allocation->width;
1123 UpdateEVCertificateLabelSize();
1125 InstantService* instant_service =
1126 InstantServiceFactory::GetForProfile(profile());
1127 if (instant_service) {
1128 instant_service->OnOmniboxStartMarginChanged(
1129 AllocationToRect(*allocation).x());
1133 void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender,
1134 GtkAllocation* allocation) {
1135 if (entry_box_width_ != allocation->width) {
1136 entry_box_width_ = allocation->width;
1137 AdjustChildrenVisibility();
1141 gboolean LocationBarViewGtk::OnZoomButtonPress(GtkWidget* widget,
1142 GdkEventButton* event) {
1143 if (event->button == 1 && GetWebContents()) {
1144 // If the zoom icon is clicked, show the zoom bubble and keep it open until
1145 // it loses focus.
1146 ZoomBubbleGtk::ShowBubble(GetWebContents(), false);
1147 return TRUE;
1149 return FALSE;
1152 gboolean LocationBarViewGtk::OnManagePasswordsIconButtonPress(
1153 GtkWidget* widget, GdkEventButton* event) {
1154 if (event->button == 1 && GetWebContents()) {
1155 // If the manage passwords icon is clicked, show the manage passwords bubble
1156 // and keep it open until the user makes a choice or clicks outside the
1157 // bubble.
1158 ManagePasswordsBubbleGtk::ShowBubble(GetWebContents());
1159 return TRUE;
1161 return FALSE;
1164 void LocationBarViewGtk::OnStarButtonSizeAllocate(GtkWidget* sender,
1165 GtkAllocation* allocation) {
1166 if (!on_star_sized_.is_null()) {
1167 on_star_sized_.Run();
1168 on_star_sized_.Reset();
1170 star_sized_ = true;
1173 gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
1174 GdkEventButton* event) {
1175 if (event->button != 1)
1176 return FALSE;
1177 chrome::ExecuteCommand(browser_, IDC_BOOKMARK_PAGE_FROM_STAR);
1178 return TRUE;
1181 void LocationBarViewGtk::UpdateSiteTypeArea() {
1182 // The icon is always visible except when the |tab_to_search_alignment_| is
1183 // visible.
1184 if (!omnibox_view_->model()->keyword().empty() &&
1185 !omnibox_view_->model()->is_keyword_hint()) {
1186 gtk_widget_hide(site_type_area());
1187 return;
1190 int resource_id = omnibox_view_->GetIcon();
1191 gtk_image_set_from_pixbuf(
1192 GTK_IMAGE(location_icon_image_),
1193 theme_service_->GetImageNamed(resource_id).ToGdkPixbuf());
1195 if (GetToolbarModel()->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) {
1196 if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1197 // Fun fact: If wee try to make |site_type_event_box_| act as a
1198 // rounded window while it doesn't have a visible window, GTK interprets
1199 // this as a sign that it should paint the skyline texture into the
1200 // omnibox.
1201 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1202 TRUE);
1204 gtk_util::ActAsRoundedWindow(site_type_event_box_,
1205 kEvSecureBorderColor,
1206 kCornerSize,
1207 gtk_util::ROUNDED_ALL,
1208 gtk_util::BORDER_ALL);
1211 base::string16 info_text = GetToolbarModel()->GetEVCertName();
1212 gtk_label_set_text(GTK_LABEL(security_info_label_),
1213 base::UTF16ToUTF8(info_text).c_str());
1215 UpdateEVCertificateLabelSize();
1217 gtk_widget_show(GTK_WIDGET(security_info_label_));
1218 } else {
1219 if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1220 gtk_util::StopActingAsRoundedWindow(site_type_event_box_);
1222 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1223 FALSE);
1226 gtk_widget_hide(GTK_WIDGET(security_info_label_));
1229 if (GetOmniboxView()->IsEditingOrEmpty()) {
1230 // Do not show the tooltip if the user has been editing the location
1231 // bar, or the location bar is at the NTP.
1232 gtk_widget_set_tooltip_text(location_icon_image_, "");
1233 } else {
1234 gtk_widget_set_tooltip_text(location_icon_image_,
1235 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
1238 gtk_widget_show(site_type_area());
1240 SetSiteTypeDragSource();
1243 void LocationBarViewGtk::UpdateEVCertificateLabelSize() {
1244 // Figure out the width of the average character.
1245 PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_));
1246 PangoContext* context = pango_layout_get_context(layout);
1247 PangoFontMetrics* metrics = pango_context_get_metrics(
1248 context,
1249 gtk_widget_get_style(security_info_label_)->font_desc,
1250 pango_context_get_language(context));
1251 int char_width =
1252 pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE;
1254 // The EV label should never take up more than half the hbox. We try to
1255 // correct our inaccurate measurement units ("the average character width")
1256 // by dividing more than an even 2.
1257 GtkAllocation security_label_allocation;
1258 gtk_widget_get_allocation(security_info_label_, &security_label_allocation);
1259 GtkAllocation entry_box_allocation;
1260 gtk_widget_get_allocation(entry_box_, &entry_box_allocation);
1261 int text_area = security_label_allocation.width +
1262 entry_box_allocation.width;
1263 int max_chars = static_cast<int>(static_cast<float>(text_area) /
1264 static_cast<float>(char_width) / 2.75);
1265 // Don't let the label be smaller than 10 characters so that the country
1266 // code is always visible.
1267 gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_),
1268 std::max(10, max_chars));
1270 pango_font_metrics_unref(metrics);
1273 void LocationBarViewGtk::SetKeywordLabel(const base::string16& keyword) {
1274 if (keyword.empty())
1275 return;
1277 TemplateURLService* template_url_service =
1278 TemplateURLServiceFactory::GetForProfile(profile());
1279 if (!template_url_service)
1280 return;
1282 bool is_extension_keyword;
1283 const base::string16 short_name = template_url_service->GetKeywordShortName(
1284 keyword, &is_extension_keyword);
1285 const base::string16 min_string =
1286 location_bar_util::CalculateMinString(short_name);
1287 const base::string16 full_name = is_extension_keyword ?
1288 short_name :
1289 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, short_name);
1290 const base::string16 partial_name = is_extension_keyword ?
1291 min_string :
1292 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, min_string);
1293 gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_),
1294 base::UTF16ToUTF8(full_name).c_str());
1295 gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_),
1296 base::UTF16ToUTF8(partial_name).c_str());
1298 if (last_keyword_ != keyword) {
1299 last_keyword_ = keyword;
1301 if (is_extension_keyword) {
1302 const TemplateURL* template_url =
1303 template_url_service->GetTemplateURLForKeyword(keyword);
1304 gfx::Image image = extensions::OmniboxAPI::Get(profile())->
1305 GetOmniboxIcon(template_url->GetExtensionId());
1306 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1307 image.ToGdkPixbuf());
1308 } else {
1309 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1310 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1311 rb.GetNativeImageNamed(IDR_OMNIBOX_SEARCH).ToGdkPixbuf());
1316 void LocationBarViewGtk::SetKeywordHintLabel(const base::string16& keyword) {
1317 if (keyword.empty())
1318 return;
1320 TemplateURLService* template_url_service =
1321 TemplateURLServiceFactory::GetForProfile(profile());
1322 if (!template_url_service)
1323 return;
1325 bool is_extension_keyword;
1326 const base::string16 short_name = template_url_service->
1327 GetKeywordShortName(keyword, &is_extension_keyword);
1328 int message_id = is_extension_keyword ?
1329 IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT;
1330 std::vector<size_t> content_param_offsets;
1331 const base::string16 keyword_hint = l10n_util::GetStringFUTF16(
1332 message_id,
1333 base::string16(),
1334 short_name,
1335 &content_param_offsets);
1336 if (content_param_offsets.size() != 2) {
1337 // See comments on an identical NOTREACHED() in search_provider.cc.
1338 NOTREACHED();
1339 return;
1342 std::string leading(base::UTF16ToUTF8(
1343 keyword_hint.substr(0, content_param_offsets.front())));
1344 std::string trailing(base::UTF16ToUTF8(
1345 keyword_hint.substr(content_param_offsets.front())));
1346 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_),
1347 leading.c_str());
1348 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_),
1349 trailing.c_str());
1352 void LocationBarViewGtk::ShowFirstRunBubbleInternal() {
1353 if (!omnibox_view_.get() || !gtk_widget_get_window(widget()))
1354 return;
1356 gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_);
1357 bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing);
1358 FirstRunBubble::Show(browser_, location_icon_image_, bounds);
1361 void LocationBarViewGtk::ShowZoomBubble() {
1362 if (GetToolbarModel()->input_in_progress() || !GetWebContents())
1363 return;
1365 ZoomBubbleGtk::ShowBubble(GetWebContents(), true);
1368 void LocationBarViewGtk::AdjustChildrenVisibility() {
1369 int text_width = omnibox_view_->GetTextWidth();
1370 int available_width = entry_box_width_ - text_width - kInnerPadding;
1372 // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be
1373 // visible at the same time.
1374 if (!show_selected_keyword_ &&
1375 gtk_widget_get_visible(tab_to_search_alignment_)) {
1376 gtk_widget_hide(tab_to_search_alignment_);
1377 } else if (!show_keyword_hint_ &&
1378 gtk_widget_get_visible(tab_to_search_hint_)) {
1379 gtk_widget_hide(tab_to_search_hint_);
1382 if (show_selected_keyword_) {
1383 GtkRequisition box, full_label, partial_label;
1384 gtk_widget_size_request(tab_to_search_box_, &box);
1385 gtk_widget_size_request(tab_to_search_full_label_, &full_label);
1386 gtk_widget_size_request(tab_to_search_partial_label_, &partial_label);
1387 int full_partial_width_diff = full_label.width - partial_label.width;
1388 int full_box_width;
1389 int partial_box_width;
1390 if (gtk_widget_get_visible(tab_to_search_full_label_)) {
1391 full_box_width = box.width;
1392 partial_box_width = full_box_width - full_partial_width_diff;
1393 } else {
1394 partial_box_width = box.width;
1395 full_box_width = partial_box_width + full_partial_width_diff;
1398 if (partial_box_width >= entry_box_width_ - kInnerPadding) {
1399 gtk_widget_hide(tab_to_search_alignment_);
1400 } else if (full_box_width >= available_width) {
1401 gtk_widget_hide(tab_to_search_full_label_);
1402 gtk_widget_show(tab_to_search_partial_label_);
1403 gtk_widget_show(tab_to_search_alignment_);
1404 } else if (full_box_width < available_width) {
1405 gtk_widget_hide(tab_to_search_partial_label_);
1406 gtk_widget_show(tab_to_search_full_label_);
1407 gtk_widget_show(tab_to_search_alignment_);
1409 } else if (show_keyword_hint_) {
1410 GtkRequisition leading, icon, trailing;
1411 gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading);
1412 gtk_widget_size_request(tab_to_search_hint_icon_, &icon);
1413 gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing);
1414 int full_width = leading.width + icon.width + trailing.width;
1416 if (icon.width >= entry_box_width_ - kInnerPadding) {
1417 gtk_widget_hide(tab_to_search_hint_);
1418 } else if (full_width >= available_width) {
1419 gtk_widget_hide(tab_to_search_hint_leading_label_);
1420 gtk_widget_hide(tab_to_search_hint_trailing_label_);
1421 gtk_widget_show(tab_to_search_hint_);
1422 } else if (full_width < available_width) {
1423 gtk_widget_show(tab_to_search_hint_leading_label_);
1424 gtk_widget_show(tab_to_search_hint_trailing_label_);
1425 gtk_widget_show(tab_to_search_hint_);
1430 GtkWidget* LocationBarViewGtk::CreateIconButton(
1431 GtkWidget** image,
1432 int image_id,
1433 ViewID debug_id,
1434 int tooltip_id,
1435 gboolean (click_callback)(GtkWidget*, GdkEventButton*, gpointer)) {
1436 *image = image_id ?
1437 gtk_image_new_from_pixbuf(
1438 theme_service_->GetImageNamed(image_id).ToGdkPixbuf()) :
1439 gtk_image_new();
1441 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
1442 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
1443 0, kInnerPadding);
1444 gtk_container_add(GTK_CONTAINER(alignment), *image);
1446 GtkWidget* result = gtk_event_box_new();
1447 gtk_event_box_set_visible_window(GTK_EVENT_BOX(result), FALSE);
1448 gtk_container_add(GTK_CONTAINER(result), alignment);
1449 gtk_widget_show_all(result);
1451 if (debug_id != VIEW_ID_NONE)
1452 ViewIDUtil::SetID(result, debug_id);
1454 if (tooltip_id) {
1455 gtk_widget_set_tooltip_text(result,
1456 l10n_util::GetStringUTF8(tooltip_id).c_str());
1459 g_signal_connect(result, "button-press-event",
1460 G_CALLBACK(click_callback), this);
1462 return result;
1465 void LocationBarViewGtk::CreateZoomButton() {
1466 zoom_.Own(CreateIconButton(&zoom_image_,
1468 VIEW_ID_ZOOM_BUTTON,
1470 OnZoomButtonPressThunk));
1473 void LocationBarViewGtk::CreateManagePasswordsIconButton() {
1474 manage_passwords_icon_.Own(CreateIconButton(
1475 &manage_passwords_icon_image_, 0, VIEW_ID_MANAGE_PASSWORDS_ICON_BUTTON, 0,
1476 OnManagePasswordsIconButtonPressThunk));
1479 void LocationBarViewGtk::CreateStarButton() {
1480 star_.Own(CreateIconButton(&star_image_,
1482 VIEW_ID_STAR_BUTTON,
1483 IDS_TOOLTIP_STAR,
1484 OnStarButtonPressThunk));
1485 // We need to track when the star button is resized to show any bubble
1486 // attached to it at this time.
1487 g_signal_connect(star_image_, "size-allocate",
1488 G_CALLBACK(&OnStarButtonSizeAllocateThunk), this);
1491 void LocationBarViewGtk::UpdateZoomIcon() {
1492 WebContents* web_contents = GetWebContents();
1493 if (!zoom_.get() || !web_contents)
1494 return;
1496 ZoomController* zoom_controller =
1497 ZoomController::FromWebContents(web_contents);
1498 if (!zoom_controller || zoom_controller->IsAtDefaultZoom() ||
1499 GetToolbarModel()->input_in_progress()) {
1500 gtk_widget_hide(zoom_.get());
1501 ZoomBubbleGtk::CloseBubble();
1502 return;
1505 const int zoom_resource = zoom_controller->GetResourceForZoomLevel();
1506 gtk_image_set_from_pixbuf(GTK_IMAGE(zoom_image_),
1507 theme_service_->GetImageNamed(zoom_resource).ToGdkPixbuf());
1509 base::string16 tooltip = l10n_util::GetStringFUTF16Int(
1510 IDS_TOOLTIP_ZOOM, zoom_controller->zoom_percent());
1511 gtk_widget_set_tooltip_text(zoom_.get(), base::UTF16ToUTF8(tooltip).c_str());
1513 gtk_widget_show(zoom_.get());
1516 void LocationBarViewGtk::UpdateManagePasswordsIcon() {
1517 WebContents* web_contents = GetWebContents();
1518 if (!manage_passwords_icon_.get() || !web_contents)
1519 return;
1521 ManagePasswordsBubbleUIController* manage_passwords_bubble_ui_controller =
1522 ManagePasswordsBubbleUIController::FromWebContents(web_contents);
1523 if (!manage_passwords_bubble_ui_controller ||
1524 !manage_passwords_bubble_ui_controller->password_to_be_saved() ||
1525 GetToolbarModel()->input_in_progress()) {
1526 gtk_widget_hide(manage_passwords_icon_.get());
1527 ManagePasswordsBubbleGtk::CloseBubble();
1528 return;
1531 gtk_image_set_from_pixbuf(
1532 GTK_IMAGE(manage_passwords_icon_image_),
1533 theme_service_->GetImageNamed(IDR_SAVE_PASSWORD).ToGdkPixbuf());
1535 base::string16 tooltip =
1536 l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_TOOLTIP_SAVE);
1537 gtk_widget_set_tooltip_text(manage_passwords_icon_.get(),
1538 base::UTF16ToUTF8(tooltip).c_str());
1540 gtk_widget_show(manage_passwords_icon_.get());
1541 if (manage_passwords_bubble_ui_controller->
1542 manage_passwords_bubble_needs_showing()) {
1543 ShowManagePasswordsBubble();
1544 manage_passwords_bubble_ui_controller->OnBubbleShown();
1548 void LocationBarViewGtk::UpdateStarIcon() {
1549 if (!star_.get())
1550 return;
1551 // Indicate the star icon is not correctly sized. It will be marked as sized
1552 // when the next size-allocate signal is received by the star widget.
1553 star_sized_ = false;
1554 if (browser_defaults::bookmarks_enabled && !popup_window_mode_ &&
1555 !GetToolbarModel()->input_in_progress() &&
1556 edit_bookmarks_enabled_.GetValue() &&
1557 !IsBookmarkStarHiddenByExtension()) {
1558 gtk_widget_show_all(star_.get());
1559 int id = starred_ ? IDR_STAR_LIT : IDR_STAR;
1560 gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_),
1561 theme_service_->GetImageNamed(id).ToGdkPixbuf());
1562 gtk_widget_set_tooltip_text(star_.get(), l10n_util::GetStringUTF8(
1563 starred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR).c_str());
1564 } else {
1565 gtk_widget_hide_all(star_.get());
1569 bool LocationBarViewGtk::ShouldOnlyShowLocation() {
1570 return !browser_->is_type_tabbed();
1573 ////////////////////////////////////////////////////////////////////////////////
1574 // LocationBarViewGtk::PageToolViewGtk
1576 LocationBarViewGtk::PageToolViewGtk::PageToolViewGtk()
1577 : alignment_(gtk_alignment_new(0, 0, 1, 1)),
1578 event_box_(gtk_event_box_new()),
1579 hbox_(gtk_hbox_new(FALSE, kInnerPadding)),
1580 image_(gtk_image_new()),
1581 label_(gtk_label_new(NULL)),
1582 animation_(this),
1583 weak_factory_(this) {
1584 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0);
1585 gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get());
1587 // Make the event box not visible so it does not paint a background.
1588 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1589 g_signal_connect(event_box_.get(), "button-press-event",
1590 G_CALLBACK(&OnButtonPressedThunk), this);
1591 g_signal_connect(event_box_.get(), "expose-event",
1592 G_CALLBACK(&OnExposeThunk), this);
1594 gtk_widget_set_no_show_all(label_.get(), TRUE);
1595 gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE);
1597 gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0);
1598 gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0);
1600 gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder);
1602 gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_);
1603 gtk_widget_hide(widget());
1605 animation_.SetSlideDuration(kPageToolAnimationTime);
1608 LocationBarViewGtk::PageToolViewGtk::~PageToolViewGtk() {
1609 image_.Destroy();
1610 label_.Destroy();
1611 event_box_.Destroy();
1612 alignment_.Destroy();
1615 bool LocationBarViewGtk::PageToolViewGtk::IsVisible() {
1616 return gtk_widget_get_visible(widget());
1619 void LocationBarViewGtk::PageToolViewGtk::AnimationProgressed(
1620 const gfx::Animation* animation) {
1621 gtk_widget_set_size_request(
1622 label_.get(),
1623 animation->GetCurrentValue() * label_req_.width,
1624 -1);
1627 void LocationBarViewGtk::PageToolViewGtk::AnimationEnded(
1628 const gfx::Animation* animation) {
1631 void LocationBarViewGtk::PageToolViewGtk::AnimationCanceled(
1632 const gfx::Animation* animation) {
1635 void LocationBarViewGtk::PageToolViewGtk::StartAnimating() {
1636 if (animation_.IsShowing() || animation_.IsClosing())
1637 return;
1639 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE);
1640 GdkColor border_color = GetButtonBorderColor();
1641 gtk_util::ActAsRoundedWindow(event_box_.get(), border_color,
1642 kCornerSize,
1643 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
1645 gtk_widget_set_size_request(label_.get(), -1, -1);
1646 gtk_widget_size_request(label_.get(), &label_req_);
1647 gtk_widget_set_size_request(label_.get(), 0, -1);
1648 gtk_widget_show(label_.get());
1650 animation_.Show();
1653 void LocationBarViewGtk::PageToolViewGtk::CloseAnimation() {
1654 animation_.Hide();
1657 gboolean LocationBarViewGtk::PageToolViewGtk::OnButtonPressed(
1658 GtkWidget* sender, GdkEvent* event) {
1659 OnClick(sender);
1660 return TRUE;
1663 gboolean LocationBarViewGtk::PageToolViewGtk::OnExpose(
1664 GtkWidget* sender, GdkEventExpose* event) {
1665 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageToolViewGtk::OnExpose");
1667 if (!(animation_.IsShowing() || animation_.IsClosing()))
1668 return FALSE;
1670 GtkAllocation allocation;
1671 gtk_widget_get_allocation(sender, &allocation);
1672 const int height = allocation.height;
1674 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
1675 gdk_cairo_rectangle(cr, &event->area);
1676 cairo_clip(cr);
1678 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
1680 const GdkColor top_color = GetGradientTopColor();
1681 const GdkColor bottom_color = GetGradientBottomColor();
1682 cairo_pattern_add_color_stop_rgb(
1683 pattern, 0.0,
1684 top_color.red/255.0,
1685 top_color.blue/255.0,
1686 top_color.green/255.0);
1687 cairo_pattern_add_color_stop_rgb(
1688 pattern, 1.0,
1689 bottom_color.red/255.0,
1690 bottom_color.blue/255.0,
1691 bottom_color.green/255.0);
1693 cairo_set_source(cr, pattern);
1694 cairo_paint(cr);
1695 cairo_pattern_destroy(pattern);
1696 cairo_destroy(cr);
1698 return FALSE;
1701 ////////////////////////////////////////////////////////////////////////////////
1702 // LocationBarViewGtk::PageActionViewGtk
1704 LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
1705 LocationBarViewGtk* owner,
1706 ExtensionAction* page_action)
1707 : owner_(NULL),
1708 page_action_(page_action),
1709 current_tab_id_(-1),
1710 window_(NULL),
1711 accel_group_(NULL),
1712 preview_enabled_(false) {
1713 event_box_.Own(gtk_event_box_new());
1714 gtk_widget_set_size_request(event_box_.get(),
1715 extensions::IconsInfo::kPageActionIconMaxSize,
1716 extensions::IconsInfo::kPageActionIconMaxSize);
1718 // Make the event box not visible so it does not paint a background.
1719 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1720 g_signal_connect(event_box_.get(), "button-press-event",
1721 G_CALLBACK(&OnButtonPressedThunk), this);
1722 g_signal_connect_after(event_box_.get(), "expose-event",
1723 G_CALLBACK(OnExposeEventThunk), this);
1724 g_signal_connect(event_box_.get(), "realize",
1725 G_CALLBACK(OnRealizeThunk), this);
1727 image_.Own(gtk_image_new());
1728 gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get());
1730 const Extension* extension =
1731 owner->profile()->GetExtensionService()->GetExtensionById(
1732 page_action->extension_id(), false);
1733 DCHECK(extension);
1735 icon_factory_.reset(new ExtensionActionIconFactory(
1736 owner->profile(), extension, page_action, this));
1738 // We set the owner last of all so that we can determine whether we are in
1739 // the process of initializing this class or not.
1740 owner_ = owner;
1743 LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
1744 DisconnectPageActionAccelerator();
1746 image_.Destroy();
1747 event_box_.Destroy();
1750 bool LocationBarViewGtk::PageActionViewGtk::IsVisible() {
1751 return gtk_widget_get_visible(widget());
1754 void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
1755 WebContents* contents, const GURL& url) {
1756 // Save this off so we can pass it back to the extension when the action gets
1757 // executed. See PageActionImageView::OnMousePressed.
1758 current_tab_id_ =
1759 contents ? extensions::ExtensionTabUtil::GetTabId(contents) : -1;
1760 current_url_ = url;
1762 bool visible = contents &&
1763 (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_));
1764 if (visible) {
1765 // Set the tooltip.
1766 gtk_widget_set_tooltip_text(event_box_.get(),
1767 page_action_->GetTitle(current_tab_id_).c_str());
1769 // Set the image.
1770 gfx::Image icon = icon_factory_->GetIcon(current_tab_id_);
1771 if (!icon.IsEmpty()) {
1772 GdkPixbuf* pixbuf = icon.ToGdkPixbuf();
1773 DCHECK(pixbuf);
1774 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
1778 bool old_visible = IsVisible();
1779 if (visible)
1780 gtk_widget_show_all(event_box_.get());
1781 else
1782 gtk_widget_hide_all(event_box_.get());
1784 if (visible != old_visible) {
1785 content::NotificationService::current()->Notify(
1786 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
1787 content::Source<ExtensionAction>(page_action_),
1788 content::Details<WebContents>(contents));
1792 void LocationBarViewGtk::PageActionViewGtk::OnIconUpdated() {
1793 // If we have no owner, that means this class is still being constructed.
1794 WebContents* web_contents = owner_ ? owner_->GetWebContents() : NULL;
1795 if (web_contents)
1796 UpdateVisibility(web_contents, current_url_);
1799 void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
1800 GdkEventButton event = {};
1801 event.type = GDK_BUTTON_PRESS;
1802 event.button = 1;
1803 OnButtonPressed(widget(), &event);
1806 void LocationBarViewGtk::PageActionViewGtk::Observe(
1807 int type,
1808 const content::NotificationSource& source,
1809 const content::NotificationDetails& details) {
1810 DCHECK_EQ(type, chrome::NOTIFICATION_WINDOW_CLOSED);
1811 DisconnectPageActionAccelerator();
1814 void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
1815 ExtensionAction* action) {
1816 ExtensionPopupGtk::Show(
1817 action->GetPopupUrl(current_tab_id_),
1818 owner_->browser_,
1819 event_box_.get(),
1820 ExtensionPopupGtk::SHOW_AND_INSPECT);
1823 void LocationBarViewGtk::PageActionViewGtk::ConnectPageActionAccelerator() {
1824 const extensions::ExtensionSet* extensions =
1825 owner_->profile()->GetExtensionService()->extensions();
1826 const Extension* extension =
1827 extensions->GetByID(page_action_->extension_id());
1828 window_ = owner_->browser()->window()->GetNativeWindow();
1830 extensions::CommandService* command_service =
1831 extensions::CommandService::Get(owner_->profile());
1833 extensions::Command command_page_action;
1834 if (command_service->GetPageActionCommand(
1835 extension->id(),
1836 extensions::CommandService::ACTIVE_ONLY,
1837 &command_page_action,
1838 NULL)) {
1839 // Found the page action shortcut command, register it.
1840 page_action_keybinding_.reset(
1841 new ui::Accelerator(command_page_action.accelerator()));
1844 if (page_action_keybinding_.get()) {
1845 accel_group_ = gtk_accel_group_new();
1846 gtk_window_add_accel_group(window_, accel_group_);
1848 gtk_accel_group_connect(
1849 accel_group_,
1850 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
1851 ui::GetGdkModifierForAccelerator(*page_action_keybinding_),
1852 GtkAccelFlags(0),
1853 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
1855 // Since we've added an accelerator, we'll need to unregister it before
1856 // the window is closed, so we listen for the window being closed.
1857 registrar_.Add(this,
1858 chrome::NOTIFICATION_WINDOW_CLOSED,
1859 content::Source<GtkWindow>(window_));
1863 void LocationBarViewGtk::PageActionViewGtk::DisconnectPageActionAccelerator() {
1864 if (accel_group_) {
1865 if (page_action_keybinding_.get()) {
1866 gtk_accel_group_disconnect_key(
1867 accel_group_,
1868 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
1869 ui::GetGdkModifierForAccelerator(*page_action_keybinding_));
1871 gtk_window_remove_accel_group(window_, accel_group_);
1872 g_object_unref(accel_group_);
1873 accel_group_ = NULL;
1874 page_action_keybinding_.reset(NULL);
1878 gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed(
1879 GtkWidget* sender,
1880 GdkEventButton* event) {
1881 // Double and triple-clicks generate both a GDK_BUTTON_PRESS and a
1882 // GDK_[23]BUTTON_PRESS event. We don't want to double-trigger by acting on
1883 // both.
1884 if (event->type != GDK_BUTTON_PRESS)
1885 return TRUE;
1887 WebContents* web_contents = owner_->GetWebContents();
1888 if (!web_contents)
1889 return TRUE;
1891 ExtensionService* extension_service =
1892 owner_->profile()->GetExtensionService();
1893 if (!extension_service)
1894 return TRUE;
1896 const Extension* extension =
1897 extension_service->extensions()->GetByID(page_action()->extension_id());
1898 if (!extension)
1899 return TRUE;
1901 LocationBarController* controller =
1902 extensions::TabHelper::FromWebContents(web_contents)->
1903 location_bar_controller();
1905 switch (controller->OnClicked(extension->id(), event->button)) {
1906 case LocationBarController::ACTION_NONE:
1907 break;
1909 case LocationBarController::ACTION_SHOW_POPUP:
1910 ExtensionPopupGtk::Show(
1911 page_action_->GetPopupUrl(current_tab_id_),
1912 owner_->browser_,
1913 event_box_.get(),
1914 ExtensionPopupGtk::SHOW);
1915 break;
1917 case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
1918 context_menu_model_ =
1919 new ExtensionContextMenuModel(extension, owner_->browser_, this);
1920 context_menu_.reset(
1921 new MenuGtk(NULL, context_menu_model_.get()));
1922 context_menu_->PopupForWidget(sender, event->button, event->time);
1923 break;
1926 return TRUE;
1929 gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
1930 GtkWidget* widget,
1931 GdkEventExpose* event) {
1932 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageActionViewGtk::OnExpose");
1933 WebContents* contents = owner_->GetWebContents();
1934 if (!contents)
1935 return FALSE;
1937 int tab_id = extensions::ExtensionTabUtil::GetTabId(contents);
1938 if (tab_id < 0)
1939 return FALSE;
1941 std::string badge_text = page_action_->GetBadgeText(tab_id);
1942 if (badge_text.empty())
1943 return FALSE;
1945 gfx::CanvasSkiaPaint canvas(event, false);
1946 GtkAllocation allocation;
1947 gtk_widget_get_allocation(widget, &allocation);
1948 page_action_->PaintBadge(&canvas, gfx::Rect(allocation), tab_id);
1949 return FALSE;
1952 void LocationBarViewGtk::PageActionViewGtk::OnRealize(GtkWidget* widget) {
1953 ConnectPageActionAccelerator();
1956 // static
1957 gboolean LocationBarViewGtk::PageActionViewGtk::OnGtkAccelerator(
1958 GtkAccelGroup* accel_group,
1959 GObject* acceleratable,
1960 guint keyval,
1961 GdkModifierType modifier,
1962 void* user_data) {
1963 PageActionViewGtk* view = static_cast<PageActionViewGtk*>(user_data);
1964 if (!gtk_widget_get_visible(view->widget()))
1965 return FALSE;
1967 GdkEventButton event = {};
1968 event.type = GDK_BUTTON_PRESS;
1969 event.button = 1;
1970 return view->OnButtonPressed(view->widget(), &event);