// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"

#include "base/bind.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/fullscreen.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/status_bubble.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/extension.h"

#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#else
#include "base/prefs/pref_service.h"
#include "chrome/common/pref_names.h"
#endif

using content::RenderViewHost;
using content::UserMetricsAction;
using content::WebContents;

FullscreenController::FullscreenController(Browser* browser)
    : browser_(browser),
      window_(browser->window()),
      profile_(browser->profile()),
      fullscreened_tab_(NULL),
      state_prior_to_tab_fullscreen_(STATE_INVALID),
      tab_fullscreen_accepted_(false),
      toggled_into_fullscreen_(false),
      mouse_lock_tab_(NULL),
      mouse_lock_state_(MOUSELOCK_NOT_REQUESTED),
      reentrant_window_state_change_call_check_(false),
      is_privileged_fullscreen_for_testing_(false),
      ptr_factory_(this) {
  DCHECK(window_);
  DCHECK(profile_);
}

FullscreenController::~FullscreenController() {
}

bool FullscreenController::IsFullscreenForBrowser() const {
  return window_->IsFullscreen() && !IsFullscreenCausedByTab();
}

void FullscreenController::ToggleFullscreenMode() {
  extension_caused_fullscreen_ = GURL();
  ToggleFullscreenModeInternal(BROWSER);
}

bool FullscreenController::IsFullscreenForTabOrPending() const {
  return fullscreened_tab_ != NULL;
}

bool FullscreenController::IsFullscreenForTabOrPending(
    const WebContents* web_contents) const {
  if (web_contents != fullscreened_tab_)
    return false;
  DCHECK(web_contents == browser_->tab_strip_model()->GetActiveWebContents());
  return true;
}

bool FullscreenController::IsFullscreenCausedByTab() const {
  return state_prior_to_tab_fullscreen_ == STATE_NORMAL;
}

void FullscreenController::ToggleFullscreenModeForTab(WebContents* web_contents,
                                                      bool enter_fullscreen) {
  if (fullscreened_tab_) {
    if (web_contents != fullscreened_tab_)
      return;
  } else if (
      web_contents != browser_->tab_strip_model()->GetActiveWebContents()) {
    return;
  }
  if (IsFullscreenForTabOrPending() == enter_fullscreen)
    return;

#if defined(OS_WIN)
  // For now, avoid breaking when initiating full screen tab mode while in
  // a metro snap.
  // TODO(robertshield): Find a way to reconcile tab-initiated fullscreen
  //                     modes with metro snap.
  if (IsInMetroSnapMode())
    return;
#endif

  bool in_browser_or_tab_fullscreen_mode = window_->IsFullscreen();
  bool window_is_fullscreen_with_chrome = false;
#if defined(OS_MACOSX)
  window_is_fullscreen_with_chrome = window_->IsFullscreenWithChrome();
#endif

  if (enter_fullscreen) {
    SetFullscreenedTab(web_contents);
    if (!in_browser_or_tab_fullscreen_mode) {
      state_prior_to_tab_fullscreen_ = STATE_NORMAL;
      ToggleFullscreenModeInternal(TAB);
    } else if (window_is_fullscreen_with_chrome) {
#if defined(OS_MACOSX)
      state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_WITH_CHROME;
      EnterFullscreenModeInternal(TAB);
#else
      NOTREACHED();
#endif
    } else {
      state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_NO_CHROME;

      // We need to update the fullscreen exit bubble, e.g., going from browser
      // fullscreen to tab fullscreen will need to show different content.
      const GURL& url = web_contents->GetURL();
      if (!tab_fullscreen_accepted_) {
        tab_fullscreen_accepted_ =
            GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW;
      }
      UpdateFullscreenExitBubbleContent();

      // This is only a change between Browser and Tab fullscreen. We generate
      // a fullscreen notification now because there is no window change.
      PostFullscreenChangeNotification(true);
    }
  } else {
    if (in_browser_or_tab_fullscreen_mode) {
      if (IsFullscreenCausedByTab()) {
        ToggleFullscreenModeInternal(TAB);
      } else {
#if defined(OS_MACOSX)
        if (state_prior_to_tab_fullscreen_ ==
            STATE_BROWSER_FULLSCREEN_WITH_CHROME) {
          EnterFullscreenModeInternal(BROWSER_WITH_CHROME);
        } else {
          // Clear the bubble URL, which forces the Mac UI to redraw.
          UpdateFullscreenExitBubbleContent();
        }
#endif
        // If currently there is a tab in "tab fullscreen" mode and fullscreen
        // was not caused by it (i.e., previously it was in "browser fullscreen"
        // mode), we need to switch back to "browser fullscreen" mode. In this
        // case, all we have to do is notifying the tab that it has exited "tab
        // fullscreen" mode.
        NotifyTabOfExitIfNecessary();

        // This is only a change between Browser and Tab fullscreen. We generate
        // a fullscreen notification now because there is no window change.
        PostFullscreenChangeNotification(true);
      }
    }
  }
}

void FullscreenController::ToggleFullscreenModeWithExtension(
    const GURL& extension_url) {
  // |extension_caused_fullscreen_| will be reset if this causes fullscreen to
  // exit.
  extension_caused_fullscreen_ = extension_url;
  ToggleFullscreenModeInternal(BROWSER);
}

bool FullscreenController::IsInMetroSnapMode() {
#if defined(OS_WIN)
  return window_->IsInMetroSnapMode();
#else
  return false;
#endif
}

#if defined(OS_WIN)
void FullscreenController::SetMetroSnapMode(bool enable) {
  reentrant_window_state_change_call_check_ = false;

  toggled_into_fullscreen_ = false;
  window_->SetMetroSnapMode(enable);

  // FullscreenController unit tests for metro snap assume that on Windows calls
  // to WindowFullscreenStateChanged are reentrant. If that assumption is
  // invalidated, the tests must be updated to maintain coverage.
  CHECK(reentrant_window_state_change_call_check_);
}
#endif  // defined(OS_WIN)

#if defined(OS_MACOSX)
void FullscreenController::ToggleFullscreenWithChrome() {
  // This method cannot be called if simplified fullscreen is enabled.
  const CommandLine* command_line = CommandLine::ForCurrentProcess();
  DCHECK(!command_line->HasSwitch(switches::kEnableSimplifiedFullscreen));
  ToggleFullscreenModeInternal(BROWSER_WITH_CHROME);
}
#endif

bool FullscreenController::IsMouseLockRequested() const {
  return mouse_lock_state_ == MOUSELOCK_REQUESTED;
}

bool FullscreenController::IsMouseLocked() const {
  return mouse_lock_state_ == MOUSELOCK_ACCEPTED ||
         mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY;
}

void FullscreenController::RequestToLockMouse(WebContents* web_contents,
                                              bool user_gesture,
                                              bool last_unlocked_by_target) {
  DCHECK(!IsMouseLocked());
  NotifyMouseLockChange();

  // Must have a user gesture to prevent misbehaving sites from constantly
  // re-locking the mouse. Exceptions are when the page has unlocked
  // (i.e. not the user), or if we're in tab fullscreen (user gesture required
  // for that)
  if (!last_unlocked_by_target && !user_gesture &&
      !IsFullscreenForTabOrPending(web_contents)) {
    web_contents->GotResponseToLockMouseRequest(false);
    return;
  }
  SetMouseLockTab(web_contents);
  FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();

  switch (GetMouseLockSetting(web_contents->GetURL())) {
    case CONTENT_SETTING_ALLOW:
      // If bubble already displaying buttons we must not lock the mouse yet,
      // or it would prevent pressing those buttons. Instead, merge the request.
      if (!IsPrivilegedFullscreenForTab() &&
          fullscreen_bubble::ShowButtonsForType(bubble_type)) {
        mouse_lock_state_ = MOUSELOCK_REQUESTED;
      } else {
        // Lock mouse.
        if (web_contents->GotResponseToLockMouseRequest(true)) {
          if (last_unlocked_by_target) {
            mouse_lock_state_ = MOUSELOCK_ACCEPTED_SILENTLY;
          } else {
            mouse_lock_state_ = MOUSELOCK_ACCEPTED;
          }
        } else {
          SetMouseLockTab(NULL);
          mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
        }
      }
      break;
    case CONTENT_SETTING_BLOCK:
      web_contents->GotResponseToLockMouseRequest(false);
      SetMouseLockTab(NULL);
      mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
      break;
    case CONTENT_SETTING_ASK:
      mouse_lock_state_ = MOUSELOCK_REQUESTED;
      break;
    default:
      NOTREACHED();
  }
  UpdateFullscreenExitBubbleContent();
}

void FullscreenController::OnTabDeactivated(WebContents* web_contents) {
  if (web_contents == fullscreened_tab_ || web_contents == mouse_lock_tab_)
    ExitTabFullscreenOrMouseLockIfNecessary();
}

void FullscreenController::OnTabClosing(WebContents* web_contents) {
  if (web_contents == fullscreened_tab_ || web_contents == mouse_lock_tab_) {
    ExitTabFullscreenOrMouseLockIfNecessary();
    // The call to exit fullscreen may result in asynchronous notification of
    // fullscreen state change (e.g., on Linux). We don't want to rely on it
    // to call NotifyTabOfExitIfNecessary(), because at that point
    // |fullscreened_tab_| may not be valid. Instead, we call it here to clean
    // up tab fullscreen related state.
    NotifyTabOfExitIfNecessary();
  }
}

void FullscreenController::WindowFullscreenStateChanged() {
  reentrant_window_state_change_call_check_ = true;

  bool exiting_fullscreen = !window_->IsFullscreen();

  PostFullscreenChangeNotification(!exiting_fullscreen);
  if (exiting_fullscreen) {
    toggled_into_fullscreen_ = false;
    extension_caused_fullscreen_ = GURL();
    NotifyTabOfExitIfNecessary();
  }
  if (exiting_fullscreen) {
    window_->GetDownloadShelf()->Unhide();
  } else {
    window_->GetDownloadShelf()->Hide();
    if (window_->GetStatusBubble())
      window_->GetStatusBubble()->Hide();
  }
}

bool FullscreenController::HandleUserPressedEscape() {
  if (IsFullscreenForTabOrPending() ||
      IsMouseLocked() || IsMouseLockRequested()) {
    ExitTabFullscreenOrMouseLockIfNecessary();
    return true;
  }

  return false;
}

void FullscreenController::ExitTabOrBrowserFullscreenToPreviousState() {
  if (IsFullscreenForTabOrPending())
    ExitTabFullscreenOrMouseLockIfNecessary();
  else if (IsFullscreenForBrowser())
    ExitFullscreenModeInternal();
}

void FullscreenController::OnAcceptFullscreenPermission() {
  FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();
  bool mouse_lock = false;
  bool fullscreen = false;
  fullscreen_bubble::PermissionRequestedByType(bubble_type, &fullscreen,
                                               &mouse_lock);
  DCHECK(!(fullscreen && tab_fullscreen_accepted_));
  DCHECK(!(mouse_lock && IsMouseLocked()));

  HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap();

  GURL url = GetFullscreenExitBubbleURL();
  ContentSettingsPattern pattern = ContentSettingsPattern::FromURL(url);

  if (mouse_lock && !IsMouseLocked()) {
    DCHECK(IsMouseLockRequested());
    // TODO(markusheintz): We should allow patterns for all possible URLs here.
    if (pattern.IsValid()) {
      settings_map->SetContentSetting(
          pattern, ContentSettingsPattern::Wildcard(),
          CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string(),
          CONTENT_SETTING_ALLOW);
    }

    if (mouse_lock_tab_ &&
        mouse_lock_tab_->GotResponseToLockMouseRequest(true)) {
      mouse_lock_state_ = MOUSELOCK_ACCEPTED;
    } else {
      mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
      SetMouseLockTab(NULL);
    }
    NotifyMouseLockChange();
  }

  if (fullscreen && !tab_fullscreen_accepted_) {
    DCHECK(fullscreened_tab_);
    if (pattern.IsValid()) {
      settings_map->SetContentSetting(
          pattern, ContentSettingsPattern::Wildcard(),
          CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string(),
          CONTENT_SETTING_ALLOW);
    }
    tab_fullscreen_accepted_ = true;
  }
  UpdateFullscreenExitBubbleContent();
}

void FullscreenController::OnDenyFullscreenPermission() {
  if (!fullscreened_tab_ && !mouse_lock_tab_)
    return;

  if (IsMouseLockRequested()) {
    mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
    if (mouse_lock_tab_)
      mouse_lock_tab_->GotResponseToLockMouseRequest(false);
    SetMouseLockTab(NULL);
    NotifyMouseLockChange();

    // UpdateFullscreenExitBubbleContent() must be called, but to avoid
    // duplicate calls we do so only if not adjusting the fullscreen state
    // below, which also calls UpdateFullscreenExitBubbleContent().
    if (!IsFullscreenForTabOrPending())
      UpdateFullscreenExitBubbleContent();
  }

  if (IsFullscreenForTabOrPending())
    ExitTabFullscreenOrMouseLockIfNecessary();
}

void FullscreenController::LostMouseLock() {
  mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
  SetMouseLockTab(NULL);
  NotifyMouseLockChange();
  UpdateFullscreenExitBubbleContent();
}

void FullscreenController::Observe(int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
      if (content::Details<content::LoadCommittedDetails>(details)->
              is_navigation_to_different_page()) {
        ExitTabFullscreenOrMouseLockIfNecessary();
      }
      break;

    default:
      NOTREACHED() << "Got a notification we didn't register for.";
  }
}

GURL FullscreenController::GetFullscreenExitBubbleURL() const {
  if (fullscreened_tab_)
    return fullscreened_tab_->GetURL();
  else if (mouse_lock_tab_)
    return mouse_lock_tab_->GetURL();
  else if (!extension_caused_fullscreen_.is_empty())
    return extension_caused_fullscreen_;
  else
    return GURL();
}

FullscreenExitBubbleType FullscreenController::GetFullscreenExitBubbleType()
    const {
  // In kiosk and exclusive app mode we always want to be fullscreen and do not
  // want to show exit instructions for browser mode fullscreen.
  bool app_mode = false;
#if !defined(OS_MACOSX)  // App mode (kiosk) is not available on Mac yet.
  app_mode = chrome::IsRunningInAppMode();
#endif

  if (mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY) {
    return FEB_TYPE_NONE;
  }

  if (fullscreened_tab_) {
    if (tab_fullscreen_accepted_) {
      if (IsPrivilegedFullscreenForTab()) {
        return FEB_TYPE_NONE;
      } else if (IsMouseLocked()) {
        return FEB_TYPE_FULLSCREEN_MOUSELOCK_EXIT_INSTRUCTION;
      } else if (IsMouseLockRequested()) {
        return FEB_TYPE_MOUSELOCK_BUTTONS;
      } else {
        return FEB_TYPE_FULLSCREEN_EXIT_INSTRUCTION;
      }
    } else {  // Full screen not yet accepted.
      if (IsMouseLockRequested()) {
        return FEB_TYPE_FULLSCREEN_MOUSELOCK_BUTTONS;
      } else {
        return FEB_TYPE_FULLSCREEN_BUTTONS;
      }
    }
  } else {  // Not tab full screen.
    if (IsMouseLocked()) {
      return FEB_TYPE_MOUSELOCK_EXIT_INSTRUCTION;
    } else if (IsMouseLockRequested()) {
      return FEB_TYPE_MOUSELOCK_BUTTONS;
    } else {
      if (!extension_caused_fullscreen_.is_empty()) {
        return FEB_TYPE_BROWSER_EXTENSION_FULLSCREEN_EXIT_INSTRUCTION;
      } else if (toggled_into_fullscreen_ && !app_mode) {
        return FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION;
      } else {
        return FEB_TYPE_NONE;
      }
    }
  }
  NOTREACHED();
  return FEB_TYPE_NONE;
}

void FullscreenController::UpdateNotificationRegistrations() {
  if (fullscreened_tab_ && mouse_lock_tab_)
    DCHECK(fullscreened_tab_ == mouse_lock_tab_);

  WebContents* tab = fullscreened_tab_ ? fullscreened_tab_ : mouse_lock_tab_;

  if (tab && registrar_.IsEmpty()) {
    registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
        content::Source<content::NavigationController>(&tab->GetController()));
  } else if (!tab && !registrar_.IsEmpty()) {
    registrar_.RemoveAll();
  }
}

void FullscreenController::PostFullscreenChangeNotification(
    bool is_fullscreen) {
  base::MessageLoop::current()->PostTask(
      FROM_HERE,
      base::Bind(&FullscreenController::NotifyFullscreenChange,
                 ptr_factory_.GetWeakPtr(),
                 is_fullscreen));
}

void FullscreenController::NotifyFullscreenChange(bool is_fullscreen) {
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_FULLSCREEN_CHANGED,
      content::Source<FullscreenController>(this),
      content::Details<bool>(&is_fullscreen));
}

void FullscreenController::NotifyTabOfExitIfNecessary() {
  if (fullscreened_tab_) {
    RenderViewHost* rvh = fullscreened_tab_->GetRenderViewHost();
    SetFullscreenedTab(NULL);
    state_prior_to_tab_fullscreen_ = STATE_INVALID;
    tab_fullscreen_accepted_ = false;
    if (rvh)
      rvh->ExitFullscreen();
  }

  if (mouse_lock_tab_) {
    if (IsMouseLockRequested()) {
      mouse_lock_tab_->GotResponseToLockMouseRequest(false);
      NotifyMouseLockChange();
    } else {
      UnlockMouse();
    }
    SetMouseLockTab(NULL);
    mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
  }

  UpdateFullscreenExitBubbleContent();
}

void FullscreenController::NotifyMouseLockChange() {
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
      content::Source<FullscreenController>(this),
      content::NotificationService::NoDetails());
}

void FullscreenController::ToggleFullscreenModeInternal(
    FullscreenInternalOption option) {
#if defined(OS_WIN)
  // When in Metro snap mode, toggling in and out of fullscreen is prevented.
  if (IsInMetroSnapMode())
    return;
#endif

  bool enter_fullscreen = !window_->IsFullscreen();
#if defined(OS_MACOSX)
  // When a Mac user requests a toggle they may be toggling between
  // FullscreenWithoutChrome and FullscreenWithChrome.
  if (!IsFullscreenForTabOrPending()) {
    if (option == BROWSER_WITH_CHROME)
      enter_fullscreen |= window_->IsFullscreenWithoutChrome();
    else
      enter_fullscreen |= window_->IsFullscreenWithChrome();
  }
#endif

  // In kiosk mode, we always want to be fullscreen. When the browser first
  // starts we're not yet fullscreen, so let the initial toggle go through.
  if (chrome::IsRunningInAppMode() && window_->IsFullscreen())
    return;

#if !defined(OS_MACOSX)
  // Do not enter fullscreen mode if disallowed by pref. This prevents the user
  // from manually entering fullscreen mode and also disables kiosk mode on
  // desktop platforms.
  if (enter_fullscreen &&
      !profile_->GetPrefs()->GetBoolean(prefs::kFullscreenAllowed)) {
    return;
  }
#endif

  if (enter_fullscreen)
    EnterFullscreenModeInternal(option);
  else
    ExitFullscreenModeInternal();
}

void FullscreenController::EnterFullscreenModeInternal(
    FullscreenInternalOption option) {
  toggled_into_fullscreen_ = true;
  GURL url;
  if (option == TAB) {
    url = browser_->tab_strip_model()->GetActiveWebContents()->GetURL();
    tab_fullscreen_accepted_ =
        GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW;
  } else {
    if (!extension_caused_fullscreen_.is_empty())
      url = extension_caused_fullscreen_;
  }

  if (option == BROWSER)
    content::RecordAction(UserMetricsAction("ToggleFullscreen"));
  // TODO(scheib): Record metrics for WITH_CHROME, without counting transitions
  // from tab fullscreen out to browser with chrome.

#if defined(OS_MACOSX)
  if (option == BROWSER_WITH_CHROME) {
    CHECK(chrome::mac::SupportsSystemFullscreen());
    window_->EnterFullscreenWithChrome();
  } else {
#else
  {
#endif
    window_->EnterFullscreen(url, GetFullscreenExitBubbleType());
  }

  UpdateFullscreenExitBubbleContent();

  // Once the window has become fullscreen it'll call back to
  // WindowFullscreenStateChanged(). We don't do this immediately as
  // BrowserWindow::EnterFullscreen() asks for bookmark_bar_state_, so we let
  // the BrowserWindow invoke WindowFullscreenStateChanged when appropriate.
}

void FullscreenController::ExitFullscreenModeInternal() {
  toggled_into_fullscreen_ = false;
#if defined(OS_MACOSX)
  // Mac windows report a state change instantly, and so we must also clear
  // state_prior_to_tab_fullscreen_ to match them else other logic using
  // state_prior_to_tab_fullscreen_ will be incorrect.
  NotifyTabOfExitIfNecessary();
#endif
  window_->ExitFullscreen();
  extension_caused_fullscreen_ = GURL();

  UpdateFullscreenExitBubbleContent();
}

void FullscreenController::SetFullscreenedTab(WebContents* tab) {
  fullscreened_tab_ = tab;
  UpdateNotificationRegistrations();
}

void FullscreenController::SetMouseLockTab(WebContents* tab) {
  mouse_lock_tab_ = tab;
  UpdateNotificationRegistrations();
}

void FullscreenController::ExitTabFullscreenOrMouseLockIfNecessary() {
  if (IsFullscreenForTabOrPending())
    ToggleFullscreenModeForTab(fullscreened_tab_, false);
  else
    NotifyTabOfExitIfNecessary();
}

void FullscreenController::UpdateFullscreenExitBubbleContent() {
  GURL url = GetFullscreenExitBubbleURL();
  FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();

  // If bubble displays buttons, unlock mouse to allow pressing them.
  if (fullscreen_bubble::ShowButtonsForType(bubble_type) && IsMouseLocked())
    UnlockMouse();

  window_->UpdateFullscreenExitBubbleContent(url, bubble_type);
}

ContentSetting
FullscreenController::GetFullscreenSetting(const GURL& url) const {
  if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile())
    return CONTENT_SETTING_ALLOW;

  return profile_->GetHostContentSettingsMap()->GetContentSetting(url, url,
      CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
}

ContentSetting
FullscreenController::GetMouseLockSetting(const GURL& url) const {
  if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile())
    return CONTENT_SETTING_ALLOW;

  HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap();
  return settings_map->GetContentSetting(url, url,
      CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string());
}

bool FullscreenController::IsPrivilegedFullscreenForTab() const {
  const bool embedded_widget_present =
      fullscreened_tab_ &&
      fullscreened_tab_->GetFullscreenRenderWidgetHostView() &&
      implicit_cast<const content::WebContentsDelegate*>(browser_)->
          EmbedsFullscreenWidget();
  return embedded_widget_present || is_privileged_fullscreen_for_testing_;
}

void FullscreenController::SetPrivilegedFullscreenForTesting(
    bool is_privileged) {
  is_privileged_fullscreen_for_testing_ = is_privileged;
}

void FullscreenController::UnlockMouse() {
  if (!mouse_lock_tab_)
    return;
  content::RenderWidgetHostView* mouse_lock_view =
      (fullscreened_tab_ == mouse_lock_tab_ && IsPrivilegedFullscreenForTab()) ?
      mouse_lock_tab_->GetFullscreenRenderWidgetHostView() : NULL;
  if (!mouse_lock_view) {
    RenderViewHost* const rvh = mouse_lock_tab_->GetRenderViewHost();
    if (rvh)
      mouse_lock_view = rvh->GetView();
  }
  if (mouse_lock_view)
    mouse_lock_view->UnlockMouse();
}