mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01: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.
|
// TODO: add a one-place cache to avoid drawing the paint image every time.
|
||||||
// https://github.com/servo/servo/issues/17369
|
// https://github.com/servo/servo/issues/17369
|
||||||
debug!("Drawing a paint image {}({},{}).", name, size.width.to_px(), size.height.to_px());
|
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) {
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
Ok(image) => image,
|
executor.draw_a_paint_image(name, size, sender);
|
||||||
Err(err) => return warn!("Error running paint worklet ({:?}).", err),
|
|
||||||
|
// 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,
|
self.build_display_list_for_webrender_image(state,
|
||||||
style,
|
style,
|
||||||
display_list_section,
|
display_list_section,
|
||||||
absolute_bounds,
|
absolute_bounds,
|
||||||
clip,
|
clip,
|
||||||
WebRenderImageInfo::from_image(&image),
|
webrender_image,
|
||||||
index);
|
index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ impl TrustedPromise {
|
||||||
struct RejectPromise(TrustedPromise, Error);
|
struct RejectPromise(TrustedPromise, Error);
|
||||||
impl Runnable for RejectPromise {
|
impl Runnable for RejectPromise {
|
||||||
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
||||||
|
debug!("Rejecting promise.");
|
||||||
let this = *self;
|
let this = *self;
|
||||||
let cx = script_thread.get_cx();
|
let cx = script_thread.get_cx();
|
||||||
let promise = this.0.root();
|
let promise = this.0.root();
|
||||||
|
@ -145,6 +146,7 @@ impl TrustedPromise {
|
||||||
struct ResolvePromise<T>(TrustedPromise, T);
|
struct ResolvePromise<T>(TrustedPromise, T);
|
||||||
impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> {
|
impl<T: ToJSValConvertible> Runnable for ResolvePromise<T> {
|
||||||
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
fn main_thread_handler(self: Box<Self>, script_thread: &ScriptThread) {
|
||||||
|
debug!("Resolving promise.");
|
||||||
let this = *self;
|
let this = *self;
|
||||||
let cx = script_thread.get_cx();
|
let cx = script_thread.get_cx();
|
||||||
let promise = this.0.root();
|
let promise = this.0.root();
|
||||||
|
|
|
@ -61,7 +61,9 @@ pub struct CanvasRenderingContext2D {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
||||||
ipc_renderer: IpcSender<CanvasMsg>,
|
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>,
|
state: DOMRefCell<CanvasContextState>,
|
||||||
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
||||||
origin_clean: Cell<bool>,
|
origin_clean: Cell<bool>,
|
||||||
|
@ -109,18 +111,21 @@ impl CanvasContextState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanvasRenderingContext2D {
|
impl CanvasRenderingContext2D {
|
||||||
fn new_inherited(global: &GlobalScope,
|
pub fn new_inherited(global: &GlobalScope,
|
||||||
canvas: &HTMLCanvasElement,
|
canvas: Option<&HTMLCanvasElement>,
|
||||||
size: Size2D<i32>)
|
size: Size2D<i32>)
|
||||||
-> CanvasRenderingContext2D {
|
-> CanvasRenderingContext2D {
|
||||||
|
debug!("Creating new canvas rendering context.");
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let constellation_chan = global.constellation_chan();
|
let constellation_chan = global.constellation_chan();
|
||||||
|
debug!("Asking constellation to create new canvas thread.");
|
||||||
constellation_chan.send(ConstellationMsg::CreateCanvasPaintThread(size, sender)).unwrap();
|
constellation_chan.send(ConstellationMsg::CreateCanvasPaintThread(size, sender)).unwrap();
|
||||||
let ipc_renderer = receiver.recv().unwrap();
|
let ipc_renderer = receiver.recv().unwrap();
|
||||||
|
debug!("Done.");
|
||||||
CanvasRenderingContext2D {
|
CanvasRenderingContext2D {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
ipc_renderer: ipc_renderer,
|
ipc_renderer: ipc_renderer,
|
||||||
canvas: JS::from_ref(canvas),
|
canvas: canvas.map(JS::from_ref),
|
||||||
state: DOMRefCell::new(CanvasContextState::new()),
|
state: DOMRefCell::new(CanvasContextState::new()),
|
||||||
saved_states: DOMRefCell::new(Vec::new()),
|
saved_states: DOMRefCell::new(Vec::new()),
|
||||||
origin_clean: Cell::new(true),
|
origin_clean: Cell::new(true),
|
||||||
|
@ -131,7 +136,7 @@ impl CanvasRenderingContext2D {
|
||||||
canvas: &HTMLCanvasElement,
|
canvas: &HTMLCanvasElement,
|
||||||
size: Size2D<i32>)
|
size: Size2D<i32>)
|
||||||
-> Root<CanvasRenderingContext2D> {
|
-> Root<CanvasRenderingContext2D> {
|
||||||
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, canvas, size),
|
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, Some(canvas), size),
|
||||||
global,
|
global,
|
||||||
CanvasRenderingContext2DBinding::Wrap)
|
CanvasRenderingContext2DBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
@ -155,7 +160,9 @@ impl CanvasRenderingContext2D {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_as_dirty(&self) {
|
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) {
|
fn update_transform(&self) {
|
||||||
|
@ -226,8 +233,12 @@ impl CanvasRenderingContext2D {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) =>
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) =>
|
||||||
image.origin_is_clean(),
|
image.origin_is_clean(),
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => {
|
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 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
|
document.url().clone().origin() == image_origin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +358,7 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
|
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(
|
let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf(
|
||||||
image_size, dest_rect, source_rect, smoothing_enabled));
|
image_size, dest_rect, source_rect, smoothing_enabled));
|
||||||
self.ipc_renderer.send(msg).unwrap();
|
self.ipc_renderer.send(msg).unwrap();
|
||||||
|
@ -442,8 +453,10 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse {
|
||||||
let window = window_from_node(&*self.canvas);
|
self.canvas.as_ref()
|
||||||
canvas_utils::request_image_from_cache(&window, url)
|
.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>> {
|
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
|
// TODO: will need to check that the context bitmap mode is fixed
|
||||||
// once we implement CanvasProxy
|
// 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 =
|
let element_not_rendered =
|
||||||
!self.canvas.upcast::<Node>().is_in_doc() ||
|
!canvas.upcast::<Node>().is_in_doc() ||
|
||||||
style.GetPropertyValue(DOMString::from("display")) == "none";
|
style.GetPropertyValue(DOMString::from("display")) == "none";
|
||||||
|
|
||||||
if element_not_rendered {
|
if element_not_rendered {
|
||||||
|
@ -530,7 +551,9 @@ impl LayoutCanvasRenderingContext2DHelpers for LayoutJS<CanvasRenderingContext2D
|
||||||
impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas
|
||||||
fn Canvas(&self) -> Root<HTMLCanvasElement> {
|
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
|
// 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 (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
|
||||||
let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
|
let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
|
||||||
Size2D::new(sw as i32, sh as i32));
|
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);
|
let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
|
||||||
self.ipc_renderer
|
self.ipc_renderer
|
||||||
.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)))
|
.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
|
* 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/. */
|
* 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;
|
||||||
|
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::js::Root;
|
||||||
use dom::bindings::reflector::Reflector;
|
use dom::bindings::num::Finite;
|
||||||
use dom::bindings::reflector::reflect_dom_object;
|
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::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use euclid::Size2D;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct PaintRenderingContext2D {
|
pub struct PaintRenderingContext2D {
|
||||||
reflector: Reflector,
|
context: CanvasRenderingContext2D,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintRenderingContext2D {
|
impl PaintRenderingContext2D {
|
||||||
fn new_inherited() -> PaintRenderingContext2D {
|
fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D {
|
||||||
|
let size = Size2D::zero();
|
||||||
PaintRenderingContext2D {
|
PaintRenderingContext2D {
|
||||||
reflector: Reflector::new(),
|
context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(global: &PaintWorkletGlobalScope) -> Root<PaintRenderingContext2D> {
|
pub fn new(global: &PaintWorkletGlobalScope) -> Root<PaintRenderingContext2D> {
|
||||||
reflect_dom_object(box PaintRenderingContext2D::new_inherited(),
|
reflect_dom_object(box PaintRenderingContext2D::new_inherited(global),
|
||||||
global,
|
global,
|
||||||
PaintRenderingContext2DBinding::Wrap)
|
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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use canvas_traits::CanvasData;
|
||||||
|
use canvas_traits::CanvasImageData;
|
||||||
use dom::bindings::callback::CallbackContainer;
|
use dom::bindings::callback::CallbackContainer;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
|
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::conversions::get_property_jsval;
|
||||||
use dom::bindings::error::Error;
|
use dom::bindings::error::Error;
|
||||||
use dom::bindings::error::Fallible;
|
use dom::bindings::error::Fallible;
|
||||||
|
use dom::bindings::js::JS;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::reflector::DomObject;
|
use dom::bindings::reflector::DomObject;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
|
@ -22,6 +25,7 @@ use dom::workletglobalscope::WorkletGlobalScope;
|
||||||
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use js::jsapi::Call;
|
use js::jsapi::Call;
|
||||||
use js::jsapi::Construct1;
|
use js::jsapi::Construct1;
|
||||||
|
@ -40,7 +44,7 @@ use js::rust::Runtime;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image::base::Image;
|
use net_traits::image::base::Image;
|
||||||
use net_traits::image::base::PixelFormat;
|
use net_traits::image::base::PixelFormat;
|
||||||
use script_traits::PaintWorkletError;
|
use net_traits::image_cache::ImageCache;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -48,19 +52,20 @@ use std::collections::HashMap;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
|
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct PaintWorkletGlobalScope {
|
pub struct PaintWorkletGlobalScope {
|
||||||
/// The worklet global for this object
|
/// The worklet global for this object
|
||||||
worklet_global: WorkletGlobalScope,
|
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
|
/// https://drafts.css-houdini.org/css-paint-api/#paint-definitions
|
||||||
paint_definitions: DOMRefCell<HashMap<Atom, Box<PaintDefinition>>>,
|
paint_definitions: DOMRefCell<HashMap<Atom, Box<PaintDefinition>>>,
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#paint-class-instances
|
/// https://drafts.css-houdini.org/css-paint-api/#paint-class-instances
|
||||||
paint_class_instances: DOMRefCell<HashMap<Atom, Box<Heap<JSVal>>>>,
|
paint_class_instances: DOMRefCell<HashMap<Atom, Box<Heap<JSVal>>>>,
|
||||||
/// A buffer to draw into
|
|
||||||
buffer: DOMRefCell<Vec<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintWorkletGlobalScope {
|
impl PaintWorkletGlobalScope {
|
||||||
|
@ -73,9 +78,9 @@ impl PaintWorkletGlobalScope {
|
||||||
debug!("Creating paint worklet global scope for pipeline {}.", pipeline_id);
|
debug!("Creating paint worklet global scope for pipeline {}.", pipeline_id);
|
||||||
let global = box PaintWorkletGlobalScope {
|
let global = box PaintWorkletGlobalScope {
|
||||||
worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init),
|
worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init),
|
||||||
|
image_cache: init.image_cache.clone(),
|
||||||
paint_definitions: Default::default(),
|
paint_definitions: Default::default(),
|
||||||
paint_class_instances: Default::default(),
|
paint_class_instances: Default::default(),
|
||||||
buffer: Default::default(),
|
|
||||||
};
|
};
|
||||||
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
|
||||||
}
|
}
|
||||||
|
@ -90,7 +95,7 @@ impl PaintWorkletGlobalScope {
|
||||||
fn draw_a_paint_image(&self,
|
fn draw_a_paint_image(&self,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
size: Size2D<Au>,
|
size: Size2D<Au>,
|
||||||
sender: Sender<Result<Image, PaintWorkletError>>)
|
sender: IpcSender<CanvasData>)
|
||||||
{
|
{
|
||||||
// TODO: document paint definitions.
|
// TODO: document paint definitions.
|
||||||
self.invoke_a_paint_callback(name, size, sender);
|
self.invoke_a_paint_callback(name, size, sender);
|
||||||
|
@ -101,7 +106,7 @@ impl PaintWorkletGlobalScope {
|
||||||
fn invoke_a_paint_callback(&self,
|
fn invoke_a_paint_callback(&self,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
size: Size2D<Au>,
|
size: Size2D<Au>,
|
||||||
sender: Sender<Result<Image, PaintWorkletError>>)
|
sender: IpcSender<CanvasData>)
|
||||||
{
|
{
|
||||||
let width = size.width.to_px().abs() as u32;
|
let width = size.width.to_px().abs() as u32;
|
||||||
let height = size.height.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.
|
// Step 2.2-5.1.
|
||||||
rooted!(in(cx) let mut class_constructor = UndefinedValue());
|
rooted!(in(cx) let mut class_constructor = UndefinedValue());
|
||||||
rooted!(in(cx) let mut paint_function = 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 => {
|
None => {
|
||||||
// Step 2.2.
|
// Step 2.2.
|
||||||
warn!("Drawing un-registered paint definition {}.", name);
|
warn!("Drawing un-registered paint definition {}.", name);
|
||||||
let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
|
return self.send_invalid_image(size, sender);
|
||||||
let _ = sender.send(Ok(image));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Some(definition) => {
|
Some(definition) => {
|
||||||
// Step 5.1
|
// Step 5.1
|
||||||
if !definition.constructor_valid_flag.get() {
|
if !definition.constructor_valid_flag.get() {
|
||||||
debug!("Drawing invalid paint definition {}.", name);
|
debug!("Drawing invalid paint definition {}.", name);
|
||||||
let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
|
return self.send_invalid_image(size, sender);
|
||||||
let _ = sender.send(Ok(image));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
class_constructor.set(definition.class_constructor.get());
|
class_constructor.set(definition.class_constructor.get());
|
||||||
paint_function.set(definition.paint_function.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)
|
self.paint_definitions.borrow_mut().get_mut(&name)
|
||||||
.expect("Vanishing paint definition.")
|
.expect("Vanishing paint definition.")
|
||||||
.constructor_valid_flag.set(false);
|
.constructor_valid_flag.set(false);
|
||||||
let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
|
return self.send_invalid_image(size, sender);
|
||||||
let _ = sender.send(Ok(image));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Step 5.4
|
// Step 5.4
|
||||||
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
|
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
|
||||||
|
@ -166,7 +166,9 @@ impl PaintWorkletGlobalScope {
|
||||||
|
|
||||||
// TODO: Steps 6-7
|
// TODO: Steps 6-7
|
||||||
// Step 8
|
// 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
|
// Step 9
|
||||||
let paint_size = PaintSize::new(self, size);
|
let paint_size = PaintSize::new(self, size);
|
||||||
|
@ -186,37 +188,37 @@ impl PaintWorkletGlobalScope {
|
||||||
if unsafe { JS_IsExceptionPending(cx) } {
|
if unsafe { JS_IsExceptionPending(cx) } {
|
||||||
debug!("Paint function threw an exception {}.", name);
|
debug!("Paint function threw an exception {}.", name);
|
||||||
unsafe { JS_ClearPendingException(cx); }
|
unsafe { JS_ClearPendingException(cx); }
|
||||||
let image = self.placeholder_image(width, height, [0x00, 0x00, 0xFF, 0xFF]);
|
return self.send_invalid_image(size, sender);
|
||||||
let _ = sender.send(Ok(image));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, we just build a dummy image.
|
rendering_context.send_data(sender);
|
||||||
let image = self.placeholder_image(width, height, [0xFF, 0x00, 0x00, 0xFF]);
|
|
||||||
let _ = sender.send(Ok(image));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn placeholder_image(&self, width: u32, height: u32, pixel: [u8; 4]) -> Image {
|
fn send_invalid_image(&self, size: Size2D<Au>, sender: IpcSender<CanvasData>) {
|
||||||
let area = (width as usize) * (height as usize);
|
debug!("Sending an invalid image.");
|
||||||
let old_buffer_size = self.buffer.borrow().len();
|
let width = size.width.to_px().abs() as u32;
|
||||||
let new_buffer_size = area * 4;
|
let height = size.height.to_px().abs() as u32;
|
||||||
if new_buffer_size > old_buffer_size {
|
let len = (width as usize) * (height as usize) * 4;
|
||||||
self.buffer.borrow_mut().extend(pixel.iter().cycle().take(new_buffer_size - old_buffer_size));
|
let pixel = [0xFF, 0x00, 0x00, 0xFF];
|
||||||
} else {
|
let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect();
|
||||||
self.buffer.borrow_mut().truncate(new_buffer_size);
|
let mut image = Image {
|
||||||
}
|
|
||||||
Image {
|
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
format: PixelFormat::BGRA8,
|
format: PixelFormat::BGRA8,
|
||||||
bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()),
|
bytes: IpcSharedMemory::from_bytes(&*bytes),
|
||||||
id: None,
|
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 {
|
impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint
|
/// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint
|
||||||
fn RegisterPaint(&self, name: DOMString, paint_ctor: Rc<VoidFunction>) -> Fallible<()> {
|
fn RegisterPaint(&self, name: DOMString, paint_ctor: Rc<VoidFunction>) -> Fallible<()> {
|
||||||
let name = Atom::from(name);
|
let name = Atom::from(name);
|
||||||
|
@ -279,11 +281,17 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
|
||||||
return Err(Error::Type(String::from("Paint function is not callable.")));
|
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);
|
debug!("Registering definition {}.", name);
|
||||||
self.paint_definitions.borrow_mut()
|
self.paint_definitions.borrow_mut().insert(name, definition);
|
||||||
.insert(name,
|
|
||||||
PaintDefinition::new(paint_val.handle(), paint_function.handle(), input_properties, alpha));
|
|
||||||
|
|
||||||
// TODO: Step 21.
|
// TODO: Step 21.
|
||||||
|
|
||||||
|
@ -293,7 +301,7 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
|
||||||
|
|
||||||
/// Tasks which can be peformed by a paint worklet
|
/// Tasks which can be peformed by a paint worklet
|
||||||
pub enum PaintWorkletTask {
|
pub enum PaintWorkletTask {
|
||||||
DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>)
|
DrawAPaintImage(Atom, Size2D<Au>, IpcSender<CanvasData>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A paint definition
|
/// A paint definition
|
||||||
|
@ -308,13 +316,18 @@ struct PaintDefinition {
|
||||||
constructor_valid_flag: Cell<bool>,
|
constructor_valid_flag: Cell<bool>,
|
||||||
input_properties: Vec<DOMString>,
|
input_properties: Vec<DOMString>,
|
||||||
context_alpha_flag: bool,
|
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 {
|
impl PaintDefinition {
|
||||||
fn new(class_constructor: HandleValue,
|
fn new(class_constructor: HandleValue,
|
||||||
paint_function: HandleValue,
|
paint_function: HandleValue,
|
||||||
input_properties: Vec<DOMString>,
|
input_properties: Vec<DOMString>,
|
||||||
alpha: bool)
|
alpha: bool,
|
||||||
|
context: &PaintRenderingContext2D)
|
||||||
-> Box<PaintDefinition>
|
-> Box<PaintDefinition>
|
||||||
{
|
{
|
||||||
let result = Box::new(PaintDefinition {
|
let result = Box::new(PaintDefinition {
|
||||||
|
@ -323,6 +336,7 @@ impl PaintDefinition {
|
||||||
constructor_valid_flag: Cell::new(true),
|
constructor_valid_flag: Cell::new(true),
|
||||||
input_properties: input_properties,
|
input_properties: input_properties,
|
||||||
context_alpha_flag: alpha,
|
context_alpha_flag: alpha,
|
||||||
|
context: JS::from_ref(context),
|
||||||
});
|
});
|
||||||
result.class_constructor.set(class_constructor.get());
|
result.class_constructor.set(class_constructor.get());
|
||||||
result.paint_function.set(paint_function.get());
|
result.paint_function.set(paint_function.get());
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#canvasgradient
|
// https://html.spec.whatwg.org/multipage/#canvasgradient
|
||||||
|
[Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasGradient {
|
interface CanvasGradient {
|
||||||
// opaque object
|
// opaque object
|
||||||
[Throws]
|
[Throws]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#canvaspattern
|
// https://html.spec.whatwg.org/multipage/#canvaspattern
|
||||||
|
[Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasPattern {
|
interface CanvasPattern {
|
||||||
//void setTransform(SVGMatrix matrix);
|
//void setTransform(SVGMatrix matrix);
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,14 +41,14 @@ CanvasRenderingContext2D implements CanvasPathDrawingStyles;
|
||||||
CanvasRenderingContext2D implements CanvasTextDrawingStyles;
|
CanvasRenderingContext2D implements CanvasTextDrawingStyles;
|
||||||
CanvasRenderingContext2D implements CanvasPath;
|
CanvasRenderingContext2D implements CanvasPath;
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasState {
|
interface CanvasState {
|
||||||
// state
|
// state
|
||||||
void save(); // push state on state stack
|
void save(); // push state on state stack
|
||||||
void restore(); // pop state stack and restore state
|
void restore(); // pop state stack and restore state
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasTransform {
|
interface CanvasTransform {
|
||||||
// transformations (default transform is the identity matrix)
|
// transformations (default transform is the identity matrix)
|
||||||
void scale(unrestricted double x, unrestricted double y);
|
void scale(unrestricted double x, unrestricted double y);
|
||||||
|
@ -72,21 +72,21 @@ interface CanvasTransform {
|
||||||
void resetTransform();
|
void resetTransform();
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasCompositing {
|
interface CanvasCompositing {
|
||||||
// compositing
|
// compositing
|
||||||
attribute unrestricted double globalAlpha; // (default 1.0)
|
attribute unrestricted double globalAlpha; // (default 1.0)
|
||||||
attribute DOMString globalCompositeOperation; // (default source-over)
|
attribute DOMString globalCompositeOperation; // (default source-over)
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasImageSmoothing {
|
interface CanvasImageSmoothing {
|
||||||
// image smoothing
|
// image smoothing
|
||||||
attribute boolean imageSmoothingEnabled; // (default true)
|
attribute boolean imageSmoothingEnabled; // (default true)
|
||||||
// attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
|
// attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasFillStrokeStyles {
|
interface CanvasFillStrokeStyles {
|
||||||
|
|
||||||
// colours and styles (see also the CanvasDrawingStyles interface)
|
// colours and styles (see also the CanvasDrawingStyles interface)
|
||||||
|
@ -99,7 +99,7 @@ interface CanvasFillStrokeStyles {
|
||||||
CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
|
CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasShadowStyles {
|
interface CanvasShadowStyles {
|
||||||
// shadows
|
// shadows
|
||||||
attribute unrestricted double shadowOffsetX; // (default 0)
|
attribute unrestricted double shadowOffsetX; // (default 0)
|
||||||
|
@ -108,7 +108,7 @@ interface CanvasShadowStyles {
|
||||||
attribute DOMString shadowColor; // (default transparent black)
|
attribute DOMString shadowColor; // (default transparent black)
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasRect {
|
interface CanvasRect {
|
||||||
// rects
|
// rects
|
||||||
//[LenientFloat]
|
//[LenientFloat]
|
||||||
|
@ -119,7 +119,7 @@ interface CanvasRect {
|
||||||
void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasDrawPath {
|
interface CanvasDrawPath {
|
||||||
// path API (see also CanvasPathMethods)
|
// path API (see also CanvasPathMethods)
|
||||||
void beginPath();
|
void beginPath();
|
||||||
|
@ -157,7 +157,7 @@ interface CanvasText {
|
||||||
//TextMetrics measureText(DOMString text);
|
//TextMetrics measureText(DOMString text);
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasDrawImage {
|
interface CanvasDrawImage {
|
||||||
// drawing images
|
// drawing images
|
||||||
[Throws]
|
[Throws]
|
||||||
|
@ -205,7 +205,7 @@ enum CanvasTextAlign { "start", "end", "left", "right", "center" };
|
||||||
enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" };
|
enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" };
|
||||||
enum CanvasDirection { "ltr", "rtl", "inherit" };
|
enum CanvasDirection { "ltr", "rtl", "inherit" };
|
||||||
|
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject, Exposed=(Window, PaintWorklet)]
|
||||||
interface CanvasPathDrawingStyles {
|
interface CanvasPathDrawingStyles {
|
||||||
// line caps/joins
|
// line caps/joins
|
||||||
attribute unrestricted double lineWidth; // (default 1)
|
attribute unrestricted double lineWidth; // (default 1)
|
||||||
|
@ -229,7 +229,7 @@ interface CanvasTextDrawingStyles {
|
||||||
//attribute CanvasDirection direction; // "ltr", "rtl", "inherit" (default: "inherit")
|
//attribute CanvasDirection direction; // "ltr", "rtl", "inherit" (default: "inherit")
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
[NoInterfaceObject, Exposed=(Window, Worker, PaintWorklet)]
|
||||||
interface CanvasPath {
|
interface CanvasPath {
|
||||||
// shared path API methods
|
// shared path API methods
|
||||||
void closePath();
|
void closePath();
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
[Exposed=PaintWorklet]
|
[Exposed=PaintWorklet]
|
||||||
interface PaintRenderingContext2D {
|
interface PaintRenderingContext2D {
|
||||||
};
|
};
|
||||||
// PaintRenderingContext2D implements CanvasState;
|
PaintRenderingContext2D implements CanvasState;
|
||||||
// PaintRenderingContext2D implements CanvasTransform;
|
PaintRenderingContext2D implements CanvasTransform;
|
||||||
// PaintRenderingContext2D implements CanvasCompositing;
|
PaintRenderingContext2D implements CanvasCompositing;
|
||||||
// PaintRenderingContext2D implements CanvasImageSmoothing;
|
PaintRenderingContext2D implements CanvasImageSmoothing;
|
||||||
// PaintRenderingContext2D implements CanvasFillStrokeStyles;
|
PaintRenderingContext2D implements CanvasFillStrokeStyles;
|
||||||
// PaintRenderingContext2D implements CanvasShadowStyles;
|
PaintRenderingContext2D implements CanvasShadowStyles;
|
||||||
// PaintRenderingContext2D implements CanvasRect;
|
PaintRenderingContext2D implements CanvasRect;
|
||||||
// PaintRenderingContext2D implements CanvasDrawPath;
|
PaintRenderingContext2D implements CanvasDrawPath;
|
||||||
// PaintRenderingContext2D implements CanvasDrawImage;
|
PaintRenderingContext2D implements CanvasDrawImage;
|
||||||
// PaintRenderingContext2D implements CanvasPathDrawingStyles;
|
PaintRenderingContext2D implements CanvasPathDrawingStyles;
|
||||||
// PaintRenderingContext2D implements CanvasPath;
|
PaintRenderingContext2D implements CanvasPath;
|
||||||
|
|
|
@ -152,6 +152,7 @@ pub enum ReflowReason {
|
||||||
ImageLoaded,
|
ImageLoaded,
|
||||||
RequestAnimationFrame,
|
RequestAnimationFrame,
|
||||||
WebFontLoaded,
|
WebFontLoaded,
|
||||||
|
WorkletLoaded,
|
||||||
FramedContentChanged,
|
FramedContentChanged,
|
||||||
IFrameLoadEvent,
|
IFrameLoadEvent,
|
||||||
MissingExplicitReflow,
|
MissingExplicitReflow,
|
||||||
|
@ -1939,6 +1940,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
|
||||||
ReflowReason::ImageLoaded => "\tImageLoaded",
|
ReflowReason::ImageLoaded => "\tImageLoaded",
|
||||||
ReflowReason::RequestAnimationFrame => "\tRequestAnimationFrame",
|
ReflowReason::RequestAnimationFrame => "\tRequestAnimationFrame",
|
||||||
ReflowReason::WebFontLoaded => "\tWebFontLoaded",
|
ReflowReason::WebFontLoaded => "\tWebFontLoaded",
|
||||||
|
ReflowReason::WorkletLoaded => "\tWorkletLoaded",
|
||||||
ReflowReason::FramedContentChanged => "\tFramedContentChanged",
|
ReflowReason::FramedContentChanged => "\tFramedContentChanged",
|
||||||
ReflowReason::IFrameLoadEvent => "\tIFrameLoadEvent",
|
ReflowReason::IFrameLoadEvent => "\tIFrameLoadEvent",
|
||||||
ReflowReason::MissingExplicitReflow => "\tMissingExplicitReflow",
|
ReflowReason::MissingExplicitReflow => "\tMissingExplicitReflow",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
//! a backup thread, not on the primary worklet thread.
|
//! a backup thread, not on the primary worklet thread.
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use canvas_traits::CanvasData;
|
||||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
||||||
|
@ -38,6 +39,7 @@ use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||||
use dom::workletglobalscope::WorkletTask;
|
use dom::workletglobalscope::WorkletTask;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
use js::jsapi::JSGCParamKey;
|
use js::jsapi::JSGCParamKey;
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
use js::jsapi::JS_GC;
|
use js::jsapi::JS_GC;
|
||||||
|
@ -45,7 +47,6 @@ use js::jsapi::JS_GetGCParameter;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use net_traits::image::base::Image;
|
|
||||||
use net_traits::load_whole_resource;
|
use net_traits::load_whole_resource;
|
||||||
use net_traits::request::Destination;
|
use net_traits::request::Destination;
|
||||||
use net_traits::request::RequestInit;
|
use net_traits::request::RequestInit;
|
||||||
|
@ -58,7 +59,6 @@ use script_runtime::new_rt_and_cx;
|
||||||
use script_thread::MainThreadScriptMsg;
|
use script_thread::MainThreadScriptMsg;
|
||||||
use script_thread::Runnable;
|
use script_thread::Runnable;
|
||||||
use script_thread::ScriptThread;
|
use script_thread::ScriptThread;
|
||||||
use script_traits::PaintWorkletError;
|
|
||||||
use script_traits::PaintWorkletExecutor;
|
use script_traits::PaintWorkletExecutor;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use servo_rand;
|
use servo_rand;
|
||||||
|
@ -76,7 +76,6 @@ use std::sync::mpsc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use swapper::Swapper;
|
use swapper::Swapper;
|
||||||
use swapper::swapper;
|
use swapper::swapper;
|
||||||
|
@ -85,7 +84,6 @@ use uuid::Uuid;
|
||||||
// Magic numbers
|
// Magic numbers
|
||||||
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
||||||
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
||||||
const PAINT_TIMEOUT_MILLISECONDS: u64 = 10;
|
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
/// https://drafts.css-houdini.org/worklets/#worklet
|
/// https://drafts.css-houdini.org/worklets/#worklet
|
||||||
|
@ -163,6 +161,7 @@ impl WorkletMethods for Worklet {
|
||||||
&promise);
|
&promise);
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
|
debug!("Returning promise.");
|
||||||
promise
|
promise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +233,17 @@ impl PendingTasksStruct {
|
||||||
/// The thread pool lives in the script thread, and is initialized
|
/// The thread pool lives in the script thread, and is initialized
|
||||||
/// when a worklet adds a module. It is dropped when the script thread
|
/// when a worklet adds a module. It is dropped when the script thread
|
||||||
/// is dropped, and asks each of the worklet threads to quit.
|
/// 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)]
|
#[derive(Clone, JSTraceable)]
|
||||||
pub struct WorkletThreadPool {
|
pub struct WorkletThreadPool {
|
||||||
|
@ -551,6 +561,7 @@ impl WorkletThread {
|
||||||
match self.global_scopes.entry(worklet_id) {
|
match self.global_scopes.entry(worklet_id) {
|
||||||
hash_map::Entry::Occupied(entry) => Root::from_ref(entry.get()),
|
hash_map::Entry::Occupied(entry) => Root::from_ref(entry.get()),
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => {
|
||||||
|
debug!("Creating new worklet global scope.");
|
||||||
let result = global_type.new(&self.runtime, pipeline_id, base_url, &self.global_init);
|
let result = global_type.new(&self.runtime, pipeline_id, base_url, &self.global_init);
|
||||||
entry.insert(JS::from_ref(&*result));
|
entry.insert(JS::from_ref(&*result));
|
||||||
result
|
result
|
||||||
|
@ -562,6 +573,7 @@ impl WorkletThread {
|
||||||
/// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
|
/// https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script
|
||||||
fn fetch_and_invoke_a_worklet_script(&self,
|
fn fetch_and_invoke_a_worklet_script(&self,
|
||||||
global_scope: &WorkletGlobalScope,
|
global_scope: &WorkletGlobalScope,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
origin: ImmutableOrigin,
|
origin: ImmutableOrigin,
|
||||||
script_url: ServoUrl,
|
script_url: ServoUrl,
|
||||||
credentials: RequestCredentials,
|
credentials: RequestCredentials,
|
||||||
|
@ -612,7 +624,9 @@ impl WorkletThread {
|
||||||
debug!("Finished adding script.");
|
debug!("Finished adding script.");
|
||||||
let old_counter = pending_tasks_struct.decrement_counter_by(1);
|
let old_counter = pending_tasks_struct.decrement_counter_by(1);
|
||||||
if old_counter == 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(()));
|
self.run_in_script_thread(promise.resolve_runnable(()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,6 +652,7 @@ impl WorkletThread {
|
||||||
global_type,
|
global_type,
|
||||||
base_url);
|
base_url);
|
||||||
self.fetch_and_invoke_a_worklet_script(&*global,
|
self.fetch_and_invoke_a_worklet_script(&*global,
|
||||||
|
pipeline_id,
|
||||||
origin,
|
origin,
|
||||||
script_url,
|
script_url,
|
||||||
credentials,
|
credentials,
|
||||||
|
@ -678,13 +693,10 @@ impl PaintWorkletExecutor for WorkletExecutor {
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||||
fn draw_a_paint_image(&self,
|
fn draw_a_paint_image(&self,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
concrete_object_size: Size2D<Au>)
|
concrete_object_size: Size2D<Au>,
|
||||||
-> Result<Image, PaintWorkletError>
|
sender: IpcSender<CanvasData>)
|
||||||
{
|
{
|
||||||
let (sender, receiver) = mpsc::channel();
|
|
||||||
let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender));
|
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);
|
self.schedule_a_worklet_task(task);
|
||||||
receiver.recv_timeout(timeout)?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use microtask::Microtask;
|
||||||
use microtask::MicrotaskQueue;
|
use microtask::MicrotaskQueue;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::ResourceThreads;
|
use net_traits::ResourceThreads;
|
||||||
|
use net_traits::image_cache::ImageCache;
|
||||||
use profile_traits::mem;
|
use profile_traits::mem;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::ScriptMsg;
|
use script_traits::ScriptMsg;
|
||||||
|
@ -27,6 +28,7 @@ use script_traits::TimerSchedulerMsg;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
use servo_url::MutableOrigin;
|
use servo_url::MutableOrigin;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
/// https://drafts.css-houdini.org/worklets/#workletglobalscope
|
/// https://drafts.css-houdini.org/worklets/#workletglobalscope
|
||||||
|
@ -123,6 +125,8 @@ pub struct WorkletGlobalScopeInit {
|
||||||
pub constellation_chan: IpcSender<ScriptMsg>,
|
pub constellation_chan: IpcSender<ScriptMsg>,
|
||||||
/// Message to send to the scheduler
|
/// Message to send to the scheduler
|
||||||
pub scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
pub scheduler_chan: IpcSender<TimerSchedulerMsg>,
|
||||||
|
/// The image cache
|
||||||
|
pub image_cache: Arc<ImageCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
||||||
|
|
|
@ -256,6 +256,9 @@ pub enum MainThreadScriptMsg {
|
||||||
DOMManipulation(DOMManipulationTask),
|
DOMManipulation(DOMManipulationTask),
|
||||||
/// Tasks that originate from the user interaction task source
|
/// Tasks that originate from the user interaction task source
|
||||||
UserInteraction(UserInteractionTask),
|
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> {
|
impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> {
|
||||||
|
@ -724,6 +727,7 @@ impl ScriptThread {
|
||||||
devtools_chan: script_thread.devtools_chan.clone(),
|
devtools_chan: script_thread.devtools_chan.clone(),
|
||||||
constellation_chan: script_thread.constellation_chan.clone(),
|
constellation_chan: script_thread.constellation_chan.clone(),
|
||||||
scheduler_chan: script_thread.scheduler_chan.clone(),
|
scheduler_chan: script_thread.scheduler_chan.clone(),
|
||||||
|
image_cache: script_thread.image_cache.clone(),
|
||||||
};
|
};
|
||||||
Rc::new(WorkletThreadPool::spawn(chan, init))
|
Rc::new(WorkletThreadPool::spawn(chan, init))
|
||||||
}).clone()
|
}).clone()
|
||||||
|
@ -828,6 +832,7 @@ impl ScriptThread {
|
||||||
debug!("Starting script thread.");
|
debug!("Starting script thread.");
|
||||||
while self.handle_msgs() {
|
while self.handle_msgs() {
|
||||||
// Go on...
|
// Go on...
|
||||||
|
debug!("Running script thread.");
|
||||||
}
|
}
|
||||||
debug!("Stopped script thread.");
|
debug!("Stopped script thread.");
|
||||||
}
|
}
|
||||||
|
@ -856,6 +861,7 @@ impl ScriptThread {
|
||||||
let mut sequential = vec![];
|
let mut sequential = vec![];
|
||||||
|
|
||||||
// Receive at least one message so we don't spinloop.
|
// Receive at least one message so we don't spinloop.
|
||||||
|
debug!("Waiting for event.");
|
||||||
let mut event = {
|
let mut event = {
|
||||||
let sel = Select::new();
|
let sel = Select::new();
|
||||||
let mut script_port = sel.handle(&self.port);
|
let mut script_port = sel.handle(&self.port);
|
||||||
|
@ -887,6 +893,7 @@ impl ScriptThread {
|
||||||
panic!("unexpected select result")
|
panic!("unexpected select result")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
debug!("Got event.");
|
||||||
|
|
||||||
// Squash any pending resize, reflow, animation tick, and mouse-move events in the queue.
|
// Squash any pending resize, reflow, animation tick, and mouse-move events in the queue.
|
||||||
let mut mouse_move_event_index = None;
|
let mut mouse_move_event_index = None;
|
||||||
|
@ -983,6 +990,7 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the gathered events.
|
// Process the gathered events.
|
||||||
|
debug!("Processing events.");
|
||||||
for msg in sequential {
|
for msg in sequential {
|
||||||
debug!("Processing event {:?}.", msg);
|
debug!("Processing event {:?}.", msg);
|
||||||
let category = self.categorize_msg(&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)
|
// 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
|
// TODO(gw): In the future we could probably batch other types of reflows
|
||||||
// into this loop too, but for now it's only images.
|
// into this loop too, but for now it's only images.
|
||||||
|
debug!("Issuing batched reflows.");
|
||||||
for (_, document) in self.documents.borrow().iter() {
|
for (_, document) in self.documents.borrow().iter() {
|
||||||
let window = document.window();
|
let window = document.window();
|
||||||
let pending_reflows = window.get_pending_reflow_count();
|
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
|
// The category of the runnable is ignored by the pattern, however
|
||||||
// it is still respected by profiling (see categorize_msg).
|
// it is still respected by profiling (see categorize_msg).
|
||||||
if !runnable.is_cancelled() {
|
if !runnable.is_cancelled() {
|
||||||
|
debug!("Running runnable.");
|
||||||
runnable.main_thread_handler(self)
|
runnable.main_thread_handler(self)
|
||||||
|
} else {
|
||||||
|
debug!("Not running cancelled runnable.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
|
MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
|
||||||
self.collect_reports(reports_chan),
|
self.collect_reports(reports_chan),
|
||||||
|
MainThreadScriptMsg::WorkletLoaded(pipeline_id) =>
|
||||||
|
self.handle_worklet_loaded(pipeline_id),
|
||||||
MainThreadScriptMsg::DOMManipulation(task) =>
|
MainThreadScriptMsg::DOMManipulation(task) =>
|
||||||
task.handle_task(self),
|
task.handle_task(self),
|
||||||
MainThreadScriptMsg::UserInteraction(task) =>
|
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
|
/// Notify a window of a storage event
|
||||||
fn handle_storage_event(&self, pipeline_id: PipelineId, storage_type: StorageType, url: ServoUrl,
|
fn handle_storage_event(&self, pipeline_id: PipelineId, storage_type: StorageType, url: ServoUrl,
|
||||||
key: Option<String>, old_value: Option<String>, new_value: Option<String>) {
|
key: Option<String>, old_value: Option<String>, new_value: Option<String>) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ pub mod webdriver_msg;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use bluetooth_traits::BluetoothRequest;
|
use bluetooth_traits::BluetoothRequest;
|
||||||
|
use canvas_traits::CanvasData;
|
||||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
|
use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
|
||||||
use gfx_traits::Epoch;
|
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 {
|
pub trait PaintWorkletExecutor: Sync + Send {
|
||||||
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||||
fn draw_a_paint_image(&self,
|
fn draw_a_paint_image(&self,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
concrete_object_size: Size2D<Au>)
|
concrete_object_size: Size2D<Au>,
|
||||||
-> Result<Image, PaintWorkletError>;
|
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