#include "config.h"
#include "EventTarget.h"
#include "EventNames.h"
#include "ExceptionCode.h"
#include "InspectorInstrumentation.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/StdLibExtras.h>
#include <wtf/Vector.h>
using namespace WTF;
namespace WebCore {
EventTargetData::EventTargetData()
{
}
EventTargetData::~EventTargetData()
{
}
EventTarget::~EventTarget()
{
}
Node* EventTarget::toNode()
{
return 0;
}
DOMWindow* EventTarget::toDOMWindow()
{
return 0;
}
bool EventTarget::isMessagePort() const
{
return false;
}
bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
}
void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const AddEventListenerOptions& options)
{
if (!listener)
return;
addEventListener(eventType, listener.releaseNonNull(), options);
}
void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const ListenerOptions& options)
{
if (!listener)
return;
removeEventListener(eventType, *listener, options);
}
bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
{
EventTargetData* d = eventTargetData();
if (!d)
return false;
size_t indexOfRemovedListener;
if (!d->eventListenerMap.remove(eventType, listener, options.capture, indexOfRemovedListener))
return false;
if (!d->firingEventIterators)
return true;
for (auto& firingIterator : *d->firingEventIterators) {
if (eventType != firingIterator.eventType)
continue;
if (indexOfRemovedListener >= firingIterator.size)
continue;
--firingIterator.size;
if (indexOfRemovedListener <= firingIterator.iterator)
--firingIterator.iterator;
}
return true;
}
bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener)
{
clearAttributeEventListener(eventType);
if (!listener)
return false;
return addEventListener(eventType, listener.releaseNonNull());
}
EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
{
for (auto& eventListener : getEventListeners(eventType)) {
if (eventListener.listener->isAttribute())
return eventListener.listener.get();
}
return 0;
}
bool EventTarget::hasActiveEventListeners(const AtomicString& eventType) const
{
EventTargetData* eventTargetData = const_cast<EventTarget*>(this)->eventTargetData();
if (!eventTargetData)
return false;
return eventTargetData->eventListenerMap.containsActive(eventType);
}
bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
{
EventListener* listener = getAttributeEventListener(eventType);
if (!listener)
return false;
return removeEventListener(eventType, *listener, false);
}
bool EventTarget::dispatchEventForBindings(Event* event, ExceptionCode& ec)
{
if (!event) {
ec = TypeError;
return false;
}
event->setUntrusted();
if (!event->isInitialized() || event->isBeingDispatched()) {
ec = INVALID_STATE_ERR;
return false;
}
if (!scriptExecutionContext())
return false;
return dispatchEvent(*event);
}
bool EventTarget::dispatchEvent(Event& event)
{
ASSERT(event.isInitialized());
ASSERT(!event.isBeingDispatched());
event.setTarget(this);
event.setCurrentTarget(this);
event.setEventPhase(Event::AT_TARGET);
bool defaultPrevented = fireEventListeners(event);
event.setEventPhase(0);
return defaultPrevented;
}
void EventTarget::uncaughtExceptionInEventHandler()
{
}
static const AtomicString& legacyType(const Event& event)
{
if (event.type() == eventNames().animationendEvent)
return eventNames().webkitAnimationEndEvent;
if (event.type() == eventNames().animationstartEvent)
return eventNames().webkitAnimationStartEvent;
if (event.type() == eventNames().animationiterationEvent)
return eventNames().webkitAnimationIterationEvent;
if (event.type() == eventNames().transitionendEvent)
return eventNames().webkitTransitionEndEvent;
if (event.type() == eventNames().wheelEvent)
return eventNames().mousewheelEvent;
return emptyAtom;
}
bool EventTarget::fireEventListeners(Event& event)
{
ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
ASSERT(event.isInitialized());
EventTargetData* d = eventTargetData();
if (!d)
return true;
EventListenerVector* legacyListenersVector = nullptr;
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);
}
return !event.defaultPrevented();
}
void EventTarget::fireEventListeners(Event& event, EventTargetData* d, EventListenerVector& entry)
{
Ref<EventTarget> protectedThis(*this);
size_t i = 0;
size_t size = entry.size();
if (!d->firingEventIterators)
d->firingEventIterators = std::make_unique<FiringEventIteratorVector>();
d->firingEventIterators->append(FiringEventIterator(event.type(), i, size));
ScriptExecutionContext* context = scriptExecutionContext();
Document* document = nullptr;
InspectorInstrumentationCookie willDispatchEventCookie;
if (is<Document>(context)) {
document = downcast<Document>(context);
willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(*document, event, size > 0);
}
for (; i < size; ++i) {
RegisteredEventListener registeredListener = entry[i];
if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
continue;
if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
continue;
if (event.immediatePropagationStopped())
break;
if (registeredListener.isOnce)
removeEventListener(event.type(), *registeredListener.listener, ListenerOptions(registeredListener.useCapture));
if (registeredListener.isPassive)
event.setInPassiveListener(true);
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
registeredListener.listener->handleEvent(context, &event);
InspectorInstrumentation::didHandleEvent(cookie);
if (registeredListener.isPassive)
event.setInPassiveListener(false);
}
d->firingEventIterators->removeLast();
if (document)
InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie);
}
const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
{
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)
return;
d->eventListenerMap.clear();
if (d->firingEventIterators) {
for (auto& firingEventIterator : *d->firingEventIterators) {
firingEventIterator.iterator = 0;
firingEventIterator.size = 0;
}
}
}
}