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>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
33 #include "EventTarget.h"
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"
43 #include "WebKitAnimationEvent.h"
44 #include "WebKitTransitionEvent.h"
45 #include <wtf/MainThread.h>
46 #include <wtf/NeverDestroyed.h>
48 #include <wtf/SetForScope.h>
49 #include <wtf/StdLibExtras.h>
50 #include <wtf/Vector.h>
56 bool EventTarget::isNode() const
61 bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
63 auto passive = options.passive;
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();
77 bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
78 auto listenerRef = listener.copyRef();
80 if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, passive.value_or(false), options.once }))
83 if (listenerCreatedFromScript)
84 InspectorInstrumentation::didAddEventListener(*this, eventType, listenerRef.get(), options.capture);
89 void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant)
94 auto visitor = WTF::makeVisitor([&](const AddEventListenerOptions& options) {
95 addEventListener(eventType, listener.releaseNonNull(), options);
96 }, [&](bool capture) {
97 addEventListener(eventType, listener.releaseNonNull(), capture);
100 WTF::visit(visitor, variant);
103 void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, ListenerOptionsOrBoolean&& variant)
108 auto visitor = WTF::makeVisitor([&](const ListenerOptions& options) {
109 removeEventListener(eventType, *listener, options);
110 }, [&](bool capture) {
111 removeEventListener(eventType, *listener, capture);
114 WTF::visit(visitor, variant);
117 bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
119 auto* data = eventTargetData();
123 InspectorInstrumentation::willRemoveEventListener(*this, eventType, listener, options.capture);
125 return data->eventListenerMap.remove(eventType, listener, options.capture);
128 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
130 auto* existingListener = attributeEventListener(eventType, isolatedWorld);
132 if (existingListener)
133 removeEventListener(eventType, *existingListener, false);
136 if (existingListener) {
137 InspectorInstrumentation::willRemoveEventListener(*this, eventType, *existingListener, false);
139 auto listenerPointer = listener.copyRef();
140 eventTargetData()->eventListenerMap.replace(eventType, *existingListener, listener.releaseNonNull(), { });
142 InspectorInstrumentation::didAddEventListener(*this, eventType, *listenerPointer, false);
146 return addEventListener(eventType, listener.releaseNonNull());
149 EventListener* EventTarget::attributeEventListener(const AtomicString& eventType, DOMWrapperWorld& isolatedWorld)
151 for (auto& eventListener : eventListeners(eventType)) {
152 auto& listener = eventListener->callback();
153 if (!listener.isAttribute())
156 auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
157 if (&listenerWorld == &isolatedWorld)
164 bool EventTarget::hasActiveEventListeners(const AtomicString& eventType) const
166 auto* data = eventTargetData();
167 return data && data->eventListenerMap.containsActive(eventType);
170 ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event)
172 event.setUntrusted();
174 if (!event.isInitialized() || event.isBeingDispatched())
175 return Exception { InvalidStateError };
177 if (!scriptExecutionContext())
180 dispatchEvent(event);
181 return event.legacyReturnValue();
184 void EventTarget::dispatchEvent(Event& event)
186 ASSERT(event.isInitialized());
187 ASSERT(!event.isBeingDispatched());
189 event.setTarget(this);
190 event.setCurrentTarget(this);
191 event.setEventPhase(Event::AT_TARGET);
192 event.resetBeforeDispatch();
193 fireEventListeners(event);
194 event.resetAfterDispatch();
197 void EventTarget::uncaughtExceptionInEventHandler()
201 static const AtomicString& legacyType(const Event& event)
203 if (event.type() == eventNames().animationendEvent)
204 return eventNames().webkitAnimationEndEvent;
206 if (event.type() == eventNames().animationstartEvent)
207 return eventNames().webkitAnimationStartEvent;
209 if (event.type() == eventNames().animationiterationEvent)
210 return eventNames().webkitAnimationIterationEvent;
212 if (event.type() == eventNames().transitionendEvent)
213 return eventNames().webkitTransitionEndEvent;
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;
222 void EventTarget::fireEventListeners(Event& event)
224 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
225 ASSERT(event.isInitialized());
227 auto* data = eventTargetData();
231 SetForScope<bool> firingEventListenersScope(data->isFiringEventListeners, true);
233 if (auto* listenersVector = data->eventListenerMap.find(event.type())) {
234 fireEventListeners(event, *listenersVector);
238 // Only fall back to legacy types for trusted events.
239 if (!event.isTrusted())
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);
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)
257 Ref<EventTarget> protectedThis(*this);
258 ASSERT(!listeners.isEmpty());
259 ASSERT(scriptExecutionContext());
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);
267 for (auto& registeredListener : listeners) {
268 if (UNLIKELY(registeredListener->wasRemoved()))
271 if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener->useCapture())
273 if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener->useCapture())
276 if (InspectorInstrumentation::isEventListenerDisabled(*this, event.type(), registeredListener->callback(), registeredListener->useCapture()))
279 // If stopImmediatePropagation has been called, we just break out immediately, without
280 // handling any more events on this target.
281 if (event.immediatePropagationStopped())
284 // Do this before invocation to avoid reentrancy issues.
285 if (registeredListener->isOnce())
286 removeEventListener(event.type(), registeredListener->callback(), ListenerOptions(registeredListener->useCapture()));
288 if (registeredListener->isPassive())
289 event.setInPassiveListener(true);
291 InspectorInstrumentation::willHandleEvent(context, event, *registeredListener);
292 registeredListener->callback().handleEvent(context, event);
293 InspectorInstrumentation::didHandleEvent(context);
295 if (registeredListener->isPassive())
296 event.setInPassiveListener(false);
299 if (contextIsDocument)
300 InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
303 const EventListenerVector& EventTarget::eventListeners(const AtomicString& eventType)
305 auto* data = eventTargetData();
306 auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
307 static NeverDestroyed<EventListenerVector> emptyVector;
308 return listenerVector ? *listenerVector : emptyVector.get();
311 void EventTarget::removeAllEventListeners()
313 auto& threadData = threadGlobalData();
314 RELEASE_ASSERT(!threadData.isInRemoveAllEventListeners());
316 threadData.setIsInRemoveAllEventListeners(true);
318 auto* data = eventTargetData();
320 data->eventListenerMap.clear();
322 threadData.setIsInRemoveAllEventListeners(false);
325 void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor)
327 EventTargetData* data = eventTargetDataConcurrently();
331 auto locker = holdLock(data->eventListenerMap.lock());
332 EventListenerIterator iterator(&data->eventListenerMap);
333 while (auto* listener = iterator.nextListener())
334 listener->visitJSFunction(visitor);
337 } // namespace WebCore