ec2109867ac30d0c31be69261631c32516fea3ce
[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, RefPtr<EventListener>&& listener, bool useCapture)
79 {
80     return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), useCapture);
81 }
82
83 bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
84 {
85     EventTargetData* d = eventTargetData();
86     if (!d)
87         return false;
88
89     size_t indexOfRemovedListener;
90
91     if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener))
92         return false;
93
94     // Notify firing events planning to invoke the listener at 'index' that
95     // they have one less listener to invoke.
96     if (!d->firingEventIterators)
97         return true;
98     for (auto& firingIterator : *d->firingEventIterators) {
99         if (eventType != firingIterator.eventType)
100             continue;
101
102         if (indexOfRemovedListener >= firingIterator.size)
103             continue;
104
105         --firingIterator.size;
106         if (indexOfRemovedListener <= firingIterator.iterator)
107             --firingIterator.iterator;
108     }
109
110     return true;
111 }
112
113 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
114 {
115     clearAttributeEventListener(eventType);
116     if (!listener)
117         return false;
118     return addEventListener(eventType, listener, false);
119 }
120
121 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
122 {
123     for (auto& eventListener : getEventListeners(eventType)) {
124         if (eventListener.listener->isAttribute())
125             return eventListener.listener.get();
126     }
127     return 0;
128 }
129
130 bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
131 {
132     EventListener* listener = getAttributeEventListener(eventType);
133     if (!listener)
134         return false;
135     return removeEventListener(eventType, listener, false);
136 }
137
138 bool EventTarget::dispatchEventForBindings(Event* event, ExceptionCode& ec)
139 {
140     if (!event) {
141         ec = TypeError;
142         return false;
143     }
144
145     event->setUntrusted();
146
147     if (!event->isInitialized() || event->isBeingDispatched()) {
148         ec = INVALID_STATE_ERR;
149         return false;
150     }
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.setEventPhase(0);
168     return defaultPrevented;
169 }
170
171 void EventTarget::uncaughtExceptionInEventHandler()
172 {
173 }
174
175 static const AtomicString& legacyType(const Event& event)
176 {
177     if (event.type() == eventNames().animationendEvent)
178         return eventNames().webkitAnimationEndEvent;
179
180     if (event.type() == eventNames().animationstartEvent)
181         return eventNames().webkitAnimationStartEvent;
182
183     if (event.type() == eventNames().animationiterationEvent)
184         return eventNames().webkitAnimationIterationEvent;
185
186     if (event.type() == eventNames().transitionendEvent)
187         return eventNames().webkitTransitionEndEvent;
188
189     if (event.type() == eventNames().wheelEvent)
190         return eventNames().mousewheelEvent;
191
192     return emptyAtom;
193 }
194
195 bool EventTarget::fireEventListeners(Event& event)
196 {
197     ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
198     ASSERT(event.isInitialized());
199
200     EventTargetData* d = eventTargetData();
201     if (!d)
202         return true;
203
204     EventListenerVector* legacyListenersVector = nullptr;
205     const AtomicString& legacyTypeName = legacyType(event);
206     if (!legacyTypeName.isEmpty())
207         legacyListenersVector = d->eventListenerMap.find(legacyTypeName);
208
209     EventListenerVector* listenersVector = d->eventListenerMap.find(event.type());
210
211     if (listenersVector)
212         fireEventListeners(event, d, *listenersVector);
213     else if (legacyListenersVector) {
214         AtomicString typeName = event.type();
215         event.setType(legacyTypeName);
216         fireEventListeners(event, d, *legacyListenersVector);
217         event.setType(typeName);
218     }
219
220     return !event.defaultPrevented();
221 }
222         
223 void EventTarget::fireEventListeners(Event& event, EventTargetData* d, EventListenerVector& entry)
224 {
225     Ref<EventTarget> protectedThis(*this);
226
227     // Fire all listeners registered for this event. Don't fire listeners removed during event dispatch.
228     // Also, don't fire event listeners added during event dispatch. Conveniently, all new event listeners will be added
229     // after or at index |size|, so iterating up to (but not including) |size| naturally excludes new event listeners.
230
231     size_t i = 0;
232     size_t size = entry.size();
233     if (!d->firingEventIterators)
234         d->firingEventIterators = std::make_unique<FiringEventIteratorVector>();
235     d->firingEventIterators->append(FiringEventIterator(event.type(), i, size));
236
237     ScriptExecutionContext* context = scriptExecutionContext();
238     Document* document = nullptr;
239     InspectorInstrumentationCookie willDispatchEventCookie;
240     if (is<Document>(context)) {
241         document = downcast<Document>(context);
242         willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(*document, event, size > 0);
243     }
244
245     for (; i < size; ++i) {
246         RegisteredEventListener& registeredListener = entry[i];
247         if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
248             continue;
249         if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
250             continue;
251
252         // If stopImmediatePropagation has been called, we just break out immediately, without
253         // handling any more events on this target.
254         if (event.immediatePropagationStopped())
255             break;
256
257         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
258         // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
259         // event listeners, even though that violates some versions of the DOM spec.
260         registeredListener.listener->handleEvent(context, &event);
261         InspectorInstrumentation::didHandleEvent(cookie);
262     }
263     d->firingEventIterators->removeLast();
264
265     if (document)
266         InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
267 }
268
269 const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
270 {
271     auto* data = eventTargetData();
272     auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
273     static NeverDestroyed<EventListenerVector> emptyVector;
274     return listenerVector ? *listenerVector : emptyVector.get();
275 }
276
277 void EventTarget::removeAllEventListeners()
278 {
279     EventTargetData* d = eventTargetData();
280     if (!d)
281         return;
282     d->eventListenerMap.clear();
283
284     // Notify firing events planning to invoke the listener at 'index' that
285     // they have one less listener to invoke.
286     if (d->firingEventIterators) {
287         for (auto& firingEventIterator : *d->firingEventIterators) {
288             firingEventIterator.iterator = 0;
289             firingEventIterator.size = 0;
290         }
291     }
292 }
293
294 } // namespace WebCore