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