There should be one MicrotaskQueue per EventLoop
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2019 23:27:25 +0000 (23:27 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2019 23:27:25 +0000 (23:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204492
<rdar://problem/57420645>

Reviewed by Antti Koivisto.

This patch makes microtask queue and the microtasks for mutation observers and custom elements
specific to each event loop so that only similar origin windows share the same microtask queue
and mutation observer's compound microtask or custom element's backup element queue.

As a result, we can remove the workaround we added in r247222.

Because microtasks for mutation observers and custom elements are shared across similar origin
window agents, we can't use any one document's task group to schedule these. Instead, we create
a new "perpetual" task group in WindowEventLoop which is never suspended or stopped. It's the
responsibility of mutation observer and custom elements' code to deal with suspened or stopped
documents as it has been the case.

See also: https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask
          https://html.spec.whatwg.org/multipage/custom-elements.html#backup-element-queue

Test: editing/pasteboard/paste-does-not-fire-promises-while-sanitizing-web-content.html

* dom/CustomElementReactionQueue.cpp:
(WebCore::CustomElementQueue::add): Renamed from CustomElementReactionQueue::ElementQueue to
allow forward declaration in EventLoop.h.
(WebCore::CustomElementQueue::invokeAll): Ditto.
(WebCore::CustomElementQueue::processQueue): Ditto.
(WebCore::CustomElementReactionQueue::enqueueElementOnAppropriateElementQueue):
(WebCore::CustomElementReactionQueue::processBackupQueue):
(WebCore::CustomElementReactionQueue::ensureBackupQueue): Deleted. Moved to WindowEventLoop.
(WebCore::CustomElementReactionQueue::backupElementQueue): Ditto.
* dom/CustomElementReactionQueue.h:
(WebCore::CustomElementQueue): Renamed from CustomElementReactionQueue::ElementQueue to allow
forward declaration in EventLoop.h.
* dom/Document.cpp:
(WebCore::Document::finishedParsing): Removed the workaround added in r247222.
(WebCore::Document::eventLoop):
(WebCore::Document::windowEventLoop): Added.
* dom/Document.h:
* dom/MutationObserver.cpp:
(WebCore::activeMutationObservers): Deleted. Moved to WindowEventLoop.
(WebCore::suspendedMutationObservers): Ditto.
(WebCore::signalSlotList): Ditto.
(WebCore::MutationObserver::queueMutationObserverCompoundMicrotask): Ditto.
(WebCore::MutationObserver::enqueueMutationRecord):
(WebCore::MutationObserver::enqueueSlotChangeEvent):
(WebCore::MutationObserver::setHasTransientRegistration):
(WebCore::MutationObserver::notifyMutationObservers): Now takes WindowEventLoop since various
lists and hash maps are specific to each WindowEventLoop.
* dom/MutationObserver.h:
* dom/WindowEventLoop.cpp:
(WebCore::WindowEventLoop::WindowEventLoop): Create m_perpetualTaskGroupForSimilarOriginWindowAgents.
(WebCore::WindowEventLoop::microtaskQueue):
(WebCore::WindowEventLoop::queueMutationObserverCompoundMicrotask):
(WebCore::WindowEventLoop::backupElementQueue):
* dom/WindowEventLoop.h:
* editing/markup.cpp:
(WebCore::createPageForSanitizingWebContent): Removed the workaround added in r247222.
* page/Page.h:
(WebCore::Page::setIsForSanitizingWebContent): Ditto.
(WebCore::Page::isForSanitizingWebContent const): Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@253279 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/dom/CustomElementReactionQueue.cpp
Source/WebCore/dom/CustomElementReactionQueue.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/MutationObserver.cpp
Source/WebCore/dom/MutationObserver.h
Source/WebCore/dom/WindowEventLoop.cpp
Source/WebCore/dom/WindowEventLoop.h
Source/WebCore/editing/markup.cpp
Source/WebCore/page/Page.h

index 89ec91a..90a5cd7 100644 (file)
@@ -1,3 +1,68 @@
+2019-12-08  Ryosuke Niwa  <rniwa@webkit.org>
+
+        There should be one MicrotaskQueue per EventLoop
+        https://bugs.webkit.org/show_bug.cgi?id=204492
+        <rdar://problem/57420645>
+
+        Reviewed by Antti Koivisto.
+
+        This patch makes microtask queue and the microtasks for mutation observers and custom elements
+        specific to each event loop so that only similar origin windows share the same microtask queue
+        and mutation observer's compound microtask or custom element's backup element queue.
+
+        As a result, we can remove the workaround we added in r247222.
+
+        Because microtasks for mutation observers and custom elements are shared across similar origin
+        window agents, we can't use any one document's task group to schedule these. Instead, we create
+        a new "perpetual" task group in WindowEventLoop which is never suspended or stopped. It's the
+        responsibility of mutation observer and custom elements' code to deal with suspened or stopped
+        documents as it has been the case.
+
+        See also: https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask
+                  https://html.spec.whatwg.org/multipage/custom-elements.html#backup-element-queue
+
+        Test: editing/pasteboard/paste-does-not-fire-promises-while-sanitizing-web-content.html
+
+        * dom/CustomElementReactionQueue.cpp:
+        (WebCore::CustomElementQueue::add): Renamed from CustomElementReactionQueue::ElementQueue to
+        allow forward declaration in EventLoop.h.
+        (WebCore::CustomElementQueue::invokeAll): Ditto.
+        (WebCore::CustomElementQueue::processQueue): Ditto.
+        (WebCore::CustomElementReactionQueue::enqueueElementOnAppropriateElementQueue):
+        (WebCore::CustomElementReactionQueue::processBackupQueue):
+        (WebCore::CustomElementReactionQueue::ensureBackupQueue): Deleted. Moved to WindowEventLoop.
+        (WebCore::CustomElementReactionQueue::backupElementQueue): Ditto.
+        * dom/CustomElementReactionQueue.h:
+        (WebCore::CustomElementQueue): Renamed from CustomElementReactionQueue::ElementQueue to allow
+        forward declaration in EventLoop.h.
+        * dom/Document.cpp:
+        (WebCore::Document::finishedParsing): Removed the workaround added in r247222.
+        (WebCore::Document::eventLoop):
+        (WebCore::Document::windowEventLoop): Added.
+        * dom/Document.h:
+        * dom/MutationObserver.cpp:
+        (WebCore::activeMutationObservers): Deleted. Moved to WindowEventLoop.
+        (WebCore::suspendedMutationObservers): Ditto.
+        (WebCore::signalSlotList): Ditto.
+        (WebCore::MutationObserver::queueMutationObserverCompoundMicrotask): Ditto.
+        (WebCore::MutationObserver::enqueueMutationRecord):
+        (WebCore::MutationObserver::enqueueSlotChangeEvent):
+        (WebCore::MutationObserver::setHasTransientRegistration):
+        (WebCore::MutationObserver::notifyMutationObservers): Now takes WindowEventLoop since various
+        lists and hash maps are specific to each WindowEventLoop.
+        * dom/MutationObserver.h:
+        * dom/WindowEventLoop.cpp:
+        (WebCore::WindowEventLoop::WindowEventLoop): Create m_perpetualTaskGroupForSimilarOriginWindowAgents.
+        (WebCore::WindowEventLoop::microtaskQueue):
+        (WebCore::WindowEventLoop::queueMutationObserverCompoundMicrotask):
+        (WebCore::WindowEventLoop::backupElementQueue):
+        * dom/WindowEventLoop.h:
+        * editing/markup.cpp:
+        (WebCore::createPageForSanitizingWebContent): Removed the workaround added in r247222.
+        * page/Page.h:
+        (WebCore::Page::setIsForSanitizingWebContent): Ditto.
+        (WebCore::Page::isForSanitizingWebContent const): Ditto.
+
 2019-12-08  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Move WebCore::Path encoders and decoders into WebCore from WebCoreArgumentCoders
index f9aa9a7..7f9345e 100644 (file)
@@ -34,6 +34,7 @@
 #include "HTMLNames.h"
 #include "JSCustomElementInterface.h"
 #include "JSDOMBinding.h"
+#include "WindowEventLoop.h"
 #include <JavaScriptCore/CatchScope.h>
 #include <JavaScriptCore/Heap.h>
 #include <wtf/NeverDestroyed.h>
@@ -238,14 +239,14 @@ void CustomElementReactionQueue::invokeAll(Element& element)
     }
 }
 
-inline void CustomElementReactionQueue::ElementQueue::add(Element& element)
+inline void CustomElementQueue::add(Element& element)
 {
     ASSERT(!m_invoking);
     // FIXME: Avoid inserting the same element multiple times.
     m_elements.append(element);
 }
 
-inline void CustomElementReactionQueue::ElementQueue::invokeAll()
+inline void CustomElementQueue::invokeAll()
 {
     RELEASE_ASSERT(!m_invoking);
     SetForScope<bool> invoking(m_invoking, true);
@@ -262,7 +263,7 @@ inline void CustomElementReactionQueue::ElementQueue::invokeAll()
     m_elements.clear();
 }
 
-inline void CustomElementReactionQueue::ElementQueue::processQueue(JSC::JSGlobalObject* state)
+inline void CustomElementQueue::processQueue(JSC::JSGlobalObject* state)
 {
     if (!state) {
         invokeAll();
@@ -293,14 +294,13 @@ void CustomElementReactionQueue::enqueueElementOnAppropriateElementQueue(Element
 {
     ASSERT(element.reactionQueue());
     if (!CustomElementReactionStack::s_currentProcessingStack) {
-        auto& queue = ensureBackupQueue(element.document());
-        queue.add(element);
+        element.document().windowEventLoop().backupElementQueue().add(element);
         return;
     }
 
     auto*& queue = CustomElementReactionStack::s_currentProcessingStack->m_queue;
     if (!queue) // We use a raw pointer to avoid genearing code to delete it in ~CustomElementReactionStack.
-        queue = new ElementQueue;
+        queue = new CustomElementQueue;
     queue->add(element);
 }
 
@@ -318,30 +318,9 @@ void CustomElementReactionStack::processQueue(JSC::JSGlobalObject* state)
     m_queue = nullptr;
 }
 
-static bool s_processingBackupElementQueue = false;
-
-// FIXME: BackupQueue must be per event loop.
-CustomElementReactionQueue::ElementQueue& CustomElementReactionQueue::ensureBackupQueue(Document& document)
-{
-    if (!s_processingBackupElementQueue) {
-        s_processingBackupElementQueue = true;
-        document.eventLoop().queueMicrotask([] {
-            CustomElementReactionQueue::processBackupQueue();
-        });
-    }
-    return backupElementQueue();
-}
-
-void CustomElementReactionQueue::processBackupQueue()
-{
-    backupElementQueue().processQueue(nullptr);
-    s_processingBackupElementQueue = false;
-}
-
-CustomElementReactionQueue::ElementQueue& CustomElementReactionQueue::backupElementQueue()
+void CustomElementReactionQueue::processBackupQueue(CustomElementQueue& backupElementQueue)
 {
-    static NeverDestroyed<ElementQueue> queue;
-    return queue.get();
+    backupElementQueue.processQueue(nullptr);
 }
 
 }
index 19e9560..c321287 100644 (file)
@@ -45,6 +45,23 @@ class Element;
 class JSCustomElementInterface;
 class QualifiedName;
 
+// https://html.spec.whatwg.org/multipage/custom-elements.html#element-queue
+class CustomElementQueue {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(CustomElementQueue);
+public:
+    CustomElementQueue() = default;
+
+    void add(Element&);
+    void processQueue(JSC::JSGlobalObject*);
+
+private:
+    void invokeAll();
+
+    Vector<GCReachableRef<Element>> m_elements;
+    bool m_invoking { false };
+};
+
 class CustomElementReactionQueue {
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(CustomElementReactionQueue);
@@ -64,24 +81,10 @@ public:
     void invokeAll(Element&);
     void clear();
 
-    static void processBackupQueue();
-
-    class ElementQueue {
-    public:
-        void add(Element&);
-        void processQueue(JSC::JSGlobalObject*);
-
-    private:
-        void invokeAll();
-
-        Vector<GCReachableRef<Element>> m_elements;
-        bool m_invoking { false };
-    };
+    static void processBackupQueue(CustomElementQueue&);
 
 private:
     static void enqueueElementOnAppropriateElementQueue(Element&);
-    static ElementQueue& ensureBackupQueue(Document&);
-    static ElementQueue& backupElementQueue();
 
     Ref<JSCustomElementInterface> m_interface;
     Vector<CustomElementReactionQueueItem> m_items;
@@ -158,7 +161,7 @@ public:
 private:
     WEBCORE_EXPORT void processQueue(JSC::JSGlobalObject*);
 
-    CustomElementReactionQueue::ElementQueue* m_queue { nullptr }; // Use raw pointer to avoid generating delete in the destructor.
+    CustomElementQueue* m_queue { nullptr }; // Use raw pointer to avoid generating delete in the destructor.
     CustomElementReactionStack* m_previousProcessingStack;
     JSC::JSGlobalObject* m_state;
 
index b23d408..1612095 100644 (file)
@@ -5803,11 +5803,8 @@ void Document::finishedParsing()
     if (!m_documentTiming.domContentLoadedEventStart)
         m_documentTiming.domContentLoadedEventStart = MonotonicTime::now();
 
-    if (!page() || !page()->isForSanitizingWebContent()) {
-        // FIXME: Schedule a task to fire DOMContentLoaded event instead. See webkit.org/b/82931
-        eventLoop().performMicrotaskCheckpoint();
-    }
-
+    // FIXME: Schedule a task to fire DOMContentLoaded event instead. See webkit.org/b/82931
+    eventLoop().performMicrotaskCheckpoint();
     dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent, Event::CanBubble::Yes, Event::IsCancelable::No));
 
     if (!m_documentTiming.domContentLoadedEventEnd)
@@ -6263,9 +6260,7 @@ EventLoopTaskGroup& Document::eventLoop()
 {
     ASSERT(isMainThread());
     if (UNLIKELY(!m_documentTaskGroup)) {
-        // https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key
-        m_eventLoop = WindowEventLoop::eventLoopForSecurityOrigin(securityOrigin());
-        m_documentTaskGroup = makeUnique<EventLoopTaskGroup>(*m_eventLoop);
+        m_documentTaskGroup = makeUnique<EventLoopTaskGroup>(windowEventLoop());
         if (activeDOMObjectsAreStopped())
             m_documentTaskGroup->stopAndDiscardAllTasks();
         else if (activeDOMObjectsAreSuspended())
@@ -6274,6 +6269,14 @@ EventLoopTaskGroup& Document::eventLoop()
     return *m_documentTaskGroup;
 }
 
+WindowEventLoop& Document::windowEventLoop()
+{
+    ASSERT(isMainThread());
+    if (UNLIKELY(!m_eventLoop))
+        m_eventLoop = WindowEventLoop::eventLoopForSecurityOrigin(securityOrigin());
+    return *m_eventLoop;
+}
+
 void Document::suspendScheduledTasks(ReasonForSuspension reason)
 {
     if (m_scheduledTasksAreSuspended) {
index 07a96a1..892b3b6 100644 (file)
@@ -1067,6 +1067,7 @@ public:
     WEBCORE_EXPORT void postTask(Task&&) final; // Executes the task on context's thread asynchronously.
 
     EventLoopTaskGroup& eventLoop() final;
+    WindowEventLoop& windowEventLoop();
 
     ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); }
     void suspendScriptedAnimationControllerCallbacks();
index ebfee24..a4ac0eb 100644 (file)
 #include "MutationObserver.h"
 
 #include "Document.h"
-#include "EventLoop.h"
 #include "GCReachableRef.h"
 #include "HTMLSlotElement.h"
 #include "InspectorInstrumentation.h"
 #include "MutationCallback.h"
 #include "MutationObserverRegistration.h"
 #include "MutationRecord.h"
+#include "WindowEventLoop.h"
 #include <algorithm>
 #include <wtf/IsoMallocInlines.h>
 #include <wtf/MainThread.h>
@@ -137,40 +137,6 @@ void MutationObserver::observationEnded(MutationObserverRegistration& registrati
     m_registrations.remove(&registration);
 }
 
-typedef HashSet<RefPtr<MutationObserver>> MutationObserverSet;
-
-static MutationObserverSet& activeMutationObservers()
-{
-    static NeverDestroyed<MutationObserverSet> activeObservers;
-    return activeObservers;
-}
-
-static MutationObserverSet& suspendedMutationObservers()
-{
-    static NeverDestroyed<MutationObserverSet> suspendedObservers;
-    return suspendedObservers;
-}
-
-// https://dom.spec.whatwg.org/#signal-slot-list
-static Vector<GCReachableRef<HTMLSlotElement>>& signalSlotList()
-{
-    static NeverDestroyed<Vector<GCReachableRef<HTMLSlotElement>>> list;
-    return list;
-}
-
-// This state must be per event loop.
-static bool mutationObserverCompoundMicrotaskQueuedFlag = false;
-
-void MutationObserver::queueMutationObserverCompoundMicrotask(Document& document)
-{
-    if (mutationObserverCompoundMicrotaskQueuedFlag)
-        return;
-    mutationObserverCompoundMicrotaskQueuedFlag = true;
-    document.eventLoop().queueMicrotask([] {
-        notifyMutationObservers();
-    });
-}
-
 void MutationObserver::enqueueMutationRecord(Ref<MutationRecord>&& mutation)
 {
     ASSERT(isMainThread());
@@ -179,26 +145,28 @@ void MutationObserver::enqueueMutationRecord(Ref<MutationRecord>&& mutation)
 
     m_pendingTargets.add(*mutation->target());
     m_records.append(WTFMove(mutation));
-    activeMutationObservers().add(this);
 
-    queueMutationObserverCompoundMicrotask(document.get());
+    auto eventLoop = makeRef(document->windowEventLoop());
+    eventLoop->activeMutationObservers().add(this);
+    eventLoop->queueMutationObserverCompoundMicrotask();
 }
 
 void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot)
 {
     ASSERT(isMainThread());
-    ASSERT(signalSlotList().findMatching([&slot](auto& entry) { return entry.ptr() == &slot; }) == notFound);
-    signalSlotList().append(slot);
+    auto eventLoop = makeRef(slot.document().windowEventLoop());
+    auto& list = eventLoop->signalSlotList();
+    ASSERT(list.findMatching([&slot](auto& entry) { return entry.ptr() == &slot; }) == notFound);
+    list.append(slot);
 
-    queueMutationObserverCompoundMicrotask(slot.document());
+    eventLoop->queueMutationObserverCompoundMicrotask();
 }
 
 void MutationObserver::setHasTransientRegistration(Document& document)
 {
-    ASSERT(isMainThread());
-    activeMutationObservers().add(this);
-
-    queueMutationObserverCompoundMicrotask(document);
+    auto eventLoop = makeRef(document.windowEventLoop());
+    eventLoop->activeMutationObservers().add(this);
+    eventLoop->queueMutationObserverCompoundMicrotask();
 }
 
 HashSet<Node*> MutationObserver::observedNodes() const
@@ -251,32 +219,23 @@ void MutationObserver::deliver()
     }
 }
 
-void MutationObserver::notifyMutationObservers()
+// https://dom.spec.whatwg.org/#notify-mutation-observers
+void MutationObserver::notifyMutationObservers(WindowEventLoop& eventLoop)
 {
-    // https://dom.spec.whatwg.org/#notify-mutation-observers
-    // 1. Unset mutation observer compound microtask queued flag.
-    mutationObserverCompoundMicrotaskQueuedFlag = false;
-
-    ASSERT(isMainThread());
-    static bool deliveryInProgress = false;
-    if (deliveryInProgress)
-        return;
-    deliveryInProgress = true;
-
-    if (!suspendedMutationObservers().isEmpty()) {
-        for (auto& observer : copyToVector(suspendedMutationObservers())) {
+    if (!eventLoop.suspendedMutationObservers().isEmpty()) {
+        for (auto& observer : copyToVector(eventLoop.suspendedMutationObservers())) {
             if (!observer->canDeliver())
                 continue;
 
-            suspendedMutationObservers().remove(observer);
-            activeMutationObservers().add(observer);
+            eventLoop.suspendedMutationObservers().remove(observer);
+            eventLoop.activeMutationObservers().add(observer);
         }
     }
 
-    while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) {
+    while (!eventLoop.activeMutationObservers().isEmpty() || !eventLoop.signalSlotList().isEmpty()) {
         // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects.
-        auto notifyList = copyToVector(activeMutationObservers());
-        activeMutationObservers().clear();
+        auto notifyList = copyToVector(eventLoop.activeMutationObservers());
+        eventLoop.activeMutationObservers().clear();
         std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) {
             return lhs->m_priority < rhs->m_priority;
         });
@@ -284,8 +243,8 @@ void MutationObserver::notifyMutationObservers()
         // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list.
         // 4. Empty unit of related similar-origin browsing contexts' signal slot list.
         Vector<GCReachableRef<HTMLSlotElement>> slotList;
-        if (!signalSlotList().isEmpty()) {
-            slotList.swap(signalSlotList());
+        if (!eventLoop.signalSlotList().isEmpty()) {
+            slotList.swap(eventLoop.signalSlotList());
             for (auto& slot : slotList)
                 slot->didRemoveFromSignalSlotList();
         }
@@ -295,15 +254,13 @@ void MutationObserver::notifyMutationObservers()
             if (observer->canDeliver())
                 observer->deliver();
             else
-                suspendedMutationObservers().add(observer);
+                eventLoop.suspendedMutationObservers().add(observer);
         }
 
         // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot.
         for (auto& slot : slotList)
             slot->dispatchSlotChangeEvent();
     }
-
-    deliveryInProgress = false;
 }
 
 } // namespace WebCore
index c567bca..477f0f9 100644 (file)
@@ -46,6 +46,7 @@ class MutationCallback;
 class MutationObserverRegistration;
 class MutationRecord;
 class Node;
+class WindowEventLoop;
 
 using MutationObserverOptions = unsigned char;
 using MutationRecordDeliveryOptions = unsigned char;
@@ -106,12 +107,12 @@ public:
 
     static void enqueueSlotChangeEvent(HTMLSlotElement&);
 
+    static void notifyMutationObservers(WindowEventLoop&);
+
 private:
     explicit MutationObserver(Ref<MutationCallback>&&);
     void deliver();
 
-    static void queueMutationObserverCompoundMicrotask(Document&);
-    static void notifyMutationObservers();
     static bool validateOptions(MutationObserverOptions);
 
     Ref<MutationCallback> m_callback;
index dcccf96..d9dcfbf 100644 (file)
 #include "WindowEventLoop.h"
 
 #include "CommonVM.h"
+#include "CustomElementReactionQueue.h"
 #include "Document.h"
+#include "HTMLSlotElement.h"
 #include "Microtasks.h"
+#include "MutationObserver.h"
 
 namespace WebCore {
 
@@ -79,6 +82,7 @@ inline Ref<WindowEventLoop> WindowEventLoop::create(const String& agentClusterKe
 inline WindowEventLoop::WindowEventLoop(const String& agentClusterKey)
     : m_agentClusterKey(agentClusterKey)
     , m_timer(*this, &WindowEventLoop::run)
+    , m_perpetualTaskGroupForSimilarOriginWindowAgents(*this)
 {
 }
 
@@ -102,9 +106,45 @@ bool WindowEventLoop::isContextThread() const
 
 MicrotaskQueue& WindowEventLoop::microtaskQueue()
 {
-    // MicrotaskQueue must be one per event loop.
-    static NeverDestroyed<MicrotaskQueue> queue(commonVM());
-    return queue;
+    if (!m_microtaskQueue)
+        m_microtaskQueue = makeUnique<MicrotaskQueue>(commonVM());
+    return *m_microtaskQueue;
+}
+
+void WindowEventLoop::queueMutationObserverCompoundMicrotask()
+{
+    if (m_mutationObserverCompoundMicrotaskQueuedFlag)
+        return;
+    m_mutationObserverCompoundMicrotaskQueuedFlag = true;
+    m_perpetualTaskGroupForSimilarOriginWindowAgents.queueMicrotask([this] {
+        // We can't make a Ref to WindowEventLoop in the lambda capture as that would result in a reference cycle & leak.
+        auto protectedThis = makeRef(*this);
+        m_mutationObserverCompoundMicrotaskQueuedFlag = false;
+
+        // FIXME: This check doesn't exist in the spec.
+        if (m_deliveringMutationRecords)
+            return;
+        m_deliveringMutationRecords = true;
+        MutationObserver::notifyMutationObservers(*this);
+        m_deliveringMutationRecords = false;
+    });
+}
+
+CustomElementQueue& WindowEventLoop::backupElementQueue()
+{
+    if (!m_processingBackupElementQueue) {
+        m_processingBackupElementQueue = true;
+        m_perpetualTaskGroupForSimilarOriginWindowAgents.queueMicrotask([this] {
+            // We can't make a Ref to WindowEventLoop in the lambda capture as that would result in a reference cycle & leak.
+            auto protectedThis = makeRef(*this);
+            m_processingBackupElementQueue = false;
+            ASSERT(m_customElementQueue);
+            CustomElementReactionQueue::processBackupQueue(*m_customElementQueue);
+        });
+    }
+    if (!m_customElementQueue)
+        m_customElementQueue = makeUnique<CustomElementQueue>();
+    return *m_customElementQueue;
 }
 
 } // namespace WebCore
index 7a079f6..7f04cef 100644 (file)
 
 #pragma once
 
-#include "DocumentIdentifier.h"
 #include "EventLoop.h"
+#include "GCReachableRef.h"
 #include "Timer.h"
 #include <wtf/HashSet.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
+class CustomElementQueue;
 class Document;
+class HTMLSlotElement;
+class MutationObserver;
 
 // https://html.spec.whatwg.org/multipage/webappapis.html#window-event-loop
 class WindowEventLoop final : public EventLoop {
@@ -42,6 +45,13 @@ public:
 
     virtual ~WindowEventLoop();
 
+    void queueMutationObserverCompoundMicrotask();
+    Vector<GCReachableRef<HTMLSlotElement>>& signalSlotList() { return m_signalSlotList; }
+    HashSet<RefPtr<MutationObserver>>& activeMutationObservers() { return m_activeObservers; }
+    HashSet<RefPtr<MutationObserver>>& suspendedMutationObservers() { return m_suspendedObservers; }
+
+    CustomElementQueue& backupElementQueue();
+
 private:
     static Ref<WindowEventLoop> create(const String&);
     WindowEventLoop(const String&);
@@ -52,6 +62,21 @@ private:
 
     String m_agentClusterKey;
     Timer m_timer;
+    std::unique_ptr<MicrotaskQueue> m_microtaskQueue;
+
+    // Each task scheduled in event loop is associated with a document so that it can be suspened or stopped
+    // when the associated document is suspened or stopped. This task group is used to schedule a task
+    // which is not scheduled to a specific document, and should only be used when it's absolutely required.
+    EventLoopTaskGroup m_perpetualTaskGroupForSimilarOriginWindowAgents;
+
+    bool m_mutationObserverCompoundMicrotaskQueuedFlag { false };
+    bool m_deliveringMutationRecords { false }; // FIXME: This flag doesn't exist in the spec.
+    Vector<GCReachableRef<HTMLSlotElement>> m_signalSlotList; // https://dom.spec.whatwg.org/#signal-slot-list
+    HashSet<RefPtr<MutationObserver>> m_activeObservers;
+    HashSet<RefPtr<MutationObserver>> m_suspendedObservers;
+
+    std::unique_ptr<CustomElementQueue> m_customElementQueue;
+    bool m_processingBackupElementQueue { false };
 };
 
 } // namespace WebCore
index b34bc06..46458ae 100644 (file)
@@ -179,7 +179,6 @@ std::unique_ptr<Page> createPageForSanitizingWebContent()
     auto pageConfiguration = pageConfigurationWithEmptyClients(PAL::SessionID::defaultSessionID());
     
     auto page = makeUnique<Page>(WTFMove(pageConfiguration));
-    page->setIsForSanitizingWebContent();
     page->settings().setMediaEnabled(false);
     page->settings().setScriptEnabled(false);
     page->settings().setPluginsEnabled(false);
index 6505af7..5f4ceb0 100644 (file)
@@ -659,9 +659,6 @@ public:
     void clearWheelEventTestMonitor() { m_wheelEventTestMonitor = nullptr; }
     bool isMonitoringWheelEvents() const { return !!m_wheelEventTestMonitor; }
 
-    void setIsForSanitizingWebContent() { m_isForSanitizingWebContent = true; }
-    bool isForSanitizingWebContent() const { return m_isForSanitizingWebContent; }
-
 #if ENABLE(VIDEO)
     bool allowsMediaDocumentInlinePlayback() const { return m_allowsMediaDocumentInlinePlayback; }
     WEBCORE_EXPORT void setAllowsMediaDocumentInlinePlayback(bool);
@@ -1011,8 +1008,6 @@ private:
     bool m_mediaPlaybackIsSuspended { false };
     bool m_mediaBufferingIsSuspended { false };
     bool m_inUpdateRendering { false };
-
-    bool m_isForSanitizingWebContent { false };
 };
 
 inline PageGroup& Page::group()