Home | History | Annotate | Download | only in gtk
      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 
     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 
     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 
    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 
    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 
    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 
    252 void BrowserToolbarGtk::Show() {
    253   gtk_widget_show(toolbar_);
    254 }
    255 
    256 void BrowserToolbarGtk::Hide() {
    257   gtk_widget_hide(toolbar_);
    258 }
    259 
    260 LocationBar* BrowserToolbarGtk::GetLocationBar() const {
    261   return location_bar_.get();
    262 }
    263 
    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 
    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 
    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 
    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 
    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.
    323 bool BrowserToolbarGtk::AlwaysShowIconForCmd(int command_id) const {
    324   return command_id == IDC_UPGRADE_DIALOG;
    325 }
    326 
    327 // ui::AcceleratorProvider
    328 
    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 
    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 
    393 void BrowserToolbarGtk::SetProfile(Profile* profile) {
    394   if (profile == profile_)
    395     return;
    396 
    397   profile_ = profile;
    398   location_bar_->SetProfile(profile);
    399 }
    400 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    623 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 
    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