Changeset 201858 in webkit


Ignore:
Timestamp:
Jun 9, 2016 2:09:50 AM (8 years ago)
Author:
rniwa@webkit.org
Message:

slotchange event should be fired at the end of microtask
https://bugs.webkit.org/show_bug.cgi?id=157374
<rdar://problem/26154024>

Reviewed by Antti Koivisto.

Source/WebCore:

Dispatch slotchange event at the end of every microtask after delivering records to mutation observers
as specified in: https://dom.spec.whatwg.org/#notify-mutation-observers

Test: fast/shadow-dom/slotchange-event.html

  • dom/Document.cpp:

(WebCore::Document::enqueueSlotchangeEvent): Deleted.

  • dom/Document.h:
  • dom/MutationObserver.cpp:

(WebCore::signalSlotList): Added.
(WebCore::MutationObserverMicrotask::run): mutationObserverCompoundMicrotaskQueuedFlag is now unset in
notifyMutationObservers to better match the concept to "notify mutation observers".
(WebCore::MutationObserver::enqueueSlotChangeEvent): Added.
(WebCore::MutationObserver::notifyMutationObservers): Renamed from deliverAllMutations. Added the code
to dispatch slotchange events as spec'ed, and also added comments for each step.

  • dom/MutationObserver.h:
  • html/HTMLSlotElement.cpp:

(WebCore::HTMLSlotElement::enqueueSlotChangeEvent): Use MutationObserver::enqueueSlotChangeEvent. Don't
create an event here since that is only needed when dispatching the event, and to keep track of whether
we've already scheduled an event or not. Use a boolean flag instead for the latter.
(WebCore::HTMLSlotElement::dispatchSlotChangeEvent): Added. Creates and dispatches an event.
(WebCore::HTMLSlotElement::dispatchEvent): Deleted.

  • html/HTMLSlotElement.h:

(WebCore::HTMLSlotElement::didRemoveFromSignalSlotList): Added.

LayoutTests:

Added a test case to ensure slotchange event is dispatched at the end of a microtask.

  • fast/shadow-dom/slotchange-event-expected.txt:
  • fast/shadow-dom/slotchange-event.html:
Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r201855 r201858  
     12016-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
    1142016-06-08  Joseph Pecoraro  <pecoraro@apple.com>
    215
  • trunk/LayoutTests/fast/shadow-dom/slotchange-event-expected.txt

    r198115 r201858  
    3030PASS slotchange event must fire on a slot element inside an open shadow root  not in a document when nested slots's contents change
    3131PASS slotchange event must fire on a slot element inside a closed shadow root  not in a document when nested slots's contents change
     32PASS 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
     33PASS 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
     34PASS 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
     35PASS 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
    3236
  • trunk/LayoutTests/fast/shadow-dom/slotchange-event.html

    r198115 r201858  
    535535testSlotchangeFiresWhenNestedSlotChange('closed', false);
    536536
     537function 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
     612testSlotchangeFiresAtEndOfMicroTask('open', true);
     613testSlotchangeFiresAtEndOfMicroTask('closed', true);
     614testSlotchangeFiresAtEndOfMicroTask('open', false);
     615testSlotchangeFiresAtEndOfMicroTask('closed', false);
     616
    537617</script>
    538618</body>
  • trunk/Source/WebCore/ChangeLog

    r201856 r201858  
     12016-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
    1342016-06-08  Youenn Fablet  <youenn.fablet@crf.canon.fr>
    235
  • trunk/Source/WebCore/dom/Document.cpp

    r201832 r201858  
    42514251}
    42524252
    4253 void Document::enqueueSlotchangeEvent(Ref<Event>&& event)
    4254 {
    4255     m_eventQueue.enqueueEvent(WTFMove(event));
    4256 }
    4257 
    42584253RefPtr<Event> Document::createEvent(const String& type, ExceptionCode& ec)
    42594254{
  • trunk/Source/WebCore/dom/Document.h

    r201832 r201858  
    10901090    void enqueueDocumentEvent(Ref<Event>&&);
    10911091    void enqueueOverflowEvent(Ref<Event>&&);
    1092     void enqueueSlotchangeEvent(Ref<Event>&&);
    10931092    void enqueuePageshowEvent(PageshowEventPersistence);
    10941093    void enqueueHashchangeEvent(const String& oldURL, const String& newURL);
  • trunk/Source/WebCore/dom/MutationObserver.cpp

    r201333 r201858  
    3636#include "Document.h"
    3737#include "ExceptionCode.h"
     38#include "HTMLSlotElement.h"
    3839#include "Microtasks.h"
    3940#include "MutationCallback.h"
     
    147148}
    148149
     150// https://dom.spec.whatwg.org/#signal-slot-list
     151static Vector<RefPtr<HTMLSlotElement>>& signalSlotList()
     152{
     153    static NeverDestroyed<Vector<RefPtr<HTMLSlotElement>>> list;
     154    return list;
     155}
     156
    149157static bool mutationObserverCompoundMicrotaskQueuedFlag;
    150158
     
    154162    Result run() final
    155163    {
    156         mutationObserverCompoundMicrotaskQueuedFlag = false;
    157         MutationObserver::deliverAllMutations();
     164        MutationObserver::notifyMutationObservers();
    158165        return Result::Done;
    159166    }
     
    173180    m_records.append(WTFMove(mutation));
    174181    activeMutationObservers().add(this);
     182
     183    queueMutationObserverCompoundMicrotask();
     184}
     185
     186void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot)
     187{
     188    ASSERT(isMainThread());
     189    ASSERT(!signalSlotList().contains(&slot));
     190    signalSlotList().append(&slot);
    175191
    176192    queueMutationObserverCompoundMicrotask();
     
    221237}
    222238
    223 void MutationObserver::deliverAllMutations()
    224 {
     239void 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
    225245    ASSERT(isMainThread());
    226246    static bool deliveryInProgress = false;
     
    241261    }
    242262
    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);
    246267        activeMutationObservers().clear();
    247         std::sort(observers.begin(), observers.end(), [](auto& lhs, auto& rhs) {
     268        std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) {
    248269            return lhs->m_priority < rhs->m_priority;
    249270        });
    250271
    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) {
    252283            if (observer->canDeliver())
    253284                observer->deliver();
     
    255286                suspendedMutationObservers().add(observer);
    256287        }
     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();
    257292    }
    258293
  • trunk/Source/WebCore/dom/MutationObserver.h

    r200552 r201858  
    4040namespace WebCore {
    4141
     42class HTMLSlotElement;
    4243class MutationCallback;
    4344class MutationObserverRegistration;
     
    9798    HashSet<Node*> observedNodes() const;
    9899
     100    static void enqueueSlotChangeEvent(HTMLSlotElement&);
     101
    99102private:
    100103    explicit MutationObserver(Ref<MutationCallback>&&);
    101104    void deliver();
    102105
    103     static void deliverAllMutations();
     106    static void notifyMutationObservers();
    104107    static bool validateOptions(MutationObserverOptions);
    105108
  • trunk/Source/WebCore/html/HTMLSlotElement.cpp

    r200557 r201858  
    3232#include "EventNames.h"
    3333#include "HTMLNames.h"
     34#include "MutationObserver.h"
    3435#include "ShadowRoot.h"
    3536
     
    127128void HTMLSlotElement::enqueueSlotChangeEvent()
    128129{
    129     if (m_enqueuedSlotChangeEvent)
     130    // https://dom.spec.whatwg.org/#signal-a-slot-change
     131    if (m_inSignalSlotList)
    130132        return;
     133    m_inSignalSlotList = true;
     134    MutationObserver::enqueueSlotChangeEvent(*this);
     135}
     136
     137void HTMLSlotElement::dispatchSlotChangeEvent()
     138{
     139    m_inSignalSlotList = false;
    131140
    132141    bool bubbles = false;
    133142    bool cancelable = false;
    134     auto event = Event::create(eventNames().slotchangeEvent, bubbles, cancelable);
     143    Ref<Event> event = Event::create(eventNames().slotchangeEvent, bubbles, cancelable);
    135144    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);
    145146}
    146147
  • trunk/Source/WebCore/html/HTMLSlotElement.h

    r200557 r201858  
    4343
    4444    void enqueueSlotChangeEvent();
     45    void didRemoveFromSignalSlotList() { m_inSignalSlotList = false; }
     46
     47    void dispatchSlotChangeEvent();
    4548
    4649private:
     
    5053    void removedFrom(ContainerNode&) final;
    5154    void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason) final;
    52     bool dispatchEvent(Event&) final;
    5355
    54     Event* m_enqueuedSlotChangeEvent { nullptr };
     56    bool m_inSignalSlotList { false };
    5557};
    5658
Note: See TracChangeset for help on using the changeset viewer.