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