Skip to main content

script/dom/canvas/
offscreencanvas.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;
6
7use dom_struct::dom_struct;
8use euclid::default::Size2D;
9use js::error::throw_type_error;
10use js::realm::CurrentRealm;
11use js::rust::{HandleObject, HandleValue};
12use pixels::{EncodedImageType, Snapshot};
13use rustc_hash::FxHashMap;
14use script_bindings::cell::{DomRefCell, Ref};
15#[cfg(feature = "webgl")]
16use script_bindings::inheritance::Castable;
17#[cfg(feature = "webgl")]
18use script_bindings::reflector::DomObject;
19use script_bindings::reflector::reflect_dom_object_with_proto;
20use script_bindings::weakref::WeakRef;
21use servo_base::id::{OffscreenCanvasId, OffscreenCanvasIndex};
22#[cfg(feature = "webgl")]
23use servo_canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
24use servo_constellation_traits::{BlobImpl, TransferableOffscreenCanvas};
25
26use crate::canvas_context::{CanvasContext, OffscreenRenderingContext};
27#[cfg(feature = "webgl")]
28use crate::conversions::Convert;
29use crate::dom::RootedPromise;
30use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DSettings;
31use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{
32    ImageEncodeOptions, OffscreenCanvasMethods,
33    OffscreenRenderingContext as RootedOffscreenRenderingContext, OffscreenRenderingContextId,
34};
35#[cfg(feature = "webgl")]
36use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
37use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas;
38use crate::dom::bindings::conversions::ConversionResult;
39use crate::dom::bindings::error::{Error, Fallible};
40use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
41use crate::dom::bindings::reflector::DomGlobal;
42use crate::dom::bindings::root::{Dom, DomRoot};
43use crate::dom::bindings::structuredclone::StructuredData;
44use crate::dom::bindings::transferable::Transferable;
45use crate::dom::blob::Blob;
46use crate::dom::eventtarget::EventTarget;
47use crate::dom::globalscope::GlobalScope;
48use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
49use crate::dom::imagebitmap::ImageBitmap;
50use crate::dom::imagebitmaprenderingcontext::ImageBitmapRenderingContext;
51use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D;
52use crate::dom::promise::Promise;
53#[cfg(feature = "webgl")]
54use crate::dom::types::{WebGLRenderingContext, Window};
55#[cfg(feature = "webgl")]
56use crate::dom::webgl::webgl2renderingcontext::WebGL2RenderingContext;
57
58/// <https://html.spec.whatwg.org/multipage/#offscreencanvas>
59#[dom_struct]
60pub(crate) struct OffscreenCanvas {
61    eventtarget: EventTarget,
62    width: Cell<u64>,
63    height: Cell<u64>,
64
65    /// Represents both the [bitmap] and the [context mode] of the canvas.
66    ///
67    /// [bitmap]: https://html.spec.whatwg.org/multipage/#offscreencanvas-bitmap
68    /// [context mode]: https://html.spec.whatwg.org/multipage/#offscreencanvas-context-mode
69    context: DomRefCell<Option<OffscreenRenderingContext>>,
70
71    /// <https://html.spec.whatwg.org/multipage/#offscreencanvas-placeholder>
72    placeholder: Option<WeakRef<HTMLCanvasElement>>,
73}
74
75impl OffscreenCanvas {
76    pub(crate) fn new_inherited(
77        width: u64,
78        height: u64,
79        placeholder: Option<WeakRef<HTMLCanvasElement>>,
80    ) -> OffscreenCanvas {
81        OffscreenCanvas {
82            eventtarget: EventTarget::new_inherited(),
83            width: Cell::new(width),
84            height: Cell::new(height),
85            context: DomRefCell::new(None),
86            placeholder,
87        }
88    }
89
90    pub(crate) fn new(
91        cx: &mut js::context::JSContext,
92        global: &GlobalScope,
93        proto: Option<HandleObject>,
94        width: u64,
95        height: u64,
96        placeholder: Option<WeakRef<HTMLCanvasElement>>,
97    ) -> DomRoot<OffscreenCanvas> {
98        reflect_dom_object_with_proto(
99            cx,
100            Box::new(OffscreenCanvas::new_inherited(width, height, placeholder)),
101            global,
102            proto,
103        )
104    }
105
106    pub(crate) fn get_size(&self) -> Size2D<u32> {
107        Size2D::new(
108            self.Width().try_into().unwrap_or(u32::MAX),
109            self.Height().try_into().unwrap_or(u32::MAX),
110        )
111    }
112
113    #[cfg(feature = "webgl")]
114    fn get_gl_attributes(
115        cx: &mut js::context::JSContext,
116        options: HandleValue,
117    ) -> Option<GLContextAttributes> {
118        match WebGLContextAttributes::new(cx, options) {
119            Ok(ConversionResult::Success(attrs)) => Some(attrs.convert()),
120            Ok(ConversionResult::Failure(error)) => {
121                throw_type_error(cx, &error);
122                None
123            },
124            _ => {
125                debug!("Unexpected error on conversion of WebGLContextAttributes");
126                None
127            },
128        }
129    }
130
131    pub(crate) fn origin_is_clean(&self) -> bool {
132        match *self.context.borrow() {
133            Some(ref context) => context.origin_is_clean(),
134            _ => true,
135        }
136    }
137
138    pub(crate) fn context(&self) -> Option<Ref<'_, OffscreenRenderingContext>> {
139        Ref::filter_map(self.context.borrow(), |ctx| ctx.as_ref()).ok()
140    }
141
142    pub(crate) fn get_image_data(&self) -> Option<Snapshot> {
143        match self.context.borrow().as_ref() {
144            Some(context) => context.get_image_data(),
145            None => {
146                let size = self.get_size();
147                if size.is_empty() ||
148                    pixels::compute_rgba8_byte_length_if_within_limit(
149                        size.width as usize,
150                        size.height as usize,
151                    )
152                    .is_none()
153                {
154                    None
155                } else {
156                    Some(Snapshot::cleared(size))
157                }
158            },
159        }
160    }
161
162    /// <https://html.spec.whatwg.org/multipage/#offscreen-2d-context-creation-algorithm>
163    pub(crate) fn get_or_init_2d_context(
164        &self,
165        cx: &mut js::context::JSContext,
166        options: HandleValue,
167    ) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> {
168        if let Some(ctx) = self.context() {
169            return match *ctx {
170                OffscreenRenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
171                _ => None,
172            };
173        }
174
175        // Step 2. Let settings be the result of converting arg to the dictionary type
176        // CanvasRenderingContext2DSettings. (This can throw an exception.)
177        let settings = match CanvasRenderingContext2DSettings::new(cx, options) {
178            Ok(ConversionResult::Success(settings)) => settings,
179            Ok(ConversionResult::Failure(error)) => {
180                throw_type_error(cx, &error);
181                return None;
182            },
183            Err(()) => return None,
184        };
185        let context = OffscreenCanvasRenderingContext2D::new(
186            cx,
187            &self.global(),
188            self,
189            self.get_size(),
190            &settings,
191        )?;
192        *self.context.safe_borrow_mut(cx.no_gc()) = Some(OffscreenRenderingContext::Context2d(
193            Dom::from_ref(&*context),
194        ));
195        Some(context)
196    }
197
198    /// <https://html.spec.whatwg.org/multipage/#offscreen-context-type-bitmaprenderer>
199    pub(crate) fn get_or_init_bitmaprenderer_context(
200        &self,
201        cx: &mut js::context::JSContext,
202    ) -> Option<DomRoot<ImageBitmapRenderingContext>> {
203        // Return the same object as was returned the last time the method was
204        // invoked with this same first argument.
205        if let Some(ctx) = self.context() {
206            return match *ctx {
207                OffscreenRenderingContext::BitmapRenderer(ref ctx) => Some(DomRoot::from_ref(ctx)),
208                _ => None,
209            };
210        }
211
212        // Step 1. Let context be the result of running the
213        // ImageBitmapRenderingContext creation algorithm given this and
214        // options.
215        let canvas =
216            RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(self));
217
218        let context = ImageBitmapRenderingContext::new(cx, &self.global(), &canvas);
219
220        // Step 2. Set this's context mode to bitmaprenderer.
221        *self.context.safe_borrow_mut(cx.no_gc()) = Some(
222            OffscreenRenderingContext::BitmapRenderer(Dom::from_ref(&*context)),
223        );
224
225        // Step 3. Return context.
226        Some(context)
227    }
228
229    #[cfg(feature = "webgl")]
230    // <https://html.spec.whatwg.org/multipage/#offscreen-context-type-webgl>
231    pub(crate) fn get_or_init_webgl_context(
232        &self,
233        cx: &mut js::context::JSContext,
234        options: HandleValue,
235    ) -> Option<DomRoot<WebGLRenderingContext>> {
236        if let Some(ctx) = self.context() {
237            return match *ctx {
238                OffscreenRenderingContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)),
239                _ => None,
240            };
241        }
242
243        // 1. Let context be the result of following the instructions given in the
244        // WebGL specifications' Context Creation sections.
245        let canvas =
246            RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(self));
247        let size = self.get_size();
248        let attrs = Self::get_gl_attributes(cx, options)?;
249        self.global()
250            .downcast::<Window>()
251            .and_then(|window| {
252                WebGLRenderingContext::new(cx, window, &canvas, WebGLVersion::WebGL1, size, attrs)
253            })
254            .map(|context| {
255                // Step 2. If context is null, then return null;
256                // otherwise set this's context mode to webgl or webgl2.
257                *self.context.safe_borrow_mut(cx.no_gc()) =
258                    Some(OffscreenRenderingContext::WebGL(Dom::from_ref(&*context)));
259
260                // Step 3. Return context.
261                context
262            })
263    }
264
265    #[cfg(feature = "webgl")]
266    // <https://html.spec.whatwg.org/multipage/#offscreen-context-type-webgl>
267    fn get_or_init_webgl2_context(
268        &self,
269        cx: &mut js::context::JSContext,
270        options: HandleValue,
271    ) -> Option<DomRoot<WebGL2RenderingContext>> {
272        if !WebGL2RenderingContext::is_webgl2_enabled(cx, self.global().reflector().get_jsobject())
273        {
274            return None;
275        }
276        if let Some(ctx) = self.context() {
277            return match *ctx {
278                OffscreenRenderingContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)),
279                _ => None,
280            };
281        }
282
283        // 1. Let context be the result of following the instructions given in the
284        // WebGL specifications' Context Creation sections.
285        let canvas =
286            RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(self));
287        let size = self.get_size();
288        let attrs = Self::get_gl_attributes(cx, options)?;
289        self.global()
290            .downcast::<Window>()
291            .and_then(|window| WebGL2RenderingContext::new(cx, window, &canvas, size, attrs))
292            .map(|context| {
293                // Step 2. If context is null, then return null;
294                // otherwise set this's context mode to webgl or webgl2.
295                *self.context.safe_borrow_mut(cx.no_gc()) =
296                    Some(OffscreenRenderingContext::WebGL2(Dom::from_ref(&*context)));
297
298                // Step 3. Return context.
299                context
300            })
301    }
302
303    pub(crate) fn placeholder(&self) -> Option<DomRoot<HTMLCanvasElement>> {
304        self.placeholder
305            .as_ref()
306            .and_then(|placeholder| placeholder.root())
307    }
308}
309
310impl Transferable for OffscreenCanvas {
311    type Index = OffscreenCanvasIndex;
312    type Data = TransferableOffscreenCanvas;
313
314    /// <https://html.spec.whatwg.org/multipage/#the-offscreencanvas-interface:transfer-steps>
315    fn transfer(
316        &self,
317        cx: &mut js::context::JSContext,
318    ) -> Fallible<(OffscreenCanvasId, TransferableOffscreenCanvas)> {
319        // <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
320        // Step 5.2. If transferable has a [[Detached]] internal slot and
321        // transferable.[[Detached]] is true, then throw a "DataCloneError"
322        // DOMException.
323        if let Some(OffscreenRenderingContext::Detached) = *self.context.borrow() {
324            return Err(Error::DataClone(None));
325        }
326
327        // Step 1. If value's context mode is not equal to none, then throw an
328        // "InvalidStateError" DOMException.
329        if !self.context.borrow().is_none() {
330            return Err(Error::InvalidState(None));
331        }
332
333        // TODO(#37882): Allow to transfer with a placeholder canvas element.
334        if self.placeholder.is_some() {
335            return Err(Error::InvalidState(None));
336        }
337
338        // Step 2. Set value's context mode to detached.
339        *self.context.safe_borrow_mut(cx.no_gc()) = Some(OffscreenRenderingContext::Detached);
340
341        // Step 3. Let width and height be the dimensions of value's bitmap.
342        // Step 5. Unset value's bitmap.
343        let width = self.width.replace(0);
344        let height = self.height.replace(0);
345
346        // TODO(#37918) Step 4. Let language and direction be the values of
347        // value's inherited language and inherited direction.
348
349        // Step 6. Set dataHolder.[[Width]] to width and dataHolder.[[Height]]
350        // to height.
351
352        // TODO(#37918) Step 7. Set dataHolder.[[Language]] to language and
353        // dataHolder.[[Direction]] to direction.
354
355        // TODO(#37882) Step 8. Set dataHolder.[[PlaceholderCanvas]] to be a
356        // weak reference to value's placeholder canvas element, if value has
357        // one, or null if it does not.
358        let transferred = TransferableOffscreenCanvas { width, height };
359
360        Ok((OffscreenCanvasId::new(), transferred))
361    }
362
363    /// <https://html.spec.whatwg.org/multipage/#the-offscreencanvas-interface:transfer-receiving-steps>
364    fn transfer_receive(
365        cx: &mut js::context::JSContext,
366        owner: &GlobalScope,
367        _: OffscreenCanvasId,
368        transferred: TransferableOffscreenCanvas,
369    ) -> Result<DomRoot<Self>, ()> {
370        // Step 1. Initialize value's bitmap to a rectangular array of
371        // transparent black pixels with width given by dataHolder.[[Width]] and
372        // height given by dataHolder.[[Height]].
373
374        // TODO(#37918) Step 2. Set value's inherited language to
375        // dataHolder.[[Language]] and its inherited direction to
376        // dataHolder.[[Direction]].
377
378        // TODO(#37882) Step 3. If dataHolder.[[PlaceholderCanvas]] is not null,
379        // set value's placeholder canvas element to
380        // dataHolder.[[PlaceholderCanvas]] (while maintaining the weak
381        // reference semantics).
382        Ok(OffscreenCanvas::new(
383            cx,
384            owner,
385            None,
386            transferred.width,
387            transferred.height,
388            None,
389        ))
390    }
391
392    fn serialized_storage<'a>(
393        data: StructuredData<'a, '_>,
394    ) -> &'a mut Option<FxHashMap<OffscreenCanvasId, Self::Data>> {
395        match data {
396            StructuredData::Reader(r) => &mut r.offscreen_canvases,
397            StructuredData::Writer(w) => &mut w.offscreen_canvases,
398        }
399    }
400}
401
402impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
403    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas>
404    fn Constructor(
405        cx: &mut js::context::JSContext,
406        global: &GlobalScope,
407        proto: Option<HandleObject>,
408        width: u64,
409        height: u64,
410    ) -> Fallible<DomRoot<OffscreenCanvas>> {
411        Ok(OffscreenCanvas::new(cx, global, proto, width, height, None))
412    }
413
414    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-getcontext>
415    fn GetContext(
416        &self,
417        cx: &mut js::context::JSContext,
418        id: OffscreenRenderingContextId,
419        options: HandleValue,
420    ) -> Fallible<Option<RootedOffscreenRenderingContext>> {
421        // Step 1. If options is not an object, then set options to null.
422        let options = if options.get().is_object() {
423            options
424        } else {
425            HandleValue::null()
426        };
427
428        // Step 3. Throw an "InvalidStateError" DOMException if the
429        // OffscreenCanvas object's context mode is detached.
430        if let Some(OffscreenRenderingContext::Detached) = *self.context.borrow() {
431            return Err(Error::InvalidState(None));
432        }
433
434        match id {
435            OffscreenRenderingContextId::_2d => Ok(self
436                .get_or_init_2d_context(cx, options)
437                .map(RootedOffscreenRenderingContext::OffscreenCanvasRenderingContext2D)),
438            OffscreenRenderingContextId::Bitmaprenderer => Ok(self
439                .get_or_init_bitmaprenderer_context(cx)
440                .map(RootedOffscreenRenderingContext::ImageBitmapRenderingContext)),
441            #[cfg(feature = "webgl")]
442            OffscreenRenderingContextId::Webgl => Ok(self
443                .get_or_init_webgl_context(cx, options)
444                .map(RootedOffscreenRenderingContext::WebGLRenderingContext)),
445            #[cfg(feature = "webgl")]
446            OffscreenRenderingContextId::Experimental_webgl => Ok(self
447                .get_or_init_webgl_context(cx, options)
448                .map(RootedOffscreenRenderingContext::WebGLRenderingContext)),
449            #[cfg(feature = "webgl")]
450            OffscreenRenderingContextId::Webgl2 => Ok(self
451                .get_or_init_webgl2_context(cx, options)
452                .map(RootedOffscreenRenderingContext::WebGL2RenderingContext)),
453            #[cfg(feature = "webgl")]
454            OffscreenRenderingContextId::Experimental_webgl2 => Ok(self
455                .get_or_init_webgl2_context(cx, options)
456                .map(RootedOffscreenRenderingContext::WebGL2RenderingContext)),
457        }
458    }
459
460    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width>
461    fn Width(&self) -> u64 {
462        self.width.get()
463    }
464
465    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width>
466    fn SetWidth(&self, cx: &mut js::context::JSContext, value: u64) {
467        self.width.set(value);
468
469        if let Some(canvas_context) = self.context() {
470            canvas_context.resize();
471        }
472
473        if let Some(canvas) = self.placeholder() {
474            canvas.set_natural_width(cx, value as _)
475        }
476    }
477
478    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height>
479    fn Height(&self) -> u64 {
480        self.height.get()
481    }
482
483    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height>
484    fn SetHeight(&self, cx: &mut js::context::JSContext, value: u64) {
485        self.height.set(value);
486
487        if let Some(canvas_context) = self.context() {
488            canvas_context.resize();
489        }
490
491        if let Some(canvas) = self.placeholder() {
492            canvas.set_natural_height(cx, value as _)
493        }
494    }
495
496    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-transfertoimagebitmap>
497    fn TransferToImageBitmap(
498        &self,
499        cx: &mut js::context::JSContext,
500    ) -> Fallible<DomRoot<ImageBitmap>> {
501        // Step 1. If the value of this OffscreenCanvas object's [[Detached]]
502        // internal slot is set to true, then throw an "InvalidStateError"
503        // DOMException.
504        if let Some(OffscreenRenderingContext::Detached) = *self.context.borrow() {
505            return Err(Error::InvalidState(None));
506        }
507
508        // Step 2. If this OffscreenCanvas object's context mode is set to none,
509        // then throw an "InvalidStateError" DOMException.
510        if self.context.borrow().is_none() {
511            return Err(Error::InvalidState(None));
512        }
513
514        // Step 3. Let image be a newly created ImageBitmap object that
515        // references the same underlying bitmap data as this OffscreenCanvas
516        // object's bitmap.
517        let Some(snapshot) = self.get_image_data() else {
518            return Err(Error::InvalidState(None));
519        };
520
521        let image_bitmap = ImageBitmap::new(cx, &self.global(), snapshot);
522        image_bitmap.set_origin_clean(self.origin_is_clean());
523
524        // Step 4. Set this OffscreenCanvas object's bitmap to reference a newly
525        // created bitmap of the same dimensions and color space as the previous
526        // bitmap, and with its pixels initialized to transparent black, or
527        // opaque black if the rendering context's alpha is false.
528        if let Some(canvas_context) = self.context() {
529            canvas_context.reset_bitmap();
530        }
531
532        // Step 5. Return image.
533        Ok(image_bitmap)
534    }
535
536    /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-converttoblob>
537    fn ConvertToBlob(
538        &self,
539        cx: &mut js::context::JSContext,
540        options: &ImageEncodeOptions,
541    ) -> RootedPromise {
542        // Step 5. Let result be a new promise object.
543        let mut realm = CurrentRealm::assert(cx);
544        let promise = Promise::new_in_realm_rooted(&mut realm);
545
546        // Step 1. If the value of this's [[Detached]] internal slot is true,
547        // then return a promise rejected with an "InvalidStateError"
548        // DOMException.
549        if let Some(OffscreenRenderingContext::Detached) = *self.context.borrow() {
550            promise.reject_error(cx, Error::InvalidState(None));
551            return promise;
552        }
553
554        // Step 2. If this's context mode is 2d and the rendering context's
555        // output bitmap's origin-clean flag is set to false, then return a
556        // promise rejected with a "SecurityError" DOMException.
557        if !self.origin_is_clean() {
558            promise.reject_error(cx, Error::Security(None));
559            return promise;
560        }
561
562        // Step 3. If this's bitmap has no pixels (i.e., either its horizontal
563        // dimension or its vertical dimension is zero), then return a promise
564        // rejected with an "IndexSizeError" DOMException.
565        if self.Width() == 0 || self.Height() == 0 {
566            promise.reject_error(cx, Error::IndexSize(None));
567            return promise;
568        }
569
570        // Step 4. Let bitmap be a copy of this's bitmap.
571        let Some(mut snapshot) = self.get_image_data() else {
572            promise.reject_error(cx, Error::InvalidState(None));
573            return promise;
574        };
575
576        // Step 7. Run these steps in parallel:
577        // Step 7.1. Let file be a serialization of bitmap as a file, with
578        // options's type and quality if present.
579        // Step 7.2. Queue a global task on the canvas blob serialization task
580        // source given global to run these steps:
581        let trusted_this = Trusted::new(self);
582        let trusted_promise = TrustedPromise::from(&promise);
583
584        let image_type = EncodedImageType::from(&options.type_.str() as &str);
585        let quality = options.quality;
586
587        self.global()
588            .task_manager()
589            .canvas_blob_task_source()
590            .queue(task!(convert_to_blob: move |cx| {
591                let this = trusted_this.root();
592                let promise = trusted_promise.root(cx);
593
594                let mut encoded: Vec<u8> = vec![];
595
596                if snapshot.encode_for_mime_type(&image_type, quality, &mut encoded).is_err() {
597                    // Step 7.2.1. If file is null, then reject result with an
598                    // "EncodingError" DOMException.
599                    promise.reject_error(cx, Error::Encoding(None));
600                    return;
601                };
602
603                // Step 7.2.2. Otherwise, resolve result with a new Blob object,
604                // created in global's relevant realm, representing file.
605                let blob_impl = BlobImpl::new_from_bytes(encoded, image_type.as_mime_type().to_owned());
606                let blob = Blob::new(cx, &this.global(), blob_impl);
607
608                promise.resolve_native(cx, &blob);
609            }));
610
611        // Step 8. Return result.
612        promise
613    }
614}