Implement EventListenerOptions argument to addEventListener
[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, 2005, 2006, 2007 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 "ExceptionCode.h"
36 #include "InspectorInstrumentation.h"
37 #include "NoEventDispatchAssertion.h"
38 #include "ScriptController.h"
39 #include "WebKitAnimationEvent.h"
40 #include "WebKitTransitionEvent.h"
41 #include <wtf/MainThread.h>
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/Ref.h>
44 #include <wtf/StdLibExtras.h>
45 #include <wtf/Vector.h>
46
47 using namespace WTF;
48
49 namespace WebCore {
50
51 EventTargetData::EventTargetData()
52 {
53 }
54
55 EventTargetData::~EventTargetData()
56 {
57 }
58
59 EventTarget::~EventTarget()
60 {
61 }
62
63 Node* EventTarget::toNode()
64 {
65     return 0;
66 }
67
68 DOMWindow* EventTarget::toDOMWindow()
69 {
70     return 0;
71 }
72
73 bool EventTarget::isMessagePort() const
74 {
75     return false;
76 }
77
78 bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
79 {
80     return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
81 }
82
83 void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const AddEventListenerOptions& options)
84 {
85     if (!listener)
86         return;
87     addEventListener(eventType, listener.releaseNonNull(), options);
88 }
89
90 void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const ListenerOptions& options)
91 {
92     if (!listener)
93         return;
94     removeEventListener(eventType, *listener, options);
95 }
96
97 bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
98 {
99     EventTargetData* d = eventTargetData();
100     if (!d)
101         return false;
102
103     size_t indexOfRemovedListener;
104
105     if (!d->eventListenerMap.remove(eventType, listener, options.capture, indexOfRemovedListener))
106         return false;
107
108     // Notify firing events planning to invoke the listener at 'index' that
109     // they have one less listener to invoke.
110     if (!d->firingEventIterators)
111         return true;
112     for (auto& firingIterator : *d->firingEventIterators) {
113         if (eventType != firingIterator.eventType)
114             continue;
115
116         if (indexOfRemovedListener >= firingIterator.size)
117             continue;
118
119         --firingIterator.size;
120         if (indexOfRemovedListener <= firingIterator.iterator)
121             --firingIterator.iterator;
122     }
123
124     return true;
125 }
126
127 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener)
128 {
129     clearAttributeEventListener(eventType);
130     if (!listener)
131         return false;
132     return addEventListener(eventType, listener.releaseNonNull());
133 }
134
135 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
136 {
137     for (auto& eventListener : getEventListeners(eventType)) {
138         if (eventListener.listener->isAttribute())
139             return eventListener.listener.get();
140     }
141     return 0;
142 }
143
144 bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
145 {
146     EventListener* listener = getAttributeEventListener(eventType);
147     if (!listener)
148         return false;
149     return removeEventListener(eventType, *listener, false);
150 }
151
152 bool EventTarget::dispatchEventForBindings(Event* event, ExceptionCode& ec)
153 {
154     if (!event) {
155         ec = TypeError;
156         return false;
157     }
158
159     event->setUntrusted();
160
161     if (!event->isInitialized() || event->isBeingDispatched()) {
162         ec = INVALID_STATE_ERR;
163         return false;
164     }
165
166     if (!scriptExecutionContext())
167         return false;
168
169     return dispatchEvent(*event);
170 }
171
172 bool EventTarget::dispatchEvent(Event& event)
173 {
174     ASSERT(event.isInitialized());
175     ASSERT(!event.isBeingDispatched());
176
177     event.setTarget(this);
178     event.setCurrentTarget(this);
179     event.setEventPhase(Event::AT_TARGET);
180     bool defaultPrevented = fireEventListeners(event);
181     event.setEventPhase(0);
182     return defaultPrevented;
183 }
184
185 void EventTarget::uncaughtExceptionInEventHandler()
186 {
187 }
188
189 static const AtomicString& legacyType(const Event& event)
190 {
191     if (event.type() == eventNames().animationendEvent)
192         return eventNames().webkitAnimationEndEvent;
193
194     if (event.type() == eventNames().animationstartEvent)
195         return eventNames().webkitAnimationStartEvent;
196
197     if (event.type() == eventNames().animationiterationEvent)
198         return eventNames().webkitAnimationIterationEvent;
199
200     if (event.type() == eventNames().transitionendEvent)
201         return eventNames().webkitTransitionEndEvent;
202
203     if (event.type() == eventNames().wheelEvent)
204         return eventNames().mousewheelEvent;
205
206     return emptyAtom;
207 }
208
209 bool EventTarget::fireEventListeners(Event& event)
210 {
211     ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
212     ASSERT(event.isInitialized());
213
214     EventTargetData* d = eventTargetData();
215     if (!d)
216         return true;
217
218     EventListenerVector* legacyListenersVector = nullptr;
219     const AtomicString& legacyTypeName = legacyType(event);
220     if (!legacyTypeName.isEmpty())
221         legacyListenersVector = d->eventListenerMap.find(legacyTypeName);
222
223     EventListenerVector* listenersVector = d->eventListenerMap.find(event.type());
224
225     if (listenersVector)
226         fireEventListeners(event, d, *listenersVector);
227     else if (legacyListenersVector) {
228         AtomicString typeName = event.type();
229         event.setType(legacyTypeName);
230         fireEventListeners(event, d, *legacyListenersVector);
231         event.setType(typeName);
232     }
233
234     return !event.defaultPrevented();
235 }
236         
237 void EventTarget::fireEventListeners(Event& event, EventTargetData* d, EventListenerVector& entry)
238 {
239     Ref<EventTarget> protectedThis(*this);
240
241     // Fire all listeners registered for this event. Don't fire listeners removed during event dispatch.
242     // Also, don't fire event listeners added during event dispatch. Conveniently, all new event listeners will be added
243     // after or at index |size|, so iterating up to (but not including) |size| naturally excludes new event listeners.
244
245     size_t i = 0;
246     size_t size = entry.size();
247     if (!d->firingEventIterators)
248         d->firingEventIterators = std::make_unique<FiringEventIteratorVector>();
249     d->firingEventIterators->append(FiringEventIterator(event.type(), i, size));
250
251     ScriptExecutionContext* context = scriptExecutionContext();
252     Document* document = nullptr;
253     InspectorInstrumentationCookie willDispatchEventCookie;
254     if (is<Document>(context)) {
255         document = downcast<Document>(context);
256         willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(*document, event, size > 0);
257     }
258
259     for (; i < size; ++i) {
260         RegisteredEventListener& registeredListener = entry[i];
261
262         if (registeredListener.isMarkedForRemoval)
263             continue;
264
265         if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
266             continue;
267         if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
268             continue;
269
270         // If stopImmediatePropagation has been called, we just break out immediately, without
271         // handling any more events on this target.
272         if (event.immediatePropagationStopped())
273             break;
274
275         if (registeredListener.isPassive)
276             event.setInPassiveListener(true);
277
278         // Mark listener for removal before executing the listener, in case the listener tries to
279         // dispatch an event that would cause it to get executed again.
280         if (registeredListener.isOnce)
281             registeredListener.isMarkedForRemoval = true;
282
283         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
284         // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
285         // event listeners, even though that violates some versions of the DOM spec.
286         registeredListener.listener->handleEvent(context, &event);
287         InspectorInstrumentation::didHandleEvent(cookie);
288
289         if (registeredListener.isPassive)
290             event.setInPassiveListener(false);
291
292         if (registeredListener.isOnce)
293             removeEventListener(event.type(), *registeredListener.listener, ListenerOptions(registeredListener.useCapture));
294     }
295
296     d->firingEventIterators->removeLast();
297
298     if (document)
299         InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
300 }
301
302 const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
303 {
304     auto* data = eventTargetData();
305     auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
306     static NeverDestroyed<EventListenerVector> emptyVector;
307     return listenerVector ? *listenerVector : emptyVector.get();
308 }
309
310 void EventTarget::removeAllEventListeners()
311 {
312     EventTargetData* d = eventTargetData();
313     if (!d)
314         return;
315     d->eventListenerMap.clear();
316
317     // Notify firing events planning to invoke the listener at 'index' that
318     // they have one less listener to invoke.
319     if (d->firingEventIterators) {
320         for (auto& firingEventIterator : *d->firingEventIterators) {
321             firingEventIterator.iterator = 0;
322             firingEventIterator.size = 0;
323         }
324     }
325 }
326
327 } // namespace WebCore