Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 34 additions & 35 deletions components/script/dom/promise/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,17 @@ use dom_struct::dom_struct;
use js::context::JSContext;
use js::conversions::{ConversionResult, FromJSValConvertibleRc};
use js::jsapi::{
AddRawValueRoot, CallArgs, GetFunctionNativeReserved, Heap, JS_GetFunctionObject,
JS_NewFunction, JSContext as RawJSContext, JSObject, PromiseState,
PromiseUserInputEventHandlingState, RemoveRawValueRoot, SetFunctionNativeReserved,
CallArgs, GetFunctionNativeReserved, Heap, JS_GetFunctionObject, JSContext as RawJSContext,
JSObject, PromiseState, PromiseUserInputEventHandlingState, RemoveRawValueRoot,
SetFunctionNativeReserved,
};
use js::jsval::{Int32Value, JSVal, NullValue, ObjectValue, UndefinedValue};
use js::realm::{AutoRealm, CurrentRealm};
use js::rust::wrappers::{
GetPromiseState, IsPromiseObject, NewPromiseObject, SetPromiseUserInputEventHandlingState,
};
use js::rust::wrappers2::{
AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve,
JS_ClearPendingException, NewFunctionWithReserved, RejectPromise, ResolvePromise,
SetAnyPromiseIsHandled,
AddPromiseReactions, AddRawValueRoot, CallOriginalPromiseReject, CallOriginalPromiseResolve,
GetPromiseState, IsPromiseObject, JS_ClearPendingException, JS_NewFunction,
NewFunctionWithReserved, NewPromiseObject, RejectPromise, ResolvePromise,
SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
use script_bindings::conversions::SafeToJSValConvertible;
Expand All @@ -48,7 +46,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::microtask::{Microtask, MicrotaskRunnable};
use crate::realms::{InRealm, enter_auto_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::script_runtime::CanGc;
use crate::script_thread::ScriptThread;

#[dom_struct]
Expand All @@ -65,17 +63,17 @@ pub(crate) struct Promise {

/// Private helper to enable adding new methods to `Rc<Promise>`.
trait PromiseHelper {
fn initialize(&self, cx: SafeJSContext);
fn initialize(&self, cx: &mut JSContext);
}

impl PromiseHelper for Rc<Promise> {
#[expect(unsafe_code)]
fn initialize(&self, cx: SafeJSContext) {
fn initialize(&self, cx: &mut JSContext) {
let obj = self.reflector().get_jsobject();
self.permanent_js_root.set(ObjectValue(*obj));
unsafe {
assert!(AddRawValueRoot(
*cx,
cx,
self.permanent_js_root.get_unsafe(),
c"Promise::root".as_ptr(),
));
Expand All @@ -100,7 +98,7 @@ impl Drop for Promise {
}

impl Promise {
pub(crate) fn new(cx: &mut js::context::JSContext, global: &GlobalScope) -> Rc<Promise> {
pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> Rc<Promise> {
let mut realm = enter_auto_realm(cx, global);
let cx = &mut realm.current_realm();
Promise::new_in_realm(cx)
Expand All @@ -109,47 +107,48 @@ impl Promise {
pub(crate) fn new_in_realm(current_realm: &mut CurrentRealm) -> Rc<Promise> {
let cx = current_realm.deref_mut();
rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
Promise::create_js_promise(cx.into(), obj.handle_mut(), CanGc::from_cx(cx));
Promise::new_with_js_promise(obj.handle(), cx.into())
Promise::create_js_promise(cx, obj.handle_mut());
Promise::new_with_js_promise(cx, obj.handle())
}

pub(crate) fn duplicate(&self) -> Rc<Promise> {
let cx = GlobalScope::get_cx();
Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
pub(crate) fn duplicate(&self, cx: &mut JSContext) -> Rc<Promise> {
Promise::new_with_js_promise(cx, self.reflector().get_jsobject())
}

#[expect(unsafe_code)]
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
pub(crate) fn new_with_js_promise(obj: HandleObject, cx: SafeJSContext) -> Rc<Promise> {
pub(crate) fn new_with_js_promise(cx: &mut JSContext, obj: HandleObject) -> Rc<Promise> {
unsafe {
assert!(IsPromiseObject(obj));
let promise = Promise {
reflector: Reflector::new(),
permanent_js_root: Heap::default(),
};
let promise = Rc::new(promise);
}
let promise = Promise {
reflector: Reflector::new(),
permanent_js_root: Heap::default(),
};
let promise = Rc::new(promise);
unsafe {
promise.init_reflector_without_associated_memory(obj.get());
promise.initialize(cx);
promise
}
promise.initialize(cx);
promise
}

#[expect(unsafe_code)]
// The apparently-unused CanGc parameter reflects the fact that the JS API calls
// like JS_NewFunction can trigger a GC.
fn create_js_promise(cx: SafeJSContext, mut obj: MutableHandleObject, _can_gc: CanGc) {
fn create_js_promise(cx: &mut JSContext, mut obj: MutableHandleObject) {
unsafe {
let do_nothing_func = JS_NewFunction(
*cx,
cx,
Some(do_nothing_promise_executor),
/* nargs = */ 2,
/* flags = */ 0,
ptr::null(),
);
assert!(!do_nothing_func.is_null());
rooted!(in(*cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
rooted!(&in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
assert!(!do_nothing_obj.is_null());
obj.set(NewPromiseObject(*cx, do_nothing_obj.handle()));
obj.set(NewPromiseObject(cx, do_nothing_obj.handle()));
assert!(!obj.is_null());
let is_user_interacting = if ScriptThread::is_user_interacting() {
PromiseUserInputEventHandlingState::HadUserInteractionAtCreation
Expand All @@ -162,7 +161,7 @@ impl Promise {

#[expect(unsafe_code)]
pub(crate) fn new_resolved(
cx: &mut js::context::JSContext,
cx: &mut JSContext,
global: &GlobalScope,
value: impl SafeToJSValConvertible,
) -> Rc<Promise> {
Expand All @@ -172,12 +171,12 @@ impl Promise {
value.safe_to_jsval(cx.into(), rval.handle_mut(), CanGc::from_cx(cx));
rooted!(&in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, rval.handle()) });
assert!(!p.handle().is_null());
Promise::new_with_js_promise(p.handle(), cx.into())
Promise::new_with_js_promise(cx, p.handle())
}

#[expect(unsafe_code)]
pub(crate) fn new_rejected(
cx: &mut js::context::JSContext,
cx: &mut JSContext,
global: &GlobalScope,
value: impl SafeToJSValConvertible,
) -> Rc<Promise> {
Expand All @@ -187,7 +186,7 @@ impl Promise {
value.safe_to_jsval(cx.into(), rval.handle_mut(), CanGc::from_cx(cx));
rooted!(&in(cx) let p = unsafe { CallOriginalPromiseReject(cx, rval.handle()) });
assert!(!p.handle().is_null());
Promise::new_with_js_promise(p.handle(), cx.into())
Promise::new_with_js_promise(cx, p.handle())
}

pub(crate) fn resolve_native<T>(&self, cx: &mut JSContext, val: &T)
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/stream/transformstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ impl TransformStreamMethods<crate::DomTypeHolder> for TransformStream {
}
};
let promise = if is_promise {
Promise::new_with_js_promise(result_object.handle(), cx.into())
Promise::new_with_js_promise(cx, result_object.handle())
} else {
Promise::new_resolved(cx, global, result.get())
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl UnderlyingSourceContainer {
}
};
let promise = if is_promise {
Promise::new_with_js_promise(result_object.handle(), cx.into())
Promise::new_with_js_promise(cx, result_object.handle())
} else {
Promise::new_resolved(cx, &self.global(), result.get())
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ impl WritableStreamDefaultController {
}
};
if is_promise {
Promise::new_with_js_promise(result_object.handle(), cx.into())
Promise::new_with_js_promise(cx, result_object.handle())
} else {
Promise::new_resolved(cx, global, result.get())
}
Expand Down
4 changes: 2 additions & 2 deletions components/script/dom/testing/testbinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,8 @@ impl TestBindingMethods<crate::DomTypeHolder> for TestBinding {
p.reject_error(cx, Error::Type(cformat!("{}", s.0)));
}

fn ResolvePromiseDelayed(&self, p: &Promise, value: DOMString, delay: u64) {
let promise = p.duplicate();
fn ResolvePromiseDelayed(&self, cx: &mut JSContext, p: &Promise, value: DOMString, delay: u64) {
let promise = p.duplicate(cx);
let cb = TestBindingCallback {
promise: TrustedPromise::new(promise),
value,
Expand Down
2 changes: 1 addition & 1 deletion components/script/microtask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl MicrotaskQueue {
// event loop is this event loop, notify about rejected promises given
// settingsObject's global object.
for global in globalscopes.clone().into_iter() {
notify_about_rejected_promises(&global);
notify_about_rejected_promises(cx, &global);
}

// https://html.spec.whatwg.org/multipage/#perform-a-microtask-checkpoint
Expand Down
2 changes: 1 addition & 1 deletion components/script/module_loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ fn continue_dynamic_import(
}

rooted!(&in(cx) let evaluate_promise = rval.to_object());
let evaluate_promise = Promise::new_with_js_promise(evaluate_promise.handle(), cx.into());
let evaluate_promise = Promise::new_with_js_promise(cx, evaluate_promise.handle());

// d. Let fulfilledClosure be a new Abstract Closure with no parameters that captures
// module and promiseCapability and performs the following steps when called:
Expand Down
2 changes: 1 addition & 1 deletion components/script/script_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ pub(crate) unsafe extern "C" fn host_import_module_dynamically(
// SAFETY: it is safe to construct a JSContext from engine hook.
let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
let cx = &mut cx;
let promise = Promise::new_with_js_promise(unsafe { Handle::from_raw(promise) }, cx.into());
let promise = Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise) });

let jsstr = unsafe { GetModuleRequestSpecifier(cx, Handle::from_raw(specifier)) };
let module_type = unsafe { GetModuleRequestType(cx, Handle::from_raw(specifier)) };
Expand Down
11 changes: 6 additions & 5 deletions components/script/script_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ unsafe extern "C" fn promise_rejection_tracker(

let target = Trusted::new(global.upcast::<EventTarget>());
let promise =
Promise::new_with_js_promise(unsafe { Handle::from_raw(promise) }, cx.into());
Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise) });
let trusted_promise = TrustedPromise::new(promise);

// Step 5-4.
Expand Down Expand Up @@ -627,17 +627,18 @@ unsafe extern "C" fn content_security_policy_allows(

#[expect(unsafe_code)]
/// <https://html.spec.whatwg.org/multipage/#notify-about-rejected-promises>
pub(crate) fn notify_about_rejected_promises(global: &GlobalScope) {
let cx = GlobalScope::get_cx();

pub(crate) fn notify_about_rejected_promises(
cx: &mut js::context::JSContext,
global: &GlobalScope,
) {
// Step 1. Let list be a clone of global's about-to-be-notified rejected promises list.
let uncaught_rejections: Vec<TrustedPromise> = global
.get_uncaught_rejections()
.borrow_mut()
.drain(..)
.map(|promise| {
let promise =
Promise::new_with_js_promise(unsafe { Handle::from_raw(promise.handle()) }, cx);
Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise.handle()) });

TrustedPromise::new(promise)
})
Expand Down
3 changes: 2 additions & 1 deletion components/script_bindings/codegen/Bindings.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1302,10 +1302,11 @@ DOMInterfaces = {
'InterfaceAttribute',
'PromiseRejectNative',
'PromiseRejectWithTypeError',
'PromiseResolveNative',
'ReceiveInterface',
'ReceiveInterfaceSequence',
'ReceiveNullableInterface',
'PromiseResolveNative',
'ResolvePromiseDelayed',
'ReturnRejectedPromise',
'ReturnResolvedPromise',
],
Expand Down