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 96namespace { 97 98// The number of milliseconds between loading animation frames. 99const int kLoadingAnimationFrameTimeMs = 30; 100 101// Default height of dev tools pane when docked to the browser window. This 102// matches the value in Views. 103const int kDefaultDevToolsHeight = 200; 104 105const int kMinDevToolsHeight = 50; 106 107const 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. 111const 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.) 117const 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. 120const 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. 124const 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. 130const 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. 134const 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. 141typedef void (*gtk_window_set_has_resize_grip_func)(GtkWindow*, gboolean); 142gtk_window_set_has_resize_grip_func gtk_window_set_has_resize_grip_sym; 143 144void 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. 157gfx::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. 166int 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. 194int 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 220GdkCursorType 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. 248void 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 265GQuark GetBrowserWindowQuarkKey() { 266 static GQuark quark = g_quark_from_static_string(kBrowserWindowKey); 267 return quark; 268} 269 270} // namespace 271 272std::map<XID, GtkWindow*> BrowserWindowGtk::xid_map_; 273 274BrowserWindowGtk::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 288BrowserWindowGtk::~BrowserWindowGtk() { 289 ui::ActiveWindowWatcherX::RemoveObserver(this); 290 291 browser_->tabstrip_model()->RemoveObserver(this); 292} 293 294void 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 374gboolean 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 409void 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 535void 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 555void 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 585int BrowserWindowGtk::GetVerticalOffset() { 586 return (IsMaximized() || (!UseCustomFrame())) ? 587 -kCustomFrameBackgroundVerticalOffset : 0; 588} 589 590int 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 603void 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 624void BrowserWindowGtk::ShowInactive() { 625 gtk_window_set_focus_on_map(window_, false); 626 gtk_widget_show(GTK_WIDGET(window_)); 627} 628 629void 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 648void BrowserWindowGtk::SetBounds(const gfx::Rect& bounds) { 649 if (IsFullscreen()) 650 SetFullscreen(false); 651 SetBoundsImpl(bounds, true, true); 652} 653 654void 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 697void BrowserWindowGtk::Activate() { 698 gtk_window_present(window_); 699} 700 701void BrowserWindowGtk::Deactivate() { 702 gdk_window_lower(GTK_WIDGET(window_)->window); 703} 704 705bool BrowserWindowGtk::IsActive() const { 706 return is_active_; 707} 708 709void BrowserWindowGtk::FlashFrame() { 710 // May not be respected by all window managers. 711 gtk_window_set_urgency_hint(window_, TRUE); 712} 713 714gfx::NativeWindow BrowserWindowGtk::GetNativeHandle() { 715 return window_; 716} 717 718BrowserWindowTesting* BrowserWindowGtk::GetBrowserWindowTesting() { 719 NOTIMPLEMENTED(); 720 return NULL; 721} 722 723StatusBubble* BrowserWindowGtk::GetStatusBubble() { 724 return status_bubble_.get(); 725} 726 727void 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 732void 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 739void BrowserWindowGtk::ShelfVisibilityChanged() { 740 MaybeShowBookmarkBar(false); 741} 742 743void BrowserWindowGtk::UpdateDevTools() { 744 UpdateDevToolsForContents( 745 browser_->GetSelectedTabContents()); 746} 747 748void 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 765void 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 783void BrowserWindowGtk::SetStarredState(bool is_starred) { 784 toolbar_->GetLocationBarView()->SetStarred(is_starred); 785} 786 787gfx::Rect BrowserWindowGtk::GetRestoredBounds() const { 788 return restored_bounds_; 789} 790 791gfx::Rect BrowserWindowGtk::GetBounds() const { 792 return bounds_; 793} 794 795bool BrowserWindowGtk::IsMaximized() const { 796 return (state_ & GDK_WINDOW_STATE_MAXIMIZED); 797} 798 799bool BrowserWindowGtk::ShouldDrawContentDropShadow() { 800 return !IsMaximized() && UseCustomFrame(); 801} 802 803void 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 826bool BrowserWindowGtk::IsFullscreen() const { 827 return (state_ & GDK_WINDOW_STATE_FULLSCREEN); 828} 829 830bool BrowserWindowGtk::IsFullscreenBubbleVisible() const { 831 return fullscreen_exit_bubble_.get() ? true : false; 832} 833 834LocationBar* BrowserWindowGtk::GetLocationBar() const { 835 return toolbar_->GetLocationBar(); 836} 837 838void BrowserWindowGtk::SetFocusToLocationBar(bool select_all) { 839 if (!IsFullscreen()) 840 GetLocationBar()->FocusLocation(select_all); 841} 842 843void BrowserWindowGtk::UpdateReloadStopState(bool is_loading, bool force) { 844 toolbar_->GetReloadButton()->ChangeMode( 845 is_loading ? ReloadButtonGtk::MODE_STOP : ReloadButtonGtk::MODE_RELOAD, 846 force); 847} 848 849void BrowserWindowGtk::UpdateToolbar(TabContentsWrapper* contents, 850 bool should_restore_state) { 851 toolbar_->UpdateTabContents(contents->tab_contents(), should_restore_state); 852} 853 854void BrowserWindowGtk::FocusToolbar() { 855 NOTIMPLEMENTED(); 856} 857 858void BrowserWindowGtk::FocusAppMenu() { 859 NOTIMPLEMENTED(); 860} 861 862void BrowserWindowGtk::FocusBookmarksToolbar() { 863 NOTIMPLEMENTED(); 864} 865 866void BrowserWindowGtk::FocusChromeOSStatus() { 867 NOTIMPLEMENTED(); 868} 869 870void BrowserWindowGtk::RotatePaneFocus(bool forwards) { 871 NOTIMPLEMENTED(); 872} 873 874bool 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 883bool BrowserWindowGtk::IsBookmarkBarAnimating() const { 884 if (IsBookmarkBarSupported() && bookmark_bar_->IsAnimating()) 885 return true; 886 return false; 887} 888 889bool BrowserWindowGtk::IsTabStripEditable() const { 890 return !tabstrip()->IsDragSessionActive() && 891 !tabstrip()->IsActiveDropTarget(); 892} 893 894bool BrowserWindowGtk::IsToolbarVisible() const { 895 return IsToolbarSupported(); 896} 897 898void BrowserWindowGtk::ConfirmAddSearchProvider(const TemplateURL* template_url, 899 Profile* profile) { 900 new EditSearchEngineDialog(window_, template_url, NULL, profile); 901} 902 903void BrowserWindowGtk::ToggleBookmarkBar() { 904 bookmark_utils::ToggleWhenVisible(browser_->profile()); 905} 906 907void BrowserWindowGtk::ShowAboutChromeDialog() { 908 ShowAboutDialogForProfile(window_, browser_->profile()); 909} 910 911void BrowserWindowGtk::ShowUpdateChromeDialog() { 912 UpdateRecommendedDialog::Show(window_); 913} 914 915void BrowserWindowGtk::ShowTaskManager() { 916 TaskManagerGtk::Show(false); 917} 918 919void BrowserWindowGtk::ShowBackgroundPages() { 920 TaskManagerGtk::Show(true); 921} 922 923void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url, 924 bool already_bookmarked) { 925 toolbar_->GetLocationBarView()->ShowStarBubble(url, !already_bookmarked); 926} 927 928bool BrowserWindowGtk::IsDownloadShelfVisible() const { 929 return download_shelf_.get() && download_shelf_->IsShowing(); 930} 931 932DownloadShelf* 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 939void BrowserWindowGtk::ShowRepostFormWarningDialog(TabContents* tab_contents) { 940 new RepostFormWarningGtk(GetNativeHandle(), tab_contents); 941} 942 943void BrowserWindowGtk::ShowCollectedCookiesDialog(TabContents* tab_contents) { 944 // Deletes itself on close. 945 new CollectedCookiesGtk(GetNativeHandle(), tab_contents); 946} 947 948void BrowserWindowGtk::ShowThemeInstallBubble() { 949 ThemeInstallBubbleViewGtk::Show(window_); 950} 951 952void BrowserWindowGtk::ShowHTMLDialog(HtmlDialogUIDelegate* delegate, 953 gfx::NativeWindow parent_window) { 954 browser::ShowHtmlDialog(parent_window, browser_->profile(), delegate); 955} 956 957void 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 964int 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 973void BrowserWindowGtk::TabContentsFocused(TabContents* tab_contents) { 974 NOTIMPLEMENTED(); 975} 976 977void 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 984void BrowserWindowGtk::ShowAppMenu() { 985 toolbar_->ShowAppMenu(); 986} 987 988bool 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 1048void 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 1069void BrowserWindowGtk::ShowCreateWebAppShortcutsDialog( 1070 TabContentsWrapper* tab_contents) { 1071 CreateWebApplicationShortcutsDialogGtk::Show(window_, tab_contents); 1072} 1073 1074void BrowserWindowGtk::ShowCreateChromeAppShortcutsDialog( 1075 Profile* profile, const Extension* app) { 1076 CreateChromeApplicationShortcutsDialogGtk::Show(window_, app); 1077} 1078 1079void BrowserWindowGtk::Cut() { 1080 gtk_util::DoCut(this); 1081} 1082 1083void BrowserWindowGtk::Copy() { 1084 gtk_util::DoCopy(this); 1085} 1086 1087void BrowserWindowGtk::Paste() { 1088 gtk_util::DoPaste(this); 1089} 1090 1091void BrowserWindowGtk::PrepareForInstant() { 1092 TabContentsWrapper* contents = contents_container_->tab(); 1093 if (contents) 1094 FadeForInstant(true); 1095} 1096 1097void 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 1106void 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 1119gfx::Rect BrowserWindowGtk::GetInstantBounds() { 1120 return gtk_util::GetWidgetScreenBounds(contents_container_->widget()); 1121} 1122 1123void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() { 1124 new DownloadInProgressDialogGtk(browser()); 1125} 1126 1127void 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 1150void 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 1161void 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 1192void 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 1220void 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 1230void 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 1238void 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 1267void 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 1299void BrowserWindowGtk::DestroyBrowser() { 1300 browser_.reset(); 1301} 1302 1303gboolean 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 1349void 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 1359gboolean 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). 1393gboolean 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 1402void 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 1419void 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 1430bool 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 1458bool BrowserWindowGtk::ShouldShowWindowIcon() const { 1459 return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR); 1460} 1461 1462void BrowserWindowGtk::DisableDebounceTimerForTests(bool is_disabled) { 1463 debounce_timer_disabled_ = is_disabled; 1464 if (is_disabled) 1465 window_configure_debounce_timer_.Stop(); 1466} 1467 1468void BrowserWindowGtk::AddFindBar(FindBarGtk* findbar) { 1469 gtk_floating_container_add_floating( 1470 GTK_FLOATING_CONTAINER(render_area_floating_container_), 1471 findbar->widget()); 1472} 1473 1474void 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 1483BrowserWindowGtk* 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 1494GtkWindow* 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 1501void 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 1513void 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 1522TabContents* BrowserWindowGtk::GetDisplayedTabContents() { 1523 return contents_container_->GetVisibleTabContents(); 1524} 1525 1526void BrowserWindowGtk::QueueToolbarRedraw() { 1527 gtk_widget_queue_draw(toolbar_->widget()); 1528} 1529 1530void 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 1558void 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 1583void 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 1730void 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 1770void BrowserWindowGtk::OnSizeChanged(int width, int height) { 1771 UpdateWindowShape(width, height); 1772} 1773 1774void 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 1811void 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 1828void BrowserWindowGtk::UpdateCustomFrame() { 1829 gtk_window_set_decorated(window_, !UseCustomFrame()); 1830 titlebar_->UpdateCustomFrame(UseCustomFrame() && !IsFullscreen()); 1831 UpdateWindowShape(bounds_.width(), bounds_.height()); 1832} 1833 1834void 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 1868void BrowserWindowGtk::SetInfoBarShowing(InfoBar* bar, bool animate) { 1869 infobar_arrow_model_.ShowArrowFor(bar, animate); 1870} 1871 1872void BrowserWindowGtk::PaintStateChanged() { 1873 InvalidateInfoBarBits(); 1874} 1875 1876void 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 1883int 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 1898void 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 1904gboolean 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 1933gfx::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 1948gboolean 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 1959void 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 1968gboolean 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. 1982gboolean 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 2013gboolean 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 2050gboolean 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 2170void 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 2178void BrowserWindowGtk::MainWindowUnMapped(GtkWidget* widget) { 2179 // Unmap the X Window ID. 2180 XID xid = ui::GetX11WindowFromGtkWidget(widget); 2181 BrowserWindowGtk::xid_map_.erase(xid); 2182} 2183 2184gboolean BrowserWindowGtk::OnFocusIn(GtkWidget* widget, 2185 GdkEventFocus* event) { 2186 BrowserList::SetLastActive(browser_.get()); 2187 return FALSE; 2188} 2189 2190gboolean BrowserWindowGtk::OnFocusOut(GtkWidget* widget, 2191 GdkEventFocus* event) { 2192 return FALSE; 2193} 2194 2195void 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 2209void 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 2219bool BrowserWindowGtk::IsTabStripSupported() const { 2220 return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP); 2221} 2222 2223bool BrowserWindowGtk::IsToolbarSupported() const { 2224 return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) || 2225 browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); 2226} 2227 2228bool BrowserWindowGtk::IsBookmarkBarSupported() const { 2229 return browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR); 2230} 2231 2232bool 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 2239bool 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 2296bool 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 2303bool 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 2314void 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 2335bool 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