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 | |
parent | de331c6bc8f987521d600043285d32f42be07048 (diff) | |
download | servo-ef033b8362b143f3671863313bcd792c4bf17f45.tar.gz servo-ef033b8362b143f3671863313bcd792c4bf17f45.zip |
Implemented paint worklet properties.
29 files changed, 353 insertions, 118 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1e1b8d66c47..d1e05813c9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ dependencies = [ "selectors 0.19.0", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "servo_atoms 0.0.1", "servo_config 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", @@ -1504,6 +1505,7 @@ dependencies = [ "script_traits 0.0.1", "selectors 0.19.0", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "servo_atoms 0.0.1", "servo_config 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", @@ -2506,6 +2508,7 @@ dependencies = [ "range 0.0.1", "script_traits 0.0.1", "selectors 0.19.0", + "servo_atoms 0.0.1", "servo_url 0.0.1", "style 0.0.1", "webrender_traits 0.44.0 (git+https://github.com/servo/webrender)", diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index e3e2c76c2f4..16f82f2236f 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -34,6 +34,7 @@ script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} selectors = { path = "../selectors" } serde = "1.0" +servo_atoms = {path = "../atoms"} servo_geometry = {path = "../geometry"} serde_json = "1.0" servo_config = {path = "../config"} diff --git a/components/layout/context.rs b/components/layout/context.rs index d261fe5d8a7..cda3811421a 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -4,6 +4,7 @@ //! Data needed by the layout thread. +use fnv::FnvHashMap; use fnv::FnvHasher; use gfx::display_list::{WebRenderImageInfo, OpaqueNode}; use gfx::font_cache_thread::FontCacheThread; @@ -15,8 +16,9 @@ use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use opaque_node::OpaqueNodeMethods; use parking_lot::RwLock; use script_layout_interface::{PendingImage, PendingImageState}; -use script_traits::PaintWorkletExecutor; +use script_traits::Painter; use script_traits::UntrustedNodeAddress; +use servo_atoms::Atom; use servo_url::ServoUrl; use std::cell::{RefCell, RefMut}; use std::collections::HashMap; @@ -24,6 +26,7 @@ use std::hash::BuildHasherDefault; use std::sync::{Arc, Mutex}; use std::thread; use style::context::SharedStyleContext; +use style::properties::PropertyId; thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<FontContext>> = RefCell::new(None)); @@ -69,8 +72,8 @@ pub struct LayoutContext<'a> { WebRenderImageInfo, BuildHasherDefault<FnvHasher>>>>, - /// The executor for worklets - pub paint_worklet_executor: Option<Arc<PaintWorkletExecutor>>, + /// Paint worklets + pub registered_painters: Arc<RwLock<FnvHashMap<Atom, RegisteredPainter>>>, /// A list of in-progress image loads to be shared with the script thread. /// A None value means that this layout was not initiated by the script thread. @@ -175,3 +178,10 @@ impl<'a> LayoutContext<'a> { } } } + +/// A registered paint worklet. +pub struct RegisteredPainter { + pub name: Atom, + pub properties: FnvHashMap<Atom, PropertyId>, + pub painter: Arc<Painter>, +} diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 9aa9301eef8..32439819d9b 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1165,17 +1165,24 @@ impl FragmentDisplayListBuilding for Fragment { let size = unbordered_box.size.to_physical(style.writing_mode); let name = paint_worklet.name.clone(); - // If the script thread has not added any paint worklet modules, there is nothing to do! - let executor = match state.layout_context.paint_worklet_executor { - Some(ref executor) => executor, - None => return debug!("Worklet {} called before any paint modules are added.", name), + // Get the painter, and the computed values for its properties. + let (properties, painter) = match state.layout_context.registered_painters.read().get(&name) { + Some(registered_painter) => ( + registered_painter.properties + .iter() + .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id))) + .map(|(name, id)| (name.clone(), style.computed_value_to_string(id))) + .collect(), + registered_painter.painter.clone() + ), + None => return debug!("Worklet {} called before registration.", name), }; // 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 (sender, receiver) = ipc::channel().unwrap(); - executor.draw_a_paint_image(name, size, sender); + painter.draw_a_paint_image(size, properties, sender); // TODO: timeout let webrender_image = match receiver.recv() { diff --git a/components/layout/lib.rs b/components/layout/lib.rs index af108249c42..f7ed79be750 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -37,6 +37,7 @@ extern crate script_layout_interface; extern crate script_traits; #[macro_use] extern crate serde; extern crate serde_json; +extern crate servo_atoms; extern crate servo_config; extern crate servo_geometry; extern crate servo_url; diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index a63b37cfed5..db9c92bb823 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -31,6 +31,7 @@ script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} selectors = { path = "../selectors" } serde_json = "1.0" +servo_atoms = {path = "../atoms"} servo_config = {path = "../config"} servo_geometry = {path = "../geometry"} servo_url = {path = "../url"} diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index b063aa0bca6..a51cdb7ac1a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -33,6 +33,7 @@ extern crate script_layout_interface; extern crate script_traits; extern crate selectors; extern crate serde_json; +extern crate servo_atoms; extern crate servo_config; extern crate servo_geometry; extern crate servo_url; @@ -53,6 +54,7 @@ use ipc_channel::router::ROUTER; use layout::animation; use layout::construct::ConstructionResult; use layout::context::LayoutContext; +use layout::context::RegisteredPainter; use layout::context::heap_size_of_persistent_local_context; use layout::display_list_builder::ToGfxColor; use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; @@ -87,8 +89,8 @@ use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::wrapper_traits::LayoutNode; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{ScrollState, UntrustedNodeAddress}; -use script_traits::PaintWorkletExecutor; use selectors::Element; +use servo_atoms::Atom; use servo_config::opts; use servo_config::prefs::PREFS; use servo_config::resource_files::read_resource_file; @@ -114,6 +116,7 @@ use style::error_reporting::{NullReporter, RustLogReporter}; use style::invalidation::element::restyle_hints::RestyleHint; use style::logical_geometry::LogicalPoint; use style::media_queries::{Device, MediaList, MediaType}; +use style::properties::PropertyId; use style::selector_parser::SnapshotMap; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; @@ -123,6 +126,7 @@ use style::stylist::{ExtraStyleData, Stylist}; use style::thread_state; use style::timer::Timer; use style::traversal::{DomTraversal, TraversalDriver, TraversalFlags}; +use style::values::CompactCowStr; /// Information needed by the layout thread. pub struct LayoutThread { @@ -223,7 +227,7 @@ pub struct LayoutThread { WebRenderImageInfo>>>, /// The executor for paint worklets. /// Will be None if the script thread hasn't added any paint worklet modules. - paint_worklet_executor: Option<Arc<PaintWorkletExecutor>>, + registered_painters: Arc<RwLock<FnvHashMap<Atom, RegisteredPainter>>>, /// Webrender interface. webrender_api: webrender_traits::RenderApi, @@ -491,7 +495,7 @@ impl LayoutThread { constellation_chan: constellation_chan.clone(), time_profiler_chan: time_profiler_chan, mem_profiler_chan: mem_profiler_chan, - paint_worklet_executor: None, + registered_painters: Arc::new(RwLock::new(FnvHashMap::default())), image_cache: image_cache.clone(), font_cache_thread: font_cache_thread, first_reflow: Cell::new(true), @@ -584,7 +588,7 @@ impl LayoutThread { webrender_image_cache: self.webrender_image_cache.clone(), pending_images: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None }, newly_transitioning_nodes: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None }, - paint_worklet_executor: self.paint_worklet_executor.clone(), + registered_painters: self.registered_painters.clone(), } } @@ -700,10 +704,19 @@ impl LayoutThread { Msg::SetFinalUrl(final_url) => { self.url = final_url; }, - Msg::SetPaintWorkletExecutor(executor) => { - debug!("Setting the paint worklet executor"); - debug_assert!(self.paint_worklet_executor.is_none()); - self.paint_worklet_executor = Some(executor); + Msg::RegisterPaint(name, mut properties, painter) => { + debug!("Registering the painter"); + let properties = properties.drain(..) + .filter_map(|name| PropertyId::parse(CompactCowStr::from(&*name)).ok().map(|id| (name.clone(), id))) + .filter(|&(_, ref id)| id.as_shorthand().is_err()) + .collect(); + let registered_painter = RegisteredPainter { + name: name.clone(), + properties: properties, + painter: painter, + }; + self.registered_painters.write() + .insert(name, registered_painter); }, Msg::PrepareToExit(response_chan) => { self.prepare_to_exit(response_chan); 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)), } } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 94318472b50..641d761b373 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -79,7 +79,7 @@ use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestIni use net_traits::storage_thread::StorageType; use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; -use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType}; +use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowQueryType}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; use script_traits::{CompositorEvent, ConstellationControlMsg}; @@ -718,8 +718,8 @@ impl ScriptThread { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; script_thread.worklet_thread_pool.borrow_mut().get_or_insert_with(|| { - let chan = script_thread.chan.0.clone(); let init = WorkletGlobalScopeInit { + script_sender: script_thread.chan.0.clone(), resource_threads: script_thread.resource_threads.clone(), mem_profiler_chan: script_thread.mem_profiler_chan.clone(), time_profiler_chan: script_thread.time_profiler_chan.clone(), @@ -728,11 +728,20 @@ impl ScriptThread { scheduler_chan: script_thread.scheduler_chan.clone(), image_cache: script_thread.image_cache.clone(), }; - Rc::new(WorkletThreadPool::spawn(chan, init)) + Rc::new(WorkletThreadPool::spawn(init)) }).clone() }) } + pub fn send_to_layout(&self, pipeline_id: PipelineId, msg: Msg) { + let window = self.documents.borrow().find_window(pipeline_id); + let window = match window { + Some(window) => window, + None => return warn!("Message sent to layout after pipeline {} closed.", pipeline_id), + }; + let _ = window.layout_chan().send(msg); + } + /// Creates a new script thread. pub fn new(state: InitialScriptState, port: Receiver<MainThreadScriptMsg>, diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml index 6a74eeda086..8a4c66c9e0d 100644 --- a/components/script_layout_interface/Cargo.toml +++ b/components/script_layout_interface/Cargo.toml @@ -28,6 +28,7 @@ profile_traits = {path = "../profile_traits"} range = {path = "../range"} script_traits = {path = "../script_traits"} selectors = { path = "../selectors" } +servo_atoms = {path = "../atoms"} servo_url = {path = "../url"} style = {path = "../style"} webrender_traits = {git = "https://github.com/servo/webrender", features = ["ipc"]} diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs index 3e0a258aa7d..ebdb4a59b7a 100644 --- a/components/script_layout_interface/lib.rs +++ b/components/script_layout_interface/lib.rs @@ -30,6 +30,7 @@ extern crate profile_traits; extern crate range; extern crate script_traits; extern crate selectors; +extern crate servo_atoms; extern crate servo_url; extern crate style; extern crate webrender_traits; diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 84b93c527aa..4b1648bb6a2 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -13,7 +13,8 @@ use profile_traits::mem::ReportsChan; use rpc::LayoutRPC; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData}; -use script_traits::PaintWorkletExecutor; +use script_traits::Painter; +use servo_atoms::Atom; use servo_url::ServoUrl; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender}; @@ -86,7 +87,7 @@ pub enum Msg { UpdateScrollStateFromScript(ScrollState), /// Tells layout that script has added some paint worklet modules. - SetPaintWorkletExecutor(Arc<PaintWorkletExecutor>), + RegisterPaint(Atom, Vec<Atom>, Arc<Painter>), } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 540bf7b47fc..85189c29229 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -828,11 +828,11 @@ impl From<RecvTimeoutError> for PaintWorkletError { } /// Execute paint code in the worklet thread pool. -pub trait PaintWorkletExecutor: Sync + Send { +pub trait Painter: Sync + Send { /// 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>, + properties: Vec<(Atom, String)>, sender: IpcSender<CanvasData>); } diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs index 0d3a4b00092..1e9cae4ea08 100644 --- a/components/style/servo/restyle_damage.rs +++ b/components/style/servo/restyle_damage.rs @@ -64,16 +64,7 @@ impl ServoRestyleDamage { new: &ServoComputedValues) -> StyleDifference { let damage = compute_damage(old, new); - // If computed values for custom properties changed, we should cascade these changes to - // children (custom properties are all inherited). - // https://www.w3.org/TR/css-variables/#defining-variables - // (With Properties & Values, not all custom properties will be inherited!) - let variable_values_changed = old.get_custom_properties() != new.get_custom_properties(); - let change = if damage.is_empty() && !variable_values_changed { - StyleChange::Unchanged - } else { - StyleChange::Changed - }; + let change = if damage.is_empty() { StyleChange::Unchanged } else { StyleChange::Changed }; StyleDifference::new(damage, change) } @@ -285,6 +276,13 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Servo get_inheritedbox.visibility ]); + + // Paint worklets may depend on custom properties, + // so if they have changed we should repaint. + if old.get_custom_properties() != new.get_custom_properties() { + damage.insert(REPAINT); + } + // If the layer requirements of this flow have changed due to the value // of the transform, then reflow is required to rebuild the layers. if old.transform_requires_layer() != new.transform_requires_layer() { diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index e9a237feec4..7947a375509 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -9966,6 +9966,11 @@ {} ] ], + "mozilla/css-paint-api/resources/html5.png": [ + [ + {} + ] + ], "mozilla/css-paint-api/valid-image-after-load-ref.html": [ [ {} @@ -26158,6 +26163,10 @@ "e4b544068821f6f8882f05836a07a19891fc85b8", "reftest" ], + "mozilla/css-paint-api/resources/html5.png": [ + "83ef56b6e4d0dcb0d2dcecfb3fc78035051e8627", + "support" + ], "mozilla/css-paint-api/valid-image-after-load-ref.html": [ "b0c34ee1480fe1108fe8dc53f2bbb2f3ffa1c408", "support" diff --git a/tests/wpt/mozilla/meta/mozilla/css-paint-api/background-image-tiled.html.ini b/tests/wpt/mozilla/meta/mozilla/css-paint-api/background-image-tiled.html.ini deleted file mode 100644 index 70236851502..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/css-paint-api/background-image-tiled.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[background-image-tiled.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/17598 diff --git a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-constructor-error.html.ini b/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-constructor-error.html.ini deleted file mode 100644 index b8b8b6a43fa..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-constructor-error.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[invalid-image-constructor-error.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/17595 diff --git a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-paint-error.html.ini b/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-paint-error.html.ini deleted file mode 100644 index 12ed50a9383..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-paint-error.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[invalid-image-paint-error.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/17595 diff --git a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-pending-script.html.ini b/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-pending-script.html.ini deleted file mode 100644 index 41e6a2089d7..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-pending-script.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[invalid-image-pending-script.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/17595 diff --git a/tests/wpt/mozilla/tests/mozilla/css-paint-api/resources/html5.png b/tests/wpt/mozilla/tests/mozilla/css-paint-api/resources/html5.png Binary files differnew file mode 100644 index 00000000000..bfc98fdc062 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/css-paint-api/resources/html5.png |