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