mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implemented paint worklet rendering context.
This commit is contained in:
parent
d47de6ccfc
commit
328fb25a65
15 changed files with 547 additions and 110 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 style = window.GetComputedStyle(&*self.canvas.upcast(), None);
|
||||
let window = window_from_node(canvas);
|
||||
|
||||
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)))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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>);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[test_paint_worklet.html]
|
||||
type: reftest
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue