2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
7 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) 2011 Google Inc. All rights reserved.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "EventDispatcher.h"
29 #include "EventContext.h"
30 #include "EventDispatchMediator.h"
31 #include "FrameView.h"
32 #include "HTMLMediaElement.h"
33 #include "InspectorInstrumentation.h"
34 #include "MouseEvent.h"
35 #include "ScopedEventQueue.h"
36 #include "WindowEventContext.h"
37 #include <wtf/RefPtr.h>
38 #include <wtf/UnusedParam.h>
41 #include "SVGElementInstance.h"
43 #include "SVGUseElement.h"
48 static HashSet<Node*>* gNodesDispatchingSimulatedClicks = 0;
50 bool EventDispatcher::dispatchEvent(Node* node, PassRefPtr<EventDispatchMediator> mediator)
52 ASSERT(!eventDispatchForbidden());
54 EventDispatcher dispatcher(node);
55 return mediator->dispatchEvent(&dispatcher);
58 static EventTarget* findElementInstance(Node* referenceNode)
61 // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included
62 // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects
63 for (Node* n = referenceNode; n; n = n->parentNode()) {
64 if (!n->isSVGShadowRoot() || !n->isSVGElement())
67 Element* shadowTreeParentElement = n->svgShadowHost();
68 ASSERT(shadowTreeParentElement->hasTagName(SVGNames::useTag));
70 if (SVGElementInstance* instance = static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode))
74 // SVG elements with SVG disabled should not be possible.
81 inline static EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNode)
83 ASSERT(referenceNode);
85 return referenceNode->isSVGElement() ? findElementInstance(referenceNode) : referenceNode;
88 void EventDispatcher::dispatchScopedEvent(Node* node, PassRefPtr<EventDispatchMediator> mediator)
90 // We need to set the target here because it can go away by the time we actually fire the event.
91 mediator->event()->setTarget(eventTargetRespectingSVGTargetRules(node));
92 ScopedEventQueue::instance()->enqueueEventDispatchMediator(mediator);
95 void EventDispatcher::dispatchSimulatedClick(Node* node, PassRefPtr<Event> underlyingEvent, bool sendMouseEvents, bool showPressedLook)
100 EventDispatcher dispatcher(node);
102 if (!gNodesDispatchingSimulatedClicks)
103 gNodesDispatchingSimulatedClicks = new HashSet<Node*>;
104 else if (gNodesDispatchingSimulatedClicks->contains(node))
107 gNodesDispatchingSimulatedClicks->add(node);
109 // send mousedown and mouseup before the click, if requested
111 dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mousedownEvent, node->document()->defaultView(), underlyingEvent));
112 node->setActive(true, showPressedLook);
114 dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mouseupEvent, node->document()->defaultView(), underlyingEvent));
115 node->setActive(false);
118 dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().clickEvent, node->document()->defaultView(), underlyingEvent));
120 gNodesDispatchingSimulatedClicks->remove(node);
123 static inline bool isShadowRootOrSVGShadowRoot(const Node* node)
125 return node->isShadowRoot() || node->isSVGShadowRoot();
128 static inline bool isShadowHost(Node* node)
130 return node->isElementNode() && toElement(node)->shadowRoot();
133 PassRefPtr<EventTarget> EventDispatcher::adjustToShadowBoundaries(PassRefPtr<Node> relatedTarget, const Vector<Node*> relatedTargetAncestors)
135 Vector<EventContext>::const_iterator lowestCommonBoundary = m_ancestors.end();
136 // Assume divergent boundary is the relatedTarget itself (in other words, related target ancestor chain does not cross any shadow DOM boundaries).
137 Vector<Node*>::const_iterator firstDivergentBoundary = relatedTargetAncestors.begin();
139 Vector<EventContext>::const_iterator targetAncestor = m_ancestors.end();
140 // Walk down from the top, looking for lowest common ancestor, also monitoring shadow DOM boundaries.
141 bool diverged = false;
142 for (Vector<Node*>::const_iterator i = relatedTargetAncestors.end() - 1; i >= relatedTargetAncestors.begin(); --i) {
144 if (isShadowRootOrSVGShadowRoot(*i)) {
145 firstDivergentBoundary = i + 1;
151 if (targetAncestor == m_ancestors.begin()) {
158 if (isShadowRootOrSVGShadowRoot(*i))
159 lowestCommonBoundary = targetAncestor;
161 if ((*i) != (*targetAncestor).node())
166 // The relatedTarget is an ancestor or shadowHost of the target.
167 // FIXME: Remove the first check once conversion to new shadow DOM is complete <http://webkit.org/b/48698>
168 if (m_node->shadowHost() == relatedTarget.get() || isShadowHost(relatedTarget.get())) {
169 Vector<EventContext>::const_iterator relatedTargetChild = targetAncestor - 1;
170 if (relatedTargetChild >= m_ancestors.begin() && isShadowRootOrSVGShadowRoot(relatedTargetChild->node()))
171 lowestCommonBoundary = relatedTargetChild;
173 } else if ((*firstDivergentBoundary) == m_node.get()) {
174 // Since ancestors does not contain target itself, we must account
175 // for the possibility that target is a shadowHost of relatedTarget
176 // and thus serves as the lowestCommonBoundary.
177 // Luckily, in this case the firstDivergentBoundary is target.
178 lowestCommonBoundary = m_ancestors.begin();
179 m_shouldPreventDispatch = true;
182 if (lowestCommonBoundary != m_ancestors.end()) {
183 // Trim ancestors to lowestCommonBoundary to keep events inside of the common shadow DOM subtree.
184 m_ancestors.shrink(lowestCommonBoundary - m_ancestors.begin());
186 // Set event's related target to the first encountered shadow DOM boundary in the divergent subtree.
187 return firstDivergentBoundary != relatedTargetAncestors.begin() ? *firstDivergentBoundary : relatedTarget;
190 inline static bool ancestorsCrossShadowBoundaries(const Vector<EventContext>& ancestors)
192 return ancestors.isEmpty() || ancestors.first().node() == ancestors.last().node();
195 // FIXME: Once https://bugs.webkit.org/show_bug.cgi?id=52963 lands, this should
196 // be greatly improved. See https://bugs.webkit.org/show_bug.cgi?id=54025.
197 PassRefPtr<EventTarget> EventDispatcher::adjustRelatedTarget(Event* event, PassRefPtr<EventTarget> prpRelatedTarget)
199 if (!prpRelatedTarget)
202 RefPtr<Node> relatedTarget = prpRelatedTarget->toNode();
206 Node* target = m_node.get();
208 return prpRelatedTarget;
210 ensureEventAncestors(event);
212 // Calculate early if the common boundary is even possible by looking at
213 // ancestors size and if the retargeting has occured (indicating the presence of shadow DOM boundaries).
214 // If there are no boundaries detected, the target and related target can't have a common boundary.
215 bool noCommonBoundary = ancestorsCrossShadowBoundaries(m_ancestors);
217 Vector<Node*> relatedTargetAncestors;
218 Node* outermostShadowBoundary = relatedTarget.get();
219 for (Node* n = outermostShadowBoundary; n; n = n->parentOrHostNode()) {
220 if (isShadowRootOrSVGShadowRoot(n))
221 outermostShadowBoundary = n->parentOrHostNode();
222 if (!noCommonBoundary)
223 relatedTargetAncestors.append(n);
226 // Short-circuit the fast case when we know there is no need to calculate a common boundary.
227 if (noCommonBoundary)
228 return outermostShadowBoundary;
230 return adjustToShadowBoundaries(relatedTarget.release(), relatedTargetAncestors);
233 EventDispatcher::EventDispatcher(Node* node)
235 , m_ancestorsInitialized(false)
236 , m_shouldPreventDispatch(false)
239 m_view = node->document()->view();
242 void EventDispatcher::ensureEventAncestors(Event* event)
244 if (!m_node->inDocument())
247 if (m_ancestorsInitialized)
250 m_ancestorsInitialized = true;
252 Node* ancestor = m_node.get();
253 EventTarget* target = eventTargetRespectingSVGTargetRules(ancestor);
254 bool shouldSkipNextAncestor = false;
256 bool isSVGShadowRoot = ancestor->isSVGShadowRoot();
257 if (isSVGShadowRoot || ancestor->isShadowRoot()) {
258 if (determineDispatchBehavior(event, ancestor) == StayInsideShadowDOM)
261 ancestor = isSVGShadowRoot ? ancestor->svgShadowHost() : ancestor->shadowHost();
263 ancestor = ancestor->shadowHost();
265 if (!shouldSkipNextAncestor)
268 ancestor = ancestor->parentNodeGuaranteedHostFree();
274 // Skip SVGShadowTreeRootElement.
275 shouldSkipNextAncestor = ancestor->isSVGShadowRoot();
276 if (shouldSkipNextAncestor)
279 // FIXME: Unroll the extra loop inside eventTargetRespectingSVGTargetRules into this loop.
280 m_ancestors.append(EventContext(ancestor, eventTargetRespectingSVGTargetRules(ancestor), target));
284 bool EventDispatcher::dispatchEvent(PassRefPtr<Event> event)
286 event->setTarget(eventTargetRespectingSVGTargetRules(m_node.get()));
288 ASSERT(!eventDispatchForbidden());
289 ASSERT(event->target());
290 ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null.
292 RefPtr<EventTarget> originalTarget = event->target();
293 ensureEventAncestors(event.get());
295 WindowEventContext windowContext(event.get(), m_node.get(), topEventContext());
297 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(m_node->document(), *event, windowContext.window(), m_node.get(), m_ancestors);
299 // Give the target node a chance to do some work before DOM event handlers get a crack.
300 void* data = m_node->preDispatchEventHandler(event.get());
301 if (m_shouldPreventDispatch || event->propagationStopped())
302 goto doneDispatching;
304 // Trigger capturing event handlers, starting at the top and working our way down.
305 event->setEventPhase(Event::CAPTURING_PHASE);
307 if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped())
308 goto doneDispatching;
310 for (size_t i = m_ancestors.size(); i; --i) {
311 m_ancestors[i - 1].handleLocalEvents(event.get());
312 if (event->propagationStopped())
313 goto doneDispatching;
316 event->setEventPhase(Event::AT_TARGET);
317 event->setTarget(originalTarget.get());
318 event->setCurrentTarget(eventTargetRespectingSVGTargetRules(m_node.get()));
319 m_node->handleLocalEvents(event.get());
320 if (event->propagationStopped())
321 goto doneDispatching;
323 if (event->bubbles() && !event->cancelBubble()) {
324 // Trigger bubbling event handlers, starting at the bottom and working our way up.
325 event->setEventPhase(Event::BUBBLING_PHASE);
327 size_t size = m_ancestors.size();
328 for (size_t i = 0; i < size; ++i) {
329 m_ancestors[i].handleLocalEvents(event.get());
330 if (event->propagationStopped() || event->cancelBubble())
331 goto doneDispatching;
333 windowContext.handleLocalEvents(event.get());
337 event->setTarget(originalTarget.get());
338 event->setCurrentTarget(0);
339 event->setEventPhase(0);
341 // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler.
342 m_node->postDispatchEventHandler(event.get(), data);
344 // Call default event handlers. While the DOM does have a concept of preventing
345 // default handling, the detail of which handlers are called is an internal
346 // implementation detail and not part of the DOM.
347 if (!event->defaultPrevented() && !event->defaultHandled()) {
348 // Non-bubbling events call only one default event handler, the one for the target.
349 m_node->defaultEventHandler(event.get());
350 ASSERT(!event->defaultPrevented());
351 if (event->defaultHandled())
352 goto doneWithDefault;
353 // For bubbling events, call default event handlers on the same targets in the
354 // same order as the bubbling phase.
355 if (event->bubbles()) {
356 size_t size = m_ancestors.size();
357 for (size_t i = 0; i < size; ++i) {
358 m_ancestors[i].node()->defaultEventHandler(event.get());
359 ASSERT(!event->defaultPrevented());
360 if (event->defaultHandled())
361 goto doneWithDefault;
368 // Ensure that after event dispatch, the event's target object is the
369 // outermost shadow DOM boundary.
370 event->setTarget(windowContext.target());
371 event->setCurrentTarget(0);
372 InspectorInstrumentation::didDispatchEvent(cookie);
374 return !event->defaultPrevented();
377 const EventContext* EventDispatcher::topEventContext()
379 return m_ancestors.isEmpty() ? 0 : &m_ancestors.last();
382 EventDispatchBehavior EventDispatcher::determineDispatchBehavior(Event* event, Node* shadowRoot)
384 #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
385 // Video-only full screen is a mode where we use the shadow DOM as an implementation
386 // detail that should not be detectable by the web content.
387 if (Element* element = m_node->document()->webkitCurrentFullScreenElement()) {
388 // FIXME: We assume that if the full screen element is a media element that it's
389 // the video-only full screen. Both here and elsewhere. But that is probably wrong.
390 if (element->isMediaElement() && shadowRoot && shadowRoot->shadowHost() == element)
391 return StayInsideShadowDOM;
394 UNUSED_PARAM(shadowRoot);
397 // Per XBL 2.0 spec, mutation events should never cross shadow DOM boundary:
398 // http://dev.w3.org/2006/xbl2/#event-flow-and-targeting-across-shadow-s
399 if (event->isMutationEvent())
400 return StayInsideShadowDOM;
402 // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
403 // Changing this breaks existing sites.
404 // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
405 if (event->type() == eventNames().selectstartEvent)
406 return StayInsideShadowDOM;
408 return RetargetEvent;