blob: 9eb606fd78464ac7aca3a7aa51c91ab0edbfe550 [file] [log] [blame]
// Copyright (c) 2015 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/exclusive_access/mouse_lock_controller.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
using content::RenderViewHost;
using content::WebContents;
namespace {
const char kMouseLockBubbleReshowsHistogramName[] =
"ExclusiveAccess.BubbleReshowsPerSession.MouseLock";
} // namespace
MouseLockController::MouseLockController(ExclusiveAccessManager* manager)
: ExclusiveAccessControllerBase(manager),
mouse_lock_state_(MOUSELOCK_UNLOCKED),
fake_mouse_lock_for_test_(false),
bubble_hide_callback_for_test_(),
weak_ptr_factory_(this) {}
MouseLockController::~MouseLockController() {
}
bool MouseLockController::IsMouseLocked() const {
return mouse_lock_state_ == MOUSELOCK_LOCKED ||
mouse_lock_state_ == MOUSELOCK_LOCKED_SILENTLY;
}
bool MouseLockController::IsMouseLockedSilently() const {
return mouse_lock_state_ == MOUSELOCK_LOCKED_SILENTLY;
}
void MouseLockController::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 &&
!exclusive_access_manager()
->fullscreen_controller()
->IsFullscreenForTabOrPending(web_contents)) {
web_contents->GotResponseToLockMouseRequest(false);
return;
}
SetTabWithExclusiveAccess(web_contents);
// Lock mouse.
if (fake_mouse_lock_for_test_ ||
web_contents->GotResponseToLockMouseRequest(true)) {
if (last_unlocked_by_target &&
web_contents_granted_silent_mouse_lock_permission_ == web_contents) {
mouse_lock_state_ = MOUSELOCK_LOCKED_SILENTLY;
} else {
mouse_lock_state_ = MOUSELOCK_LOCKED;
}
} else {
SetTabWithExclusiveAccess(nullptr);
mouse_lock_state_ = MOUSELOCK_UNLOCKED;
}
exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent(
base::BindOnce(&MouseLockController::OnBubbleHidden,
weak_ptr_factory_.GetWeakPtr(), web_contents));
}
void MouseLockController::ExitExclusiveAccessIfNecessary() {
NotifyTabExclusiveAccessLost();
}
void MouseLockController::NotifyTabExclusiveAccessLost() {
WebContents* tab = exclusive_access_tab();
if (tab) {
UnlockMouse();
SetTabWithExclusiveAccess(nullptr);
mouse_lock_state_ = MOUSELOCK_UNLOCKED;
exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent(
ExclusiveAccessBubbleHideCallback());
}
}
void MouseLockController::RecordBubbleReshowsHistogram(
int bubble_reshow_count) {
UMA_HISTOGRAM_COUNTS_100(kMouseLockBubbleReshowsHistogramName,
bubble_reshow_count);
}
bool MouseLockController::HandleUserPressedEscape() {
if (IsMouseLocked()) {
ExitExclusiveAccessIfNecessary();
return true;
}
return false;
}
void MouseLockController::ExitExclusiveAccessToPreviousState() {
// Nothing to do for mouse lock.
}
void MouseLockController::LostMouseLock() {
RecordExitingUMA();
mouse_lock_state_ = MOUSELOCK_UNLOCKED;
SetTabWithExclusiveAccess(nullptr);
NotifyMouseLockChange();
exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent(
ExclusiveAccessBubbleHideCallback());
}
void MouseLockController::NotifyMouseLockChange() {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
content::Source<MouseLockController>(this),
content::NotificationService::NoDetails());
}
void MouseLockController::UnlockMouse() {
WebContents* tab = exclusive_access_tab();
if (!tab)
return;
content::RenderWidgetHostView* mouse_lock_view = nullptr;
FullscreenController* fullscreen_controller =
exclusive_access_manager()->fullscreen_controller();
if ((fullscreen_controller->exclusive_access_tab() == tab) &&
fullscreen_controller->IsPrivilegedFullscreenForTab()) {
mouse_lock_view =
exclusive_access_tab()->GetFullscreenRenderWidgetHostView();
}
if (!mouse_lock_view) {
RenderViewHost* const rvh = exclusive_access_tab()->GetRenderViewHost();
if (rvh)
mouse_lock_view = rvh->GetWidget()->GetView();
}
if (mouse_lock_view)
mouse_lock_view->UnlockMouse();
}
void MouseLockController::OnBubbleHidden(
WebContents* web_contents,
ExclusiveAccessBubbleHideReason reason) {
if (bubble_hide_callback_for_test_)
bubble_hide_callback_for_test_.Run(reason);
// Allow silent mouse lock if the bubble has been display for a period of
// time and dismissed due to timeout.
if (reason == ExclusiveAccessBubbleHideReason::kTimeout)
web_contents_granted_silent_mouse_lock_permission_ = web_contents;
}