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