7de346707ac84008cbc68b9b1b428769ed2d70dd
[WebKit-https.git] / Source / WebCore / html / ImageBitmap.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ImageBitmap.h"
28
29 #include "BitmapImage.h"
30 #include "Blob.h"
31 #include "CachedImage.h"
32 #include "ExceptionOr.h"
33 #include "FileReaderLoader.h"
34 #include "FileReaderLoaderClient.h"
35 #include "GraphicsContext.h"
36 #include "HTMLCanvasElement.h"
37 #include "HTMLImageElement.h"
38 #include "HTMLVideoElement.h"
39 #include "ImageBitmapOptions.h"
40 #include "ImageBuffer.h"
41 #include "ImageData.h"
42 #include "IntRect.h"
43 #include "JSImageBitmap.h"
44 #include "LayoutSize.h"
45 #include "RenderElement.h"
46 #include "SharedBuffer.h"
47 #include <wtf/StdLibExtras.h>
48
49 namespace WebCore {
50
51 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
52 static RenderingMode bufferRenderingMode = Accelerated;
53 #else
54 static RenderingMode bufferRenderingMode = Unaccelerated;
55 #endif
56
57 Ref<ImageBitmap> ImageBitmap::create(IntSize size)
58 {
59     return create(ImageBuffer::create(FloatSize(size.width(), size.height()), bufferRenderingMode));
60 }
61
62 Ref<ImageBitmap> ImageBitmap::create(std::pair<std::unique_ptr<ImageBuffer>, bool>&& buffer)
63 {
64     auto imageBitmap = create(WTFMove(buffer.first));
65     imageBitmap->m_originClean = buffer.second;
66     return imageBitmap;
67 }
68
69 Ref<ImageBitmap> ImageBitmap::create(std::unique_ptr<ImageBuffer>&& buffer)
70 {
71     return adoptRef(*new ImageBitmap(WTFMove(buffer)));
72 }
73
74 void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, ImageBitmap::Source&& source, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise)
75 {
76     WTF::switchOn(source,
77         [&] (auto& specificSource) {
78             createPromise(scriptExecutionContext, specificSource, WTFMove(options), std::nullopt, WTFMove(promise));
79         }
80     );
81 }
82
83 Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>> ImageBitmap::detachBitmaps(Vector<RefPtr<ImageBitmap>>&& bitmaps)
84 {
85     Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>> buffers;
86     for (auto& bitmap : bitmaps)
87         buffers.append(std::make_pair(bitmap->transferOwnershipAndClose(), bitmap->originClean()));
88     return buffers;
89 }
90
91
92 void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, ImageBitmap::Source&& source, ImageBitmapOptions&& options, int sx, int sy, int sw, int sh, ImageBitmap::Promise&& promise)
93 {
94     // 1. If either the sw or sh arguments are specified but zero, return a promise
95     //    rejected with an "RangeError" DOMException and abort these steps.
96     if (!sw || !sh) {
97         promise.reject(RangeError, "Cannot create ImageBitmap with a width or height of 0");
98         return;
99     }
100
101     auto left = sw >= 0 ? sx : sx + sw;
102     auto top = sh >= 0 ? sy : sy + sh;
103     auto width = std::abs(sw);
104     auto height = std::abs(sh);
105
106     WTF::switchOn(source,
107         [&] (auto& specificSource) {
108             createPromise(scriptExecutionContext, specificSource, WTFMove(options), IntRect { left, top, width, height }, WTFMove(promise));
109         }
110     );
111 }
112
113 static bool taintsOrigin(CachedImage& cachedImage)
114 {
115     auto* image = cachedImage.image();
116     if (!image)
117         return false;
118
119     if (image->sourceURL().protocolIsData())
120         return false;
121
122     if (!image->hasSingleSecurityOrigin())
123         return true;
124
125     if (!cachedImage.isCORSSameOrigin())
126         return true;
127
128     return false;
129 }
130
131 #if ENABLE(VIDEO)
132 static bool taintsOrigin(SecurityOrigin* origin, HTMLVideoElement& video)
133 {
134     if (!video.hasSingleSecurityOrigin())
135         return true;
136
137     if (video.player()->didPassCORSAccessCheck())
138         return false;
139
140     auto url = video.currentSrc();
141     if (url.protocolIsData())
142         return false;
143
144     return !origin->canRequest(url);
145 }
146 #endif
147
148 // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
149 static ExceptionOr<IntRect> croppedSourceRectangleWithFormatting(IntSize inputSize, ImageBitmapOptions& options, std::optional<IntRect> rect)
150 {
151     // 2. If either or both of resizeWidth and resizeHeight members of options are less
152     //    than or equal to 0, then return a promise rejected with "InvalidStateError"
153     //    DOMException and abort these steps.
154     if ((options.resizeWidth && options.resizeWidth.value() <= 0) || (options.resizeHeight && options.resizeHeight.value() <= 0))
155         return Exception { InvalidStateError, "Invalid resize dimensions" };
156
157     // 3. If sx, sy, sw and sh are specified, let sourceRectangle be a rectangle whose
158     //    corners are the four points (sx, sy), (sx+sw, sy),(sx+sw, sy+sh), (sx,sy+sh).
159     //    Otherwise let sourceRectangle be a rectangle whose corners are the four points
160     //    (0,0), (width of input, 0), (width of input, height of input), (0, height of
161     //    input).
162     auto sourceRectangle = rect.value_or(IntRect { 0, 0, inputSize.width(), inputSize.height() });
163
164     // 4. Clip sourceRectangle to the dimensions of input.
165     sourceRectangle.intersect(IntRect { 0, 0, inputSize.width(), inputSize.height() });
166
167     return { WTFMove(sourceRectangle) };
168 }
169
170 static IntSize outputSizeForSourceRectangle(IntRect sourceRectangle, ImageBitmapOptions& options)
171 {
172     // 5. Let outputWidth be determined as follows:
173     auto outputWidth = [&] () -> int {
174         if (options.resizeWidth)
175             return options.resizeWidth.value();
176         if (options.resizeHeight)
177             return ceil(sourceRectangle.width() * static_cast<double>(options.resizeHeight.value()) / sourceRectangle.height());
178         return sourceRectangle.width();
179     }();
180
181     // 6. Let outputHeight be determined as follows:
182     auto outputHeight = [&] () -> int {
183         if (options.resizeHeight)
184             return options.resizeHeight.value();
185         if (options.resizeWidth)
186             return ceil(sourceRectangle.height() * static_cast<double>(options.resizeWidth.value()) / sourceRectangle.width());
187         return sourceRectangle.height();
188     }();
189
190     return { outputWidth, outputHeight };
191 }
192
193 static InterpolationQuality interpolationQualityForResizeQuality(ImageBitmapOptions::ResizeQuality resizeQuality)
194 {
195     switch (resizeQuality) {
196     case ImageBitmapOptions::ResizeQuality::Pixelated:
197         return InterpolationNone;
198     case ImageBitmapOptions::ResizeQuality::Low:
199         return InterpolationDefault; // Low is the default.
200     case ImageBitmapOptions::ResizeQuality::Medium:
201         return InterpolationMedium;
202     case ImageBitmapOptions::ResizeQuality::High:
203         return InterpolationHigh;
204     }
205     ASSERT_NOT_REACHED();
206     return InterpolationDefault;
207 }
208
209 // FIXME: More steps from https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
210
211 // 7. Place input on an infinite transparent black grid plane, positioned so that its
212 //    top left corner is at the origin of the plane, with the x-coordinate increasing
213 //    to the right, and the y-coordinate increasing down, and with each pixel in the
214 //    input image data occupying a cell on the plane's grid.
215
216 // 8. Let output be the rectangle on the plane denoted by sourceRectangle.
217
218 // 9. Scale output to the size specified by outputWidth and outputHeight. The user
219 //    agent should use the value of the resizeQuality option to guide the choice of
220 //    scaling algorithm.
221
222 // 10. If the value of the imageOrientation member of options is "flipY", output must
223 //     be flipped vertically, disregarding any image orientation metadata of the source
224 //     (such as EXIF metadata), if any.
225
226 // 11. If image is an img element or a Blob object, let val be the value of the
227 //     colorSpaceConversion member of options, and then run these substeps:
228 //
229 //     1. If val is "default", the color space conversion behavior is implementation-specific,
230 //        and should be chosen according to the color space that the implementation uses for
231 //        drawing images onto the canvas.
232 //
233 //     2. If val is "none", output must be decoded without performing any color space
234 //        conversions. This means that the image decoding algorithm must ignore color profile
235 //        metadata embedded in the source data as well as the display device color profile.
236
237 // 12. Let val be the value of premultiplyAlpha member of options, and then run these substeps:
238 //
239 //     1. If val is "default", the alpha premultiplication behavior is implementation-specific,
240 //        and should be chosen according to implementation deems optimal for drawing images
241 //        onto the canvas.
242 //
243 //     2. If val is "premultiply", the output that is not premultiplied by alpha must have its
244 //        color components multiplied by alpha and that is premultiplied by alpha must be left
245 //        untouched.
246 //
247 //     3. If val is "none", the output that is not premultiplied by alpha must be left untouched
248 //        and that is premultiplied by alpha must have its color components divided by alpha.
249
250 // 13. Return output.
251
252 void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<HTMLImageElement>& imageElement, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
253 {
254     // 2. If image is not completely available, then return a promise rejected with
255     // an "InvalidStateError" DOMException and abort these steps.
256
257     auto* cachedImage = imageElement->cachedImage();
258     if (!cachedImage || !imageElement->complete()) {
259         promise.reject(InvalidStateError, "Cannot create ImageBitmap that is not completely available");
260         return;
261     }
262
263     // 3. If image's media data has no intrinsic dimensions (e.g. it's a vector graphic
264     //    with no specified content size), and both or either of the resizeWidth and
265     //    resizeHeight options are not specified, then return a promise rejected with
266     //    an "InvalidStateError" DOMException and abort these steps.
267
268     auto imageSize = cachedImage->imageSizeForRenderer(imageElement->renderer(), 1.0f);
269     if ((!imageSize.width() || !imageSize.height()) && (!options.resizeWidth || !options.resizeHeight)) {
270         promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing resize dimensions");
271         return;
272     }
273
274     // 4. If image's media data has no intrinsic dimensions (e.g. it's a vector graphics
275     //    with no specified content size), it should be rendered to a bitmap of the size
276     //    specified by the resizeWidth and the resizeHeight options.
277
278     if (!imageSize.width() && !imageSize.height()) {
279         imageSize.setWidth(options.resizeWidth.value());
280         imageSize.setHeight(options.resizeHeight.value());
281     }
282
283     // 5. If the sw and sh arguments are not specified and image's media data has both or
284     //    either of its intrinsic width and intrinsic height values equal to 0, then return
285     //    a promise rejected with an "InvalidStateError" DOMException and abort these steps.
286     // 6. If the sh argument is not specified and image's media data has an intrinsic height
287     //    of 0, then return a promise rejected with an "InvalidStateError" DOMException and
288     //    abort these steps.
289
290     // FIXME: It's unclear how these steps can happen, since step 4 required setting a
291     // width and height for the image.
292
293     if (!rect && (!imageSize.width() || !imageSize.height())) {
294         promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing dimensions");
295         return;
296     }
297
298     // 8. Let the ImageBitmap object's bitmap data be a copy of image's media data, cropped to
299     //    the source rectangle with formatting. If this is an animated image, the ImageBitmap
300     //    object's bitmap data must only be taken from the default image of the animation (the
301     //    one that the format defines is to be used when animation is not supported or is disabled),
302     //    or, if there is no such image, the first frame of the animation.
303
304     auto sourceRectangle = croppedSourceRectangleWithFormatting(roundedIntSize(imageSize), options, WTFMove(rect));
305     if (sourceRectangle.hasException()) {
306         promise.reject(sourceRectangle.releaseException());
307         return;
308     }
309
310     auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
311     auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
312
313     auto imageForRender = cachedImage->imageForRenderer(imageElement->renderer());
314     if (!imageForRender) {
315         promise.reject(InvalidStateError, "Cannot create ImageBitmap from image that can't be rendered");
316         return;
317     }
318
319     FloatRect destRect(FloatPoint(), outputSize);
320     ImagePaintingOptions paintingOptions;
321     paintingOptions.m_interpolationQuality = interpolationQualityForResizeQuality(options.resizeQuality);
322
323     bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), paintingOptions);
324
325     // 7. Create a new ImageBitmap object.
326     auto imageBitmap = create(WTFMove(bitmapData));
327
328     // 9. If the origin of image's image is not the same origin as the origin specified by the
329     //    entry settings object, then set the origin-clean flag of the ImageBitmap object's
330     //    bitmap to false.
331
332     imageBitmap->m_originClean = !taintsOrigin(*cachedImage);
333
334     // 10. Return a new promise, but continue running these steps in parallel.
335     // 11. Resolve the promise with the new ImageBitmap object as the value.
336
337     promise.resolve(WTFMove(imageBitmap));
338 }
339
340 void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<HTMLCanvasElement>& canvasElement, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
341 {
342     // 2. If the canvas element's bitmap has either a horizontal dimension or a vertical
343     //    dimension equal to zero, then return a promise rejected with an "InvalidStateError"
344     //    DOMException and abort these steps.
345     auto size = canvasElement->size();
346     if (!size.width() || !size.height()) {
347         promise.reject(InvalidStateError, "Cannot create ImageBitmap from a canvas that has zero width or height");
348         return;
349     }
350
351     // 4. Let the ImageBitmap object's bitmap data be a copy of the canvas element's bitmap
352     //    data, cropped to the source rectangle with formatting.
353
354     auto sourceRectangle = croppedSourceRectangleWithFormatting(size, options, WTFMove(rect));
355     if (sourceRectangle.hasException()) {
356         promise.reject(sourceRectangle.releaseException());
357         return;
358     }
359
360     auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
361     auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
362
363     auto imageForRender = canvasElement->copiedImage();
364     if (!imageForRender) {
365         promise.reject(InvalidStateError, "Cannot create ImageBitmap from canvas that can't be rendered");
366         return;
367     }
368
369     FloatRect destRect(FloatPoint(), outputSize);
370     ImagePaintingOptions paintingOptions;
371     paintingOptions.m_interpolationQuality = interpolationQualityForResizeQuality(options.resizeQuality);
372
373     bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), paintingOptions);
374
375     // 3. Create a new ImageBitmap object.
376     auto imageBitmap = create(WTFMove(bitmapData));
377
378     // 5. Set the origin-clean flag of the ImageBitmap object's bitmap to the same value as
379     //    the origin-clean flag of the canvas element's bitmap.
380
381     imageBitmap->m_originClean = canvasElement->originClean();
382
383     // 6. Return a new promise, but continue running these steps in parallel.
384     // 7. Resolve the promise with the new ImageBitmap object as the value.
385
386     promise.resolve(WTFMove(imageBitmap));
387 }
388
389 #if ENABLE(VIDEO)
390 void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<HTMLVideoElement>& video, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
391 {
392     // https://html.spec.whatwg.org/multipage/#dom-createimagebitmap
393     // WHATWG HTML 2102913b313078cd8eeac7e81e6a8756cbd3e773
394     // Steps 3-7.
395     // (Step 3 is handled in croppedSourceRectangleWithFormatting.)
396
397     // 4. Check the usability of the image argument. If this throws an exception
398     //    or returns bad, then return p rejected with an "InvalidStateError"
399     //    DOMException.
400     if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA) {
401         promise.reject(InvalidStateError, "Cannot create ImageBitmap before the HTMLVideoElement has data");
402         return;
403     }
404
405     // 6.1. If image's networkState attribute is NETWORK_EMPTY, then return p
406     //      rejected with an "InvalidStateError" DOMException.
407     if (video->networkState() == HTMLMediaElement::NETWORK_EMPTY) {
408         promise.reject(InvalidStateError, "Cannot create ImageBitmap before the HTMLVideoElement has data");
409         return;
410     }
411
412     // 6.2. Set imageBitmap's bitmap data to a copy of the frame at the current
413     //      playback position, at the media resource's intrinsic width and
414     //      intrinsic height (i.e., after any aspect-ratio correction has been
415     //      applied), cropped to the source rectangle with formatting.
416     auto size = video->player() ? roundedIntSize(video->player()->naturalSize()) : IntSize();
417     auto maybeSourceRectangle = croppedSourceRectangleWithFormatting(size, options, WTFMove(rect));
418     if (maybeSourceRectangle.hasException()) {
419         promise.reject(maybeSourceRectangle.releaseException());
420         return;
421     }
422     auto sourceRectangle = maybeSourceRectangle.releaseReturnValue();
423
424     auto outputSize = outputSizeForSourceRectangle(sourceRectangle, options);
425     auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
426
427     {
428         GraphicsContext& c = bitmapData->context();
429         GraphicsContextStateSaver stateSaver(c);
430         c.clip(FloatRect(FloatPoint(), outputSize));
431         auto scaleX = float(outputSize.width()) / float(sourceRectangle.width());
432         auto scaleY = float(outputSize.height()) / float(sourceRectangle.height());
433         c.scale(FloatSize(scaleX, scaleY));
434         c.translate(-sourceRectangle.location());
435         video->paintCurrentFrameInContext(c, FloatRect(FloatPoint(), size));
436     }
437
438     // 5. Let imageBitmap be a new ImageBitmap object.
439     auto imageBitmap = create(WTFMove(bitmapData));
440
441     // 6.3. If the origin of image's video is not same origin with entry
442     //      settings object's origin, then set the origin-clean flag of
443     //      image's bitmap to false.
444     imageBitmap->m_originClean = !taintsOrigin(scriptExecutionContext.securityOrigin(), *video);
445
446     // 6.4.1. Resolve p with imageBitmap.
447     promise.resolve(WTFMove(imageBitmap));
448 }
449 #endif
450
451 void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<ImageBitmap>& existingImageBitmap, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
452 {
453     // 2. If image's [[Detached]] internal slot value is true, return a promise
454     //    rejected with an "InvalidStateError" DOMException and abort these steps.
455     if (existingImageBitmap->isDetached() || !existingImageBitmap->buffer()) {
456         promise.reject(InvalidStateError, "Cannot create ImageBitmap from a detached ImageBitmap");
457         return;
458     }
459
460     // 4. Let the ImageBitmap object's bitmap data be a copy of the image argument's
461     //    bitmap data, cropped to the source rectangle with formatting.
462     auto sourceRectangle = croppedSourceRectangleWithFormatting(existingImageBitmap->buffer()->logicalSize(), options, WTFMove(rect));
463     if (sourceRectangle.hasException()) {
464         promise.reject(sourceRectangle.releaseException());
465         return;
466     }
467
468     auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
469     auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
470
471     auto imageForRender = existingImageBitmap->buffer()->copyImage();
472
473     FloatRect destRect(FloatPoint(), outputSize);
474     ImagePaintingOptions paintingOptions;
475     paintingOptions.m_interpolationQuality = interpolationQualityForResizeQuality(options.resizeQuality);
476
477     bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), paintingOptions);
478
479     // 3. Create a new ImageBitmap object.
480     auto imageBitmap = create(WTFMove(bitmapData));
481
482     // 5. Set the origin-clean flag of the ImageBitmap object's bitmap to the same
483     //    value as the origin-clean flag of the bitmap of the image argument.
484     imageBitmap->m_originClean = existingImageBitmap->originClean();
485
486     // 6. Return a new promise, but continue running these steps in parallel.
487     // 7. Resolve the promise with the new ImageBitmap object as the value.
488     promise.resolve(WTFMove(imageBitmap));
489 }
490
491 class ImageBitmapImageObserver final : public RefCounted<ImageBitmapImageObserver>, public ImageObserver {
492 public:
493     static Ref<ImageBitmapImageObserver> create(String mimeType, long long expectedContentLength, const URL& sourceUrl)
494     {
495         return adoptRef(*new ImageBitmapImageObserver(mimeType, expectedContentLength, sourceUrl));
496     }
497
498     URL sourceUrl() const override { return m_sourceUrl; }
499     String mimeType() const override { return m_mimeType; }
500     long long expectedContentLength() const override { return m_expectedContentLength; }
501
502     void decodedSizeChanged(const Image&, long long) override { }
503
504     void didDraw(const Image&) override { }
505
506     bool canDestroyDecodedData(const Image&) override { return true; }
507     void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* = nullptr, DecodingStatus = DecodingStatus::Invalid) override { }
508     void changedInRect(const Image&, const IntRect* = nullptr) override { }
509
510 private:
511     ImageBitmapImageObserver(String mimeType, long long expectedContentLength, const URL& sourceUrl)
512         : m_mimeType(mimeType)
513         , m_expectedContentLength(expectedContentLength)
514         , m_sourceUrl(sourceUrl)
515     { }
516
517     String m_mimeType;
518     long long m_expectedContentLength;
519     URL m_sourceUrl;
520 };
521
522 class PendingImageBitmap final : public ActiveDOMObject, public FileReaderLoaderClient {
523 public:
524     static void fetch(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>&& blob, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
525     {
526         auto pendingImageBitmap = new PendingImageBitmap(scriptExecutionContext, WTFMove(blob), WTFMove(options), WTFMove(rect), WTFMove(promise));
527         pendingImageBitmap->start(scriptExecutionContext);
528     }
529
530 private:
531     PendingImageBitmap(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>&& blob, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
532         : ActiveDOMObject(&scriptExecutionContext)
533         , m_blobLoader(FileReaderLoader::ReadAsArrayBuffer, this)
534         , m_blob(WTFMove(blob))
535         , m_options(WTFMove(options))
536         , m_rect(WTFMove(rect))
537         , m_promise(WTFMove(promise))
538     {
539         suspendIfNeeded();
540     }
541
542     void start(ScriptExecutionContext& scriptExecutionContext)
543     {
544         m_blobLoader.start(&scriptExecutionContext, *m_blob);
545     }
546
547     // ActiveDOMObject
548
549     const char* activeDOMObjectName() const override
550     {
551         return "PendingImageBitmap";
552     }
553
554     bool canSuspendForDocumentSuspension() const override
555     {
556         // FIXME: Deal with suspension.
557         return false;
558     }
559
560     // FileReaderLoaderClient
561
562     void didStartLoading() override
563     {
564     }
565
566     void didReceiveData() override
567     {
568     }
569
570     void didFinishLoading() override
571     {
572         createImageBitmap(m_blobLoader.arrayBufferResult());
573         delete this;
574     }
575
576     void didFail(int) override
577     {
578         createImageBitmap(nullptr);
579         delete this;
580     }
581
582     void createImageBitmap(RefPtr<ArrayBuffer>&& arrayBuffer)
583     {
584         if (!arrayBuffer) {
585             m_promise.reject(InvalidStateError, "An error occured reading the Blob argument to createImageBitmap");
586             return;
587         }
588
589         ImageBitmap::createFromBuffer(arrayBuffer.releaseNonNull(), m_blob->type(), m_blob->size(), m_blobLoader.url(), WTFMove(m_options), WTFMove(m_rect), WTFMove(m_promise));
590     }
591
592     FileReaderLoader m_blobLoader;
593     RefPtr<Blob> m_blob;
594     ImageBitmapOptions m_options;
595     std::optional<IntRect> m_rect;
596     ImageBitmap::Promise m_promise;
597 };
598
599 void ImageBitmap::createFromBuffer(
600     Ref<ArrayBuffer>&& arrayBuffer,
601     String mimeType,
602     long long expectedContentLength,
603     const URL& sourceUrl,
604     ImageBitmapOptions&& options,
605     std::optional<IntRect> rect,
606     ImageBitmap::Promise&& promise)
607 {
608     if (!arrayBuffer->byteLength()) {
609         promise.reject(InvalidStateError, "Cannot create an ImageBitmap from an empty buffer");
610         return;
611     }
612
613     auto sharedBuffer = SharedBuffer::create(static_cast<const char*>(arrayBuffer->data()), arrayBuffer->byteLength());
614     auto observer = ImageBitmapImageObserver::create(mimeType, expectedContentLength, sourceUrl);
615     auto image = Image::create(observer.get());
616     if (!image) {
617         promise.reject(InvalidStateError, "The type of the argument to createImageBitmap is not supported");
618         return;
619     }
620
621     auto result = image->setData(sharedBuffer.copyRef(), true);
622     if (result != EncodedDataStatus::Complete) {
623         promise.reject(InvalidStateError, "Cannot decode the data in the argument to createImageBitmap");
624         return;
625     }
626
627     auto sourceRectangle = croppedSourceRectangleWithFormatting(roundedIntSize(image->size()), options, rect);
628     if (sourceRectangle.hasException()) {
629         promise.reject(sourceRectangle.releaseException());
630         return;
631     }
632
633     auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
634     auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
635     if (!bitmapData) {
636         promise.reject(InvalidStateError, "Cannot create an image buffer from the argument to createImageBitmap");
637         return;
638     }
639
640     FloatRect destRect(FloatPoint(), outputSize);
641     ImagePaintingOptions paintingOptions;
642     paintingOptions.m_interpolationQuality = interpolationQualityForResizeQuality(options.resizeQuality);
643
644     bitmapData->context().drawImage(*image, destRect, sourceRectangle.releaseReturnValue(), paintingOptions);
645
646     auto imageBitmap = create(WTFMove(bitmapData));
647
648     promise.resolve(WTFMove(imageBitmap));
649 }
650
651 void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>& blob, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
652 {
653     // 2. Return a new promise, but continue running these steps in parallel.
654     PendingImageBitmap::fetch(scriptExecutionContext, WTFMove(blob), WTFMove(options), WTFMove(rect), WTFMove(promise));
655 }
656
657 void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<ImageData>& imageData, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
658 {
659     UNUSED_PARAM(imageData);
660     UNUSED_PARAM(options);
661     UNUSED_PARAM(rect);
662
663     // 2. If the image object's data attribute value's [[Detached]] internal slot value
664     //    is true, return a promise rejected with an "InvalidStateError" DOMException
665     //    and abort these steps.
666
667     // 3. Create a new ImageBitmap object.
668
669     // 4. Let the ImageBitmap object's bitmap data be the image data given by the ImageData
670     //    object, cropped to the source rectangle with formatting.
671
672     // 5. Return a new promise, but continue running these steps in parallel.
673     // 6. Resolve the promise with the new ImageBitmap object as the value.
674     promise.reject(TypeError, "createImageBitmap with ImageData is not implemented");
675 }
676
677 ImageBitmap::ImageBitmap(std::unique_ptr<ImageBuffer>&& buffer)
678     : m_bitmapData(WTFMove(buffer))
679 {
680     ASSERT(m_bitmapData);
681 }
682
683 ImageBitmap::~ImageBitmap() = default;
684
685 unsigned ImageBitmap::width() const
686 {
687     if (m_detached || !m_bitmapData)
688         return 0;
689
690     // FIXME: Is this the right width?
691     return m_bitmapData->logicalSize().width();
692 }
693
694 unsigned ImageBitmap::height() const
695 {
696     if (m_detached || !m_bitmapData)
697         return 0;
698
699     // FIXME: Is this the right height?
700     return m_bitmapData->logicalSize().height();
701 }
702
703 void ImageBitmap::close()
704 {
705     m_detached = true;
706     m_bitmapData = nullptr;
707 }
708
709 std::unique_ptr<ImageBuffer> ImageBitmap::transferOwnershipAndClose()
710 {
711     m_detached = true;
712     return WTFMove(m_bitmapData);
713 }
714
715 }