Let (Offscreen)RenderingContext implement CanvasContext (#36712)

this allows us to simplify canvas element/offscreen impl to only call
CanvasContext implementations (no more match statements).

This will help with impl more offscreen canvas contextl.

Testing: WPT and rustc

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-04-30 20:52:57 +02:00 committed by GitHub
parent 772f0b2df9
commit c3fcefdc32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 258 additions and 123 deletions

View file

@ -5,6 +5,7 @@
//! Common interfaces for Canvas Contexts //! Common interfaces for Canvas Contexts
use euclid::default::Size2D; use euclid::default::Size2D;
use script_bindings::root::Dom;
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
use snapshot::Snapshot; use snapshot::Snapshot;
@ -12,6 +13,10 @@ use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanva
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::node::{Node, NodeDamage}; use crate::dom::node::{Node, NodeDamage};
use crate::dom::types::{
CanvasRenderingContext2D, GPUCanvasContext, OffscreenCanvas, OffscreenCanvasRenderingContext2D,
WebGL2RenderingContext, WebGLRenderingContext,
};
pub(crate) trait LayoutCanvasRenderingContextHelpers { pub(crate) trait LayoutCanvasRenderingContextHelpers {
fn canvas_data_source(self) -> HTMLCanvasDataSource; fn canvas_data_source(self) -> HTMLCanvasDataSource;
@ -85,3 +90,180 @@ impl CanvasHelpers for HTMLCanvasElementOrOffscreenCanvas {
} }
} }
} }
/// Non rooted variant of [`crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::RenderingContext`]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum RenderingContext {
Placeholder(Dom<OffscreenCanvas>),
Context2d(Dom<CanvasRenderingContext2D>),
WebGL(Dom<WebGLRenderingContext>),
WebGL2(Dom<WebGL2RenderingContext>),
#[cfg(feature = "webgpu")]
WebGPU(Dom<GPUCanvasContext>),
}
impl CanvasContext for RenderingContext {
type ID = ();
fn context_id(&self) -> Self::ID {}
fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
match self {
RenderingContext::Placeholder(context) => (*context.context().unwrap()).canvas(),
RenderingContext::Context2d(context) => context.canvas(),
RenderingContext::WebGL(context) => context.canvas(),
RenderingContext::WebGL2(context) => context.canvas(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.canvas(),
}
}
fn resize(&self) {
match self {
RenderingContext::Placeholder(context) => (*context.context().unwrap()).resize(),
RenderingContext::Context2d(context) => context.resize(),
RenderingContext::WebGL(context) => context.resize(),
RenderingContext::WebGL2(context) => context.resize(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.resize(),
}
}
fn get_image_data(&self) -> Option<Snapshot> {
match self {
RenderingContext::Placeholder(context) => {
(*context.context().unwrap()).get_image_data()
},
RenderingContext::Context2d(context) => context.get_image_data(),
RenderingContext::WebGL(context) => context.get_image_data(),
RenderingContext::WebGL2(context) => context.get_image_data(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.get_image_data(),
}
}
fn origin_is_clean(&self) -> bool {
match self {
RenderingContext::Placeholder(context) => {
(*context.context().unwrap()).origin_is_clean()
},
RenderingContext::Context2d(context) => context.origin_is_clean(),
RenderingContext::WebGL(context) => context.origin_is_clean(),
RenderingContext::WebGL2(context) => context.origin_is_clean(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.origin_is_clean(),
}
}
fn size(&self) -> Size2D<u64> {
match self {
RenderingContext::Placeholder(context) => (*context.context().unwrap()).size(),
RenderingContext::Context2d(context) => context.size(),
RenderingContext::WebGL(context) => context.size(),
RenderingContext::WebGL2(context) => context.size(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.size(),
}
}
fn mark_as_dirty(&self) {
match self {
RenderingContext::Placeholder(context) => (*context.context().unwrap()).mark_as_dirty(),
RenderingContext::Context2d(context) => context.mark_as_dirty(),
RenderingContext::WebGL(context) => context.mark_as_dirty(),
RenderingContext::WebGL2(context) => context.mark_as_dirty(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.mark_as_dirty(),
}
}
fn update_rendering(&self) {
match self {
RenderingContext::Placeholder(context) => {
(*context.context().unwrap()).update_rendering()
},
RenderingContext::Context2d(context) => context.update_rendering(),
RenderingContext::WebGL(context) => context.update_rendering(),
RenderingContext::WebGL2(context) => context.update_rendering(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.update_rendering(),
}
}
fn onscreen(&self) -> bool {
match self {
RenderingContext::Placeholder(context) => (*context.context().unwrap()).onscreen(),
RenderingContext::Context2d(context) => context.onscreen(),
RenderingContext::WebGL(context) => context.onscreen(),
RenderingContext::WebGL2(context) => context.onscreen(),
#[cfg(feature = "webgpu")]
RenderingContext::WebGPU(context) => context.onscreen(),
}
}
}
/// Non rooted variant of [`crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::OffscreenRenderingContext`]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum OffscreenRenderingContext {
Context2d(Dom<OffscreenCanvasRenderingContext2D>),
//WebGL(Dom<WebGLRenderingContext>),
//WebGL2(Dom<WebGL2RenderingContext>),
//#[cfg(feature = "webgpu")]
//WebGPU(Dom<GPUCanvasContext>),
}
impl CanvasContext for OffscreenRenderingContext {
type ID = ();
fn context_id(&self) -> Self::ID {}
fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
match self {
OffscreenRenderingContext::Context2d(context) => context.canvas(),
}
}
fn resize(&self) {
match self {
OffscreenRenderingContext::Context2d(context) => context.resize(),
}
}
fn get_image_data(&self) -> Option<Snapshot> {
match self {
OffscreenRenderingContext::Context2d(context) => context.get_image_data(),
}
}
fn origin_is_clean(&self) -> bool {
match self {
OffscreenRenderingContext::Context2d(context) => context.origin_is_clean(),
}
}
fn size(&self) -> Size2D<u64> {
match self {
OffscreenRenderingContext::Context2d(context) => context.size(),
}
}
fn mark_as_dirty(&self) {
match self {
OffscreenRenderingContext::Context2d(context) => context.mark_as_dirty(),
}
}
fn update_rendering(&self) {
match self {
OffscreenRenderingContext::Context2d(context) => context.update_rendering(),
}
}
fn onscreen(&self) -> bool {
match self {
OffscreenRenderingContext::Context2d(context) => context.onscreen(),
}
}
}

View file

@ -36,6 +36,7 @@ use style_traits::{CssWriter, ParsingMode};
use url::Url; use url::Url;
use webrender_api::ImageKey; use webrender_api::ImageKey;
use crate::canvas_context::{OffscreenRenderingContext, RenderingContext};
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin, CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin,
@ -52,10 +53,10 @@ use crate::dom::canvaspattern::CanvasPattern;
use crate::dom::dommatrix::DOMMatrix; use crate::dom::dommatrix::DOMMatrix;
use crate::dom::element::{Element, cors_setting_for_element}; use crate::dom::element::{Element, cors_setting_for_element};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement}; use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::imagedata::ImageData; use crate::dom::imagedata::ImageData;
use crate::dom::node::{Node, NodeTraits}; use crate::dom::node::{Node, NodeTraits};
use crate::dom::offscreencanvas::{OffscreenCanvas, OffscreenCanvasContext}; use crate::dom::offscreencanvas::OffscreenCanvas;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::textmetrics::TextMetrics; use crate::dom::textmetrics::TextMetrics;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
@ -522,7 +523,7 @@ impl CanvasState {
if let Some(context) = canvas.context() { if let Some(context) = canvas.context() {
match *context { match *context {
OffscreenCanvasContext::OffscreenContext2d(ref context) => { OffscreenRenderingContext::Context2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size, image_size,
@ -577,7 +578,7 @@ impl CanvasState {
if let Some(context) = canvas.context() { if let Some(context) = canvas.context() {
match *context { match *context {
CanvasContext::Context2d(ref context) => { RenderingContext::Context2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size, image_size,
@ -586,12 +587,12 @@ impl CanvasState {
smoothing_enabled, smoothing_enabled,
)); ));
}, },
CanvasContext::Placeholder(ref context) => { RenderingContext::Placeholder(ref context) => {
let Some(context) = context.context() else { let Some(context) = context.context() else {
return Err(Error::InvalidState); return Err(Error::InvalidState);
}; };
match *context { match *context {
OffscreenCanvasContext::OffscreenContext2d(ref context) => context OffscreenRenderingContext::Context2d(ref context) => context
.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( .send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size, image_size,

View file

@ -27,14 +27,13 @@ use servo_media::streams::registry::MediaStreamId;
use snapshot::Snapshot; use snapshot::Snapshot;
use style::attr::AttrValue; use style::attr::AttrValue;
use crate::canvas_context::CanvasContext as _;
pub(crate) use crate::canvas_context::*; pub(crate) use crate::canvas_context::*;
use crate::conversions::Convert; use crate::conversions::Convert;
use crate::dom::attr::Attr; use crate::dom::attr::Attr;
use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map}; use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map};
use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{ use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{
BlobCallback, HTMLCanvasElementMethods, RenderingContext, BlobCallback, HTMLCanvasElementMethods, RenderingContext as RootedRenderingContext,
}; };
use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods; use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
@ -104,21 +103,10 @@ impl EncodedImageType {
} }
} }
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum CanvasContext {
Placeholder(Dom<OffscreenCanvas>),
Context2d(Dom<CanvasRenderingContext2D>),
WebGL(Dom<WebGLRenderingContext>),
WebGL2(Dom<WebGL2RenderingContext>),
#[cfg(feature = "webgpu")]
WebGPU(Dom<GPUCanvasContext>),
}
#[dom_struct] #[dom_struct]
pub(crate) struct HTMLCanvasElement { pub(crate) struct HTMLCanvasElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
context: DomRefCell<Option<CanvasContext>>, context: DomRefCell<Option<RenderingContext>>,
// This id and hashmap are used to keep track of ongoing toBlob() calls. // This id and hashmap are used to keep track of ongoing toBlob() calls.
callback_id: Cell<u32>, callback_id: Cell<u32>,
#[ignore_malloc_size_of = "not implemented for webidl callbacks"] #[ignore_malloc_size_of = "not implemented for webidl callbacks"]
@ -159,14 +147,7 @@ impl HTMLCanvasElement {
fn recreate_contexts_after_resize(&self) { fn recreate_contexts_after_resize(&self) {
if let Some(ref context) = *self.context.borrow() { if let Some(ref context) = *self.context.borrow() {
match *context { context.resize()
CanvasContext::Context2d(ref context) => context.resize(),
CanvasContext::WebGL(ref context) => context.resize(),
CanvasContext::WebGL2(ref context) => context.resize(),
#[cfg(feature = "webgpu")]
CanvasContext::WebGPU(ref context) => context.resize(),
CanvasContext::Placeholder(ref context) => context.resize(self.get_size().cast()),
}
} }
} }
@ -176,23 +157,14 @@ impl HTMLCanvasElement {
pub(crate) fn origin_is_clean(&self) -> bool { pub(crate) fn origin_is_clean(&self) -> bool {
match *self.context.borrow() { match *self.context.borrow() {
Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(), Some(ref context) => context.origin_is_clean(),
_ => true, _ => true,
} }
} }
pub(crate) fn mark_as_dirty(&self) { pub(crate) fn mark_as_dirty(&self) {
if let Some(ref context) = *self.context.borrow() { if let Some(ref context) = *self.context.borrow() {
match *context { context.mark_as_dirty()
CanvasContext::Context2d(ref context) => context.mark_as_dirty(),
CanvasContext::WebGL(ref context) => context.mark_as_dirty(),
CanvasContext::WebGL2(ref context) => context.mark_as_dirty(),
#[cfg(feature = "webgpu")]
CanvasContext::WebGPU(ref context) => context.mark_as_dirty(),
CanvasContext::Placeholder(ref _context) => {
// TODO: Should this be marked as dirty?
},
}
} }
} }
@ -222,12 +194,14 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
fn data(self) -> HTMLCanvasData { fn data(self) -> HTMLCanvasData {
let source = unsafe { let source = unsafe {
match self.unsafe_get().context.borrow_for_layout().as_ref() { match self.unsafe_get().context.borrow_for_layout().as_ref() {
Some(CanvasContext::Context2d(context)) => context.to_layout().canvas_data_source(), Some(RenderingContext::Context2d(context)) => {
Some(CanvasContext::WebGL(context)) => context.to_layout().canvas_data_source(), context.to_layout().canvas_data_source()
Some(CanvasContext::WebGL2(context)) => context.to_layout().canvas_data_source(), },
Some(RenderingContext::WebGL(context)) => context.to_layout().canvas_data_source(),
Some(RenderingContext::WebGL2(context)) => context.to_layout().canvas_data_source(),
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
Some(CanvasContext::WebGPU(context)) => context.to_layout().canvas_data_source(), Some(RenderingContext::WebGPU(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty, Some(RenderingContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty,
} }
}; };
@ -246,14 +220,14 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
} }
impl HTMLCanvasElement { impl HTMLCanvasElement {
pub(crate) fn context(&self) -> Option<Ref<CanvasContext>> { pub(crate) fn context(&self) -> Option<Ref<RenderingContext>> {
ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
} }
fn get_or_init_2d_context(&self, can_gc: CanGc) -> Option<DomRoot<CanvasRenderingContext2D>> { fn get_or_init_2d_context(&self, can_gc: CanGc) -> Option<DomRoot<CanvasRenderingContext2D>> {
if let Some(ctx) = self.context() { if let Some(ctx) = self.context() {
return match *ctx { return match *ctx {
CanvasContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)), RenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None, _ => None,
}; };
} }
@ -261,7 +235,7 @@ impl HTMLCanvasElement {
let window = self.owner_window(); let window = self.owner_window();
let size = self.get_size(); let size = self.get_size();
let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc); let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc);
*self.context.borrow_mut() = Some(CanvasContext::Context2d(Dom::from_ref(&*context))); *self.context.borrow_mut() = Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
Some(context) Some(context)
} }
@ -273,7 +247,7 @@ impl HTMLCanvasElement {
) -> Option<DomRoot<WebGLRenderingContext>> { ) -> Option<DomRoot<WebGLRenderingContext>> {
if let Some(ctx) = self.context() { if let Some(ctx) = self.context() {
return match *ctx { return match *ctx {
CanvasContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)), RenderingContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None, _ => None,
}; };
} }
@ -289,7 +263,7 @@ impl HTMLCanvasElement {
attrs, attrs,
can_gc, can_gc,
)?; )?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL(Dom::from_ref(&*context))); *self.context.borrow_mut() = Some(RenderingContext::WebGL(Dom::from_ref(&*context)));
Some(context) Some(context)
} }
@ -305,7 +279,7 @@ impl HTMLCanvasElement {
} }
if let Some(ctx) = self.context() { if let Some(ctx) = self.context() {
return match *ctx { return match *ctx {
CanvasContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)), RenderingContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None, _ => None,
}; };
} }
@ -314,7 +288,7 @@ impl HTMLCanvasElement {
let attrs = Self::get_gl_attributes(cx, options)?; let attrs = Self::get_gl_attributes(cx, options)?;
let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self));
let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?; let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL2(Dom::from_ref(&*context))); *self.context.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context)));
Some(context) Some(context)
} }
@ -327,7 +301,7 @@ impl HTMLCanvasElement {
fn get_or_init_webgpu_context(&self, can_gc: CanGc) -> Option<DomRoot<GPUCanvasContext>> { fn get_or_init_webgpu_context(&self, can_gc: CanGc) -> Option<DomRoot<GPUCanvasContext>> {
if let Some(ctx) = self.context() { if let Some(ctx) = self.context() {
return match *ctx { return match *ctx {
CanvasContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)), RenderingContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None, _ => None,
}; };
} }
@ -341,7 +315,8 @@ impl HTMLCanvasElement {
.expect("Failed to get WebGPU channel") .expect("Failed to get WebGPU channel")
.map(|channel| { .map(|channel| {
let context = GPUCanvasContext::new(&global_scope, self, channel, can_gc); let context = GPUCanvasContext::new(&global_scope, self, channel, can_gc);
*self.context.borrow_mut() = Some(CanvasContext::WebGPU(Dom::from_ref(&*context))); *self.context.borrow_mut() =
Some(RenderingContext::WebGPU(Dom::from_ref(&*context)));
context context
}) })
} }
@ -349,8 +324,8 @@ impl HTMLCanvasElement {
/// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists. /// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists.
pub(crate) fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> { pub(crate) fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> {
match *self.context.borrow() { match *self.context.borrow() {
Some(CanvasContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)), Some(RenderingContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)),
Some(CanvasContext::WebGL2(ref context)) => Some(context.base_context()), Some(RenderingContext::WebGL2(ref context)) => Some(context.base_context()),
_ => None, _ => None,
} }
} }
@ -378,12 +353,7 @@ impl HTMLCanvasElement {
pub(crate) fn get_image_data(&self) -> Option<Snapshot> { pub(crate) fn get_image_data(&self) -> Option<Snapshot> {
match self.context.borrow().as_ref() { match self.context.borrow().as_ref() {
Some(CanvasContext::Context2d(context)) => context.get_image_data(), Some(context) => context.get_image_data(),
Some(CanvasContext::WebGL(context)) => context.get_image_data(),
Some(CanvasContext::WebGL2(context)) => context.get_image_data(),
#[cfg(feature = "webgpu")]
Some(CanvasContext::WebGPU(context)) => context.get_image_data(),
Some(CanvasContext::Placeholder(context)) => context.get_image_data(),
None => { None => {
let size = self.get_size(); let size = self.get_size();
if size.width == 0 || size.height == 0 { if size.width == 0 || size.height == 0 {
@ -466,7 +436,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
// is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the // is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
// attribute's value unchanged. // attribute's value unchanged.
fn SetWidth(&self, value: u32, can_gc: CanGc) -> Fallible<()> { fn SetWidth(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
return Err(Error::InvalidState); return Err(Error::InvalidState);
} }
@ -485,7 +455,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
// https://html.spec.whatwg.org/multipage/#dom-canvas-height // https://html.spec.whatwg.org/multipage/#dom-canvas-height
fn SetHeight(&self, value: u32, can_gc: CanGc) -> Fallible<()> { fn SetHeight(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
return Err(Error::InvalidState); return Err(Error::InvalidState);
} }
@ -506,26 +476,26 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
id: DOMString, id: DOMString,
options: HandleValue, options: HandleValue,
can_gc: CanGc, can_gc: CanGc,
) -> Fallible<Option<RenderingContext>> { ) -> Fallible<Option<RootedRenderingContext>> {
// Always throw an InvalidState exception when the canvas is in Placeholder mode (See table in the spec). // Always throw an InvalidState exception when the canvas is in Placeholder mode (See table in the spec).
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
return Err(Error::InvalidState); return Err(Error::InvalidState);
} }
Ok(match &*id { Ok(match &*id {
"2d" => self "2d" => self
.get_or_init_2d_context(can_gc) .get_or_init_2d_context(can_gc)
.map(RenderingContext::CanvasRenderingContext2D), .map(RootedRenderingContext::CanvasRenderingContext2D),
"webgl" | "experimental-webgl" => self "webgl" | "experimental-webgl" => self
.get_or_init_webgl_context(cx, options, can_gc) .get_or_init_webgl_context(cx, options, can_gc)
.map(RenderingContext::WebGLRenderingContext), .map(RootedRenderingContext::WebGLRenderingContext),
"webgl2" | "experimental-webgl2" => self "webgl2" | "experimental-webgl2" => self
.get_or_init_webgl2_context(cx, options, can_gc) .get_or_init_webgl2_context(cx, options, can_gc)
.map(RenderingContext::WebGL2RenderingContext), .map(RootedRenderingContext::WebGL2RenderingContext),
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
"webgpu" => self "webgpu" => self
.get_or_init_webgpu_context(can_gc) .get_or_init_webgpu_context(can_gc)
.map(RenderingContext::GPUCanvasContext), .map(RootedRenderingContext::GPUCanvasContext),
_ => None, _ => None,
}) })
} }
@ -672,7 +642,8 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
can_gc, can_gc,
); );
// Step 4. Set this canvas element's context mode to placeholder. // Step 4. Set this canvas element's context mode to placeholder.
*self.context.borrow_mut() = Some(CanvasContext::Placeholder(offscreen_canvas.as_traced())); *self.context.borrow_mut() =
Some(RenderingContext::Placeholder(offscreen_canvas.as_traced()));
// Step 5. Return offscreenCanvas. // Step 5. Return offscreenCanvas.
Ok(offscreen_canvas) Ok(offscreen_canvas)

View file

@ -9,9 +9,10 @@ use euclid::default::Size2D;
use js::rust::{HandleObject, HandleValue}; use js::rust::{HandleObject, HandleValue};
use snapshot::Snapshot; use snapshot::Snapshot;
use crate::canvas_context::{CanvasContext, OffscreenRenderingContext};
use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map}; use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map};
use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{ use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{
OffscreenCanvasMethods, OffscreenRenderingContext, OffscreenCanvasMethods, OffscreenRenderingContext as RootedOffscreenRenderingContext,
}; };
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
@ -23,20 +24,12 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D; use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D;
use crate::script_runtime::{CanGc, JSContext}; use crate::script_runtime::{CanGc, JSContext};
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum OffscreenCanvasContext {
OffscreenContext2d(Dom<OffscreenCanvasRenderingContext2D>),
//WebGL(Dom<WebGLRenderingContext>),
//WebGL2(Dom<WebGL2RenderingContext>),
}
#[dom_struct] #[dom_struct]
pub(crate) struct OffscreenCanvas { pub(crate) struct OffscreenCanvas {
eventtarget: EventTarget, eventtarget: EventTarget,
width: Cell<u64>, width: Cell<u64>,
height: Cell<u64>, height: Cell<u64>,
context: DomRefCell<Option<OffscreenCanvasContext>>, context: DomRefCell<Option<OffscreenRenderingContext>>,
placeholder: Option<Dom<HTMLCanvasElement>>, placeholder: Option<Dom<HTMLCanvasElement>>,
} }
@ -77,20 +70,18 @@ impl OffscreenCanvas {
pub(crate) fn origin_is_clean(&self) -> bool { pub(crate) fn origin_is_clean(&self) -> bool {
match *self.context.borrow() { match *self.context.borrow() {
Some(OffscreenCanvasContext::OffscreenContext2d(ref context)) => { Some(ref context) => context.origin_is_clean(),
context.origin_is_clean()
},
_ => true, _ => true,
} }
} }
pub(crate) fn context(&self) -> Option<Ref<OffscreenCanvasContext>> { pub(crate) fn context(&self) -> Option<Ref<OffscreenRenderingContext>> {
ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
} }
pub(crate) fn get_image_data(&self) -> Option<Snapshot> { pub(crate) fn get_image_data(&self) -> Option<Snapshot> {
match self.context.borrow().as_ref() { match self.context.borrow().as_ref() {
Some(OffscreenCanvasContext::OffscreenContext2d(context)) => context.get_image_data(), Some(context) => context.get_image_data(),
None => { None => {
let size = self.get_size(); let size = self.get_size();
if size.width == 0 || size.height == 0 { if size.width == 0 || size.height == 0 {
@ -108,13 +99,13 @@ impl OffscreenCanvas {
) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> { ) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> {
if let Some(ctx) = self.context() { if let Some(ctx) = self.context() {
return match *ctx { return match *ctx {
OffscreenCanvasContext::OffscreenContext2d(ref ctx) => Some(DomRoot::from_ref(ctx)), OffscreenRenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
}; };
} }
let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc); let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc);
*self.context.borrow_mut() = Some(OffscreenCanvasContext::OffscreenContext2d( *self.context.borrow_mut() = Some(OffscreenRenderingContext::Context2d(Dom::from_ref(
Dom::from_ref(&*context), &*context,
)); )));
Some(context) Some(context)
} }
@ -125,19 +116,6 @@ impl OffscreenCanvas {
pub(crate) fn placeholder(&self) -> Option<&HTMLCanvasElement> { pub(crate) fn placeholder(&self) -> Option<&HTMLCanvasElement> {
self.placeholder.as_deref() self.placeholder.as_deref()
} }
pub(crate) fn resize(&self, size: Size2D<u64>) {
self.width.set(size.width);
self.height.set(size.height);
if let Some(canvas_context) = self.context() {
match &*canvas_context {
OffscreenCanvasContext::OffscreenContext2d(rendering_context) => {
rendering_context.set_canvas_bitmap_dimensions(self.get_size());
},
}
}
}
} }
impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas { impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
@ -160,11 +138,11 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
id: DOMString, id: DOMString,
_options: HandleValue, _options: HandleValue,
can_gc: CanGc, can_gc: CanGc,
) -> Fallible<Option<OffscreenRenderingContext>> { ) -> Fallible<Option<RootedOffscreenRenderingContext>> {
match &*id { match &*id {
"2d" => Ok(self "2d" => Ok(self
.get_or_init_2d_context(can_gc) .get_or_init_2d_context(can_gc)
.map(OffscreenRenderingContext::OffscreenCanvasRenderingContext2D)), .map(RootedOffscreenRenderingContext::OffscreenCanvasRenderingContext2D)),
/*"webgl" | "experimental-webgl" => self /*"webgl" | "experimental-webgl" => self
.get_or_init_webgl_context(cx, options) .get_or_init_webgl_context(cx, options)
.map(OffscreenRenderingContext::WebGLRenderingContext), .map(OffscreenRenderingContext::WebGLRenderingContext),
@ -187,11 +165,7 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
self.width.set(value); self.width.set(value);
if let Some(canvas_context) = self.context() { if let Some(canvas_context) = self.context() {
match &*canvas_context { canvas_context.resize();
OffscreenCanvasContext::OffscreenContext2d(rendering_context) => {
rendering_context.set_canvas_bitmap_dimensions(self.get_size());
},
}
} }
if let Some(canvas) = &self.placeholder { if let Some(canvas) = &self.placeholder {
@ -209,11 +183,7 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
self.height.set(value); self.height.set(value);
if let Some(canvas_context) = self.context() { if let Some(canvas_context) = self.context() {
match &*canvas_context { canvas_context.resize();
OffscreenCanvasContext::OffscreenContext2d(rendering_context) => {
rendering_context.set_canvas_bitmap_dimensions(self.get_size());
},
}
} }
if let Some(canvas) = &self.placeholder { if let Some(canvas) = &self.placeholder {

View file

@ -3,11 +3,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::GenericBindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2D_Binding::CanvasRenderingContext2DMethods; use crate::dom::bindings::codegen::GenericBindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2D_Binding::CanvasRenderingContext2DMethods;
use crate::canvas_context::CanvasContext as _; use crate::canvas_context::CanvasContext;
use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas;
use canvas_traits::canvas::Canvas2dMsg; use canvas_traits::canvas::Canvas2dMsg;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D;
use snapshot::Snapshot; use snapshot::Snapshot;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
@ -64,21 +63,33 @@ impl OffscreenCanvasRenderingContext2D {
reflect_dom_object(boxed, global, can_gc) reflect_dom_object(boxed, global, can_gc)
} }
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
self.context.set_canvas_bitmap_dimensions(size.cast());
}
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
self.context.send_canvas_2d_msg(msg) self.context.send_canvas_2d_msg(msg)
} }
}
pub(crate) fn origin_is_clean(&self) -> bool { impl CanvasContext for OffscreenCanvasRenderingContext2D {
self.context.origin_is_clean() type ID = <CanvasRenderingContext2D as CanvasContext>::ID;
fn context_id(&self) -> Self::ID {
self.context.context_id()
} }
pub(crate) fn get_image_data(&self) -> Option<Snapshot> { fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas {
self.context.canvas()
}
fn resize(&self) {
self.context.resize()
}
fn get_image_data(&self) -> Option<Snapshot> {
self.context.get_image_data() self.context.get_image_data()
} }
fn origin_is_clean(&self) -> bool {
self.context.origin_is_clean()
}
} }
impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder> impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder>