1// Copyright (c) 2011 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/command_line.h"
13#include "base/i18n/rtl.h"
14#include "base/logging.h"
15#include "base/string_util.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/app/chrome_command_ids.h"
18#include "chrome/browser/accessibility_events.h"
19#include "chrome/browser/alternate_nav_url_fetcher.h"
20#include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h"
21#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
22#include "chrome/browser/command_updater.h"
23#include "chrome/browser/content_setting_bubble_model.h"
24#include "chrome/browser/content_setting_image_model.h"
25#include "chrome/browser/defaults.h"
26#include "chrome/browser/extensions/extension_browser_event_router.h"
27#include "chrome/browser/extensions/extension_service.h"
28#include "chrome/browser/extensions/extension_tabs_module.h"
29#include "chrome/browser/instant/instant_controller.h"
30#include "chrome/browser/profiles/profile.h"
31#include "chrome/browser/search_engines/template_url.h"
32#include "chrome/browser/search_engines/template_url_model.h"
33#include "chrome/browser/ui/browser.h"
34#include "chrome/browser/ui/browser_list.h"
35#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
36#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
37#include "chrome/browser/ui/gtk/cairo_cached_surface.h"
38#include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h"
39#include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h"
40#include "chrome/browser/ui/gtk/first_run_bubble.h"
41#include "chrome/browser/ui/gtk/gtk_theme_service.h"
42#include "chrome/browser/ui/gtk/gtk_util.h"
43#include "chrome/browser/ui/gtk/nine_box.h"
44#include "chrome/browser/ui/gtk/rounded_window.h"
45#include "chrome/browser/ui/gtk/view_id_util.h"
46#include "chrome/browser/ui/omnibox/location_bar_util.h"
47#include "chrome/common/chrome_switches.h"
48#include "chrome/common/extensions/extension.h"
49#include "chrome/common/extensions/extension_action.h"
50#include "chrome/common/extensions/extension_resource.h"
51#include "chrome/common/pref_names.h"
52#include "content/browser/tab_contents/tab_contents.h"
53#include "content/common/notification_service.h"
54#include "content/common/page_transition_types.h"
55#include "grit/generated_resources.h"
56#include "grit/theme_resources.h"
57#include "net/base/net_util.h"
58#include "ui/base/dragdrop/gtk_dnd_util.h"
59#include "ui/base/l10n/l10n_util.h"
60#include "ui/base/resource/resource_bundle.h"
61#include "ui/gfx/canvas_skia_paint.h"
62#include "ui/gfx/font.h"
63#include "ui/gfx/gtk_util.h"
64#include "webkit/glue/window_open_disposition.h"
65
66namespace {
67
68// We are positioned with a little bit of extra space that we don't use now.
69const int kTopMargin = 1;
70const int kBottomMargin = 1;
71const int kLeftMargin = 1;
72const int kRightMargin = 1;
73// We draw a border on the top and bottom (but not on left or right).
74const int kBorderThickness = 1;
75
76// Left margin of first run bubble.
77const int kFirstRunBubbleLeftMargin = 8;
78// Extra vertical spacing for first run bubble.
79const int kFirstRunBubbleTopMargin = 5;
80// Spacing needed to align the bubble with the left side of the omnibox.
81const int kFirstRunBubbleLeftSpacing = 4;
82
83// The padding around the top, bottom, and sides of the location bar hbox.
84// We don't want to edit control's text to be right against the edge,
85// as well the tab to search box and other widgets need to have the padding on
86// top and bottom to avoid drawing larger than the location bar space.
87const int kHboxBorder = 2;
88
89// Padding between the elements in the bar.
90const int kInnerPadding = 2;
91
92// Padding between the right of the star and the edge of the URL entry.
93const int kStarRightPadding = 2;
94
95// Colors used to draw the EV certificate rounded bubble.
96const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
97const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
98const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
99
100// Colors used to draw the Tab to Search rounded bubble.
101const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
102const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
103
104// Use weak gray for showing search and keyword hint text.
105const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
106
107// Size of the rounding of the "Search site for:" box.
108const int kCornerSize = 3;
109
110// The time, in ms, that the content setting label is fully displayed, for the
111// cases where we animate it into and out of view.
112const int kContentSettingImageDisplayTime = 3200;
113// The time, in ms, of the animation (open and close).
114const int kContentSettingImageAnimationTime = 150;
115
116// Color of border of content setting area (icon/label).
117const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
118// Colors for the background gradient.
119const double kContentSettingTopColor[] = { 0xff / 255.0,
120                                           0xf8 / 255.0,
121                                           0xd4 / 255.0 };
122const double kContentSettingBottomColor[] = { 0xff / 255.0,
123                                              0xe6 / 255.0,
124                                              0xaf / 255.0 };
125
126// If widget is visible, increment the int pointed to by count.
127// Suitible for use with gtk_container_foreach.
128void CountVisibleWidgets(GtkWidget* widget, gpointer count) {
129  if (GTK_WIDGET_VISIBLE(widget))
130    *static_cast<int*>(count) += 1;
131}
132
133}  // namespace
134
135////////////////////////////////////////////////////////////////////////////////
136// LocationBarViewGtk
137
138// static
139const GdkColor LocationBarViewGtk::kBackgroundColor =
140    GDK_COLOR_RGB(255, 255, 255);
141
142LocationBarViewGtk::LocationBarViewGtk(Browser* browser)
143    : star_image_(NULL),
144      starred_(false),
145      site_type_alignment_(NULL),
146      site_type_event_box_(NULL),
147      location_icon_image_(NULL),
148      drag_icon_(NULL),
149      enable_location_drag_(false),
150      security_info_label_(NULL),
151      tab_to_search_alignment_(NULL),
152      tab_to_search_box_(NULL),
153      tab_to_search_full_label_(NULL),
154      tab_to_search_partial_label_(NULL),
155      tab_to_search_hint_(NULL),
156      tab_to_search_hint_leading_label_(NULL),
157      tab_to_search_hint_icon_(NULL),
158      tab_to_search_hint_trailing_label_(NULL),
159      profile_(NULL),
160      command_updater_(browser->command_updater()),
161      toolbar_model_(browser->toolbar_model()),
162      browser_(browser),
163      disposition_(CURRENT_TAB),
164      transition_(PageTransition::TYPED),
165      first_run_bubble_(this),
166      popup_window_mode_(false),
167      theme_service_(NULL),
168      hbox_width_(0),
169      entry_box_width_(0),
170      show_selected_keyword_(false),
171      show_keyword_hint_(false) {
172}
173
174LocationBarViewGtk::~LocationBarViewGtk() {
175  // All of our widgets should have be children of / owned by the alignment.
176  star_.Destroy();
177  hbox_.Destroy();
178  content_setting_hbox_.Destroy();
179  page_action_hbox_.Destroy();
180}
181
182void LocationBarViewGtk::Init(bool popup_window_mode) {
183  popup_window_mode_ = popup_window_mode;
184
185  // Create the widget first, so we can pass it to the AutocompleteEditViewGtk.
186  hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding));
187  gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
188  // We will paint for the alignment, to paint the background and border.
189  gtk_widget_set_app_paintable(hbox_.get(), TRUE);
190  // Redraw the whole location bar when it changes size (e.g., when toggling
191  // the home button on/off.
192  gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
193
194  // Now initialize the AutocompleteEditViewGtk.
195  location_entry_.reset(new AutocompleteEditViewGtk(this,
196                                                    toolbar_model_,
197                                                    profile_,
198                                                    command_updater_,
199                                                    popup_window_mode_,
200                                                    hbox_.get()));
201  location_entry_->Init();
202
203  g_signal_connect(hbox_.get(), "expose-event",
204                   G_CALLBACK(&HandleExposeThunk), this);
205
206  BuildSiteTypeArea();
207
208  // Put |tab_to_search_box_|, |location_entry_|, and |tab_to_search_hint_| into
209  // a sub hbox, so that we can make this part horizontally shrinkable without
210  // affecting other elements in the location bar.
211  entry_box_ = gtk_hbox_new(FALSE, kInnerPadding);
212  gtk_widget_show(entry_box_);
213  gtk_widget_set_size_request(entry_box_, 0, -1);
214  gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0);
215
216  // We need to adjust the visibility of the search hint widgets according to
217  // the horizontal space in the |entry_box_|.
218  g_signal_connect(entry_box_, "size-allocate",
219                   G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this);
220
221  // Tab to search (the keyword box on the left hand side).
222  tab_to_search_full_label_ = gtk_label_new(NULL);
223  tab_to_search_partial_label_ = gtk_label_new(NULL);
224  GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0);
225  gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
226                     tab_to_search_full_label_, FALSE, FALSE, 0);
227  gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox),
228                     tab_to_search_partial_label_, FALSE, FALSE, 0);
229  GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0);
230  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
231  tab_to_search_magnifier_ = gtk_image_new_from_pixbuf(
232      rb.GetPixbufNamed(IDR_KEYWORD_SEARCH_MAGNIFIER));
233  gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_,
234                     FALSE, FALSE, 0);
235  gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox,
236                               false, 0);
237
238  // This creates a box around the keyword text with a border, background color,
239  // and padding around the text.
240  tab_to_search_box_ = gtk_util::CreateGtkBorderBin(
241      tab_to_search_hbox, NULL, 1, 1, 1, 3);
242  gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box");
243  gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor,
244                               kCornerSize,
245                               gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
246
247  // Put the event box in an alignment to get the padding correct.
248  tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1);
249  gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_),
250                    tab_to_search_box_);
251  gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_,
252                     FALSE, FALSE, 0);
253
254  // Show all children widgets of |tab_to_search_box_| initially, except
255  // |tab_to_search_partial_label_|.
256  gtk_widget_show_all(tab_to_search_box_);
257  gtk_widget_hide(tab_to_search_partial_label_);
258
259  location_entry_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
260  gtk_container_add(GTK_CONTAINER(location_entry_alignment_),
261                    location_entry_->GetNativeView());
262  gtk_box_pack_start(GTK_BOX(entry_box_), location_entry_alignment_,
263                     TRUE, TRUE, 0);
264
265  // Tab to search notification (the hint on the right hand side).
266  tab_to_search_hint_ = gtk_hbox_new(FALSE, 0);
267  gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint");
268  tab_to_search_hint_leading_label_ = gtk_label_new(NULL);
269  gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE);
270  tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf(
271      rb.GetPixbufNamed(IDR_LOCATION_BAR_KEYWORD_HINT_TAB));
272  tab_to_search_hint_trailing_label_ = gtk_label_new(NULL);
273  gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE);
274  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
275                     tab_to_search_hint_leading_label_,
276                     FALSE, FALSE, 0);
277  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
278                     tab_to_search_hint_icon_,
279                     FALSE, FALSE, 0);
280  gtk_box_pack_start(GTK_BOX(tab_to_search_hint_),
281                     tab_to_search_hint_trailing_label_,
282                     FALSE, FALSE, 0);
283  // Show all children widgets of |tab_to_search_hint_| initially.
284  gtk_widget_show_all(tab_to_search_hint_);
285  gtk_widget_hide(tab_to_search_hint_);
286  // tab_to_search_hint_ gets hidden initially in OnChanged.  Hiding it here
287  // doesn't work, someone is probably calling show_all on our parent box.
288  gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0);
289
290  // We don't show the star in popups, app windows, etc.
291  if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) {
292    CreateStarButton();
293    gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0);
294  }
295
296  content_setting_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding + 1));
297  gtk_widget_set_name(content_setting_hbox_.get(),
298                      "chrome-content-setting-hbox");
299  gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(),
300                   FALSE, FALSE, 1);
301
302  for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
303    ContentSettingImageViewGtk* content_setting_view =
304        new ContentSettingImageViewGtk(
305            static_cast<ContentSettingsType>(i), this, profile_);
306    content_setting_views_.push_back(content_setting_view);
307    gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()),
308                     content_setting_view->widget(), FALSE, FALSE, 0);
309  }
310
311  page_action_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding));
312  gtk_widget_set_name(page_action_hbox_.get(),
313                      "chrome-page-action-hbox");
314  gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(),
315                   FALSE, FALSE, 0);
316
317  // Now that we've created the widget hierarchy, connect to the main |hbox_|'s
318  // size-allocate so we can do proper resizing and eliding on
319  // |security_info_label_|.
320  g_signal_connect(hbox_.get(), "size-allocate",
321                   G_CALLBACK(&OnHboxSizeAllocateThunk), this);
322
323  registrar_.Add(this,
324                 NotificationType::BROWSER_THEME_CHANGED,
325                 NotificationService::AllSources());
326  edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
327                               profile_->GetPrefs(), this);
328
329  theme_service_ = GtkThemeService::GetFrom(profile_);
330  theme_service_->InitThemesFor(this);
331}
332
333void LocationBarViewGtk::BuildSiteTypeArea() {
334  location_icon_image_ = gtk_image_new();
335  gtk_widget_set_name(location_icon_image_, "chrome-location-icon");
336
337  GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1);
338  gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0);
339  gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_);
340  gtk_widget_show_all(icon_alignment);
341
342  security_info_label_ = gtk_label_new(NULL);
343  gtk_label_set_ellipsize(GTK_LABEL(security_info_label_),
344                          PANGO_ELLIPSIZE_MIDDLE);
345  gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL,
346                       &kEvSecureTextColor);
347  gtk_widget_set_name(security_info_label_,
348                      "chrome-location-bar-security-info-label");
349
350  GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1);
351  gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment,
352                     FALSE, FALSE, 0);
353  gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_,
354                     FALSE, FALSE, 2);
355
356  site_type_event_box_ = gtk_event_box_new();
357  gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL,
358                       &kEvSecureBackgroundColor);
359  g_signal_connect(site_type_event_box_, "drag-data-get",
360                   G_CALLBACK(&OnIconDragDataThunk), this);
361  g_signal_connect(site_type_event_box_, "drag-begin",
362                   G_CALLBACK(&OnIconDragBeginThunk), this);
363  g_signal_connect(site_type_event_box_, "drag-end",
364                   G_CALLBACK(&OnIconDragEndThunk), this);
365
366  // Make the event box not visible so it does not paint a background.
367  gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
368                                   FALSE);
369  gtk_widget_set_name(site_type_event_box_,
370                      "chrome-location-icon-eventbox");
371  gtk_container_add(GTK_CONTAINER(site_type_event_box_),
372                    site_type_hbox);
373
374  // Put the event box in an alignment to get the padding correct.
375  site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1);
376  gtk_container_add(GTK_CONTAINER(site_type_alignment_),
377                    site_type_event_box_);
378  gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_,
379                     FALSE, FALSE, 0);
380
381  gtk_widget_set_tooltip_text(location_icon_image_,
382      l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
383
384  g_signal_connect(site_type_event_box_, "button-release-event",
385                   G_CALLBACK(&OnIconReleasedThunk), this);
386}
387
388void LocationBarViewGtk::SetSiteTypeDragSource() {
389  bool enable = !location_entry()->IsEditingOrEmpty();
390  if (enable_location_drag_ == enable)
391    return;
392  enable_location_drag_ = enable;
393
394  if (!enable) {
395    gtk_drag_source_unset(site_type_event_box_);
396    return;
397  }
398
399  gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK,
400                      NULL, 0, GDK_ACTION_COPY);
401  ui::SetSourceTargetListFromCodeMask(site_type_event_box_,
402                                      ui::TEXT_PLAIN |
403                                      ui::TEXT_URI_LIST |
404                                      ui::CHROME_NAMED_URL);
405}
406
407void LocationBarViewGtk::SetProfile(Profile* profile) {
408  profile_ = profile;
409}
410
411TabContents* LocationBarViewGtk::GetTabContents() const {
412  return browser_->GetSelectedTabContents();
413}
414
415void LocationBarViewGtk::SetPreviewEnabledPageAction(
416    ExtensionAction *page_action,
417    bool preview_enabled) {
418  DCHECK(page_action);
419  UpdatePageActions();
420  for (ScopedVector<PageActionViewGtk>::iterator iter =
421       page_action_views_.begin(); iter != page_action_views_.end();
422       ++iter) {
423    if ((*iter)->page_action() == page_action) {
424      (*iter)->set_preview_enabled(preview_enabled);
425      UpdatePageActions();
426      return;
427    }
428  }
429}
430
431GtkWidget* LocationBarViewGtk::GetPageActionWidget(
432    ExtensionAction *page_action) {
433  DCHECK(page_action);
434  for (ScopedVector<PageActionViewGtk>::iterator iter =
435           page_action_views_.begin();
436       iter != page_action_views_.end();
437       ++iter) {
438    if ((*iter)->page_action() == page_action)
439      return (*iter)->widget();
440  }
441  return NULL;
442}
443
444void LocationBarViewGtk::Update(const TabContents* contents) {
445  UpdateStarIcon();
446  UpdateSiteTypeArea();
447  UpdateContentSettingsIcons();
448  UpdatePageActions();
449  location_entry_->Update(contents);
450  // The security level (background color) could have changed, etc.
451  if (theme_service_->UseGtkTheme()) {
452    // In GTK mode, we need our parent to redraw, as it draws the text entry
453    // border.
454    gtk_widget_queue_draw(widget()->parent);
455  } else {
456    gtk_widget_queue_draw(widget());
457  }
458}
459
460void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url,
461    WindowOpenDisposition disposition,
462    PageTransition::Type transition,
463    const GURL& alternate_nav_url) {
464  if (url.is_valid()) {
465    location_input_ = UTF8ToWide(url.spec());
466    disposition_ = disposition;
467    transition_ = transition;
468
469    if (command_updater_) {
470      if (!alternate_nav_url.is_valid()) {
471        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
472      } else {
473        AlternateNavURLFetcher* fetcher =
474            new AlternateNavURLFetcher(alternate_nav_url);
475        // The AlternateNavURLFetcher will listen for the pending navigation
476        // notification that will be issued as a result of the "open URL." It
477        // will automatically install itself into that navigation controller.
478        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
479        if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
480          // I'm not sure this should be reachable, but I'm not also sure enough
481          // that it shouldn't to stick in a NOTREACHED().  In any case, this is
482          // harmless.
483          delete fetcher;
484        } else {
485          // The navigation controller will delete the fetcher.
486        }
487      }
488    }
489  }
490}
491
492void LocationBarViewGtk::OnChanged() {
493  UpdateSiteTypeArea();
494
495  const string16 keyword(location_entry_->model()->keyword());
496  const bool is_keyword_hint = location_entry_->model()->is_keyword_hint();
497  show_selected_keyword_ = !keyword.empty() && !is_keyword_hint;
498  show_keyword_hint_ = !keyword.empty() && is_keyword_hint;
499
500  if (show_selected_keyword_)
501    SetKeywordLabel(keyword);
502
503  if (show_keyword_hint_)
504    SetKeywordHintLabel(keyword);
505
506  AdjustChildrenVisibility();
507}
508
509void LocationBarViewGtk::OnSelectionBoundsChanged() {
510  NOTIMPLEMENTED();
511}
512
513void LocationBarViewGtk::CreateStarButton() {
514  star_image_ = gtk_image_new();
515
516  GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
517  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
518                            0, kStarRightPadding);
519  gtk_container_add(GTK_CONTAINER(alignment), star_image_);
520
521  star_.Own(gtk_event_box_new());
522  gtk_event_box_set_visible_window(GTK_EVENT_BOX(star_.get()), FALSE);
523  gtk_container_add(GTK_CONTAINER(star_.get()), alignment);
524  gtk_widget_show_all(star_.get());
525  ViewIDUtil::SetID(star_.get(), VIEW_ID_STAR_BUTTON);
526
527  gtk_widget_set_tooltip_text(star_.get(),
528      l10n_util::GetStringUTF8(IDS_TOOLTIP_STAR).c_str());
529  g_signal_connect(star_.get(), "button-press-event",
530                   G_CALLBACK(OnStarButtonPressThunk), this);
531}
532
533void LocationBarViewGtk::OnInputInProgress(bool in_progress) {
534  // This is identical to the Windows code, except that we don't proxy the call
535  // back through the Toolbar, and just access the model here.
536  // The edit should make sure we're only notified when something changes.
537  DCHECK(toolbar_model_->input_in_progress() != in_progress);
538
539  toolbar_model_->set_input_in_progress(in_progress);
540  Update(NULL);
541}
542
543void LocationBarViewGtk::OnKillFocus() {
544}
545
546void LocationBarViewGtk::OnSetFocus() {
547  AccessibilityTextBoxInfo info(
548      profile_,
549      l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION).c_str(),
550      false);
551  NotificationService::current()->Notify(
552      NotificationType::ACCESSIBILITY_CONTROL_FOCUSED,
553      Source<Profile>(profile_),
554      Details<AccessibilityTextBoxInfo>(&info));
555
556  // Update the keyword and search hint states.
557  OnChanged();
558}
559
560SkBitmap LocationBarViewGtk::GetFavicon() const {
561  return GetTabContents()->GetFavicon();
562}
563
564string16 LocationBarViewGtk::GetTitle() const {
565  return GetTabContents()->GetTitle();
566}
567
568InstantController* LocationBarViewGtk::GetInstant() {
569  return browser_->instant();
570}
571
572TabContentsWrapper* LocationBarViewGtk::GetTabContentsWrapper() const {
573  return browser_->GetSelectedTabContentsWrapper();
574}
575
576void LocationBarViewGtk::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) {
577  // We need the browser window to be shown before we can show the bubble, but
578  // we get called before that's happened.
579  Task* task = first_run_bubble_.NewRunnableMethod(
580      &LocationBarViewGtk::ShowFirstRunBubbleInternal, bubble_type);
581  MessageLoop::current()->PostTask(FROM_HERE, task);
582}
583
584void LocationBarViewGtk::SetSuggestedText(const string16& text,
585                                          InstantCompleteBehavior behavior) {
586  location_entry_->model()->SetSuggestedText(text, behavior);
587}
588
589std::wstring LocationBarViewGtk::GetInputString() const {
590  return location_input_;
591}
592
593WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
594  return disposition_;
595}
596
597PageTransition::Type LocationBarViewGtk::GetPageTransition() const {
598  return transition_;
599}
600
601void LocationBarViewGtk::AcceptInput() {
602  location_entry_->model()->AcceptInput(CURRENT_TAB, false);
603}
604
605void LocationBarViewGtk::FocusLocation(bool select_all) {
606  location_entry_->SetFocus();
607  if (select_all)
608    location_entry_->SelectAll(true);
609}
610
611void LocationBarViewGtk::FocusSearch() {
612  location_entry_->SetFocus();
613  location_entry_->SetForcedQuery();
614}
615
616void LocationBarViewGtk::UpdateContentSettingsIcons() {
617  TabContents* tab_contents = GetTabContents();
618  bool any_visible = false;
619  for (ScopedVector<ContentSettingImageViewGtk>::iterator i(
620           content_setting_views_.begin());
621       i != content_setting_views_.end(); ++i) {
622    (*i)->UpdateFromTabContents(
623        toolbar_model_->input_in_progress() ? NULL : tab_contents);
624    any_visible = (*i)->IsVisible() || any_visible;
625  }
626
627  // If there are no visible content things, hide the top level box so it
628  // doesn't mess with padding.
629  if (any_visible)
630    gtk_widget_show(content_setting_hbox_.get());
631  else
632    gtk_widget_hide(content_setting_hbox_.get());
633}
634
635void LocationBarViewGtk::UpdatePageActions() {
636  std::vector<ExtensionAction*> page_actions;
637  ExtensionService* service = profile_->GetExtensionService();
638  if (!service)
639    return;
640
641  // Find all the page actions.
642  for (size_t i = 0; i < service->extensions()->size(); ++i) {
643    if (service->extensions()->at(i)->page_action())
644      page_actions.push_back(service->extensions()->at(i)->page_action());
645  }
646
647  // Initialize on the first call, or re-inialize if more extensions have been
648  // loaded or added after startup.
649  if (page_actions.size() != page_action_views_.size()) {
650    page_action_views_.reset();  // Delete the old views (if any).
651
652    for (size_t i = 0; i < page_actions.size(); ++i) {
653      page_action_views_.push_back(
654          new PageActionViewGtk(this, profile_, page_actions[i]));
655      gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()),
656                       page_action_views_[i]->widget(), FALSE, FALSE, 0);
657    }
658    NotificationService::current()->Notify(
659        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
660        Source<LocationBar>(this),
661        NotificationService::NoDetails());
662  }
663
664  TabContents* contents = GetTabContents();
665  if (!page_action_views_.empty() && contents) {
666    GURL url = GURL(WideToUTF8(toolbar_model_->GetText()));
667
668    for (size_t i = 0; i < page_action_views_.size(); i++) {
669      page_action_views_[i]->UpdateVisibility(
670          toolbar_model_->input_in_progress() ? NULL : contents, url);
671    }
672  }
673
674  // If there are no visible page actions, hide the hbox too, so that it does
675  // not affect the padding in the location bar.
676  if (PageActionVisibleCount() && !ShouldOnlyShowLocation())
677    gtk_widget_show(page_action_hbox_.get());
678  else
679    gtk_widget_hide(page_action_hbox_.get());
680}
681
682void LocationBarViewGtk::InvalidatePageActions() {
683  size_t count_before = page_action_views_.size();
684  page_action_views_.reset();
685  if (page_action_views_.size() != count_before) {
686    NotificationService::current()->Notify(
687        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
688        Source<LocationBar>(this),
689        NotificationService::NoDetails());
690  }
691}
692
693void LocationBarViewGtk::SaveStateToContents(TabContents* contents) {
694  location_entry_->SaveStateToTab(contents);
695}
696
697void LocationBarViewGtk::Revert() {
698  location_entry_->RevertAll();
699}
700
701const AutocompleteEditView* LocationBarViewGtk::location_entry() const {
702  return location_entry_.get();
703}
704
705AutocompleteEditView* LocationBarViewGtk::location_entry() {
706  return location_entry_.get();
707}
708
709LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
710  return this;
711}
712
713int LocationBarViewGtk::PageActionCount() {
714  return page_action_views_.size();
715}
716
717int LocationBarViewGtk::PageActionVisibleCount() {
718  int count = 0;
719  gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
720                        CountVisibleWidgets, &count);
721  return count;
722}
723
724ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) {
725  if (index >= page_action_views_.size()) {
726    NOTREACHED();
727    return NULL;
728  }
729
730  return page_action_views_[index]->page_action();
731}
732
733ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) {
734  size_t visible_index = 0;
735  for (size_t i = 0; i < page_action_views_.size(); ++i) {
736    if (page_action_views_[i]->IsVisible()) {
737      if (index == visible_index++)
738        return page_action_views_[i]->page_action();
739    }
740  }
741
742  NOTREACHED();
743  return NULL;
744}
745
746void LocationBarViewGtk::TestPageActionPressed(size_t index) {
747  if (index >= page_action_views_.size()) {
748    NOTREACHED();
749    return;
750  }
751
752  page_action_views_[index]->TestActivatePageAction();
753}
754
755void LocationBarViewGtk::Observe(NotificationType type,
756                                 const NotificationSource& source,
757                                 const NotificationDetails& details) {
758  if (type.value == NotificationType::PREF_CHANGED) {
759    UpdateStarIcon();
760    return;
761  }
762
763  DCHECK_EQ(type.value, NotificationType::BROWSER_THEME_CHANGED);
764
765  if (theme_service_->UseGtkTheme()) {
766    gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL);
767
768    GdkColor border_color = theme_service_->GetGdkColor(
769        ThemeService::COLOR_FRAME);
770    gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color);
771
772    gtk_util::SetLabelColor(tab_to_search_full_label_, NULL);
773    gtk_util::SetLabelColor(tab_to_search_partial_label_, NULL);
774    gtk_util::SetLabelColor(tab_to_search_hint_leading_label_, NULL);
775    gtk_util::SetLabelColor(tab_to_search_hint_trailing_label_, NULL);
776
777    gtk_util::UndoForceFontSize(security_info_label_);
778    gtk_util::UndoForceFontSize(tab_to_search_full_label_);
779    gtk_util::UndoForceFontSize(tab_to_search_partial_label_);
780    gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_);
781    gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_);
782
783    gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
784                              0, 0, 0, 0);
785    gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
786                              1, 1, 1, 0);
787    gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
788                              1, 1, 1, 0);
789  } else {
790    gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL,
791                         &kKeywordBackgroundColor);
792    gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_,
793                                          kKeywordBorderColor);
794
795    gtk_util::SetLabelColor(tab_to_search_full_label_, &gtk_util::kGdkBlack);
796    gtk_util::SetLabelColor(tab_to_search_partial_label_, &gtk_util::kGdkBlack);
797    gtk_util::SetLabelColor(tab_to_search_hint_leading_label_,
798                            &kHintTextColor);
799    gtk_util::SetLabelColor(tab_to_search_hint_trailing_label_,
800                            &kHintTextColor);
801
802    // Until we switch to vector graphics, force the font size of labels.
803    // 12.1px = 9pt @ 96dpi
804    gtk_util::ForceFontSizePixels(security_info_label_, 12.1);
805    gtk_util::ForceFontSizePixels(tab_to_search_full_label_,
806        browser_defaults::kAutocompleteEditFontPixelSize);
807    gtk_util::ForceFontSizePixels(tab_to_search_partial_label_,
808        browser_defaults::kAutocompleteEditFontPixelSize);
809    gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_,
810        browser_defaults::kAutocompleteEditFontPixelSize);
811    gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_,
812        browser_defaults::kAutocompleteEditFontPixelSize);
813
814    const int top_bottom = popup_window_mode_ ? kBorderThickness : 0;
815    gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_),
816                              kTopMargin + kBorderThickness,
817                              kBottomMargin + kBorderThickness,
818                              top_bottom, top_bottom);
819    gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_),
820                              1, 1, 0, 0);
821    gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_),
822                              1, 1, 0, 0);
823  }
824
825  UpdateStarIcon();
826  UpdateSiteTypeArea();
827  UpdateContentSettingsIcons();
828}
829
830gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget,
831                                          GdkEventExpose* event) {
832  // If we're not using GTK theming, draw our own border over the edge pixels
833  // of the background.
834  if (!profile_ ||
835      !GtkThemeService::GetFrom(profile_)->UseGtkTheme()) {
836    int left, center, right;
837    if (popup_window_mode_) {
838      left = right = IDR_LOCATIONBG_POPUPMODE_EDGE;
839      center = IDR_LOCATIONBG_POPUPMODE_CENTER;
840    } else {
841      left = IDR_LOCATIONBG_L;
842      center = IDR_LOCATIONBG_C;
843      right = IDR_LOCATIONBG_R;
844    }
845
846    NineBox background(left, center, right,
847                       0, 0, 0, 0, 0, 0);
848    background.RenderToWidget(widget);
849  }
850
851  return FALSE;  // Continue propagating the expose.
852}
853
854void LocationBarViewGtk::UpdateSiteTypeArea() {
855  // The icon is always visible except when the |tab_to_search_alignment_| is
856  // visible.
857  if (!location_entry_->model()->keyword().empty() &&
858      !location_entry_->model()->is_keyword_hint()) {
859    gtk_widget_hide(site_type_area());
860    return;
861  }
862
863  int resource_id = location_entry_->GetIcon();
864  gtk_image_set_from_pixbuf(GTK_IMAGE(location_icon_image_),
865                            theme_service_->GetPixbufNamed(resource_id));
866
867  if (toolbar_model_->GetSecurityLevel() == ToolbarModel::EV_SECURE) {
868    if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
869      // Fun fact: If wee try to make |site_type_event_box_| act as a
870      // rounded window while it doesn't have a visible window, GTK interprets
871      // this as a sign that it should paint the skyline texture into the
872      // omnibox.
873      gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
874                                       TRUE);
875
876      gtk_util::ActAsRoundedWindow(site_type_event_box_,
877                                   kEvSecureBorderColor,
878                                   kCornerSize,
879                                   gtk_util::ROUNDED_ALL,
880                                   gtk_util::BORDER_ALL);
881    }
882
883    std::wstring info_text = toolbar_model_->GetEVCertName();
884    gtk_label_set_text(GTK_LABEL(security_info_label_),
885                       WideToUTF8(info_text).c_str());
886
887    UpdateEVCertificateLabelSize();
888
889    gtk_widget_show(GTK_WIDGET(security_info_label_));
890  } else {
891    if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) {
892      gtk_util::StopActingAsRoundedWindow(site_type_event_box_);
893
894      gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_),
895                                       FALSE);
896    }
897
898    gtk_widget_hide(GTK_WIDGET(security_info_label_));
899  }
900
901  if (location_entry()->IsEditingOrEmpty()) {
902    // Do not show the tooltip if the user has been editing the location
903    // bar, or the location bar is at the NTP.
904    gtk_widget_set_tooltip_text(location_icon_image_, "");
905  } else {
906    gtk_widget_set_tooltip_text(location_icon_image_,
907        l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str());
908  }
909
910  gtk_widget_show(site_type_area());
911
912  SetSiteTypeDragSource();
913}
914
915void LocationBarViewGtk::UpdateEVCertificateLabelSize() {
916  // Figure out the width of the average character.
917  PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_));
918  PangoContext* context = pango_layout_get_context(layout);
919  PangoFontMetrics* metrics = pango_context_get_metrics(
920      context,
921      gtk_widget_get_style(security_info_label_)->font_desc,
922      pango_context_get_language(context));
923  int char_width =
924      pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE;
925
926  // The EV label should never take up more than half the hbox. We try to
927  // correct our inaccurate measurement units ("the average character width")
928  // by dividing more than an even 2.
929  int text_area = security_info_label_->allocation.width +
930                  entry_box_->allocation.width;
931  int max_chars = static_cast<int>(static_cast<float>(text_area) /
932                                   static_cast<float>(char_width) / 2.75);
933  // Don't let the label be smaller than 10 characters so that the country
934  // code is always visible.
935  gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_),
936                                std::max(10, max_chars));
937
938  pango_font_metrics_unref(metrics);
939}
940
941void LocationBarViewGtk::SetKeywordLabel(const string16& keyword) {
942  if (keyword.empty())
943    return;
944
945  DCHECK(profile_);
946  if (!profile_->GetTemplateURLModel())
947    return;
948
949  bool is_extension_keyword;
950  const string16 short_name = profile_->GetTemplateURLModel()->
951      GetKeywordShortName(keyword, &is_extension_keyword);
952  int message_id = is_extension_keyword ?
953      IDS_OMNIBOX_EXTENSION_KEYWORD_TEXT : IDS_OMNIBOX_KEYWORD_TEXT;
954  string16 full_name = l10n_util::GetStringFUTF16(message_id,
955                                                  short_name);
956  string16 partial_name = l10n_util::GetStringFUTF16(
957      message_id,
958      WideToUTF16Hack(
959          location_bar_util::CalculateMinString(UTF16ToWideHack(short_name))));
960  gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_),
961                     UTF16ToUTF8(full_name).c_str());
962  gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_),
963                     UTF16ToUTF8(partial_name).c_str());
964
965  if (last_keyword_ != keyword) {
966    last_keyword_ = keyword;
967
968    if (is_extension_keyword) {
969      const TemplateURL* template_url =
970          profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
971      const SkBitmap& bitmap = profile_->GetExtensionService()->
972          GetOmniboxIcon(template_url->GetExtensionId());
973      GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap);
974      gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), pixbuf);
975      g_object_unref(pixbuf);
976    } else {
977      ResourceBundle& rb = ResourceBundle::GetSharedInstance();
978      gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_),
979                                rb.GetPixbufNamed(IDR_OMNIBOX_SEARCH));
980    }
981  }
982}
983
984void LocationBarViewGtk::SetKeywordHintLabel(const string16& keyword) {
985  if (keyword.empty())
986    return;
987
988  DCHECK(profile_);
989  if (!profile_->GetTemplateURLModel())
990    return;
991
992  bool is_extension_keyword;
993  const string16 short_name = profile_->GetTemplateURLModel()->
994      GetKeywordShortName(keyword, &is_extension_keyword);
995  int message_id = is_extension_keyword ?
996      IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT;
997  std::vector<size_t> content_param_offsets;
998  const string16 keyword_hint = l10n_util::GetStringFUTF16(
999      message_id,
1000      string16(),
1001      short_name,
1002      &content_param_offsets);
1003  if (content_param_offsets.size() != 2) {
1004    // See comments on an identical NOTREACHED() in search_provider.cc.
1005    NOTREACHED();
1006    return;
1007  }
1008
1009  std::string leading(UTF16ToUTF8(
1010      keyword_hint.substr(0, content_param_offsets.front())));
1011  std::string trailing(UTF16ToUTF8(
1012      keyword_hint.substr(content_param_offsets.front())));
1013  gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_),
1014                     leading.c_str());
1015  gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_),
1016                     trailing.c_str());
1017}
1018
1019void LocationBarViewGtk::ShowFirstRunBubbleInternal(
1020    FirstRun::BubbleType bubble_type) {
1021  if (!location_entry_.get() || !widget()->window)
1022    return;
1023
1024  gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_);
1025  bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing);
1026
1027  FirstRunBubble::Show(profile_, location_icon_image_, bounds, bubble_type);
1028}
1029
1030gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender,
1031                                            GdkEventButton* event) {
1032  TabContents* tab = GetTabContents();
1033
1034  if (event->button == 1) {
1035    // Do not show page info if the user has been editing the location
1036    // bar, or the location bar is at the NTP.
1037    if (location_entry()->IsEditingOrEmpty())
1038      return FALSE;
1039
1040    // (0,0) event coordinates indicates that the release came at the end of
1041    // a drag.
1042    if (event->x == 0 && event->y == 0)
1043      return FALSE;
1044
1045    NavigationEntry* nav_entry = tab->controller().GetActiveEntry();
1046    if (!nav_entry) {
1047      NOTREACHED();
1048      return FALSE;
1049    }
1050    tab->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), true);
1051    return TRUE;
1052  } else if (event->button == 2) {
1053    // When the user middle clicks on the location icon, try to open the
1054    // contents of the PRIMARY selection in the current tab.
1055    // If the click was outside our bounds, do nothing.
1056    if (!gtk_util::WidgetBounds(sender).Contains(
1057            gfx::Point(event->x, event->y))) {
1058      return FALSE;
1059    }
1060
1061    GURL url;
1062    if (!gtk_util::URLFromPrimarySelection(profile_, &url))
1063      return FALSE;
1064
1065    tab->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
1066    return TRUE;
1067  }
1068
1069  return FALSE;
1070}
1071
1072void LocationBarViewGtk::OnIconDragData(GtkWidget* sender,
1073                                        GdkDragContext* context,
1074                                        GtkSelectionData* data,
1075                                        guint info, guint time) {
1076  TabContents* tab = GetTabContents();
1077  if (!tab)
1078    return;
1079  ui::WriteURLWithName(data, tab->GetURL(), tab->GetTitle(), info);
1080}
1081
1082void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender,
1083                                         GdkDragContext* context) {
1084  SkBitmap favicon = GetFavicon();
1085  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&favicon);
1086  if (!pixbuf)
1087    return;
1088  drag_icon_ = bookmark_utils::GetDragRepresentation(pixbuf,
1089      GetTitle(), theme_service_);
1090  g_object_unref(pixbuf);
1091  gtk_drag_set_icon_widget(context, drag_icon_, 0, 0);
1092}
1093
1094void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
1095                                       GdkDragContext* context) {
1096  DCHECK(drag_icon_);
1097  gtk_widget_destroy(drag_icon_);
1098  drag_icon_ = NULL;
1099}
1100
1101void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
1102                                            GtkAllocation* allocation) {
1103  if (hbox_width_ != allocation->width) {
1104    hbox_width_ = allocation->width;
1105    UpdateEVCertificateLabelSize();
1106  }
1107}
1108
1109void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender,
1110                                                GtkAllocation* allocation) {
1111  if (entry_box_width_ != allocation->width) {
1112    entry_box_width_ = allocation->width;
1113    AdjustChildrenVisibility();
1114  }
1115}
1116
1117gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
1118                                               GdkEventButton* event) {
1119  browser_->ExecuteCommand(IDC_BOOKMARK_PAGE);
1120  return FALSE;
1121}
1122
1123void LocationBarViewGtk::ShowStarBubble(const GURL& url,
1124                                        bool newly_bookmarked) {
1125  if (!star_.get())
1126    return;
1127
1128  BookmarkBubbleGtk::Show(star_.get(), profile_, url, newly_bookmarked);
1129}
1130
1131void LocationBarViewGtk::SetStarred(bool starred) {
1132  if (starred == starred_)
1133    return;
1134
1135  starred_ = starred;
1136  UpdateStarIcon();
1137}
1138
1139void LocationBarViewGtk::UpdateStarIcon() {
1140  if (!star_.get())
1141    return;
1142  bool star_enabled = !toolbar_model_->input_in_progress() &&
1143                      edit_bookmarks_enabled_.GetValue();
1144  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
1145  if (star_enabled) {
1146    gtk_widget_show_all(star_.get());
1147    gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_),
1148        theme_service_->GetPixbufNamed(
1149            starred_ ? IDR_STAR_LIT : IDR_STAR));
1150  } else {
1151    gtk_widget_hide_all(star_.get());
1152  }
1153}
1154
1155bool LocationBarViewGtk::ShouldOnlyShowLocation() {
1156  return browser_->type() != Browser::TYPE_NORMAL;
1157}
1158
1159void LocationBarViewGtk::AdjustChildrenVisibility() {
1160  int text_width = location_entry_->TextWidth();
1161  int available_width = entry_box_width_ - text_width - kInnerPadding;
1162
1163  // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be
1164  // visible at the same time.
1165  if (!show_selected_keyword_ && GTK_WIDGET_VISIBLE(tab_to_search_alignment_))
1166    gtk_widget_hide(tab_to_search_alignment_);
1167  else if (!show_keyword_hint_ && GTK_WIDGET_VISIBLE(tab_to_search_hint_))
1168    gtk_widget_hide(tab_to_search_hint_);
1169
1170  if (show_selected_keyword_) {
1171    GtkRequisition box, full_label, partial_label;
1172    gtk_widget_size_request(tab_to_search_box_, &box);
1173    gtk_widget_size_request(tab_to_search_full_label_, &full_label);
1174    gtk_widget_size_request(tab_to_search_partial_label_, &partial_label);
1175    int full_partial_width_diff = full_label.width - partial_label.width;
1176    int full_box_width;
1177    int partial_box_width;
1178    if (GTK_WIDGET_VISIBLE(tab_to_search_full_label_)) {
1179      full_box_width = box.width;
1180      partial_box_width = full_box_width - full_partial_width_diff;
1181    } else {
1182      partial_box_width = box.width;
1183      full_box_width = partial_box_width + full_partial_width_diff;
1184    }
1185
1186    if (partial_box_width >= entry_box_width_ - kInnerPadding) {
1187      gtk_widget_hide(tab_to_search_alignment_);
1188    } else if (full_box_width >= available_width) {
1189      gtk_widget_hide(tab_to_search_full_label_);
1190      gtk_widget_show(tab_to_search_partial_label_);
1191      gtk_widget_show(tab_to_search_alignment_);
1192    } else if (full_box_width < available_width) {
1193      gtk_widget_hide(tab_to_search_partial_label_);
1194      gtk_widget_show(tab_to_search_full_label_);
1195      gtk_widget_show(tab_to_search_alignment_);
1196    }
1197  } else if (show_keyword_hint_) {
1198    GtkRequisition leading, icon, trailing;
1199    gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading);
1200    gtk_widget_size_request(tab_to_search_hint_icon_, &icon);
1201    gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing);
1202    int full_width = leading.width + icon.width + trailing.width;
1203
1204    if (icon.width >= entry_box_width_ - kInnerPadding) {
1205      gtk_widget_hide(tab_to_search_hint_);
1206    } else if (full_width >= available_width) {
1207      gtk_widget_hide(tab_to_search_hint_leading_label_);
1208      gtk_widget_hide(tab_to_search_hint_trailing_label_);
1209      gtk_widget_show(tab_to_search_hint_);
1210    } else if (full_width < available_width) {
1211      gtk_widget_show(tab_to_search_hint_leading_label_);
1212      gtk_widget_show(tab_to_search_hint_trailing_label_);
1213      gtk_widget_show(tab_to_search_hint_);
1214    }
1215  }
1216}
1217
1218////////////////////////////////////////////////////////////////////////////////
1219// LocationBarViewGtk::ContentSettingImageViewGtk
1220LocationBarViewGtk::ContentSettingImageViewGtk::ContentSettingImageViewGtk(
1221    ContentSettingsType content_type,
1222    const LocationBarViewGtk* parent,
1223    Profile* profile)
1224    : content_setting_image_model_(
1225          ContentSettingImageModel::CreateContentSettingImageModel(
1226              content_type)),
1227      alignment_(gtk_alignment_new(0, 0, 1, 1)),
1228      event_box_(gtk_event_box_new()),
1229      hbox_(gtk_hbox_new(FALSE, kInnerPadding)),
1230      image_(gtk_image_new()),
1231      label_(gtk_label_new(NULL)),
1232      parent_(parent),
1233      profile_(profile),
1234      info_bubble_(NULL),
1235      animation_(this),
1236      method_factory_(this) {
1237  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0);
1238  gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get());
1239
1240  // Make the event box not visible so it does not paint a background.
1241  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1242  g_signal_connect(event_box_.get(), "button-press-event",
1243                   G_CALLBACK(&OnButtonPressedThunk), this);
1244  g_signal_connect(event_box_.get(), "expose-event",
1245                   G_CALLBACK(&OnExposeThunk), this);
1246
1247  gtk_widget_set_no_show_all(label_.get(), TRUE);
1248  gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE);
1249
1250  gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0);
1251  gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0);
1252
1253  // The +1 accounts for the pixel that is devoted to drawing the border.
1254  gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder + 1);
1255
1256  gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_);
1257  gtk_widget_hide(widget());
1258
1259  animation_.SetSlideDuration(kContentSettingImageAnimationTime);
1260}
1261
1262LocationBarViewGtk::ContentSettingImageViewGtk::~ContentSettingImageViewGtk() {
1263  image_.Destroy();
1264  label_.Destroy();
1265  event_box_.Destroy();
1266  alignment_.Destroy();
1267
1268  if (info_bubble_)
1269    info_bubble_->Close();
1270}
1271
1272void LocationBarViewGtk::ContentSettingImageViewGtk::UpdateFromTabContents(
1273    TabContents* tab_contents) {
1274  content_setting_image_model_->UpdateFromTabContents(tab_contents);
1275  if (!content_setting_image_model_->is_visible()) {
1276    gtk_widget_hide(widget());
1277    return;
1278  }
1279
1280  gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()),
1281      GtkThemeService::GetFrom(profile_)->GetPixbufNamed(
1282          content_setting_image_model_->get_icon()));
1283
1284  gtk_widget_set_tooltip_text(widget(),
1285      content_setting_image_model_->get_tooltip().c_str());
1286  gtk_widget_show_all(widget());
1287
1288  TabSpecificContentSettings* content_settings = tab_contents ?
1289      tab_contents->GetTabSpecificContentSettings() : NULL;
1290  if (!content_settings || content_settings->IsBlockageIndicated(
1291      content_setting_image_model_->get_content_settings_type()))
1292    return;
1293
1294  // The content blockage was not yet indicated to the user. Start indication
1295  // animation and clear "not yet shown" flag.
1296  content_settings->SetBlockageHasBeenIndicated(
1297      content_setting_image_model_->get_content_settings_type());
1298
1299  int label_string_id =
1300      content_setting_image_model_->explanatory_string_id();
1301  // Check if the animation is enabled and if the string for animation is
1302  // available. If there's no string for the content type, we don't animate.
1303  if (CommandLine::ForCurrentProcess()->HasSwitch(
1304      switches::kDisableBlockContentAnimation) || !label_string_id)
1305    return;
1306
1307  gtk_label_set_text(GTK_LABEL(label_.get()),
1308      l10n_util::GetStringUTF8(label_string_id).c_str());
1309  StartAnimating();
1310}
1311
1312void LocationBarViewGtk::ContentSettingImageViewGtk::StartAnimating() {
1313  if (animation_.IsShowing() || animation_.IsClosing())
1314    return;
1315
1316  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE);
1317  gtk_util::ActAsRoundedWindow(event_box_.get(), kContentSettingBorderColor,
1318                               kCornerSize,
1319                               gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
1320
1321  gtk_widget_set_size_request(label_.get(), -1, -1);
1322  gtk_widget_size_request(label_.get(), &label_req_);
1323  gtk_widget_set_size_request(label_.get(), 0, -1);
1324  gtk_widget_show(label_.get());
1325
1326  animation_.Show();
1327}
1328
1329void LocationBarViewGtk::ContentSettingImageViewGtk::CloseAnimation() {
1330  animation_.Hide();
1331}
1332
1333void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationProgressed(
1334    const ui::Animation* animation) {
1335  gtk_widget_set_size_request(
1336      label_.get(),
1337      animation->GetCurrentValue() * label_req_.width,
1338      -1);
1339}
1340
1341void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationEnded(
1342    const ui::Animation* animation) {
1343  if (animation_.IsShowing()) {
1344    MessageLoop::current()->PostDelayedTask(FROM_HERE,
1345        method_factory_.NewRunnableMethod(
1346            &ContentSettingImageViewGtk::CloseAnimation),
1347        kContentSettingImageDisplayTime);
1348  } else {
1349    gtk_widget_hide(label_.get());
1350    gtk_util::StopActingAsRoundedWindow(event_box_.get());
1351    gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1352  }
1353}
1354
1355void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationCanceled(
1356    const ui::Animation* animation) {
1357}
1358
1359gboolean LocationBarViewGtk::ContentSettingImageViewGtk::OnButtonPressed(
1360    GtkWidget* sender, GdkEvent* event) {
1361  TabContents* tab_contents = parent_->GetTabContents();
1362  if (!tab_contents)
1363    return TRUE;
1364  const ContentSettingsType content_settings_type =
1365      content_setting_image_model_->get_content_settings_type();
1366  if (content_settings_type == CONTENT_SETTINGS_TYPE_PRERENDER)
1367    return TRUE;
1368  GURL url = tab_contents->GetURL();
1369  std::wstring display_host;
1370  net::AppendFormattedHost(url,
1371      UTF8ToWide(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)),
1372      &display_host,
1373      NULL, NULL);
1374
1375  info_bubble_ = new ContentSettingBubbleGtk(
1376      sender, this,
1377      ContentSettingBubbleModel::CreateContentSettingBubbleModel(
1378          tab_contents, profile_, content_settings_type),
1379      profile_, tab_contents);
1380  return TRUE;
1381}
1382
1383gboolean LocationBarViewGtk::ContentSettingImageViewGtk::OnExpose(
1384    GtkWidget* sender, GdkEventExpose* event) {
1385  if (!(animation_.IsShowing() || animation_.IsClosing()))
1386    return FALSE;
1387
1388  const int height = sender->allocation.height;
1389
1390  cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window));
1391  gdk_cairo_rectangle(cr, &event->area);
1392  cairo_clip(cr);
1393
1394  cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
1395
1396  cairo_pattern_add_color_stop_rgb(pattern, 0.0,
1397                                   kContentSettingTopColor[0],
1398                                   kContentSettingTopColor[1],
1399                                   kContentSettingTopColor[2]);
1400  cairo_pattern_add_color_stop_rgb(pattern, 1.0,
1401                                   kContentSettingBottomColor[0],
1402                                   kContentSettingBottomColor[1],
1403                                   kContentSettingBottomColor[2]);
1404  cairo_set_source(cr, pattern);
1405  cairo_paint(cr);
1406  cairo_pattern_destroy(pattern);
1407  cairo_destroy(cr);
1408
1409  return FALSE;
1410}
1411
1412void LocationBarViewGtk::ContentSettingImageViewGtk::InfoBubbleClosing(
1413    InfoBubbleGtk* info_bubble,
1414    bool closed_by_escape) {
1415  info_bubble_ = NULL;
1416}
1417
1418////////////////////////////////////////////////////////////////////////////////
1419// LocationBarViewGtk::PageActionViewGtk
1420
1421LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
1422    LocationBarViewGtk* owner, Profile* profile,
1423    ExtensionAction* page_action)
1424    : owner_(NULL),
1425      profile_(profile),
1426      page_action_(page_action),
1427      last_icon_pixbuf_(NULL),
1428      tracker_(this),
1429      preview_enabled_(false) {
1430  event_box_.Own(gtk_event_box_new());
1431  gtk_widget_set_size_request(event_box_.get(),
1432                              Extension::kPageActionIconMaxSize,
1433                              Extension::kPageActionIconMaxSize);
1434
1435  // Make the event box not visible so it does not paint a background.
1436  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
1437  g_signal_connect(event_box_.get(), "button-press-event",
1438                   G_CALLBACK(&OnButtonPressedThunk), this);
1439  g_signal_connect_after(event_box_.get(), "expose-event",
1440                         G_CALLBACK(OnExposeEventThunk), this);
1441
1442  image_.Own(gtk_image_new());
1443  gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get());
1444
1445  const Extension* extension = profile->GetExtensionService()->
1446      GetExtensionById(page_action->extension_id(), false);
1447  DCHECK(extension);
1448
1449  // Load all the icons declared in the manifest. This is the contents of the
1450  // icons array, plus the default_icon property, if any.
1451  std::vector<std::string> icon_paths(*page_action->icon_paths());
1452  if (!page_action_->default_icon_path().empty())
1453    icon_paths.push_back(page_action_->default_icon_path());
1454
1455  for (std::vector<std::string>::iterator iter = icon_paths.begin();
1456       iter != icon_paths.end(); ++iter) {
1457    tracker_.LoadImage(extension, extension->GetResource(*iter),
1458                       gfx::Size(Extension::kPageActionIconMaxSize,
1459                                 Extension::kPageActionIconMaxSize),
1460                       ImageLoadingTracker::DONT_CACHE);
1461  }
1462
1463  // We set the owner last of all so that we can determine whether we are in
1464  // the process of initializing this class or not.
1465  owner_ = owner;
1466}
1467
1468LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
1469  image_.Destroy();
1470  event_box_.Destroy();
1471  for (PixbufMap::iterator iter = pixbufs_.begin(); iter != pixbufs_.end();
1472       ++iter) {
1473    g_object_unref(iter->second);
1474  }
1475  if (last_icon_pixbuf_)
1476    g_object_unref(last_icon_pixbuf_);
1477}
1478
1479void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
1480    TabContents* contents, const GURL& url) {
1481  // Save this off so we can pass it back to the extension when the action gets
1482  // executed. See PageActionImageView::OnMousePressed.
1483  current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1;
1484  current_url_ = url;
1485
1486  bool visible = contents &&
1487      (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_));
1488  if (visible) {
1489    // Set the tooltip.
1490    gtk_widget_set_tooltip_text(event_box_.get(),
1491        page_action_->GetTitle(current_tab_id_).c_str());
1492
1493    // Set the image.
1494    // It can come from three places. In descending order of priority:
1495    // - The developer can set it dynamically by path or bitmap. It will be in
1496    //   page_action_->GetIcon().
1497    // - The developer can set it dyanmically by index. It will be in
1498    //   page_action_->GetIconIndex().
1499    // - It can be set in the manifest by path. It will be in page_action_->
1500    //   default_icon_path().
1501
1502    // First look for a dynamically set bitmap.
1503    SkBitmap icon = page_action_->GetIcon(current_tab_id_);
1504    GdkPixbuf* pixbuf = NULL;
1505    if (!icon.isNull()) {
1506      if (icon.pixelRef() != last_icon_skbitmap_.pixelRef()) {
1507        if (last_icon_pixbuf_)
1508          g_object_unref(last_icon_pixbuf_);
1509        last_icon_skbitmap_ = icon;
1510        last_icon_pixbuf_ = gfx::GdkPixbufFromSkBitmap(&icon);
1511      }
1512      DCHECK(last_icon_pixbuf_);
1513      pixbuf = last_icon_pixbuf_;
1514    } else {
1515      // Otherwise look for a dynamically set index, or fall back to the
1516      // default path.
1517      int icon_index = page_action_->GetIconIndex(current_tab_id_);
1518      std::string icon_path = (icon_index < 0) ?
1519          page_action_->default_icon_path() :
1520          page_action_->icon_paths()->at(icon_index);
1521      if (!icon_path.empty()) {
1522        PixbufMap::iterator iter = pixbufs_.find(icon_path);
1523        if (iter != pixbufs_.end())
1524          pixbuf = iter->second;
1525      }
1526    }
1527    // The pixbuf might not be loaded yet.
1528    if (pixbuf)
1529      gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
1530  }
1531
1532  bool old_visible = IsVisible();
1533  if (visible)
1534    gtk_widget_show_all(event_box_.get());
1535  else
1536    gtk_widget_hide_all(event_box_.get());
1537
1538  if (visible != old_visible) {
1539    NotificationService::current()->Notify(
1540        NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
1541        Source<ExtensionAction>(page_action_),
1542        Details<TabContents>(contents));
1543  }
1544}
1545
1546void LocationBarViewGtk::PageActionViewGtk::OnImageLoaded(
1547    SkBitmap* image, const ExtensionResource& resource, int index) {
1548  // We loaded icons()->size() icons, plus one extra if the page action had
1549  // a default icon.
1550  int total_icons = static_cast<int>(page_action_->icon_paths()->size());
1551  if (!page_action_->default_icon_path().empty())
1552    total_icons++;
1553  DCHECK(index < total_icons);
1554
1555  // Map the index of the loaded image back to its name. If we ever get an
1556  // index greater than the number of icons, it must be the default icon.
1557  if (image) {
1558    GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(image);
1559    if (index < static_cast<int>(page_action_->icon_paths()->size()))
1560      pixbufs_[page_action_->icon_paths()->at(index)] = pixbuf;
1561    else
1562      pixbufs_[page_action_->default_icon_path()] = pixbuf;
1563  }
1564
1565  // If we have no owner, that means this class is still being constructed and
1566  // we should not UpdatePageActions, since it leads to the PageActions being
1567  // destroyed again and new ones recreated (causing an infinite loop).
1568  if (owner_)
1569    owner_->UpdatePageActions();
1570}
1571
1572void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
1573  GdkEventButton event = {};
1574  event.button = 1;
1575  OnButtonPressed(widget(), &event);
1576}
1577
1578void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
1579    ExtensionAction* action) {
1580  ShowPopup(true);
1581}
1582
1583bool LocationBarViewGtk::PageActionViewGtk::ShowPopup(bool devtools) {
1584  if (!page_action_->HasPopup(current_tab_id_))
1585    return false;
1586
1587  ExtensionPopupGtk::Show(
1588      page_action_->GetPopupUrl(current_tab_id_),
1589      owner_->browser_,
1590      event_box_.get(),
1591      devtools);
1592  return true;
1593}
1594
1595gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed(
1596    GtkWidget* sender,
1597    GdkEventButton* event) {
1598  if (event->button != 3) {
1599    if (!ShowPopup(false)) {
1600      ExtensionService* service = profile_->GetExtensionService();
1601      service->browser_event_router()->PageActionExecuted(
1602          profile_,
1603          page_action_->extension_id(),
1604          page_action_->id(),
1605          current_tab_id_,
1606          current_url_.spec(),
1607          event->button);
1608    }
1609  } else {
1610    const Extension* extension = profile_->GetExtensionService()->
1611        GetExtensionById(page_action()->extension_id(), false);
1612
1613    if (extension->ShowConfigureContextMenus()) {
1614      context_menu_model_ =
1615          new ExtensionContextMenuModel(extension, owner_->browser_, this);
1616      context_menu_.reset(
1617          new MenuGtk(NULL, context_menu_model_.get()));
1618      context_menu_->PopupForWidget(sender, event->button, event->time);
1619    }
1620  }
1621
1622  return TRUE;
1623}
1624
1625gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
1626    GtkWidget* widget, GdkEventExpose* event) {
1627  TabContents* contents = owner_->GetTabContents();
1628  if (!contents)
1629    return FALSE;
1630
1631  int tab_id = ExtensionTabUtil::GetTabId(contents);
1632  if (tab_id < 0)
1633    return FALSE;
1634
1635  std::string badge_text = page_action_->GetBadgeText(tab_id);
1636  if (badge_text.empty())
1637    return FALSE;
1638
1639  gfx::CanvasSkiaPaint canvas(event, false);
1640  gfx::Rect bounding_rect(widget->allocation);
1641  page_action_->PaintBadge(&canvas, bounding_rect, tab_id);
1642  return FALSE;
1643}
1644