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