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