Code evaluated inside browser console has microtask level == 0 and results in unexpected evaluation order
Categories
(DevTools :: Console, defect)
Tracking
(firefox101 fixed)
Tracking | Status | |
---|---|---|
firefox101 | --- | fixed |
People
(Reporter: arai, Assigned: arai)
References
(Blocks 1 open bug)
Details
Attachments
(1 file)
(deleted),
text/x-phabricator-request
|
Details |
separated from bug 1760791.
Steps to reproduce:
- run Nightly 100.0a1 (2022-03-24) (64-bit) on macOS
- Evaluate the following code in browser console:
queueMicrotask(() => console.log(2));
document.createXULElement("browser");
console.log(1);
Expected result:
1
2
Microtask checkpoint is performed after evaluating the whole code.
Actual result:
2
1
Microtask checkpoint is performed inside createXULElement
.
This is because the code is evaluated with microtask level == 0, and CallSetup
inside createXULElement
enters/leaves microtask, that performs microtask checkpoint when leaving.
To make it work as expected (or, simply to avoid surprise), we should enter microtask before evaluating the browser console input.
Updated•3 years ago
|
Comment 1•3 years ago
|
||
Note this also happens in a privileged web console, like in about:config
.
Or when putting the code in a watch expression in the debugger.
Assignee | ||
Comment 2•3 years ago
|
||
Assignee | ||
Comment 3•3 years ago
|
||
The evaluation of console input is done by executeInGlobalWithBindings
:
function getEvalResult(
dbg,
string,
evalOptions,
bindings,
frame,
dbgGlobal,
noSideEffectDebugger
) {
...
result = dbgGlobal.executeInGlobalWithBindings(
string,
bindings,
evalOptions
);
and this is called from a runnable created by Services.tm.dispatchToMainThread
.
const WebConsoleActor = ActorClassWithSpec(webconsoleSpec, {
...
evaluateJSAsync: async function(request) {
...
DevToolsUtils.executeSoon(async () => {
try {
// Execute the script that may pause.
let response = await this.evaluateJS(request);
exports.executeSoon = function(fn) {
...
Services.tm.dispatchToMainThread({
run: exports.makeInfallible(executor),
and I don't see any suitable place to put nsAutoMicroTask
in between them.
Possible options are:
- (a) Add native helper function that takes function and calls it in microtask
and enclose some part of the code with it - (b) Add native helper function that enter/leave microtask,
and enclose some part of the code with it - (c) extend
Services.tm.dispatchToMainThread
to allocatensAutoMicroTask
before calling the runnable
The attached patch takes (b).
Assignee | ||
Comment 4•3 years ago
|
||
Given the finally
block isn't guaranteed to be executed on OOM, it would be better taking (a)
Assignee | ||
Comment 5•3 years ago
|
||
(c) doesn't fit here because the function is async function, and it's not guaranteed that the console input is evaluated before the first await
.
the microtask should be entered/left just before/after evaluating the console input.
Assignee | ||
Comment 6•3 years ago
|
||
Updated to use (a), and triggered try run
https://treeherder.mozilla.org/jobs?repo=try&revision=f907b397081fa76d18e4138d91b5b85841431010
Updated•3 years ago
|
Assignee | ||
Comment 7•3 years ago
|
||
(In reply to Tooru Fujisawa [:arai] from comment #5)
(c) doesn't fit here because the function is async function, and it's not guaranteed that the console input is evaluated before the first
await
.
the microtask should be entered/left just before/after evaluating the console input.
(For later reference) this wasn't true.
If there's await
before the console input, the remaining part of the function is evaluated inside PromiseJobRunnable
that has CallSetup
.
So this issue is specific to the current case that the console input is evaluated in the first runnable.
the patch is modified to wrap the runnable with nsAutoMicroTask
.
Comment 9•3 years ago
|
||
bugherder |
Description
•