Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / fetch / ImageResource.cpp
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6     Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "core/fetch/ImageResource.h"
26
27 #include "RuntimeEnabledFeatures.h"
28 #include "core/fetch/ImageResourceClient.h"
29 #include "core/fetch/MemoryCache.h"
30 #include "core/fetch/ResourceClient.h"
31 #include "core/fetch/ResourceClientWalker.h"
32 #include "core/fetch/ResourceFetcher.h"
33 #include "core/frame/FrameView.h"
34 #include "core/rendering/RenderObject.h"
35 #include "core/svg/graphics/SVGImage.h"
36 #include "platform/SharedBuffer.h"
37 #include "platform/graphics/BitmapImage.h"
38 #include "wtf/CurrentTime.h"
39 #include "wtf/StdLibExtras.h"
40 #include "wtf/Vector.h"
41
42 namespace WebCore {
43
44 ImageResource::ImageResource(const ResourceRequest& resourceRequest)
45     : Resource(resourceRequest, Image)
46     , m_devicePixelRatioHeaderValue(1.0)
47     , m_image(nullptr)
48     , m_loadingMultipartContent(false)
49     , m_hasDevicePixelRatioHeaderValue(false)
50 {
51     setStatus(Unknown);
52     setCustomAcceptHeader();
53 }
54
55 ImageResource::ImageResource(WebCore::Image* image)
56     : Resource(ResourceRequest(""), Image)
57     , m_image(image)
58 {
59     setStatus(Cached);
60     setLoading(false);
61     setCustomAcceptHeader();
62 }
63
64 ImageResource::ImageResource(const ResourceRequest& resourceRequest, WebCore::Image* image)
65     : Resource(resourceRequest, Image)
66     , m_image(image)
67 {
68     setStatus(Cached);
69     setLoading(false);
70     setCustomAcceptHeader();
71 }
72
73 ImageResource::~ImageResource()
74 {
75     clearImage();
76 }
77
78 void ImageResource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
79 {
80     if (!fetcher || fetcher->autoLoadImages())
81         Resource::load(fetcher, options);
82     else
83         setLoading(false);
84 }
85
86 void ImageResource::didAddClient(ResourceClient* c)
87 {
88     if (m_data && !m_image && !errorOccurred()) {
89         createImage();
90         m_image->setData(m_data, true);
91     }
92
93     ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
94     if (m_image && !m_image->isNull())
95         static_cast<ImageResourceClient*>(c)->imageChanged(this);
96
97     Resource::didAddClient(c);
98 }
99
100 void ImageResource::didRemoveClient(ResourceClient* c)
101 {
102     ASSERT(c);
103     ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
104
105     m_pendingContainerSizeRequests.remove(static_cast<ImageResourceClient*>(c));
106     if (m_svgImageCache)
107         m_svgImageCache->removeClientFromCache(static_cast<ImageResourceClient*>(c));
108
109     Resource::didRemoveClient(c);
110 }
111
112 void ImageResource::switchClientsToRevalidatedResource()
113 {
114     ASSERT(resourceToRevalidate());
115     ASSERT(resourceToRevalidate()->isImage());
116     // Pending container size requests need to be transferred to the revalidated resource.
117     if (!m_pendingContainerSizeRequests.isEmpty()) {
118         // A copy of pending size requests is needed as they are deleted during Resource::switchClientsToRevalidateResouce().
119         ContainerSizeRequests switchContainerSizeRequests;
120         for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
121             switchContainerSizeRequests.set(it->key, it->value);
122         Resource::switchClientsToRevalidatedResource();
123         ImageResource* revalidatedImageResource = toImageResource(resourceToRevalidate());
124         for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it)
125             revalidatedImageResource->setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
126         return;
127     }
128
129     Resource::switchClientsToRevalidatedResource();
130 }
131
132 bool ImageResource::isSafeToUnlock() const
133 {
134     // Note that |m_image| holds a reference to |m_data| in addition to the one held by the Resource parent class.
135     return !m_image || (m_image->hasOneRef() && m_data->refCount() == 2);
136 }
137
138 void ImageResource::destroyDecodedDataIfPossible()
139 {
140     if (!hasClients() && !isLoading() && (!m_image || (m_image->hasOneRef() && m_image->isBitmapImage()))) {
141         m_image = nullptr;
142         setDecodedSize(0);
143     } else if (m_image && !errorOccurred()) {
144         m_image->destroyDecodedData(true);
145     }
146 }
147
148 void ImageResource::allClientsRemoved()
149 {
150     m_pendingContainerSizeRequests.clear();
151     if (m_image && !errorOccurred())
152         m_image->resetAnimation();
153     Resource::allClientsRemoved();
154 }
155
156 pair<WebCore::Image*, float> ImageResource::brokenImage(float deviceScaleFactor)
157 {
158     if (deviceScaleFactor >= 2) {
159         DEFINE_STATIC_REF(WebCore::Image, brokenImageHiRes, (WebCore::Image::loadPlatformResource("missingImage@2x")));
160         return std::make_pair(brokenImageHiRes, 2);
161     }
162
163     DEFINE_STATIC_REF(WebCore::Image, brokenImageLoRes, (WebCore::Image::loadPlatformResource("missingImage")));
164     return std::make_pair(brokenImageLoRes, 1);
165 }
166
167 bool ImageResource::willPaintBrokenImage() const
168 {
169     return errorOccurred();
170 }
171
172 WebCore::Image* ImageResource::image()
173 {
174     ASSERT(!isPurgeable());
175
176     if (errorOccurred()) {
177         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
178         // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
179         // when they need the real, deviceScaleFactor-appropriate broken image icon.
180         return brokenImage(1).first;
181     }
182
183     if (m_image)
184         return m_image.get();
185
186     return WebCore::Image::nullImage();
187 }
188
189 WebCore::Image* ImageResource::imageForRenderer(const RenderObject* renderer)
190 {
191     ASSERT(!isPurgeable());
192
193     if (errorOccurred()) {
194         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
195         // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
196         // when they need the real, deviceScaleFactor-appropriate broken image icon.
197         return brokenImage(1).first;
198     }
199
200     if (!m_image)
201         return WebCore::Image::nullImage();
202
203     if (m_image->isSVGImage()) {
204         WebCore::Image* image = m_svgImageCache->imageForRenderer(renderer);
205         if (image != WebCore::Image::nullImage())
206             return image;
207     }
208
209     return m_image.get();
210 }
211
212 void ImageResource::setContainerSizeForRenderer(const ImageResourceClient* renderer, const IntSize& containerSize, float containerZoom)
213 {
214     if (containerSize.isEmpty())
215         return;
216     ASSERT(renderer);
217     ASSERT(containerZoom);
218     if (!m_image) {
219         m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
220         return;
221     }
222     if (!m_image->isSVGImage()) {
223         m_image->setContainerSize(containerSize);
224         return;
225     }
226
227     m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
228 }
229
230 bool ImageResource::usesImageContainerSize() const
231 {
232     if (m_image)
233         return m_image->usesContainerSize();
234
235     return false;
236 }
237
238 bool ImageResource::imageHasRelativeWidth() const
239 {
240     if (m_image)
241         return m_image->hasRelativeWidth();
242
243     return false;
244 }
245
246 bool ImageResource::imageHasRelativeHeight() const
247 {
248     if (m_image)
249         return m_image->hasRelativeHeight();
250
251     return false;
252 }
253
254 LayoutSize ImageResource::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
255 {
256     ASSERT(!isPurgeable());
257
258     if (!m_image)
259         return IntSize();
260
261     LayoutSize imageSize;
262
263     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
264         imageSize = toBitmapImage(m_image.get())->sizeRespectingOrientation();
265     else if (m_image->isSVGImage() && sizeType == NormalSize)
266         imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
267     else
268         imageSize = m_image->size();
269
270     if (multiplier == 1.0f)
271         return imageSize;
272
273     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
274     float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
275     float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
276     LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
277     imageSize.scale(widthScale, heightScale);
278     imageSize.clampToMinimumSize(minimumSize);
279     ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
280     return imageSize;
281 }
282
283 void ImageResource::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
284 {
285     if (m_image)
286         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
287 }
288
289 void ImageResource::notifyObservers(const IntRect* changeRect)
290 {
291     ResourceClientWalker<ImageResourceClient> w(m_clients);
292     while (ImageResourceClient* c = w.next())
293         c->imageChanged(this, changeRect);
294 }
295
296 void ImageResource::clear()
297 {
298     prune();
299     clearImage();
300     m_pendingContainerSizeRequests.clear();
301     setEncodedSize(0);
302 }
303
304 void ImageResource::setCustomAcceptHeader()
305 {
306     DEFINE_STATIC_LOCAL(const AtomicString, acceptWebP, ("image/webp,*/*;q=0.8", AtomicString::ConstructFromLiteral));
307     setAccept(acceptWebP);
308 }
309
310 inline void ImageResource::createImage()
311 {
312     // Create the image if it doesn't yet exist.
313     if (m_image)
314         return;
315
316     if (m_response.mimeType() == "image/svg+xml") {
317         RefPtr<SVGImage> svgImage = SVGImage::create(this);
318         m_svgImageCache = SVGImageCache::create(svgImage.get());
319         m_image = svgImage.release();
320     } else {
321         m_image = BitmapImage::create(this);
322     }
323
324     if (m_image) {
325         // Send queued container size requests.
326         if (m_image->usesContainerSize()) {
327             for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
328                 setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
329         }
330         m_pendingContainerSizeRequests.clear();
331     }
332 }
333
334 inline void ImageResource::clearImage()
335 {
336     // If our Image has an observer, it's always us so we need to clear the back pointer
337     // before dropping our reference.
338     if (m_image)
339         m_image->setImageObserver(0);
340     m_image.clear();
341 }
342
343 void ImageResource::appendData(const char* data, int length)
344 {
345     Resource::appendData(data, length);
346     if (!m_loadingMultipartContent)
347         updateImage(false);
348 }
349
350 void ImageResource::updateImage(bool allDataReceived)
351 {
352     TRACE_EVENT0("webkit", "ImageResource::updateImage");
353
354     if (m_data)
355         createImage();
356
357     bool sizeAvailable = false;
358
359     // Have the image update its data from its internal buffer.
360     // It will not do anything now, but will delay decoding until
361     // queried for info (like size or specific image frames).
362     if (m_image)
363         sizeAvailable = m_image->setData(m_data, allDataReceived);
364
365     // Go ahead and tell our observers to try to draw if we have either
366     // received all the data or the size is known. Each chunk from the
367     // network causes observers to repaint, which will force that chunk
368     // to decode.
369     if (sizeAvailable || allDataReceived) {
370         if (!m_image || m_image->isNull()) {
371             error(errorOccurred() ? status() : DecodeError);
372             if (memoryCache()->contains(this))
373                 memoryCache()->remove(this);
374             return;
375         }
376
377         // It would be nice to only redraw the decoded band of the image, but with the current design
378         // (decoding delayed until painting) that seems hard.
379         notifyObservers();
380     }
381 }
382
383 void ImageResource::finishOnePart()
384 {
385     if (m_loadingMultipartContent)
386         clear();
387     updateImage(true);
388     if (m_loadingMultipartContent)
389         m_data.clear();
390     Resource::finishOnePart();
391 }
392
393 void ImageResource::error(Resource::Status status)
394 {
395     clear();
396     Resource::error(status);
397     notifyObservers();
398 }
399
400 void ImageResource::responseReceived(const ResourceResponse& response)
401 {
402     if (m_loadingMultipartContent && m_data)
403         finishOnePart();
404     else if (response.isMultipart())
405         m_loadingMultipartContent = true;
406     if (RuntimeEnabledFeatures::clientHintsDprEnabled()) {
407         m_devicePixelRatioHeaderValue = response.httpHeaderField("DPR").toFloat(&m_hasDevicePixelRatioHeaderValue);
408         if (!m_hasDevicePixelRatioHeaderValue || m_devicePixelRatioHeaderValue <= 0.0) {
409             m_devicePixelRatioHeaderValue = 1.0;
410             m_hasDevicePixelRatioHeaderValue = false;
411         }
412     }
413     Resource::responseReceived(response);
414 }
415
416 void ImageResource::decodedSizeChanged(const WebCore::Image* image, int delta)
417 {
418     if (!image || image != m_image)
419         return;
420
421     setDecodedSize(decodedSize() + delta);
422 }
423
424 void ImageResource::didDraw(const WebCore::Image* image)
425 {
426     if (!image || image != m_image)
427         return;
428     Resource::didAccessDecodedData();
429 }
430
431 bool ImageResource::shouldPauseAnimation(const WebCore::Image* image)
432 {
433     if (!image || image != m_image)
434         return false;
435
436     ResourceClientWalker<ImageResourceClient> w(m_clients);
437     while (ImageResourceClient* c = w.next()) {
438         if (c->willRenderImage(this))
439             return false;
440     }
441
442     return true;
443 }
444
445 void ImageResource::animationAdvanced(const WebCore::Image* image)
446 {
447     if (!image || image != m_image)
448         return;
449     notifyObservers();
450 }
451
452 void ImageResource::changedInRect(const WebCore::Image* image, const IntRect& rect)
453 {
454     if (!image || image != m_image)
455         return;
456     notifyObservers(&rect);
457 }
458
459 bool ImageResource::currentFrameKnownToBeOpaque(const RenderObject* renderer)
460 {
461     WebCore::Image* image = imageForRenderer(renderer);
462     if (image->isBitmapImage())
463         image->nativeImageForCurrentFrame(); // force decode
464     return image->currentFrameKnownToBeOpaque();
465 }
466
467 bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin)
468 {
469     if (!image()->currentFrameHasSingleSecurityOrigin())
470         return false;
471     if (passesAccessControlCheck(securityOrigin))
472         return true;
473     return !securityOrigin->taintsCanvas(response().url());
474 }
475
476 } // namespace WebCore