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