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