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_, >k_util::kGdkBlack);
796 gtk_util::SetLabelColor(tab_to_search_partial_label_, >k_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