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