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 "MIMETypeRegistry.h" |
38 | #include "MemoryCache.h" |
39 | #include "RenderElement.h" |
40 | #include "SVGImage.h" |
41 | #include "SecurityOrigin.h" |
42 | #include "Settings.h" |
43 | #include "SharedBuffer.h" |
44 | #include "SubresourceLoader.h" |
45 | #include <wtf/NeverDestroyed.h> |
46 | #include <wtf/StdLibExtras.h> |
47 | |
48 | #if PLATFORM(IOS_FAMILY) |
49 | #include "SystemMemory.h" |
50 | #endif |
51 | |
52 | #if USE(CG) |
53 | #include "PDFDocumentImage.h" |
54 | #endif |
55 | |
56 | namespace WebCore { |
57 | |
58 | CachedImage::CachedImage(CachedResourceRequest&& request, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
59 | : CachedResource(WTFMove(request), Type::ImageResource, sessionID, cookieJar) |
60 | { |
61 | setStatus(Unknown); |
62 | } |
63 | |
64 | CachedImage::CachedImage(Image* image, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
65 | : CachedResource(URL(), Type::ImageResource, sessionID, cookieJar) |
66 | , m_image(image) |
67 | { |
68 | } |
69 | |
70 | CachedImage::CachedImage(const URL& url, Image* image, const PAL::SessionID& sessionID, const CookieJar* cookieJar, const String& domainForCachePartition) |
71 | : CachedResource(url, Type::ImageResource, sessionID, cookieJar) |
72 | , m_image(image) |
73 | , m_isManuallyCached(true) |
74 | { |
75 | m_resourceRequest.setDomainForCachePartition(domainForCachePartition); |
76 | |
77 | // Use the incoming URL in the response field. This ensures that code using the response directly, |
78 | // such as origin checks for security, actually see something. |
79 | m_response.setURL(url); |
80 | } |
81 | |
82 | CachedImage::~CachedImage() |
83 | { |
84 | clearImage(); |
85 | } |
86 | |
87 | void CachedImage::load(CachedResourceLoader& loader) |
88 | { |
89 | if (loader.shouldPerformImageLoad(url())) |
90 | CachedResource::load(loader); |
91 | else |
92 | setLoading(false); |
93 | } |
94 | |
95 | void CachedImage::setBodyDataFrom(const CachedResource& resource) |
96 | { |
97 | ASSERT(resource.type() == type()); |
98 | const CachedImage& image = static_cast<const CachedImage&>(resource); |
99 | |
100 | CachedResource::setBodyDataFrom(resource); |
101 | |
102 | m_image = image.m_image; |
103 | m_imageObserver = image.m_imageObserver; |
104 | if (m_imageObserver) |
105 | m_imageObserver->cachedImages().add(this); |
106 | |
107 | if (m_image && is<SVGImage>(*m_image)) |
108 | m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image)); |
109 | } |
110 | |
111 | void CachedImage::didAddClient(CachedResourceClient& client) |
112 | { |
113 | if (m_data && !m_image && !errorOccurred()) { |
114 | createImage(); |
115 | m_image->setData(m_data.copyRef(), true); |
116 | } |
117 | |
118 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
119 | if (m_image && !m_image->isNull()) |
120 | static_cast<CachedImageClient&>(client).imageChanged(this); |
121 | |
122 | if (m_image) |
123 | m_image->startAnimationAsynchronously(); |
124 | |
125 | CachedResource::didAddClient(client); |
126 | } |
127 | |
128 | void CachedImage::didRemoveClient(CachedResourceClient& client) |
129 | { |
130 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
131 | |
132 | m_pendingContainerContextRequests.remove(&static_cast<CachedImageClient&>(client)); |
133 | m_clientsWaitingForAsyncDecoding.remove(&static_cast<CachedImageClient&>(client)); |
134 | |
135 | if (m_svgImageCache) |
136 | m_svgImageCache->removeClientFromCache(&static_cast<CachedImageClient&>(client)); |
137 | |
138 | CachedResource::didRemoveClient(client); |
139 | |
140 | static_cast<CachedImageClient&>(client).didRemoveCachedImageClient(*this); |
141 | } |
142 | |
143 | bool CachedImage::isClientWaitingForAsyncDecoding(CachedImageClient& client) const |
144 | { |
145 | return m_clientsWaitingForAsyncDecoding.contains(&client); |
146 | } |
147 | |
148 | void CachedImage::addClientWaitingForAsyncDecoding(CachedImageClient& client) |
149 | { |
150 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
151 | if (m_clientsWaitingForAsyncDecoding.contains(&client)) |
152 | return; |
153 | if (!m_clients.contains(&client)) { |
154 | // If the <html> element does not have its own background specified, painting the root box |
155 | // renderer uses the style of the <body> element, see RenderView::rendererForRootBackground(). |
156 | // In this case, the client we are asked to add is the root box renderer. Since we can't add |
157 | // a client to m_clientsWaitingForAsyncDecoding unless it is one of the m_clients, we are going |
158 | // to cancel the repaint optimization we do in CachedImage::imageFrameAvailable() by adding |
159 | // all the m_clients to m_clientsWaitingForAsyncDecoding. |
160 | CachedResourceClientWalker<CachedImageClient> walker(m_clients); |
161 | while (auto* client = walker.next()) |
162 | m_clientsWaitingForAsyncDecoding.add(client); |
163 | } else |
164 | m_clientsWaitingForAsyncDecoding.add(&client); |
165 | } |
166 | |
167 | void CachedImage::removeAllClientsWaitingForAsyncDecoding() |
168 | { |
169 | if (m_clientsWaitingForAsyncDecoding.isEmpty() || !hasImage() || !is<BitmapImage>(image())) |
170 | return; |
171 | downcast<BitmapImage>(image())->stopAsyncDecodingQueue(); |
172 | for (auto* client : m_clientsWaitingForAsyncDecoding) |
173 | client->imageChanged(this); |
174 | m_clientsWaitingForAsyncDecoding.clear(); |
175 | } |
176 | |
177 | void CachedImage::switchClientsToRevalidatedResource() |
178 | { |
179 | ASSERT(is<CachedImage>(resourceToRevalidate())); |
180 | // Pending container size requests need to be transferred to the revalidated resource. |
181 | if (!m_pendingContainerContextRequests.isEmpty()) { |
182 | // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce(). |
183 | ContainerContextRequests switchContainerContextRequests; |
184 | for (auto& request : m_pendingContainerContextRequests) |
185 | switchContainerContextRequests.set(request.key, request.value); |
186 | CachedResource::switchClientsToRevalidatedResource(); |
187 | CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate()); |
188 | for (auto& request : switchContainerContextRequests) |
189 | revalidatedCachedImage.setContainerContextForClient(*request.key, request.value.containerSize, request.value.containerZoom, request.value.imageURL); |
190 | return; |
191 | } |
192 | |
193 | CachedResource::switchClientsToRevalidatedResource(); |
194 | } |
195 | |
196 | void CachedImage::allClientsRemoved() |
197 | { |
198 | m_pendingContainerContextRequests.clear(); |
199 | m_clientsWaitingForAsyncDecoding.clear(); |
200 | if (m_image && !errorOccurred()) |
201 | m_image->resetAnimation(); |
202 | } |
203 | |
204 | std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const |
205 | { |
206 | if (deviceScaleFactor >= 3) { |
207 | static NeverDestroyed<Image*> brokenImageVeryHiRes(&Image::loadPlatformResource("missingImage@3x" ).leakRef()); |
208 | return std::make_pair(brokenImageVeryHiRes, 3); |
209 | } |
210 | |
211 | if (deviceScaleFactor >= 2) { |
212 | static NeverDestroyed<Image*> brokenImageHiRes(&Image::loadPlatformResource("missingImage@2x" ).leakRef()); |
213 | return std::make_pair(brokenImageHiRes, 2); |
214 | } |
215 | |
216 | static NeverDestroyed<Image*> brokenImageLoRes(&Image::loadPlatformResource("missingImage" ).leakRef()); |
217 | return std::make_pair(brokenImageLoRes, 1); |
218 | } |
219 | |
220 | bool CachedImage::willPaintBrokenImage() const |
221 | { |
222 | return errorOccurred() && m_shouldPaintBrokenImage; |
223 | } |
224 | |
225 | Image* CachedImage::image() |
226 | { |
227 | if (errorOccurred() && m_shouldPaintBrokenImage) { |
228 | // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate |
229 | // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() |
230 | // when they need the real, deviceScaleFactor-appropriate broken image icon. |
231 | return brokenImage(1).first; |
232 | } |
233 | |
234 | if (m_image) |
235 | return m_image.get(); |
236 | |
237 | return &Image::nullImage(); |
238 | } |
239 | |
240 | Image* CachedImage::imageForRenderer(const RenderObject* renderer) |
241 | { |
242 | if (errorOccurred() && m_shouldPaintBrokenImage) { |
243 | // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate |
244 | // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() |
245 | // when they need the real, deviceScaleFactor-appropriate broken image icon. |
246 | return brokenImage(1).first; |
247 | } |
248 | |
249 | if (!m_image) |
250 | return &Image::nullImage(); |
251 | |
252 | if (m_image->isSVGImage()) { |
253 | Image* image = m_svgImageCache->imageForRenderer(renderer); |
254 | if (image != &Image::nullImage()) |
255 | return image; |
256 | } |
257 | return m_image.get(); |
258 | } |
259 | |
260 | void CachedImage::setContainerContextForClient(const CachedImageClient& client, const LayoutSize& containerSize, float containerZoom, const URL& imageURL) |
261 | { |
262 | if (containerSize.isEmpty()) |
263 | return; |
264 | ASSERT(containerZoom); |
265 | if (!m_image) { |
266 | m_pendingContainerContextRequests.set(&client, ContainerContext { containerSize, containerZoom, imageURL }); |
267 | return; |
268 | } |
269 | |
270 | if (!m_image->isSVGImage()) { |
271 | m_image->setContainerSize(containerSize); |
272 | return; |
273 | } |
274 | |
275 | m_svgImageCache->setContainerContextForClient(client, containerSize, containerZoom, imageURL); |
276 | } |
277 | |
278 | LayoutSize CachedImage::imageSizeForRenderer(const RenderElement* renderer, float multiplier, SizeType sizeType) |
279 | { |
280 | if (!m_image) |
281 | return LayoutSize(); |
282 | |
283 | LayoutSize imageSize; |
284 | |
285 | if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation) |
286 | imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation()); |
287 | else if (is<SVGImage>(*m_image) && sizeType == UsedSize) |
288 | imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer)); |
289 | else |
290 | imageSize = LayoutSize(m_image->size()); |
291 | |
292 | if (multiplier == 1.0f) |
293 | return imageSize; |
294 | |
295 | // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. |
296 | float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; |
297 | float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; |
298 | LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); |
299 | imageSize.scale(widthScale, heightScale); |
300 | imageSize.clampToMinimumSize(minimumSize); |
301 | ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); |
302 | return imageSize; |
303 | } |
304 | |
305 | void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) |
306 | { |
307 | if (m_image) |
308 | m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); |
309 | } |
310 | |
311 | void CachedImage::notifyObservers(const IntRect* changeRect) |
312 | { |
313 | CachedResourceClientWalker<CachedImageClient> w(m_clients); |
314 | while (CachedImageClient* c = w.next()) |
315 | c->imageChanged(this, changeRect); |
316 | } |
317 | |
318 | void CachedImage::checkShouldPaintBrokenImage() |
319 | { |
320 | if (!m_loader || m_loader->reachedTerminalState()) |
321 | return; |
322 | |
323 | m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url()); |
324 | } |
325 | |
326 | bool CachedImage::isPDFResource() const |
327 | { |
328 | return Image::isPDFResource(m_response.mimeType(), url()); |
329 | } |
330 | |
331 | bool CachedImage::isPostScriptResource() const |
332 | { |
333 | return Image::isPostScriptResource(m_response.mimeType(), url()); |
334 | } |
335 | |
336 | void CachedImage::clear() |
337 | { |
338 | destroyDecodedData(); |
339 | clearImage(); |
340 | m_pendingContainerContextRequests.clear(); |
341 | m_clientsWaitingForAsyncDecoding.clear(); |
342 | setEncodedSize(0); |
343 | } |
344 | |
345 | inline void CachedImage::createImage() |
346 | { |
347 | // Create the image if it doesn't yet exist. |
348 | if (m_image) |
349 | return; |
350 | |
351 | m_imageObserver = CachedImageObserver::create(*this); |
352 | |
353 | m_image = Image::create(*m_imageObserver); |
354 | |
355 | if (m_image) { |
356 | if (is<SVGImage>(*m_image)) |
357 | m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image)); |
358 | |
359 | // Send queued container size requests. |
360 | if (m_image->usesContainerSize()) { |
361 | for (auto& request : m_pendingContainerContextRequests) |
362 | setContainerContextForClient(*request.key, request.value.containerSize, request.value.containerZoom, request.value.imageURL); |
363 | } |
364 | m_pendingContainerContextRequests.clear(); |
365 | m_clientsWaitingForAsyncDecoding.clear(); |
366 | } |
367 | } |
368 | |
369 | CachedImage::CachedImageObserver::CachedImageObserver(CachedImage& image) |
370 | { |
371 | m_cachedImages.add(&image); |
372 | } |
373 | |
374 | void CachedImage::CachedImageObserver::encodedDataStatusChanged(const Image& image, EncodedDataStatus status) |
375 | { |
376 | for (auto cachedImage : m_cachedImages) |
377 | cachedImage->encodedDataStatusChanged(image, status); |
378 | } |
379 | |
380 | void CachedImage::CachedImageObserver::decodedSizeChanged(const Image& image, long long delta) |
381 | { |
382 | for (auto cachedImage : m_cachedImages) |
383 | cachedImage->decodedSizeChanged(image, delta); |
384 | } |
385 | |
386 | void CachedImage::CachedImageObserver::didDraw(const Image& image) |
387 | { |
388 | for (auto cachedImage : m_cachedImages) |
389 | cachedImage->didDraw(image); |
390 | } |
391 | |
392 | bool CachedImage::CachedImageObserver::canDestroyDecodedData(const Image& image) |
393 | { |
394 | for (auto cachedImage : m_cachedImages) { |
395 | if (&image != cachedImage->image()) |
396 | continue; |
397 | if (!cachedImage->canDestroyDecodedData(image)) |
398 | return false; |
399 | } |
400 | return true; |
401 | } |
402 | |
403 | void CachedImage::CachedImageObserver::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus) |
404 | { |
405 | for (auto cachedImage : m_cachedImages) |
406 | cachedImage->imageFrameAvailable(image, animatingState, changeRect, decodingStatus); |
407 | } |
408 | |
409 | void CachedImage::CachedImageObserver::changedInRect(const Image& image, const IntRect* rect) |
410 | { |
411 | for (auto cachedImage : m_cachedImages) |
412 | cachedImage->changedInRect(image, rect); |
413 | } |
414 | |
415 | inline void CachedImage::clearImage() |
416 | { |
417 | if (!m_image) |
418 | return; |
419 | |
420 | if (m_imageObserver) { |
421 | m_imageObserver->cachedImages().remove(this); |
422 | |
423 | if (m_imageObserver->cachedImages().isEmpty()) { |
424 | ASSERT(m_imageObserver->hasOneRef()); |
425 | m_image->setImageObserver(nullptr); |
426 | } |
427 | |
428 | m_imageObserver = nullptr; |
429 | } |
430 | |
431 | m_image = nullptr; |
432 | m_lastUpdateImageDataTime = { }; |
433 | m_updateImageDataCount = 0; |
434 | } |
435 | |
436 | void CachedImage::updateBufferInternal(SharedBuffer& data) |
437 | { |
438 | m_data = &data; |
439 | setEncodedSize(m_data->size()); |
440 | createImage(); |
441 | |
442 | // Don't update the image with the new buffer very often. Changing the decoder |
443 | // internal data and repainting the observers sometimes are very expensive operations. |
444 | if (!m_forceUpdateImageDataEnabledForTesting && shouldDeferUpdateImageData()) |
445 | return; |
446 | |
447 | EncodedDataStatus encodedDataStatus = EncodedDataStatus::Unknown; |
448 | |
449 | if (isPostScriptResource()) { |
450 | #if PLATFORM(MAC) && !USE(WEBKIT_IMAGE_DECODERS) |
451 | // Delay updating the image with the PostScript data till all the data |
452 | // is received so it can be converted to PDF data. |
453 | return; |
454 | #else |
455 | // Set the encodedDataStatus to Error so loading this image will be canceled. |
456 | encodedDataStatus = EncodedDataStatus::Error; |
457 | #endif |
458 | } else { |
459 | // Have the image update its data from its internal buffer. Decoding the image data |
460 | // will be delayed until info (like size or specific image frames) are queried which |
461 | // usually happens when the observers are repainted. |
462 | encodedDataStatus = updateImageData(false); |
463 | } |
464 | |
465 | if (encodedDataStatus > EncodedDataStatus::Error && encodedDataStatus < EncodedDataStatus::SizeAvailable) |
466 | return; |
467 | |
468 | if (encodedDataStatus == EncodedDataStatus::Error || m_image->isNull()) { |
469 | // Image decoding failed. Either we need more image data or the image data is malformed. |
470 | error(errorOccurred() ? status() : DecodeError); |
471 | if (m_loader && encodedDataStatus == EncodedDataStatus::Error) |
472 | m_loader->cancel(); |
473 | if (inCache()) |
474 | MemoryCache::singleton().remove(*this); |
475 | return; |
476 | } |
477 | |
478 | // Tell our observers to try to draw. |
479 | notifyObservers(); |
480 | } |
481 | |
482 | bool CachedImage::shouldDeferUpdateImageData() const |
483 | { |
484 | static const double updateImageDataBackoffIntervals[] = { 0, 1, 3, 6, 15 }; |
485 | unsigned interval = std::min<unsigned>(m_updateImageDataCount, 4); |
486 | |
487 | // The first time through, the chunk time will be 0 and the image will get an update. |
488 | return (MonotonicTime::now() - m_lastUpdateImageDataTime).seconds() < updateImageDataBackoffIntervals[interval]; |
489 | } |
490 | |
491 | RefPtr<SharedBuffer> CachedImage::convertedDataIfNeeded(SharedBuffer* data) const |
492 | { |
493 | if (!data || !isPostScriptResource()) |
494 | return data; |
495 | #if PLATFORM(MAC) && !USE(WEBKIT_IMAGE_DECODERS) |
496 | return SharedBuffer::create(PDFDocumentImage::convertPostScriptDataToPDF(data->createCFData()).get()); |
497 | #else |
498 | // Loading the image should have been canceled if the system does not support converting PostScript to PDF. |
499 | ASSERT_NOT_REACHED(); |
500 | return nullptr; |
501 | #endif |
502 | } |
503 | |
504 | void CachedImage::didUpdateImageData() |
505 | { |
506 | m_lastUpdateImageDataTime = MonotonicTime::now(); |
507 | ASSERT(m_updateImageDataCount < std::numeric_limits<unsigned>::max()); |
508 | ++m_updateImageDataCount; |
509 | } |
510 | |
511 | EncodedDataStatus CachedImage::updateImageData(bool allDataReceived) |
512 | { |
513 | if (!m_image || !m_data) |
514 | return EncodedDataStatus::Error; |
515 | EncodedDataStatus result = m_image->setData(m_data.get(), allDataReceived); |
516 | didUpdateImageData(); |
517 | return result; |
518 | } |
519 | |
520 | void CachedImage::updateBuffer(SharedBuffer& data) |
521 | { |
522 | ASSERT(dataBufferingPolicy() == DataBufferingPolicy::BufferData); |
523 | updateBufferInternal(data); |
524 | CachedResource::updateBuffer(data); |
525 | } |
526 | |
527 | void CachedImage::updateData(const char* data, unsigned length) |
528 | { |
529 | ASSERT(dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData); |
530 | updateBufferInternal(SharedBuffer::create(data, length)); |
531 | CachedResource::updateData(data, length); |
532 | } |
533 | |
534 | void CachedImage::finishLoading(SharedBuffer* data) |
535 | { |
536 | m_data = convertedDataIfNeeded(data); |
537 | if (m_data) { |
538 | setEncodedSize(m_data->size()); |
539 | createImage(); |
540 | } |
541 | |
542 | EncodedDataStatus encodedDataStatus = updateImageData(true); |
543 | |
544 | if (encodedDataStatus == EncodedDataStatus::Error || m_image->isNull()) { |
545 | // Image decoding failed; the image data is malformed. |
546 | error(errorOccurred() ? status() : DecodeError); |
547 | if (inCache()) |
548 | MemoryCache::singleton().remove(*this); |
549 | return; |
550 | } |
551 | |
552 | notifyObservers(); |
553 | CachedResource::finishLoading(data); |
554 | } |
555 | |
556 | void CachedImage::didReplaceSharedBufferContents() |
557 | { |
558 | if (m_image) { |
559 | // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer. |
560 | // FIXME(rdar://problem/24275617): It would be better if we could somehow tell the Image's decoder to swap in the new contents without destroying anything. |
561 | m_image->destroyDecodedData(true); |
562 | } |
563 | CachedResource::didReplaceSharedBufferContents(); |
564 | } |
565 | |
566 | void CachedImage::error(CachedResource::Status status) |
567 | { |
568 | checkShouldPaintBrokenImage(); |
569 | clear(); |
570 | CachedResource::error(status); |
571 | notifyObservers(); |
572 | } |
573 | |
574 | void CachedImage::responseReceived(const ResourceResponse& response) |
575 | { |
576 | if (!m_response.isNull()) |
577 | clear(); |
578 | CachedResource::responseReceived(response); |
579 | } |
580 | |
581 | void CachedImage::destroyDecodedData() |
582 | { |
583 | bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); |
584 | if (canDeleteImage && !isLoading() && !hasClients()) { |
585 | m_image = nullptr; |
586 | setDecodedSize(0); |
587 | } else if (m_image && !errorOccurred()) |
588 | m_image->destroyDecodedData(); |
589 | } |
590 | |
591 | void CachedImage::encodedDataStatusChanged(const Image& image, EncodedDataStatus) |
592 | { |
593 | if (&image != m_image) |
594 | return; |
595 | |
596 | notifyObservers(); |
597 | } |
598 | |
599 | void CachedImage::decodedSizeChanged(const Image& image, long long delta) |
600 | { |
601 | if (&image != m_image) |
602 | return; |
603 | |
604 | ASSERT(delta >= 0 || decodedSize() + delta >= 0); |
605 | setDecodedSize(static_cast<unsigned>(decodedSize() + delta)); |
606 | } |
607 | |
608 | void CachedImage::didDraw(const Image& image) |
609 | { |
610 | if (&image != m_image) |
611 | return; |
612 | |
613 | MonotonicTime timeStamp = FrameView::currentPaintTimeStamp(); |
614 | if (!timeStamp) // If didDraw is called outside of a Frame paint. |
615 | timeStamp = MonotonicTime::now(); |
616 | |
617 | CachedResource::didAccessDecodedData(timeStamp); |
618 | } |
619 | |
620 | bool CachedImage::canDestroyDecodedData(const Image& image) |
621 | { |
622 | if (&image != m_image) |
623 | return false; |
624 | |
625 | CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); |
626 | while (CachedImageClient* client = clientWalker.next()) { |
627 | if (!client->canDestroyDecodedData()) |
628 | return false; |
629 | } |
630 | |
631 | return true; |
632 | } |
633 | |
634 | void CachedImage::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus) |
635 | { |
636 | if (&image != m_image) |
637 | return; |
638 | |
639 | CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); |
640 | VisibleInViewportState visibleState = VisibleInViewportState::No; |
641 | |
642 | while (CachedImageClient* client = clientWalker.next()) { |
643 | // All the clients of animated images have to be notified. The new frame has to be drawn in all of them. |
644 | if (animatingState == ImageAnimatingState::No && !m_clientsWaitingForAsyncDecoding.contains(client)) |
645 | continue; |
646 | if (client->imageFrameAvailable(*this, animatingState, changeRect) == VisibleInViewportState::Yes) |
647 | visibleState = VisibleInViewportState::Yes; |
648 | } |
649 | |
650 | if (visibleState == VisibleInViewportState::No && animatingState == ImageAnimatingState::Yes) |
651 | m_image->stopAnimation(); |
652 | |
653 | if (decodingStatus != DecodingStatus::Partial) |
654 | m_clientsWaitingForAsyncDecoding.clear(); |
655 | } |
656 | |
657 | void CachedImage::changedInRect(const Image& image, const IntRect* rect) |
658 | { |
659 | if (&image != m_image) |
660 | return; |
661 | notifyObservers(rect); |
662 | } |
663 | |
664 | bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer) |
665 | { |
666 | Image* image = imageForRenderer(renderer); |
667 | return image->currentFrameKnownToBeOpaque(); |
668 | } |
669 | |
670 | bool CachedImage::isOriginClean(SecurityOrigin* origin) |
671 | { |
672 | ASSERT_UNUSED(origin, origin); |
673 | ASSERT(this->origin()); |
674 | ASSERT(origin->toString() == this->origin()->toString()); |
675 | return !loadFailedOrCanceled() && isCORSSameOrigin(); |
676 | } |
677 | |
678 | CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const |
679 | { |
680 | if (UNLIKELY(isManuallyCached())) { |
681 | // Do not revalidate manually cached images. This mechanism is used as a |
682 | // way to efficiently share an image from the client to content and |
683 | // the URL for that image may not represent a resource that can be |
684 | // retrieved by standard means. If the manual caching SPI is used, it is |
685 | // incumbent on the client to only use valid resources. |
686 | return RevalidationDecision::No; |
687 | } |
688 | return CachedResource::makeRevalidationDecision(cachePolicy); |
689 | } |
690 | |
691 | } // namespace WebCore |
692 | |