Rename AtomicString to AtomString
[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 "HTMLHtmlElement.h"
39 #include "InspectorInstrumentation.h"
40 #include "JSEventListener.h"
41 #include "JSLazyEventListener.h"
42 #include "RuntimeEnabledFeatures.h"
43 #include "ScriptController.h"
44 #include "ScriptDisallowedScope.h"
45 #include "Settings.h"
46 #include "WebKitAnimationEvent.h"
47 #include "WebKitTransitionEvent.h"
48 #include <wtf/IsoMallocInlines.h>
49 #include <wtf/MainThread.h>
50 #include <wtf/NeverDestroyed.h>
51 #include <wtf/Ref.h>
52 #include <wtf/SetForScope.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/Vector.h>
55
56 namespace WebCore {
57
58 WTF_MAKE_ISO_ALLOCATED_IMPL(EventTarget);
59 WTF_MAKE_ISO_ALLOCATED_IMPL(EventTargetWithInlineData);
60
61 bool EventTarget::isNode() const
62 {
63     return false;
64 }
65
66 bool EventTarget::isPaymentRequest() const
67 {
68     return false;
69 }
70
71 bool EventTarget::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
72 {
73 #if !ASSERT_DISABLED
74     listener->checkValidityForEventTarget(*this);
75 #endif
76
77     auto passive = options.passive;
78
79     if (!passive.hasValue() && eventNames().isTouchScrollBlockingEventType(eventType)) {
80         if (is<DOMWindow>(*this)) {
81             auto& window = downcast<DOMWindow>(*this);
82             if (auto* document = window.document())
83                 passive = document->settings().passiveTouchListenersAsDefaultOnDocument();
84         } else if (is<Node>(*this)) {
85             auto& node = downcast<Node>(*this);
86             if (is<Document>(node) || node.document().documentElement() == &node || node.document().body() == &node)
87                 passive = node.document().settings().passiveTouchListenersAsDefaultOnDocument();
88         }
89     }
90
91     bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
92     auto listenerRef = listener.copyRef();
93
94     if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, passive.valueOr(false), options.once }))
95         return false;
96
97     if (listenerCreatedFromScript)
98         InspectorInstrumentation::didAddEventListener(*this, eventType, listenerRef.get(), options.capture);
99
100     return true;
101 }
102
103 void EventTarget::addEventListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant)
104 {
105     if (!listener)
106         return;
107
108     auto visitor = WTF::makeVisitor([&](const AddEventListenerOptions& options) {
109         addEventListener(eventType, listener.releaseNonNull(), options);
110     }, [&](bool capture) {
111         addEventListener(eventType, listener.releaseNonNull(), capture);
112     });
113
114     WTF::visit(visitor, variant);
115 }
116
117 void EventTarget::removeEventListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener, ListenerOptionsOrBoolean&& variant)
118 {
119     if (!listener)
120         return;
121
122     auto visitor = WTF::makeVisitor([&](const ListenerOptions& options) {
123         removeEventListener(eventType, *listener, options);
124     }, [&](bool capture) {
125         removeEventListener(eventType, *listener, capture);
126     });
127
128     WTF::visit(visitor, variant);
129 }
130
131 bool EventTarget::removeEventListener(const AtomString& eventType, EventListener& listener, const ListenerOptions& options)
132 {
133     auto* data = eventTargetData();
134     if (!data)
135         return false;
136
137     InspectorInstrumentation::willRemoveEventListener(*this, eventType, listener, options.capture);
138
139     return data->eventListenerMap.remove(eventType, listener, options.capture);
140 }
141
142 bool EventTarget::setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
143 {
144     auto* existingListener = attributeEventListener(eventType, isolatedWorld);
145     if (!listener) {
146         if (existingListener)
147             removeEventListener(eventType, *existingListener, false);
148         return false;
149     }
150     if (existingListener) {
151         InspectorInstrumentation::willRemoveEventListener(*this, eventType, *existingListener, false);
152
153 #if !ASSERT_DISABLED
154         listener->checkValidityForEventTarget(*this);
155 #endif
156
157         auto listenerPointer = listener.copyRef();
158         eventTargetData()->eventListenerMap.replace(eventType, *existingListener, listener.releaseNonNull(), { });
159
160         InspectorInstrumentation::didAddEventListener(*this, eventType, *listenerPointer, false);
161
162         return true;
163     }
164     return addEventListener(eventType, listener.releaseNonNull());
165 }
166
167 EventListener* EventTarget::attributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
168 {
169     for (auto& eventListener : eventListeners(eventType)) {
170         auto& listener = eventListener->callback();
171         if (!listener.isAttribute())
172             continue;
173
174         auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
175         if (&listenerWorld == &isolatedWorld)
176             return &listener;
177     }
178
179     return nullptr;
180 }
181
182 bool EventTarget::hasActiveEventListeners(const AtomString& eventType) const
183 {
184     auto* data = eventTargetData();
185     return data && data->eventListenerMap.containsActive(eventType);
186 }
187
188 ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event)
189 {
190     event.setUntrusted();
191
192     if (!event.isInitialized() || event.isBeingDispatched())
193         return Exception { InvalidStateError };
194
195     if (!scriptExecutionContext())
196         return false;
197
198     dispatchEvent(event);
199     return event.legacyReturnValue();
200 }
201
202 void EventTarget::dispatchEvent(Event& event)
203 {
204     // FIXME: We should always use EventDispatcher.
205     ASSERT(event.isInitialized());
206     ASSERT(!event.isBeingDispatched());
207
208     event.setTarget(this);
209     event.setCurrentTarget(this);
210     event.setEventPhase(Event::AT_TARGET);
211     event.resetBeforeDispatch();
212     fireEventListeners(event, EventInvokePhase::Capturing);
213     fireEventListeners(event, EventInvokePhase::Bubbling);
214     event.resetAfterDispatch();
215 }
216
217 void EventTarget::uncaughtExceptionInEventHandler()
218 {
219 }
220
221 static const AtomString& legacyType(const Event& event)
222 {
223     if (event.type() == eventNames().animationendEvent)
224         return eventNames().webkitAnimationEndEvent;
225
226     if (event.type() == eventNames().animationstartEvent)
227         return eventNames().webkitAnimationStartEvent;
228
229     if (event.type() == eventNames().animationiterationEvent)
230         return eventNames().webkitAnimationIterationEvent;
231
232     if (event.type() == eventNames().transitionendEvent)
233         return eventNames().webkitTransitionEndEvent;
234
235     // FIXME: This legacy name is not part of the specification (https://dom.spec.whatwg.org/#dispatching-events).
236     if (event.type() == eventNames().wheelEvent)
237         return eventNames().mousewheelEvent;
238
239     return nullAtom();
240 }
241
242 // https://dom.spec.whatwg.org/#concept-event-listener-invoke
243 void EventTarget::fireEventListeners(Event& event, EventInvokePhase phase)
244 {
245     ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
246     ASSERT(event.isInitialized());
247
248     auto* data = eventTargetData();
249     if (!data)
250         return;
251
252     SetForScope<bool> firingEventListenersScope(data->isFiringEventListeners, true);
253
254     if (auto* listenersVector = data->eventListenerMap.find(event.type())) {
255         innerInvokeEventListeners(event, *listenersVector, phase);
256         return;
257     }
258
259     // Only fall back to legacy types for trusted events.
260     if (!event.isTrusted())
261         return;
262
263     const AtomString& legacyTypeName = legacyType(event);
264     if (!legacyTypeName.isNull()) {
265         if (auto* legacyListenersVector = data->eventListenerMap.find(legacyTypeName)) {
266             AtomString typeName = event.type();
267             event.setType(legacyTypeName);
268             innerInvokeEventListeners(event, *legacyListenersVector, phase);
269             event.setType(typeName);
270         }
271     }
272 }
273
274 // Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run.
275 // Note that removal still has an effect due to the removed field in RegisteredEventListener.
276 // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
277 void EventTarget::innerInvokeEventListeners(Event& event, EventListenerVector listeners, EventInvokePhase phase)
278 {
279     Ref<EventTarget> protectedThis(*this);
280     ASSERT(!listeners.isEmpty());
281     ASSERT(scriptExecutionContext());
282
283     auto& context = *scriptExecutionContext();
284     bool contextIsDocument = is<Document>(context);
285     InspectorInstrumentationCookie willDispatchEventCookie;
286     if (contextIsDocument)
287         willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(downcast<Document>(context), event, true);
288
289     for (auto& registeredListener : listeners) {
290         if (UNLIKELY(registeredListener->wasRemoved()))
291             continue;
292
293         if (phase == EventInvokePhase::Capturing && !registeredListener->useCapture())
294             continue;
295         if (phase == EventInvokePhase::Bubbling && registeredListener->useCapture())
296             continue;
297
298         if (InspectorInstrumentation::isEventListenerDisabled(*this, event.type(), registeredListener->callback(), registeredListener->useCapture()))
299             continue;
300
301         // If stopImmediatePropagation has been called, we just break out immediately, without
302         // handling any more events on this target.
303         if (event.immediatePropagationStopped())
304             break;
305
306         // Do this before invocation to avoid reentrancy issues.
307         if (registeredListener->isOnce())
308             removeEventListener(event.type(), registeredListener->callback(), ListenerOptions(registeredListener->useCapture()));
309
310         if (registeredListener->isPassive())
311             event.setInPassiveListener(true);
312
313 #if !ASSERT_DISABLED
314         registeredListener->callback().checkValidityForEventTarget(*this);
315 #endif
316
317         InspectorInstrumentation::willHandleEvent(context, event, *registeredListener);
318         registeredListener->callback().handleEvent(context, event);
319         InspectorInstrumentation::didHandleEvent(context);
320
321         if (registeredListener->isPassive())
322             event.setInPassiveListener(false);
323     }
324
325     if (contextIsDocument)
326         InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie, event.defaultPrevented());
327 }
328
329 Vector<AtomString> EventTarget::eventTypes()
330 {
331     if (auto* data = eventTargetData())
332         return data->eventListenerMap.eventTypes();
333     return { };
334 }
335
336 const EventListenerVector& EventTarget::eventListeners(const AtomString& eventType)
337 {
338     auto* data = eventTargetData();
339     auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
340     static NeverDestroyed<EventListenerVector> emptyVector;
341     return listenerVector ? *listenerVector : emptyVector.get();
342 }
343
344 void EventTarget::removeAllEventListeners()
345 {
346     auto& threadData = threadGlobalData();
347     RELEASE_ASSERT(!threadData.isInRemoveAllEventListeners());
348
349     threadData.setIsInRemoveAllEventListeners(true);
350
351     auto* data = eventTargetData();
352     if (data)
353         data->eventListenerMap.clear();
354
355     threadData.setIsInRemoveAllEventListeners(false);
356 }
357
358 void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor)
359 {
360     EventTargetData* data = eventTargetDataConcurrently();
361     if (!data)
362         return;
363     
364     auto locker = holdLock(data->eventListenerMap.lock());
365     EventListenerIterator iterator(&data->eventListenerMap);
366     while (auto* listener = iterator.nextListener())
367         listener->visitJSFunction(visitor);
368 }
369
370 } // namespace WebCore