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