diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/paintworkletglobalscope.rs | 97 | ||||
-rw-r--r-- | components/script/dom/webidls/PaintWorkletGlobalScope.webidl | 9 | ||||
-rw-r--r-- | components/script/dom/webidls/Window.webidl | 4 | ||||
-rw-r--r-- | components/script/dom/window.rs | 17 | ||||
-rw-r--r-- | components/script/dom/worklet.rs | 52 | ||||
-rw-r--r-- | components/script/dom/workletglobalscope.rs | 14 |
7 files changed, 191 insertions, 3 deletions
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 8ab41391bb2..3ea53f2b4ca 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -391,6 +391,7 @@ pub mod node; pub mod nodeiterator; pub mod nodelist; pub mod pagetransitionevent; +pub mod paintworkletglobalscope; pub mod performance; pub mod performancetiming; pub mod permissions; diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs new file mode 100644 index 00000000000..6a4babe896d --- /dev/null +++ b/components/script/dom/paintworkletglobalscope.rs @@ -0,0 +1,97 @@ +/* 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::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::js::Root; +use dom::bindings::str::DOMString; +use dom::workletglobalscope::WorkletGlobalScope; +use dom::workletglobalscope::WorkletGlobalScopeInit; +use dom_struct::dom_struct; +use euclid::Size2D; +use ipc_channel::ipc::IpcSharedMemory; +use js::rust::Runtime; +use msg::constellation_msg::PipelineId; +use net_traits::image::base::Image; +use net_traits::image::base::PixelFormat; +use script_traits::PaintWorkletError; +use servo_atoms::Atom; +use servo_url::ServoUrl; +use std::rc::Rc; +use std::sync::mpsc::Sender; + +#[dom_struct] +/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope +pub struct PaintWorkletGlobalScope { + /// The worklet global for this object + worklet_global: WorkletGlobalScope, + /// A buffer to draw into + buffer: DOMRefCell<Vec<u8>>, +} + +impl PaintWorkletGlobalScope { + #[allow(unsafe_code)] + pub fn new(runtime: &Runtime, + pipeline_id: PipelineId, + base_url: ServoUrl, + init: &WorkletGlobalScopeInit) + -> Root<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), + buffer: Default::default(), + }; + unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) } + } + + pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) { + match task { + PaintWorkletTask::DrawAPaintImage(name, size, sender) => self.draw_a_paint_image(name, size, sender), + } + } + + 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; + 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 { + width: width, + height: height, + format: PixelFormat::RGBA8, + bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()), + id: None, + }; + let _ = sender.send(Ok(image)); + } +} + +impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { + /// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint + fn RegisterPaint(&self, name: DOMString, _paintCtor: Rc<VoidFunction>) { + debug!("Registering paint image name {}.", name); + // TODO + } +} + +/// Tasks which can be peformed by a paint worklet +pub enum PaintWorkletTask { + DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>) +} diff --git a/components/script/dom/webidls/PaintWorkletGlobalScope.webidl b/components/script/dom/webidls/PaintWorkletGlobalScope.webidl new file mode 100644 index 00000000000..1611ad23694 --- /dev/null +++ b/components/script/dom/webidls/PaintWorkletGlobalScope.webidl @@ -0,0 +1,9 @@ +/* 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/#paintworkletglobalscope +[Global=(Worklet,PaintWorklet), Exposed=PaintWorklet] +interface PaintWorkletGlobalScope : WorkletGlobalScope { + void registerPaint(DOMString name, VoidFunction paintCtor); +}; diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index c4c25399743..50808c83016 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -204,3 +204,7 @@ partial interface Window { //readonly attribute EventSender eventSender; }; +// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet +partial interface Window { + [SameObject] readonly attribute Worklet paintWorklet; +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index fc793c206c4..e627721bd05 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -51,6 +51,7 @@ use dom::storage::Storage; use dom::testrunner::TestRunner; use dom::windowproxy::WindowProxy; use dom::worklet::Worklet; +use dom::workletglobalscope::WorkletGlobalScopeType; use dom_struct::dom_struct; use euclid::{Point2D, Rect, Size2D}; use fetch; @@ -279,6 +280,8 @@ pub struct Window { /// Worklets test_worklet: MutNullableJS<Worklet>, + /// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet + paint_worklet: MutNullableJS<Worklet>, } impl Window { @@ -373,6 +376,14 @@ impl Window { self.webvr_thread.clone() } + fn new_paint_worklet(&self) -> Root<Worklet> { + debug!("Creating new paint worklet."); + let worklet = Worklet::new(self, WorkletGlobalScopeType::Paint); + let executor = Arc::new(worklet.executor()); + let _ = self.layout_chan.send(Msg::SetPaintWorkletExecutor(executor)); + worklet + } + pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> { &self.permission_state_invocation_results } @@ -1011,6 +1022,11 @@ impl WindowMethods for Window { fetch::Fetch(&self.upcast(), input, init) } + // https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet + fn PaintWorklet(&self) -> Root<Worklet> { + self.paint_worklet.or_init(|| self.new_paint_worklet()) + } + fn TestRunner(&self) -> Root<TestRunner> { self.test_runner.or_init(|| TestRunner::new(self.upcast())) } @@ -1856,6 +1872,7 @@ impl Window { pending_layout_images: DOMRefCell::new(HashMap::new()), unminified_js_dir: DOMRefCell::new(None), test_worklet: Default::default(), + paint_worklet: Default::default(), }; unsafe { diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index fa5b3950b51..da74215f752 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -10,6 +10,7 @@ //! thread pool implementation, which only performs GC or code loading on //! a backup thread, not on the primary worklet thread. +use app_units::Au; use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials; use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods; @@ -27,6 +28,7 @@ use dom::bindings::str::USVString; use dom::bindings::trace::JSTraceable; use dom::bindings::trace::RootedTraceableBox; use dom::globalscope::GlobalScope; +use dom::paintworkletglobalscope::PaintWorkletTask; use dom::promise::Promise; use dom::testworkletglobalscope::TestWorkletTask; use dom::window::Window; @@ -35,6 +37,7 @@ use dom::workletglobalscope::WorkletGlobalScopeInit; use dom::workletglobalscope::WorkletGlobalScopeType; use dom::workletglobalscope::WorkletTask; use dom_struct::dom_struct; +use euclid::Size2D; use js::jsapi::JSGCParamKey; use js::jsapi::JSTracer; use js::jsapi::JS_GC; @@ -42,6 +45,7 @@ use js::jsapi::JS_GetGCParameter; use js::rust::Runtime; use msg::constellation_msg::PipelineId; use net_traits::IpcSend; +use net_traits::image::base::Image; use net_traits::load_whole_resource; use net_traits::request::Destination; use net_traits::request::RequestInit; @@ -54,6 +58,9 @@ use script_runtime::new_rt_and_cx; use script_thread::MainThreadScriptMsg; use script_thread::Runnable; use script_thread::ScriptThread; +use script_traits::PaintWorkletError; +use script_traits::PaintWorkletExecutor; +use servo_atoms::Atom; use servo_rand; use servo_url::ImmutableOrigin; use servo_url::ServoUrl; @@ -62,12 +69,14 @@ use std::collections::HashMap; use std::collections::hash_map; use std::rc::Rc; use std::sync::Arc; +use std::sync::Mutex; use std::sync::atomic::AtomicIsize; use std::sync::atomic::Ordering; use std::sync::mpsc; use std::sync::mpsc::Receiver; use std::sync::mpsc::Sender; use std::thread; +use std::time::Duration; use style::thread_state; use swapper::Swapper; use swapper::swapper; @@ -76,6 +85,7 @@ use uuid::Uuid; // Magic numbers const WORKLET_THREAD_POOL_SIZE: u32 = 3; const MIN_GC_THRESHOLD: u32 = 1_000_000; +const PAINT_TIMEOUT_MILLISECONDS: u64 = 10; #[dom_struct] /// https://drafts.css-houdini.org/worklets/#worklet @@ -109,6 +119,13 @@ impl Worklet { pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType { self.global_type } + + pub fn executor(&self) -> WorkletExecutor { + WorkletExecutor { + worklet_id: self.worklet_id, + primary_sender: Mutex::new(ScriptThread::worklet_thread_pool().primary_sender.clone()), + } + } } impl WorkletMethods for Worklet { @@ -561,7 +578,8 @@ impl WorkletThread { // TODO: Caching. // TODO: Avoid re-parsing the origin as a URL. let resource_fetcher = self.global_init.resource_threads.sender(); - let origin_url = ServoUrl::parse(&*origin.unicode_serialization()).expect("Failed to parse origin as URL."); + let origin_url = ServoUrl::parse(&*origin.unicode_serialization()) + .unwrap_or_else(|_| ServoUrl::parse("about:blank").unwrap()); let request = RequestInit { url: script_url, type_: RequestType::Script, @@ -635,3 +653,35 @@ impl WorkletThread { self.script_sender.send(msg).expect("Worklet thread outlived script thread."); } } + +/// An executor of worklet tasks +pub struct WorkletExecutor { + worklet_id: WorkletId, + // Rather annoyingly, we have to use a mutex here because + // layout threads share their context rather than cloning it. + primary_sender: Mutex<Sender<WorkletData>>, +} + +impl WorkletExecutor { + /// Schedule a worklet task to be peformed by the worklet thread pool. + fn schedule_a_worklet_task(&self, task: WorkletTask) { + let _ = self.primary_sender.lock() + .expect("Locking the worklet channel.") + .send(WorkletData::Task(self.worklet_id, task)); + } +} + +impl PaintWorkletExecutor for WorkletExecutor { + /// 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>) + -> Result<Image, PaintWorkletError> + { + let (sender, receiver) = mpsc::channel(); + let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender)); + let timeout = Duration::from_millis(PAINT_TIMEOUT_MILLISECONDS); + self.schedule_a_worklet_task(task); + receiver.recv_timeout(timeout)? + } +} diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index a2e2463ca27..78804d66ae0 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -6,6 +6,8 @@ use devtools_traits::ScriptToDevtoolsControlMsg; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::globalscope::GlobalScope; +use dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use dom::paintworkletglobalscope::PaintWorkletTask; use dom::testworkletglobalscope::TestWorkletGlobalScope; use dom::testworkletglobalscope::TestWorkletTask; use dom_struct::dom_struct; @@ -92,6 +94,10 @@ impl WorkletGlobalScope { Some(global) => global.perform_a_worklet_task(task), None => warn!("This is not a test worklet."), }, + WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() { + Some(global) => global.perform_a_worklet_task(task), + None => warn!("This is not a paint worklet."), + }, } } } @@ -116,8 +122,10 @@ pub struct WorkletGlobalScopeInit { /// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type #[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)] pub enum WorkletGlobalScopeType { - /// https://drafts.css-houdini.org/worklets/#examples + /// A servo-specific testing worklet Test, + /// A paint worklet + Paint, } impl WorkletGlobalScopeType { @@ -132,6 +140,8 @@ impl WorkletGlobalScopeType { match *self { WorkletGlobalScopeType::Test => Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)), + WorkletGlobalScopeType::Paint => + Root::upcast(PaintWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)), } } } @@ -139,5 +149,5 @@ impl WorkletGlobalScopeType { /// A task which can be performed in the context of a worklet global. pub enum WorkletTask { Test(TestWorkletTask), + Paint(PaintWorkletTask), } - |