mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Implemented the plumbing for paint worklets.
This commit is contained in:
parent
7e273d6c9b
commit
fd17dcd604
23 changed files with 600 additions and 156 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2484,6 +2484,7 @@ dependencies = [
|
||||||
name = "script_traits"
|
name = "script_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"app_units 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bluetooth_traits 0.0.1",
|
"bluetooth_traits 0.0.1",
|
||||||
"canvas_traits 0.0.1",
|
"canvas_traits 0.0.1",
|
||||||
"cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -2503,6 +2504,7 @@ dependencies = [
|
||||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"servo_atoms 0.0.1",
|
||||||
"servo_url 0.0.1",
|
"servo_url 0.0.1",
|
||||||
"style_traits 0.0.1",
|
"style_traits 0.0.1",
|
||||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -15,6 +15,7 @@ use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
|
||||||
use opaque_node::OpaqueNodeMethods;
|
use opaque_node::OpaqueNodeMethods;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use script_layout_interface::{PendingImage, PendingImageState};
|
use script_layout_interface::{PendingImage, PendingImageState};
|
||||||
|
use script_traits::PaintWorkletExecutor;
|
||||||
use script_traits::UntrustedNodeAddress;
|
use script_traits::UntrustedNodeAddress;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::borrow::{Borrow, BorrowMut};
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
@ -95,6 +96,9 @@ pub struct LayoutContext<'a> {
|
||||||
WebRenderImageInfo,
|
WebRenderImageInfo,
|
||||||
BuildHasherDefault<FnvHasher>>>>,
|
BuildHasherDefault<FnvHasher>>>>,
|
||||||
|
|
||||||
|
/// The executor for worklets
|
||||||
|
pub paint_worklet_executor: Option<Arc<PaintWorkletExecutor>>,
|
||||||
|
|
||||||
/// A list of in-progress image loads to be shared with the script thread.
|
/// 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.
|
/// A None value means that this layout was not initiated by the script thread.
|
||||||
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
|
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
|
||||||
|
|
|
@ -64,6 +64,7 @@ use style::values::generics::background::BackgroundSize;
|
||||||
use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
||||||
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
|
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
|
||||||
use style::values::generics::image::{Image, ShapeExtent};
|
use style::values::generics::image::{Image, ShapeExtent};
|
||||||
|
use style::values::generics::image::PaintWorklet;
|
||||||
use style::values::specified::position::{X, Y};
|
use style::values::specified::position::{X, Y};
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use style_traits::cursor::Cursor;
|
use style_traits::cursor::Cursor;
|
||||||
|
@ -395,6 +396,28 @@ pub trait FragmentDisplayListBuilding {
|
||||||
image_url: &ServoUrl,
|
image_url: &ServoUrl,
|
||||||
background_index: usize);
|
background_index: usize);
|
||||||
|
|
||||||
|
/// Adds the display items necessary to paint a webrender image of this fragment to the
|
||||||
|
/// appropriate section of the display list.
|
||||||
|
fn build_display_list_for_webrender_image(&self,
|
||||||
|
state: &mut DisplayListBuildState,
|
||||||
|
style: &ServoComputedValues,
|
||||||
|
display_list_section: DisplayListSection,
|
||||||
|
absolute_bounds: &Rect<Au>,
|
||||||
|
clip: &ClippingRegion,
|
||||||
|
webrender_image: WebRenderImageInfo,
|
||||||
|
index: usize);
|
||||||
|
|
||||||
|
/// Adds the display items necessary to paint the background image created by this fragment's
|
||||||
|
/// worklet to the appropriate section of the display list.
|
||||||
|
fn build_display_list_for_background_paint_worklet(&self,
|
||||||
|
state: &mut DisplayListBuildState,
|
||||||
|
style: &ServoComputedValues,
|
||||||
|
display_list_section: DisplayListSection,
|
||||||
|
absolute_bounds: &Rect<Au>,
|
||||||
|
clip: &ClippingRegion,
|
||||||
|
paint_worklet: &PaintWorklet,
|
||||||
|
index: usize);
|
||||||
|
|
||||||
fn convert_linear_gradient(&self,
|
fn convert_linear_gradient(&self,
|
||||||
bounds: &Rect<Au>,
|
bounds: &Rect<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
|
@ -893,6 +916,15 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
i);
|
i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Either::Second(Image::PaintWorklet(ref paint_worklet)) => {
|
||||||
|
self.build_display_list_for_background_paint_worklet(state,
|
||||||
|
style,
|
||||||
|
display_list_section,
|
||||||
|
&bounds,
|
||||||
|
&clip,
|
||||||
|
paint_worklet,
|
||||||
|
i);
|
||||||
|
}
|
||||||
Either::Second(Image::Rect(_)) => {
|
Either::Second(Image::Rect(_)) => {
|
||||||
// TODO: Implement `-moz-image-rect`
|
// TODO: Implement `-moz-image-rect`
|
||||||
}
|
}
|
||||||
|
@ -956,146 +988,206 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
clip: &ClippingRegion,
|
clip: &ClippingRegion,
|
||||||
image_url: &ServoUrl,
|
image_url: &ServoUrl,
|
||||||
index: usize) {
|
index: usize) {
|
||||||
let background = style.get_background();
|
|
||||||
let webrender_image = state.layout_context
|
let webrender_image = state.layout_context
|
||||||
.get_webrender_image_for_url(self.node,
|
.get_webrender_image_for_url(self.node,
|
||||||
image_url.clone(),
|
image_url.clone(),
|
||||||
UsePlaceholder::No);
|
UsePlaceholder::No);
|
||||||
|
|
||||||
if let Some(webrender_image) = webrender_image {
|
if let Some(webrender_image) = webrender_image {
|
||||||
debug!("(building display list) building background image");
|
self.build_display_list_for_webrender_image(state,
|
||||||
|
style,
|
||||||
// Use `background-size` to get the size.
|
display_list_section,
|
||||||
let mut bounds = *absolute_bounds;
|
absolute_bounds,
|
||||||
let image_size = self.compute_background_image_size(style, &bounds,
|
clip,
|
||||||
&webrender_image, index);
|
webrender_image,
|
||||||
|
index);
|
||||||
// Clip.
|
|
||||||
//
|
|
||||||
// TODO: Check the bounds to see if a clip item is actually required.
|
|
||||||
let mut clip = clip.clone();
|
|
||||||
clip.intersect_rect(&bounds);
|
|
||||||
|
|
||||||
// Background image should be positioned on the padding box basis.
|
|
||||||
let border = style.logical_border_width().to_physical(style.writing_mode);
|
|
||||||
|
|
||||||
// Use 'background-origin' to get the origin value.
|
|
||||||
let origin = get_cyclic(&background.background_origin.0, index);
|
|
||||||
let (mut origin_x, mut origin_y) = match *origin {
|
|
||||||
background_origin::single_value::T::padding_box => {
|
|
||||||
(Au(0), Au(0))
|
|
||||||
}
|
|
||||||
background_origin::single_value::T::border_box => {
|
|
||||||
(-border.left, -border.top)
|
|
||||||
}
|
|
||||||
background_origin::single_value::T::content_box => {
|
|
||||||
let border_padding = self.border_padding.to_physical(self.style.writing_mode);
|
|
||||||
(border_padding.left - border.left, border_padding.top - border.top)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use `background-attachment` to get the initial virtual origin
|
|
||||||
let attachment = get_cyclic(&background.background_attachment.0, index);
|
|
||||||
let (virtual_origin_x, virtual_origin_y) = match *attachment {
|
|
||||||
background_attachment::single_value::T::scroll => {
|
|
||||||
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
|
||||||
}
|
|
||||||
background_attachment::single_value::T::fixed => {
|
|
||||||
// If the ‘background-attachment’ value for this image is ‘fixed’, then
|
|
||||||
// 'background-origin' has no effect.
|
|
||||||
origin_x = Au(0);
|
|
||||||
origin_y = Au(0);
|
|
||||||
(Au(0), Au(0))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let horiz_position = *get_cyclic(&background.background_position_x.0, index);
|
|
||||||
let vert_position = *get_cyclic(&background.background_position_y.0, index);
|
|
||||||
// Use `background-position` to get the offset.
|
|
||||||
let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width);
|
|
||||||
let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height);
|
|
||||||
|
|
||||||
// The anchor position for this background, based on both the background-attachment
|
|
||||||
// and background-position properties.
|
|
||||||
let anchor_origin_x = border.left + virtual_origin_x + origin_x + horizontal_position;
|
|
||||||
let anchor_origin_y = border.top + virtual_origin_y + origin_y + vertical_position;
|
|
||||||
|
|
||||||
let mut tile_spacing = Size2D::zero();
|
|
||||||
let mut stretch_size = image_size;
|
|
||||||
|
|
||||||
// Adjust origin and size based on background-repeat
|
|
||||||
let background_repeat = get_cyclic(&background.background_repeat.0, index);
|
|
||||||
match background_repeat.0 {
|
|
||||||
background_repeat::single_value::RepeatKeyword::NoRepeat => {
|
|
||||||
bounds.origin.x = anchor_origin_x;
|
|
||||||
bounds.size.width = image_size.width;
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Repeat => {
|
|
||||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
|
||||||
&mut bounds.size.width,
|
|
||||||
anchor_origin_x,
|
|
||||||
image_size.width);
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Space => {
|
|
||||||
ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.x,
|
|
||||||
&mut bounds.size.width,
|
|
||||||
&mut tile_spacing.width,
|
|
||||||
anchor_origin_x,
|
|
||||||
image_size.width);
|
|
||||||
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Round => {
|
|
||||||
ImageFragmentInfo::tile_image_round(&mut bounds.origin.x,
|
|
||||||
&mut bounds.size.width,
|
|
||||||
anchor_origin_x,
|
|
||||||
&mut stretch_size.width);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match background_repeat.1 {
|
|
||||||
background_repeat::single_value::RepeatKeyword::NoRepeat => {
|
|
||||||
bounds.origin.y = anchor_origin_y;
|
|
||||||
bounds.size.height = image_size.height;
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Repeat => {
|
|
||||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
|
||||||
&mut bounds.size.height,
|
|
||||||
anchor_origin_y,
|
|
||||||
image_size.height);
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Space => {
|
|
||||||
ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.y,
|
|
||||||
&mut bounds.size.height,
|
|
||||||
&mut tile_spacing.height,
|
|
||||||
anchor_origin_y,
|
|
||||||
image_size.height);
|
|
||||||
|
|
||||||
}
|
|
||||||
background_repeat::single_value::RepeatKeyword::Round => {
|
|
||||||
ImageFragmentInfo::tile_image_round(&mut bounds.origin.y,
|
|
||||||
&mut bounds.size.height,
|
|
||||||
anchor_origin_y,
|
|
||||||
&mut stretch_size.height);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the image display item.
|
|
||||||
let base = state.create_base_display_item(&bounds,
|
|
||||||
&clip,
|
|
||||||
self.node,
|
|
||||||
style.get_cursor(Cursor::Default),
|
|
||||||
display_list_section);
|
|
||||||
state.add_display_item(DisplayItem::Image(box ImageDisplayItem {
|
|
||||||
base: base,
|
|
||||||
webrender_image: webrender_image,
|
|
||||||
image_data: None,
|
|
||||||
stretch_size: stretch_size,
|
|
||||||
tile_spacing: tile_spacing,
|
|
||||||
image_rendering: style.get_inheritedbox().image_rendering.clone(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_display_list_for_webrender_image(&self,
|
||||||
|
state: &mut DisplayListBuildState,
|
||||||
|
style: &ServoComputedValues,
|
||||||
|
display_list_section: DisplayListSection,
|
||||||
|
absolute_bounds: &Rect<Au>,
|
||||||
|
clip: &ClippingRegion,
|
||||||
|
webrender_image: WebRenderImageInfo,
|
||||||
|
index: usize) {
|
||||||
|
debug!("(building display list) building background image");
|
||||||
|
let background = style.get_background();
|
||||||
|
|
||||||
|
// Use `background-size` to get the size.
|
||||||
|
let mut bounds = *absolute_bounds;
|
||||||
|
let image_size = self.compute_background_image_size(style, &bounds,
|
||||||
|
&webrender_image, index);
|
||||||
|
|
||||||
|
// Clip.
|
||||||
|
//
|
||||||
|
// TODO: Check the bounds to see if a clip item is actually required.
|
||||||
|
let mut clip = clip.clone();
|
||||||
|
clip.intersect_rect(&bounds);
|
||||||
|
|
||||||
|
// Background image should be positioned on the padding box basis.
|
||||||
|
let border = style.logical_border_width().to_physical(style.writing_mode);
|
||||||
|
|
||||||
|
// Use 'background-origin' to get the origin value.
|
||||||
|
let origin = get_cyclic(&background.background_origin.0, index);
|
||||||
|
let (mut origin_x, mut origin_y) = match *origin {
|
||||||
|
background_origin::single_value::T::padding_box => {
|
||||||
|
(Au(0), Au(0))
|
||||||
|
}
|
||||||
|
background_origin::single_value::T::border_box => {
|
||||||
|
(-border.left, -border.top)
|
||||||
|
}
|
||||||
|
background_origin::single_value::T::content_box => {
|
||||||
|
let border_padding = self.border_padding.to_physical(self.style.writing_mode);
|
||||||
|
(border_padding.left - border.left, border_padding.top - border.top)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use `background-attachment` to get the initial virtual origin
|
||||||
|
let attachment = get_cyclic(&background.background_attachment.0, index);
|
||||||
|
let (virtual_origin_x, virtual_origin_y) = match *attachment {
|
||||||
|
background_attachment::single_value::T::scroll => {
|
||||||
|
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
||||||
|
}
|
||||||
|
background_attachment::single_value::T::fixed => {
|
||||||
|
// If the ‘background-attachment’ value for this image is ‘fixed’, then
|
||||||
|
// 'background-origin' has no effect.
|
||||||
|
origin_x = Au(0);
|
||||||
|
origin_y = Au(0);
|
||||||
|
(Au(0), Au(0))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let horiz_position = *get_cyclic(&background.background_position_x.0, index);
|
||||||
|
let vert_position = *get_cyclic(&background.background_position_y.0, index);
|
||||||
|
// Use `background-position` to get the offset.
|
||||||
|
let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width);
|
||||||
|
let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height);
|
||||||
|
|
||||||
|
// The anchor position for this background, based on both the background-attachment
|
||||||
|
// and background-position properties.
|
||||||
|
let anchor_origin_x = border.left + virtual_origin_x + origin_x + horizontal_position;
|
||||||
|
let anchor_origin_y = border.top + virtual_origin_y + origin_y + vertical_position;
|
||||||
|
|
||||||
|
let mut tile_spacing = Size2D::zero();
|
||||||
|
let mut stretch_size = image_size;
|
||||||
|
|
||||||
|
// Adjust origin and size based on background-repeat
|
||||||
|
let background_repeat = get_cyclic(&background.background_repeat.0, index);
|
||||||
|
match background_repeat.0 {
|
||||||
|
background_repeat::single_value::RepeatKeyword::NoRepeat => {
|
||||||
|
bounds.origin.x = anchor_origin_x;
|
||||||
|
bounds.size.width = image_size.width;
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Repeat => {
|
||||||
|
ImageFragmentInfo::tile_image(&mut bounds.origin.x,
|
||||||
|
&mut bounds.size.width,
|
||||||
|
anchor_origin_x,
|
||||||
|
image_size.width);
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Space => {
|
||||||
|
ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.x,
|
||||||
|
&mut bounds.size.width,
|
||||||
|
&mut tile_spacing.width,
|
||||||
|
anchor_origin_x,
|
||||||
|
image_size.width);
|
||||||
|
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Round => {
|
||||||
|
ImageFragmentInfo::tile_image_round(&mut bounds.origin.x,
|
||||||
|
&mut bounds.size.width,
|
||||||
|
anchor_origin_x,
|
||||||
|
&mut stretch_size.width);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match background_repeat.1 {
|
||||||
|
background_repeat::single_value::RepeatKeyword::NoRepeat => {
|
||||||
|
bounds.origin.y = anchor_origin_y;
|
||||||
|
bounds.size.height = image_size.height;
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Repeat => {
|
||||||
|
ImageFragmentInfo::tile_image(&mut bounds.origin.y,
|
||||||
|
&mut bounds.size.height,
|
||||||
|
anchor_origin_y,
|
||||||
|
image_size.height);
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Space => {
|
||||||
|
ImageFragmentInfo::tile_image_spaced(&mut bounds.origin.y,
|
||||||
|
&mut bounds.size.height,
|
||||||
|
&mut tile_spacing.height,
|
||||||
|
anchor_origin_y,
|
||||||
|
image_size.height);
|
||||||
|
|
||||||
|
}
|
||||||
|
background_repeat::single_value::RepeatKeyword::Round => {
|
||||||
|
ImageFragmentInfo::tile_image_round(&mut bounds.origin.y,
|
||||||
|
&mut bounds.size.height,
|
||||||
|
anchor_origin_y,
|
||||||
|
&mut stretch_size.height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the image display item.
|
||||||
|
let base = state.create_base_display_item(&bounds,
|
||||||
|
&clip,
|
||||||
|
self.node,
|
||||||
|
style.get_cursor(Cursor::Default),
|
||||||
|
display_list_section);
|
||||||
|
|
||||||
|
debug!("(building display list) adding background image.");
|
||||||
|
state.add_display_item(DisplayItem::Image(box ImageDisplayItem {
|
||||||
|
base: base,
|
||||||
|
webrender_image: webrender_image,
|
||||||
|
image_data: None,
|
||||||
|
stretch_size: stretch_size,
|
||||||
|
tile_spacing: tile_spacing,
|
||||||
|
image_rendering: style.get_inheritedbox().image_rendering.clone(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_display_list_for_background_paint_worklet(&self,
|
||||||
|
state: &mut DisplayListBuildState,
|
||||||
|
style: &ServoComputedValues,
|
||||||
|
display_list_section: DisplayListSection,
|
||||||
|
absolute_bounds: &Rect<Au>,
|
||||||
|
clip: &ClippingRegion,
|
||||||
|
paint_worklet: &PaintWorklet,
|
||||||
|
index: usize)
|
||||||
|
{
|
||||||
|
// TODO: check that this is the servo equivalent of "concrete object size".
|
||||||
|
// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||||
|
// https://drafts.csswg.org/css-images-3/#concrete-object-size
|
||||||
|
let size = self.content_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),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: add a one-place cache to avoid drawing the paint image every time.
|
||||||
|
debug!("Drawing a paint image {}({},{}).", name, size.width.to_px(), size.height.to_px());
|
||||||
|
let mut image = match executor.draw_a_paint_image(name, size) {
|
||||||
|
Ok(image) => image,
|
||||||
|
Err(err) => return warn!("Error running paint worklet ({:?}).", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure the image has a webrender key.
|
||||||
|
state.layout_context.image_cache.set_webrender_image_key(&mut image);
|
||||||
|
|
||||||
|
debug!("Drew a paint image ({},{}).", image.width, image.height);
|
||||||
|
self.build_display_list_for_webrender_image(state,
|
||||||
|
style,
|
||||||
|
display_list_section,
|
||||||
|
absolute_bounds,
|
||||||
|
clip,
|
||||||
|
WebRenderImageInfo::from_image(&image),
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_linear_gradient(&self,
|
fn convert_linear_gradient(&self,
|
||||||
bounds: &Rect<Au>,
|
bounds: &Rect<Au>,
|
||||||
stops: &[GradientItem],
|
stops: &[GradientItem],
|
||||||
|
@ -1402,6 +1494,9 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Either::Second(Image::PaintWorklet(..)) => {
|
||||||
|
// TODO: Handle border-image with `paint()`.
|
||||||
|
}
|
||||||
Either::Second(Image::Rect(..)) => {
|
Either::Second(Image::Rect(..)) => {
|
||||||
// TODO: Handle border-image with `-moz-image-rect`.
|
// TODO: Handle border-image with `-moz-image-rect`.
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ use script_layout_interface::rpc::TextIndexResponse;
|
||||||
use script_layout_interface::wrapper_traits::LayoutNode;
|
use script_layout_interface::wrapper_traits::LayoutNode;
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
||||||
use script_traits::{ScrollState, UntrustedNodeAddress};
|
use script_traits::{ScrollState, UntrustedNodeAddress};
|
||||||
|
use script_traits::PaintWorkletExecutor;
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
use servo_config::prefs::PREFS;
|
use servo_config::prefs::PREFS;
|
||||||
|
@ -225,6 +226,9 @@ pub struct LayoutThread {
|
||||||
|
|
||||||
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder),
|
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder),
|
||||||
WebRenderImageInfo>>>,
|
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>>,
|
||||||
|
|
||||||
/// Webrender interface.
|
/// Webrender interface.
|
||||||
webrender_api: webrender_traits::RenderApi,
|
webrender_api: webrender_traits::RenderApi,
|
||||||
|
@ -477,6 +481,7 @@ impl LayoutThread {
|
||||||
constellation_chan: constellation_chan.clone(),
|
constellation_chan: constellation_chan.clone(),
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
mem_profiler_chan: mem_profiler_chan,
|
mem_profiler_chan: mem_profiler_chan,
|
||||||
|
paint_worklet_executor: None,
|
||||||
image_cache: image_cache.clone(),
|
image_cache: image_cache.clone(),
|
||||||
font_cache_thread: font_cache_thread,
|
font_cache_thread: font_cache_thread,
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
|
@ -574,6 +579,7 @@ impl LayoutThread {
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
|
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 },
|
newly_transitioning_nodes: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
|
||||||
|
paint_worklet_executor: self.paint_worklet_executor.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,6 +695,11 @@ impl LayoutThread {
|
||||||
Msg::SetFinalUrl(final_url) => {
|
Msg::SetFinalUrl(final_url) => {
|
||||||
self.url = 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::PrepareToExit(response_chan) => {
|
Msg::PrepareToExit(response_chan) => {
|
||||||
self.prepare_to_exit(response_chan);
|
self.prepare_to_exit(response_chan);
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -57,9 +57,18 @@ fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi, path: &Pat
|
||||||
let mut image_data = vec![];
|
let mut image_data = vec![];
|
||||||
try!(file.read_to_end(&mut image_data));
|
try!(file.read_to_end(&mut image_data));
|
||||||
let mut image = load_from_memory(&image_data).unwrap();
|
let mut image = load_from_memory(&image_data).unwrap();
|
||||||
|
set_webrender_image_key(webrender_api, &mut image);
|
||||||
|
Ok(Arc::new(image))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_webrender_image_key(webrender_api: &webrender_traits::RenderApi, image: &mut Image) {
|
||||||
|
if image.id.is_some() { return; }
|
||||||
let format = convert_format(image.format);
|
let format = convert_format(image.format);
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
bytes.extend_from_slice(&*image.bytes);
|
bytes.extend_from_slice(&*image.bytes);
|
||||||
|
if format == webrender_traits::ImageFormat::RGBA8 {
|
||||||
|
premultiply(bytes.as_mut_slice());
|
||||||
|
}
|
||||||
let descriptor = webrender_traits::ImageDescriptor {
|
let descriptor = webrender_traits::ImageDescriptor {
|
||||||
width: image.width,
|
width: image.width,
|
||||||
height: image.height,
|
height: image.height,
|
||||||
|
@ -72,7 +81,6 @@ fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi, path: &Pat
|
||||||
let image_key = webrender_api.generate_image_key();
|
let image_key = webrender_api.generate_image_key();
|
||||||
webrender_api.add_image(image_key, descriptor, data, None);
|
webrender_api.add_image(image_key, descriptor, data, None);
|
||||||
image.id = Some(image_key);
|
image.id = Some(image_key);
|
||||||
Ok(Arc::new(image))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gw): This is a port of the old is_image_opaque code from WR.
|
// TODO(gw): This is a port of the old is_image_opaque code from WR.
|
||||||
|
@ -338,26 +346,7 @@ impl ImageCacheStore {
|
||||||
};
|
};
|
||||||
|
|
||||||
match load_result {
|
match load_result {
|
||||||
LoadResult::Loaded(ref mut image) => {
|
LoadResult::Loaded(ref mut image) => set_webrender_image_key(&self.webrender_api, image),
|
||||||
let format = convert_format(image.format);
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
bytes.extend_from_slice(&*image.bytes);
|
|
||||||
if format == webrender_traits::ImageFormat::RGBA8 {
|
|
||||||
premultiply(bytes.as_mut_slice());
|
|
||||||
}
|
|
||||||
let descriptor = webrender_traits::ImageDescriptor {
|
|
||||||
width: image.width,
|
|
||||||
height: image.height,
|
|
||||||
stride: None,
|
|
||||||
format: format,
|
|
||||||
offset: 0,
|
|
||||||
is_opaque: is_image_opaque(format, &bytes),
|
|
||||||
};
|
|
||||||
let data = webrender_traits::ImageData::new(bytes);
|
|
||||||
let image_key = self.webrender_api.generate_image_key();
|
|
||||||
self.webrender_api.add_image(image_key, descriptor, data, None);
|
|
||||||
image.id = Some(image_key);
|
|
||||||
}
|
|
||||||
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
|
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,4 +565,9 @@ impl ImageCache for ImageCacheImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure an image has a webrender key.
|
||||||
|
fn set_webrender_image_key(&self, image: &mut Image) {
|
||||||
|
set_webrender_image_key(&self.store.lock().unwrap().webrender_api, image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,4 +118,7 @@ pub trait ImageCache: Sync + Send {
|
||||||
|
|
||||||
/// Inform the image cache about a response for a pending request.
|
/// Inform the image cache about a response for a pending request.
|
||||||
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);
|
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);
|
||||||
|
|
||||||
|
/// Ensure an image has a webrender key.
|
||||||
|
fn set_webrender_image_key(&self, image: &mut Image);
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,6 +391,7 @@ pub mod node;
|
||||||
pub mod nodeiterator;
|
pub mod nodeiterator;
|
||||||
pub mod nodelist;
|
pub mod nodelist;
|
||||||
pub mod pagetransitionevent;
|
pub mod pagetransitionevent;
|
||||||
|
pub mod paintworkletglobalscope;
|
||||||
pub mod performance;
|
pub mod performance;
|
||||||
pub mod performancetiming;
|
pub mod performancetiming;
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
|
|
97
components/script/dom/paintworkletglobalscope.rs
Normal file
97
components/script/dom/paintworkletglobalscope.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
|
use dom::bindings::cell::DOMRefCell;
|
||||||
|
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding::PaintWorkletGlobalScopeMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use euclid::Size2D;
|
||||||
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
|
use js::rust::Runtime;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use net_traits::image::base::Image;
|
||||||
|
use net_traits::image::base::PixelFormat;
|
||||||
|
use script_traits::PaintWorkletError;
|
||||||
|
use servo_atoms::Atom;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
|
||||||
|
pub struct PaintWorkletGlobalScope {
|
||||||
|
/// The worklet global for this object
|
||||||
|
worklet_global: WorkletGlobalScope,
|
||||||
|
/// A buffer to draw into
|
||||||
|
buffer: DOMRefCell<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintWorkletGlobalScope {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn new(runtime: &Runtime,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
base_url: ServoUrl,
|
||||||
|
init: &WorkletGlobalScopeInit)
|
||||||
|
-> Root<PaintWorkletGlobalScope> {
|
||||||
|
debug!("Creating paint worklet global scope for pipeline {}.", pipeline_id);
|
||||||
|
let global = box PaintWorkletGlobalScope {
|
||||||
|
worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init),
|
||||||
|
buffer: Default::default(),
|
||||||
|
};
|
||||||
|
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
|
||||||
|
match task {
|
||||||
|
PaintWorkletTask::DrawAPaintImage(name, size, sender) => self.draw_a_paint_image(name, size, sender),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_a_paint_image(&self,
|
||||||
|
name: Atom,
|
||||||
|
concrete_object_size: Size2D<Au>,
|
||||||
|
sender: Sender<Result<Image, PaintWorkletError>>) {
|
||||||
|
let width = concrete_object_size.width.to_px().abs() as u32;
|
||||||
|
let height = concrete_object_size.height.to_px().abs() as u32;
|
||||||
|
let area = (width as usize) * (height as usize);
|
||||||
|
let old_buffer_size = self.buffer.borrow().len();
|
||||||
|
let new_buffer_size = area * 4;
|
||||||
|
debug!("Drawing a paint image {}({},{}).", name, width, height);
|
||||||
|
// TODO: call into script to create the image.
|
||||||
|
// For now, we just build a dummy.
|
||||||
|
if new_buffer_size > old_buffer_size {
|
||||||
|
let pixel = [0xFF, 0x00, 0x00, 0xFF];
|
||||||
|
self.buffer.borrow_mut().extend(pixel.iter().cycle().take(new_buffer_size - old_buffer_size));
|
||||||
|
} else {
|
||||||
|
self.buffer.borrow_mut().truncate(new_buffer_size);
|
||||||
|
}
|
||||||
|
let image = Image {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
format: PixelFormat::RGBA8,
|
||||||
|
bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()),
|
||||||
|
id: None,
|
||||||
|
};
|
||||||
|
let _ = sender.send(Ok(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint
|
||||||
|
fn RegisterPaint(&self, name: DOMString, _paintCtor: Rc<VoidFunction>) {
|
||||||
|
debug!("Registering paint image name {}.", name);
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tasks which can be peformed by a paint worklet
|
||||||
|
pub enum PaintWorkletTask {
|
||||||
|
DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
|
||||||
|
[Global=(Worklet,PaintWorklet), Exposed=PaintWorklet]
|
||||||
|
interface PaintWorkletGlobalScope : WorkletGlobalScope {
|
||||||
|
void registerPaint(DOMString name, VoidFunction paintCtor);
|
||||||
|
};
|
|
@ -204,3 +204,7 @@ partial interface Window {
|
||||||
//readonly attribute EventSender eventSender;
|
//readonly attribute EventSender eventSender;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
|
||||||
|
partial interface Window {
|
||||||
|
[SameObject] readonly attribute Worklet paintWorklet;
|
||||||
|
};
|
||||||
|
|
|
@ -51,6 +51,7 @@ use dom::storage::Storage;
|
||||||
use dom::testrunner::TestRunner;
|
use dom::testrunner::TestRunner;
|
||||||
use dom::windowproxy::WindowProxy;
|
use dom::windowproxy::WindowProxy;
|
||||||
use dom::worklet::Worklet;
|
use dom::worklet::Worklet;
|
||||||
|
use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use fetch;
|
use fetch;
|
||||||
|
@ -279,6 +280,8 @@ pub struct Window {
|
||||||
|
|
||||||
/// Worklets
|
/// Worklets
|
||||||
test_worklet: MutNullableJS<Worklet>,
|
test_worklet: MutNullableJS<Worklet>,
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
|
||||||
|
paint_worklet: MutNullableJS<Worklet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -373,6 +376,14 @@ impl Window {
|
||||||
self.webvr_thread.clone()
|
self.webvr_thread.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_paint_worklet(&self) -> Root<Worklet> {
|
||||||
|
debug!("Creating new paint worklet.");
|
||||||
|
let worklet = Worklet::new(self, WorkletGlobalScopeType::Paint);
|
||||||
|
let executor = Arc::new(worklet.executor());
|
||||||
|
let _ = self.layout_chan.send(Msg::SetPaintWorkletExecutor(executor));
|
||||||
|
worklet
|
||||||
|
}
|
||||||
|
|
||||||
pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> {
|
pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> {
|
||||||
&self.permission_state_invocation_results
|
&self.permission_state_invocation_results
|
||||||
}
|
}
|
||||||
|
@ -1011,6 +1022,11 @@ impl WindowMethods for Window {
|
||||||
fetch::Fetch(&self.upcast(), input, init)
|
fetch::Fetch(&self.upcast(), input, init)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
|
||||||
|
fn PaintWorklet(&self) -> Root<Worklet> {
|
||||||
|
self.paint_worklet.or_init(|| self.new_paint_worklet())
|
||||||
|
}
|
||||||
|
|
||||||
fn TestRunner(&self) -> Root<TestRunner> {
|
fn TestRunner(&self) -> Root<TestRunner> {
|
||||||
self.test_runner.or_init(|| TestRunner::new(self.upcast()))
|
self.test_runner.or_init(|| TestRunner::new(self.upcast()))
|
||||||
}
|
}
|
||||||
|
@ -1856,6 +1872,7 @@ impl Window {
|
||||||
pending_layout_images: DOMRefCell::new(HashMap::new()),
|
pending_layout_images: DOMRefCell::new(HashMap::new()),
|
||||||
unminified_js_dir: DOMRefCell::new(None),
|
unminified_js_dir: DOMRefCell::new(None),
|
||||||
test_worklet: Default::default(),
|
test_worklet: Default::default(),
|
||||||
|
paint_worklet: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//! thread pool implementation, which only performs GC or code loading on
|
//! thread pool implementation, which only performs GC or code loading on
|
||||||
//! a backup thread, not on the primary worklet thread.
|
//! a backup thread, not on the primary worklet thread.
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
||||||
|
@ -27,6 +28,7 @@ use dom::bindings::str::USVString;
|
||||||
use dom::bindings::trace::JSTraceable;
|
use dom::bindings::trace::JSTraceable;
|
||||||
use dom::bindings::trace::RootedTraceableBox;
|
use dom::bindings::trace::RootedTraceableBox;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
|
use dom::paintworkletglobalscope::PaintWorkletTask;
|
||||||
use dom::promise::Promise;
|
use dom::promise::Promise;
|
||||||
use dom::testworkletglobalscope::TestWorkletTask;
|
use dom::testworkletglobalscope::TestWorkletTask;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
@ -35,6 +37,7 @@ use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
use dom::workletglobalscope::WorkletGlobalScopeType;
|
use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||||
use dom::workletglobalscope::WorkletTask;
|
use dom::workletglobalscope::WorkletTask;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use euclid::Size2D;
|
||||||
use js::jsapi::JSGCParamKey;
|
use js::jsapi::JSGCParamKey;
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
use js::jsapi::JS_GC;
|
use js::jsapi::JS_GC;
|
||||||
|
@ -42,6 +45,7 @@ use js::jsapi::JS_GetGCParameter;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
|
use net_traits::image::base::Image;
|
||||||
use net_traits::load_whole_resource;
|
use net_traits::load_whole_resource;
|
||||||
use net_traits::request::Destination;
|
use net_traits::request::Destination;
|
||||||
use net_traits::request::RequestInit;
|
use net_traits::request::RequestInit;
|
||||||
|
@ -54,6 +58,9 @@ use script_runtime::new_rt_and_cx;
|
||||||
use script_thread::MainThreadScriptMsg;
|
use script_thread::MainThreadScriptMsg;
|
||||||
use script_thread::Runnable;
|
use script_thread::Runnable;
|
||||||
use script_thread::ScriptThread;
|
use script_thread::ScriptThread;
|
||||||
|
use script_traits::PaintWorkletError;
|
||||||
|
use script_traits::PaintWorkletExecutor;
|
||||||
|
use servo_atoms::Atom;
|
||||||
use servo_rand;
|
use servo_rand;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -62,12 +69,14 @@ use std::collections::HashMap;
|
||||||
use std::collections::hash_map;
|
use std::collections::hash_map;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::sync::atomic::AtomicIsize;
|
use std::sync::atomic::AtomicIsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use swapper::Swapper;
|
use swapper::Swapper;
|
||||||
use swapper::swapper;
|
use swapper::swapper;
|
||||||
|
@ -76,6 +85,7 @@ use uuid::Uuid;
|
||||||
// Magic numbers
|
// Magic numbers
|
||||||
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
||||||
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
||||||
|
const PAINT_TIMEOUT_MILLISECONDS: u64 = 10;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
/// https://drafts.css-houdini.org/worklets/#worklet
|
/// https://drafts.css-houdini.org/worklets/#worklet
|
||||||
|
@ -109,6 +119,13 @@ impl Worklet {
|
||||||
pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType {
|
pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType {
|
||||||
self.global_type
|
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 {
|
impl WorkletMethods for Worklet {
|
||||||
|
@ -561,7 +578,8 @@ impl WorkletThread {
|
||||||
// TODO: Caching.
|
// TODO: Caching.
|
||||||
// TODO: Avoid re-parsing the origin as a URL.
|
// TODO: Avoid re-parsing the origin as a URL.
|
||||||
let resource_fetcher = self.global_init.resource_threads.sender();
|
let resource_fetcher = self.global_init.resource_threads.sender();
|
||||||
let origin_url = ServoUrl::parse(&*origin.unicode_serialization()).expect("Failed to parse origin as URL.");
|
let origin_url = ServoUrl::parse(&*origin.unicode_serialization())
|
||||||
|
.unwrap_or_else(|_| ServoUrl::parse("about:blank").unwrap());
|
||||||
let request = RequestInit {
|
let request = RequestInit {
|
||||||
url: script_url,
|
url: script_url,
|
||||||
type_: RequestType::Script,
|
type_: RequestType::Script,
|
||||||
|
@ -635,3 +653,35 @@ impl WorkletThread {
|
||||||
self.script_sender.send(msg).expect("Worklet thread outlived script thread.");
|
self.script_sender.send(msg).expect("Worklet thread outlived script thread.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An executor of worklet tasks
|
||||||
|
pub struct WorkletExecutor {
|
||||||
|
worklet_id: WorkletId,
|
||||||
|
// Rather annoyingly, we have to use a mutex here because
|
||||||
|
// layout threads share their context rather than cloning it.
|
||||||
|
primary_sender: Mutex<Sender<WorkletData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkletExecutor {
|
||||||
|
/// Schedule a worklet task to be peformed by the worklet thread pool.
|
||||||
|
fn schedule_a_worklet_task(&self, task: WorkletTask) {
|
||||||
|
let _ = self.primary_sender.lock()
|
||||||
|
.expect("Locking the worklet channel.")
|
||||||
|
.send(WorkletData::Task(self.worklet_id, task));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaintWorkletExecutor for WorkletExecutor {
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||||
|
fn draw_a_paint_image(&self,
|
||||||
|
name: Atom,
|
||||||
|
concrete_object_size: Size2D<Au>)
|
||||||
|
-> Result<Image, PaintWorkletError>
|
||||||
|
{
|
||||||
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender));
|
||||||
|
let timeout = Duration::from_millis(PAINT_TIMEOUT_MILLISECONDS);
|
||||||
|
self.schedule_a_worklet_task(task);
|
||||||
|
receiver.recv_timeout(timeout)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
|
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||||
|
use dom::paintworkletglobalscope::PaintWorkletTask;
|
||||||
use dom::testworkletglobalscope::TestWorkletGlobalScope;
|
use dom::testworkletglobalscope::TestWorkletGlobalScope;
|
||||||
use dom::testworkletglobalscope::TestWorkletTask;
|
use dom::testworkletglobalscope::TestWorkletTask;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
@ -92,6 +94,10 @@ impl WorkletGlobalScope {
|
||||||
Some(global) => global.perform_a_worklet_task(task),
|
Some(global) => global.perform_a_worklet_task(task),
|
||||||
None => warn!("This is not a test worklet."),
|
None => warn!("This is not a test worklet."),
|
||||||
},
|
},
|
||||||
|
WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() {
|
||||||
|
Some(global) => global.perform_a_worklet_task(task),
|
||||||
|
None => warn!("This is not a paint worklet."),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,8 +122,10 @@ pub struct WorkletGlobalScopeInit {
|
||||||
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
||||||
#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)]
|
#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)]
|
||||||
pub enum WorkletGlobalScopeType {
|
pub enum WorkletGlobalScopeType {
|
||||||
/// https://drafts.css-houdini.org/worklets/#examples
|
/// A servo-specific testing worklet
|
||||||
Test,
|
Test,
|
||||||
|
/// A paint worklet
|
||||||
|
Paint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkletGlobalScopeType {
|
impl WorkletGlobalScopeType {
|
||||||
|
@ -132,6 +140,8 @@ impl WorkletGlobalScopeType {
|
||||||
match *self {
|
match *self {
|
||||||
WorkletGlobalScopeType::Test =>
|
WorkletGlobalScopeType::Test =>
|
||||||
Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
||||||
|
WorkletGlobalScopeType::Paint =>
|
||||||
|
Root::upcast(PaintWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,5 +149,5 @@ impl WorkletGlobalScopeType {
|
||||||
/// A task which can be performed in the context of a worklet global.
|
/// A task which can be performed in the context of a worklet global.
|
||||||
pub enum WorkletTask {
|
pub enum WorkletTask {
|
||||||
Test(TestWorkletTask),
|
Test(TestWorkletTask),
|
||||||
|
Paint(PaintWorkletTask),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use profile_traits::mem::ReportsChan;
|
||||||
use rpc::LayoutRPC;
|
use rpc::LayoutRPC;
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
|
||||||
use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData};
|
use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData};
|
||||||
|
use script_traits::PaintWorkletExecutor;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
|
@ -84,6 +85,9 @@ pub enum Msg {
|
||||||
/// Tells layout about a single new scrolling offset from the script. The rest will
|
/// Tells layout about a single new scrolling offset from the script. The rest will
|
||||||
/// remain untouched and layout won't forward this back to script.
|
/// remain untouched and layout won't forward this back to script.
|
||||||
UpdateScrollStateFromScript(ScrollState),
|
UpdateScrollStateFromScript(ScrollState),
|
||||||
|
|
||||||
|
/// Tells layout that script has added some paint worklet modules.
|
||||||
|
SetPaintWorkletExecutor(Arc<PaintWorkletExecutor>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ name = "script_traits"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
app_units = "0.4"
|
||||||
bluetooth_traits = {path = "../bluetooth_traits"}
|
bluetooth_traits = {path = "../bluetooth_traits"}
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
cookie = "0.6"
|
cookie = "0.6"
|
||||||
|
@ -29,6 +30,7 @@ profile_traits = {path = "../profile_traits"}
|
||||||
rustc-serialize = "0.3.4"
|
rustc-serialize = "0.3.4"
|
||||||
serde = "0.9"
|
serde = "0.9"
|
||||||
serde_derive = "0.9"
|
serde_derive = "0.9"
|
||||||
|
servo_atoms = {path = "../atoms"}
|
||||||
servo_url = {path = "../url"}
|
servo_url = {path = "../url"}
|
||||||
style_traits = {path = "../style_traits", features = ["servo"]}
|
style_traits = {path = "../style_traits", features = ["servo"]}
|
||||||
time = "0.1.12"
|
time = "0.1.12"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
|
extern crate app_units;
|
||||||
extern crate bluetooth_traits;
|
extern crate bluetooth_traits;
|
||||||
extern crate canvas_traits;
|
extern crate canvas_traits;
|
||||||
extern crate cookie as cookie_rs;
|
extern crate cookie as cookie_rs;
|
||||||
|
@ -30,6 +31,7 @@ extern crate rustc_serialize;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
extern crate servo_atoms;
|
||||||
extern crate servo_url;
|
extern crate servo_url;
|
||||||
extern crate style_traits;
|
extern crate style_traits;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
@ -39,6 +41,7 @@ extern crate webvr_traits;
|
||||||
mod script_msg;
|
mod script_msg;
|
||||||
pub mod webdriver_msg;
|
pub mod webdriver_msg;
|
||||||
|
|
||||||
|
use app_units::Au;
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
|
@ -63,12 +66,13 @@ use net_traits::storage_thread::StorageType;
|
||||||
use profile_traits::mem;
|
use profile_traits::mem;
|
||||||
use profile_traits::time as profile_time;
|
use profile_traits::time as profile_time;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use servo_atoms::Atom;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender, RecvTimeoutError};
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||||
use webrender_traits::ClipId;
|
use webrender_traits::ClipId;
|
||||||
|
@ -813,3 +817,28 @@ pub struct WorkerScriptLoadOrigin {
|
||||||
/// the pipeline id of the entity requesting the load
|
/// the pipeline id of the entity requesting the load
|
||||||
pub pipeline_id: Option<PipelineId>,
|
pub pipeline_id: Option<PipelineId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors from executing a paint worklet
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum PaintWorkletError {
|
||||||
|
/// Execution timed out.
|
||||||
|
Timeout,
|
||||||
|
/// No such worklet.
|
||||||
|
WorkletNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RecvTimeoutError> for PaintWorkletError {
|
||||||
|
fn from(_: RecvTimeoutError) -> PaintWorkletError {
|
||||||
|
PaintWorkletError::Timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute paint code in the worklet thread pool.<
|
||||||
|
pub trait PaintWorkletExecutor: 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>)
|
||||||
|
-> Result<Image, PaintWorkletError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,10 @@ pub enum Image<Gradient, ImageRect> {
|
||||||
Rect(ImageRect),
|
Rect(ImageRect),
|
||||||
/// A `-moz-element(# <element-id>)`
|
/// A `-moz-element(# <element-id>)`
|
||||||
Element(Atom),
|
Element(Atom),
|
||||||
|
/// A paint worklet image.
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api/
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
PaintWorklet(PaintWorklet),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A CSS gradient.
|
/// A CSS gradient.
|
||||||
|
@ -128,6 +132,23 @@ pub struct ColorStop<Color, LengthOrPercentage> {
|
||||||
pub position: Option<LengthOrPercentage>,
|
pub position: Option<LengthOrPercentage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specified values for a paint worklet.
|
||||||
|
/// https://drafts.css-houdini.org/css-paint-api/
|
||||||
|
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct PaintWorklet {
|
||||||
|
/// The name the worklet was registered with.
|
||||||
|
pub name: Atom,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for PaintWorklet {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
dest.write_str("paint(")?;
|
||||||
|
serialize_identifier(&*self.name.to_string(), dest)?;
|
||||||
|
dest.write_str(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Values for `moz-image-rect`.
|
/// Values for `moz-image-rect`.
|
||||||
///
|
///
|
||||||
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
|
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
|
||||||
|
@ -150,6 +171,8 @@ impl<G, R> fmt::Debug for Image<G, R>
|
||||||
Image::Url(ref url) => url.to_css(f),
|
Image::Url(ref url) => url.to_css(f),
|
||||||
Image::Gradient(ref grad) => grad.fmt(f),
|
Image::Gradient(ref grad) => grad.fmt(f),
|
||||||
Image::Rect(ref rect) => rect.fmt(f),
|
Image::Rect(ref rect) => rect.fmt(f),
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
|
||||||
Image::Element(ref selector) => {
|
Image::Element(ref selector) => {
|
||||||
f.write_str("-moz-element(#")?;
|
f.write_str("-moz-element(#")?;
|
||||||
serialize_identifier(&selector.to_string(), f)?;
|
serialize_identifier(&selector.to_string(), f)?;
|
||||||
|
@ -167,6 +190,8 @@ impl<G, R> ToCss for Image<G, R>
|
||||||
Image::Url(ref url) => url.to_css(dest),
|
Image::Url(ref url) => url.to_css(dest),
|
||||||
Image::Gradient(ref gradient) => gradient.to_css(dest),
|
Image::Gradient(ref gradient) => gradient.to_css(dest),
|
||||||
Image::Rect(ref rect) => rect.to_css(dest),
|
Image::Rect(ref rect) => rect.to_css(dest),
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
|
||||||
Image::Element(ref selector) => {
|
Image::Element(ref selector) => {
|
||||||
dest.write_str("-moz-element(#")?;
|
dest.write_str("-moz-element(#")?;
|
||||||
serialize_identifier(&selector.to_string(), dest)?;
|
serialize_identifier(&selector.to_string(), dest)?;
|
||||||
|
|
|
@ -22,6 +22,7 @@ use values::generics::image::{EndingShape as GenericEndingShape, Gradient as Gen
|
||||||
use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
|
use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
|
||||||
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
use values::generics::image::{Image as GenericImage, ImageRect as GenericImageRect};
|
||||||
use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent};
|
use values::generics::image::{LineDirection as GenericsLineDirection, ShapeExtent};
|
||||||
|
use values::generics::image::PaintWorklet;
|
||||||
use values::generics::position::Position as GenericPosition;
|
use values::generics::position::Position as GenericPosition;
|
||||||
use values::specified::{Angle, CSSColor, Color, Length, LengthOrPercentage};
|
use values::specified::{Angle, CSSColor, Color, Length, LengthOrPercentage};
|
||||||
use values::specified::{Number, NumberOrPercentage, Percentage};
|
use values::specified::{Number, NumberOrPercentage, Percentage};
|
||||||
|
@ -98,6 +99,12 @@ impl Parse for Image {
|
||||||
if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) {
|
if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) {
|
||||||
return Ok(GenericImage::Gradient(gradient));
|
return Ok(GenericImage::Gradient(gradient));
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
{
|
||||||
|
if let Ok(paint_worklet) = input.try(|i| PaintWorklet::parse(context, i)) {
|
||||||
|
return Ok(GenericImage::PaintWorklet(paint_worklet));
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
{
|
{
|
||||||
if let Ok(mut image_rect) = input.try(|input| ImageRect::parse(context, input)) {
|
if let Ok(mut image_rect) = input.try(|input| ImageRect::parse(context, input)) {
|
||||||
|
@ -673,6 +680,18 @@ impl Parse for ColorStop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for PaintWorklet {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
input.expect_function_matching("paint")?;
|
||||||
|
input.parse_nested_block(|i| {
|
||||||
|
let name = i.expect_ident()?;
|
||||||
|
Ok(PaintWorklet {
|
||||||
|
name: Atom::from(name),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for ImageRect {
|
impl Parse for ImageRect {
|
||||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
input.try(|i| i.expect_function_matching("-moz-image-rect"))?;
|
input.try(|i| i.expect_function_matching("-moz-image-rect"))?;
|
||||||
|
|
|
@ -6736,6 +6736,18 @@
|
||||||
],
|
],
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet.html": [
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/worklets/test_paint_worklet.html",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/worklets/test_paint_worklet_ref.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reftest_node": {
|
"reftest_node": {
|
||||||
|
@ -11208,6 +11220,16 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet.js": [
|
||||||
|
[
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet_ref.html": [
|
||||||
|
[
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"mozilla/worklets/test_worklet.js": [
|
"mozilla/worklets/test_worklet.js": [
|
||||||
[
|
[
|
||||||
{}
|
{}
|
||||||
|
@ -31775,6 +31797,18 @@
|
||||||
"f3a9b8c78346507bc0b3190c8000ccf80cc133f6",
|
"f3a9b8c78346507bc0b3190c8000ccf80cc133f6",
|
||||||
"support"
|
"support"
|
||||||
],
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet.html": [
|
||||||
|
"67fccbde17c28e13b5f4dc54d70b1279d6e9d602",
|
||||||
|
"reftest"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet.js": [
|
||||||
|
"e714db50da9e5cb18c652629fcc1b5ccc453564d",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
|
"mozilla/worklets/test_paint_worklet_ref.html": [
|
||||||
|
"e9cfa945824a8ecf07c41a269f82a2c2ca002406",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
"mozilla/worklets/test_worklet.html": [
|
"mozilla/worklets/test_worklet.html": [
|
||||||
"fe9c93a5307c616f878b6623155e1b04c86dd994",
|
"fe9c93a5307c616f878b6623155e1b04c86dd994",
|
||||||
"testharness"
|
"testharness"
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[test_paint_worklet.html]
|
||||||
|
type: reftest
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html class="reftest-wait">
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>A basic paint worklet test</title>
|
||||||
|
<link rel=match href=/_mozilla/mozilla/worklets/test_paint_worklet_ref.html>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="height: 100px; width: 100px; background: paint(test);"></div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
// This reftest will TIMEOUT if loading the paint worklet fails,
|
||||||
|
// It will PASS if the worklet draws a green rectangle.
|
||||||
|
window.paintWorklet
|
||||||
|
.addModule("test_paint_worklet.js")
|
||||||
|
.then(function() { document.documentElement.classList.remove("reftest-wait"); });
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -0,0 +1,6 @@
|
||||||
|
registerPaint("test", class {
|
||||||
|
paint(ctx, size) {
|
||||||
|
ctx.fillStyle = 'green';
|
||||||
|
ctx.fillRect(0, 0, size.width, size.height);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div style="height: 100px; width: 100px; background: green;"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue