Home | History | Annotate | Download | only in gtk
      1 // Copyright 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_window_gtk.h"
      6 
      7 #include <gdk/gdkkeysyms.h>
      8 
      9 #include <algorithm>
     10 #include <string>
     11 
     12 #include "base/base_paths.h"
     13 #include "base/bind.h"
     14 #include "base/command_line.h"
     15 #include "base/debug/trace_event.h"
     16 #include "base/environment.h"
     17 #include "base/i18n/file_util_icu.h"
     18 #include "base/logging.h"
     19 #include "base/memory/scoped_ptr.h"
     20 #include "base/memory/singleton.h"
     21 #include "base/message_loop/message_loop.h"
     22 #include "base/nix/xdg_util.h"
     23 #include "base/path_service.h"
     24 #include "base/prefs/pref_service.h"
     25 #include "base/strings/string_util.h"
     26 #include "base/strings/utf_string_conversions.h"
     27 #include "base/time/time.h"
     28 #include "chrome/app/chrome_command_ids.h"
     29 #include "chrome/browser/browser_process.h"
     30 #include "chrome/browser/chrome_notification_types.h"
     31 #include "chrome/browser/download/download_item_model.h"
     32 #include "chrome/browser/extensions/tab_helper.h"
     33 #include "chrome/browser/infobars/infobar_service.h"
     34 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     35 #include "chrome/browser/profiles/profile.h"
     36 #include "chrome/browser/themes/theme_properties.h"
     37 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
     38 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
     39 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
     40 #include "chrome/browser/ui/browser.h"
     41 #include "chrome/browser/ui/browser_command_controller.h"
     42 #include "chrome/browser/ui/browser_commands.h"
     43 #include "chrome/browser/ui/browser_dialogs.h"
     44 #include "chrome/browser/ui/browser_list.h"
     45 #include "chrome/browser/ui/browser_window_state.h"
     46 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
     47 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
     48 #include "chrome/browser/ui/gtk/accelerators_gtk.h"
     49 #include "chrome/browser/ui/gtk/avatar_menu_bubble_gtk.h"
     50 #include "chrome/browser/ui/gtk/avatar_menu_button_gtk.h"
     51 #include "chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h"
     52 #include "chrome/browser/ui/gtk/browser_titlebar.h"
     53 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
     54 #include "chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h"
     55 #include "chrome/browser/ui/gtk/download/download_in_progress_dialog_gtk.h"
     56 #include "chrome/browser/ui/gtk/download/download_shelf_gtk.h"
     57 #include "chrome/browser/ui/gtk/edit_search_engine_dialog.h"
     58 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h"
     59 #include "chrome/browser/ui/gtk/find_bar_gtk.h"
     60 #include "chrome/browser/ui/gtk/fullscreen_exit_bubble_gtk.h"
     61 #include "chrome/browser/ui/gtk/global_menu_bar.h"
     62 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     63 #include "chrome/browser/ui/gtk/gtk_util.h"
     64 #include "chrome/browser/ui/gtk/gtk_window_util.h"
     65 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
     66 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
     67 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
     68 #include "chrome/browser/ui/gtk/nine_box.h"
     69 #include "chrome/browser/ui/gtk/one_click_signin_bubble_gtk.h"
     70 #include "chrome/browser/ui/gtk/password_generation_bubble_gtk.h"
     71 #include "chrome/browser/ui/gtk/reload_button_gtk.h"
     72 #include "chrome/browser/ui/gtk/status_bubble_gtk.h"
     73 #include "chrome/browser/ui/gtk/tab_contents_container_gtk.h"
     74 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
     75 #include "chrome/browser/ui/gtk/task_manager_gtk.h"
     76 #include "chrome/browser/ui/gtk/update_recommended_dialog.h"
     77 #include "chrome/browser/ui/gtk/website_settings/website_settings_popup_gtk.h"
     78 #include "chrome/browser/ui/omnibox/location_bar.h"
     79 #include "chrome/browser/ui/omnibox/omnibox_view.h"
     80 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     81 #include "chrome/browser/web_applications/web_app.h"
     82 #include "chrome/common/chrome_switches.h"
     83 #include "chrome/common/pref_names.h"
     84 #include "components/user_prefs/pref_registry_syncable.h"
     85 #include "content/public/browser/download_manager.h"
     86 #include "content/public/browser/native_web_keyboard_event.h"
     87 #include "content/public/browser/notification_service.h"
     88 #include "content/public/browser/render_view_host.h"
     89 #include "content/public/browser/render_widget_host_view.h"
     90 #include "content/public/browser/web_contents.h"
     91 #include "content/public/browser/web_contents_view.h"
     92 #include "grit/chromium_strings.h"
     93 #include "grit/generated_resources.h"
     94 #include "grit/theme_resources.h"
     95 #include "grit/ui_resources.h"
     96 #include "ui/base/accelerators/platform_accelerator_gtk.h"
     97 #include "ui/base/gtk/gtk_floating_container.h"
     98 #include "ui/base/gtk/gtk_hig_constants.h"
     99 #include "ui/base/gtk/gtk_screen_util.h"
    100 #include "ui/base/keycodes/keyboard_codes.h"
    101 #include "ui/base/l10n/l10n_util.h"
    102 #include "ui/base/resource/resource_bundle.h"
    103 #include "ui/base/x/active_window_watcher_x.h"
    104 #include "ui/gfx/gtk_util.h"
    105 #include "ui/gfx/image/cairo_cached_surface.h"
    106 #include "ui/gfx/image/image.h"
    107 #include "ui/gfx/rect.h"
    108 #include "ui/gfx/screen.h"
    109 #include "ui/gfx/skia_utils_gtk.h"
    110 
    111 using content::NativeWebKeyboardEvent;
    112 using content::SSLStatus;
    113 using content::WebContents;
    114 using web_modal::WebContentsModalDialogHost;
    115 
    116 namespace {
    117 
    118 // The number of milliseconds between loading animation frames.
    119 const int kLoadingAnimationFrameTimeMs = 30;
    120 
    121 const char* kBrowserWindowKey = "__BROWSER_WINDOW_GTK__";
    122 
    123 // While resize areas on Windows are normally the same size as the window
    124 // borders, our top area is shrunk by 1 px to make it easier to move the window
    125 // around with our thinner top grabbable strip.  (Incidentally, our side and
    126 // bottom resize areas don't match the frame border thickness either -- they
    127 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
    128 const int kTopResizeAdjust = 1;
    129 // The thickness of the shadow around the toolbar+web content area.  There are
    130 // actually a couple pixels more that should overlap the toolbar and web
    131 // content area, but we don't use those pixels.
    132 const int kContentShadowThickness = 2;
    133 // The offset to the background when the custom frame is off.  We want the
    134 // window background to line up with the tab background regardless of whether
    135 // we're in custom frame mode or not.  Since themes are designed with the
    136 // custom frame in mind, we need to offset the background when the custom frame
    137 // is off.
    138 const int kCustomFrameBackgroundVerticalOffset = 15;
    139 
    140 // The timeout in milliseconds before we'll get the true window position with
    141 // gtk_window_get_position() after the last GTK configure-event signal.
    142 const int kDebounceTimeoutMilliseconds = 100;
    143 
    144 // Using gtk_window_get_position/size creates a race condition, so only use
    145 // this to get the initial bounds.  After window creation, we pick up the
    146 // normal bounds by connecting to the configure-event signal.
    147 gfx::Rect GetInitialWindowBounds(GtkWindow* window) {
    148   gint x, y, width, height;
    149   gtk_window_get_position(window, &x, &y);
    150   gtk_window_get_size(window, &width, &height);
    151   return gfx::Rect(x, y, width, height);
    152 }
    153 
    154 // Get the command ids of the key combinations that are not valid gtk
    155 // accelerators.
    156 int GetCustomCommandId(GdkEventKey* event) {
    157   // Filter modifier to only include accelerator modifiers.
    158   guint modifier = event->state & gtk_accelerator_get_default_mod_mask();
    159   switch (event->keyval) {
    160     // Gtk doesn't allow GDK_Tab or GDK_ISO_Left_Tab to be an accelerator (see
    161     // gtk_accelerator_valid), so we need to handle these accelerators
    162     // manually.
    163     // Some X clients (e.g. cygwin, NX client, etc.) also send GDK_KP_Tab when
    164     // typing a tab key. We should also handle GDK_KP_Tab for such X clients as
    165     // Firefox does.
    166     case GDK_Tab:
    167     case GDK_ISO_Left_Tab:
    168     case GDK_KP_Tab:
    169       if (GDK_CONTROL_MASK == modifier) {
    170         return IDC_SELECT_NEXT_TAB;
    171       } else if ((GDK_CONTROL_MASK | GDK_SHIFT_MASK) == modifier) {
    172         return IDC_SELECT_PREVIOUS_TAB;
    173       }
    174       break;
    175 
    176     default:
    177       break;
    178   }
    179   return -1;
    180 }
    181 
    182 // Get the command ids of the accelerators that we don't want the native widget
    183 // to be able to override.
    184 int GetPreHandleCommandId(GdkEventKey* event) {
    185   // Filter modifier to only include accelerator modifiers.
    186   guint modifier = event->state & gtk_accelerator_get_default_mod_mask();
    187   switch (event->keyval) {
    188     case GDK_Page_Down:
    189       if (GDK_CONTROL_MASK == modifier) {
    190         return IDC_SELECT_NEXT_TAB;
    191       } else if ((GDK_CONTROL_MASK | GDK_SHIFT_MASK) == modifier) {
    192         return IDC_MOVE_TAB_NEXT;
    193       }
    194       break;
    195 
    196     case GDK_Page_Up:
    197       if (GDK_CONTROL_MASK == modifier) {
    198         return IDC_SELECT_PREVIOUS_TAB;
    199       } else if ((GDK_CONTROL_MASK | GDK_SHIFT_MASK) == modifier) {
    200         return IDC_MOVE_TAB_PREVIOUS;
    201       }
    202       break;
    203 
    204     default:
    205       break;
    206   }
    207   return -1;
    208 }
    209 
    210 GQuark GetBrowserWindowQuarkKey() {
    211   static GQuark quark = g_quark_from_static_string(kBrowserWindowKey);
    212   return quark;
    213 }
    214 
    215 }  // namespace
    216 
    217 BrowserWindowGtk::BrowserWindowGtk(Browser* browser)
    218     :  window_(NULL),
    219        window_has_shown_(false),
    220        window_container_(NULL),
    221        window_vbox_(NULL),
    222        render_area_vbox_(NULL),
    223        render_area_floating_container_(NULL),
    224        render_area_event_box_(NULL),
    225        toolbar_border_(NULL),
    226        browser_(browser),
    227        state_(GDK_WINDOW_STATE_WITHDRAWN),
    228        devtools_dock_side_(DEVTOOLS_DOCK_SIDE_BOTTOM),
    229        devtools_window_(NULL),
    230        contents_hsplit_(NULL),
    231        contents_vsplit_(NULL),
    232        frame_cursor_(NULL),
    233        is_active_(false),
    234        show_state_after_show_(ui::SHOW_STATE_DEFAULT),
    235        suppress_window_raise_(false),
    236        accel_group_(NULL),
    237        fullscreen_exit_bubble_type_(
    238            FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION) {
    239 }
    240 
    241 BrowserWindowGtk::~BrowserWindowGtk() {
    242   ui::ActiveWindowWatcherX::RemoveObserver(this);
    243 
    244   browser_->tab_strip_model()->RemoveObserver(this);
    245 }
    246 
    247 void BrowserWindowGtk::Init() {
    248   // We register first so that other views like the toolbar can use the
    249   // is_active() function in their ActiveWindowChanged() handlers.
    250   ui::ActiveWindowWatcherX::AddObserver(this);
    251 
    252   use_custom_frame_pref_.Init(
    253       prefs::kUseCustomChromeFrame,
    254       browser_->profile()->GetPrefs(),
    255       base::Bind(&BrowserWindowGtk::OnUseCustomChromeFrameChanged,
    256                  base::Unretained(this)));
    257 
    258   // Register to be notified of changes to the profile's avatar icon.
    259   if (!browser_->profile()->IsOffTheRecord()) {
    260     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
    261                    content::NotificationService::AllSources());
    262   }
    263 
    264   // In some (older) versions of compiz, raising top-level windows when they
    265   // are partially off-screen causes them to get snapped back on screen, not
    266   // always even on the current virtual desktop.  If we are running under
    267   // compiz, suppress such raises, as they are not necessary in compiz anyway.
    268   if (ui::GuessWindowManager() == ui::WM_COMPIZ)
    269     suppress_window_raise_ = true;
    270 
    271   window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
    272   g_object_set_qdata(G_OBJECT(window_), GetBrowserWindowQuarkKey(), this);
    273   gtk_widget_add_events(GTK_WIDGET(window_), GDK_BUTTON_PRESS_MASK |
    274                                              GDK_POINTER_MOTION_MASK);
    275 
    276   // Disable the resize gripper on Ubuntu.
    277   gtk_window_util::DisableResizeGrip(window_);
    278 
    279   // Add this window to its own unique window group to allow for
    280   // window-to-parent modality.
    281   gtk_window_group_add_window(gtk_window_group_new(), window_);
    282   g_object_unref(gtk_window_get_group(window_));
    283 
    284   // Set up a custom WM_CLASS for some sorts of window types.  This allows
    285   // task switchers to distinguish between main browser windows and e.g
    286   // app windows.
    287   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    288   if (browser_->is_app()) {
    289     std::string app_name = browser_->app_name();
    290     if (app_name != DevToolsWindow::kDevToolsApp) {
    291       gtk_window_util::SetWindowCustomClass(window_,
    292           web_app::GetWMClassFromAppName(app_name));
    293     }
    294   } else if (command_line.HasSwitch(switches::kUserDataDir)) {
    295     // Set the class name to e.g. "Chrome (/tmp/my-user-data)".  The
    296     // class name will show up in the alt-tab list in gnome-shell if
    297     // you're running a binary that doesn't have a matching .desktop
    298     // file.
    299     const std::string user_data_dir =
    300         command_line.GetSwitchValueNative(switches::kUserDataDir);
    301     gtk_window_util::SetWindowCustomClass(window_,
    302         std::string(gdk_get_program_class()) + " (" + user_data_dir + ")");
    303   }
    304 
    305   // For popups, we initialize widgets then set the window geometry, because
    306   // popups need the widgets inited before they can set the window size
    307   // properly. For other windows, we set the geometry first to prevent resize
    308   // flicker.
    309   if (browser_->is_type_popup()) {
    310     gtk_window_set_role(window_, "pop-up");
    311     InitWidgets();
    312     SetGeometryHints();
    313   } else {
    314     gtk_window_set_role(window_, "browser");
    315     SetGeometryHints();
    316     InitWidgets();
    317   }
    318 
    319   ConnectAccelerators();
    320 
    321   // Set the initial background color of widgets.
    322   SetBackgroundColor();
    323   HideUnsupportedWindowFeatures();
    324 
    325   if (UseCustomFrame()) {
    326     // Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force
    327     // fullscreen on the window when it matches the desktop size.
    328     ui::SetHideTitlebarWhenMaximizedProperty(
    329         ui::GetX11WindowFromGtkWidget(GTK_WIDGET(window_)),
    330         ui::HIDE_TITLEBAR_WHEN_MAXIMIZED);
    331   }
    332 }
    333 
    334 gboolean BrowserWindowGtk::OnCustomFrameExpose(GtkWidget* widget,
    335                                                GdkEventExpose* event) {
    336   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnCustomFrameExpose");
    337 
    338   // Draw the default background.
    339   cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
    340   gdk_cairo_rectangle(cr, &event->area);
    341   cairo_clip(cr);
    342 
    343   if (UsingCustomPopupFrame()) {
    344     DrawPopupFrame(cr, widget, event);
    345   } else {
    346     DrawCustomFrame(cr, widget, event);
    347   }
    348 
    349   DrawContentShadow(cr);
    350 
    351   cairo_destroy(cr);
    352 
    353   if (UseCustomFrame() && !IsMaximized())
    354     DrawCustomFrameBorder(widget);
    355 
    356   return FALSE;  // Allow subwidgets to paint.
    357 }
    358 
    359 void BrowserWindowGtk::DrawCustomFrameBorder(GtkWidget* widget) {
    360   static NineBox* custom_frame_border = NULL;
    361   if (!custom_frame_border) {
    362     custom_frame_border = new NineBox(IDR_WINDOW_TOP_LEFT_CORNER,
    363                                       IDR_WINDOW_TOP_CENTER,
    364                                       IDR_WINDOW_TOP_RIGHT_CORNER,
    365                                       IDR_WINDOW_LEFT_SIDE,
    366                                       0,
    367                                       IDR_WINDOW_RIGHT_SIDE,
    368                                       IDR_WINDOW_BOTTOM_LEFT_CORNER,
    369                                       IDR_WINDOW_BOTTOM_CENTER,
    370                                       IDR_WINDOW_BOTTOM_RIGHT_CORNER);
    371   }
    372   custom_frame_border->RenderToWidget(widget);
    373 }
    374 
    375 void BrowserWindowGtk::DrawContentShadow(cairo_t* cr) {
    376   // Draw the shadow above the toolbar. Tabs on the tabstrip will draw over us.
    377   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    378   int left_x, top_y;
    379   gtk_widget_translate_coordinates(toolbar_->widget(),
    380       GTK_WIDGET(window_), 0, 0, &left_x,
    381       &top_y);
    382 
    383   GtkAllocation window_vbox_allocation;
    384   gtk_widget_get_allocation(window_vbox_, &window_vbox_allocation);
    385   int center_width = window_vbox_allocation.width;
    386 
    387   gfx::CairoCachedSurface* top_center =
    388       rb.GetNativeImageNamed(IDR_CONTENT_TOP_CENTER).ToCairo();
    389   gfx::CairoCachedSurface* top_right =
    390       rb.GetNativeImageNamed(IDR_CONTENT_TOP_RIGHT_CORNER).ToCairo();
    391   gfx::CairoCachedSurface* top_left =
    392       rb.GetNativeImageNamed(IDR_CONTENT_TOP_LEFT_CORNER).ToCairo();
    393 
    394   int center_left_x = left_x;
    395   if (ShouldDrawContentDropShadow()) {
    396     // Don't draw over the corners.
    397     center_left_x += top_left->Width() - kContentShadowThickness;
    398     center_width -= (top_left->Width() + top_right->Width());
    399     center_width += 2 * kContentShadowThickness;
    400   }
    401 
    402   top_center->SetSource(cr, GTK_WIDGET(window_),
    403                         center_left_x, top_y - kContentShadowThickness);
    404   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    405   cairo_rectangle(cr, center_left_x, top_y - kContentShadowThickness,
    406                   center_width, top_center->Height());
    407   cairo_fill(cr);
    408 
    409   // Only draw the rest of the shadow if the user has the custom frame enabled
    410   // and the browser is not maximized.
    411   if (!ShouldDrawContentDropShadow())
    412     return;
    413 
    414   // The top left corner has a width of 3 pixels. On Windows, the last column
    415   // of pixels overlap the toolbar. We just crop it off on Linux.  The top
    416   // corners extend to the base of the toolbar (one pixel above the dividing
    417   // line).
    418   int right_x = center_left_x + center_width;
    419   top_left->SetSource(cr, GTK_WIDGET(window_),
    420       left_x - kContentShadowThickness, top_y - kContentShadowThickness);
    421   // The toolbar is shorter in location bar only mode so clip the image to the
    422   // height of the toolbar + the amount of shadow above the toolbar.
    423   cairo_rectangle(cr,
    424       left_x - kContentShadowThickness,
    425       top_y - kContentShadowThickness,
    426       top_left->Width(),
    427       top_left->Height());
    428   cairo_fill(cr);
    429 
    430   // Likewise, we crop off the left column of pixels for the top right corner.
    431   top_right->SetSource(cr, GTK_WIDGET(window_),
    432                        right_x, top_y - kContentShadowThickness);
    433   cairo_rectangle(cr,
    434       right_x,
    435       top_y - kContentShadowThickness,
    436       top_right->Width(),
    437       top_right->Height());
    438   cairo_fill(cr);
    439 
    440   // Fill in the sides.  As above, we only draw 2 of the 3 columns on Linux.
    441   int bottom_y;
    442   gtk_widget_translate_coordinates(window_vbox_,
    443       GTK_WIDGET(window_),
    444       0, window_vbox_allocation.height,
    445       NULL, &bottom_y);
    446   // |side_y| is where to start drawing the side shadows.  The top corners draw
    447   // the sides down to the bottom of the toolbar.
    448   int side_y = top_y - kContentShadowThickness + top_right->Height();
    449   // |side_height| is how many pixels to draw for the side borders.  We do one
    450   // pixel before the bottom of the web contents because that extra pixel is
    451   // drawn by the bottom corners.
    452   int side_height = bottom_y - side_y - 1;
    453   if (side_height > 0) {
    454     gfx::CairoCachedSurface* left =
    455         rb.GetNativeImageNamed(IDR_CONTENT_LEFT_SIDE).ToCairo();
    456     left->SetSource(cr, GTK_WIDGET(window_),
    457                     left_x - kContentShadowThickness, side_y);
    458     cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    459     cairo_rectangle(cr,
    460         left_x - kContentShadowThickness,
    461         side_y,
    462         kContentShadowThickness,
    463         side_height);
    464     cairo_fill(cr);
    465 
    466     gfx::CairoCachedSurface* right =
    467         rb.GetNativeImageNamed(IDR_CONTENT_RIGHT_SIDE).ToCairo();
    468     int right_side_x =
    469         right_x + top_right->Width() - kContentShadowThickness - 1;
    470     right->SetSource(cr, GTK_WIDGET(window_), right_side_x, side_y);
    471     cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    472     cairo_rectangle(cr,
    473         right_side_x,
    474         side_y,
    475         kContentShadowThickness,
    476         side_height);
    477     cairo_fill(cr);
    478   }
    479 
    480   // Draw the bottom corners.  The bottom corners also draw the bottom row of
    481   // pixels of the side shadows.
    482   gfx::CairoCachedSurface* bottom_left =
    483       rb.GetNativeImageNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER).ToCairo();
    484   bottom_left->SetSource(cr, GTK_WIDGET(window_),
    485                          left_x - kContentShadowThickness, bottom_y - 1);
    486   cairo_paint(cr);
    487 
    488   gfx::CairoCachedSurface* bottom_right =
    489       rb.GetNativeImageNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER).ToCairo();
    490   bottom_right->SetSource(cr, GTK_WIDGET(window_), right_x - 1, bottom_y - 1);
    491   cairo_paint(cr);
    492 
    493   // Finally, draw the bottom row. Since we don't overlap the contents, we clip
    494   // the top row of pixels.
    495   gfx::CairoCachedSurface* bottom =
    496       rb.GetNativeImageNamed(IDR_CONTENT_BOTTOM_CENTER).ToCairo();
    497   bottom->SetSource(cr, GTK_WIDGET(window_), left_x + 1, bottom_y - 1);
    498   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    499   cairo_rectangle(cr,
    500       left_x + 1,
    501       bottom_y,
    502       window_vbox_allocation.width - 2,
    503       kContentShadowThickness);
    504   cairo_fill(cr);
    505 }
    506 
    507 void BrowserWindowGtk::DrawPopupFrame(cairo_t* cr,
    508                                       GtkWidget* widget,
    509                                       GdkEventExpose* event) {
    510   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    511 
    512   // Like DrawCustomFrame(), except that we use the unthemed resources to draw
    513   // the background. We do this because we can't rely on sane images in the
    514   // theme that we can draw text on. (We tried using the tab background, but
    515   // that has inverse saturation from what the user usually expects).
    516   int image_name = GetThemeFrameResource();
    517   gfx::CairoCachedSurface* surface =
    518       rb.GetNativeImageNamed(image_name).ToCairo();
    519   surface->SetSource(cr, widget, 0, GetVerticalOffset());
    520   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT);
    521   cairo_rectangle(cr, event->area.x, event->area.y,
    522                   event->area.width, event->area.height);
    523   cairo_fill(cr);
    524 }
    525 
    526 void BrowserWindowGtk::DrawCustomFrame(cairo_t* cr,
    527                                        GtkWidget* widget,
    528                                        GdkEventExpose* event) {
    529   GtkThemeService* theme_provider = GtkThemeService::GetFrom(
    530       browser()->profile());
    531 
    532   int image_name = GetThemeFrameResource();
    533 
    534   gfx::CairoCachedSurface* surface = theme_provider->GetImageNamed(
    535       image_name).ToCairo();
    536   if (event->area.y < surface->Height()) {
    537     surface->SetSource(cr, widget, 0, GetVerticalOffset());
    538 
    539     // The frame background isn't tiled vertically.
    540     cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    541     cairo_rectangle(cr, event->area.x, event->area.y,
    542                     event->area.width, surface->Height() - event->area.y);
    543     cairo_fill(cr);
    544   }
    545 
    546   if (theme_provider->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
    547       !browser()->profile()->IsOffTheRecord()) {
    548     gfx::CairoCachedSurface* theme_overlay = theme_provider->GetImageNamed(
    549         DrawFrameAsActive() ? IDR_THEME_FRAME_OVERLAY
    550         : IDR_THEME_FRAME_OVERLAY_INACTIVE).ToCairo();
    551     theme_overlay->SetSource(cr, widget, 0, GetVerticalOffset());
    552     cairo_paint(cr);
    553   }
    554 }
    555 
    556 int BrowserWindowGtk::GetVerticalOffset() {
    557   return (IsMaximized() || (!UseCustomFrame())) ?
    558       -kCustomFrameBackgroundVerticalOffset : 0;
    559 }
    560 
    561 int BrowserWindowGtk::GetThemeFrameResource() {
    562   bool incognito = browser()->profile()->IsOffTheRecord();
    563   int image_name;
    564   if (DrawFrameAsActive()) {
    565     image_name = incognito ? IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
    566   } else {
    567     image_name = incognito ? IDR_THEME_FRAME_INCOGNITO_INACTIVE :
    568                  IDR_THEME_FRAME_INACTIVE;
    569   }
    570 
    571   return image_name;
    572 }
    573 
    574 void BrowserWindowGtk::Show() {
    575   // The Browser associated with this browser window must become the active
    576   // browser at the time Show() is called. This is the natural behaviour under
    577   // Windows, but gtk_widget_show won't show the widget (and therefore won't
    578   // call OnFocusIn()) until we return to the runloop. Therefore any calls to
    579   // chrome::FindLastActiveWithHostDesktopType will return the previous
    580   // browser instead if we don't explicitly set it here.
    581   BrowserList::SetLastActive(browser());
    582 
    583   gtk_window_present(window_);
    584   if (show_state_after_show_ == ui::SHOW_STATE_MAXIMIZED) {
    585     gtk_window_maximize(window_);
    586     show_state_after_show_ = ui::SHOW_STATE_NORMAL;
    587   } else if (show_state_after_show_ == ui::SHOW_STATE_MINIMIZED) {
    588     gtk_window_iconify(window_);
    589     show_state_after_show_ = ui::SHOW_STATE_NORMAL;
    590   }
    591 
    592   // If we have sized the window by setting a size request for the render
    593   // area, then undo it so that the render view can later adjust its own
    594   // size.
    595   gtk_widget_set_size_request(contents_container_->widget(), -1, -1);
    596 
    597   bool update_devtools = !window_has_shown_ && devtools_window_;
    598   window_has_shown_ = true;
    599   browser()->OnWindowDidShow();
    600   if (update_devtools)
    601     UpdateDevToolsSplitPosition();
    602 }
    603 
    604 void BrowserWindowGtk::ShowInactive() {
    605   gtk_window_set_focus_on_map(window_, false);
    606   gtk_widget_show(GTK_WIDGET(window_));
    607 }
    608 
    609 void BrowserWindowGtk::Hide() {
    610   // Not implemented.
    611 }
    612 
    613 void BrowserWindowGtk::SetBoundsImpl(const gfx::Rect& bounds,
    614                                      bool exterior,
    615                                      bool move) {
    616   gint x = static_cast<gint>(bounds.x());
    617   gint y = static_cast<gint>(bounds.y());
    618   gint width = static_cast<gint>(bounds.width());
    619   gint height = static_cast<gint>(bounds.height());
    620 
    621   if (move)
    622     gtk_window_move(window_, x, y);
    623 
    624   if (exterior) {
    625     gtk_window_util::SetWindowSize(window_, gfx::Size(width, height));
    626   } else {
    627     gtk_widget_set_size_request(contents_container_->widget(),
    628                                 width, height);
    629   }
    630 }
    631 
    632 void BrowserWindowGtk::SetBounds(const gfx::Rect& bounds) {
    633   if (IsFullscreen())
    634     ExitFullscreen();
    635   SetBoundsImpl(bounds, true, true);
    636 }
    637 
    638 void BrowserWindowGtk::Close() {
    639   // We're already closing.  Do nothing.
    640   if (!window_)
    641     return;
    642 
    643   if (!CanClose())
    644     return;
    645 
    646   // We're going to destroy the window, make sure the tab strip isn't running
    647   // any animations which may still reference GtkWidgets.
    648   tabstrip_->StopAnimation();
    649 
    650   SaveWindowPosition();
    651 
    652   if (accel_group_) {
    653     // Disconnecting the keys we connected to our accelerator group frees the
    654     // closures allocated in ConnectAccelerators.
    655     AcceleratorsGtk* accelerators = AcceleratorsGtk::GetInstance();
    656     for (AcceleratorsGtk::const_iterator iter = accelerators->begin();
    657          iter != accelerators->end(); ++iter) {
    658       gtk_accel_group_disconnect_key(accel_group_,
    659           ui::GetGdkKeyCodeForAccelerator(iter->second),
    660           ui::GetGdkModifierForAccelerator(iter->second));
    661     }
    662     gtk_window_remove_accel_group(window_, accel_group_);
    663     g_object_unref(accel_group_);
    664     accel_group_ = NULL;
    665   }
    666 
    667   // Cancel any pending callback from the window configure debounce timer.
    668   window_configure_debounce_timer_.Stop();
    669 
    670   // Likewise for the loading animation.
    671   loading_animation_timer_.Stop();
    672 
    673   GtkWidget* window = GTK_WIDGET(window_);
    674   // To help catch bugs in any event handlers that might get fired during the
    675   // destruction, set window_ to NULL before any handlers will run.
    676   window_ = NULL;
    677   // Avoid use-after-free in any code that runs after Close() and forgets to
    678   // check window_.
    679   window_container_ = NULL;
    680   window_vbox_ = NULL;
    681   render_area_vbox_ = NULL;
    682   render_area_floating_container_ = NULL;
    683   render_area_event_box_ = NULL;
    684   toolbar_border_ = NULL;
    685   contents_vsplit_ = NULL;
    686   contents_hsplit_ = NULL;
    687 
    688   window_has_shown_ = false;
    689   titlebar_->set_window(NULL);
    690 
    691   // We don't want GlobalMenuBar handling any notifications or commands after
    692   // the window is destroyed.
    693   global_menu_bar_->Disable();
    694   gtk_widget_destroy(window);
    695 }
    696 
    697 void BrowserWindowGtk::Activate() {
    698   gtk_window_present(window_);
    699 }
    700 
    701 void BrowserWindowGtk::Deactivate() {
    702   gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
    703 }
    704 
    705 bool BrowserWindowGtk::IsActive() const {
    706   if (ui::ActiveWindowWatcherX::WMSupportsActivation())
    707     return is_active_;
    708 
    709   // This still works even though we don't get the activation notification.
    710   return window_ && gtk_window_is_active(window_);
    711 }
    712 
    713 void BrowserWindowGtk::FlashFrame(bool flash) {
    714   // May not be respected by all window managers.
    715   gtk_window_set_urgency_hint(window_, flash);
    716 }
    717 
    718 bool BrowserWindowGtk::IsAlwaysOnTop() const {
    719   return false;
    720 }
    721 
    722 gfx::NativeWindow BrowserWindowGtk::GetNativeWindow() {
    723   return window_;
    724 }
    725 
    726 BrowserWindowTesting* BrowserWindowGtk::GetBrowserWindowTesting() {
    727   NOTIMPLEMENTED();
    728   return NULL;
    729 }
    730 
    731 StatusBubble* BrowserWindowGtk::GetStatusBubble() {
    732   return status_bubble_.get();
    733 }
    734 
    735 void BrowserWindowGtk::UpdateTitleBar() {
    736   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::UpdateTitleBar");
    737   string16 title = browser_->GetWindowTitleForCurrentTab();
    738   gtk_window_set_title(window_, UTF16ToUTF8(title).c_str());
    739   if (ShouldShowWindowIcon())
    740     titlebar_->UpdateTitleAndIcon();
    741 }
    742 
    743 void BrowserWindowGtk::BookmarkBarStateChanged(
    744     BookmarkBar::AnimateChangeType change_type) {
    745   MaybeShowBookmarkBar(change_type == BookmarkBar::ANIMATE_STATE_CHANGE);
    746 }
    747 
    748 void BrowserWindowGtk::UpdateDevTools() {
    749   UpdateDevToolsForContents(
    750       browser_->tab_strip_model()->GetActiveWebContents());
    751 }
    752 
    753 void BrowserWindowGtk::UpdateLoadingAnimations(bool should_animate) {
    754   if (should_animate) {
    755     if (!loading_animation_timer_.IsRunning()) {
    756       // Loads are happening, and the timer isn't running, so start it.
    757       loading_animation_timer_.Start(FROM_HERE,
    758           base::TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs), this,
    759           &BrowserWindowGtk::LoadingAnimationCallback);
    760     }
    761   } else {
    762     if (loading_animation_timer_.IsRunning()) {
    763       loading_animation_timer_.Stop();
    764       // Loads are now complete, update the state if a task was scheduled.
    765       LoadingAnimationCallback();
    766     }
    767   }
    768 }
    769 
    770 void BrowserWindowGtk::LoadingAnimationCallback() {
    771   if (browser_->is_type_tabbed()) {
    772     // Loading animations are shown in the tab for tabbed windows.  We check the
    773     // browser type instead of calling IsTabStripVisible() because the latter
    774     // will return false for fullscreen windows, but we still need to update
    775     // their animations (so that when they come out of fullscreen mode they'll
    776     // be correct).
    777     tabstrip_->UpdateLoadingAnimations();
    778   } else if (ShouldShowWindowIcon()) {
    779     // ... or in the window icon area for popups and app windows.
    780     WebContents* web_contents =
    781         browser_->tab_strip_model()->GetActiveWebContents();
    782     // GetSelectedTabContents can return NULL for example under Purify when
    783     // the animations are running slowly and this function is called on
    784     // a timer through LoadingAnimationCallback.
    785     titlebar_->UpdateThrobber(web_contents);
    786   }
    787 }
    788 
    789 void BrowserWindowGtk::SetStarredState(bool is_starred) {
    790   toolbar_->GetLocationBarView()->SetStarred(is_starred);
    791 }
    792 
    793 void BrowserWindowGtk::ZoomChangedForActiveTab(bool can_show_bubble) {
    794   toolbar_->GetLocationBarView()->ZoomChangedForActiveTab(
    795       can_show_bubble && !toolbar_->IsWrenchMenuShowing());
    796 }
    797 
    798 gfx::Rect BrowserWindowGtk::GetRestoredBounds() const {
    799   return restored_bounds_;
    800 }
    801 
    802 ui::WindowShowState BrowserWindowGtk::GetRestoredState() const {
    803   if (IsMaximized())
    804     return ui::SHOW_STATE_MAXIMIZED;
    805   if (IsMinimized())
    806     return ui::SHOW_STATE_MINIMIZED;
    807   return ui::SHOW_STATE_NORMAL;
    808 }
    809 
    810 gfx::Rect BrowserWindowGtk::GetBounds() const {
    811   return bounds_;
    812 }
    813 
    814 bool BrowserWindowGtk::IsMaximized() const {
    815   return (state_ & GDK_WINDOW_STATE_MAXIMIZED);
    816 }
    817 
    818 bool BrowserWindowGtk::IsMinimized() const {
    819   return (state_ & GDK_WINDOW_STATE_ICONIFIED);
    820 }
    821 
    822 void BrowserWindowGtk::Maximize() {
    823   gtk_window_maximize(window_);
    824 }
    825 
    826 void BrowserWindowGtk::Minimize() {
    827   gtk_window_iconify(window_);
    828 }
    829 
    830 void BrowserWindowGtk::Restore() {
    831   if (IsMaximized())
    832     UnMaximize();
    833   else if (IsMinimized())
    834     gtk_window_deiconify(window_);
    835 }
    836 
    837 bool BrowserWindowGtk::ShouldDrawContentDropShadow() const {
    838   return !IsMaximized() && UseCustomFrame();
    839 }
    840 
    841 void BrowserWindowGtk::EnterFullscreen(
    842       const GURL& url, FullscreenExitBubbleType type) {
    843   // gtk_window_(un)fullscreen asks the window manager to toggle the EWMH
    844   // for fullscreen windows.  Not all window managers support this.
    845   gtk_window_fullscreen(window_);
    846   fullscreen_exit_bubble_type_ = type;
    847 }
    848 
    849 void BrowserWindowGtk::UpdateFullscreenExitBubbleContent(
    850      const GURL& url,
    851       FullscreenExitBubbleType bubble_type) {
    852   if (!window_) {
    853     // Don't create a fullscreen bubble for a closing window.
    854     return;
    855   } else if (bubble_type == FEB_TYPE_NONE) {
    856    fullscreen_exit_bubble_.reset();
    857   } else if (fullscreen_exit_bubble_.get()) {
    858    fullscreen_exit_bubble_->UpdateContent(url, bubble_type);
    859   } else {
    860     fullscreen_exit_bubble_.reset(new FullscreenExitBubbleGtk(
    861         GTK_FLOATING_CONTAINER(render_area_floating_container_),
    862         browser(),
    863         url,
    864         bubble_type));
    865   }
    866 }
    867 
    868 void BrowserWindowGtk::ExitFullscreen() {
    869   // Work around a bug where if we try to unfullscreen, metacity immediately
    870   // fullscreens us again.  This is a little flickery and not necessary if
    871   // there's a gnome-panel, but it's not easy to detect whether there's a
    872   // panel or not.
    873   bool unmaximize_before_unfullscreen = IsMaximized() &&
    874       ui::GuessWindowManager() == ui::WM_METACITY;
    875   if (unmaximize_before_unfullscreen)
    876     UnMaximize();
    877 
    878   gtk_window_unfullscreen(window_);
    879 
    880   if (unmaximize_before_unfullscreen)
    881     gtk_window_maximize(window_);
    882 }
    883 
    884 bool BrowserWindowGtk::ShouldHideUIForFullscreen() const {
    885   return IsFullscreen();
    886 }
    887 
    888 bool BrowserWindowGtk::IsFullscreen() const {
    889   return (state_ & GDK_WINDOW_STATE_FULLSCREEN);
    890 }
    891 
    892 bool BrowserWindowGtk::IsFullscreenBubbleVisible() const {
    893   return fullscreen_exit_bubble_ != NULL;
    894 }
    895 
    896 LocationBar* BrowserWindowGtk::GetLocationBar() const {
    897   return toolbar_->GetLocationBar();
    898 }
    899 
    900 void BrowserWindowGtk::SetFocusToLocationBar(bool select_all) {
    901   if (!IsFullscreen())
    902     GetLocationBar()->FocusLocation(select_all);
    903 }
    904 
    905 void BrowserWindowGtk::UpdateReloadStopState(bool is_loading, bool force) {
    906   toolbar_->GetReloadButton()->ChangeMode(
    907       is_loading ? ReloadButtonGtk::MODE_STOP : ReloadButtonGtk::MODE_RELOAD,
    908       force);
    909 }
    910 
    911 void BrowserWindowGtk::UpdateToolbar(content::WebContents* contents,
    912                                      bool should_restore_state) {
    913   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::UpdateToolbar");
    914   toolbar_->UpdateWebContents(contents, should_restore_state);
    915 }
    916 
    917 void BrowserWindowGtk::FocusToolbar() {
    918   NOTIMPLEMENTED();
    919 }
    920 
    921 void BrowserWindowGtk::FocusAppMenu() {
    922   NOTIMPLEMENTED();
    923 }
    924 
    925 void BrowserWindowGtk::FocusBookmarksToolbar() {
    926   NOTIMPLEMENTED();
    927 }
    928 
    929 void BrowserWindowGtk::FocusInfobars() {
    930   NOTIMPLEMENTED();
    931 }
    932 
    933 void BrowserWindowGtk::RotatePaneFocus(bool forwards) {
    934   NOTIMPLEMENTED();
    935 }
    936 
    937 bool BrowserWindowGtk::IsBookmarkBarVisible() const {
    938   return browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR) &&
    939          bookmark_bar_.get() &&
    940          browser_->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
    941 }
    942 
    943 bool BrowserWindowGtk::IsBookmarkBarAnimating() const {
    944   if (IsBookmarkBarSupported() && bookmark_bar_->IsAnimating())
    945     return true;
    946   return false;
    947 }
    948 
    949 bool BrowserWindowGtk::IsTabStripEditable() const {
    950   return !tabstrip()->IsDragSessionActive() &&
    951       !tabstrip()->IsActiveDropTarget();
    952 }
    953 
    954 bool BrowserWindowGtk::IsToolbarVisible() const {
    955   return IsToolbarSupported();
    956 }
    957 
    958 gfx::Rect BrowserWindowGtk::GetRootWindowResizerRect() const {
    959   return gfx::Rect();
    960 }
    961 
    962 void BrowserWindowGtk::ConfirmAddSearchProvider(TemplateURL* template_url,
    963                                                 Profile* profile) {
    964   new EditSearchEngineDialog(window_, template_url, NULL, profile);
    965 }
    966 
    967 void BrowserWindowGtk::ToggleBookmarkBar() {
    968   chrome::ToggleBookmarkBarWhenVisible(browser_->profile());
    969 }
    970 
    971 void BrowserWindowGtk::ShowUpdateChromeDialog() {
    972   UpdateRecommendedDialog::Show(window_);
    973 }
    974 
    975 void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url,
    976                                           bool already_bookmarked) {
    977   toolbar_->GetLocationBarView()->ShowStarBubble(url, !already_bookmarked);
    978 }
    979 
    980 #if defined(ENABLE_ONE_CLICK_SIGNIN)
    981 void BrowserWindowGtk::ShowOneClickSigninBubble(
    982     OneClickSigninBubbleType type,
    983     const string16& email,
    984     const string16& error_message,
    985     const StartSyncCallback& start_sync_callback) {
    986 
    987   new OneClickSigninBubbleGtk(this, type, email,
    988                               error_message, start_sync_callback);
    989 }
    990 #endif
    991 
    992 bool BrowserWindowGtk::IsDownloadShelfVisible() const {
    993   return download_shelf_.get() && download_shelf_->IsShowing();
    994 }
    995 
    996 DownloadShelf* BrowserWindowGtk::GetDownloadShelf() {
    997   if (!download_shelf_.get())
    998     download_shelf_.reset(new DownloadShelfGtk(browser_.get(),
    999                                                render_area_vbox_));
   1000   return download_shelf_.get();
   1001 }
   1002 
   1003 void BrowserWindowGtk::UserChangedTheme() {
   1004   SetBackgroundColor();
   1005   InvalidateWindow();
   1006   UpdateWindowShape(bounds_.width(), bounds_.height());
   1007 }
   1008 
   1009 int BrowserWindowGtk::GetExtraRenderViewHeight() const {
   1010   int sum = infobar_container_->TotalHeightOfAnimatingBars();
   1011   if (IsBookmarkBarSupported() && bookmark_bar_->IsAnimating())
   1012     sum += bookmark_bar_->GetHeight();
   1013   if (download_shelf_.get() && download_shelf_->IsClosing())
   1014     sum += download_shelf_->GetHeight();
   1015   return sum;
   1016 }
   1017 
   1018 void BrowserWindowGtk::WebContentsFocused(WebContents* contents) {
   1019   NOTIMPLEMENTED();
   1020 }
   1021 
   1022 void BrowserWindowGtk::ShowWebsiteSettings(
   1023     Profile* profile,
   1024     content::WebContents* web_contents,
   1025     const GURL& url,
   1026     const content::SSLStatus& ssl) {
   1027   WebsiteSettingsPopupGtk::Show(GetNativeWindow(), profile, web_contents, url,
   1028                                 ssl);
   1029 }
   1030 
   1031 void BrowserWindowGtk::ShowAppMenu() {
   1032   toolbar_->ShowAppMenu();
   1033 }
   1034 
   1035 bool BrowserWindowGtk::PreHandleKeyboardEvent(
   1036     const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
   1037   GdkEventKey* os_event = &event.os_event->key;
   1038 
   1039   if (!os_event || event.type != WebKit::WebInputEvent::RawKeyDown)
   1040     return false;
   1041 
   1042   if (ExtensionKeybindingRegistryGtk::shortcut_handling_suspended())
   1043     return false;
   1044 
   1045   // We first find out the browser command associated to the |event|.
   1046   // Then if the command is a reserved one, and should be processed immediately
   1047   // according to the |event|, the command will be executed immediately.
   1048   // Otherwise we just set |*is_keyboard_shortcut| properly and return false.
   1049 
   1050   // First check if it's a custom accelerator.
   1051   int id = GetCustomCommandId(os_event);
   1052 
   1053   // Then check if it's a predefined accelerator bound to the window.
   1054   if (id == -1) {
   1055     // This piece of code is based on the fact that calling
   1056     // gtk_window_activate_key() method against |window_| may only trigger a
   1057     // browser command execution, by matching a global accelerator
   1058     // defined in above |kAcceleratorMap|.
   1059     //
   1060     // Here we need to retrieve the command id (if any) associated to the
   1061     // keyboard event. Instead of looking up the command id in above
   1062     // |kAcceleratorMap| table by ourselves, we block the command execution of
   1063     // the |browser_| object then send the keyboard event to the |window_| by
   1064     // calling gtk_window_activate_key() method, as if we are activating an
   1065     // accelerator key. Then we can retrieve the command id from the
   1066     // |browser_| object.
   1067     //
   1068     // Pros of this approach:
   1069     // 1. We don't need to care about keyboard layout problem, as
   1070     //    gtk_window_activate_key() method handles it for us.
   1071     //
   1072     // Cons:
   1073     // 1. The logic is a little complicated.
   1074     // 2. We should be careful not to introduce any accelerators that trigger
   1075     //    customized code instead of browser commands.
   1076     browser_->command_controller()->SetBlockCommandExecution(true);
   1077     gtk_window_activate_key(window_, os_event);
   1078     // We don't need to care about the WindowOpenDisposition value,
   1079     // because all commands executed in this path use the default value.
   1080     id = browser_->command_controller()->GetLastBlockedCommand(NULL);
   1081     browser_->command_controller()->SetBlockCommandExecution(false);
   1082   }
   1083 
   1084   if (id == -1)
   1085     return false;
   1086 
   1087   // Executing the command may cause |this| object to be destroyed.
   1088   if (browser_->command_controller()->IsReservedCommandOrKey(id, event) &&
   1089       !event.match_edit_command) {
   1090     return chrome::ExecuteCommand(browser_.get(), id);
   1091   }
   1092 
   1093   // The |event| is a keyboard shortcut.
   1094   DCHECK(is_keyboard_shortcut != NULL);
   1095   *is_keyboard_shortcut = true;
   1096 
   1097   return false;
   1098 }
   1099 
   1100 void BrowserWindowGtk::HandleKeyboardEvent(
   1101     const NativeWebKeyboardEvent& event) {
   1102   GdkEventKey* os_event = &event.os_event->key;
   1103 
   1104   if (!os_event || event.type != WebKit::WebInputEvent::RawKeyDown)
   1105     return;
   1106 
   1107   // Handles a key event in following sequence:
   1108   // 1. Our special key accelerators, such as ctrl-tab, etc.
   1109   // 2. Gtk accelerators.
   1110   // This sequence matches the default key press handler of GtkWindow.
   1111   //
   1112   // It's not necessary to care about the keyboard layout, as
   1113   // gtk_window_activate_key() takes care of it automatically.
   1114   int id = GetCustomCommandId(os_event);
   1115   if (id != -1)
   1116     chrome::ExecuteCommand(browser_.get(), id);
   1117   else
   1118     gtk_window_activate_key(window_, os_event);
   1119 }
   1120 
   1121 void BrowserWindowGtk::ShowCreateChromeAppShortcutsDialog(
   1122     Profile* profile, const extensions::Extension* app) {
   1123   CreateChromeApplicationShortcutsDialogGtk::Show(window_, profile, app);
   1124 }
   1125 
   1126 void BrowserWindowGtk::Cut() {
   1127   gtk_window_util::DoCut(
   1128       window_, browser_->tab_strip_model()->GetActiveWebContents());
   1129 }
   1130 
   1131 void BrowserWindowGtk::Copy() {
   1132   gtk_window_util::DoCopy(
   1133       window_, browser_->tab_strip_model()->GetActiveWebContents());
   1134 }
   1135 
   1136 void BrowserWindowGtk::Paste() {
   1137   gtk_window_util::DoPaste(
   1138       window_, browser_->tab_strip_model()->GetActiveWebContents());
   1139 }
   1140 
   1141 WindowOpenDisposition BrowserWindowGtk::GetDispositionForPopupBounds(
   1142     const gfx::Rect& bounds) {
   1143   return NEW_POPUP;
   1144 }
   1145 
   1146 FindBar* BrowserWindowGtk::CreateFindBar() {
   1147   return new FindBarGtk(this);
   1148 }
   1149 
   1150 WebContentsModalDialogHost* BrowserWindowGtk::GetWebContentsModalDialogHost() {
   1151   return NULL;
   1152 }
   1153 
   1154 void BrowserWindowGtk::ShowAvatarBubble(WebContents* web_contents,
   1155                                         const gfx::Rect& rect) {
   1156   GtkWidget* widget = web_contents->GetView()->GetContentNativeView();
   1157   new AvatarMenuBubbleGtk(browser_.get(), widget,
   1158       BubbleGtk::ANCHOR_TOP_LEFT, &rect);
   1159 }
   1160 
   1161 void BrowserWindowGtk::ShowAvatarBubbleFromAvatarButton() {
   1162   if (titlebar_->avatar_button())
   1163     titlebar_->avatar_button()->ShowAvatarBubble();
   1164 }
   1165 
   1166 void BrowserWindowGtk::ShowPasswordGenerationBubble(
   1167     const gfx::Rect& rect,
   1168     const content::PasswordForm& form,
   1169     autofill::PasswordGenerator* password_generator) {
   1170   WebContents* web_contents =
   1171       browser_->tab_strip_model()->GetActiveWebContents();
   1172   if (!web_contents || !web_contents->GetView()->GetContentNativeView()) {
   1173     return;
   1174   }
   1175 
   1176   new PasswordGenerationBubbleGtk(rect, form, web_contents, password_generator);
   1177 }
   1178 
   1179 void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() {
   1180   DownloadInProgressDialogGtk::Show(browser(), GetNativeWindow());
   1181 }
   1182 
   1183 void BrowserWindowGtk::Observe(int type,
   1184                                const content::NotificationSource& source,
   1185                                const content::NotificationDetails& details) {
   1186   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, type);
   1187   // The profile avatar icon may have changed.
   1188   gtk_util::SetWindowIcon(window_, browser_->profile());
   1189 }
   1190 
   1191 void BrowserWindowGtk::TabDetachedAt(WebContents* contents, int index) {
   1192   // We use index here rather than comparing |contents| because by this time
   1193   // the model has already removed |contents| from its list, so
   1194   // browser_->tab_strip_model()->GetActiveWebContents() will return NULL or
   1195   // something else.
   1196   if (index == browser_->tab_strip_model()->active_index()) {
   1197     infobar_container_->ChangeInfoBarService(NULL);
   1198     UpdateDevToolsForContents(NULL);
   1199   }
   1200   contents_container_->DetachTab(contents);
   1201 }
   1202 
   1203 void BrowserWindowGtk::ActiveTabChanged(WebContents* old_contents,
   1204                                         WebContents* new_contents,
   1205                                         int index,
   1206                                         int reason) {
   1207   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::ActiveTabChanged");
   1208   if (old_contents && !old_contents->IsBeingDestroyed())
   1209     old_contents->GetView()->StoreFocus();
   1210 
   1211   // Update various elements that are interested in knowing the current
   1212   // WebContents.
   1213   UpdateDevToolsForContents(new_contents);
   1214   infobar_container_->ChangeInfoBarService(
   1215       InfoBarService::FromWebContents(new_contents));
   1216   contents_container_->SetTab(new_contents);
   1217 
   1218   // TODO(estade): after we manage browser activation, add a check to make sure
   1219   // we are the active browser before calling RestoreFocus().
   1220   if (!browser_->tab_strip_model()->closing_all()) {
   1221     new_contents->GetView()->RestoreFocus();
   1222     FindTabHelper* find_tab_helper =
   1223         FindTabHelper::FromWebContents(new_contents);
   1224     if (find_tab_helper->find_ui_active())
   1225       browser_->GetFindBarController()->find_bar()->SetFocusAndSelection();
   1226   }
   1227 
   1228   // Update all the UI bits.
   1229   UpdateTitleBar();
   1230   MaybeShowBookmarkBar(false);
   1231 }
   1232 
   1233 void BrowserWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
   1234   // Do nothing if we're in the process of closing the browser window.
   1235   if (!window_)
   1236     return;
   1237 
   1238   bool is_active = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window;
   1239   bool changed = (is_active != is_active_);
   1240 
   1241   if (is_active && changed) {
   1242     // If there's an app modal dialog (e.g., JS alert), try to redirect
   1243     // the user's attention to the window owning the dialog.
   1244     if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) {
   1245       AppModalDialogQueue::GetInstance()->ActivateModalDialog();
   1246       return;
   1247     }
   1248   }
   1249 
   1250   is_active_ = is_active;
   1251   if (changed) {
   1252     SetBackgroundColor();
   1253     InvalidateWindow();
   1254     // For some reason, the above two calls cause the window shape to be
   1255     // lost so reset it.
   1256     UpdateWindowShape(bounds_.width(), bounds_.height());
   1257   }
   1258 }
   1259 
   1260 SkColor BrowserWindowGtk::GetInfoBarSeparatorColor() const {
   1261   GtkThemeService* theme_service = GtkThemeService::GetFrom(
   1262       browser()->profile());
   1263   return gfx::GdkColorToSkColor(theme_service->GetBorderColor());
   1264 }
   1265 
   1266 void BrowserWindowGtk::InfoBarContainerStateChanged(bool is_animating) {
   1267   InvalidateInfoBarBits();
   1268 }
   1269 
   1270 bool BrowserWindowGtk::DrawInfoBarArrows(int* x) const {
   1271   if (x) {
   1272     // This is a views specific call that made its way into the interface. We
   1273     // go through GetXPositionOfLocationIcon() since we need widget relativity.
   1274     *x = 0;
   1275     NOTREACHED();
   1276   }
   1277   return true;
   1278 }
   1279 
   1280 extensions::ActiveTabPermissionGranter*
   1281     BrowserWindowGtk::GetActiveTabPermissionGranter() {
   1282   WebContents* tab = GetDisplayedTab();
   1283   if (!tab)
   1284     return NULL;
   1285   return extensions::TabHelper::FromWebContents(tab)->
   1286       active_tab_permission_granter();
   1287 }
   1288 
   1289 void BrowserWindowGtk::DestroyBrowser() {
   1290   browser_.reset();
   1291 }
   1292 
   1293 gboolean BrowserWindowGtk::OnConfigure(GtkWidget* widget,
   1294                                        GdkEventConfigure* event) {
   1295   gfx::Rect bounds(event->x, event->y, event->width, event->height);
   1296 
   1297   // When the window moves, we'll get multiple configure-event signals. We can
   1298   // also get events when the bounds haven't changed, but the window's stacking
   1299   // has, which we aren't interested in. http://crbug.com/70125
   1300   if (bounds == configure_bounds_)
   1301     return FALSE;
   1302 
   1303   GetLocationBar()->GetLocationEntry()->CloseOmniboxPopup();
   1304 
   1305   WebContents* tab = GetDisplayedTab();
   1306   if (tab)
   1307     tab->GetRenderViewHost()->NotifyMoveOrResizeStarted();
   1308 
   1309   if (bounds_.size() != bounds.size())
   1310     UpdateWindowShape(bounds.width(), bounds.height());
   1311 
   1312   // We update |bounds_| but not |restored_bounds_| here.  The latter needs
   1313   // to be updated conditionally when the window is non-maximized and non-
   1314   // fullscreen, but whether those state updates have been processed yet is
   1315   // window-manager specific.  We update |restored_bounds_| in the debounced
   1316   // handler below, after the window state has been updated.
   1317   bounds_ = bounds;
   1318   configure_bounds_ = bounds;
   1319 
   1320   // The GdkEventConfigure* we get here doesn't have quite the right
   1321   // coordinates though (they're relative to the drawable window area, rather
   1322   // than any window manager decorations, if enabled), so we need to call
   1323   // gtk_window_get_position() to get the right values. (Otherwise session
   1324   // restore, if enabled, will restore windows to incorrect positions.) That's
   1325   // a round trip to the X server though, so we set a debounce timer and only
   1326   // call it (in OnDebouncedBoundsChanged() below) after we haven't seen a
   1327   // reconfigure event in a short while.
   1328   // We don't use Reset() because the timer may not yet be running.
   1329   // (In that case Stop() is a no-op.)
   1330   window_configure_debounce_timer_.Stop();
   1331   window_configure_debounce_timer_.Start(FROM_HERE,
   1332       base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this,
   1333       &BrowserWindowGtk::OnDebouncedBoundsChanged);
   1334 
   1335   return FALSE;
   1336 }
   1337 
   1338 void BrowserWindowGtk::OnDebouncedBoundsChanged() {
   1339   gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_);
   1340   SaveWindowPosition();
   1341 }
   1342 
   1343 gboolean BrowserWindowGtk::OnWindowState(GtkWidget* sender,
   1344                                          GdkEventWindowState* event) {
   1345   state_ = event->new_window_state;
   1346 
   1347   if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
   1348     browser_->WindowFullscreenStateChanged();
   1349     if (state_ & GDK_WINDOW_STATE_FULLSCREEN) {
   1350       UpdateCustomFrame();
   1351       toolbar_->Hide();
   1352       tabstrip_->Hide();
   1353       if (bookmark_bar_.get())
   1354         gtk_widget_hide(bookmark_bar_->widget());
   1355       bool is_kiosk =
   1356           CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
   1357       if (!is_kiosk && !fullscreen_exit_bubble_.get()) {
   1358         fullscreen_exit_bubble_.reset(new FullscreenExitBubbleGtk(
   1359             GTK_FLOATING_CONTAINER(render_area_floating_container_),
   1360             browser(),
   1361             GURL(),
   1362             fullscreen_exit_bubble_type_));
   1363       }
   1364       gtk_widget_hide(titlebar_widget());
   1365       gtk_widget_hide(toolbar_border_);
   1366     } else {
   1367       fullscreen_exit_bubble_type_ =
   1368           FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION;
   1369       gtk_widget_show(titlebar_widget());
   1370       fullscreen_exit_bubble_.reset();
   1371       UpdateCustomFrame();
   1372       ShowSupportedWindowFeatures();
   1373     }
   1374   }
   1375 
   1376   if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
   1377     content::NotificationService::current()->Notify(
   1378         chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED,
   1379         content::Source<BrowserWindow>(this),
   1380         content::NotificationService::NoDetails());
   1381   }
   1382 
   1383   titlebar_->UpdateCustomFrame(UseCustomFrame() && !IsFullscreen());
   1384   UpdateWindowShape(bounds_.width(), bounds_.height());
   1385   SaveWindowPosition();
   1386   return FALSE;
   1387 }
   1388 
   1389 // Callback for the delete event.  This event is fired when the user tries to
   1390 // close the window (e.g., clicking on the X in the window manager title bar).
   1391 gboolean BrowserWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget,
   1392                                                    GdkEvent* event) {
   1393   Close();
   1394 
   1395   // Return true to prevent the gtk window from being destroyed.  Close will
   1396   // destroy it for us.
   1397   return TRUE;
   1398 }
   1399 
   1400 void BrowserWindowGtk::OnMainWindowDestroy(GtkWidget* widget) {
   1401   // Make sure we destroy this object while the main window is still valid.
   1402   extension_keybinding_registry_.reset();
   1403 
   1404   // BUG 8712. When we gtk_widget_destroy() in Close(), this will emit the
   1405   // signal right away, and we will be here (while Close() is still in the
   1406   // call stack).  In order to not reenter Close(), and to also follow the
   1407   // expectations of BrowserList, we should run the BrowserWindowGtk destructor
   1408   // not now, but after the run loop goes back to process messages.  Otherwise
   1409   // we will remove ourself from BrowserList while it's being iterated.
   1410   // Additionally, now that we know the window is gone, we need to make sure to
   1411   // set window_ to NULL, otherwise we will try to close the window again when
   1412   // we call Close() in the destructor.
   1413   //
   1414   // We don't want to use DeleteSoon() here since it won't work on a nested pump
   1415   // (like in UI tests).
   1416   base::MessageLoop::current()->PostTask(
   1417       FROM_HERE, base::Bind(&base::DeletePointer<BrowserWindowGtk>, this));
   1418 }
   1419 
   1420 void BrowserWindowGtk::UnMaximize() {
   1421   gtk_window_util::UnMaximize(window_, bounds_, restored_bounds_);
   1422 }
   1423 
   1424 bool BrowserWindowGtk::CanClose() const {
   1425   // You cannot close a frame for which there is an active originating drag
   1426   // session.
   1427   if (tabstrip_->IsDragSessionActive())
   1428     return false;
   1429 
   1430   // Give beforeunload handlers the chance to cancel the close before we hide
   1431   // the window below.
   1432   if (!browser_->ShouldCloseWindow())
   1433     return false;
   1434 
   1435   bool fast_tab_closing_enabled =
   1436       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFastUnload);
   1437 
   1438   if (!browser_->tab_strip_model()->empty()) {
   1439     // Tab strip isn't empty.  Hide the window (so it appears to have closed
   1440     // immediately) and close all the tabs, allowing the renderers to shut
   1441     // down. When the tab strip is empty we'll be called back again.
   1442     gtk_widget_hide(GTK_WIDGET(window_));
   1443     browser_->OnWindowClosing();
   1444 
   1445     if (fast_tab_closing_enabled)
   1446       browser_->tab_strip_model()->CloseAllTabs();
   1447     return false;
   1448   } else if (fast_tab_closing_enabled &&
   1449       !browser_->HasCompletedUnloadProcessing()) {
   1450     // The browser needs to finish running unload handlers.
   1451     // Hide the window (so it appears to have closed immediately), and
   1452     // the browser will call us back again when it is ready to close.
   1453     gtk_widget_hide(GTK_WIDGET(window_));
   1454     return false;
   1455   }
   1456 
   1457   // Empty TabStripModel, it's now safe to allow the Window to be closed.
   1458   content::NotificationService::current()->Notify(
   1459       chrome::NOTIFICATION_WINDOW_CLOSED,
   1460       content::Source<GtkWindow>(window_),
   1461       content::NotificationService::NoDetails());
   1462   return true;
   1463 }
   1464 
   1465 bool BrowserWindowGtk::ShouldShowWindowIcon() const {
   1466   return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR);
   1467 }
   1468 
   1469 void BrowserWindowGtk::AddFindBar(FindBarGtk* findbar) {
   1470   gtk_floating_container_add_floating(
   1471       GTK_FLOATING_CONTAINER(render_area_floating_container_),
   1472       findbar->widget());
   1473 }
   1474 
   1475 void BrowserWindowGtk::ResetCustomFrameCursor() {
   1476   if (!frame_cursor_)
   1477     return;
   1478 
   1479   frame_cursor_ = NULL;
   1480   gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
   1481 }
   1482 
   1483 // static
   1484 BrowserWindowGtk* BrowserWindowGtk::GetBrowserWindowForNativeWindow(
   1485     gfx::NativeWindow window) {
   1486   if (window) {
   1487     return static_cast<BrowserWindowGtk*>(
   1488         g_object_get_qdata(G_OBJECT(window), GetBrowserWindowQuarkKey()));
   1489   }
   1490 
   1491   return NULL;
   1492 }
   1493 
   1494 // static
   1495 GtkWindow* BrowserWindowGtk::GetBrowserWindowForXID(XID xid) {
   1496   GtkWindow* window = ui::GetGtkWindowFromX11Window(xid);
   1497   // Use GetBrowserWindowForNativeWindow() to verify the GtkWindow we found
   1498   // is actually a browser window (and not e.g. a dialog).
   1499   if (!GetBrowserWindowForNativeWindow(window))
   1500     return NULL;
   1501   return window;
   1502 }
   1503 
   1504 GtkWidget* BrowserWindowGtk::titlebar_widget() const {
   1505   return titlebar_->widget();
   1506 }
   1507 
   1508 // static
   1509 void BrowserWindowGtk::RegisterProfilePrefs(
   1510     user_prefs::PrefRegistrySyncable* registry) {
   1511   bool custom_frame_default = false;
   1512   // Avoid checking the window manager if we're not connected to an X server (as
   1513   // is the case in Valgrind tests).
   1514   if (ui::XDisplayExists())
   1515     custom_frame_default = GetCustomFramePrefDefault();
   1516 
   1517   registry->RegisterBooleanPref(
   1518       prefs::kUseCustomChromeFrame,
   1519       custom_frame_default,
   1520       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   1521 }
   1522 
   1523 WebContents* BrowserWindowGtk::GetDisplayedTab() {
   1524   return contents_container_->GetVisibleTab();
   1525 }
   1526 
   1527 void BrowserWindowGtk::QueueToolbarRedraw() {
   1528   gtk_widget_queue_draw(toolbar_->widget());
   1529 }
   1530 
   1531 void BrowserWindowGtk::SetGeometryHints() {
   1532   // If we call gtk_window_maximize followed by gtk_window_present, compiz gets
   1533   // confused and maximizes the window, but doesn't set the
   1534   // GDK_WINDOW_STATE_MAXIMIZED bit.  So instead, we keep track of whether to
   1535   // maximize and call it after gtk_window_present.
   1536   gfx::Rect bounds;
   1537   chrome::GetSavedWindowBoundsAndShowState(browser_.get(),
   1538                                            &bounds,
   1539                                            &show_state_after_show_);
   1540   // We don't blindly call SetBounds here: that sets a forced position
   1541   // on the window and we intentionally *don't* do that for normal
   1542   // windows.  Most programs do not restore their window position on
   1543   // Linux, instead letting the window manager choose a position.
   1544   //
   1545   // However, in cases like dropping a tab where the bounds are
   1546   // specifically set, we do want to position explicitly.  We also
   1547   // force the position as part of session restore, as applications
   1548   // that restore other, similar state (for instance GIMP, audacity,
   1549   // pidgin, dia, and gkrellm) do tend to restore their positions.
   1550   //
   1551   // For popup windows, we assume that if x == y == 0, the opening page
   1552   // did not specify a position.  Let the WM position the popup instead.
   1553   bool is_popup = browser_->is_type_popup();
   1554   bool popup_without_position = is_popup &&
   1555       bounds.x() == 0 && bounds.y() == 0;
   1556   bool move = browser_->bounds_overridden() && !popup_without_position;
   1557   SetBoundsImpl(bounds, !is_popup, move);
   1558 }
   1559 
   1560 void BrowserWindowGtk::ConnectHandlersToSignals() {
   1561   g_signal_connect(window_, "delete-event",
   1562                    G_CALLBACK(OnMainWindowDeleteEventThunk), this);
   1563   g_signal_connect(window_, "destroy",
   1564                    G_CALLBACK(OnMainWindowDestroyThunk), this);
   1565   g_signal_connect(window_, "configure-event",
   1566                    G_CALLBACK(OnConfigureThunk), this);
   1567   g_signal_connect(window_, "window-state-event",
   1568                    G_CALLBACK(OnWindowStateThunk), this);
   1569   g_signal_connect(window_, "key-press-event",
   1570                    G_CALLBACK(OnKeyPressThunk), this);
   1571   g_signal_connect(window_, "motion-notify-event",
   1572                    G_CALLBACK(OnMouseMoveEventThunk), this);
   1573   g_signal_connect(window_, "button-press-event",
   1574                    G_CALLBACK(OnButtonPressEventThunk), this);
   1575   g_signal_connect(window_, "focus-in-event",
   1576                    G_CALLBACK(OnFocusInThunk), this);
   1577   g_signal_connect(window_, "focus-out-event",
   1578                    G_CALLBACK(OnFocusOutThunk), this);
   1579 }
   1580 
   1581 void BrowserWindowGtk::InitWidgets() {
   1582   ConnectHandlersToSignals();
   1583 
   1584   bounds_ = configure_bounds_ = restored_bounds_ =
   1585       GetInitialWindowBounds(window_);
   1586 
   1587   // This vbox encompasses all of the widgets within the browser.  This is
   1588   // everything except the custom frame border.
   1589   window_vbox_ = gtk_vbox_new(FALSE, 0);
   1590   gtk_widget_show(window_vbox_);
   1591 
   1592   // We hold an always hidden GtkMenuBar inside our browser window simply to
   1593   // fool the Unity desktop, which will mirror the contents of the first
   1594   // GtkMenuBar it sees into the global menu bar. (It doesn't seem to check the
   1595   // visibility of the GtkMenuBar, so we can just permanently hide it.)
   1596   global_menu_bar_.reset(new GlobalMenuBar(browser_.get()));
   1597   gtk_container_add(GTK_CONTAINER(window_vbox_), global_menu_bar_->widget());
   1598 
   1599   // The window container draws the custom browser frame.
   1600   window_container_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
   1601   gtk_widget_set_name(window_container_, "chrome-custom-frame-border");
   1602   gtk_widget_set_app_paintable(window_container_, TRUE);
   1603   gtk_widget_set_double_buffered(window_container_, FALSE);
   1604   gtk_widget_set_redraw_on_allocate(window_container_, TRUE);
   1605   g_signal_connect(window_container_, "expose-event",
   1606                    G_CALLBACK(OnCustomFrameExposeThunk), this);
   1607   gtk_container_add(GTK_CONTAINER(window_container_), window_vbox_);
   1608 
   1609   tabstrip_.reset(new TabStripGtk(browser_->tab_strip_model(), this));
   1610   tabstrip_->Init();
   1611 
   1612   // Build the titlebar (tabstrip + header space + min/max/close buttons).
   1613   titlebar_.reset(new BrowserTitlebar(this, window_));
   1614   titlebar_->Init();
   1615 
   1616   // Insert the tabstrip into the window.
   1617   gtk_box_pack_start(GTK_BOX(window_vbox_), titlebar_->widget(), FALSE, FALSE,
   1618                      0);
   1619 
   1620   toolbar_.reset(new BrowserToolbarGtk(browser_.get(), this));
   1621   toolbar_->Init(window_);
   1622   gtk_box_pack_start(GTK_BOX(window_vbox_), toolbar_->widget(),
   1623                      FALSE, FALSE, 0);
   1624   g_signal_connect_after(toolbar_->widget(), "expose-event",
   1625                          G_CALLBACK(OnExposeDrawInfobarBitsThunk), this);
   1626   // This vbox surrounds the render area: find bar, info bars and render view.
   1627   // The reason is that this area as a whole needs to be grouped in its own
   1628   // GdkWindow hierarchy so that animations originating inside it (infobar,
   1629   // download shelf, find bar) are all clipped to that area. This is why
   1630   // |render_area_vbox_| is packed in |render_area_event_box_|.
   1631   render_area_vbox_ = gtk_vbox_new(FALSE, 0);
   1632   gtk_widget_set_name(render_area_vbox_, "chrome-render-area-vbox");
   1633   render_area_floating_container_ = gtk_floating_container_new();
   1634   gtk_container_add(GTK_CONTAINER(render_area_floating_container_),
   1635                     render_area_vbox_);
   1636 
   1637   GtkWidget* location_icon = toolbar_->GetLocationBarView()->
   1638       location_icon_widget();
   1639   g_signal_connect(location_icon, "size-allocate",
   1640                    G_CALLBACK(OnLocationIconSizeAllocateThunk), this);
   1641   g_signal_connect_after(location_icon, "expose-event",
   1642                          G_CALLBACK(OnExposeDrawInfobarBitsThunk), this);
   1643 
   1644   toolbar_border_ = gtk_event_box_new();
   1645   gtk_box_pack_start(GTK_BOX(render_area_vbox_),
   1646                      toolbar_border_, FALSE, FALSE, 0);
   1647   gtk_widget_set_size_request(toolbar_border_, -1, 1);
   1648   gtk_widget_set_no_show_all(toolbar_border_, TRUE);
   1649   g_signal_connect_after(toolbar_border_, "expose-event",
   1650                          G_CALLBACK(OnExposeDrawInfobarBitsThunk), this);
   1651 
   1652   if (IsToolbarSupported())
   1653     gtk_widget_show(toolbar_border_);
   1654 
   1655   infobar_container_.reset(
   1656       new InfoBarContainerGtk(this, browser_->profile()));
   1657   gtk_box_pack_start(GTK_BOX(render_area_vbox_),
   1658                      infobar_container_->widget(),
   1659                      FALSE, FALSE, 0);
   1660 
   1661   status_bubble_.reset(new StatusBubbleGtk(browser_->profile()));
   1662 
   1663   contents_container_.reset(new TabContentsContainerGtk(status_bubble_.get()));
   1664   devtools_container_.reset(new TabContentsContainerGtk(NULL));
   1665   ViewIDUtil::SetID(devtools_container_->widget(), VIEW_ID_DEV_TOOLS_DOCKED);
   1666 
   1667   contents_hsplit_ = gtk_hpaned_new();
   1668   gtk_paned_pack1(GTK_PANED(contents_hsplit_), contents_container_->widget(),
   1669                   TRUE, TRUE);
   1670   contents_vsplit_ = gtk_vpaned_new();
   1671   gtk_paned_pack1(GTK_PANED(contents_vsplit_), contents_hsplit_, TRUE, TRUE);
   1672 
   1673   gtk_box_pack_end(GTK_BOX(render_area_vbox_),
   1674                    contents_vsplit_, TRUE, TRUE, 0);
   1675 
   1676   gtk_widget_show_all(render_area_floating_container_);
   1677   render_area_event_box_ = gtk_event_box_new();
   1678   // Set a white background so during startup the user sees white in the
   1679   // content area before we get a WebContents in place.
   1680   gtk_widget_modify_bg(render_area_event_box_, GTK_STATE_NORMAL,
   1681                        &ui::kGdkWhite);
   1682   gtk_container_add(GTK_CONTAINER(render_area_event_box_),
   1683                     render_area_floating_container_);
   1684   gtk_widget_show(render_area_event_box_);
   1685   gtk_box_pack_end(GTK_BOX(window_vbox_), render_area_event_box_,
   1686                    TRUE, TRUE, 0);
   1687 
   1688   if (IsBookmarkBarSupported()) {
   1689     bookmark_bar_.reset(new BookmarkBarGtk(this,
   1690                                            browser_.get(),
   1691                                            tabstrip_.get()));
   1692     PlaceBookmarkBar(false);
   1693     gtk_widget_show(bookmark_bar_->widget());
   1694 
   1695     g_signal_connect_after(bookmark_bar_->widget(), "expose-event",
   1696                            G_CALLBACK(OnBookmarkBarExposeThunk), this);
   1697     g_signal_connect(bookmark_bar_->widget(), "size-allocate",
   1698                      G_CALLBACK(OnBookmarkBarSizeAllocateThunk), this);
   1699   }
   1700 
   1701   // We have to realize the window before we try to apply a window shape mask.
   1702   gtk_widget_realize(GTK_WIDGET(window_));
   1703   state_ = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(window_)));
   1704   // Note that calling this the first time is necessary to get the
   1705   // proper control layout.
   1706   UpdateCustomFrame();
   1707 
   1708   // Add the keybinding registry, now that the window has been realized.
   1709   extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk(
   1710       browser_->profile(),
   1711       window_,
   1712       extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
   1713       this));
   1714 
   1715   // We have to call this after the first window is created, but after that only
   1716   // when the theme changes. This sets the icon that will be used for windows
   1717   // that have not explicitly been assigned an icon.
   1718   static bool default_icon_set = false;
   1719   if (!default_icon_set) {
   1720     gtk_util::SetDefaultWindowIcon(window_);
   1721     default_icon_set = true;
   1722   }
   1723   // Set this window's (potentially profile-avatar-emblemed) icon, overriding
   1724   // the default.
   1725   gtk_util::SetWindowIcon(window_, browser_->profile());
   1726 
   1727   gtk_container_add(GTK_CONTAINER(window_), window_container_);
   1728   gtk_widget_show(window_container_);
   1729   browser_->tab_strip_model()->AddObserver(this);
   1730 }
   1731 
   1732 void BrowserWindowGtk::SetBackgroundColor() {
   1733   Profile* profile = browser()->profile();
   1734   GtkThemeService* theme_provider = GtkThemeService::GetFrom(profile);
   1735   int frame_color_id;
   1736   if (UsingCustomPopupFrame()) {
   1737     frame_color_id = ThemeProperties::COLOR_TOOLBAR;
   1738   } else if (DrawFrameAsActive()) {
   1739     frame_color_id = browser()->profile()->IsOffTheRecord()
   1740        ? ThemeProperties::COLOR_FRAME_INCOGNITO
   1741        : ThemeProperties::COLOR_FRAME;
   1742   } else {
   1743     frame_color_id = browser()->profile()->IsOffTheRecord()
   1744        ? ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE
   1745        : ThemeProperties::COLOR_FRAME_INACTIVE;
   1746   }
   1747 
   1748   SkColor frame_color = theme_provider->GetColor(frame_color_id);
   1749 
   1750   // Paint the frame color on the left, right and bottom.
   1751   GdkColor frame_color_gdk = gfx::SkColorToGdkColor(frame_color);
   1752   gtk_widget_modify_bg(GTK_WIDGET(window_), GTK_STATE_NORMAL,
   1753                        &frame_color_gdk);
   1754 
   1755   // Set the color of the dev tools divider.
   1756   gtk_widget_modify_bg(contents_vsplit_, GTK_STATE_NORMAL, &frame_color_gdk);
   1757   gtk_widget_modify_bg(contents_hsplit_, GTK_STATE_NORMAL, &frame_color_gdk);
   1758 
   1759   // When the cursor is over the divider, GTK+ normally lightens the background
   1760   // color by 1.3 (see LIGHTNESS_MULT in gtkstyle.c).  Since we're setting the
   1761   // color, override the prelight also.
   1762   color_utils::HSL hsl = { -1, 0.5, 0.65 };
   1763   SkColor frame_prelight_color = color_utils::HSLShift(frame_color, hsl);
   1764   GdkColor frame_prelight_color_gdk =
   1765       gfx::SkColorToGdkColor(frame_prelight_color);
   1766   gtk_widget_modify_bg(contents_hsplit_, GTK_STATE_PRELIGHT,
   1767       &frame_prelight_color_gdk);
   1768   gtk_widget_modify_bg(contents_vsplit_, GTK_STATE_PRELIGHT,
   1769       &frame_prelight_color_gdk);
   1770 
   1771   GdkColor border_color = theme_provider->GetBorderColor();
   1772   gtk_widget_modify_bg(toolbar_border_, GTK_STATE_NORMAL, &border_color);
   1773 }
   1774 
   1775 void BrowserWindowGtk::UpdateWindowShape(int width, int height) {
   1776   using gtk_window_util::kFrameBorderThickness;
   1777   GdkRegion* mask = GetWindowShape(width, height);
   1778   gdk_window_shape_combine_region(
   1779       gtk_widget_get_window(GTK_WIDGET(window_)), mask, 0, 0);
   1780   if (mask)
   1781     gdk_region_destroy(mask);
   1782 
   1783   if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
   1784     gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 1,
   1785         kFrameBorderThickness, kFrameBorderThickness, kFrameBorderThickness);
   1786   } else {
   1787     gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 0, 0, 0, 0);
   1788   }
   1789 }
   1790 
   1791 GdkRegion* BrowserWindowGtk::GetWindowShape(int width, int height) const {
   1792   if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
   1793     // Make the corners rounded.  We set a mask that includes most of the
   1794     // window except for a few pixels in each corner.
   1795     GdkRectangle top_top_rect = { 3, 0, width - 6, 1 };
   1796     GdkRectangle top_mid_rect = { 1, 1, width - 2, 2 };
   1797     GdkRectangle mid_rect = { 0, 3, width, height - 6 };
   1798     // The bottom two rects are mirror images of the top two rects.
   1799     GdkRectangle bot_mid_rect = top_mid_rect;
   1800     bot_mid_rect.y = height - 3;
   1801     GdkRectangle bot_bot_rect = top_top_rect;
   1802     bot_bot_rect.y = height - 1;
   1803     GdkRegion* mask = gdk_region_rectangle(&top_top_rect);
   1804     gdk_region_union_with_rect(mask, &top_mid_rect);
   1805     gdk_region_union_with_rect(mask, &mid_rect);
   1806     gdk_region_union_with_rect(mask, &bot_mid_rect);
   1807     gdk_region_union_with_rect(mask, &bot_bot_rect);
   1808     return mask;
   1809   } else if (UseCustomFrame()) {
   1810     // Disable rounded corners.  Simply passing in a NULL region doesn't
   1811     // seem to work on KWin, so manually set the shape to the whole window.
   1812     GdkRectangle rect = { 0, 0, width, height };
   1813     GdkRegion* mask = gdk_region_rectangle(&rect);
   1814     return mask;
   1815   } else {
   1816     // XFCE disables the system decorations if there's an xshape set. Do not
   1817     // use the KWin hack when the custom frame is not enabled.
   1818     return NULL;
   1819   }
   1820 }
   1821 
   1822 void BrowserWindowGtk::ConnectAccelerators() {
   1823   accel_group_ = gtk_accel_group_new();
   1824   gtk_window_add_accel_group(window_, accel_group_);
   1825 
   1826   AcceleratorsGtk* accelerators = AcceleratorsGtk::GetInstance();
   1827   for (AcceleratorsGtk::const_iterator iter = accelerators->begin();
   1828        iter != accelerators->end(); ++iter) {
   1829     gtk_accel_group_connect(
   1830         accel_group_,
   1831         ui::GetGdkKeyCodeForAccelerator(iter->second),
   1832         ui::GetGdkModifierForAccelerator(iter->second),
   1833         GtkAccelFlags(0),
   1834         g_cclosure_new(G_CALLBACK(OnGtkAccelerator),
   1835                        GINT_TO_POINTER(iter->first), NULL));
   1836   }
   1837 }
   1838 
   1839 void BrowserWindowGtk::UpdateCustomFrame() {
   1840   gtk_window_set_decorated(window_, !UseCustomFrame());
   1841   titlebar_->UpdateCustomFrame(UseCustomFrame() && !IsFullscreen());
   1842   UpdateWindowShape(bounds_.width(), bounds_.height());
   1843 }
   1844 
   1845 void BrowserWindowGtk::InvalidateWindow() {
   1846   GtkAllocation allocation;
   1847   gtk_widget_get_allocation(GTK_WIDGET(window_), &allocation);
   1848   gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(window_)),
   1849                              &allocation, TRUE);
   1850 }
   1851 
   1852 void BrowserWindowGtk::SaveWindowPosition() {
   1853   // Browser::SaveWindowPlacement is used for session restore.
   1854   ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
   1855   if (IsMaximized())
   1856     show_state = ui::SHOW_STATE_MAXIMIZED;
   1857   else if (IsMinimized())
   1858     show_state = ui::SHOW_STATE_MINIMIZED;
   1859 
   1860   if (chrome::ShouldSaveWindowPlacement(browser_.get()))
   1861     chrome::SaveWindowPlacement(browser_.get(), restored_bounds_, show_state);
   1862 
   1863   // We also need to save the placement for startup.
   1864   // This is a web of calls between views and delegates on Windows, but the
   1865   // crux of the logic follows.  See also cocoa/browser_window_controller.mm.
   1866   if (!browser_->profile()->GetPrefs())
   1867     return;
   1868 
   1869   std::string window_name = chrome::GetWindowPlacementKey(browser_.get());
   1870   DictionaryPrefUpdate update(browser_->profile()->GetPrefs(),
   1871                               window_name.c_str());
   1872   DictionaryValue* window_preferences = update.Get();
   1873   // Note that we store left/top for consistency with Windows, but that we
   1874   // *don't* obey them; we only use them for computing width/height.  See
   1875   // comments in SetGeometryHints().
   1876   window_preferences->SetInteger("left", restored_bounds_.x());
   1877   window_preferences->SetInteger("top", restored_bounds_.y());
   1878   window_preferences->SetInteger("right", restored_bounds_.right());
   1879   window_preferences->SetInteger("bottom", restored_bounds_.bottom());
   1880   window_preferences->SetBoolean("maximized", IsMaximized());
   1881 
   1882   gfx::Rect work_area(gfx::Screen::GetNativeScreen()->GetDisplayMatching(
   1883       restored_bounds_).work_area());
   1884   window_preferences->SetInteger("work_area_left", work_area.x());
   1885   window_preferences->SetInteger("work_area_top", work_area.y());
   1886   window_preferences->SetInteger("work_area_right", work_area.right());
   1887   window_preferences->SetInteger("work_area_bottom", work_area.bottom());
   1888 }
   1889 
   1890 void BrowserWindowGtk::InvalidateInfoBarBits() {
   1891   gtk_widget_queue_draw(toolbar_border_);
   1892   gtk_widget_queue_draw(toolbar_->widget());
   1893   if (bookmark_bar_.get() &&
   1894       browser_->bookmark_bar_state() != BookmarkBar::DETACHED) {
   1895     gtk_widget_queue_draw(bookmark_bar_->widget());
   1896   }
   1897 }
   1898 
   1899 int BrowserWindowGtk::GetXPositionOfLocationIcon(GtkWidget* relative_to) {
   1900   GtkWidget* location_icon = toolbar_->GetLocationBarView()->
   1901       location_icon_widget();
   1902 
   1903   GtkAllocation location_icon_allocation;
   1904   gtk_widget_get_allocation(location_icon, &location_icon_allocation);
   1905 
   1906   int x = 0;
   1907   gtk_widget_translate_coordinates(
   1908       location_icon, relative_to,
   1909       (location_icon_allocation.width + 1) / 2,
   1910       0, &x, NULL);
   1911 
   1912   if (!gtk_widget_get_has_window(relative_to)) {
   1913     GtkAllocation allocation;
   1914     gtk_widget_get_allocation(relative_to, &allocation);
   1915     x += allocation.x;
   1916   }
   1917 
   1918   return x;
   1919 }
   1920 
   1921 void BrowserWindowGtk::MaybeShowBookmarkBar(bool animate) {
   1922   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::MaybeShowBookmarkBar");
   1923   if (!IsBookmarkBarSupported())
   1924     return;
   1925 
   1926   if (GetDisplayedTab())
   1927     bookmark_bar_->SetPageNavigator(browser_.get());
   1928 
   1929   BookmarkBar::State state = browser_->bookmark_bar_state();
   1930   if (contents_container_->HasOverlay() && state == BookmarkBar::DETACHED)
   1931     state = BookmarkBar::HIDDEN;
   1932 
   1933   toolbar_->UpdateForBookmarkBarVisibility(state == BookmarkBar::DETACHED);
   1934   PlaceBookmarkBar(state == BookmarkBar::DETACHED);
   1935   bookmark_bar_->SetBookmarkBarState(
   1936       state,
   1937       animate ? BookmarkBar::ANIMATE_STATE_CHANGE :
   1938                 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
   1939 }
   1940 
   1941 void BrowserWindowGtk::OnLocationIconSizeAllocate(GtkWidget* sender,
   1942                                                   GtkAllocation* allocation) {
   1943   // The position of the arrow may have changed, so we'll have to redraw it.
   1944   InvalidateInfoBarBits();
   1945 }
   1946 
   1947 gboolean BrowserWindowGtk::OnExposeDrawInfobarBits(GtkWidget* sender,
   1948                                                    GdkEventExpose* expose) {
   1949   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnExposeDrawInfobarBits");
   1950   // Maybe draw infobars
   1951   infobar_container_->PaintInfobarBitsOn(sender, expose, NULL);
   1952 
   1953   return FALSE;
   1954 }
   1955 
   1956 gboolean BrowserWindowGtk::OnBookmarkBarExpose(GtkWidget* sender,
   1957                                                GdkEventExpose* expose) {
   1958   if (browser_->bookmark_bar_state() == BookmarkBar::DETACHED)
   1959     return FALSE;
   1960 
   1961   return OnExposeDrawInfobarBits(sender, expose);
   1962 }
   1963 
   1964 void BrowserWindowGtk::OnBookmarkBarSizeAllocate(GtkWidget* sender,
   1965                                                  GtkAllocation* allocation) {
   1966   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnBookmarkBarSizeAllocate");
   1967   // The size of the bookmark bar affects how the infobar arrow is drawn on
   1968   // the toolbar.
   1969   if (infobar_container_->ContainsInfobars())
   1970     InvalidateInfoBarBits();
   1971 
   1972   // Pass the new size to our infobar container.
   1973   int arrow_size = InfoBar::kDefaultArrowTargetHeight;
   1974   if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED)
   1975     arrow_size += allocation->height;
   1976   infobar_container_->SetMaxTopArrowHeight(arrow_size);
   1977 }
   1978 
   1979 // static
   1980 gboolean BrowserWindowGtk::OnGtkAccelerator(GtkAccelGroup* accel_group,
   1981                                             GObject* acceleratable,
   1982                                             guint keyval,
   1983                                             GdkModifierType modifier,
   1984                                             void* user_data) {
   1985   int command_id = GPOINTER_TO_INT(user_data);
   1986   BrowserWindowGtk* browser_window =
   1987       GetBrowserWindowForNativeWindow(GTK_WINDOW(acceleratable));
   1988   DCHECK(browser_window != NULL);
   1989   return chrome::ExecuteCommand(browser_window->browser(), command_id);
   1990 }
   1991 
   1992 // Let the focused widget have first crack at the key event so we don't
   1993 // override their accelerators, except if there is a priority keybinding
   1994 // handler registered (it should take precedence).
   1995 gboolean BrowserWindowGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) {
   1996   if (extension_keybinding_registry_->HasPriorityHandler(event))
   1997     return FALSE;
   1998 
   1999   // If a widget besides the native view is focused, we have to try to handle
   2000   // the custom accelerators before letting it handle them.
   2001   WebContents* current_web_contents =
   2002       browser()->tab_strip_model()->GetActiveWebContents();
   2003   // The current tab might not have a render view if it crashed.
   2004   if (!current_web_contents ||
   2005       !current_web_contents->GetView()->GetContentNativeView() ||
   2006       !gtk_widget_is_focus(
   2007           current_web_contents->GetView()->GetContentNativeView())) {
   2008     int command_id = GetCustomCommandId(event);
   2009     if (command_id == -1)
   2010       command_id = GetPreHandleCommandId(event);
   2011 
   2012     if (command_id != -1 && chrome::ExecuteCommand(browser_.get(), command_id))
   2013       return TRUE;
   2014 
   2015     // Propagate the key event to child widget first, so we don't override their
   2016     // accelerators.
   2017     if (!gtk_window_propagate_key_event(GTK_WINDOW(widget), event)) {
   2018       if (!gtk_window_activate_key(GTK_WINDOW(widget), event)) {
   2019         gtk_bindings_activate_event(GTK_OBJECT(widget), event);
   2020       }
   2021     }
   2022   } else {
   2023     bool rv = gtk_window_propagate_key_event(GTK_WINDOW(widget), event);
   2024     DCHECK(rv);
   2025   }
   2026 
   2027   // Prevents the default handler from handling this event.
   2028   return TRUE;
   2029 }
   2030 
   2031 gboolean BrowserWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
   2032                                             GdkEventMotion* event) {
   2033   // This method is used to update the mouse cursor when over the edge of the
   2034   // custom frame.  If the custom frame is off or we're over some other widget,
   2035   // do nothing.
   2036   if (!UseCustomFrame() || event->window != gtk_widget_get_window(widget)) {
   2037     // Reset the cursor.
   2038     if (frame_cursor_) {
   2039       frame_cursor_ = NULL;
   2040       gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
   2041     }
   2042     return FALSE;
   2043   }
   2044 
   2045   // Update the cursor if we're on the custom frame border.
   2046   GdkWindowEdge edge;
   2047   bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x),
   2048                                     static_cast<int>(event->y), &edge);
   2049   GdkCursorType new_cursor = GDK_LAST_CURSOR;
   2050   if (has_hit_edge)
   2051     new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
   2052 
   2053   GdkCursorType last_cursor = GDK_LAST_CURSOR;
   2054   if (frame_cursor_)
   2055     last_cursor = frame_cursor_->type;
   2056 
   2057   if (last_cursor != new_cursor) {
   2058     if (has_hit_edge) {
   2059       frame_cursor_ = gfx::GetCursor(new_cursor);
   2060     } else {
   2061       frame_cursor_ = NULL;
   2062     }
   2063     gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
   2064                           frame_cursor_);
   2065   }
   2066   return FALSE;
   2067 }
   2068 
   2069 gboolean BrowserWindowGtk::OnButtonPressEvent(GtkWidget* widget,
   2070                                               GdkEventButton* event) {
   2071   // Handle back/forward.
   2072   if (event->type == GDK_BUTTON_PRESS) {
   2073     if (event->button == 8) {
   2074       chrome::GoBack(browser_.get(), CURRENT_TAB);
   2075       return TRUE;
   2076     } else if (event->button == 9) {
   2077       chrome::GoForward(browser_.get(), CURRENT_TAB);
   2078       return TRUE;
   2079     }
   2080   }
   2081 
   2082   // Handle left, middle and right clicks.  In particular, we care about clicks
   2083   // in the custom frame border and clicks in the titlebar.
   2084 
   2085   // Make the button press coordinate relative to the browser window.
   2086   int win_x, win_y;
   2087   GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
   2088   gdk_window_get_origin(gdk_window, &win_x, &win_y);
   2089 
   2090   GdkWindowEdge edge;
   2091   gfx::Point point(static_cast<int>(event->x_root - win_x),
   2092                    static_cast<int>(event->y_root - win_y));
   2093   bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge);
   2094 
   2095   // Ignore clicks that are in/below the browser toolbar.
   2096   GtkWidget* toolbar = toolbar_->widget();
   2097   if (!gtk_widget_get_visible(toolbar)) {
   2098     // If the toolbar is not showing, use the location of web contents as the
   2099     // boundary of where to ignore clicks.
   2100     toolbar = render_area_vbox_;
   2101   }
   2102   gint toolbar_y;
   2103   gtk_widget_get_pointer(toolbar, NULL, &toolbar_y);
   2104   bool has_hit_titlebar = !IsFullscreen() && (toolbar_y < 0)
   2105                           && !has_hit_edge;
   2106   if (event->button == 1) {
   2107     if (GDK_BUTTON_PRESS == event->type) {
   2108       // Raise the window after a click on either the titlebar or the border to
   2109       // match the behavior of most window managers, unless that behavior has
   2110       // been suppressed.
   2111       if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
   2112         gdk_window_raise(gdk_window);
   2113 
   2114       if (has_hit_titlebar) {
   2115         return gtk_window_util::HandleTitleBarLeftMousePress(
   2116             window_, bounds_, event);
   2117       } else if (has_hit_edge) {
   2118         gtk_window_begin_resize_drag(window_, edge, event->button,
   2119                                      static_cast<gint>(event->x_root),
   2120                                      static_cast<gint>(event->y_root),
   2121                                      event->time);
   2122         return TRUE;
   2123       }
   2124     } else if (GDK_2BUTTON_PRESS == event->type) {
   2125       if (has_hit_titlebar) {
   2126         // Maximize/restore on double click.
   2127         if (IsMaximized()) {
   2128           UnMaximize();
   2129         } else {
   2130           gtk_window_maximize(window_);
   2131         }
   2132         return TRUE;
   2133       }
   2134     }
   2135   } else if (event->button == 2) {
   2136     if (has_hit_titlebar || has_hit_edge) {
   2137       gdk_window_lower(gdk_window);
   2138     }
   2139     return TRUE;
   2140   } else if (event->button == 3) {
   2141     if (has_hit_titlebar) {
   2142       titlebar_->ShowContextMenu(event);
   2143       return TRUE;
   2144     }
   2145   }
   2146 
   2147   return FALSE;  // Continue to propagate the event.
   2148 }
   2149 
   2150 gboolean BrowserWindowGtk::OnFocusIn(GtkWidget* widget,
   2151                                      GdkEventFocus* event) {
   2152   BrowserList::SetLastActive(browser_.get());
   2153   return FALSE;
   2154 }
   2155 
   2156 gboolean BrowserWindowGtk::OnFocusOut(GtkWidget* widget,
   2157                                       GdkEventFocus* event) {
   2158   return FALSE;
   2159 }
   2160 
   2161 void BrowserWindowGtk::ShowSupportedWindowFeatures() {
   2162   if (IsTabStripSupported())
   2163     tabstrip_->Show();
   2164 
   2165   if (IsToolbarSupported()) {
   2166     toolbar_->Show();
   2167     gtk_widget_show(toolbar_border_);
   2168     gdk_window_lower(gtk_widget_get_window(toolbar_border_));
   2169   }
   2170 
   2171   if (IsBookmarkBarSupported())
   2172     MaybeShowBookmarkBar(false);
   2173 }
   2174 
   2175 void BrowserWindowGtk::HideUnsupportedWindowFeatures() {
   2176   if (!IsTabStripSupported())
   2177     tabstrip_->Hide();
   2178 
   2179   if (!IsToolbarSupported())
   2180     toolbar_->Hide();
   2181 
   2182   // If the bookmark bar shelf is unsupported, then we never create it.
   2183 }
   2184 
   2185 bool BrowserWindowGtk::IsTabStripSupported() const {
   2186   return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP);
   2187 }
   2188 
   2189 bool BrowserWindowGtk::IsToolbarSupported() const {
   2190   return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
   2191          browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR);
   2192 }
   2193 
   2194 bool BrowserWindowGtk::IsBookmarkBarSupported() const {
   2195   return browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR);
   2196 }
   2197 
   2198 bool BrowserWindowGtk::UsingCustomPopupFrame() const {
   2199   GtkThemeService* theme_provider = GtkThemeService::GetFrom(
   2200       browser()->profile());
   2201   return !theme_provider->UsingNativeTheme() && browser()->is_type_popup();
   2202 }
   2203 
   2204 bool BrowserWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
   2205   if (!UseCustomFrame())
   2206     return false;
   2207 
   2208   if (IsMaximized() || IsFullscreen())
   2209     return false;
   2210 
   2211   return gtk_window_util::GetWindowEdge(
   2212       bounds_.size(), kTopResizeAdjust, x, y, edge);
   2213 }
   2214 
   2215 bool BrowserWindowGtk::UseCustomFrame() const {
   2216   // We don't use the custom frame for app mode windows or app window popups.
   2217   return use_custom_frame_pref_.GetValue() && !browser_->is_app();
   2218 }
   2219 
   2220 void BrowserWindowGtk::PlaceBookmarkBar(bool is_floating) {
   2221   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::PlaceBookmarkBar");
   2222 
   2223   GtkWidget* target_parent = NULL;
   2224   if (!is_floating) {
   2225     // Place the bookmark bar at the end of |window_vbox_|; this happens after
   2226     // we have placed the render area at the end of |window_vbox_| so we will
   2227     // be above the render area.
   2228     target_parent = window_vbox_;
   2229   } else {
   2230     // Place the bookmark bar at the end of the render area; this happens after
   2231     // the tab contents container has been placed there so we will be
   2232     // above the webpage (in terms of y).
   2233     target_parent = render_area_vbox_;
   2234   }
   2235 
   2236   GtkWidget* parent = gtk_widget_get_parent(bookmark_bar_->widget());
   2237   if (parent != target_parent) {
   2238     if (parent)
   2239       gtk_container_remove(GTK_CONTAINER(parent), bookmark_bar_->widget());
   2240 
   2241     gtk_box_pack_end(GTK_BOX(target_parent), bookmark_bar_->widget(),
   2242                      FALSE, FALSE, 0);
   2243   }
   2244 }
   2245 
   2246 bool BrowserWindowGtk::DrawFrameAsActive() const {
   2247   if (ui::ActiveWindowWatcherX::WMSupportsActivation())
   2248     return is_active_;
   2249 
   2250   // Since we don't get notifications when the active state of the frame
   2251   // changes, we can't consistently repaint the frame at the right time. Instead
   2252   // we always draw the frame as active.
   2253   return true;
   2254 }
   2255 
   2256 void BrowserWindowGtk::UpdateDevToolsForContents(WebContents* contents) {
   2257   TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::UpdateDevToolsForContents");
   2258   DevToolsWindow* new_devtools_window = contents ?
   2259       DevToolsWindow::GetDockedInstanceForInspectedTab(contents) : NULL;
   2260 
   2261   // Fast return in case of the same window having same orientation.
   2262   if (devtools_window_ == new_devtools_window && (!new_devtools_window ||
   2263         new_devtools_window->dock_side() == devtools_dock_side_)) {
   2264     return;
   2265   }
   2266 
   2267   // Replace tab contents.
   2268   if (devtools_window_ != new_devtools_window) {
   2269     if (devtools_window_)
   2270       devtools_container_->DetachTab(devtools_window_->web_contents());
   2271     devtools_container_->SetTab(
   2272         new_devtools_window ? new_devtools_window->web_contents() : NULL);
   2273     if (new_devtools_window) {
   2274       // WebContentsViewGtk::WasShown is not called when a web contents is shown
   2275       // by anything other than user selecting a Tab.
   2276       // See TabContentsViewViews::OnWindowPosChanged for reference on how it
   2277       // should be implemented.
   2278       new_devtools_window->web_contents()->WasShown();
   2279     }
   2280   }
   2281 
   2282   // Store last used position.
   2283   if (devtools_window_) {
   2284     GtkAllocation contents_rect;
   2285     gtk_widget_get_allocation(contents_vsplit_, &contents_rect);
   2286     int split_size;
   2287     if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT) {
   2288       gtk_widget_style_get(contents_hsplit_, "handle-size", &split_size, NULL);
   2289       devtools_window_->SetWidth(
   2290           contents_rect.width - split_size -
   2291           gtk_paned_get_position(GTK_PANED(contents_hsplit_)));
   2292     } else if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_BOTTOM) {
   2293       gtk_widget_style_get(contents_vsplit_, "handle-size", &split_size, NULL);
   2294       devtools_window_->SetHeight(
   2295           contents_rect.height - split_size -
   2296           gtk_paned_get_position(GTK_PANED(contents_vsplit_)));
   2297     }
   2298   }
   2299 
   2300   // Show / hide container if necessary. Changing dock orientation is
   2301   // hide + show.
   2302   bool should_hide = devtools_window_ && (!new_devtools_window ||
   2303       devtools_dock_side_ != new_devtools_window->dock_side());
   2304   bool should_show = new_devtools_window && (!devtools_window_ || should_hide);
   2305 
   2306   if (should_hide)
   2307     HideDevToolsContainer();
   2308 
   2309   devtools_window_ = new_devtools_window;
   2310 
   2311   if (should_show) {
   2312     devtools_dock_side_ = new_devtools_window->dock_side();
   2313     ShowDevToolsContainer();
   2314   } else if (new_devtools_window) {
   2315     UpdateDevToolsSplitPosition();
   2316   }
   2317 }
   2318 
   2319 void BrowserWindowGtk::ShowDevToolsContainer() {
   2320   if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED) {
   2321     gtk_box_pack_end(GTK_BOX(render_area_vbox_),
   2322                      devtools_container_->widget(), FALSE, FALSE, 0);
   2323     gtk_box_reorder_child(GTK_BOX(render_area_vbox_),
   2324                           devtools_container_->widget(), 0);
   2325   } else {
   2326     gtk_widget_set_size_request(devtools_container_->widget(),
   2327         devtools_window_->GetMinimumWidth(),
   2328         devtools_window_->GetMinimumHeight());
   2329     bool to_right = devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT;
   2330     gtk_paned_pack2(GTK_PANED(to_right ? contents_hsplit_ : contents_vsplit_),
   2331                     devtools_container_->widget(),
   2332                     FALSE,
   2333                     FALSE);
   2334   }
   2335   UpdateDevToolsSplitPosition();
   2336   gtk_widget_show(devtools_container_->widget());
   2337 }
   2338 
   2339 void BrowserWindowGtk::HideDevToolsContainer() {
   2340   gtk_container_remove(GTK_CONTAINER(
   2341           devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT ? contents_hsplit_ :
   2342           devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_BOTTOM ? contents_vsplit_ :
   2343           render_area_vbox_),
   2344       devtools_container_->widget());
   2345   gtk_widget_hide(devtools_container_->widget());
   2346 }
   2347 
   2348 void BrowserWindowGtk::UpdateDevToolsSplitPosition() {
   2349   if (!window_has_shown_)
   2350     return;
   2351 
   2352   // This is required if infobar appears/disappears, or devtools container is
   2353   // moved between |render_area_vbox_| and |contents_{v,h}split_|.
   2354   gtk_container_check_resize(GTK_CONTAINER(render_area_vbox_));
   2355 
   2356   GtkAllocation contents_rect;
   2357   gtk_widget_get_allocation(contents_vsplit_, &contents_rect);
   2358   int split_size;
   2359 
   2360   if (devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT) {
   2361     gtk_widget_style_get(contents_hsplit_, "handle-size", &split_size, NULL);
   2362     int split_offset = contents_rect.width -
   2363         devtools_window_->GetWidth(contents_rect.width) - split_size;
   2364     gtk_paned_set_position(GTK_PANED(contents_hsplit_), split_offset);
   2365   } else if (devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_BOTTOM) {
   2366     gtk_widget_style_get(contents_vsplit_, "handle-size", &split_size, NULL);
   2367     int split_offset = contents_rect.height -
   2368         devtools_window_->GetHeight(contents_rect.height) - split_size;
   2369     gtk_paned_set_position(GTK_PANED(contents_vsplit_), split_offset);
   2370   } else {
   2371     gtk_widget_set_size_request(devtools_container_->widget(),
   2372         0, devtools_window_->GetMinimizedHeight());
   2373   }
   2374 }
   2375 
   2376 void BrowserWindowGtk::OnUseCustomChromeFrameChanged() {
   2377   UpdateCustomFrame();
   2378   ui::SetHideTitlebarWhenMaximizedProperty(
   2379       ui::GetX11WindowFromGtkWidget(GTK_WIDGET(window_)),
   2380       UseCustomFrame() ? ui::HIDE_TITLEBAR_WHEN_MAXIMIZED :
   2381                          ui::SHOW_TITLEBAR_WHEN_MAXIMIZED);
   2382 }
   2383 
   2384 // static
   2385 bool BrowserWindowGtk::GetCustomFramePrefDefault() {
   2386   // Ideally, we'd use the custom frame by default and just fall back on using
   2387   // system decorations for the few (?) tiling window managers where the custom
   2388   // frame doesn't make sense (e.g. awesome, ion3, ratpoison, xmonad, etc.) or
   2389   // other WMs where it has issues (e.g. Fluxbox -- see issue 19130).  The EWMH
   2390   // _NET_SUPPORTING_WM property makes it easy to look up a name for the current
   2391   // WM, but at least some of the WMs in the latter group don't set it.
   2392   // Instead, we default to using system decorations for all WMs and
   2393   // special-case the ones where the custom frame should be used.
   2394   ui::WindowManagerName wm_type = ui::GuessWindowManager();
   2395   return (wm_type == ui::WM_BLACKBOX ||
   2396           wm_type == ui::WM_COMPIZ ||
   2397           wm_type == ui::WM_ENLIGHTENMENT ||
   2398           wm_type == ui::WM_METACITY ||
   2399           wm_type == ui::WM_MUFFIN ||
   2400           wm_type == ui::WM_MUTTER ||
   2401           wm_type == ui::WM_OPENBOX ||
   2402           wm_type == ui::WM_XFWM4);
   2403 }
   2404 
   2405 // static
   2406 BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) {
   2407   BrowserWindowGtk* browser_window_gtk = new BrowserWindowGtk(browser);
   2408   browser_window_gtk->Init();
   2409   return browser_window_gtk;
   2410 }
   2411