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