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/views/frame/browser_view.h" 6 7 #include <algorithm> 8 9 #include "base/auto_reset.h" 10 #include "base/command_line.h" 11 #include "base/i18n/rtl.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/metrics/histogram.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "chrome/app/chrome_command_ids.h" 18 #include "chrome/app/chrome_dll_resource.h" 19 #include "chrome/browser/app_mode/app_mode_utils.h" 20 #include "chrome/browser/bookmarks/bookmark_utils.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/chrome_notification_types.h" 23 #include "chrome/browser/extensions/tab_helper.h" 24 #include "chrome/browser/infobars/infobar_service.h" 25 #include "chrome/browser/native_window_notification_source.h" 26 #include "chrome/browser/password_manager/password_manager.h" 27 #include "chrome/browser/profiles/avatar_menu_model.h" 28 #include "chrome/browser/profiles/profile.h" 29 #include "chrome/browser/profiles/profile_info_cache.h" 30 #include "chrome/browser/profiles/profile_manager.h" 31 #include "chrome/browser/search/search.h" 32 #include "chrome/browser/sessions/tab_restore_service.h" 33 #include "chrome/browser/sessions/tab_restore_service_factory.h" 34 #include "chrome/browser/speech/tts_controller.h" 35 #include "chrome/browser/themes/theme_properties.h" 36 #include "chrome/browser/themes/theme_service_factory.h" 37 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h" 38 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" 39 #include "chrome/browser/ui/bookmarks/bookmark_bubble_delegate.h" 40 #include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h" 41 #include "chrome/browser/ui/bookmarks/bookmark_utils.h" 42 #include "chrome/browser/ui/browser.h" 43 #include "chrome/browser/ui/browser_command_controller.h" 44 #include "chrome/browser/ui/browser_commands.h" 45 #include "chrome/browser/ui/browser_dialogs.h" 46 #include "chrome/browser/ui/browser_finder.h" 47 #include "chrome/browser/ui/browser_list.h" 48 #include "chrome/browser/ui/browser_window_state.h" 49 #include "chrome/browser/ui/immersive_fullscreen_configuration.h" 50 #include "chrome/browser/ui/ntp_background_util.h" 51 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 52 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h" 53 #include "chrome/browser/ui/omnibox/omnibox_view.h" 54 #include "chrome/browser/ui/search/search_delegate.h" 55 #include "chrome/browser/ui/search/search_model.h" 56 #include "chrome/browser/ui/search/search_ui.h" 57 #include "chrome/browser/ui/tabs/tab_menu_model.h" 58 #include "chrome/browser/ui/tabs/tab_strip_model.h" 59 #include "chrome/browser/ui/view_ids.h" 60 #include "chrome/browser/ui/views/accelerator_table.h" 61 #include "chrome/browser/ui/views/accessibility/invert_bubble_view.h" 62 #include "chrome/browser/ui/views/avatar_menu_bubble_view.h" 63 #include "chrome/browser/ui/views/avatar_menu_button.h" 64 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" 65 #include "chrome/browser/ui/views/browser_dialogs.h" 66 #include "chrome/browser/ui/views/download/download_in_progress_dialog_view.h" 67 #include "chrome/browser/ui/views/download/download_shelf_view.h" 68 #include "chrome/browser/ui/views/frame/browser_view_layout.h" 69 #include "chrome/browser/ui/views/frame/browser_view_layout_delegate.h" 70 #include "chrome/browser/ui/views/frame/contents_container.h" 71 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h" 72 #include "chrome/browser/ui/views/frame/top_container_view.h" 73 #include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h" 74 #include "chrome/browser/ui/views/infobars/infobar_container_view.h" 75 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 76 #include "chrome/browser/ui/views/location_bar/location_icon_view.h" 77 #include "chrome/browser/ui/views/omnibox/omnibox_view_views.h" 78 #include "chrome/browser/ui/views/omnibox/omnibox_views.h" 79 #include "chrome/browser/ui/views/password_generation_bubble_view.h" 80 #include "chrome/browser/ui/views/status_bubble_views.h" 81 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h" 82 #include "chrome/browser/ui/views/tabs/tab.h" 83 #include "chrome/browser/ui/views/tabs/tab_strip.h" 84 #include "chrome/browser/ui/views/toolbar_view.h" 85 #include "chrome/browser/ui/views/update_recommended_message_box.h" 86 #include "chrome/browser/ui/views/website_settings/website_settings_popup_view.h" 87 #include "chrome/browser/ui/window_sizer/window_sizer.h" 88 #include "chrome/common/chrome_switches.h" 89 #include "chrome/common/pref_names.h" 90 #include "chrome/common/url_constants.h" 91 #include "content/public/browser/download_manager.h" 92 #include "content/public/browser/native_web_keyboard_event.h" 93 #include "content/public/browser/notification_service.h" 94 #include "content/public/browser/render_view_host.h" 95 #include "content/public/browser/render_widget_host.h" 96 #include "content/public/browser/user_metrics.h" 97 #include "content/public/browser/web_contents.h" 98 #include "content/public/browser/web_contents_view.h" 99 #include "content/public/common/content_switches.h" 100 #include "grit/chromium_strings.h" 101 #include "grit/generated_resources.h" 102 #include "grit/locale_settings.h" 103 #include "grit/theme_resources.h" 104 #include "grit/ui_resources.h" 105 #include "grit/ui_strings.h" 106 #include "grit/webkit_resources.h" 107 #include "ui/base/accelerators/accelerator.h" 108 #include "ui/base/accessibility/accessible_view_state.h" 109 #include "ui/base/events/event_utils.h" 110 #include "ui/base/hit_test.h" 111 #include "ui/base/l10n/l10n_util.h" 112 #include "ui/base/resource/resource_bundle.h" 113 #include "ui/base/theme_provider.h" 114 #include "ui/gfx/canvas.h" 115 #include "ui/gfx/color_utils.h" 116 #include "ui/gfx/sys_color_change_listener.h" 117 #include "ui/views/controls/button/menu_button.h" 118 #include "ui/views/controls/single_split_view.h" 119 #include "ui/views/controls/textfield/textfield.h" 120 #include "ui/views/controls/webview/webview.h" 121 #include "ui/views/focus/external_focus_tracker.h" 122 #include "ui/views/focus/view_storage.h" 123 #include "ui/views/layout/grid_layout.h" 124 #include "ui/views/widget/native_widget.h" 125 #include "ui/views/widget/root_view.h" 126 #include "ui/views/widget/widget.h" 127 #include "ui/views/window/dialog_delegate.h" 128 129 #if defined(USE_ASH) 130 #include "ash/launcher/launcher.h" 131 #include "ash/launcher/launcher_model.h" 132 #include "ash/shell.h" 133 #include "chrome/browser/ui/ash/ash_util.h" 134 #include "chrome/browser/ui/ash/chrome_shell_delegate.h" 135 #include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h" 136 #include "chrome/browser/ui/ash/window_positioner.h" 137 #endif 138 139 #if defined(USE_AURA) 140 #include "ui/aura/window.h" 141 #include "ui/gfx/screen.h" 142 #elif defined(OS_WIN) // !defined(USE_AURA) 143 #include "chrome/browser/jumplist_win.h" 144 #include "chrome/browser/ui/views/omnibox/omnibox_view_win.h" 145 #include "ui/views/widget/native_widget_win.h" 146 #include "ui/views/win/scoped_fullscreen_visibility.h" 147 #endif 148 149 #if defined(OS_WIN) 150 #include "base/win/windows_version.h" 151 #include "win8/util/win8_util.h" 152 #endif 153 154 #if defined(ENABLE_ONE_CLICK_SIGNIN) 155 #include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h" 156 #include "chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h" 157 #include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h" 158 #endif 159 160 using base::TimeDelta; 161 using content::NativeWebKeyboardEvent; 162 using content::SSLStatus; 163 using content::UserMetricsAction; 164 using content::WebContents; 165 using views::ColumnSet; 166 using views::GridLayout; 167 using web_modal::WebContentsModalDialogHost; 168 169 namespace { 170 // The height of the status bubble. 171 const int kStatusBubbleHeight = 20; 172 // The name of a key to store on the window handle so that other code can 173 // locate this object using just the handle. 174 const char* const kBrowserViewKey = "__BROWSER_VIEW__"; 175 176 // The number of milliseconds between loading animation frames. 177 const int kLoadingAnimationFrameTimeMs = 30; 178 // The amount of space we expect the window border to take up. 179 const int kWindowBorderWidth = 5; 180 181 // How round the 'new tab' style bookmarks bar is. 182 const int kNewtabBarRoundness = 5; 183 184 // TODO(kuan): These functions are temporarily for the bookmark bar while its 185 // detached state is at the top of the page; it'll be moved to float on the 186 // content page in the very near future, at which time, these local functions 187 // will be removed. 188 void PaintDetachedBookmarkBar(gfx::Canvas* canvas, 189 DetachableToolbarView* view, 190 ThemeService* theme_service) { 191 // Paint background for detached state; if animating, this is fade in/out. 192 canvas->DrawColor( 193 chrome::GetDetachedBookmarkBarBackgroundColor(theme_service)); 194 // Draw the separators above and below bookmark bar; 195 // if animating, these are fading in/out. 196 SkColor separator_color = 197 chrome::GetDetachedBookmarkBarSeparatorColor(theme_service); 198 DetachableToolbarView::PaintHorizontalBorder(canvas, view, true, 199 separator_color); 200 // The bottom border needs to be 1-px thick in both regular and retina 201 // displays, so we can't use DetachableToolbarView::PaintHorizontalBorder 202 // which paints a 2-px thick border in retina display. 203 SkPaint paint; 204 paint.setAntiAlias(false); 205 // Sets border to 1-px thick regardless of scale factor. 206 paint.setStrokeWidth(0); 207 // Bottom border is at 50% opacity of top border. 208 paint.setColor(SkColorSetA(separator_color, 209 SkColorGetA(separator_color) / 2)); 210 // Calculate thickness of bottom border as per current scale factor to 211 // determine where to draw the 1-px thick border. 212 float thickness = views::NonClientFrameView::kClientEdgeThickness / 213 ui::GetScaleFactorScale(canvas->scale_factor()); 214 SkScalar y = SkIntToScalar(view->height()) - SkFloatToScalar(thickness); 215 canvas->sk_canvas()->drawLine(SkIntToScalar(0), y, 216 SkIntToScalar(view->width()), y, paint); 217 } 218 219 void PaintAttachedBookmarkBar(gfx::Canvas* canvas, 220 DetachableToolbarView* view, 221 BrowserView* browser_view, 222 chrome::HostDesktopType host_desktop_type, 223 int toolbar_overlap) { 224 // Paint background for attached state, this is fade in/out. 225 gfx::Point background_image_offset = 226 browser_view->OffsetPointForToolbarBackgroundImage( 227 gfx::Point(view->GetMirroredX(), view->y())); 228 DetachableToolbarView::PaintBackgroundAttachedMode(canvas, 229 view->GetThemeProvider(), view->GetLocalBounds(), 230 background_image_offset, host_desktop_type); 231 if (view->height() >= toolbar_overlap) { 232 // Draw the separator below bookmark bar; this is fading in/out. 233 DetachableToolbarView::PaintHorizontalBorder(canvas, view, false, 234 ThemeProperties::GetDefaultColor( 235 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 236 } 237 } 238 239 } // namespace 240 241 // static 242 const char BrowserView::kViewClassName[] = "BrowserView"; 243 244 namespace { 245 246 bool ShouldSaveOrRestoreWindowPos() { 247 #if defined(OS_WIN) 248 // In Windows 8's single window Metro mode the window is always maximized 249 // (without the WS_MAXIMIZE style). 250 if (win8::IsSingleWindowMetroMode()) 251 return false; 252 #endif 253 return true; 254 } 255 256 } // namespace 257 258 /////////////////////////////////////////////////////////////////////////////// 259 260 // Delegate implementation for BrowserViewLayout. Usually just forwards calls 261 // into BrowserView. 262 class BrowserViewLayoutDelegateImpl : public BrowserViewLayoutDelegate { 263 public: 264 explicit BrowserViewLayoutDelegateImpl(BrowserView* browser_view) 265 : browser_view_(browser_view) {} 266 virtual ~BrowserViewLayoutDelegateImpl() {} 267 268 // BrowserViewLayoutDelegate overrides: 269 virtual views::View* GetWindowSwitcherButton() const OVERRIDE { 270 return browser_view_->window_switcher_button(); 271 } 272 273 virtual bool DownloadShelfNeedsLayout() const OVERRIDE { 274 DownloadShelfView* download_shelf = browser_view_->download_shelf_.get(); 275 // Re-layout the shelf either if it is visible or if its close animation 276 // is currently running. 277 return download_shelf && 278 (download_shelf->IsShowing() || download_shelf->IsClosing()); 279 } 280 281 virtual bool IsTabStripVisible() const OVERRIDE { 282 return browser_view_->IsTabStripVisible(); 283 } 284 285 virtual gfx::Rect GetBoundsForTabStrip( 286 views::View* tab_strip) const OVERRIDE { 287 return browser_view_->frame()->GetBoundsForTabStrip(tab_strip); 288 } 289 290 virtual bool IsToolbarVisible() const OVERRIDE { 291 return browser_view_->IsToolbarVisible(); 292 } 293 294 virtual bool IsBookmarkBarVisible() const OVERRIDE { 295 return browser_view_->IsBookmarkBarVisible(); 296 } 297 298 virtual FullscreenExitBubbleViews* GetFullscreenExitBubble() const OVERRIDE { 299 return browser_view_->fullscreen_exit_bubble(); 300 } 301 302 private: 303 BrowserView* browser_view_; 304 305 DISALLOW_COPY_AND_ASSIGN(BrowserViewLayoutDelegateImpl); 306 }; 307 308 /////////////////////////////////////////////////////////////////////////////// 309 // BookmarkExtensionBackground, private: 310 // This object serves as the views::Background object which is used to layout 311 // and paint the bookmark bar. 312 class BookmarkExtensionBackground : public views::Background { 313 public: 314 BookmarkExtensionBackground(BrowserView* browser_view, 315 DetachableToolbarView* host_view, 316 Browser* browser); 317 318 // View methods overridden from views:Background. 319 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE; 320 321 private: 322 BrowserView* browser_view_; 323 324 // The view hosting this background. 325 DetachableToolbarView* host_view_; 326 327 Browser* browser_; 328 329 DISALLOW_COPY_AND_ASSIGN(BookmarkExtensionBackground); 330 }; 331 332 BookmarkExtensionBackground::BookmarkExtensionBackground( 333 BrowserView* browser_view, 334 DetachableToolbarView* host_view, 335 Browser* browser) 336 : browser_view_(browser_view), 337 host_view_(host_view), 338 browser_(browser) { 339 } 340 341 void BookmarkExtensionBackground::Paint(gfx::Canvas* canvas, 342 views::View* view) const { 343 int toolbar_overlap = host_view_->GetToolbarOverlap(); 344 if (!host_view_->IsDetached()) { 345 PaintAttachedBookmarkBar(canvas, host_view_, browser_view_, 346 browser_->host_desktop_type(), toolbar_overlap); 347 return; 348 } 349 350 // As 'hidden' according to the animation is the full in-tab state, we invert 351 // the value - when current_state is at '0', we expect the bar to be docked. 352 double current_state = 1 - host_view_->GetAnimationValue(); 353 354 ThemeService* ts = 355 ThemeServiceFactory::GetForProfile(browser_->profile()); 356 if (current_state == 0.0 || current_state == 1.0) { 357 PaintDetachedBookmarkBar(canvas, host_view_, ts); 358 return; 359 } 360 // While animating, set opacity to cross-fade between attached and detached 361 // backgrounds including their respective separators. 362 int detached_alpha = static_cast<uint8>(current_state * 255); 363 int attached_alpha = 255 - detached_alpha; 364 if (browser_->bookmark_bar_state() == BookmarkBar::DETACHED) { 365 // To animate from attached to detached state: 366 // - fade out attached background 367 // - fade in detached background. 368 canvas->SaveLayerAlpha(attached_alpha); 369 PaintAttachedBookmarkBar(canvas, host_view_, browser_view_, 370 browser_->host_desktop_type(), 371 toolbar_overlap); 372 canvas->Restore(); 373 canvas->SaveLayerAlpha(detached_alpha); 374 PaintDetachedBookmarkBar(canvas, host_view_, ts); 375 } else { 376 // To animate from detached to attached state: 377 // - fade out detached background 378 // - fade in attached background. 379 canvas->SaveLayerAlpha(detached_alpha); 380 PaintDetachedBookmarkBar(canvas, host_view_, ts); 381 canvas->Restore(); 382 canvas->SaveLayerAlpha(attached_alpha); 383 PaintAttachedBookmarkBar(canvas, host_view_, browser_view_, 384 browser_->host_desktop_type(), 385 toolbar_overlap); 386 } 387 canvas->Restore(); 388 } 389 390 /////////////////////////////////////////////////////////////////////////////// 391 // BrowserView, public: 392 393 BrowserView::BrowserView() 394 : views::ClientView(NULL, NULL), 395 last_focused_view_storage_id_( 396 views::ViewStorage::GetInstance()->CreateStorageID()), 397 frame_(NULL), 398 top_container_(NULL), 399 tabstrip_(NULL), 400 toolbar_(NULL), 401 window_switcher_button_(NULL), 402 find_bar_host_view_(NULL), 403 infobar_container_(NULL), 404 contents_web_view_(NULL), 405 devtools_container_(NULL), 406 contents_container_(NULL), 407 contents_split_(NULL), 408 devtools_dock_side_(DEVTOOLS_DOCK_SIDE_BOTTOM), 409 devtools_window_(NULL), 410 initialized_(false), 411 ignore_layout_(true), 412 #if defined(OS_WIN) && !defined(USE_AURA) 413 hung_window_detector_(&hung_plugin_action_), 414 ticker_(0), 415 #endif 416 force_location_bar_focus_(false), 417 immersive_mode_controller_(chrome::CreateImmersiveModeController()), 418 #if defined(OS_CHROMEOS) 419 scroll_end_effect_controller_(ScrollEndEffectController::Create()), 420 #endif 421 color_change_listener_(this), 422 activate_modal_dialog_factory_(this) { 423 } 424 425 BrowserView::~BrowserView() { 426 #if defined(USE_ASH) 427 // Destroy BrowserLauncherItemController early on as it listens to the 428 // TabstripModel, which is destroyed by the browser. 429 launcher_item_controller_.reset(); 430 #endif 431 432 // Immersive mode may need to reparent views before they are removed/deleted. 433 immersive_mode_controller_.reset(); 434 435 browser_->tab_strip_model()->RemoveObserver(this); 436 437 #if defined(OS_WIN) && !defined(USE_AURA) 438 // Stop hung plugin monitoring. 439 ticker_.Stop(); 440 ticker_.UnregisterTickHandler(&hung_window_detector_); 441 442 // Terminate the jumplist (must be called before browser_->profile() is 443 // destroyed. 444 if (jumplist_) { 445 jumplist_->Terminate(); 446 } 447 #endif 448 449 // We destroy the download shelf before |browser_| to remove its child 450 // download views from the set of download observers (since the observed 451 // downloads can be destroyed along with |browser_| and the observer 452 // notifications will call back into deleted objects). 453 BrowserViewLayout* browser_view_layout = GetBrowserViewLayout(); 454 if (browser_view_layout) 455 browser_view_layout->set_download_shelf(NULL); 456 download_shelf_.reset(); 457 458 // The TabStrip attaches a listener to the model. Make sure we shut down the 459 // TabStrip first so that it can cleanly remove the listener. 460 if (tabstrip_) { 461 tabstrip_->parent()->RemoveChildView(tabstrip_); 462 if (browser_view_layout) 463 browser_view_layout->set_tab_strip(NULL); 464 delete tabstrip_; 465 tabstrip_ = NULL; 466 } 467 // Child views maintain PrefMember attributes that point to 468 // OffTheRecordProfile's PrefService which gets deleted by ~Browser. 469 RemoveAllChildViews(true); 470 471 // It is possible that we were forced-closed by the native view system and 472 // that tabs remain in the browser. Close any such remaining tabs. 473 while (browser_->tab_strip_model()->count()) 474 delete browser_->tab_strip_model()->GetWebContentsAt(0); 475 476 // Explicitly set browser_ to NULL. 477 browser_.reset(); 478 } 479 480 void BrowserView::Init(Browser* browser) { 481 browser_.reset(browser); 482 browser_->tab_strip_model()->AddObserver(this); 483 } 484 485 // static 486 BrowserView* BrowserView::GetBrowserViewForNativeWindow( 487 gfx::NativeWindow window) { 488 views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window); 489 return widget ? 490 reinterpret_cast<BrowserView*>(widget->GetNativeWindowProperty( 491 kBrowserViewKey)) : NULL; 492 } 493 494 // static 495 BrowserView* BrowserView::GetBrowserViewForBrowser(const Browser* browser) { 496 return static_cast<BrowserView*>(browser->window()); 497 } 498 499 void BrowserView::InitStatusBubble() { 500 status_bubble_.reset(new StatusBubbleViews(contents_container_)); 501 } 502 503 gfx::Rect BrowserView::GetToolbarBounds() const { 504 gfx::Rect toolbar_bounds(toolbar_->bounds()); 505 if (toolbar_bounds.IsEmpty()) 506 return toolbar_bounds; 507 // The apparent toolbar edges are outside the "real" toolbar edges. 508 toolbar_bounds.Inset(-views::NonClientFrameView::kClientEdgeThickness, 0); 509 return toolbar_bounds; 510 } 511 512 gfx::Rect BrowserView::GetClientAreaBounds() const { 513 gfx::Rect container_bounds = contents_container_->bounds(); 514 gfx::Point container_origin = container_bounds.origin(); 515 ConvertPointToTarget(this, parent(), &container_origin); 516 container_bounds.set_origin(container_origin); 517 return container_bounds; 518 } 519 520 gfx::Rect BrowserView::GetFindBarBoundingBox() const { 521 return GetBrowserViewLayout()->GetFindBarBoundingBox(); 522 } 523 524 int BrowserView::GetTabStripHeight() const { 525 // We want to return tabstrip_->height(), but we might be called in the midst 526 // of layout, when that hasn't yet been updated to reflect the current state. 527 // So return what the tabstrip height _ought_ to be right now. 528 return IsTabStripVisible() ? tabstrip_->GetPreferredSize().height() : 0; 529 } 530 531 gfx::Point BrowserView::OffsetPointForToolbarBackgroundImage( 532 const gfx::Point& point) const { 533 // The background image starts tiling horizontally at the window left edge and 534 // vertically at the top edge of the horizontal tab strip (or where it would 535 // be). We expect our parent's origin to be the window origin. 536 gfx::Point window_point(point + GetMirroredPosition().OffsetFromOrigin()); 537 window_point.Offset(frame_->GetThemeBackgroundXInset(), 538 -frame_->GetTabStripInsets(false).top); 539 return window_point; 540 } 541 542 bool BrowserView::IsTabStripVisible() const { 543 if (immersive_mode_controller_->ShouldHideTopViews() && 544 immersive_mode_controller_->ShouldHideTabIndicators()) 545 return false; 546 return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP); 547 } 548 549 bool BrowserView::IsOffTheRecord() const { 550 return browser_->profile()->IsOffTheRecord(); 551 } 552 553 int BrowserView::GetOTRIconResourceID() const { 554 int otr_resource_id = IDR_OTR_ICON; 555 if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) { 556 if (IsFullscreen()) 557 otr_resource_id = IDR_OTR_ICON_FULLSCREEN; 558 #if defined(OS_WIN) 559 if (win8::IsSingleWindowMetroMode()) 560 otr_resource_id = IDR_OTR_ICON_FULLSCREEN; 561 #endif 562 } 563 564 return otr_resource_id; 565 } 566 567 bool BrowserView::IsGuestSession() const { 568 return browser_->profile()->IsGuestSession(); 569 } 570 571 bool BrowserView::ShouldShowAvatar() const { 572 if (!IsBrowserTypeNormal()) 573 return false; 574 if (IsOffTheRecord() && !IsGuestSession()) 575 return true; 576 // Tests may not have a profile manager. 577 if (!g_browser_process->profile_manager()) 578 return false; 579 ProfileInfoCache& cache = 580 g_browser_process->profile_manager()->GetProfileInfoCache(); 581 if (cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath()) == 582 std::string::npos) { 583 return false; 584 } 585 586 return AvatarMenuModel::ShouldShowAvatarMenu(); 587 } 588 589 bool BrowserView::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { 590 // We retrieve the accelerator information for standard accelerators 591 // for cut, copy and paste. 592 if (chrome::GetStandardAcceleratorForCommandId(cmd_id, accelerator)) 593 return true; 594 // Else, we retrieve the accelerator information from the accelerator table. 595 for (std::map<ui::Accelerator, int>::const_iterator it = 596 accelerator_table_.begin(); it != accelerator_table_.end(); ++it) { 597 if (it->second == cmd_id) { 598 *accelerator = it->first; 599 return true; 600 } 601 } 602 // Else, we retrieve the accelerator information from Ash (if applicable). 603 return chrome::GetAshAcceleratorForCommandId( 604 cmd_id, browser_->host_desktop_type(), accelerator); 605 } 606 607 bool BrowserView::IsAcceleratorRegistered(const ui::Accelerator& accelerator) { 608 return accelerator_table_.find(accelerator) != accelerator_table_.end(); 609 } 610 611 WebContents* BrowserView::GetActiveWebContents() const { 612 return browser_->tab_strip_model()->GetActiveWebContents(); 613 } 614 615 gfx::ImageSkia BrowserView::GetOTRAvatarIcon() const { 616 return *GetThemeProvider()->GetImageSkiaNamed(GetOTRIconResourceID()); 617 } 618 619 /////////////////////////////////////////////////////////////////////////////// 620 // BrowserView, BrowserWindow implementation: 621 622 void BrowserView::Show() { 623 // If the window is already visible, just activate it. 624 if (frame_->IsVisible()) { 625 frame_->Activate(); 626 return; 627 } 628 629 CreateLauncherIcon(); 630 631 // Showing the window doesn't make the browser window active right away. 632 // This can cause SetFocusToLocationBar() to skip setting focus to the 633 // location bar. To avoid this we explicilty let SetFocusToLocationBar() 634 // know that it's ok to steal focus. 635 force_location_bar_focus_ = true; 636 637 // Setting the focus doesn't work when the window is invisible, so any focus 638 // initialization that happened before this will be lost. 639 // 640 // We really "should" restore the focus whenever the window becomes unhidden, 641 // but I think initializing is the only time where this can happen where 642 // there is some focus change we need to pick up, and this is easier than 643 // plumbing through an un-hide message all the way from the frame. 644 // 645 // If we do find there are cases where we need to restore the focus on show, 646 // that should be added and this should be removed. 647 RestoreFocus(); 648 649 frame_->Show(); 650 651 force_location_bar_focus_ = false; 652 653 browser()->OnWindowDidShow(); 654 655 chrome::MaybeShowInvertBubbleView(browser_.get(), contents_container_); 656 } 657 658 void BrowserView::ShowInactive() { 659 if (frame_->IsVisible()) 660 return; 661 CreateLauncherIcon(); 662 frame_->ShowInactive(); 663 } 664 665 void BrowserView::Hide() { 666 // Not implemented. 667 } 668 669 void BrowserView::SetBounds(const gfx::Rect& bounds) { 670 ExitFullscreen(); 671 GetWidget()->SetBounds(bounds); 672 } 673 674 void BrowserView::Close() { 675 frame_->Close(); 676 } 677 678 void BrowserView::Activate() { 679 frame_->Activate(); 680 } 681 682 void BrowserView::Deactivate() { 683 frame_->Deactivate(); 684 } 685 686 bool BrowserView::IsActive() const { 687 return frame_->IsActive(); 688 } 689 690 void BrowserView::FlashFrame(bool flash) { 691 frame_->FlashFrame(flash); 692 } 693 694 bool BrowserView::IsAlwaysOnTop() const { 695 return false; 696 } 697 698 gfx::NativeWindow BrowserView::GetNativeWindow() { 699 // While the browser destruction is going on, the widget can already be gone, 700 // but utility functions like FindBrowserWithWindow will come here and crash. 701 // We short circuit therefore. 702 if (!GetWidget()) 703 return NULL; 704 return GetWidget()->GetTopLevelWidget()->GetNativeWindow(); 705 } 706 707 BrowserWindowTesting* BrowserView::GetBrowserWindowTesting() { 708 return this; 709 } 710 711 StatusBubble* BrowserView::GetStatusBubble() { 712 return status_bubble_.get(); 713 } 714 715 namespace { 716 // Only used by ToolbarSizeChanged() below, but placed here because template 717 // arguments (to base::AutoReset<>) must have external linkage. 718 enum CallState { NORMAL, REENTRANT, REENTRANT_FORCE_FAST_RESIZE }; 719 } 720 721 void BrowserView::UpdateTitleBar() { 722 frame_->UpdateWindowTitle(); 723 if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning()) 724 frame_->UpdateWindowIcon(); 725 } 726 727 void BrowserView::BookmarkBarStateChanged( 728 BookmarkBar::AnimateChangeType change_type) { 729 if (bookmark_bar_view_.get()) { 730 BookmarkBar::State new_state = browser_->bookmark_bar_state(); 731 732 // We don't properly support animating the bookmark bar to and from the 733 // detached state in immersive fullscreen. 734 bool detached_changed = (new_state == BookmarkBar::DETACHED) || 735 bookmark_bar_view_->IsDetached(); 736 if (detached_changed && immersive_mode_controller_->IsEnabled()) 737 change_type = BookmarkBar::DONT_ANIMATE_STATE_CHANGE; 738 739 bookmark_bar_view_->SetBookmarkBarState(new_state, change_type); 740 } 741 if (MaybeShowBookmarkBar(GetActiveWebContents())) 742 Layout(); 743 } 744 745 void BrowserView::UpdateDevTools() { 746 UpdateDevToolsForContents(GetActiveWebContents()); 747 Layout(); 748 } 749 750 void BrowserView::UpdateLoadingAnimations(bool should_animate) { 751 if (should_animate) { 752 if (!loading_animation_timer_.IsRunning()) { 753 // Loads are happening, and the timer isn't running, so start it. 754 last_animation_time_ = base::TimeTicks::Now(); 755 loading_animation_timer_.Start(FROM_HERE, 756 TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs), this, 757 &BrowserView::LoadingAnimationCallback); 758 } 759 } else { 760 if (loading_animation_timer_.IsRunning()) { 761 last_animation_time_ = base::TimeTicks(); 762 loading_animation_timer_.Stop(); 763 // Loads are now complete, update the state if a task was scheduled. 764 LoadingAnimationCallback(); 765 } 766 } 767 } 768 769 void BrowserView::SetStarredState(bool is_starred) { 770 GetLocationBarView()->SetStarToggled(is_starred); 771 } 772 773 void BrowserView::ZoomChangedForActiveTab(bool can_show_bubble) { 774 GetLocationBarView()->ZoomChangedForActiveTab( 775 can_show_bubble && !toolbar_->IsWrenchMenuShowing()); 776 } 777 778 gfx::Rect BrowserView::GetRestoredBounds() const { 779 return frame_->GetRestoredBounds(); 780 } 781 782 ui::WindowShowState BrowserView::GetRestoredState() const { 783 if (IsMaximized()) 784 return ui::SHOW_STATE_MAXIMIZED; 785 if (IsMinimized()) 786 return ui::SHOW_STATE_MINIMIZED; 787 return ui::SHOW_STATE_NORMAL; 788 } 789 790 gfx::Rect BrowserView::GetBounds() const { 791 return frame_->GetWindowBoundsInScreen(); 792 } 793 794 bool BrowserView::IsMaximized() const { 795 return frame_->IsMaximized(); 796 } 797 798 bool BrowserView::IsMinimized() const { 799 return frame_->IsMinimized(); 800 } 801 802 void BrowserView::Maximize() { 803 frame_->Maximize(); 804 } 805 806 void BrowserView::Minimize() { 807 frame_->Minimize(); 808 } 809 810 void BrowserView::Restore() { 811 frame_->Restore(); 812 } 813 814 void BrowserView::EnterFullscreen( 815 const GURL& url, FullscreenExitBubbleType bubble_type) { 816 if (IsFullscreen()) 817 return; // Nothing to do. 818 819 ProcessFullscreen(true, FOR_DESKTOP, url, bubble_type); 820 } 821 822 void BrowserView::ExitFullscreen() { 823 if (!IsFullscreen()) 824 return; // Nothing to do. 825 826 ProcessFullscreen(false, FOR_DESKTOP, GURL(), FEB_TYPE_NONE); 827 } 828 829 void BrowserView::UpdateFullscreenExitBubbleContent( 830 const GURL& url, 831 FullscreenExitBubbleType bubble_type) { 832 // Immersive mode has no exit bubble because it has a visible strip at the 833 // top that gives the user a hover target. 834 // TODO(jamescook): Figure out what to do with mouse-lock. 835 if (bubble_type == FEB_TYPE_NONE || ShouldUseImmersiveFullscreenForUrl(url)) { 836 fullscreen_bubble_.reset(); 837 } else if (fullscreen_bubble_.get()) { 838 fullscreen_bubble_->UpdateContent(url, bubble_type); 839 } else { 840 fullscreen_bubble_.reset(new FullscreenExitBubbleViews( 841 this, url, bubble_type)); 842 } 843 } 844 845 bool BrowserView::ShouldHideUIForFullscreen() const { 846 #if defined(USE_ASH) 847 // Immersive mode needs UI for the slide-down top panel. 848 return IsFullscreen() && !immersive_mode_controller_->IsEnabled(); 849 #endif 850 return IsFullscreen(); 851 } 852 853 bool BrowserView::IsFullscreen() const { 854 return frame_->IsFullscreen(); 855 } 856 857 bool BrowserView::IsFullscreenBubbleVisible() const { 858 return fullscreen_bubble_ != NULL; 859 } 860 861 #if defined(OS_WIN) 862 void BrowserView::SetMetroSnapMode(bool enable) { 863 HISTOGRAM_COUNTS("Metro.SnapModeToggle", enable); 864 ProcessFullscreen(enable, FOR_METRO, GURL(), FEB_TYPE_NONE); 865 } 866 867 bool BrowserView::IsInMetroSnapMode() const { 868 #if defined(USE_AURA) 869 return false; 870 #else 871 return static_cast<views::NativeWidgetWin*>( 872 frame_->native_widget())->IsInMetroSnapMode(); 873 #endif 874 } 875 #endif // defined(OS_WIN) 876 877 void BrowserView::RestoreFocus() { 878 WebContents* selected_web_contents = GetActiveWebContents(); 879 if (selected_web_contents) 880 selected_web_contents->GetView()->RestoreFocus(); 881 } 882 883 void BrowserView::SetWindowSwitcherButton(views::Button* button) { 884 if (window_switcher_button_) 885 RemoveChildView(window_switcher_button_); 886 window_switcher_button_ = button; 887 } 888 889 void BrowserView::ToolbarSizeChanged(bool is_animating) { 890 // The call to InfoBarContainer::SetMaxTopArrowHeight() below can result in 891 // reentrancy; |call_state| tracks whether we're reentrant. We can't just 892 // early-return in this case because we need to layout again so the infobar 893 // container's bounds are set correctly. 894 static CallState call_state = NORMAL; 895 896 // A reentrant call can (and should) use the fast resize path unless both it 897 // and the normal call are both non-animating. 898 bool use_fast_resize = 899 is_animating || (call_state == REENTRANT_FORCE_FAST_RESIZE); 900 if (use_fast_resize) 901 contents_web_view_->SetFastResize(true); 902 UpdateUIForContents(GetActiveWebContents()); 903 if (use_fast_resize) 904 contents_web_view_->SetFastResize(false); 905 906 // Inform the InfoBarContainer that the distance to the location icon may have 907 // changed. We have to do this after the block above so that the toolbars are 908 // laid out correctly for calculating the maximum arrow height below. 909 { 910 base::AutoReset<CallState> resetter(&call_state, 911 is_animating ? REENTRANT_FORCE_FAST_RESIZE : REENTRANT); 912 infobar_container_->SetMaxTopArrowHeight(GetMaxTopInfoBarArrowHeight()); 913 } 914 915 // When transitioning from animating to not animating we need to make sure the 916 // contents_container_ gets layed out. If we don't do this and the bounds 917 // haven't changed contents_container_ won't get a Layout out and we'll end up 918 // with a gray rect because the clip wasn't updated. Note that a reentrant 919 // call never needs to do this, because after it returns, the normal call 920 // wrapping it will do it. 921 if ((call_state == NORMAL) && !is_animating) { 922 contents_web_view_->InvalidateLayout(); 923 contents_split_->Layout(); 924 } 925 } 926 927 LocationBar* BrowserView::GetLocationBar() const { 928 return GetLocationBarView(); 929 } 930 931 void BrowserView::SetFocusToLocationBar(bool select_all) { 932 // On Windows, changing focus to the location bar causes the browser 933 // window to become active. This can steal focus if the user has 934 // another window open already. On ChromeOS, changing focus makes a 935 // view believe it has a focus even if the widget doens't have a 936 // focus. Either cases, we need to ignore this when the browser 937 // window isn't active. 938 if (!force_location_bar_focus_ && !IsActive()) 939 return; 940 941 // Temporarily reveal the top-of-window views (if not already revealed) so 942 // that the location bar view is visible and is considered focusable. If the 943 // location bar view gains focus, |immersive_mode_controller_| will keep the 944 // top-of-window views revealed. 945 scoped_ptr<ImmersiveRevealedLock> focus_reveal_lock( 946 immersive_mode_controller_->GetRevealedLock( 947 ImmersiveModeController::ANIMATE_REVEAL_YES)); 948 949 LocationBarView* location_bar = GetLocationBarView(); 950 if (location_bar->IsLocationEntryFocusableInRootView()) { 951 // Location bar got focus. 952 location_bar->FocusLocation(select_all); 953 } else { 954 // If none of location bar got focus, 955 // then clear focus. 956 views::FocusManager* focus_manager = GetFocusManager(); 957 DCHECK(focus_manager); 958 focus_manager->ClearFocus(); 959 } 960 } 961 962 void BrowserView::UpdateReloadStopState(bool is_loading, bool force) { 963 toolbar_->reload_button()->ChangeMode( 964 is_loading ? ReloadButton::MODE_STOP : ReloadButton::MODE_RELOAD, force); 965 } 966 967 void BrowserView::UpdateToolbar(content::WebContents* contents, 968 bool should_restore_state) { 969 toolbar_->Update(contents, should_restore_state); 970 } 971 972 void BrowserView::FocusToolbar() { 973 // Temporarily reveal the top-of-window views (if not already revealed) so 974 // that the toolbar is visible and is considered focusable. If the 975 // toolbar gains focus, |immersive_mode_controller_| will keep the 976 // top-of-window views revealed. 977 scoped_ptr<ImmersiveRevealedLock> focus_reveal_lock( 978 immersive_mode_controller_->GetRevealedLock( 979 ImmersiveModeController::ANIMATE_REVEAL_YES)); 980 981 // Start the traversal within the main toolbar. SetPaneFocus stores 982 // the current focused view before changing focus. 983 toolbar_->SetPaneFocus(NULL); 984 } 985 986 void BrowserView::FocusBookmarksToolbar() { 987 DCHECK(!immersive_mode_controller_->IsEnabled()); 988 if (bookmark_bar_view_.get() && 989 bookmark_bar_view_->visible() && 990 bookmark_bar_view_->GetPreferredSize().height() != 0) { 991 bookmark_bar_view_->SetPaneFocusAndFocusDefault(); 992 } 993 } 994 995 void BrowserView::FocusInfobars() { 996 if (infobar_container_->child_count() > 0) 997 infobar_container_->SetPaneFocusAndFocusDefault(); 998 } 999 1000 void BrowserView::FocusAppMenu() { 1001 // Chrome doesn't have a traditional menu bar, but it has a menu button in the 1002 // main toolbar that plays the same role. If the user presses a key that 1003 // would typically focus the menu bar, tell the toolbar to focus the menu 1004 // button. If the user presses the key again, return focus to the previous 1005 // location. 1006 // 1007 // Not used on the Mac, which has a normal menu bar. 1008 if (toolbar_->IsAppMenuFocused()) { 1009 RestoreFocus(); 1010 } else { 1011 DCHECK(!immersive_mode_controller_->IsEnabled()); 1012 toolbar_->SetPaneFocusAndFocusAppMenu(); 1013 } 1014 } 1015 1016 void BrowserView::RotatePaneFocus(bool forwards) { 1017 GetWidget()->GetFocusManager()->RotatePaneFocus( 1018 forwards ? 1019 views::FocusManager::kForward : views::FocusManager::kBackward, 1020 views::FocusManager::kWrap); 1021 } 1022 1023 void BrowserView::DestroyBrowser() { 1024 // After this returns other parts of Chrome are going to be shutdown. Close 1025 // the window now so that we are deleted immediately and aren't left holding 1026 // references to deleted objects. 1027 GetWidget()->RemoveObserver(this); 1028 GetLocationBar()->GetLocationEntry()->model()->popup_model()->RemoveObserver( 1029 this); 1030 frame_->CloseNow(); 1031 } 1032 1033 bool BrowserView::IsBookmarkBarVisible() const { 1034 if (!browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR)) 1035 return false; 1036 if (!bookmark_bar_view_.get()) 1037 return false; 1038 if (bookmark_bar_view_->GetPreferredSize().height() == 0) 1039 return false; 1040 // New tab page needs visible bookmarks even when top-views are hidden. 1041 if (immersive_mode_controller_->ShouldHideTopViews() && 1042 !bookmark_bar_view_->IsDetached()) 1043 return false; 1044 return true; 1045 } 1046 1047 bool BrowserView::IsBookmarkBarAnimating() const { 1048 return bookmark_bar_view_.get() && bookmark_bar_view_->is_animating(); 1049 } 1050 1051 bool BrowserView::IsTabStripEditable() const { 1052 return tabstrip_->IsTabStripEditable(); 1053 } 1054 1055 bool BrowserView::IsToolbarVisible() const { 1056 if (immersive_mode_controller_->ShouldHideTopViews()) 1057 return false; 1058 return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) || 1059 browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); 1060 } 1061 1062 gfx::Rect BrowserView::GetRootWindowResizerRect() const { 1063 // Views does not support resizer rects because they caused page cycler 1064 // performance regressions when they were added. See crrev.com/9654 1065 return gfx::Rect(); 1066 } 1067 1068 void BrowserView::DisableInactiveFrame() { 1069 #if defined(OS_WIN) && !defined(USE_AURA) 1070 frame_->DisableInactiveRendering(); 1071 #endif // No tricks are needed to get the right behavior on Linux. 1072 } 1073 1074 void BrowserView::ConfirmAddSearchProvider(TemplateURL* template_url, 1075 Profile* profile) { 1076 chrome::EditSearchEngine(GetWidget()->GetNativeWindow(), template_url, NULL, 1077 profile); 1078 } 1079 1080 void BrowserView::ToggleBookmarkBar() { 1081 chrome::ToggleBookmarkBarWhenVisible(browser_->profile()); 1082 } 1083 1084 void BrowserView::ShowUpdateChromeDialog() { 1085 UpdateRecommendedMessageBox::Show(GetWidget()->GetNativeWindow()); 1086 } 1087 1088 void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) { 1089 scoped_ptr<BookmarkBubbleDelegate> delegate; 1090 delegate.reset(new BookmarkBubbleSignInDelegate(browser_.get())); 1091 1092 chrome::ShowBookmarkBubbleView(GetToolbarView()->GetBookmarkBubbleAnchor(), 1093 bookmark_bar_view_.get(), 1094 delegate.Pass(), 1095 browser_->profile(), 1096 url, 1097 !already_bookmarked); 1098 } 1099 1100 void BrowserView::ShowBookmarkPrompt() { 1101 GetLocationBarView()->ShowBookmarkPrompt(); 1102 } 1103 1104 #if defined(ENABLE_ONE_CLICK_SIGNIN) 1105 void BrowserView::ShowOneClickSigninBubble( 1106 OneClickSigninBubbleType type, 1107 const string16& email, 1108 const string16& error_message, 1109 const StartSyncCallback& start_sync_callback) { 1110 scoped_ptr<OneClickSigninBubbleDelegate> delegate; 1111 delegate.reset(new OneClickSigninBubbleLinksDelegate(browser())); 1112 1113 views::View* anchor_view; 1114 if (type == BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE) 1115 anchor_view = toolbar_->app_menu(); 1116 else 1117 anchor_view = toolbar_->location_bar(); 1118 1119 OneClickSigninBubbleView::ShowBubble(type, email, error_message, 1120 delegate.Pass(), anchor_view, 1121 start_sync_callback); 1122 } 1123 #endif 1124 1125 void BrowserView::SetDownloadShelfVisible(bool visible) { 1126 // This can be called from the superclass destructor, when it destroys our 1127 // child views. At that point, browser_ is already gone. 1128 if (browser_ == NULL) 1129 return; 1130 1131 if (visible && IsDownloadShelfVisible() != visible) { 1132 // Invoke GetDownloadShelf to force the shelf to be created. 1133 GetDownloadShelf(); 1134 } 1135 1136 if (browser_ != NULL) 1137 browser_->UpdateDownloadShelfVisibility(visible); 1138 1139 // SetDownloadShelfVisible can force-close the shelf, so make sure we lay out 1140 // everything correctly, as if the animation had finished. This doesn't 1141 // matter for showing the shelf, as the show animation will do it. 1142 ToolbarSizeChanged(false); 1143 } 1144 1145 bool BrowserView::IsDownloadShelfVisible() const { 1146 return download_shelf_.get() && download_shelf_->IsShowing(); 1147 } 1148 1149 DownloadShelf* BrowserView::GetDownloadShelf() { 1150 if (!download_shelf_.get()) { 1151 download_shelf_.reset(new DownloadShelfView(browser_.get(), this)); 1152 download_shelf_->set_owned_by_client(); 1153 GetBrowserViewLayout()->set_download_shelf(download_shelf_.get()); 1154 } 1155 return download_shelf_.get(); 1156 } 1157 1158 void BrowserView::ConfirmBrowserCloseWithPendingDownloads() { 1159 DownloadInProgressDialogView::Show(browser_.get(), GetNativeWindow()); 1160 } 1161 1162 void BrowserView::ShowCreateChromeAppShortcutsDialog( 1163 Profile* profile, 1164 const extensions::Extension* app) { 1165 chrome::ShowCreateChromeAppShortcutsDialog( 1166 GetNativeWindow(), profile, app, base::Closure()); 1167 } 1168 1169 void BrowserView::UserChangedTheme() { 1170 frame_->FrameTypeChanged(); 1171 } 1172 1173 int BrowserView::GetExtraRenderViewHeight() const { 1174 // Currently this is only used on linux. 1175 return 0; 1176 } 1177 1178 void BrowserView::WebContentsFocused(WebContents* contents) { 1179 if (contents_web_view_->GetWebContents() == contents) 1180 contents_web_view_->OnWebContentsFocused(contents); 1181 else 1182 devtools_container_->OnWebContentsFocused(contents); 1183 } 1184 1185 void BrowserView::ShowWebsiteSettings(Profile* profile, 1186 content::WebContents* web_contents, 1187 const GURL& url, 1188 const content::SSLStatus& ssl) { 1189 WebsiteSettingsPopupView::ShowPopup( 1190 GetLocationBarView()->location_icon_view(), profile, 1191 web_contents, url, ssl, browser_.get()); 1192 } 1193 1194 void BrowserView::ShowAppMenu() { 1195 // Keep the top-of-window views revealed as long as the app menu is visible. 1196 scoped_ptr<ImmersiveRevealedLock> revealed_lock( 1197 immersive_mode_controller_->GetRevealedLock( 1198 ImmersiveModeController::ANIMATE_REVEAL_NO)); 1199 1200 toolbar_->app_menu()->Activate(); 1201 } 1202 1203 bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, 1204 bool* is_keyboard_shortcut) { 1205 *is_keyboard_shortcut = false; 1206 1207 if ((event.type != WebKit::WebInputEvent::RawKeyDown) && 1208 (event.type != WebKit::WebInputEvent::KeyUp)) { 1209 return false; 1210 } 1211 1212 #if defined(OS_WIN) && !defined(USE_AURA) 1213 // As Alt+F4 is the close-app keyboard shortcut, it needs processing 1214 // immediately. 1215 if (event.windowsKeyCode == ui::VKEY_F4 && 1216 event.type == WebKit::WebInputEvent::RawKeyDown && 1217 event.modifiers == NativeWebKeyboardEvent::AltKey) { 1218 DefWindowProc(event.os_event.hwnd, event.os_event.message, 1219 event.os_event.wParam, event.os_event.lParam); 1220 return true; 1221 } 1222 #endif 1223 1224 views::FocusManager* focus_manager = GetFocusManager(); 1225 DCHECK(focus_manager); 1226 1227 if (focus_manager->shortcut_handling_suspended()) 1228 return false; 1229 1230 ui::Accelerator accelerator( 1231 static_cast<ui::KeyboardCode>(event.windowsKeyCode), 1232 content::GetModifiersFromNativeWebKeyboardEvent(event)); 1233 if (event.type == WebKit::WebInputEvent::KeyUp) 1234 accelerator.set_type(ui::ET_KEY_RELEASED); 1235 1236 // What we have to do here is as follows: 1237 // - If the |browser_| is for an app, do nothing. 1238 // - If the |browser_| is not for an app, and the |accelerator| is not 1239 // associated with the browser (e.g. an Ash shortcut), process it. 1240 // - If the |browser_| is not for an app, and the |accelerator| is associated 1241 // with the browser, and it is a reserved one (e.g. Ctrl+w), process it. 1242 // - If the |browser_| is not for an app, and the |accelerator| is associated 1243 // with the browser, and it is not a reserved one, do nothing. 1244 1245 if (browser_->is_app()) { 1246 // We don't have to flip |is_keyboard_shortcut| here. If we do that, the app 1247 // might not be able to see a subsequent Char event. See OnHandleInputEvent 1248 // in content/renderer/render_widget.cc for details. 1249 return false; 1250 } 1251 1252 chrome::BrowserCommandController* controller = browser_->command_controller(); 1253 1254 // Here we need to retrieve the command id (if any) associated to the 1255 // keyboard event. Instead of looking up the command id in the 1256 // |accelerator_table_| by ourselves, we block the command execution of 1257 // the |browser_| object then send the keyboard event to the 1258 // |focus_manager| as if we are activating an accelerator key. 1259 // Then we can retrieve the command id from the |browser_| object. 1260 controller->SetBlockCommandExecution(true); 1261 // If the |accelerator| is a non-browser shortcut (e.g. Ash shortcut), the 1262 // command execution cannot be blocked and true is returned. However, it is 1263 // okay as long as is_app() is false. See comments in this function. 1264 const bool processed = focus_manager->ProcessAccelerator(accelerator); 1265 const int id = controller->GetLastBlockedCommand(NULL); 1266 controller->SetBlockCommandExecution(false); 1267 1268 // Executing the command may cause |this| object to be destroyed. 1269 if (controller->IsReservedCommandOrKey(id, event)) { 1270 UpdateAcceleratorMetrics(accelerator, id); 1271 return chrome::ExecuteCommand(browser_.get(), id); 1272 } 1273 1274 if (id != -1) { 1275 // |accelerator| is a non-reserved browser shortcut (e.g. Ctrl+f). 1276 if (event.type == WebKit::WebInputEvent::RawKeyDown) 1277 *is_keyboard_shortcut = true; 1278 } else if (processed) { 1279 // |accelerator| is a non-browser shortcut (e.g. F4-F10 on Ash). 1280 return true; 1281 } 1282 1283 return false; 1284 } 1285 1286 void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { 1287 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, 1288 GetFocusManager()); 1289 } 1290 1291 // TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always 1292 // enabled in the page menu regardless of whether the command will do 1293 // anything. When someone selects the menu item, we just act as if they hit 1294 // the keyboard shortcut for the command by sending the associated key press 1295 // to windows. The real fix to this bug is to disable the commands when they 1296 // won't do anything. We'll need something like an overall clipboard command 1297 // manager to do that. 1298 void BrowserView::Cut() { 1299 // If a WebContent is focused, call RenderWidgetHost::Cut. Otherwise, e.g. if 1300 // Omnibox is focused, send a Ctrl+x key event to Chrome. Using RWH interface 1301 // rather than the fake key event for a WebContent is important since the fake 1302 // event might be consumed by the web content (crbug.com/137908). 1303 DoCutCopyPaste(&content::RenderWidgetHost::Cut, 1304 #if defined(OS_WIN) 1305 WM_CUT, 1306 #endif 1307 IDS_APP_CUT); 1308 } 1309 1310 void BrowserView::Copy() { 1311 DoCutCopyPaste(&content::RenderWidgetHost::Copy, 1312 #if defined(OS_WIN) 1313 WM_COPY, 1314 #endif 1315 IDS_APP_COPY); 1316 } 1317 1318 void BrowserView::Paste() { 1319 DoCutCopyPaste(&content::RenderWidgetHost::Paste, 1320 #if defined(OS_WIN) 1321 WM_PASTE, 1322 #endif 1323 IDS_APP_PASTE); 1324 } 1325 1326 WindowOpenDisposition BrowserView::GetDispositionForPopupBounds( 1327 const gfx::Rect& bounds) { 1328 #if defined(OS_WIN) 1329 // If we are in Win8's single window Metro mode, we can't allow popup windows. 1330 return win8::IsSingleWindowMetroMode() ? NEW_BACKGROUND_TAB : NEW_POPUP; 1331 #else 1332 return NEW_POPUP; 1333 #endif 1334 } 1335 1336 FindBar* BrowserView::CreateFindBar() { 1337 return chrome::CreateFindBar(this); 1338 } 1339 1340 WebContentsModalDialogHost* BrowserView::GetWebContentsModalDialogHost() { 1341 return GetBrowserViewLayout()->GetWebContentsModalDialogHost(); 1342 } 1343 1344 /////////////////////////////////////////////////////////////////////////////// 1345 // BrowserView, BrowserWindowTesting implementation: 1346 1347 BookmarkBarView* BrowserView::GetBookmarkBarView() const { 1348 return bookmark_bar_view_.get(); 1349 } 1350 1351 LocationBarView* BrowserView::GetLocationBarView() const { 1352 return toolbar_ ? toolbar_->location_bar() : NULL; 1353 } 1354 1355 views::View* BrowserView::GetTabContentsContainerView() const { 1356 return contents_web_view_; 1357 } 1358 1359 ToolbarView* BrowserView::GetToolbarView() const { 1360 return toolbar_; 1361 } 1362 1363 /////////////////////////////////////////////////////////////////////////////// 1364 // BrowserView, TabStripModelObserver implementation: 1365 1366 void BrowserView::TabDetachedAt(WebContents* contents, int index) { 1367 // We use index here rather than comparing |contents| because by this time 1368 // the model has already removed |contents| from its list, so 1369 // browser_->GetActiveWebContents() will return NULL or something else. 1370 if (index == browser_->tab_strip_model()->active_index()) { 1371 // We need to reset the current tab contents to NULL before it gets 1372 // freed. This is because the focus manager performs some operations 1373 // on the selected WebContents when it is removed. 1374 contents_web_view_->SetWebContents(NULL); 1375 infobar_container_->ChangeInfoBarService(NULL); 1376 UpdateDevToolsForContents(NULL); 1377 } 1378 } 1379 1380 void BrowserView::TabDeactivated(WebContents* contents) { 1381 // We do not store the focus when closing the tab to work-around bug 4633. 1382 // Some reports seem to show that the focus manager and/or focused view can 1383 // be garbage at that point, it is not clear why. 1384 if (!contents->IsBeingDestroyed()) 1385 contents->GetView()->StoreFocus(); 1386 } 1387 1388 void BrowserView::ActiveTabChanged(content::WebContents* old_contents, 1389 content::WebContents* new_contents, 1390 int index, 1391 int reason) { 1392 DCHECK(new_contents); 1393 1394 // If |contents_container_| already has the correct WebContents, we can save 1395 // some work. This also prevents extra events from being reported by the 1396 // Visibility API under Windows, as ChangeWebContents will briefly hide 1397 // the WebContents window. 1398 bool change_tab_contents = 1399 contents_web_view_->web_contents() != new_contents; 1400 1401 // Update various elements that are interested in knowing the current 1402 // WebContents. 1403 1404 // When we toggle the NTP floating bookmarks bar and/or the info bar, 1405 // we don't want any WebContents to be attached, so that we 1406 // avoid an unnecessary resize and re-layout of a WebContents. 1407 if (change_tab_contents) 1408 contents_web_view_->SetWebContents(NULL); 1409 infobar_container_->ChangeInfoBarService( 1410 InfoBarService::FromWebContents(new_contents)); 1411 if (bookmark_bar_view_.get()) { 1412 bookmark_bar_view_->SetBookmarkBarState( 1413 browser_->bookmark_bar_state(), 1414 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 1415 } 1416 UpdateUIForContents(new_contents); 1417 1418 // Layout for DevTools _before_ setting the main WebContents to avoid 1419 // toggling the size of the main WebContents. 1420 UpdateDevToolsForContents(new_contents); 1421 1422 if (change_tab_contents) 1423 contents_web_view_->SetWebContents(new_contents); 1424 1425 if (!browser_->tab_strip_model()->closing_all() && GetWidget()->IsActive() && 1426 GetWidget()->IsVisible()) { 1427 // We only restore focus if our window is visible, to avoid invoking blur 1428 // handlers when we are eventually shown. 1429 new_contents->GetView()->RestoreFocus(); 1430 } 1431 1432 // Update all the UI bits. 1433 UpdateTitleBar(); 1434 1435 // No need to update Toolbar because it's already updated in 1436 // browser.cc. 1437 } 1438 1439 void BrowserView::TabStripEmpty() { 1440 // Make sure all optional UI is removed before we are destroyed, otherwise 1441 // there will be consequences (since our view hierarchy will still have 1442 // references to freed views). 1443 UpdateUIForContents(NULL); 1444 } 1445 1446 /////////////////////////////////////////////////////////////////////////////// 1447 // BrowserView, ui::AcceleratorProvider implementation: 1448 1449 bool BrowserView::GetAcceleratorForCommandId(int command_id, 1450 ui::Accelerator* accelerator) { 1451 // Let's let the ToolbarView own the canonical implementation of this method. 1452 return toolbar_->GetAcceleratorForCommandId(command_id, accelerator); 1453 } 1454 1455 /////////////////////////////////////////////////////////////////////////////// 1456 // BrowserView, views::WidgetDelegate implementation: 1457 1458 bool BrowserView::CanResize() const { 1459 return true; 1460 } 1461 1462 bool BrowserView::CanMaximize() const { 1463 return true; 1464 } 1465 1466 bool BrowserView::CanActivate() const { 1467 if (!AppModalDialogQueue::GetInstance()->active_dialog()) 1468 return true; 1469 1470 #if defined(USE_AURA) && defined(OS_CHROMEOS) 1471 // On Aura window manager controls all windows so settings focus via PostTask 1472 // will make only worse because posted task will keep trying to steal focus. 1473 AppModalDialogQueue::GetInstance()->ActivateModalDialog(); 1474 #else 1475 // If another browser is app modal, flash and activate the modal browser. This 1476 // has to be done in a post task, otherwise if the user clicked on a window 1477 // that doesn't have the modal dialog the windows keep trying to get the focus 1478 // from each other on Windows. http://crbug.com/141650. 1479 base::MessageLoop::current()->PostTask( 1480 FROM_HERE, 1481 base::Bind(&BrowserView::ActivateAppModalDialog, 1482 activate_modal_dialog_factory_.GetWeakPtr())); 1483 #endif 1484 return false; 1485 } 1486 1487 string16 BrowserView::GetWindowTitle() const { 1488 return browser_->GetWindowTitleForCurrentTab(); 1489 } 1490 1491 string16 BrowserView::GetAccessibleWindowTitle() const { 1492 if (IsOffTheRecord()) { 1493 return l10n_util::GetStringFUTF16( 1494 IDS_ACCESSIBLE_INCOGNITO_WINDOW_TITLE_FORMAT, 1495 GetWindowTitle()); 1496 } 1497 return GetWindowTitle(); 1498 } 1499 1500 views::View* BrowserView::GetInitiallyFocusedView() { 1501 return NULL; 1502 } 1503 1504 bool BrowserView::ShouldShowWindowTitle() const { 1505 // For Ash only, app host windows do not show an icon, crbug.com/119411. 1506 // Child windows (i.e. popups) do show an icon. 1507 if (browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH && 1508 browser_->is_app() && browser_->app_type() == Browser::APP_TYPE_HOST) 1509 return false; 1510 1511 return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR); 1512 } 1513 1514 gfx::ImageSkia BrowserView::GetWindowAppIcon() { 1515 if (browser_->is_app()) { 1516 WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents(); 1517 extensions::TabHelper* extensions_tab_helper = 1518 contents ? extensions::TabHelper::FromWebContents(contents) : NULL; 1519 if (extensions_tab_helper && extensions_tab_helper->GetExtensionAppIcon()) 1520 return gfx::ImageSkia::CreateFrom1xBitmap( 1521 *extensions_tab_helper->GetExtensionAppIcon()); 1522 } 1523 1524 return GetWindowIcon(); 1525 } 1526 1527 gfx::ImageSkia BrowserView::GetWindowIcon() { 1528 if (browser_->is_app()) 1529 return browser_->GetCurrentPageIcon().AsImageSkia(); 1530 return gfx::ImageSkia(); 1531 } 1532 1533 bool BrowserView::ShouldShowWindowIcon() const { 1534 #if defined(USE_ASH) 1535 // For Ash only, app host windows do not show an icon, crbug.com/119411. 1536 // Child windows (i.e. popups) do show an icon. 1537 if (browser_->is_app() && browser_->app_type() == Browser::APP_TYPE_HOST) 1538 return false; 1539 #endif 1540 return browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR); 1541 } 1542 1543 bool BrowserView::ExecuteWindowsCommand(int command_id) { 1544 // This function handles WM_SYSCOMMAND, WM_APPCOMMAND, and WM_COMMAND. 1545 #if defined(OS_WIN) 1546 if (command_id == IDC_DEBUG_FRAME_TOGGLE) 1547 GetWidget()->DebugToggleFrameType(); 1548 1549 // In Windows 8 metro mode prevent sizing and moving. 1550 if (win8::IsSingleWindowMetroMode()) { 1551 // Windows uses the 4 lower order bits of |notification_code| for type- 1552 // specific information so we must exclude this when comparing. 1553 static const int sc_mask = 0xFFF0; 1554 if (((command_id & sc_mask) == SC_MOVE) || 1555 ((command_id & sc_mask) == SC_SIZE) || 1556 ((command_id & sc_mask) == SC_MAXIMIZE)) 1557 return true; 1558 } 1559 #endif 1560 // Translate WM_APPCOMMAND command ids into a command id that the browser 1561 // knows how to handle. 1562 int command_id_from_app_command = GetCommandIDForAppCommandID(command_id); 1563 if (command_id_from_app_command != -1) 1564 command_id = command_id_from_app_command; 1565 1566 return chrome::ExecuteCommand(browser_.get(), command_id); 1567 } 1568 1569 std::string BrowserView::GetWindowName() const { 1570 return chrome::GetWindowPlacementKey(browser_.get()); 1571 } 1572 1573 void BrowserView::SaveWindowPlacement(const gfx::Rect& bounds, 1574 ui::WindowShowState show_state) { 1575 // If IsFullscreen() is true, we've just changed into fullscreen mode, and 1576 // we're catching the going-into-fullscreen sizing and positioning calls, 1577 // which we want to ignore. 1578 if (!ShouldSaveOrRestoreWindowPos()) 1579 return; 1580 1581 if (!IsFullscreen() && chrome::ShouldSaveWindowPlacement(browser_.get())) { 1582 WidgetDelegate::SaveWindowPlacement(bounds, show_state); 1583 chrome::SaveWindowPlacement(browser_.get(), bounds, show_state); 1584 } 1585 } 1586 1587 bool BrowserView::GetSavedWindowPlacement( 1588 gfx::Rect* bounds, 1589 ui::WindowShowState* show_state) const { 1590 if (!ShouldSaveOrRestoreWindowPos()) 1591 return false; 1592 chrome::GetSavedWindowBoundsAndShowState(browser_.get(), bounds, show_state); 1593 1594 #if defined(USE_ASH) 1595 if (chrome::IsNativeWindowInAsh( 1596 const_cast<BrowserView*>(this)->GetNativeWindow())) { 1597 if (browser_->is_type_popup()) { 1598 // In case of a popup with an 'unspecified' location we are 1599 // looking for a good screen location. We are interpreting (0,0) as an 1600 // unspecified location. 1601 if (bounds->x() == 0 && bounds->y() == 0) { 1602 *bounds = ChromeShellDelegate::instance()->window_positioner()-> 1603 GetPopupPosition(*bounds); 1604 } 1605 } 1606 } 1607 #endif 1608 1609 if (browser_->is_type_popup() && 1610 !browser_->is_app() && 1611 !browser_->is_devtools()) { 1612 // This is non-app popup window. The value passed in |bounds| represents 1613 // two pieces of information: 1614 // - the position of the window, in screen coordinates (outer position). 1615 // - the size of the content area (inner size). 1616 // We need to use these values to determine the appropriate size and 1617 // position of the resulting window. 1618 if (IsToolbarVisible()) { 1619 // If we're showing the toolbar, we need to adjust |*bounds| to include 1620 // its desired height, since the toolbar is considered part of the 1621 // window's client area as far as GetWindowBoundsForClientBounds is 1622 // concerned... 1623 bounds->set_height( 1624 bounds->height() + toolbar_->GetPreferredSize().height()); 1625 } 1626 1627 gfx::Rect window_rect = frame_->non_client_view()-> 1628 GetWindowBoundsForClientBounds(*bounds); 1629 window_rect.set_origin(bounds->origin()); 1630 1631 // When we are given x/y coordinates of 0 on a created popup window, 1632 // assume none were given by the window.open() command. 1633 if (window_rect.x() == 0 && window_rect.y() == 0) { 1634 gfx::Size size = window_rect.size(); 1635 window_rect.set_origin( 1636 WindowSizer::GetDefaultPopupOrigin(size, 1637 browser_->host_desktop_type())); 1638 } 1639 1640 *bounds = window_rect; 1641 *show_state = ui::SHOW_STATE_NORMAL; 1642 } 1643 1644 // We return true because we can _always_ locate reasonable bounds using the 1645 // WindowSizer, and we don't want to trigger the Window's built-in "size to 1646 // default" handling because the browser window has no default preferred 1647 // size. 1648 return true; 1649 } 1650 1651 views::View* BrowserView::GetContentsView() { 1652 return contents_web_view_; 1653 } 1654 1655 views::ClientView* BrowserView::CreateClientView(views::Widget* widget) { 1656 return this; 1657 } 1658 1659 void BrowserView::OnWidgetActivationChanged(views::Widget* widget, 1660 bool active) { 1661 #if defined(USE_ASH) 1662 if (launcher_item_controller_.get()) 1663 launcher_item_controller_->BrowserActivationStateChanged(); 1664 #endif 1665 1666 if (active) 1667 BrowserList::SetLastActive(browser_.get()); 1668 } 1669 1670 void BrowserView::OnWindowBeginUserBoundsChange() { 1671 WebContents* web_contents = GetActiveWebContents(); 1672 if (!web_contents) 1673 return; 1674 web_contents->GetRenderViewHost()->NotifyMoveOrResizeStarted(); 1675 } 1676 1677 void BrowserView::OnWidgetMove() { 1678 if (!initialized_) { 1679 // Creating the widget can trigger a move. Ignore it until we've initialized 1680 // things. 1681 return; 1682 } 1683 1684 // Cancel any tabstrip animations, some of them may be invalidated by the 1685 // window being repositioned. 1686 // Comment out for one cycle to see if this fixes dist tests. 1687 // tabstrip_->DestroyDragController(); 1688 1689 // status_bubble_ may be NULL if this is invoked during construction. 1690 if (status_bubble_.get()) 1691 status_bubble_->Reposition(); 1692 1693 chrome::HideBookmarkBubbleView(); 1694 1695 // Close the omnibox popup, if any. 1696 LocationBarView* location_bar_view = GetLocationBarView(); 1697 if (location_bar_view) 1698 location_bar_view->GetLocationEntry()->CloseOmniboxPopup(); 1699 } 1700 1701 views::Widget* BrowserView::GetWidget() { 1702 return View::GetWidget(); 1703 } 1704 1705 const views::Widget* BrowserView::GetWidget() const { 1706 return View::GetWidget(); 1707 } 1708 1709 void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) { 1710 // This should be in the order of pane traversal of the panes using F6 1711 // (Windows) or Ctrl+Back/Forward (Chrome OS). If one of these is 1712 // invisible or has no focusable children, it will be automatically 1713 // skipped. 1714 panes->push_back(toolbar_); 1715 if (bookmark_bar_view_.get()) 1716 panes->push_back(bookmark_bar_view_.get()); 1717 if (infobar_container_) 1718 panes->push_back(infobar_container_); 1719 if (download_shelf_.get()) 1720 panes->push_back(download_shelf_.get()); 1721 panes->push_back(GetTabContentsContainerView()); 1722 if (devtools_container_->visible()) 1723 panes->push_back(devtools_container_); 1724 } 1725 1726 /////////////////////////////////////////////////////////////////////////////// 1727 // BrowserView, views::ClientView overrides: 1728 1729 bool BrowserView::CanClose() { 1730 // You cannot close a frame for which there is an active originating drag 1731 // session. 1732 if (tabstrip_ && !tabstrip_->IsTabStripCloseable()) 1733 return false; 1734 1735 // Give beforeunload handlers the chance to cancel the close before we hide 1736 // the window below. 1737 if (!browser_->ShouldCloseWindow()) 1738 return false; 1739 1740 bool fast_tab_closing_enabled = 1741 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFastUnload); 1742 1743 if (!browser_->tab_strip_model()->empty()) { 1744 // Tab strip isn't empty. Hide the frame (so it appears to have closed 1745 // immediately) and close all the tabs, allowing the renderers to shut 1746 // down. When the tab strip is empty we'll be called back again. 1747 frame_->Hide(); 1748 browser_->OnWindowClosing(); 1749 if (fast_tab_closing_enabled) 1750 browser_->tab_strip_model()->CloseAllTabs(); 1751 return false; 1752 } else if (fast_tab_closing_enabled && 1753 !browser_->HasCompletedUnloadProcessing()) { 1754 // The browser needs to finish running unload handlers. 1755 // Hide the frame (so it appears to have closed immediately), and 1756 // the browser will call us back again when it is ready to close. 1757 frame_->Hide(); 1758 return false; 1759 } 1760 1761 // Empty TabStripModel, it's now safe to allow the Window to be closed. 1762 content::NotificationService::current()->Notify( 1763 chrome::NOTIFICATION_WINDOW_CLOSED, 1764 content::Source<gfx::NativeWindow>(frame_->GetNativeWindow()), 1765 content::NotificationService::NoDetails()); 1766 return true; 1767 } 1768 1769 int BrowserView::NonClientHitTest(const gfx::Point& point) { 1770 return GetBrowserViewLayout()->NonClientHitTest(point); 1771 } 1772 1773 gfx::Size BrowserView::GetMinimumSize() { 1774 return GetBrowserViewLayout()->GetMinimumSize(); 1775 } 1776 1777 /////////////////////////////////////////////////////////////////////////////// 1778 // BrowserView, views::View overrides: 1779 1780 const char* BrowserView::GetClassName() const { 1781 return kViewClassName; 1782 } 1783 1784 void BrowserView::Layout() { 1785 if (ignore_layout_) 1786 return; 1787 1788 views::View::Layout(); 1789 1790 // TODO(jamescook): Why was this in the middle of layout code? 1791 toolbar_->location_bar()->SetLocationEntryFocusable(IsToolbarVisible()); 1792 1793 // The status bubble position requires that all other layout finish first. 1794 LayoutStatusBubble(); 1795 } 1796 1797 void BrowserView::PaintChildren(gfx::Canvas* canvas) { 1798 // Paint the |infobar_container_| last so that it may paint its 1799 // overlapping tabs. 1800 for (int i = 0; i < child_count(); ++i) { 1801 View* child = child_at(i); 1802 if (child != infobar_container_ && !child->layer()) 1803 child->Paint(canvas); 1804 } 1805 1806 infobar_container_->Paint(canvas); 1807 } 1808 1809 void BrowserView::ViewHierarchyChanged( 1810 const ViewHierarchyChangedDetails& details) { 1811 if (!initialized_ && details.is_add && details.child == this && GetWidget()) { 1812 InitViews(); 1813 initialized_ = true; 1814 } 1815 } 1816 1817 void BrowserView::ChildPreferredSizeChanged(View* child) { 1818 Layout(); 1819 } 1820 1821 void BrowserView::GetAccessibleState(ui::AccessibleViewState* state) { 1822 state->role = ui::AccessibilityTypes::ROLE_CLIENT; 1823 } 1824 1825 /////////////////////////////////////////////////////////////////////////////// 1826 // BrowserView, ui::AcceleratorTarget overrides: 1827 1828 bool BrowserView::AcceleratorPressed(const ui::Accelerator& accelerator) { 1829 #if defined(OS_CHROMEOS) 1830 // If accessibility is enabled, stop speech and return false so that key 1831 // combinations involving Search can be used for extra accessibility 1832 // functionality. 1833 if (accelerator.key_code() == ui::VKEY_LWIN && 1834 g_browser_process->local_state()->GetBoolean( 1835 prefs::kSpokenFeedbackEnabled)) { 1836 TtsController::GetInstance()->Stop(); 1837 return false; 1838 } 1839 #endif 1840 1841 std::map<ui::Accelerator, int>::const_iterator iter = 1842 accelerator_table_.find(accelerator); 1843 DCHECK(iter != accelerator_table_.end()); 1844 int command_id = iter->second; 1845 1846 chrome::BrowserCommandController* controller = browser_->command_controller(); 1847 if (!controller->block_command_execution()) 1848 UpdateAcceleratorMetrics(accelerator, command_id); 1849 return chrome::ExecuteCommand(browser_.get(), command_id); 1850 } 1851 1852 /////////////////////////////////////////////////////////////////////////////// 1853 // BrowserView, OmniboxPopupModelObserver overrides: 1854 void BrowserView::OnOmniboxPopupShownOrHidden() { 1855 infobar_container_->SetMaxTopArrowHeight(GetMaxTopInfoBarArrowHeight()); 1856 } 1857 1858 /////////////////////////////////////////////////////////////////////////////// 1859 // BrowserView, ImmersiveModeController::Delegate overrides: 1860 1861 BookmarkBarView* BrowserView::GetBookmarkBar() { 1862 return bookmark_bar_view_.get(); 1863 } 1864 1865 FullscreenController* BrowserView::GetFullscreenController() { 1866 // Cannot be injected into ImmersiveModeController because it is constructed 1867 // after BrowserView. 1868 return browser()->fullscreen_controller(); 1869 } 1870 1871 void BrowserView::FullscreenStateChanged() { 1872 if (IsFullscreen()) { 1873 ProcessFullscreen(true, FOR_DESKTOP, GURL(), 1874 FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION); 1875 } else { 1876 ProcessFullscreen(false, FOR_DESKTOP, GURL(), FEB_TYPE_NONE); 1877 } 1878 } 1879 1880 void BrowserView::SetImmersiveStyle(bool immersive) { 1881 // Only the tab strip changes its painting style for immersive fullscreen. 1882 if (tabstrip_) 1883 tabstrip_->SetImmersiveStyle(immersive); 1884 } 1885 1886 WebContents* BrowserView::GetWebContents() { 1887 return GetActiveWebContents(); 1888 } 1889 1890 /////////////////////////////////////////////////////////////////////////////// 1891 // BrowserView, InfoBarContainer::Delegate overrides: 1892 1893 SkColor BrowserView::GetInfoBarSeparatorColor() const { 1894 // NOTE: Keep this in sync with ToolbarView::OnPaint()! 1895 return (IsTabStripVisible() || !frame_->ShouldUseNativeFrame()) ? 1896 ThemeProperties::GetDefaultColor( 1897 ThemeProperties::COLOR_TOOLBAR_SEPARATOR) : 1898 SK_ColorBLACK; 1899 } 1900 1901 void BrowserView::InfoBarContainerStateChanged(bool is_animating) { 1902 ToolbarSizeChanged(is_animating); 1903 } 1904 1905 bool BrowserView::DrawInfoBarArrows(int* x) const { 1906 if (x) { 1907 const LocationIconView* location_icon_view = 1908 toolbar_->location_bar()->location_icon_view(); 1909 gfx::Point icon_center(location_icon_view->GetImageBounds().CenterPoint()); 1910 ConvertPointToTarget(location_icon_view, this, &icon_center); 1911 *x = icon_center.x(); 1912 } 1913 return true; 1914 } 1915 1916 bool BrowserView::SplitHandleMoved(views::SingleSplitView* sender) { 1917 for (int i = 0; i < sender->child_count(); ++i) 1918 sender->child_at(i)->InvalidateLayout(); 1919 SchedulePaint(); 1920 Layout(); 1921 return false; 1922 } 1923 1924 void BrowserView::OnSysColorChange() { 1925 chrome::MaybeShowInvertBubbleView(browser_.get(), contents_container_); 1926 } 1927 1928 void BrowserView::InitViews() { 1929 GetWidget()->AddObserver(this); 1930 1931 // Stow a pointer to this object onto the window handle so that we can get at 1932 // it later when all we have is a native view. 1933 GetWidget()->SetNativeWindowProperty(kBrowserViewKey, this); 1934 1935 // Stow a pointer to the browser's profile onto the window handle so that we 1936 // can get it later when all we have is a native view. 1937 GetWidget()->SetNativeWindowProperty(Profile::kProfileKey, 1938 browser_->profile()); 1939 1940 // Start a hung plugin window detector for this browser object (as long as 1941 // hang detection is not disabled). 1942 if (!CommandLine::ForCurrentProcess()->HasSwitch( 1943 switches::kDisableHangMonitor)) { 1944 InitHangMonitor(); 1945 } 1946 1947 LoadAccelerators(); 1948 1949 infobar_container_ = new InfoBarContainerView(this); 1950 AddChildView(infobar_container_); 1951 1952 contents_web_view_ = new views::WebView(browser_->profile()); 1953 contents_web_view_->set_id(VIEW_ID_TAB_CONTAINER); 1954 contents_container_ = new ContentsContainer(contents_web_view_); 1955 1956 SkColor bg_color = GetWidget()->GetThemeProvider()-> 1957 GetColor(ThemeProperties::COLOR_TOOLBAR); 1958 1959 devtools_container_ = new views::WebView(browser_->profile()); 1960 devtools_container_->set_id(VIEW_ID_DEV_TOOLS_DOCKED); 1961 devtools_container_->SetVisible(false); 1962 1963 views::View* contents_container_view = contents_container_; 1964 1965 contents_split_ = new views::SingleSplitView( 1966 contents_container_view, 1967 devtools_container_, 1968 views::SingleSplitView::VERTICAL_SPLIT, 1969 this); 1970 contents_split_->set_id(VIEW_ID_CONTENTS_SPLIT); 1971 contents_split_->SetAccessibleName( 1972 l10n_util::GetStringUTF16(IDS_ACCNAME_WEB_CONTENTS)); 1973 contents_split_->set_background( 1974 views::Background::CreateSolidBackground(bg_color)); 1975 AddChildView(contents_split_); 1976 set_contents_view(contents_split_); 1977 1978 InitStatusBubble(); 1979 1980 // Top container holds tab strip and toolbar and lives at the front of the 1981 // view hierarchy. 1982 top_container_ = new TopContainerView(this); 1983 AddChildView(top_container_); 1984 1985 // TabStrip takes ownership of the controller. 1986 BrowserTabStripController* tabstrip_controller = 1987 new BrowserTabStripController(browser_.get(), 1988 browser_->tab_strip_model()); 1989 tabstrip_ = new TabStrip(tabstrip_controller); 1990 top_container_->AddChildView(tabstrip_); 1991 tabstrip_controller->InitFromModel(tabstrip_); 1992 1993 toolbar_ = new ToolbarView(browser_.get()); 1994 top_container_->AddChildView(toolbar_); 1995 toolbar_->Init(); 1996 1997 // Create do-nothing view for the sake of controlling the z-order of the find 1998 // bar widget. 1999 find_bar_host_view_ = new View(); 2000 AddChildView(find_bar_host_view_); 2001 2002 if (window_switcher_button_) 2003 AddChildView(window_switcher_button_); 2004 2005 immersive_mode_controller_->Init(this, GetWidget(), top_container_); 2006 2007 BrowserViewLayout* browser_view_layout = new BrowserViewLayout; 2008 browser_view_layout->Init(new BrowserViewLayoutDelegateImpl(this), 2009 browser(), 2010 this, 2011 top_container_, 2012 tabstrip_, 2013 toolbar_, 2014 infobar_container_, 2015 contents_split_, 2016 contents_container_, 2017 immersive_mode_controller_.get()); 2018 SetLayoutManager(browser_view_layout); 2019 2020 #if defined(OS_WIN) && !defined(USE_AURA) 2021 // Create a custom JumpList and add it to an observer of TabRestoreService 2022 // so we can update the custom JumpList when a tab is added or removed. 2023 if (JumpList::Enabled()) { 2024 load_complete_listener_.reset(new LoadCompleteListener(this)); 2025 } 2026 #endif 2027 2028 GetLocationBar()->GetLocationEntry()->model()->popup_model()->AddObserver( 2029 this); 2030 2031 // We're now initialized and ready to process Layout requests. 2032 ignore_layout_ = false; 2033 } 2034 2035 void BrowserView::LoadingAnimationCallback() { 2036 base::TimeTicks now = base::TimeTicks::Now(); 2037 if (!last_animation_time_.is_null()) { 2038 UMA_HISTOGRAM_TIMES( 2039 "Tabs.LoadingAnimationTime", 2040 now - last_animation_time_); 2041 } 2042 last_animation_time_ = now; 2043 if (browser_->is_type_tabbed()) { 2044 // Loading animations are shown in the tab for tabbed windows. We check the 2045 // browser type instead of calling IsTabStripVisible() because the latter 2046 // will return false for fullscreen windows, but we still need to update 2047 // their animations (so that when they come out of fullscreen mode they'll 2048 // be correct). 2049 tabstrip_->UpdateLoadingAnimations(); 2050 } else if (ShouldShowWindowIcon()) { 2051 // ... or in the window icon area for popups and app windows. 2052 WebContents* web_contents = 2053 browser_->tab_strip_model()->GetActiveWebContents(); 2054 // GetActiveWebContents can return NULL for example under Purify when 2055 // the animations are running slowly and this function is called on a timer 2056 // through LoadingAnimationCallback. 2057 frame_->UpdateThrobber(web_contents && web_contents->IsLoading()); 2058 } 2059 } 2060 2061 void BrowserView::OnLoadCompleted() { 2062 #if defined(OS_WIN) && !defined(USE_AURA) 2063 DCHECK(!jumplist_); 2064 jumplist_ = new JumpList(); 2065 jumplist_->AddObserver(browser_->profile()); 2066 #endif 2067 } 2068 2069 BrowserViewLayout* BrowserView::GetBrowserViewLayout() const { 2070 return static_cast<BrowserViewLayout*>(GetLayoutManager()); 2071 } 2072 2073 void BrowserView::LayoutStatusBubble() { 2074 // In restored mode, the client area has a client edge between it and the 2075 // frame. 2076 int overlap = StatusBubbleViews::kShadowThickness; 2077 // The extra pixels defined by kClientEdgeThickness is only drawn in frame 2078 // content border on windows for non-aura build. 2079 #if !defined(USE_ASH) 2080 overlap += 2081 IsMaximized() ? 0 : views::NonClientFrameView::kClientEdgeThickness; 2082 #endif 2083 int height = status_bubble_->GetPreferredSize().height(); 2084 int contents_height = status_bubble_->base_view()->bounds().height(); 2085 gfx::Point origin(-overlap, contents_height - height + overlap); 2086 status_bubble_->SetBounds(origin.x(), origin.y(), width() / 3, height); 2087 } 2088 2089 bool BrowserView::MaybeShowBookmarkBar(WebContents* contents) { 2090 bool show_bookmark_bar = contents && 2091 browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR); 2092 if (!show_bookmark_bar && !bookmark_bar_view_.get()) 2093 return false; 2094 if (!bookmark_bar_view_.get()) { 2095 bookmark_bar_view_.reset(new BookmarkBarView(browser_.get(), this)); 2096 bookmark_bar_view_->set_owned_by_client(); 2097 bookmark_bar_view_->set_background( 2098 new BookmarkExtensionBackground(this, 2099 bookmark_bar_view_.get(), 2100 browser_.get())); 2101 bookmark_bar_view_->SetBookmarkBarState( 2102 browser_->bookmark_bar_state(), 2103 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 2104 GetBrowserViewLayout()->set_bookmark_bar(bookmark_bar_view_.get()); 2105 } 2106 bookmark_bar_view_->SetVisible(show_bookmark_bar); 2107 bookmark_bar_view_->SetPageNavigator(contents); 2108 2109 // Update parenting for the bookmark bar. This may detach it from all views. 2110 bool needs_layout = false; 2111 views::View* new_parent = NULL; 2112 if (show_bookmark_bar) { 2113 if (bookmark_bar_view_->IsDetached()) 2114 new_parent = this; 2115 else 2116 new_parent = top_container_; 2117 } 2118 if (new_parent != bookmark_bar_view_->parent()) { 2119 SetBookmarkBarParent(new_parent); 2120 needs_layout = true; 2121 } 2122 2123 // Check for updates to the desired size. 2124 if (bookmark_bar_view_->GetPreferredSize().height() != 2125 bookmark_bar_view_->height()) 2126 needs_layout = true; 2127 2128 return needs_layout; 2129 } 2130 2131 void BrowserView::SetBookmarkBarParent(views::View* new_parent) { 2132 if (new_parent == this) { 2133 // Add it underneath |top_container_| or at the end if top container isn't 2134 // found. 2135 int top_container_index = GetIndexOf(top_container_); 2136 if (top_container_index >= 0) 2137 AddChildViewAt(bookmark_bar_view_.get(), top_container_index); 2138 else 2139 AddChildView(bookmark_bar_view_.get()); 2140 } else if (new_parent) { 2141 // No special stacking is required for other parents. 2142 new_parent->AddChildView(bookmark_bar_view_.get()); 2143 } else { 2144 // Bookmark bar is being detached from all views because it is hidden. 2145 bookmark_bar_view_->parent()->RemoveChildView(bookmark_bar_view_.get()); 2146 } 2147 } 2148 2149 bool BrowserView::MaybeShowInfoBar(WebContents* contents) { 2150 // TODO(beng): Remove this function once the interface between 2151 // InfoBarContainer, DownloadShelfView and WebContents and this 2152 // view is sorted out. 2153 return true; 2154 } 2155 2156 void BrowserView::UpdateDevToolsForContents(WebContents* web_contents) { 2157 DevToolsWindow* new_devtools_window = web_contents ? 2158 DevToolsWindow::GetDockedInstanceForInspectedTab(web_contents) : NULL; 2159 // Fast return in case of the same window having same orientation. 2160 if (devtools_window_ == new_devtools_window) { 2161 if (!new_devtools_window || 2162 (new_devtools_window->dock_side() == devtools_dock_side_)) { 2163 return; 2164 } 2165 } 2166 2167 // Replace tab contents. 2168 if (devtools_window_ != new_devtools_window) { 2169 devtools_container_->SetWebContents( 2170 new_devtools_window ? new_devtools_window->web_contents() : NULL); 2171 } 2172 2173 // Store last used position. 2174 if (devtools_window_) { 2175 int split_size = contents_split_->GetDividerSize(); 2176 if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT) { 2177 devtools_window_->SetWidth(contents_split_->width() - 2178 split_size - contents_split_->divider_offset()); 2179 } else if (devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_BOTTOM) { 2180 devtools_window_->SetHeight(contents_split_->height() - 2181 split_size - contents_split_->divider_offset()); 2182 } 2183 } 2184 2185 // Show / hide container if necessary. Changing dock orientation is 2186 // hide + show. 2187 bool should_hide = devtools_window_ && (!new_devtools_window || 2188 devtools_dock_side_ != new_devtools_window->dock_side()); 2189 bool should_show = new_devtools_window && (!devtools_window_ || should_hide); 2190 2191 if (should_hide) 2192 HideDevToolsContainer(); 2193 2194 devtools_window_ = new_devtools_window; 2195 2196 if (should_show) { 2197 devtools_dock_side_ = new_devtools_window->dock_side(); 2198 ShowDevToolsContainer(); 2199 } else if (new_devtools_window) { 2200 UpdateDevToolsSplitPosition(); 2201 contents_split_->Layout(); 2202 } 2203 } 2204 2205 void BrowserView::ShowDevToolsContainer() { 2206 if (!devtools_focus_tracker_.get()) { 2207 // Install devtools focus tracker when dev tools window is shown for the 2208 // first time. 2209 devtools_focus_tracker_.reset( 2210 new views::ExternalFocusTracker(devtools_container_, 2211 GetFocusManager())); 2212 } 2213 2214 gfx::Size min_devtools_size(devtools_window_->GetMinimumWidth(), 2215 devtools_window_->GetMinimumHeight()); 2216 devtools_container_->SetPreferredSize(min_devtools_size); 2217 2218 devtools_container_->SetVisible(true); 2219 devtools_dock_side_ = devtools_window_->dock_side(); 2220 bool dock_to_right = devtools_dock_side_ == DEVTOOLS_DOCK_SIDE_RIGHT; 2221 contents_split_->set_orientation( 2222 dock_to_right ? views::SingleSplitView::HORIZONTAL_SPLIT 2223 : views::SingleSplitView::VERTICAL_SPLIT); 2224 UpdateDevToolsSplitPosition(); 2225 contents_split_->InvalidateLayout(); 2226 Layout(); 2227 } 2228 2229 void BrowserView::HideDevToolsContainer() { 2230 // Restore focus to the last focused view when hiding devtools window. 2231 devtools_focus_tracker_->FocusLastFocusedExternalView(); 2232 devtools_container_->SetVisible(false); 2233 contents_split_->InvalidateLayout(); 2234 Layout(); 2235 } 2236 2237 void BrowserView::UpdateDevToolsSplitPosition() { 2238 contents_split_->set_resize_disabled( 2239 devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED); 2240 int split_size = contents_split_->GetDividerSize(); 2241 if (devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_RIGHT) { 2242 int split_offset = contents_split_->width() - split_size - 2243 devtools_window_->GetWidth(contents_split_->width()); 2244 contents_split_->set_divider_offset(split_offset); 2245 } else { 2246 int height = devtools_window_->dock_side() == DEVTOOLS_DOCK_SIDE_MINIMIZED ? 2247 devtools_window_->GetMinimizedHeight() : 2248 devtools_window_->GetHeight(contents_split_->height()); 2249 int split_offset = contents_split_->height() - split_size - height; 2250 contents_split_->set_divider_offset(split_offset); 2251 } 2252 } 2253 2254 void BrowserView::UpdateUIForContents(WebContents* contents) { 2255 bool needs_layout = MaybeShowBookmarkBar(contents); 2256 // TODO(jamescook): This function always returns true. Remove it and figure 2257 // out when layout is actually required. 2258 needs_layout |= MaybeShowInfoBar(contents); 2259 if (needs_layout) 2260 Layout(); 2261 } 2262 2263 void BrowserView::ProcessFullscreen(bool fullscreen, 2264 FullscreenType type, 2265 const GURL& url, 2266 FullscreenExitBubbleType bubble_type) { 2267 // Reduce jankiness during the following position changes by: 2268 // * Hiding the window until it's in the final position 2269 // * Ignoring all intervening Layout() calls, which resize the webpage and 2270 // thus are slow and look ugly 2271 ignore_layout_ = true; 2272 LocationBarView* location_bar = GetLocationBarView(); 2273 #if defined(OS_WIN) && !defined(USE_AURA) 2274 OmniboxViewWin* omnibox_win = 2275 GetOmniboxViewWin(location_bar->GetLocationEntry()); 2276 #endif 2277 2278 if (type == FOR_METRO || !fullscreen) { 2279 // Hide the fullscreen bubble as soon as possible, since the mode toggle can 2280 // take enough time for the user to notice. 2281 fullscreen_bubble_.reset(); 2282 } 2283 2284 if (fullscreen) { 2285 // Move focus out of the location bar if necessary. 2286 views::FocusManager* focus_manager = GetFocusManager(); 2287 DCHECK(focus_manager); 2288 // Look for focus in the location bar itself or any child view. 2289 if (location_bar->Contains(focus_manager->GetFocusedView())) 2290 focus_manager->ClearFocus(); 2291 2292 #if defined(OS_WIN) && !defined(USE_AURA) 2293 if (omnibox_win) { 2294 // If we don't hide the edit and force it to not show until we come out of 2295 // fullscreen, then if the user was on the New Tab Page, the edit contents 2296 // will appear atop the web contents once we go into fullscreen mode. This 2297 // has something to do with how we move the main window while it's hidden; 2298 // if we don't hide the main window below, we don't get this problem. 2299 omnibox_win->set_force_hidden(true); 2300 ShowWindow(omnibox_win->m_hWnd, SW_HIDE); 2301 } 2302 #endif 2303 } 2304 #if defined(OS_WIN) && !defined(USE_AURA) 2305 views::ScopedFullscreenVisibility visibility(frame_->GetNativeView()); 2306 #endif 2307 2308 if (type == FOR_METRO) { 2309 #if defined(OS_WIN) && !defined(USE_AURA) 2310 // Enter metro snap mode. 2311 static_cast<views::NativeWidgetWin*>( 2312 frame_->native_widget())->SetMetroSnapFullscreen(fullscreen); 2313 #endif 2314 } else { 2315 // Toggle fullscreen mode. 2316 frame_->SetFullscreen(fullscreen); 2317 } 2318 2319 // Enable immersive before the browser refreshes its list of enabled commands. 2320 if (ShouldUseImmersiveFullscreenForUrl(url)) 2321 immersive_mode_controller_->SetEnabled(fullscreen); 2322 2323 browser_->WindowFullscreenStateChanged(); 2324 2325 if (fullscreen) { 2326 if (!chrome::IsRunningInAppMode() && 2327 type != FOR_METRO && 2328 !ShouldUseImmersiveFullscreenForUrl(url)) { 2329 fullscreen_bubble_.reset(new FullscreenExitBubbleViews( 2330 this, url, bubble_type)); 2331 } 2332 } else { 2333 #if defined(OS_WIN) && !defined(USE_AURA) 2334 if (omnibox_win) { 2335 // Show the edit again since we're no longer in fullscreen mode. 2336 omnibox_win->set_force_hidden(false); 2337 ShowWindow(omnibox_win->m_hWnd, SW_SHOW); 2338 } 2339 #endif 2340 } 2341 2342 // Undo our anti-jankiness hacks and force a re-layout. We also need to 2343 // recompute the height of the infobar top arrow because toggling in and out 2344 // of fullscreen changes it. Calling ToolbarSizeChanged() will do both these 2345 // things since it computes the arrow height directly and forces a layout 2346 // indirectly via UpdateUIForContents(). 2347 ignore_layout_ = false; 2348 ToolbarSizeChanged(false); 2349 } 2350 2351 bool BrowserView::ShouldUseImmersiveFullscreenForUrl(const GURL& url) const { 2352 bool is_browser_fullscreen = url.is_empty(); 2353 return ImmersiveFullscreenConfiguration::UseImmersiveFullscreen() && 2354 is_browser_fullscreen && IsBrowserTypeNormal(); 2355 } 2356 2357 void BrowserView::LoadAccelerators() { 2358 #if defined(OS_WIN) && !defined(USE_AURA) 2359 HACCEL accelerator_table = AtlLoadAccelerators(IDR_MAINFRAME); 2360 DCHECK(accelerator_table); 2361 2362 // We have to copy the table to access its contents. 2363 int count = CopyAcceleratorTable(accelerator_table, 0, 0); 2364 if (count == 0) { 2365 // Nothing to do in that case. 2366 return; 2367 } 2368 2369 ACCEL* accelerators = static_cast<ACCEL*>(malloc(sizeof(ACCEL) * count)); 2370 CopyAcceleratorTable(accelerator_table, accelerators, count); 2371 2372 views::FocusManager* focus_manager = GetFocusManager(); 2373 DCHECK(focus_manager); 2374 2375 // Let's fill our own accelerator table. 2376 for (int i = 0; i < count; ++i) { 2377 ui::Accelerator accelerator( 2378 static_cast<ui::KeyboardCode>(accelerators[i].key), 2379 ui::GetModifiersFromACCEL(accelerators[i])); 2380 accelerator_table_[accelerator] = accelerators[i].cmd; 2381 2382 // Also register with the focus manager. 2383 focus_manager->RegisterAccelerator( 2384 accelerator, ui::AcceleratorManager::kNormalPriority, this); 2385 } 2386 2387 // We don't need the Windows accelerator table anymore. 2388 free(accelerators); 2389 #else 2390 views::FocusManager* focus_manager = GetFocusManager(); 2391 DCHECK(focus_manager); 2392 2393 // Let's fill our own accelerator table. 2394 const bool is_app_mode = chrome::IsRunningInForcedAppMode(); 2395 const std::vector<chrome::AcceleratorMapping> accelerator_list( 2396 chrome::GetAcceleratorList()); 2397 for (std::vector<chrome::AcceleratorMapping>::const_iterator it = 2398 accelerator_list.begin(); it != accelerator_list.end(); ++it) { 2399 // In app mode, only allow accelerators of white listed commands to pass 2400 // through. 2401 if (is_app_mode && !chrome::IsCommandAllowedInAppMode(it->command_id)) 2402 continue; 2403 2404 ui::Accelerator accelerator(it->keycode, it->modifiers); 2405 accelerator_table_[accelerator] = it->command_id; 2406 2407 // Also register with the focus manager. 2408 focus_manager->RegisterAccelerator( 2409 accelerator, ui::AcceleratorManager::kNormalPriority, this); 2410 } 2411 #endif 2412 } 2413 2414 int BrowserView::GetCommandIDForAppCommandID(int app_command_id) const { 2415 #if defined(OS_WIN) 2416 switch (app_command_id) { 2417 // NOTE: The order here matches the APPCOMMAND declaration order in the 2418 // Windows headers. 2419 case APPCOMMAND_BROWSER_BACKWARD: return IDC_BACK; 2420 case APPCOMMAND_BROWSER_FORWARD: return IDC_FORWARD; 2421 case APPCOMMAND_BROWSER_REFRESH: return IDC_RELOAD; 2422 case APPCOMMAND_BROWSER_HOME: return IDC_HOME; 2423 case APPCOMMAND_BROWSER_STOP: return IDC_STOP; 2424 case APPCOMMAND_BROWSER_SEARCH: return IDC_FOCUS_SEARCH; 2425 case APPCOMMAND_HELP: return IDC_HELP_PAGE_VIA_KEYBOARD; 2426 case APPCOMMAND_NEW: return IDC_NEW_TAB; 2427 case APPCOMMAND_OPEN: return IDC_OPEN_FILE; 2428 case APPCOMMAND_CLOSE: return IDC_CLOSE_TAB; 2429 case APPCOMMAND_SAVE: return IDC_SAVE_PAGE; 2430 case APPCOMMAND_PRINT: return IDC_PRINT; 2431 case APPCOMMAND_COPY: return IDC_COPY; 2432 case APPCOMMAND_CUT: return IDC_CUT; 2433 case APPCOMMAND_PASTE: return IDC_PASTE; 2434 2435 // TODO(pkasting): http://b/1113069 Handle these. 2436 case APPCOMMAND_UNDO: 2437 case APPCOMMAND_REDO: 2438 case APPCOMMAND_SPELL_CHECK: 2439 default: return -1; 2440 } 2441 #else 2442 // App commands are Windows-specific so there's nothing to do here. 2443 return -1; 2444 #endif 2445 } 2446 2447 void BrowserView::InitHangMonitor() { 2448 #if defined(OS_WIN) && !defined(USE_AURA) 2449 PrefService* pref_service = g_browser_process->local_state(); 2450 if (!pref_service) 2451 return; 2452 2453 int plugin_message_response_timeout = 2454 pref_service->GetInteger(prefs::kPluginMessageResponseTimeout); 2455 int hung_plugin_detect_freq = 2456 pref_service->GetInteger(prefs::kHungPluginDetectFrequency); 2457 if ((hung_plugin_detect_freq > 0) && 2458 hung_window_detector_.Initialize(GetWidget()->GetNativeView(), 2459 plugin_message_response_timeout)) { 2460 ticker_.set_tick_interval(hung_plugin_detect_freq); 2461 ticker_.RegisterTickHandler(&hung_window_detector_); 2462 ticker_.Start(); 2463 2464 pref_service->SetInteger(prefs::kPluginMessageResponseTimeout, 2465 plugin_message_response_timeout); 2466 pref_service->SetInteger(prefs::kHungPluginDetectFrequency, 2467 hung_plugin_detect_freq); 2468 } 2469 #endif 2470 } 2471 2472 void BrowserView::UpdateAcceleratorMetrics( 2473 const ui::Accelerator& accelerator, int command_id) { 2474 const ui::KeyboardCode key_code = accelerator.key_code(); 2475 if (command_id == IDC_HELP_PAGE_VIA_KEYBOARD && key_code == ui::VKEY_F1) 2476 content::RecordAction(UserMetricsAction("ShowHelpTabViaF1")); 2477 2478 if (command_id == IDC_BOOKMARK_PAGE) 2479 UMA_HISTOGRAM_ENUMERATION("Bookmarks.EntryPoint", 2480 bookmark_utils::ENTRY_POINT_ACCELERATOR, 2481 bookmark_utils::ENTRY_POINT_LIMIT); 2482 2483 #if defined(OS_CHROMEOS) 2484 // Collect information about the relative popularity of various accelerators 2485 // on Chrome OS. 2486 switch (command_id) { 2487 case IDC_BACK: 2488 if (key_code == ui::VKEY_BACK) 2489 content::RecordAction(UserMetricsAction("Accel_Back_Backspace")); 2490 else if (key_code == ui::VKEY_BROWSER_BACK) 2491 content::RecordAction(UserMetricsAction("Accel_Back_F1")); 2492 else if (key_code == ui::VKEY_LEFT) 2493 content::RecordAction(UserMetricsAction("Accel_Back_Left")); 2494 break; 2495 case IDC_FORWARD: 2496 if (key_code == ui::VKEY_BACK) 2497 content::RecordAction(UserMetricsAction("Accel_Forward_Backspace")); 2498 else if (key_code == ui::VKEY_BROWSER_FORWARD) 2499 content::RecordAction(UserMetricsAction("Accel_Forward_F2")); 2500 else if (key_code == ui::VKEY_RIGHT) 2501 content::RecordAction(UserMetricsAction("Accel_Forward_Right")); 2502 break; 2503 case IDC_RELOAD: 2504 case IDC_RELOAD_IGNORING_CACHE: 2505 if (key_code == ui::VKEY_R) 2506 content::RecordAction(UserMetricsAction("Accel_Reload_R")); 2507 else if (key_code == ui::VKEY_BROWSER_REFRESH) 2508 content::RecordAction(UserMetricsAction("Accel_Reload_F3")); 2509 break; 2510 case IDC_FOCUS_LOCATION: 2511 if (key_code == ui::VKEY_D) 2512 content::RecordAction(UserMetricsAction("Accel_FocusLocation_D")); 2513 else if (key_code == ui::VKEY_L) 2514 content::RecordAction(UserMetricsAction("Accel_FocusLocation_L")); 2515 break; 2516 case IDC_FOCUS_SEARCH: 2517 if (key_code == ui::VKEY_E) 2518 content::RecordAction(UserMetricsAction("Accel_FocusSearch_E")); 2519 else if (key_code == ui::VKEY_K) 2520 content::RecordAction(UserMetricsAction("Accel_FocusSearch_K")); 2521 break; 2522 default: 2523 // Do nothing. 2524 break; 2525 } 2526 #endif 2527 } 2528 2529 void BrowserView::CreateLauncherIcon() { 2530 #if defined(USE_ASH) 2531 if (chrome::IsNativeWindowInAsh(GetNativeWindow()) && 2532 !launcher_item_controller_.get()) { 2533 launcher_item_controller_.reset( 2534 BrowserLauncherItemController::Create(browser_.get())); 2535 } 2536 #endif // defined(USE_ASH) 2537 } 2538 2539 // static 2540 BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) { 2541 // Create the view and the frame. The frame will attach itself via the view 2542 // so we don't need to do anything with the pointer. 2543 BrowserView* view = new BrowserView(); 2544 view->Init(browser); 2545 (new BrowserFrame(view))->InitBrowserFrame(); 2546 view->GetWidget()->non_client_view()->SetAccessibleName( 2547 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 2548 return view; 2549 } 2550 2551 void BrowserView::ShowAvatarBubble(WebContents* web_contents, 2552 const gfx::Rect& rect) { 2553 gfx::Point origin(rect.origin()); 2554 views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin); 2555 gfx::Rect bounds(origin, rect.size()); 2556 2557 AvatarMenuBubbleView::ShowBubble(this, views::BubbleBorder::TOP_RIGHT, 2558 views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, bounds, browser_.get()); 2559 } 2560 2561 void BrowserView::ShowAvatarBubbleFromAvatarButton() { 2562 AvatarMenuButton* button = frame_->GetAvatarMenuButton(); 2563 if (button) 2564 button->ShowAvatarBubble(); 2565 } 2566 2567 void BrowserView::ShowPasswordGenerationBubble( 2568 const gfx::Rect& rect, 2569 const content::PasswordForm& form, 2570 autofill::PasswordGenerator* password_generator) { 2571 // Create a rect in the content bounds that the bubble will point to. 2572 gfx::Point origin(rect.origin()); 2573 views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin); 2574 gfx::Rect bounds(origin, rect.size()); 2575 2576 // Create the bubble. 2577 WebContents* web_contents = GetActiveWebContents(); 2578 if (!web_contents) 2579 return; 2580 2581 PasswordGenerationBubbleView* bubble = 2582 new PasswordGenerationBubbleView( 2583 form, 2584 bounds, 2585 this, 2586 web_contents->GetRenderViewHost(), 2587 PasswordManager::FromWebContents(web_contents), 2588 password_generator, 2589 browser_.get(), 2590 GetWidget()->GetThemeProvider()); 2591 2592 views::BubbleDelegateView::CreateBubble(bubble); 2593 bubble->SetAlignment(views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR); 2594 bubble->GetWidget()->Show(); 2595 } 2596 2597 void BrowserView::OverscrollUpdate(int delta_y) { 2598 if (scroll_end_effect_controller_) 2599 scroll_end_effect_controller_->OverscrollUpdate(delta_y); 2600 } 2601 2602 void BrowserView::DoCutCopyPaste(void (content::RenderWidgetHost::*method)(), 2603 #if defined(OS_WIN) 2604 int windows_msg_id, 2605 #endif 2606 int command_id) { 2607 WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents(); 2608 if (!contents) 2609 return; 2610 if (DoCutCopyPasteForWebContents(contents, method)) 2611 return; 2612 2613 DevToolsWindow* devtools_window = 2614 DevToolsWindow::GetDockedInstanceForInspectedTab(contents); 2615 if (devtools_window && 2616 DoCutCopyPasteForWebContents(devtools_window->web_contents(), method)) { 2617 return; 2618 } 2619 2620 views::FocusManager* focus_manager = GetFocusManager(); 2621 views::View* focused = focus_manager->GetFocusedView(); 2622 if (focused && 2623 (!strcmp(focused->GetClassName(), views::Textfield::kViewClassName) || 2624 !strcmp(focused->GetClassName(), OmniboxViewViews::kViewClassName))) { 2625 views::Textfield* textfield = static_cast<views::Textfield*>(focused); 2626 textfield->ExecuteCommand(command_id); 2627 return; 2628 } 2629 2630 #if defined(OS_WIN) && !defined(USE_AURA) 2631 OmniboxView* omnibox_view = GetLocationBarView()->GetLocationEntry(); 2632 if (omnibox_view->model()->has_focus()) { 2633 OmniboxViewWin* omnibox_win = GetOmniboxViewWin(omnibox_view); 2634 ::SendMessage(omnibox_win->GetNativeView(), windows_msg_id, 0, 0); 2635 } 2636 #endif 2637 } 2638 2639 bool BrowserView::DoCutCopyPasteForWebContents( 2640 WebContents* contents, 2641 void (content::RenderWidgetHost::*method)()) { 2642 gfx::NativeView native_view = contents->GetView()->GetContentNativeView(); 2643 if (!native_view) 2644 return false; 2645 #if defined(USE_AURA) 2646 if (native_view->HasFocus()) { 2647 #elif defined(OS_WIN) 2648 if (native_view == ::GetFocus()) { 2649 #endif 2650 (contents->GetRenderViewHost()->*method)(); 2651 return true; 2652 } 2653 2654 return false; 2655 } 2656 2657 void BrowserView::ActivateAppModalDialog() const { 2658 // If another browser is app modal, flash and activate the modal browser. 2659 AppModalDialog* active_dialog = 2660 AppModalDialogQueue::GetInstance()->active_dialog(); 2661 if (!active_dialog) 2662 return; 2663 2664 Browser* modal_browser = 2665 chrome::FindBrowserWithWebContents(active_dialog->web_contents()); 2666 if (modal_browser && (browser_ != modal_browser)) { 2667 modal_browser->window()->FlashFrame(true); 2668 modal_browser->window()->Activate(); 2669 } 2670 2671 AppModalDialogQueue::GetInstance()->ActivateModalDialog(); 2672 } 2673 2674 int BrowserView::GetMaxTopInfoBarArrowHeight() { 2675 int top_arrow_height = 0; 2676 // Only show the arrows when not in fullscreen and when there's no omnibox 2677 // popup. 2678 if (!IsFullscreen() && 2679 !GetLocationBar()->GetLocationEntry()->model()->popup_model()->IsOpen()) { 2680 const LocationIconView* location_icon_view = 2681 toolbar_->location_bar()->location_icon_view(); 2682 // The +1 in the next line creates a 1-px gap between icon and arrow tip. 2683 gfx::Point icon_bottom(0, location_icon_view->GetImageBounds().bottom() - 2684 LocationBarView::kIconInternalPadding + 1); 2685 ConvertPointToTarget(location_icon_view, this, &icon_bottom); 2686 gfx::Point infobar_top(0, infobar_container_->GetVerticalOverlap(NULL)); 2687 ConvertPointToTarget(infobar_container_, this, &infobar_top); 2688 top_arrow_height = infobar_top.y() - icon_bottom.y(); 2689 } 2690 return top_arrow_height; 2691 } 2692