Changeset 236103 in webkit
- Timestamp:
- Sep 18, 2018 12:47:35 AM (6 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/imported/w3c/ChangeLog
r236031 r236103 1 2018-09-12 Ryosuke Niwa <rniwa@webkit.org> 2 3 Update composedPath to match the latest spec 4 https://bugs.webkit.org/show_bug.cgi?id=180378 5 <rdar://problem/42843004> 6 7 Reviewed by Darin Adler. 8 9 Rebaselined the test now that all test cases pass. 10 11 * web-platform-tests/shadow-dom/event-composed-path-after-dom-mutation-expected.txt: 12 1 13 2018-09-15 Rob Buis <rbuis@igalia.com> 2 14 -
trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/event-composed-path-after-dom-mutation-expected.txt
r235881 r236103 1 1 2 FAIL Event.composedPath() should return the same result even if DOM is mutated (1/2) assert_array_equals: lengths differ, expected 3 got 2 3 FAIL Event.composedPath() should return the same result even if DOM is mutated (2/2) assert_array_equals: lengths differ, expected 5 got 2 2 PASS Event.composedPath() should return the same result even if DOM is mutated (1/2) 3 PASS Event.composedPath() should return the same result even if DOM is mutated (2/2) 4 4 -
trunk/Source/WebCore/ChangeLog
r236101 r236103 1 2018-09-12 Ryosuke Niwa <rniwa@webkit.org> 2 3 Update composedPath to match the latest spec 4 https://bugs.webkit.org/show_bug.cgi?id=180378 5 <rdar://problem/42843004> 6 7 Reviewed by Darin Adler. 8 9 This patch makes the check for whether a given node in the event path be included in composedPath 10 pre-determined at the time of the event dispatching per https://github.com/whatwg/dom/issues/525. 11 This was a fix for the issue that if an event listener in a closed shadow tree removes a node in the 12 same tree in the event path, then composedPath called on its shadow host, for example, will include 13 the removed node since it's no longer in the closed shadow tree. 14 15 Naively, implementing this behavior would require remembering the original document or shadow root 16 of each node in the event path as well as its parent shadow root, or worse which node is disclosed 17 to every other node at the time of computing the event path. 18 19 This patch takes a more novel and efficient approach to implement the new behavior by adding a single 20 integer indicating the number of closed-mode shadow root ancestors of each node in the event path. 21 In computePathUnclosedToTarget, any node whose *depth* is greater than the context object is excluded. 22 23 Consider the following example: 24 div ------- ShadowRoot (closed) 25 +- span +- slot 26 If an event is dispatched on span, then the event path would be [span, slot, ShadowRoot, div]. Then 27 the values of integers assigned to each node would be: [0, 1, 1, 0] respectively. When composedPath 28 is called on span or div, slot and ShadowRoot are excluded because they have a greater *depth* value. 29 30 Unfortunately, this simplistic solution doesn't work when there are multiple shadow roots of the same 31 depth through which an event is dispatched as in: 32 section -- ShadowRoot (closed, SR2) 33 | +- slot (s2) 34 +div ------ ShadowRoot (closed, SR1) 35 +- span +- slot (s1) 36 If an event is dispatched on span, the event path would be [span, s1, SR1, div, s2, SR2, section]. 37 The values of integers assigned are: [0, 1, 1, 0, 1, 1, 0] respectively. When composedPath is called 38 on SR1, the simplistic approach would include s2 and SR2, which would be wrong. 39 40 To account for this case, in computePathUnclosedToTarget, we traverse the event path upwards (i.e. 41 ancestors) and downwards (i.e. descendants) from the context object and decrease the *allowed depth* 42 of shadow trees when we traverse out of a shadow tree in either direction. When traversing upwards, 43 therefore, moving out of a shadow root to its host would would decrease the allowed depth. When 44 traversing dowards, moving from a slot element to its assigned node would decrease the allowed depth. 45 46 Note that the depths can be negative when a composed event is dispatched inside a closed shadow tree, 47 and it gets out of its shadow host. 48 49 Unfortunately, the latest DOM specification has a bug and doesn't match the behavior of Chrome. This 50 patch proposes a new algorithm which can be adopted in https://github.com/whatwg/dom/issues/684. 51 52 Test: imported/w3c/web-platform-tests/shadow-dom/event-composed-path-after-dom-mutation.html 53 54 * dom/EventContext.cpp: 55 (WebCore::EventContext::EventContext): 56 (WebCore::MouseOrFocusEventContext::MouseOrFocusEventContext): 57 (WebCore::TouchEventContext::TouchEventContext): 58 * dom/EventContext.h: 59 (WebCore::EventContext::closedShadowDepth const): Added. 60 * dom/EventPath.cpp: 61 (WebCore::WindowEventContext::WindowEventContext): 62 (WebCore::EventPath::buildPath): Compute the closed shadow tree's depths for each node in the path. 63 (WebCore::computePathUnclosedToTarget const): Implemented the aforementioned algorithm. 64 (WebCore::EventPath::EventPath): 65 1 66 2018-09-17 Yusuke Suzuki <utatane.tea@gmail.com> 2 67 -
trunk/Source/WebCore/dom/EventContext.cpp
r236002 r236103 36 36 namespace WebCore { 37 37 38 EventContext::EventContext(Node* node, EventTarget* currentTarget, EventTarget* target) 39 : m_node(node) 40 , m_currentTarget(currentTarget) 41 , m_target(target) 38 EventContext::EventContext(Node* node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) 39 : m_node { node } 40 , m_currentTarget { currentTarget } 41 , m_target { target } 42 , m_closedShadowDepth { closedShadowDepth } 42 43 { 43 44 ASSERT(!isUnreachableNode(m_target.get())); … … 67 68 } 68 69 69 MouseOrFocusEventContext::MouseOrFocusEventContext(Node& node, EventTarget* currentTarget, EventTarget* target )70 : EventContext(&node, currentTarget, target )70 MouseOrFocusEventContext::MouseOrFocusEventContext(Node& node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) 71 : EventContext(&node, currentTarget, target, closedShadowDepth) 71 72 { 72 73 } … … 88 89 #if ENABLE(TOUCH_EVENTS) 89 90 90 TouchEventContext::TouchEventContext(Node& node, EventTarget* currentTarget, EventTarget* target )91 : EventContext(&node, currentTarget, target )91 TouchEventContext::TouchEventContext(Node& node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) 92 : EventContext(&node, currentTarget, target, closedShadowDepth) 92 93 , m_touches(TouchList::create()) 93 94 , m_targetTouches(TouchList::create()) -
trunk/Source/WebCore/dom/EventContext.h
r236002 r236103 39 39 using EventInvokePhase = EventTarget::EventInvokePhase; 40 40 41 EventContext(Node*, EventTarget* currentTarget, EventTarget* );41 EventContext(Node*, EventTarget* currentTarget, EventTarget*, int closedShadowDepth); 42 42 virtual ~EventContext(); 43 43 … … 45 45 EventTarget* currentTarget() const { return m_currentTarget.get(); } 46 46 EventTarget* target() const { return m_target.get(); } 47 int closedShadowDepth() const { return m_closedShadowDepth; } 47 48 48 49 virtual void handleLocalEvents(Event&, EventInvokePhase) const; … … 59 60 RefPtr<EventTarget> m_currentTarget; 60 61 RefPtr<EventTarget> m_target; 62 int m_closedShadowDepth { 0 }; 61 63 }; 62 64 63 65 class MouseOrFocusEventContext final : public EventContext { 64 66 public: 65 MouseOrFocusEventContext(Node&, EventTarget* currentTarget, EventTarget* );67 MouseOrFocusEventContext(Node&, EventTarget* currentTarget, EventTarget*, int closedShadowDepth); 66 68 virtual ~MouseOrFocusEventContext(); 67 69 … … 80 82 class TouchEventContext final : public EventContext { 81 83 public: 82 TouchEventContext(Node&, EventTarget* currentTarget, EventTarget* );84 TouchEventContext(Node&, EventTarget* currentTarget, EventTarget*, int closedShadowDepth); 83 85 virtual ~TouchEventContext(); 84 86 -
trunk/Source/WebCore/dom/EventPath.cpp
r236002 r236103 36 36 class WindowEventContext final : public EventContext { 37 37 public: 38 WindowEventContext(Node&, DOMWindow&, EventTarget& );38 WindowEventContext(Node&, DOMWindow&, EventTarget&, int closedShadowDepth); 39 39 private: 40 40 void handleLocalEvents(Event&, EventInvokePhase) const final; 41 41 }; 42 42 43 inline WindowEventContext::WindowEventContext(Node& node, DOMWindow& currentTarget, EventTarget& target )44 : EventContext(&node, ¤tTarget, &target )43 inline WindowEventContext::WindowEventContext(Node& node, DOMWindow& currentTarget, EventTarget& target, int closedShadowDepth) 44 : EventContext(&node, ¤tTarget, &target, closedShadowDepth) 45 45 { 46 46 } … … 112 112 void EventPath::buildPath(Node& originalTarget, Event& event) 113 113 { 114 using MakeEventContext = std::unique_ptr<EventContext> (*)(Node&, EventTarget*, EventTarget* );115 MakeEventContext makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target ) {116 return std::make_unique<EventContext>(&node, currentTarget, target );114 using MakeEventContext = std::unique_ptr<EventContext> (*)(Node&, EventTarget*, EventTarget*, int closedShadowDepth); 115 MakeEventContext makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) { 116 return std::make_unique<EventContext>(&node, currentTarget, target, closedShadowDepth); 117 117 }; 118 118 if (is<MouseEvent>(event) || event.isFocusEvent()) { 119 makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target ) -> std::unique_ptr<EventContext> {120 return std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target );119 makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) -> std::unique_ptr<EventContext> { 120 return std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target, closedShadowDepth); 121 121 }; 122 122 } 123 123 #if ENABLE(TOUCH_EVENTS) 124 124 if (is<TouchEvent>(event)) { 125 makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target ) -> std::unique_ptr<EventContext> {126 return std::make_unique<TouchEventContext>(node, currentTarget, target );125 makeEventContext = [] (Node& node, EventTarget* currentTarget, EventTarget* target, int closedShadowDepth) -> std::unique_ptr<EventContext> { 126 return std::make_unique<TouchEventContext>(node, currentTarget, target, closedShadowDepth); 127 127 }; 128 128 } … … 131 131 Node* node = nodeOrHostIfPseudoElement(&originalTarget); 132 132 Node* target = node ? eventTargetRespectingTargetRules(*node) : nullptr; 133 int closedShadowDepth = 0; 134 // Depths are used to decided which nodes are excluded in event.composedPath when the tree is mutated during event dispatching. 135 // They could be negative for nodes outside the shadow tree of the target node. 133 136 while (node) { 134 137 while (node) { 135 m_path.append(makeEventContext(*node, eventTargetRespectingTargetRules(*node), target ));138 m_path.append(makeEventContext(*node, eventTargetRespectingTargetRules(*node), target, closedShadowDepth)); 136 139 137 140 if (is<ShadowRoot>(*node)) … … 145 148 if (target) { 146 149 if (auto* window = downcast<Document>(*node).domWindow()) 147 m_path.append(std::make_unique<WindowEventContext>(*node, *window, *target ));150 m_path.append(std::make_unique<WindowEventContext>(*node, *window, *target, closedShadowDepth)); 148 151 } 149 152 } … … 154 157 if (UNLIKELY(shadowRootOfParent)) { 155 158 if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) { 159 if (shadowRootOfParent->mode() != ShadowRootMode::Open) 160 closedShadowDepth++; 156 161 // node is assigned to a slot. Continue dispatching the event at this slot. 157 162 parent = assignedSlot; … … 166 171 return; 167 172 node = shadowRoot.host(); 173 if (shadowRoot.mode() != ShadowRootMode::Open) 174 closedShadowDepth--; 168 175 if (exitingShadowTreeOfTarget) 169 176 target = eventTargetRespectingTargetRules(*node); … … 252 259 253 260 // https://dom.spec.whatwg.org/#dom-event-composedpath 261 // Any node whose depth computed in EventPath::buildPath is greater than the context object is excluded. 262 // Because we can exit out of a closed shadow tree and re-enter another closed shadow tree via a slot, 263 // we decrease the *allowed depth* whenever we moved to a "shallower" (closer-to-document) tree. 254 264 Vector<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const 255 265 { 256 266 Vector<EventTarget*> path; 257 path.reserveInitialCapacity(m_path.size()); 258 const Node* targetNode = nullptr; 259 if (is<Node>(target)) 260 targetNode = &downcast<Node>(target); 261 else if (is<DOMWindow>(target)) { 262 targetNode = downcast<DOMWindow>(target).document(); 263 ASSERT(targetNode); 264 } 265 for (auto& context : m_path) { 266 auto* currentTarget = context->currentTarget(); 267 if (!is<Node>(currentTarget) || !targetNode || !targetNode->isClosedShadowHidden(downcast<Node>(*currentTarget))) 268 path.uncheckedAppend(currentTarget); 269 } 267 auto pathSize = m_path.size(); 268 RELEASE_ASSERT(pathSize); 269 path.reserveInitialCapacity(pathSize); 270 271 auto currentTargetIndex = m_path.findMatching([&target] (auto& context) { 272 return context->currentTarget() == ⌖ 273 }); 274 RELEASE_ASSERT(currentTargetIndex != notFound); 275 auto currentTargetDepth = m_path[currentTargetIndex]->closedShadowDepth(); 276 277 auto appendTargetWithLesserDepth = [&path] (const EventContext& currentContext, int& currentDepthAllowed) { 278 auto depth = currentContext.closedShadowDepth(); 279 bool contextIsInsideInnerShadowTree = depth > currentDepthAllowed; 280 if (contextIsInsideInnerShadowTree) 281 return; 282 bool movedOutOfShadowTree = depth < currentDepthAllowed; 283 if (movedOutOfShadowTree) 284 currentDepthAllowed = depth; 285 path.uncheckedAppend(currentContext.currentTarget()); 286 }; 287 288 auto currentDepthAllowed = currentTargetDepth; 289 auto i = currentTargetIndex; 290 do { 291 appendTargetWithLesserDepth(*m_path[i], currentDepthAllowed); 292 } while (i--); 293 path.reverse(); 294 295 currentDepthAllowed = currentTargetDepth; 296 for (auto i = currentTargetIndex + 1; i < pathSize; ++i) 297 appendTargetWithLesserDepth(*m_path[i], currentDepthAllowed); 298 270 299 return path; 271 300 } … … 273 302 EventPath::EventPath(const Vector<Element*>& targets) 274 303 { 304 // FIXME: This function seems wrong. Why are we not firing events in the closed shadow trees? 275 305 for (auto* target : targets) { 276 306 ASSERT(target); 277 307 Node* origin = *targets.begin(); 278 308 if (!target->isClosedShadowHidden(*origin)) 279 m_path.append(std::make_unique<EventContext>(target, target, origin ));309 m_path.append(std::make_unique<EventContext>(target, target, origin, 0)); 280 310 } 281 311 } … … 286 316 ASSERT(target); 287 317 ASSERT(!is<Node>(target)); 288 m_path.append(std::make_unique<EventContext>(nullptr, target, *targets.begin() ));318 m_path.append(std::make_unique<EventContext>(nullptr, target, *targets.begin(), 0)); 289 319 } 290 320 }
Note: See TracChangeset
for help on using the changeset viewer.