/* * 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 "config.h" #include "EventTarget.h" #include "Event.h" #include "EventException.h" #include using namespace WTF; namespace WebCore { #ifndef NDEBUG static int gEventDispatchForbidden = 0; void forbidEventDispatch() { if (!isMainThread()) return; ++gEventDispatchForbidden; } void allowEventDispatch() { if (!isMainThread()) return; if (gEventDispatchForbidden > 0) --gEventDispatchForbidden; } bool eventDispatchForbidden() { if (!isMainThread()) return false; return gEventDispatchForbidden > 0; } #endif // NDEBUG EventTargetData::EventTargetData() { } EventTargetData::~EventTargetData() { deleteAllValues(eventListenerMap); } EventTarget::~EventTarget() { } EventSource* EventTarget::toEventSource() { return 0; } Node* EventTarget::toNode() { return 0; } DOMWindow* EventTarget::toDOMWindow() { return 0; } XMLHttpRequest* EventTarget::toXMLHttpRequest() { return 0; } XMLHttpRequestUpload* EventTarget::toXMLHttpRequestUpload() { return 0; } #if ENABLE(OFFLINE_WEB_APPLICATIONS) DOMApplicationCache* EventTarget::toDOMApplicationCache() { return 0; } #endif #if ENABLE(SVG) SVGElementInstance* EventTarget::toSVGElementInstance() { return 0; } #endif #if ENABLE(WEB_AUDIO) AudioContext* EventTarget::toAudioContext() { return 0; } JavaScriptAudioNode* EventTarget::toJavaScriptAudioNode() { return 0; } #endif #if ENABLE(WEB_SOCKETS) WebSocket* EventTarget::toWebSocket() { return 0; } #endif MessagePort* EventTarget::toMessagePort() { return 0; } #if ENABLE(WORKERS) Worker* EventTarget::toWorker() { return 0; } DedicatedWorkerContext* EventTarget::toDedicatedWorkerContext() { return 0; } #endif #if ENABLE(SHARED_WORKERS) SharedWorker* EventTarget::toSharedWorker() { return 0; } SharedWorkerContext* EventTarget::toSharedWorkerContext() { return 0; } #endif #if ENABLE(NOTIFICATIONS) Notification* EventTarget::toNotification() { return 0; } #endif #if ENABLE(BLOB) FileReader* EventTarget::toFileReader() { return 0; } #endif #if ENABLE(FILE_SYSTEM) FileWriter* EventTarget::toFileWriter() { return 0; } #endif #if ENABLE(INDEXED_DATABASE) IDBDatabase* EventTarget::toIDBDatabase() { return 0; } IDBRequest* EventTarget::toIDBRequest() { return 0; } IDBTransaction* EventTarget::toIDBTransaction() { return 0; } IDBVersionChangeRequest* EventTarget::toIDBVersionChangeRequest() { return 0; } #endif bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr listener, bool useCapture) { EventTargetData* d = ensureEventTargetData(); pair result = d->eventListenerMap.add(eventType, 0); EventListenerVector*& entry = result.first->second; const bool isNewEntry = result.second; if (isNewEntry) entry = new EventListenerVector(); RegisteredEventListener registeredListener(listener, useCapture); if (!isNewEntry) { if (entry->find(registeredListener) != notFound) // duplicate listener return false; } entry->append(registeredListener); return true; } bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) { EventTargetData* d = eventTargetData(); if (!d) return false; EventListenerMap::iterator result = d->eventListenerMap.find(eventType); if (result == d->eventListenerMap.end()) return false; EventListenerVector* entry = result->second; RegisteredEventListener registeredListener(listener, useCapture); size_t index = entry->find(registeredListener); if (index == notFound) return false; entry->remove(index); if (entry->isEmpty()) { delete entry; d->eventListenerMap.remove(result); } // Notify firing events planning to invoke the listener at 'index' that // they have one less listener to invoke. for (size_t i = 0; i < d->firingEventIterators.size(); ++i) { if (eventType != d->firingEventIterators[i].eventType) continue; if (index >= d->firingEventIterators[i].end) continue; --d->firingEventIterators[i].end; if (index <= d->firingEventIterators[i].iterator) --d->firingEventIterators[i].iterator; } return true; } bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr listener) { clearAttributeEventListener(eventType); if (!listener) return false; return addEventListener(eventType, listener, false); } EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType) { const EventListenerVector& entry = getEventListeners(eventType); for (size_t i = 0; i < entry.size(); ++i) { if (entry[i].listener->isAttribute()) return entry[i].listener.get(); } return 0; } bool EventTarget::clearAttributeEventListener(const AtomicString& eventType) { EventListener* listener = getAttributeEventListener(eventType); if (!listener) return false; return removeEventListener(eventType, listener, false); } bool EventTarget::dispatchEvent(PassRefPtr event, ExceptionCode& ec) { if (!event || event->type().isEmpty()) { ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; return false; } if (!scriptExecutionContext()) return false; return dispatchEvent(event); } bool EventTarget::dispatchEvent(PassRefPtr event) { event->setTarget(this); event->setCurrentTarget(this); event->setEventPhase(Event::AT_TARGET); return fireEventListeners(event.get()); } void EventTarget::uncaughtExceptionInEventHandler() { } bool EventTarget::fireEventListeners(Event* event) { ASSERT(!eventDispatchForbidden()); ASSERT(event && !event->type().isEmpty()); EventTargetData* d = eventTargetData(); if (!d) return true; EventListenerMap::iterator result = d->eventListenerMap.find(event->type()); if (result != d->eventListenerMap.end()) fireEventListeners(event, d, *result->second); #if ENABLE(TOUCH_EVENTS) && PLATFORM(ANDROID) if (event->isTouchEvent() && !event->hitTouchHandler()) { // Check for touchmove or touchend to see if we can skip // the rest of the stream (we always get touchstart, don't need to check that) if (d->eventListenerMap.contains(eventNames().touchmoveEvent) || d->eventListenerMap.contains(eventNames().touchendEvent)) event->setHitTouchHandler(); } #endif return !event->defaultPrevented(); } void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry) { RefPtr protect = this; #if ENABLE(TOUCH_EVENTS) && PLATFORM(ANDROID) if (event->isTouchEvent()) event->setHitTouchHandler(); #endif // 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 'end', // so iterating to 'end' naturally excludes new event listeners. size_t i = 0; size_t end = entry.size(); d->firingEventIterators.append(FiringEventIterator(event->type(), i, end)); for ( ; i < end; ++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; // 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(scriptExecutionContext(), event); } d->firingEventIterators.removeLast(); } const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType) { DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ()); EventTargetData* d = eventTargetData(); if (!d) return emptyVector; EventListenerMap::iterator it = d->eventListenerMap.find(eventType); if (it == d->eventListenerMap.end()) return emptyVector; return *it->second; } void EventTarget::removeAllEventListeners() { EventTargetData* d = eventTargetData(); if (!d) return; deleteAllValues(d->eventListenerMap); d->eventListenerMap.clear(); // Notify firing events planning to invoke the listener at 'index' that // they have one less listener to invoke. for (size_t i = 0; i < d->firingEventIterators.size(); ++i) { d->firingEventIterators[i].iterator = 0; d->firingEventIterators[i].end = 0; } } } // namespace WebCore