/* * 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) 2006 Alexey Proskuryakov (ap@webkit.org) * (C) 2007, 2008 Nikolas Zimmermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * 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 * 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 * 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 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "sky/engine/config.h" #include "sky/engine/core/events/EventTarget.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/bindings/core/v8/ExceptionState.h" #include "sky/engine/core/dom/Document.h" #include "sky/engine/core/dom/ExceptionCode.h" #include "sky/engine/core/dom/ExecutionContext.h" #include "sky/engine/core/editing/Editor.h" #include "sky/engine/core/events/Event.h" #include "sky/engine/core/frame/LocalDOMWindow.h" #include "sky/engine/core/frame/UseCounter.h" #include "sky/engine/platform/EventDispatchForbiddenScope.h" #include "sky/engine/wtf/StdLibExtras.h" #include "sky/engine/wtf/Vector.h" using namespace WTF; namespace blink { EventTargetData::EventTargetData() { } EventTargetData::~EventTargetData() { } EventTarget::EventTarget() { } EventTarget::~EventTarget() { } Node* EventTarget::toNode() { return 0; } LocalDOMWindow* EventTarget::toDOMWindow() { return 0; } inline LocalDOMWindow* EventTarget::executingWindow() { if (ExecutionContext* context = executionContext()) return context->executingWindow(); return 0; } bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr listener, bool useCapture) { // FIXME: listener null check should throw TypeError (and be done in // generated bindings), but breaks legacy content. http://crbug.com/249598 if (!listener) return false; return ensureEventTargetData().eventListenerMap.add(eventType, listener, useCapture); } bool EventTarget::removeEventListener(const AtomicString& eventType, PassRefPtr listener, bool useCapture) { EventTargetData* d = eventTargetData(); if (!d) return false; size_t indexOfRemovedListener; if (!d->eventListenerMap.remove(eventType, listener.get(), useCapture, indexOfRemovedListener)) return false; // 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; if (indexOfRemovedListener >= firingIterator.end) continue; --firingIterator.end; if (indexOfRemovedListener <= firingIterator.iterator) --firingIterator.iterator; } return true; } bool EventTarget::dispatchEvent(PassRefPtr event, ExceptionState& exceptionState) { if (!event) { exceptionState.throwDOMException(InvalidStateError, "The event provided is null."); return false; } if (event->type().isEmpty()) { exceptionState.throwDOMException(InvalidStateError, "The event provided is uninitialized."); return false; } if (event->isBeingDispatched()) { exceptionState.throwDOMException(InvalidStateError, "The event is already being dispatched."); return false; } if (!executionContext()) return false; return dispatchEvent(event); } bool EventTarget::dispatchEvent(PassRefPtr event) { event->setTarget(this); event->setCurrentTarget(this); event->setEventPhase(Event::AT_TARGET); bool defaultPrevented = fireEventListeners(event.get()); event->setEventPhase(0); return defaultPrevented; } void EventTarget::uncaughtExceptionInEventHandler() { } static const AtomicString& legacyType(const Event* event) { if (event->type() == EventTypeNames::wheel) return EventTypeNames::mousewheel; return emptyAtom; } bool EventTarget::fireEventListeners(Event* event) { ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden()); ASSERT(event && !event->type().isEmpty()); EventTargetData* d = eventTargetData(); if (!d) return true; EventListenerVector* legacyListenersVector = 0; 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 unprefixedTypeName = event->type(); event->setType(legacyTypeName); fireEventListeners(event, d, *legacyListenersVector); event->setType(unprefixedTypeName); } Editor::countEvent(executionContext(), event); return !event->defaultPrevented(); } void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry) { RefPtr 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. if (event->type() == EventTypeNames::unload) { if (LocalDOMWindow* executingWindow = this->executingWindow()) UseCounter::count(executingWindow->document(), UseCounter::DocumentUnloadFired); } else if (event->type() == EventTypeNames::DOMFocusIn || event->type() == EventTypeNames::DOMFocusOut) { if (LocalDOMWindow* executingWindow = this->executingWindow()) UseCounter::count(executingWindow->document(), UseCounter::DOMFocusInOutEvent); } else if (event->type() == EventTypeNames::focusin || event->type() == EventTypeNames::focusout) { if (LocalDOMWindow* executingWindow = this->executingWindow()) UseCounter::count(executingWindow->document(), UseCounter::FocusInOutEvent); } 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)); 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 stopImmediatePropagation has been called, we just break out immediately, without // handling any more events on this target. if (event->immediatePropagationStopped()) break; ExecutionContext* context = executionContext(); if (!context) break; // 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); } d->firingEventIterators->removeLast(); } const EventListenerVector& EventTarget::getEventListeners(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; } Vector EventTarget::eventTypes() { EventTargetData* d = eventTargetData(); return d ? d->eventListenerMap.eventTypes() : Vector(); } void EventTarget::removeAllEventListeners() { EventTargetData* d = eventTargetData(); if (!d) 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).end = 0; } } } } // namespace blink