• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
66 namespace {
67 
68 // We are positioned with a little bit of extra space that we don't use now.
69 const int kTopMargin = 1;
70 const int kBottomMargin = 1;
71 const int kLeftMargin = 1;
72 const int kRightMargin = 1;
73 // We draw a border on the top and bottom (but not on left or right).
74 const int kBorderThickness = 1;
75 
76 // Left margin of first run bubble.
77 const int kFirstRunBubbleLeftMargin = 8;
78 // Extra vertical spacing for first run bubble.
79 const int kFirstRunBubbleTopMargin = 5;
80 // Spacing needed to align the bubble with the left side of the omnibox.
81 const 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.
87 const int kHboxBorder = 2;
88 
89 // Padding between the elements in the bar.
90 const int kInnerPadding = 2;
91 
92 // Padding between the right of the star and the edge of the URL entry.
93 const int kStarRightPadding = 2;
94 
95 // Colors used to draw the EV certificate rounded bubble.
96 const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00);
97 const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef);
98 const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90);
99 
100 // Colors used to draw the Tab to Search rounded bubble.
101 const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa);
102 const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7);
103 
104 // Use weak gray for showing search and keyword hint text.
105 const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75);
106 
107 // Size of the rounding of the "Search site for:" box.
108 const 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.
112 const int kContentSettingImageDisplayTime = 3200;
113 // The time, in ms, of the animation (open and close).
114 const int kContentSettingImageAnimationTime = 150;
115 
116 // Color of border of content setting area (icon/label).
117 const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66);
118 // Colors for the background gradient.
119 const double kContentSettingTopColor[] = { 0xff / 255.0,
120                                            0xf8 / 255.0,
121                                            0xd4 / 255.0 };
122 const 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.
CountVisibleWidgets(GtkWidget * widget,gpointer count)128 void 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
139 const GdkColor LocationBarViewGtk::kBackgroundColor =
140     GDK_COLOR_RGB(255, 255, 255);
141 
LocationBarViewGtk(Browser * browser)142 LocationBarViewGtk::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 
~LocationBarViewGtk()174 LocationBarViewGtk::~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 
Init(bool popup_window_mode)182 void 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 
BuildSiteTypeArea()333 void 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 
SetSiteTypeDragSource()388 void 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 
SetProfile(Profile * profile)407 void LocationBarViewGtk::SetProfile(Profile* profile) {
408   profile_ = profile;
409 }
410 
GetTabContents() const411 TabContents* LocationBarViewGtk::GetTabContents() const {
412   return browser_->GetSelectedTabContents();
413 }
414 
SetPreviewEnabledPageAction(ExtensionAction * page_action,bool preview_enabled)415 void 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 
GetPageActionWidget(ExtensionAction * page_action)431 GtkWidget* 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 
Update(const TabContents * contents)444 void 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 
OnAutocompleteAccept(const GURL & url,WindowOpenDisposition disposition,PageTransition::Type transition,const GURL & alternate_nav_url)460 void 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 
OnChanged()492 void 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 
OnSelectionBoundsChanged()509 void LocationBarViewGtk::OnSelectionBoundsChanged() {
510   NOTIMPLEMENTED();
511 }
512 
CreateStarButton()513 void 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 
OnInputInProgress(bool in_progress)533 void 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 
OnKillFocus()543 void LocationBarViewGtk::OnKillFocus() {
544 }
545 
OnSetFocus()546 void 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 
GetFavicon() const560 SkBitmap LocationBarViewGtk::GetFavicon() const {
561   return GetTabContents()->GetFavicon();
562 }
563 
GetTitle() const564 string16 LocationBarViewGtk::GetTitle() const {
565   return GetTabContents()->GetTitle();
566 }
567 
GetInstant()568 InstantController* LocationBarViewGtk::GetInstant() {
569   return browser_->instant();
570 }
571 
GetTabContentsWrapper() const572 TabContentsWrapper* LocationBarViewGtk::GetTabContentsWrapper() const {
573   return browser_->GetSelectedTabContentsWrapper();
574 }
575 
ShowFirstRunBubble(FirstRun::BubbleType bubble_type)576 void 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 
SetSuggestedText(const string16 & text,InstantCompleteBehavior behavior)584 void LocationBarViewGtk::SetSuggestedText(const string16& text,
585                                           InstantCompleteBehavior behavior) {
586   location_entry_->model()->SetSuggestedText(text, behavior);
587 }
588 
GetInputString() const589 std::wstring LocationBarViewGtk::GetInputString() const {
590   return location_input_;
591 }
592 
GetWindowOpenDisposition() const593 WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const {
594   return disposition_;
595 }
596 
GetPageTransition() const597 PageTransition::Type LocationBarViewGtk::GetPageTransition() const {
598   return transition_;
599 }
600 
AcceptInput()601 void LocationBarViewGtk::AcceptInput() {
602   location_entry_->model()->AcceptInput(CURRENT_TAB, false);
603 }
604 
FocusLocation(bool select_all)605 void LocationBarViewGtk::FocusLocation(bool select_all) {
606   location_entry_->SetFocus();
607   if (select_all)
608     location_entry_->SelectAll(true);
609 }
610 
FocusSearch()611 void LocationBarViewGtk::FocusSearch() {
612   location_entry_->SetFocus();
613   location_entry_->SetForcedQuery();
614 }
615 
UpdateContentSettingsIcons()616 void 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 
UpdatePageActions()635 void 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 
InvalidatePageActions()682 void 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 
SaveStateToContents(TabContents * contents)693 void LocationBarViewGtk::SaveStateToContents(TabContents* contents) {
694   location_entry_->SaveStateToTab(contents);
695 }
696 
Revert()697 void LocationBarViewGtk::Revert() {
698   location_entry_->RevertAll();
699 }
700 
location_entry() const701 const AutocompleteEditView* LocationBarViewGtk::location_entry() const {
702   return location_entry_.get();
703 }
704 
location_entry()705 AutocompleteEditView* LocationBarViewGtk::location_entry() {
706   return location_entry_.get();
707 }
708 
GetLocationBarForTesting()709 LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() {
710   return this;
711 }
712 
PageActionCount()713 int LocationBarViewGtk::PageActionCount() {
714   return page_action_views_.size();
715 }
716 
PageActionVisibleCount()717 int LocationBarViewGtk::PageActionVisibleCount() {
718   int count = 0;
719   gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()),
720                         CountVisibleWidgets, &count);
721   return count;
722 }
723 
GetPageAction(size_t index)724 ExtensionAction* 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 
GetVisiblePageAction(size_t index)733 ExtensionAction* 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 
TestPageActionPressed(size_t index)746 void 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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)755 void 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 
HandleExpose(GtkWidget * widget,GdkEventExpose * event)830 gboolean 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 
UpdateSiteTypeArea()854 void 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 
UpdateEVCertificateLabelSize()915 void 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 
SetKeywordLabel(const string16 & keyword)941 void 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 
SetKeywordHintLabel(const string16 & keyword)984 void 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 
ShowFirstRunBubbleInternal(FirstRun::BubbleType bubble_type)1019 void 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 
OnIconReleased(GtkWidget * sender,GdkEventButton * event)1030 gboolean 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 
OnIconDragData(GtkWidget * sender,GdkDragContext * context,GtkSelectionData * data,guint info,guint time)1072 void 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 
OnIconDragBegin(GtkWidget * sender,GdkDragContext * context)1082 void 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 
OnIconDragEnd(GtkWidget * sender,GdkDragContext * context)1094 void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender,
1095                                        GdkDragContext* context) {
1096   DCHECK(drag_icon_);
1097   gtk_widget_destroy(drag_icon_);
1098   drag_icon_ = NULL;
1099 }
1100 
OnHboxSizeAllocate(GtkWidget * sender,GtkAllocation * allocation)1101 void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender,
1102                                             GtkAllocation* allocation) {
1103   if (hbox_width_ != allocation->width) {
1104     hbox_width_ = allocation->width;
1105     UpdateEVCertificateLabelSize();
1106   }
1107 }
1108 
OnEntryBoxSizeAllocate(GtkWidget * sender,GtkAllocation * allocation)1109 void 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 
OnStarButtonPress(GtkWidget * widget,GdkEventButton * event)1117 gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget,
1118                                                GdkEventButton* event) {
1119   browser_->ExecuteCommand(IDC_BOOKMARK_PAGE);
1120   return FALSE;
1121 }
1122 
ShowStarBubble(const GURL & url,bool newly_bookmarked)1123 void 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 
SetStarred(bool starred)1131 void LocationBarViewGtk::SetStarred(bool starred) {
1132   if (starred == starred_)
1133     return;
1134 
1135   starred_ = starred;
1136   UpdateStarIcon();
1137 }
1138 
UpdateStarIcon()1139 void 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 
ShouldOnlyShowLocation()1155 bool LocationBarViewGtk::ShouldOnlyShowLocation() {
1156   return browser_->type() != Browser::TYPE_NORMAL;
1157 }
1158 
AdjustChildrenVisibility()1159 void 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
ContentSettingImageViewGtk(ContentSettingsType content_type,const LocationBarViewGtk * parent,Profile * profile)1220 LocationBarViewGtk::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 
~ContentSettingImageViewGtk()1262 LocationBarViewGtk::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 
UpdateFromTabContents(TabContents * tab_contents)1272 void 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 
StartAnimating()1312 void 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 
CloseAnimation()1329 void LocationBarViewGtk::ContentSettingImageViewGtk::CloseAnimation() {
1330   animation_.Hide();
1331 }
1332 
AnimationProgressed(const ui::Animation * animation)1333 void 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 
AnimationEnded(const ui::Animation * animation)1341 void 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 
AnimationCanceled(const ui::Animation * animation)1355 void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationCanceled(
1356     const ui::Animation* animation) {
1357 }
1358 
OnButtonPressed(GtkWidget * sender,GdkEvent * event)1359 gboolean 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 
OnExpose(GtkWidget * sender,GdkEventExpose * event)1383 gboolean 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 
InfoBubbleClosing(InfoBubbleGtk * info_bubble,bool closed_by_escape)1412 void LocationBarViewGtk::ContentSettingImageViewGtk::InfoBubbleClosing(
1413     InfoBubbleGtk* info_bubble,
1414     bool closed_by_escape) {
1415   info_bubble_ = NULL;
1416 }
1417 
1418 ////////////////////////////////////////////////////////////////////////////////
1419 // LocationBarViewGtk::PageActionViewGtk
1420 
PageActionViewGtk(LocationBarViewGtk * owner,Profile * profile,ExtensionAction * page_action)1421 LocationBarViewGtk::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 
~PageActionViewGtk()1468 LocationBarViewGtk::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 
UpdateVisibility(TabContents * contents,const GURL & url)1479 void 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 
OnImageLoaded(SkBitmap * image,const ExtensionResource & resource,int index)1546 void 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 
TestActivatePageAction()1572 void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() {
1573   GdkEventButton event = {};
1574   event.button = 1;
1575   OnButtonPressed(widget(), &event);
1576 }
1577 
InspectPopup(ExtensionAction * action)1578 void LocationBarViewGtk::PageActionViewGtk::InspectPopup(
1579     ExtensionAction* action) {
1580   ShowPopup(true);
1581 }
1582 
ShowPopup(bool devtools)1583 bool 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 
OnButtonPressed(GtkWidget * sender,GdkEventButton * event)1595 gboolean 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 
OnExposeEvent(GtkWidget * widget,GdkEventExpose * event)1625 gboolean 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