diff options
20 files changed, 575 insertions, 19 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 3bcc44014e7..3fdb41d3da1 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1172,6 +1172,7 @@ impl FragmentDisplayListBuilding for Fragment { }; // TODO: add a one-place cache to avoid drawing the paint image every time. + // https://github.com/servo/servo/issues/17369 debug!("Drawing a paint image {}({},{}).", name, size.width.to_px(), size.height.to_px()); let mut image = match executor.draw_a_paint_image(name, size) { Ok(image) => image, diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index a44a3f54c30..9d927a884f1 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -32,6 +32,7 @@ //! | sequences | `Vec<T>` | //! | union types | `T` | +use dom::bindings::error::{Error, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::num::Finite; @@ -48,15 +49,15 @@ use js::glue::{GetProxyPrivate, IsWrapper}; use js::glue::{RUST_JSID_IS_INT, RUST_JSID_TO_INT}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; use js::jsapi::{HandleId, HandleObject, HandleValue, JSContext, JSObject, JSString}; -use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot}; -use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_IsArrayObject}; +use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetProperty, JS_GetReservedSlot}; +use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_IsArrayObject, JS_IsExceptionPending}; use js::jsapi::{JS_NewStringCopyN, JS_StringHasLatin1Chars, MutableHandleValue}; -use js::jsval::{ObjectValue, StringValue}; +use js::jsval::{ObjectValue, StringValue, UndefinedValue}; use js::rust::{ToString, get_object_class, is_dom_class, is_dom_object, maybe_wrap_value}; use libc; use num_traits::Float; use servo_config::opts; -use std::{char, ptr, slice}; +use std::{char, ffi, ptr, slice}; /// A trait to check whether a given `JSObject` implements an IDL interface. pub trait IDLInterface { @@ -481,3 +482,45 @@ pub unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool { assert!(JS_IsArrayObject(cx, value, &mut result)); result } + +/// Get a property from a JS object. +pub unsafe fn get_property_jsval(cx: *mut JSContext, + object: HandleObject, + name: &str, + rval: MutableHandleValue) + -> Fallible<()> +{ + rval.set(UndefinedValue()); + let cname = match ffi::CString::new(name) { + Ok(cname) => cname, + Err(_) => return Ok(()), + }; + JS_GetProperty(cx, object, cname.as_ptr(), rval); + if JS_IsExceptionPending(cx) { + return Err(Error::JSFailed); + } + Ok(()) +} + +/// Get a property from a JS object, and convert it to a Rust value. +pub unsafe fn get_property<T>(cx: *mut JSContext, + object: HandleObject, + name: &str, + option: T::Config) + -> Fallible<Option<T>> where + T: FromJSValConvertible +{ + debug!("Getting property {}.", name); + rooted!(in(cx) let mut result = UndefinedValue()); + get_property_jsval(cx, object, name, result.handle_mut())?; + if result.is_undefined() { + debug!("No property {}.", name); + return Ok(None); + } + debug!("Converting property {}.", name); + match T::from_jsval(cx, result.handle(), option) { + Ok(ConversionResult::Success(value)) => Ok(Some(value)), + Ok(ConversionResult::Failure(_)) => Ok(None), + Err(()) => Err(Error::JSFailed), + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 3ea53f2b4ca..6f800625148 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -391,6 +391,8 @@ pub mod node; pub mod nodeiterator; pub mod nodelist; pub mod pagetransitionevent; +pub mod paintrenderingcontext2d; +pub mod paintsize; pub mod paintworkletglobalscope; pub mod performance; pub mod performancetiming; diff --git a/components/script/dom/paintrenderingcontext2d.rs b/components/script/dom/paintrenderingcontext2d.rs new file mode 100644 index 00000000000..a70e62a45ed --- /dev/null +++ b/components/script/dom/paintrenderingcontext2d.rs @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding; +use dom::bindings::js::Root; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct PaintRenderingContext2D { + reflector: Reflector, +} + +impl PaintRenderingContext2D { + fn new_inherited() -> PaintRenderingContext2D { + PaintRenderingContext2D { + reflector: Reflector::new(), + } + } + + pub fn new(global: &PaintWorkletGlobalScope) -> Root<PaintRenderingContext2D> { + reflect_dom_object(box PaintRenderingContext2D::new_inherited(), + global, + PaintRenderingContext2DBinding::Wrap) + } +} diff --git a/components/script/dom/paintsize.rs b/components/script/dom/paintsize.rs new file mode 100644 index 00000000000..c012850f344 --- /dev/null +++ b/components/script/dom/paintsize.rs @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use app_units::Au; +use dom::bindings::codegen::Bindings::PaintSizeBinding; +use dom::bindings::codegen::Bindings::PaintSizeBinding::PaintSizeMethods; +use dom::bindings::js::Root; +use dom::bindings::num::Finite; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use dom_struct::dom_struct; +use euclid::Size2D; + +#[dom_struct] +pub struct PaintSize { + reflector: Reflector, + width: Finite<f64>, + height: Finite<f64>, +} + +impl PaintSize { + fn new_inherited(size: Size2D<Au>) -> PaintSize { + PaintSize { + reflector: Reflector::new(), + width: Finite::wrap(size.width.to_px().abs() as f64), + height: Finite::wrap(size.height.to_px().abs() as f64), + } + } + + pub fn new(global: &PaintWorkletGlobalScope, size: Size2D<Au>) -> Root<PaintSize> { + reflect_dom_object(box PaintSize::new_inherited(size), global, PaintSizeBinding::Wrap) + } +} + +impl PaintSizeMethods for PaintSize { + /// https://drafts.css-houdini.org/css-paint-api/#paintsize + fn Width(&self) -> Finite<f64> { + self.width + } + + /// https://drafts.css-houdini.org/css-paint-api/#paintsize + fn Height(&self) -> Finite<f64> { + self.height + } +} diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index ee0b07928c4..7a6117659ac 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -3,17 +3,39 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; +use dom::bindings::callback::CallbackContainer; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding; use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding::PaintWorkletGlobalScopeMethods; use dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; +use dom::bindings::conversions::StringificationBehavior; +use dom::bindings::conversions::get_property; +use dom::bindings::conversions::get_property_jsval; +use dom::bindings::error::Error; +use dom::bindings::error::Fallible; use dom::bindings::js::Root; +use dom::bindings::reflector::DomObject; use dom::bindings::str::DOMString; +use dom::paintrenderingcontext2d::PaintRenderingContext2D; +use dom::paintsize::PaintSize; use dom::workletglobalscope::WorkletGlobalScope; use dom::workletglobalscope::WorkletGlobalScopeInit; use dom_struct::dom_struct; use euclid::Size2D; use ipc_channel::ipc::IpcSharedMemory; +use js::jsapi::Call; +use js::jsapi::Construct1; +use js::jsapi::HandleValue; +use js::jsapi::HandleValueArray; +use js::jsapi::Heap; +use js::jsapi::IsCallable; +use js::jsapi::IsConstructor; +use js::jsapi::JSAutoCompartment; +use js::jsapi::JS_ClearPendingException; +use js::jsapi::JS_IsExceptionPending; +use js::jsval::JSVal; +use js::jsval::ObjectValue; +use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; use net_traits::image::base::Image; @@ -21,14 +43,22 @@ use net_traits::image::base::PixelFormat; use script_traits::PaintWorkletError; use servo_atoms::Atom; use servo_url::ServoUrl; +use std::cell::Cell; +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use std::ptr::null_mut; use std::rc::Rc; use std::sync::mpsc::Sender; -#[dom_struct] /// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope +#[dom_struct] pub struct PaintWorkletGlobalScope { /// The worklet global for this object worklet_global: WorkletGlobalScope, + /// https://drafts.css-houdini.org/css-paint-api/#paint-definitions + paint_definitions: DOMRefCell<HashMap<Atom, Box<PaintDefinition>>>, + /// https://drafts.css-houdini.org/css-paint-api/#paint-class-instances + paint_class_instances: DOMRefCell<HashMap<Atom, Box<Heap<JSVal>>>>, /// A buffer to draw into buffer: DOMRefCell<Vec<u8>>, } @@ -43,6 +73,8 @@ impl PaintWorkletGlobalScope { debug!("Creating paint worklet global scope for pipeline {}.", pipeline_id); let global = box PaintWorkletGlobalScope { worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init), + paint_definitions: Default::default(), + paint_class_instances: Default::default(), buffer: Default::default(), }; unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) } @@ -54,40 +86,208 @@ impl PaintWorkletGlobalScope { } } + /// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image fn draw_a_paint_image(&self, name: Atom, - concrete_object_size: Size2D<Au>, - sender: Sender<Result<Image, PaintWorkletError>>) { - let width = concrete_object_size.width.to_px().abs() as u32; - let height = concrete_object_size.height.to_px().abs() as u32; + size: Size2D<Au>, + sender: Sender<Result<Image, PaintWorkletError>>) + { + // TODO: document paint definitions. + self.invoke_a_paint_callback(name, size, sender); + } + + /// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback + #[allow(unsafe_code)] + fn invoke_a_paint_callback(&self, + name: Atom, + size: Size2D<Au>, + sender: Sender<Result<Image, PaintWorkletError>>) + { + let width = size.width.to_px().abs() as u32; + let height = size.height.to_px().abs() as u32; + debug!("Invoking a paint callback {}({},{}).", name, width, height); + + let cx = self.worklet_global.get_cx(); + let _ac = JSAutoCompartment::new(cx, self.worklet_global.reflector().get_jsobject().get()); + + // TODO: Steps 1-2.1. + // Step 2.2-5.1. + rooted!(in(cx) let mut class_constructor = UndefinedValue()); + rooted!(in(cx) let mut paint_function = UndefinedValue()); + match self.paint_definitions.borrow().get(&name) { + None => { + // Step 2.2. + warn!("Drawing un-registered paint definition {}.", name); + let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]); + let _ = sender.send(Ok(image)); + return; + } + Some(definition) => { + // Step 5.1 + if !definition.constructor_valid_flag.get() { + debug!("Drawing invalid paint definition {}.", name); + let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]); + let _ = sender.send(Ok(image)); + return; + } + class_constructor.set(definition.class_constructor.get()); + paint_function.set(definition.paint_function.get()); + } + }; + + // Steps 5.2-5.4 + // TODO: the spec requires calling the constructor now, but we might want to + // prepopulate the paint instance in `RegisterPaint`, to avoid calling it in + // the primary worklet thread. + // https://github.com/servo/servo/issues/17377 + rooted!(in(cx) let mut paint_instance = UndefinedValue()); + match self.paint_class_instances.borrow_mut().entry(name.clone()) { + Entry::Occupied(entry) => paint_instance.set(entry.get().get()), + Entry::Vacant(entry) => { + // Step 5.2-5.3 + let args = HandleValueArray::new(); + rooted!(in(cx) let mut result = null_mut()); + unsafe { Construct1(cx, class_constructor.handle(), &args, result.handle_mut()); } + paint_instance.set(ObjectValue(result.get())); + if unsafe { JS_IsExceptionPending(cx) } { + debug!("Paint constructor threw an exception {}.", name); + unsafe { JS_ClearPendingException(cx); } + self.paint_definitions.borrow_mut().get_mut(&name) + .expect("Vanishing paint definition.") + .constructor_valid_flag.set(false); + let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]); + let _ = sender.send(Ok(image)); + return; + } + // Step 5.4 + entry.insert(Box::new(Heap::default())).set(paint_instance.get()); + } + }; + + // TODO: Steps 6-7 + // Step 8 + let rendering_context = PaintRenderingContext2D::new(self); + + // Step 9 + let paint_size = PaintSize::new(self, size); + + // TODO: Step 10 + // Steps 11-12 + debug!("Invoking paint function {}.", name); + let args_slice = [ + ObjectValue(rendering_context.reflector().get_jsobject().get()), + ObjectValue(paint_size.reflector().get_jsobject().get()), + ]; + let args = unsafe { HandleValueArray::from_rooted_slice(&args_slice) }; + rooted!(in(cx) let mut result = UndefinedValue()); + unsafe { Call(cx, paint_instance.handle(), paint_function.handle(), &args, result.handle_mut()); } + + // Step 13. + if unsafe { JS_IsExceptionPending(cx) } { + debug!("Paint function threw an exception {}.", name); + unsafe { JS_ClearPendingException(cx); } + let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]); + let _ = sender.send(Ok(image)); + return; + } + + // For now, we just build a dummy image. + let image = self.placeholder_image(width, height, [0xFF, 0x00, 0x00, 0xFF]); + let _ = sender.send(Ok(image)); + } + + fn placeholder_image(&self, width: u32, height: u32, pixel: [u8; 4]) -> Image { let area = (width as usize) * (height as usize); let old_buffer_size = self.buffer.borrow().len(); let new_buffer_size = area * 4; - debug!("Drawing a paint image {}({},{}).", name, width, height); - // TODO: call into script to create the image. - // For now, we just build a dummy. if new_buffer_size > old_buffer_size { - let pixel = [0xFF, 0x00, 0x00, 0xFF]; self.buffer.borrow_mut().extend(pixel.iter().cycle().take(new_buffer_size - old_buffer_size)); } else { self.buffer.borrow_mut().truncate(new_buffer_size); } - let image = Image { + Image { width: width, height: height, format: PixelFormat::BGRA8, bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()), id: None, - }; - let _ = sender.send(Ok(image)); + } } } impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { + #[allow(unsafe_code)] /// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint - fn RegisterPaint(&self, name: DOMString, _paintCtor: Rc<VoidFunction>) { + fn RegisterPaint(&self, name: DOMString, paint_ctor: Rc<VoidFunction>) -> Fallible<()> { + let name = Atom::from(name); + let cx = self.worklet_global.get_cx(); + rooted!(in(cx) let paint_obj = paint_ctor.callback_holder().get()); + rooted!(in(cx) let paint_val = ObjectValue(paint_obj.get())); + debug!("Registering paint image name {}.", name); - // TODO + + // Step 1. + if name.is_empty() { + return Err(Error::Type(String::from("Empty paint name."))) ; + } + + // Step 2-3. + if self.paint_definitions.borrow().contains_key(&name) { + return Err(Error::InvalidModification); + } + + // Step 4-6. + debug!("Getting input properties."); + let input_properties: Vec<DOMString> = + unsafe { get_property(cx, paint_obj.handle(), "inputProperties", StringificationBehavior::Default) }? + .unwrap_or_default(); + debug!("Got {:?}.", input_properties); + + // Step 7-9. + debug!("Getting input arguments."); + let input_arguments: Vec<DOMString> = + unsafe { get_property(cx, paint_obj.handle(), "inputArguments", StringificationBehavior::Default) }? + .unwrap_or_default(); + debug!("Got {:?}.", input_arguments); + + // TODO: Steps 10-11. + + // Steps 12-13. + debug!("Getting alpha."); + let alpha: bool = + unsafe { get_property(cx, paint_obj.handle(), "alpha", ()) }? + .unwrap_or(true); + debug!("Got {:?}.", alpha); + + // Step 14 + if unsafe { !IsConstructor(paint_obj.get()) } { + return Err(Error::Type(String::from("Not a constructor."))); + } + + // Steps 15-16 + rooted!(in(cx) let mut prototype = UndefinedValue()); + unsafe { get_property_jsval(cx, paint_obj.handle(), "prototype", prototype.handle_mut())?; } + if !prototype.is_object() { + return Err(Error::Type(String::from("Prototype is not an object."))); + } + rooted!(in(cx) let prototype = prototype.to_object()); + + // Steps 17-18 + rooted!(in(cx) let mut paint_function = UndefinedValue()); + unsafe { get_property_jsval(cx, prototype.handle(), "paint", paint_function.handle_mut())?; } + if !paint_function.is_object() || unsafe { !IsCallable(paint_function.to_object()) } { + return Err(Error::Type(String::from("Paint function is not callable."))); + } + + // Steps 19-20. + debug!("Registering definition {}.", name); + self.paint_definitions.borrow_mut() + .insert(name, + PaintDefinition::new(paint_val.handle(), paint_function.handle(), input_properties, alpha)); + + // TODO: Step 21. + + Ok(()) } } @@ -95,3 +295,37 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { pub enum PaintWorkletTask { DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>) } + +/// A paint definition +/// https://drafts.css-houdini.org/css-paint-api/#paint-definition +/// This type is dangerous, because it contains uboxed `Heap<JSVal>` values, +/// which can't be moved. +#[derive(JSTraceable, HeapSizeOf)] +#[must_root] +struct PaintDefinition { + class_constructor: Heap<JSVal>, + paint_function: Heap<JSVal>, + constructor_valid_flag: Cell<bool>, + input_properties: Vec<DOMString>, + context_alpha_flag: bool, +} + +impl PaintDefinition { + fn new(class_constructor: HandleValue, + paint_function: HandleValue, + input_properties: Vec<DOMString>, + alpha: bool) + -> Box<PaintDefinition> + { + let result = Box::new(PaintDefinition { + class_constructor: Heap::default(), + paint_function: Heap::default(), + constructor_valid_flag: Cell::new(true), + input_properties: input_properties, + context_alpha_flag: alpha, + }); + result.class_constructor.set(class_constructor.get()); + result.paint_function.set(paint_function.get()); + result + } +} diff --git a/components/script/dom/webidls/PaintRenderingContext2D.webidl b/components/script/dom/webidls/PaintRenderingContext2D.webidl new file mode 100644 index 00000000000..3483df305b1 --- /dev/null +++ b/components/script/dom/webidls/PaintRenderingContext2D.webidl @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://drafts.css-houdini.org/css-paint-api/#paintrenderingcontext2d +[Exposed=PaintWorklet] +interface PaintRenderingContext2D { +}; +// PaintRenderingContext2D implements CanvasState; +// PaintRenderingContext2D implements CanvasTransform; +// PaintRenderingContext2D implements CanvasCompositing; +// PaintRenderingContext2D implements CanvasImageSmoothing; +// PaintRenderingContext2D implements CanvasFillStrokeStyles; +// PaintRenderingContext2D implements CanvasShadowStyles; +// PaintRenderingContext2D implements CanvasRect; +// PaintRenderingContext2D implements CanvasDrawPath; +// PaintRenderingContext2D implements CanvasDrawImage; +// PaintRenderingContext2D implements CanvasPathDrawingStyles; +// PaintRenderingContext2D implements CanvasPath; diff --git a/components/script/dom/webidls/PaintSize.webidl b/components/script/dom/webidls/PaintSize.webidl new file mode 100644 index 00000000000..6f90416401a --- /dev/null +++ b/components/script/dom/webidls/PaintSize.webidl @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://drafts.css-houdini.org/css-paint-api/#paintsize +[Exposed=PaintWorklet] +interface PaintSize { + readonly attribute double width; + readonly attribute double height; +}; diff --git a/components/script/dom/webidls/PaintWorkletGlobalScope.webidl b/components/script/dom/webidls/PaintWorkletGlobalScope.webidl index 1611ad23694..95d4388866b 100644 --- a/components/script/dom/webidls/PaintWorkletGlobalScope.webidl +++ b/components/script/dom/webidls/PaintWorkletGlobalScope.webidl @@ -5,5 +5,5 @@ // https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope [Global=(Worklet,PaintWorklet), Exposed=PaintWorklet] interface PaintWorkletGlobalScope : WorkletGlobalScope { - void registerPaint(DOMString name, VoidFunction paintCtor); + [Throws] void registerPaint(DOMString name, VoidFunction paintCtor); }; diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index da74215f752..e5e55b97453 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -595,6 +595,9 @@ impl WorkletThread { // Step 4. // NOTE: the spec parses and executes the script in separate steps, // but our JS API doesn't separate these, so we do the steps out of order. + // Also, the spec currently doesn't allow exceptions to be propagated + // to the main script thread. + // https://github.com/w3c/css-houdini-drafts/issues/407 let ok = script.map(|script| global_scope.evaluate_js(&*script)).unwrap_or(false); if !ok { diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 78804d66ae0..e34be97851e 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -13,6 +13,7 @@ use dom::testworkletglobalscope::TestWorkletTask; use dom_struct::dom_struct; use ipc_channel::ipc; use ipc_channel::ipc::IpcSender; +use js::jsapi::JSContext; use js::jsval::UndefinedValue; use js::rust::Runtime; use microtask::Microtask; @@ -61,6 +62,11 @@ impl WorkletGlobalScope { } } + /// Get the JS context. + pub fn get_cx(&self) -> *mut JSContext { + self.globalscope.get_cx() + } + /// Evaluate a JS script in this global. pub fn evaluate_js(&self, script: &str) -> bool { debug!("Evaluating JS."); diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 8bbbfa62328..3bd8906353e 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -11271,6 +11271,41 @@ {} ] ], + "mozilla/worklets/test_paint_worklet_alpha_throws.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_arguments_throws.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_empty_name.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_no_paint.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_not_constructor.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_paint_not_callable.js": [ + [ + {} + ] + ], + "mozilla/worklets/test_paint_worklet_properties_throws.js": [ + [ + {} + ] + ], "mozilla/worklets/test_paint_worklet_ref.html": [ [ {} @@ -20162,6 +20197,12 @@ {} ] ], + "mozilla/worklets/test_paint_worklet_loading.html": [ + [ + "/_mozilla/mozilla/worklets/test_paint_worklet_loading.html", + {} + ] + ], "mozilla/worklets/test_worklet.html": [ [ "/_mozilla/mozilla/worklets/test_worklet.html", @@ -31907,6 +31948,38 @@ "e714db50da9e5cb18c652629fcc1b5ccc453564d", "support" ], + "mozilla/worklets/test_paint_worklet_alpha_throws.js": [ + "fe7df9db6799494f4d24f5ad00349f3aeebb2c23", + "support" + ], + "mozilla/worklets/test_paint_worklet_arguments_throws.js": [ + "64ef8ea909c96c23932f77dd9ef793f85cfbb38d", + "support" + ], + "mozilla/worklets/test_paint_worklet_empty_name.js": [ + "589e2a4061e51122750afe2d72f8f609ffb9b216", + "support" + ], + "mozilla/worklets/test_paint_worklet_loading.html": [ + "20fef90b09fdf8dfb5e7461a01c8bd0f7e5d31af", + "testharness" + ], + "mozilla/worklets/test_paint_worklet_no_paint.js": [ + "ad637c4f670a8be2814ff21dbc916157293892e5", + "support" + ], + "mozilla/worklets/test_paint_worklet_not_constructor.js": [ + "70e69524890892e855155fc762a46c8cebaa1fbe", + "support" + ], + "mozilla/worklets/test_paint_worklet_paint_not_callable.js": [ + "51f1b485d5c505c918edec8a146d54d9e0cf60d9", + "support" + ], + "mozilla/worklets/test_paint_worklet_properties_throws.js": [ + "50fd53b8f561edda7c6bc8a0e9ac7207bdf22d8b", + "support" + ], "mozilla/worklets/test_paint_worklet_ref.html": [ "e9cfa945824a8ecf07c41a269f82a2c2ca002406", "support" diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_alpha_throws.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_alpha_throws.js new file mode 100644 index 00000000000..b245f8077ba --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_alpha_throws.js @@ -0,0 +1,4 @@ +registerPaint("alphaThrows", class { + static get alpha() { throw new TypeError(); } + paint(ctx, size) { } +}); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_arguments_throws.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_arguments_throws.js new file mode 100644 index 00000000000..84976121d6d --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_arguments_throws.js @@ -0,0 +1,4 @@ +registerPaint("argumentsThrows", class { + static get inputArguments() { throw new TypeError(); } + paint(ctx, size) { } +}); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_empty_name.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_empty_name.js new file mode 100644 index 00000000000..40e6f676025 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_empty_name.js @@ -0,0 +1,6 @@ +registerPaint("", class { + paint(ctx, size) { + ctx.fillStyle = 'green'; + ctx.fillRect(0, 0, size.width, size.height); + } +}); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_loading.html b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_loading.html new file mode 100644 index 00000000000..e1edce96d12 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_loading.html @@ -0,0 +1,63 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Test paint worklet loading</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script> +var host_info = get_host_info(); + +promise_test(function() { + return paintWorklet.addModule("test_paint_worklet.js"); +}, "Loading a paint worklet."); + +promise_test(function(t) { + var path = new URL("test_paint_worklet.js", document.location).pathname; + var url = new URL(path, host_info.HTTP_REMOTE_ORIGIN); + return promise_rejects(t, "AbortError", paintWorklet.addModule(url)); +}, "Loading a cross-origin paint worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("nonexistent_worklet.js")); +}, "Loading a nonexistent paint worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("syntax_error.js")); +}, "Loading a syntactically incorrect paint worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("throw_exception.js")); +}, "Loading an exception-throwing paint worklet."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet.js")); +}, "Loading a paint worklet again."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_empty_name.js")); +}, "Loading a paint worklet with an empty name."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_properties_throws.js")); +}, "Loading a paint worklet whose inputProperties throws an exception."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_arguments_throws.js")); +}, "Loading a paint worklet whose inputArguments throws an exception."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_alpha_throws.js")); +}, "Loading a paint worklet whose alpha throws an exception."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_not_constructor.js")); +}, "Loading a paint worklet which isn't a constructor function."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_no_paint.js")); +}, "Loading a paint worklet with no paint."); + +promise_test(function(t) { + return promise_rejects(t, "AbortError", paintWorklet.addModule("test_paint_worklet_paint_not_callable.js")); +}, "Loading a paint worklet with a paint that is not callable."); +</script> diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_no_paint.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_no_paint.js new file mode 100644 index 00000000000..a288ed1e4a3 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_no_paint.js @@ -0,0 +1,2 @@ +registerPaint("noPaint", class { +}); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_not_constructor.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_not_constructor.js new file mode 100644 index 00000000000..d6a4d57333a --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_not_constructor.js @@ -0,0 +1,3 @@ +registerPaint("notAConstructr", function() { return { + "prototype": { "paint": function() {} } +}; }); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_paint_not_callable.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_paint_not_callable.js new file mode 100644 index 00000000000..b0cfc5a7766 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_paint_not_callable.js @@ -0,0 +1,3 @@ +registerPaint("paintNotCallable", class { + get paint() { return 0; } +}); diff --git a/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_properties_throws.js b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_properties_throws.js new file mode 100644 index 00000000000..e745796f5d6 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/worklets/test_paint_worklet_properties_throws.js @@ -0,0 +1,4 @@ +registerPaint("propertiesThrows", class { + static get inputProperties() { throw new TypeError(); } + paint(ctx, size) { } +}); |