1use euclid::default::Size2D;
8use pixels::Snapshot;
9use script_bindings::root::{Dom, DomRoot};
10use webrender_api::ImageKey;
11
12use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas;
13use crate::dom::bindings::inheritance::Castable;
14use crate::dom::node::{Node, NodeTraits};
15#[cfg(feature = "webgpu")]
16use crate::dom::types::GPUCanvasContext;
17use crate::dom::types::{
18 CanvasRenderingContext2D, HTMLCanvasElement, ImageBitmapRenderingContext, OffscreenCanvas,
19 OffscreenCanvasRenderingContext2D, WebGL2RenderingContext, WebGLRenderingContext,
20};
21
22#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
24#[derive(Clone, JSTraceable, MallocSizeOf)]
25pub(crate) enum HTMLCanvasElementOrOffscreenCanvas {
26 HTMLCanvasElement(Dom<HTMLCanvasElement>),
27 OffscreenCanvas(Dom<OffscreenCanvas>),
28}
29
30impl HTMLCanvasElementOrOffscreenCanvas {
31 pub(crate) fn mark_as_dirty(&self) {
32 if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) = self {
33 canvas.owner_document().mark_canvas_as_dirty(canvas);
34 }
35 }
36}
37
38impl From<&RootedHTMLCanvasElementOrOffscreenCanvas> for HTMLCanvasElementOrOffscreenCanvas {
39 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
41 fn from(
42 value: &RootedHTMLCanvasElementOrOffscreenCanvas,
43 ) -> HTMLCanvasElementOrOffscreenCanvas {
44 match value {
45 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
46 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas.as_traced())
47 },
48 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
49 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas.as_traced())
50 },
51 }
52 }
53}
54
55impl From<&HTMLCanvasElementOrOffscreenCanvas> for RootedHTMLCanvasElementOrOffscreenCanvas {
56 fn from(
58 value: &HTMLCanvasElementOrOffscreenCanvas,
59 ) -> RootedHTMLCanvasElementOrOffscreenCanvas {
60 match value {
61 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
62 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas.as_rooted())
63 },
64 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
65 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas.as_rooted())
66 },
67 }
68 }
69}
70
71pub(crate) trait CanvasContext {
72 type ID;
73
74 fn context_id(&self) -> Self::ID;
75
76 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas>;
77
78 fn resize(&self);
79
80 fn reset_bitmap(&self);
84
85 fn get_image_data(&self) -> Option<Snapshot>;
89
90 fn origin_is_clean(&self) -> bool {
91 true
92 }
93
94 fn size(&self) -> Size2D<u32> {
95 self.canvas()
96 .map(|canvas| canvas.size())
97 .unwrap_or_default()
98 }
99
100 fn mark_as_dirty(&self);
101
102 fn onscreen(&self) -> bool {
103 let Some(canvas) = self.canvas() else {
104 return false;
105 };
106
107 match canvas {
108 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
109 canvas.upcast::<Node>().is_connected()
110 },
111 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false,
114 }
115 }
116}
117
118pub(crate) trait CanvasHelpers {
119 fn size(&self) -> Size2D<u32>;
120 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>>;
121}
122
123impl CanvasHelpers for HTMLCanvasElementOrOffscreenCanvas {
124 fn size(&self) -> Size2D<u32> {
125 match self {
126 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
127 canvas.get_size().cast()
128 },
129 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
130 }
131 }
132
133 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>> {
134 match self {
135 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
136 Some(canvas.as_rooted())
137 },
138 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.placeholder(),
139 }
140 }
141}
142
143impl CanvasHelpers for RootedHTMLCanvasElementOrOffscreenCanvas {
144 fn size(&self) -> Size2D<u32> {
145 match self {
146 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
147 canvas.get_size().cast()
148 },
149 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
150 }
151 }
152
153 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>> {
154 match self {
155 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
156 Some(canvas.clone())
157 },
158 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
159 canvas.placeholder()
160 },
161 }
162 }
163}
164
165#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
167#[derive(Clone, JSTraceable, MallocSizeOf)]
168pub(crate) enum RenderingContext {
169 Placeholder(Dom<OffscreenCanvas>),
170 Context2d(Dom<CanvasRenderingContext2D>),
171 BitmapRenderer(Dom<ImageBitmapRenderingContext>),
172 WebGL(Dom<WebGLRenderingContext>),
173 WebGL2(Dom<WebGL2RenderingContext>),
174 #[cfg(feature = "webgpu")]
175 WebGPU(Dom<GPUCanvasContext>),
176}
177
178impl RenderingContext {
179 pub(crate) fn set_image_key(&self, image_key: ImageKey) {
180 match self {
181 RenderingContext::Placeholder(_) => {
182 unreachable!("Should never set an `ImageKey` on a Placeholder")
183 },
184 RenderingContext::Context2d(context) => context.set_image_key(image_key),
185 RenderingContext::BitmapRenderer(_) => {
186 unreachable!("Should never set an `ImageKey` on a ImageBitmapRenderingContext")
187 },
188 RenderingContext::WebGL(context) => context.set_image_key(image_key),
189 RenderingContext::WebGL2(context) => context.set_image_key(image_key),
190 #[cfg(feature = "webgpu")]
191 RenderingContext::WebGPU(context) => context.set_image_key(image_key),
192 }
193 }
194}
195
196impl CanvasContext for RenderingContext {
197 type ID = ();
198
199 fn context_id(&self) -> Self::ID {}
200
201 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
202 match self {
203 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas.context()?.canvas(),
204 RenderingContext::Context2d(context) => context.canvas(),
205 RenderingContext::BitmapRenderer(context) => context.canvas(),
206 RenderingContext::WebGL(context) => context.canvas(),
207 RenderingContext::WebGL2(context) => context.canvas(),
208 #[cfg(feature = "webgpu")]
209 RenderingContext::WebGPU(context) => context.canvas(),
210 }
211 }
212
213 fn resize(&self) {
214 match self {
215 RenderingContext::Placeholder(offscreen_canvas) => {
216 if let Some(context) = offscreen_canvas.context() {
217 context.resize()
218 }
219 },
220 RenderingContext::Context2d(context) => context.resize(),
221 RenderingContext::BitmapRenderer(context) => context.resize(),
222 RenderingContext::WebGL(context) => context.resize(),
223 RenderingContext::WebGL2(context) => context.resize(),
224 #[cfg(feature = "webgpu")]
225 RenderingContext::WebGPU(context) => context.resize(),
226 }
227 }
228
229 fn reset_bitmap(&self) {
230 match self {
231 RenderingContext::Placeholder(offscreen_canvas) => {
232 if let Some(context) = offscreen_canvas.context() {
233 context.reset_bitmap()
234 }
235 },
236 RenderingContext::Context2d(context) => context.reset_bitmap(),
237 RenderingContext::BitmapRenderer(context) => context.reset_bitmap(),
238 RenderingContext::WebGL(context) => context.reset_bitmap(),
239 RenderingContext::WebGL2(context) => context.reset_bitmap(),
240 #[cfg(feature = "webgpu")]
241 RenderingContext::WebGPU(context) => context.reset_bitmap(),
242 }
243 }
244
245 fn get_image_data(&self) -> Option<Snapshot> {
246 match self {
247 RenderingContext::Placeholder(offscreen_canvas) => {
248 offscreen_canvas.context()?.get_image_data()
249 },
250 RenderingContext::Context2d(context) => context.get_image_data(),
251 RenderingContext::BitmapRenderer(context) => context.get_image_data(),
252 RenderingContext::WebGL(context) => context.get_image_data(),
253 RenderingContext::WebGL2(context) => context.get_image_data(),
254 #[cfg(feature = "webgpu")]
255 RenderingContext::WebGPU(context) => context.get_image_data(),
256 }
257 }
258
259 fn origin_is_clean(&self) -> bool {
260 match self {
261 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
262 .context()
263 .is_none_or(|context| context.origin_is_clean()),
264 RenderingContext::Context2d(context) => context.origin_is_clean(),
265 RenderingContext::BitmapRenderer(context) => context.origin_is_clean(),
266 RenderingContext::WebGL(context) => context.origin_is_clean(),
267 RenderingContext::WebGL2(context) => context.origin_is_clean(),
268 #[cfg(feature = "webgpu")]
269 RenderingContext::WebGPU(context) => context.origin_is_clean(),
270 }
271 }
272
273 fn size(&self) -> Size2D<u32> {
274 match self {
275 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
276 .context()
277 .map(|context| context.size())
278 .unwrap_or_default(),
279 RenderingContext::Context2d(context) => context.size(),
280 RenderingContext::BitmapRenderer(context) => context.size(),
281 RenderingContext::WebGL(context) => context.size(),
282 RenderingContext::WebGL2(context) => context.size(),
283 #[cfg(feature = "webgpu")]
284 RenderingContext::WebGPU(context) => context.size(),
285 }
286 }
287
288 fn mark_as_dirty(&self) {
289 match self {
290 RenderingContext::Placeholder(offscreen_canvas) => {
291 if let Some(context) = offscreen_canvas.context() {
292 context.mark_as_dirty()
293 }
294 },
295 RenderingContext::Context2d(context) => context.mark_as_dirty(),
296 RenderingContext::BitmapRenderer(context) => context.mark_as_dirty(),
297 RenderingContext::WebGL(context) => context.mark_as_dirty(),
298 RenderingContext::WebGL2(context) => context.mark_as_dirty(),
299 #[cfg(feature = "webgpu")]
300 RenderingContext::WebGPU(context) => context.mark_as_dirty(),
301 }
302 }
303
304 fn onscreen(&self) -> bool {
305 match self {
306 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
307 .context()
308 .is_some_and(|context| context.onscreen()),
309 RenderingContext::Context2d(context) => context.onscreen(),
310 RenderingContext::BitmapRenderer(context) => context.onscreen(),
311 RenderingContext::WebGL(context) => context.onscreen(),
312 RenderingContext::WebGL2(context) => context.onscreen(),
313 #[cfg(feature = "webgpu")]
314 RenderingContext::WebGPU(context) => context.onscreen(),
315 }
316 }
317}
318
319#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
321#[derive(Clone, JSTraceable, MallocSizeOf)]
322pub(crate) enum OffscreenRenderingContext {
323 Context2d(Dom<OffscreenCanvasRenderingContext2D>),
324 BitmapRenderer(Dom<ImageBitmapRenderingContext>),
325 Detached,
330}
331
332impl CanvasContext for OffscreenRenderingContext {
333 type ID = ();
334
335 fn context_id(&self) -> Self::ID {}
336
337 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
338 match self {
339 OffscreenRenderingContext::Context2d(context) => context.canvas(),
340 OffscreenRenderingContext::BitmapRenderer(context) => context.canvas(),
341 OffscreenRenderingContext::Detached => None,
342 }
343 }
344
345 fn resize(&self) {
346 match self {
347 OffscreenRenderingContext::Context2d(context) => context.resize(),
348 OffscreenRenderingContext::BitmapRenderer(context) => context.resize(),
349 OffscreenRenderingContext::Detached => {},
350 }
351 }
352
353 fn reset_bitmap(&self) {
354 match self {
355 OffscreenRenderingContext::Context2d(context) => context.reset_bitmap(),
356 OffscreenRenderingContext::BitmapRenderer(context) => context.reset_bitmap(),
357 OffscreenRenderingContext::Detached => {},
358 }
359 }
360
361 fn get_image_data(&self) -> Option<Snapshot> {
362 match self {
363 OffscreenRenderingContext::Context2d(context) => context.get_image_data(),
364 OffscreenRenderingContext::BitmapRenderer(context) => context.get_image_data(),
365 OffscreenRenderingContext::Detached => None,
366 }
367 }
368
369 fn origin_is_clean(&self) -> bool {
370 match self {
371 OffscreenRenderingContext::Context2d(context) => context.origin_is_clean(),
372 OffscreenRenderingContext::BitmapRenderer(context) => context.origin_is_clean(),
373 OffscreenRenderingContext::Detached => true,
374 }
375 }
376
377 fn size(&self) -> Size2D<u32> {
378 match self {
379 OffscreenRenderingContext::Context2d(context) => context.size(),
380 OffscreenRenderingContext::BitmapRenderer(context) => context.size(),
381 OffscreenRenderingContext::Detached => Size2D::default(),
382 }
383 }
384
385 fn mark_as_dirty(&self) {
386 match self {
387 OffscreenRenderingContext::Context2d(context) => context.mark_as_dirty(),
388 OffscreenRenderingContext::BitmapRenderer(context) => context.mark_as_dirty(),
389 OffscreenRenderingContext::Detached => {},
390 }
391 }
392
393 fn onscreen(&self) -> bool {
394 match self {
395 OffscreenRenderingContext::Context2d(context) => context.onscreen(),
396 OffscreenRenderingContext::BitmapRenderer(context) => context.onscreen(),
397 OffscreenRenderingContext::Detached => false,
398 }
399 }
400}