canvas: Do not update ImageKey during canvas layout (#35719)

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
Samson 2025-03-12 16:36:52 +01:00 committed by GitHub
parent f31043602a
commit 6f6840d63c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 90 additions and 126 deletions

View file

@ -21,7 +21,7 @@ use servo_arc::Arc as ServoArc;
use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct;
use unicode_script::Script;
use webrender_api::units::{DeviceIntSize, RectExt as RectExt_};
use webrender_api::units::RectExt as RectExt_;
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey};
use webrender_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
@ -465,6 +465,10 @@ impl<'a> CanvasData<'a> {
}
}
pub fn image_key(&self) -> ImageKey {
self.image_key
}
pub fn draw_image(
&mut self,
image_data: &[u8],
@ -1249,7 +1253,7 @@ impl<'a> CanvasData<'a> {
.create_drawtarget(Size2D::new(size.width, size.height));
self.state = self.backend.recreate_paint_state(&self.state);
self.saved_states.clear();
self.update_wr_image(size.cast().cast_unit());
self.update_image_rendering();
}
pub fn send_pixels(&mut self, chan: IpcSender<IpcSharedMemory>) {
@ -1260,20 +1264,10 @@ impl<'a> CanvasData<'a> {
});
}
pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) {
let size = self.drawtarget.get_size();
self.update_wr_image(size.cast_unit());
let data = CanvasImageData {
image_key: self.image_key,
};
chan.send(data).unwrap();
}
fn update_wr_image(&mut self, size: DeviceIntSize) {
/// Update image in WebRender
pub fn update_image_rendering(&mut self) {
let descriptor = ImageDescriptor {
size,
size: self.drawtarget.get_size().cast_unit(),
stride: None,
format: ImageFormat::BGRA8,
offset: 0,

View file

@ -16,6 +16,7 @@ use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use log::warn;
use net_traits::ResourceThreads;
use webrender_api::ImageKey;
use webrender_traits::CrossProcessCompositorApi;
use crate::canvas_data::*;
@ -79,8 +80,9 @@ impl<'a> CanvasPaintThread<'a> {
},
},
Ok(CanvasMsg::FromLayout(message, canvas_id)) => match message {
FromLayoutMsg::SendData(chan) => {
canvas_paint_thread.canvas(canvas_id).send_data(chan);
FromLayoutMsg::UpdateImage(sender) => {
canvas_paint_thread.canvas(canvas_id).update_image_rendering();
sender.send(()).unwrap();
},
},
Err(e) => {
@ -90,9 +92,9 @@ impl<'a> CanvasPaintThread<'a> {
}
recv(create_receiver) -> msg => {
match msg {
Ok(ConstellationCanvasMsg::Create { id_sender: creator, size }) => {
let canvas_id = canvas_paint_thread.create_canvas(size);
creator.send(canvas_id).unwrap();
Ok(ConstellationCanvasMsg::Create { sender: creator, size }) => {
let canvas_data = canvas_paint_thread.create_canvas(size);
creator.send(canvas_data).unwrap();
},
Ok(ConstellationCanvasMsg::Exit) => break,
Err(e) => {
@ -109,15 +111,16 @@ impl<'a> CanvasPaintThread<'a> {
(create_sender, ipc_sender)
}
pub fn create_canvas(&mut self, size: Size2D<u64>) -> CanvasId {
pub fn create_canvas(&mut self, size: Size2D<u64>) -> (CanvasId, ImageKey) {
let canvas_id = self.next_canvas_id;
self.next_canvas_id.0 += 1;
let canvas_data =
CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone());
let image_key = canvas_data.image_key();
self.canvases.insert(canvas_id, canvas_data);
canvas_id
(canvas_id, image_key)
}
fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {

View file

@ -159,7 +159,7 @@ use webgpu::{self, WebGPU, WebGPURequest, WebGPUResponse};
#[cfg(feature = "webgpu")]
use webrender::RenderApi;
use webrender::RenderApiSender;
use webrender_api::DocumentId;
use webrender_api::{DocumentId, ImageKey};
use webrender_traits::{CompositorHitTestResult, WebrenderExternalImageRegistry};
use crate::browsingcontext::{
@ -4362,21 +4362,22 @@ where
fn handle_create_canvas_paint_thread_msg(
&mut self,
size: UntypedSize2D<u64>,
response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId)>,
response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>,
) {
let (canvas_id_sender, canvas_id_receiver) = unbounded();
let (canvas_data_sender, canvas_data_receiver) = unbounded();
if let Err(e) = self.canvas_sender.send(ConstellationCanvasMsg::Create {
id_sender: canvas_id_sender,
sender: canvas_data_sender,
size,
}) {
return warn!("Create canvas paint thread failed ({})", e);
}
let canvas_id = match canvas_id_receiver.recv() {
Ok(canvas_id) => canvas_id,
let (canvas_id, image_key) = match canvas_data_receiver.recv() {
Ok(canvas_data) => canvas_data,
Err(e) => return warn!("Create canvas paint thread id response failed ({})", e),
};
if let Err(e) = response_sender.send((self.canvas_ipc_sender.clone(), canvas_id)) {
if let Err(e) = response_sender.send((self.canvas_ipc_sender.clone(), canvas_id, image_key))
{
warn!("Create canvas paint thread response failed ({})", e);
}
}

View file

@ -21,7 +21,6 @@ use euclid::default::{Point2D, Rect, SideOffsets2D as UntypedSideOffsets2D, Size
use euclid::{Scale, SideOffsets2D, rect};
use fnv::FnvHashMap;
use fonts::ByteIndex;
use ipc_channel::ipc;
use log::{debug, warn};
use net_traits::image_cache::UsePlaceholder;
use range::Range;
@ -1770,16 +1769,17 @@ impl Fragment {
let image_key = match canvas_fragment_info.source {
CanvasFragmentSource::WebGL(image_key) => image_key,
CanvasFragmentSource::WebGPU(image_key) => image_key,
CanvasFragmentSource::Image(ref ipc_renderer) => {
CanvasFragmentSource::Image((image_key, canvas_id, ref ipc_renderer)) => {
let ipc_renderer = ipc_renderer.lock().unwrap();
let (sender, receiver) = ipc::channel().unwrap();
let (sender, receiver) = ipc_channel::ipc::channel().unwrap();
ipc_renderer
.send(CanvasMsg::FromLayout(
FromLayoutMsg::SendData(sender),
canvas_fragment_info.canvas_id,
FromLayoutMsg::UpdateImage(sender),
canvas_id,
))
.unwrap();
receiver.recv().unwrap().image_key
receiver.recv().unwrap();
image_key
},
CanvasFragmentSource::Empty => return,
};

View file

@ -338,7 +338,7 @@ impl InlineAbsoluteFragmentInfo {
#[derive(Clone)]
pub enum CanvasFragmentSource {
WebGL(ImageKey),
Image(Arc<Mutex<IpcSender<CanvasMsg>>>),
Image((ImageKey, CanvasId, Arc<Mutex<IpcSender<CanvasMsg>>>)),
WebGPU(ImageKey),
/// Transparent black
Empty,
@ -349,15 +349,18 @@ pub struct CanvasFragmentInfo {
pub source: CanvasFragmentSource,
pub dom_width: Au,
pub dom_height: Au,
pub canvas_id: CanvasId,
}
impl CanvasFragmentInfo {
pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
let source = match data.source {
HTMLCanvasDataSource::WebGL(texture_id) => CanvasFragmentSource::WebGL(texture_id),
HTMLCanvasDataSource::Image(ipc_sender) => {
CanvasFragmentSource::Image(Arc::new(Mutex::new(ipc_sender)))
HTMLCanvasDataSource::Image((image_key, canvas_id, ipc_sender)) => {
CanvasFragmentSource::Image((
image_key,
canvas_id,
Arc::new(Mutex::new(ipc_sender)),
))
},
HTMLCanvasDataSource::WebGPU(image_key) => CanvasFragmentSource::WebGPU(image_key),
HTMLCanvasDataSource::Empty => CanvasFragmentSource::Empty,
@ -367,7 +370,6 @@ impl CanvasFragmentInfo {
source,
dom_width: Au::from_px(data.width as i32),
dom_height: Au::from_px(data.height as i32),
canvas_id: data.canvas_id,
}
}
}

View file

@ -155,17 +155,14 @@ where
let canvas_data = node.canvas_data()?;
let source = match canvas_data.source {
HTMLCanvasDataSource::WebGL(texture_id) => CanvasSource::WebGL(texture_id),
HTMLCanvasDataSource::Image(ipc_sender) => {
CanvasSource::Image(Arc::new(Mutex::new(ipc_sender)))
HTMLCanvasDataSource::Image((image_key, canvas_id, ipc_sender)) => {
CanvasSource::Image((image_key, canvas_id, Arc::new(Mutex::new(ipc_sender))))
},
HTMLCanvasDataSource::WebGPU(image_key) => CanvasSource::WebGPU(image_key),
HTMLCanvasDataSource::Empty => CanvasSource::Empty,
};
Some((
CanvasInfo {
source,
canvas_id: canvas_data.canvas_id,
},
CanvasInfo { source },
PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()),
))
}

View file

@ -98,7 +98,7 @@ impl NaturalSizes {
pub(crate) enum CanvasSource {
WebGL(ImageKey),
Image(Arc<Mutex<IpcSender<CanvasMsg>>>),
Image((ImageKey, CanvasId, Arc<Mutex<IpcSender<CanvasMsg>>>)),
WebGPU(ImageKey),
/// transparent black
Empty,
@ -122,7 +122,6 @@ impl fmt::Debug for CanvasSource {
#[derive(Debug)]
pub(crate) struct CanvasInfo {
pub source: CanvasSource,
pub canvas_id: CanvasId,
}
#[derive(Debug)]
@ -382,16 +381,17 @@ impl ReplacedContents {
let image_key = match canvas_info.source {
CanvasSource::WebGL(image_key) => image_key,
CanvasSource::WebGPU(image_key) => image_key,
CanvasSource::Image(ref ipc_renderer) => {
CanvasSource::Image((image_key, canvas_id, ref ipc_renderer)) => {
let ipc_renderer = ipc_renderer.lock().unwrap();
let (sender, receiver) = ipc::channel().unwrap();
ipc_renderer
.send(CanvasMsg::FromLayout(
FromLayoutMsg::SendData(sender),
canvas_info.canvas_id,
FromLayoutMsg::UpdateImage(sender),
canvas_id,
))
.unwrap();
receiver.recv().unwrap().image_key
receiver.recv().unwrap();
image_key
},
CanvasSource::Empty => return vec![],
};

View file

@ -4,7 +4,6 @@
//! Common interfaces for Canvas Contexts
use canvas_traits::canvas::CanvasId;
use euclid::default::Size2D;
use ipc_channel::ipc::IpcSharedMemory;
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
@ -20,7 +19,6 @@ pub(crate) trait LayoutCanvasRenderingContextHelpers {
pub(crate) trait LayoutHTMLCanvasElementHelpers {
fn data(self) -> HTMLCanvasData;
fn get_canvas_id_for_layout(self) -> CanvasId;
}
pub(crate) trait CanvasContext {

View file

@ -34,6 +34,7 @@ use style::values::specified::color::Color;
use style_traits::values::ToCss;
use style_traits::{CssWriter, ParsingMode};
use url::Url;
use webrender_api::ImageKey;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
@ -145,6 +146,8 @@ pub(crate) struct CanvasState {
ipc_renderer: IpcSender<CanvasMsg>,
#[no_trace]
canvas_id: CanvasId,
#[no_trace]
image_key: ImageKey,
state: DomRefCell<CanvasContextState>,
origin_clean: Cell<bool>,
#[ignore_malloc_size_of = "Arc"]
@ -172,7 +175,7 @@ impl CanvasState {
script_to_constellation_chan
.send(ScriptMsg::CreateCanvasPaintThread(size, sender))
.unwrap();
let (ipc_renderer, canvas_id) = receiver.recv().unwrap();
let (ipc_renderer, canvas_id, image_key) = receiver.recv().unwrap();
debug!("Done.");
// Worklets always receive a unique origin. This messes with fetching
// cached images in the case of paint worklets, since the image cache
@ -191,6 +194,7 @@ impl CanvasState {
base_url: global.api_base_url(),
missing_image_urls: DomRefCell::new(Vec::new()),
saved_states: DomRefCell::new(Vec::new()),
image_key,
origin,
}
}
@ -199,6 +203,10 @@ impl CanvasState {
&self.ipc_renderer
}
pub(crate) fn image_key(&self) -> ImageKey {
self.image_key
}
pub(crate) fn get_missing_image_urls(&self) -> &DomRefCell<Vec<ServoUrl>> {
&self.missing_image_urls
}

View file

@ -2,16 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::canvas::{
Canvas2dMsg, CanvasId, CanvasImageData, CanvasMsg, FromLayoutMsg, FromScriptMsg,
};
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, FromScriptMsg};
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use ipc_channel::ipc::IpcSharedMemory;
use profile_traits::ipc;
use script_layout_interface::HTMLCanvasDataSource;
use servo_url::ServoUrl;
use crate::canvas_context::CanvasContext;
use crate::canvas_context::{CanvasContext, LayoutCanvasRenderingContextHelpers};
use crate::canvas_state::CanvasState;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin,
@ -112,28 +111,16 @@ impl CanvasRenderingContext2D {
);
self.canvas_state.get_rect(self.canvas.size(), rect)
}
pub(crate) fn send_data(&self, sender: IpcSender<CanvasImageData>) {
let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender), self.get_canvas_id());
let _ = self.canvas_state.get_ipc_renderer().send(msg);
}
}
pub(crate) trait LayoutCanvasRenderingContext2DHelpers {
fn get_ipc_renderer(self) -> IpcSender<CanvasMsg>;
fn get_canvas_id(self) -> CanvasId;
}
impl LayoutCanvasRenderingContext2DHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
fn get_ipc_renderer(self) -> IpcSender<CanvasMsg> {
(self.unsafe_get()).canvas_state.get_ipc_renderer().clone()
}
fn get_canvas_id(self) -> CanvasId {
// FIXME(nox): This relies on the fact that CanvasState::get_canvas_id
// does nothing fancy but it would be easier to trust a
// LayoutDom<_>-like type that would wrap the &CanvasState.
self.unsafe_get().canvas_state.get_canvas_id()
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
fn canvas_data_source(self) -> HTMLCanvasDataSource {
let canvas_state = &self.unsafe_get().canvas_state;
HTMLCanvasDataSource::Image((
canvas_state.image_key(),
canvas_state.get_canvas_id(),
canvas_state.get_ipc_renderer().clone(),
))
}
}

View file

@ -6,7 +6,6 @@ use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::rc::Rc;
use canvas_traits::canvas::CanvasId;
use canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
use dom_struct::dom_struct;
use euclid::default::Size2D;
@ -48,9 +47,7 @@ use crate::dom::bindings::reflector::{DomGlobal, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, ToLayout};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::blob::Blob;
use crate::dom::canvasrenderingcontext2d::{
CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers,
};
use crate::dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
#[cfg(not(feature = "webgpu"))]
@ -208,9 +205,7 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
fn data(self) -> HTMLCanvasData {
let source = unsafe {
match self.unsafe_get().context.borrow_for_layout().as_ref() {
Some(CanvasContext::Context2d(context)) => {
HTMLCanvasDataSource::Image(context.to_layout().get_ipc_renderer())
},
Some(CanvasContext::Context2d(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::WebGL(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::WebGL2(context)) => context.to_layout().canvas_data_source(),
#[cfg(feature = "webgpu")]
@ -229,20 +224,6 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
source,
width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
canvas_id: self.get_canvas_id_for_layout(),
}
}
#[allow(unsafe_code)]
fn get_canvas_id_for_layout(self) -> CanvasId {
let canvas = self.unsafe_get();
unsafe {
if let &Some(CanvasContext::Context2d(ref context)) = canvas.context.borrow_for_layout()
{
context.to_layout().get_canvas_id()
} else {
CanvasId(0)
}
}
}
}

View file

@ -4,13 +4,14 @@
use std::cell::Cell;
use canvas_traits::canvas::{CanvasId, CanvasImageData, CanvasMsg, FromLayoutMsg};
use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg};
use dom_struct::dom_struct;
use euclid::{Scale, Size2D};
use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc;
use script_bindings::reflector::Reflector;
use servo_url::ServoUrl;
use style_traits::CSSPixel;
use webrender_api::ImageKey;
use webrender_api::units::DevicePixel;
use super::bindings::reflector::DomGlobal as _;
@ -64,9 +65,13 @@ impl PaintRenderingContext2D {
self.canvas_state.get_canvas_id()
}
pub(crate) fn send_data(&self, sender: IpcSender<CanvasImageData>) {
let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender), self.get_canvas_id());
/// Send update to canvas paint thread and returns [`ImageKey`]
pub(crate) fn image_key(&self) -> ImageKey {
let (sender, receiver) = ipc::channel().unwrap();
let msg = CanvasMsg::FromLayout(FromLayoutMsg::UpdateImage(sender), self.get_canvas_id());
let _ = self.canvas_state.get_ipc_renderer().send(msg);
receiver.recv().unwrap();
self.canvas_state.image_key()
}
pub(crate) fn take_missing_image_urls(&self) -> Vec<ServoUrl> {

View file

@ -23,7 +23,6 @@ use js::rust::wrappers::{Call, Construct1};
use js::rust::{HandleValue, Runtime};
use net_traits::image_cache::ImageCache;
use pixels::PixelFormat;
use profile_traits::ipc;
use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter};
use servo_config::pref;
use servo_url::ServoUrl;
@ -40,7 +39,7 @@ use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
use crate::dom::bindings::conversions::{get_property, get_property_jsval};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, DomObject};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssstylevalue::CSSStyleValue;
@ -354,19 +353,13 @@ impl PaintWorkletGlobalScope {
return self.invalid_image(size_in_dpx, missing_image_urls);
}
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone())
.expect("IPC channel creation.");
rendering_context.send_data(sender);
let image_key = match receiver.recv() {
Ok(data) => Some(data.image_key),
_ => None,
};
let image_key = rendering_context.image_key();
DrawAPaintImageResult {
width: size_in_dpx.width,
height: size_in_dpx.height,
format: PixelFormat::BGRA8,
image_key,
image_key: Some(image_key),
missing_image_urls,
}
}

View file

@ -12,7 +12,6 @@ use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct;
use webrender_api::ImageKey;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum FillRule {
@ -32,11 +31,6 @@ pub enum CanvasMsg {
Close(CanvasId),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CanvasImageData {
pub image_key: ImageKey,
}
#[derive(Debug, Deserialize, Serialize)]
pub enum Canvas2dMsg {
Arc(Point2D<f32>, f32, f32, f32, bool),
@ -84,7 +78,7 @@ pub enum Canvas2dMsg {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum FromLayoutMsg {
SendData(IpcSender<CanvasImageData>),
UpdateImage(IpcSender<()>),
}
#[derive(Clone, Debug, Deserialize, Serialize)]

View file

@ -8,6 +8,7 @@
use crossbeam_channel::Sender;
use euclid::default::Size2D;
use webrender_api::ImageKey;
use crate::canvas::CanvasId;
@ -17,7 +18,7 @@ pub mod webgl;
pub enum ConstellationCanvasMsg {
Create {
id_sender: Sender<CanvasId>,
sender: Sender<(CanvasId, ImageKey)>,
size: Size2D<u64>,
},
Exit,

View file

@ -25,6 +25,7 @@ use servo_url::{ImmutableOrigin, ServoUrl};
use style_traits::CSSPixel;
#[cfg(feature = "webgpu")]
use webgpu::{WebGPU, WebGPUResponse, wgc};
use webrender_api::ImageKey;
use crate::mem::MemoryReportResult;
use crate::{
@ -144,7 +145,7 @@ pub enum ScriptMsg {
/// 2D canvases may use the GPU and we don't want to give untrusted content access to the GPU.)
CreateCanvasPaintThread(
UntypedSize2D<u64>,
IpcSender<(IpcSender<CanvasMsg>, CanvasId)>,
IpcSender<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>,
),
/// Notifies the constellation that this frame has received focus.
Focus,

View file

@ -120,7 +120,7 @@ pub enum LayoutElementType {
pub enum HTMLCanvasDataSource {
WebGL(ImageKey),
Image(IpcSender<CanvasMsg>),
Image((ImageKey, CanvasId, IpcSender<CanvasMsg>)),
WebGPU(ImageKey),
/// transparent black
Empty,
@ -130,7 +130,6 @@ pub struct HTMLCanvasData {
pub source: HTMLCanvasDataSource,
pub width: u32,
pub height: u32,
pub canvas_id: CanvasId,
}
pub struct SVGSVGData {