Unique origin's window must get its own event loop
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2019 00:41:03 +0000 (00:41 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2019 00:41:03 +0000 (00:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204978

Reviewed by Antti Koivisto.

Source/WebCore:

This patch fixes the bug that unique origin documents and documents of the same registrable domains
with different schemes / protocols were sharing the same event loop. Note that we continue to share
the event loop across file URI documents.

We now use the agent cluster key to looking up the event loop to better match the HTML5 spec:
https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key

Tests: fast/eventloop/data-uri-document-has-its-own-event-loop.html
       fast/eventloop/queue-task-across-frames-in-file-uri.html
       http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html

* dom/Document.cpp:
(WebCore::Document::eventLoop):
* dom/EventLoop.h:
(WebCore::EventLoopTaskGroup::hasSameEventLoopAs): Added for testing purposes.
* dom/WindowEventLoop.cpp:
(WebCore::agentClusterKeyOrNullIfUnique): Added.
(WebCore::WindowEventLoop::eventLoopForSecurityOrigin): Replaced ensureForRegistrableDomain.
(WebCore::WindowEventLoop::create): Added.
(WebCore::WindowEventLoop::WindowEventLoop):
(WebCore::WindowEventLoop::~WindowEventLoop):
* dom/WindowEventLoop.h:
* testing/Internals.cpp:
(WebCore::Internals::hasSameEventLoopAs): Added for testing purposes.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Added tests to make sure data URI documents continue to use the same event loop but documents of
unique origin and of different schemes will use distinct event loops using newly added internals
method (hasSameEventLoopAs). Also added assertions to the existing tests using this new method.

* fast/eventloop: Added.
* fast/eventloop/data-uri-document-has-its-own-event-loop-expected.txt: Added.
* fast/eventloop/data-uri-document-has-its-own-event-loop.html: Added.
* fast/eventloop/queue-task-across-frames-in-file-uri-expected.txt: Added.
* fast/eventloop/queue-task-across-frames-in-file-uri.html: Added.
* fast/eventloop/resources: Added.
* fast/eventloop/resources/eventloop-helper.html: Added.
* http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop-expected.txt: Added.
* http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html: Added.
* http/tests/eventloop/queue-task-across-cross-site-frames-expected.txt:
* http/tests/eventloop/queue-task-across-cross-site-frames.html:
* http/tests/eventloop/queue-task-across-frames-expected.txt:
* http/tests/eventloop/queue-task-across-frames.html:

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop-expected.txt [new file with mode: 0644]
LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop.html [new file with mode: 0644]
LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri-expected.txt [new file with mode: 0644]
LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri.html [new file with mode: 0644]
LayoutTests/fast/eventloop/resources/eventloop-helper.html [new file with mode: 0644]
LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html [new file with mode: 0644]
LayoutTests/http/tests/eventloop/queue-task-across-cross-site-frames-expected.txt
LayoutTests/http/tests/eventloop/queue-task-across-cross-site-frames.html
LayoutTests/http/tests/eventloop/queue-task-across-frames-expected.txt
LayoutTests/http/tests/eventloop/queue-task-across-frames.html
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/EventLoop.h
Source/WebCore/dom/WindowEventLoop.cpp
Source/WebCore/dom/WindowEventLoop.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index bb39f68..f5266a9 100644 (file)
@@ -1,3 +1,28 @@
+2019-12-07  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Unique origin's window must get its own event loop
+        https://bugs.webkit.org/show_bug.cgi?id=204978
+
+        Reviewed by Antti Koivisto.
+
+        Added tests to make sure data URI documents continue to use the same event loop but documents of
+        unique origin and of different schemes will use distinct event loops using newly added internals
+        method (hasSameEventLoopAs). Also added assertions to the existing tests using this new method.
+
+        * fast/eventloop: Added.
+        * fast/eventloop/data-uri-document-has-its-own-event-loop-expected.txt: Added.
+        * fast/eventloop/data-uri-document-has-its-own-event-loop.html: Added.
+        * fast/eventloop/queue-task-across-frames-in-file-uri-expected.txt: Added.
+        * fast/eventloop/queue-task-across-frames-in-file-uri.html: Added.
+        * fast/eventloop/resources: Added.
+        * fast/eventloop/resources/eventloop-helper.html: Added.
+        * http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop-expected.txt: Added.
+        * http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html: Added.
+        * http/tests/eventloop/queue-task-across-cross-site-frames-expected.txt:
+        * http/tests/eventloop/queue-task-across-cross-site-frames.html:
+        * http/tests/eventloop/queue-task-across-frames-expected.txt:
+        * http/tests/eventloop/queue-task-across-frames.html:
+
 2019-12-07  Andres Gonzalez  <andresg_22@apple.com>
 
         Implementation of additional attribute caching in the IsolatedTree.
diff --git a/LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop-expected.txt b/LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop-expected.txt
new file mode 100644 (file)
index 0000000..23495ba
--- /dev/null
@@ -0,0 +1,11 @@
+This tests that each data URI document has its own unique event loop
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.hasSameEventLoopAs(childFrame.contentWindow) is false
+PASS nestedFrameWithDataURLHasSameEventLoopAsParent is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop.html b/LayoutTests/fast/eventloop/data-uri-document-has-its-own-event-loop.html
new file mode 100644 (file)
index 0000000..a339b72
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+
+description('This tests that each data URI document has its own unique event loop');
+
+if (!window.internals)
+    testFailed('This test relies on window.internals');
+else {
+    jsTestIsAsync = true;
+
+    childFrame = document.createElement('iframe');
+    document.body.appendChild(childFrame);
+
+    childFrame.src = `data:text/html,<!DOCTYPE html>
+    <body onload="parent.postMessage(internals.hasSameEventLoopAs(nestedFrame.contentWindow), '*')">
+    <iframe id="nestedFrame" src="data:text/html,<!DOCTYPE html>">`;
+
+    onmessage = (event) => {
+        window.nestedFrameWithDataURLHasSameEventLoopAsParent = event.data;
+        shouldBeFalse('internals.hasSameEventLoopAs(childFrame.contentWindow)');
+        shouldBeFalse('nestedFrameWithDataURLHasSameEventLoopAsParent');
+        finishJSTest();
+    }
+}
+
+successfullyParsed = true;
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri-expected.txt b/LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri-expected.txt
new file mode 100644 (file)
index 0000000..964da5c
--- /dev/null
@@ -0,0 +1,12 @@
+This tests the order by which tasks are scheduled across documents of file URI documents.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS logs.join(", ") is "1, 2, 3, 4, 5, 6"
+PASS internals.hasSameEventLoopAs(frame1.contentWindow) is true
+PASS internals.hasSameEventLoopAs(frame2.contentWindow) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri.html b/LayoutTests/fast/eventloop/queue-task-across-frames-in-file-uri.html
new file mode 100644 (file)
index 0000000..b1bc191
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+
+description('This tests the order by which tasks are scheduled across documents of file URI documents.');
+
+if (!window.internals)
+    testFailed('This test relies on window.internals');
+else {
+    jsTestIsAsync = true;
+    logs = [];
+
+    frame1 = document.createElement('iframe');
+    document.body.appendChild(frame1);
+
+    frame2 = document.createElement('iframe');
+    frame2.src = 'resources/eventloop-helper.html';
+    frame2.addEventListener('load', runTest);
+    document.body.appendChild(frame2);
+}
+
+function runTest() {
+    internals.queueTask("DOMManipulation", () => logs.push('1'));
+    frame1.contentWindow.internals.queueTask("DOMManipulation", () => logs.push('2'));
+    internals.queueTask("DOMManipulation", () => logs.push('3'));
+    frame2.contentWindow.internals.queueTask("DOMManipulation", () => logs.push('4'));
+    frame1.contentWindow.internals.queueTask("DOMManipulation", () => logs.push('5'));
+    internals.queueTask("DOMManipulation", () => logs.push('6'));
+
+    setTimeout(() => {
+        shouldBeEqualToString('logs.join(", ")', '1, 2, 3, 4, 5, 6');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame1.contentWindow)');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame2.contentWindow)');
+        finishJSTest();
+    }, 100);
+}
+
+successfullyParsed = true;
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/eventloop/resources/eventloop-helper.html b/LayoutTests/fast/eventloop/resources/eventloop-helper.html
new file mode 100644 (file)
index 0000000..56c6b46
--- /dev/null
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<!-- This document is empty for now -->
+</body>
+</html>
diff --git a/LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop-expected.txt b/LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop-expected.txt
new file mode 100644 (file)
index 0000000..a06cb86
--- /dev/null
@@ -0,0 +1,11 @@
+This tests the order by which tasks are scheduled across documents that are not similar origins.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.hasSameEventLoopAs(frame1.contentWindow) is true
+PASS internals.hasSameEventLoopAs(frame2.contentWindow) is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html b/LayoutTests/http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html
new file mode 100644 (file)
index 0000000..2e88a8f
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script>
+
+description('This tests the order by which tasks are scheduled across documents that are not similar origins.');
+
+if (!window.internals)
+    testFailed('This test relies on window.internals');
+else {
+    jsTestIsAsync = true;
+    window.onload = startTest;
+}
+
+logs = [];
+crossOriginLogs = [];
+
+async function startTest()
+{
+    window.frame1 = document.createElement('iframe');
+    frame1.src = 'http://127.0.0.1:8000/eventloop/resources/eventloop-helper.html';
+    document.body.appendChild(frame1);
+
+    window.frame2 = document.createElement('iframe');
+    frame2.src = 'https://127.0.0.1:8443/eventloop/resources/eventloop-helper.html';
+    document.body.appendChild(frame2);
+
+    await waitForLoad(frame1);
+    await waitForLoad(frame2);
+
+    shouldBeTrue('internals.hasSameEventLoopAs(frame1.contentWindow)');
+    shouldBeFalse('internals.hasSameEventLoopAs(frame2.contentWindow)');
+    finishJSTest();
+}
+
+const loadedFrames = new Map;
+onmessage = (event) => {
+    if (event.data.type == 'load') {
+        const resolve = loadedFrames.get(event.source);
+        if (resolve)
+            resolve();
+        else
+            loadedFrames.set(event.source, null);
+    }
+}
+
+function waitForLoad(frame)
+{
+    if (loadedFrames.has(frame.contentWindow))
+        return Promise.resolve();
+    return new Promise((resolve) => loadedFrames.set(frame.contentWindow, resolve));
+}
+
+successfullyParsed = true;
+
+</script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
index 6ccec2f..31f79f4 100644 (file)
@@ -5,6 +5,10 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS logs.join(", ") is "1, 2, 3, 4, 5"
 PASS crossOriginLogs.join(", ") is "10, 11, 12"
+PASS internals.hasSameEventLoopAs(frame1.contentWindow) is true
+PASS internals.hasSameEventLoopAs(frame2.contentWindow) is false
+PASS internals.hasSameEventLoopAs(frame3.contentWindow) is true
+PASS internals.hasSameEventLoopAs(frame4.contentWindow) is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 77c56c2..c0ca072 100644 (file)
@@ -18,19 +18,19 @@ crossOriginLogs = [];
 
 async function startTest()
 {
-    const frame1 = document.createElement('iframe');
+    window.frame1 = document.createElement('iframe');
     document.body.appendChild(frame1);
 
-    const frame2 = document.createElement('iframe');
+    window.frame2 = document.createElement('iframe');
     frame2.name = 'frame2';
     frame2.src = 'http://localhost:8000/eventloop/resources/eventloop-helper.html';
     document.body.appendChild(frame2);
 
-    const frame3 = document.createElement('iframe');
+    window.frame3 = document.createElement('iframe');
     frame3.src = 'resources/eventloop-helper.html';
     document.body.appendChild(frame3);
 
-    const frame4 = document.createElement('iframe');
+    window.frame4 = document.createElement('iframe');
     frame4.name = 'frame4';
     frame4.src = 'http://localhost:8000/eventloop/resources/eventloop-helper.html';
 
@@ -57,6 +57,10 @@ async function startTest()
     setTimeout(() => {
         shouldBeEqualToString('logs.join(", ")', '1, 2, 3, 4, 5');
         shouldBeEqualToString('crossOriginLogs.join(", ")', '10, 11, 12');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame1.contentWindow)');
+        shouldBeFalse('internals.hasSameEventLoopAs(frame2.contentWindow)');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame3.contentWindow)');
+        shouldBeFalse('internals.hasSameEventLoopAs(frame4.contentWindow)');
         finishJSTest();
     }, 100);
 }
index 0c0465d..ef3d404 100644 (file)
@@ -4,6 +4,8 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS logs.join(", ") is "1, 2, 3, 4, 5, 6"
+PASS internals.hasSameEventLoopAs(frame1.contentWindow) is true
+PASS internals.hasSameEventLoopAs(frame2.contentWindow) is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 9485ca1..aef5c54 100644 (file)
@@ -31,6 +31,8 @@ function runTest() {
 
     setTimeout(() => {
         shouldBeEqualToString('logs.join(", ")', '1, 2, 3, 4, 5, 6');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame1.contentWindow)');
+        shouldBeTrue('internals.hasSameEventLoopAs(frame2.contentWindow)');
         finishJSTest();
     }, 100);
 }
index 1837ef4..48e2831 100644 (file)
@@ -1,3 +1,37 @@
+2019-12-07  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Unique origin's window must get its own event loop
+        https://bugs.webkit.org/show_bug.cgi?id=204978
+
+        Reviewed by Antti Koivisto.
+
+        This patch fixes the bug that unique origin documents and documents of the same registrable domains
+        with different schemes / protocols were sharing the same event loop. Note that we continue to share
+        the event loop across file URI documents.
+
+        We now use the agent cluster key to looking up the event loop to better match the HTML5 spec:
+        https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key
+
+        Tests: fast/eventloop/data-uri-document-has-its-own-event-loop.html
+               fast/eventloop/queue-task-across-frames-in-file-uri.html
+               http/tests/eventloop/documents-with-different-protocols-do-not-share-event-loop.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::eventLoop):
+        * dom/EventLoop.h:
+        (WebCore::EventLoopTaskGroup::hasSameEventLoopAs): Added for testing purposes.
+        * dom/WindowEventLoop.cpp:
+        (WebCore::agentClusterKeyOrNullIfUnique): Added.
+        (WebCore::WindowEventLoop::eventLoopForSecurityOrigin): Replaced ensureForRegistrableDomain.
+        (WebCore::WindowEventLoop::create): Added.
+        (WebCore::WindowEventLoop::WindowEventLoop):
+        (WebCore::WindowEventLoop::~WindowEventLoop):
+        * dom/WindowEventLoop.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::hasSameEventLoopAs): Added for testing purposes.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2019-12-07  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Display::LineBox should adopt to InlineLayoutUnit
index af688c7..b23d408 100644 (file)
@@ -6263,7 +6263,8 @@ EventLoopTaskGroup& Document::eventLoop()
 {
     ASSERT(isMainThread());
     if (UNLIKELY(!m_documentTaskGroup)) {
-        m_eventLoop = WindowEventLoop::ensureForRegistrableDomain(RegistrableDomain { securityOrigin().data() });
+        // https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key
+        m_eventLoop = WindowEventLoop::eventLoopForSecurityOrigin(securityOrigin());
         m_documentTaskGroup = makeUnique<EventLoopTaskGroup>(*m_eventLoop);
         if (activeDOMObjectsAreStopped())
             m_documentTaskGroup->stopAndDiscardAllTasks();
index f69590a..fb2c349 100644 (file)
@@ -105,6 +105,12 @@ public:
     {
     }
 
+    bool hasSameEventLoopAs(EventLoopTaskGroup& otherGroup)
+    {
+        ASSERT(m_eventLoop);
+        return m_eventLoop == otherGroup.m_eventLoop;
+    }
+
     bool matchesTask(EventLoopTask& task) const
     {
         auto* group = task.group();
index b8ad194..dcccf96 100644 (file)
 
 namespace WebCore {
 
-static HashMap<RegistrableDomain, WindowEventLoop*>& windowEventLoopMap()
+static HashMap<String, WindowEventLoop*>& windowEventLoopMap()
 {
     RELEASE_ASSERT(isMainThread());
-    static NeverDestroyed<HashMap<RegistrableDomain, WindowEventLoop*>> map;
+    static NeverDestroyed<HashMap<String, WindowEventLoop*>> map;
     return map.get();
 }
 
-Ref<WindowEventLoop> WindowEventLoop::ensureForRegistrableDomain(const RegistrableDomain& domain)
+static String agentClusterKeyOrNullIfUnique(const SecurityOrigin& origin)
 {
-    auto addResult = windowEventLoopMap().add(domain, nullptr);
+    auto computeKey = [&] {
+        // https://html.spec.whatwg.org/multipage/webappapis.html#obtain-agent-cluster-key
+        if (origin.isUnique())
+            return origin.toString();
+        RegistrableDomain registrableDomain { origin.data() };
+        if (registrableDomain.isEmpty())
+            return origin.toString();
+        return makeString(origin.protocol(), "://", registrableDomain.string());
+    };
+    auto key = computeKey();
+    if (key.isEmpty() || key == "null"_s)
+        return { };
+    return key;
+}
+
+Ref<WindowEventLoop> WindowEventLoop::eventLoopForSecurityOrigin(const SecurityOrigin& origin)
+{
+    auto key = agentClusterKeyOrNullIfUnique(origin);
+    if (key.isNull())
+        return create({ });
+
+    auto addResult = windowEventLoopMap().add(key, nullptr);
     if (UNLIKELY(addResult.isNewEntry)) {
-        auto newEventLoop = adoptRef(*new WindowEventLoop(domain));
+        auto newEventLoop = create(key);
         addResult.iterator->value = newEventLoop.ptr();
         return newEventLoop;
     }
     return *addResult.iterator->value;
 }
 
-inline WindowEventLoop::WindowEventLoop(const RegistrableDomain& domain)
-    : m_domain(domain)
+inline Ref<WindowEventLoop> WindowEventLoop::create(const String& agentClusterKey)
+{
+    return adoptRef(*new WindowEventLoop(agentClusterKey));
+}
+
+inline WindowEventLoop::WindowEventLoop(const String& agentClusterKey)
+    : m_agentClusterKey(agentClusterKey)
     , m_timer(*this, &WindowEventLoop::run)
 {
 }
 
 WindowEventLoop::~WindowEventLoop()
 {
-    auto didRemove = windowEventLoopMap().remove(m_domain);
+    if (m_agentClusterKey.isNull())
+        return;
+    auto didRemove = windowEventLoopMap().remove(m_agentClusterKey);
     RELEASE_ASSERT(didRemove);
 }
 
index 1196ad6..7a079f6 100644 (file)
@@ -27,9 +27,9 @@
 
 #include "DocumentIdentifier.h"
 #include "EventLoop.h"
-#include "RegistrableDomain.h"
 #include "Timer.h"
 #include <wtf/HashSet.h>
+#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
@@ -38,18 +38,19 @@ class Document;
 // https://html.spec.whatwg.org/multipage/webappapis.html#window-event-loop
 class WindowEventLoop final : public EventLoop {
 public:
-    static Ref<WindowEventLoop> ensureForRegistrableDomain(const RegistrableDomain&);
+    static Ref<WindowEventLoop> eventLoopForSecurityOrigin(const SecurityOrigin&);
 
     virtual ~WindowEventLoop();
 
 private:
-    WindowEventLoop(const RegistrableDomain&);
+    static Ref<WindowEventLoop> create(const String&);
+    WindowEventLoop(const String&);
 
     void scheduleToRun() final;
     bool isContextThread() const final;
     MicrotaskQueue& microtaskQueue() final;
 
-    RegistrableDomain m_domain;
+    String m_agentClusterKey;
     Timer m_timer;
 };
 
index 0d0aa07..6f50809 100644 (file)
@@ -4690,6 +4690,22 @@ ExceptionOr<void> Internals::queueTaskToQueueMicrotask(Document& document, const
     return { };
 }
 
+ExceptionOr<bool> Internals::hasSameEventLoopAs(WindowProxy& proxy)
+{
+    RefPtr<ScriptExecutionContext> context = contextDocument();
+    if (!context || !proxy.frame())
+        return Exception { InvalidStateError };
+
+    auto& proxyFrame = *proxy.frame();
+    if (!is<Frame>(proxyFrame))
+        return false;
+    RefPtr<ScriptExecutionContext> proxyContext = downcast<Frame>(proxyFrame).document();
+    if (!proxyContext)
+        return Exception { InvalidStateError };
+
+    return context->eventLoop().hasSameEventLoopAs(proxyContext->eventLoop());
+}
+
 Vector<String> Internals::accessKeyModifiers() const
 {
     Vector<String> accessKeyModifierStrings;
index 31e98f7..6583c3a 100644 (file)
@@ -780,6 +780,8 @@ public:
     void postTask(RefPtr<VoidCallback>&&);
     ExceptionOr<void> queueTask(ScriptExecutionContext&, const String& source, RefPtr<VoidCallback>&&);
     ExceptionOr<void> queueTaskToQueueMicrotask(Document&, const String& source, RefPtr<VoidCallback>&&);
+    ExceptionOr<bool> hasSameEventLoopAs(WindowProxy&);
+
     void markContextAsInsecure();
 
     bool usingAppleInternalSDK() const;
index ee000cc..332e5a6 100644 (file)
@@ -767,6 +767,8 @@ enum CompositingPolicy {
     void postTask(VoidCallback callback);
     [CallWith=ScriptExecutionContext, MayThrowException] void queueTask(DOMString source, VoidCallback callback);
     [CallWith=Document] void queueTaskToQueueMicrotask(DOMString source, VoidCallback callback);
+    [MayThrowException] boolean hasSameEventLoopAs(WindowProxy windowProxy);
+
     void markContextAsInsecure();
 
     void setMaxCanvasPixelMemory(unsigned long size);