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