blob: dc0adb5c046e3d3cb4806f2fcf1f76a93779085c [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// 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/exclusive_access_manager.h"
#include <utility>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "build/build_config.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/exclusive_access/pointer_lock_controller.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/common/input/native_web_keyboard_event.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "ui/events/keycodes/keyboard_codes.h"
using content::WebContents;
namespace {
constexpr char kHistogramFullscreenLockStateAtEntryViaApi[] =
"WebCore.Fullscreen.LockStateAtEntryViaApi";
constexpr char kHistogramFullscreenLockStateAtEntryViaBrowserUi[] =
"WebCore.Fullscreen.LockStateAtEntryViaBrowserUi";
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class LockState {
kUnlocked = 0,
kKeyboardLocked = 1,
kPointerLocked = 2,
kKeyboardAndPointerLocked = 3,
kMaxValue = kKeyboardAndPointerLocked,
};
} // namespace
ExclusiveAccessManager::ExclusiveAccessManager(
ExclusiveAccessContext* exclusive_access_context)
: exclusive_access_context_(exclusive_access_context),
fullscreen_controller_(this),
keyboard_lock_controller_(this),
pointer_lock_controller_(this) {}
ExclusiveAccessManager::~ExclusiveAccessManager() = default;
ExclusiveAccessBubbleType
ExclusiveAccessManager::GetExclusiveAccessExitBubbleType() 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 !BUILDFLAG(IS_MAC) // App mode (kiosk) is not available on Mac yet.
app_mode = chrome::IsRunningInAppMode();
#endif
if (fullscreen_controller_.IsWindowFullscreenForTabOrPending()) {
if (!fullscreen_controller_.IsTabFullscreen())
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_FULLSCREEN_EXIT_INSTRUCTION;
if (pointer_lock_controller_.IsPointerLockedSilently()) {
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE;
}
if (keyboard_lock_controller_.RequiresPressAndHoldEscToExit())
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_KEYBOARD_LOCK_EXIT_INSTRUCTION;
if (pointer_lock_controller_.IsPointerLocked()) {
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_FULLSCREEN_POINTERLOCK_EXIT_INSTRUCTION;
}
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_FULLSCREEN_EXIT_INSTRUCTION;
}
if (pointer_lock_controller_.IsPointerLockedSilently()) {
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE;
}
if (pointer_lock_controller_.IsPointerLocked()) {
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_POINTERLOCK_EXIT_INSTRUCTION;
}
if (fullscreen_controller_.IsExtensionFullscreenOrPending())
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_EXTENSION_FULLSCREEN_EXIT_INSTRUCTION;
if (fullscreen_controller_.IsControllerInitiatedFullscreen() && !app_mode)
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION;
return EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE;
}
void ExclusiveAccessManager::UpdateExclusiveAccessExitBubbleContent(
ExclusiveAccessBubbleHideCallback bubble_first_hide_callback,
bool force_update) {
GURL url = GetExclusiveAccessBubbleURL();
ExclusiveAccessBubbleType bubble_type = GetExclusiveAccessExitBubbleType();
exclusive_access_context_->UpdateExclusiveAccessExitBubbleContent(
url, bubble_type, std::move(bubble_first_hide_callback),
/*notify_download=*/false, force_update);
}
GURL ExclusiveAccessManager::GetExclusiveAccessBubbleURL() const {
GURL result = fullscreen_controller_.GetURLForExclusiveAccessBubble();
if (!result.is_valid())
result = pointer_lock_controller_.GetURLForExclusiveAccessBubble();
return result;
}
void ExclusiveAccessManager::RecordLockStateOnEnteringApiFullscreen() const {
RecordLockStateOnEnteringFullscreen(
kHistogramFullscreenLockStateAtEntryViaApi);
}
void ExclusiveAccessManager::RecordLockStateOnEnteringBrowserFullscreen()
const {
RecordLockStateOnEnteringFullscreen(
kHistogramFullscreenLockStateAtEntryViaBrowserUi);
}
void ExclusiveAccessManager::OnTabDeactivated(WebContents* web_contents) {
fullscreen_controller_.OnTabDeactivated(web_contents);
keyboard_lock_controller_.OnTabDeactivated(web_contents);
pointer_lock_controller_.OnTabDeactivated(web_contents);
}
void ExclusiveAccessManager::OnTabDetachedFromView(WebContents* web_contents) {
fullscreen_controller_.OnTabDetachedFromView(web_contents);
keyboard_lock_controller_.OnTabDetachedFromView(web_contents);
pointer_lock_controller_.OnTabDetachedFromView(web_contents);
}
void ExclusiveAccessManager::OnTabClosing(WebContents* web_contents) {
fullscreen_controller_.OnTabClosing(web_contents);
keyboard_lock_controller_.OnTabClosing(web_contents);
pointer_lock_controller_.OnTabClosing(web_contents);
}
bool ExclusiveAccessManager::HandleUserKeyEvent(
const content::NativeWebKeyboardEvent& event) {
if (event.windows_key_code != ui::VKEY_ESCAPE) {
OnUserInput();
return false;
}
// Give the |keyboard_lock_controller_| first chance at handling the ESC event
// as there are specific UX behaviors that occur when that mode is active
// which are coordinated by that class. Return false as we don't want to
// prevent the event from propagating to the webpage.
if (keyboard_lock_controller_.HandleKeyEvent(event))
return false;
bool handled = false;
handled = fullscreen_controller_.HandleUserPressedEscape();
handled |= pointer_lock_controller_.HandleUserPressedEscape();
handled |= keyboard_lock_controller_.HandleUserPressedEscape();
return handled;
}
void ExclusiveAccessManager::OnUserInput() {
exclusive_access_context_->OnExclusiveAccessUserInput();
}
void ExclusiveAccessManager::ExitExclusiveAccess() {
fullscreen_controller_.ExitExclusiveAccessToPreviousState();
keyboard_lock_controller_.LostKeyboardLock();
pointer_lock_controller_.LostPointerLock();
}
void ExclusiveAccessManager::RecordLockStateOnEnteringFullscreen(
const char histogram_name[]) const {
LockState lock_state = LockState::kUnlocked;
if (keyboard_lock_controller_.IsKeyboardLockActive()) {
if (pointer_lock_controller_.IsPointerLocked()) {
lock_state = LockState::kKeyboardAndPointerLocked;
} else {
lock_state = LockState::kKeyboardLocked;
}
} else if (pointer_lock_controller_.IsPointerLocked()) {
lock_state = LockState::kPointerLocked;
}
base::UmaHistogramEnumeration(histogram_name, lock_state);
if (fullscreen_controller_.exclusive_access_tab()) {
ukm::SourceId source_id = fullscreen_controller_.exclusive_access_tab()
->GetPrimaryMainFrame()
->GetPageUkmSourceId();
ukm::builders::Fullscreen_Enter(source_id)
.SetLockState(static_cast<int64_t>(lock_state))
.Record(ukm::UkmRecorder::Get());
}
}