1 | /* |
2 | * Copyright (C) 2012 Google Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
16 | * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
17 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include "PointerLockController.h" |
27 | |
28 | #if ENABLE(POINTER_LOCK) |
29 | |
30 | #include "Chrome.h" |
31 | #include "ChromeClient.h" |
32 | #include "Element.h" |
33 | #include "Event.h" |
34 | #include "EventNames.h" |
35 | #include "Page.h" |
36 | #include "PlatformMouseEvent.h" |
37 | #include "RuntimeEnabledFeatures.h" |
38 | #include "UserGestureIndicator.h" |
39 | #include "VoidCallback.h" |
40 | |
41 | #if ENABLE(POINTER_EVENTS) |
42 | #include "PointerCaptureController.h" |
43 | #endif |
44 | |
45 | namespace WebCore { |
46 | |
47 | PointerLockController::PointerLockController(Page& page) |
48 | : m_page(page) |
49 | { |
50 | } |
51 | |
52 | void PointerLockController::requestPointerLock(Element* target) |
53 | { |
54 | if (!target || !target->isConnected() || m_documentOfRemovedElementWhileWaitingForUnlock) { |
55 | enqueueEvent(eventNames().pointerlockerrorEvent, target); |
56 | return; |
57 | } |
58 | |
59 | if (m_documentAllowedToRelockWithoutUserGesture != &target->document() && !UserGestureIndicator::processingUserGesture()) { |
60 | enqueueEvent(eventNames().pointerlockerrorEvent, target); |
61 | return; |
62 | } |
63 | |
64 | if (target->document().isSandboxed(SandboxPointerLock)) { |
65 | // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. |
66 | target->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked pointer lock on an element because the element's frame is sandboxed and the 'allow-pointer-lock' permission is not set."_s ); |
67 | enqueueEvent(eventNames().pointerlockerrorEvent, target); |
68 | return; |
69 | } |
70 | |
71 | if (m_element) { |
72 | if (&m_element->document() != &target->document()) { |
73 | enqueueEvent(eventNames().pointerlockerrorEvent, target); |
74 | return; |
75 | } |
76 | m_element = target; |
77 | enqueueEvent(eventNames().pointerlockchangeEvent, target); |
78 | #if ENABLE(POINTER_EVENTS) |
79 | m_page.pointerCaptureController().pointerLockWasApplied(); |
80 | #endif |
81 | } else { |
82 | m_lockPending = true; |
83 | m_element = target; |
84 | if (!m_page.chrome().client().requestPointerLock()) { |
85 | clearElement(); |
86 | enqueueEvent(eventNames().pointerlockerrorEvent, target); |
87 | } |
88 | } |
89 | } |
90 | |
91 | void PointerLockController::requestPointerUnlock() |
92 | { |
93 | if (!m_element) |
94 | return; |
95 | |
96 | m_unlockPending = true; |
97 | m_page.chrome().client().requestPointerUnlock(); |
98 | } |
99 | |
100 | void PointerLockController::requestPointerUnlockAndForceCursorVisible() |
101 | { |
102 | m_documentAllowedToRelockWithoutUserGesture = nullptr; |
103 | |
104 | if (!m_element) |
105 | return; |
106 | |
107 | m_unlockPending = true; |
108 | m_page.chrome().client().requestPointerUnlock(); |
109 | m_forceCursorVisibleUponUnlock = true; |
110 | } |
111 | |
112 | void PointerLockController::elementRemoved(Element& element) |
113 | { |
114 | if (m_element == &element) { |
115 | m_documentOfRemovedElementWhileWaitingForUnlock = makeWeakPtr(m_element->document()); |
116 | // Set element null immediately to block any future interaction with it |
117 | // including mouse events received before the unlock completes. |
118 | requestPointerUnlock(); |
119 | clearElement(); |
120 | } |
121 | } |
122 | |
123 | void PointerLockController::documentDetached(Document& document) |
124 | { |
125 | if (m_documentAllowedToRelockWithoutUserGesture == &document) |
126 | m_documentAllowedToRelockWithoutUserGesture = nullptr; |
127 | |
128 | if (m_element && &m_element->document() == &document) { |
129 | m_documentOfRemovedElementWhileWaitingForUnlock = makeWeakPtr(m_element->document()); |
130 | requestPointerUnlock(); |
131 | clearElement(); |
132 | } |
133 | } |
134 | |
135 | bool PointerLockController::isLocked() const |
136 | { |
137 | return m_element && !m_lockPending; |
138 | } |
139 | |
140 | bool PointerLockController::lockPending() const |
141 | { |
142 | return m_lockPending; |
143 | } |
144 | |
145 | Element* PointerLockController::element() const |
146 | { |
147 | return m_element.get(); |
148 | } |
149 | |
150 | void PointerLockController::didAcquirePointerLock() |
151 | { |
152 | if (!m_lockPending) |
153 | return; |
154 | |
155 | ASSERT(m_element); |
156 | |
157 | enqueueEvent(eventNames().pointerlockchangeEvent, m_element.get()); |
158 | m_lockPending = false; |
159 | m_forceCursorVisibleUponUnlock = false; |
160 | m_documentAllowedToRelockWithoutUserGesture = makeWeakPtr(m_element->document()); |
161 | } |
162 | |
163 | void PointerLockController::didNotAcquirePointerLock() |
164 | { |
165 | enqueueEvent(eventNames().pointerlockerrorEvent, m_element.get()); |
166 | clearElement(); |
167 | m_unlockPending = false; |
168 | } |
169 | |
170 | void PointerLockController::didLosePointerLock() |
171 | { |
172 | if (!m_unlockPending) |
173 | m_documentAllowedToRelockWithoutUserGesture = nullptr; |
174 | |
175 | enqueueEvent(eventNames().pointerlockchangeEvent, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get()); |
176 | clearElement(); |
177 | m_unlockPending = false; |
178 | m_documentOfRemovedElementWhileWaitingForUnlock = nullptr; |
179 | if (m_forceCursorVisibleUponUnlock) { |
180 | m_forceCursorVisibleUponUnlock = false; |
181 | m_page.chrome().client().setCursorHiddenUntilMouseMoves(false); |
182 | } |
183 | } |
184 | |
185 | void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType) |
186 | { |
187 | if (!m_element || !m_element->document().frame()) |
188 | return; |
189 | |
190 | m_element->dispatchMouseEvent(event, eventType, event.clickCount()); |
191 | |
192 | // Create click events |
193 | if (eventType == eventNames().mouseupEvent) |
194 | m_element->dispatchMouseEvent(event, eventNames().clickEvent, event.clickCount()); |
195 | } |
196 | |
197 | void PointerLockController::dispatchLockedWheelEvent(const PlatformWheelEvent& event) |
198 | { |
199 | if (!m_element || !m_element->document().frame()) |
200 | return; |
201 | |
202 | m_element->dispatchWheelEvent(event); |
203 | } |
204 | |
205 | void PointerLockController::clearElement() |
206 | { |
207 | m_lockPending = false; |
208 | m_element = nullptr; |
209 | } |
210 | |
211 | void PointerLockController::enqueueEvent(const AtomicString& type, Element* element) |
212 | { |
213 | if (element) |
214 | enqueueEvent(type, &element->document()); |
215 | } |
216 | |
217 | void PointerLockController::enqueueEvent(const AtomicString& type, Document* document) |
218 | { |
219 | if (document) |
220 | document->enqueueDocumentEvent(Event::create(type, Event::CanBubble::Yes, Event::IsCancelable::No)); |
221 | } |
222 | |
223 | } // namespace WebCore |
224 | |
225 | #endif // ENABLE(POINTER_LOCK) |
226 | |