1 // Copyright (c) 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/fullscreen/fullscreen_controller.h" 6 7 #include "apps/ui/web_contents_sizer.h" 8 #include "base/bind.h" 9 #include "base/command_line.h" 10 #include "base/message_loop/message_loop.h" 11 #include "chrome/browser/app_mode/app_mode_utils.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/content_settings/host_content_settings_map.h" 14 #include "chrome/browser/download/download_shelf.h" 15 #include "chrome/browser/fullscreen.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/ui/browser.h" 18 #include "chrome/browser/ui/browser_window.h" 19 #include "chrome/browser/ui/fullscreen/fullscreen_within_tab_helper.h" 20 #include "chrome/browser/ui/status_bubble.h" 21 #include "chrome/browser/ui/tabs/tab_strip_model.h" 22 #include "chrome/common/chrome_switches.h" 23 #include "content/public/browser/navigation_details.h" 24 #include "content/public/browser/navigation_entry.h" 25 #include "content/public/browser/notification_service.h" 26 #include "content/public/browser/render_view_host.h" 27 #include "content/public/browser/render_widget_host_view.h" 28 #include "content/public/browser/user_metrics.h" 29 #include "content/public/browser/web_contents.h" 30 #include "extensions/common/extension.h" 31 32 #if defined(OS_MACOSX) 33 #include "base/mac/mac_util.h" 34 #else 35 #include "base/prefs/pref_service.h" 36 #include "chrome/common/pref_names.h" 37 #endif 38 39 using base::UserMetricsAction; 40 using content::RenderViewHost; 41 using content::WebContents; 42 43 FullscreenController::FullscreenController(Browser* browser) 44 : browser_(browser), 45 window_(browser->window()), 46 profile_(browser->profile()), 47 fullscreened_tab_(NULL), 48 state_prior_to_tab_fullscreen_(STATE_INVALID), 49 tab_fullscreen_accepted_(false), 50 toggled_into_fullscreen_(false), 51 mouse_lock_tab_(NULL), 52 mouse_lock_state_(MOUSELOCK_NOT_REQUESTED), 53 reentrant_window_state_change_call_check_(false), 54 is_privileged_fullscreen_for_testing_(false), 55 ptr_factory_(this) { 56 DCHECK(window_); 57 DCHECK(profile_); 58 } 59 60 FullscreenController::~FullscreenController() { 61 } 62 63 bool FullscreenController::IsFullscreenForBrowser() const { 64 return window_->IsFullscreen() && !IsFullscreenCausedByTab(); 65 } 66 67 void FullscreenController::ToggleBrowserFullscreenMode() { 68 extension_caused_fullscreen_ = GURL(); 69 ToggleFullscreenModeInternal(BROWSER); 70 } 71 72 void FullscreenController::ToggleBrowserFullscreenModeWithExtension( 73 const GURL& extension_url) { 74 // |extension_caused_fullscreen_| will be reset if this causes fullscreen to 75 // exit. 76 extension_caused_fullscreen_ = extension_url; 77 ToggleFullscreenModeInternal(BROWSER); 78 } 79 80 bool FullscreenController::IsWindowFullscreenForTabOrPending() const { 81 return fullscreened_tab_ != NULL; 82 } 83 84 bool FullscreenController::IsFullscreenForTabOrPending( 85 const WebContents* web_contents) const { 86 if (web_contents == fullscreened_tab_) { 87 DCHECK(web_contents == browser_->tab_strip_model()->GetActiveWebContents()); 88 DCHECK(!IsFullscreenWithinTabPossible() || 89 web_contents->GetCapturerCount() == 0); 90 return true; 91 } 92 return IsFullscreenForCapturedTab(web_contents); 93 } 94 95 bool FullscreenController::IsFullscreenCausedByTab() const { 96 return state_prior_to_tab_fullscreen_ == STATE_NORMAL; 97 } 98 99 void FullscreenController::ToggleFullscreenModeForTab(WebContents* web_contents, 100 bool enter_fullscreen) { 101 if (MaybeToggleFullscreenForCapturedTab(web_contents, enter_fullscreen)) { 102 // During tab capture of fullscreen-within-tab views, the browser window 103 // fullscreen state is unchanged, so return now. 104 return; 105 } 106 if (fullscreened_tab_) { 107 if (web_contents != fullscreened_tab_) 108 return; 109 } else if ( 110 web_contents != browser_->tab_strip_model()->GetActiveWebContents()) { 111 return; 112 } 113 if (IsWindowFullscreenForTabOrPending() == enter_fullscreen) 114 return; 115 116 #if defined(OS_WIN) 117 // For now, avoid breaking when initiating full screen tab mode while in 118 // a metro snap. 119 // TODO(robertshield): Find a way to reconcile tab-initiated fullscreen 120 // modes with metro snap. 121 if (IsInMetroSnapMode()) 122 return; 123 #endif 124 125 bool in_browser_or_tab_fullscreen_mode = window_->IsFullscreen(); 126 bool window_is_fullscreen_with_chrome = false; 127 #if defined(OS_MACOSX) 128 window_is_fullscreen_with_chrome = window_->IsFullscreenWithChrome(); 129 #endif 130 131 if (enter_fullscreen) { 132 SetFullscreenedTab(web_contents); 133 if (!in_browser_or_tab_fullscreen_mode) { 134 state_prior_to_tab_fullscreen_ = STATE_NORMAL; 135 ToggleFullscreenModeInternal(TAB); 136 } else if (window_is_fullscreen_with_chrome) { 137 #if defined(OS_MACOSX) 138 state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_WITH_CHROME; 139 EnterFullscreenModeInternal(TAB); 140 #else 141 NOTREACHED(); 142 #endif 143 } else { 144 state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_NO_CHROME; 145 146 // We need to update the fullscreen exit bubble, e.g., going from browser 147 // fullscreen to tab fullscreen will need to show different content. 148 const GURL& url = web_contents->GetURL(); 149 if (!tab_fullscreen_accepted_) { 150 tab_fullscreen_accepted_ = 151 GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW; 152 } 153 UpdateFullscreenExitBubbleContent(); 154 155 // This is only a change between Browser and Tab fullscreen. We generate 156 // a fullscreen notification now because there is no window change. 157 PostFullscreenChangeNotification(true); 158 } 159 } else { 160 if (in_browser_or_tab_fullscreen_mode) { 161 if (IsFullscreenCausedByTab()) { 162 ToggleFullscreenModeInternal(TAB); 163 } else { 164 #if defined(OS_MACOSX) 165 if (state_prior_to_tab_fullscreen_ == 166 STATE_BROWSER_FULLSCREEN_WITH_CHROME) { 167 EnterFullscreenModeInternal(BROWSER_WITH_CHROME); 168 } else { 169 // Clear the bubble URL, which forces the Mac UI to redraw. 170 UpdateFullscreenExitBubbleContent(); 171 } 172 #endif 173 // If currently there is a tab in "tab fullscreen" mode and fullscreen 174 // was not caused by it (i.e., previously it was in "browser fullscreen" 175 // mode), we need to switch back to "browser fullscreen" mode. In this 176 // case, all we have to do is notifying the tab that it has exited "tab 177 // fullscreen" mode. 178 NotifyTabOfExitIfNecessary(); 179 180 // This is only a change between Browser and Tab fullscreen. We generate 181 // a fullscreen notification now because there is no window change. 182 PostFullscreenChangeNotification(true); 183 } 184 } 185 } 186 } 187 188 bool FullscreenController::IsInMetroSnapMode() { 189 #if defined(OS_WIN) 190 return window_->IsInMetroSnapMode(); 191 #else 192 return false; 193 #endif 194 } 195 196 #if defined(OS_WIN) 197 void FullscreenController::SetMetroSnapMode(bool enable) { 198 reentrant_window_state_change_call_check_ = false; 199 200 toggled_into_fullscreen_ = false; 201 window_->SetMetroSnapMode(enable); 202 203 // FullscreenController unit tests for metro snap assume that on Windows calls 204 // to WindowFullscreenStateChanged are reentrant. If that assumption is 205 // invalidated, the tests must be updated to maintain coverage. 206 CHECK(reentrant_window_state_change_call_check_); 207 } 208 #endif // defined(OS_WIN) 209 210 #if defined(OS_MACOSX) 211 void FullscreenController::ToggleBrowserFullscreenWithChrome() { 212 // This method cannot be called if simplified fullscreen is enabled. 213 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 214 DCHECK(!command_line->HasSwitch(switches::kEnableSimplifiedFullscreen)); 215 ToggleFullscreenModeInternal(BROWSER_WITH_CHROME); 216 } 217 #endif 218 219 bool FullscreenController::IsMouseLockRequested() const { 220 return mouse_lock_state_ == MOUSELOCK_REQUESTED; 221 } 222 223 bool FullscreenController::IsMouseLocked() const { 224 return mouse_lock_state_ == MOUSELOCK_ACCEPTED || 225 mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY; 226 } 227 228 void FullscreenController::RequestToLockMouse(WebContents* web_contents, 229 bool user_gesture, 230 bool last_unlocked_by_target) { 231 DCHECK(!IsMouseLocked()); 232 NotifyMouseLockChange(); 233 234 // Must have a user gesture to prevent misbehaving sites from constantly 235 // re-locking the mouse. Exceptions are when the page has unlocked 236 // (i.e. not the user), or if we're in tab fullscreen (user gesture required 237 // for that) 238 if (!last_unlocked_by_target && !user_gesture && 239 !IsFullscreenForTabOrPending(web_contents)) { 240 web_contents->GotResponseToLockMouseRequest(false); 241 return; 242 } 243 SetMouseLockTab(web_contents); 244 FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType(); 245 246 switch (GetMouseLockSetting(web_contents->GetURL())) { 247 case CONTENT_SETTING_ALLOW: 248 // If bubble already displaying buttons we must not lock the mouse yet, 249 // or it would prevent pressing those buttons. Instead, merge the request. 250 if (!IsPrivilegedFullscreenForTab() && 251 fullscreen_bubble::ShowButtonsForType(bubble_type)) { 252 mouse_lock_state_ = MOUSELOCK_REQUESTED; 253 } else { 254 // Lock mouse. 255 if (web_contents->GotResponseToLockMouseRequest(true)) { 256 if (last_unlocked_by_target) { 257 mouse_lock_state_ = MOUSELOCK_ACCEPTED_SILENTLY; 258 } else { 259 mouse_lock_state_ = MOUSELOCK_ACCEPTED; 260 } 261 } else { 262 SetMouseLockTab(NULL); 263 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 264 } 265 } 266 break; 267 case CONTENT_SETTING_BLOCK: 268 web_contents->GotResponseToLockMouseRequest(false); 269 SetMouseLockTab(NULL); 270 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 271 break; 272 case CONTENT_SETTING_ASK: 273 mouse_lock_state_ = MOUSELOCK_REQUESTED; 274 break; 275 default: 276 NOTREACHED(); 277 } 278 UpdateFullscreenExitBubbleContent(); 279 } 280 281 void FullscreenController::OnTabDeactivated(WebContents* web_contents) { 282 if (web_contents == fullscreened_tab_ || web_contents == mouse_lock_tab_) 283 ExitTabFullscreenOrMouseLockIfNecessary(); 284 } 285 286 void FullscreenController::OnTabDetachedFromView(WebContents* old_contents) { 287 if (!IsFullscreenForCapturedTab(old_contents)) 288 return; 289 290 // A fullscreen-within-tab view undergoing screen capture has been detached 291 // and is no longer visible to the user. Set it to exactly the WebContents' 292 // preferred size. See 'FullscreenWithinTab Note'. 293 // 294 // When the user later selects the tab to show |old_contents| again, UI code 295 // elsewhere (e.g., views::WebView) will resize the view to fit within the 296 // browser window once again. 297 298 // If the view has been detached from the browser window (e.g., to drag a tab 299 // off into a new browser window), return immediately to avoid an unnecessary 300 // resize. 301 if (!old_contents->GetDelegate()) 302 return; 303 304 // Do nothing if tab capture ended after toggling fullscreen, or a preferred 305 // size was never specified by the capturer. 306 if (old_contents->GetCapturerCount() == 0 || 307 old_contents->GetPreferredSize().IsEmpty()) { 308 return; 309 } 310 311 content::RenderWidgetHostView* const current_fs_view = 312 old_contents->GetFullscreenRenderWidgetHostView(); 313 if (current_fs_view) 314 current_fs_view->SetSize(old_contents->GetPreferredSize()); 315 apps::ResizeWebContents(old_contents, old_contents->GetPreferredSize()); 316 } 317 318 void FullscreenController::OnTabClosing(WebContents* web_contents) { 319 if (IsFullscreenForCapturedTab(web_contents)) { 320 RenderViewHost* const rvh = web_contents->GetRenderViewHost(); 321 if (rvh) 322 rvh->ExitFullscreen(); 323 } else if (web_contents == fullscreened_tab_ || 324 web_contents == mouse_lock_tab_) { 325 ExitTabFullscreenOrMouseLockIfNecessary(); 326 // The call to exit fullscreen may result in asynchronous notification of 327 // fullscreen state change (e.g., on Linux). We don't want to rely on it 328 // to call NotifyTabOfExitIfNecessary(), because at that point 329 // |fullscreened_tab_| may not be valid. Instead, we call it here to clean 330 // up tab fullscreen related state. 331 NotifyTabOfExitIfNecessary(); 332 } 333 } 334 335 void FullscreenController::WindowFullscreenStateChanged() { 336 reentrant_window_state_change_call_check_ = true; 337 338 bool exiting_fullscreen = !window_->IsFullscreen(); 339 340 PostFullscreenChangeNotification(!exiting_fullscreen); 341 if (exiting_fullscreen) { 342 toggled_into_fullscreen_ = false; 343 extension_caused_fullscreen_ = GURL(); 344 NotifyTabOfExitIfNecessary(); 345 } 346 if (exiting_fullscreen) { 347 window_->GetDownloadShelf()->Unhide(); 348 } else { 349 window_->GetDownloadShelf()->Hide(); 350 if (window_->GetStatusBubble()) 351 window_->GetStatusBubble()->Hide(); 352 } 353 } 354 355 bool FullscreenController::HandleUserPressedEscape() { 356 WebContents* const active_web_contents = 357 browser_->tab_strip_model()->GetActiveWebContents(); 358 if (IsFullscreenForCapturedTab(active_web_contents)) { 359 RenderViewHost* const rvh = active_web_contents->GetRenderViewHost(); 360 if (rvh) 361 rvh->ExitFullscreen(); 362 return true; 363 } else if (IsWindowFullscreenForTabOrPending() || 364 IsMouseLocked() || IsMouseLockRequested()) { 365 ExitTabFullscreenOrMouseLockIfNecessary(); 366 return true; 367 } 368 369 return false; 370 } 371 372 void FullscreenController::ExitTabOrBrowserFullscreenToPreviousState() { 373 if (IsWindowFullscreenForTabOrPending()) 374 ExitTabFullscreenOrMouseLockIfNecessary(); 375 else if (IsFullscreenForBrowser()) 376 ExitFullscreenModeInternal(); 377 } 378 379 void FullscreenController::OnAcceptFullscreenPermission() { 380 FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType(); 381 bool mouse_lock = false; 382 bool fullscreen = false; 383 fullscreen_bubble::PermissionRequestedByType(bubble_type, &fullscreen, 384 &mouse_lock); 385 DCHECK(!(fullscreen && tab_fullscreen_accepted_)); 386 DCHECK(!(mouse_lock && IsMouseLocked())); 387 388 HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap(); 389 390 GURL url = GetFullscreenExitBubbleURL(); 391 ContentSettingsPattern pattern = ContentSettingsPattern::FromURL(url); 392 393 if (mouse_lock && !IsMouseLocked()) { 394 DCHECK(IsMouseLockRequested()); 395 // TODO(markusheintz): We should allow patterns for all possible URLs here. 396 if (pattern.IsValid()) { 397 settings_map->SetContentSetting( 398 pattern, ContentSettingsPattern::Wildcard(), 399 CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string(), 400 CONTENT_SETTING_ALLOW); 401 } 402 403 if (mouse_lock_tab_ && 404 mouse_lock_tab_->GotResponseToLockMouseRequest(true)) { 405 mouse_lock_state_ = MOUSELOCK_ACCEPTED; 406 } else { 407 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 408 SetMouseLockTab(NULL); 409 } 410 NotifyMouseLockChange(); 411 } 412 413 if (fullscreen && !tab_fullscreen_accepted_) { 414 DCHECK(fullscreened_tab_); 415 if (pattern.IsValid()) { 416 settings_map->SetContentSetting( 417 pattern, ContentSettingsPattern::Wildcard(), 418 CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string(), 419 CONTENT_SETTING_ALLOW); 420 } 421 tab_fullscreen_accepted_ = true; 422 } 423 UpdateFullscreenExitBubbleContent(); 424 } 425 426 void FullscreenController::OnDenyFullscreenPermission() { 427 if (!fullscreened_tab_ && !mouse_lock_tab_) 428 return; 429 430 if (IsMouseLockRequested()) { 431 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 432 if (mouse_lock_tab_) 433 mouse_lock_tab_->GotResponseToLockMouseRequest(false); 434 SetMouseLockTab(NULL); 435 NotifyMouseLockChange(); 436 437 // UpdateFullscreenExitBubbleContent() must be called, but to avoid 438 // duplicate calls we do so only if not adjusting the fullscreen state 439 // below, which also calls UpdateFullscreenExitBubbleContent(). 440 if (!IsWindowFullscreenForTabOrPending()) 441 UpdateFullscreenExitBubbleContent(); 442 } 443 444 if (IsWindowFullscreenForTabOrPending()) 445 ExitTabFullscreenOrMouseLockIfNecessary(); 446 } 447 448 void FullscreenController::LostMouseLock() { 449 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 450 SetMouseLockTab(NULL); 451 NotifyMouseLockChange(); 452 UpdateFullscreenExitBubbleContent(); 453 } 454 455 void FullscreenController::Observe(int type, 456 const content::NotificationSource& source, 457 const content::NotificationDetails& details) { 458 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type); 459 if (content::Details<content::LoadCommittedDetails>(details)-> 460 is_navigation_to_different_page()) 461 ExitTabFullscreenOrMouseLockIfNecessary(); 462 } 463 464 GURL FullscreenController::GetFullscreenExitBubbleURL() const { 465 if (fullscreened_tab_) 466 return fullscreened_tab_->GetURL(); 467 if (mouse_lock_tab_) 468 return mouse_lock_tab_->GetURL(); 469 return extension_caused_fullscreen_; 470 } 471 472 FullscreenExitBubbleType FullscreenController::GetFullscreenExitBubbleType() 473 const { 474 // In kiosk and exclusive app mode we always want to be fullscreen and do not 475 // want to show exit instructions for browser mode fullscreen. 476 bool app_mode = false; 477 #if !defined(OS_MACOSX) // App mode (kiosk) is not available on Mac yet. 478 app_mode = chrome::IsRunningInAppMode(); 479 #endif 480 481 if (mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY) 482 return FEB_TYPE_NONE; 483 484 if (!fullscreened_tab_) { 485 if (IsMouseLocked()) 486 return FEB_TYPE_MOUSELOCK_EXIT_INSTRUCTION; 487 if (IsMouseLockRequested()) 488 return FEB_TYPE_MOUSELOCK_BUTTONS; 489 if (!extension_caused_fullscreen_.is_empty()) 490 return FEB_TYPE_BROWSER_EXTENSION_FULLSCREEN_EXIT_INSTRUCTION; 491 if (toggled_into_fullscreen_ && !app_mode) 492 return FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION; 493 return FEB_TYPE_NONE; 494 } 495 496 if (tab_fullscreen_accepted_) { 497 if (IsPrivilegedFullscreenForTab()) 498 return FEB_TYPE_NONE; 499 if (IsMouseLocked()) 500 return FEB_TYPE_FULLSCREEN_MOUSELOCK_EXIT_INSTRUCTION; 501 if (IsMouseLockRequested()) 502 return FEB_TYPE_MOUSELOCK_BUTTONS; 503 return FEB_TYPE_FULLSCREEN_EXIT_INSTRUCTION; 504 } 505 506 if (IsMouseLockRequested()) 507 return FEB_TYPE_FULLSCREEN_MOUSELOCK_BUTTONS; 508 return FEB_TYPE_FULLSCREEN_BUTTONS; 509 } 510 511 void FullscreenController::UpdateNotificationRegistrations() { 512 if (fullscreened_tab_ && mouse_lock_tab_) 513 DCHECK(fullscreened_tab_ == mouse_lock_tab_); 514 515 WebContents* tab = fullscreened_tab_ ? fullscreened_tab_ : mouse_lock_tab_; 516 517 if (tab && registrar_.IsEmpty()) { 518 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 519 content::Source<content::NavigationController>(&tab->GetController())); 520 } else if (!tab && !registrar_.IsEmpty()) { 521 registrar_.RemoveAll(); 522 } 523 } 524 525 void FullscreenController::PostFullscreenChangeNotification( 526 bool is_fullscreen) { 527 base::MessageLoop::current()->PostTask( 528 FROM_HERE, 529 base::Bind(&FullscreenController::NotifyFullscreenChange, 530 ptr_factory_.GetWeakPtr(), 531 is_fullscreen)); 532 } 533 534 void FullscreenController::NotifyFullscreenChange(bool is_fullscreen) { 535 content::NotificationService::current()->Notify( 536 chrome::NOTIFICATION_FULLSCREEN_CHANGED, 537 content::Source<FullscreenController>(this), 538 content::Details<bool>(&is_fullscreen)); 539 } 540 541 void FullscreenController::NotifyTabOfExitIfNecessary() { 542 if (fullscreened_tab_) { 543 RenderViewHost* rvh = fullscreened_tab_->GetRenderViewHost(); 544 SetFullscreenedTab(NULL); 545 state_prior_to_tab_fullscreen_ = STATE_INVALID; 546 tab_fullscreen_accepted_ = false; 547 if (rvh) 548 rvh->ExitFullscreen(); 549 } 550 551 if (mouse_lock_tab_) { 552 if (IsMouseLockRequested()) { 553 mouse_lock_tab_->GotResponseToLockMouseRequest(false); 554 NotifyMouseLockChange(); 555 } else { 556 UnlockMouse(); 557 } 558 SetMouseLockTab(NULL); 559 mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED; 560 } 561 562 UpdateFullscreenExitBubbleContent(); 563 } 564 565 void FullscreenController::NotifyMouseLockChange() { 566 content::NotificationService::current()->Notify( 567 chrome::NOTIFICATION_MOUSE_LOCK_CHANGED, 568 content::Source<FullscreenController>(this), 569 content::NotificationService::NoDetails()); 570 } 571 572 void FullscreenController::ToggleFullscreenModeInternal( 573 FullscreenInternalOption option) { 574 #if defined(OS_WIN) 575 // When in Metro snap mode, toggling in and out of fullscreen is prevented. 576 if (IsInMetroSnapMode()) 577 return; 578 #endif 579 580 bool enter_fullscreen = !window_->IsFullscreen(); 581 #if defined(OS_MACOSX) 582 // When a Mac user requests a toggle they may be toggling between 583 // FullscreenWithoutChrome and FullscreenWithChrome. 584 if (!IsWindowFullscreenForTabOrPending()) { 585 if (option == BROWSER_WITH_CHROME) 586 enter_fullscreen |= window_->IsFullscreenWithoutChrome(); 587 else 588 enter_fullscreen |= window_->IsFullscreenWithChrome(); 589 } 590 #endif 591 592 // In kiosk mode, we always want to be fullscreen. When the browser first 593 // starts we're not yet fullscreen, so let the initial toggle go through. 594 if (chrome::IsRunningInAppMode() && window_->IsFullscreen()) 595 return; 596 597 #if !defined(OS_MACOSX) 598 // Do not enter fullscreen mode if disallowed by pref. This prevents the user 599 // from manually entering fullscreen mode and also disables kiosk mode on 600 // desktop platforms. 601 if (enter_fullscreen && 602 !profile_->GetPrefs()->GetBoolean(prefs::kFullscreenAllowed)) { 603 return; 604 } 605 #endif 606 607 if (enter_fullscreen) 608 EnterFullscreenModeInternal(option); 609 else 610 ExitFullscreenModeInternal(); 611 } 612 613 void FullscreenController::EnterFullscreenModeInternal( 614 FullscreenInternalOption option) { 615 toggled_into_fullscreen_ = true; 616 GURL url; 617 if (option == TAB) { 618 url = browser_->tab_strip_model()->GetActiveWebContents()->GetURL(); 619 tab_fullscreen_accepted_ = 620 GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW; 621 } else { 622 if (!extension_caused_fullscreen_.is_empty()) 623 url = extension_caused_fullscreen_; 624 } 625 626 if (option == BROWSER) 627 content::RecordAction(UserMetricsAction("ToggleFullscreen")); 628 // TODO(scheib): Record metrics for WITH_CHROME, without counting transitions 629 // from tab fullscreen out to browser with chrome. 630 631 #if defined(OS_MACOSX) 632 if (option == BROWSER_WITH_CHROME) { 633 CHECK(chrome::mac::SupportsSystemFullscreen()); 634 window_->EnterFullscreenWithChrome(); 635 } else { 636 #else 637 { 638 #endif 639 window_->EnterFullscreen(url, GetFullscreenExitBubbleType()); 640 } 641 642 UpdateFullscreenExitBubbleContent(); 643 644 // Once the window has become fullscreen it'll call back to 645 // WindowFullscreenStateChanged(). We don't do this immediately as 646 // BrowserWindow::EnterFullscreen() asks for bookmark_bar_state_, so we let 647 // the BrowserWindow invoke WindowFullscreenStateChanged when appropriate. 648 } 649 650 void FullscreenController::ExitFullscreenModeInternal() { 651 toggled_into_fullscreen_ = false; 652 #if defined(OS_MACOSX) 653 // Mac windows report a state change instantly, and so we must also clear 654 // state_prior_to_tab_fullscreen_ to match them else other logic using 655 // state_prior_to_tab_fullscreen_ will be incorrect. 656 NotifyTabOfExitIfNecessary(); 657 #endif 658 window_->ExitFullscreen(); 659 extension_caused_fullscreen_ = GURL(); 660 661 UpdateFullscreenExitBubbleContent(); 662 } 663 664 void FullscreenController::SetFullscreenedTab(WebContents* tab) { 665 fullscreened_tab_ = tab; 666 UpdateNotificationRegistrations(); 667 } 668 669 void FullscreenController::SetMouseLockTab(WebContents* tab) { 670 mouse_lock_tab_ = tab; 671 UpdateNotificationRegistrations(); 672 } 673 674 void FullscreenController::ExitTabFullscreenOrMouseLockIfNecessary() { 675 if (IsWindowFullscreenForTabOrPending()) 676 ToggleFullscreenModeForTab(fullscreened_tab_, false); 677 else 678 NotifyTabOfExitIfNecessary(); 679 } 680 681 void FullscreenController::UpdateFullscreenExitBubbleContent() { 682 GURL url = GetFullscreenExitBubbleURL(); 683 FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType(); 684 685 // If bubble displays buttons, unlock mouse to allow pressing them. 686 if (fullscreen_bubble::ShowButtonsForType(bubble_type) && IsMouseLocked()) 687 UnlockMouse(); 688 689 window_->UpdateFullscreenExitBubbleContent(url, bubble_type); 690 } 691 692 ContentSetting 693 FullscreenController::GetFullscreenSetting(const GURL& url) const { 694 if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile()) 695 return CONTENT_SETTING_ALLOW; 696 697 return profile_->GetHostContentSettingsMap()->GetContentSetting(url, url, 698 CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string()); 699 } 700 701 ContentSetting 702 FullscreenController::GetMouseLockSetting(const GURL& url) const { 703 if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile()) 704 return CONTENT_SETTING_ALLOW; 705 706 HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap(); 707 return settings_map->GetContentSetting(url, url, 708 CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string()); 709 } 710 711 bool FullscreenController::IsPrivilegedFullscreenForTab() const { 712 const bool embedded_widget_present = 713 fullscreened_tab_ && 714 fullscreened_tab_->GetFullscreenRenderWidgetHostView() && 715 IsFullscreenWithinTabPossible(); 716 return embedded_widget_present || is_privileged_fullscreen_for_testing_; 717 } 718 719 void FullscreenController::SetPrivilegedFullscreenForTesting( 720 bool is_privileged) { 721 is_privileged_fullscreen_for_testing_ = is_privileged; 722 } 723 724 bool FullscreenController::IsFullscreenWithinTabPossible() const { 725 return implicit_cast<const content::WebContentsDelegate*>(browser_)-> 726 EmbedsFullscreenWidget(); 727 } 728 729 bool FullscreenController::MaybeToggleFullscreenForCapturedTab( 730 WebContents* web_contents, bool enter_fullscreen) { 731 if (!IsFullscreenWithinTabPossible()) 732 return false; 733 734 if (enter_fullscreen) { 735 if (web_contents->GetCapturerCount() > 0) { 736 FullscreenWithinTabHelper::CreateForWebContents(web_contents); 737 FullscreenWithinTabHelper::FromWebContents(web_contents)-> 738 SetIsFullscreenForCapturedTab(true); 739 return true; 740 } 741 } else { 742 if (IsFullscreenForCapturedTab(web_contents)) { 743 FullscreenWithinTabHelper::RemoveForWebContents(web_contents); 744 return true; 745 } 746 } 747 748 return false; 749 } 750 751 bool FullscreenController::IsFullscreenForCapturedTab( 752 const WebContents* web_contents) const { 753 // Note: On Mac, some of the OnTabXXX() methods get called with a NULL value 754 // for web_contents. Check for that here. 755 const FullscreenWithinTabHelper* const helper = web_contents ? 756 FullscreenWithinTabHelper::FromWebContents(web_contents) : NULL; 757 if (helper && helper->is_fullscreen_for_captured_tab()) { 758 DCHECK(IsFullscreenWithinTabPossible()); 759 DCHECK_NE(fullscreened_tab_, web_contents); 760 return true; 761 } 762 return false; 763 } 764 765 void FullscreenController::UnlockMouse() { 766 if (!mouse_lock_tab_) 767 return; 768 content::RenderWidgetHostView* mouse_lock_view = 769 (fullscreened_tab_ == mouse_lock_tab_ && IsPrivilegedFullscreenForTab()) ? 770 mouse_lock_tab_->GetFullscreenRenderWidgetHostView() : NULL; 771 if (!mouse_lock_view) { 772 RenderViewHost* const rvh = mouse_lock_tab_->GetRenderViewHost(); 773 if (rvh) 774 mouse_lock_view = rvh->GetView(); 775 } 776 if (mouse_lock_view) 777 mouse_lock_view->UnlockMouse(); 778 } 779