• 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/browser_toolbar_gtk.h"
6 
7 #include <X11/XF86keysym.h>
8 #include <gdk/gdkkeysyms.h>
9 #include <gtk/gtk.h>
10 
11 #include "base/base_paths.h"
12 #include "base/command_line.h"
13 #include "base/i18n/rtl.h"
14 #include "base/logging.h"
15 #include "base/memory/singleton.h"
16 #include "base/path_service.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #include "chrome/browser/metrics/user_metrics.h"
19 #include "chrome/browser/net/url_fixer_upper.h"
20 #include "chrome/browser/prefs/pref_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/themes/theme_service.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/gtk/accelerators_gtk.h"
25 #include "chrome/browser/ui/gtk/back_forward_button_gtk.h"
26 #include "chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h"
27 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
28 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
29 #include "chrome/browser/ui/gtk/custom_button.h"
30 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
31 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
32 #include "chrome/browser/ui/gtk/gtk_util.h"
33 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
34 #include "chrome/browser/ui/gtk/reload_button_gtk.h"
35 #include "chrome/browser/ui/gtk/rounded_window.h"
36 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
37 #include "chrome/browser/ui/gtk/view_id_util.h"
38 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
39 #include "chrome/browser/upgrade_detector.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/browser/tab_contents/tab_contents.h"
43 #include "content/common/notification_details.h"
44 #include "content/common/notification_service.h"
45 #include "content/common/notification_type.h"
46 #include "grit/chromium_strings.h"
47 #include "grit/generated_resources.h"
48 #include "grit/theme_resources.h"
49 #include "ui/base/dragdrop/gtk_dnd_util.h"
50 #include "ui/base/l10n/l10n_util.h"
51 #include "ui/base/models/accelerator_gtk.h"
52 #include "ui/gfx/canvas_skia_paint.h"
53 #include "ui/gfx/gtk_util.h"
54 #include "ui/gfx/skbitmap_operations.h"
55 
56 namespace {
57 
58 // Padding on left and right of the left toolbar buttons (back, forward, reload,
59 // etc.).
60 const int kToolbarLeftAreaPadding = 4;
61 
62 // Height of the toolbar in pixels (not counting padding).
63 const int kToolbarHeight = 29;
64 
65 // Padding within the toolbar above the buttons and location bar.
66 const int kTopBottomPadding = 3;
67 
68 // Height of the toolbar in pixels when we only show the location bar.
69 const int kToolbarHeightLocationBarOnly = kToolbarHeight - 2;
70 
71 // Interior spacing between toolbar widgets.
72 const int kToolbarWidgetSpacing = 1;
73 
74 // Amount of rounding on top corners of toolbar. Only used in Gtk theme mode.
75 const int kToolbarCornerSize = 3;
76 
SetWidgetHeightRequest(GtkWidget * widget,gpointer user_data)77 void SetWidgetHeightRequest(GtkWidget* widget, gpointer user_data) {
78   gtk_widget_set_size_request(widget, -1, GPOINTER_TO_INT(user_data));
79 }
80 
81 }  // namespace
82 
83 // BrowserToolbarGtk, public ---------------------------------------------------
84 
BrowserToolbarGtk(Browser * browser,BrowserWindowGtk * window)85 BrowserToolbarGtk::BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window)
86     : toolbar_(NULL),
87       location_bar_(new LocationBarViewGtk(browser)),
88       model_(browser->toolbar_model()),
89       wrench_menu_model_(this, browser),
90       browser_(browser),
91       window_(window),
92       profile_(NULL) {
93   browser_->command_updater()->AddCommandObserver(IDC_BACK, this);
94   browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this);
95   browser_->command_updater()->AddCommandObserver(IDC_HOME, this);
96   browser_->command_updater()->AddCommandObserver(IDC_BOOKMARK_PAGE, this);
97 
98   registrar_.Add(this,
99                  NotificationType::BROWSER_THEME_CHANGED,
100                  NotificationService::AllSources());
101   registrar_.Add(this,
102                  NotificationType::UPGRADE_RECOMMENDED,
103                  NotificationService::AllSources());
104 }
105 
~BrowserToolbarGtk()106 BrowserToolbarGtk::~BrowserToolbarGtk() {
107   browser_->command_updater()->RemoveCommandObserver(IDC_BACK, this);
108   browser_->command_updater()->RemoveCommandObserver(IDC_FORWARD, this);
109   browser_->command_updater()->RemoveCommandObserver(IDC_HOME, this);
110   browser_->command_updater()->RemoveCommandObserver(IDC_BOOKMARK_PAGE, this);
111 
112   offscreen_entry_.Destroy();
113 
114   wrench_menu_.reset();
115 }
116 
Init(Profile * profile,GtkWindow * top_level_window)117 void BrowserToolbarGtk::Init(Profile* profile,
118                              GtkWindow* top_level_window) {
119   // Make sure to tell the location bar the profile before calling its Init.
120   SetProfile(profile);
121 
122   theme_service_ = GtkThemeService::GetFrom(profile);
123   offscreen_entry_.Own(gtk_entry_new());
124 
125   show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this);
126   home_page_.Init(prefs::kHomePage, profile->GetPrefs(), this);
127   home_page_is_new_tab_page_.Init(prefs::kHomePageIsNewTabPage,
128                                   profile->GetPrefs(), this);
129 
130   event_box_ = gtk_event_box_new();
131   // Make the event box transparent so themes can use transparent toolbar
132   // backgrounds.
133   if (!theme_service_->UseGtkTheme())
134     gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), FALSE);
135 
136   toolbar_ = gtk_hbox_new(FALSE, 0);
137   alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
138   UpdateForBookmarkBarVisibility(false);
139   g_signal_connect(alignment_, "expose-event",
140                    G_CALLBACK(&OnAlignmentExposeThunk), this);
141   gtk_container_add(GTK_CONTAINER(event_box_), alignment_);
142   gtk_container_add(GTK_CONTAINER(alignment_), toolbar_);
143 
144   toolbar_left_ = gtk_hbox_new(FALSE, kToolbarWidgetSpacing);
145 
146   back_.reset(new BackForwardButtonGtk(browser_, false));
147   g_signal_connect(back_->widget(), "clicked",
148                    G_CALLBACK(OnButtonClickThunk), this);
149   gtk_box_pack_start(GTK_BOX(toolbar_left_), back_->widget(), FALSE,
150                      FALSE, 0);
151 
152   forward_.reset(new BackForwardButtonGtk(browser_, true));
153   g_signal_connect(forward_->widget(), "clicked",
154                    G_CALLBACK(OnButtonClickThunk), this);
155   gtk_box_pack_start(GTK_BOX(toolbar_left_), forward_->widget(), FALSE,
156                      FALSE, 0);
157 
158   reload_.reset(new ReloadButtonGtk(location_bar_.get(), browser_));
159   gtk_box_pack_start(GTK_BOX(toolbar_left_), reload_->widget(), FALSE, FALSE,
160                      0);
161 
162   home_.reset(new CustomDrawButton(GtkThemeService::GetFrom(profile_),
163       IDR_HOME, IDR_HOME_P, IDR_HOME_H, 0, GTK_STOCK_HOME,
164       GTK_ICON_SIZE_SMALL_TOOLBAR));
165   gtk_widget_set_tooltip_text(home_->widget(),
166       l10n_util::GetStringUTF8(IDS_TOOLTIP_HOME).c_str());
167   g_signal_connect(home_->widget(), "clicked",
168                    G_CALLBACK(OnButtonClickThunk), this);
169   gtk_box_pack_start(GTK_BOX(toolbar_left_), home_->widget(), FALSE, FALSE,
170                      kToolbarWidgetSpacing);
171   gtk_util::SetButtonTriggersNavigation(home_->widget());
172 
173   gtk_box_pack_start(GTK_BOX(toolbar_), toolbar_left_, FALSE, FALSE,
174                      kToolbarLeftAreaPadding);
175 
176   location_hbox_ = gtk_hbox_new(FALSE, 0);
177   location_bar_->Init(ShouldOnlyShowLocation());
178   gtk_box_pack_start(GTK_BOX(location_hbox_), location_bar_->widget(), TRUE,
179                      TRUE, 0);
180 
181   g_signal_connect(location_hbox_, "expose-event",
182                    G_CALLBACK(OnLocationHboxExposeThunk), this);
183   gtk_box_pack_start(GTK_BOX(toolbar_), location_hbox_, TRUE, TRUE,
184       ShouldOnlyShowLocation() ? 1 : 0);
185 
186   if (!ShouldOnlyShowLocation()) {
187     actions_toolbar_.reset(new BrowserActionsToolbarGtk(browser_));
188     gtk_box_pack_start(GTK_BOX(toolbar_), actions_toolbar_->widget(),
189                        FALSE, FALSE, 0);
190   }
191 
192   wrench_menu_image_ = gtk_image_new_from_pixbuf(
193       theme_service_->GetRTLEnabledPixbufNamed(IDR_TOOLS));
194   wrench_menu_button_.reset(new CustomDrawButton(
195       GtkThemeService::GetFrom(profile_),
196       IDR_TOOLS, IDR_TOOLS_P, IDR_TOOLS_H, 0,
197       wrench_menu_image_));
198   GtkWidget* wrench_button = wrench_menu_button_->widget();
199 
200   gtk_widget_set_tooltip_text(
201       wrench_button,
202       l10n_util::GetStringFUTF8(IDS_APPMENU_TOOLTIP,
203           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str());
204   g_signal_connect(wrench_button, "button-press-event",
205                    G_CALLBACK(OnMenuButtonPressEventThunk), this);
206   GTK_WIDGET_UNSET_FLAGS(wrench_button, GTK_CAN_FOCUS);
207 
208   // Put the wrench button in a box so that we can paint the update notification
209   // over it.
210   GtkWidget* wrench_box = gtk_alignment_new(0, 0, 1, 1);
211   g_signal_connect_after(wrench_box, "expose-event",
212                          G_CALLBACK(OnWrenchMenuButtonExposeThunk), this);
213   gtk_container_add(GTK_CONTAINER(wrench_box), wrench_button);
214   gtk_box_pack_start(GTK_BOX(toolbar_), wrench_box, FALSE, FALSE, 4);
215 
216   wrench_menu_.reset(new MenuGtk(this, &wrench_menu_model_));
217   registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
218                  Source<Profile>(browser_->profile()));
219 
220   if (ShouldOnlyShowLocation()) {
221     gtk_widget_show(event_box_);
222     gtk_widget_show(alignment_);
223     gtk_widget_show(toolbar_);
224     gtk_widget_show_all(location_hbox_);
225     gtk_widget_hide(reload_->widget());
226   } else {
227     gtk_widget_show_all(event_box_);
228     if (actions_toolbar_->button_count() == 0)
229       gtk_widget_hide(actions_toolbar_->widget());
230   }
231   // Initialize pref-dependent UI state.
232   NotifyPrefChanged(NULL);
233 
234   // Because the above does a recursive show all on all widgets we need to
235   // update the icon visibility to hide them.
236   location_bar_->UpdateContentSettingsIcons();
237 
238   SetViewIDs();
239   theme_service_->InitThemesFor(this);
240 }
241 
SetViewIDs()242 void BrowserToolbarGtk::SetViewIDs() {
243   ViewIDUtil::SetID(widget(), VIEW_ID_TOOLBAR);
244   ViewIDUtil::SetID(back_->widget(), VIEW_ID_BACK_BUTTON);
245   ViewIDUtil::SetID(forward_->widget(), VIEW_ID_FORWARD_BUTTON);
246   ViewIDUtil::SetID(reload_->widget(), VIEW_ID_RELOAD_BUTTON);
247   ViewIDUtil::SetID(home_->widget(), VIEW_ID_HOME_BUTTON);
248   ViewIDUtil::SetID(location_bar_->widget(), VIEW_ID_LOCATION_BAR);
249   ViewIDUtil::SetID(wrench_menu_button_->widget(), VIEW_ID_APP_MENU);
250 }
251 
Show()252 void BrowserToolbarGtk::Show() {
253   gtk_widget_show(toolbar_);
254 }
255 
Hide()256 void BrowserToolbarGtk::Hide() {
257   gtk_widget_hide(toolbar_);
258 }
259 
GetLocationBar() const260 LocationBar* BrowserToolbarGtk::GetLocationBar() const {
261   return location_bar_.get();
262 }
263 
UpdateForBookmarkBarVisibility(bool show_bottom_padding)264 void BrowserToolbarGtk::UpdateForBookmarkBarVisibility(
265     bool show_bottom_padding) {
266   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_),
267       ShouldOnlyShowLocation() ? 0 : kTopBottomPadding,
268       !show_bottom_padding || ShouldOnlyShowLocation() ? 0 : kTopBottomPadding,
269       0, 0);
270 }
271 
ShowAppMenu()272 void BrowserToolbarGtk::ShowAppMenu() {
273   wrench_menu_->Cancel();
274   wrench_menu_button_->SetPaintOverride(GTK_STATE_ACTIVE);
275   UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu"));
276   wrench_menu_->PopupAsFromKeyEvent(wrench_menu_button_->widget());
277 }
278 
279 // CommandUpdater::CommandObserver ---------------------------------------------
280 
EnabledStateChangedForCommand(int id,bool enabled)281 void BrowserToolbarGtk::EnabledStateChangedForCommand(int id, bool enabled) {
282   GtkWidget* widget = NULL;
283   switch (id) {
284     case IDC_BACK:
285       widget = back_->widget();
286       break;
287     case IDC_FORWARD:
288       widget = forward_->widget();
289       break;
290     case IDC_HOME:
291       if (home_.get())
292         widget = home_->widget();
293       break;
294   }
295   if (widget) {
296     if (!enabled && GTK_WIDGET_STATE(widget) == GTK_STATE_PRELIGHT) {
297       // If we're disabling a widget, GTK will helpfully restore it to its
298       // previous state when we re-enable it, even if that previous state
299       // is the prelight.  This looks bad.  See the bug for a simple repro.
300       // http://code.google.com/p/chromium/issues/detail?id=13729
301       gtk_widget_set_state(widget, GTK_STATE_NORMAL);
302     }
303     gtk_widget_set_sensitive(widget, enabled);
304   }
305 }
306 
307 // MenuGtk::Delegate -----------------------------------------------------------
308 
StoppedShowing()309 void BrowserToolbarGtk::StoppedShowing() {
310   // Without these calls, the hover state can get stuck since the leave-notify
311   // event is not sent when clicking a button brings up the menu.
312   gtk_chrome_button_set_hover_state(
313       GTK_CHROME_BUTTON(wrench_menu_button_->widget()), 0.0);
314   wrench_menu_button_->UnsetPaintOverride();
315 }
316 
GetIconSetForId(int idr)317 GtkIconSet* BrowserToolbarGtk::GetIconSetForId(int idr) {
318   return theme_service_->GetIconSetForId(idr);
319 }
320 
321 // Always show images because we desire that some icons always show
322 // regardless of the system setting.
AlwaysShowIconForCmd(int command_id) const323 bool BrowserToolbarGtk::AlwaysShowIconForCmd(int command_id) const {
324   return command_id == IDC_UPGRADE_DIALOG;
325 }
326 
327 // ui::AcceleratorProvider
328 
GetAcceleratorForCommandId(int id,ui::Accelerator * accelerator)329 bool BrowserToolbarGtk::GetAcceleratorForCommandId(
330     int id,
331     ui::Accelerator* accelerator) {
332   const ui::AcceleratorGtk* accelerator_gtk =
333       AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand(id);
334   if (accelerator_gtk)
335     *accelerator = *accelerator_gtk;
336   return !!accelerator_gtk;
337 }
338 
339 // NotificationObserver --------------------------------------------------------
340 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)341 void BrowserToolbarGtk::Observe(NotificationType type,
342                                 const NotificationSource& source,
343                                 const NotificationDetails& details) {
344   if (type == NotificationType::PREF_CHANGED) {
345     NotifyPrefChanged(Details<std::string>(details).ptr());
346   } else if (type == NotificationType::BROWSER_THEME_CHANGED) {
347     // Update the spacing around the menu buttons
348     bool use_gtk = theme_service_->UseGtkTheme();
349     int border = use_gtk ? 0 : 2;
350     gtk_container_set_border_width(
351         GTK_CONTAINER(wrench_menu_button_->widget()), border);
352 
353     // Force the height of the toolbar so we get the right amount of padding
354     // above and below the location bar. We always force the size of the widgets
355     // to either side of the location box, but we only force the location box
356     // size in chrome-theme mode because that's the only time we try to control
357     // the font size.
358     int toolbar_height = ShouldOnlyShowLocation() ?
359                          kToolbarHeightLocationBarOnly : kToolbarHeight;
360     gtk_container_foreach(GTK_CONTAINER(toolbar_), SetWidgetHeightRequest,
361                           GINT_TO_POINTER(toolbar_height));
362     gtk_widget_set_size_request(location_hbox_, -1,
363                                 use_gtk ? -1 : toolbar_height);
364 
365     // When using the GTK+ theme, we need to have the event box be visible so
366     // buttons don't get a halo color from the background.  When using Chromium
367     // themes, we want to let the background show through the toolbar.
368     gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), use_gtk);
369 
370     if (use_gtk) {
371       // We need to manually update the icon if we are in GTK mode. (Note that
372       // we set the initial value in Init()).
373       gtk_image_set_from_pixbuf(
374           GTK_IMAGE(wrench_menu_image_),
375           theme_service_->GetRTLEnabledPixbufNamed(IDR_TOOLS));
376     }
377 
378     UpdateRoundedness();
379   } else if (type == NotificationType::UPGRADE_RECOMMENDED) {
380     // Redraw the wrench menu to update the badge.
381     gtk_widget_queue_draw(wrench_menu_button_->widget());
382   } else if (type == NotificationType::ZOOM_LEVEL_CHANGED) {
383     // If our zoom level changed, we need to tell the menu to update its state,
384     // since the menu could still be open.
385     wrench_menu_->UpdateMenu();
386   } else {
387     NOTREACHED();
388   }
389 }
390 
391 // BrowserToolbarGtk, public ---------------------------------------------------
392 
SetProfile(Profile * profile)393 void BrowserToolbarGtk::SetProfile(Profile* profile) {
394   if (profile == profile_)
395     return;
396 
397   profile_ = profile;
398   location_bar_->SetProfile(profile);
399 }
400 
UpdateTabContents(TabContents * contents,bool should_restore_state)401 void BrowserToolbarGtk::UpdateTabContents(TabContents* contents,
402                                           bool should_restore_state) {
403   location_bar_->Update(should_restore_state ? contents : NULL);
404 
405   if (actions_toolbar_.get())
406     actions_toolbar_->Update();
407 }
408 
409 // BrowserToolbarGtk, private --------------------------------------------------
410 
SetUpDragForHomeButton(bool enable)411 void BrowserToolbarGtk::SetUpDragForHomeButton(bool enable) {
412   if (enable) {
413     gtk_drag_dest_set(home_->widget(), GTK_DEST_DEFAULT_ALL,
414                       NULL, 0, GDK_ACTION_COPY);
415     static const int targets[] = { ui::TEXT_PLAIN, ui::TEXT_URI_LIST, -1 };
416     ui::SetDestTargetList(home_->widget(), targets);
417 
418     drop_handler_.reset(new ui::GtkSignalRegistrar());
419     drop_handler_->Connect(home_->widget(), "drag-data-received",
420                            G_CALLBACK(OnDragDataReceivedThunk), this);
421   } else {
422     gtk_drag_dest_unset(home_->widget());
423     drop_handler_.reset(NULL);
424   }
425 }
426 
UpdateRoundedness()427 bool BrowserToolbarGtk::UpdateRoundedness() {
428   // We still round the corners if we are in chrome theme mode, but we do it by
429   // drawing theme resources rather than changing the physical shape of the
430   // widget.
431   bool should_be_rounded = theme_service_->UseGtkTheme() &&
432       window_->ShouldDrawContentDropShadow();
433 
434   if (should_be_rounded == gtk_util::IsActingAsRoundedWindow(alignment_))
435     return false;
436 
437   if (should_be_rounded) {
438     gtk_util::ActAsRoundedWindow(alignment_, GdkColor(), kToolbarCornerSize,
439                                  gtk_util::ROUNDED_TOP,
440                                  gtk_util::BORDER_NONE);
441   } else {
442     gtk_util::StopActingAsRoundedWindow(alignment_);
443   }
444 
445   return true;
446 }
447 
OnAlignmentExpose(GtkWidget * widget,GdkEventExpose * e)448 gboolean BrowserToolbarGtk::OnAlignmentExpose(GtkWidget* widget,
449                                               GdkEventExpose* e) {
450   // We may need to update the roundedness of the toolbar's top corners. In
451   // this case, don't draw; we'll be called again soon enough.
452   if (UpdateRoundedness())
453     return TRUE;
454 
455   // We don't need to render the toolbar image in GTK mode.
456   if (theme_service_->UseGtkTheme())
457     return FALSE;
458 
459   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
460   gdk_cairo_rectangle(cr, &e->area);
461   cairo_clip(cr);
462 
463   gfx::Point tabstrip_origin =
464       window_->tabstrip()->GetTabStripOriginForWidget(widget);
465   // Fill the entire region with the toolbar color.
466   GdkColor color = theme_service_->GetGdkColor(
467       ThemeService::COLOR_TOOLBAR);
468   gdk_cairo_set_source_color(cr, &color);
469   cairo_fill(cr);
470 
471   // The horizontal size of the top left and right corner images.
472   const int kCornerWidth = 4;
473   // The thickness of the shadow outside the toolbar's bounds; the offset
474   // between the edge of the toolbar and where we anchor the corner images.
475   const int kShadowThickness = 2;
476 
477   gfx::Rect area(e->area);
478   gfx::Rect right(widget->allocation.x + widget->allocation.width -
479                       kCornerWidth,
480                   widget->allocation.y - kShadowThickness,
481                   kCornerWidth,
482                   widget->allocation.height + kShadowThickness);
483   gfx::Rect left(widget->allocation.x - kShadowThickness,
484                  widget->allocation.y - kShadowThickness,
485                  kCornerWidth,
486                  widget->allocation.height + kShadowThickness);
487 
488   if (window_->ShouldDrawContentDropShadow()) {
489     // Leave room to draw rounded corners.
490     area = area.Subtract(right).Subtract(left);
491   }
492 
493   CairoCachedSurface* background = theme_service_->GetSurfaceNamed(
494       IDR_THEME_TOOLBAR, widget);
495   background->SetSource(cr, tabstrip_origin.x(), tabstrip_origin.y());
496   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
497   cairo_rectangle(cr, area.x(), area.y(), area.width(), area.height());
498   cairo_fill(cr);
499 
500   if (!window_->ShouldDrawContentDropShadow()) {
501     // The rest of this function is for rounded corners. Our work is done here.
502     cairo_destroy(cr);
503     return FALSE;
504   }
505 
506   bool draw_left_corner = left.Intersects(gfx::Rect(e->area));
507   bool draw_right_corner = right.Intersects(gfx::Rect(e->area));
508 
509   if (draw_left_corner || draw_right_corner) {
510     // Create a mask which is composed of the left and/or right corners.
511     cairo_surface_t* target = cairo_surface_create_similar(
512         cairo_get_target(cr),
513         CAIRO_CONTENT_COLOR_ALPHA,
514         widget->allocation.x + widget->allocation.width,
515         widget->allocation.y + widget->allocation.height);
516     cairo_t* copy_cr = cairo_create(target);
517 
518     cairo_set_operator(copy_cr, CAIRO_OPERATOR_SOURCE);
519     if (draw_left_corner) {
520       CairoCachedSurface* left_corner = theme_service_->GetSurfaceNamed(
521           IDR_CONTENT_TOP_LEFT_CORNER_MASK, widget);
522       left_corner->SetSource(copy_cr, left.x(), left.y());
523       cairo_paint(copy_cr);
524     }
525     if (draw_right_corner) {
526       CairoCachedSurface* right_corner = theme_service_->GetSurfaceNamed(
527           IDR_CONTENT_TOP_RIGHT_CORNER_MASK, widget);
528       right_corner->SetSource(copy_cr, right.x(), right.y());
529       // We fill a path rather than just painting because we don't want to
530       // overwrite the left corner.
531       cairo_rectangle(copy_cr, right.x(), right.y(),
532                       right.width(), right.height());
533       cairo_fill(copy_cr);
534     }
535 
536     // Draw the background. CAIRO_OPERATOR_IN uses the existing pixel data as
537     // an alpha mask.
538     background->SetSource(copy_cr, tabstrip_origin.x(), tabstrip_origin.y());
539     cairo_set_operator(copy_cr, CAIRO_OPERATOR_IN);
540     cairo_pattern_set_extend(cairo_get_source(copy_cr), CAIRO_EXTEND_REPEAT);
541     cairo_paint(copy_cr);
542     cairo_destroy(copy_cr);
543 
544     // Copy the temporary surface to the screen.
545     cairo_set_source_surface(cr, target, 0, 0);
546     cairo_paint(cr);
547     cairo_surface_destroy(target);
548   }
549 
550   cairo_destroy(cr);
551 
552   return FALSE;  // Allow subwidgets to paint.
553 }
554 
OnLocationHboxExpose(GtkWidget * location_hbox,GdkEventExpose * e)555 gboolean BrowserToolbarGtk::OnLocationHboxExpose(GtkWidget* location_hbox,
556                                                  GdkEventExpose* e) {
557   if (theme_service_->UseGtkTheme()) {
558     gtk_util::DrawTextEntryBackground(offscreen_entry_.get(),
559                                       location_hbox, &e->area,
560                                       &location_hbox->allocation);
561   }
562 
563   return FALSE;
564 }
565 
OnButtonClick(GtkWidget * button)566 void BrowserToolbarGtk::OnButtonClick(GtkWidget* button) {
567   if ((button == back_->widget()) || (button == forward_->widget())) {
568     if (gtk_util::DispositionForCurrentButtonPressEvent() == CURRENT_TAB)
569       location_bar_->Revert();
570     return;
571   }
572 
573   DCHECK(home_.get() && button == home_->widget()) <<
574       "Unexpected button click callback";
575   browser_->Home(gtk_util::DispositionForCurrentButtonPressEvent());
576 }
577 
OnMenuButtonPressEvent(GtkWidget * button,GdkEventButton * event)578 gboolean BrowserToolbarGtk::OnMenuButtonPressEvent(GtkWidget* button,
579                                                    GdkEventButton* event) {
580   if (event->button != 1)
581     return FALSE;
582 
583   wrench_menu_button_->SetPaintOverride(GTK_STATE_ACTIVE);
584   wrench_menu_->PopupForWidget(button, event->button, event->time);
585 
586   return TRUE;
587 }
588 
OnDragDataReceived(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time)589 void BrowserToolbarGtk::OnDragDataReceived(GtkWidget* widget,
590     GdkDragContext* drag_context, gint x, gint y,
591     GtkSelectionData* data, guint info, guint time) {
592   if (info != ui::TEXT_PLAIN) {
593     NOTIMPLEMENTED() << "Only support plain text drops for now, sorry!";
594     return;
595   }
596 
597   GURL url(reinterpret_cast<char*>(data->data));
598   if (!url.is_valid())
599     return;
600 
601   bool url_is_newtab = url.spec() == chrome::kChromeUINewTabURL;
602   home_page_is_new_tab_page_.SetValue(url_is_newtab);
603   if (!url_is_newtab)
604     home_page_.SetValue(url.spec());
605 }
606 
NotifyPrefChanged(const std::string * pref)607 void BrowserToolbarGtk::NotifyPrefChanged(const std::string* pref) {
608   if (!pref || *pref == prefs::kShowHomeButton) {
609     if (show_home_button_.GetValue() && !ShouldOnlyShowLocation()) {
610       gtk_widget_show(home_->widget());
611     } else {
612       gtk_widget_hide(home_->widget());
613     }
614   }
615 
616   if (!pref ||
617       *pref == prefs::kHomePage ||
618       *pref == prefs::kHomePageIsNewTabPage)
619     SetUpDragForHomeButton(!home_page_.IsManaged() &&
620                            !home_page_is_new_tab_page_.IsManaged());
621 }
622 
ShouldOnlyShowLocation() const623 bool BrowserToolbarGtk::ShouldOnlyShowLocation() const {
624   // If we're a popup window, only show the location bar (omnibox).
625   return browser_->type() != Browser::TYPE_NORMAL;
626 }
627 
OnWrenchMenuButtonExpose(GtkWidget * sender,GdkEventExpose * expose)628 gboolean BrowserToolbarGtk::OnWrenchMenuButtonExpose(GtkWidget* sender,
629                                                      GdkEventExpose* expose) {
630   const SkBitmap* badge = NULL;
631   if (UpgradeDetector::GetInstance()->notify_upgrade()) {
632     badge = theme_service_->GetBitmapNamed(IDR_UPDATE_BADGE);
633   } else {
634     return FALSE;
635   }
636 
637   // Draw the chrome app menu icon onto the canvas.
638   gfx::CanvasSkiaPaint canvas(expose, false);
639   int x_offset = base::i18n::IsRTL() ? 0 :
640       sender->allocation.width - badge->width();
641   int y_offset = 0;
642   canvas.DrawBitmapInt(
643       *badge,
644       sender->allocation.x + x_offset,
645       sender->allocation.y + y_offset);
646 
647   return FALSE;
648 }
649