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/content_settings/host_content_settings_map_factory.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
12 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
13 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
14 #include "components/content_settings/core/browser/host_content_settings_map.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/render_widget_host_view.h"
18 #include "content/public/browser/web_contents.h"
20 using content::RenderViewHost
;
21 using content::WebContents
;
23 MouseLockController::MouseLockController(ExclusiveAccessManager
* manager
)
24 : ExclusiveAccessControllerBase(manager
),
25 mouse_lock_state_(MOUSELOCK_NOT_REQUESTED
) {
28 MouseLockController::~MouseLockController() {
31 bool MouseLockController::IsMouseLocked() const {
32 return mouse_lock_state_
== MOUSELOCK_ACCEPTED
||
33 mouse_lock_state_
== MOUSELOCK_ACCEPTED_SILENTLY
;
36 bool MouseLockController::IsMouseLockSilentlyAccepted() const {
37 return mouse_lock_state_
== MOUSELOCK_ACCEPTED_SILENTLY
;
40 void MouseLockController::RequestToLockMouse(WebContents
* web_contents
,
42 bool last_unlocked_by_target
) {
43 DCHECK(!IsMouseLocked());
44 NotifyMouseLockChange();
46 // Must have a user gesture to prevent misbehaving sites from constantly
47 // re-locking the mouse. Exceptions are when the page has unlocked
48 // (i.e. not the user), or if we're in tab fullscreen (user gesture required
50 if (!last_unlocked_by_target
&& !user_gesture
&&
51 !exclusive_access_manager()
52 ->fullscreen_controller()
53 ->IsFullscreenForTabOrPending(web_contents
)) {
54 web_contents
->GotResponseToLockMouseRequest(false);
57 SetTabWithExclusiveAccess(web_contents
);
58 ExclusiveAccessBubbleType bubble_type
=
59 exclusive_access_manager()->GetExclusiveAccessExitBubbleType();
61 switch (GetMouseLockSetting(web_contents
->GetURL())) {
62 case CONTENT_SETTING_ALLOW
:
63 // If bubble already displaying buttons we must not lock the mouse yet,
64 // or it would prevent pressing those buttons. Instead, merge the request.
65 if (!exclusive_access_manager()
66 ->fullscreen_controller()
67 ->IsPrivilegedFullscreenForTab() &&
68 exclusive_access_bubble::ShowButtonsForType(bubble_type
)) {
69 mouse_lock_state_
= MOUSELOCK_REQUESTED
;
72 if (web_contents
->GotResponseToLockMouseRequest(true)) {
73 if (last_unlocked_by_target
) {
74 mouse_lock_state_
= MOUSELOCK_ACCEPTED_SILENTLY
;
76 mouse_lock_state_
= MOUSELOCK_ACCEPTED
;
79 SetTabWithExclusiveAccess(nullptr);
80 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
84 case CONTENT_SETTING_BLOCK
:
85 web_contents
->GotResponseToLockMouseRequest(false);
86 SetTabWithExclusiveAccess(nullptr);
87 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
89 case CONTENT_SETTING_ASK
:
90 mouse_lock_state_
= MOUSELOCK_REQUESTED
;
95 exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent();
98 void MouseLockController::ExitExclusiveAccessIfNecessary() {
99 NotifyTabExclusiveAccessLost();
102 void MouseLockController::NotifyTabExclusiveAccessLost() {
103 WebContents
* tab
= exclusive_access_tab();
105 if (IsMouseLockRequested()) {
106 tab
->GotResponseToLockMouseRequest(false);
107 NotifyMouseLockChange();
111 SetTabWithExclusiveAccess(nullptr);
112 mouse_lock_state_
= MOUSELOCK_NOT_REQUESTED
;
113 exclusive_access_manager()->UpdateExclusiveAccessExitBubbleContent();
117 bool MouseLockController::HandleUserPressedEscape() {
118 if (IsMouseLocked() || IsMouseLockRequested()) {
119 ExitExclusiveAccessIfNecessary();
126 void MouseLockController::ExitExclusiveAccessToPreviousState() {
127 // Nothing to do for mouse lock.
130 bool MouseLockController::OnAcceptExclusiveAccessPermission() {
131 ExclusiveAccessBubbleType bubble_type
=
132 exclusive_access_manager()->GetExclusiveAccessExitBubbleType();
133 bool mouse_lock
= false;
134 exclusive_access_bubble::PermissionRequestedByType(bubble_type
, nullptr,
136 DCHECK(!(mouse_lock
&& IsMouseLocked()));
138 if (mouse_lock
&& !IsMouseLocked()) {
139 DCHECK(IsMouseLockRequested());
141 HostContentSettingsMap
* settings_map
=
142 HostContentSettingsMapFactory::GetForProfile(
143 exclusive_access_manager()->context()->GetProfile());
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
=
249 HostContentSettingsMapFactory::GetForProfile(
250 exclusive_access_manager()->context()->GetProfile());
251 ContentSetting setting
= settings_map
->GetContentSetting(
252 url
, url
, CONTENT_SETTINGS_TYPE_MOUSELOCK
, std::string());
254 if (simplified_ui
&& setting
== CONTENT_SETTING_ASK
)
255 return CONTENT_SETTING_ALLOW
;