Add self.queueMicrotask(f) on DOMWindow
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Aug 2018 03:55:47 +0000 (03:55 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Aug 2018 03:55:47 +0000 (03:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188212

Reviewed by Ryosuke Niwa.

Source/JavaScriptCore:

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* runtime/JSGlobalObject.cpp:
(JSC::enqueueJob):
* runtime/JSMicrotask.cpp: Renamed from Source/JavaScriptCore/runtime/JSJob.cpp.
(JSC::createJSMicrotask):
Export them to WebCore.

(JSC::JSMicrotask::run):
* runtime/JSMicrotask.h: Renamed from Source/JavaScriptCore/runtime/JSJob.h.
Add another version of JSMicrotask which does not have arguments.

Source/WebCore:

This patch adds self.queueMicrotask(f) in DOMWindow, which takes a function and enqueue it into microtask queue.
We do not add this to Worker's global scope since our worker does not support microtasks correctly.

Tests: js/dom/queue-microtask-window.html

* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::JSDOMWindow::queueMicrotask):
Post a microtask to JSC's microtask mechanism. This will eventually go to WebCore's MicrotaskQueue code.

* page/DOMWindow.idl:

LayoutTests:

* js/dom/queue-microtask-window-expected.txt: Added.
* js/dom/queue-microtask-window.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/queue-microtask-window-expected.txt [new file with mode: 0644]
LayoutTests/js/dom/queue-microtask-window.html [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSMicrotask.cpp [moved from Source/JavaScriptCore/runtime/JSJob.cpp with 72% similarity]
Source/JavaScriptCore/runtime/JSMicrotask.h [moved from Source/JavaScriptCore/runtime/JSJob.h with 90% similarity]
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMWindowCustom.cpp
Source/WebCore/page/DOMWindow.idl

index 70ba58a..1422f2f 100644 (file)
@@ -1,3 +1,13 @@
+2018-08-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Add self.queueMicrotask(f) on DOMWindow
+        https://bugs.webkit.org/show_bug.cgi?id=188212
+
+        Reviewed by Ryosuke Niwa.
+
+        * js/dom/queue-microtask-window-expected.txt: Added.
+        * js/dom/queue-microtask-window.html: Added.
+
 2018-08-01  Ryosuke Niwa  <rniwa@webkit.org>
 
         REGRESSION(r227983): fast/dom/adopt-node-crash-2.html is flaky
diff --git a/LayoutTests/js/dom/queue-microtask-window-expected.txt b/LayoutTests/js/dom/queue-microtask-window-expected.txt
new file mode 100644 (file)
index 0000000..cc06f58
--- /dev/null
@@ -0,0 +1,7 @@
+
+
+PASS Queued microtasks should be drained before executing macrotasks 
+PASS queueMicrotask's callback has zero arguments and self as |this| 
+PASS queueMicrotask and Promise uses the same Microtask queue 
+PASS queueMicrotask should reject non-function arguments 
+
diff --git a/LayoutTests/js/dom/queue-microtask-window.html b/LayoutTests/js/dom/queue-microtask-window.html
new file mode 100644 (file)
index 0000000..4b5e4f9
--- /dev/null
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>self.queueMicrotask works in Window</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<iframe></iframe>
+<script>
+promise_test(() => {
+    var counter = 0;
+    self.queueMicrotask(() => {
+        assert_equals(counter++, 1);
+        self.queueMicrotask(() => {
+            assert_equals(counter++, 2);
+        });
+    });
+    var promise = new Promise((resolve, reject) => {
+        setTimeout(() => {
+            assert_equals(counter++, 3);
+            resolve();
+        }, 0);
+    });
+    assert_equals(counter++, 0);
+    return promise;
+}, `Queued microtasks should be drained before executing macrotasks`);
+
+promise_test(() => {
+    return new Promise((resolve, reject) => {
+        self.queueMicrotask(function () {
+            try {
+                assert_equals(arguments.length, 0);
+                assert_equals(this, self);
+                assert_equals(frames.length, 1);
+                assert_not_equals(frames[0], self);
+                var iframe = frames[0];
+                iframe.queueMicrotask(function () {
+                    try {
+                        assert_equals(this, self);
+                        assert_not_equals(this, iframe);
+                        self.queueMicrotask(function () {
+                            'use strict';
+                            try {
+                                assert_equals(this, undefined);
+                                resolve();
+                            } catch (e) {
+                                reject(e);
+                            }
+                        });
+                    } catch (e) {
+                        reject(e);
+                    }
+                });
+            } catch (e) {
+                reject(e);
+            }
+        });
+    });
+}, `queueMicrotask's callback has zero arguments and self as |this|`);
+
+promise_test(() => {
+    return new Promise((resolve ,reject) => {
+        var counter = 0;
+        Promise.resolve().then(() => {
+            assert_equals(counter++, 1);
+            self.queueMicrotask(() => {
+                assert_equals(counter++, 3);
+                resolve();
+            });
+        });
+        self.queueMicrotask(() => {
+            assert_equals(counter++, 2);
+        });
+        assert_equals(counter++, 0);
+    });
+}, `queueMicrotask and Promise uses the same Microtask queue`);
+
+test(() => {
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask();
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask(null);
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask(undefined);
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask(42);
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask("42");
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask(true);
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask(Symbol("42"));
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask({});
+    });
+    assert_throws(new TypeError, () => {
+        self.queueMicrotask({ handleEvent() { } });
+    });
+}, `queueMicrotask should reject non-function arguments`);
+</script>
+</body>
+</html>
index 349f17d..f610d72 100644 (file)
@@ -794,6 +794,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/JSGlobalObjectInlines.h
     runtime/JSInternalPromise.h
     runtime/JSInternalPromiseDeferred.h
+    runtime/JSMicrotask.h
     runtime/JSLock.h
     runtime/JSMap.h
     runtime/JSMapIterator.h
index e430ac2..1bada63 100644 (file)
@@ -1,3 +1,23 @@
+2018-08-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Add self.queueMicrotask(f) on DOMWindow
+        https://bugs.webkit.org/show_bug.cgi?id=188212
+
+        Reviewed by Ryosuke Niwa.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::enqueueJob):
+        * runtime/JSMicrotask.cpp: Renamed from Source/JavaScriptCore/runtime/JSJob.cpp.
+        (JSC::createJSMicrotask):
+        Export them to WebCore.
+
+        (JSC::JSMicrotask::run):
+        * runtime/JSMicrotask.h: Renamed from Source/JavaScriptCore/runtime/JSJob.h.
+        Add another version of JSMicrotask which does not have arguments.
+
 2018-08-01  Tomas Popela  <tpopela@redhat.com>
 
         [WTF] Rename String::format to String::deprecatedFormat
index a8e0bc9..57ad436 100644 (file)
                6A38CFAA1E32B5AB0060206F /* AsyncStackTrace.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A38CFA81E32B58B0060206F /* AsyncStackTrace.h */; };
                6AD2CB4D19B9140100065719 /* DebuggerEvalEnabler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6AD2CB4C19B9140100065719 /* DebuggerEvalEnabler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                70113D4C1A8DB093003848C4 /* IteratorOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 70113D4A1A8DB093003848C4 /* IteratorOperations.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               7013CA8C1B491A9400CAE613 /* JSJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 7013CA8A1B491A9400CAE613 /* JSJob.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               7013CA8C1B491A9400CAE613 /* JSMicrotask.h in Headers */ = {isa = PBXBuildFile; fileRef = 7013CA8A1B491A9400CAE613 /* JSMicrotask.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41AC1A6E501E00716757 /* Symbol.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41A41A6E501E00716757 /* Symbol.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41AE1A6E501E00716757 /* SymbolConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41A61A6E501E00716757 /* SymbolConstructor.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41B01A6E501E00716757 /* SymbolObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41A81A6E501E00716757 /* SymbolObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                6BA93C9590484C5BAD9316EA /* JSScriptFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSScriptFetcher.h; sourceTree = "<group>"; };
                70113D491A8DB093003848C4 /* IteratorOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IteratorOperations.cpp; sourceTree = "<group>"; };
                70113D4A1A8DB093003848C4 /* IteratorOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorOperations.h; sourceTree = "<group>"; };
-               7013CA891B491A9400CAE613 /* JSJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSJob.cpp; sourceTree = "<group>"; };
-               7013CA8A1B491A9400CAE613 /* JSJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSJob.h; sourceTree = "<group>"; };
+               7013CA891B491A9400CAE613 /* JSMicrotask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMicrotask.cpp; sourceTree = "<group>"; };
+               7013CA8A1B491A9400CAE613 /* JSMicrotask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMicrotask.h; sourceTree = "<group>"; };
                7035587C1C418419004BD7BF /* MapPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = MapPrototype.js; sourceTree = "<group>"; };
                7035587D1C418419004BD7BF /* SetPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SetPrototype.js; sourceTree = "<group>"; };
                7035587E1C418458004BD7BF /* MapPrototype.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapPrototype.lut.h; sourceTree = "<group>"; };
                                E33F50831B8437A000413856 /* JSInternalPromiseDeferred.h */,
                                E33F50721B8421C000413856 /* JSInternalPromisePrototype.cpp */,
                                E33F50731B8421C000413856 /* JSInternalPromisePrototype.h */,
-                               7013CA891B491A9400CAE613 /* JSJob.cpp */,
-                               7013CA8A1B491A9400CAE613 /* JSJob.h */,
                                14DA818F0D99FD2000B0A4FB /* JSLexicalEnvironment.cpp */,
                                14DA818E0D99FD2000B0A4FB /* JSLexicalEnvironment.h */,
                                65EA4C99092AF9E20093D800 /* JSLock.cpp */,
                                A700874017CBE8EB00C3E643 /* JSMap.h */,
                                A74DEF8F182D991400522C22 /* JSMapIterator.cpp */,
                                A74DEF90182D991400522C22 /* JSMapIterator.h */,
+                               7013CA891B491A9400CAE613 /* JSMicrotask.cpp */,
+                               7013CA8A1B491A9400CAE613 /* JSMicrotask.h */,
                                E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */,
                                E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */,
                                1879510614C540FFB561C124 /* JSModuleLoader.cpp */,
                                E33F50851B8437A000413856 /* JSInternalPromiseDeferred.h in Headers */,
                                E33F50751B8421C000413856 /* JSInternalPromisePrototype.h in Headers */,
                                A503FA1E188E0FB000110F14 /* JSJavaScriptCallFramePrototype.h in Headers */,
-                               7013CA8C1B491A9400CAE613 /* JSJob.h in Headers */,
                                BC18C4160E16F5CD00B34460 /* JSLexicalEnvironment.h in Headers */,
                                BC18C4230E16F5CD00B34460 /* JSLock.h in Headers */,
                                C25D709C16DE99F400FCA6BC /* JSManagedValue.h in Headers */,
                                A700874217CBE8EB00C3E643 /* JSMap.h in Headers */,
                                A74DEF96182D991400522C22 /* JSMapIterator.h in Headers */,
                                0F0B286D1EB8E6D5000EB5D2 /* JSMarkingConstraintPrivate.h in Headers */,
+                               7013CA8C1B491A9400CAE613 /* JSMicrotask.h in Headers */,
                                9959E92D1BD17FA4001AA413 /* jsmin.py in Headers */,
                                E3D239C91B829C1C00BBEF67 /* JSModuleEnvironment.h in Headers */,
                                D9722752DC54459B9125B539 /* JSModuleLoader.h in Headers */,
index 5f6f344..05c72e5 100644 (file)
@@ -819,11 +819,11 @@ runtime/JSInternalPromise.cpp
 runtime/JSInternalPromiseConstructor.cpp
 runtime/JSInternalPromiseDeferred.cpp
 runtime/JSInternalPromisePrototype.cpp
-runtime/JSJob.cpp
 runtime/JSLexicalEnvironment.cpp
 runtime/JSLock.cpp
 runtime/JSMap.cpp
 runtime/JSMapIterator.cpp
+runtime/JSMicrotask.cpp
 runtime/JSModuleEnvironment.cpp
 runtime/JSModuleLoader.cpp
 runtime/JSModuleNamespaceObject.cpp
index 047cedf..d1ff936 100644 (file)
 #include "JSInternalPromise.h"
 #include "JSInternalPromiseConstructor.h"
 #include "JSInternalPromisePrototype.h"
-#include "JSJob.h"
 #include "JSLexicalEnvironment.h"
 #include "JSLock.h"
 #include "JSMap.h"
+#include "JSMicrotask.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleLoader.h"
 #include "JSModuleNamespaceObject.h"
@@ -336,7 +336,7 @@ static EncodedJSValue JSC_HOST_CALL enqueueJob(ExecState* exec)
     JSValue arguments = exec->argument(1);
     ASSERT(arguments.inherits<JSArray>(vm));
 
-    globalObject->queueMicrotask(createJSJob(vm, job, jsCast<JSArray*>(arguments)));
+    globalObject->queueMicrotask(createJSMicrotask(vm, job, jsCast<JSArray*>(arguments)));
 
     return JSValue::encode(jsUndefined());
 }
similarity index 72%
rename from Source/JavaScriptCore/runtime/JSJob.cpp
rename to Source/JavaScriptCore/runtime/JSMicrotask.cpp
index 7ac663a..b08ed30 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "config.h"
-#include "JSJob.h"
+#include "JSMicrotask.h"
 
 #include "CatchScope.h"
 #include "Error.h"
 
 namespace JSC {
 
-class JSJobMicrotask final : public Microtask {
+class JSMicrotask final : public Microtask {
 public:
-    JSJobMicrotask(VM& vm, JSValue job, JSArray* arguments)
+    JSMicrotask(VM& vm, JSValue job, JSArray* arguments)
     {
         m_job.set(vm, job);
         m_arguments.set(vm, arguments);
     }
 
-    virtual ~JSJobMicrotask()
+    JSMicrotask(VM& vm, JSValue job)
     {
+        m_job.set(vm, job);
     }
 
 private:
@@ -56,12 +57,17 @@ private:
     Strong<JSArray> m_arguments;
 };
 
-Ref<Microtask> createJSJob(VM& vm, JSValue job, JSArray* arguments)
+Ref<Microtask> createJSMicrotask(VM& vm, JSValue job)
+{
+    return adoptRef(*new JSMicrotask(vm, job));
+}
+
+Ref<Microtask> createJSMicrotask(VM& vm, JSValue job, JSArray* arguments)
 {
-    return adoptRef(*new JSJobMicrotask(vm, job, arguments));
+    return adoptRef(*new JSMicrotask(vm, job, arguments));
 }
 
-void JSJobMicrotask::run(ExecState* exec)
+void JSMicrotask::run(ExecState* exec)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_CATCH_SCOPE(vm);
@@ -71,13 +77,15 @@ void JSJobMicrotask::run(ExecState* exec)
     ASSERT(handlerCallType != CallType::None);
 
     MarkedArgumentBuffer handlerArguments;
-    for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) {
-        JSValue arg = m_arguments->JSArray::get(exec, index);
-        CLEAR_AND_RETURN_IF_EXCEPTION(scope, handlerArguments.overflowCheckNotNeeded());
-        handlerArguments.append(arg);
+    if (m_arguments) {
+        for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) {
+            JSValue arg = m_arguments->JSArray::get(exec, index);
+            CLEAR_AND_RETURN_IF_EXCEPTION(scope, handlerArguments.overflowCheckNotNeeded());
+            handlerArguments.append(arg);
+        }
+        if (UNLIKELY(handlerArguments.hasOverflowed()))
+            return;
     }
-    if (UNLIKELY(handlerArguments.hasOverflowed()))
-        return;
     profiledCall(exec, ProfilingReason::Microtask, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments);
     scope.clearException();
 }
similarity index 90%
rename from Source/JavaScriptCore/runtime/JSJob.h
rename to Source/JavaScriptCore/runtime/JSMicrotask.h
index 0a9e636..c3c6ebd 100644 (file)
@@ -33,6 +33,7 @@ namespace JSC {
 class Microtask;
 class JSArray;
 
-Ref<Microtask> createJSJob(VM&, JSValue job, JSArray* arguments);
+JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job);
+JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job, JSArray* arguments);
 
 } // namespace JSC
index 8a3fd40..f8cf4b1 100644 (file)
@@ -1,3 +1,21 @@
+2018-08-01  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Add self.queueMicrotask(f) on DOMWindow
+        https://bugs.webkit.org/show_bug.cgi?id=188212
+
+        Reviewed by Ryosuke Niwa.
+
+        This patch adds self.queueMicrotask(f) in DOMWindow, which takes a function and enqueue it into microtask queue.
+        We do not add this to Worker's global scope since our worker does not support microtasks correctly.
+
+        Tests: js/dom/queue-microtask-window.html
+
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::queueMicrotask):
+        Post a microtask to JSC's microtask mechanism. This will eventually go to WebCore's MicrotaskQueue code.
+
+        * page/DOMWindow.idl:
+
 2018-08-01  Tomas Popela  <tpopela@redhat.com>
 
         [WTF] Rename String::format to String::deprecatedFormat
index 1405015..a761db1 100644 (file)
@@ -46,6 +46,7 @@
 #include "Settings.h"
 #include "WebCoreJSClientData.h"
 #include <JavaScriptCore/JSCInlines.h>
+#include <JavaScriptCore/JSMicrotask.h>
 #include <JavaScriptCore/Lookup.h>
 
 #if ENABLE(USER_MESSAGE_HANDLERS)
@@ -521,6 +522,23 @@ JSValue JSDOMWindow::showModalDialog(ExecState& state)
     return handler.returnValue();
 }
 
+JSValue JSDOMWindow::queueMicrotask(ExecState& state)
+{
+    VM& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (UNLIKELY(state.argumentCount() < 1))
+        return throwException(&state, scope, createNotEnoughArgumentsError(&state));
+
+    JSValue functionValue = state.uncheckedArgument(0);
+    if (UNLIKELY(!functionValue.isFunction(vm)))
+        return JSValue::decode(throwArgumentMustBeFunctionError(state, scope, 0, "callback", "Window", "queueMicrotask"));
+
+    scope.release();
+    Base::queueMicrotask(JSC::createJSMicrotask(vm, functionValue));
+    return jsUndefined();
+}
+
 DOMWindow* JSDOMWindow::toWrapped(VM& vm, JSValue value)
 {
     if (!value.isObject())
index 14c392d..ff4d957 100644 (file)
@@ -89,6 +89,8 @@ typedef USVString CSSOMString;
     DOMString? prompt(optional DOMString message = "", optional DOMString defaultValue = "");
     void print();
 
+    [Custom] void queueMicrotask(VoidCallback callback);
+
     long requestAnimationFrame(RequestAnimationFrameCallback callback); // FIXME: Should return an unsigned long.
     void cancelAnimationFrame(long handle); // FIXME: handle should be an unsigned long.