Changeset 201858 in webkit
- Timestamp:
- Jun 9, 2016 2:09:50 AM (8 years ago)
- Location:
- trunk
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r201855 r201858 1 2016-06-09 Ryosuke Niwa <rniwa@webkit.org> 2 3 slotchange event should be fired at the end of microtask 4 https://bugs.webkit.org/show_bug.cgi?id=157374 5 <rdar://problem/26154024> 6 7 Reviewed by Antti Koivisto. 8 9 Added a test case to ensure slotchange event is dispatched at the end of a microtask. 10 11 * fast/shadow-dom/slotchange-event-expected.txt: 12 * fast/shadow-dom/slotchange-event.html: 13 1 14 2016-06-08 Joseph Pecoraro <pecoraro@apple.com> 2 15 -
trunk/LayoutTests/fast/shadow-dom/slotchange-event-expected.txt
r198115 r201858 30 30 PASS slotchange event must fire on a slot element inside an open shadow root not in a document when nested slots's contents change 31 31 PASS slotchange event must fire on a slot element inside a closed shadow root not in a document when nested slots's contents change 32 PASS slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root in a document when slots's contents change 33 PASS slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root in a document when slots's contents change 34 PASS slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root not in a document when slots's contents change 35 PASS slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root not in a document when slots's contents change 32 36 -
trunk/LayoutTests/fast/shadow-dom/slotchange-event.html
r198115 r201858 535 535 testSlotchangeFiresWhenNestedSlotChange('closed', false); 536 536 537 function testSlotchangeFiresAtEndOfMicroTask(mode, connectedToDocument) 538 { 539 var test = async_test('slotchange event must fire at the end of current microtask after mutation observers are invoked inside ' 540 + treeName(mode, connectedToDocument) + ' when slots\'s contents change'); 541 542 var outerHost; 543 var innerHost; 544 var outerSlot; 545 var innerSlot; 546 var slotchangeEvents = []; 547 548 test.step(function () { 549 outerHost = document.createElement('div'); 550 if (connectedToDocument) 551 document.body.appendChild(outerHost); 552 553 var outerShadow = outerHost.attachShadow({'mode': mode}); 554 outerShadow.appendChild(document.createElement('span')); 555 outerSlot = document.createElement('slot'); 556 outerSlot.addEventListener('slotchange', function (event) { 557 event.stopPropagation(); 558 test.step(function () { 559 assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the slot element'); 560 }); 561 slotchangeEvents.push('outer'); 562 }); 563 564 innerHost = document.createElement('div'); 565 innerHost.appendChild(outerSlot); 566 outerShadow.appendChild(innerHost); 567 568 var innerShadow = innerHost.attachShadow({'mode': mode}); 569 innerShadow.appendChild(document.createElement('span')); 570 innerSlot = document.createElement('slot'); 571 innerSlot.addEventListener('slotchange', function (event) { 572 event.stopPropagation(); 573 test.step(function () { 574 assert_equals(event.target, innerSlot, 'slotchange event\'s target must be the slot element'); 575 }); 576 slotchangeEvents.push('inner'); 577 }); 578 innerShadow.appendChild(innerSlot); 579 580 outerHost.appendChild(document.createElement('span')); 581 582 assert_equals(slotchangeEvents.length, 0, 'slotchange event must not be fired synchronously'); 583 }); 584 585 var element = document.createElement('div'); 586 587 new MutationObserver(function () { 588 test.step(function () { 589 assert_equals(slotchangeEvents.length, 0, 'slotchange event must not be fired before mutation records are delivered'); 590 }); 591 element.setAttribute('title', 'bar'); 592 innerHost.appendChild(document.createElement('span')); 593 }).observe(element, {attributes: true, attributeFilter: ['id']}); 594 595 new MutationObserver(function () { 596 test.step(function () { 597 assert_array_equals(slotchangeEvents, ['outer', 'inner'], 'slotchange event must be fired during a single compound microtask'); 598 }); 599 }).observe(element, {attributes: true, attributeFilter: ['title']}); 600 601 element.setAttribute('id', 'foo'); 602 603 setTimeout(function () { 604 test.step(function () { 605 assert_array_equals(slotchangeEvents, ['outer', 'inner', 'inner'], 606 'a distinct slotchange event must be enqueued for changes made during a mutation observer delivery'); 607 }); 608 test.done(); 609 }, 0); 610 } 611 612 testSlotchangeFiresAtEndOfMicroTask('open', true); 613 testSlotchangeFiresAtEndOfMicroTask('closed', true); 614 testSlotchangeFiresAtEndOfMicroTask('open', false); 615 testSlotchangeFiresAtEndOfMicroTask('closed', false); 616 537 617 </script> 538 618 </body> -
trunk/Source/WebCore/ChangeLog
r201856 r201858 1 2016-06-09 Ryosuke Niwa <rniwa@webkit.org> 2 3 slotchange event should be fired at the end of microtask 4 https://bugs.webkit.org/show_bug.cgi?id=157374 5 <rdar://problem/26154024> 6 7 Reviewed by Antti Koivisto. 8 9 Dispatch slotchange event at the end of every microtask after delivering records to mutation observers 10 as specified in: https://dom.spec.whatwg.org/#notify-mutation-observers 11 12 Test: fast/shadow-dom/slotchange-event.html 13 14 * dom/Document.cpp: 15 (WebCore::Document::enqueueSlotchangeEvent): Deleted. 16 * dom/Document.h: 17 * dom/MutationObserver.cpp: 18 (WebCore::signalSlotList): Added. 19 (WebCore::MutationObserverMicrotask::run): mutationObserverCompoundMicrotaskQueuedFlag is now unset in 20 notifyMutationObservers to better match the concept to "notify mutation observers". 21 (WebCore::MutationObserver::enqueueSlotChangeEvent): Added. 22 (WebCore::MutationObserver::notifyMutationObservers): Renamed from deliverAllMutations. Added the code 23 to dispatch slotchange events as spec'ed, and also added comments for each step. 24 * dom/MutationObserver.h: 25 * html/HTMLSlotElement.cpp: 26 (WebCore::HTMLSlotElement::enqueueSlotChangeEvent): Use MutationObserver::enqueueSlotChangeEvent. Don't 27 create an event here since that is only needed when dispatching the event, and to keep track of whether 28 we've already scheduled an event or not. Use a boolean flag instead for the latter. 29 (WebCore::HTMLSlotElement::dispatchSlotChangeEvent): Added. Creates and dispatches an event. 30 (WebCore::HTMLSlotElement::dispatchEvent): Deleted. 31 * html/HTMLSlotElement.h: 32 (WebCore::HTMLSlotElement::didRemoveFromSignalSlotList): Added. 33 1 34 2016-06-08 Youenn Fablet <youenn.fablet@crf.canon.fr> 2 35 -
trunk/Source/WebCore/dom/Document.cpp
r201832 r201858 4251 4251 } 4252 4252 4253 void Document::enqueueSlotchangeEvent(Ref<Event>&& event)4254 {4255 m_eventQueue.enqueueEvent(WTFMove(event));4256 }4257 4258 4253 RefPtr<Event> Document::createEvent(const String& type, ExceptionCode& ec) 4259 4254 { -
trunk/Source/WebCore/dom/Document.h
r201832 r201858 1090 1090 void enqueueDocumentEvent(Ref<Event>&&); 1091 1091 void enqueueOverflowEvent(Ref<Event>&&); 1092 void enqueueSlotchangeEvent(Ref<Event>&&);1093 1092 void enqueuePageshowEvent(PageshowEventPersistence); 1094 1093 void enqueueHashchangeEvent(const String& oldURL, const String& newURL); -
trunk/Source/WebCore/dom/MutationObserver.cpp
r201333 r201858 36 36 #include "Document.h" 37 37 #include "ExceptionCode.h" 38 #include "HTMLSlotElement.h" 38 39 #include "Microtasks.h" 39 40 #include "MutationCallback.h" … … 147 148 } 148 149 150 // https://dom.spec.whatwg.org/#signal-slot-list 151 static Vector<RefPtr<HTMLSlotElement>>& signalSlotList() 152 { 153 static NeverDestroyed<Vector<RefPtr<HTMLSlotElement>>> list; 154 return list; 155 } 156 149 157 static bool mutationObserverCompoundMicrotaskQueuedFlag; 150 158 … … 154 162 Result run() final 155 163 { 156 mutationObserverCompoundMicrotaskQueuedFlag = false; 157 MutationObserver::deliverAllMutations(); 164 MutationObserver::notifyMutationObservers(); 158 165 return Result::Done; 159 166 } … … 173 180 m_records.append(WTFMove(mutation)); 174 181 activeMutationObservers().add(this); 182 183 queueMutationObserverCompoundMicrotask(); 184 } 185 186 void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot) 187 { 188 ASSERT(isMainThread()); 189 ASSERT(!signalSlotList().contains(&slot)); 190 signalSlotList().append(&slot); 175 191 176 192 queueMutationObserverCompoundMicrotask(); … … 221 237 } 222 238 223 void MutationObserver::deliverAllMutations() 224 { 239 void MutationObserver::notifyMutationObservers() 240 { 241 // https://dom.spec.whatwg.org/#notify-mutation-observers 242 // 1. Unset mutation observer compound microtask queued flag. 243 mutationObserverCompoundMicrotaskQueuedFlag = false; 244 225 245 ASSERT(isMainThread()); 226 246 static bool deliveryInProgress = false; … … 241 261 } 242 262 243 while (!activeMutationObservers().isEmpty()) { 244 Vector<RefPtr<MutationObserver>> observers; 245 copyToVector(activeMutationObservers(), observers); 263 while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) { 264 // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects. 265 Vector<RefPtr<MutationObserver>> notifyList; 266 copyToVector(activeMutationObservers(), notifyList); 246 267 activeMutationObservers().clear(); 247 std::sort( observers.begin(), observers.end(), [](auto& lhs, auto& rhs) {268 std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) { 248 269 return lhs->m_priority < rhs->m_priority; 249 270 }); 250 271 251 for (auto& observer : observers) { 272 // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list. 273 // 4. Empty unit of related similar-origin browsing contexts' signal slot list. 274 Vector<RefPtr<HTMLSlotElement>> slotList; 275 if (!signalSlotList().isEmpty()) { 276 slotList.swap(signalSlotList()); 277 for (auto& slot : slotList) 278 slot->didRemoveFromSignalSlotList(); 279 } 280 281 // 5. For each MutationObserver object mo in notify list, execute a compound microtask subtask 282 for (auto& observer : notifyList) { 252 283 if (observer->canDeliver()) 253 284 observer->deliver(); … … 255 286 suspendedMutationObservers().add(observer); 256 287 } 288 289 // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot. 290 for (auto& slot : slotList) 291 slot->dispatchSlotChangeEvent(); 257 292 } 258 293 -
trunk/Source/WebCore/dom/MutationObserver.h
r200552 r201858 40 40 namespace WebCore { 41 41 42 class HTMLSlotElement; 42 43 class MutationCallback; 43 44 class MutationObserverRegistration; … … 97 98 HashSet<Node*> observedNodes() const; 98 99 100 static void enqueueSlotChangeEvent(HTMLSlotElement&); 101 99 102 private: 100 103 explicit MutationObserver(Ref<MutationCallback>&&); 101 104 void deliver(); 102 105 103 static void deliverAllMutations();106 static void notifyMutationObservers(); 104 107 static bool validateOptions(MutationObserverOptions); 105 108 -
trunk/Source/WebCore/html/HTMLSlotElement.cpp
r200557 r201858 32 32 #include "EventNames.h" 33 33 #include "HTMLNames.h" 34 #include "MutationObserver.h" 34 35 #include "ShadowRoot.h" 35 36 … … 127 128 void HTMLSlotElement::enqueueSlotChangeEvent() 128 129 { 129 if (m_enqueuedSlotChangeEvent) 130 // https://dom.spec.whatwg.org/#signal-a-slot-change 131 if (m_inSignalSlotList) 130 132 return; 133 m_inSignalSlotList = true; 134 MutationObserver::enqueueSlotChangeEvent(*this); 135 } 136 137 void HTMLSlotElement::dispatchSlotChangeEvent() 138 { 139 m_inSignalSlotList = false; 131 140 132 141 bool bubbles = false; 133 142 bool cancelable = false; 134 autoevent = Event::create(eventNames().slotchangeEvent, bubbles, cancelable);143 Ref<Event> event = Event::create(eventNames().slotchangeEvent, bubbles, cancelable); 135 144 event->setTarget(this); 136 m_enqueuedSlotChangeEvent = event.ptr(); 137 document().enqueueSlotchangeEvent(WTFMove(event)); 138 } 139 140 bool HTMLSlotElement::dispatchEvent(Event& event) 141 { 142 if (&event == m_enqueuedSlotChangeEvent) 143 m_enqueuedSlotChangeEvent = nullptr; 144 return HTMLElement::dispatchEvent(event); 145 dispatchEvent(event); 145 146 } 146 147 -
trunk/Source/WebCore/html/HTMLSlotElement.h
r200557 r201858 43 43 44 44 void enqueueSlotChangeEvent(); 45 void didRemoveFromSignalSlotList() { m_inSignalSlotList = false; } 46 47 void dispatchSlotChangeEvent(); 45 48 46 49 private: … … 50 53 void removedFrom(ContainerNode&) final; 51 54 void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason) final; 52 bool dispatchEvent(Event&) final;53 55 54 Event* m_enqueuedSlotChangeEvent { nullptr};56 bool m_inSignalSlotList { false }; 55 57 }; 56 58
Note: See TracChangeset
for help on using the changeset viewer.