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