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/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