summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/EventTarget.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/EventTarget.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/dom/EventTarget.cpp')
-rw-r--r--Source/WebCore/dom/EventTarget.cpp357
1 files changed, 165 insertions, 192 deletions
diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp
index d1a2e0cec..f18722904 100644
--- a/Source/WebCore/dom/EventTarget.cpp
+++ b/Source/WebCore/dom/EventTarget.cpp
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
* Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
* (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
*
@@ -15,10 +15,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -32,12 +32,19 @@
#include "config.h"
#include "EventTarget.h"
-#include "EventException.h"
+#include "DOMWrapperWorld.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
#include "InspectorInstrumentation.h"
+#include "JSEventListener.h"
+#include "NoEventDispatchAssertion.h"
#include "ScriptController.h"
+#include "WebKitAnimationEvent.h"
#include "WebKitTransitionEvent.h"
#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/Ref.h>
+#include <wtf/SetForScope.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
@@ -45,26 +52,14 @@ using namespace WTF;
namespace WebCore {
-EventTargetData::EventTargetData()
-{
-}
-
-EventTargetData::~EventTargetData()
-{
-}
-
-EventTarget::~EventTarget()
-{
-}
-
Node* EventTarget::toNode()
{
- return 0;
+ return nullptr;
}
DOMWindow* EventTarget::toDOMWindow()
{
- return 0;
+ return nullptr;
}
bool EventTarget::isMessagePort() const
@@ -72,79 +67,87 @@ bool EventTarget::isMessagePort() const
return false;
}
-bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
+bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
- return ensureEventTargetData().eventListenerMap.add(eventType, listener, useCapture);
+ return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
}
-bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
+void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant)
{
- EventTargetData* d = eventTargetData();
- if (!d)
- return false;
+ if (!listener)
+ return;
- size_t indexOfRemovedListener;
+ auto visitor = WTF::makeVisitor([&](const AddEventListenerOptions& options) {
+ addEventListener(eventType, listener.releaseNonNull(), options);
+ }, [&](bool capture) {
+ addEventListener(eventType, listener.releaseNonNull(), capture);
+ });
- if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener))
- return false;
+ WTF::visit(visitor, variant);
+}
- // Notify firing events planning to invoke the listener at 'index' that
- // they have one less listener to invoke.
- if (!d->firingEventIterators)
- return true;
- for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
- FiringEventIterator& firingIterator = d->firingEventIterators->at(i);
- if (eventType != firingIterator.eventType)
- continue;
+void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, ListenerOptionsOrBoolean&& variant)
+{
+ if (!listener)
+ return;
- if (indexOfRemovedListener >= firingIterator.size)
- continue;
+ auto visitor = WTF::makeVisitor([&](const ListenerOptions& options) {
+ removeEventListener(eventType, *listener, options);
+ }, [&](bool capture) {
+ removeEventListener(eventType, *listener, capture);
+ });
- --firingIterator.size;
- if (indexOfRemovedListener <= firingIterator.iterator)
- --firingIterator.iterator;
- }
+ WTF::visit(visitor, variant);
+}
- return true;
+bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
+{
+ auto* data = eventTargetData();
+ return data && data->eventListenerMap.remove(eventType, listener, options.capture);
}
-bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
+bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
{
- clearAttributeEventListener(eventType);
- if (!listener)
+ auto* existingListener = attributeEventListener(eventType, isolatedWorld);
+ if (!listener) {
+ if (existingListener)
+ removeEventListener(eventType, *existingListener, false);
return false;
- return addEventListener(eventType, listener, false);
+ }
+ if (existingListener) {
+ eventTargetData()->eventListenerMap.replace(eventType, *existingListener, listener.releaseNonNull(), { });
+ return true;
+ }
+ return addEventListener(eventType, listener.releaseNonNull());
}
-EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
+EventListener* EventTarget::attributeEventListener(const AtomicString& eventType, DOMWrapperWorld& isolatedWorld)
{
- const EventListenerVector& entry = getEventListeners(eventType);
- for (size_t i = 0; i < entry.size(); ++i) {
- if (entry[i].listener->isAttribute())
- return entry[i].listener.get();
+ for (auto& eventListener : eventListeners(eventType)) {
+ auto& listener = eventListener->callback();
+ if (!listener.isAttribute())
+ continue;
+
+ auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
+ if (&listenerWorld == &isolatedWorld)
+ return &listener;
}
- return 0;
+
+ return nullptr;
}
-bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
+bool EventTarget::hasActiveEventListeners(const AtomicString& eventType) const
{
- EventListener* listener = getAttributeEventListener(eventType);
- if (!listener)
- return false;
- return removeEventListener(eventType, listener, false);
+ auto* data = eventTargetData();
+ return data && data->eventListenerMap.containsActive(eventType);
}
-bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
+ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event)
{
- if (!event || event->type().isEmpty()) {
- ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
- return false;
- }
+ event.setUntrusted();
- if (event->isBeingDispatched()) {
- ec = EventException::DISPATCH_REQUEST_ERR;
- return false;
- }
+ if (!event.isInitialized() || event.isBeingDispatched())
+ return Exception { INVALID_STATE_ERR };
if (!scriptExecutionContext())
return false;
@@ -152,13 +155,17 @@ bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
return dispatchEvent(event);
}
-bool EventTarget::dispatchEvent(PassRefPtr<Event> event)
+bool EventTarget::dispatchEvent(Event& event)
{
- event->setTarget(this);
- event->setCurrentTarget(this);
- event->setEventPhase(Event::AT_TARGET);
- bool defaultPrevented = fireEventListeners(event.get());
- event->setEventPhase(0);
+ ASSERT(event.isInitialized());
+ ASSERT(!event.isBeingDispatched());
+
+ event.setTarget(this);
+ event.setCurrentTarget(this);
+ event.setEventPhase(Event::AT_TARGET);
+ bool defaultPrevented = fireEventListeners(event);
+ event.resetPropagationFlags();
+ event.setEventPhase(Event::NONE);
return defaultPrevented;
}
@@ -166,164 +173,130 @@ void EventTarget::uncaughtExceptionInEventHandler()
{
}
-static const AtomicString& legacyType(const Event* event)
+static const AtomicString& legacyType(const Event& event)
{
- if (event->type() == eventNames().transitionendEvent)
- return eventNames().webkitTransitionEndEvent;
+ if (event.type() == eventNames().animationendEvent)
+ return eventNames().webkitAnimationEndEvent;
- if (event->type() == eventNames().wheelEvent)
- return eventNames().mousewheelEvent;
+ if (event.type() == eventNames().animationstartEvent)
+ return eventNames().webkitAnimationStartEvent;
- return emptyAtom;
-}
-
-static inline bool shouldObserveLegacyType(const AtomicString& legacyTypeName, bool hasLegacyTypeListeners, bool hasNewTypeListeners, FeatureObserver::Feature& feature)
-{
- if (legacyTypeName == eventNames().webkitTransitionEndEvent) {
- if (hasLegacyTypeListeners) {
- if (hasNewTypeListeners)
- feature = FeatureObserver::PrefixedAndUnprefixedTransitionEndEvent;
- else
- feature = FeatureObserver::PrefixedTransitionEndEvent;
- } else {
- ASSERT(hasNewTypeListeners);
- feature = FeatureObserver::UnprefixedTransitionEndEvent;
- }
- return true;
- }
- return false;
-}
-
-void EventTarget::setupLegacyTypeObserverIfNeeded(const AtomicString& legacyTypeName, bool hasLegacyTypeListeners, bool hasNewTypeListeners)
-{
- ASSERT(!legacyTypeName.isEmpty());
- ASSERT(hasLegacyTypeListeners || hasNewTypeListeners);
+ if (event.type() == eventNames().animationiterationEvent)
+ return eventNames().webkitAnimationIterationEvent;
- ScriptExecutionContext* context = scriptExecutionContext();
- if (!context || !context->isDocument())
- return;
+ if (event.type() == eventNames().transitionendEvent)
+ return eventNames().webkitTransitionEndEvent;
- Document* document = toDocument(context);
- if (!document->domWindow())
- return;
+ // FIXME: This legacy name is not part of the specification (https://dom.spec.whatwg.org/#dispatching-events).
+ if (event.type() == eventNames().wheelEvent)
+ return eventNames().mousewheelEvent;
- FeatureObserver::Feature feature;
- if (shouldObserveLegacyType(legacyTypeName, hasLegacyTypeListeners, hasNewTypeListeners, feature))
- FeatureObserver::observe(document->domWindow(), feature);
+ return nullAtom;
}
-bool EventTarget::fireEventListeners(Event* event)
+bool EventTarget::fireEventListeners(Event& event)
{
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
- ASSERT(event && !event->type().isEmpty());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
+ ASSERT(event.isInitialized());
- EventTargetData* d = eventTargetData();
- if (!d)
+ auto* data = eventTargetData();
+ if (!data)
return true;
- EventListenerVector* legacyListenersVector = 0;
- const AtomicString& legacyTypeName = legacyType(event);
- if (!legacyTypeName.isEmpty())
- legacyListenersVector = d->eventListenerMap.find(legacyTypeName);
-
- EventListenerVector* listenersVector = d->eventListenerMap.find(event->type());
-
- if (listenersVector)
- fireEventListeners(event, d, *listenersVector);
- else if (legacyListenersVector) {
- AtomicString typeName = event->type();
- event->setType(legacyTypeName);
- fireEventListeners(event, d, *legacyListenersVector);
- event->setType(typeName);
+ SetForScope<bool> firingEventListenersScope(data->isFiringEventListeners, true);
+
+ if (auto* listenersVector = data->eventListenerMap.find(event.type())) {
+ fireEventListeners(event, *listenersVector);
+ return !event.defaultPrevented();
}
- if (!legacyTypeName.isEmpty() && (legacyListenersVector || listenersVector))
- setupLegacyTypeObserverIfNeeded(legacyTypeName, !!legacyListenersVector, !!listenersVector);
+ // Only fall back to legacy types for trusted events.
+ if (!event.isTrusted())
+ return !event.defaultPrevented();
- return !event->defaultPrevented();
+ const AtomicString& legacyTypeName = legacyType(event);
+ if (!legacyTypeName.isNull()) {
+ if (auto* legacyListenersVector = data->eventListenerMap.find(legacyTypeName)) {
+ AtomicString typeName = event.type();
+ event.setType(legacyTypeName);
+ fireEventListeners(event, *legacyListenersVector);
+ event.setType(typeName);
+ }
+ }
+ return !event.defaultPrevented();
}
-
-void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
-{
- Ref<EventTarget> protect(*this);
-
- // Fire all listeners registered for this event. Don't fire listeners removed during event dispatch.
- // Also, don't fire event listeners added during event dispatch. Conveniently, all new event listeners will be added
- // after or at index |size|, so iterating up to (but not including) |size| naturally excludes new event listeners.
- bool userEventWasHandled = false;
- size_t i = 0;
- size_t size = entry.size();
- if (!d->firingEventIterators)
- d->firingEventIterators = adoptPtr(new FiringEventIteratorVector);
- d->firingEventIterators->append(FiringEventIterator(event->type(), i, size));
+// Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run.
+// Note that removal still has an effect due to the removed field in RegisteredEventListener.
+void EventTarget::fireEventListeners(Event& event, EventListenerVector listeners)
+{
+ Ref<EventTarget> protectedThis(*this);
+ ASSERT(!listeners.isEmpty());
- ScriptExecutionContext* context = scriptExecutionContext();
- Document* document = nullptr;
+ auto* context = scriptExecutionContext();
+ bool contextIsDocument = is<Document>(context);
InspectorInstrumentationCookie willDispatchEventCookie;
- if (context && context->isDocument()) {
- document = toDocument(context);
- willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(document, *event, size > 0);
- }
+ if (contextIsDocument)
+ willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(downcast<Document>(*context), event, true);
- for (; i < size; ++i) {
- RegisteredEventListener& registeredListener = entry[i];
- if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
+ for (auto& registeredListener : listeners) {
+ if (UNLIKELY(registeredListener->wasRemoved()))
continue;
- if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
+
+ if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener->useCapture())
+ continue;
+ if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener->useCapture())
continue;
// If stopImmediatePropagation has been called, we just break out immediately, without
// handling any more events on this target.
- if (event->immediatePropagationStopped())
+ if (event.immediatePropagationStopped())
break;
- InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
- // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
- // event listeners, even though that violates some versions of the DOM spec.
- registeredListener.listener->handleEvent(context, event);
- if (!userEventWasHandled && ScriptController::processingUserGesture())
- userEventWasHandled = true;
- InspectorInstrumentation::didHandleEvent(cookie);
+ // Do this before invocation to avoid reentrancy issues.
+ if (registeredListener->isOnce())
+ removeEventListener(event.type(), registeredListener->callback(), ListenerOptions(registeredListener->useCapture()));
+
+ if (registeredListener->isPassive())
+ event.setInPassiveListener(true);
+
+ InspectorInstrumentation::willHandleEvent(context, event);
+ registeredListener->callback().handleEvent(context, &event);
+
+ if (registeredListener->isPassive())
+ event.setInPassiveListener(false);
}
- d->firingEventIterators->removeLast();
- if (userEventWasHandled && document)
- document->resetLastHandledUserGestureTimestamp();
- if (document)
+ if (contextIsDocument)
InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
}
-const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
+const EventListenerVector& EventTarget::eventListeners(const AtomicString& eventType)
{
- DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ());
-
- EventTargetData* d = eventTargetData();
- if (!d)
- return emptyVector;
-
- EventListenerVector* listenerVector = d->eventListenerMap.find(eventType);
- if (!listenerVector)
- return emptyVector;
-
- return *listenerVector;
+ auto* data = eventTargetData();
+ auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
+ static NeverDestroyed<EventListenerVector> emptyVector;
+ return listenerVector ? *listenerVector : emptyVector.get();
}
void EventTarget::removeAllEventListeners()
{
- EventTargetData* d = eventTargetData();
- if (!d)
+ auto* data = eventTargetData();
+ if (!data)
return;
- d->eventListenerMap.clear();
-
- // Notify firing events planning to invoke the listener at 'index' that
- // they have one less listener to invoke.
- if (d->firingEventIterators) {
- for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
- d->firingEventIterators->at(i).iterator = 0;
- d->firingEventIterators->at(i).size = 0;
- }
- }
+ data->eventListenerMap.clear();
+}
+
+void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor)
+{
+ EventTargetData* data = eventTargetDataConcurrently();
+ if (!data)
+ return;
+
+ auto locker = holdLock(data->eventListenerMap.lock());
+ EventListenerIterator iterator(&data->eventListenerMap);
+ while (auto* listener = iterator.nextListener())
+ listener->visitJSFunction(visitor);
}
} // namespace WebCore