Use Optional::valueOr() instead of Optional::value_or()
[WebKit-https.git] / Source / WebCore / dom / EventTarget.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
6  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7  *           (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29  *
30  */
31
32 #include "config.h"
33 #include "EventTarget.h"
34
35 #include "DOMWrapperWorld.h"
36 #include "EventNames.h"
37 #include "HTMLBodyElement.h"
38 #include "InspectorInstrumentation.h"
39 #include "JSEventListener.h"
40 #include "ScriptController.h"
41 #include "ScriptDisallowedScope.h"
42 #include "Settings.h"
43 #include "WebKitAnimationEvent.h"
44 #include "WebKitTransitionEvent.h"
45 #include <wtf/MainThread.h>
46 #include <wtf/NeverDestroyed.h>
47 #include <wtf/Ref.h>
48 #include <wtf/SetForScope.h>
49 #include <wtf/StdLibExtras.h>
50 #include <wtf/Vector.h>
51
52 namespace WebCore {
53
54 using namespace WTF;
55
56 bool EventTarget::isNode() const
57 {
58     return false;
59 }
60
61 bool EventTarget::isPaymentRequest() const
62 {
63     return false;
64 }
65
66 bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
67 {
68     auto passive = options.passive;
69
70     if (!passive.has_value() && eventNames().isTouchScrollBlockingEventType(eventType)) {
71         if (is<DOMWindow>(*this)) {
72             auto& window = downcast<DOMWindow>(*this);
73             if (auto* document = window.document())
74                 passive = document->settings().passiveTouchListenersAsDefaultOnDocument();
75         } else if (is<Node>(*this)) {
76             auto& node = downcast<Node>(*this);
77             if (is<Document>(node) || node.document().documentElement() == &node || node.document().body() == &node)
78                 passive = node.document().settings().passiveTouchListenersAsDefaultOnDocument();
79         }
80     }
81
82     bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
83     auto listenerRef = listener.copyRef();
84
85     if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, passive.valueOr(false), options.once }))
86         return false;
87
88     if (listenerCreatedFromScript)
89         InspectorInstrumentation::didAddEventListener(*this, eventType, listenerRef.get(), options.capture);
90
91     return true;
92 }
93
94 void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant)
95 {
96     if (!listener)
97         return;
98
99     auto visitor = WTF::makeVisitor([&](const AddEventListenerOptions& options) {
100         addEventListener(eventType, listener.releaseNonNull(), options);
101     }, [&](bool capture) {
102         addEventListener(eventType, listener.releaseNonNull(), capture);
103     });
104
105     WTF::visit(visitor, variant);
106 }
107
108 void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, ListenerOptionsOrBoolean&& variant)
109 {
110     if (!listener)
111         return;
112
113     auto visitor = WTF::makeVisitor([&](const ListenerOptions& options) {
114         removeEventListener(eventType, *listener, options);
115     }, [&](bool capture) {
116         removeEventListener(eventType, *listener, capture);
117     });
118
119     WTF::visit(visitor, variant);
120 }
121
122 bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
123 {
124     auto* data = eventTargetData();
125     if (!data)
126         return false;
127
128     InspectorInstrumentation::willRemoveEventListener(*this, eventType, listener, options.capture);
129
130     return data->eventListenerMap.remove(eventType, listener, options.capture);
131 }
132
133 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
134 {
135     auto* existingListener = attributeEventListener(eventType, isolatedWorld);
136     if (!listener) {
137         if (existingListener)
138             removeEventListener(eventType, *existingListener, false);
139         return false;
140     }
141     if (existingListener) {
142         InspectorInstrumentation::willRemoveEventListener(*this, eventType, *existingListener, false);
143
144         auto listenerPointer = listener.copyRef();
145         eventTargetData()->eventListenerMap.replace(eventType, *existingListener, listener.releaseNonNull(), { });
146
147         InspectorInstrumentation::didAddEventListener(*this, eventType, *listenerPointer, false);
148
149         return true;
150     }
151     return addEventListener(eventType, listener.releaseNonNull());
152 }
153
154 EventListener* EventTarget::attributeEventListener(const AtomicString& eventType, DOMWrapperWorld& isolatedWorld)
155 {
156     for (auto& eventListener : eventListeners(eventType)) {
157         auto& listener = eventListener->callback();
158         if (!listener.isAttribute())
159             continue;
160
161         auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
162         if (&listenerWorld == &isolatedWorld)
163             return &listener;
164     }
165
166     return nullptr;
167 }
168
169 bool EventTarget::hasActiveEventListeners(const AtomicString& eventType) const
170 {
171     auto* data = eventTargetData();
172     return data && data->eventListenerMap.containsActive(eventType);
173 }
174
175 ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event)
176 {
177     event.setUntrusted();
178
179     if (!event.isInitialized() || event.isBeingDispatched())
180         return Exception { InvalidStateError };
181
182     if (!scriptExecutionContext())
183         return false;
184
185     dispatchEvent(event);
186     return event.legacyReturnValue();
187 }
188
189 void EventTarget::dispatchEvent(Event& event)
190 {
191     // FIXME: We should always use EventDispatcher.
192     ASSERT(event.isInitialized());
193     ASSERT(!event.isBeingDispatched());
194
195     event.setTarget(this);
196     event.setCurrentTarget(this);
197     event.setEventPhase(Event::AT_TARGET);
198     event.resetBeforeDispatch();
199     fireEventListeners(event, EventInvokePhase::Capturing);
200     fireEventListeners(event, EventInvokePhase::Bubbling);
201     event.resetAfterDispatch();
202 }
203
204 void EventTarget::uncaughtExceptionInEventHandler()
205 {
206 }
207
208 static const AtomicString& legacyType(const Event& event)
209 {
210     if (event.type() == eventNames().animationendEvent)
211         return eventNames().webkitAnimationEndEvent;
212
213     if (event.type() == eventNames().animationstartEvent)
214         return eventNames().webkitAnimationStartEvent;
215
216     if (event.type() == eventNames().animationiterationEvent)
217         return eventNames().webkitAnimationIterationEvent;
218
219     if (event.type() == eventNames().transitionendEvent)
220         return eventNames().webkitTransitionEndEvent;
221
222     // FIXME: This legacy name is not part of the specification (https://dom.spec.whatwg.org/#dispatching-events).
223     if (event.type() == eventNames().wheelEvent)
224         return eventNames().mousewheelEvent;
225
226     return nullAtom();
227 }
228
229 // https://dom.spec.whatwg.org/#concept-event-listener-invoke
230 void EventTarget::fireEventListeners(Event& event, EventInvokePhase phase)
231 {
232     ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
233     ASSERT(event.isInitialized());
234
235     auto* data = eventTargetData();
236     if (!data)
237         return;
238
239     SetForScope<bool> firingEventListenersScope(data->isFiringEventListeners, true);
240
241     if (auto* listenersVector = data->eventListenerMap.find(event.type())) {
242         innerInvokeEventListeners(event, *listenersVector, phase);
243         return;
244     }
245
246     // Only fall back to legacy types for trusted events.
247     if (!event.isTrusted())
248         return;
249
250     const AtomicString& legacyTypeName = legacyType(event);
251     if (!legacyTypeName.isNull()) {
252         if (auto* legacyListenersVector = data->eventListenerMap.find(legacyTypeName)) {
253             AtomicString typeName = event.type();
254             event.setType(legacyTypeName);
255             innerInvokeEventListeners(event, *legacyListenersVector, phase);
256             event.setType(typeName);
257         }
258     }
259 }
260
261 // Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run.
262 // Note that removal still has an effect due to the removed field in RegisteredEventListener.
263 // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
264 void EventTarget::innerInvokeEventListeners(Event& event, EventListenerVector listeners, EventInvokePhase phase)
265 {
266     Ref<EventTarget> protectedThis(*this);
267     ASSERT(!listeners.isEmpty());
268     ASSERT(scriptExecutionContext());
269
270     auto& context = *scriptExecutionContext();
271     bool contextIsDocument = is<Document>(context);
272     InspectorInstrumentationCookie willDispatchEventCookie;
273     if (contextIsDocument)
274         willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(downcast<Document>(context), event, true);
275
276     for (auto& registeredListener : listeners) {
277         if (UNLIKELY(registeredListener->wasRemoved()))
278             continue;
279
280         if (phase == EventInvokePhase::Capturing && !registeredListener->useCapture())
281             continue;
282         if (phase == EventInvokePhase::Bubbling && registeredListener->useCapture())
283             continue;
284
285         if (InspectorInstrumentation::isEventListenerDisabled(*this, event.type(), registeredListener->callback(), registeredListener->useCapture()))
286             continue;
287
288         // If stopImmediatePropagation has been called, we just break out immediately, without
289         // handling any more events on this target.
290         if (event.immediatePropagationStopped())
291             break;
292
293         // Do this before invocation to avoid reentrancy issues.
294         if (registeredListener->isOnce())
295             removeEventListener(event.type(), registeredListener->callback(), ListenerOptions(registeredListener->useCapture()));
296
297         if (registeredListener->isPassive())
298             event.setInPassiveListener(true);
299
300         InspectorInstrumentation::willHandleEvent(context, event, *registeredListener);
301         registeredListener->callback().handleEvent(context, event);
302         InspectorInstrumentation::didHandleEvent(context);
303
304         if (registeredListener->isPassive())
305             event.setInPassiveListener(false);
306     }
307
308     if (contextIsDocument)
309         InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
310 }
311
312 const EventListenerVector& EventTarget::eventListeners(const AtomicString& eventType)
313 {
314     auto* data = eventTargetData();
315     auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
316     static NeverDestroyed<EventListenerVector> emptyVector;
317     return listenerVector ? *listenerVector : emptyVector.get();
318 }
319
320 void EventTarget::removeAllEventListeners()
321 {
322     auto& threadData = threadGlobalData();
323     RELEASE_ASSERT(!threadData.isInRemoveAllEventListeners());
324
325     threadData.setIsInRemoveAllEventListeners(true);
326
327     auto* data = eventTargetData();
328     if (data)
329         data->eventListenerMap.clear();
330
331     threadData.setIsInRemoveAllEventListeners(false);
332 }
333
334 void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor)
335 {
336     EventTargetData* data = eventTargetDataConcurrently();
337     if (!data)
338         return;
339     
340     auto locker = holdLock(data->eventListenerMap.lock());
341     EventListenerIterator iterator(&data->eventListenerMap);
342     while (auto* listener = iterator.nextListener())
343         listener->visitJSFunction(visitor);
344 }
345
346 } // namespace WebCore