Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: chrome/browser/ui/gtk/browser_window_gtk.cc

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

Powered by Google App Engine
This is Rietveld 408576698