diff options
-rw-r--r-- | components/layout/display_list_builder.rs | 28 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 170 | ||||
-rw-r--r-- | components/script/dom/cssstylevalue.rs | 16 | ||||
-rw-r--r-- | components/script/dom/paintrenderingcontext2d.rs | 20 | ||||
-rw-r--r-- | components/script/dom/paintworkletglobalscope.rs | 90 | ||||
-rw-r--r-- | components/script/dom/webidls/CanvasRenderingContext2D.webidl | 7 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 22 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/mozilla/css-paint-api/paint2d-image.html.ini | 4 |
8 files changed, 232 insertions, 125 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index c7850965758..d61d3bfbf6e 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1173,7 +1173,6 @@ impl FragmentDisplayListBuilding for Fragment { let device_pixel_ratio = state.layout_context.style_context.device_pixel_ratio(); let size_in_au = unbordered_box.size.to_physical(style.writing_mode); let size_in_px = TypedSize2D::new(size_in_au.width.to_f32_px(), size_in_au.height.to_f32_px()); - let size_in_dpx = size_in_px * device_pixel_ratio; let name = paint_worklet.name.clone(); // Get the painter, and the computed values for its properties. @@ -1192,24 +1191,19 @@ 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_in_px.width, size_in_px.height); - let (sender, receiver) = ipc::channel().unwrap(); - painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, 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_in_dpx.width as u32, - height: size_in_dpx.height 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), + let mut draw_result = painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties); + let webrender_image = WebRenderImageInfo { + width: draw_result.width, + height: draw_result.height, + format: draw_result.format, + key: draw_result.image_key, }; + for url in draw_result.missing_image_urls.drain(..) { + debug!("Requesting missing image URL {}.", url); + state.layout_context.get_webrender_image_for_url(self.node, url, UsePlaceholder::No); + } + self.build_display_list_for_webrender_image(state, style, display_list_section, diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index c1a2be92bcc..f57df77cfa4 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -12,12 +12,12 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding; use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; +use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; 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::ImageDataBinding::ImageDataMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D; use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; @@ -29,21 +29,25 @@ use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeSty use dom::canvaspattern::CanvasPattern; use dom::globalscope::GlobalScope; use dom::htmlcanvaselement::HTMLCanvasElement; -use dom::htmlcanvaselement::utils as canvas_utils; -use dom::htmlimageelement::HTMLImageElement; use dom::imagedata::ImageData; use dom::node::{document_from_node, Node, NodeDamage, window_from_node}; use dom_struct::dom_struct; use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D, vec2}; use ipc_channel::ipc::{self, IpcSender}; use net_traits::image::base::PixelFormat; +use net_traits::image_cache::CanRequestImages; +use net_traits::image_cache::ImageCache; +use net_traits::image_cache::ImageOrMetadataAvailable; use net_traits::image_cache::ImageResponse; +use net_traits::image_cache::ImageState; +use net_traits::image_cache::UsePlaceholder; use num_traits::ToPrimitive; use script_traits::ScriptMsg as ConstellationMsg; use servo_url::ServoUrl; -use std::{cmp, fmt}; +use std::{cmp, fmt, mem}; use std::cell::Cell; use std::str::FromStr; +use std::sync::Arc; use unpremultiplytable::UNPREMULTIPLY_TABLE; #[must_root] @@ -61,9 +65,16 @@ pub struct CanvasRenderingContext2D { reflector_: Reflector, #[ignore_heap_size_of = "Defined in ipc-channel"] ipc_renderer: IpcSender<CanvasMsg>, - // For rendering contexts created by an HTML canvas element, this is Some, - // for ones created by a paint worklet, this is None. + /// 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>>, + #[ignore_heap_size_of = "Arc"] + image_cache: Arc<ImageCache>, + /// Any missing image URLs. + missing_image_urls: DOMRefCell<Vec<ServoUrl>>, + /// The base URL for resolving CSS image URL values. + /// Needed because of https://github.com/servo/servo/issues/17625 + base_url: ServoUrl, state: DOMRefCell<CanvasContextState>, saved_states: DOMRefCell<Vec<CanvasContextState>>, origin_clean: Cell<bool>, @@ -113,6 +124,8 @@ impl CanvasContextState { impl CanvasRenderingContext2D { pub fn new_inherited(global: &GlobalScope, canvas: Option<&HTMLCanvasElement>, + image_cache: Arc<ImageCache>, + base_url: ServoUrl, size: Size2D<i32>) -> CanvasRenderingContext2D { debug!("Creating new canvas rendering context."); @@ -126,6 +139,9 @@ impl CanvasRenderingContext2D { reflector_: Reflector::new(), ipc_renderer: ipc_renderer, canvas: canvas.map(JS::from_ref), + image_cache: image_cache, + missing_image_urls: DOMRefCell::new(Vec::new()), + base_url: base_url, state: DOMRefCell::new(CanvasContextState::new()), saved_states: DOMRefCell::new(Vec::new()), origin_clean: Cell::new(true), @@ -136,9 +152,11 @@ impl CanvasRenderingContext2D { canvas: &HTMLCanvasElement, size: Size2D<i32>) -> Root<CanvasRenderingContext2D> { - reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, Some(canvas), size), - global, - CanvasRenderingContext2DBinding::Wrap) + let window = window_from_node(canvas); + let image_cache = window.image_cache(); + let base_url = window.get_url(); + let boxed = box CanvasRenderingContext2D::new_inherited(global, Some(canvas), image_cache, base_url, size); + reflect_dom_object(boxed, global, CanvasRenderingContext2DBinding::Wrap) } // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions @@ -224,15 +242,15 @@ impl CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean fn is_origin_clean(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D) + image: CanvasImageSource) -> bool { match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(canvas) => { + CanvasImageSource::HTMLCanvasElement(canvas) => { canvas.origin_is_clean() } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(image) => + CanvasImageSource::CanvasRenderingContext2D(image) => image.origin_is_clean(), - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(image) => { + CanvasImageSource::HTMLImageElement(image) => { let canvas = match self.canvas { Some(ref canvas) => canvas, None => return false, @@ -241,6 +259,7 @@ impl CanvasRenderingContext2D { let document = document_from_node(&**canvas); document.url().clone().origin() == image_origin } + CanvasImageSource::CSSStyleValue(_) => true, } } @@ -266,7 +285,7 @@ impl CanvasRenderingContext2D { // // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn draw_image(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: CanvasImageSource, sx: f64, sy: f64, sw: Option<f64>, @@ -277,38 +296,30 @@ impl CanvasRenderingContext2D { dh: Option<f64>) -> ErrorResult { let result = match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => { + CanvasImageSource::HTMLCanvasElement(ref canvas) => { self.draw_html_canvas_element(&canvas, sx, sy, sw, sh, dx, dy, dw, dh) } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref image) => { + CanvasImageSource::CanvasRenderingContext2D(ref image) => { self.draw_html_canvas_element(&image.Canvas(), sx, sy, sw, sh, dx, dy, dw, dh) } - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => { + CanvasImageSource::HTMLImageElement(ref image) => { // https://html.spec.whatwg.org/multipage/#img-error // If the image argument is an HTMLImageElement object that is in the broken state, // then throw an InvalidStateError exception - let (image_data, image_size) = match self.fetch_image_data(image) { - Some((mut data, size)) => { - // Pixels come from cache in BGRA order and drawImage expects RGBA so we - // have to swap the color values - byte_swap_and_premultiply(&mut data); - let size = Size2D::new(size.width as f64, size.height as f64); - (data, size) - }, - None => return Err(Error::InvalidState), - }; - let dw = dw.unwrap_or(image_size.width); - let dh = dh.unwrap_or(image_size.height); - let sw = sw.unwrap_or(image_size.width); - let sh = sh.unwrap_or(image_size.height); - self.draw_image_data(image_data, - image_size, - sx, sy, sw, sh, - dx, dy, dw, dh) + let url = image.get_url().ok_or(Error::InvalidState)?; + self.fetch_and_draw_image_data(url, + sx, sy, sw, sh, + dx, dy, dw, dh) + } + CanvasImageSource::CSSStyleValue(ref value) => { + let url = value.get_url(self.base_url.clone()).ok_or(Error::InvalidState)?; + self.fetch_and_draw_image_data(url, + sx, sy, sw, sh, + dx, dy, dw, dh) } }; @@ -386,6 +397,41 @@ impl CanvasRenderingContext2D { Ok(()) } + fn fetch_and_draw_image_data(&self, + url: ServoUrl, + sx: f64, + sy: f64, + sw: Option<f64>, + sh: Option<f64>, + dx: f64, + dy: f64, + dw: Option<f64>, + dh: Option<f64>) + -> ErrorResult { + debug!("Fetching image {}.", url); + // https://html.spec.whatwg.org/multipage/#img-error + // If the image argument is an HTMLImageElement object that is in the broken state, + // then throw an InvalidStateError exception + let (image_data, image_size) = match self.fetch_image_data(url) { + Some((mut data, size)) => { + // Pixels come from cache in BGRA order and drawImage expects RGBA so we + // have to swap the color values + byte_swap_and_premultiply(&mut data); + let size = Size2D::new(size.width as f64, size.height as f64); + (data, size) + }, + None => return Err(Error::InvalidState), + }; + let dw = dw.unwrap_or(image_size.width); + let dh = dh.unwrap_or(image_size.height); + let sw = sw.unwrap_or(image_size.width); + let sh = sh.unwrap_or(image_size.height); + self.draw_image_data(image_data, + image_size, + sx, sy, sw, sh, + dx, dy, dw, dh) + } + fn draw_image_data(&self, image_data: Vec<u8>, image_size: Size2D<f64>, @@ -425,12 +471,7 @@ impl CanvasRenderingContext2D { Ok(()) } - fn fetch_image_data(&self, image_element: &HTMLImageElement) -> Option<(Vec<u8>, Size2D<i32>)> { - let url = match image_element.get_url() { - Some(url) => url, - None => return None, - }; - + fn fetch_image_data(&self, url: ServoUrl) -> Option<(Vec<u8>, Size2D<i32>)> { let img = match self.request_image_from_cache(url) { ImageResponse::Loaded(img, _) => img, ImageResponse::PlaceholderLoaded(_, _) | @@ -453,10 +494,26 @@ impl CanvasRenderingContext2D { #[inline] fn request_image_from_cache(&self, url: ServoUrl) -> ImageResponse { - self.canvas.as_ref() - .map(|canvas| window_from_node(&**canvas)) - .map(|window| canvas_utils::request_image_from_cache(&window, url)) - .unwrap_or(ImageResponse::None) + let response = self.image_cache + .find_image_or_metadata(url.clone(), + UsePlaceholder::No, + CanRequestImages::No); + match response { + Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => + ImageResponse::Loaded(image, url), + Err(ImageState::Pending(_)) => + ImageResponse::None, + _ => { + // Rather annoyingly, we get the same response back from + // A load which really failed and from a load which hasn't started yet. + self.missing_image_urls.borrow_mut().push(url); + ImageResponse::None + }, + } + } + + pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> { + mem::replace(&mut self.missing_image_urls.borrow_mut(), vec![]) } fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> { @@ -749,7 +806,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { @@ -762,7 +819,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage_(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: CanvasImageSource, dx: f64, dy: f64, dw: f64, @@ -777,7 +834,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage__(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: CanvasImageSource, sx: f64, sy: f64, sw: f64, @@ -1152,27 +1209,34 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern fn CreatePattern(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: CanvasImageSource, mut repetition: DOMString) -> Fallible<Root<CanvasPattern>> { let (image_data, image_size) = match image { - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLImageElement(ref image) => { + CanvasImageSource::HTMLImageElement(ref image) => { // https://html.spec.whatwg.org/multipage/#img-error // If the image argument is an HTMLImageElement object that is in the broken state, // then throw an InvalidStateError exception - self.fetch_image_data(image).ok_or(Error::InvalidState)? + image.get_url() + .and_then(|url| self.fetch_image_data(url)) + .ok_or(Error::InvalidState)? }, - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::HTMLCanvasElement(ref canvas) => { + CanvasImageSource::HTMLCanvasElement(ref canvas) => { let _ = canvas.get_or_init_2d_context(); canvas.fetch_all_data().ok_or(Error::InvalidState)? }, - HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::CanvasRenderingContext2D(ref context) => { + CanvasImageSource::CanvasRenderingContext2D(ref context) => { let canvas = context.Canvas(); let _ = canvas.get_or_init_2d_context(); canvas.fetch_all_data().ok_or(Error::InvalidState)? } + CanvasImageSource::CSSStyleValue(ref value) => { + value.get_url(self.base_url.clone()) + .and_then(|url| self.fetch_image_data(url)) + .ok_or(Error::InvalidState)? + } }; if repetition.is_empty() { diff --git a/components/script/dom/cssstylevalue.rs b/components/script/dom/cssstylevalue.rs index 7ee9d5509f0..adc70ed7972 100644 --- a/components/script/dom/cssstylevalue.rs +++ b/components/script/dom/cssstylevalue.rs @@ -2,6 +2,8 @@ * 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 cssparser::Parser; +use cssparser::ParserInput; use dom::bindings::codegen::Bindings::CSSStyleValueBinding::CSSStyleValueMethods; use dom::bindings::codegen::Bindings::CSSStyleValueBinding::Wrap; use dom::bindings::js::Root; @@ -10,6 +12,7 @@ use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::globalscope::GlobalScope; use dom_struct::dom_struct; +use servo_url::ServoUrl; #[dom_struct] pub struct CSSStyleValue { @@ -44,3 +47,16 @@ impl CSSStyleValueMethods for CSSStyleValue { self.Stringifier() } } + +impl CSSStyleValue { + /// Parse the value as a `url()`. + /// TODO: This should really always be an absolute URL, but we currently + /// return relative URLs for computed values, so we pass in a base. + /// https://github.com/servo/servo/issues/17625 + pub fn get_url(&self, base_url: ServoUrl) -> Option<ServoUrl> { + let mut input = ParserInput::new(&*self.value); + let mut parser = Parser::new(&mut input); + parser.expect_url().ok() + .and_then(|string| base_url.join(&*string).ok()) + } +} diff --git a/components/script/dom/paintrenderingcontext2d.rs b/components/script/dom/paintrenderingcontext2d.rs index 48ae1b18637..c3aa9e4c98b 100644 --- a/components/script/dom/paintrenderingcontext2d.rs +++ b/components/script/dom/paintrenderingcontext2d.rs @@ -11,7 +11,7 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLin 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::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue; use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use dom::bindings::error::ErrorResult; use dom::bindings::error::Fallible; @@ -24,11 +24,13 @@ use dom::canvasgradient::CanvasGradient; use dom::canvaspattern::CanvasPattern; use dom::canvasrenderingcontext2d::CanvasRenderingContext2D; use dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use dom::workletglobalscope::WorkletGlobalScope; use dom_struct::dom_struct; use euclid::ScaleFactor; use euclid::Size2D; use euclid::TypedSize2D; use ipc_channel::ipc::IpcSender; +use servo_url::ServoUrl; use std::cell::Cell; use style_traits::CSSPixel; use style_traits::DevicePixel; @@ -42,8 +44,10 @@ pub struct PaintRenderingContext2D { impl PaintRenderingContext2D { fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D { let size = Size2D::zero(); + let image_cache = global.image_cache(); + let base_url = global.upcast::<WorkletGlobalScope>().base_url(); PaintRenderingContext2D { - context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, size), + context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, image_cache, base_url, size), device_pixel_ratio: Cell::new(ScaleFactor::new(1.0)), } } @@ -59,6 +63,10 @@ impl PaintRenderingContext2D { let _ = self.context.ipc_renderer().send(msg); } + pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> { + self.context.take_missing_image_urls() + } + pub fn set_bitmap_dimensions(&self, size: TypedSize2D<f32, CSSPixel>, device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>) @@ -187,7 +195,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue, dx: f64, dy: f64) -> ErrorResult { @@ -196,7 +204,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage_(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue, dx: f64, dy: f64, dw: f64, @@ -207,7 +215,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage__(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue, sx: f64, sy: f64, sw: f64, @@ -309,7 +317,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern fn CreatePattern(&self, - image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D, + image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2DOrCSSStyleValue, repetition: DOMString) -> Fallible<Root<CanvasPattern>> { self.context.CreatePattern(image, repetition) diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index 058c017693f..2b66452e9f2 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use canvas_traits::CanvasData; -use canvas_traits::CanvasImageData; use dom::bindings::callback::CallbackContainer; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding; @@ -28,8 +27,7 @@ use dom::workletglobalscope::WorkletTask; use dom_struct::dom_struct; use euclid::ScaleFactor; use euclid::TypedSize2D; -use ipc_channel::ipc::IpcSender; -use ipc_channel::ipc::IpcSharedMemory; +use ipc_channel::ipc; use js::jsapi::Call; use js::jsapi::Construct1; use js::jsapi::HandleValue; @@ -45,10 +43,10 @@ use js::jsval::ObjectValue; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::image::base::Image; use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageCache; use script_layout_interface::message::Msg; +use script_traits::DrawAPaintImageResult; use script_traits::Painter; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -59,6 +57,8 @@ use std::ptr::null_mut; use std::rc::Rc; use std::sync::Arc; use std::sync::Mutex; +use std::sync::mpsc; +use std::sync::mpsc::Sender; use style_traits::CSSPixel; use style_traits::DevicePixel; @@ -67,7 +67,7 @@ use style_traits::DevicePixel; pub struct PaintWorkletGlobalScope { /// The worklet global for this object worklet_global: WorkletGlobalScope, - /// The image cache (used for generating invalid images). + /// The image cache #[ignore_heap_size_of = "Arc"] image_cache: Arc<ImageCache>, /// https://drafts.css-houdini.org/css-paint-api/#paint-definitions @@ -94,11 +94,16 @@ impl PaintWorkletGlobalScope { unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) } } + pub fn image_cache(&self) -> Arc<ImageCache> { + self.image_cache.clone() + } + pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) { match task { - PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender) => { + PaintWorkletTask::DrawAPaintImage(name, size_in_px, device_pixel_ratio, properties, sender) => { let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties); - self.draw_a_paint_image(name, size, device_pixel_ratio, &*properties, sender); + let result = self.draw_a_paint_image(name, size_in_px, device_pixel_ratio, &*properties); + let _ = sender.send(result); } } } @@ -108,11 +113,11 @@ impl PaintWorkletGlobalScope { name: Atom, size_in_px: TypedSize2D<f32, CSSPixel>, device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>, - properties: &StylePropertyMapReadOnly, - sender: IpcSender<CanvasData>) + properties: &StylePropertyMapReadOnly) + -> DrawAPaintImageResult { // TODO: document paint definitions. - self.invoke_a_paint_callback(name, size_in_px, device_pixel_ratio, properties, sender); + self.invoke_a_paint_callback(name, size_in_px, device_pixel_ratio, properties) } /// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback @@ -121,8 +126,8 @@ impl PaintWorkletGlobalScope { name: Atom, size_in_px: TypedSize2D<f32, CSSPixel>, device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>, - properties: &StylePropertyMapReadOnly, - sender: IpcSender<CanvasData>) + properties: &StylePropertyMapReadOnly) + -> DrawAPaintImageResult { let size_in_dpx = size_in_px * device_pixel_ratio; let size_in_dpx = TypedSize2D::new(size_in_dpx.width.abs() as u32, size_in_dpx.height.abs() as u32); @@ -140,13 +145,13 @@ impl PaintWorkletGlobalScope { None => { // Step 2.2. warn!("Drawing un-registered paint definition {}.", name); - return self.send_invalid_image(size_in_dpx, sender); + return self.invalid_image(size_in_dpx, vec![]); } Some(definition) => { // Step 5.1 if !definition.constructor_valid_flag.get() { debug!("Drawing invalid paint definition {}.", name); - return self.send_invalid_image(size_in_dpx, sender); + return self.invalid_image(size_in_dpx, vec![]); } class_constructor.set(definition.class_constructor.get()); paint_function.set(definition.paint_function.get()); @@ -174,7 +179,7 @@ impl PaintWorkletGlobalScope { self.paint_definitions.borrow_mut().get_mut(&name) .expect("Vanishing paint definition.") .constructor_valid_flag.set(false); - return self.send_invalid_image(size_in_dpx, sender); + return self.invalid_image(size_in_dpx, vec![]); } // Step 5.4 entry.insert(Box::new(Heap::default())).set(paint_instance.get()); @@ -202,39 +207,42 @@ impl PaintWorkletGlobalScope { rooted!(in(cx) let mut result = UndefinedValue()); unsafe { Call(cx, paint_instance.handle(), paint_function.handle(), &args, result.handle_mut()); } + let missing_image_urls = rendering_context.take_missing_image_urls(); // Step 13. if unsafe { JS_IsExceptionPending(cx) } { debug!("Paint function threw an exception {}.", name); unsafe { JS_ClearPendingException(cx); } - return self.send_invalid_image(size_in_dpx, sender); + return self.invalid_image(size_in_dpx, missing_image_urls); } + let (sender, receiver) = ipc::channel().expect("IPC channel creation."); rendering_context.send_data(sender); + let image_key = match receiver.recv() { + Ok(CanvasData::Image(data)) => Some(data.image_key), + _ => None, + }; + + DrawAPaintImageResult { + width: size_in_dpx.width, + height: size_in_dpx.height, + format: PixelFormat::BGRA8, + image_key: image_key, + missing_image_urls: missing_image_urls, + } } - fn send_invalid_image(&self, - size: TypedSize2D<u32, DevicePixel>, - sender: IpcSender<CanvasData>) - { - debug!("Sending an invalid image."); - let width = size.width as u32; - let height = size.height as u32; - let len = (width as usize) * (height as usize) * 4; - let pixel = [0x00, 0x00, 0x00, 0x00]; - let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect(); - let mut image = Image { - width: width, - height: height, + // https://drafts.csswg.org/css-images-4/#invalid-image + fn invalid_image(&self, size: TypedSize2D<u32, DevicePixel>, missing_image_urls: Vec<ServoUrl>) + -> DrawAPaintImageResult { + debug!("Returning an invalid image."); + DrawAPaintImageResult { + width: size.width as u32, + height: size.height as u32, format: PixelFormat::BGRA8, - 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); + image_key: None, + missing_image_urls: missing_image_urls, + } } fn painter(&self, name: Atom) -> Arc<Painter> { @@ -244,13 +252,15 @@ impl PaintWorkletGlobalScope { fn draw_a_paint_image(&self, size: TypedSize2D<f32, CSSPixel>, device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>, - properties: Vec<(Atom, String)>, - sender: IpcSender<CanvasData>) + properties: Vec<(Atom, String)>) + -> DrawAPaintImageResult { let name = self.0.clone(); + let (sender, receiver) = mpsc::channel(); let task = PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender); self.1.lock().expect("Locking a painter.") .schedule_a_worklet_task(WorkletTask::Paint(task)); + receiver.recv().expect("Worklet thread died?") } } Arc::new(WorkletPainter(name, Mutex::new(self.worklet_global.executor()))) @@ -346,7 +356,7 @@ pub enum PaintWorkletTask { TypedSize2D<f32, CSSPixel>, ScaleFactor<f32, CSSPixel, DevicePixel>, Vec<(Atom, String)>, - IpcSender<CanvasData>) + Sender<DrawAPaintImageResult>) } /// A paint definition diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index 3937e74521a..b20b2b8ef3a 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -8,8 +8,11 @@ enum CanvasFillRule { "nonzero", "evenodd" }; typedef (HTMLImageElement or /* HTMLVideoElement or */ HTMLCanvasElement or - CanvasRenderingContext2D /* or - ImageBitmap */) CanvasImageSource; + CanvasRenderingContext2D or + /* ImageBitmap or */ + // This should probably be a CSSImageValue + // https://github.com/w3c/css-houdini-drafts/issues/416 + CSSStyleValue) CanvasImageSource; //[Constructor(optional unsigned long width, unsigned long height)] interface CanvasRenderingContext2D { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 72af4d23108..f48b9708e49 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -39,7 +39,6 @@ mod script_msg; pub mod webdriver_msg; 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; @@ -52,6 +51,7 @@ use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, Frame use msg::constellation_msg::{PipelineId, PipelineNamespaceId, TraversalDirection}; use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads}; use net_traits::image::base::Image; +use net_traits::image::base::PixelFormat; use net_traits::image_cache::ImageCache; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageType; @@ -69,6 +69,7 @@ use style_traits::CSSPixel; use style_traits::DevicePixel; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; use webrender_api::ClipId; +use webrender_api::ImageKey; use webvr_traits::{WebVREvent, WebVRMsg}; pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry}; @@ -823,7 +824,22 @@ pub trait Painter: Sync + Send { fn draw_a_paint_image(&self, size: TypedSize2D<f32, CSSPixel>, zoom: ScaleFactor<f32, CSSPixel, DevicePixel>, - properties: Vec<(Atom, String)>, - sender: IpcSender<CanvasData>); + properties: Vec<(Atom, String)>) + -> DrawAPaintImageResult; } +/// The result of executing paint code: the image together with any image URLs that need to be loaded. +/// TODO: this should return a WR display list. https://github.com/servo/servo/issues/17497 +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct DrawAPaintImageResult { + /// The image height + pub width: u32, + /// The image width + pub height: u32, + /// The image format + pub format: PixelFormat, + /// The image drawn, or None if an invalid paint image was drawn + pub image_key: Option<ImageKey>, + /// Drawing the image might have requested loading some image URLs. + pub missing_image_urls: Vec<ServoUrl>, +} diff --git a/tests/wpt/mozilla/meta/mozilla/css-paint-api/paint2d-image.html.ini b/tests/wpt/mozilla/meta/mozilla/css-paint-api/paint2d-image.html.ini deleted file mode 100644 index eb920e369bf..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/css-paint-api/paint2d-image.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[paint2d-image.html] - type: reftest - expected: FAIL - bug: https://github.com/servo/servo/issues/17432 |