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