From 8a62984c2f92a5c0c17c1ad78519cfbcc95b9070 Mon Sep 17 00:00:00 2001 From: Andrei Volykhin Date: Wed, 3 Sep 2025 22:24:48 +0300 Subject: [PATCH] canvas: Use non rooted variant of HTMLCanvasElementOrOffscreenCanvas type (#38970) Any RenderingContext/OffscreenRenderingContext type has readonly "canvas" attribute and associated native-code DOM context objects have reference to target DOM canvas objects. https://html.spec.whatwg.org/multipage/canvas.html#renderingcontext https://html.spec.whatwg.org/multipage/canvas.html#offscreenrenderingcontext And currently the reference to DOM canvas object is the rooting pointer on the stack, which leads to the circular reference problem. The SpiderMonkey's (SM) garbage collector will not be able to free the DOM canvas and context objects (unreacheble from JS) because of the rooting pointer on stack (see STACK_ROOTS). And these objects will be stored until the associated script runtime/thread will be terminated. SM -> JS Roots -> DOM Canvas* (on heap) -> DOM Context (on heap) SM -> Rust Roots -> Dom Canvas* (on stack) <- as "canvas" member field Let's replace the rooting pointer to the traceble pointer (DomRoot -> Dom) in the "canvas" member field of DOM context object, which allows to broke circular referencing problem. Testing: No changes in existed tests Signed-off-by: Andrei Volykhin Co-authored-by: Andrei Volykhin --- .../dom/canvas/2d/canvasrenderingcontext2d.rs | 25 +++--- .../2d/offscreencanvasrenderingcontext2d.rs | 43 +++++----- .../script/dom/canvas/canvas_context.rs | 86 ++++++++++++++++--- .../dom/canvas/imagebitmaprenderingcontext.rs | 22 +++-- .../script/dom/canvas/offscreencanvas.rs | 14 +-- components/script/dom/gpucanvascontext.rs | 7 +- .../script/dom/html/htmlcanvaselement.rs | 23 +++-- .../dom/webgl/webgl2renderingcontext.rs | 18 ++-- .../script/dom/webgl/webglrenderingcontext.rs | 35 +++++--- .../script/dom/webgpu/gpucanvascontext.rs | 22 +++-- components/script/dom/webxr/xrwebgllayer.rs | 12 +-- 11 files changed, 191 insertions(+), 116 deletions(-) diff --git a/components/script/dom/canvas/2d/canvasrenderingcontext2d.rs b/components/script/dom/canvas/2d/canvasrenderingcontext2d.rs index ee63ffd1a94..fc13f82426c 100644 --- a/components/script/dom/canvas/2d/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvas/2d/canvasrenderingcontext2d.rs @@ -13,19 +13,23 @@ use servo_url::ServoUrl; use webrender_api::ImageKey; use super::canvas_state::CanvasState; -use crate::canvas_context::{CanvasContext, CanvasHelpers, LayoutCanvasRenderingContextHelpers}; +use crate::canvas_context::{ + CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas, + LayoutCanvasRenderingContextHelpers, +}; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin, CanvasRenderingContext2DMethods, CanvasTextAlign, CanvasTextBaseline, }; use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit; use crate::dom::bindings::codegen::UnionTypes::{ - HTMLCanvasElementOrOffscreenCanvas, StringOrCanvasGradientOrCanvasPattern, + HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas, + StringOrCanvasGradientOrCanvasPattern, }; use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; -use crate::dom::bindings::root::{DomRoot, LayoutDom}; +use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom}; use crate::dom::bindings::str::DOMString; use crate::dom::canvasgradient::CanvasGradient; use crate::dom::canvaspattern::CanvasPattern; @@ -62,19 +66,18 @@ impl CanvasRenderingContext2D { }) } - #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( global: &GlobalScope, canvas: &HTMLCanvasElement, size: Size2D, can_gc: CanGc, ) -> Option> { - let boxed = Box::new(CanvasRenderingContext2D::new_inherited( + CanvasRenderingContext2D::new_inherited( global, - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)), + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(Dom::from_ref(canvas)), size, - )?); - Some(reflect_dom_object(boxed, global, can_gc)) + ) + .map(|context| reflect_dom_object(Box::new(context), global, can_gc)) } // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state @@ -119,8 +122,8 @@ impl CanvasContext for CanvasRenderingContext2D { self.canvas_state.get_canvas_id() } - fn canvas(&self) -> Option { - Some(self.canvas.clone()) + fn canvas(&self) -> Option { + Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)) } fn update_rendering(&self, canvas_epoch: Epoch) -> bool { @@ -178,7 +181,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingCo // https://html.spec.whatwg.org/multipage/#dom-context-2d-canvas fn Canvas(&self) -> DomRoot { match &self.canvas { - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.clone(), + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.as_rooted(), _ => panic!("Should not be called from offscreen canvas"), } } diff --git a/components/script/dom/canvas/2d/offscreencanvasrenderingcontext2d.rs b/components/script/dom/canvas/2d/offscreencanvasrenderingcontext2d.rs index cbad02f8499..c0fe793fc8f 100644 --- a/components/script/dom/canvas/2d/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/canvas/2d/offscreencanvasrenderingcontext2d.rs @@ -2,27 +2,30 @@ * 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 crate::dom::bindings::codegen::GenericBindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2D_Binding::CanvasRenderingContext2DMethods; -use crate::canvas_context::CanvasContext; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; use canvas_traits::canvas::Canvas2dMsg; use dom_struct::dom_struct; +use euclid::default::Size2D; use pixels::Snapshot; +use crate::canvas_context::{CanvasContext, HTMLCanvasElementOrOffscreenCanvas}; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin, - CanvasTextAlign, CanvasTextBaseline, + CanvasRenderingContext2DMethods, CanvasTextAlign, CanvasTextBaseline, }; use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit; use crate::dom::bindings::codegen::Bindings::OffscreenCanvasRenderingContext2DBinding::OffscreenCanvasRenderingContext2DMethods; -use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; +use crate::dom::bindings::codegen::UnionTypes::{ + HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas, + StringOrCanvasGradientOrCanvasPattern, +}; use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::canvasgradient::CanvasGradient; use crate::dom::canvaspattern::CanvasPattern; +use crate::dom::canvasrenderingcontext2d::CanvasRenderingContext2D; use crate::dom::dommatrix::DOMMatrix; use crate::dom::globalscope::GlobalScope; use crate::dom::imagedata::ImageData; @@ -31,8 +34,6 @@ use crate::dom::path2d::Path2D; use crate::dom::textmetrics::TextMetrics; use crate::script_runtime::CanGc; -use super::canvasrenderingcontext2d::CanvasRenderingContext2D; - #[dom_struct] pub(crate) struct OffscreenCanvasRenderingContext2D { context: CanvasRenderingContext2D, @@ -42,28 +43,26 @@ impl OffscreenCanvasRenderingContext2D { #[cfg_attr(crown, allow(crown::unrooted_must_root))] fn new_inherited( global: &GlobalScope, - canvas: &OffscreenCanvas, + canvas: HTMLCanvasElementOrOffscreenCanvas, + size: Size2D, ) -> Option { - let size = canvas.get_size().cast(); Some(OffscreenCanvasRenderingContext2D { - context: CanvasRenderingContext2D::new_inherited( - global, - HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(canvas)), - size, - )?, + context: CanvasRenderingContext2D::new_inherited(global, canvas, size)?, }) } - #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( global: &GlobalScope, canvas: &OffscreenCanvas, + size: Size2D, can_gc: CanGc, ) -> Option> { - let boxed = Box::new(OffscreenCanvasRenderingContext2D::new_inherited( - global, canvas, - )?); - Some(reflect_dom_object(boxed, global, can_gc)) + OffscreenCanvasRenderingContext2D::new_inherited( + global, + HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(Dom::from_ref(canvas)), + size, + ) + .map(|context| reflect_dom_object(Box::new(context), global, can_gc)) } pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { @@ -78,7 +77,7 @@ impl CanvasContext for OffscreenCanvasRenderingContext2D { self.context.context_id() } - fn canvas(&self) -> Option { + fn canvas(&self) -> Option { self.context.canvas() } @@ -109,7 +108,7 @@ impl OffscreenCanvasRenderingContext2DMethods // https://html.spec.whatwg.org/multipage/offscreencontext2d-canvas fn Canvas(&self) -> DomRoot { match self.context.canvas() { - Some(HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas)) => canvas, + Some(RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas)) => canvas, _ => panic!("Should not be called from onscreen canvas"), } } diff --git a/components/script/dom/canvas/canvas_context.rs b/components/script/dom/canvas/canvas_context.rs index dd452d5488d..594da353a7c 100644 --- a/components/script/dom/canvas/canvas_context.rs +++ b/components/script/dom/canvas/canvas_context.rs @@ -6,19 +6,17 @@ use base::Epoch; use euclid::default::Size2D; -use layout_api::HTMLCanvasData; use pixels::Snapshot; use script_bindings::root::{Dom, DomRoot}; use webrender_api::ImageKey; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::inheritance::Castable; -use crate::dom::html::htmlcanvaselement::HTMLCanvasElement; use crate::dom::node::{Node, NodeDamage}; #[cfg(feature = "webgpu")] use crate::dom::types::GPUCanvasContext; use crate::dom::types::{ - CanvasRenderingContext2D, ImageBitmapRenderingContext, OffscreenCanvas, + CanvasRenderingContext2D, HTMLCanvasElement, ImageBitmapRenderingContext, OffscreenCanvas, OffscreenCanvasRenderingContext2D, WebGL2RenderingContext, WebGLRenderingContext, }; @@ -27,8 +25,45 @@ pub(crate) trait LayoutCanvasRenderingContextHelpers { fn canvas_data_source(self) -> Option; } -pub(crate) trait LayoutHTMLCanvasElementHelpers { - fn data(self) -> HTMLCanvasData; +/// Non rooted variant of [`crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas`] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub(crate) enum HTMLCanvasElementOrOffscreenCanvas { + HTMLCanvasElement(Dom), + OffscreenCanvas(Dom), +} + +impl From<&RootedHTMLCanvasElementOrOffscreenCanvas> for HTMLCanvasElementOrOffscreenCanvas { + /// Returns a traced version suitable for use as member of other DOM objects. + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + fn from( + value: &RootedHTMLCanvasElementOrOffscreenCanvas, + ) -> HTMLCanvasElementOrOffscreenCanvas { + match value { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas.as_traced()) + }, + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { + HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas.as_traced()) + }, + } + } +} + +impl From<&HTMLCanvasElementOrOffscreenCanvas> for RootedHTMLCanvasElementOrOffscreenCanvas { + /// Returns a rooted version suitable for use on the stack. + fn from( + value: &HTMLCanvasElementOrOffscreenCanvas, + ) -> RootedHTMLCanvasElementOrOffscreenCanvas { + match value { + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas.as_rooted()) + }, + HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas.as_rooted()) + }, + } + } } pub(crate) trait CanvasContext { @@ -36,7 +71,7 @@ pub(crate) trait CanvasContext { fn context_id(&self) -> Self::ID; - fn canvas(&self) -> Option; + fn canvas(&self) -> Option; fn resize(&self); @@ -61,7 +96,8 @@ pub(crate) trait CanvasContext { } fn mark_as_dirty(&self) { - if let Some(HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas)) = &self.canvas() + if let Some(RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas)) = + self.canvas() { canvas.upcast::().dirty(NodeDamage::Other); } @@ -82,12 +118,12 @@ pub(crate) trait CanvasContext { }; match canvas { - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { canvas.upcast::().is_connected() }, // FIXME(34628): Offscreen canvases should be considered offscreen if a placeholder is set. // - HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false, + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false, } } } @@ -109,12 +145,36 @@ impl CanvasHelpers for HTMLCanvasElementOrOffscreenCanvas { fn canvas(&self) -> Option> { match self { - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => Some(canvas.clone()), + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + Some(canvas.as_rooted()) + }, HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.placeholder(), } } } +impl CanvasHelpers for RootedHTMLCanvasElementOrOffscreenCanvas { + fn size(&self) -> Size2D { + match self { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + canvas.get_size().cast() + }, + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(), + } + } + + fn canvas(&self) -> Option> { + match self { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + Some(canvas.clone()) + }, + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { + canvas.placeholder() + }, + } + } +} + /// 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)] @@ -133,7 +193,7 @@ impl CanvasContext for RenderingContext { fn context_id(&self) -> Self::ID {} - fn canvas(&self) -> Option { + fn canvas(&self) -> Option { match self { RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas.context()?.canvas(), RenderingContext::Context2d(context) => context.canvas(), @@ -297,7 +357,7 @@ impl CanvasContext for OffscreenRenderingContext { fn context_id(&self) -> Self::ID {} - fn canvas(&self) -> Option { + fn canvas(&self) -> Option { match self { OffscreenRenderingContext::Context2d(context) => context.canvas(), OffscreenRenderingContext::BitmapRenderer(context) => context.canvas(), diff --git a/components/script/dom/canvas/imagebitmaprenderingcontext.rs b/components/script/dom/canvas/imagebitmaprenderingcontext.rs index 47c3e2ebe02..b37d6533882 100644 --- a/components/script/dom/canvas/imagebitmaprenderingcontext.rs +++ b/components/script/dom/canvas/imagebitmaprenderingcontext.rs @@ -9,11 +9,14 @@ use euclid::default::Size2D; use pixels::Snapshot; use webrender_api::ImageKey; -use crate::canvas_context::{CanvasContext, CanvasHelpers, LayoutCanvasRenderingContextHelpers}; +use crate::canvas_context::{ + CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas, + LayoutCanvasRenderingContextHelpers, +}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::ImageBitmapMethods; use crate::dom::bindings::codegen::Bindings::ImageBitmapRenderingContextBinding::ImageBitmapRenderingContextMethods; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::{DomRoot, LayoutDom}; @@ -37,6 +40,7 @@ pub(crate) struct ImageBitmapRenderingContext { impl ImageBitmapRenderingContext { /// + #[cfg_attr(crown, allow(crown::unrooted_must_root))] fn new_inherited(canvas: HTMLCanvasElementOrOffscreenCanvas) -> ImageBitmapRenderingContext { ImageBitmapRenderingContext { reflector_: Reflector::new(), @@ -48,11 +52,13 @@ impl ImageBitmapRenderingContext { pub(crate) fn new( global: &GlobalScope, - canvas: HTMLCanvasElementOrOffscreenCanvas, + canvas: &RootedHTMLCanvasElementOrOffscreenCanvas, can_gc: CanGc, ) -> DomRoot { reflect_dom_object( - Box::new(ImageBitmapRenderingContext::new_inherited(canvas)), + Box::new(ImageBitmapRenderingContext::new_inherited( + HTMLCanvasElementOrOffscreenCanvas::from(canvas), + )), global, can_gc, ) @@ -99,8 +105,8 @@ impl CanvasContext for ImageBitmapRenderingContext { fn context_id(&self) -> Self::ID {} - fn canvas(&self) -> Option { - Some(self.canvas.clone()) + fn canvas(&self) -> Option { + Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)) } /// @@ -158,8 +164,8 @@ impl CanvasContext for ImageBitmapRenderingContext { impl ImageBitmapRenderingContextMethods for ImageBitmapRenderingContext { /// - fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { - self.canvas.clone() + fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas { + RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas) } /// diff --git a/components/script/dom/canvas/offscreencanvas.rs b/components/script/dom/canvas/offscreencanvas.rs index ced6a4cf76f..f95f173ebbe 100644 --- a/components/script/dom/canvas/offscreencanvas.rs +++ b/components/script/dom/canvas/offscreencanvas.rs @@ -20,7 +20,7 @@ use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{ ImageEncodeOptions, OffscreenCanvasMethods, OffscreenRenderingContext as RootedOffscreenRenderingContext, }; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; @@ -135,7 +135,8 @@ impl OffscreenCanvas { _ => None, }; } - let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc)?; + let context = + OffscreenCanvasRenderingContext2D::new(&self.global(), self, self.get_size(), can_gc)?; *self.context.borrow_mut() = Some(OffscreenRenderingContext::Context2d(Dom::from_ref( &*context, ))); @@ -159,11 +160,10 @@ impl OffscreenCanvas { // Step 1. Let context be the result of running the // ImageBitmapRenderingContext creation algorithm given this and // options. - let context = ImageBitmapRenderingContext::new( - &self.global(), - HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(self)), - can_gc, - ); + let canvas = + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(self)); + + let context = ImageBitmapRenderingContext::new(&self.global(), &canvas, can_gc); // Step 2. Set this's context mode to bitmaprenderer. *self.context.borrow_mut() = Some(OffscreenRenderingContext::BitmapRenderer( diff --git a/components/script/dom/gpucanvascontext.rs b/components/script/dom/gpucanvascontext.rs index 312b7e119fa..42e19d71e80 100644 --- a/components/script/dom/gpucanvascontext.rs +++ b/components/script/dom/gpucanvascontext.rs @@ -2,15 +2,14 @@ * 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 base::Epoch; use dom_struct::dom_struct; use webrender_api::ImageKey; +use crate::canvas_context::LayoutCanvasRenderingContextHelpers; use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods; -use crate::dom::bindings::codegen::UnionTypes; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::reflector::Reflector; use crate::dom::bindings::root::LayoutDom; -use crate::dom::html::htmlcanvaselement::LayoutCanvasRenderingContextHelpers; #[dom_struct] pub(crate) struct GPUCanvasContext { @@ -26,7 +25,7 @@ impl GPUCanvasContext { impl GPUCanvasContextMethods for GPUCanvasContext { /// - fn Canvas(&self) -> UnionTypes::HTMLCanvasElementOrOffscreenCanvas { + fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas { unimplemented!() } } diff --git a/components/script/dom/html/htmlcanvaselement.rs b/components/script/dom/html/htmlcanvaselement.rs index 52f1fc7c81c..a0e5456aa67 100644 --- a/components/script/dom/html/htmlcanvaselement.rs +++ b/components/script/dom/html/htmlcanvaselement.rs @@ -24,7 +24,7 @@ use servo_media::streams::MediaStreamType; use servo_media::streams::registry::MediaStreamId; use style::attr::AttrValue; -pub(crate) use crate::canvas_context::*; +use crate::canvas_context::{CanvasContext, LayoutCanvasRenderingContextHelpers, RenderingContext}; use crate::conversions::Convert; use crate::dom::attr::Attr; use crate::dom::bindings::callback::ExceptionHandling; @@ -34,7 +34,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{ }; use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::conversions::ConversionResult; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; @@ -156,6 +156,10 @@ impl HTMLCanvasElement { } } +pub(crate) trait LayoutHTMLCanvasElementHelpers { + fn data(self) -> HTMLCanvasData; +} + impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> { #[allow(unsafe_code)] fn data(self) -> HTMLCanvasData { @@ -227,11 +231,10 @@ impl HTMLCanvasElement { // Step 1. Let context be the result of running the // ImageBitmapRenderingContext creation algorithm given this and // options. - let context = ImageBitmapRenderingContext::new( - &self.owner_global(), - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)), - can_gc, - ); + let canvas = + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); + + let context = ImageBitmapRenderingContext::new(&self.owner_global(), &canvas, can_gc); // Step 2. Set this's context mode to bitmaprenderer. *self.context_mode.borrow_mut() = @@ -254,9 +257,10 @@ impl HTMLCanvasElement { }; } let window = self.owner_window(); + let canvas = + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let size = self.get_size(); let attrs = Self::get_gl_attributes(cx, options)?; - let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let context = WebGLRenderingContext::new( &window, &canvas, @@ -286,9 +290,10 @@ impl HTMLCanvasElement { }; } let window = self.owner_window(); + let canvas = + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let size = self.get_size(); let attrs = Self::get_gl_attributes(cx, options)?; - let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?; *self.context_mode.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context))); Some(context) diff --git a/components/script/dom/webgl/webgl2renderingcontext.rs b/components/script/dom/webgl/webgl2renderingcontext.rs index 7117cf526cf..0b523d7950d 100644 --- a/components/script/dom/webgl/webgl2renderingcontext.rs +++ b/components/script/dom/webgl/webgl2renderingcontext.rs @@ -29,7 +29,7 @@ use url::Host; use webrender_api::ImageKey; use super::validations::types::TexImageTarget; -use crate::canvas_context::CanvasContext; +use crate::canvas_context::{CanvasContext, LayoutCanvasRenderingContextHelpers}; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{ WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods, }; @@ -38,15 +38,14 @@ use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{ }; use crate::dom::bindings::codegen::UnionTypes::{ ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence, - HTMLCanvasElementOrOffscreenCanvas, Int32ArrayOrLongSequence, - Uint32ArrayOrUnsignedLongSequence, + HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas, + Int32ArrayOrLongSequence, Uint32ArrayOrUnsignedLongSequence, }; use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayout}; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; -use crate::dom::html::htmlcanvaselement::LayoutCanvasRenderingContextHelpers; #[cfg(feature = "webxr")] use crate::dom::promise::Promise; use crate::dom::webgl::validations::WebGLValidator; @@ -133,7 +132,7 @@ struct ReadPixelsSizes { impl WebGL2RenderingContext { fn new_inherited( window: &Window, - canvas: &HTMLCanvasElementOrOffscreenCanvas, + canvas: &RootedHTMLCanvasElementOrOffscreenCanvas, size: Size2D, attrs: GLContextAttributes, can_gc: CanGc, @@ -179,10 +178,9 @@ impl WebGL2RenderingContext { }) } - #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( window: &Window, - canvas: &HTMLCanvasElementOrOffscreenCanvas, + canvas: &RootedHTMLCanvasElementOrOffscreenCanvas, size: Size2D, attrs: GLContextAttributes, can_gc: CanGc, @@ -973,8 +971,8 @@ impl CanvasContext for WebGL2RenderingContext { self.base.context_id() } - fn canvas(&self) -> Option { - self.base.canvas().clone() + fn canvas(&self) -> Option { + self.base.canvas() } fn resize(&self) { @@ -1000,7 +998,7 @@ impl CanvasContext for WebGL2RenderingContext { impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// - fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { + fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas { self.base.Canvas() } diff --git a/components/script/dom/webgl/webglrenderingcontext.rs b/components/script/dom/webgl/webglrenderingcontext.rs index 6664639fb3d..4e96bd7c2d6 100644 --- a/components/script/dom/webgl/webglrenderingcontext.rs +++ b/components/script/dom/webgl/webglrenderingcontext.rs @@ -35,7 +35,9 @@ use serde::{Deserialize, Serialize}; use servo_config::pref; use webrender_api::ImageKey; -use crate::canvas_context::CanvasContext; +use crate::canvas_context::{ + CanvasContext, HTMLCanvasElementOrOffscreenCanvas, LayoutCanvasRenderingContextHelpers, +}; use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut}; use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; @@ -47,7 +49,8 @@ use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{ }; use crate::dom::bindings::codegen::UnionTypes::{ ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence, - HTMLCanvasElementOrOffscreenCanvas, Int32ArrayOrLongSequence, + HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas, + Int32ArrayOrLongSequence, }; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; @@ -56,7 +59,6 @@ use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector, reflect_d use crate::dom::bindings::root::{DomOnceCell, DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::event::{Event, EventBubbles, EventCancelable}; -use crate::dom::html::htmlcanvaselement::LayoutCanvasRenderingContextHelpers; use crate::dom::node::{Node, NodeDamage, NodeTraits}; #[cfg(feature = "webxr")] use crate::dom::promise::Promise; @@ -216,9 +218,10 @@ pub(crate) struct WebGLRenderingContext { } impl WebGLRenderingContext { + #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new_inherited( window: &Window, - canvas: &HTMLCanvasElementOrOffscreenCanvas, + canvas: HTMLCanvasElementOrOffscreenCanvas, webgl_version: WebGLVersion, size: Size2D, attrs: GLContextAttributes, @@ -248,7 +251,7 @@ impl WebGLRenderingContext { webgl_version, glsl_version: ctx_data.glsl_version, limits: ctx_data.limits, - canvas: canvas.clone(), + canvas, last_error: Cell::new(None), texture_packing_alignment: Cell::new(4), texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE), @@ -285,13 +288,19 @@ impl WebGLRenderingContext { #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( window: &Window, - canvas: &HTMLCanvasElementOrOffscreenCanvas, + canvas: &RootedHTMLCanvasElementOrOffscreenCanvas, webgl_version: WebGLVersion, size: Size2D, attrs: GLContextAttributes, can_gc: CanGc, ) -> Option> { - match WebGLRenderingContext::new_inherited(window, canvas, webgl_version, size, attrs) { + match WebGLRenderingContext::new_inherited( + window, + HTMLCanvasElementOrOffscreenCanvas::from(canvas), + webgl_version, + size, + attrs, + ) { Ok(ctx) => Some(reflect_dom_object(Box::new(ctx), window, can_gc)), Err(msg) => { error!("Couldn't create WebGLRenderingContext: {}", msg); @@ -304,10 +313,10 @@ impl WebGLRenderingContext { can_gc, ); match canvas { - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { + RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => { event.upcast::().fire(canvas.upcast(), can_gc); }, - HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { + RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { event.upcast::().fire(canvas.upcast(), can_gc); }, } @@ -1930,8 +1939,8 @@ impl CanvasContext for WebGLRenderingContext { self.webgl_sender.context_id() } - fn canvas(&self) -> Option { - Some(self.canvas.clone()) + fn canvas(&self) -> Option { + Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)) } fn resize(&self) { @@ -2075,8 +2084,8 @@ impl Drop for WebGLRenderingContext { impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1 - fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { - self.canvas.clone() + fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas { + RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 diff --git a/components/script/dom/webgpu/gpucanvascontext.rs b/components/script/dom/webgpu/gpucanvascontext.rs index d3769695af0..a00b2ec56c1 100644 --- a/components/script/dom/webgpu/gpucanvascontext.rs +++ b/components/script/dom/webgpu/gpucanvascontext.rs @@ -20,7 +20,10 @@ use wgpu_core::id; use super::gpuconvert::convert_texture_descriptor; use super::gputexture::GPUTexture; -use crate::canvas_context::{CanvasContext, CanvasHelpers}; +use crate::canvas_context::{ + CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas, + LayoutCanvasRenderingContextHelpers, +}; use crate::conversions::Convert; use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUTexture_Binding::GPUTextureMethods; @@ -29,15 +32,15 @@ use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ GPUObjectDescriptorBase, GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat, GPUTextureUsageConstants, }; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; +use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; -use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom}; +use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::USVString; use crate::dom::bindings::weakref::WeakRef; use crate::dom::document::WebGPUContextsMap; use crate::dom::globalscope::GlobalScope; -use crate::dom::html::htmlcanvaselement::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers}; +use crate::dom::html::htmlcanvaselement::HTMLCanvasElement; use crate::dom::node::NodeTraits; use crate::script_runtime::CanGc; @@ -91,6 +94,7 @@ pub(crate) struct GPUCanvasContext { } impl GPUCanvasContext { + #[cfg_attr(crown, allow(crown::unrooted_must_root))] fn new_inherited( global: &GlobalScope, canvas: HTMLCanvasElementOrOffscreenCanvas, @@ -139,7 +143,7 @@ impl GPUCanvasContext { let this = reflect_dom_object( Box::new(GPUCanvasContext::new_inherited( global, - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)), + HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(Dom::from_ref(canvas)), channel, document.webgpu_contexts(), )), @@ -328,8 +332,8 @@ impl CanvasContext for GPUCanvasContext { }) } - fn canvas(&self) -> Option { - Some(self.canvas.clone()) + fn canvas(&self) -> Option { + Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)) } } @@ -341,8 +345,8 @@ impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> { impl GPUCanvasContextMethods for GPUCanvasContext { /// - fn Canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { - self.canvas.clone() + fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas { + RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas) } /// diff --git a/components/script/dom/webxr/xrwebgllayer.rs b/components/script/dom/webxr/xrwebgllayer.rs index 32a745f42b7..087aad6e7f4 100644 --- a/components/script/dom/webxr/xrwebgllayer.rs +++ b/components/script/dom/webxr/xrwebgllayer.rs @@ -10,14 +10,13 @@ use euclid::{Rect, Size2D}; use js::rust::HandleObject; use webxr_api::{ContextId as WebXRContextId, LayerId, LayerInit, Viewport}; -use crate::canvas_context::CanvasContext as _; +use crate::canvas_context::CanvasContext; use crate::conversions::Convert; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::{ XRWebGLLayerInit, XRWebGLLayerMethods, XRWebGLRenderingContext, }; -use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; @@ -125,14 +124,7 @@ impl XRWebGLLayer { size.1.try_into().unwrap_or(0), ) } else { - let size = match self.context().Canvas() { - HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.get_size(), - HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => { - let size = canvas.get_size(); - Size2D::new(size.width, size.height) - }, - }; - Size2D::from_untyped(size) + Size2D::from_untyped(self.context().size()) } }