Diff
Modified: trunk/Source/WebCore/CMakeLists.txt (197923 => 197924)
--- trunk/Source/WebCore/CMakeLists.txt 2016-03-10 05:26:25 UTC (rev 197923)
+++ trunk/Source/WebCore/CMakeLists.txt 2016-03-10 05:27:07 UTC (rev 197924)
@@ -1449,6 +1449,7 @@
dom/EventDispatcher.cpp
dom/EventListenerMap.cpp
dom/EventNames.cpp
+ dom/EventPath.cpp
dom/EventTarget.cpp
dom/ExceptionBase.cpp
dom/ExceptionCodePlaceholder.cpp
Modified: trunk/Source/WebCore/ChangeLog (197923 => 197924)
--- trunk/Source/WebCore/ChangeLog 2016-03-10 05:26:25 UTC (rev 197923)
+++ trunk/Source/WebCore/ChangeLog 2016-03-10 05:27:07 UTC (rev 197924)
@@ -1,3 +1,36 @@
+2016-03-09 Ryosuke Niwa <[email protected]>
+
+ Extract EventPath.h/cpp out of EventDispatcher.cpp
+ https://bugs.webkit.org/show_bug.cgi?id=155285
+
+ Reviewed by Chris Dumez.
+
+ Extracted EventPath.h/cpp out of EventDispatcher.cpp to add the support for Event.deepPath()
+ in webkit.org/b/153538. The new file defines member functions of EventPath and RelatedNodeRetargeter.
+
+ * CMakeLists.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * dom/DOMAllInOne.cpp:
+ * dom/EventDispatcher.cpp:
+ (WebCore::EventDispatcher::dispatchScopedEvent):
+ (WebCore::EventDispatcher::dispatchEvent):
+ (WebCore::EventPath): Moved to EventPath.cpp.
+ (WebCore::eventTargetRespectingTargetRules): Moved to EventPath.h.
+ (WebCore::shouldEventCrossShadowBoundary): Moved to EventPath.cpp.
+ (WebCore::nodeOrHostIfPseudoElement): Ditto.
+ (WebCore::RelatedNodeRetargeter): Moved to EventPath.cpp.
+ * dom/EventPath.cpp: Added.
+ (WebCore::shouldEventCrossShadowBoundary): Moved from EventDispatcher.cpp.
+ (WebCore::nodeOrHostIfPseudoElement): Ditto.
+ (WebCore::EventPath::EventPath): Ditto.
+ (WebCore::RelatedNodeRetargeter): Ditto.
+ * dom/EventPath.h: Added.
+ (WebCore::EventPath::isEmpty):
+ (WebCore::EventPath::size):
+ (WebCore::EventPath::contextAt):
+ (WebCore::EventPath::lastContextIfExists):
+ (WebCore::EventPath::eventTargetRespectingTargetRules): Moved from EventDispatcher.cpp.
+
2016-03-09 Simon Fraser <[email protected]>
Font antialiasing (smoothing) changes when elements are rendered into compositing layers
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (197923 => 197924)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-03-10 05:26:25 UTC (rev 197923)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-03-10 05:27:07 UTC (rev 197924)
@@ -4036,6 +4036,8 @@
9B69D3B81B99100700E3512B /* JSHTMLSlotElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B69D3B61B99100700E3512B /* JSHTMLSlotElement.cpp */; };
9B69D3B91B99100700E3512B /* JSHTMLSlotElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B69D3B71B99100700E3512B /* JSHTMLSlotElement.h */; };
9B6C41531344949000085B62 /* StringWithDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B6C41521344949000085B62 /* StringWithDirection.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 9B714E201C91166900AC0E92 /* EventPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B714E1E1C91166900AC0E92 /* EventPath.cpp */; };
+ 9B714E211C91166900AC0E92 /* EventPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B714E1F1C91166900AC0E92 /* EventPath.h */; };
9BA273F4172206BB0097CE47 /* LogicalSelectionOffsetCaches.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BA273F3172206BB0097CE47 /* LogicalSelectionOffsetCaches.h */; };
9BAB6C6C12550631001626D4 /* EditingStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BAB6C6A12550631001626D4 /* EditingStyle.h */; settings = {ATTRIBUTES = (Private, ); }; };
9BAB6C6D12550631001626D4 /* EditingStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB6C6B12550631001626D4 /* EditingStyle.cpp */; };
@@ -11738,6 +11740,8 @@
9B6BC9601B975966005AE1F0 /* JSShadowRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSShadowRoot.cpp; sourceTree = "<group>"; };
9B6BC9611B975966005AE1F0 /* JSShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSShadowRoot.h; sourceTree = "<group>"; };
9B6C41521344949000085B62 /* StringWithDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringWithDirection.h; sourceTree = "<group>"; };
+ 9B714E1E1C91166900AC0E92 /* EventPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventPath.cpp; sourceTree = "<group>"; };
+ 9B714E1F1C91166900AC0E92 /* EventPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventPath.h; sourceTree = "<group>"; };
9BA273F3172206BB0097CE47 /* LogicalSelectionOffsetCaches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogicalSelectionOffsetCaches.h; sourceTree = "<group>"; };
9BAB6C6A12550631001626D4 /* EditingStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditingStyle.h; sourceTree = "<group>"; };
9BAB6C6B12550631001626D4 /* EditingStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditingStyle.cpp; sourceTree = "<group>"; };
@@ -24344,6 +24348,8 @@
939885C108B7E3D100E707C4 /* EventNames.cpp */,
939885C208B7E3D100E707C4 /* EventNames.h */,
BC9A6142146859D9006057FD /* EventNames.in */,
+ 9B714E1E1C91166900AC0E92 /* EventPath.cpp */,
+ 9B714E1F1C91166900AC0E92 /* EventPath.h */,
8F6756191288B17B0047ACA3 /* EventQueue.h */,
CE5CB1B314EDAB6F00BB2795 /* EventSender.h */,
E12EDBE90B308E0B002704B6 /* EventTarget.cpp */,
@@ -27619,6 +27625,7 @@
BCA846D70DC67A350026C309 /* RenderReplica.h in Headers */,
1479FAEE109AE37500DED655 /* RenderRuby.h in Headers */,
1479FAF0109AE37500DED655 /* RenderRubyBase.h in Headers */,
+ 9B714E211C91166900AC0E92 /* EventPath.h in Headers */,
1479FAF2109AE37500DED655 /* RenderRubyRun.h in Headers */,
1479FAF4109AE37500DED655 /* RenderRubyText.h in Headers */,
BC3BE9940E9C1C7C00835588 /* RenderScrollbar.h in Headers */,
@@ -29406,6 +29413,7 @@
59A8F1D411A69508001AC34A /* DeviceOrientationController.cpp in Sources */,
59D1C10411EB5DCF00B638C8 /* DeviceOrientationData.cpp in Sources */,
59A85EA2119D68D900DEF1EF /* DeviceOrientationEvent.cpp in Sources */,
+ 9B714E201C91166900AC0E92 /* EventPath.cpp in Sources */,
267725FC1A5B3AD9003C24DD /* DFA.cpp in Sources */,
5C9A7A751AA0F6EA00958ACF /* DFABytecodeCompiler.cpp in Sources */,
5C9A7A761AA0F6ED00958ACF /* DFABytecodeInterpreter.cpp in Sources */,
Modified: trunk/Source/WebCore/dom/DOMAllInOne.cpp (197923 => 197924)
--- trunk/Source/WebCore/dom/DOMAllInOne.cpp 2016-03-10 05:26:25 UTC (rev 197923)
+++ trunk/Source/WebCore/dom/DOMAllInOne.cpp 2016-03-10 05:27:07 UTC (rev 197924)
@@ -83,6 +83,7 @@
#include "EventDispatcher.cpp"
#include "EventListenerMap.cpp"
#include "EventNames.cpp"
+#include "EventPath.cpp"
#include "EventTarget.cpp"
#include "ExceptionBase.cpp"
#include "ExceptionCodePlaceholder.cpp"
Modified: trunk/Source/WebCore/dom/EventDispatcher.cpp (197923 => 197924)
--- trunk/Source/WebCore/dom/EventDispatcher.cpp 2016-03-10 05:26:25 UTC (rev 197923)
+++ trunk/Source/WebCore/dom/EventDispatcher.cpp 2016-03-10 05:27:07 UTC (rev 197924)
@@ -27,17 +27,13 @@
#include "EventDispatcher.h"
#include "EventContext.h"
-#include "FocusEvent.h"
+#include "EventPath.h"
#include "FrameView.h"
#include "HTMLInputElement.h"
-#include "HTMLMediaElement.h"
-#include "HTMLSlotElement.h"
#include "MouseEvent.h"
#include "PseudoElement.h"
#include "ScopedEventQueue.h"
#include "ShadowRoot.h"
-#include "SVGNames.h"
-#include "SVGUseElement.h"
#include "TouchEvent.h"
#include <wtf/NeverDestroyed.h>
@@ -77,51 +73,10 @@
return true;
}
-class EventPath {
-public:
- EventPath(Node& origin, Event&);
-
- bool isEmpty() const { return m_path.isEmpty(); }
- size_t size() const { return m_path.size(); }
- const EventContext& contextAt(size_t i) const { return *m_path[i]; }
- EventContext& contextAt(size_t i) { return *m_path[i]; }
-
-#if ENABLE(TOUCH_EVENTS)
- void retargetTouchLists(const TouchEvent&);
-#endif
- void setRelatedTarget(Node& origin, EventTarget&);
-
- bool hasEventListeners(const AtomicString& eventType) const;
-
- EventContext* lastContextIfExists() { return m_path.isEmpty() ? nullptr : m_path.last().get(); }
-
-private:
-#if ENABLE(TOUCH_EVENTS)
- void retargetTouch(TouchEventContext::TouchListType, const Touch&);
-#endif
-
- Event& m_event;
- Vector<std::unique_ptr<EventContext>, 32> m_path;
-};
-
-inline EventTarget* eventTargetRespectingTargetRules(Node& referenceNode)
-{
- if (is<PseudoElement>(referenceNode))
- return downcast<PseudoElement>(referenceNode).hostElement();
-
- // Events sent to elements inside an SVG use element's shadow tree go to the use element.
- if (is<SVGElement>(referenceNode)) {
- if (auto* useElement = downcast<SVGElement>(referenceNode).correspondingUseElement())
- return useElement;
- }
-
- return &referenceNode;
-}
-
void EventDispatcher::dispatchScopedEvent(Node& node, Event& event)
{
// We need to set the target here because it can go away by the time we actually fire the event.
- event.setTarget(eventTargetRespectingTargetRules(node));
+ event.setTarget(EventPath::eventTargetRespectingTargetRules(node));
ScopedEventQueue::singleton().enqueueEvent(event);
}
@@ -209,7 +164,7 @@
ChildNodesLazySnapshot::takeChildNodesLazySnapshot();
- EventTarget* target = eventTargetRespectingTargetRules(*node);
+ EventTarget* target = EventPath::eventTargetRespectingTargetRules(*node);
event.setTarget(target);
if (!event.target())
return true;
@@ -225,7 +180,7 @@
if (!event.propagationStopped() && !eventPath.isEmpty())
dispatchEventInDOM(event, eventPath, windowEventContext);
- event.setTarget(eventTargetRespectingTargetRules(*node));
+ event.setTarget(EventPath::eventTargetRespectingTargetRules(*node));
event.setCurrentTarget(nullptr);
event.setEventPhase(0);
@@ -246,325 +201,4 @@
return !event.defaultPrevented();
}
-static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target)
-{
- Node* targetNode = target.toNode();
-#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
- // Video-only full screen is a mode where we use the shadow DOM as an implementation
- // detail that should not be detectable by the web content.
- if (targetNode) {
- if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) {
- // FIXME: We assume that if the full screen element is a media element that it's
- // the video-only full screen. Both here and elsewhere. But that is probably wrong.
- if (element->isMediaElement() && shadowRoot.host() == element)
- return false;
- }
- }
-#endif
-
- // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
- // Changing this breaks existing sites.
- // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
- const AtomicString& eventType = event.type();
- bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot;
- return !targetIsInShadowRoot
- || !(eventType == eventNames().abortEvent
- || eventType == eventNames().changeEvent
- || eventType == eventNames().errorEvent
- || eventType == eventNames().loadEvent
- || eventType == eventNames().resetEvent
- || eventType == eventNames().resizeEvent
- || eventType == eventNames().scrollEvent
- || eventType == eventNames().selectEvent
- || eventType == eventNames().selectstartEvent);
}
-
-static Node* nodeOrHostIfPseudoElement(Node* node)
-{
- return is<PseudoElement>(*node) ? downcast<PseudoElement>(*node).hostElement() : node;
-}
-
-EventPath::EventPath(Node& originalTarget, Event& event)
- : m_event(event)
-{
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
- Vector<EventTarget*, 16> targetStack;
-#endif
-
- bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent();
-#if ENABLE(TOUCH_EVENTS)
- bool isTouchEvent = event.isTouchEvent();
-#endif
- EventTarget* target = nullptr;
-
- Node* node = nodeOrHostIfPseudoElement(&originalTarget);
- while (node) {
- if (!target)
- target = eventTargetRespectingTargetRules(*node);
- ContainerNode* parent;
- for (; node; node = parent) {
- EventTarget* currentTarget = eventTargetRespectingTargetRules(*node);
- if (isMouseOrFocusEvent)
- m_path.append(std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target));
-#if ENABLE(TOUCH_EVENTS)
- else if (isTouchEvent)
- m_path.append(std::make_unique<TouchEventContext>(node, currentTarget, target));
-#endif
- else
- m_path.append(std::make_unique<EventContext>(node, currentTarget, target));
- if (is<ShadowRoot>(*node))
- break;
- parent = node->parentNode();
- if (!parent)
- return;
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
- if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) {
- if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) {
- // node is assigned to a slot. Continue dispatching the event at this slot.
- targetStack.append(target);
- parent = assignedSlot;
- target = assignedSlot;
- }
- }
-#endif
- node = parent;
- }
-
- ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node);
- // At a shadow root. Continue dispatching the event at the shadow host.
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
- if (!targetStack.isEmpty()) {
- // Move target back to a descendant of the shadow host if the event did not originate in this shadow tree or its inner shadow trees.
- target = targetStack.last();
- targetStack.removeLast();
- ASSERT(shadowRoot.host()->contains(target->toNode()));
- } else
-#endif
- target = nullptr;
-
- if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget))
- return;
- node = shadowRoot.host();
- }
-}
-
-class RelatedNodeRetargeter {
-public:
- RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope)
- : m_relatedNode(relatedNode)
- , m_retargetedRelatedNode(&relatedNode)
- {
- TreeScope* currentTreeScope = &m_relatedNode.treeScope();
- if (LIKELY(currentTreeScope == &targetTreeScope))
- return;
-
- if (¤tTreeScope->documentScope() != &targetTreeScope.documentScope()) {
- m_hasDifferentTreeRoot = true;
- m_retargetedRelatedNode = nullptr;
- return;
- }
- if (relatedNode.inDocument() != targetTreeScope.rootNode().inDocument()) {
- m_hasDifferentTreeRoot = true;
- while (m_retargetedRelatedNode->isInShadowTree())
- m_retargetedRelatedNode = downcast<ShadowRoot>(m_retargetedRelatedNode->treeScope().rootNode()).host();
- return;
- }
-
- collectTreeScopes();
-
- // FIXME: We should collect this while constructing the event path.
- Vector<TreeScope*, 8> targetTreeScopeAncestors;
- for (TreeScope* currentTreeScope = &targetTreeScope; currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope())
- targetTreeScopeAncestors.append(currentTreeScope);
- ASSERT_WITH_SECURITY_IMPLICATION(!targetTreeScopeAncestors.isEmpty());
-
- unsigned i = m_ancestorTreeScopes.size();
- unsigned j = targetTreeScopeAncestors.size();
- ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.last() == targetTreeScopeAncestors.last());
- while (m_ancestorTreeScopes[i - 1] == targetTreeScopeAncestors[j - 1]) {
- i--;
- j--;
- if (!i || !j)
- break;
- }
-
- m_lowestCommonAncestorIndex = i;
- m_retargetedRelatedNode = nodeInLowestCommonAncestor();
- }
-
- Node* currentNode(TreeScope& currentTreeScope)
- {
- checkConsistency(currentTreeScope);
- return m_retargetedRelatedNode;
- }
-
- void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope)
- {
- if (m_hasDifferentTreeRoot)
- return;
-
- auto& currentRelatedNodeScope = m_retargetedRelatedNode->treeScope();
- if (previousTreeScope != ¤tRelatedNodeScope) {
- // currentRelatedNode is still outside our shadow tree. New tree scope may contain currentRelatedNode
- // but there is no need to re-target it. Moving into a slot (thereby a deeper shadow tree) doesn't matter.
- return;
- }
-
- bool enteredSlot = newTreeScope.parentTreeScope() == previousTreeScope;
- if (enteredSlot) {
- if (m_lowestCommonAncestorIndex) {
- if (m_ancestorTreeScopes.isEmpty())
- collectTreeScopes();
- bool relatedNodeIsInSlot = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1] == &newTreeScope;
- if (relatedNodeIsInSlot) {
- m_lowestCommonAncestorIndex--;
- m_retargetedRelatedNode = nodeInLowestCommonAncestor();
- ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope());
- }
- } else
- ASSERT(m_retargetedRelatedNode == &m_relatedNode);
- } else {
- ASSERT(previousTreeScope->parentTreeScope() == &newTreeScope);
- m_lowestCommonAncestorIndex++;
- ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.isEmpty() || m_lowestCommonAncestorIndex < m_ancestorTreeScopes.size());
- m_retargetedRelatedNode = downcast<ShadowRoot>(currentRelatedNodeScope.rootNode()).host();
- ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope());
- }
- }
-
- void checkConsistency(TreeScope& currentTreeScope)
- {
-#if !ASSERT_DISABLED
- for (auto* relatedNodeScope = &m_relatedNode.treeScope(); relatedNodeScope; relatedNodeScope = relatedNodeScope->parentTreeScope()) {
- for (auto* targetScope = ¤tTreeScope; targetScope; targetScope = targetScope->parentTreeScope()) {
- if (targetScope == relatedNodeScope) {
- ASSERT(&m_retargetedRelatedNode->treeScope() == relatedNodeScope);
- return;
- }
- }
- }
- ASSERT(!m_retargetedRelatedNode);
-#else
- UNUSED_PARAM(currentTreeScope);
-#endif
- }
-
-private:
- Node* nodeInLowestCommonAncestor()
- {
- if (!m_lowestCommonAncestorIndex)
- return &m_relatedNode;
- auto& rootNode = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1]->rootNode();
- return downcast<ShadowRoot>(rootNode).host();
- }
-
- void collectTreeScopes()
- {
- ASSERT(m_ancestorTreeScopes.isEmpty());
- for (TreeScope* currentTreeScope = &m_relatedNode.treeScope(); currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope())
- m_ancestorTreeScopes.append(currentTreeScope);
- ASSERT_WITH_SECURITY_IMPLICATION(!m_ancestorTreeScopes.isEmpty());
- }
-
- Node& m_relatedNode;
- Node* m_retargetedRelatedNode;
- Vector<TreeScope*, 8> m_ancestorTreeScopes;
- unsigned m_lowestCommonAncestorIndex { 0 };
- bool m_hasDifferentTreeRoot { false };
-};
-
-void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget)
-{
- Node* relatedNode = relatedTarget.toNode();
- if (!relatedNode || m_path.isEmpty())
- return;
-
- RelatedNodeRetargeter retargeter(*relatedNode, downcast<MouseOrFocusEventContext>(*m_path[0]).node()->treeScope());
-
- bool originIsRelatedTarget = &origin == relatedNode;
- // FIXME: We should add a new flag on Event instead.
- bool shouldTrimEventPath = m_event.type() == eventNames().mouseoverEvent
- || m_event.type() == eventNames().mousemoveEvent
- || m_event.type() == eventNames().mouseoutEvent;
- Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode();
- TreeScope* previousTreeScope = nullptr;
- size_t originalEventPathSize = m_path.size();
- for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) {
- auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
-
- TreeScope& currentTreeScope = context.node()->treeScope();
- if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
- retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
-
- Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
- if (UNLIKELY(shouldTrimEventPath && !originIsRelatedTarget && context.target() == currentRelatedNode)) {
- m_path.shrink(contextIndex);
- break;
- }
-
- context.setRelatedTarget(currentRelatedNode);
-
- if (UNLIKELY(shouldTrimEventPath && originIsRelatedTarget && context.node() == &rootNodeInOriginTreeScope)) {
- m_path.shrink(contextIndex + 1);
- break;
- }
-
- previousTreeScope = ¤tTreeScope;
- }
-}
-
-#if ENABLE(TOUCH_EVENTS)
-void EventPath::retargetTouch(TouchEventContext::TouchListType touchListType, const Touch& touch)
-{
- EventTarget* eventTarget = touch.target();
- if (!eventTarget)
- return;
-
- Node* targetNode = eventTarget->toNode();
- if (!targetNode)
- return;
-
- RelatedNodeRetargeter retargeter(*targetNode, m_path[0]->node()->treeScope());
- TreeScope* previousTreeScope = nullptr;
- for (auto& context : m_path) {
- TreeScope& currentTreeScope = context->node()->treeScope();
- if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
- retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
-
- Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
- downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
-
- previousTreeScope = ¤tTreeScope;
- }
-}
-
-void EventPath::retargetTouchLists(const TouchEvent& touchEvent)
-{
- if (touchEvent.touches()) {
- for (size_t i = 0; i < touchEvent.touches()->length(); ++i)
- retargetTouch(TouchEventContext::Touches, *touchEvent.touches()->item(i));
- }
-
- if (touchEvent.targetTouches()) {
- for (size_t i = 0; i < touchEvent.targetTouches()->length(); ++i)
- retargetTouch(TouchEventContext::TargetTouches, *touchEvent.targetTouches()->item(i));
- }
-
- if (touchEvent.changedTouches()) {
- for (size_t i = 0; i < touchEvent.changedTouches()->length(); ++i)
- retargetTouch(TouchEventContext::ChangedTouches, *touchEvent.changedTouches()->item(i));
- }
-}
-#endif
-
-bool EventPath::hasEventListeners(const AtomicString& eventType) const
-{
- for (auto& eventPath : m_path) {
- if (eventPath->node()->hasEventListeners(eventType))
- return true;
- }
-
- return false;
-}
-
-}
Added: trunk/Source/WebCore/dom/EventPath.cpp (0 => 197924)
--- trunk/Source/WebCore/dom/EventPath.cpp (rev 0)
+++ trunk/Source/WebCore/dom/EventPath.cpp 2016-03-10 05:27:07 UTC (rev 197924)
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "EventPath.h"
+
+#include "Event.h"
+#include "EventContext.h"
+#include "EventNames.h"
+#include "HTMLSlotElement.h"
+#include "Node.h"
+#include "PseudoElement.h"
+#include "ShadowRoot.h"
+#include "TouchEvent.h"
+
+namespace WebCore {
+
+static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target)
+{
+ Node* targetNode = target.toNode();
+
+#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
+ // Video-only full screen is a mode where we use the shadow DOM as an implementation
+ // detail that should not be detectable by the web content.
+ if (targetNode) {
+ if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) {
+ // FIXME: We assume that if the full screen element is a media element that it's
+ // the video-only full screen. Both here and elsewhere. But that is probably wrong.
+ if (element->isMediaElement() && shadowRoot.host() == element)
+ return false;
+ }
+ }
+#endif
+
+ // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
+ // Changing this breaks existing sites.
+ // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
+ const AtomicString& eventType = event.type();
+ bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot;
+ return !targetIsInShadowRoot
+ || !(eventType == eventNames().abortEvent
+ || eventType == eventNames().changeEvent
+ || eventType == eventNames().errorEvent
+ || eventType == eventNames().loadEvent
+ || eventType == eventNames().resetEvent
+ || eventType == eventNames().resizeEvent
+ || eventType == eventNames().scrollEvent
+ || eventType == eventNames().selectEvent
+ || eventType == eventNames().selectstartEvent);
+}
+
+static Node* nodeOrHostIfPseudoElement(Node* node)
+{
+ return is<PseudoElement>(*node) ? downcast<PseudoElement>(*node).hostElement() : node;
+}
+
+class RelatedNodeRetargeter {
+public:
+ RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope);
+
+ Node* currentNode(TreeScope& currentTreeScope);
+ void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope);
+
+private:
+
+ Node* nodeInLowestCommonAncestor();
+ void collectTreeScopes();
+
+#if ASSERT_DISABLED
+ void checkConsistency(TreeScope&) { }
+#else
+ void checkConsistency(TreeScope& currentTreeScope);
+#endif
+
+ Node& m_relatedNode;
+ Node* m_retargetedRelatedNode;
+ Vector<TreeScope*, 8> m_ancestorTreeScopes;
+ unsigned m_lowestCommonAncestorIndex { 0 };
+ bool m_hasDifferentTreeRoot { false };
+};
+
+EventPath::EventPath(Node& originalTarget, Event& event)
+ : m_event(event)
+{
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+ Vector<EventTarget*, 16> targetStack;
+#endif
+
+ bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent();
+#if ENABLE(TOUCH_EVENTS)
+ bool isTouchEvent = event.isTouchEvent();
+#endif
+ EventTarget* target = nullptr;
+
+ Node* node = nodeOrHostIfPseudoElement(&originalTarget);
+ while (node) {
+ if (!target)
+ target = eventTargetRespectingTargetRules(*node);
+ ContainerNode* parent;
+ for (; node; node = parent) {
+ EventTarget* currentTarget = eventTargetRespectingTargetRules(*node);
+
+ if (isMouseOrFocusEvent)
+ m_path.append(std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target));
+#if ENABLE(TOUCH_EVENTS)
+ else if (isTouchEvent)
+ m_path.append(std::make_unique<TouchEventContext>(node, currentTarget, target));
+#endif
+ else
+ m_path.append(std::make_unique<EventContext>(node, currentTarget, target));
+
+ if (is<ShadowRoot>(*node))
+ break;
+
+ parent = node->parentNode();
+
+ if (!parent)
+ return;
+
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+ if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) {
+ if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) {
+ // node is assigned to a slot. Continue dispatching the event at this slot.
+ targetStack.append(target);
+ parent = assignedSlot;
+ target = assignedSlot;
+ }
+ }
+#endif
+ node = parent;
+ }
+
+ ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node);
+ // At a shadow root. Continue dispatching the event at the shadow host.
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+ if (!targetStack.isEmpty()) {
+ // Move target back to a descendant of the shadow host if the event did not originate in this shadow tree or its inner shadow trees.
+ target = targetStack.last();
+ targetStack.removeLast();
+ ASSERT(shadowRoot.host()->contains(target->toNode()));
+ } else
+#endif
+ target = nullptr;
+
+ if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget))
+ return;
+ node = shadowRoot.host();
+ }
+}
+
+void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget)
+{
+ Node* relatedNode = relatedTarget.toNode();
+ if (!relatedNode || m_path.isEmpty())
+ return;
+
+ RelatedNodeRetargeter retargeter(*relatedNode, downcast<MouseOrFocusEventContext>(*m_path[0]).node()->treeScope());
+
+ bool originIsRelatedTarget = &origin == relatedNode;
+ // FIXME: We should add a new flag on Event instead.
+ bool shouldTrimEventPath = m_event.type() == eventNames().mouseoverEvent
+ || m_event.type() == eventNames().mousemoveEvent
+ || m_event.type() == eventNames().mouseoutEvent;
+ Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode();
+ TreeScope* previousTreeScope = nullptr;
+ size_t originalEventPathSize = m_path.size();
+ for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) {
+ auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
+
+ TreeScope& currentTreeScope = context.node()->treeScope();
+ if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
+ retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
+
+ Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
+ if (UNLIKELY(shouldTrimEventPath && !originIsRelatedTarget && context.target() == currentRelatedNode)) {
+ m_path.shrink(contextIndex);
+ break;
+ }
+
+ context.setRelatedTarget(currentRelatedNode);
+
+ if (UNLIKELY(shouldTrimEventPath && originIsRelatedTarget && context.node() == &rootNodeInOriginTreeScope)) {
+ m_path.shrink(contextIndex + 1);
+ break;
+ }
+
+ previousTreeScope = ¤tTreeScope;
+ }
+}
+
+#if ENABLE(TOUCH_EVENTS)
+void EventPath::retargetTouch(TouchEventContext::TouchListType touchListType, const Touch& touch)
+{
+ EventTarget* eventTarget = touch.target();
+ if (!eventTarget)
+ return;
+
+ Node* targetNode = eventTarget->toNode();
+ if (!targetNode)
+ return;
+
+ RelatedNodeRetargeter retargeter(*targetNode, m_path[0]->node()->treeScope());
+ TreeScope* previousTreeScope = nullptr;
+ for (auto& context : m_path) {
+ TreeScope& currentTreeScope = context->node()->treeScope();
+ if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope))
+ retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
+
+ Node* currentRelatedNode = retargeter.currentNode(currentTreeScope);
+ downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
+
+ previousTreeScope = ¤tTreeScope;
+ }
+}
+
+void EventPath::retargetTouchLists(const TouchEvent& touchEvent)
+{
+ if (touchEvent.touches()) {
+ for (size_t i = 0; i < touchEvent.touches()->length(); ++i)
+ retargetTouch(TouchEventContext::Touches, *touchEvent.touches()->item(i));
+ }
+
+ if (touchEvent.targetTouches()) {
+ for (size_t i = 0; i < touchEvent.targetTouches()->length(); ++i)
+ retargetTouch(TouchEventContext::TargetTouches, *touchEvent.targetTouches()->item(i));
+ }
+
+ if (touchEvent.changedTouches()) {
+ for (size_t i = 0; i < touchEvent.changedTouches()->length(); ++i)
+ retargetTouch(TouchEventContext::ChangedTouches, *touchEvent.changedTouches()->item(i));
+ }
+}
+#endif
+
+bool EventPath::hasEventListeners(const AtomicString& eventType) const
+{
+ for (auto& eventPath : m_path) {
+ if (eventPath->node()->hasEventListeners(eventType))
+ return true;
+ }
+
+ return false;
+}
+
+RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, TreeScope& targetTreeScope)
+ : m_relatedNode(relatedNode)
+ , m_retargetedRelatedNode(&relatedNode)
+{
+ TreeScope* currentTreeScope = &m_relatedNode.treeScope();
+ if (LIKELY(currentTreeScope == &targetTreeScope))
+ return;
+
+ if (¤tTreeScope->documentScope() != &targetTreeScope.documentScope()) {
+ m_hasDifferentTreeRoot = true;
+ m_retargetedRelatedNode = nullptr;
+ return;
+ }
+ if (relatedNode.inDocument() != targetTreeScope.rootNode().inDocument()) {
+ m_hasDifferentTreeRoot = true;
+ while (m_retargetedRelatedNode->isInShadowTree())
+ m_retargetedRelatedNode = downcast<ShadowRoot>(m_retargetedRelatedNode->treeScope().rootNode()).host();
+ return;
+ }
+
+ collectTreeScopes();
+
+ // FIXME: We should collect this while constructing the event path.
+ Vector<TreeScope*, 8> targetTreeScopeAncestors;
+ for (TreeScope* currentTreeScope = &targetTreeScope; currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope())
+ targetTreeScopeAncestors.append(currentTreeScope);
+ ASSERT_WITH_SECURITY_IMPLICATION(!targetTreeScopeAncestors.isEmpty());
+
+ unsigned i = m_ancestorTreeScopes.size();
+ unsigned j = targetTreeScopeAncestors.size();
+ ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.last() == targetTreeScopeAncestors.last());
+ while (m_ancestorTreeScopes[i - 1] == targetTreeScopeAncestors[j - 1]) {
+ i--;
+ j--;
+ if (!i || !j)
+ break;
+ }
+
+ m_lowestCommonAncestorIndex = i;
+ m_retargetedRelatedNode = nodeInLowestCommonAncestor();
+}
+
+inline Node* RelatedNodeRetargeter::currentNode(TreeScope& currentTreeScope)
+{
+ checkConsistency(currentTreeScope);
+ return m_retargetedRelatedNode;
+}
+
+void RelatedNodeRetargeter::moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope)
+{
+ if (m_hasDifferentTreeRoot)
+ return;
+
+ auto& currentRelatedNodeScope = m_retargetedRelatedNode->treeScope();
+ if (previousTreeScope != ¤tRelatedNodeScope) {
+ // currentRelatedNode is still outside our shadow tree. New tree scope may contain currentRelatedNode
+ // but there is no need to re-target it. Moving into a slot (thereby a deeper shadow tree) doesn't matter.
+ return;
+ }
+
+ bool enteredSlot = newTreeScope.parentTreeScope() == previousTreeScope;
+ if (enteredSlot) {
+ if (m_lowestCommonAncestorIndex) {
+ if (m_ancestorTreeScopes.isEmpty())
+ collectTreeScopes();
+ bool relatedNodeIsInSlot = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1] == &newTreeScope;
+ if (relatedNodeIsInSlot) {
+ m_lowestCommonAncestorIndex--;
+ m_retargetedRelatedNode = nodeInLowestCommonAncestor();
+ ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope());
+ }
+ } else
+ ASSERT(m_retargetedRelatedNode == &m_relatedNode);
+ } else {
+ ASSERT(previousTreeScope->parentTreeScope() == &newTreeScope);
+ m_lowestCommonAncestorIndex++;
+ ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.isEmpty() || m_lowestCommonAncestorIndex < m_ancestorTreeScopes.size());
+ m_retargetedRelatedNode = downcast<ShadowRoot>(currentRelatedNodeScope.rootNode()).host();
+ ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope());
+ }
+}
+
+inline Node* RelatedNodeRetargeter::nodeInLowestCommonAncestor()
+{
+ if (!m_lowestCommonAncestorIndex)
+ return &m_relatedNode;
+ auto& rootNode = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1]->rootNode();
+ return downcast<ShadowRoot>(rootNode).host();
+}
+
+void RelatedNodeRetargeter::collectTreeScopes()
+{
+ ASSERT(m_ancestorTreeScopes.isEmpty());
+ for (TreeScope* currentTreeScope = &m_relatedNode.treeScope(); currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope())
+ m_ancestorTreeScopes.append(currentTreeScope);
+ ASSERT_WITH_SECURITY_IMPLICATION(!m_ancestorTreeScopes.isEmpty());
+}
+
+#if !ASSERT_DISABLED
+void RelatedNodeRetargeter::checkConsistency(TreeScope& currentTreeScope)
+{
+ for (auto* relatedNodeScope = &m_relatedNode.treeScope(); relatedNodeScope; relatedNodeScope = relatedNodeScope->parentTreeScope()) {
+ for (auto* targetScope = ¤tTreeScope; targetScope; targetScope = targetScope->parentTreeScope()) {
+ if (targetScope == relatedNodeScope) {
+ ASSERT(&m_retargetedRelatedNode->treeScope() == relatedNodeScope);
+ return;
+ }
+ }
+ }
+ ASSERT(!m_retargetedRelatedNode);
+}
+#endif
+
+}
Added: trunk/Source/WebCore/dom/EventPath.h (0 => 197924)
--- trunk/Source/WebCore/dom/EventPath.h (rev 0)
+++ trunk/Source/WebCore/dom/EventPath.h 2016-03-10 05:27:07 UTC (rev 197924)
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef EventPath_h
+#define EventPath_h
+
+#include "EventContext.h"
+#include "PseudoElement.h"
+#include "SVGElement.h"
+#include "SVGUseElement.h"
+#include <wtf/Forward.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class EventPath {
+public:
+ EventPath(Node& origin, Event&);
+
+ bool isEmpty() const { return m_path.isEmpty(); }
+ size_t size() const { return m_path.size(); }
+ const EventContext& contextAt(size_t i) const { return *m_path[i]; }
+ EventContext& contextAt(size_t i) { return *m_path[i]; }
+
+#if ENABLE(TOUCH_EVENTS)
+ void retargetTouchLists(const TouchEvent&);
+#endif
+ void setRelatedTarget(Node& origin, EventTarget&);
+
+ bool hasEventListeners(const AtomicString& eventType) const;
+
+ EventContext* lastContextIfExists() { return m_path.isEmpty() ? nullptr : m_path.last().get(); }
+
+ static EventTarget* eventTargetRespectingTargetRules(Node& referenceNode)
+ {
+ if (is<PseudoElement>(referenceNode))
+ return downcast<PseudoElement>(referenceNode).hostElement();
+
+ // Events sent to elements inside an SVG use element's shadow tree go to the use element.
+ if (is<SVGElement>(referenceNode)) {
+ if (auto* useElement = downcast<SVGElement>(referenceNode).correspondingUseElement())
+ return useElement;
+ }
+
+ return &referenceNode;
+ }
+
+private:
+#if ENABLE(TOUCH_EVENTS)
+ void retargetTouch(TouchEventContext::TouchListType, const Touch&);
+#endif
+
+ Event& m_event;
+ Vector<std::unique_ptr<EventContext>, 32> m_path;
+};
+
+}
+
+#endif /* EventPath_h */