From ef033b8362b143f3671863313bcd792c4bf17f45 Mon Sep 17 00:00:00 2001 From: Alan Jeffrey Date: Wed, 14 Jun 2017 18:14:54 -0500 Subject: [PATCH] Implemented paint worklet properties. --- Cargo.lock | 3 + components/layout/Cargo.toml | 1 + components/layout/context.rs | 16 +++- components/layout/display_list_builder.rs | 17 +++-- components/layout/lib.rs | 1 + components/layout_thread/Cargo.toml | 1 + components/layout_thread/lib.rs | 29 +++++-- components/script/dom/cssstylevalue.rs | 46 +++++++++++ components/script/dom/mod.rs | 2 + .../script/dom/paintworkletglobalscope.rs | 72 +++++++++++++----- .../script/dom/stylepropertymapreadonly.rs | 66 ++++++++++++++++ .../script/dom/testworkletglobalscope.rs | 4 +- .../script/dom/webidls/CSSStyleValue.webidl | 14 ++++ .../webidls/StylePropertyMapReadOnly.webidl | 15 ++++ components/script/dom/window.rs | 5 +- components/script/dom/worklet.rs | 61 +++++---------- components/script/dom/workletglobalscope.rs | 49 +++++++++++- components/script/script_thread.rs | 15 +++- components/script_layout_interface/Cargo.toml | 1 + components/script_layout_interface/lib.rs | 1 + components/script_layout_interface/message.rs | 5 +- components/script_traits/lib.rs | 4 +- components/style/servo/restyle_damage.rs | 18 ++--- tests/wpt/mozilla/meta/MANIFEST.json | 9 +++ .../background-image-tiled.html.ini | 4 - .../invalid-image-constructor-error.html.ini | 4 - .../invalid-image-paint-error.html.ini | 4 - .../invalid-image-pending-script.html.ini | 4 - .../mozilla/css-paint-api/resources/html5.png | Bin 0 -> 4144 bytes 29 files changed, 353 insertions(+), 118 deletions(-) create mode 100644 components/script/dom/cssstylevalue.rs create mode 100644 components/script/dom/stylepropertymapreadonly.rs create mode 100644 components/script/dom/webidls/CSSStyleValue.webidl create mode 100644 components/script/dom/webidls/StylePropertyMapReadOnly.webidl delete mode 100644 tests/wpt/mozilla/meta/mozilla/css-paint-api/background-image-tiled.html.ini delete mode 100644 tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-constructor-error.html.ini delete mode 100644 tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-paint-error.html.ini delete mode 100644 tests/wpt/mozilla/meta/mozilla/css-paint-api/invalid-image-pending-script.html.ini create mode 100644 tests/wpt/mozilla/tests/mozilla/css-paint-api/resources/html5.png 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> = RefCell::new(None)); @@ -69,8 +72,8 @@ pub struct LayoutContext<'a> { WebRenderImageInfo, BuildHasherDefault>>>, - /// The executor for worklets - pub paint_worklet_executor: Option>, + /// Paint worklets + pub registered_painters: Arc>>, /// 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, + pub painter: Arc, +} 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>, + registered_painters: Arc>>, /// 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 { + 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 { 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, + properties: &StylePropertyMapReadOnly, sender: IpcSender) { // 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, + properties: &StylePropertyMapReadOnly, sender: IpcSender) { 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, sender: IpcSender) { 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 = 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 { + // Rather annoyingly we have to use a mutex here to make the painter Sync. + struct WorkletPainter(Atom, Mutex); + impl Painter for WorkletPainter { + fn draw_a_paint_image(&self, + size: Size2D, + properties: Vec<(Atom, String)>, + sender: IpcSender) + { + 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 = - unsafe { get_property(cx, paint_obj.handle(), "inputProperties", StringificationBehavior::Default) }? + let mut property_names: Vec = + 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 = - unsafe { get_property(cx, paint_obj.handle(), "inputArguments", StringificationBehavior::Default) }? + let _argument_names: Vec = + 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, IpcSender) + DrawAPaintImage(Atom, Size2D, Vec<(Atom, String)>, IpcSender) } /// A paint definition @@ -314,7 +347,6 @@ struct PaintDefinition { class_constructor: Heap, paint_function: Heap, constructor_valid_flag: Cell, - input_properties: Vec, 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, alpha: bool, context: &PaintRenderingContext2D) -> Box @@ -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>, +} + +impl StylePropertyMapReadOnly { + fn new_inherited(entries: Entries) -> StylePropertyMapReadOnly where + Entries: IntoIterator)> + { + StylePropertyMapReadOnly { + reflector: Reflector::new(), + entries: entries.into_iter().collect(), + } + } + + pub fn from_iter(global: &GlobalScope, entries: Entries) -> Root where + Entries: IntoIterator, + { + 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> { + // 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 { 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? 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 getAll(DOMString property); + boolean has(DOMString property); + // iterable)>; + // sequence 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 { 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> { 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, 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, hot_backup_sender: Sender, cold_backup_sender: Sender, - script_sender: Sender, /// Data for initializing new worklet global scopes global_init: WorkletGlobalScopeInit, @@ -406,9 +391,9 @@ struct WorkletThread { control_receiver: Receiver, /// Senders + primary_sender: Sender, hot_backup_sender: Sender, cold_backup_sender: Sender, - script_sender: Sender, /// 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>, + #[ignore_heap_size_of = "channels are hard"] + primary_sender: Sender, } 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) -> 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, - sender: IpcSender) - { - 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, + /// 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(&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, 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, /// 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 { 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, 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), + RegisterPaint(Atom, Vec, Arc), } 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 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, + properties: Vec<(Atom, String)>, sender: IpcSender); } 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 new file mode 100644 index 0000000000000000000000000000000000000000..bfc98fdc062eae555e8c6e001a515a969273f779 GIT binary patch literal 4144 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 literal 0 HcmV?d00001