script: pass &mut JSContext inside worklet code (#44441)

Testing: It compiles 
Part of #40600

Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
This commit is contained in:
Gae24
2026-04-23 07:58:09 +02:00
committed by GitHub
parent 353e5248f1
commit 20e94989eb
7 changed files with 128 additions and 95 deletions

View File

@@ -4,6 +4,7 @@
use cssparser::{Parser, ParserInput, serialize_identifier}; use cssparser::{Parser, ParserInput, serialize_identifier};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::context::JSContext;
use script_bindings::codegen::GenericBindings::CSSBinding::PropertyDefinition; use script_bindings::codegen::GenericBindings::CSSBinding::PropertyDefinition;
use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration}; use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration};
use style::stylesheets::{CssRuleType, UrlExtraData}; use style::stylesheets::{CssRuleType, UrlExtraData};
@@ -70,8 +71,8 @@ impl CSSMethods<crate::DomTypeHolder> for CSS {
} }
/// <https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet> /// <https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet>
fn PaintWorklet(win: &Window) -> DomRoot<Worklet> { fn PaintWorklet(cx: &mut JSContext, win: &Window) -> DomRoot<Worklet> {
win.paint_worklet() win.paint_worklet(cx)
} }
/// <https://drafts.css-houdini.org/css-properties-values-api/#the-registerproperty-function> /// <https://drafts.css-houdini.org/css-properties-values-api/#the-registerproperty-function>

View File

@@ -4,7 +4,7 @@
use std::cell::Cell; use std::cell::Cell;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::ptr::null_mut; use std::ptr::{NonNull, null_mut};
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
@@ -14,13 +14,13 @@ use crossbeam_channel::{Sender, unbounded};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::{Scale, Size2D}; use euclid::{Scale, Size2D};
use js::context::JSContext; use js::context::JSContext;
use js::jsapi::{ use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSObject, Value};
HandleValueArray, Heap, IsCallable, IsConstructor, JS_ClearPendingException,
JS_IsExceptionPending, JSAutoRealm, JSObject, NewArrayObject, Value,
};
use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::realm::AutoRealm;
use js::rust::HandleValue; use js::rust::HandleValue;
use js::rust::wrappers::{Call, Construct1}; use js::rust::wrappers2::{
Call, Construct1, JS_ClearPendingException, JS_IsExceptionPending, NewArrayObject,
};
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
use pixels::PixelFormat; use pixels::PixelFormat;
use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter}; use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter};
@@ -130,7 +130,7 @@ impl PaintWorkletGlobalScope {
self.image_cache.clone() self.image_cache.clone()
} }
pub(crate) fn perform_a_worklet_task(&self, task: PaintWorkletTask) { pub(crate) fn perform_a_worklet_task(&self, cx: &mut JSContext, task: PaintWorkletTask) {
match task { match task {
PaintWorkletTask::DrawAPaintImage( PaintWorkletTask::DrawAPaintImage(
name, name,
@@ -153,10 +153,16 @@ impl PaintWorkletGlobalScope {
let map = StylePropertyMapReadOnly::from_iter( let map = StylePropertyMapReadOnly::from_iter(
self.upcast(), self.upcast(),
properties.iter().cloned(), properties.iter().cloned(),
CanGc::deprecated_note(), CanGc::from_cx(cx),
);
let result = self.draw_a_paint_image(
cx,
&name,
size,
device_pixel_ratio,
&map,
&arguments,
); );
let result =
self.draw_a_paint_image(&name, size, device_pixel_ratio, &map, &arguments);
if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) { if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) {
*self.cached_name.borrow_mut() = name; *self.cached_name.borrow_mut() = name;
self.cached_size.set(size); self.cached_size.set(size);
@@ -179,10 +185,16 @@ impl PaintWorkletGlobalScope {
let map = StylePropertyMapReadOnly::from_iter( let map = StylePropertyMapReadOnly::from_iter(
self.upcast(), self.upcast(),
properties.iter().cloned(), properties.iter().cloned(),
CanGc::deprecated_note(), CanGc::from_cx(cx),
);
let result = self.draw_a_paint_image(
cx,
&name,
size,
device_pixel_ratio,
&map,
&arguments,
); );
let result =
self.draw_a_paint_image(&name, size, device_pixel_ratio, &map, &arguments);
if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) { if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) {
*self.cached_name.borrow_mut() = name; *self.cached_name.borrow_mut() = name;
*self.cached_properties.borrow_mut() = properties; *self.cached_properties.borrow_mut() = properties;
@@ -197,6 +209,7 @@ impl PaintWorkletGlobalScope {
/// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image> /// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image>
fn draw_a_paint_image( fn draw_a_paint_image(
&self, &self,
cx: &mut JSContext,
name: &Atom, name: &Atom,
size_in_px: Size2D<f32, CSSPixel>, size_in_px: Size2D<f32, CSSPixel>,
device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>, device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
@@ -213,13 +226,13 @@ impl PaintWorkletGlobalScope {
// TODO: document paint definitions. // TODO: document paint definitions.
self.invoke_a_paint_callback( self.invoke_a_paint_callback(
cx,
name, name,
size_in_px, size_in_px,
size_in_dpx, size_in_dpx,
device_pixel_ratio, device_pixel_ratio,
properties, properties,
arguments, arguments,
CanGc::deprecated_note(),
) )
} }
@@ -228,26 +241,29 @@ impl PaintWorkletGlobalScope {
#[expect(unsafe_code)] #[expect(unsafe_code)]
fn invoke_a_paint_callback( fn invoke_a_paint_callback(
&self, &self,
cx: &mut JSContext,
name: &Atom, name: &Atom,
size_in_px: Size2D<f32, CSSPixel>, size_in_px: Size2D<f32, CSSPixel>,
size_in_dpx: Size2D<u32, DevicePixel>, size_in_dpx: Size2D<u32, DevicePixel>,
device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>, device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
properties: &StylePropertyMapReadOnly, properties: &StylePropertyMapReadOnly,
arguments: &[String], arguments: &[String],
can_gc: CanGc,
) -> DrawAPaintImageResult { ) -> DrawAPaintImageResult {
debug!( debug!(
"Invoking a paint callback {}({},{}) at {:?}.", "Invoking a paint callback {}({},{}) at {:?}.",
name, size_in_px.width, size_in_px.height, device_pixel_ratio name, size_in_px.width, size_in_px.height, device_pixel_ratio
); );
let cx = WorkletGlobalScope::get_cx(); let mut realm = AutoRealm::new(
let _ac = JSAutoRealm::new(*cx, self.worklet_global.reflector().get_jsobject().get()); cx,
NonNull::new(self.worklet_global.reflector().get_jsobject().get()).unwrap(),
);
let cx = &mut *realm;
// TODO: Steps 1-2.1. // TODO: Steps 1-2.1.
// Step 2.2-5.1. // Step 2.2-5.1.
rooted!(in(*cx) let mut class_constructor = UndefinedValue()); rooted!(&in(cx) let mut class_constructor = UndefinedValue());
rooted!(in(*cx) let mut paint_function = UndefinedValue()); rooted!(&in(cx) let mut paint_function = UndefinedValue());
let rendering_context = match self.paint_definitions.borrow().get(name) { let rendering_context = match self.paint_definitions.borrow().get(name) {
None => { None => {
// Step 2.2. // Step 2.2.
@@ -271,21 +287,21 @@ impl PaintWorkletGlobalScope {
// prepopulate the paint instance in `RegisterPaint`, to avoid calling it in // prepopulate the paint instance in `RegisterPaint`, to avoid calling it in
// the primary worklet thread. // the primary worklet thread.
// https://github.com/servo/servo/issues/17377 // https://github.com/servo/servo/issues/17377
rooted!(in(*cx) let mut paint_instance = UndefinedValue()); rooted!(&in(cx) let mut paint_instance = UndefinedValue());
match self.paint_class_instances.borrow_mut().entry(name.clone()) { match self.paint_class_instances.borrow_mut().entry(name.clone()) {
Entry::Occupied(entry) => paint_instance.set(entry.get().get()), Entry::Occupied(entry) => paint_instance.set(entry.get().get()),
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
// Step 5.2-5.3 // Step 5.2-5.3
let args = HandleValueArray::empty(); let args = HandleValueArray::empty();
rooted!(in(*cx) let mut result = null_mut::<JSObject>()); rooted!(&in(cx) let mut result = null_mut::<JSObject>());
unsafe { unsafe {
Construct1(*cx, class_constructor.handle(), &args, result.handle_mut()); Construct1(cx, class_constructor.handle(), &args, result.handle_mut());
} }
paint_instance.set(ObjectValue(result.get())); paint_instance.set(ObjectValue(result.get()));
if unsafe { JS_IsExceptionPending(*cx) } { if unsafe { JS_IsExceptionPending(cx) } {
debug!("Paint constructor threw an exception {}.", name); debug!("Paint constructor threw an exception {}.", name);
unsafe { unsafe {
JS_ClearPendingException(*cx); JS_ClearPendingException(cx);
} }
self.paint_definitions self.paint_definitions
.borrow_mut() .borrow_mut()
@@ -309,18 +325,19 @@ impl PaintWorkletGlobalScope {
rendering_context.set_bitmap_dimensions(size_in_px, device_pixel_ratio); rendering_context.set_bitmap_dimensions(size_in_px, device_pixel_ratio);
// Step 9 // Step 9
let paint_size = PaintSize::new(self, size_in_px, can_gc); let paint_size = PaintSize::new(self, size_in_px, CanGc::from_cx(cx));
// TODO: Step 10 // TODO: Step 10
// Steps 11-12 // Steps 11-12
debug!("Invoking paint function {}.", name); debug!("Invoking paint function {}.", name);
rooted_vec!(let mut arguments_values); rooted_vec!(let mut arguments_values);
for argument in arguments { for argument in arguments {
let style_value = CSSStyleValue::new(self.upcast(), argument.clone(), can_gc); let style_value =
CSSStyleValue::new(self.upcast(), argument.clone(), CanGc::from_cx(cx));
arguments_values.push(ObjectValue(style_value.reflector().get_jsobject().get())); arguments_values.push(ObjectValue(style_value.reflector().get_jsobject().get()));
} }
let arguments_value_array = HandleValueArray::from(&arguments_values); let arguments_value_array = HandleValueArray::from(&arguments_values);
rooted!(in(*cx) let argument_object = unsafe { NewArrayObject(*cx, &arguments_value_array) }); rooted!(&in(cx) let argument_object = unsafe { NewArrayObject(cx, &arguments_value_array) });
rooted_vec!(let mut callback_args); rooted_vec!(let mut callback_args);
callback_args.push(ObjectValue( callback_args.push(ObjectValue(
@@ -331,10 +348,10 @@ impl PaintWorkletGlobalScope {
callback_args.push(ObjectValue(argument_object.get())); callback_args.push(ObjectValue(argument_object.get()));
let args = HandleValueArray::from(&callback_args); let args = HandleValueArray::from(&callback_args);
rooted!(in(*cx) let mut result = UndefinedValue()); rooted!(&in(cx) let mut result = UndefinedValue());
unsafe { unsafe {
Call( Call(
*cx, cx,
paint_instance.handle(), paint_instance.handle(),
paint_function.handle(), paint_function.handle(),
&args, &args,
@@ -344,10 +361,10 @@ impl PaintWorkletGlobalScope {
let missing_image_urls = rendering_context.take_missing_image_urls(); let missing_image_urls = rendering_context.take_missing_image_urls();
// Step 13. // Step 13.
if unsafe { JS_IsExceptionPending(*cx) } { if unsafe { JS_IsExceptionPending(cx) } {
debug!("Paint function threw an exception {}.", name); debug!("Paint function threw an exception {}.", name);
unsafe { unsafe {
JS_ClearPendingException(*cx); JS_ClearPendingException(cx);
} }
return self.invalid_image(size_in_dpx, missing_image_urls); return self.invalid_image(size_in_dpx, missing_image_urls);
} }
@@ -497,11 +514,15 @@ impl PaintWorkletGlobalScopeMethods<crate::DomTypeHolder> for PaintWorkletGlobal
#[expect(unsafe_code)] #[expect(unsafe_code)]
#[cfg_attr(crown, expect(crown::unrooted_must_root))] #[cfg_attr(crown, expect(crown::unrooted_must_root))]
/// <https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint> /// <https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint>
fn RegisterPaint(&self, name: DOMString, paint_ctor: Rc<VoidFunction>) -> Fallible<()> { fn RegisterPaint(
&self,
cx: &mut JSContext,
name: DOMString,
paint_ctor: Rc<VoidFunction>,
) -> Fallible<()> {
let name = Atom::from(name); let name = Atom::from(name);
let cx = WorkletGlobalScope::get_cx(); rooted!(&in(cx) let paint_obj = paint_ctor.callback_holder().get());
rooted!(in(*cx) let paint_obj = paint_ctor.callback_holder().get()); rooted!(&in(cx) let paint_val = ObjectValue(paint_obj.get()));
rooted!(in(*cx) let paint_val = ObjectValue(paint_obj.get()));
debug!("Registering paint image name {}.", name); debug!("Registering paint image name {}.", name);
@@ -517,17 +538,19 @@ impl PaintWorkletGlobalScopeMethods<crate::DomTypeHolder> for PaintWorkletGlobal
// Step 4-6. // Step 4-6.
let property_names: Vec<String> = let property_names: Vec<String> =
get_property(cx, paint_obj.handle(), c"inputProperties", ())?.unwrap_or_default(); get_property(cx.into(), paint_obj.handle(), c"inputProperties", ())?
.unwrap_or_default();
let properties = property_names.into_iter().map(Atom::from).collect(); let properties = property_names.into_iter().map(Atom::from).collect();
// Step 7-9. // Step 7-9.
let input_arguments: Vec<String> = let input_arguments: Vec<String> =
get_property(cx, paint_obj.handle(), c"inputArguments", ())?.unwrap_or_default(); get_property(cx.into(), paint_obj.handle(), c"inputArguments", ())?.unwrap_or_default();
// TODO: Steps 10-11. // TODO: Steps 10-11.
// Steps 12-13. // Steps 12-13.
let alpha: bool = get_property(cx, paint_obj.handle(), c"alpha", ())?.unwrap_or(true); let alpha: bool =
get_property(cx.into(), paint_obj.handle(), c"alpha", ())?.unwrap_or(true);
// Step 14 // Step 14
if unsafe { !IsConstructor(paint_obj.get()) } { if unsafe { !IsConstructor(paint_obj.get()) } {
@@ -535,17 +558,22 @@ impl PaintWorkletGlobalScopeMethods<crate::DomTypeHolder> for PaintWorkletGlobal
} }
// Steps 15-16 // Steps 15-16
rooted!(in(*cx) let mut prototype = UndefinedValue()); rooted!(&in(cx) let mut prototype = UndefinedValue());
get_property_jsval(cx, paint_obj.handle(), c"prototype", prototype.handle_mut())?; get_property_jsval(
cx.into(),
paint_obj.handle(),
c"prototype",
prototype.handle_mut(),
)?;
if !prototype.is_object() { if !prototype.is_object() {
return Err(Error::Type(c"Prototype is not an object.".to_owned())); return Err(Error::Type(c"Prototype is not an object.".to_owned()));
} }
rooted!(in(*cx) let prototype = prototype.to_object()); rooted!(&in(cx) let prototype = prototype.to_object());
// Steps 17-18 // Steps 17-18
rooted!(in(*cx) let mut paint_function = UndefinedValue()); rooted!(&in(cx) let mut paint_function = UndefinedValue());
get_property_jsval( get_property_jsval(
cx, cx.into(),
prototype.handle(), prototype.handle(),
c"paint", c"paint",
paint_function.handle_mut(), paint_function.handle_mut(),
@@ -555,7 +583,7 @@ impl PaintWorkletGlobalScopeMethods<crate::DomTypeHolder> for PaintWorkletGlobal
} }
// Step 19. // Step 19.
let Some(context) = PaintRenderingContext2D::new(self, CanGc::deprecated_note()) else { let Some(context) = PaintRenderingContext2D::new(self, CanGc::from_cx(cx)) else {
return Err(Error::Operation(None)); return Err(Error::Operation(None));
}; };
let definition = PaintDefinition::new( let definition = PaintDefinition::new(

View File

@@ -6,21 +6,21 @@
use std::rc::Rc; use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::context::JSContext;
use js::realm::CurrentRealm;
use js::rust::HandleObject; use js::rust::HandleObject;
use crate::dom::bindings::codegen::Bindings::TestWorkletBinding::TestWorkletMethods; use crate::dom::bindings::codegen::Bindings::TestWorkletBinding::TestWorkletMethods;
use crate::dom::bindings::codegen::Bindings::WorkletBinding::Worklet_Binding::WorkletMethods; use crate::dom::bindings::codegen::Bindings::WorkletBinding::Worklet_Binding::WorkletMethods;
use crate::dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions; use crate::dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions;
use crate::dom::bindings::error::Fallible; use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto_and_cx};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::promise::Promise; use crate::dom::promise::Promise;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::dom::worklet::Worklet; use crate::dom::worklet::Worklet;
use crate::dom::workletglobalscope::WorkletGlobalScopeType; use crate::dom::workletglobalscope::WorkletGlobalScopeType;
use crate::realms::InRealm;
use crate::script_runtime::CanGc;
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
#[dom_struct] #[dom_struct]
@@ -37,35 +37,37 @@ impl TestWorklet {
} }
} }
fn new(window: &Window, proto: Option<HandleObject>, can_gc: CanGc) -> DomRoot<TestWorklet> { fn new(
let worklet = Worklet::new(window, WorkletGlobalScopeType::Test, can_gc); cx: &mut JSContext,
reflect_dom_object_with_proto( window: &Window,
proto: Option<HandleObject>,
) -> DomRoot<TestWorklet> {
let worklet = Worklet::new(cx, window, WorkletGlobalScopeType::Test);
reflect_dom_object_with_proto_and_cx(
Box::new(TestWorklet::new_inherited(&worklet)), Box::new(TestWorklet::new_inherited(&worklet)),
window, window,
proto, proto,
can_gc, cx,
) )
} }
} }
impl TestWorkletMethods<crate::DomTypeHolder> for TestWorklet { impl TestWorkletMethods<crate::DomTypeHolder> for TestWorklet {
fn Constructor( fn Constructor(
cx: &mut JSContext,
window: &Window, window: &Window,
proto: Option<HandleObject>, proto: Option<HandleObject>,
can_gc: CanGc,
) -> Fallible<DomRoot<TestWorklet>> { ) -> Fallible<DomRoot<TestWorklet>> {
Ok(TestWorklet::new(window, proto, can_gc)) Ok(TestWorklet::new(cx, window, proto))
} }
#[expect(non_snake_case)]
fn AddModule( fn AddModule(
&self, &self,
moduleURL: USVString, realm: &mut CurrentRealm,
module_url: USVString,
options: &WorkletOptions, options: &WorkletOptions,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> { ) -> Rc<Promise> {
self.worklet.AddModule(moduleURL, options, comp, can_gc) self.worklet.AddModule(realm, module_url, options)
} }
fn Lookup(&self, key: DOMString) -> Option<DOMString> { fn Lookup(&self, key: DOMString) -> Option<DOMString> {

View File

@@ -690,9 +690,9 @@ impl Window {
self.webxr_registry.clone() self.webxr_registry.clone()
} }
fn new_paint_worklet(&self, can_gc: CanGc) -> DomRoot<Worklet> { fn new_paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
debug!("Creating new paint worklet."); debug!("Creating new paint worklet.");
Worklet::new(self, WorkletGlobalScopeType::Paint, can_gc) Worklet::new(cx, self, WorkletGlobalScopeType::Paint)
} }
pub(crate) fn register_image_cache_listener( pub(crate) fn register_image_cache_listener(
@@ -2451,9 +2451,8 @@ impl Window {
} }
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet // https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
pub(crate) fn paint_worklet(&self) -> DomRoot<Worklet> { pub(crate) fn paint_worklet(&self, cx: &mut JSContext) -> DomRoot<Worklet> {
self.paint_worklet self.paint_worklet.or_init(|| self.new_paint_worklet(cx))
.or_init(|| self.new_paint_worklet(CanGc::deprecated_note()))
} }
pub(crate) fn has_document(&self) -> bool { pub(crate) fn has_document(&self) -> bool {

View File

@@ -20,7 +20,9 @@ use std::thread;
use crossbeam_channel::{Receiver, Sender, unbounded}; use crossbeam_channel::{Receiver, Sender, unbounded};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::context::JSContext;
use js::jsapi::{GCReason, JSGCParamKey, JSTracer}; use js::jsapi::{GCReason, JSGCParamKey, JSTracer};
use js::realm::CurrentRealm;
use js::rust::wrappers2::{JS_GC, JS_GetGCParameter}; use js::rust::wrappers2::{JS_GC, JS_GetGCParameter};
use malloc_size_of::malloc_size_of_is_0; use malloc_size_of::malloc_size_of_is_0;
use net_traits::blob_url_store::UrlWithBlobClaim; use net_traits::blob_url_store::UrlWithBlobClaim;
@@ -41,7 +43,7 @@ use crate::dom::bindings::codegen::Bindings::WorkletBinding::{WorkletMethods, Wo
use crate::dom::bindings::error::Error; use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::TrustedPromise; use crate::dom::bindings::refcounted::TrustedPromise;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_cx};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::USVString; use crate::dom::bindings::str::USVString;
use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox}; use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox};
@@ -56,7 +58,6 @@ use crate::dom::workletglobalscope::{
}; };
use crate::fetch::{CspViolationsProcessor, load_whole_resource}; use crate::fetch::{CspViolationsProcessor, load_whole_resource};
use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg}; use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg};
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, Runtime, ScriptThreadEventCategory}; use crate::script_runtime::{CanGc, Runtime, ScriptThreadEventCategory};
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
use crate::task::TaskBox; use crate::task::TaskBox;
@@ -107,15 +108,15 @@ impl Worklet {
} }
pub(crate) fn new( pub(crate) fn new(
cx: &mut JSContext,
window: &Window, window: &Window,
global_type: WorkletGlobalScopeType, global_type: WorkletGlobalScopeType,
can_gc: CanGc,
) -> DomRoot<Worklet> { ) -> DomRoot<Worklet> {
debug!("Creating worklet {:?}.", global_type); debug!("Creating worklet {:?}.", global_type);
reflect_dom_object( reflect_dom_object_with_cx(
Box::new(Worklet::new_inherited(window, global_type)), Box::new(Worklet::new_inherited(window, global_type)),
window, window,
can_gc, cx,
) )
} }
@@ -134,13 +135,12 @@ impl WorkletMethods<crate::DomTypeHolder> for Worklet {
/// <https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule> /// <https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule>
fn AddModule( fn AddModule(
&self, &self,
realm: &mut CurrentRealm,
module_url: USVString, module_url: USVString,
options: &WorkletOptions, options: &WorkletOptions,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> { ) -> Rc<Promise> {
// Step 1. // Step 1.
let promise = Promise::new_in_current_realm(comp, can_gc); let promise = Promise::new_in_realm(realm);
// Step 3. // Step 3.
let module_url_record = match self.window.Document().base_url().join(&module_url.0) { let module_url_record = match self.window.Document().base_url().join(&module_url.0) {
@@ -148,7 +148,7 @@ impl WorkletMethods<crate::DomTypeHolder> for Worklet {
Err(err) => { Err(err) => {
// Step 4. // Step 4.
debug!("URL {:?} parse error {:?}.", module_url.0, err); debug!("URL {:?} parse error {:?}.", module_url.0, err);
promise.reject_error(Error::Syntax(None), can_gc); promise.reject_error(Error::Syntax(None), CanGc::from_cx(realm));
return promise; return promise;
}, },
}; };
@@ -534,14 +534,14 @@ impl WorkletThread {
} }
/// The main event loop for a worklet thread /// The main event loop for a worklet thread
fn run(&mut self, cx: &mut js::context::JSContext) { fn run(&mut self, cx: &mut JSContext) {
loop { loop {
// The handler for data messages // The handler for data messages
let message = self.role.receiver.recv().unwrap(); let message = self.role.receiver.recv().unwrap();
match message { match message {
// The whole point of this thread pool is to perform tasks! // The whole point of this thread pool is to perform tasks!
WorkletData::Task(id, task) => { WorkletData::Task(id, task) => {
self.perform_a_worklet_task(id, task); self.perform_a_worklet_task(cx, id, task);
}, },
// To start swapping roles, get ready to perform an atomic swap, // To start swapping roles, get ready to perform an atomic swap,
// and block waiting for the other end to finish it. // and block waiting for the other end to finish it.
@@ -615,7 +615,7 @@ impl WorkletThread {
/// Perform a GC. /// Perform a GC.
#[expect(unsafe_code)] #[expect(unsafe_code)]
fn gc(&mut self, cx: &mut js::context::JSContext) { fn gc(&mut self, cx: &mut JSContext) {
debug!( debug!(
"BEGIN GC (usage = {}, threshold = {}).", "BEGIN GC (usage = {}, threshold = {}).",
self.current_memory_usage(), self.current_memory_usage(),
@@ -641,7 +641,7 @@ impl WorkletThread {
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
global_type: WorkletGlobalScopeType, global_type: WorkletGlobalScopeType,
base_url: ServoUrl, base_url: ServoUrl,
cx: &mut js::context::JSContext, cx: &mut JSContext,
) -> DomRoot<WorkletGlobalScope> { ) -> DomRoot<WorkletGlobalScope> {
match self.global_scopes.entry(worklet_id) { match self.global_scopes.entry(worklet_id) {
hash_map::Entry::Occupied(entry) => DomRoot::from_ref(entry.get()), hash_map::Entry::Occupied(entry) => DomRoot::from_ref(entry.get()),
@@ -677,7 +677,7 @@ impl WorkletThread {
credentials: RequestCredentials, credentials: RequestCredentials,
pending_tasks_struct: PendingTasksStruct, pending_tasks_struct: PendingTasksStruct,
promise: TrustedPromise, promise: TrustedPromise,
cx: &mut js::context::JSContext, cx: &mut JSContext,
) { ) {
debug!("Fetching from {}.", script_url); debug!("Fetching from {}.", script_url);
// Step 1. // Step 1.
@@ -742,9 +742,9 @@ impl WorkletThread {
} }
/// Perform a task. /// Perform a task.
fn perform_a_worklet_task(&self, worklet_id: WorkletId, task: WorkletTask) { fn perform_a_worklet_task(&self, cx: &mut JSContext, worklet_id: WorkletId, task: WorkletTask) {
match self.global_scopes.get(&worklet_id) { match self.global_scopes.get(&worklet_id) {
Some(global) => global.perform_a_worklet_task(task), Some(global) => global.perform_a_worklet_task(cx, task),
None => warn!("No such worklet as {:?}.", worklet_id), None => warn!("No such worklet as {:?}.", worklet_id),
} }
} }

View File

@@ -9,6 +9,7 @@ use crossbeam_channel::Sender;
use devtools_traits::ScriptToDevtoolsControlMsg; use devtools_traits::ScriptToDevtoolsControlMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan}; use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan};
use js::context::JSContext;
use net_traits::ResourceThreads; use net_traits::ResourceThreads;
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
use profile_traits::{mem, time}; use profile_traits::{mem, time};
@@ -33,7 +34,7 @@ use crate::dom::webgpu::identityhub::IdentityHub;
use crate::dom::worklet::WorkletExecutor; use crate::dom::worklet::WorkletExecutor;
use crate::messaging::MainThreadScriptMsg; use crate::messaging::MainThreadScriptMsg;
use crate::realms::enter_auto_realm; use crate::realms::enter_auto_realm;
use crate::script_runtime::{IntroductionType, JSContext}; use crate::script_runtime::IntroductionType;
#[dom_struct] #[dom_struct]
/// <https://drafts.css-houdini.org/worklets/#workletglobalscope> /// <https://drafts.css-houdini.org/worklets/#workletglobalscope>
@@ -60,7 +61,7 @@ impl WorkletGlobalScope {
inherited_secure_context: Option<bool>, inherited_secure_context: Option<bool>,
executor: WorkletExecutor, executor: WorkletExecutor,
init: &WorkletGlobalScopeInit, init: &WorkletGlobalScopeInit,
cx: &mut js::context::JSContext, cx: &mut JSContext,
) -> DomRoot<WorkletGlobalScope> { ) -> DomRoot<WorkletGlobalScope> {
let scope: DomRoot<WorkletGlobalScope> = match scope_type { let scope: DomRoot<WorkletGlobalScope> = match scope_type {
#[cfg(feature = "testbinding")] #[cfg(feature = "testbinding")]
@@ -130,16 +131,11 @@ impl WorkletGlobalScope {
} }
} }
/// Get the JS context.
pub(crate) fn get_cx() -> JSContext {
GlobalScope::get_cx()
}
/// Evaluate a JS script in this global. /// Evaluate a JS script in this global.
pub(crate) fn evaluate_js( pub(crate) fn evaluate_js(
&self, &self,
script: Cow<'_, str>, script: Cow<'_, str>,
cx: &mut js::context::JSContext, cx: &mut JSContext,
) -> Result<(), JavaScriptEvaluationError> { ) -> Result<(), JavaScriptEvaluationError> {
let mut realm = enter_auto_realm(cx, self); let mut realm = enter_auto_realm(cx, self);
let cx = &mut realm.current_realm(); let cx = &mut realm.current_realm();
@@ -182,7 +178,7 @@ impl WorkletGlobalScope {
} }
/// Perform a worklet task /// Perform a worklet task
pub(crate) fn perform_a_worklet_task(&self, task: WorkletTask) { pub(crate) fn perform_a_worklet_task(&self, cx: &mut JSContext, task: WorkletTask) {
match task { match task {
#[cfg(feature = "testbinding")] #[cfg(feature = "testbinding")]
WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() { WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() {
@@ -190,7 +186,7 @@ impl WorkletGlobalScope {
None => warn!("This is not a test worklet."), None => warn!("This is not a test worklet."),
}, },
WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() { WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() {
Some(global) => global.perform_a_worklet_task(task), Some(global) => global.perform_a_worklet_task(cx, task),
None => warn!("This is not a paint worklet."), None => warn!("This is not a paint worklet."),
}, },
} }

View File

@@ -126,6 +126,10 @@ DOMInterfaces = {
'canGc': ['IsConditionalMediationAvailable', 'WillRequestConditionalCreation'], 'canGc': ['IsConditionalMediationAvailable', 'WillRequestConditionalCreation'],
}, },
'CSS': {
'cx': ['PaintWorklet'],
},
'CSSGroupingRule': { 'CSSGroupingRule': {
'cx': ['CssRules', 'InsertRule', 'DeleteRule'], 'cx': ['CssRules', 'InsertRule', 'DeleteRule'],
}, },
@@ -746,6 +750,10 @@ DOMInterfaces = {
'cx': ['CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient', 'GetTransform'], 'cx': ['CreateLinearGradient', 'CreatePattern', 'CreateRadialGradient', 'GetTransform'],
}, },
'PaintWorkletGlobalScope': {
'cx': ['RegisterPaint'],
},
'PerformanceObserver': { 'PerformanceObserver': {
'canGc': ['SupportedEntryTypes'], 'canGc': ['SupportedEntryTypes'],
}, },
@@ -856,8 +864,8 @@ DOMInterfaces = {
}, },
'TestWorklet': { 'TestWorklet': {
'inRealms': ['AddModule'], 'cx': ['Constructor'],
'canGc': ['AddModule'], 'realm': ['AddModule'],
}, },
'Text': { 'Text': {
@@ -955,8 +963,7 @@ DOMInterfaces = {
}, },
'Worklet': { 'Worklet': {
'inRealms': ['AddModule'], 'realm': ['AddModule'],
'canGc': ['AddModule'],
}, },
'XMLDocument': { 'XMLDocument': {