bool EventTargetNode::dispatchGenericEvent(PassRefPtr<Event> prpEvent) { RefPtr<Event> event(prpEvent); ASSERT(!eventDispatchForbidden()); ASSERT(event->target()); ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. // Make a vector of ancestors to send the event to. // If the node is not in a document just send the event to it. // Be sure to ref all of nodes since event handlers could result in the last reference going away. RefPtr<EventTargetNode> thisNode(this); Vector<RefPtr<ContainerNode> > ancestors; if (inDocument()) { for (ContainerNode* ancestor = eventParentNode(); ancestor; ancestor = ancestor->eventParentNode()) { #if ENABLE(SVG) // Skip <use> shadow tree elements. if (ancestor->isSVGElement() && ancestor->isShadowNode()) continue; #endif ancestors.append(ancestor); } } // Set up a pointer to indicate whether to dispatch window events. // We don't dispatch load events to the window. That quirk was originally // added because Mozilla doesn't propagate load events to the window object. Document* documentForWindowEvents = 0; if (event->type() != eventNames().loadEvent) { EventTargetNode* topLevelContainer = ancestors.isEmpty() ? this : ancestors.last().get(); if (topLevelContainer->isDocumentNode()) documentForWindowEvents = static_cast<Document*>(topLevelContainer); } // Give the target node a chance to do some work before DOM event handlers get a crack. void* data = preDispatchEventHandler(event.get()); if (event->propagationStopped()) goto doneDispatching; // Trigger capturing event handlers, starting at the top and working our way down. event->setEventPhase(Event::CAPTURING_PHASE); if (documentForWindowEvents) { event->setCurrentTarget(documentForWindowEvents); documentForWindowEvents->handleWindowEvent(event.get(), true); if (event->propagationStopped()) goto doneDispatching; } for (size_t i = ancestors.size(); i; --i) { ContainerNode* ancestor = ancestors[i - 1].get(); event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); ancestor->handleLocalEvents(event.get(), true); if (event->propagationStopped()) goto doneDispatching; } event->setEventPhase(Event::AT_TARGET); // We do want capturing event listeners to be invoked here, even though // that violates some versions of the DOM specification; Mozilla does it. event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this)); handleLocalEvents(event.get(), true); if (event->propagationStopped()) goto doneDispatching; handleLocalEvents(event.get(), false); if (event->propagationStopped()) goto doneDispatching; if (event->bubbles() && !event->cancelBubble()) { // Trigger bubbling event handlers, starting at the bottom and working our way up. event->setEventPhase(Event::BUBBLING_PHASE); size_t size = ancestors.size(); for (size_t i = 0; i < size; ++i) { ContainerNode* ancestor = ancestors[i].get(); event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); ancestor->handleLocalEvents(event.get(), false); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } if (documentForWindowEvents) { event->setCurrentTarget(documentForWindowEvents); documentForWindowEvents->handleWindowEvent(event.get(), false); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } } doneDispatching: event->setCurrentTarget(0); event->setEventPhase(0); // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. postDispatchEventHandler(event.get(), data); // Call default event handlers. While the DOM does have a concept of preventing // default handling, the detail of which handlers are called is an internal // implementation detail and not part of the DOM. if (!event->defaultPrevented() && !event->defaultHandled()) { // Non-bubbling events call only one default event handler, the one for the target. defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; // For bubbling events, call default event handlers on the same targets in the // same order as the bubbling phase. if (event->bubbles()) { size_t size = ancestors.size(); for (size_t i = 0; i < size; ++i) { ContainerNode* ancestor = ancestors[i].get(); ancestor->defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; } } } doneWithDefault: Document::updateDocumentsRendering(); return !event->defaultPrevented(); }
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() != eventNames().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() != eventNames().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->script()->isEnabled()) frame->script()->finishedWithEvent(evt.get()); return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent? }