Skip to content

Commit c4e31e4

Browse files
duonglaiquangrbri
authored andcommitted
Window: implement queueMicrotask()
1 parent 52f9ba4 commit c4e31e4

3 files changed

Lines changed: 109 additions & 0 deletions

File tree

src/main/java/org/htmlunit/javascript/host/Window.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
* @author Colin Alworth
140140
* @author Atsushi Nakagawa
141141
* @author Sven Strickroth
142+
* @author Lai Quang Duong
142143
*
143144
* @see <a href="http://msdn.microsoft.com/en-us/library/ms535873.aspx">MSDN documentation</a>
144145
*/
@@ -492,6 +493,24 @@ public static Object setTimeout(final Context context, final Scriptable scope,
492493
return WindowOrWorkerGlobalScopeMixin.setTimeout(context, thisObj, args, function);
493494
}
494495

496+
/**
497+
* Queues a microtask to be executed.
498+
*
499+
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask">
500+
* MDN web docs</a>
501+
* @param context the JavaScript context
502+
* @param scope the scope
503+
* @param thisObj the scriptable
504+
* @param args the arguments passed into the method
505+
* @param function the function
506+
* @return undefined
507+
*/
508+
@JsxFunction
509+
public static Object queueMicrotask(final Context context, final Scriptable scope,
510+
final Scriptable thisObj, final Object[] args, final Function function) {
511+
return WindowOrWorkerGlobalScopeMixin.queueMicrotask(thisObj, args);
512+
}
513+
495514
/**
496515
* Sets a chunk of JavaScript to be invoked each time a specified number of milliseconds has elapsed.
497516
*

src/main/java/org/htmlunit/javascript/host/WindowOrWorkerGlobalScopeMixin.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.htmlunit.corejs.javascript.Function;
2525
import org.htmlunit.corejs.javascript.FunctionObject;
2626
import org.htmlunit.corejs.javascript.Scriptable;
27+
import org.htmlunit.corejs.javascript.ScriptableObject;
2728
import org.htmlunit.javascript.HtmlUnitScriptable;
2829
import org.htmlunit.javascript.JavaScriptEngine;
2930
import org.htmlunit.javascript.background.BackgroundJavaScriptFactory;
@@ -36,6 +37,7 @@
3637
*
3738
* @author Ronald Brill
3839
* @author Rural Hunter
40+
* @author Lai Quang Duong
3941
*/
4042
public final class WindowOrWorkerGlobalScopeMixin {
4143

@@ -151,6 +153,38 @@ public static Object setInterval(final Context context, final Scriptable thisObj
151153
return setTimeoutIntervalImpl((Window) thisObj, args[0], timeout, false, params);
152154
}
153155

156+
/**
157+
* Queues a microtask to be executed.
158+
*
159+
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask">
160+
* MDN web docs</a>
161+
* @param thisObj the scriptable
162+
* @param args the arguments passed into the method
163+
* @return undefined
164+
*/
165+
public static Object queueMicrotask(final Scriptable thisObj, final Object[] args) {
166+
if (args.length < 1) {
167+
throw JavaScriptEngine.typeError("At least 1 argument required");
168+
}
169+
if (!(args[0] instanceof Function)) {
170+
throw JavaScriptEngine.typeError("Argument 1 is not callable");
171+
}
172+
173+
final Function callback = (Function) args[0];
174+
final Scriptable scope = ScriptableObject.getTopLevelScope(thisObj);
175+
final Context cx = Context.getCurrentContext();
176+
cx.enqueueMicrotask(() -> {
177+
try {
178+
callback.call(cx, scope, thisObj, JavaScriptEngine.EMPTY_ARGS);
179+
}
180+
catch (final Exception e) {
181+
// uncaught exception in a microtask must not prevent remaining microtasks from running.
182+
}
183+
});
184+
185+
return JavaScriptEngine.UNDEFINED;
186+
}
187+
154188
private static int setTimeoutIntervalImpl(final Window window, final Object code,
155189
int timeout, final boolean isTimeout, final Object[] params) {
156190
if (timeout < MIN_TIMER_DELAY) {

src/test/java/org/htmlunit/javascript/host/Window2Test.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.OutputStreamWriter;
2020
import java.net.URL;
2121
import java.nio.charset.StandardCharsets;
22+
import java.time.Duration;
2223

2324
import org.apache.commons.io.FileUtils;
2425
import org.htmlunit.CookieManager4Test;
@@ -43,6 +44,7 @@
4344
* @author Carsten Steul
4445
* @author Colin Alworth
4546
* @author Christoph Burgmer
47+
* @author Lai Quang Duong
4648
*/
4749
public class Window2Test extends WebDriverTestCase {
4850

@@ -3496,4 +3498,58 @@ public void overwriteProperty_top() throws Exception {
34963498
final WebDriver driver = loadPage2(html);
34973499
verifySessionStorage2(driver, getExpectedAlerts());
34983500
}
3501+
3502+
/**
3503+
* @throws Exception if the test fails
3504+
*/
3505+
@Test
3506+
@Alerts("TypeError")
3507+
public void queueMicrotaskNoArgs() throws Exception {
3508+
final String html = DOCTYPE_HTML
3509+
+ "<html><head><script>\n"
3510+
+ LOG_TITLE_FUNCTION
3511+
+ " try { queueMicrotask(); } catch(e) { logEx(e); }\n"
3512+
+ "</script></head></html>";
3513+
3514+
loadPageVerifyTitle2(html);
3515+
}
3516+
3517+
/**
3518+
* @throws Exception if the test fails
3519+
*/
3520+
@Test
3521+
@Alerts("TypeError")
3522+
public void queueMicrotaskNonFunction() throws Exception {
3523+
final String html = DOCTYPE_HTML
3524+
+ "<html><head><script>\n"
3525+
+ LOG_TITLE_FUNCTION
3526+
+ " try { queueMicrotask('str'); } catch(e) { logEx(e); }\n"
3527+
+ "</script></head></html>";
3528+
3529+
loadPageVerifyTitle2(html);
3530+
}
3531+
3532+
/**
3533+
* @throws Exception if the test fails
3534+
*/
3535+
@Test
3536+
@Alerts("1-sync, 2-microtask, 3-microtask, 4-nested-microtask, 5-timeout")
3537+
public void queueMicrotaskExecutionOrder() throws Exception {
3538+
final String html = DOCTYPE_HTML
3539+
+ "<html><head><script>\n"
3540+
+ LOG_TITLE_FUNCTION
3541+
+ " var r = [];\n"
3542+
+ " queueMicrotask(function() { r.push('2-microtask'); });\n"
3543+
+ " setTimeout(function() { r.push('5-timeout'); }, 0);\n"
3544+
+ " queueMicrotask(function() {\n"
3545+
+ " r.push('3-microtask');\n"
3546+
+ " queueMicrotask(function() { r.push('4-nested-microtask'); });\n"
3547+
+ " });\n"
3548+
+ " r.push('1-sync');\n"
3549+
+ " setTimeout(function() { log(r.join(', ')); }, 200);\n"
3550+
+ "</script></head></html>";
3551+
3552+
final WebDriver driver = loadPage2(html);
3553+
verifyTitle2(Duration.ofSeconds(1), driver, getExpectedAlerts());
3554+
}
34993555
}

0 commit comments

Comments
 (0)