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