[CSS Shapes] CORS-enabled fetch for shape image values
[WebKit-https.git] / Source / WebCore / loader / cache / CachedImage.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 "CachedImage.h"
26
27 #include "BitmapImage.h"
28 #include "CachedImageClient.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
32 #include "Frame.h"
33 #include "FrameLoader.h"
34 #include "FrameLoaderClient.h"
35 #include "FrameLoaderTypes.h"
36 #include "FrameView.h"
37 #include "MemoryCache.h"
38 #include "Page.h"
39 #include "RenderElement.h"
40 #include "ResourceBuffer.h"
41 #include "SecurityOrigin.h"
42 #include "Settings.h"
43 #include "SubresourceLoader.h"
44 #include <wtf/CurrentTime.h>
45 #include <wtf/StdLibExtras.h>
46 #include <wtf/Vector.h>
47
48 #if USE(CG)
49 #include "PDFDocumentImage.h"
50 #endif
51
52 #if ENABLE(SVG)
53 #include "SVGImage.h"
54 #endif
55
56 #if ENABLE(DISK_IMAGE_CACHE)
57 #include "DiskImageCacheIOS.h"
58 #endif
59
60 namespace WebCore {
61
62 CachedImage::CachedImage(const ResourceRequest& resourceRequest)
63     : CachedResource(resourceRequest, ImageResource)
64     , m_image(0)
65     , m_shouldPaintBrokenImage(true)
66 {
67     setStatus(Unknown);
68 }
69
70 CachedImage::CachedImage(Image* image)
71     : CachedResource(ResourceRequest(), ImageResource)
72     , m_image(image)
73     , m_shouldPaintBrokenImage(true)
74 {
75     setStatus(Cached);
76     setLoading(false);
77 }
78
79 CachedImage::~CachedImage()
80 {
81     clearImage();
82 }
83
84 void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
85 {
86     if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
87         CachedResource::load(cachedResourceLoader, options);
88     else
89         setLoading(false);
90 }
91
92 void CachedImage::didAddClient(CachedResourceClient* c)
93 {
94     if (m_data && !m_image && !errorOccurred()) {
95         createImage();
96         m_image->setData(m_data->sharedBuffer(), true);
97     }
98     
99     ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
100     if (m_image && !m_image->isNull())
101         static_cast<CachedImageClient*>(c)->imageChanged(this);
102
103     CachedResource::didAddClient(c);
104 }
105
106 void CachedImage::didRemoveClient(CachedResourceClient* c)
107 {
108     ASSERT(c);
109     ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
110
111     m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c));
112 #if ENABLE(SVG)
113     if (m_svgImageCache)
114         m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c));
115 #endif
116
117     CachedResource::didRemoveClient(c);
118 }
119
120 void CachedImage::switchClientsToRevalidatedResource()
121 {
122     ASSERT(resourceToRevalidate());
123     ASSERT(resourceToRevalidate()->isImage());
124     // Pending container size requests need to be transferred to the revalidated resource.
125     if (!m_pendingContainerSizeRequests.isEmpty()) {
126         // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce().
127         ContainerSizeRequests switchContainerSizeRequests;
128         for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
129             switchContainerSizeRequests.set(it->key, it->value);
130         CachedResource::switchClientsToRevalidatedResource();
131         CachedImage* revalidatedCachedImage = static_cast<CachedImage*>(resourceToRevalidate());
132         for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it)
133             revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
134         return;
135     }
136
137     CachedResource::switchClientsToRevalidatedResource();
138 }
139
140 void CachedImage::allClientsRemoved()
141 {
142     m_pendingContainerSizeRequests.clear();
143     if (m_image && !errorOccurred())
144         m_image->resetAnimation();
145 }
146
147 pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
148 {
149     if (deviceScaleFactor >= 2) {
150         DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
151         return std::make_pair(brokenImageHiRes, 2);
152     }
153
154     DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
155     return std::make_pair(brokenImageLoRes, 1);
156 }
157
158 bool CachedImage::willPaintBrokenImage() const
159 {
160     return errorOccurred() && m_shouldPaintBrokenImage;
161 }
162
163 Image* CachedImage::image()
164 {
165     ASSERT(!isPurgeable());
166
167     if (errorOccurred() && m_shouldPaintBrokenImage) {
168         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
169         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
170         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
171         return brokenImage(1).first;
172     }
173
174     if (m_image)
175         return m_image.get();
176
177     return Image::nullImage();
178 }
179
180 Image* CachedImage::imageForRenderer(const RenderObject* renderer)
181 {
182     ASSERT(!isPurgeable());
183
184     if (errorOccurred() && m_shouldPaintBrokenImage) {
185         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
186         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
187         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
188         return brokenImage(1).first;
189     }
190
191     if (!m_image)
192         return Image::nullImage();
193
194 #if ENABLE(SVG)
195     if (m_image->isSVGImage()) {
196         Image* image = m_svgImageCache->imageForRenderer(renderer);
197         if (image != Image::nullImage())
198             return image;
199     }
200 #else
201     UNUSED_PARAM(renderer);
202 #endif
203     return m_image.get();
204 }
205
206 void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const IntSize& containerSize, float containerZoom)
207 {
208     if (containerSize.isEmpty())
209         return;
210     ASSERT(renderer);
211     ASSERT(containerZoom);
212     if (!m_image) {
213         m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
214         return;
215     }
216 #if ENABLE(SVG)
217     if (!m_image->isSVGImage()) {
218         m_image->setContainerSize(containerSize);
219         return;
220     }
221
222     m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
223 #else
224     UNUSED_PARAM(containerZoom);
225     m_image->setContainerSize(containerSize);
226 #endif
227 }
228
229 bool CachedImage::usesImageContainerSize() const
230 {
231     if (m_image)
232         return m_image->usesContainerSize();
233
234     return false;
235 }
236
237 bool CachedImage::imageHasRelativeWidth() const
238 {
239     if (m_image)
240         return m_image->hasRelativeWidth();
241
242     return false;
243 }
244
245 bool CachedImage::imageHasRelativeHeight() const
246 {
247     if (m_image)
248         return m_image->hasRelativeHeight();
249
250     return false;
251 }
252
253 LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
254 {
255     ASSERT(!isPurgeable());
256
257     if (!m_image)
258         return IntSize();
259
260     LayoutSize imageSize;
261
262 #if ENABLE(CSS_IMAGE_ORIENTATION)
263     if (!renderer)
264         return IntSize();
265
266     ImageOrientationDescription orientationDescription(renderer->shouldRespectImageOrientation());
267     orientationDescription.setImageOrientationEnum(renderer->style()->imageOrientation());
268
269     if (m_image->isBitmapImage()
270         && (orientationDescription.respectImageOrientation() == RespectImageOrientation && orientationDescription.imageOrientation() != DefaultImageOrientation))
271         imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation(orientationDescription);
272 #else
273     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
274         imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation();
275 #endif
276
277 #if ENABLE(SVG)
278     else if (m_image->isSVGImage() && sizeType == UsedSize) {
279         imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
280     }
281 #endif
282     else
283         imageSize = m_image->size();
284
285     if (multiplier == 1.0f)
286         return imageSize;
287         
288     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
289     float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
290     float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
291     LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
292     imageSize.scale(widthScale, heightScale);
293     imageSize.clampToMinimumSize(minimumSize);
294     ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
295     return imageSize;
296 }
297
298 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
299 {
300     if (m_image)
301         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
302 }
303
304 void CachedImage::notifyObservers(const IntRect* changeRect)
305 {
306     CachedResourceClientWalker<CachedImageClient> w(m_clients);
307     while (CachedImageClient* c = w.next())
308         c->imageChanged(this, changeRect);
309 }
310
311 void CachedImage::checkShouldPaintBrokenImage()
312 {
313     if (!m_loader || m_loader->reachedTerminalState())
314         return;
315
316     m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(m_resourceRequest.url());
317 }
318
319 void CachedImage::clear()
320 {
321     destroyDecodedData();
322     clearImage();
323     m_pendingContainerSizeRequests.clear();
324     setEncodedSize(0);
325 }
326
327 inline void CachedImage::createImage()
328 {
329     // Create the image if it doesn't yet exist.
330     if (m_image)
331         return;
332 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
333     else if (m_response.mimeType() == "application/pdf")
334         m_image = PDFDocumentImage::create(this);
335 #endif
336 #if ENABLE(SVG)
337     else if (m_response.mimeType() == "image/svg+xml") {
338         RefPtr<SVGImage> svgImage = SVGImage::create(this);
339         m_svgImageCache = SVGImageCache::create(svgImage.get());
340         m_image = svgImage.release();
341     }
342 #endif
343     else
344         m_image = BitmapImage::create(this);
345
346     if (m_image) {
347         // Send queued container size requests.
348         if (m_image->usesContainerSize()) {
349             for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
350                 setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
351         }
352         m_pendingContainerSizeRequests.clear();
353     }
354 }
355
356 inline void CachedImage::clearImage()
357 {
358     // If our Image has an observer, it's always us so we need to clear the back pointer
359     // before dropping our reference.
360     if (m_image)
361         m_image->setImageObserver(0);
362     m_image.clear();
363 }
364
365 bool CachedImage::canBeDrawn() const
366 {
367     if (!m_image || m_image->isNull())
368         return false;
369
370     if (!m_loader || m_loader->reachedTerminalState())
371         return true;
372
373     size_t estimatedDecodedImageSize = m_image->width() * m_image->height() * 4; // no overflow check
374     return estimatedDecodedImageSize <= m_loader->frameLoader()->frame().settings().maximumDecodedImageSize();
375 }
376
377 void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data)
378 {
379     m_data = data;
380     if (!data)
381         return;
382
383     createImage();
384
385     // Have the image update its data from its internal buffer.
386     // It will not do anything now, but will delay decoding until
387     // queried for info (like size or specific image frames).
388     bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false);
389     if (!sizeAvailable)
390         return;
391
392     if (!canBeDrawn()) {
393         // There's no image to draw or its decoded size is bigger than the maximum allowed.
394         error(errorOccurred() ? status() : DecodeError);
395         if (inCache())
396             memoryCache()->remove(this);
397         return;
398     }
399
400     // Go ahead and tell our observers to try to draw.
401     // Each chunk from the network causes observers to repaint, which will
402     // force that chunk to decode.
403     // It would be nice to only redraw the decoded band of the image, but with the current design
404     // (decoding delayed until painting) that seems hard.
405     notifyObservers();
406
407     setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
408 }
409
410 void CachedImage::addDataBuffer(ResourceBuffer* data)
411 {
412     ASSERT(m_options.dataBufferingPolicy == BufferData);
413     addIncrementalDataBuffer(data);
414 }
415
416 void CachedImage::addData(const char* data, unsigned length)
417 {
418     ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
419     addIncrementalDataBuffer(ResourceBuffer::create(data, length).get());
420 }
421
422 void CachedImage::finishLoading(ResourceBuffer* data)
423 {
424     m_data = data;
425     if (!m_image && data)
426         createImage();
427
428     if (m_image)
429         m_image->setData(m_data->sharedBuffer(), true);
430
431     if (!canBeDrawn()) {
432         // There's no image to draw or its decoded size is bigger than the maximum allowed.
433         error(errorOccurred() ? status() : DecodeError);
434         if (inCache())
435             memoryCache()->remove(this);
436         return;
437     }
438
439     notifyObservers();
440     if (m_image)
441         setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
442     CachedResource::finishLoading(data);
443 }
444
445 void CachedImage::error(CachedResource::Status status)
446 {
447     checkShouldPaintBrokenImage();
448     clear();
449     CachedResource::error(status);
450     notifyObservers();
451 }
452
453 void CachedImage::responseReceived(const ResourceResponse& response)
454 {
455     if (!m_response.isNull())
456         clear();
457     CachedResource::responseReceived(response);
458 }
459
460 void CachedImage::destroyDecodedData()
461 {
462     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
463     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
464         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
465         // Invoking addClient() will reconstruct the image object.
466         m_image = 0;
467         setDecodedSize(0);
468         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
469             makePurgeable(true);
470     } else if (m_image && !errorOccurred())
471         m_image->destroyDecodedData();
472 }
473
474 void CachedImage::decodedSizeChanged(const Image* image, int delta)
475 {
476     if (!image || image != m_image)
477         return;
478     
479     setDecodedSize(decodedSize() + delta);
480 }
481
482 void CachedImage::didDraw(const Image* image)
483 {
484     if (!image || image != m_image)
485         return;
486     
487     double timeStamp = FrameView::currentPaintTimeStamp();
488     if (!timeStamp) // If didDraw is called outside of a Frame paint.
489         timeStamp = monotonicallyIncreasingTime();
490     
491     CachedResource::didAccessDecodedData(timeStamp);
492 }
493
494 bool CachedImage::shouldPauseAnimation(const Image* image)
495 {
496     if (!image || image != m_image)
497         return false;
498     
499     CachedResourceClientWalker<CachedImageClient> w(m_clients);
500     while (CachedImageClient* c = w.next()) {
501         if (c->willRenderImage(this))
502             return false;
503     }
504
505     return true;
506 }
507
508 void CachedImage::animationAdvanced(const Image* image)
509 {
510     if (!image || image != m_image)
511         return;
512     notifyObservers();
513 }
514
515 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
516 {
517     if (!image || image != m_image)
518         return;
519     notifyObservers(&rect);
520 }
521
522 void CachedImage::resumeAnimatingImagesForLoader(CachedResourceLoader* loader)
523 {
524     const CachedResourceLoader::DocumentResourceMap& resources = loader->allCachedResources();
525
526     for (CachedResourceLoader::DocumentResourceMap::const_iterator it = resources.begin(), end = resources.end(); it != end; ++it) {
527         const CachedResourceHandle<CachedResource>& resource = it->value;
528         if (!resource || !resource->isImage())
529             continue;
530         CachedImage* cachedImage = static_cast<CachedImage*>(resource.get());
531         if (!cachedImage->hasImage())
532             continue;
533         Image* image = cachedImage->image();
534         if (!image->isBitmapImage())
535             continue;
536         BitmapImage* bitmapImage = static_cast<BitmapImage*>(image);
537         if (!bitmapImage->canAnimate())
538             continue;
539         cachedImage->animationAdvanced(bitmapImage);
540     }
541 }
542
543 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
544 {
545     Image* image = imageForRenderer(renderer);
546     if (image->isBitmapImage())
547         image->nativeImageForCurrentFrame(); // force decode
548     return image->currentFrameKnownToBeOpaque();
549 }
550
551 #if ENABLE(DISK_IMAGE_CACHE)
552 bool CachedImage::canUseDiskImageCache() const
553 {
554     if (isLoading() || errorOccurred())
555         return false;
556
557     if (!m_data)
558         return false;
559
560     if (isPurgeable())
561         return false;
562
563     if (m_data->size() < diskImageCache().minimumImageSize())
564         return false;
565
566     // "Cache-Control: no-store" resources may be marked as such because they may
567     // contain sensitive information. We should not write these resources to disk.
568     if (m_response.cacheControlContainsNoStore())
569         return false;
570
571     // Testing shows that PDF images did not work when memory mapped.
572     // However, SVG images and Bitmap images were fine. See:
573     // <rdar://problem/8591834> Disk Image Cache should support PDF Images
574     if (m_response.mimeType() == "application/pdf")
575         return false;
576
577     return true;
578 }
579
580 void CachedImage::useDiskImageCache()
581 {
582     ASSERT(canUseDiskImageCache());
583     ASSERT(!isUsingDiskImageCache());
584     m_data->sharedBuffer()->allowToBeMemoryMapped();
585 }
586 #endif
587
588 bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
589 {
590     if (!image()->hasSingleSecurityOrigin())
591         return false;
592     if (passesAccessControlCheck(securityOrigin))
593         return true;
594     return !securityOrigin->taintsCanvas(response().url());
595 }
596
597 } // namespace WebCore