blob: 8eb8b9a19ec5f02e3b56b8cc0ee552cc926fad90 [file] [log] [blame]
/*
* This file is part of the DOM implementation for KDE.
*
* 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 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 "Node.h"
#include "NodeList.h"
#include "Document.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameView.h"
#include "kjs_proxy.h"
#include "RegisteredEventListener.h"
namespace WebCore {
using namespace EventNames;
#ifndef NDEBUG
static int gEventDispatchForbidden = 0;
#endif
EventTarget::~EventTarget()
{
}
EventTargetNode* EventTarget::toNode()
{
return 0;
}
XMLHttpRequest* EventTarget::toXMLHttpRequest()
{
return 0;
}
#if ENABLE(SVG)
SVGElementInstance* EventTarget::toSVGElementInstance()
{
return 0;
}
#endif
static inline void addListenerTypeToDocumentIfNeeded(const AtomicString& eventType, Document* document)
{
Document::ListenerType type = static_cast<Document::ListenerType>(0);
if (eventType == DOMSubtreeModifiedEvent)
type = Document::DOMSUBTREEMODIFIED_LISTENER;
else if (eventType == DOMNodeInsertedEvent)
type = Document::DOMNODEINSERTED_LISTENER;
else if (eventType == DOMNodeRemovedEvent)
type = Document::DOMNODEREMOVED_LISTENER;
else if (eventType == DOMNodeRemovedFromDocumentEvent)
type = Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER;
else if (eventType == DOMNodeInsertedIntoDocumentEvent)
type = Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER;
else if (eventType == DOMAttrModifiedEvent)
type = Document::DOMATTRMODIFIED_LISTENER;
else if (eventType == DOMCharacterDataModifiedEvent)
type = Document::DOMCHARACTERDATAMODIFIED_LISTENER;
else if (eventType == overflowchangedEvent)
type = Document::OVERFLOWCHANGED_LISTENER;
if (type)
document->addListenerType(type);
}
void EventTarget::addEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
{
ASSERT(referenceNode);
if (!referenceNode->document()->attached())
return;
addListenerTypeToDocumentIfNeeded(eventType, referenceNode->document());
if (!referenceNode->m_regdListeners)
referenceNode->m_regdListeners = new RegisteredEventListenerList;
// Remove existing identical listener set with identical arguments.
// The DOM2 spec says that "duplicate instances are discarded" in this case.
removeEventListener(referenceNode, eventType, listener.get(), useCapture);
// adding the first one
if (referenceNode->m_regdListeners->isEmpty() && !referenceNode->inDocument())
referenceNode->document()->registerDisconnectedNodeWithEventListeners(referenceNode);
referenceNode->m_regdListeners->append(new RegisteredEventListener(eventType, listener.get(), useCapture));
}
void EventTarget::removeEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, EventListener* listener, bool useCapture)
{
ASSERT(referenceNode);
if (!referenceNode->m_regdListeners)
return;
RegisteredEventListener rl(eventType, listener, useCapture);
RegisteredEventListenerList::Iterator end = referenceNode->m_regdListeners->end();
for (RegisteredEventListenerList::Iterator it = referenceNode->m_regdListeners->begin(); it != end; ++it) {
if (*(*it).get() == rl) {
(*it)->setRemoved(true);
it = referenceNode->m_regdListeners->remove(it);
// removed last
if (referenceNode->m_regdListeners->isEmpty() && !referenceNode->inDocument())
referenceNode->document()->unregisterDisconnectedNodeWithEventListeners(referenceNode);
return;
}
}
}
bool EventTarget::dispatchGenericEvent(EventTargetNode* referenceNode, PassRefPtr<Event> e, ExceptionCode&, bool tempEvent)
{
RefPtr<Event> evt(e);
ASSERT(!eventDispatchForbidden());
ASSERT(evt->target());
ASSERT(!evt->type().isNull()); // JavaScript code could create an event with an empty name
// work out what nodes to send event to
DeprecatedPtrList<Node> nodeChain;
if (referenceNode->inDocument()) {
for (Node* n = referenceNode; n; n = n->eventParentNode()) {
n->ref();
nodeChain.prepend(n);
}
} else {
// if node is not in the document just send event to itself
referenceNode->ref();
nodeChain.prepend(referenceNode);
}
DeprecatedPtrListIterator<Node> it(nodeChain);
// Before we begin dispatching events, give the target node a chance to do some work prior
// to the DOM event handlers getting a crack.
void* data = preDispatchEventHandler(evt.get());
// trigger any capturing event handlers on our way down
evt->setEventPhase(Event::CAPTURING_PHASE);
it.toFirst();
// Handle window events for capture phase, except load events, this quirk is needed
// because Mozilla used to never propagate load events to the window object
if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped())
static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), true);
EventTargetNode* eventTargetNode = 0;
for (; it.current() && it.current() != referenceNode && !evt->propagationStopped(); ++it) {
eventTargetNode = EventTargetNodeCast(it.current());
evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode));
eventTargetNode->handleLocalEvents(evt.get(), true);
}
// dispatch to the actual target node
it.toLast();
if (!evt->propagationStopped()) {
evt->setEventPhase(Event::AT_TARGET);
eventTargetNode = EventTargetNodeCast(it.current());
evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode));
// We do want capturing event listeners to be invoked here, even though
// that violates the specification since Mozilla does it.
eventTargetNode->handleLocalEvents(evt.get(), true);
eventTargetNode->handleLocalEvents(evt.get(), false);
}
--it;
// ok, now bubble up again (only non-capturing event handlers will be called)
// ### recalculate the node chain here? (e.g. if target node moved in document by previous event handlers)
// no. the DOM specs says:
// The chain of EventTargets from the event target to the top of the tree
// is determined before the initial dispatch of the event.
// If modifications occur to the tree during event processing,
// event flow will proceed based on the initial state of the tree.
//
// since the initial dispatch is before the capturing phase,
// there's no need to recalculate the node chain.
// (tobias)
if (evt->bubbles()) {
evt->setEventPhase(Event::BUBBLING_PHASE);
for (; it.current() && !evt->propagationStopped() && !evt->cancelBubble(); --it) {
eventTargetNode = EventTargetNodeCast(it.current());
evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode));
eventTargetNode->handleLocalEvents(evt.get(), false);
}
it.toFirst();
// Handle window events for bubbling phase, except load events, this quirk is needed
// because Mozilla used to never propagate load events at all
if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped() && !evt->cancelBubble()) {
evt->setCurrentTarget(EventTargetNodeCast(it.current()));
static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), false);
}
}
evt->setCurrentTarget(0);
evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say
// anything about the default event handler phase.
// Now call the post dispatch.
postDispatchEventHandler(evt.get(), data);
// now we call all default event handlers (this is not part of DOM - it is internal to WebCore)
it.toLast();
if (evt->bubbles())
for (; it.current() && !evt->defaultPrevented() && !evt->defaultHandled(); --it)
EventTargetNodeCast(it.current())->defaultEventHandler(evt.get());
else if (!evt->defaultPrevented() && !evt->defaultHandled())
EventTargetNodeCast(it.current())->defaultEventHandler(evt.get());
// deref all nodes in chain
it.toFirst();
for (; it.current(); ++it)
it.current()->deref(); // this may delete us
Document::updateDocumentsRendering();
// If tempEvent is true, this means that the DOM implementation
// will not be storing a reference to the event, i.e. there is no
// way to retrieve it from javascript if a script does not already
// have a reference to it in a variable. So there is no need for
// the interpreter to keep the event in it's cache
Frame* frame = referenceNode->document()->frame();
if (tempEvent && frame && frame->scriptProxy()->isEnabled())
frame->scriptProxy()->finishedWithEvent(evt.get());
return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent?
}
void EventTarget::removeAllEventListeners(EventTargetNode* referenceNode)
{
delete referenceNode->m_regdListeners;
referenceNode->m_regdListeners = 0;
}
void EventTarget::insertedIntoDocument(EventTargetNode* referenceNode)
{
if (referenceNode && referenceNode->m_regdListeners && !referenceNode->m_regdListeners->isEmpty())
referenceNode->document()->unregisterDisconnectedNodeWithEventListeners(referenceNode);
}
void EventTarget::removedFromDocument(EventTargetNode* referenceNode)
{
if (referenceNode && referenceNode->m_regdListeners && !referenceNode->m_regdListeners->isEmpty())
referenceNode->document()->registerDisconnectedNodeWithEventListeners(referenceNode);
}
void EventTarget::handleLocalEvents(EventTargetNode* referenceNode, Event* evt, bool useCapture)
{
ASSERT(referenceNode);
if (!referenceNode->m_regdListeners || referenceNode->m_regdListeners->isEmpty())
return;
RegisteredEventListenerList listenersCopy = *referenceNode->m_regdListeners;
RegisteredEventListenerList::Iterator end = listenersCopy.end();
for (RegisteredEventListenerList::Iterator it = listenersCopy.begin(); it != end; ++it) {
if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed())
(*it)->listener()->handleEvent(evt, false);
}
}
EventTarget* EventTarget::eventTargetRespectingSVGTargetRules(EventTargetNode*& referenceNode)
{
// TODO: SVG will add logic here soon.
return referenceNode;
}
#ifndef NDEBUG
void forbidEventDispatch()
{
++gEventDispatchForbidden;
}
void allowEventDispatch()
{
if (gEventDispatchForbidden > 0)
--gEventDispatchForbidden;
}
bool eventDispatchForbidden()
{
return gEventDispatchForbidden > 0;
}
#endif // NDEBUG
} // end namespace