2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
27 #include "EventTargetNode.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
35 #include "HTMLNames.h"
36 #include "TextStream.h"
37 #include "KeyboardEvent.h"
38 #include "MouseEvent.h"
39 #include "MutationEvent.h"
40 #include "PlatformMouseEvent.h"
41 #include "PlatformWheelEvent.h"
42 #include "RegisteredEventListener.h"
44 #include "WheelEvent.h"
45 #include "kjs_proxy.h"
49 using namespace EventNames;
50 using namespace HTMLNames;
53 static int gEventDispatchForbidden = 0;
56 EventTargetNode::EventTargetNode(Document *doc)
62 EventTargetNode::~EventTargetNode()
64 if (m_regdListeners && !m_regdListeners->isEmpty() && !inDocument())
65 document()->unregisterDisconnectedNodeWithEventListeners(this);
66 delete m_regdListeners;
69 void EventTargetNode::insertedIntoDocument()
71 if (m_regdListeners && !m_regdListeners->isEmpty())
72 document()->unregisterDisconnectedNodeWithEventListeners(this);
74 Node::insertedIntoDocument();
77 void EventTargetNode::removedFromDocument()
79 if (m_regdListeners && !m_regdListeners->isEmpty())
80 document()->registerDisconnectedNodeWithEventListeners(this);
82 Node::removedFromDocument();
85 void EventTargetNode::addEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener, const bool useCapture)
87 if (!document()->attached())
90 Document::ListenerType type = static_cast<Document::ListenerType>(0);
91 if (eventType == DOMSubtreeModifiedEvent)
92 type = Document::DOMSUBTREEMODIFIED_LISTENER;
93 else if (eventType == DOMNodeInsertedEvent)
94 type = Document::DOMNODEINSERTED_LISTENER;
95 else if (eventType == DOMNodeRemovedEvent)
96 type = Document::DOMNODEREMOVED_LISTENER;
97 else if (eventType == DOMNodeRemovedFromDocumentEvent)
98 type = Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER;
99 else if (eventType == DOMNodeInsertedIntoDocumentEvent)
100 type = Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER;
101 else if (eventType == DOMAttrModifiedEvent)
102 type = Document::DOMATTRMODIFIED_LISTENER;
103 else if (eventType == DOMCharacterDataModifiedEvent)
104 type = Document::DOMCHARACTERDATAMODIFIED_LISTENER;
105 else if (eventType == overflowchangedEvent)
106 type = Document::OVERFLOWCHANGED_LISTENER;
109 document()->addListenerType(type);
111 if (!m_regdListeners)
112 m_regdListeners = new RegisteredEventListenerList;
114 // Remove existing identical listener set with identical arguments.
115 // The DOM2 spec says that "duplicate instances are discarded" in this case.
116 removeEventListener(eventType, listener.get(), useCapture);
118 // adding the first one
119 if (m_regdListeners->isEmpty() && !inDocument())
120 document()->registerDisconnectedNodeWithEventListeners(this);
122 m_regdListeners->append(new RegisteredEventListener(eventType, listener.get(), useCapture));
125 void EventTargetNode::removeEventListener(const AtomicString &eventType, EventListener *listener, bool useCapture)
127 if (!m_regdListeners) // nothing to remove
130 RegisteredEventListener rl(eventType, listener, useCapture);
132 RegisteredEventListenerList::Iterator end = m_regdListeners->end();
133 for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it)
134 if (*(*it).get() == rl) {
135 (*it)->setRemoved(true);
137 it = m_regdListeners->remove(it);
139 if (m_regdListeners->isEmpty() && !inDocument())
140 document()->unregisterDisconnectedNodeWithEventListeners(this);
145 void EventTargetNode::removeAllEventListeners()
147 delete m_regdListeners;
151 void EventTargetNode::handleLocalEvents(Event *evt, bool useCapture)
153 if (!m_regdListeners)
156 if (disabled() && evt->isMouseEvent())
159 RegisteredEventListenerList listenersCopy = *m_regdListeners;
160 RegisteredEventListenerList::Iterator end = listenersCopy.end();
162 for (RegisteredEventListenerList::Iterator it = listenersCopy.begin(); it != end; ++it)
163 if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed())
164 (*it)->listener()->handleEvent(evt, false);
167 bool EventTargetNode::dispatchGenericEvent(PassRefPtr<Event> e, ExceptionCode&, bool tempEvent)
169 RefPtr<Event> evt(e);
170 assert(!eventDispatchForbidden());
171 assert(evt->target());
172 assert(!evt->type().isNull()); // JavaScript code could create an event with an empty name
174 // work out what nodes to send event to
175 DeprecatedPtrList<Node> nodeChain;
177 for (n = this; n; n = n->parentNode()) {
179 nodeChain.prepend(n);
182 DeprecatedPtrListIterator<Node> it(nodeChain);
184 // Before we begin dispatching events, give the target node a chance to do some work prior
185 // to the DOM event handlers getting a crack.
186 void* data = preDispatchEventHandler(evt.get());
188 // trigger any capturing event handlers on our way down
189 evt->setEventPhase(Event::CAPTURING_PHASE);
192 // Handle window events for capture phase, except load events, this quirk is needed
193 // because Mozilla used to never propagate load events to the window object
194 if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped())
195 static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), true);
197 for (; it.current() && it.current() != this && !evt->propagationStopped(); ++it) {
198 evt->setCurrentTarget(it.current());
199 EventTargetNodeCast(it.current())->handleLocalEvents(evt.get(), true);
202 // dispatch to the actual target node
204 if (!evt->propagationStopped()) {
205 evt->setEventPhase(Event::AT_TARGET);
206 evt->setCurrentTarget(it.current());
208 // We do want capturing event listeners to be invoked here, even though
209 // that violates the specification since Mozilla does it.
210 EventTargetNodeCast(it.current())->handleLocalEvents(evt.get(), true);
212 EventTargetNodeCast(it.current())->handleLocalEvents(evt.get(), false);
216 // ok, now bubble up again (only non-capturing event handlers will be called)
217 // ### recalculate the node chain here? (e.g. if target node moved in document by previous event handlers)
218 // no. the DOM specs says:
219 // The chain of EventTargets from the event target to the top of the tree
220 // is determined before the initial dispatch of the event.
221 // If modifications occur to the tree during event processing,
222 // event flow will proceed based on the initial state of the tree.
224 // since the initial dispatch is before the capturing phase,
225 // there's no need to recalculate the node chain.
228 if (evt->bubbles()) {
229 evt->setEventPhase(Event::BUBBLING_PHASE);
230 for (; it.current() && !evt->propagationStopped() && !evt->getCancelBubble(); --it) {
231 evt->setCurrentTarget(it.current());
232 EventTargetNodeCast(it.current())->handleLocalEvents(evt.get(), false);
234 // Handle window events for bubbling phase, except load events, this quirk is needed
235 // because Mozilla used to never propagate load events at all
238 if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped() && !evt->getCancelBubble()) {
239 evt->setCurrentTarget(it.current());
240 static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), false);
244 evt->setCurrentTarget(0);
245 evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say
246 // anything about the default event handler phase.
249 // Now call the post dispatch.
250 postDispatchEventHandler(evt.get(), data);
252 // now we call all default event handlers (this is not part of DOM - it is internal to khtml)
256 for (; it.current() && !evt->defaultPrevented() && !evt->defaultHandled(); --it)
257 EventTargetNodeCast(it.current())->defaultEventHandler(evt.get());
258 else if (!evt->defaultPrevented() && !evt->defaultHandled())
259 EventTargetNodeCast(it.current())->defaultEventHandler(evt.get());
261 // deref all nodes in chain
263 for (; it.current(); ++it)
264 it.current()->deref(); // this may delete us
266 Document::updateDocumentsRendering();
268 // If tempEvent is true, this means that the DOM implementation
269 // will not be storing a reference to the event, i.e. there is no
270 // way to retrieve it from javascript if a script does not already
271 // have a reference to it in a variable. So there is no need for
272 // the interpreter to keep the event in it's cache
273 Frame *frame = document()->frame();
274 if (tempEvent && frame && frame->jScript())
275 frame->jScript()->finishedWithEvent(evt.get());
277 return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent?
280 bool EventTargetNode::dispatchEvent(PassRefPtr<Event> e, ExceptionCode& ec, bool tempEvent)
282 RefPtr<Event> evt(e);
283 assert(!eventDispatchForbidden());
284 if (!evt || evt->type().isEmpty()) {
285 ec = UNSPECIFIED_EVENT_TYPE_ERR;
288 evt->setTarget(this);
290 RefPtr<FrameView> view = document()->view();
292 return dispatchGenericEvent(evt.release(), ec, tempEvent);
295 bool EventTargetNode::dispatchSubtreeModifiedEvent(bool sendChildrenChanged)
297 assert(!eventDispatchForbidden());
299 // FIXME: Pull this whole if clause out of this function.
300 if (sendChildrenChanged) {
301 notifyNodeListsChildrenChanged();
304 notifyNodeListsAttributeChanged(); // FIXME: Can do better some day. Really only care about the name attribute changing.
306 if (!document()->hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER))
308 ExceptionCode ec = 0;
309 return dispatchEvent(new MutationEvent(DOMSubtreeModifiedEvent,
310 true,false,0,String(),String(),String(),0),ec,true);
313 void EventTargetNode::dispatchWindowEvent(const AtomicString &eventType, bool canBubbleArg, bool cancelableArg)
315 assert(!eventDispatchForbidden());
316 ExceptionCode ec = 0;
317 RefPtr<Event> evt = new Event(eventType, canBubbleArg, cancelableArg);
318 RefPtr<Document> doc = document();
319 evt->setTarget(doc.get());
320 doc->handleWindowEvent(evt.get(), true);
321 doc->handleWindowEvent(evt.get(), false);
323 if (eventType == loadEvent) {
324 // For onload events, send a separate load event to the enclosing frame only.
325 // This is a DOM extension and is independent of bubbling/capturing rules of
327 Element* ownerElement = doc->ownerElement();
329 RefPtr<Event> ownerEvent = new Event(eventType, false, cancelableArg);
330 ownerEvent->setTarget(ownerElement);
331 ownerElement->dispatchGenericEvent(ownerEvent.release(), ec, true);
336 bool EventTargetNode::dispatchUIEvent(const AtomicString &eventType, int detail)
338 assert(!eventDispatchForbidden());
339 assert(eventType == DOMFocusInEvent || eventType == DOMFocusOutEvent || eventType == DOMActivateEvent);
341 bool cancelable = eventType == DOMActivateEvent;
343 ExceptionCode ec = 0;
344 UIEvent* evt = new UIEvent(eventType, true, cancelable, document()->defaultView(), detail);
345 return dispatchEvent(evt, ec, true);
348 bool EventTargetNode::dispatchKeyEvent(const PlatformKeyboardEvent& key)
350 assert(!eventDispatchForbidden());
351 ExceptionCode ec = 0;
352 RefPtr<KeyboardEvent> keyboardEvent = new KeyboardEvent(key, document()->defaultView());
353 bool r = dispatchEvent(keyboardEvent,ec,true);
355 // we want to return false if default is prevented (already taken care of)
356 // or if the element is default-handled by the DOM. Otherwise we let it just
357 // let it get handled by AppKit
358 if (keyboardEvent->defaultHandled())
364 bool EventTargetNode::dispatchMouseEvent(const PlatformMouseEvent& _mouse, const AtomicString& eventType,
365 int detail, Node* relatedTarget)
367 assert(!eventDispatchForbidden());
369 IntPoint contentsPos;
370 if (FrameView* view = document()->view())
371 contentsPos = view->viewportToContents(_mouse.pos());
373 return dispatchMouseEvent(eventType, _mouse.button(), detail,
374 contentsPos.x(), contentsPos.y(), _mouse.globalX(), _mouse.globalY(),
375 _mouse.ctrlKey(), _mouse.altKey(), _mouse.shiftKey(), _mouse.metaKey(),
376 false, relatedTarget);
379 bool EventTargetNode::dispatchSimulatedMouseEvent(const AtomicString &eventType)
381 assert(!eventDispatchForbidden());
382 // Like Gecko, we just pass 0 for everything when we make a fake mouse event.
383 // Internet Explorer instead gives the current mouse position and state.
384 return dispatchMouseEvent(eventType, 0, 0, 0, 0, 0, 0, false, false, false, false, true);
387 bool EventTargetNode::dispatchMouseEvent(const AtomicString& eventType, int button, int detail,
388 int pageX, int pageY, int screenX, int screenY,
389 bool ctrlKey, bool altKey, bool shiftKey, bool metaKey,
390 bool isSimulated, Node* relatedTargetArg)
392 assert(!eventDispatchForbidden());
393 if (disabled()) // Don't even send DOM events for disabled controls..
396 if (eventType.isEmpty())
397 return false; // Shouldn't happen.
399 // Dispatching the first event can easily result in this node being destroyed.
400 // Since we dispatch up to three events here, we need to make sure we're referenced
401 // so the pointer will be good for the two subsequent ones.
402 RefPtr<Node> protect(this);
404 bool cancelable = eventType != mousemoveEvent;
406 ExceptionCode ec = 0;
408 bool swallowEvent = false;
410 // Attempting to dispatch with a non-EventTarget relatedTarget causes the relatedTarget to be silently ignored.
411 EventTargetNode *relatedTarget = (relatedTargetArg && relatedTargetArg->isEventTargetNode()) ? static_cast<EventTargetNode*>(relatedTargetArg) : 0;
413 RefPtr<Event> me = new MouseEvent(eventType, true, cancelable, document()->defaultView(),
414 detail, screenX, screenY, pageX, pageY,
415 ctrlKey, altKey, shiftKey, metaKey, button,
416 relatedTarget, 0, isSimulated);
418 dispatchEvent(me, ec, true);
419 bool defaultHandled = me->defaultHandled();
420 bool defaultPrevented = me->defaultPrevented();
421 if (defaultHandled || defaultPrevented)
424 // Special case: If it's a double click event, we also send the dblclick event. This is not part
425 // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated
426 // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same.
427 if (eventType == clickEvent && detail == 2) {
428 me = new MouseEvent(dblclickEvent, true, cancelable, document()->defaultView(),
429 detail, screenX, screenY, pageX, pageY,
430 ctrlKey, altKey, shiftKey, metaKey, button,
431 relatedTarget, 0, isSimulated);
433 me->setDefaultHandled();
434 dispatchEvent(me, ec, true);
435 if (me->defaultHandled() || me->defaultPrevented())
439 // Also send a DOMActivate event, which causes things like form submissions to occur.
440 if (eventType == clickEvent && !defaultPrevented)
441 dispatchUIEvent(DOMActivateEvent, detail);
446 void EventTargetNode::dispatchWheelEvent(PlatformWheelEvent& e)
448 assert(!eventDispatchForbidden());
449 if (e.deltaX() == 0 && e.deltaY() == 0)
452 FrameView* view = document()->view();
456 IntPoint pos = view->viewportToContents(e.pos());
458 RefPtr<WheelEvent> we = new WheelEvent(e.deltaX(), e.deltaY(),
459 document()->defaultView(), e.globalX(), e.globalY(), pos.x(), pos.y(),
460 e.ctrlKey(), e.altKey(), e.shiftKey(), e.metaKey());
461 ExceptionCode ec = 0;
462 if (!dispatchEvent(we, ec, true))
467 void EventTargetNode::dispatchFocusEvent()
469 dispatchHTMLEvent(focusEvent, false, false);
472 void EventTargetNode::dispatchBlurEvent()
474 dispatchHTMLEvent(blurEvent, false, false);
477 bool EventTargetNode::dispatchHTMLEvent(const AtomicString &eventType, bool canBubbleArg, bool cancelableArg)
479 assert(!eventDispatchForbidden());
480 ExceptionCode ec = 0;
481 return dispatchEvent(new Event(eventType, canBubbleArg, cancelableArg), ec, true);
484 void EventTargetNode::removeHTMLEventListener(const AtomicString &eventType)
486 if (!m_regdListeners) // nothing to remove
489 RegisteredEventListenerList::Iterator end = m_regdListeners->end();
490 for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it)
491 if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) {
492 it = m_regdListeners->remove(it);
494 if (m_regdListeners->isEmpty() && !inDocument())
495 document()->unregisterDisconnectedNodeWithEventListeners(this);
500 void EventTargetNode::setHTMLEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener)
502 // In case we are the only one holding a reference to it, we don't want removeHTMLEventListener to destroy it.
503 removeHTMLEventListener(eventType);
505 addEventListener(eventType, listener.get(), false);
508 EventListener *EventTargetNode::getHTMLEventListener(const AtomicString &eventType)
510 if (!m_regdListeners)
513 RegisteredEventListenerList::Iterator end = m_regdListeners->end();
514 for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it)
515 if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener())
516 return (*it)->listener();
520 bool EventTargetNode::disabled() const
525 void EventTargetNode::defaultEventHandler(Event *evt)
531 void EventTargetNode::dump(TextStream* stream, DeprecatedString ind) const
534 *stream << " #regdListeners=" << m_regdListeners->count(); // ### more detail
536 Node::dump(stream,ind);
539 void forbidEventDispatch()
541 ++gEventDispatchForbidden;
544 void allowEventDispatch()
546 if (gEventDispatchForbidden > 0)
547 --gEventDispatchForbidden;
550 bool eventDispatchForbidden()
552 return gEventDispatchForbidden > 0;
557 } // namespace WebCore