Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / imagebitmap / ImageBitmapFactories.cpp
1 /*
2  * Copyright (c) 2013, Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "core/imagebitmap/ImageBitmapFactories.h"
33
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/ScriptPromiseResolver.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/fileapi/Blob.h"
38 #include "core/frame/ImageBitmap.h"
39 #include "core/frame/LocalDOMWindow.h"
40 #include "core/html/HTMLCanvasElement.h"
41 #include "core/html/HTMLImageElement.h"
42 #include "core/html/HTMLVideoElement.h"
43 #include "core/html/ImageData.h"
44 #include "core/html/canvas/CanvasRenderingContext2D.h"
45 #include "core/workers/WorkerGlobalScope.h"
46 #include "platform/SharedBuffer.h"
47 #include "platform/graphics/BitmapImage.h"
48 #include "platform/graphics/ImageSource.h"
49 #include "platform/graphics/skia/NativeImageSkia.h"
50 #include <v8.h>
51
52 namespace blink {
53
54 static LayoutSize sizeFor(HTMLImageElement* image)
55 {
56     if (ImageResource* cachedImage = image->cachedImage())
57         return cachedImage->imageSizeForRenderer(image->renderer(), 1.0f); // FIXME: Not sure about this.
58     return IntSize();
59 }
60
61 static IntSize sizeFor(HTMLVideoElement* video)
62 {
63     if (blink::WebMediaPlayer* webMediaPlayer = video->webMediaPlayer())
64         return webMediaPlayer->naturalSize();
65     return IntSize();
66 }
67
68 static ScriptPromise fulfillImageBitmap(ScriptState* scriptState, PassRefPtrWillBeRawPtr<ImageBitmap> imageBitmap)
69 {
70     RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
71     ScriptPromise promise = resolver->promise();
72     if (imageBitmap) {
73         resolver->resolve(imageBitmap);
74     } else {
75         resolver->reject(ScriptValue(scriptState, v8::Null(scriptState->isolate())));
76     }
77     return promise;
78 }
79
80 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLImageElement* image, ExceptionState& exceptionState)
81 {
82     LayoutSize s = sizeFor(image);
83     return createImageBitmap(scriptState, eventTarget, image, 0, 0, s.width(), s.height(), exceptionState);
84 }
85
86 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLImageElement* image, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
87 {
88     // This variant does not work in worker threads.
89     ASSERT(eventTarget.toDOMWindow());
90
91     if (!image->cachedImage()) {
92         exceptionState.throwDOMException(InvalidStateError, "No image can be retrieved from the provided element.");
93         return ScriptPromise();
94     }
95     if (image->cachedImage()->image()->isSVGImage()) {
96         exceptionState.throwDOMException(InvalidStateError, "The image element contains an SVG image, which is unsupported.");
97         return ScriptPromise();
98     }
99     if (!sw || !sh) {
100         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
101         return ScriptPromise();
102     }
103     if (!image->cachedImage()->image()->currentFrameHasSingleSecurityOrigin()) {
104         exceptionState.throwSecurityError("The source image contains image data from multiple origins.");
105         return ScriptPromise();
106     }
107     if (!image->cachedImage()->passesAccessControlCheck(eventTarget.toDOMWindow()->document()->securityOrigin()) && eventTarget.toDOMWindow()->document()->securityOrigin()->taintsCanvas(image->src())) {
108         exceptionState.throwSecurityError("Cross-origin access to the source image is denied.");
109         return ScriptPromise();
110     }
111     // FIXME: make ImageBitmap creation asynchronous crbug.com/258082
112     return fulfillImageBitmap(scriptState, ImageBitmap::create(image, IntRect(sx, sy, sw, sh)));
113 }
114
115 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLVideoElement* video, ExceptionState& exceptionState)
116 {
117     IntSize s = sizeFor(video);
118     return createImageBitmap(scriptState, eventTarget, video, 0, 0, s.width(), s.height(), exceptionState);
119 }
120
121 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLVideoElement* video, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
122 {
123     // This variant does not work in worker threads.
124     ASSERT(eventTarget.toDOMWindow());
125
126     if (video->networkState() == HTMLMediaElement::NETWORK_EMPTY) {
127         exceptionState.throwDOMException(InvalidStateError, "The provided element has not retrieved data.");
128         return ScriptPromise();
129     }
130     if (video->readyState() <= HTMLMediaElement::HAVE_METADATA) {
131         exceptionState.throwDOMException(InvalidStateError, "The provided element's player has no current data.");
132         return ScriptPromise();
133     }
134     if (!sw || !sh) {
135         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
136         return ScriptPromise();
137     }
138     if (!video->hasSingleSecurityOrigin()) {
139         exceptionState.throwSecurityError("The source video contains image data from multiple origins.");
140         return ScriptPromise();
141     }
142     if (!video->webMediaPlayer()->didPassCORSAccessCheck()
143         && eventTarget.toDOMWindow()->document()->securityOrigin()->taintsCanvas(video->currentSrc())) {
144         exceptionState.throwSecurityError("Cross-origin access to the source video is denied.");
145         return ScriptPromise();
146     }
147     // FIXME: make ImageBitmap creation asynchronous crbug.com/258082
148     return fulfillImageBitmap(scriptState, ImageBitmap::create(video, IntRect(sx, sy, sw, sh)));
149 }
150
151 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, CanvasRenderingContext2D* context, ExceptionState& exceptionState)
152 {
153     return createImageBitmap(scriptState, eventTarget, context->canvas(), exceptionState);
154 }
155
156 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, CanvasRenderingContext2D* context, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
157 {
158     return createImageBitmap(scriptState, eventTarget, context->canvas(), sx, sy, sw, sh, exceptionState);
159 }
160
161 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLCanvasElement* canvas, ExceptionState& exceptionState)
162 {
163     return createImageBitmap(scriptState, eventTarget, canvas, 0, 0, canvas->width(), canvas->height(), exceptionState);
164 }
165
166 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, HTMLCanvasElement* canvas, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
167 {
168     // This variant does not work in worker threads.
169     ASSERT(eventTarget.toDOMWindow());
170
171     if (!canvas->originClean()) {
172         exceptionState.throwSecurityError("The canvas element provided is tainted with cross-origin data.");
173         return ScriptPromise();
174     }
175     if (!sw || !sh) {
176         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
177         return ScriptPromise();
178     }
179
180     // FIXME: make ImageBitmap creation asynchronous crbug.com/258082
181     return fulfillImageBitmap(scriptState, canvas->buffer() ? ImageBitmap::create(canvas, IntRect(sx, sy, sw, sh)) : nullptr);
182 }
183
184 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, Blob* blob, ExceptionState& exceptionState)
185 {
186     ImageBitmapLoader* loader = ImageBitmapFactories::ImageBitmapLoader::create(from(eventTarget), IntRect(), scriptState);
187     ScriptPromise promise = loader->promise();
188     from(eventTarget).addLoader(loader);
189     loader->loadBlobAsync(eventTarget.executionContext(), blob);
190     return promise;
191 }
192
193 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, Blob* blob, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
194 {
195     if (!sw || !sh) {
196         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
197         return ScriptPromise();
198     }
199     ImageBitmapLoader* loader = ImageBitmapFactories::ImageBitmapLoader::create(from(eventTarget), IntRect(sx, sy, sw, sh), scriptState);
200     ScriptPromise promise = loader->promise();
201     from(eventTarget).addLoader(loader);
202     loader->loadBlobAsync(eventTarget.executionContext(), blob);
203     return promise;
204 }
205
206 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, ImageData* data, ExceptionState& exceptionState)
207 {
208     return createImageBitmap(scriptState, eventTarget, data, 0, 0, data->width(), data->height(), exceptionState);
209 }
210
211 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, ImageData* data, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
212 {
213     if (!sw || !sh) {
214         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
215         return ScriptPromise();
216     }
217     // FIXME: make ImageBitmap creation asynchronous crbug.com/258082
218     return fulfillImageBitmap(scriptState, ImageBitmap::create(data, IntRect(sx, sy, sw, sh)));
219 }
220
221 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, ImageBitmap* bitmap, ExceptionState& exceptionState)
222 {
223     return createImageBitmap(scriptState, eventTarget, bitmap, 0, 0, bitmap->width(), bitmap->height(), exceptionState);
224 }
225
226 ScriptPromise ImageBitmapFactories::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, ImageBitmap* bitmap, int sx, int sy, int sw, int sh, ExceptionState& exceptionState)
227 {
228     if (!sw || !sh) {
229         exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sw ? "height" : "width"));
230         return ScriptPromise();
231     }
232     // FIXME: make ImageBitmap creation asynchronous crbug.com/258082
233     return fulfillImageBitmap(scriptState, ImageBitmap::create(bitmap, IntRect(sx, sy, sw, sh)));
234 }
235
236 const char* ImageBitmapFactories::supplementName()
237 {
238     return "ImageBitmapFactories";
239 }
240
241 ImageBitmapFactories& ImageBitmapFactories::from(EventTarget& eventTarget)
242 {
243     if (LocalDOMWindow* window = eventTarget.toDOMWindow())
244         return fromInternal(*window);
245
246     ASSERT(eventTarget.executionContext()->isWorkerGlobalScope());
247     return ImageBitmapFactories::fromInternal(*toWorkerGlobalScope(eventTarget.executionContext()));
248 }
249
250 template<class GlobalObject>
251 ImageBitmapFactories& ImageBitmapFactories::fromInternal(GlobalObject& object)
252 {
253     ImageBitmapFactories* supplement = static_cast<ImageBitmapFactories*>(WillBeHeapSupplement<GlobalObject>::from(object, supplementName()));
254     if (!supplement) {
255         supplement = new ImageBitmapFactories();
256         WillBeHeapSupplement<GlobalObject>::provideTo(object, supplementName(), adoptPtrWillBeNoop(supplement));
257     }
258     return *supplement;
259 }
260
261 void ImageBitmapFactories::addLoader(ImageBitmapLoader* loader)
262 {
263     m_pendingLoaders.add(loader);
264 }
265
266 void ImageBitmapFactories::didFinishLoading(ImageBitmapLoader* loader)
267 {
268     ASSERT(m_pendingLoaders.contains(loader));
269     m_pendingLoaders.remove(loader);
270 }
271
272 ImageBitmapFactories::ImageBitmapLoader::ImageBitmapLoader(ImageBitmapFactories& factory, const IntRect& cropRect, ScriptState* scriptState)
273     : m_loader(FileReaderLoader::ReadAsArrayBuffer, this)
274     , m_factory(&factory)
275     , m_resolver(ScriptPromiseResolver::create(scriptState))
276     , m_cropRect(cropRect)
277 {
278 }
279
280 void ImageBitmapFactories::ImageBitmapLoader::loadBlobAsync(ExecutionContext* context, Blob* blob)
281 {
282     m_loader.start(context, blob->blobDataHandle());
283 }
284
285 void ImageBitmapFactories::trace(Visitor* visitor)
286 {
287     visitor->trace(m_pendingLoaders);
288     WillBeHeapSupplement<LocalDOMWindow>::trace(visitor);
289     WillBeHeapSupplement<WorkerGlobalScope>::trace(visitor);
290 }
291
292 void ImageBitmapFactories::ImageBitmapLoader::rejectPromise()
293 {
294     m_resolver->reject(ScriptValue(m_resolver->scriptState(), v8::Null(m_resolver->scriptState()->isolate())));
295     m_factory->didFinishLoading(this);
296 }
297
298 void ImageBitmapFactories::ImageBitmapLoader::didFinishLoading()
299 {
300     if (!m_loader.arrayBufferResult()) {
301         rejectPromise();
302         return;
303     }
304     RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create((char*)m_loader.arrayBufferResult()->data(), m_loader.arrayBufferResult()->byteLength());
305
306     OwnPtr<ImageSource> source = adoptPtr(new ImageSource());
307     source->setData(*sharedBuffer, true);
308     RefPtr<NativeImageSkia> imageSkia = source->createFrameAtIndex(0);
309     if (!imageSkia) {
310         rejectPromise();
311         return;
312     }
313
314     RefPtr<Image> image = BitmapImage::create(imageSkia);
315     if (!image->width() || !image->height()) {
316         rejectPromise();
317         return;
318     }
319     if (!m_cropRect.width() && !m_cropRect.height()) {
320         // No cropping variant was called.
321         m_cropRect = IntRect(IntPoint(), image->size());
322     }
323
324     RefPtrWillBeRawPtr<ImageBitmap> imageBitmap = ImageBitmap::create(image.get(), m_cropRect);
325     m_resolver->resolve(imageBitmap.release());
326     m_factory->didFinishLoading(this);
327 }
328
329 void ImageBitmapFactories::ImageBitmapLoader::didFail(FileError::ErrorCode)
330 {
331     rejectPromise();
332 }
333
334 void ImageBitmapFactories::ImageBitmapLoader::trace(Visitor* visitor)
335 {
336     visitor->trace(m_factory);
337 }
338
339 } // namespace blink