1 // Copyright (c) 2015 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.
5 #include "chrome/browser/ui/exclusive_access/mouse_lock_controller.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
11 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
12 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
13 #include "components/content_settings/core/browser/host_content_settings_map.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/render_widget_host_view.h"
17 #include "content/public/browser/web_contents.h"
19 using content::RenderViewHost
;
20 using content::WebContents
;
22 MouseLockController::MouseLockController(ExclusiveAccessManager
* manager
)
23 : ExclusiveAccessControllerBase(manager
),
24 mouse_lock_state_(MOUSELOCK_NOT_REQUESTED
) {
27 MouseLockController::~MouseLockController() {
30 bool MouseLockController::IsMouseLocked() const {
31 return mouse_lock_state_
== MOUSELOCK_ACCEPTED
||
32 mouse_lock_state_
== MOUSELOCK_ACCEPTED_SILENTLY
;
35 bool MouseLockController::IsMouseLockSilentlyAccepted() const {
36 return mouse_lock_state_
== MOUSELOCK_ACCEPTED_SILENTLY
;
39 void MouseLockController::RequestToLockMouse(WebContents
* web_contents
,
41 bool last_unlocked_by_target
) {
42 DCHECK(!IsMouseLocked());
43 NotifyMouseLockChange();
45 // Must have a user gesture to prevent misbehaving sites from constantly
46 // re-locking the mouse. Exceptions are when the page has unlocked
47 // (i.e. not the user), or if we're in tab fullscreen (user gesture required
49 if (!last_unlocked_by_target
&& !user_gesture
&&
50 !exclusive_access_manager()
51 ->fullscreen_controller()
52 ->IsFullscreenForTabOrPending(web_contents
)) {
53 web_contents
->GotResponseToLockMouseRequest(false);
56 SetTabWithExclusiveAccess(web_contents
);
57 ExclusiveAccessBubbleType bubble_type
=
58 exclusive_access_manager()->GetExclusiveAccessExitBubbleType();
60 switch (GetMouseLockSetting(web_contents
->GetURL())) {
61 case CONTENT_SETTING_ALLOW
:
62 // If bubble already displaying buttons we must not lock the mouse yet,
63 // or it would prevent pressing those buttons. Instead, merge the request.
64 if (!exclusive_access_manager()
65 ->fullscreen_controller()
66 ->IsPrivilegedFullscreenForTab() &&
67 exclusive_access_bubble::ShowButtonsForType(bubble_type
)) {
68 mouse_lock_state_
= MOUSELOCK_REQUESTED
;
71 if (web_contents
->GotResponseToLockMouseRequest(true)) {
72 if (last_unlocked_by_target
) {
73 mouse_lock_state_
= MOUSELOCK_ACCEPTED_SILENTLY
;
75 mouse_lock_state_
= MOUSELOCK_ACCEPTED
;
78 SetTabWithExclusiveAccess(nullptr);
79 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
83 case CONTENT_SETTING_BLOCK
:
84 web_contents
->GotResponseToLockMouseRequest(false);
85 SetTabWithExclusiveAccess(nullptr);
86 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
88 case CONTENT_SETTING_ASK
:
89 mouse_lock_state_
= MOUSELOCK_REQUESTED
;
94 exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent();
97 void MouseLockController::ExitExclusiveAccessIfNecessary() {
98 NotifyTabExclusiveAccessLost();
101 void MouseLockController::NotifyTabExclusiveAccessLost() {
102 WebContents
* tab
= exclusive_access_tab();
104 if (IsMouseLockRequested()) {
105 tab
->GotResponseToLockMouseRequest(false);
106 NotifyMouseLockChange();
110 SetTabWithExclusiveAccess(nullptr);
111 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
112 exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent();
116 bool MouseLockController::HandleUserPressedEscape() {
117 if (IsMouseLocked() || IsMouseLockRequested()) {
118 ExitExclusiveAccessIfNecessary();
125 void MouseLockController::ExitExclusiveAccessToPreviousState() {
126 // Nothing to do for mouse lock.
129 bool MouseLockController::OnAcceptExclusiveAccessPermission() {
130 ExclusiveAccessBubbleType bubble_type
=
131 exclusive_access_manager()->GetExclusiveAccessExitBubbleType();
132 bool mouse_lock
= false;
133 exclusive_access_bubble::PermissionRequestedByType(bubble_type
, nullptr,
135 DCHECK(!(mouse_lock
&& IsMouseLocked()));
137 if (mouse_lock
&& !IsMouseLocked()) {
138 DCHECK(IsMouseLockRequested());
140 HostContentSettingsMap
* settings_map
= exclusive_access_manager()
143 ->GetHostContentSettingsMap();
145 GURL url
= GetExclusiveAccessBubbleURL();
146 ContentSettingsPattern pattern
= ContentSettingsPattern::FromURL(url
);
148 // TODO(markusheintz): We should allow patterns for all possible URLs here.
150 // Do not store preference on file:// URLs, they don't have a clean
152 // TODO(estark): Revisit this when crbug.com/455882 is fixed.
153 if (!url
.SchemeIsFile() && pattern
.IsValid()) {
154 settings_map
->SetContentSetting(pattern
,
155 ContentSettingsPattern::Wildcard(),
156 CONTENT_SETTINGS_TYPE_MOUSELOCK
,
157 std::string(), CONTENT_SETTING_ALLOW
);
160 WebContents
* tab
= exclusive_access_tab();
161 if (tab
&& tab
->GotResponseToLockMouseRequest(true)) {
162 mouse_lock_state_
= MOUSELOCK_ACCEPTED
;
164 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
165 SetTabWithExclusiveAccess(nullptr);
167 NotifyMouseLockChange();
174 bool MouseLockController::OnDenyExclusiveAccessPermission() {
175 WebContents
* tab
= exclusive_access_tab();
177 if (tab
&& IsMouseLockRequested()) {
178 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
179 tab
->GotResponseToLockMouseRequest(false);
180 SetTabWithExclusiveAccess(nullptr);
181 NotifyMouseLockChange();
188 void MouseLockController::LostMouseLock() {
189 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
190 SetTabWithExclusiveAccess(nullptr);
191 NotifyMouseLockChange();
192 exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent();
195 bool MouseLockController::IsMouseLockRequested() const {
196 return mouse_lock_state_
== MOUSELOCK_REQUESTED
;
199 void MouseLockController::NotifyMouseLockChange() {
200 content::NotificationService::current()->Notify(
201 chrome::NOTIFICATION_MOUSE_LOCK_CHANGED
,
202 content::Source
<MouseLockController
>(this),
203 content::NotificationService::NoDetails());
206 void MouseLockController::UnlockMouse() {
207 WebContents
* tab
= exclusive_access_tab();
212 content::RenderWidgetHostView
* mouse_lock_view
= nullptr;
213 FullscreenController
* fullscreen_controller
=
214 exclusive_access_manager()->fullscreen_controller();
215 if ((fullscreen_controller
->exclusive_access_tab() == tab
) &&
216 fullscreen_controller
->IsPrivilegedFullscreenForTab()) {
218 exclusive_access_tab()->GetFullscreenRenderWidgetHostView();
221 if (!mouse_lock_view
) {
222 RenderViewHost
* const rvh
= exclusive_access_tab()->GetRenderViewHost();
224 mouse_lock_view
= rvh
->GetView();
228 mouse_lock_view
->UnlockMouse();
231 ContentSetting
MouseLockController::GetMouseLockSetting(const GURL
& url
) const {
232 // If simplified UI is enabled, never ask the user, just auto-allow. (Always
233 // return CONTENT_SETTING_ALLOW in favour of CONTENT_SETTING_ASK.)
235 ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled();
237 // Always ask on file:// URLs, since we can't meaningfully make the
238 // decision stick for a particular origin.
239 // TODO(estark): Revisit this when crbug.com/455882 is fixed.
240 if (url
.SchemeIsFile() && !simplified_ui
)
241 return CONTENT_SETTING_ASK
;
243 if (exclusive_access_manager()
244 ->fullscreen_controller()
245 ->IsPrivilegedFullscreenForTab())
246 return CONTENT_SETTING_ALLOW
;
248 HostContentSettingsMap
* settings_map
= exclusive_access_manager()
251 ->GetHostContentSettingsMap();
252 ContentSetting setting
= settings_map
->GetContentSetting(
253 url
, url
, CONTENT_SETTINGS_TYPE_MOUSELOCK
, std::string());
255 if (simplified_ui
&& setting
== CONTENT_SETTING_ASK
)
256 return CONTENT_SETTING_ALLOW
;