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