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;
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
100 fn image_key(&self) -> Option<ImageKey>;
102
103 fn update_rendering(&self, _canvas_epoch: Epoch) -> bool {
111 false
112 }
113
114 fn onscreen(&self) -> bool {
115 let Some(canvas) = self.canvas() else {
116 return false;
117 };
118
119 match canvas {
120 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
121 canvas.upcast::<Node>().is_connected()
122 },
123 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(_) => false,
126 }
127 }
128}
129
130pub(crate) trait CanvasHelpers {
131 fn size(&self) -> Size2D<u32>;
132 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>>;
133}
134
135impl CanvasHelpers for HTMLCanvasElementOrOffscreenCanvas {
136 fn size(&self) -> Size2D<u32> {
137 match self {
138 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
139 canvas.get_size().cast()
140 },
141 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
142 }
143 }
144
145 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>> {
146 match self {
147 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
148 Some(canvas.as_rooted())
149 },
150 HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.placeholder(),
151 }
152 }
153}
154
155impl CanvasHelpers for RootedHTMLCanvasElementOrOffscreenCanvas {
156 fn size(&self) -> Size2D<u32> {
157 match self {
158 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
159 canvas.get_size().cast()
160 },
161 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.get_size(),
162 }
163 }
164
165 fn canvas(&self) -> Option<DomRoot<HTMLCanvasElement>> {
166 match self {
167 RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
168 Some(canvas.clone())
169 },
170 RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
171 canvas.placeholder()
172 },
173 }
174 }
175}
176
177#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
179#[derive(Clone, JSTraceable, MallocSizeOf)]
180pub(crate) enum RenderingContext {
181 Placeholder(Dom<OffscreenCanvas>),
182 Context2d(Dom<CanvasRenderingContext2D>),
183 BitmapRenderer(Dom<ImageBitmapRenderingContext>),
184 WebGL(Dom<WebGLRenderingContext>),
185 WebGL2(Dom<WebGL2RenderingContext>),
186 #[cfg(feature = "webgpu")]
187 WebGPU(Dom<GPUCanvasContext>),
188}
189
190impl CanvasContext for RenderingContext {
191 type ID = ();
192
193 fn context_id(&self) -> Self::ID {}
194
195 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
196 match self {
197 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas.context()?.canvas(),
198 RenderingContext::Context2d(context) => context.canvas(),
199 RenderingContext::BitmapRenderer(context) => context.canvas(),
200 RenderingContext::WebGL(context) => context.canvas(),
201 RenderingContext::WebGL2(context) => context.canvas(),
202 #[cfg(feature = "webgpu")]
203 RenderingContext::WebGPU(context) => context.canvas(),
204 }
205 }
206
207 fn resize(&self) {
208 match self {
209 RenderingContext::Placeholder(offscreen_canvas) => {
210 if let Some(context) = offscreen_canvas.context() {
211 context.resize()
212 }
213 },
214 RenderingContext::Context2d(context) => context.resize(),
215 RenderingContext::BitmapRenderer(context) => context.resize(),
216 RenderingContext::WebGL(context) => context.resize(),
217 RenderingContext::WebGL2(context) => context.resize(),
218 #[cfg(feature = "webgpu")]
219 RenderingContext::WebGPU(context) => context.resize(),
220 }
221 }
222
223 fn reset_bitmap(&self) {
224 match self {
225 RenderingContext::Placeholder(offscreen_canvas) => {
226 if let Some(context) = offscreen_canvas.context() {
227 context.reset_bitmap()
228 }
229 },
230 RenderingContext::Context2d(context) => context.reset_bitmap(),
231 RenderingContext::BitmapRenderer(context) => context.reset_bitmap(),
232 RenderingContext::WebGL(context) => context.reset_bitmap(),
233 RenderingContext::WebGL2(context) => context.reset_bitmap(),
234 #[cfg(feature = "webgpu")]
235 RenderingContext::WebGPU(context) => context.reset_bitmap(),
236 }
237 }
238
239 fn get_image_data(&self) -> Option<Snapshot> {
240 match self {
241 RenderingContext::Placeholder(offscreen_canvas) => {
242 offscreen_canvas.context()?.get_image_data()
243 },
244 RenderingContext::Context2d(context) => context.get_image_data(),
245 RenderingContext::BitmapRenderer(context) => context.get_image_data(),
246 RenderingContext::WebGL(context) => context.get_image_data(),
247 RenderingContext::WebGL2(context) => context.get_image_data(),
248 #[cfg(feature = "webgpu")]
249 RenderingContext::WebGPU(context) => context.get_image_data(),
250 }
251 }
252
253 fn origin_is_clean(&self) -> bool {
254 match self {
255 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
256 .context()
257 .is_none_or(|context| context.origin_is_clean()),
258 RenderingContext::Context2d(context) => context.origin_is_clean(),
259 RenderingContext::BitmapRenderer(context) => context.origin_is_clean(),
260 RenderingContext::WebGL(context) => context.origin_is_clean(),
261 RenderingContext::WebGL2(context) => context.origin_is_clean(),
262 #[cfg(feature = "webgpu")]
263 RenderingContext::WebGPU(context) => context.origin_is_clean(),
264 }
265 }
266
267 fn size(&self) -> Size2D<u32> {
268 match self {
269 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
270 .context()
271 .map(|context| context.size())
272 .unwrap_or_default(),
273 RenderingContext::Context2d(context) => context.size(),
274 RenderingContext::BitmapRenderer(context) => context.size(),
275 RenderingContext::WebGL(context) => context.size(),
276 RenderingContext::WebGL2(context) => context.size(),
277 #[cfg(feature = "webgpu")]
278 RenderingContext::WebGPU(context) => context.size(),
279 }
280 }
281
282 fn mark_as_dirty(&self) {
283 match self {
284 RenderingContext::Placeholder(offscreen_canvas) => {
285 if let Some(context) = offscreen_canvas.context() {
286 context.mark_as_dirty()
287 }
288 },
289 RenderingContext::Context2d(context) => context.mark_as_dirty(),
290 RenderingContext::BitmapRenderer(context) => context.mark_as_dirty(),
291 RenderingContext::WebGL(context) => context.mark_as_dirty(),
292 RenderingContext::WebGL2(context) => context.mark_as_dirty(),
293 #[cfg(feature = "webgpu")]
294 RenderingContext::WebGPU(context) => context.mark_as_dirty(),
295 }
296 }
297
298 fn image_key(&self) -> Option<ImageKey> {
299 match self {
300 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
301 .context()
302 .and_then(|context| context.image_key()),
303 RenderingContext::Context2d(context) => context.image_key(),
304 RenderingContext::BitmapRenderer(context) => context.image_key(),
305 RenderingContext::WebGL(context) => context.image_key(),
306 RenderingContext::WebGL2(context) => context.image_key(),
307 #[cfg(feature = "webgpu")]
308 RenderingContext::WebGPU(context) => context.image_key(),
309 }
310 }
311
312 fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
313 match self {
314 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
315 .context()
316 .is_some_and(|context| context.update_rendering(canvas_epoch)),
317 RenderingContext::Context2d(context) => context.update_rendering(canvas_epoch),
318 RenderingContext::BitmapRenderer(context) => context.update_rendering(canvas_epoch),
319 RenderingContext::WebGL(context) => context.update_rendering(canvas_epoch),
320 RenderingContext::WebGL2(context) => context.update_rendering(canvas_epoch),
321 #[cfg(feature = "webgpu")]
322 RenderingContext::WebGPU(context) => context.update_rendering(canvas_epoch),
323 }
324 }
325
326 fn onscreen(&self) -> bool {
327 match self {
328 RenderingContext::Placeholder(offscreen_canvas) => offscreen_canvas
329 .context()
330 .is_some_and(|context| context.onscreen()),
331 RenderingContext::Context2d(context) => context.onscreen(),
332 RenderingContext::BitmapRenderer(context) => context.onscreen(),
333 RenderingContext::WebGL(context) => context.onscreen(),
334 RenderingContext::WebGL2(context) => context.onscreen(),
335 #[cfg(feature = "webgpu")]
336 RenderingContext::WebGPU(context) => context.onscreen(),
337 }
338 }
339}
340
341#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
343#[derive(Clone, JSTraceable, MallocSizeOf)]
344pub(crate) enum OffscreenRenderingContext {
345 Context2d(Dom<OffscreenCanvasRenderingContext2D>),
346 BitmapRenderer(Dom<ImageBitmapRenderingContext>),
347 Detached,
352}
353
354impl CanvasContext for OffscreenRenderingContext {
355 type ID = ();
356
357 fn context_id(&self) -> Self::ID {}
358
359 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
360 match self {
361 OffscreenRenderingContext::Context2d(context) => context.canvas(),
362 OffscreenRenderingContext::BitmapRenderer(context) => context.canvas(),
363 OffscreenRenderingContext::Detached => None,
364 }
365 }
366
367 fn resize(&self) {
368 match self {
369 OffscreenRenderingContext::Context2d(context) => context.resize(),
370 OffscreenRenderingContext::BitmapRenderer(context) => context.resize(),
371 OffscreenRenderingContext::Detached => {},
372 }
373 }
374
375 fn reset_bitmap(&self) {
376 match self {
377 OffscreenRenderingContext::Context2d(context) => context.reset_bitmap(),
378 OffscreenRenderingContext::BitmapRenderer(context) => context.reset_bitmap(),
379 OffscreenRenderingContext::Detached => {},
380 }
381 }
382
383 fn get_image_data(&self) -> Option<Snapshot> {
384 match self {
385 OffscreenRenderingContext::Context2d(context) => context.get_image_data(),
386 OffscreenRenderingContext::BitmapRenderer(context) => context.get_image_data(),
387 OffscreenRenderingContext::Detached => None,
388 }
389 }
390
391 fn origin_is_clean(&self) -> bool {
392 match self {
393 OffscreenRenderingContext::Context2d(context) => context.origin_is_clean(),
394 OffscreenRenderingContext::BitmapRenderer(context) => context.origin_is_clean(),
395 OffscreenRenderingContext::Detached => true,
396 }
397 }
398
399 fn size(&self) -> Size2D<u32> {
400 match self {
401 OffscreenRenderingContext::Context2d(context) => context.size(),
402 OffscreenRenderingContext::BitmapRenderer(context) => context.size(),
403 OffscreenRenderingContext::Detached => Size2D::default(),
404 }
405 }
406
407 fn mark_as_dirty(&self) {
408 match self {
409 OffscreenRenderingContext::Context2d(context) => context.mark_as_dirty(),
410 OffscreenRenderingContext::BitmapRenderer(context) => context.mark_as_dirty(),
411 OffscreenRenderingContext::Detached => {},
412 }
413 }
414
415 fn image_key(&self) -> Option<ImageKey> {
416 None
417 }
418
419 fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
420 match self {
421 OffscreenRenderingContext::Context2d(context) => context.update_rendering(canvas_epoch),
422 OffscreenRenderingContext::BitmapRenderer(context) => {
423 context.update_rendering(canvas_epoch)
424 },
425 OffscreenRenderingContext::Detached => false,
426 }
427 }
428
429 fn onscreen(&self) -> bool {
430 match self {
431 OffscreenRenderingContext::Context2d(context) => context.onscreen(),
432 OffscreenRenderingContext::BitmapRenderer(context) => context.onscreen(),
433 OffscreenRenderingContext::Detached => false,
434 }
435 }
436}