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