| /* |
| * 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 <zimmermann@kde.org> |
| * |
| * 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 <wtf/StdLibExtras.h> |
| |
| 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<EventListener> listener, bool useCapture) |
| { |
| EventTargetData* d = ensureEventTargetData(); |
| |
| pair<EventListenerMap::iterator, bool> 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<EventListener> 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> 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) |
| { |
| 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<EventTarget> 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 |