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.
5 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
7 #include <gdk/gdkkeysyms.h>
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"
111 using content::NativeWebKeyboardEvent
;
112 using content::SSLStatus
;
113 using content::WebContents
;
114 using web_modal::WebContentsModalDialogHost
;
118 // The number of milliseconds between loading animation frames.
119 const int kLoadingAnimationFrameTimeMs
= 30;
121 const char* kBrowserWindowKey
= "__BROWSER_WINDOW_GTK__";
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
138 const int kCustomFrameBackgroundVerticalOffset
= 15;
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;
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
);
154 // Get the command ids of the key combinations that are not valid gtk
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
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
167 case GDK_ISO_Left_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
;
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
) {
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
;
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
;
210 GQuark
GetBrowserWindowQuarkKey() {
211 static GQuark quark
= g_quark_from_static_string(kBrowserWindowKey
);
217 BrowserWindowGtk::BrowserWindowGtk(Browser
* browser
)
219 window_has_shown_(false),
220 window_container_(NULL
),
222 render_area_vbox_(NULL
),
223 render_area_floating_container_(NULL
),
224 render_area_event_box_(NULL
),
225 toolbar_border_(NULL
),
227 state_(GDK_WINDOW_STATE_WITHDRAWN
),
228 devtools_window_(NULL
),
229 devtools_floating_container_(NULL
),
232 show_state_after_show_(ui::SHOW_STATE_DEFAULT
),
233 suppress_window_raise_(false),
235 is_fullscreen_(false) {
238 BrowserWindowGtk::~BrowserWindowGtk() {
239 ui::ActiveWindowWatcherX::RemoveObserver(this);
241 browser_
->tab_strip_model()->RemoveObserver(this);
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);
249 use_custom_frame_pref_
.Init(
250 prefs::kUseCustomChromeFrame
,
251 browser_
->profile()->GetPrefs(),
252 base::Bind(&BrowserWindowGtk::OnUseCustomChromeFrameChanged
,
253 base::Unretained(this)));
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());
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;
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
);
273 // Disable the resize gripper on Ubuntu.
274 gtk_window_util::DisableResizeGrip(window_
);
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_
));
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
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
));
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
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
+ ")");
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
306 if (browser_
->is_type_popup()) {
307 gtk_window_set_role(window_
, "pop-up");
311 gtk_window_set_role(window_
, "browser");
316 ConnectAccelerators();
318 // Set the initial background color of widgets.
319 SetBackgroundColor();
320 HideUnsupportedWindowFeatures();
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
);
331 gboolean
BrowserWindowGtk::OnCustomFrameExpose(GtkWidget
* widget
,
332 GdkEventExpose
* event
) {
333 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnCustomFrameExpose");
335 // Draw the default background.
336 cairo_t
* cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
337 gdk_cairo_rectangle(cr
, &event
->area
);
340 if (UsingCustomPopupFrame()) {
341 DrawPopupFrame(cr
, widget
, event
);
343 DrawCustomFrame(cr
, widget
, event
);
346 DrawContentShadow(cr
);
350 if (UseCustomFrame() && !IsMaximized())
351 DrawCustomFrameBorder(widget
);
353 return FALSE
; // Allow subwidgets to paint.
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
,
364 IDR_WINDOW_RIGHT_SIDE
,
365 IDR_WINDOW_BOTTOM_LEFT_CORNER
,
366 IDR_WINDOW_BOTTOM_CENTER
,
367 IDR_WINDOW_BOTTOM_RIGHT_CORNER
);
369 custom_frame_border
->RenderToWidget(widget
);
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();
376 gtk_widget_translate_coordinates(toolbar_
->widget(),
377 GTK_WIDGET(window_
), 0, 0, &left_x
,
380 GtkAllocation window_vbox_allocation
;
381 gtk_widget_get_allocation(window_vbox_
, &window_vbox_allocation
);
382 int center_width
= window_vbox_allocation
.width
;
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();
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
;
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());
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())
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
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.
421 left_x
- kContentShadowThickness
,
422 top_y
- kContentShadowThickness
,
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
);
432 top_y
- kContentShadowThickness
,
434 top_right
->Height());
437 // Fill in the sides. As above, we only draw 2 of the 3 columns on Linux.
439 gtk_widget_translate_coordinates(window_vbox_
,
441 0, window_vbox_allocation
.height
,
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
);
457 left_x
- kContentShadowThickness
,
459 kContentShadowThickness
,
463 gfx::CairoCachedSurface
* right
=
464 rb
.GetNativeImageNamed(IDR_CONTENT_RIGHT_SIDE
).ToCairo();
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
);
472 kContentShadowThickness
,
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);
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);
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
);
499 window_vbox_allocation
.width
- 2,
500 kContentShadowThickness
);
504 void BrowserWindowGtk::DrawPopupFrame(cairo_t
* cr
,
506 GdkEventExpose
* event
) {
507 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
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
);
523 void BrowserWindowGtk::DrawCustomFrame(cairo_t
* cr
,
525 GdkEventExpose
* event
) {
526 GtkThemeService
* theme_provider
= GtkThemeService::GetFrom(
527 browser()->profile());
529 int image_name
= GetThemeFrameResource();
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());
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
);
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());
553 int BrowserWindowGtk::GetVerticalOffset() {
554 return (IsMaximized() || (!UseCustomFrame())) ?
555 -kCustomFrameBackgroundVerticalOffset
: 0;
558 int BrowserWindowGtk::GetThemeFrameResource() {
559 bool incognito
= browser()->profile()->IsOffTheRecord();
561 if (DrawFrameAsActive()) {
562 image_name
= incognito
? IDR_THEME_FRAME_INCOGNITO
: IDR_THEME_FRAME
;
564 image_name
= incognito
? IDR_THEME_FRAME_INCOGNITO_INACTIVE
:
565 IDR_THEME_FRAME_INACTIVE
;
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());
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
;
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
592 gtk_widget_set_size_request(devtools_floating_container_
, -1, -1);
594 window_has_shown_
= true;
595 browser()->OnWindowDidShow();
598 void BrowserWindowGtk::ShowInactive() {
599 gtk_window_set_focus_on_map(window_
, false);
600 gtk_widget_show(GTK_WIDGET(window_
));
603 void BrowserWindowGtk::Hide() {
607 void BrowserWindowGtk::SetBoundsImpl(const gfx::Rect
& bounds
,
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());
616 gtk_window_move(window_
, x
, y
);
619 gtk_window_util::SetWindowSize(window_
, gfx::Size(width
, height
));
621 gtk_widget_set_size_request(devtools_floating_container_
,
626 void BrowserWindowGtk::SetBounds(const gfx::Rect
& bounds
) {
629 SetBoundsImpl(bounds
, true, true);
632 void BrowserWindowGtk::Close() {
633 // We're already closing. Do nothing.
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();
644 SaveWindowPosition();
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
));
656 gtk_window_remove_accel_group(window_
, accel_group_
);
657 g_object_unref(accel_group_
);
661 // Cancel any pending callback from the window configure debounce timer.
662 window_configure_debounce_timer_
.Stop();
664 // Likewise for the loading animation.
665 loading_animation_timer_
.Stop();
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.
671 // Avoid use-after-free in any code that runs after Close() and forgets to
673 window_container_
= 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
;
681 window_has_shown_
= false;
682 titlebar_
->set_window(NULL
);
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
);
690 void BrowserWindowGtk::Activate() {
691 gtk_window_present(window_
);
694 void BrowserWindowGtk::Deactivate() {
695 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_
)));
698 bool BrowserWindowGtk::IsActive() const {
699 if (ui::ActiveWindowWatcherX::WMSupportsActivation())
702 // This still works even though we don't get the activation notification.
703 return window_
&& gtk_window_is_active(window_
);
706 void BrowserWindowGtk::FlashFrame(bool flash
) {
707 // May not be respected by all window managers.
708 gtk_window_set_urgency_hint(window_
, flash
);
711 bool BrowserWindowGtk::IsAlwaysOnTop() const {
715 void BrowserWindowGtk::SetAlwaysOnTop(bool always_on_top
) {
716 // Not implemented for browser windows.
720 gfx::NativeWindow
BrowserWindowGtk::GetNativeWindow() {
724 BrowserWindowTesting
* BrowserWindowGtk::GetBrowserWindowTesting() {
729 StatusBubble
* BrowserWindowGtk::GetStatusBubble() {
730 return status_bubble_
.get();
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();
741 void BrowserWindowGtk::BookmarkBarStateChanged(
742 BookmarkBar::AnimateChangeType change_type
) {
743 MaybeShowBookmarkBar(change_type
== BookmarkBar::ANIMATE_STATE_CHANGE
);
746 void BrowserWindowGtk::UpdateDevTools() {
747 UpdateDevToolsForContents(
748 browser_
->tab_strip_model()->GetActiveWebContents());
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
);
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();
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
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
);
787 void BrowserWindowGtk::SetStarredState(bool is_starred
) {
788 toolbar_
->GetLocationBarView()->SetStarred(is_starred
);
791 void BrowserWindowGtk::SetTranslateIconToggled(bool is_lit
) {
795 void BrowserWindowGtk::OnActiveTabChanged(WebContents
* old_contents
,
796 WebContents
* new_contents
,
799 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::ActiveTabChanged");
800 if (old_contents
&& !old_contents
->IsBeingDestroyed())
801 old_contents
->GetView()->StoreFocus();
803 // Update various elements that are interested in knowing the current
805 infobar_container_
->ChangeInfoBarService(
806 InfoBarService::FromWebContents(new_contents
));
807 contents_container_
->SetTab(new_contents
);
808 UpdateDevToolsForContents(new_contents
);
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();
820 // Update all the UI bits.
822 MaybeShowBookmarkBar(false);
824 void BrowserWindowGtk::ZoomChangedForActiveTab(bool can_show_bubble
) {
825 toolbar_
->GetLocationBarView()->ZoomChangedForActiveTab(
826 can_show_bubble
&& !toolbar_
->IsWrenchMenuShowing());
829 gfx::Rect
BrowserWindowGtk::GetRestoredBounds() const {
830 return restored_bounds_
;
833 ui::WindowShowState
BrowserWindowGtk::GetRestoredState() const {
835 return ui::SHOW_STATE_MAXIMIZED
;
837 return ui::SHOW_STATE_MINIMIZED
;
838 return ui::SHOW_STATE_NORMAL
;
841 gfx::Rect
BrowserWindowGtk::GetBounds() const {
845 bool BrowserWindowGtk::IsMaximized() const {
846 return (state_
& GDK_WINDOW_STATE_MAXIMIZED
);
849 bool BrowserWindowGtk::IsMinimized() const {
850 return (state_
& GDK_WINDOW_STATE_ICONIFIED
);
853 void BrowserWindowGtk::Maximize() {
854 gtk_window_maximize(window_
);
857 void BrowserWindowGtk::Minimize() {
858 gtk_window_iconify(window_
);
861 void BrowserWindowGtk::Restore() {
864 else if (IsMinimized())
865 gtk_window_deiconify(window_
);
868 bool BrowserWindowGtk::ShouldDrawContentDropShadow() const {
869 return !IsMaximized() && UseCustomFrame();
872 void BrowserWindowGtk::EnterFullscreen(
873 const GURL
& url
, FullscreenExitBubbleType type
) {
875 return; // Nothing to do.
876 is_fullscreen_
= true;
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_
);
882 browser_
->WindowFullscreenStateChanged();
886 if (bookmark_bar_
.get())
887 gtk_widget_hide(bookmark_bar_
->widget());
888 if (!chrome::IsRunningInAppMode()) {
889 UpdateFullscreenExitBubbleContent(url
, type
);
891 gtk_widget_hide(titlebar_widget());
892 gtk_widget_hide(toolbar_border_
);
895 void BrowserWindowGtk::UpdateFullscreenExitBubbleContent(
896 const GURL
& url
, FullscreenExitBubbleType bubble_type
) {
898 // Don't create a fullscreen bubble for a closing window.
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
);
905 fullscreen_exit_bubble_
.reset(new FullscreenExitBubbleGtk(
906 GTK_FLOATING_CONTAINER(render_area_floating_container_
),
913 void BrowserWindowGtk::ExitFullscreen() {
915 return; // Nothing to do.
916 is_fullscreen_
= false;
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
922 bool unmaximize_before_unfullscreen
= IsMaximized() &&
923 ui::GuessWindowManager() == ui::WM_METACITY
;
924 if (unmaximize_before_unfullscreen
)
927 gtk_window_unfullscreen(window_
);
929 if (unmaximize_before_unfullscreen
)
930 gtk_window_maximize(window_
);
932 browser_
->WindowFullscreenStateChanged();
933 gtk_widget_show(titlebar_widget());
934 UpdateFullscreenExitBubbleContent(GURL(), FEB_TYPE_NONE
);
936 ShowSupportedWindowFeatures();
939 bool BrowserWindowGtk::ShouldHideUIForFullscreen() const {
940 return IsFullscreen();
943 bool BrowserWindowGtk::IsFullscreen() const {
944 return is_fullscreen_
;
947 bool BrowserWindowGtk::IsFullscreenBubbleVisible() const {
948 return fullscreen_exit_bubble_
!= NULL
;
951 LocationBar
* BrowserWindowGtk::GetLocationBar() const {
952 return toolbar_
->GetLocationBar();
955 void BrowserWindowGtk::SetFocusToLocationBar(bool select_all
) {
957 GetLocationBar()->FocusLocation(select_all
);
960 void BrowserWindowGtk::UpdateReloadStopState(bool is_loading
, bool force
) {
961 toolbar_
->GetReloadButton()->ChangeMode(
962 is_loading
? ReloadButtonGtk::MODE_STOP
: ReloadButtonGtk::MODE_RELOAD
,
966 void BrowserWindowGtk::UpdateToolbar(content::WebContents
* contents
) {
967 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::UpdateToolbar");
968 toolbar_
->UpdateWebContents(contents
);
971 void BrowserWindowGtk::FocusToolbar() {
975 void BrowserWindowGtk::FocusAppMenu() {
979 void BrowserWindowGtk::FocusBookmarksToolbar() {
983 void BrowserWindowGtk::FocusInfobars() {
987 void BrowserWindowGtk::RotatePaneFocus(bool forwards
) {
991 bool BrowserWindowGtk::IsBookmarkBarVisible() const {
992 return browser_
->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR
) &&
993 bookmark_bar_
.get() &&
994 browser_
->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar
);
997 bool BrowserWindowGtk::IsBookmarkBarAnimating() const {
998 if (IsBookmarkBarSupported() && bookmark_bar_
->IsAnimating())
1003 bool BrowserWindowGtk::IsTabStripEditable() const {
1004 return !tabstrip()->IsDragSessionActive() &&
1005 !tabstrip()->IsActiveDropTarget();
1008 bool BrowserWindowGtk::IsToolbarVisible() const {
1009 return IsToolbarSupported();
1012 gfx::Rect
BrowserWindowGtk::GetRootWindowResizerRect() const {
1016 void BrowserWindowGtk::ConfirmAddSearchProvider(TemplateURL
* template_url
,
1018 new EditSearchEngineDialog(window_
, template_url
, NULL
, profile
);
1021 void BrowserWindowGtk::ShowUpdateChromeDialog() {
1022 UpdateRecommendedDialog::Show(window_
);
1025 void BrowserWindowGtk::ShowBookmarkBubble(const GURL
& url
,
1026 bool already_bookmarked
) {
1027 toolbar_
->GetLocationBarView()->ShowStarBubble(url
, !already_bookmarked
);
1030 void BrowserWindowGtk::ShowBookmarkAppBubble(
1031 const WebApplicationInfo
& web_app_info
,
1032 const std::string
& extension_id
) {
1036 void BrowserWindowGtk::ShowTranslateBubble(
1037 content::WebContents
* contents
,
1038 TranslateTabHelper::TranslateStep step
,
1039 TranslateErrors::Type error_type
) {
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
) {
1050 new OneClickSigninBubbleGtk(this, type
, email
,
1051 error_message
, start_sync_callback
);
1055 bool BrowserWindowGtk::IsDownloadShelfVisible() const {
1056 return download_shelf_
.get() && download_shelf_
->IsShowing();
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();
1066 void BrowserWindowGtk::UserChangedTheme() {
1067 SetBackgroundColor();
1069 UpdateWindowShape(bounds_
.width(), bounds_
.height());
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();
1081 void BrowserWindowGtk::WebContentsFocused(WebContents
* contents
) {
1085 void BrowserWindowGtk::ShowWebsiteSettings(
1087 content::WebContents
* web_contents
,
1089 const content::SSLStatus
& ssl
) {
1090 WebsiteSettingsPopupGtk::Show(GetNativeWindow(), profile
, web_contents
, url
,
1094 void BrowserWindowGtk::ShowAppMenu() {
1095 toolbar_
->ShowAppMenu();
1098 bool BrowserWindowGtk::PreHandleKeyboardEvent(
1099 const NativeWebKeyboardEvent
& event
, bool* is_keyboard_shortcut
) {
1100 GdkEventKey
* os_event
= &event
.os_event
->key
;
1102 if (!os_event
|| event
.type
!= blink::WebInputEvent::RawKeyDown
)
1105 if (ExtensionKeybindingRegistryGtk::shortcut_handling_suspended())
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.
1113 // First check if it's a custom accelerator.
1114 int id
= GetCustomCommandId(os_event
);
1116 // Then check if it's a predefined accelerator bound to the window.
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|.
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.
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.
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
);
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
);
1159 // The |event| is a keyboard shortcut.
1160 DCHECK(is_keyboard_shortcut
!= NULL
);
1161 *is_keyboard_shortcut
= true;
1166 void BrowserWindowGtk::HandleKeyboardEvent(
1167 const NativeWebKeyboardEvent
& event
) {
1168 GdkEventKey
* os_event
= &event
.os_event
->key
;
1170 if (!os_event
|| event
.type
!= blink::WebInputEvent::RawKeyDown
)
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.
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
);
1182 chrome::ExecuteCommand(browser_
.get(), id
);
1184 gtk_window_activate_key(window_
, os_event
);
1187 void BrowserWindowGtk::Cut() {
1188 gtk_window_util::DoCut(
1189 window_
, browser_
->tab_strip_model()->GetActiveWebContents());
1192 void BrowserWindowGtk::Copy() {
1193 gtk_window_util::DoCopy(
1194 window_
, browser_
->tab_strip_model()->GetActiveWebContents());
1197 void BrowserWindowGtk::Paste() {
1198 gtk_window_util::DoPaste(
1199 window_
, browser_
->tab_strip_model()->GetActiveWebContents());
1202 WindowOpenDisposition
BrowserWindowGtk::GetDispositionForPopupBounds(
1203 const gfx::Rect
& bounds
) {
1207 FindBar
* BrowserWindowGtk::CreateFindBar() {
1208 return new FindBarGtk(this);
1211 WebContentsModalDialogHost
* BrowserWindowGtk::GetWebContentsModalDialogHost() {
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
,
1222 void BrowserWindowGtk::ShowAvatarBubbleFromAvatarButton() {
1223 if (titlebar_
->avatar_button())
1224 titlebar_
->avatar_button()->ShowAvatarBubble();
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()) {
1237 new PasswordGenerationBubbleGtk(rect
, form
, web_contents
, password_generator
);
1240 void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads(
1242 Browser::DownloadClosePreventionType dialog_type
,
1244 const base::Callback
<void(bool)>& callback
) {
1245 DownloadInProgressDialogGtk::Show(
1246 GetNativeWindow(), download_count
, dialog_type
, app_modal
, callback
);
1250 BrowserWindowGtk::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
1251 if (!bookmark_bar_
.get() ||
1252 browser_
->bookmark_bar_state() != BookmarkBar::DETACHED
) {
1255 return bookmark_bar_
->max_height();
1258 void BrowserWindowGtk::Observe(int type
,
1259 const content::NotificationSource
& source
,
1260 const content::NotificationDetails
& details
) {
1261 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED
, type
);
1262 // The profile avatar icon may have changed.
1263 gtk_util::SetWindowIcon(window_
, browser_
->profile());
1266 void BrowserWindowGtk::TabDetachedAt(WebContents
* contents
, int index
) {
1267 // We use index here rather than comparing |contents| because by this time
1268 // the model has already removed |contents| from its list, so
1269 // browser_->tab_strip_model()->GetActiveWebContents() will return NULL or
1271 if (index
== browser_
->tab_strip_model()->active_index()) {
1272 infobar_container_
->ChangeInfoBarService(NULL
);
1273 UpdateDevToolsForContents(NULL
);
1275 contents_container_
->DetachTab(contents
);
1278 void BrowserWindowGtk::ActiveWindowChanged(GdkWindow
* active_window
) {
1279 // Do nothing if we're in the process of closing the browser window.
1283 bool is_active
= gtk_widget_get_window(GTK_WIDGET(window_
)) == active_window
;
1284 bool changed
= (is_active
!= is_active_
);
1286 if (is_active
&& changed
) {
1287 // If there's an app modal dialog (e.g., JS alert), try to redirect
1288 // the user's attention to the window owning the dialog.
1289 if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) {
1290 AppModalDialogQueue::GetInstance()->ActivateModalDialog();
1295 is_active_
= is_active
;
1297 SetBackgroundColor();
1299 // For some reason, the above two calls cause the window shape to be
1300 // lost so reset it.
1301 UpdateWindowShape(bounds_
.width(), bounds_
.height());
1305 SkColor
BrowserWindowGtk::GetInfoBarSeparatorColor() const {
1306 GtkThemeService
* theme_service
= GtkThemeService::GetFrom(
1307 browser()->profile());
1308 return gfx::GdkColorToSkColor(theme_service
->GetBorderColor());
1311 void BrowserWindowGtk::InfoBarContainerStateChanged(bool is_animating
) {
1312 InvalidateInfoBarBits();
1315 bool BrowserWindowGtk::DrawInfoBarArrows(int* x
) const {
1317 // This is a views specific call that made its way into the interface. We
1318 // go through GetXPositionOfLocationIcon() since we need widget relativity.
1325 extensions::ActiveTabPermissionGranter
*
1326 BrowserWindowGtk::GetActiveTabPermissionGranter() {
1327 WebContents
* tab
= GetDisplayedTab();
1330 return extensions::TabHelper::FromWebContents(tab
)->
1331 active_tab_permission_granter();
1334 void BrowserWindowGtk::DestroyBrowser() {
1338 gboolean
BrowserWindowGtk::OnConfigure(GtkWidget
* widget
,
1339 GdkEventConfigure
* event
) {
1340 gfx::Rect
bounds(event
->x
, event
->y
, event
->width
, event
->height
);
1342 // When the window moves, we'll get multiple configure-event signals. We can
1343 // also get events when the bounds haven't changed, but the window's stacking
1344 // has, which we aren't interested in. http://crbug.com/70125
1345 if (bounds
== configure_bounds_
)
1348 GetLocationBar()->GetOmniboxView()->CloseOmniboxPopup();
1350 WebContents
* tab
= GetDisplayedTab();
1352 tab
->GetRenderViewHost()->NotifyMoveOrResizeStarted();
1354 if (bounds_
.size() != bounds
.size())
1355 UpdateWindowShape(bounds
.width(), bounds
.height());
1357 // We update |bounds_| but not |restored_bounds_| here. The latter needs
1358 // to be updated conditionally when the window is non-maximized and non-
1359 // fullscreen, but whether those state updates have been processed yet is
1360 // window-manager specific. We update |restored_bounds_| in the debounced
1361 // handler below, after the window state has been updated.
1363 configure_bounds_
= bounds
;
1365 // The GdkEventConfigure* we get here doesn't have quite the right
1366 // coordinates though (they're relative to the drawable window area, rather
1367 // than any window manager decorations, if enabled), so we need to call
1368 // gtk_window_get_position() to get the right values. (Otherwise session
1369 // restore, if enabled, will restore windows to incorrect positions.) That's
1370 // a round trip to the X server though, so we set a debounce timer and only
1371 // call it (in OnDebouncedBoundsChanged() below) after we haven't seen a
1372 // reconfigure event in a short while.
1373 // We don't use Reset() because the timer may not yet be running.
1374 // (In that case Stop() is a no-op.)
1375 window_configure_debounce_timer_
.Stop();
1376 window_configure_debounce_timer_
.Start(FROM_HERE
,
1377 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds
), this,
1378 &BrowserWindowGtk::OnDebouncedBoundsChanged
);
1383 void BrowserWindowGtk::OnDebouncedBoundsChanged() {
1384 gtk_window_util::UpdateWindowPosition(this, &bounds_
, &restored_bounds_
);
1385 SaveWindowPosition();
1388 gboolean
BrowserWindowGtk::OnWindowState(GtkWidget
* sender
,
1389 GdkEventWindowState
* event
) {
1390 state_
= event
->new_window_state
;
1392 if (event
->changed_mask
& GDK_WINDOW_STATE_MAXIMIZED
) {
1393 content::NotificationService::current()->Notify(
1394 chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED
,
1395 content::Source
<BrowserWindow
>(this),
1396 content::NotificationService::NoDetails());
1399 titlebar_
->UpdateCustomFrame(UseCustomFrame() && !IsFullscreen());
1400 UpdateWindowShape(bounds_
.width(), bounds_
.height());
1401 SaveWindowPosition();
1405 // Callback for the delete event. This event is fired when the user tries to
1406 // close the window (e.g., clicking on the X in the window manager title bar).
1407 gboolean
BrowserWindowGtk::OnMainWindowDeleteEvent(GtkWidget
* widget
,
1411 // Return true to prevent the gtk window from being destroyed. Close will
1412 // destroy it for us.
1416 void BrowserWindowGtk::OnMainWindowDestroy(GtkWidget
* widget
) {
1417 // Make sure we destroy this object while the main window is still valid.
1418 extension_keybinding_registry_
.reset();
1420 // BUG 8712. When we gtk_widget_destroy() in Close(), this will emit the
1421 // signal right away, and we will be here (while Close() is still in the
1422 // call stack). In order to not reenter Close(), and to also follow the
1423 // expectations of BrowserList, we should run the BrowserWindowGtk destructor
1424 // not now, but after the run loop goes back to process messages. Otherwise
1425 // we will remove ourself from BrowserList while it's being iterated.
1426 // Additionally, now that we know the window is gone, we need to make sure to
1427 // set window_ to NULL, otherwise we will try to close the window again when
1428 // we call Close() in the destructor.
1430 // We don't want to use DeleteSoon() here since it won't work on a nested pump
1431 // (like in UI tests).
1432 base::MessageLoop::current()->PostTask(
1433 FROM_HERE
, base::Bind(&base::DeletePointer
<BrowserWindowGtk
>, this));
1436 void BrowserWindowGtk::UnMaximize() {
1437 gtk_window_util::UnMaximize(window_
, bounds_
, restored_bounds_
);
1440 bool BrowserWindowGtk::CanClose() const {
1441 // You cannot close a frame for which there is an active originating drag
1443 if (tabstrip_
->IsDragSessionActive())
1446 // Give beforeunload handlers the chance to cancel the close before we hide
1447 // the window below.
1448 if (!browser_
->ShouldCloseWindow())
1451 bool fast_tab_closing_enabled
=
1452 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFastUnload
);
1454 if (!browser_
->tab_strip_model()->empty()) {
1455 // Tab strip isn't empty. Hide the window (so it appears to have closed
1456 // immediately) and close all the tabs, allowing the renderers to shut
1457 // down. When the tab strip is empty we'll be called back again.
1458 gtk_widget_hide(GTK_WIDGET(window_
));
1459 browser_
->OnWindowClosing();
1461 if (fast_tab_closing_enabled
)
1462 browser_
->tab_strip_model()->CloseAllTabs();
1464 } else if (fast_tab_closing_enabled
&&
1465 !browser_
->HasCompletedUnloadProcessing()) {
1466 // The browser needs to finish running unload handlers.
1467 // Hide the window (so it appears to have closed immediately), and
1468 // the browser will call us back again when it is ready to close.
1469 gtk_widget_hide(GTK_WIDGET(window_
));
1473 // Empty TabStripModel, it's now safe to allow the Window to be closed.
1474 content::NotificationService::current()->Notify(
1475 chrome::NOTIFICATION_WINDOW_CLOSED
,
1476 content::Source
<GtkWindow
>(window_
),
1477 content::NotificationService::NoDetails());
1481 bool BrowserWindowGtk::ShouldShowWindowIcon() const {
1482 return browser_
->SupportsWindowFeature(Browser::FEATURE_TITLEBAR
);
1485 void BrowserWindowGtk::AddFindBar(FindBarGtk
* findbar
) {
1486 gtk_floating_container_add_floating(
1487 GTK_FLOATING_CONTAINER(render_area_floating_container_
),
1491 void BrowserWindowGtk::ResetCustomFrameCursor() {
1495 frame_cursor_
= NULL
;
1496 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_
)), NULL
);
1500 BrowserWindowGtk
* BrowserWindowGtk::GetBrowserWindowForNativeWindow(
1501 gfx::NativeWindow window
) {
1503 return static_cast<BrowserWindowGtk
*>(
1504 g_object_get_qdata(G_OBJECT(window
), GetBrowserWindowQuarkKey()));
1511 GtkWindow
* BrowserWindowGtk::GetBrowserWindowForXID(XID xid
) {
1512 GtkWindow
* window
= ui::GetGtkWindowFromX11Window(xid
);
1513 // Use GetBrowserWindowForNativeWindow() to verify the GtkWindow we found
1514 // is actually a browser window (and not e.g. a dialog).
1515 if (!GetBrowserWindowForNativeWindow(window
))
1520 GtkWidget
* BrowserWindowGtk::titlebar_widget() const {
1521 return titlebar_
->widget();
1525 void BrowserWindowGtk::RegisterProfilePrefs(
1526 user_prefs::PrefRegistrySyncable
* registry
) {
1527 bool custom_frame_default
= false;
1528 // Avoid checking the window manager if we're not connected to an X server (as
1529 // is the case in Valgrind tests).
1530 if (ui::XDisplayExists())
1531 custom_frame_default
= GetCustomFramePrefDefault();
1533 registry
->RegisterBooleanPref(
1534 prefs::kUseCustomChromeFrame
,
1535 custom_frame_default
,
1536 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
1539 WebContents
* BrowserWindowGtk::GetDisplayedTab() {
1540 return contents_container_
->tab();
1543 void BrowserWindowGtk::QueueToolbarRedraw() {
1544 gtk_widget_queue_draw(toolbar_
->widget());
1547 void BrowserWindowGtk::SetGeometryHints() {
1548 // If we call gtk_window_maximize followed by gtk_window_present, compiz gets
1549 // confused and maximizes the window, but doesn't set the
1550 // GDK_WINDOW_STATE_MAXIMIZED bit. So instead, we keep track of whether to
1551 // maximize and call it after gtk_window_present.
1553 chrome::GetSavedWindowBoundsAndShowState(browser_
.get(),
1555 &show_state_after_show_
);
1556 // We don't blindly call SetBounds here: that sets a forced position
1557 // on the window and we intentionally *don't* do that for normal
1558 // windows. Most programs do not restore their window position on
1559 // Linux, instead letting the window manager choose a position.
1561 // However, in cases like dropping a tab where the bounds are
1562 // specifically set, we do want to position explicitly. We also
1563 // force the position as part of session restore, as applications
1564 // that restore other, similar state (for instance GIMP, audacity,
1565 // pidgin, dia, and gkrellm) do tend to restore their positions.
1567 // For popup windows, we assume that if x == y == 0, the opening page
1568 // did not specify a position. Let the WM position the popup instead.
1569 bool is_popup
= browser_
->is_type_popup();
1570 bool popup_without_position
= is_popup
&&
1571 bounds
.x() == 0 && bounds
.y() == 0;
1572 bool move
= browser_
->bounds_overridden() && !popup_without_position
;
1573 SetBoundsImpl(bounds
, !is_popup
, move
);
1576 void BrowserWindowGtk::ConnectHandlersToSignals() {
1577 g_signal_connect(window_
, "delete-event",
1578 G_CALLBACK(OnMainWindowDeleteEventThunk
), this);
1579 g_signal_connect(window_
, "destroy",
1580 G_CALLBACK(OnMainWindowDestroyThunk
), this);
1581 g_signal_connect(window_
, "configure-event",
1582 G_CALLBACK(OnConfigureThunk
), this);
1583 g_signal_connect(window_
, "window-state-event",
1584 G_CALLBACK(OnWindowStateThunk
), this);
1585 g_signal_connect(window_
, "key-press-event",
1586 G_CALLBACK(OnKeyPressThunk
), this);
1587 g_signal_connect(window_
, "motion-notify-event",
1588 G_CALLBACK(OnMouseMoveEventThunk
), this);
1589 g_signal_connect(window_
, "button-press-event",
1590 G_CALLBACK(OnButtonPressEventThunk
), this);
1591 g_signal_connect(window_
, "focus-in-event",
1592 G_CALLBACK(OnFocusInThunk
), this);
1593 g_signal_connect(window_
, "focus-out-event",
1594 G_CALLBACK(OnFocusOutThunk
), this);
1597 void BrowserWindowGtk::InitWidgets() {
1598 ConnectHandlersToSignals();
1600 bounds_
= configure_bounds_
= restored_bounds_
=
1601 GetInitialWindowBounds(window_
);
1603 // This vbox encompasses all of the widgets within the browser. This is
1604 // everything except the custom frame border.
1605 window_vbox_
= gtk_vbox_new(FALSE
, 0);
1606 gtk_widget_show(window_vbox_
);
1608 // We hold an always hidden GtkMenuBar inside our browser window simply to
1609 // fool the Unity desktop, which will mirror the contents of the first
1610 // GtkMenuBar it sees into the global menu bar. (It doesn't seem to check the
1611 // visibility of the GtkMenuBar, so we can just permanently hide it.)
1612 global_menu_bar_
.reset(new GlobalMenuBar(browser_
.get()));
1613 gtk_container_add(GTK_CONTAINER(window_vbox_
), global_menu_bar_
->widget());
1615 // The window container draws the custom browser frame.
1616 window_container_
= gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
1617 gtk_widget_set_name(window_container_
, "chrome-custom-frame-border");
1618 gtk_widget_set_app_paintable(window_container_
, TRUE
);
1619 gtk_widget_set_double_buffered(window_container_
, FALSE
);
1620 gtk_widget_set_redraw_on_allocate(window_container_
, TRUE
);
1621 g_signal_connect(window_container_
, "expose-event",
1622 G_CALLBACK(OnCustomFrameExposeThunk
), this);
1623 gtk_container_add(GTK_CONTAINER(window_container_
), window_vbox_
);
1625 tabstrip_
.reset(new TabStripGtk(browser_
->tab_strip_model(), this));
1628 // Build the titlebar (tabstrip + header space + min/max/close buttons).
1629 titlebar_
.reset(new BrowserTitlebar(this, window_
));
1632 // Insert the tabstrip into the window.
1633 gtk_box_pack_start(GTK_BOX(window_vbox_
), titlebar_
->widget(), FALSE
, FALSE
,
1636 toolbar_
.reset(new BrowserToolbarGtk(browser_
.get(), this));
1637 toolbar_
->Init(window_
);
1638 gtk_box_pack_start(GTK_BOX(window_vbox_
), toolbar_
->widget(),
1640 g_signal_connect_after(toolbar_
->widget(), "expose-event",
1641 G_CALLBACK(OnExposeDrawInfobarBitsThunk
), this);
1642 // This vbox surrounds the render area: find bar, info bars and render view.
1643 // The reason is that this area as a whole needs to be grouped in its own
1644 // GdkWindow hierarchy so that animations originating inside it (infobar,
1645 // download shelf, find bar) are all clipped to that area. This is why
1646 // |render_area_vbox_| is packed in |render_area_event_box_|.
1647 render_area_vbox_
= gtk_vbox_new(FALSE
, 0);
1648 gtk_widget_set_name(render_area_vbox_
, "chrome-render-area-vbox");
1649 render_area_floating_container_
= gtk_floating_container_new();
1650 gtk_container_add(GTK_CONTAINER(render_area_floating_container_
),
1653 GtkWidget
* location_icon
= toolbar_
->GetLocationBarView()->
1654 location_icon_widget();
1655 g_signal_connect(location_icon
, "size-allocate",
1656 G_CALLBACK(OnLocationIconSizeAllocateThunk
), this);
1657 g_signal_connect_after(location_icon
, "expose-event",
1658 G_CALLBACK(OnExposeDrawInfobarBitsThunk
), this);
1660 toolbar_border_
= gtk_event_box_new();
1661 gtk_box_pack_start(GTK_BOX(render_area_vbox_
),
1662 toolbar_border_
, FALSE
, FALSE
, 0);
1663 gtk_widget_set_size_request(toolbar_border_
, -1, 1);
1664 gtk_widget_set_no_show_all(toolbar_border_
, TRUE
);
1665 g_signal_connect_after(toolbar_border_
, "expose-event",
1666 G_CALLBACK(OnExposeDrawInfobarBitsThunk
), this);
1668 if (IsToolbarSupported())
1669 gtk_widget_show(toolbar_border_
);
1671 infobar_container_
.reset(
1672 new InfoBarContainerGtk(this, browser_
->profile()));
1673 gtk_box_pack_start(GTK_BOX(render_area_vbox_
),
1674 infobar_container_
->widget(),
1677 status_bubble_
.reset(new StatusBubbleGtk(browser_
->profile()));
1679 contents_container_
.reset(new TabContentsContainerGtk(
1680 status_bubble_
.get(),
1681 implicit_cast
<content::WebContentsDelegate
*>(browser_
.get())->
1682 EmbedsFullscreenWidget()));
1683 devtools_container_
.reset(new TabContentsContainerGtk(NULL
, false));
1684 // DevTools container should only have DevTools-specific view ID.
1685 ViewIDUtil::SetDelegateForWidget(devtools_container_
->widget(), NULL
);
1686 ViewIDUtil::SetID(devtools_container_
->widget(), VIEW_ID_DEV_TOOLS_DOCKED
);
1688 devtools_floating_container_
= gtk_floating_container_new();
1689 gtk_container_add(GTK_CONTAINER(devtools_floating_container_
),
1690 devtools_container_
->widget());
1691 gtk_floating_container_add_floating(
1692 GTK_FLOATING_CONTAINER(devtools_floating_container_
),
1693 contents_container_
->widget());
1694 g_signal_connect(devtools_floating_container_
, "set-floating-position",
1695 G_CALLBACK(OnDevToolsContainerSetFloatingPosition
), this);
1696 gtk_box_pack_end(GTK_BOX(render_area_vbox_
),
1697 devtools_floating_container_
, TRUE
, TRUE
, 0);
1699 gtk_widget_show_all(render_area_floating_container_
);
1701 render_area_event_box_
= gtk_event_box_new();
1702 // Set a white background so during startup the user sees white in the
1703 // content area before we get a WebContents in place.
1704 gtk_widget_modify_bg(render_area_event_box_
, GTK_STATE_NORMAL
,
1706 gtk_container_add(GTK_CONTAINER(render_area_event_box_
),
1707 render_area_floating_container_
);
1708 gtk_widget_show(render_area_event_box_
);
1709 gtk_box_pack_end(GTK_BOX(window_vbox_
), render_area_event_box_
,
1712 if (IsBookmarkBarSupported()) {
1713 bookmark_bar_
.reset(new BookmarkBarGtk(this,
1716 PlaceBookmarkBar(false);
1717 gtk_widget_show(bookmark_bar_
->widget());
1719 g_signal_connect_after(bookmark_bar_
->widget(), "expose-event",
1720 G_CALLBACK(OnBookmarkBarExposeThunk
), this);
1721 g_signal_connect(bookmark_bar_
->widget(), "size-allocate",
1722 G_CALLBACK(OnBookmarkBarSizeAllocateThunk
), this);
1725 // We have to realize the window before we try to apply a window shape mask.
1726 gtk_widget_realize(GTK_WIDGET(window_
));
1727 state_
= gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(window_
)));
1728 // Note that calling this the first time is necessary to get the
1729 // proper control layout.
1730 UpdateCustomFrame();
1732 // Add the keybinding registry, now that the window has been realized.
1733 extension_keybinding_registry_
.reset(new ExtensionKeybindingRegistryGtk(
1734 browser_
->profile(),
1736 extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS
,
1739 // We have to call this after the first window is created, but after that only
1740 // when the theme changes. This sets the icon that will be used for windows
1741 // that have not explicitly been assigned an icon.
1742 static bool default_icon_set
= false;
1743 if (!default_icon_set
) {
1744 gtk_util::SetDefaultWindowIcon(window_
);
1745 default_icon_set
= true;
1747 // Set this window's (potentially profile-avatar-emblemed) icon, overriding
1749 gtk_util::SetWindowIcon(window_
, browser_
->profile());
1751 gtk_container_add(GTK_CONTAINER(window_
), window_container_
);
1752 gtk_widget_show(window_container_
);
1753 browser_
->tab_strip_model()->AddObserver(this);
1756 void BrowserWindowGtk::SetBackgroundColor() {
1757 Profile
* profile
= browser()->profile();
1758 GtkThemeService
* theme_provider
= GtkThemeService::GetFrom(profile
);
1760 if (UsingCustomPopupFrame()) {
1761 frame_color_id
= ThemeProperties::COLOR_TOOLBAR
;
1762 } else if (DrawFrameAsActive()) {
1763 frame_color_id
= browser()->profile()->IsOffTheRecord()
1764 ? ThemeProperties::COLOR_FRAME_INCOGNITO
1765 : ThemeProperties::COLOR_FRAME
;
1767 frame_color_id
= browser()->profile()->IsOffTheRecord()
1768 ? ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE
1769 : ThemeProperties::COLOR_FRAME_INACTIVE
;
1772 SkColor frame_color
= theme_provider
->GetColor(frame_color_id
);
1774 // Paint the frame color on the left, right and bottom.
1775 GdkColor frame_color_gdk
= gfx::SkColorToGdkColor(frame_color
);
1776 gtk_widget_modify_bg(GTK_WIDGET(window_
), GTK_STATE_NORMAL
,
1779 GdkColor border_color
= theme_provider
->GetBorderColor();
1780 gtk_widget_modify_bg(toolbar_border_
, GTK_STATE_NORMAL
, &border_color
);
1783 void BrowserWindowGtk::UpdateWindowShape(int width
, int height
) {
1784 using gtk_window_util::kFrameBorderThickness
;
1785 GdkRegion
* mask
= GetWindowShape(width
, height
);
1786 gdk_window_shape_combine_region(
1787 gtk_widget_get_window(GTK_WIDGET(window_
)), mask
, 0, 0);
1789 gdk_region_destroy(mask
);
1791 if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
1792 gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_
), 1,
1793 kFrameBorderThickness
, kFrameBorderThickness
, kFrameBorderThickness
);
1795 gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_
), 0, 0, 0, 0);
1799 GdkRegion
* BrowserWindowGtk::GetWindowShape(int width
, int height
) const {
1800 if (UseCustomFrame() && !IsFullscreen() && !IsMaximized()) {
1801 // Make the corners rounded. We set a mask that includes most of the
1802 // window except for a few pixels in each corner.
1803 GdkRectangle top_top_rect
= { 3, 0, width
- 6, 1 };
1804 GdkRectangle top_mid_rect
= { 1, 1, width
- 2, 2 };
1805 GdkRectangle mid_rect
= { 0, 3, width
, height
- 6 };
1806 // The bottom two rects are mirror images of the top two rects.
1807 GdkRectangle bot_mid_rect
= top_mid_rect
;
1808 bot_mid_rect
.y
= height
- 3;
1809 GdkRectangle bot_bot_rect
= top_top_rect
;
1810 bot_bot_rect
.y
= height
- 1;
1811 GdkRegion
* mask
= gdk_region_rectangle(&top_top_rect
);
1812 gdk_region_union_with_rect(mask
, &top_mid_rect
);
1813 gdk_region_union_with_rect(mask
, &mid_rect
);
1814 gdk_region_union_with_rect(mask
, &bot_mid_rect
);
1815 gdk_region_union_with_rect(mask
, &bot_bot_rect
);
1817 } else if (UseCustomFrame()) {
1818 // Disable rounded corners. Simply passing in a NULL region doesn't
1819 // seem to work on KWin, so manually set the shape to the whole window.
1820 GdkRectangle rect
= { 0, 0, width
, height
};
1821 GdkRegion
* mask
= gdk_region_rectangle(&rect
);
1824 // XFCE disables the system decorations if there's an xshape set. Do not
1825 // use the KWin hack when the custom frame is not enabled.
1830 void BrowserWindowGtk::ConnectAccelerators() {
1831 accel_group_
= gtk_accel_group_new();
1832 gtk_window_add_accel_group(window_
, accel_group_
);
1834 AcceleratorsGtk
* accelerators
= AcceleratorsGtk::GetInstance();
1835 for (AcceleratorsGtk::const_iterator iter
= accelerators
->begin();
1836 iter
!= accelerators
->end(); ++iter
) {
1837 gtk_accel_group_connect(
1839 ui::GetGdkKeyCodeForAccelerator(iter
->second
),
1840 ui::GetGdkModifierForAccelerator(iter
->second
),
1842 g_cclosure_new(G_CALLBACK(OnGtkAccelerator
),
1843 GINT_TO_POINTER(iter
->first
), NULL
));
1847 void BrowserWindowGtk::UpdateCustomFrame() {
1848 gtk_window_set_decorated(window_
, !UseCustomFrame());
1849 titlebar_
->UpdateCustomFrame(UseCustomFrame() && !IsFullscreen());
1850 UpdateWindowShape(bounds_
.width(), bounds_
.height());
1853 void BrowserWindowGtk::InvalidateWindow() {
1854 GtkAllocation allocation
;
1855 gtk_widget_get_allocation(GTK_WIDGET(window_
), &allocation
);
1856 gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(window_
)),
1860 void BrowserWindowGtk::SaveWindowPosition() {
1861 // Browser::SaveWindowPlacement is used for session restore.
1862 ui::WindowShowState show_state
= ui::SHOW_STATE_NORMAL
;
1864 show_state
= ui::SHOW_STATE_MAXIMIZED
;
1865 else if (IsMinimized())
1866 show_state
= ui::SHOW_STATE_MINIMIZED
;
1868 if (chrome::ShouldSaveWindowPlacement(browser_
.get()))
1869 chrome::SaveWindowPlacement(browser_
.get(), restored_bounds_
, show_state
);
1871 // We also need to save the placement for startup.
1872 // This is a web of calls between views and delegates on Windows, but the
1873 // crux of the logic follows. See also cocoa/browser_window_controller.mm.
1874 if (!browser_
->profile()->GetPrefs())
1877 std::string window_name
= chrome::GetWindowPlacementKey(browser_
.get());
1878 DictionaryPrefUpdate
update(browser_
->profile()->GetPrefs(),
1879 window_name
.c_str());
1880 base::DictionaryValue
* window_preferences
= update
.Get();
1881 // Note that we store left/top for consistency with Windows, but that we
1882 // *don't* obey them; we only use them for computing width/height. See
1883 // comments in SetGeometryHints().
1884 window_preferences
->SetInteger("left", restored_bounds_
.x());
1885 window_preferences
->SetInteger("top", restored_bounds_
.y());
1886 window_preferences
->SetInteger("right", restored_bounds_
.right());
1887 window_preferences
->SetInteger("bottom", restored_bounds_
.bottom());
1888 window_preferences
->SetBoolean("maximized", IsMaximized());
1890 gfx::Rect
work_area(gfx::Screen::GetNativeScreen()->GetDisplayMatching(
1891 restored_bounds_
).work_area());
1892 window_preferences
->SetInteger("work_area_left", work_area
.x());
1893 window_preferences
->SetInteger("work_area_top", work_area
.y());
1894 window_preferences
->SetInteger("work_area_right", work_area
.right());
1895 window_preferences
->SetInteger("work_area_bottom", work_area
.bottom());
1898 void BrowserWindowGtk::InvalidateInfoBarBits() {
1899 gtk_widget_queue_draw(toolbar_border_
);
1900 gtk_widget_queue_draw(toolbar_
->widget());
1901 if (bookmark_bar_
.get() &&
1902 browser_
->bookmark_bar_state() != BookmarkBar::DETACHED
) {
1903 gtk_widget_queue_draw(bookmark_bar_
->widget());
1907 int BrowserWindowGtk::GetXPositionOfLocationIcon(GtkWidget
* relative_to
) {
1908 GtkWidget
* location_icon
= toolbar_
->GetLocationBarView()->
1909 location_icon_widget();
1911 GtkAllocation location_icon_allocation
;
1912 gtk_widget_get_allocation(location_icon
, &location_icon_allocation
);
1915 gtk_widget_translate_coordinates(
1916 location_icon
, relative_to
,
1917 (location_icon_allocation
.width
+ 1) / 2,
1920 if (!gtk_widget_get_has_window(relative_to
)) {
1921 GtkAllocation allocation
;
1922 gtk_widget_get_allocation(relative_to
, &allocation
);
1929 void BrowserWindowGtk::MaybeShowBookmarkBar(bool animate
) {
1930 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::MaybeShowBookmarkBar");
1931 if (!IsBookmarkBarSupported())
1934 if (GetDisplayedTab())
1935 bookmark_bar_
->SetPageNavigator(browser_
.get());
1937 BookmarkBar::State state
= browser_
->bookmark_bar_state();
1938 toolbar_
->UpdateForBookmarkBarVisibility(state
== BookmarkBar::DETACHED
);
1939 PlaceBookmarkBar(state
== BookmarkBar::DETACHED
);
1940 bookmark_bar_
->SetBookmarkBarState(
1942 animate
? BookmarkBar::ANIMATE_STATE_CHANGE
:
1943 BookmarkBar::DONT_ANIMATE_STATE_CHANGE
);
1946 void BrowserWindowGtk::OnLocationIconSizeAllocate(GtkWidget
* sender
,
1947 GtkAllocation
* allocation
) {
1948 // The position of the arrow may have changed, so we'll have to redraw it.
1949 InvalidateInfoBarBits();
1952 gboolean
BrowserWindowGtk::OnExposeDrawInfobarBits(GtkWidget
* sender
,
1953 GdkEventExpose
* expose
) {
1954 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnExposeDrawInfobarBits");
1955 // Maybe draw infobars
1956 infobar_container_
->PaintInfobarBitsOn(sender
, expose
, NULL
);
1961 gboolean
BrowserWindowGtk::OnBookmarkBarExpose(GtkWidget
* sender
,
1962 GdkEventExpose
* expose
) {
1963 if (browser_
->bookmark_bar_state() == BookmarkBar::DETACHED
)
1966 return OnExposeDrawInfobarBits(sender
, expose
);
1969 void BrowserWindowGtk::OnBookmarkBarSizeAllocate(GtkWidget
* sender
,
1970 GtkAllocation
* allocation
) {
1971 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::OnBookmarkBarSizeAllocate");
1972 // The size of the bookmark bar affects how the infobar arrow is drawn on
1974 if (infobar_container_
->ContainsInfobars())
1975 InvalidateInfoBarBits();
1977 // Pass the new size to our infobar container.
1978 int arrow_size
= InfoBar::kDefaultArrowTargetHeight
;
1979 if (browser_
->bookmark_bar_state() != BookmarkBar::DETACHED
)
1980 arrow_size
+= allocation
->height
;
1981 infobar_container_
->SetMaxTopArrowHeight(arrow_size
);
1985 gboolean
BrowserWindowGtk::OnGtkAccelerator(GtkAccelGroup
* accel_group
,
1986 GObject
* acceleratable
,
1988 GdkModifierType modifier
,
1990 int command_id
= GPOINTER_TO_INT(user_data
);
1991 BrowserWindowGtk
* browser_window
=
1992 GetBrowserWindowForNativeWindow(GTK_WINDOW(acceleratable
));
1993 DCHECK(browser_window
!= NULL
);
1994 return chrome::ExecuteCommand(browser_window
->browser(), command_id
);
1997 // Let the focused widget have first crack at the key event so we don't
1998 // override their accelerators, except if there is a priority keybinding
1999 // handler registered (it should take precedence).
2000 gboolean
BrowserWindowGtk::OnKeyPress(GtkWidget
* widget
, GdkEventKey
* event
) {
2001 if (extension_keybinding_registry_
->HasPriorityHandler(event
))
2004 // If a widget besides the native view is focused, we have to try to handle
2005 // the custom accelerators before letting it handle them.
2006 WebContents
* current_web_contents
=
2007 browser()->tab_strip_model()->GetActiveWebContents();
2008 // The current tab might not have a render view if it crashed.
2009 if (!current_web_contents
||
2010 !current_web_contents
->GetView()->GetContentNativeView() ||
2011 !gtk_widget_is_focus(
2012 current_web_contents
->GetView()->GetContentNativeView())) {
2013 int command_id
= GetCustomCommandId(event
);
2014 if (command_id
== -1)
2015 command_id
= GetPreHandleCommandId(event
);
2017 if (command_id
!= -1 && chrome::ExecuteCommand(browser_
.get(), command_id
))
2020 // Propagate the key event to child widget first, so we don't override their
2022 if (!gtk_window_propagate_key_event(GTK_WINDOW(widget
), event
)) {
2023 if (!gtk_window_activate_key(GTK_WINDOW(widget
), event
)) {
2024 gtk_bindings_activate_event(GTK_OBJECT(widget
), event
);
2028 bool rv
= gtk_window_propagate_key_event(GTK_WINDOW(widget
), event
);
2032 // Prevents the default handler from handling this event.
2036 gboolean
BrowserWindowGtk::OnMouseMoveEvent(GtkWidget
* widget
,
2037 GdkEventMotion
* event
) {
2038 // This method is used to update the mouse cursor when over the edge of the
2039 // custom frame. If the custom frame is off or we're over some other widget,
2041 if (!UseCustomFrame() || event
->window
!= gtk_widget_get_window(widget
)) {
2042 // Reset the cursor.
2043 if (frame_cursor_
) {
2044 frame_cursor_
= NULL
;
2045 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_
)), NULL
);
2050 // Update the cursor if we're on the custom frame border.
2052 bool has_hit_edge
= GetWindowEdge(static_cast<int>(event
->x
),
2053 static_cast<int>(event
->y
), &edge
);
2054 GdkCursorType new_cursor
= GDK_LAST_CURSOR
;
2056 new_cursor
= gtk_window_util::GdkWindowEdgeToGdkCursorType(edge
);
2058 GdkCursorType last_cursor
= GDK_LAST_CURSOR
;
2060 last_cursor
= frame_cursor_
->type
;
2062 if (last_cursor
!= new_cursor
) {
2064 frame_cursor_
= gfx::GetCursor(new_cursor
);
2066 frame_cursor_
= NULL
;
2068 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_
)),
2074 gboolean
BrowserWindowGtk::OnButtonPressEvent(GtkWidget
* widget
,
2075 GdkEventButton
* event
) {
2076 // Handle back/forward.
2077 if (event
->type
== GDK_BUTTON_PRESS
) {
2078 if (event
->button
== 8) {
2079 chrome::GoBack(browser_
.get(), CURRENT_TAB
);
2081 } else if (event
->button
== 9) {
2082 chrome::GoForward(browser_
.get(), CURRENT_TAB
);
2087 // Handle left, middle and right clicks. In particular, we care about clicks
2088 // in the custom frame border and clicks in the titlebar.
2090 // Make the button press coordinate relative to the browser window.
2092 GdkWindow
* gdk_window
= gtk_widget_get_window(GTK_WIDGET(window_
));
2093 gdk_window_get_origin(gdk_window
, &win_x
, &win_y
);
2096 gfx::Point
point(static_cast<int>(event
->x_root
- win_x
),
2097 static_cast<int>(event
->y_root
- win_y
));
2098 bool has_hit_edge
= GetWindowEdge(point
.x(), point
.y(), &edge
);
2100 // Ignore clicks that are in/below the browser toolbar.
2101 GtkWidget
* toolbar
= toolbar_
->widget();
2102 if (!gtk_widget_get_visible(toolbar
)) {
2103 // If the toolbar is not showing, use the location of web contents as the
2104 // boundary of where to ignore clicks.
2105 toolbar
= render_area_vbox_
;
2108 gtk_widget_get_pointer(toolbar
, NULL
, &toolbar_y
);
2109 bool has_hit_titlebar
= !IsFullscreen() && (toolbar_y
< 0)
2111 if (event
->button
== 1) {
2112 if (GDK_BUTTON_PRESS
== event
->type
) {
2113 // Raise the window after a click on either the titlebar or the border to
2114 // match the behavior of most window managers, unless that behavior has
2116 if ((has_hit_titlebar
|| has_hit_edge
) && !suppress_window_raise_
)
2117 gdk_window_raise(gdk_window
);
2119 if (has_hit_titlebar
) {
2120 return gtk_window_util::HandleTitleBarLeftMousePress(
2121 window_
, bounds_
, event
);
2122 } else if (has_hit_edge
) {
2123 gtk_window_begin_resize_drag(window_
, edge
, event
->button
,
2124 static_cast<gint
>(event
->x_root
),
2125 static_cast<gint
>(event
->y_root
),
2129 } else if (GDK_2BUTTON_PRESS
== event
->type
) {
2130 if (has_hit_titlebar
) {
2131 // Maximize/restore on double click.
2132 if (IsMaximized()) {
2135 gtk_window_maximize(window_
);
2140 } else if (event
->button
== 2) {
2141 if (has_hit_titlebar
|| has_hit_edge
) {
2142 gdk_window_lower(gdk_window
);
2145 } else if (event
->button
== 3) {
2146 if (has_hit_titlebar
) {
2147 titlebar_
->ShowContextMenu(event
);
2152 return FALSE
; // Continue to propagate the event.
2155 gboolean
BrowserWindowGtk::OnFocusIn(GtkWidget
* widget
,
2156 GdkEventFocus
* event
) {
2157 BrowserList::SetLastActive(browser_
.get());
2161 gboolean
BrowserWindowGtk::OnFocusOut(GtkWidget
* widget
,
2162 GdkEventFocus
* event
) {
2166 void BrowserWindowGtk::ShowSupportedWindowFeatures() {
2167 if (IsTabStripSupported())
2170 if (IsToolbarSupported()) {
2172 gtk_widget_show(toolbar_border_
);
2173 gdk_window_lower(gtk_widget_get_window(toolbar_border_
));
2176 if (IsBookmarkBarSupported())
2177 MaybeShowBookmarkBar(false);
2180 void BrowserWindowGtk::HideUnsupportedWindowFeatures() {
2181 if (!IsTabStripSupported())
2184 if (!IsToolbarSupported())
2187 // If the bookmark bar shelf is unsupported, then we never create it.
2190 bool BrowserWindowGtk::IsTabStripSupported() const {
2191 return browser_
->SupportsWindowFeature(Browser::FEATURE_TABSTRIP
);
2194 bool BrowserWindowGtk::IsToolbarSupported() const {
2195 return browser_
->SupportsWindowFeature(Browser::FEATURE_TOOLBAR
) ||
2196 browser_
->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR
);
2199 bool BrowserWindowGtk::IsBookmarkBarSupported() const {
2200 return browser_
->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR
);
2203 bool BrowserWindowGtk::UsingCustomPopupFrame() const {
2204 GtkThemeService
* theme_provider
= GtkThemeService::GetFrom(
2205 browser()->profile());
2206 return !theme_provider
->UsingNativeTheme() && browser()->is_type_popup();
2209 bool BrowserWindowGtk::GetWindowEdge(int x
, int y
, GdkWindowEdge
* edge
) {
2210 if (!UseCustomFrame())
2213 if (IsMaximized() || IsFullscreen())
2216 return gtk_window_util::GetWindowEdge(
2217 bounds_
.size(), kTopResizeAdjust
, x
, y
, edge
);
2220 bool BrowserWindowGtk::UseCustomFrame() const {
2221 // We don't use the custom frame for app mode windows or app window popups.
2222 return use_custom_frame_pref_
.GetValue() && !browser_
->is_app();
2225 void BrowserWindowGtk::PlaceBookmarkBar(bool is_floating
) {
2226 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::PlaceBookmarkBar");
2228 GtkWidget
* target_parent
= NULL
;
2230 // Place the bookmark bar at the end of |window_vbox_|; this happens after
2231 // we have placed the render area at the end of |window_vbox_| so we will
2232 // be above the render area.
2233 target_parent
= window_vbox_
;
2235 // Place the bookmark bar at the end of the render area; this happens after
2236 // the tab contents container has been placed there so we will be
2237 // above the webpage (in terms of y).
2238 target_parent
= render_area_vbox_
;
2241 GtkWidget
* parent
= gtk_widget_get_parent(bookmark_bar_
->widget());
2242 if (parent
!= target_parent
) {
2244 gtk_container_remove(GTK_CONTAINER(parent
), bookmark_bar_
->widget());
2246 gtk_box_pack_end(GTK_BOX(target_parent
), bookmark_bar_
->widget(),
2251 bool BrowserWindowGtk::DrawFrameAsActive() const {
2252 if (ui::ActiveWindowWatcherX::WMSupportsActivation())
2255 // Since we don't get notifications when the active state of the frame
2256 // changes, we can't consistently repaint the frame at the right time. Instead
2257 // we always draw the frame as active.
2261 void BrowserWindowGtk::UpdateDevToolsForContents(WebContents
* contents
) {
2262 TRACE_EVENT0("ui::gtk", "BrowserWindowGtk::UpdateDevToolsForContents");
2263 DevToolsWindow
* new_devtools_window
= contents
?
2264 DevToolsWindow::GetDockedInstanceForInspectedTab(contents
) : NULL
;
2266 // Replace tab contents.
2267 if (devtools_window_
!= new_devtools_window
) {
2268 if (devtools_window_
)
2269 devtools_container_
->DetachTab(devtools_window_
->web_contents());
2270 devtools_container_
->SetTab(
2271 new_devtools_window
? new_devtools_window
->web_contents() : NULL
);
2272 if (new_devtools_window
) {
2273 // WebContentsViewGtk::WasShown is not called when a web contents is shown
2274 // by anything other than user selecting a Tab.
2275 // See TabContentsViewViews::OnWindowPosChanged for reference on how it
2276 // should be implemented.
2277 new_devtools_window
->web_contents()->WasShown();
2281 // Show / hide container if necessary.
2282 bool should_hide
= devtools_window_
&& !new_devtools_window
;
2283 bool should_show
= new_devtools_window
&& !devtools_window_
;
2286 HideDevToolsContainer();
2288 devtools_window_
= new_devtools_window
;
2289 if (devtools_window_
) {
2290 contents_resizing_strategy_
.CopyFrom(
2291 devtools_window_
->GetContentsResizingStrategy());
2293 contents_resizing_strategy_
.CopyFrom(DevToolsContentsResizingStrategy());
2297 ShowDevToolsContainer();
2299 gtk_widget_queue_resize(devtools_floating_container_
);
2300 gtk_widget_queue_draw(devtools_floating_container_
);
2303 void BrowserWindowGtk::ShowDevToolsContainer() {
2304 // Move devtools below contents.
2305 GdkWindow
* const devtools_gdk_window
=
2306 gtk_widget_get_window(devtools_container_
->widget());
2307 if (devtools_gdk_window
)
2308 gdk_window_lower(devtools_gdk_window
);
2311 void BrowserWindowGtk::HideDevToolsContainer() {
2312 // This method is left intentionally blank.
2316 void BrowserWindowGtk::OnDevToolsContainerSetFloatingPosition(
2317 GtkFloatingContainer
* container
, GtkAllocation
* allocation
,
2318 BrowserWindowGtk
* browser_window
) {
2319 GtkAllocation contents_allocation
;
2320 gtk_widget_get_allocation(browser_window
->contents_container_
->widget(),
2321 &contents_allocation
);
2323 gfx::Size
container_size(allocation
->width
, allocation
->height
);
2324 gfx::Rect
old_devtools_bounds(0, 0, allocation
->width
, allocation
->height
);
2325 gfx::Rect
old_contents_bounds(contents_allocation
.x
, contents_allocation
.y
,
2326 contents_allocation
.width
, contents_allocation
.height
);
2327 gfx::Rect new_devtools_bounds
;
2328 gfx::Rect new_contents_bounds
;
2330 ApplyDevToolsContentsResizingStrategy(
2331 browser_window
->contents_resizing_strategy_
, container_size
,
2332 old_devtools_bounds
, old_contents_bounds
,
2333 &new_devtools_bounds
, &new_contents_bounds
);
2335 gtk_widget_set_size_request(browser_window
->contents_container_
->widget(),
2336 new_contents_bounds
.width(), new_contents_bounds
.height());
2338 GValue value
= { 0, };
2339 g_value_init(&value
, G_TYPE_INT
);
2340 g_value_set_int(&value
, new_contents_bounds
.x());
2341 gtk_container_child_set_property(GTK_CONTAINER(container
),
2342 browser_window
->contents_container_
->widget(), "x", &value
);
2343 g_value_set_int(&value
, new_contents_bounds
.y());
2344 gtk_container_child_set_property(GTK_CONTAINER(container
),
2345 browser_window
->contents_container_
->widget(), "y", &value
);
2346 g_value_unset(&value
);
2349 void BrowserWindowGtk::OnUseCustomChromeFrameChanged() {
2350 UpdateCustomFrame();
2351 ui::SetHideTitlebarWhenMaximizedProperty(
2352 ui::GetX11WindowFromGtkWidget(GTK_WIDGET(window_
)),
2353 UseCustomFrame() ? ui::HIDE_TITLEBAR_WHEN_MAXIMIZED
:
2354 ui::SHOW_TITLEBAR_WHEN_MAXIMIZED
);
2358 bool BrowserWindowGtk::GetCustomFramePrefDefault() {
2359 // Ideally, we'd use the custom frame by default and just fall back on using
2360 // system decorations for the few (?) tiling window managers where the custom
2361 // frame doesn't make sense (e.g. awesome, ion3, ratpoison, xmonad, etc.) or
2362 // other WMs where it has issues (e.g. Fluxbox -- see issue 19130). The EWMH
2363 // _NET_SUPPORTING_WM property makes it easy to look up a name for the current
2364 // WM, but at least some of the WMs in the latter group don't set it.
2365 // Instead, we default to using system decorations for all WMs and
2366 // special-case the ones where the custom frame should be used.
2367 ui::WindowManagerName wm_type
= ui::GuessWindowManager();
2368 return (wm_type
== ui::WM_BLACKBOX
||
2369 wm_type
== ui::WM_COMPIZ
||
2370 wm_type
== ui::WM_ENLIGHTENMENT
||
2371 wm_type
== ui::WM_METACITY
||
2372 wm_type
== ui::WM_MUFFIN
||
2373 wm_type
== ui::WM_MUTTER
||
2374 wm_type
== ui::WM_OPENBOX
||
2375 wm_type
== ui::WM_XFWM4
);
2379 BrowserWindow
* BrowserWindow::CreateBrowserWindow(Browser
* browser
) {
2380 BrowserWindowGtk
* browser_window_gtk
= new BrowserWindowGtk(browser
);
2381 browser_window_gtk
->Init();
2382 return browser_window_gtk
;
2386 chrome::HostDesktopType
BrowserWindow::AdjustHostDesktopType(
2387 chrome::HostDesktopType desktop_type
) {
2388 return desktop_type
;