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