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