diff options
author | Alan Jeffrey <ajeffrey@mozilla.com> | 2017-06-14 18:14:54 -0500 |
---|---|---|
committer | Alan Jeffrey <ajeffrey@mozilla.com> | 2017-07-11 17:28:56 -0500 |
commit | ef033b8362b143f3671863313bcd792c4bf17f45 (patch) | |
tree | 24da8ea60c03dc7d8ed07a675dbff79135a07ba8 /components/script/dom | |
parent | de331c6bc8f987521d600043285d32f42be07048 (diff) | |
download | servo-ef033b8362b143f3671863313bcd792c4bf17f45.tar.gz servo-ef033b8362b143f3671863313bcd792c4bf17f45.zip |
Implemented paint worklet properties.
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/cssstylevalue.rs | 46 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/paintworkletglobalscope.rs | 72 | ||||
-rw-r--r-- | components/script/dom/stylepropertymapreadonly.rs | 66 | ||||
-rw-r--r-- | components/script/dom/testworkletglobalscope.rs | 4 | ||||
-rw-r--r-- | components/script/dom/webidls/CSSStyleValue.webidl | 14 | ||||
-rw-r--r-- | components/script/dom/webidls/StylePropertyMapReadOnly.webidl | 15 | ||||
-rw-r--r-- | components/script/dom/window.rs | 5 | ||||
-rw-r--r-- | components/script/dom/worklet.rs | 61 | ||||
-rw-r--r-- | components/script/dom/workletglobalscope.rs | 49 |
10 files changed, 265 insertions, 69 deletions
diff --git a/components/script/dom/cssstylevalue.rs b/components/script/dom/cssstylevalue.rs new file mode 100644 index 00000000000..7ee9d5509f0 --- /dev/null +++ b/components/script/dom/cssstylevalue.rs @@ -0,0 +1,46 @@ +/* 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::CSSStyleValueBinding::CSSStyleValueMethods; +use dom::bindings::codegen::Bindings::CSSStyleValueBinding::Wrap; +use dom::bindings::js::Root; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::DOMString; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct CSSStyleValue { + reflector: Reflector, + value: String, +} + +impl CSSStyleValue { + fn new_inherited(value: String) -> CSSStyleValue { + CSSStyleValue { + reflector: Reflector::new(), + value: value, + } + } + + pub fn new(global: &GlobalScope, value: String) -> Root<CSSStyleValue> { + reflect_dom_object(box CSSStyleValue::new_inherited(value), global, Wrap) + } +} + +impl CSSStyleValueMethods for CSSStyleValue { + /// https://drafts.css-houdini.org/css-typed-om-1/#CSSStyleValue-stringification-behavior + fn Stringifier(&self) -> DOMString { + DOMString::from(&*self.value) + } + + /// This attribute is no longer part of the `CSSStyleValue` interface, + /// but is still used in some examples. + /// https://github.com/GoogleChrome/houdini-samples/issues/16 + // check-tidy: no specs after this line + fn CssText(&self) -> DOMString { + self.Stringifier() + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 1a2a3fc7171..fe17bb712d7 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -253,6 +253,7 @@ pub mod cssrulelist; pub mod cssstyledeclaration; pub mod cssstylerule; pub mod cssstylesheet; +pub mod cssstylevalue; pub mod csssupportsrule; pub mod cssviewportrule; pub mod customelementregistry; @@ -418,6 +419,7 @@ pub mod serviceworkerregistration; pub mod servoparser; pub mod storage; pub mod storageevent; +pub mod stylepropertymapreadonly; pub mod stylesheet; pub mod stylesheetlist; pub mod svgelement; diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index 5ac021491ad..95f885cd662 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -10,19 +10,22 @@ 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::inheritance::Castable; use dom::bindings::js::JS; 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::stylepropertymapreadonly::StylePropertyMapReadOnly; +use dom::worklet::WorkletExecutor; use dom::workletglobalscope::WorkletGlobalScope; use dom::workletglobalscope::WorkletGlobalScopeInit; +use dom::workletglobalscope::WorkletTask; use dom_struct::dom_struct; use euclid::Size2D; use ipc_channel::ipc::IpcSender; @@ -45,6 +48,8 @@ use msg::constellation_msg::PipelineId; use net_traits::image::base::Image; use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageCache; +use script_layout_interface::message::Msg; +use script_traits::Painter; use servo_atoms::Atom; use servo_url::ServoUrl; use std::cell::Cell; @@ -53,6 +58,7 @@ use std::collections::hash_map::Entry; use std::ptr::null_mut; use std::rc::Rc; use std::sync::Arc; +use std::sync::Mutex; /// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope #[dom_struct] @@ -73,11 +79,12 @@ impl PaintWorkletGlobalScope { pub fn new(runtime: &Runtime, pipeline_id: PipelineId, base_url: ServoUrl, + executor: WorkletExecutor, 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), + worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, executor, init), image_cache: init.image_cache.clone(), paint_definitions: Default::default(), paint_class_instances: Default::default(), @@ -87,7 +94,10 @@ impl PaintWorkletGlobalScope { pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) { match task { - PaintWorkletTask::DrawAPaintImage(name, size, sender) => self.draw_a_paint_image(name, size, sender), + PaintWorkletTask::DrawAPaintImage(name, size, properties, sender) => { + let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties); + self.draw_a_paint_image(name, size, &*properties, sender); + } } } @@ -95,10 +105,11 @@ impl PaintWorkletGlobalScope { fn draw_a_paint_image(&self, name: Atom, size: Size2D<Au>, + properties: &StylePropertyMapReadOnly, sender: IpcSender<CanvasData>) { // TODO: document paint definitions. - self.invoke_a_paint_callback(name, size, sender); + self.invoke_a_paint_callback(name, size, properties, sender); } /// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback @@ -106,6 +117,7 @@ impl PaintWorkletGlobalScope { fn invoke_a_paint_callback(&self, name: Atom, size: Size2D<Au>, + properties: &StylePropertyMapReadOnly, sender: IpcSender<CanvasData>) { let width = size.width.to_px().abs() as u32; @@ -179,8 +191,10 @@ impl PaintWorkletGlobalScope { let args_slice = [ ObjectValue(rendering_context.reflector().get_jsobject().get()), ObjectValue(paint_size.reflector().get_jsobject().get()), + ObjectValue(properties.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()); } @@ -194,12 +208,13 @@ impl PaintWorkletGlobalScope { rendering_context.send_data(sender); } + // https://drafts.csswg.org/css-images-4/#invalid-image fn send_invalid_image(&self, size: Size2D<Au>, sender: IpcSender<CanvasData>) { debug!("Sending an invalid image."); let width = size.width.to_px().abs() as u32; let height = size.height.to_px().abs() as u32; let len = (width as usize) * (height as usize) * 4; - let pixel = [0xFF, 0x00, 0x00, 0xFF]; + let pixel = [0x00, 0x00, 0x00, 0x00]; let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect(); let mut image = Image { width: width, @@ -214,6 +229,24 @@ impl PaintWorkletGlobalScope { let canvas_data = CanvasData::Image(image_data); let _ = sender.send(canvas_data); } + + fn painter(&self, name: Atom) -> Arc<Painter> { + // Rather annoyingly we have to use a mutex here to make the painter Sync. + struct WorkletPainter(Atom, Mutex<WorkletExecutor>); + impl Painter for WorkletPainter { + fn draw_a_paint_image(&self, + size: Size2D<Au>, + properties: Vec<(Atom, String)>, + sender: IpcSender<CanvasData>) + { + let name = self.0.clone(); + let task = PaintWorkletTask::DrawAPaintImage(name, size, properties, sender); + self.1.lock().expect("Locking a painter.") + .schedule_a_worklet_task(WorkletTask::Paint(task)); + } + } + Arc::new(WorkletPainter(name, Mutex::new(self.worklet_global.executor()))) + } } impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { @@ -239,27 +272,22 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { } // Step 4-6. - debug!("Getting input properties."); - let input_properties: Vec<DOMString> = - unsafe { get_property(cx, paint_obj.handle(), "inputProperties", StringificationBehavior::Default) }? + let mut property_names: Vec<String> = + unsafe { get_property(cx, paint_obj.handle(), "inputProperties", ()) }? .unwrap_or_default(); - debug!("Got {:?}.", input_properties); + let properties = property_names.drain(..).map(Atom::from).collect(); // Step 7-9. - debug!("Getting input arguments."); - let input_arguments: Vec<DOMString> = - unsafe { get_property(cx, paint_obj.handle(), "inputArguments", StringificationBehavior::Default) }? + let _argument_names: Vec<String> = + unsafe { get_property(cx, paint_obj.handle(), "inputArguments", ()) }? .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()) } { @@ -285,23 +313,28 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope { let context = PaintRenderingContext2D::new(self); let definition = PaintDefinition::new(paint_val.handle(), paint_function.handle(), - input_properties, alpha, &*context); // Step 20. debug!("Registering definition {}.", name); - self.paint_definitions.borrow_mut().insert(name, definition); + self.paint_definitions.borrow_mut().insert(name.clone(), definition); // TODO: Step 21. + // Inform layout that there is a registered paint worklet. + // TODO: layout will end up getting this message multiple times. + let painter = self.painter(name.clone()); + let msg = Msg::RegisterPaint(name, properties, painter); + self.worklet_global.send_to_layout(msg); + Ok(()) } } /// Tasks which can be peformed by a paint worklet pub enum PaintWorkletTask { - DrawAPaintImage(Atom, Size2D<Au>, IpcSender<CanvasData>) + DrawAPaintImage(Atom, Size2D<Au>, Vec<(Atom, String)>, IpcSender<CanvasData>) } /// A paint definition @@ -314,7 +347,6 @@ struct PaintDefinition { class_constructor: Heap<JSVal>, paint_function: Heap<JSVal>, constructor_valid_flag: Cell<bool>, - input_properties: Vec<DOMString>, context_alpha_flag: bool, // TODO: the spec calls for fresh rendering contexts each time a paint image is drawn, // but to avoid having the primary worklet thread create a new renering context, @@ -325,7 +357,6 @@ struct PaintDefinition { impl PaintDefinition { fn new(class_constructor: HandleValue, paint_function: HandleValue, - input_properties: Vec<DOMString>, alpha: bool, context: &PaintRenderingContext2D) -> Box<PaintDefinition> @@ -334,7 +365,6 @@ impl PaintDefinition { class_constructor: Heap::default(), paint_function: Heap::default(), constructor_valid_flag: Cell::new(true), - input_properties: input_properties, context_alpha_flag: alpha, context: JS::from_ref(context), }); diff --git a/components/script/dom/stylepropertymapreadonly.rs b/components/script/dom/stylepropertymapreadonly.rs new file mode 100644 index 00000000000..323f5dae5a9 --- /dev/null +++ b/components/script/dom/stylepropertymapreadonly.rs @@ -0,0 +1,66 @@ +/* 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::StylePropertyMapReadOnlyBinding::StylePropertyMapReadOnlyMethods; +use dom::bindings::codegen::Bindings::StylePropertyMapReadOnlyBinding::Wrap; +use dom::bindings::js::JS; +use dom::bindings::js::Root; +use dom::bindings::reflector::Reflector; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::DOMString; +use dom::cssstylevalue::CSSStyleValue; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use servo_atoms::Atom; +use std::collections::HashMap; +use std::iter::Iterator; + +#[dom_struct] +pub struct StylePropertyMapReadOnly { + reflector: Reflector, + entries: HashMap<Atom, JS<CSSStyleValue>>, +} + +impl StylePropertyMapReadOnly { + fn new_inherited<Entries>(entries: Entries) -> StylePropertyMapReadOnly where + Entries: IntoIterator<Item=(Atom, JS<CSSStyleValue>)> + { + StylePropertyMapReadOnly { + reflector: Reflector::new(), + entries: entries.into_iter().collect(), + } + } + + pub fn from_iter<Entries>(global: &GlobalScope, entries: Entries) -> Root<StylePropertyMapReadOnly> where + Entries: IntoIterator<Item=(Atom, String)>, + { + let mut keys = Vec::new(); + rooted_vec!(let mut values); + let iter = entries.into_iter(); + let (lo, _) = iter.size_hint(); + keys.reserve(lo); + values.reserve(lo); + for (key, value) in iter { + let value = CSSStyleValue::new(global, value); + keys.push(key); + values.push(JS::from_ref(&*value)); + } + let iter = keys.drain(..).zip(values.iter().cloned()); + reflect_dom_object(box StylePropertyMapReadOnly::new_inherited(iter), global, Wrap) + } +} + +impl StylePropertyMapReadOnlyMethods for StylePropertyMapReadOnly { + /// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-get + fn Get(&self, property: DOMString) -> Option<Root<CSSStyleValue>> { + // TODO: avoid constructing an Atom + self.entries.get(&Atom::from(property)).map(|value| Root::from_ref(&**value)) + } + + /// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-has + fn Has(&self, property: DOMString) -> bool { + // TODO: avoid constructing an Atom + self.entries.contains_key(&Atom::from(property)) + } +} diff --git a/components/script/dom/testworkletglobalscope.rs b/components/script/dom/testworkletglobalscope.rs index dfd000ac5c1..5c9641d5fd2 100644 --- a/components/script/dom/testworkletglobalscope.rs +++ b/components/script/dom/testworkletglobalscope.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding; use dom::bindings::codegen::Bindings::TestWorkletGlobalScopeBinding::TestWorkletGlobalScopeMethods; use dom::bindings::js::Root; use dom::bindings::str::DOMString; +use dom::worklet::WorkletExecutor; use dom::workletglobalscope::WorkletGlobalScope; use dom::workletglobalscope::WorkletGlobalScopeInit; use dom_struct::dom_struct; @@ -31,12 +32,13 @@ impl TestWorkletGlobalScope { pub fn new(runtime: &Runtime, pipeline_id: PipelineId, base_url: ServoUrl, + executor: WorkletExecutor, init: &WorkletGlobalScopeInit) -> Root<TestWorkletGlobalScope> { debug!("Creating test worklet global scope for pipeline {}.", pipeline_id); let global = box TestWorkletGlobalScope { - worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init), + worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, executor, init), lookup_table: Default::default(), }; unsafe { TestWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) } diff --git a/components/script/dom/webidls/CSSStyleValue.webidl b/components/script/dom/webidls/CSSStyleValue.webidl new file mode 100644 index 00000000000..7f9ab308ff1 --- /dev/null +++ b/components/script/dom/webidls/CSSStyleValue.webidl @@ -0,0 +1,14 @@ +/* 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-typed-om-1/#cssstylevalue +// NOTE: should this be exposed to Window? +[Exposed=(Worklet)] +interface CSSStyleValue { + stringifier; + // static CSSStyleValue? parse(DOMString property, DOMString cssText); + // static sequence<CSSStyleValue>? parseAll(DOMString property, DOMString cssText); + // This is a deprecated property, it's not in the spec any more but is used in houdini-samples + readonly attribute DOMString cssText; +}; diff --git a/components/script/dom/webidls/StylePropertyMapReadOnly.webidl b/components/script/dom/webidls/StylePropertyMapReadOnly.webidl new file mode 100644 index 00000000000..6ff50ac4be9 --- /dev/null +++ b/components/script/dom/webidls/StylePropertyMapReadOnly.webidl @@ -0,0 +1,15 @@ +/* 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-typed-om-1/#stylepropertymapreadonly +// NOTE: should this be exposed to Window? +[Exposed=(Worklet)] +interface StylePropertyMapReadOnly { + CSSStyleValue? get(DOMString property); + // sequence<CSSStyleValue> getAll(DOMString property); + boolean has(DOMString property); + // iterable<DOMString, (CSSStyleValue or sequence<CSSStyleValue>)>; + // sequence<DOMString> getProperties(); + // stringifier; +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 59c3c678fb3..89877cd780c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -386,10 +386,7 @@ impl Window { 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 + Worklet::new(self, WorkletGlobalScopeType::Paint) } pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> { diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index bbcc777ab11..a034ec00f47 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -10,8 +10,6 @@ //! 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 canvas_traits::CanvasData; use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials; use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods; @@ -29,7 +27,6 @@ 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; @@ -38,8 +35,6 @@ use dom::workletglobalscope::WorkletGlobalScopeInit; use dom::workletglobalscope::WorkletGlobalScopeType; use dom::workletglobalscope::WorkletTask; use dom_struct::dom_struct; -use euclid::Size2D; -use ipc_channel::ipc::IpcSender; use js::jsapi::JSGCParamKey; use js::jsapi::JSTracer; use js::jsapi::JS_GC; @@ -59,8 +54,6 @@ use script_runtime::new_rt_and_cx; use script_thread::MainThreadScriptMsg; use script_thread::Runnable; use script_thread::ScriptThread; -use script_traits::PaintWorkletExecutor; -use servo_atoms::Atom; use servo_rand; use servo_url::ImmutableOrigin; use servo_url::ServoUrl; @@ -69,7 +62,6 @@ 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; @@ -117,13 +109,6 @@ 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 { @@ -268,7 +253,7 @@ impl Drop for WorkletThreadPool { impl WorkletThreadPool { /// Create a new thread pool and spawn the threads. /// When the thread pool is dropped, the threads will be asked to quit. - pub fn spawn(script_sender: Sender<MainThreadScriptMsg>, global_init: WorkletGlobalScopeInit) -> WorkletThreadPool { + pub fn spawn(global_init: WorkletGlobalScopeInit) -> WorkletThreadPool { let primary_role = WorkletThreadRole::new(false, false); let hot_backup_role = WorkletThreadRole::new(true, false); let cold_backup_role = WorkletThreadRole::new(false, true); @@ -276,9 +261,9 @@ impl WorkletThreadPool { let hot_backup_sender = hot_backup_role.sender.clone(); let cold_backup_sender = cold_backup_role.sender.clone(); let init = WorkletThreadInit { + primary_sender: primary_sender.clone(), hot_backup_sender: hot_backup_sender.clone(), cold_backup_sender: cold_backup_sender.clone(), - script_sender: script_sender.clone(), global_init: global_init, }; WorkletThreadPool { @@ -388,9 +373,9 @@ impl WorkletThreadRole { #[derive(Clone)] struct WorkletThreadInit { /// Senders + primary_sender: Sender<WorkletData>, hot_backup_sender: Sender<WorkletData>, cold_backup_sender: Sender<WorkletData>, - script_sender: Sender<MainThreadScriptMsg>, /// Data for initializing new worklet global scopes global_init: WorkletGlobalScopeInit, @@ -406,9 +391,9 @@ struct WorkletThread { control_receiver: Receiver<WorkletControl>, /// Senders + primary_sender: Sender<WorkletData>, hot_backup_sender: Sender<WorkletData>, cold_backup_sender: Sender<WorkletData>, - script_sender: Sender<MainThreadScriptMsg>, /// Data for initializing new worklet global scopes global_init: WorkletGlobalScopeInit, @@ -451,9 +436,9 @@ impl WorkletThread { let mut thread = RootedTraceableBox::new(WorkletThread { role: role, control_receiver: control_receiver, + primary_sender: init.primary_sender, hot_backup_sender: init.hot_backup_sender, cold_backup_sender: init.cold_backup_sender, - script_sender: init.script_sender, global_init: init.global_init, global_scopes: HashMap::new(), control_buffer: None, @@ -562,7 +547,8 @@ impl WorkletThread { hash_map::Entry::Occupied(entry) => Root::from_ref(entry.get()), hash_map::Entry::Vacant(entry) => { debug!("Creating new worklet global scope."); - let result = global_type.new(&self.runtime, pipeline_id, base_url, &self.global_init); + let executor = WorkletExecutor::new(worklet_id, self.primary_sender.clone()); + let result = global_type.new(&self.runtime, pipeline_id, base_url, executor, &self.global_init); entry.insert(JS::from_ref(&*result)); result }, @@ -626,7 +612,7 @@ impl WorkletThread { if old_counter == 1 { debug!("Resolving promise."); let msg = MainThreadScriptMsg::WorkletLoaded(pipeline_id); - self.script_sender.send(msg).expect("Worklet thread outlived script thread."); + self.global_init.script_sender.send(msg).expect("Worklet thread outlived script thread."); self.run_in_script_thread(promise.resolve_runnable(())); } } @@ -668,35 +654,28 @@ impl WorkletThread { { let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::WorkletEvent, box runnable); let msg = MainThreadScriptMsg::Common(msg); - self.script_sender.send(msg).expect("Worklet thread outlived script thread."); + self.global_init.script_sender.send(msg).expect("Worklet thread outlived script thread."); } } /// An executor of worklet tasks +#[derive(Clone, JSTraceable, HeapSizeOf)] 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>>, + #[ignore_heap_size_of = "channels are hard"] + primary_sender: 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)); + fn new(worklet_id: WorkletId, primary_sender: Sender<WorkletData>) -> WorkletExecutor { + WorkletExecutor { + worklet_id: worklet_id, + primary_sender: primary_sender, + } } -} -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>, - sender: IpcSender<CanvasData>) - { - let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender)); - self.schedule_a_worklet_task(task); + /// Schedule a worklet task to be peformed by the worklet thread pool. + pub fn schedule_a_worklet_task(&self, task: WorkletTask) { + let _ = self.primary_sender.send(WorkletData::Task(self.worklet_id, task)); } } diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index bbe3c47e3f8..d8b5fdac22d 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -10,6 +10,7 @@ use dom::paintworkletglobalscope::PaintWorkletGlobalScope; use dom::paintworkletglobalscope::PaintWorkletTask; use dom::testworkletglobalscope::TestWorkletGlobalScope; use dom::testworkletglobalscope::TestWorkletTask; +use dom::worklet::WorkletExecutor; use dom_struct::dom_struct; use ipc_channel::ipc; use ipc_channel::ipc::IpcSender; @@ -23,12 +24,19 @@ use net_traits::ResourceThreads; use net_traits::image_cache::ImageCache; use profile_traits::mem; use profile_traits::time; +use script_layout_interface::message::Msg; +use script_runtime::CommonScriptMsg; +use script_runtime::ScriptThreadEventCategory; +use script_thread::MainThreadScriptMsg; +use script_thread::Runnable; +use script_thread::ScriptThread; use script_traits::ScriptMsg; use script_traits::TimerSchedulerMsg; use servo_url::ImmutableOrigin; use servo_url::MutableOrigin; use servo_url::ServoUrl; use std::sync::Arc; +use std::sync::mpsc::Sender; #[dom_struct] /// https://drafts.css-houdini.org/worklets/#workletglobalscope @@ -39,12 +47,18 @@ pub struct WorkletGlobalScope { base_url: ServoUrl, /// The microtask queue for this worklet microtask_queue: MicrotaskQueue, + /// Sender back to the script thread + #[ignore_heap_size_of = "channels are hard"] + script_sender: Sender<MainThreadScriptMsg>, + /// Worklet task executor + executor: WorkletExecutor, } impl WorkletGlobalScope { /// Create a new stack-allocated `WorkletGlobalScope`. pub fn new_inherited(pipeline_id: PipelineId, base_url: ServoUrl, + executor: WorkletExecutor, init: &WorkletGlobalScopeInit) -> WorkletGlobalScope { // Any timer events fired on this global are ignored. @@ -61,6 +75,8 @@ impl WorkletGlobalScope { MutableOrigin::new(ImmutableOrigin::new_opaque())), base_url: base_url, microtask_queue: MicrotaskQueue::default(), + script_sender: init.script_sender.clone(), + executor: executor, } } @@ -76,11 +92,37 @@ impl WorkletGlobalScope { self.globalscope.evaluate_js_on_global_with_result(&*script, rval.handle_mut()) } + /// Run a runnable in the main script thread. + pub fn run_in_script_thread<R>(&self, runnable: R) where + R: 'static + Send + Runnable, + { + let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::WorkletEvent, box runnable); + let msg = MainThreadScriptMsg::Common(msg); + self.script_sender.send(msg).expect("Worklet thread outlived script thread."); + } + + /// Send a message to layout. + pub fn send_to_layout(&self, msg: Msg) { + struct RunnableMsg(PipelineId, Msg); + impl Runnable for RunnableMsg { + fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) { + script_thread.send_to_layout(self.0, self.1); + } + } + let pipeline_id = self.globalscope.pipeline_id(); + self.run_in_script_thread(RunnableMsg(pipeline_id, msg)); + } + /// The base URL of this global. pub fn base_url(&self) -> ServoUrl { self.base_url.clone() } + /// The worklet executor. + pub fn executor(&self) -> WorkletExecutor { + self.executor.clone() + } + /// Queue up a microtask to be executed in this global. pub fn enqueue_microtask(&self, job: Microtask) { self.microtask_queue.enqueue(job); @@ -113,6 +155,8 @@ impl WorkletGlobalScope { /// Resources required by workletglobalscopes #[derive(Clone)] pub struct WorkletGlobalScopeInit { + /// Channel to the main script thread + pub script_sender: Sender<MainThreadScriptMsg>, /// Channel to a resource thread pub resource_threads: ResourceThreads, /// Channel to the memory profiler @@ -144,14 +188,15 @@ impl WorkletGlobalScopeType { runtime: &Runtime, pipeline_id: PipelineId, base_url: ServoUrl, + executor: WorkletExecutor, init: &WorkletGlobalScopeInit) -> Root<WorkletGlobalScope> { match *self { WorkletGlobalScopeType::Test => - Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)), + Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, executor, init)), WorkletGlobalScopeType::Paint => - Root::upcast(PaintWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)), + Root::upcast(PaintWorkletGlobalScope::new(runtime, pipeline_id, base_url, executor, init)), } } } |