aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/display_list_builder.rs25
-rw-r--r--components/script/dom/bindings/refcounted.rs2
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs57
-rw-r--r--components/script/dom/paintrenderingcontext2d.rs359
-rw-r--r--components/script/dom/paintworkletglobalscope.rs98
-rw-r--r--components/script/dom/webidls/CanvasGradient.webidl1
-rw-r--r--components/script/dom/webidls/CanvasPattern.webidl1
-rw-r--r--components/script/dom/webidls/CanvasRenderingContext2D.webidl22
-rw-r--r--components/script/dom/webidls/PaintRenderingContext2D.webidl22
-rw-r--r--components/script/dom/window.rs2
-rw-r--r--components/script/dom/worklet.rs32
-rw-r--r--components/script/dom/workletglobalscope.rs4
-rw-r--r--components/script/script_thread.rs22
-rw-r--r--components/script_traits/lib.rs7
-rw-r--r--tests/wpt/mozilla/meta/mozilla/worklets/test_paint_worklet.html.ini3
15 files changed, 547 insertions, 110 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 3fdb41d3da1..7ab6c7f78b7 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -1174,21 +1174,30 @@ impl FragmentDisplayListBuilding for Fragment {
// 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 mut image = match executor.draw_a_paint_image(name, size) {
- Ok(image) => image,
- Err(err) => return warn!("Error running paint worklet ({:?}).", err),
+ let (sender, receiver) = ipc::channel().unwrap();
+ executor.draw_a_paint_image(name, size, sender);
+
+ // TODO: timeout
+ let webrender_image = match receiver.recv() {
+ Ok(CanvasData::Image(canvas_data)) => {
+ WebRenderImageInfo {
+ // TODO: it would be nice to get this data back from the canvas
+ width: size.width.to_px().abs() as u32,
+ height: size.height.to_px().abs() as u32,
+ format: PixelFormat::BGRA8,
+ key: Some(canvas_data.image_key),
+ }
+ },
+ Ok(CanvasData::WebGL(_)) => return warn!("Paint worklet generated WebGL."),
+ Err(err) => return warn!("Paint worklet recv generated error ({}).", 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),
+ webrender_image,
index);
}
diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs
index f22074d5fef..db3215fbdd1 100644
--- a/components/script/dom/bindings/refcounted.rs
+++ b/components/script/dom/bindings/refcounted.rs
@@ -127,6 +127,7 @@ impl TrustedPromise {
struct RejectPromise(TrustedPromise, Error);
impl Runnable for RejectPromise {
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
+ debug!("Rejecting promise.");
let this = *self;
let cx = script_thread.get_cx();
let promise = this.0.root();
@@ -145,6 +146,7 @@ impl TrustedPromise {
struct ResolvePromise<T>(TrustedPromise, T);
impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> {
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
+ debug!("Resolving promise.");
let this = *self;
let cx = script_thread.get_cx();
let promise = this.0.root();
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index be7c8e945c2..c1a2be92bcc 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -61,7 +61,9 @@ pub struct CanvasRenderingContext2D {
reflector_: Reflector,
#[ignore_heap_size_of = "Defined in ipc-channel"]
ipc_renderer: IpcSender<CanvasMsg>,
- canvas: JS<HTMLCanvasElement>,
+ // For rendering contexts created by an HTML canvas element, this is Some,
+ // for ones created by a paint worklet, this is None.
+ canvas: Option<JS<HTMLCanvasElement>>,
state: DOMRefCell<CanvasContextState>,
saved_states: DOMRefCell<Vec<CanvasContextState>>,
origin_clean: Cell<bool>,
@@ -109,18 +111,21 @@ impl CanvasContextState {
}
impl CanvasRenderingContext2D {
- fn new_inherited(global: &GlobalScope,
- canvas: &HTMLCanvasElement,
- size: Size2D<i32>)
- -> CanvasRenderingContext2D {
+ pub fn new_inherited(global: &GlobalScope,
+ canvas: Option<&HTMLCanvasElement>,
+ size: Size2D<i32>)
+ -> CanvasRenderingContext2D {
+ debug!("Creating new canvas rendering context.");
let (sender, receiver) = ipc::channel().unwrap();
let constellation_chan = global.constellation_chan();
+ debug!("Asking constellation to create new canvas thread.");
constellation_chan.send(ConstellationMsg::CreateCanvasPaintThread(size, sender)).unwrap();
let ipc_renderer = receiver.recv().unwrap();
+ debug!("Done.");
CanvasRenderingContext2D {
reflector_: Reflector::new(),
ipc_renderer: ipc_renderer,
- canvas: JS::from_ref(canvas),
+ canvas: canvas.map(JS::from_ref),
state: DOMRefCell::new(CanvasContextState::new()),
saved_states: DOMRefCell::new(Vec::new()),
origin_clean: Cell::new(true),
@@ -131,7 +136,7 @@ impl CanvasRenderingContext2D {
canvas: &HTMLCanvasElement,
size: Size2D<i32>)
-> Root<CanvasRenderingContext2D> {
- reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, canvas, size),
+ reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, Some(canvas), size),
global,
CanvasRenderingContext2DBinding::Wrap)
}
@@ -155,7 +160,9 @@ impl CanvasRenderingContext2D {
}
fn mark_as_dirty(&self) {
- self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ if let Some(ref canvas) = self.canvas {
+ canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ }
}
fn update_transform(&self) {
@@ -226,8 +233,12 @@ impl CanvasRenderingContext2D {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) =>
image.origin_is_clean(),
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => {
+ let canvas = match self.canvas {
+ Some(ref canvas) => canvas,
+ None => return false,
+ };
let image_origin = image.get_origin().expect("Image's origin is missing");
- let document = document_from_node(&*self.canvas);
+ let document = document_from_node(&**canvas);
document.url().clone().origin() == image_origin
}
}
@@ -347,7 +358,7 @@ impl CanvasRenderingContext2D {
let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
- if &*self.canvas == canvas {
+ if self.canvas.as_ref().map_or(false, |c| &**c == canvas) {
let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf(
image_size, dest_rect, source_rect, smoothing_enabled));
self.ipc_renderer.send(msg).unwrap();
@@ -442,8 +453,10 @@ impl CanvasRenderingContext2D {
#[inline]
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
- let window = window_from_node(&*self.canvas);
- canvas_utils::request_image_from_cache(&window, url)
+ self.canvas.as_ref()
+ .map(|canvas| window_from_node(&**canvas))
+ .map(|window| canvas_utils::request_image_from_cache(&window, url))
+ .unwrap_or(ImageResponse::None)
}
fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> {
@@ -472,12 +485,20 @@ impl CanvasRenderingContext2D {
// TODO: will need to check that the context bitmap mode is fixed
// once we implement CanvasProxy
- let window = window_from_node(&*self.canvas);
+ let canvas = match self.canvas {
+ // https://drafts.css-houdini.org/css-paint-api/#2d-rendering-context
+ // Whenever "currentColor" is used as a color in the PaintRenderingContext2D API,
+ // it is treated as opaque black.
+ None => return Ok(RGBA::new(0, 0, 0, 255)),
+ Some(ref canvas) => &**canvas,
+ };
+
+ let window = window_from_node(canvas);
- let style = window.GetComputedStyle(&*self.canvas.upcast(), None);
+ let style = window.GetComputedStyle(canvas.upcast(), None);
let element_not_rendered =
- !self.canvas.upcast::<Node>().is_in_doc() ||
+ !canvas.upcast::<Node>().is_in_doc() ||
style.GetPropertyValue(DOMString::from("display")) == "none";
if element_not_rendered {
@@ -530,7 +551,9 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutJS<CanvasRenderingContext2D
impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
// https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas
fn Canvas(&self) -> Root<HTMLCanvasElement> {
- Root::from_ref(&*self.canvas)
+ // This method is not called from a paint worklet rendering context,
+ // so it's OK to panic if self.canvas is None.
+ Root::from_ref(self.canvas.as_ref().expect("No canvas."))
}
// https://html.spec.whatwg.org/multipage/#dom-context-2d-save
@@ -1037,7 +1060,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
Size2D::new(sw as i32, sh as i32));
- let canvas_size = self.canvas.get_size();
+ let canvas_size = self.canvas.as_ref().map(|c| c.get_size()).unwrap_or(Size2D::zero());
let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
self.ipc_renderer
.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)))
diff --git a/components/script/dom/paintrenderingcontext2d.rs b/components/script/dom/paintrenderingcontext2d.rs
index a70e62a45ed..dd91ec0960e 100644
--- a/components/script/dom/paintrenderingcontext2d.rs
+++ b/components/script/dom/paintrenderingcontext2d.rs
@@ -2,28 +2,377 @@
* 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 canvas_traits::CanvasData;
+use canvas_traits::CanvasMsg;
+use canvas_traits::FromLayoutMsg;
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap;
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin;
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding;
+use dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding::PaintRenderingContext2DMethods;
+use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
+use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
+use dom::bindings::error::ErrorResult;
+use dom::bindings::error::Fallible;
+use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
-use dom::bindings::reflector::Reflector;
+use dom::bindings::num::Finite;
use dom::bindings::reflector::reflect_dom_object;
+use dom::bindings::str::DOMString;
+use dom::canvasgradient::CanvasGradient;
+use dom::canvaspattern::CanvasPattern;
+use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use dom_struct::dom_struct;
+use euclid::Size2D;
+use ipc_channel::ipc::IpcSender;
#[dom_struct]
pub struct PaintRenderingContext2D {
- reflector: Reflector,
+ context: CanvasRenderingContext2D,
}
impl PaintRenderingContext2D {
- fn new_inherited() -> PaintRenderingContext2D {
+ fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D {
+ let size = Size2D::zero();
PaintRenderingContext2D {
- reflector: Reflector::new(),
+ context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, size),
}
}
pub fn new(global: &PaintWorkletGlobalScope) -> Root<PaintRenderingContext2D> {
- reflect_dom_object(box PaintRenderingContext2D::new_inherited(),
+ reflect_dom_object(box PaintRenderingContext2D::new_inherited(global),
global,
PaintRenderingContext2DBinding::Wrap)
}
+
+ pub fn send_data(&self, sender: IpcSender<CanvasData>) {
+ let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender));
+ let _ = self.context.ipc_renderer().send(msg);
+ }
+
+ pub fn set_bitmap_dimensions(&self, size: Size2D<Au>) {
+ let size = Size2D::new(size.width.to_px(), size.height.to_px());
+ self.context.set_bitmap_dimensions(size);
+ }
+}
+
+impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-save
+ fn Save(&self) {
+ self.context.Save()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore
+ fn Restore(&self) {
+ self.context.Restore()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale
+ fn Scale(&self, x: f64, y: f64) {
+ self.context.Scale(x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate
+ fn Rotate(&self, angle: f64) {
+ self.context.Rotate(angle)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate
+ fn Translate(&self, x: f64, y: f64) {
+ self.context.Translate(x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-transform
+ fn Transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
+ self.context.Transform(a, b, c, d, e, f)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform
+ fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
+ self.context.SetTransform(a, b, c, d, e, f)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform
+ fn ResetTransform(&self) {
+ self.context.ResetTransform()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha
+ fn GlobalAlpha(&self) -> f64 {
+ self.context.GlobalAlpha()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha
+ fn SetGlobalAlpha(&self, alpha: f64) {
+ self.context.SetGlobalAlpha(alpha)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
+ fn GlobalCompositeOperation(&self) -> DOMString {
+ self.context.GlobalCompositeOperation()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
+ fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
+ self.context.SetGlobalCompositeOperation(op_str)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect
+ fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ self.context.FillRect(x, y, width, height)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect
+ fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ self.context.ClearRect(x, y, width, height)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect
+ fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ self.context.StrokeRect(x, y, width, height)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath
+ fn BeginPath(&self) {
+ self.context.BeginPath()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath
+ fn ClosePath(&self) {
+ self.context.ClosePath()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill
+ fn Fill(&self, fill_rule: CanvasFillRule) {
+ self.context.Fill(fill_rule)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke
+ fn Stroke(&self) {
+ self.context.Stroke()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip
+ fn Clip(&self, fill_rule: CanvasFillRule) {
+ self.context.Clip(fill_rule)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath
+ fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
+ self.context.IsPointInPath(x, y, fill_rule)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
+ fn DrawImage(&self,
+ image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
+ dx: f64,
+ dy: f64)
+ -> ErrorResult {
+ self.context.DrawImage(image, dx, dy)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
+ fn DrawImage_(&self,
+ image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
+ dx: f64,
+ dy: f64,
+ dw: f64,
+ dh: f64)
+ -> ErrorResult {
+ self.context.DrawImage_(image, dx, dy, dw, dh)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
+ fn DrawImage__(&self,
+ image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
+ sx: f64,
+ sy: f64,
+ sw: f64,
+ sh: f64,
+ dx: f64,
+ dy: f64,
+ dw: f64,
+ dh: f64)
+ -> ErrorResult {
+ self.context.DrawImage__(image, sx, sy, sw, sh, dx, dy, dw, dh)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto
+ fn MoveTo(&self, x: f64, y: f64) {
+ self.context.MoveTo(x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto
+ fn LineTo(&self, x: f64, y: f64) {
+ self.context.LineTo(x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect
+ fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
+ self.context.Rect(x, y, width, height)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto
+ fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
+ self.context.QuadraticCurveTo(cpx, cpy, x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto
+ fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
+ self.context.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc
+ fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
+ self.context.Arc(x, y, r, start, end, ccw)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto
+ fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
+ self.context.ArcTo(cp1x, cp1y, cp2x, cp2y, r)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled
+ fn ImageSmoothingEnabled(&self) -> bool {
+ self.context.ImageSmoothingEnabled()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled
+ fn SetImageSmoothingEnabled(&self, value: bool) {
+ self.context.SetImageSmoothingEnabled(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
+ fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
+ self.context.StrokeStyle()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
+ fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
+ self.context.SetStrokeStyle(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
+ fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
+ self.context.FillStyle()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
+ fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
+ self.context.SetFillStyle(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient
+ fn CreateLinearGradient(&self,
+ x0: Finite<f64>,
+ y0: Finite<f64>,
+ x1: Finite<f64>,
+ y1: Finite<f64>)
+ -> Root<CanvasGradient> {
+ self.context.CreateLinearGradient(x0, y0, x1, y1)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient
+ fn CreateRadialGradient(&self,
+ x0: Finite<f64>,
+ y0: Finite<f64>,
+ r0: Finite<f64>,
+ x1: Finite<f64>,
+ y1: Finite<f64>,
+ r1: Finite<f64>)
+ -> Fallible<Root<CanvasGradient>> {
+ self.context.CreateRadialGradient(x0, y0, r0, x1, y1, r1)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern
+ fn CreatePattern(&self,
+ image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
+ repetition: DOMString)
+ -> Fallible<Root<CanvasPattern>> {
+ self.context.CreatePattern(image, repetition)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
+ fn LineWidth(&self) -> f64 {
+ self.context.LineWidth()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
+ fn SetLineWidth(&self, width: f64) {
+ self.context.SetLineWidth(width)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
+ fn LineCap(&self) -> CanvasLineCap {
+ self.context.LineCap()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
+ fn SetLineCap(&self, cap: CanvasLineCap) {
+ self.context.SetLineCap(cap)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
+ fn LineJoin(&self) -> CanvasLineJoin {
+ self.context.LineJoin()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
+ fn SetLineJoin(&self, join: CanvasLineJoin) {
+ self.context.SetLineJoin(join)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
+ fn MiterLimit(&self) -> f64 {
+ self.context.MiterLimit()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
+ fn SetMiterLimit(&self, limit: f64) {
+ self.context.SetMiterLimit(limit)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
+ fn ShadowOffsetX(&self) -> f64 {
+ self.context.ShadowOffsetX()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
+ fn SetShadowOffsetX(&self, value: f64) {
+ self.context.SetShadowOffsetX(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
+ fn ShadowOffsetY(&self) -> f64 {
+ self.context.ShadowOffsetY()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
+ fn SetShadowOffsetY(&self, value: f64) {
+ self.context.SetShadowOffsetY(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
+ fn ShadowBlur(&self) -> f64 {
+ self.context.ShadowBlur()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
+ fn SetShadowBlur(&self, value: f64) {
+ self.context.SetShadowBlur(value)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
+ fn ShadowColor(&self) -> DOMString {
+ self.context.ShadowColor()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
+ fn SetShadowColor(&self, value: DOMString) {
+ self.context.SetShadowColor(value)
+ }
+
}
diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs
index 7a6117659ac..5ac021491ad 100644
--- a/components/script/dom/paintworkletglobalscope.rs
+++ b/components/script/dom/paintworkletglobalscope.rs
@@ -3,6 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
+use canvas_traits::CanvasData;
+use canvas_traits::CanvasImageData;
use dom::bindings::callback::CallbackContainer;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
@@ -13,6 +15,7 @@ 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::js::JS;
use dom::bindings::js::Root;
use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
@@ -22,6 +25,7 @@ use dom::workletglobalscope::WorkletGlobalScope;
use dom::workletglobalscope::WorkletGlobalScopeInit;
use dom_struct::dom_struct;
use euclid::Size2D;
+use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc::IpcSharedMemory;
use js::jsapi::Call;
use js::jsapi::Construct1;
@@ -40,7 +44,7 @@ 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 net_traits::image_cache::ImageCache;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::cell::Cell;
@@ -48,19 +52,20 @@ use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ptr::null_mut;
use std::rc::Rc;
-use std::sync::mpsc::Sender;
+use std::sync::Arc;
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
#[dom_struct]
pub struct PaintWorkletGlobalScope {
/// The worklet global for this object
worklet_global: WorkletGlobalScope,
+ /// The image cache (used for generating invalid images).
+ #[ignore_heap_size_of = "Arc"]
+ image_cache: Arc<ImageCache>,
/// https://drafts.css-houdini.org/css-paint-api/#paint-definitions
paint_definitions: DOMRefCell<HashMap<Atom, Box<PaintDefinition>>>,
/// https://drafts.css-houdini.org/css-paint-api/#paint-class-instances
paint_class_instances: DOMRefCell<HashMap<Atom, Box<Heap<JSVal>>>>,
- /// A buffer to draw into
- buffer: DOMRefCell<Vec<u8>>,
}
impl PaintWorkletGlobalScope {
@@ -73,9 +78,9 @@ impl 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),
+ image_cache: init.image_cache.clone(),
paint_definitions: Default::default(),
paint_class_instances: Default::default(),
- buffer: Default::default(),
};
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
}
@@ -90,7 +95,7 @@ impl PaintWorkletGlobalScope {
fn draw_a_paint_image(&self,
name: Atom,
size: Size2D<Au>,
- sender: Sender<Result<Image, PaintWorkletError>>)
+ sender: IpcSender<CanvasData>)
{
// TODO: document paint definitions.
self.invoke_a_paint_callback(name, size, sender);
@@ -101,7 +106,7 @@ impl PaintWorkletGlobalScope {
fn invoke_a_paint_callback(&self,
name: Atom,
size: Size2D<Au>,
- sender: Sender<Result<Image, PaintWorkletError>>)
+ sender: IpcSender<CanvasData>)
{
let width = size.width.to_px().abs() as u32;
let height = size.height.to_px().abs() as u32;
@@ -114,24 +119,21 @@ impl PaintWorkletGlobalScope {
// Step 2.2-5.1.
rooted!(in(cx) let mut class_constructor = UndefinedValue());
rooted!(in(cx) let mut paint_function = UndefinedValue());
- match self.paint_definitions.borrow().get(&name) {
+ let rendering_context = match self.paint_definitions.borrow().get(&name) {
None => {
// Step 2.2.
warn!("Drawing un-registered paint definition {}.", name);
- let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
- let _ = sender.send(Ok(image));
- return;
+ return self.send_invalid_image(size, sender);
}
Some(definition) => {
// Step 5.1
if !definition.constructor_valid_flag.get() {
debug!("Drawing invalid paint definition {}.", name);
- let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
- let _ = sender.send(Ok(image));
- return;
+ return self.send_invalid_image(size, sender);
}
class_constructor.set(definition.class_constructor.get());
paint_function.set(definition.paint_function.get());
+ Root::from_ref(&*definition.context)
}
};
@@ -155,9 +157,7 @@ impl PaintWorkletGlobalScope {
self.paint_definitions.borrow_mut().get_mut(&name)
.expect("Vanishing paint definition.")
.constructor_valid_flag.set(false);
- let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
- let _ = sender.send(Ok(image));
- return;
+ return self.send_invalid_image(size, sender);
}
// Step 5.4
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
@@ -166,7 +166,9 @@ impl PaintWorkletGlobalScope {
// TODO: Steps 6-7
// Step 8
- let rendering_context = PaintRenderingContext2D::new(self);
+ // TODO: the spec requires creating a new paint rendering context each time,
+ // this code recycles the same one.
+ rendering_context.set_bitmap_dimensions(size);
// Step 9
let paint_size = PaintSize::new(self, size);
@@ -186,37 +188,37 @@ impl PaintWorkletGlobalScope {
if unsafe { JS_IsExceptionPending(cx) } {
debug!("Paint function threw an exception {}.", name);
unsafe { JS_ClearPendingException(cx); }
- let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
- let _ = sender.send(Ok(image));
- return;
+ return self.send_invalid_image(size, sender);
}
- // For now, we just build a dummy image.
- let image = self.placeholder_image(width, height, [0xFF, 0x00, 0x00, 0xFF]);
- let _ = sender.send(Ok(image));
+ rendering_context.send_data(sender);
}
- fn placeholder_image(&self, width: u32, height: u32, pixel: [u8; 4]) -> Image {
- let area = (width as usize) * (height as usize);
- let old_buffer_size = self.buffer.borrow().len();
- let new_buffer_size = area * 4;
- if new_buffer_size > old_buffer_size {
- self.buffer.borrow_mut().extend(pixel.iter().cycle().take(new_buffer_size - old_buffer_size));
- } else {
- self.buffer.borrow_mut().truncate(new_buffer_size);
- }
- Image {
+ fn send_invalid_image(&self, size: Size2D<Au>, sender: IpcSender<CanvasData>) {
+ debug!("Sending an invalid image.");
+ let width = size.width.to_px().abs() as u32;
+ let height = size.height.to_px().abs() as u32;
+ let len = (width as usize) * (height as usize) * 4;
+ let pixel = [0xFF, 0x00, 0x00, 0xFF];
+ let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect();
+ let mut image = Image {
width: width,
height: height,
format: PixelFormat::BGRA8,
- bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()),
+ bytes: IpcSharedMemory::from_bytes(&*bytes),
id: None,
- }
+ };
+ self.image_cache.set_webrender_image_key(&mut image);
+ let image_key = image.id.expect("Image cache should set image key.");
+ let image_data = CanvasImageData { image_key: image_key };
+ let canvas_data = CanvasData::Image(image_data);
+ let _ = sender.send(canvas_data);
}
}
impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
#[allow(unsafe_code)]
+ #[allow(unrooted_must_root)]
/// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint
fn RegisterPaint(&self, name: DOMString, paint_ctor: Rc<VoidFunction>) -> Fallible<()> {
let name = Atom::from(name);
@@ -279,11 +281,17 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
return Err(Error::Type(String::from("Paint function is not callable.")));
}
- // Steps 19-20.
+ // Step 19.
+ 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,
- PaintDefinition::new(paint_val.handle(), paint_function.handle(), input_properties, alpha));
+ self.paint_definitions.borrow_mut().insert(name, definition);
// TODO: Step 21.
@@ -293,7 +301,7 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
/// Tasks which can be peformed by a paint worklet
pub enum PaintWorkletTask {
- DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>)
+ DrawAPaintImage(Atom, Size2D<Au>, IpcSender<CanvasData>)
}
/// A paint definition
@@ -308,13 +316,18 @@ struct PaintDefinition {
constructor_valid_flag: Cell<bool>,
input_properties: Vec<DOMString>,
context_alpha_flag: bool,
+ // TODO: the spec calls for fresh rendering contexts each time a paint image is drawn,
+ // but to avoid having the primary worklet thread create a new renering context,
+ // we recycle them.
+ context: JS<PaintRenderingContext2D>,
}
impl PaintDefinition {
fn new(class_constructor: HandleValue,
paint_function: HandleValue,
input_properties: Vec<DOMString>,
- alpha: bool)
+ alpha: bool,
+ context: &PaintRenderingContext2D)
-> Box<PaintDefinition>
{
let result = Box::new(PaintDefinition {
@@ -323,6 +336,7 @@ impl PaintDefinition {
constructor_valid_flag: Cell::new(true),
input_properties: input_properties,
context_alpha_flag: alpha,
+ context: JS::from_ref(context),
});
result.class_constructor.set(class_constructor.get());
result.paint_function.set(paint_function.get());
diff --git a/components/script/dom/webidls/CanvasGradient.webidl b/components/script/dom/webidls/CanvasGradient.webidl
index 236c077551a..4854dda89d6 100644
--- a/components/script/dom/webidls/CanvasGradient.webidl
+++ b/components/script/dom/webidls/CanvasGradient.webidl
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#canvasgradient
+[Exposed=(Window, PaintWorklet)]
interface CanvasGradient {
// opaque object
[Throws]
diff --git a/components/script/dom/webidls/CanvasPattern.webidl b/components/script/dom/webidls/CanvasPattern.webidl
index eb382b82b1a..d56a4ec9cd5 100644
--- a/components/script/dom/webidls/CanvasPattern.webidl
+++ b/components/script/dom/webidls/CanvasPattern.webidl
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#canvaspattern
+[Exposed=(Window, PaintWorklet)]
interface CanvasPattern {
//void setTransform(SVGMatrix matrix);
};
diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
index ed5f9477476..3937e74521a 100644
--- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl
+++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
@@ -41,14 +41,14 @@ CanvasRenderingContext2D implements CanvasPathDrawingStyles;
CanvasRenderingContext2D implements CanvasTextDrawingStyles;
CanvasRenderingContext2D implements CanvasPath;
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasState {
// state
void save(); // push state on state stack
void restore(); // pop state stack and restore state
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasTransform {
// transformations (default transform is the identity matrix)
void scale(unrestricted double x, unrestricted double y);
@@ -72,21 +72,21 @@ interface CanvasTransform {
void resetTransform();
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasCompositing {
// compositing
attribute unrestricted double globalAlpha; // (default 1.0)
attribute DOMString globalCompositeOperation; // (default source-over)
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasImageSmoothing {
// image smoothing
attribute boolean imageSmoothingEnabled; // (default true)
// attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasFillStrokeStyles {
// colours and styles (see also the CanvasDrawingStyles interface)
@@ -99,7 +99,7 @@ interface CanvasFillStrokeStyles {
CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasShadowStyles {
// shadows
attribute unrestricted double shadowOffsetX; // (default 0)
@@ -108,7 +108,7 @@ interface CanvasShadowStyles {
attribute DOMString shadowColor; // (default transparent black)
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasRect {
// rects
//[LenientFloat]
@@ -119,7 +119,7 @@ interface CanvasRect {
void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasDrawPath {
// path API (see also CanvasPathMethods)
void beginPath();
@@ -157,7 +157,7 @@ interface CanvasText {
//TextMetrics measureText(DOMString text);
};
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasDrawImage {
// drawing images
[Throws]
@@ -205,7 +205,7 @@ enum CanvasTextAlign { "start", "end", "left", "right", "center" };
enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" };
enum CanvasDirection { "ltr", "rtl", "inherit" };
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
interface CanvasPathDrawingStyles {
// line caps/joins
attribute unrestricted double lineWidth; // (default 1)
@@ -229,7 +229,7 @@ interface CanvasTextDrawingStyles {
//attribute CanvasDirection direction; // "ltr", "rtl", "inherit" (default: "inherit")
};
-[NoInterfaceObject, Exposed=(Window,Worker)]
+[NoInterfaceObject, Exposed=(Window, Worker, PaintWorklet)]
interface CanvasPath {
// shared path API methods
void closePath();
diff --git a/components/script/dom/webidls/PaintRenderingContext2D.webidl b/components/script/dom/webidls/PaintRenderingContext2D.webidl
index 3483df305b1..d2097bd4f7c 100644
--- a/components/script/dom/webidls/PaintRenderingContext2D.webidl
+++ b/components/script/dom/webidls/PaintRenderingContext2D.webidl
@@ -6,14 +6,14 @@
[Exposed=PaintWorklet]
interface PaintRenderingContext2D {
};
-// PaintRenderingContext2D implements CanvasState;
-// PaintRenderingContext2D implements CanvasTransform;
-// PaintRenderingContext2D implements CanvasCompositing;
-// PaintRenderingContext2D implements CanvasImageSmoothing;
-// PaintRenderingContext2D implements CanvasFillStrokeStyles;
-// PaintRenderingContext2D implements CanvasShadowStyles;
-// PaintRenderingContext2D implements CanvasRect;
-// PaintRenderingContext2D implements CanvasDrawPath;
-// PaintRenderingContext2D implements CanvasDrawImage;
-// PaintRenderingContext2D implements CanvasPathDrawingStyles;
-// PaintRenderingContext2D implements CanvasPath;
+PaintRenderingContext2D implements CanvasState;
+PaintRenderingContext2D implements CanvasTransform;
+PaintRenderingContext2D implements CanvasCompositing;
+PaintRenderingContext2D implements CanvasImageSmoothing;
+PaintRenderingContext2D implements CanvasFillStrokeStyles;
+PaintRenderingContext2D implements CanvasShadowStyles;
+PaintRenderingContext2D implements CanvasRect;
+PaintRenderingContext2D implements CanvasDrawPath;
+PaintRenderingContext2D implements CanvasDrawImage;
+PaintRenderingContext2D implements CanvasPathDrawingStyles;
+PaintRenderingContext2D implements CanvasPath;
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 837e10c921b..59c3c678fb3 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -152,6 +152,7 @@ pub enum ReflowReason {
ImageLoaded,
RequestAnimationFrame,
WebFontLoaded,
+ WorkletLoaded,
FramedContentChanged,
IFrameLoadEvent,
MissingExplicitReflow,
@@ -1939,6 +1940,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
ReflowReason::ImageLoaded => "\tImageLoaded",
ReflowReason::RequestAnimationFrame => "\tRequestAnimationFrame",
ReflowReason::WebFontLoaded => "\tWebFontLoaded",
+ ReflowReason::WorkletLoaded => "\tWorkletLoaded",
ReflowReason::FramedContentChanged => "\tFramedContentChanged",
ReflowReason::IFrameLoadEvent => "\tIFrameLoadEvent",
ReflowReason::MissingExplicitReflow => "\tMissingExplicitReflow",
diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs
index e5e55b97453..bbcc777ab11 100644
--- a/components/script/dom/worklet.rs
+++ b/components/script/dom/worklet.rs
@@ -11,6 +11,7 @@
//! 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;
@@ -38,6 +39,7 @@ 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;
@@ -45,7 +47,6 @@ use js::jsapi::JS_GetGCParameter;
use js::rust::Runtime;
use msg::constellation_msg::PipelineId;
use net_traits::IpcSend;
-use net_traits::image::base::Image;
use net_traits::load_whole_resource;
use net_traits::request::Destination;
use net_traits::request::RequestInit;
@@ -58,7 +59,6 @@ use script_runtime::new_rt_and_cx;
use script_thread::MainThreadScriptMsg;
use script_thread::Runnable;
use script_thread::ScriptThread;
-use script_traits::PaintWorkletError;
use script_traits::PaintWorkletExecutor;
use servo_atoms::Atom;
use servo_rand;
@@ -76,7 +76,6 @@ use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::Sender;
use std::thread;
-use std::time::Duration;
use style::thread_state;
use swapper::Swapper;
use swapper::swapper;
@@ -85,7 +84,6 @@ use uuid::Uuid;
// Magic numbers
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
const MIN_GC_THRESHOLD: u32 = 1_000_000;
-const PAINT_TIMEOUT_MILLISECONDS: u64 = 10;
#[dom_struct]
/// https://drafts.css-houdini.org/worklets/#worklet
@@ -163,6 +161,7 @@ impl WorkletMethods for Worklet {
&promise);
// Step 5.
+ debug!("Returning promise.");
promise
}
}
@@ -234,6 +233,17 @@ impl PendingTasksStruct {
/// The thread pool lives in the script thread, and is initialized
/// when a worklet adds a module. It is dropped when the script thread
/// is dropped, and asks each of the worklet threads to quit.
+///
+/// The layout thread can end up blocking on the primary worklet thread
+/// (e.g. when invoking a paint callback), so it is important to avoid
+/// deadlock by making sure the primary worklet thread doesn't end up
+/// blocking waiting on layout. In particular, since the constellation
+/// can block waiting on layout, this means the primary worklet thread
+/// can't block waiting on the constellation. In general, the primary
+/// worklet thread shouldn't perform any blocking operations. If a worklet
+/// thread needs to do anything blocking, it should send a control
+/// message, to make sure that the blocking operation is performed
+/// by a backup thread, not by the primary thread.
#[derive(Clone, JSTraceable)]
pub struct WorkletThreadPool {
@@ -551,6 +561,7 @@ impl WorkletThread {
match self.global_scopes.entry(worklet_id) {
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);
entry.insert(JS::from_ref(&*result));
result
@@ -562,6 +573,7 @@ impl WorkletThread {
/// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
fn fetch_and_invoke_a_worklet_script(&self,
global_scope: &WorkletGlobalScope,
+ pipeline_id: PipelineId,
origin: ImmutableOrigin,
script_url: ServoUrl,
credentials: RequestCredentials,
@@ -612,7 +624,9 @@ impl WorkletThread {
debug!("Finished adding script.");
let old_counter = pending_tasks_struct.decrement_counter_by(1);
if old_counter == 1 {
- // TODO: trigger a reflow?
+ debug!("Resolving promise.");
+ let msg = MainThreadScriptMsg::WorkletLoaded(pipeline_id);
+ self.script_sender.send(msg).expect("Worklet thread outlived script thread.");
self.run_in_script_thread(promise.resolve_runnable(()));
}
}
@@ -638,6 +652,7 @@ impl WorkletThread {
global_type,
base_url);
self.fetch_and_invoke_a_worklet_script(&*global,
+ pipeline_id,
origin,
script_url,
credentials,
@@ -678,13 +693,10 @@ 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>
+ concrete_object_size: Size2D<Au>,
+ sender: IpcSender<CanvasData>)
{
- let (sender, receiver) = mpsc::channel();
let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender));
- let timeout = Duration::from_millis(PAINT_TIMEOUT_MILLISECONDS);
self.schedule_a_worklet_task(task);
- receiver.recv_timeout(timeout)?
}
}
diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs
index e34be97851e..bbe3c47e3f8 100644
--- a/components/script/dom/workletglobalscope.rs
+++ b/components/script/dom/workletglobalscope.rs
@@ -20,6 +20,7 @@ use microtask::Microtask;
use microtask::MicrotaskQueue;
use msg::constellation_msg::PipelineId;
use net_traits::ResourceThreads;
+use net_traits::image_cache::ImageCache;
use profile_traits::mem;
use profile_traits::time;
use script_traits::ScriptMsg;
@@ -27,6 +28,7 @@ use script_traits::TimerSchedulerMsg;
use servo_url::ImmutableOrigin;
use servo_url::MutableOrigin;
use servo_url::ServoUrl;
+use std::sync::Arc;
#[dom_struct]
/// https://drafts.css-houdini.org/worklets/#workletglobalscope
@@ -123,6 +125,8 @@ pub struct WorkletGlobalScopeInit {
pub constellation_chan: IpcSender<ScriptMsg>,
/// Message to send to the scheduler
pub scheduler_chan: IpcSender<TimerSchedulerMsg>,
+ /// The image cache
+ pub image_cache: Arc<ImageCache>,
}
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 764a7c87a4f..8319ad3cba3 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -256,6 +256,9 @@ pub enum MainThreadScriptMsg {
DOMManipulation(DOMManipulationTask),
/// Tasks that originate from the user interaction task source
UserInteraction(UserInteractionTask),
+ /// Notifies the script thread that a new worklet has been loaded, and thus the page should be
+ /// reflowed.
+ WorkletLoaded(PipelineId),
}
impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> {
@@ -724,6 +727,7 @@ impl ScriptThread {
devtools_chan: script_thread.devtools_chan.clone(),
constellation_chan: script_thread.constellation_chan.clone(),
scheduler_chan: script_thread.scheduler_chan.clone(),
+ image_cache: script_thread.image_cache.clone(),
};
Rc::new(WorkletThreadPool::spawn(chan, init))
}).clone()
@@ -828,6 +832,7 @@ impl ScriptThread {
debug!("Starting script thread.");
while self.handle_msgs() {
// Go on...
+ debug!("Running script thread.");
}
debug!("Stopped script thread.");
}
@@ -856,6 +861,7 @@ impl ScriptThread {
let mut sequential = vec![];
// Receive at least one message so we don't spinloop.
+ debug!("Waiting for event.");
let mut event = {
let sel = Select::new();
let mut script_port = sel.handle(&self.port);
@@ -887,6 +893,7 @@ impl ScriptThread {
panic!("unexpected select result")
}
};
+ debug!("Got event.");
// Squash any pending resize, reflow, animation tick, and mouse-move events in the queue.
let mut mouse_move_event_index = None;
@@ -983,6 +990,7 @@ impl ScriptThread {
}
// Process the gathered events.
+ debug!("Processing events.");
for msg in sequential {
debug!("Processing event {:?}.", msg);
let category = self.categorize_msg(&msg);
@@ -1025,6 +1033,7 @@ impl ScriptThread {
// Issue batched reflows on any pages that require it (e.g. if images loaded)
// TODO(gw): In the future we could probably batch other types of reflows
// into this loop too, but for now it's only images.
+ debug!("Issuing batched reflows.");
for (_, document) in self.documents.borrow().iter() {
let window = document.window();
let pending_reflows = window.get_pending_reflow_count();
@@ -1189,11 +1198,16 @@ impl ScriptThread {
// The category of the runnable is ignored by the pattern, however
// it is still respected by profiling (see categorize_msg).
if !runnable.is_cancelled() {
+ debug!("Running runnable.");
runnable.main_thread_handler(self)
+ } else {
+ debug!("Not running cancelled runnable.");
}
}
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
self.collect_reports(reports_chan),
+ MainThreadScriptMsg::WorkletLoaded(pipeline_id) =>
+ self.handle_worklet_loaded(pipeline_id),
MainThreadScriptMsg::DOMManipulation(task) =>
task.handle_task(self),
MainThreadScriptMsg::UserInteraction(task) =>
@@ -1759,6 +1773,14 @@ impl ScriptThread {
}
}
+ /// Handles a worklet being loaded. Does nothing if the page no longer exists.
+ fn handle_worklet_loaded(&self, pipeline_id: PipelineId) {
+ let document = self.documents.borrow().find_document(pipeline_id);
+ if let Some(document) = document {
+ self.rebuild_and_force_reflow(&document, ReflowReason::WorkletLoaded);
+ }
+ }
+
/// Notify a window of a storage event
fn handle_storage_event(&self, pipeline_id: PipelineId, storage_type: StorageType, url: ServoUrl,
key: Option<String>, old_value: Option<String>, new_value: Option<String>) {
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 1eb94f59ae2..540bf7b47fc 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -41,6 +41,7 @@ pub mod webdriver_msg;
use app_units::Au;
use bluetooth_traits::BluetoothRequest;
+use canvas_traits::CanvasData;
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
use gfx_traits::Epoch;
@@ -826,12 +827,12 @@ impl From<RecvTimeoutError> for PaintWorkletError {
}
}
-/// Execute paint code in the worklet thread pool.<
+/// 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>;
+ concrete_object_size: Size2D<Au>,
+ sender: IpcSender<CanvasData>);
}
diff --git a/tests/wpt/mozilla/meta/mozilla/worklets/test_paint_worklet.html.ini b/tests/wpt/mozilla/meta/mozilla/worklets/test_paint_worklet.html.ini
deleted file mode 100644
index b4bd067b933..00000000000
--- a/tests/wpt/mozilla/meta/mozilla/worklets/test_paint_worklet.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[test_paint_worklet.html]
- type: reftest
- expected: FAIL