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>
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 COMPUTER, 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 COMPUTER, 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 "core/events/EventTarget.h"
35 #include "bindings/core/v8/ExceptionState.h"
36 #include "bindings/core/v8/V8DOMActivityLogger.h"
37 #include "core/dom/ExceptionCode.h"
38 #include "core/editing/Editor.h"
39 #include "core/events/Event.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "core/frame/LocalDOMWindow.h"
42 #include "core/frame/UseCounter.h"
43 #include "platform/EventDispatchForbiddenScope.h"
44 #include "platform/RuntimeEnabledFeatures.h"
45 #include "wtf/StdLibExtras.h"
46 #include "wtf/Threading.h"
47 #include "wtf/Vector.h"
53 EventTargetData::EventTargetData()
57 EventTargetData::~EventTargetData()
61 EventTarget::EventTarget()
65 EventTarget::~EventTarget()
69 Node* EventTarget::toNode()
74 LocalDOMWindow* EventTarget::toDOMWindow()
79 MessagePort* EventTarget::toMessagePort()
84 inline LocalDOMWindow* EventTarget::executingWindow()
86 if (ExecutionContext* context = executionContext())
87 return context->executingWindow();
91 bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
93 // FIXME: listener null check should throw TypeError (and be done in
94 // generated bindings), but breaks legacy content. http://crbug.com/249598
98 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
101 argv.append(toNode() ? toNode()->nodeName() : interfaceName());
102 argv.append(eventType);
103 activityLogger->logEvent("blinkAddEventListener", argv.size(), argv.data());
106 return ensureEventTargetData().eventListenerMap.add(eventType, listener, useCapture);
109 bool EventTarget::removeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
111 // FIXME: listener null check should throw TypeError (and be done in
112 // generated bindings), but breaks legacy content. http://crbug.com/249598
116 EventTargetData* d = eventTargetData();
120 size_t indexOfRemovedListener;
122 if (!d->eventListenerMap.remove(eventType, listener.get(), useCapture, indexOfRemovedListener))
125 // Notify firing events planning to invoke the listener at 'index' that
126 // they have one less listener to invoke.
127 if (!d->firingEventIterators)
129 for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
130 FiringEventIterator& firingIterator = d->firingEventIterators->at(i);
131 if (eventType != firingIterator.eventType)
134 if (indexOfRemovedListener >= firingIterator.end)
137 --firingIterator.end;
138 if (indexOfRemovedListener <= firingIterator.iterator)
139 --firingIterator.iterator;
145 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
147 clearAttributeEventListener(eventType);
150 return addEventListener(eventType, listener, false);
153 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
155 const EventListenerVector& entry = getEventListeners(eventType);
156 for (const auto& eventListener : entry) {
157 EventListener* listener = eventListener.listener.get();
158 if (listener->isAttribute() && listener->belongsToTheCurrentWorld())
164 bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
166 EventListener* listener = getAttributeEventListener(eventType);
169 return removeEventListener(eventType, listener, false);
172 bool EventTarget::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event, ExceptionState& exceptionState)
175 exceptionState.throwDOMException(InvalidStateError, "The event provided is null.");
178 if (event->type().isEmpty()) {
179 exceptionState.throwDOMException(InvalidStateError, "The event provided is uninitialized.");
182 if (event->isBeingDispatched()) {
183 exceptionState.throwDOMException(InvalidStateError, "The event is already being dispatched.");
187 if (!executionContext())
190 return dispatchEvent(event);
193 bool EventTarget::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
195 event->setTarget(this);
196 event->setCurrentTarget(this);
197 event->setEventPhase(Event::AT_TARGET);
198 bool defaultPrevented = fireEventListeners(event.get());
199 event->setEventPhase(0);
200 return defaultPrevented;
203 void EventTarget::uncaughtExceptionInEventHandler()
207 static const AtomicString& legacyType(const Event* event)
209 if (event->type() == EventTypeNames::transitionend)
210 return EventTypeNames::webkitTransitionEnd;
212 if (event->type() == EventTypeNames::animationstart)
213 return EventTypeNames::webkitAnimationStart;
215 if (event->type() == EventTypeNames::animationend)
216 return EventTypeNames::webkitAnimationEnd;
218 if (event->type() == EventTypeNames::animationiteration)
219 return EventTypeNames::webkitAnimationIteration;
221 if (event->type() == EventTypeNames::wheel)
222 return EventTypeNames::mousewheel;
227 void EventTarget::countLegacyEvents(const AtomicString& legacyTypeName, EventListenerVector* listenersVector, EventListenerVector* legacyListenersVector)
229 UseCounter::Feature unprefixedFeature;
230 UseCounter::Feature prefixedFeature;
231 UseCounter::Feature prefixedAndUnprefixedFeature;
232 if (legacyTypeName == EventTypeNames::webkitTransitionEnd) {
233 prefixedFeature = UseCounter::PrefixedTransitionEndEvent;
234 unprefixedFeature = UseCounter::UnprefixedTransitionEndEvent;
235 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedTransitionEndEvent;
236 } else if (legacyTypeName == EventTypeNames::webkitAnimationEnd) {
237 prefixedFeature = UseCounter::PrefixedAnimationEndEvent;
238 unprefixedFeature = UseCounter::UnprefixedAnimationEndEvent;
239 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationEndEvent;
240 } else if (legacyTypeName == EventTypeNames::webkitAnimationStart) {
241 prefixedFeature = UseCounter::PrefixedAnimationStartEvent;
242 unprefixedFeature = UseCounter::UnprefixedAnimationStartEvent;
243 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationStartEvent;
244 } else if (legacyTypeName == EventTypeNames::webkitAnimationIteration) {
245 prefixedFeature = UseCounter::PrefixedAnimationIterationEvent;
246 unprefixedFeature = UseCounter::UnprefixedAnimationIterationEvent;
247 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationIterationEvent;
252 if (LocalDOMWindow* executingWindow = this->executingWindow()) {
253 if (legacyListenersVector) {
255 UseCounter::count(executingWindow->document(), prefixedAndUnprefixedFeature);
257 UseCounter::count(executingWindow->document(), prefixedFeature);
258 } else if (listenersVector) {
259 UseCounter::count(executingWindow->document(), unprefixedFeature);
264 bool EventTarget::fireEventListeners(Event* event)
266 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
267 ASSERT(event && !event->type().isEmpty());
269 EventTargetData* d = eventTargetData();
273 EventListenerVector* legacyListenersVector = 0;
274 AtomicString legacyTypeName = legacyType(event);
275 if (!legacyTypeName.isEmpty())
276 legacyListenersVector = d->eventListenerMap.find(legacyTypeName);
278 EventListenerVector* listenersVector = d->eventListenerMap.find(event->type());
279 if (!RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled() && (event->type() == EventTypeNames::animationiteration || event->type() == EventTypeNames::animationend
280 || event->type() == EventTypeNames::animationstart)
281 // Some code out-there uses custom events to dispatch unprefixed animation events manually,
282 // we can safely remove all this block when cssAnimationUnprefixedEnabled is always on, this
283 // is really a special case. DO NOT ADD MORE EVENTS HERE.
284 && event->interfaceName() != EventNames::CustomEvent)
287 if (listenersVector) {
288 fireEventListeners(event, d, *listenersVector);
289 } else if (legacyListenersVector) {
290 AtomicString unprefixedTypeName = event->type();
291 event->setType(legacyTypeName);
292 fireEventListeners(event, d, *legacyListenersVector);
293 event->setType(unprefixedTypeName);
296 Editor::countEvent(executionContext(), event);
297 countLegacyEvents(legacyTypeName, listenersVector, legacyListenersVector);
298 return !event->defaultPrevented();
301 void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
303 RefPtrWillBeRawPtr<EventTarget> protect(this);
305 // Fire all listeners registered for this event. Don't fire listeners removed
306 // during event dispatch. Also, don't fire event listeners added during event
307 // dispatch. Conveniently, all new event listeners will be added after or at
308 // index |size|, so iterating up to (but not including) |size| naturally excludes
309 // new event listeners.
311 if (event->type() == EventTypeNames::beforeunload) {
312 if (LocalDOMWindow* executingWindow = this->executingWindow()) {
313 if (executingWindow->top())
314 UseCounter::count(executingWindow->document(), UseCounter::SubFrameBeforeUnloadFired);
315 UseCounter::count(executingWindow->document(), UseCounter::DocumentBeforeUnloadFired);
317 } else if (event->type() == EventTypeNames::unload) {
318 if (LocalDOMWindow* executingWindow = this->executingWindow())
319 UseCounter::count(executingWindow->document(), UseCounter::DocumentUnloadFired);
320 } else if (event->type() == EventTypeNames::DOMFocusIn || event->type() == EventTypeNames::DOMFocusOut) {
321 if (LocalDOMWindow* executingWindow = this->executingWindow())
322 UseCounter::count(executingWindow->document(), UseCounter::DOMFocusInOutEvent);
323 } else if (event->type() == EventTypeNames::focusin || event->type() == EventTypeNames::focusout) {
324 if (LocalDOMWindow* executingWindow = this->executingWindow())
325 UseCounter::count(executingWindow->document(), UseCounter::FocusInOutEvent);
329 size_t size = entry.size();
330 if (!d->firingEventIterators)
331 d->firingEventIterators = adoptPtr(new FiringEventIteratorVector);
332 d->firingEventIterators->append(FiringEventIterator(event->type(), i, size));
333 for ( ; i < size; ++i) {
334 RegisteredEventListener& registeredListener = entry[i];
335 if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
337 if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
340 // If stopImmediatePropagation has been called, we just break out immediately, without
341 // handling any more events on this target.
342 if (event->immediatePropagationStopped())
345 ExecutionContext* context = executionContext();
349 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(this, event, registeredListener.listener.get(), registeredListener.useCapture);
350 // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
351 // event listeners, even though that violates some versions of the DOM spec.
352 registeredListener.listener->handleEvent(context, event);
353 InspectorInstrumentation::didHandleEvent(cookie);
355 d->firingEventIterators->removeLast();
358 const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
360 AtomicallyInitializedStatic(EventListenerVector*, emptyVector = new EventListenerVector);
362 EventTargetData* d = eventTargetData();
366 EventListenerVector* listenerVector = d->eventListenerMap.find(eventType);
370 return *listenerVector;
373 Vector<AtomicString> EventTarget::eventTypes()
375 EventTargetData* d = eventTargetData();
376 return d ? d->eventListenerMap.eventTypes() : Vector<AtomicString>();
379 void EventTarget::removeAllEventListeners()
381 EventTargetData* d = eventTargetData();
384 d->eventListenerMap.clear();
386 // Notify firing events planning to invoke the listener at 'index' that
387 // they have one less listener to invoke.
388 if (d->firingEventIterators) {
389 for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
390 d->firingEventIterators->at(i).iterator = 0;
391 d->firingEventIterators->at(i).end = 0;