script: Queue a microtask in wait_for_all of promise type (#39108)

In the wait-for-all algorithm of the IDL promise type, we need to queue
a microtask to perform successSteps given « » if total is 0.

This step was previously implemented in a workaround, which perform
successSteps immediately.

This patch properly queue the microtask, and remove the workaround.

Testing: Refactoring only. Existing tests are enough.
Fixes: #37259

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
This commit is contained in:
Kingsley Yung 2025-09-04 21:50:41 +08:00 committed by GitHub
parent ae2d9674e8
commit 925b7c5dad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 27 deletions

View file

@ -35,10 +35,11 @@ use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorToJsval};
use crate::dom::bindings::reflector::{DomGlobal, DomObject, MutDomObject, Reflector};
use crate::dom::bindings::root::AsHandleValue;
use crate::dom::bindings::root::{AsHandleValue, DomRoot};
use crate::dom::bindings::settings_stack::AutoEntryScript;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
@ -490,6 +491,27 @@ impl Callback for WaitForAllRejectionHandler {
}
}
/// The microtask for performing successSteps given « » in
/// <https://webidl.spec.whatwg.org/#wait-for-all>.
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct WaitForAllSuccessStepsMicrotask {
global: DomRoot<GlobalScope>,
#[ignore_malloc_size_of = "Closure is hard"]
#[no_trace]
success_steps: WaitForAllSuccessSteps,
}
impl MicrotaskRunnable for WaitForAllSuccessStepsMicrotask {
fn handler(&self, _can_gc: CanGc) {
(self.success_steps)(vec![]);
}
fn enter_realm(&self) -> JSAutoRealm {
enter_realm(&*self.global)
}
}
/// <https://webidl.spec.whatwg.org/#wait-for-all>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn wait_for_all(
@ -521,8 +543,19 @@ pub(crate) fn wait_for_all(
// Note: done using the len of result.
// If total is 0, then:
// Queue a microtask to perform successSteps given « ».
// TODO: #37259
if promises.is_empty() {
// Queue a microtask to perform successSteps given « ».
global.microtask_queue().enqueue(
Microtask::WaitForAllSuccessSteps(WaitForAllSuccessStepsMicrotask {
global: DomRoot::from_ref(global),
success_steps,
}),
cx,
);
// Return.
return;
}
// Let index be 0.
// Note: done with `enumerate` below.
@ -594,26 +627,16 @@ pub(crate) fn wait_for_all_promise(
failure_promise.reject_native(&reason, can_gc);
});
if promises.is_empty() {
// Note: part of `wait_for_all`.
// Done here by using `resolve_native`.
// TODO: #37259
// If total is 0, then:
// Queue a microtask to perform successSteps given « ».
let empty_list: Vec<HandleValue> = vec![];
promise.resolve_native(&empty_list, can_gc);
} else {
// Wait for all with promises, given successSteps and failureSteps.
wait_for_all(
cx,
global,
promises,
success_steps,
failure_steps,
realm,
can_gc,
);
}
// Wait for all with promises, given successSteps and failureSteps.
wait_for_all(
cx,
global,
promises,
success_steps,
failure_steps,
realm,
can_gc,
);
// Return promise.
promise

View file

@ -23,6 +23,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::html::htmlimageelement::ImageElementMicrotask;
use crate::dom::html::htmlmediaelement::MediaElementMicrotask;
use crate::dom::mutationobserver::MutationObserver;
use crate::dom::promise::WaitForAllSuccessStepsMicrotask;
use crate::realms::enter_realm;
use crate::script_runtime::{CanGc, JSContext, notify_about_rejected_promises};
use crate::script_thread::ScriptThread;
@ -43,6 +44,7 @@ pub(crate) enum Microtask {
MediaElement(MediaElementMicrotask),
ImageElement(ImageElementMicrotask),
ReadableStreamTeeReadRequest(DefaultTeeReadRequestMicrotask),
WaitForAllSuccessSteps(WaitForAllSuccessStepsMicrotask),
CustomElementReaction,
NotifyMutationObservers,
}
@ -141,16 +143,20 @@ impl MicrotaskQueue {
let _realm = task.enter_realm();
task.handler(can_gc);
},
Microtask::ReadableStreamTeeReadRequest(ref task) => {
let _realm = task.enter_realm();
task.handler(can_gc);
},
Microtask::WaitForAllSuccessSteps(ref task) => {
let _realm = task.enter_realm();
task.handler(can_gc);
},
Microtask::CustomElementReaction => {
ScriptThread::invoke_backup_element_queue(can_gc);
},
Microtask::NotifyMutationObservers => {
MutationObserver::notify_mutation_observers(can_gc);
},
Microtask::ReadableStreamTeeReadRequest(ref task) => {
let _realm = task.enter_realm();
task.handler(can_gc);
},
}
}
}