script/dom/imagebitmap.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, Ref};
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use base::id::{ImageBitmapId, ImageBitmapIndex};
10use constellation_traits::SerializableImageBitmap;
11use dom_struct::dom_struct;
12use euclid::default::{Point2D, Rect, Size2D};
13use pixels::{CorsStatus, PixelFormat, Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
14use script_bindings::error::{Error, Fallible};
15use script_bindings::realms::{AlreadyInRealm, InRealm};
16
17use crate::dom::bindings::cell::DomRefCell;
18use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
19 ImageBitmapMethods, ImageBitmapOptions, ImageBitmapSource, ImageOrientation, PremultiplyAlpha,
20 ResizeQuality,
21};
22use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
23use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::bindings::serializable::Serializable;
26use crate::dom::bindings::structuredclone::StructuredData;
27use crate::dom::bindings::transferable::Transferable;
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::types::Promise;
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
33pub(crate) struct ImageBitmap {
34 reflector_: Reflector,
35 /// The actual pixel data of the bitmap
36 ///
37 /// If this is `None`, then the bitmap data has been released by calling
38 /// [`close`](https://html.spec.whatwg.org/multipage/#dom-imagebitmap-close)
39 #[no_trace]
40 bitmap_data: DomRefCell<Option<Snapshot>>,
41 origin_clean: Cell<bool>,
42}
43
44impl ImageBitmap {
45 fn new_inherited(bitmap_data: Snapshot) -> ImageBitmap {
46 ImageBitmap {
47 reflector_: Reflector::new(),
48 bitmap_data: DomRefCell::new(Some(bitmap_data)),
49 origin_clean: Cell::new(true),
50 }
51 }
52
53 pub(crate) fn new(
54 global: &GlobalScope,
55 bitmap_data: Snapshot,
56 can_gc: CanGc,
57 ) -> DomRoot<ImageBitmap> {
58 reflect_dom_object(
59 Box::new(ImageBitmap::new_inherited(bitmap_data)),
60 global,
61 can_gc,
62 )
63 }
64
65 #[allow(dead_code)]
66 pub(crate) fn bitmap_data(&self) -> Ref<'_, Option<Snapshot>> {
67 self.bitmap_data.borrow()
68 }
69
70 pub(crate) fn origin_is_clean(&self) -> bool {
71 self.origin_clean.get()
72 }
73
74 pub(crate) fn set_origin_clean(&self, origin_is_clean: bool) {
75 self.origin_clean.set(origin_is_clean);
76 }
77
78 /// Return the value of the [`[[Detached]]`](https://html.spec.whatwg.org/multipage/#detached)
79 /// internal slot
80 pub(crate) fn is_detached(&self) -> bool {
81 self.bitmap_data.borrow().is_none()
82 }
83
84 /// <https://html.spec.whatwg.org/multipage/#cropped-to-the-source-rectangle-with-formatting>
85 pub(crate) fn crop_and_transform_bitmap_data(
86 input: Snapshot,
87 mut sx: i32,
88 mut sy: i32,
89 sw: Option<i32>,
90 sh: Option<i32>,
91 options: &ImageBitmapOptions,
92 ) -> Option<Snapshot> {
93 let input_size = input.size().to_i32();
94
95 // Step 2. If sx, sy, sw and sh are specified, let sourceRectangle be a rectangle whose corners
96 // are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh). Otherwise,
97 // let sourceRectangle be a rectangle whose corners are the four points (0, 0), (width of input, 0),
98 // (width of input, height of input), (0, height of input). If either sw or sh are negative,
99 // then the top-left corner of this rectangle will be to the left or above the (sx, sy) point.
100 let sw = sw.map_or(input_size.width, |width| {
101 if width < 0 {
102 sx = sx.saturating_add(width);
103 width.saturating_abs()
104 } else {
105 width
106 }
107 });
108
109 let sh = sh.map_or(input_size.height, |height| {
110 if height < 0 {
111 sy = sy.saturating_add(height);
112 height.saturating_abs()
113 } else {
114 height
115 }
116 });
117
118 let source_rect = Rect::new(Point2D::new(sx, sy), Size2D::new(sw, sh));
119
120 // Whether the byte length of the source bitmap exceeds the supported range.
121 // In the case the source is too large, we should fail, and that is not defined.
122 // <https://github.com/whatwg/html/issues/3323>
123 let Some(source_byte_length) = pixels::compute_rgba8_byte_length_if_within_limit(
124 source_rect.size.width as usize,
125 source_rect.size.height as usize,
126 ) else {
127 log::warn!(
128 "Failed to allocate bitmap of size {:?}, too large",
129 source_rect.size
130 );
131 return None;
132 };
133
134 // Step 3. Let outputWidth be determined as follows:
135 // Step 4. Let outputHeight be determined as follows:
136 let output_size = match (options.resizeWidth, options.resizeHeight) {
137 (Some(width), Some(height)) => Size2D::new(width, height),
138 (Some(width), None) => {
139 let height =
140 source_rect.size.height as f64 * width as f64 / source_rect.size.width as f64;
141 Size2D::new(width, height.round() as u32)
142 },
143 (None, Some(height)) => {
144 let width =
145 source_rect.size.width as f64 * height as f64 / source_rect.size.height as f64;
146 Size2D::new(width.round() as u32, height)
147 },
148 (None, None) => source_rect.size.to_u32(),
149 };
150
151 // Whether the byte length of the output bitmap exceeds the supported range.
152 // In the case the output is too large, we should fail, and that is not defined.
153 // <https://github.com/whatwg/html/issues/3323>
154 let Some(output_byte_length) = pixels::compute_rgba8_byte_length_if_within_limit(
155 output_size.width as usize,
156 output_size.height as usize,
157 ) else {
158 log::warn!(
159 "Failed to allocate bitmap of size {:?}, too large",
160 output_size
161 );
162 return None;
163 };
164
165 // TODO: Take into account the image orientation (such as EXIF metadata).
166
167 // Step 5. Place input on an infinite transparent black grid plane, positioned so that
168 // its top left corner is at the origin of the plane, with the x-coordinate increasing to the right,
169 // and the y-coordinate increasing down, and with each pixel in the input image data occupying a cell
170 // on the plane's grid.
171 let input_rect = Rect::new(Point2D::zero(), input_size);
172
173 let input_rect_cropped = source_rect
174 .intersection(&input_rect)
175 .unwrap_or(Rect::zero());
176
177 // Early out for empty tranformations.
178 if input_rect_cropped.is_empty() {
179 return Some(Snapshot::cleared(output_size));
180 }
181
182 // Step 6. Let output be the rectangle on the plane denoted by sourceRectangle.
183 let mut source: Snapshot = Snapshot::from_vec(
184 source_rect.size.cast(),
185 input.format(),
186 input.alpha_mode(),
187 vec![0; source_byte_length],
188 );
189
190 let source_rect_cropped = Rect::new(
191 Point2D::new(
192 input_rect_cropped.origin.x - source_rect.origin.x,
193 input_rect_cropped.origin.y - source_rect.origin.y,
194 ),
195 input_rect_cropped.size,
196 );
197
198 pixels::copy_rgba8_image(
199 input.size(),
200 input_rect_cropped.cast(),
201 input.as_raw_bytes(),
202 source.size(),
203 source_rect_cropped.cast(),
204 source.as_raw_bytes_mut(),
205 );
206
207 // Step 7. Scale output to the size specified by outputWidth and outputHeight.
208 let mut output = if source.size() != output_size {
209 let quality = match options.resizeQuality {
210 ResizeQuality::Pixelated => pixels::FilterQuality::None,
211 ResizeQuality::Low => pixels::FilterQuality::Low,
212 ResizeQuality::Medium => pixels::FilterQuality::Medium,
213 ResizeQuality::High => pixels::FilterQuality::High,
214 };
215
216 let Some(output_data) = pixels::scale_rgba8_image(
217 source.size(),
218 source.as_raw_bytes(),
219 output_size,
220 quality,
221 ) else {
222 log::warn!(
223 "Failed to scale the bitmap of size {:?} to required size {:?}",
224 source.size(),
225 output_size
226 );
227 return None;
228 };
229
230 debug_assert_eq!(output_data.len(), output_byte_length);
231
232 Snapshot::from_vec(
233 output_size,
234 source.format(),
235 source.alpha_mode(),
236 output_data,
237 )
238 } else {
239 source
240 };
241
242 // Step 8. If the value of the imageOrientation member of options is "flipY",
243 // output must be flipped vertically, disregarding any image orientation metadata
244 // of the source (such as EXIF metadata), if any.
245 if options.imageOrientation == ImageOrientation::FlipY {
246 pixels::flip_y_rgba8_image_inplace(output.size(), output.as_raw_bytes_mut());
247 }
248
249 // TODO: Step 9. If image is an img element or a Blob object, let val be the value
250 // of the colorSpaceConversion member of options, and then run these substeps:
251
252 // Step 10. Let val be the value of premultiplyAlpha member of options,
253 // and then run these substeps:
254 // TODO: Preserve the original input pixel format and perform conversion on demand.
255 match options.premultiplyAlpha {
256 PremultiplyAlpha::Default | PremultiplyAlpha::Premultiply => {
257 output.transform(
258 SnapshotAlphaMode::Transparent {
259 premultiplied: true,
260 },
261 SnapshotPixelFormat::BGRA,
262 );
263 },
264 PremultiplyAlpha::None => {
265 output.transform(
266 SnapshotAlphaMode::Transparent {
267 premultiplied: false,
268 },
269 SnapshotPixelFormat::BGRA,
270 );
271 },
272 }
273
274 // Step 11. Return output.
275 Some(output)
276 }
277
278 /// <https://html.spec.whatwg.org/multipage/#dom-createimagebitmap>
279 #[allow(clippy::too_many_arguments)]
280 pub(crate) fn create_image_bitmap(
281 global_scope: &GlobalScope,
282 image: ImageBitmapSource,
283 sx: i32,
284 sy: i32,
285 sw: Option<i32>,
286 sh: Option<i32>,
287 options: &ImageBitmapOptions,
288 can_gc: CanGc,
289 ) -> Rc<Promise> {
290 let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
291 let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
292
293 // Step 1. If either sw or sh is given and is 0, then return a promise rejected with a RangeError.
294 if sw.is_some_and(|w| w == 0) {
295 p.reject_error(
296 Error::Range("'sw' must be a non-zero value".to_owned()),
297 can_gc,
298 );
299 return p;
300 }
301
302 if sh.is_some_and(|h| h == 0) {
303 p.reject_error(
304 Error::Range("'sh' must be a non-zero value".to_owned()),
305 can_gc,
306 );
307 return p;
308 }
309
310 // Step 2. If either options's resizeWidth or options's resizeHeight is present and is 0,
311 // then return a promise rejected with an "InvalidStateError" DOMException.
312 if options.resizeWidth.is_some_and(|w| w == 0) {
313 p.reject_error(Error::InvalidState, can_gc);
314 return p;
315 }
316
317 if options.resizeHeight.is_some_and(|h| h == 0) {
318 p.reject_error(Error::InvalidState, can_gc);
319 return p;
320 }
321
322 // The promise with image bitmap should be fulfilled on the the bitmap task source.
323 let fullfill_promise_on_bitmap_task_source =
324 |promise: &Rc<Promise>, image_bitmap: &ImageBitmap| {
325 let trusted_promise = TrustedPromise::new(promise.clone());
326 let trusted_image_bitmap = Trusted::new(image_bitmap);
327
328 global_scope.task_manager().bitmap_task_source().queue(
329 task!(resolve_promise: move || {
330 let promise = trusted_promise.root();
331 let image_bitmap = trusted_image_bitmap.root();
332
333 promise.resolve_native(&image_bitmap, CanGc::note());
334 }),
335 );
336 };
337
338 // The promise with "InvalidStateError" DOMException should be rejected
339 // on the the bitmap task source.
340 let reject_promise_on_bitmap_task_source = |promise: &Rc<Promise>| {
341 let trusted_promise = TrustedPromise::new(promise.clone());
342
343 global_scope
344 .task_manager()
345 .bitmap_task_source()
346 .queue(task!(reject_promise: move || {
347 let promise = trusted_promise.root();
348
349 promise.reject_error(Error::InvalidState, CanGc::note());
350 }));
351 };
352
353 // Step 3. Check the usability of the image argument. If this throws an exception or returns bad,
354 // then return a promise rejected with an "InvalidStateError" DOMException.
355 // Step 6. Switch on image:
356 match image {
357 ImageBitmapSource::HTMLImageElement(ref image) => {
358 // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
359 if !image.is_usable().is_ok_and(|u| u) {
360 p.reject_error(Error::InvalidState, can_gc);
361 return p;
362 }
363
364 // If no ImageBitmap object can be constructed, then the promise
365 // is rejected instead.
366 let Some(snapshot) = image.get_raster_image_data() else {
367 p.reject_error(Error::InvalidState, can_gc);
368 return p;
369 };
370
371 // Step 6.3. Set imageBitmap's bitmap data to a copy of image's media data,
372 // cropped to the source rectangle with formatting.
373 let Some(bitmap_data) =
374 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
375 else {
376 p.reject_error(Error::InvalidState, can_gc);
377 return p;
378 };
379
380 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
381 // Step 6.4. If image is not origin-clean, then set the origin-clean flag
382 // of imageBitmap's bitmap to false.
383 image_bitmap.set_origin_clean(image.same_origin(GlobalScope::entry().origin()));
384
385 // Step 6.5. Queue a global task, using the bitmap task source,
386 // to resolve promise with imageBitmap.
387 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
388 },
389 ImageBitmapSource::HTMLVideoElement(ref video) => {
390 // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
391 if !video.is_usable() {
392 p.reject_error(Error::InvalidState, can_gc);
393 return p;
394 }
395
396 // Step 6.1. If image's networkState attribute is NETWORK_EMPTY, then return
397 // a promise rejected with an "InvalidStateError" DOMException.
398 if video.is_network_state_empty() {
399 p.reject_error(Error::InvalidState, can_gc);
400 return p;
401 }
402
403 // If no ImageBitmap object can be constructed, then the promise is rejected instead.
404 let Some(snapshot) = video.get_current_frame_data() else {
405 p.reject_error(Error::InvalidState, can_gc);
406 return p;
407 };
408
409 // Step 6.2. Set imageBitmap's bitmap data to a copy of the frame at the current
410 // playback position, at the media resource's natural width and natural height
411 // (i.e., after any aspect-ratio correction has been applied),
412 // cropped to the source rectangle with formatting.
413 let Some(bitmap_data) =
414 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
415 else {
416 p.reject_error(Error::InvalidState, can_gc);
417 return p;
418 };
419
420 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
421 // Step 6.3. If image is not origin-clean, then set the origin-clean flag
422 // of imageBitmap's bitmap to false.
423 image_bitmap.set_origin_clean(video.origin_is_clean());
424
425 // Step 6.4. Queue a global task, using the bitmap task source,
426 // to resolve promise with imageBitmap.
427 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
428 },
429 ImageBitmapSource::HTMLCanvasElement(ref canvas) => {
430 // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
431 if canvas.get_size().is_empty() {
432 p.reject_error(Error::InvalidState, can_gc);
433 return p;
434 }
435
436 // If no ImageBitmap object can be constructed, then the promise is rejected instead.
437 let Some(snapshot) = canvas.get_image_data() else {
438 p.reject_error(Error::InvalidState, can_gc);
439 return p;
440 };
441
442 // Step 6.1. Set imageBitmap's bitmap data to a copy of image's bitmap data,
443 // cropped to the source rectangle with formatting.
444 let Some(bitmap_data) =
445 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
446 else {
447 p.reject_error(Error::InvalidState, can_gc);
448 return p;
449 };
450
451 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
452 // Step 6.2. Set the origin-clean flag of the imageBitmap's bitmap to the same value
453 // as the origin-clean flag of image's bitmap.
454 image_bitmap.set_origin_clean(canvas.origin_is_clean());
455
456 // Step 6.3. Queue a global task, using the bitmap task source,
457 // to resolve promise with imageBitmap.
458 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
459 },
460 ImageBitmapSource::ImageBitmap(ref bitmap) => {
461 // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
462 if bitmap.is_detached() {
463 p.reject_error(Error::InvalidState, can_gc);
464 return p;
465 }
466
467 // If no ImageBitmap object can be constructed, then the promise is rejected instead.
468 let Some(snapshot) = bitmap.bitmap_data().clone() else {
469 p.reject_error(Error::InvalidState, can_gc);
470 return p;
471 };
472
473 // Step 6.1. Set imageBitmap's bitmap data to a copy of image's bitmap data,
474 // cropped to the source rectangle with formatting.
475 let Some(bitmap_data) =
476 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
477 else {
478 p.reject_error(Error::InvalidState, can_gc);
479 return p;
480 };
481
482 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
483 // Step 6.2. Set the origin-clean flag of imageBitmap's bitmap to the same value
484 // as the origin-clean flag of image's bitmap.
485 image_bitmap.set_origin_clean(bitmap.origin_is_clean());
486
487 // Step 6.3. Queue a global task, using the bitmap task source,
488 // to resolve promise with imageBitmap.
489 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
490 },
491 ImageBitmapSource::OffscreenCanvas(ref canvas) => {
492 // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
493 if canvas.get_size().is_empty() {
494 p.reject_error(Error::InvalidState, can_gc);
495 return p;
496 }
497
498 // If no ImageBitmap object can be constructed, then the promise is rejected instead.
499 let Some(snapshot) = canvas.get_image_data() else {
500 p.reject_error(Error::InvalidState, can_gc);
501 return p;
502 };
503
504 // Step 6.1. Set imageBitmap's bitmap data to a copy of image's bitmap data,
505 // cropped to the source rectangle with formatting.
506 let Some(bitmap_data) =
507 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
508 else {
509 p.reject_error(Error::InvalidState, can_gc);
510 return p;
511 };
512
513 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
514 // Step 6.2. Set the origin-clean flag of the imageBitmap's bitmap to the same value
515 // as the origin-clean flag of image's bitmap.
516 image_bitmap.set_origin_clean(canvas.origin_is_clean());
517
518 // Step 6.3. Queue a global task, using the bitmap task source,
519 // to resolve promise with imageBitmap.
520 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
521 },
522 ImageBitmapSource::Blob(ref blob) => {
523 // Step 6.1. Let imageData be the result of reading image's data.
524 // If an error occurs during reading of the object, then queue
525 // a global task, using the bitmap task source, to reject promise
526 // with an "InvalidStateError" DOMException and abort these steps.
527 let Ok(bytes) = blob.get_bytes() else {
528 reject_promise_on_bitmap_task_source(&p);
529 return p;
530 };
531
532 // Step 6.2. Apply the image sniffing rules to determine the file
533 // format of imageData, with MIME type of image (as given by
534 // image's type attribute) giving the official type.
535 // Step 6.3. If imageData is not in a supported image file format
536 // (e.g., it's not an image at all), or if imageData is corrupted
537 // in some fatal way such that the image dimensions cannot be obtained
538 // (e.g., a vector graphic with no natural size), then queue
539 // a global task, using the bitmap task source, to reject promise
540 // with an "InvalidStateError" DOMException and abort these steps.
541 let Some(img) = pixels::load_from_memory(&bytes, CorsStatus::Safe) else {
542 reject_promise_on_bitmap_task_source(&p);
543 return p;
544 };
545
546 let size = Size2D::new(img.metadata.width, img.metadata.height);
547 let format = match img.format {
548 PixelFormat::BGRA8 => SnapshotPixelFormat::BGRA,
549 PixelFormat::RGBA8 => SnapshotPixelFormat::RGBA,
550 pixel_format => {
551 unimplemented!("unsupported pixel format ({:?})", pixel_format)
552 },
553 };
554 let alpha_mode = SnapshotAlphaMode::Transparent {
555 premultiplied: false,
556 };
557
558 let snapshot = Snapshot::from_vec(
559 size.cast(),
560 format,
561 alpha_mode,
562 img.first_frame().bytes.to_vec(),
563 );
564
565 // Step 6.4. Set imageBitmap's bitmap data to imageData, cropped
566 // to the source rectangle with formatting.
567 let Some(bitmap_data) =
568 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
569 else {
570 reject_promise_on_bitmap_task_source(&p);
571 return p;
572 };
573
574 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
575
576 // Step 6.5. Queue a global task, using the bitmap task source,
577 // to resolve promise with imageBitmap.
578 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
579 },
580 ImageBitmapSource::ImageData(ref image_data) => {
581 // Step 6.1. Let buffer be image's data attribute value's [[ViewedArrayBuffer]] internal slot.
582 // Step 6.2. If IsDetachedBuffer(buffer) is true, then return a promise rejected
583 // with an "InvalidStateError" DOMException.
584 if image_data.is_detached() {
585 p.reject_error(Error::InvalidState, can_gc);
586 return p;
587 }
588
589 let alpha_mode = SnapshotAlphaMode::Transparent {
590 premultiplied: false,
591 };
592
593 let snapshot = Snapshot::from_vec(
594 image_data.get_size().cast(),
595 SnapshotPixelFormat::RGBA,
596 alpha_mode,
597 image_data.to_vec(),
598 );
599
600 // Step 6.3. Set imageBitmap's bitmap data to image's image data,
601 // cropped to the source rectangle with formatting.
602 let Some(bitmap_data) =
603 ImageBitmap::crop_and_transform_bitmap_data(snapshot, sx, sy, sw, sh, options)
604 else {
605 p.reject_error(Error::InvalidState, can_gc);
606 return p;
607 };
608
609 let image_bitmap = Self::new(global_scope, bitmap_data, can_gc);
610
611 // Step 6.4. Queue a global task, using the bitmap task source,
612 // to resolve promise with imageBitmap.
613 fullfill_promise_on_bitmap_task_source(&p, &image_bitmap);
614 },
615 ImageBitmapSource::CSSStyleValue(_) => {
616 // TODO: CSSStyleValue is not part of ImageBitmapSource
617 // <https://html.spec.whatwg.org/multipage/#imagebitmapsource>
618 p.reject_error(Error::NotSupported, can_gc);
619 },
620 }
621
622 // Step 7. Return promise.
623 p
624 }
625}
626
627impl Serializable for ImageBitmap {
628 type Index = ImageBitmapIndex;
629 type Data = SerializableImageBitmap;
630
631 /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:serialization-steps>
632 fn serialize(&self) -> Result<(ImageBitmapId, Self::Data), ()> {
633 // <https://html.spec.whatwg.org/multipage/#structuredserializeinternal>
634 // Step 19.1. If value has a [[Detached]] internal slot whose value is
635 // true, then throw a "DataCloneError" DOMException.
636 if self.is_detached() {
637 return Err(());
638 }
639
640 // Step 1. If value's origin-clean flag is not set, then throw a
641 // "DataCloneError" DOMException.
642 if !self.origin_is_clean() {
643 return Err(());
644 }
645
646 // Step 2. Set serialized.[[BitmapData]] to a copy of value's bitmap data.
647 let serialized = SerializableImageBitmap {
648 bitmap_data: self.bitmap_data.borrow().clone().unwrap(),
649 };
650
651 Ok((ImageBitmapId::new(), serialized))
652 }
653
654 /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:deserialization-steps>
655 fn deserialize(
656 owner: &GlobalScope,
657 serialized: Self::Data,
658 can_gc: CanGc,
659 ) -> Result<DomRoot<Self>, ()> {
660 // Step 1. Set value's bitmap data to serialized.[[BitmapData]].
661 Ok(ImageBitmap::new(owner, serialized.bitmap_data, can_gc))
662 }
663
664 fn serialized_storage<'a>(
665 data: StructuredData<'a, '_>,
666 ) -> &'a mut Option<HashMap<ImageBitmapId, Self::Data>> {
667 match data {
668 StructuredData::Reader(r) => &mut r.image_bitmaps,
669 StructuredData::Writer(w) => &mut w.image_bitmaps,
670 }
671 }
672}
673
674impl Transferable for ImageBitmap {
675 type Index = ImageBitmapIndex;
676 type Data = SerializableImageBitmap;
677
678 /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-steps>
679 fn transfer(&self) -> Fallible<(ImageBitmapId, SerializableImageBitmap)> {
680 // <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
681 // Step 5.2. If transferable has a [[Detached]] internal slot and
682 // transferable.[[Detached]] is true, then throw a "DataCloneError"
683 // DOMException.
684 if self.is_detached() {
685 return Err(Error::DataClone(None));
686 }
687
688 // Step 1. If value's origin-clean flag is not set, then throw a
689 // "DataCloneError" DOMException.
690 if !self.origin_is_clean() {
691 return Err(Error::DataClone(None));
692 }
693
694 // Step 2. Set dataHolder.[[BitmapData]] to value's bitmap data.
695 // Step 3. Unset value's bitmap data.
696 let transferred = SerializableImageBitmap {
697 bitmap_data: self.bitmap_data.borrow_mut().take().unwrap(),
698 };
699
700 Ok((ImageBitmapId::new(), transferred))
701 }
702
703 /// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-receiving-steps>
704 fn transfer_receive(
705 owner: &GlobalScope,
706 _: ImageBitmapId,
707 transferred: SerializableImageBitmap,
708 ) -> Result<DomRoot<Self>, ()> {
709 // Step 1. Set value's bitmap data to serialized.[[BitmapData]].
710 Ok(ImageBitmap::new(
711 owner,
712 transferred.bitmap_data,
713 CanGc::note(),
714 ))
715 }
716
717 fn serialized_storage<'a>(
718 data: StructuredData<'a, '_>,
719 ) -> &'a mut Option<HashMap<ImageBitmapId, Self::Data>> {
720 match data {
721 StructuredData::Reader(r) => &mut r.transferred_image_bitmaps,
722 StructuredData::Writer(w) => &mut w.transferred_image_bitmaps,
723 }
724 }
725}
726
727impl ImageBitmapMethods<crate::DomTypeHolder> for ImageBitmap {
728 /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmap-height>
729 fn Height(&self) -> u32 {
730 // Step 1. If this's [[Detached]] internal slot's value is true, then return 0.
731 if self.is_detached() {
732 return 0;
733 }
734
735 // Step 2. Return this's height, in CSS pixels.
736 self.bitmap_data
737 .borrow()
738 .as_ref()
739 .unwrap()
740 .size()
741 .cast()
742 .height
743 }
744
745 /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmap-width>
746 fn Width(&self) -> u32 {
747 // Step 1. If this's [[Detached]] internal slot's value is true, then return 0.
748 if self.is_detached() {
749 return 0;
750 }
751
752 // Step 2. Return this's width, in CSS pixels.
753 self.bitmap_data
754 .borrow()
755 .as_ref()
756 .unwrap()
757 .size()
758 .cast()
759 .width
760 }
761
762 /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmap-close>
763 fn Close(&self) {
764 // Step 1. Set this's [[Detached]] internal slot value to true.
765 // Step 2. Unset this's bitmap data.
766 // NOTE: The existence of the bitmap data is the internal slot in our implementation
767 self.bitmap_data.borrow_mut().take();
768 }
769}