Changeset 236002 in webkit
- Timestamp:
- Sep 13, 2018 10:56:19 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r236001 r236002 1 2018-09-13 Ryosuke Niwa <rniwa@webkit.org> 2 3 Capturing event listeners are called during bubbling phase for shadow hosts 4 https://bugs.webkit.org/show_bug.cgi?id=174288 5 6 Reviewed by Darin Adler. 7 8 Added a W3C style testharness.js test and rebaselined two tests. See below for rationals of rebaselines. 9 10 * fast/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt: Added. 11 * fast/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html: Added. 12 13 * media/media-load-event-expected.txt: Rebaselined. The logging of oncanplaythrough event is now happening 14 before canplaythrough() is called because the logging is done by waitForEvent which uses a capturing event 15 listener whereas canplaythrough is called by a event handler, which is non-capturing. 16 17 * platform/ios-11/imported/w3c/web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt: 18 * platform/ios/imported/w3c/web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt: 19 1 20 2018-09-13 Justin Fan <justin_fan@apple.com> 2 21 -
trunk/LayoutTests/imported/w3c/ChangeLog
r235958 r236002 1 2018-09-12 Ryosuke Niwa <rniwa@webkit.org> 2 3 Capturing event listeners are called during bubbling phase for shadow hosts 4 https://bugs.webkit.org/show_bug.cgi?id=174288 5 6 Reviewed by Darin Adler. 7 8 * web-platform-tests/dom/events/Event-dispatch-handlers-changed-expected.txt: Rebaselined. This test's 9 expectation is now wrong because event listner 3 is added after the event listener list is cloned for 10 capturing event listeners but before cloned for bubbling event listeners. As a result, event listener 3 11 is now invoked. It used to be not called because both bubbling and capturing event listeners are called 12 after cloning the event listner list once, which didn't have event listener 3. 13 14 * web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt: Rebaselined. This test expects 15 event listener 2, which is bubbling, to be called between two capturing event listeners 1 and 3, which 16 is no longer true after this patch. 17 1 18 2018-09-12 Chris Dumez <cdumez@apple.com> 2 19 -
trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/Event-dispatch-handlers-changed-expected.txt
r189471 r236002 1 1 2 PASS Dispatch additional events inside an event listener 2 FAIL Dispatch additional events inside an event listener assert_array_equals: actual_targets lengths differ, expected 16 got 17 3 3 -
trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt
r223023 r236002 26 26 PASS Exceptions from event listeners must not be propagated. 27 27 PASS Event listeners added during dispatch should be called 28 PASS Event listeners should be called in order of addition 28 FAIL Event listeners should be called in order of addition assert_array_equals: property 1, expected 2 but got 3 29 29 -
trunk/LayoutTests/media/media-load-event-expected.txt
r50063 r236002 9 9 EVENT(loadeddata) 10 10 EVENT(canplaythrough) 11 EVENT(canplaythrough) 11 12 12 13 RUN(document.getElementById('parent').appendChild(mediaElement)) 13 14 RUN(mediaElement.play()) 14 15 15 EVENT(canplaythrough)16 16 EVENT(play) 17 17 EVENT(playing) -
trunk/LayoutTests/platform/ios-11/imported/w3c/web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt
r233832 r236002 26 26 PASS Exceptions from event listeners must not be propagated. 27 27 PASS Event listeners added during dispatch should be called 28 PASS Event listeners should be called in order of addition 28 FAIL Event listeners should be called in order of addition assert_array_equals: property 1, expected 2 but got 3 29 29 -
trunk/LayoutTests/platform/ios/imported/w3c/web-platform-tests/dom/events/EventTarget-dispatchEvent-expected.txt
r233832 r236002 26 26 PASS Exceptions from event listeners must not be propagated. 27 27 PASS Event listeners added during dispatch should be called 28 PASS Event listeners should be called in order of addition28 PASS Event listeners should be +FAIL Event listeners should be called in order of addition assert_array_equals: property 1, expected 2 but got 3 29 29 -
trunk/Source/WebCore/ChangeLog
r235999 r236002 1 2018-09-13 Ryosuke Niwa <rniwa@webkit.org> 2 3 Capturing event listeners are called during bubbling phase for shadow hosts 4 https://bugs.webkit.org/show_bug.cgi?id=174288 5 <rdar://problem/33530455> 6 7 Reviewed by Darin Adler. 8 9 Implemented the new behavior proposed in https://github.com/whatwg/dom/pull/686 [1] to fix the problem 10 that capturing event listeners on a shadow host is invoked during bubbling phase when an event is 11 dispatched within its shadow tree. 12 13 To see why this is a problem, suppose we fire a composed event at span#target in the following DOM tree: 14 section#hostParent 15 + div#host -- ShadowRoot 16 - p#parent 17 - span#target 18 Then capturing and bubbling event listeners on #target, #parent, #host, and #hostParent are invoked in 19 the following order in WebKit & Chrome right now: 20 21 1. #hostParent, capturing, eventPhase: CAPTURING_PHASE 22 2. #parent, capturing, eventPhase: CAPTURING_PHASE 23 3. #target, capturing, eventPhase: AT_TARGET 24 4. #target, non-capturing, eventPhase: AT_TARGET 25 5. #parent, non-capturing, eventPhase: BUBBLING_PHASE 26 6. #host, capturing, eventPhase: AT_TARGET 27 7. #host, non-capturing, eventPhase: AT_TARGET 28 8. #hostParent, non-capturing, eventPhase: BUBBLING_PHASE 29 30 This is counter-intuitive because capturing event listeners on #host isn't invoked until bubblign phase 31 started. A more natural ordering would be: 32 33 1. #hostParent, capturing, eventPhase: CAPTURING_PHASE 34 2. #host, capturing, eventPhase: AT_TARGET 35 3. #parent, capturing, eventPhase: CAPTURING_PHASE 36 4. #target, capturing, eventPhase: AT_TARGET 37 5. #target, non-capturing, eventPhase: AT_TARGET 38 6. #parent, non-capturing, eventPhase: BUBBLING_PHASE 39 7. #host, non-capturing, eventPhase: AT_TARGET 40 8. #hostParent, non-capturing, eventPhase: BUBBLING_PHASE 41 42 This also happens to be the order by which Gecko's current shadow DOM implementation invoke event listners. 43 This patch implements this new behavior using the spec-change proposed in [1]. Note that this patch also 44 impacts the invocation order of event listeners when there is no shadow tree. Namely, before this patch, 45 event listeners on the event's target is invoked in the registration order. After this patch, all capturing 46 event listeners are invoked before bubbling event listeners are invoked. 47 48 To implement this behavior, this patch introduces EventTarget::EventInvokePhase indicating whether we're 49 in the capturing phase or bubbling phase to EventTarget::fireEventListeners. We can't use Event's eventPhase 50 enum because that's set to Event::Phase::AT_TARGET when we're at a shadow host. 51 52 Test: fast/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html 53 54 * Modules/modern-media-controls/media/media-controller-support.js: 55 (MediaControllerSupport.prototype.enable): Use capturing event listeners so that we can update the states of 56 media controls before author scripts recieve the event. 57 (MediaControllerSupport.prototype.disable): Ditto. 58 * dom/EventContext.cpp: 59 (WebCore::EventContext::handleLocalEvents const): 60 (WebCore::MouseOrFocusEventContext::handleLocalEvents const): 61 (WebCore::TouchEventContext::handleLocalEvents const): 62 * dom/EventContext.h: 63 * dom/EventDispatcher.cpp: 64 (WebCore::dispatchEventInDOM): Invoke capturing event listners even when target and current target are same. 65 This happens when the current target is a shadow host and event's target is in its shadow tree. Also merged 66 the special code path for the event's target with the code in the bubbling phase. 67 * dom/EventPath.cpp: 68 (WebCore::WindowEventContext::handleLocalEvents const): 69 * dom/EventTarget.cpp: 70 (WebCore::EventTarget::dispatchEvent): Invoke capturing and bubbling event listeners in the order. 71 (WebCore::EventTarget::fireEventListeners): 72 (WebCore::EventTarget::innerInvokeEventListeners): Renamed from fireEventListeners to match the spec. Use 73 EventInvokePhase to filter out event listeners so that we can invoke capturing event listners before bubbling 74 event listeners even when eventPhase is Event::Phase::AT_TARGET. 75 * dom/EventTarget.h: 76 * dom/Node.cpp: 77 (WebCore::Node::handleLocalEvents): 78 * dom/Node.h: 79 * html/HTMLFormElement.cpp: 80 (WebCore::HTMLFormElement::handleLocalEvents): 81 * html/HTMLFormElement.h: 82 * page/DOMWindow.cpp: 83 (WebCore::DOMWindow::dispatchEvent): 84 1 85 2018-09-13 Megan Gardner <megan_gardner@apple.com> 2 86 -
trunk/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js
r226796 r236002 39 39 { 40 40 for (let eventType of this.mediaEvents) 41 this.mediaController.media.addEventListener(eventType, this );41 this.mediaController.media.addEventListener(eventType, this, true); 42 42 43 43 for (let tracks of this.tracksToMonitor) { … … 56 56 { 57 57 for (let eventType of this.mediaEvents) 58 this.mediaController.media.removeEventListener(eventType, this );58 this.mediaController.media.removeEventListener(eventType, this, true); 59 59 60 60 for (let tracks of this.tracksToMonitor) { -
trunk/Source/WebCore/dom/EventContext.cpp
r224748 r236002 46 46 EventContext::~EventContext() = default; 47 47 48 void EventContext::handleLocalEvents(Event& event ) const48 void EventContext::handleLocalEvents(Event& event, EventInvokePhase phase) const 49 49 { 50 50 event.setTarget(m_target.get()); … … 52 52 // FIXME: Consider merging handleLocalEvents and fireEventListeners. 53 53 if (m_node) 54 m_node->handleLocalEvents(event );54 m_node->handleLocalEvents(event, phase); 55 55 else 56 m_currentTarget->fireEventListeners(event );56 m_currentTarget->fireEventListeners(event, phase); 57 57 } 58 58 … … 74 74 MouseOrFocusEventContext::~MouseOrFocusEventContext() = default; 75 75 76 void MouseOrFocusEventContext::handleLocalEvents(Event& event ) const76 void MouseOrFocusEventContext::handleLocalEvents(Event& event, EventInvokePhase phase) const 77 77 { 78 78 if (m_relatedTarget) 79 79 event.setRelatedTarget(*m_relatedTarget); 80 EventContext::handleLocalEvents(event );80 EventContext::handleLocalEvents(event, phase); 81 81 } 82 82 … … 98 98 TouchEventContext::~TouchEventContext() = default; 99 99 100 void TouchEventContext::handleLocalEvents(Event& event ) const100 void TouchEventContext::handleLocalEvents(Event& event, EventInvokePhase phase) const 101 101 { 102 102 checkReachability(m_touches); … … 107 107 touchEvent.setTargetTouches(m_targetTouches.ptr()); 108 108 touchEvent.setChangedTouches(m_changedTouches.ptr()); 109 EventContext::handleLocalEvents(event );109 EventContext::handleLocalEvents(event, phase); 110 110 } 111 111 -
trunk/Source/WebCore/dom/EventContext.h
r224740 r236002 37 37 WTF_MAKE_FAST_ALLOCATED; 38 38 public: 39 using EventInvokePhase = EventTarget::EventInvokePhase; 40 39 41 EventContext(Node*, EventTarget* currentTarget, EventTarget*); 40 42 virtual ~EventContext(); … … 44 46 EventTarget* target() const { return m_target.get(); } 45 47 46 virtual void handleLocalEvents(Event& ) const;48 virtual void handleLocalEvents(Event&, EventInvokePhase) const; 47 49 48 50 virtual bool isMouseOrFocusEventContext() const; … … 68 70 69 71 private: 70 void handleLocalEvents(Event& ) const final;72 void handleLocalEvents(Event&, EventInvokePhase) const final; 71 73 bool isMouseOrFocusEventContext() const final; 72 74 … … 85 87 86 88 private: 87 void handleLocalEvents(Event& ) const final;89 void handleLocalEvents(Event&, EventInvokePhase) const final; 88 90 bool isTouchEventContext() const final; 89 91 -
trunk/Source/WebCore/dom/EventDispatcher.cpp
r234718 r236002 76 76 static void dispatchEventInDOM(Event& event, const EventPath& path) 77 77 { 78 // Trigger capturing event handlers, starting at the top and working our way down. 79 event.setEventPhase(Event::CAPTURING_PHASE); 80 81 for (size_t i = path.size() - 1; i > 0; --i) { 82 const EventContext& eventContext = path.contextAt(i); 78 // Invoke capturing event listeners in the reverse order. 79 for (size_t i = path.size(); i > 0; --i) { 80 const EventContext& eventContext = path.contextAt(i - 1); 83 81 if (eventContext.currentTarget() == eventContext.target()) 84 continue; 85 eventContext.handleLocalEvents(event); 82 event.setEventPhase(Event::AT_TARGET); 83 else 84 event.setEventPhase(Event::CAPTURING_PHASE); 85 eventContext.handleLocalEvents(event, EventTarget::EventInvokePhase::Capturing); 86 86 if (event.propagationStopped()) 87 87 return; 88 88 } 89 89 90 event.setEventPhase(Event::AT_TARGET); 91 path.contextAt(0).handleLocalEvents(event); 92 if (event.propagationStopped()) 93 return; 94 95 // Trigger bubbling event handlers, starting at the bottom and working our way up. 90 // Invoke bubbling event listeners. 96 91 size_t size = path.size(); 97 for (size_t i = 1; i < size; ++i) {92 for (size_t i = 0; i < size; ++i) { 98 93 const EventContext& eventContext = path.contextAt(i); 99 94 if (eventContext.currentTarget() == eventContext.target()) … … 103 98 else 104 99 continue; 105 eventContext.handleLocalEvents(event );100 eventContext.handleLocalEvents(event, EventTarget::EventInvokePhase::Bubbling); 106 101 if (event.propagationStopped()) 107 102 return; -
trunk/Source/WebCore/dom/EventPath.cpp
r228827 r236002 38 38 WindowEventContext(Node&, DOMWindow&, EventTarget&); 39 39 private: 40 void handleLocalEvents(Event& ) const final;40 void handleLocalEvents(Event&, EventInvokePhase) const final; 41 41 }; 42 42 … … 46 46 } 47 47 48 void WindowEventContext::handleLocalEvents(Event& event ) const48 void WindowEventContext::handleLocalEvents(Event& event, EventInvokePhase phase) const 49 49 { 50 50 event.setTarget(m_target.get()); 51 51 event.setCurrentTarget(m_currentTarget.get()); 52 m_currentTarget->fireEventListeners(event );52 m_currentTarget->fireEventListeners(event, phase); 53 53 } 54 54 -
trunk/Source/WebCore/dom/EventTarget.cpp
r233493 r236002 184 184 void EventTarget::dispatchEvent(Event& event) 185 185 { 186 // FIXME: We should always use EventDispatcher. 186 187 ASSERT(event.isInitialized()); 187 188 ASSERT(!event.isBeingDispatched()); … … 191 192 event.setEventPhase(Event::AT_TARGET); 192 193 event.resetBeforeDispatch(); 193 fireEventListeners(event); 194 fireEventListeners(event, EventInvokePhase::Capturing); 195 fireEventListeners(event, EventInvokePhase::Bubbling); 194 196 event.resetAfterDispatch(); 195 197 } … … 220 222 } 221 223 222 void EventTarget::fireEventListeners(Event& event) 224 // https://dom.spec.whatwg.org/#concept-event-listener-invoke 225 void EventTarget::fireEventListeners(Event& event, EventInvokePhase phase) 223 226 { 224 227 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread()); … … 232 235 233 236 if (auto* listenersVector = data->eventListenerMap.find(event.type())) { 234 fireEventListeners(event, *listenersVector);237 innerInvokeEventListeners(event, *listenersVector, phase); 235 238 return; 236 239 } … … 245 248 AtomicString typeName = event.type(); 246 249 event.setType(legacyTypeName); 247 fireEventListeners(event, *legacyListenersVector);250 innerInvokeEventListeners(event, *legacyListenersVector, phase); 248 251 event.setType(typeName); 249 252 } … … 253 256 // Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run. 254 257 // Note that removal still has an effect due to the removed field in RegisteredEventListener. 255 void EventTarget::fireEventListeners(Event& event, EventListenerVector listeners) 258 // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke 259 void EventTarget::innerInvokeEventListeners(Event& event, EventListenerVector listeners, EventInvokePhase phase) 256 260 { 257 261 Ref<EventTarget> protectedThis(*this); … … 269 273 continue; 270 274 271 if ( event.eventPhase() == Event::CAPTURING_PHASE&& !registeredListener->useCapture())272 continue; 273 if ( event.eventPhase() == Event::BUBBLING_PHASE&& registeredListener->useCapture())275 if (phase == EventInvokePhase::Capturing && !registeredListener->useCapture()) 276 continue; 277 if (phase == EventInvokePhase::Bubbling && registeredListener->useCapture()) 274 278 continue; 275 279 -
trunk/Source/WebCore/dom/EventTarget.h
r233443 r236002 103 103 const EventListenerVector& eventListeners(const AtomicString& eventType); 104 104 105 void fireEventListeners(Event&); 105 enum class EventInvokePhase { Capturing, Bubbling }; 106 void fireEventListeners(Event&, EventInvokePhase); 106 107 bool isFiringEventListeners() const; 107 108 … … 121 122 virtual void derefEventTarget() = 0; 122 123 123 void fireEventListeners(Event&, EventListenerVector);124 void innerInvokeEventListeners(Event&, EventListenerVector, EventInvokePhase); 124 125 125 126 friend class EventListenerIterator; -
trunk/Source/WebCore/dom/Node.cpp
r235331 r236002 2308 2308 } 2309 2309 2310 void Node::handleLocalEvents(Event& event )2310 void Node::handleLocalEvents(Event& event, EventInvokePhase phase) 2311 2311 { 2312 2312 if (!hasEventTargetData()) … … 2317 2317 return; 2318 2318 2319 fireEventListeners(event );2319 fireEventListeners(event, phase); 2320 2320 } 2321 2321 -
trunk/Source/WebCore/dom/Node.h
r235780 r236002 487 487 void dispatchScopedEvent(Event&); 488 488 489 virtual void handleLocalEvents(Event& );489 virtual void handleLocalEvents(Event&, EventInvokePhase); 490 490 491 491 void dispatchSubtreeModifiedEvent(); -
trunk/Source/WebCore/html/HTMLFormElement.cpp
r234995 r236002 143 143 } 144 144 145 void HTMLFormElement::handleLocalEvents(Event& event )145 void HTMLFormElement::handleLocalEvents(Event& event, EventInvokePhase phase) 146 146 { 147 147 if (event.eventPhase() != Event::CAPTURING_PHASE && is<Node>(event.target()) && event.target() != this && (event.type() == eventNames().submitEvent || event.type() == eventNames().resetEvent)) { … … 149 149 return; 150 150 } 151 HTMLElement::handleLocalEvents(event );151 HTMLElement::handleLocalEvents(event, phase); 152 152 } 153 153 -
trunk/Source/WebCore/html/HTMLFormElement.h
r230229 r236002 134 134 void finishParsingChildren() final; 135 135 136 void handleLocalEvents(Event& ) final;136 void handleLocalEvents(Event&, EventInvokePhase) final; 137 137 138 138 void parseAttribute(const QualifiedName&, const AtomicString&) final; -
trunk/Source/WebCore/page/DOMWindow.cpp
r235994 r236002 2053 2053 event.resetBeforeDispatch(); 2054 2054 auto cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), event, *this); 2055 fireEventListeners(event); 2055 // FIXME: We should use EventDispatcher everywhere. 2056 fireEventListeners(event, EventInvokePhase::Capturing); 2057 fireEventListeners(event, EventInvokePhase::Bubbling); 2056 2058 InspectorInstrumentation::didDispatchEventOnWindow(cookie); 2057 2059 event.resetAfterDispatch();
Note: See TracChangeset
for help on using the changeset viewer.