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.
4
5#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
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/chrome_notification_types.h"
25#include "chrome/browser/command_updater.h"
26#include "chrome/browser/content_settings/tab_specific_content_settings.h"
27#include "chrome/browser/defaults.h"
28#include "chrome/browser/extensions/api/commands/command_service.h"
29#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
30#include "chrome/browser/extensions/extension_action.h"
31#include "chrome/browser/extensions/extension_service.h"
32#include "chrome/browser/extensions/extension_tab_util.h"
33#include "chrome/browser/extensions/location_bar_controller.h"
34#include "chrome/browser/extensions/script_bubble_controller.h"
35#include "chrome/browser/extensions/tab_helper.h"
36#include "chrome/browser/favicon/favicon_tab_helper.h"
37#include "chrome/browser/profiles/profile.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/nine_box.h"
59#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
60#include "chrome/browser/ui/gtk/rounded_window.h"
61#include "chrome/browser/ui/gtk/script_bubble_gtk.h"
62#include "chrome/browser/ui/gtk/view_id_util.h"
63#include "chrome/browser/ui/gtk/zoom_bubble_gtk.h"
64#include "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.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/tabs/tab_strip_model.h"
69#include "chrome/browser/ui/webui/extensions/extension_info_ui.h"
70#include "chrome/browser/ui/zoom/zoom_controller.h"
71#include "chrome/common/badge_util.h"
72#include "chrome/common/chrome_switches.h"
73#include "chrome/common/extensions/extension.h"
74#include "chrome/common/extensions/extension_manifest_constants.h"
75#include "chrome/common/extensions/feature_switch.h"
76#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
77#include "chrome/common/pref_names.h"
78#include "content/public/browser/navigation_entry.h"
79#include "content/public/browser/notification_service.h"
80#include "content/public/browser/web_contents.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/dragdrop/gtk_dnd_util.h"
86#include "ui/base/gtk/gtk_hig_constants.h"
87#include "ui/base/gtk/gtk_signal_registrar.h"
88#include "ui/base/l10n/l10n_util.h"
89#include "ui/base/resource/resource_bundle.h"
90#include "ui/base/window_open_disposition.h"
91#include "ui/gfx/canvas_skia_paint.h"
92#include "ui/gfx/font.h"
93#include "ui/gfx/gtk_util.h"
94#include "ui/gfx/image/image.h"
95
96using content::NavigationEntry;
97using content::OpenURLParams;
98using content::WebContents;
99using extensions::LocationBarController;
100using extensions::Extension;
101
102namespace {
103
104// We are positioned with a little bit of extra space that we don't use now.
105const int kTopMargin = 1;
106const int kBottomMargin = 1;
107// We draw a border on the top and bottom (but not on left or right).
108const int kBorderThickness = 1;
109
110const int kPopupEdgeThickness = 1;
111const int kNormalEdgeThickness = 2;
112
113// Spacing needed to align the bubble with the left side of the omnibox.
114const int kFirstRunBubbleLeftSpacing = 4;
115
116// The padding around the top, bottom, and sides of the location bar hbox.
117// We don't want to edit control's text to be right against the edge,
118// as well the tab to search box and other widgets need to have the padding on
119// top and bottom to avoid drawing larger than the location bar space.
120const int kHboxBorder = 2;
121
122// Padding between the elements in the bar.
123const int kInnerPadding = 2;
124const int kScriptBadgeInnerPadding = 9;
125
126// Colors used to draw the EV certificate rounded bubble.
127const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
128const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
129const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
130
131// Colors used to draw the Tab to Search rounded bubble.
132const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
133const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
134
135// Use weak gray for showing search and keyword hint text.
136const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
137
138// Size of the rounding of the "Search site for:" box.
139const int kCornerSize = 3;
140
141// Default page tool animation time (open and close). In ms.
142const int kPageToolAnimationTime = 150;
143
144// The time, in ms, that the content setting label is fully displayed, for the
145// cases where we animate it into and out of view.
146const int kContentSettingImageDisplayTime = 3200;
147// The time, in ms, of the animation (open and close).
148const int kContentSettingImageAnimationTime = 150;
149
150// Color of border of content setting area (icon/label).
151const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
152// Colors for the background gradient.
153const GdkColor kContentSettingTopColor = GDK_COLOR_RGB(0xff, 0xf8, 0xd4);
154const GdkColor kContentSettingBottomColor = GDK_COLOR_RGB(0xff, 0xe6, 0xaf);
155
156// Styling for gray button.
157const GdkColor kGrayBorderColor = GDK_COLOR_RGB(0xa0, 0xa0, 0xa0);
158const GdkColor kTopColorGray = GDK_COLOR_RGB(0xe5, 0xe5, 0xe5);
159const GdkColor kBottomColorGray = GDK_COLOR_RGB(0xd0, 0xd0, 0xd0);
160
161inline int InnerPadding() {
162  return extensions::FeatureSwitch::script_badges()->IsEnabled() ?
163      kScriptBadgeInnerPadding : kInnerPadding;
164}
165
166// If widget is visible, increment the int pointed to by count.
167// Suitible for use with gtk_container_foreach.
168void CountVisibleWidgets(GtkWidget* widget, gpointer count) {
169  if (gtk_widget_get_visible(widget))
170    *static_cast<int*>(count) += 1;
171}
172
173class ContentSettingImageViewGtk : public LocationBarViewGtk::PageToolViewGtk,
174                                   public BubbleDelegateGtk {
175 public:
176  ContentSettingImageViewGtk(ContentSettingsType content_type,
177                             const LocationBarViewGtk* parent);
178  virtual ~ContentSettingImageViewGtk();
179
180  // PageToolViewGtk
181  virtual void Update(WebContents* web_contents) OVERRIDE;
182
183  // ui::AnimationDelegate
184  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
185
186 private:
187  // PageToolViewGtk
188  virtual GdkColor button_border_color() const OVERRIDE;
189  virtual GdkColor gradient_top_color() const OVERRIDE;
190  virtual GdkColor gradient_bottom_color() const OVERRIDE;
191  virtual void OnClick(GtkWidget* sender) OVERRIDE;
192
193  // BubbleDelegateGtk
194  virtual void BubbleClosing(BubbleGtk* bubble,
195                             bool closed_by_escape) OVERRIDE;
196
197  scoped_ptr<ContentSettingImageModel> content_setting_image_model_;
198
199  // The currently shown bubble if any.
200  ContentSettingBubbleGtk* content_setting_bubble_;
201
202  DISALLOW_COPY_AND_ASSIGN(ContentSettingImageViewGtk);
203};
204
205ContentSettingImageViewGtk::ContentSettingImageViewGtk(
206    ContentSettingsType content_type,
207    const LocationBarViewGtk* parent)
208    : PageToolViewGtk(parent),
209      content_setting_image_model_(
210          ContentSettingImageModel::CreateContentSettingImageModel(
211              content_type)),
212      content_setting_bubble_(NULL) {
213  animation_.SetSlideDuration(kContentSettingImageAnimationTime);
214}
215
216ContentSettingImageViewGtk::~ContentSettingImageViewGtk() {
217  if (content_setting_bubble_)
218    content_setting_bubble_->Close();
219}
220
221void ContentSettingImageViewGtk::Update(WebContents* web_contents) {
222  if (web_contents)
223    content_setting_image_model_->UpdateFromWebContents(web_contents);
224
225  if (!content_setting_image_model_->is_visible()) {
226    gtk_widget_hide(widget());
227    return;
228  }
229
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());
233
234  gtk_widget_set_tooltip_text(widget(),
235      content_setting_image_model_->get_tooltip().c_str());
236  gtk_widget_show_all(widget());
237
238  if (!web_contents)
239    return;
240
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;
246
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());
251
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;
257
258  gtk_label_set_text(GTK_LABEL(label_.get()),
259      l10n_util::GetStringUTF8(label_string_id).c_str());
260  StartAnimating();
261}
262
263void ContentSettingImageViewGtk::AnimationEnded(
264    const ui::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);
275  }
276}
277
278GdkColor ContentSettingImageViewGtk::
279    button_border_color() const {
280  return kContentSettingBorderColor;
281}
282
283GdkColor ContentSettingImageViewGtk::
284    gradient_top_color() const {
285  return kContentSettingTopColor;
286}
287
288GdkColor ContentSettingImageViewGtk::
289    gradient_bottom_color() const {
290  return kContentSettingBottomColor;
291}
292
293void ContentSettingImageViewGtk::OnClick(
294    GtkWidget* sender) {
295  WebContents* web_contents = parent_->GetWebContents();
296  if (!web_contents)
297    return;
298  Profile* profile = parent_->browser()->profile();
299  content_setting_bubble_ = new ContentSettingBubbleGtk(
300      sender, this,
301      ContentSettingBubbleModel::CreateContentSettingBubbleModel(
302          parent_->browser()->content_setting_bubble_model_delegate(),
303          web_contents,
304          profile,
305          content_setting_image_model_->get_content_settings_type()),
306      profile, web_contents);
307  return;
308}
309
310void ContentSettingImageViewGtk::BubbleClosing(
311    BubbleGtk* bubble,
312    bool closed_by_escape) {
313  content_setting_bubble_ = NULL;
314}
315
316gfx::Rect AllocationToRect(const GtkAllocation& allocation) {
317  return gfx::Rect(allocation.x, allocation.y,
318                   allocation.width, allocation.height);
319}
320
321}  // namespace
322
323////////////////////////////////////////////////////////////////////////////////
324// LocationBarViewGtk
325
326// static
327const GdkColor LocationBarViewGtk::kBackgroundColor =
328    GDK_COLOR_RGB(255, 255, 255);
329
330LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
331    : zoom_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      command_updater_(browser->command_controller()->command_updater()),
352      toolbar_model_(browser->toolbar_model()),
353      browser_(browser),
354      disposition_(CURRENT_TAB),
355      transition_(content::PageTransitionFromInt(
356          content::PAGE_TRANSITION_TYPED |
357          content::PAGE_TRANSITION_FROM_ADDRESS_BAR)),
358      weak_ptr_factory_(this),
359      popup_window_mode_(false),
360      theme_service_(NULL),
361      hbox_width_(0),
362      entry_box_width_(0),
363      show_selected_keyword_(false),
364      show_keyword_hint_(false) {
365}
366
367LocationBarViewGtk::~LocationBarViewGtk() {
368  // All of our widgets should be children of / owned by the alignment.
369  zoom_.Destroy();
370  script_bubble_button_.Destroy();
371  star_.Destroy();
372  hbox_.Destroy();
373  content_setting_hbox_.Destroy();
374  page_action_hbox_.Destroy();
375}
376
377void LocationBarViewGtk::Init(bool popup_window_mode) {
378  popup_window_mode_ = popup_window_mode;
379
380  Profile* profile = browser_->profile();
381  theme_service_ = GtkThemeService::GetFrom(profile);
382
383  // Create the widget first, so we can pass it to the OmniboxViewGtk.
384  hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
385  gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
386  // We will paint for the alignment, to paint the background and border.
387  gtk_widget_set_app_paintable(hbox_.get(), TRUE);
388  // Redraw the whole location bar when it changes size (e.g., when toggling
389  // the home button on/off.
390  gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
391
392  // Now initialize the OmniboxViewGtk.
393  location_entry_.reset(new OmniboxViewGtk(this, toolbar_model_, browser_,
394      browser_->profile(), command_updater_, popup_window_mode_, hbox_.get()));
395  location_entry_->Init();
396
397  g_signal_connect(hbox_.get(), "expose-event",
398                   G_CALLBACK(&HandleExposeThunk), this);
399
400  BuildSiteTypeArea();
401
402  // Put |tab_to_search_box_|, |location_entry_|, and |tab_to_search_hint_| into
403  // a sub hbox, so that we can make this part horizontally shrinkable without
404  // affecting other elements in the location bar.
405  entry_box_ = gtk_hbox_new(FALSE, InnerPadding());
406  gtk_widget_show(entry_box_);
407  gtk_widget_set_size_request(entry_box_, 0, -1);
408  gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0);
409
410  // We need to adjust the visibility of the search hint widgets according to
411  // the horizontal space in the |entry_box_|.
412  g_signal_connect(entry_box_, "size-allocate",
413                   G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this);
414
415  // Tab to search (the keyword box on the left hand side).
416  tab_to_search_full_label_ =
417      theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
418  tab_to_search_partial_label_ =
419      theme_service_->BuildLabel(std::string(), ui::kGdkBlack);
420  GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0);
421  gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
422                     tab_to_search_full_label_, FALSE, FALSE, 0);
423  gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
424                     tab_to_search_partial_label_, FALSE, FALSE, 0);
425  GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0);
426  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
427  tab_to_search_magnifier_ = gtk_image_new_from_pixbuf(
428      rb.GetNativeImageNamed(IDR_KEYWORD_SEARCH_MAGNIFIER).ToGdkPixbuf());
429  gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_,
430                     FALSE, FALSE, 0);
431  gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox,
432                               false, 0);
433
434  // This creates a box around the keyword text with a border, background color,
435  // and padding around the text.
436  tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
437      tab_to_search_hbox, NULL, 1, 1, 1, 3);
438  gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box");
439  gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor,
440                               kCornerSize,
441                               gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
442
443  // Put the event box in an alignment to get the padding correct.
444  tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1);
445  gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_),
446                    tab_to_search_box_);
447  gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_,
448                     FALSE, FALSE, 0);
449
450  // Show all children widgets of |tab_to_search_box_| initially, except
451  // |tab_to_search_partial_label_|.
452  gtk_widget_show_all(tab_to_search_box_);
453  gtk_widget_hide(tab_to_search_partial_label_);
454
455  location_entry_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
456  gtk_container_add(GTK_CONTAINER(location_entry_alignment_),
457                    location_entry_->GetNativeView());
458  gtk_box_pack_start(GTK_BOX(entry_box_), location_entry_alignment_,
459                     TRUE, TRUE, 0);
460
461  // Tab to search notification (the hint on the right hand side).
462  tab_to_search_hint_ = gtk_hbox_new(FALSE, 0);
463  gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint");
464  tab_to_search_hint_leading_label_ =
465      theme_service_->BuildLabel(std::string(), kHintTextColor);
466  gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE);
467  tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf(
468      rb.GetNativeImageNamed(IDR_OMNIBOX_KEYWORD_HINT_TAB).ToGdkPixbuf());
469  tab_to_search_hint_trailing_label_ =
470      theme_service_->BuildLabel(std::string(), kHintTextColor);
471  gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE);
472  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
473                     tab_to_search_hint_leading_label_,
474                     FALSE, FALSE, 0);
475  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
476                     tab_to_search_hint_icon_,
477                     FALSE, FALSE, 0);
478  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
479                     tab_to_search_hint_trailing_label_,
480                     FALSE, FALSE, 0);
481  // Show all children widgets of |tab_to_search_hint_| initially.
482  gtk_widget_show_all(tab_to_search_hint_);
483  gtk_widget_hide(tab_to_search_hint_);
484  // tab_to_search_hint_ gets hidden initially in OnChanged.  Hiding it here
485  // doesn't work, someone is probably calling show_all on our parent box.
486  gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
487
488  if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
489    // Hide the star icon in popups, app windows, etc.
490    CreateStarButton();
491    gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
492  }
493
494  CreateScriptBubbleButton();
495  gtk_box_pack_end(GTK_BOX(hbox_.get()), script_bubble_button_.get(), FALSE,
496                   FALSE, 0);
497
498  CreateZoomButton();
499  gtk_box_pack_end(GTK_BOX(hbox_.get()), zoom_.get(), FALSE, FALSE, 0);
500
501  content_setting_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding() + 1));
502  gtk_widget_set_name(content_setting_hbox_.get(),
503                      "chrome-content-setting-hbox");
504  gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(),
505                   FALSE, FALSE, 1);
506
507  for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
508    ContentSettingImageViewGtk* content_setting_view =
509        new ContentSettingImageViewGtk(
510            static_cast<ContentSettingsType>(i), this);
511    content_setting_views_.push_back(content_setting_view);
512    gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()),
513                     content_setting_view->widget(), FALSE, FALSE, 0);
514  }
515
516  page_action_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding()));
517  gtk_widget_set_name(page_action_hbox_.get(),
518                      "chrome-page-action-hbox");
519  gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(),
520                   FALSE, FALSE, 0);
521
522  // Now that we've created the widget hierarchy, connect to the main |hbox_|'s
523  // size-allocate so we can do proper resizing and eliding on
524  // |security_info_label_|.
525  g_signal_connect(hbox_.get(), "size-allocate",
526                   G_CALLBACK(&OnHboxSizeAllocateThunk), this);
527
528  registrar_.Add(this,
529                 chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
530                 content::Source<ThemeService>(theme_service_));
531  registrar_.Add(this,
532                 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
533                 content::Source<Profile>(browser()->profile()));
534  edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
535                               profile->GetPrefs(),
536                               base::Bind(&LocationBarViewGtk::UpdateStarIcon,
537                                          base::Unretained(this)));
538
539  theme_service_->InitThemesFor(this);
540}
541
542void LocationBarViewGtk::BuildSiteTypeArea() {
543  location_icon_image_ = gtk_image_new();
544  gtk_widget_set_name(location_icon_image_, "chrome-location-icon");
545
546  GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1);
547  gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0);
548  gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_);
549  gtk_widget_show_all(icon_alignment);
550
551  security_info_label_ = gtk_label_new(NULL);
552  gtk_label_set_ellipsize(GTK_LABEL(security_info_label_),
553                          PANGO_ELLIPSIZE_MIDDLE);
554  gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL,
555                       &kEvSecureTextColor);
556  gtk_widget_set_name(security_info_label_,
557                      "chrome-location-bar-security-info-label");
558
559  GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1);
560  gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment,
561                     FALSE, FALSE, 0);
562  gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_,
563                     FALSE, FALSE, 2);
564
565  site_type_event_box_ = gtk_event_box_new();
566  gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL,
567                       &kEvSecureBackgroundColor);
568  g_signal_connect(site_type_event_box_, "drag-data-get",
569                   G_CALLBACK(&OnIconDragDataThunk), this);
570  g_signal_connect(site_type_event_box_, "drag-begin",
571                   G_CALLBACK(&OnIconDragBeginThunk), this);
572  g_signal_connect(site_type_event_box_, "drag-end",
573                   G_CALLBACK(&OnIconDragEndThunk), this);
574
575  // Make the event box not visible so it does not paint a background.
576  gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
577                                   FALSE);
578  gtk_widget_set_name(site_type_event_box_,
579                      "chrome-location-icon-eventbox");
580  gtk_container_add(GTK_CONTAINER(site_type_event_box_),
581                    site_type_hbox);
582
583  // Put the event box in an alignment to get the padding correct.
584  site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1);
585  gtk_container_add(GTK_CONTAINER(site_type_alignment_),
586                    site_type_event_box_);
587  gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_,
588                     FALSE, FALSE, 0);
589
590  gtk_widget_set_tooltip_text(location_icon_image_,
591      l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
592
593  g_signal_connect(site_type_event_box_, "button-release-event",
594                   G_CALLBACK(&OnIconReleasedThunk), this);
595}
596
597void LocationBarViewGtk::SetSiteTypeDragSource() {
598  bool enable = !GetLocationEntry()->IsEditingOrEmpty();
599  if (enable_location_drag_ == enable)
600    return;
601  enable_location_drag_ = enable;
602
603  if (!enable) {
604    gtk_drag_source_unset(site_type_event_box_);
605    return;
606  }
607
608  gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK,
609                      NULL, 0, GDK_ACTION_COPY);
610  ui::SetSourceTargetListFromCodeMask(site_type_event_box_,
611                                      ui::TEXT_PLAIN |
612                                      ui::TEXT_URI_LIST |
613                                      ui::CHROME_NAMED_URL);
614}
615
616WebContents* LocationBarViewGtk::GetWebContents() const {
617  return browser_->tab_strip_model()->GetActiveWebContents();
618}
619
620void LocationBarViewGtk::SetPreviewEnabledPageAction(
621    ExtensionAction* page_action,
622    bool preview_enabled) {
623  DCHECK(page_action);
624  for (ScopedVector<PageActionViewGtk>::iterator iter =
625       page_action_views_.begin(); iter != page_action_views_.end();
626       ++iter) {
627    if ((*iter)->page_action() == page_action) {
628      (*iter)->set_preview_enabled(preview_enabled);
629      UpdatePageActions();
630      return;
631    }
632  }
633}
634
635GtkWidget* LocationBarViewGtk::GetPageActionWidget(
636    ExtensionAction* page_action) {
637  DCHECK(page_action);
638  for (ScopedVector<PageActionViewGtk>::iterator iter =
639           page_action_views_.begin();
640       iter != page_action_views_.end();
641       ++iter) {
642    if ((*iter)->page_action() == page_action)
643      return (*iter)->widget();
644  }
645  return NULL;
646}
647
648void LocationBarViewGtk::Update(const WebContents* contents) {
649  UpdateZoomIcon();
650  UpdateScriptBubbleIcon();
651  UpdateStarIcon();
652  UpdateSiteTypeArea();
653  UpdateContentSettingsIcons();
654  UpdatePageActions();
655  location_entry_->Update(contents);
656  // The security level (background color) could have changed, etc.
657  if (theme_service_->UsingNativeTheme()) {
658    // In GTK mode, we need our parent to redraw, as it draws the text entry
659    // border.
660    gtk_widget_queue_draw(gtk_widget_get_parent(widget()));
661  } else {
662    gtk_widget_queue_draw(widget());
663  }
664  ZoomBubbleGtk::CloseBubble();
665}
666
667void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url,
668    WindowOpenDisposition disposition,
669    content::PageTransition transition,
670    const GURL& alternate_nav_url) {
671  if (url.is_valid()) {
672    location_input_ = UTF8ToUTF16(url.spec());
673    disposition_ = disposition;
674    transition_ = content::PageTransitionFromInt(
675        transition | content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
676
677    if (command_updater_) {
678      if (!alternate_nav_url.is_valid()) {
679        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
680      } else {
681        AlternateNavURLFetcher* fetcher =
682            new AlternateNavURLFetcher(alternate_nav_url);
683        // The AlternateNavURLFetcher will listen for the pending navigation
684        // notification that will be issued as a result of the "open URL." It
685        // will automatically install itself into that navigation controller.
686        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
687        if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
688          // I'm not sure this should be reachable, but I'm not also sure enough
689          // that it shouldn't to stick in a NOTREACHED().  In any case, this is
690          // harmless.
691          delete fetcher;
692        } else {
693          // The navigation controller will delete the fetcher.
694        }
695      }
696    }
697  }
698}
699
700void LocationBarViewGtk::OnChanged() {
701  UpdateSiteTypeArea();
702
703  const string16 keyword(location_entry_->model()->keyword());
704  const bool is_keyword_hint = location_entry_->model()->is_keyword_hint();
705  show_selected_keyword_ = !keyword.empty() && !is_keyword_hint;
706  show_keyword_hint_ = !keyword.empty() && is_keyword_hint;
707
708  if (show_selected_keyword_)
709    SetKeywordLabel(keyword);
710
711  if (show_keyword_hint_)
712    SetKeywordHintLabel(keyword);
713
714  AdjustChildrenVisibility();
715}
716
717void LocationBarViewGtk::OnSelectionBoundsChanged() {
718  NOTIMPLEMENTED();
719}
720
721GtkWidget* LocationBarViewGtk::CreateIconButton(
722    GtkWidget** image,
723    int image_id,
724    ViewID debug_id,
725    int tooltip_id,
726    gboolean (click_callback)(GtkWidget*, GdkEventButton*, gpointer)) {
727  *image = image_id ?
728      gtk_image_new_from_pixbuf(
729          theme_service_->GetImageNamed(image_id).ToGdkPixbuf()) :
730      gtk_image_new();
731
732  GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
733  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
734                            0, InnerPadding());
735  gtk_container_add(GTK_CONTAINER(alignment), *image);
736
737  GtkWidget* result = gtk_event_box_new();
738  gtk_event_box_set_visible_window(GTK_EVENT_BOX(result), FALSE);
739  gtk_container_add(GTK_CONTAINER(result), alignment);
740  gtk_widget_show_all(result);
741
742  if (debug_id != VIEW_ID_NONE)
743    ViewIDUtil::SetID(result, debug_id);
744
745  if (tooltip_id) {
746    gtk_widget_set_tooltip_text(result,
747                                l10n_util::GetStringUTF8(tooltip_id).c_str());
748  }
749
750  g_signal_connect(result, "button-press-event",
751                     G_CALLBACK(click_callback), this);
752
753  return result;
754}
755
756void LocationBarViewGtk::CreateZoomButton() {
757  zoom_.Own(CreateIconButton(&zoom_image_,
758                             0,
759                             VIEW_ID_ZOOM_BUTTON,
760                             0,
761                             OnZoomButtonPressThunk));
762}
763
764void LocationBarViewGtk::CreateScriptBubbleButton() {
765  script_bubble_button_.Own(CreateIconButton(&script_bubble_button_image_,
766                                             0,
767                                             VIEW_ID_SCRIPT_BUBBLE,
768                                             IDS_TOOLTIP_SCRIPT_BUBBLE,
769                                             OnScriptBubbleButtonPressThunk));
770  gtk_image_set_from_pixbuf(
771      GTK_IMAGE(script_bubble_button_image_),
772      theme_service_->GetImageNamed(
773          IDR_EXTENSIONS_SCRIPT_BUBBLE).ToGdkPixbuf());
774  g_signal_connect_after(script_bubble_button_image_, "expose-event",
775                         G_CALLBACK(&OnScriptBubbleButtonExposeThunk), this);
776}
777
778void LocationBarViewGtk::CreateStarButton() {
779  star_.Own(CreateIconButton(&star_image_,
780                             0,
781                             VIEW_ID_STAR_BUTTON,
782                             IDS_TOOLTIP_STAR,
783                             OnStarButtonPressThunk));
784  // We need to track when the star button is resized to show any bubble
785  // attached to it at this time.
786  g_signal_connect(star_image_, "size-allocate",
787                   G_CALLBACK(&OnStarButtonSizeAllocateThunk), this);
788}
789
790void LocationBarViewGtk::OnInputInProgress(bool in_progress) {
791  // This is identical to the Windows code, except that we don't proxy the call
792  // back through the Toolbar, and just access the model here.
793  // The edit should make sure we're only notified when something changes.
794  DCHECK(toolbar_model_->GetInputInProgress() != in_progress);
795
796  toolbar_model_->SetInputInProgress(in_progress);
797  Update(NULL);
798}
799
800void LocationBarViewGtk::OnKillFocus() {
801}
802
803void LocationBarViewGtk::OnSetFocus() {
804  Profile* profile = browser_->profile();
805  AccessibilityTextBoxInfo info(
806      profile,
807      l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION),
808      std::string(),
809      false);
810  content::NotificationService::current()->Notify(
811      chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_FOCUSED,
812      content::Source<Profile>(profile),
813      content::Details<AccessibilityTextBoxInfo>(&info));
814
815  // Update the keyword and search hint states.
816  OnChanged();
817}
818
819gfx::Image LocationBarViewGtk::GetFavicon() const {
820  return FaviconTabHelper::FromWebContents(GetWebContents())->GetFavicon();
821}
822
823string16 LocationBarViewGtk::GetTitle() const {
824  return GetWebContents()->GetTitle();
825}
826
827InstantController* LocationBarViewGtk::GetInstant() {
828  return browser_->instant_controller() ?
829      browser_->instant_controller()->instant() : NULL;
830}
831
832void LocationBarViewGtk::ShowFirstRunBubble() {
833  // We need the browser window to be shown before we can show the bubble, but
834  // we get called before that's happened.
835  base::MessageLoop::current()->PostTask(
836      FROM_HERE,
837      base::Bind(&LocationBarViewGtk::ShowFirstRunBubbleInternal,
838                 weak_ptr_factory_.GetWeakPtr()));
839}
840
841string16 LocationBarViewGtk::GetInputString() const {
842  return location_input_;
843}
844
845WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
846  return disposition_;
847}
848
849content::PageTransition LocationBarViewGtk::GetPageTransition() const {
850  return transition_;
851}
852
853void LocationBarViewGtk::AcceptInput() {
854  location_entry_->model()->AcceptInput(CURRENT_TAB, false);
855}
856
857void LocationBarViewGtk::FocusLocation(bool select_all) {
858  location_entry_->SetFocus();
859  if (select_all)
860    location_entry_->SelectAll(true);
861}
862
863void LocationBarViewGtk::FocusSearch() {
864  location_entry_->SetFocus();
865  location_entry_->SetForcedQuery();
866}
867
868void LocationBarViewGtk::UpdateContentSettingsIcons() {
869  bool any_visible = false;
870  for (ScopedVector<PageToolViewGtk>::iterator i(
871           content_setting_views_.begin());
872       i != content_setting_views_.end(); ++i) {
873    (*i)->Update(
874        toolbar_model_->GetInputInProgress() ? NULL : GetWebContents());
875    any_visible = (*i)->IsVisible() || any_visible;
876  }
877
878  // If there are no visible content things, hide the top level box so it
879  // doesn't mess with padding.
880  gtk_widget_set_visible(content_setting_hbox_.get(), any_visible);
881}
882
883void LocationBarViewGtk::UpdatePageActions() {
884  UpdateScriptBubbleIcon();
885
886  std::vector<ExtensionAction*> new_page_actions;
887
888  WebContents* contents = GetWebContents();
889  if (contents) {
890    LocationBarController* location_bar_controller =
891        extensions::TabHelper::FromWebContents(contents)->
892            location_bar_controller();
893    new_page_actions = location_bar_controller->GetCurrentActions();
894  }
895
896  // Initialize on the first call, or re-initialize if more extensions have been
897  // loaded or added after startup.
898  if (new_page_actions != page_actions_) {
899    page_actions_.swap(new_page_actions);
900    page_action_views_.clear();
901
902    for (size_t i = 0; i < page_actions_.size(); ++i) {
903      page_action_views_.push_back(
904          new PageActionViewGtk(this, page_actions_[i]));
905      gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()),
906                       page_action_views_[i]->widget(), FALSE, FALSE, 0);
907    }
908    content::NotificationService::current()->Notify(
909        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
910        content::Source<LocationBar>(this),
911        content::NotificationService::NoDetails());
912  }
913
914  if (!page_action_views_.empty() && contents) {
915    GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
916
917    for (size_t i = 0; i < page_action_views_.size(); i++) {
918      page_action_views_[i]->UpdateVisibility(
919          toolbar_model_->GetInputInProgress() ? NULL : contents, url);
920    }
921    gtk_widget_queue_draw(hbox_.get());
922  }
923
924  // If there are no visible page actions, hide the hbox too, so that it does
925  // not affect the padding in the location bar.
926  gtk_widget_set_visible(page_action_hbox_.get(),
927                         PageActionVisibleCount() && !ShouldOnlyShowLocation());
928}
929
930void LocationBarViewGtk::InvalidatePageActions() {
931  size_t count_before = page_action_views_.size();
932  page_action_views_.clear();
933  if (page_action_views_.size() != count_before) {
934    content::NotificationService::current()->Notify(
935        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
936        content::Source<LocationBar>(this),
937        content::NotificationService::NoDetails());
938  }
939}
940
941void LocationBarViewGtk::UpdateOpenPDFInReaderPrompt() {
942  // Not implemented on Gtk.
943}
944
945void LocationBarViewGtk::UpdateGeneratedCreditCardView() {
946  NOTIMPLEMENTED();
947}
948
949void LocationBarViewGtk::SaveStateToContents(WebContents* contents) {
950  location_entry_->SaveStateToTab(contents);
951}
952
953void LocationBarViewGtk::Revert() {
954  location_entry_->RevertAll();
955}
956
957const OmniboxView* LocationBarViewGtk::GetLocationEntry() const {
958  return location_entry_.get();
959}
960
961OmniboxView* LocationBarViewGtk::GetLocationEntry() {
962  return location_entry_.get();
963}
964
965LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
966  return this;
967}
968
969int LocationBarViewGtk::PageActionCount() {
970  return page_action_views_.size();
971}
972
973int LocationBarViewGtk::PageActionVisibleCount() {
974  int count = 0;
975  gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
976                        CountVisibleWidgets, &count);
977  return count;
978}
979
980ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) {
981  if (index >= page_action_views_.size()) {
982    NOTREACHED();
983    return NULL;
984  }
985
986  return page_action_views_[index]->page_action();
987}
988
989ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) {
990  size_t visible_index = 0;
991  for (size_t i = 0; i < page_action_views_.size(); ++i) {
992    if (page_action_views_[i]->IsVisible()) {
993      if (index == visible_index++)
994        return page_action_views_[i]->page_action();
995    }
996  }
997
998  NOTREACHED();
999  return NULL;
1000}
1001
1002void LocationBarViewGtk::TestPageActionPressed(size_t index) {
1003  if (index >= page_action_views_.size()) {
1004    NOTREACHED();
1005    return;
1006  }
1007
1008  page_action_views_[index]->TestActivatePageAction();
1009}
1010
1011bool LocationBarViewGtk::GetBookmarkStarVisibility() {
1012  return starred_;
1013}
1014
1015void LocationBarViewGtk::Observe(int type,
1016                                 const content::NotificationSource& source,
1017                                 const content::NotificationDetails& details) {
1018  switch (type) {
1019    case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: {
1020      // Only update if the updated action box was for the active tab contents.
1021      WebContents* target_tab = content::Details<WebContents>(details).ptr();
1022      if (target_tab == GetWebContents())
1023        UpdatePageActions();
1024      break;
1025    }
1026
1027    case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
1028      if (theme_service_->UsingNativeTheme()) {
1029        gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL);
1030
1031        GdkColor border_color = theme_service_->GetGdkColor(
1032            ThemeProperties::COLOR_FRAME);
1033        gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
1034
1035        gtk_util::UndoForceFontSize(security_info_label_);
1036        gtk_util::UndoForceFontSize(tab_to_search_full_label_);
1037        gtk_util::UndoForceFontSize(tab_to_search_partial_label_);
1038        gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_);
1039        gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_);
1040
1041        gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
1042                                  0, 0, 0, 0);
1043        gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
1044                                  1, 1, 1, 0);
1045        gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
1046                                  1, 1, 1, 0);
1047      } else {
1048        gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
1049                             &kKeywordBackgroundColor);
1050        gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
1051                                              kKeywordBorderColor);
1052
1053        // Until we switch to vector graphics, force the font size of labels.
1054        // 12.1px = 9pt @ 96dpi
1055        gtk_util::ForceFontSizePixels(security_info_label_, 12.1);
1056        gtk_util::ForceFontSizePixels(tab_to_search_full_label_,
1057                                      browser_defaults::kOmniboxFontPixelSize);
1058        gtk_util::ForceFontSizePixels(tab_to_search_partial_label_,
1059                                      browser_defaults::kOmniboxFontPixelSize);
1060        gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_,
1061                                      browser_defaults::kOmniboxFontPixelSize);
1062        gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_,
1063                                      browser_defaults::kOmniboxFontPixelSize);
1064
1065        const int top_bottom = popup_window_mode_ ? kPopupEdgeThickness : 0;
1066        gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
1067                                  kTopMargin + kBorderThickness,
1068                                  kBottomMargin + kBorderThickness,
1069                                  top_bottom, top_bottom);
1070        gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
1071                                  1, 1, 0, 0);
1072        gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
1073                                  1, 1, 0, 0);
1074      }
1075
1076      UpdateZoomIcon();
1077      UpdateScriptBubbleIcon();
1078      UpdateStarIcon();
1079      UpdateSiteTypeArea();
1080      UpdateContentSettingsIcons();
1081      break;
1082    }
1083
1084    default:
1085      NOTREACHED();
1086  }
1087}
1088
1089gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
1090                                          GdkEventExpose* event) {
1091  // If we're not using GTK theming, draw our own border over the edge pixels
1092  // of the background.
1093  GtkThemeService* theme_service =
1094      GtkThemeService::GetFrom(browser_->profile());
1095  if (!theme_service->UsingNativeTheme()) {
1096    // Perform a scoped paint to fill in the background color.
1097    {
1098      gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
1099
1100      GtkAllocation allocation;
1101      gtk_widget_get_allocation(widget, &allocation);
1102
1103      int thickness = popup_window_mode_ ?
1104          kPopupEdgeThickness : kNormalEdgeThickness;
1105      gfx::Rect bounds(allocation);
1106      bounds.Inset(thickness, thickness);
1107
1108      const SkColor color = SK_ColorWHITE;
1109      if (popup_window_mode_) {
1110        canvas.FillRect(bounds, color);
1111      } else {
1112        SkPaint paint;
1113        paint.setStyle(SkPaint::kFill_Style);
1114        paint.setColor(color);
1115        const int kBorderCornerRadius = 2;
1116        canvas.DrawRoundRect(bounds, kBorderCornerRadius, paint);
1117      }
1118    }
1119
1120    if (popup_window_mode_) {
1121      NineBox(IDR_OMNIBOX_POPUP_BORDER_TOP_LEFT,
1122              IDR_OMNIBOX_POPUP_BORDER_TOP,
1123              IDR_OMNIBOX_POPUP_BORDER_TOP_RIGHT,
1124              IDR_OMNIBOX_POPUP_BORDER_LEFT,
1125              IDR_OMNIBOX_POPUP_BORDER_CENTER,
1126              IDR_OMNIBOX_POPUP_BORDER_RIGHT,
1127              IDR_OMNIBOX_POPUP_BORDER_BOTTOM_LEFT,
1128              IDR_OMNIBOX_POPUP_BORDER_BOTTOM,
1129              IDR_OMNIBOX_POPUP_BORDER_BOTTOM_RIGHT).RenderToWidget(widget);
1130    } else {
1131      NineBox(IDR_OMNIBOX_BORDER_TOP_LEFT,
1132              IDR_OMNIBOX_BORDER_TOP,
1133              IDR_OMNIBOX_BORDER_TOP_RIGHT,
1134              IDR_OMNIBOX_BORDER_LEFT,
1135              IDR_OMNIBOX_BORDER_CENTER,
1136              IDR_OMNIBOX_BORDER_RIGHT,
1137              IDR_OMNIBOX_BORDER_BOTTOM_LEFT,
1138              IDR_OMNIBOX_BORDER_BOTTOM,
1139              IDR_OMNIBOX_BORDER_BOTTOM_RIGHT).RenderToWidget(widget);
1140    }
1141  }
1142
1143  // Draw ExtensionAction backgrounds and borders, if necessary.  The borders
1144  // appear exactly between the elements, so they can't draw the borders
1145  // themselves.
1146  gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false);
1147  for (ScopedVector<PageActionViewGtk>::const_iterator
1148           page_action_view = page_action_views_.begin();
1149       page_action_view != page_action_views_.end();
1150       ++page_action_view) {
1151    if ((*page_action_view)->IsVisible()) {
1152      // Figure out where the page action is drawn so we can draw
1153      // borders to its left and right.
1154      GtkAllocation allocation;
1155      gtk_widget_get_allocation((*page_action_view)->widget(), &allocation);
1156      ExtensionAction* action = (*page_action_view)->page_action();
1157      gfx::Rect bounds(allocation);
1158      // Make the bounding rectangle include the whole vertical range of the
1159      // location bar, and the mid-point pixels between adjacent page actions.
1160      //
1161      // For odd InnerPadding()s, "InnerPadding() + 1" includes the mid-point
1162      // between two page actions in the bounding rectangle.  For even paddings,
1163      // the +1 is dropped, which is right since there is no pixel at the
1164      // mid-point.
1165      bounds.Inset(-(InnerPadding() + 1) / 2,
1166                   theme_service_->UsingNativeTheme() ? -1 : 0);
1167      location_bar_util::PaintExtensionActionBackground(
1168          *action, SessionID::IdForTab(GetWebContents()),
1169          &canvas, bounds,
1170          theme_service_->get_location_bar_text_color(),
1171          theme_service_->get_location_bar_bg_color());
1172    }
1173  }
1174  // Destroying |canvas| draws the background.
1175
1176  return FALSE;  // Continue propagating the expose.
1177}
1178
1179void LocationBarViewGtk::UpdateSiteTypeArea() {
1180  // The icon is always visible except when the |tab_to_search_alignment_| is
1181  // visible.
1182  if (!location_entry_->model()->keyword().empty() &&
1183      !location_entry_->model()->is_keyword_hint()) {
1184    gtk_widget_hide(site_type_area());
1185    return;
1186  }
1187
1188  int resource_id = location_entry_->GetIcon();
1189  gtk_image_set_from_pixbuf(
1190      GTK_IMAGE(location_icon_image_),
1191      theme_service_->GetImageNamed(resource_id).ToGdkPixbuf());
1192
1193  if (toolbar_model_->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) {
1194    if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1195      // Fun fact: If wee try to make |site_type_event_box_| act as a
1196      // rounded window while it doesn't have a visible window, GTK interprets
1197      // this as a sign that it should paint the skyline texture into the
1198      // omnibox.
1199      gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1200                                       TRUE);
1201
1202      gtk_util::ActAsRoundedWindow(site_type_event_box_,
1203                                   kEvSecureBorderColor,
1204                                   kCornerSize,
1205                                   gtk_util::ROUNDED_ALL,
1206                                   gtk_util::BORDER_ALL);
1207    }
1208
1209    string16 info_text = toolbar_model_->GetEVCertName();
1210    gtk_label_set_text(GTK_LABEL(security_info_label_),
1211                       UTF16ToUTF8(info_text).c_str());
1212
1213    UpdateEVCertificateLabelSize();
1214
1215    gtk_widget_show(GTK_WIDGET(security_info_label_));
1216  } else {
1217    if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
1218      gtk_util::StopActingAsRoundedWindow(site_type_event_box_);
1219
1220      gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
1221                                       FALSE);
1222    }
1223
1224    gtk_widget_hide(GTK_WIDGET(security_info_label_));
1225  }
1226
1227  if (GetLocationEntry()->IsEditingOrEmpty()) {
1228    // Do not show the tooltip if the user has been editing the location
1229    // bar, or the location bar is at the NTP.
1230    gtk_widget_set_tooltip_text(location_icon_image_, "");
1231  } else {
1232    gtk_widget_set_tooltip_text(location_icon_image_,
1233        l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
1234  }
1235
1236  gtk_widget_show(site_type_area());
1237
1238  SetSiteTypeDragSource();
1239}
1240
1241void LocationBarViewGtk::UpdateEVCertificateLabelSize() {
1242  // Figure out the width of the average character.
1243  PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_));
1244  PangoContext* context = pango_layout_get_context(layout);
1245  PangoFontMetrics* metrics = pango_context_get_metrics(
1246      context,
1247      gtk_widget_get_style(security_info_label_)->font_desc,
1248      pango_context_get_language(context));
1249  int char_width =
1250      pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE;
1251
1252  // The EV label should never take up more than half the hbox. We try to
1253  // correct our inaccurate measurement units ("the average character width")
1254  // by dividing more than an even 2.
1255  GtkAllocation security_label_allocation;
1256  gtk_widget_get_allocation(security_info_label_, &security_label_allocation);
1257  GtkAllocation entry_box_allocation;
1258  gtk_widget_get_allocation(entry_box_, &entry_box_allocation);
1259  int text_area = security_label_allocation.width +
1260                  entry_box_allocation.width;
1261  int max_chars = static_cast<int>(static_cast<float>(text_area) /
1262                                   static_cast<float>(char_width) / 2.75);
1263  // Don't let the label be smaller than 10 characters so that the country
1264  // code is always visible.
1265  gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_),
1266                                std::max(10, max_chars));
1267
1268  pango_font_metrics_unref(metrics);
1269}
1270
1271void LocationBarViewGtk::SetKeywordLabel(const string16& keyword) {
1272  if (keyword.empty())
1273    return;
1274
1275  Profile* profile = browser_->profile();
1276  TemplateURLService* template_url_service =
1277      TemplateURLServiceFactory::GetForProfile(profile);
1278  if (!template_url_service)
1279    return;
1280
1281  bool is_extension_keyword;
1282  const string16 short_name = template_url_service->GetKeywordShortName(
1283      keyword, &is_extension_keyword);
1284  const string16 min_string = location_bar_util::CalculateMinString(short_name);
1285  const string16 full_name = is_extension_keyword ?
1286      short_name :
1287      l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, short_name);
1288  const string16 partial_name = is_extension_keyword ?
1289      min_string :
1290      l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, min_string);
1291  gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_),
1292                     UTF16ToUTF8(full_name).c_str());
1293  gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_),
1294                     UTF16ToUTF8(partial_name).c_str());
1295
1296  if (last_keyword_ != keyword) {
1297    last_keyword_ = keyword;
1298
1299    if (is_extension_keyword) {
1300      const TemplateURL* template_url =
1301          template_url_service->GetTemplateURLForKeyword(keyword);
1302      gfx::Image image = extensions::OmniboxAPI::Get(profile)->
1303          GetOmniboxIcon(template_url->GetExtensionId());
1304      gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1305                                image.ToGdkPixbuf());
1306    } else {
1307      ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1308      gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
1309          rb.GetNativeImageNamed(IDR_OMNIBOX_SEARCH).ToGdkPixbuf());
1310    }
1311  }
1312}
1313
1314void LocationBarViewGtk::SetKeywordHintLabel(const string16& keyword) {
1315  if (keyword.empty())
1316    return;
1317
1318  TemplateURLService* template_url_service =
1319      TemplateURLServiceFactory::GetForProfile(browser_->profile());
1320  if (!template_url_service)
1321    return;
1322
1323  bool is_extension_keyword;
1324  const string16 short_name = template_url_service->
1325      GetKeywordShortName(keyword, &is_extension_keyword);
1326  int message_id = is_extension_keyword ?
1327      IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT;
1328  std::vector<size_t> content_param_offsets;
1329  const string16 keyword_hint = l10n_util::GetStringFUTF16(
1330      message_id,
1331      string16(),
1332      short_name,
1333      &content_param_offsets);
1334  if (content_param_offsets.size() != 2) {
1335    // See comments on an identical NOTREACHED() in search_provider.cc.
1336    NOTREACHED();
1337    return;
1338  }
1339
1340  std::string leading(UTF16ToUTF8(
1341      keyword_hint.substr(0, content_param_offsets.front())));
1342  std::string trailing(UTF16ToUTF8(
1343      keyword_hint.substr(content_param_offsets.front())));
1344  gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_),
1345                     leading.c_str());
1346  gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_),
1347                     trailing.c_str());
1348}
1349
1350void LocationBarViewGtk::ShowFirstRunBubbleInternal() {
1351  if (!location_entry_.get() || !gtk_widget_get_window(widget()))
1352    return;
1353
1354  gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_);
1355  bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing);
1356  FirstRunBubble::Show(browser_, location_icon_image_, bounds);
1357}
1358
1359gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender,
1360                                            GdkEventButton* event) {
1361  WebContents* tab = GetWebContents();
1362
1363  if (event->button == 1) {
1364    // Do not show page info if the user has been editing the location
1365    // bar, or the location bar is at the NTP.
1366    if (GetLocationEntry()->IsEditingOrEmpty())
1367      return FALSE;
1368
1369    // (0,0) event coordinates indicates that the release came at the end of
1370    // a drag.
1371    if (event->x == 0 && event->y == 0)
1372      return FALSE;
1373
1374    // Important to use GetVisibleEntry to match what's showing in the omnibox.
1375    NavigationEntry* nav_entry = tab->GetController().GetVisibleEntry();
1376    if (!nav_entry) {
1377      NOTREACHED();
1378      return FALSE;
1379    }
1380    chrome::ShowWebsiteSettings(browser_, tab, nav_entry->GetURL(),
1381                                nav_entry->GetSSL());
1382    return TRUE;
1383  } else if (event->button == 2) {
1384    // When the user middle clicks on the location icon, try to open the
1385    // contents of the PRIMARY selection in the current tab.
1386    // If the click was outside our bounds, do nothing.
1387    if (!gtk_util::WidgetBounds(sender).Contains(
1388            gfx::Point(event->x, event->y))) {
1389      return FALSE;
1390    }
1391
1392    GURL url;
1393    if (!gtk_util::URLFromPrimarySelection(browser_->profile(), &url))
1394      return FALSE;
1395
1396    tab->OpenURL(OpenURLParams(
1397        url, content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1398        false));
1399    return TRUE;
1400  }
1401
1402  return FALSE;
1403}
1404
1405void LocationBarViewGtk::OnIconDragData(GtkWidget* sender,
1406                                        GdkDragContext* context,
1407                                        GtkSelectionData* data,
1408                                        guint info, guint time) {
1409  ui::WriteURLWithName(data, drag_url_, drag_title_, info);
1410}
1411
1412void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender,
1413                                         GdkDragContext* context) {
1414  gfx::Image favicon = GetFavicon();
1415  if (favicon.IsEmpty())
1416    return;
1417  drag_icon_ = bookmark_utils::GetDragRepresentation(favicon.ToGdkPixbuf(),
1418      GetTitle(), theme_service_);
1419  gtk_drag_set_icon_widget(context, drag_icon_, 0, 0);
1420
1421  WebContents* tab = GetWebContents();
1422  if (!tab)
1423    return;
1424  drag_url_ = tab->GetURL();
1425  drag_title_ = tab->GetTitle();
1426}
1427
1428void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
1429                                       GdkDragContext* context) {
1430  DCHECK(drag_icon_);
1431  gtk_widget_destroy(drag_icon_);
1432  drag_icon_ = NULL;
1433  drag_url_ = GURL::EmptyGURL();
1434  drag_title_.clear();
1435}
1436
1437void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
1438                                            GtkAllocation* allocation) {
1439  if (hbox_width_ != allocation->width) {
1440    hbox_width_ = allocation->width;
1441    UpdateEVCertificateLabelSize();
1442  }
1443  if (browser_ && browser_->instant_controller()) {
1444    browser_->instant_controller()->
1445        SetOmniboxBounds(AllocationToRect(*allocation));
1446  }
1447}
1448
1449void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender,
1450                                                GtkAllocation* allocation) {
1451  if (entry_box_width_ != allocation->width) {
1452    entry_box_width_ = allocation->width;
1453    AdjustChildrenVisibility();
1454  }
1455}
1456
1457gboolean LocationBarViewGtk::OnZoomButtonPress(GtkWidget* widget,
1458                                               GdkEventButton* event) {
1459  if (event->button == 1 && GetWebContents()) {
1460    // If the zoom icon is clicked, show the zoom bubble and keep it open until
1461    // it loses focus.
1462    ZoomBubbleGtk::ShowBubble(GetWebContents(), false);
1463    return TRUE;
1464  }
1465  return FALSE;
1466}
1467
1468gboolean LocationBarViewGtk::OnScriptBubbleButtonPress(GtkWidget* widget,
1469                                                       GdkEventButton* event) {
1470  if (event->button == 1 && GetWebContents()) {
1471    ScriptBubbleGtk::Show(script_bubble_button_image_, GetWebContents());
1472    return TRUE;
1473  }
1474  return FALSE;
1475}
1476
1477gboolean LocationBarViewGtk::OnScriptBubbleButtonExpose(GtkWidget* widget,
1478                                                        GdkEventExpose* event) {
1479  gfx::CanvasSkiaPaint canvas(event, false);
1480  GtkAllocation allocation;
1481  gtk_widget_get_allocation(widget, &allocation);
1482  badge_util::PaintBadge(&canvas,
1483                         gfx::Rect(allocation),
1484                         base::UintToString(num_running_scripts_),
1485                         SK_ColorWHITE,
1486                         SkColorSetRGB(0, 170, 0),
1487                         allocation.width,
1488                         extensions::ActionInfo::TYPE_PAGE);
1489  return FALSE;
1490}
1491
1492void LocationBarViewGtk::OnStarButtonSizeAllocate(GtkWidget* sender,
1493                                                  GtkAllocation* allocation) {
1494  if (!on_star_sized_.is_null()) {
1495    on_star_sized_.Run();
1496    on_star_sized_.Reset();
1497  }
1498  star_sized_ = true;
1499}
1500
1501gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
1502                                               GdkEventButton* event) {
1503  if (event->button == 1) {
1504    chrome::ExecuteCommand(browser_, IDC_BOOKMARK_PAGE);
1505    return TRUE;
1506  }
1507  return FALSE;
1508}
1509
1510void LocationBarViewGtk::ShowZoomBubble() {
1511  if (toolbar_model_->GetInputInProgress() || !GetWebContents())
1512    return;
1513
1514  ZoomBubbleGtk::ShowBubble(GetWebContents(), true);
1515}
1516
1517void LocationBarViewGtk::ShowStarBubble(const GURL& url,
1518                                        bool newly_bookmarked) {
1519  if (!star_.get())
1520    return;
1521
1522  if (star_sized_) {
1523    BookmarkBubbleGtk::Show(star_.get(), browser_->profile(), url,
1524                            newly_bookmarked);
1525  } else {
1526    on_star_sized_ = base::Bind(&BookmarkBubbleGtk::Show,
1527                                star_.get(), browser_->profile(),
1528                                url, newly_bookmarked);
1529  }
1530}
1531
1532void LocationBarViewGtk::SetStarred(bool starred) {
1533  if (starred == starred_)
1534    return;
1535
1536  starred_ = starred;
1537  UpdateStarIcon();
1538}
1539
1540void LocationBarViewGtk::ZoomChangedForActiveTab(bool can_show_bubble) {
1541  UpdateZoomIcon();
1542
1543  if (can_show_bubble && gtk_widget_get_visible(zoom_.get()))
1544    ShowZoomBubble();
1545}
1546
1547void LocationBarViewGtk::UpdateZoomIcon() {
1548  WebContents* web_contents = GetWebContents();
1549  if (!zoom_.get() || !web_contents)
1550    return;
1551
1552  ZoomController* zoom_controller =
1553      ZoomController::FromWebContents(web_contents);
1554  if (!zoom_controller || zoom_controller->IsAtDefaultZoom() ||
1555      toolbar_model_->GetInputInProgress()) {
1556    gtk_widget_hide(zoom_.get());
1557    ZoomBubbleGtk::CloseBubble();
1558    return;
1559  }
1560
1561  const int zoom_resource = zoom_controller->GetResourceForZoomLevel();
1562  gtk_image_set_from_pixbuf(GTK_IMAGE(zoom_image_),
1563      theme_service_->GetImageNamed(zoom_resource).ToGdkPixbuf());
1564
1565  string16 tooltip = l10n_util::GetStringFUTF16Int(
1566      IDS_TOOLTIP_ZOOM, zoom_controller->zoom_percent());
1567  gtk_widget_set_tooltip_text(zoom_.get(), UTF16ToUTF8(tooltip).c_str());
1568
1569  gtk_widget_show(zoom_.get());
1570}
1571
1572void LocationBarViewGtk::UpdateScriptBubbleIcon() {
1573  num_running_scripts_ = 0;
1574  if (GetWebContents()) {
1575    extensions::TabHelper* tab_helper =
1576        extensions::TabHelper::FromWebContents(GetWebContents());
1577    if (tab_helper && tab_helper->script_bubble_controller()) {
1578      num_running_scripts_ = tab_helper->script_bubble_controller()->
1579          extensions_running_scripts().size();
1580    }
1581  }
1582
1583  if (num_running_scripts_ == 0u)
1584    gtk_widget_hide(script_bubble_button_.get());
1585  else
1586    gtk_widget_show(script_bubble_button_.get());
1587}
1588
1589void LocationBarViewGtk::UpdateStarIcon() {
1590  if (!star_.get())
1591    return;
1592  // Indicate the star icon is not correctly sized. It will be marked as sized
1593  // when the next size-allocate signal is received by the star widget.
1594  star_sized_ = false;
1595  bool star_enabled = !toolbar_model_->GetInputInProgress() &&
1596                      edit_bookmarks_enabled_.GetValue();
1597  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
1598  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE_FROM_STAR,
1599                                         star_enabled);
1600  if (star_enabled) {
1601    gtk_widget_show_all(star_.get());
1602    int id = starred_ ? IDR_STAR_LIT : IDR_STAR;
1603    gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_),
1604                              theme_service_->GetImageNamed(id).ToGdkPixbuf());
1605    gtk_widget_set_tooltip_text(star_.get(), l10n_util::GetStringUTF8(
1606          starred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR).c_str());
1607  } else {
1608    gtk_widget_hide_all(star_.get());
1609  }
1610}
1611
1612bool LocationBarViewGtk::ShouldOnlyShowLocation() {
1613  return !browser_->is_type_tabbed();
1614}
1615
1616void LocationBarViewGtk::AdjustChildrenVisibility() {
1617  int text_width = location_entry_->TextWidth();
1618  int available_width = entry_box_width_ - text_width - InnerPadding();
1619
1620  // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be
1621  // visible at the same time.
1622  if (!show_selected_keyword_ &&
1623      gtk_widget_get_visible(tab_to_search_alignment_)) {
1624    gtk_widget_hide(tab_to_search_alignment_);
1625  } else if (!show_keyword_hint_ &&
1626             gtk_widget_get_visible(tab_to_search_hint_)) {
1627    gtk_widget_hide(tab_to_search_hint_);
1628  }
1629
1630  if (show_selected_keyword_) {
1631    GtkRequisition box, full_label, partial_label;
1632    gtk_widget_size_request(tab_to_search_box_, &box);
1633    gtk_widget_size_request(tab_to_search_full_label_, &full_label);
1634    gtk_widget_size_request(tab_to_search_partial_label_, &partial_label);
1635    int full_partial_width_diff = full_label.width - partial_label.width;
1636    int full_box_width;
1637    int partial_box_width;
1638    if (gtk_widget_get_visible(tab_to_search_full_label_)) {
1639      full_box_width = box.width;
1640      partial_box_width = full_box_width - full_partial_width_diff;
1641    } else {
1642      partial_box_width = box.width;
1643      full_box_width = partial_box_width + full_partial_width_diff;
1644    }
1645
1646    if (partial_box_width >= entry_box_width_ - InnerPadding()) {
1647      gtk_widget_hide(tab_to_search_alignment_);
1648    } else if (full_box_width >= available_width) {
1649      gtk_widget_hide(tab_to_search_full_label_);
1650      gtk_widget_show(tab_to_search_partial_label_);
1651      gtk_widget_show(tab_to_search_alignment_);
1652    } else if (full_box_width < available_width) {
1653      gtk_widget_hide(tab_to_search_partial_label_);
1654      gtk_widget_show(tab_to_search_full_label_);
1655      gtk_widget_show(tab_to_search_alignment_);
1656    }
1657  } else if (show_keyword_hint_) {
1658    GtkRequisition leading, icon, trailing;
1659    gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading);
1660    gtk_widget_size_request(tab_to_search_hint_icon_, &icon);
1661    gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing);
1662    int full_width = leading.width + icon.width + trailing.width;
1663
1664    if (icon.width >= entry_box_width_ - InnerPadding()) {
1665      gtk_widget_hide(tab_to_search_hint_);
1666    } else if (full_width >= available_width) {
1667      gtk_widget_hide(tab_to_search_hint_leading_label_);
1668      gtk_widget_hide(tab_to_search_hint_trailing_label_);
1669      gtk_widget_show(tab_to_search_hint_);
1670    } else if (full_width < available_width) {
1671      gtk_widget_show(tab_to_search_hint_leading_label_);
1672      gtk_widget_show(tab_to_search_hint_trailing_label_);
1673      gtk_widget_show(tab_to_search_hint_);
1674    }
1675  }
1676}
1677
1678////////////////////////////////////////////////////////////////////////////////
1679// LocationBarViewGtk::PageToolViewGtk
1680
1681LocationBarViewGtk::PageToolViewGtk::PageToolViewGtk(
1682    const LocationBarViewGtk* parent)
1683    : alignment_(gtk_alignment_new(0, 0, 1, 1)),
1684      event_box_(gtk_event_box_new()),
1685      hbox_(gtk_hbox_new(FALSE, InnerPadding())),
1686      image_(gtk_image_new()),
1687      label_(gtk_label_new(NULL)),
1688      parent_(parent),
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());
1693
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);
1700
1701  gtk_widget_set_no_show_all(label_.get(), TRUE);
1702  gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE);
1703
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);
1706
1707  gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder);
1708
1709  gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_);
1710  gtk_widget_hide(widget());
1711
1712  animation_.SetSlideDuration(kPageToolAnimationTime);
1713}
1714
1715LocationBarViewGtk::PageToolViewGtk::~PageToolViewGtk() {
1716  image_.Destroy();
1717  label_.Destroy();
1718  event_box_.Destroy();
1719  alignment_.Destroy();
1720}
1721
1722GtkWidget* LocationBarViewGtk::PageToolViewGtk::widget() {
1723  return alignment_.get();
1724}
1725
1726bool LocationBarViewGtk::PageToolViewGtk::IsVisible() {
1727  return gtk_widget_get_visible(widget());
1728}
1729
1730void LocationBarViewGtk::PageToolViewGtk::StartAnimating() {
1731  if (animation_.IsShowing() || animation_.IsClosing())
1732    return;
1733
1734  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE);
1735  GdkColor border_color = button_border_color();
1736  gtk_util::ActAsRoundedWindow(event_box_.get(), border_color,
1737                               kCornerSize,
1738                               gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
1739
1740  gtk_widget_set_size_request(label_.get(), -1, -1);
1741  gtk_widget_size_request(label_.get(), &label_req_);
1742  gtk_widget_set_size_request(label_.get(), 0, -1);
1743  gtk_widget_show(label_.get());
1744
1745  animation_.Show();
1746}
1747
1748void LocationBarViewGtk::PageToolViewGtk::CloseAnimation() {
1749  animation_.Hide();
1750}
1751
1752void LocationBarViewGtk::PageToolViewGtk::AnimationProgressed(
1753    const ui::Animation* animation) {
1754  gtk_widget_set_size_request(
1755      label_.get(),
1756      animation->GetCurrentValue() * label_req_.width,
1757      -1);
1758}
1759
1760void LocationBarViewGtk::PageToolViewGtk::AnimationEnded(
1761    const ui::Animation* animation) {
1762}
1763
1764void LocationBarViewGtk::PageToolViewGtk::AnimationCanceled(
1765    const ui::Animation* animation) {
1766}
1767
1768gboolean LocationBarViewGtk::PageToolViewGtk::OnButtonPressed(
1769    GtkWidget* sender, GdkEvent* event) {
1770  OnClick(sender);
1771  return TRUE;
1772}
1773
1774gboolean LocationBarViewGtk::PageToolViewGtk::OnExpose(
1775    GtkWidget* sender, GdkEventExpose* event) {
1776  TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageToolViewGtk::OnExpose");
1777
1778  if (!(animation_.IsShowing() || animation_.IsClosing()))
1779    return FALSE;
1780
1781  GtkAllocation allocation;
1782  gtk_widget_get_allocation(sender, &allocation);
1783  const int height = allocation.height;
1784
1785  cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
1786  gdk_cairo_rectangle(cr, &event->area);
1787  cairo_clip(cr);
1788
1789  cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
1790
1791  const GdkColor top_color = gradient_top_color();
1792  const GdkColor bottom_color = gradient_bottom_color();
1793  cairo_pattern_add_color_stop_rgb(
1794      pattern, 0.0,
1795      top_color.red/255.0,
1796      top_color.blue/255.0,
1797      top_color.green/255.0);
1798  cairo_pattern_add_color_stop_rgb(
1799      pattern, 1.0,
1800      bottom_color.red/255.0,
1801      bottom_color.blue/255.0,
1802      bottom_color.green/255.0);
1803
1804  cairo_set_source(cr, pattern);
1805  cairo_paint(cr);
1806  cairo_pattern_destroy(pattern);
1807  cairo_destroy(cr);
1808
1809  return FALSE;
1810}
1811
1812////////////////////////////////////////////////////////////////////////////////
1813// LocationBarViewGtk::PageActionViewGtk
1814
1815LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
1816    LocationBarViewGtk* owner,
1817    ExtensionAction* page_action)
1818    : owner_(NULL),
1819      page_action_(page_action),
1820      current_tab_id_(-1),
1821      window_(NULL),
1822      accel_group_(NULL),
1823      preview_enabled_(false),
1824      scoped_icon_animation_observer_(
1825          page_action->GetIconAnimation(
1826              SessionID::IdForTab(owner->GetWebContents())),
1827          this) {
1828  event_box_.Own(gtk_event_box_new());
1829  gtk_widget_set_size_request(event_box_.get(),
1830                              extensions::IconsInfo::kPageActionIconMaxSize,
1831                              extensions::IconsInfo::kPageActionIconMaxSize);
1832
1833  // Make the event box not visible so it does not paint a background.
1834  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1835  g_signal_connect(event_box_.get(), "button-press-event",
1836                   G_CALLBACK(&OnButtonPressedThunk), this);
1837  g_signal_connect_after(event_box_.get(), "expose-event",
1838                         G_CALLBACK(OnExposeEventThunk), this);
1839  g_signal_connect(event_box_.get(), "realize",
1840                   G_CALLBACK(OnRealizeThunk), this);
1841
1842  image_.Own(gtk_image_new());
1843  gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get());
1844
1845  const Extension* extension = owner->browser()->profile()->
1846      GetExtensionService()->GetExtensionById(page_action->extension_id(),
1847                                              false);
1848  DCHECK(extension);
1849
1850  icon_factory_.reset(
1851      new ExtensionActionIconFactory(
1852          owner->browser()->profile(), extension, page_action, this));
1853
1854  // We set the owner last of all so that we can determine whether we are in
1855  // the process of initializing this class or not.
1856  owner_ = owner;
1857}
1858
1859LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
1860  DisconnectPageActionAccelerator();
1861
1862  image_.Destroy();
1863  event_box_.Destroy();
1864}
1865
1866bool LocationBarViewGtk::PageActionViewGtk::IsVisible() {
1867  return gtk_widget_get_visible(widget());
1868}
1869
1870void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
1871    WebContents* contents, const GURL& url) {
1872  // Save this off so we can pass it back to the extension when the action gets
1873  // executed. See PageActionImageView::OnMousePressed.
1874  current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1;
1875  current_url_ = url;
1876
1877  bool visible = contents &&
1878      (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_));
1879  if (visible) {
1880    // Set the tooltip.
1881    gtk_widget_set_tooltip_text(event_box_.get(),
1882        page_action_->GetTitle(current_tab_id_).c_str());
1883
1884    // Set the image.
1885    gfx::Image icon = icon_factory_->GetIcon(current_tab_id_);
1886    if (!icon.IsEmpty()) {
1887      GdkPixbuf* pixbuf = icon.ToGdkPixbuf();
1888      DCHECK(pixbuf);
1889      gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
1890    }
1891  }
1892
1893  bool old_visible = IsVisible();
1894  if (visible)
1895    gtk_widget_show_all(event_box_.get());
1896  else
1897    gtk_widget_hide_all(event_box_.get());
1898
1899  if (visible != old_visible) {
1900    content::NotificationService::current()->Notify(
1901        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
1902        content::Source<ExtensionAction>(page_action_),
1903        content::Details<WebContents>(contents));
1904  }
1905}
1906
1907void LocationBarViewGtk::PageActionViewGtk::OnIconUpdated() {
1908  // If we have no owner, that means this class is still being constructed.
1909  WebContents* web_contents = owner_ ? owner_->GetWebContents() : NULL;
1910  if (web_contents)
1911    UpdateVisibility(web_contents, current_url_);
1912}
1913
1914void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
1915  GdkEventButton event = {};
1916  event.type = GDK_BUTTON_PRESS;
1917  event.button = 1;
1918  OnButtonPressed(widget(), &event);
1919}
1920
1921void LocationBarViewGtk::PageActionViewGtk::Observe(
1922    int type,
1923    const content::NotificationSource& source,
1924    const content::NotificationDetails& details) {
1925  DCHECK_EQ(type, chrome::NOTIFICATION_WINDOW_CLOSED);
1926  DisconnectPageActionAccelerator();
1927}
1928
1929void LocationBarViewGtk::PageActionViewGtk::ConnectPageActionAccelerator() {
1930  const ExtensionSet* extensions = owner_->browser()->profile()->
1931      GetExtensionService()->extensions();
1932  const Extension* extension =
1933      extensions->GetByID(page_action_->extension_id());
1934  window_ = owner_->browser()->window()->GetNativeWindow();
1935
1936  extensions::CommandService* command_service =
1937      extensions::CommandService::Get(owner_->browser()->profile());
1938
1939  extensions::Command command_page_action;
1940  if (command_service->GetPageActionCommand(
1941          extension->id(),
1942          extensions::CommandService::ACTIVE_ONLY,
1943          &command_page_action,
1944          NULL)) {
1945    // Found the page action shortcut command, register it.
1946    page_action_keybinding_.reset(
1947        new ui::Accelerator(command_page_action.accelerator()));
1948  }
1949
1950  extensions::Command command_script_badge;
1951  if (command_service->GetScriptBadgeCommand(
1952          extension->id(),
1953          extensions::CommandService::ACTIVE_ONLY,
1954          &command_script_badge,
1955          NULL)) {
1956    // Found the script badge shortcut command, register it.
1957    script_badge_keybinding_.reset(
1958        new ui::Accelerator(command_script_badge.accelerator()));
1959  }
1960
1961  if (page_action_keybinding_.get() || script_badge_keybinding_.get()) {
1962    accel_group_ = gtk_accel_group_new();
1963    gtk_window_add_accel_group(window_, accel_group_);
1964
1965    if (page_action_keybinding_.get()) {
1966      gtk_accel_group_connect(
1967          accel_group_,
1968          ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
1969          ui::GetGdkModifierForAccelerator(*page_action_keybinding_),
1970          GtkAccelFlags(0),
1971          g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
1972    }
1973    if (script_badge_keybinding_.get()) {
1974      gtk_accel_group_connect(
1975          accel_group_,
1976          ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
1977          ui::GetGdkModifierForAccelerator(*script_badge_keybinding_),
1978          GtkAccelFlags(0),
1979          g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL));
1980    }
1981
1982    // Since we've added an accelerator, we'll need to unregister it before
1983    // the window is closed, so we listen for the window being closed.
1984    registrar_.Add(this,
1985                   chrome::NOTIFICATION_WINDOW_CLOSED,
1986                   content::Source<GtkWindow>(window_));
1987  }
1988}
1989
1990void LocationBarViewGtk::PageActionViewGtk::OnIconChanged() {
1991  UpdateVisibility(owner_->GetWebContents(), current_url_);
1992}
1993
1994void LocationBarViewGtk::PageActionViewGtk::DisconnectPageActionAccelerator() {
1995  if (accel_group_) {
1996    if (page_action_keybinding_.get()) {
1997      gtk_accel_group_disconnect_key(
1998          accel_group_,
1999          ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_),
2000          ui::GetGdkModifierForAccelerator(*page_action_keybinding_));
2001    }
2002    if (script_badge_keybinding_.get()) {
2003      gtk_accel_group_disconnect_key(
2004          accel_group_,
2005          ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_),
2006          ui::GetGdkModifierForAccelerator(*script_badge_keybinding_));
2007    }
2008    gtk_window_remove_accel_group(window_, accel_group_);
2009    g_object_unref(accel_group_);
2010    accel_group_ = NULL;
2011    page_action_keybinding_.reset(NULL);
2012    script_badge_keybinding_.reset(NULL);
2013  }
2014}
2015
2016gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed(
2017    GtkWidget* sender,
2018    GdkEventButton* event) {
2019  // Double and triple-clicks generate both a GDK_BUTTON_PRESS and a
2020  // GDK_[23]BUTTON_PRESS event. We don't want to double-trigger by acting on
2021  // both.
2022  if (event->type != GDK_BUTTON_PRESS)
2023    return TRUE;
2024
2025  WebContents* web_contents = owner_->GetWebContents();
2026  if (!web_contents)
2027    return TRUE;
2028
2029  ExtensionService* extension_service =
2030      owner_->browser()->profile()->GetExtensionService();
2031  if (!extension_service)
2032    return TRUE;
2033
2034  const Extension* extension =
2035      extension_service->extensions()->GetByID(page_action()->extension_id());
2036  if (!extension)
2037    return TRUE;
2038
2039  LocationBarController* controller =
2040      extensions::TabHelper::FromWebContents(web_contents)->
2041          location_bar_controller();
2042
2043  switch (controller->OnClicked(extension->id(), event->button)) {
2044    case LocationBarController::ACTION_NONE:
2045      break;
2046
2047    case LocationBarController::ACTION_SHOW_POPUP:
2048      ExtensionPopupGtk::Show(
2049          page_action_->GetPopupUrl(current_tab_id_),
2050          owner_->browser_,
2051          event_box_.get(),
2052          ExtensionPopupGtk::SHOW);
2053      break;
2054
2055    case LocationBarController::ACTION_SHOW_CONTEXT_MENU:
2056      context_menu_model_ =
2057          new ExtensionContextMenuModel(extension, owner_->browser_, this);
2058      context_menu_.reset(
2059          new MenuGtk(NULL, context_menu_model_.get()));
2060      context_menu_->PopupForWidget(sender, event->button, event->time);
2061      break;
2062
2063    case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
2064      ExtensionPopupGtk::Show(
2065          extensions::ExtensionInfoUI::GetURL(extension->id()),
2066          owner_->browser_,
2067          event_box_.get(),
2068          ExtensionPopupGtk::SHOW);
2069      break;
2070  }
2071
2072  return TRUE;
2073}
2074
2075gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
2076    GtkWidget* widget,
2077    GdkEventExpose* event) {
2078  TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageActionViewGtk::OnExpose");
2079  WebContents* contents = owner_->GetWebContents();
2080  if (!contents)
2081    return FALSE;
2082
2083  int tab_id = ExtensionTabUtil::GetTabId(contents);
2084  if (tab_id < 0)
2085    return FALSE;
2086
2087  std::string badge_text = page_action_->GetBadgeText(tab_id);
2088  if (badge_text.empty())
2089    return FALSE;
2090
2091  gfx::CanvasSkiaPaint canvas(event, false);
2092  GtkAllocation allocation;
2093  gtk_widget_get_allocation(widget, &allocation);
2094  page_action_->PaintBadge(&canvas, gfx::Rect(allocation), tab_id);
2095  return FALSE;
2096}
2097
2098void LocationBarViewGtk::PageActionViewGtk::OnRealize(GtkWidget* widget) {
2099  ConnectPageActionAccelerator();
2100}
2101
2102// static
2103gboolean LocationBarViewGtk::PageActionViewGtk::OnGtkAccelerator(
2104    GtkAccelGroup* accel_group,
2105    GObject* acceleratable,
2106    guint keyval,
2107    GdkModifierType modifier,
2108    void* user_data) {
2109  PageActionViewGtk* view = static_cast<PageActionViewGtk*>(user_data);
2110  if (!gtk_widget_get_visible(view->widget()))
2111    return FALSE;
2112
2113  GdkEventButton event = {};
2114  event.type = GDK_BUTTON_PRESS;
2115  event.button = 1;
2116  return view->OnButtonPressed(view->widget(), &event);
2117}
2118
2119void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
2120    ExtensionAction* action) {
2121  ExtensionPopupGtk::Show(
2122      action->GetPopupUrl(current_tab_id_),
2123      owner_->browser_,
2124      event_box_.get(),
2125      ExtensionPopupGtk::SHOW_AND_INSPECT);
2126}
2127