diff options
-rw-r--r-- | components/layout/display_list_builder.rs | 25 | ||||
-rw-r--r-- | components/script/dom/bindings/refcounted.rs | 2 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 57 | ||||
-rw-r--r-- | components/script/dom/paintrenderingcontext2d.rs | 359 | ||||
-rw-r--r-- | components/script/dom/paintworkletglobalscope.rs | 98 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasGradient.webidl | 1 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasPattern.webidl | 1 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasRenderingContext2D.webidl | 22 | ||||
-rw-r--r-- | components/script/dom/webidls/PaintRenderingContext2D.webidl | 22 | ||||
-rw-r--r-- | components/script/dom/window.rs | 2 | ||||
-rw-r--r-- | components/script/dom/worklet.rs | 32 | ||||
-rw-r--r-- | components/script/dom/workletglobalscope.rs | 4 | ||||
-rw-r--r-- | components/script/script_thread.rs | 22 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 7 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/mozilla/worklets/test_paint_worklet.html.ini | 3 |
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 |