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