TEST(RawResourceTest, RevalidationSucceededForResourceWithoutBody) { ResourcePtr<Resource> resource = new RawResource(ResourceRequest("data:text/html,"), Resource::Raw); ResourceResponse response; response.setHTTPStatusCode(200); resource->responseReceived(response, nullptr); resource->finish(); memoryCache()->add(resource.get()); // Simulate a successful revalidation. resource->setRevalidatingRequest(ResourceRequest("data:text/html,")); OwnPtr<DummyClient> client = adoptPtr(new DummyClient); resource->addClient(client.get()); ResourceResponse revalidatingResponse; revalidatingResponse.setHTTPStatusCode(304); resource->responseReceived(revalidatingResponse, nullptr); EXPECT_FALSE(resource->isCacheValidator()); EXPECT_EQ(200, resource->response().httpStatusCode()); EXPECT_EQ(nullptr, resource->resourceBuffer()); EXPECT_EQ(memoryCache()->resourceForURL(KURL(ParsedURLString, "data:text/html,")), resource.get()); memoryCache()->remove(resource.get()); resource->removeClient(client.get()); EXPECT_FALSE(resource->hasClients()); EXPECT_FALSE(client->called()); EXPECT_EQ(0u, client->data().size()); }
TEST(RawResourceTest, RemoveClientDuringCallback) { ResourcePtr<Resource> raw = new RawResource(ResourceRequest("data:text/html,"), Resource::Raw); raw->setLoading(false); // Create a non-null response. ResourceResponse response = raw->response(); response.setURL(KURL(ParsedURLString, "http://600.613/")); raw->setResponse(response); EXPECT_FALSE(raw->response().isNull()); OwnPtr<DummyClient> dummyClient = adoptPtr(new DummyClient()); OwnPtr<RemovingClient> removingClient = adoptPtr(new RemovingClient(dummyClient.get())); raw->addClient(dummyClient.get()); raw->addClient(removingClient.get()); testing::runPendingTasks(); EXPECT_FALSE(raw->hasClients()); }
// Verified that when ordering a prune in a runLoop task, the prune // is deferred to the end of the task. TEST_F(MemoryCacheTest, LiveResourceEvictionAtEndOfTask) { memoryCache()->setDelayBeforeLiveDecodedPrune(0); const unsigned totalCapacity = 1; const unsigned minDeadCapacity = 0; const unsigned maxDeadCapacity = 0; memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); const char data[6] = "abcde"; ResourcePtr<Resource> cachedDeadResource = new Resource(ResourceRequest(""), Resource::Raw); cachedDeadResource->appendData(data, 3); ResourcePtr<Resource> cachedLiveResource = new FakeDecodedResource(ResourceRequest(""), Resource::Raw); MockImageResourceClient client; cachedLiveResource->addClient(&client); cachedLiveResource->appendData(data, 4); class Task1 : public WebKit::WebThread::Task { public: Task1(const ResourcePtr<Resource>& live, const ResourcePtr<Resource>& dead) : m_live(live) , m_dead(dead) { } virtual void run() OVERRIDE { // The resource size has to be nonzero for this test to be meaningful, but // we do not rely on it having any particular value. ASSERT_GT(m_live->size(), 0u); ASSERT_GT(m_dead->size(), 0u); ASSERT_EQ(0u, memoryCache()->deadSize()); ASSERT_EQ(0u, memoryCache()->liveSize()); memoryCache()->add(m_dead.get()); memoryCache()->add(m_live.get()); memoryCache()->insertInLiveDecodedResourcesList(m_live.get()); ASSERT_EQ(m_dead->size(), memoryCache()->deadSize()); ASSERT_EQ(m_live->size(), memoryCache()->liveSize()); ASSERT_GT(m_live->decodedSize(), 0u); memoryCache()->prune(); // Dead resources are pruned immediately ASSERT_EQ(m_dead->size(), memoryCache()->deadSize()); ASSERT_EQ(m_live->size(), memoryCache()->liveSize()); ASSERT_GT(m_live->decodedSize(), 0u); } private:
void ImageLoader::updateFromElement() { // Don't load images for inactive documents. We don't want to slow down the // raw HTML parsing case by loading images we don't intend to display. Document& document = m_element->document(); if (!document.isActive()) return; AtomicString attr = m_element->imageSourceURL(); if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL) return; // Do not load any image if the 'src' attribute is missing or if it is // an empty string. ResourcePtr<ImageResource> newImage = 0; if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { FetchRequest request(ResourceRequest(document.completeURL(sourceURI(attr))), element()->localName()); AtomicString crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); if (!crossOriginMode.isNull()) request.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode); if (m_loadManually) { bool autoLoadOtherImages = document.fetcher()->autoLoadImages(); document.fetcher()->setAutoLoadImages(false); newImage = new ImageResource(request.resourceRequest()); newImage->setLoading(true); document.fetcher()->m_documentResources.set(newImage->url(), newImage.get()); document.fetcher()->setAutoLoadImages(autoLoadOtherImages); } else { newImage = document.fetcher()->fetchImage(request); } // If we do not have an image here, it means that a cross-site // violation occurred, or that the image was blocked via Content // Security Policy, or the page is being dismissed. Trigger an // error event if the page is not being dismissed. if (!newImage && !pageIsBeingDismissed(&document)) { m_failedLoadURL = attr; m_hasPendingErrorEvent = true; errorEventSender().dispatchEventSoon(this); } else clearFailedLoadURL(); } else if (!attr.isNull()) { // Fire an error event if the url is empty. m_hasPendingErrorEvent = true; errorEventSender().dispatchEventSoon(this); } ImageResource* oldImage = m_image.get(); if (newImage != oldImage) { sourceImageChanged(); if (m_hasPendingBeforeLoadEvent) { beforeLoadEventSender().cancelEvent(this); m_hasPendingBeforeLoadEvent = false; } if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; } // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by // this load and we should not cancel the event. // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. if (m_hasPendingErrorEvent && newImage) { errorEventSender().cancelEvent(this); m_hasPendingErrorEvent = false; } m_image = newImage; m_hasPendingBeforeLoadEvent = !m_element->document().isImageDocument() && newImage; m_hasPendingLoadEvent = newImage; m_imageComplete = !newImage; if (newImage) { if (!m_element->document().isImageDocument()) { if (!m_element->document().hasListenerType(Document::BEFORELOAD_LISTENER)) dispatchPendingBeforeLoadEvent(); else beforeLoadEventSender().dispatchEventSoon(this); } else updateRenderer(); // If newImage is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is // dispatched. newImage->addClient(this); } else { updateRenderer(); } if (oldImage) oldImage->removeClient(this); } if (RenderImageResource* imageResource = renderImageResource()) imageResource->resetAnimation(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); }
// Verifies that HTMLImageElements are given an elevated CacheLiveResourcePriority when used to construct an ImageBitmap. // ImageBitmaps that have crop rects outside of the bounds of the HTMLImageElement do not have elevated CacheLiveResourcePriority. TEST_F(ImageBitmapTest, ImageBitmapLiveResourcePriority) { RefPtr<HTMLImageElement> imageNoCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageNoCrop = new ImageResource(ResourceRequest("http://foo.com/1"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageNoCrop->setImageResource(cachedImageNoCrop.get()); RefPtr<HTMLImageElement> imageInteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageInteriorCrop = new ImageResource(ResourceRequest("http://foo.com/2"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageInteriorCrop->setImageResource(cachedImageInteriorCrop.get()); RefPtr<HTMLImageElement> imageExteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageExteriorCrop = new ImageResource(ResourceRequest("http://foo.com/3"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageExteriorCrop->setImageResource(cachedImageExteriorCrop.get()); RefPtr<HTMLImageElement> imageOutsideCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageOutsideCrop = new ImageResource(ResourceRequest("http://foo.com/4"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageOutsideCrop->setImageResource(cachedImageOutsideCrop.get()); MockImageResourceClient mockClient1, mockClient2, mockClient3, mockClient4; cachedImageNoCrop->addClient(&mockClient1); cachedImageInteriorCrop->addClient(&mockClient2); cachedImageExteriorCrop->addClient(&mockClient3); cachedImageOutsideCrop->addClient(&mockClient4); memoryCache()->add(cachedImageNoCrop.get()); memoryCache()->add(cachedImageInteriorCrop.get()); memoryCache()->add(cachedImageExteriorCrop.get()); memoryCache()->add(cachedImageOutsideCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageNoCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageInteriorCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageExteriorCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageOutsideCrop.get()); // HTMLImageElements should default to CacheLiveResourcePriorityLow. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); RefPtrWillBeRawPtr<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); { RefPtrWillBeRawPtr<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageNoCrop.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); RefPtrWillBeRawPtr<ImageBitmap> imageBitmapInteriorCrop2 = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtrWillBeRawPtr<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageExteriorCrop.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtrWillBeRawPtr<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageOutsideCrop.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority. ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); // Stub out references to the ImageBitmaps created and force a // garbage collection to have the ImageBitmaps be collected and // destructed. imageBitmapNoCrop = nullptr; imageBitmapInteriorCrop2 = nullptr; imageBitmapExteriorCrop = nullptr; imageBitmapOutsideCrop = nullptr; Heap::collectGarbage(ThreadState::HeapPointersOnStack, Heap::ForcedForTesting); } // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); // There is still an ImageBitmap that references this image. ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); imageBitmapInteriorCrop = nullptr; }
// Verifies that HTMLImageElements are given an elevated CacheLiveResourcePriority when used to construct an ImageBitmap. // ImageBitmaps that have crop rects outside of the bounds of the HTMLImageElement do not have elevated CacheLiveResourcePriority. TEST_F(ImageBitmapTest, ImageBitmapLiveResourcePriority) { RefPtrWillBePersistent<HTMLImageElement> imageNoCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageNoCrop = new ImageResource(ResourceRequest("http://foo.com/1"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageNoCrop->setImageResource(cachedImageNoCrop.get()); RefPtrWillBePersistent<HTMLImageElement> imageInteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageInteriorCrop = new ImageResource(ResourceRequest("http://foo.com/2"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageInteriorCrop->setImageResource(cachedImageInteriorCrop.get()); RefPtrWillBePersistent<HTMLImageElement> imageExteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageExteriorCrop = new ImageResource(ResourceRequest("http://foo.com/3"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageExteriorCrop->setImageResource(cachedImageExteriorCrop.get()); RefPtrWillBePersistent<HTMLImageElement> imageOutsideCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageOutsideCrop = new ImageResource(ResourceRequest("http://foo.com/4"), BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageOutsideCrop->setImageResource(cachedImageOutsideCrop.get()); MockImageResourceClient mockClient1, mockClient2, mockClient3, mockClient4; cachedImageNoCrop->addClient(&mockClient1); cachedImageInteriorCrop->addClient(&mockClient2); cachedImageExteriorCrop->addClient(&mockClient3); cachedImageOutsideCrop->addClient(&mockClient4); memoryCache()->add(cachedImageNoCrop.get()); memoryCache()->add(cachedImageInteriorCrop.get()); memoryCache()->add(cachedImageExteriorCrop.get()); memoryCache()->add(cachedImageOutsideCrop.get()); memoryCache()->updateDecodedResource(cachedImageNoCrop.get(), UpdateForPropertyChange); memoryCache()->updateDecodedResource(cachedImageInteriorCrop.get(), UpdateForPropertyChange); memoryCache()->updateDecodedResource(cachedImageExteriorCrop.get(), UpdateForPropertyChange); memoryCache()->updateDecodedResource(cachedImageOutsideCrop.get(), UpdateForPropertyChange); // HTMLImageElements should default to CacheLiveResourcePriorityLow. ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); { RefPtrWillBePersistent<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageNoCrop.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); RefPtrWillBePersistent<ImageBitmap> imageBitmapInteriorCrop2 = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtrWillBePersistent<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageExteriorCrop.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtrWillBePersistent<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageOutsideCrop.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh. ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority. ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); } // Force a garbage collection to sweep out the local ImageBitmaps. Heap::collectGarbage(ThreadState::NoHeapPointersOnStack); // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image. ASSERT_EQ(memoryCache()->priority(imageNoCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); ASSERT_EQ(memoryCache()->priority(imageExteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); ASSERT_EQ(memoryCache()->priority(imageOutsideCrop->cachedImage()), MemoryCacheLiveResourcePriorityLow); // There is still an ImageBitmap that references this image. ASSERT_EQ(memoryCache()->priority(imageInteriorCrop->cachedImage()), MemoryCacheLiveResourcePriorityHigh); imageBitmapInteriorCrop = nullptr; }
// Verifies that HTMLImageElements are given an elevated CacheLiveResourcePriority when used to construct an ImageBitmap. // ImageBitmaps that have crop rects outside of the bounds of the HTMLImageElement do not have elevated CacheLiveResourcePriority. TEST_F(ImageBitmapTest, ImageBitmapLiveResourcePriority) { RefPtr<HTMLImageElement> imageNoCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageNoCrop = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageNoCrop->setImageResource(cachedImageNoCrop.get()); RefPtr<HTMLImageElement> imageInteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageInteriorCrop = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageInteriorCrop->setImageResource(cachedImageInteriorCrop.get()); RefPtr<HTMLImageElement> imageExteriorCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageExteriorCrop = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageExteriorCrop->setImageResource(cachedImageExteriorCrop.get()); RefPtr<HTMLImageElement> imageOutsideCrop = HTMLImageElement::create(*Document::create().get()); ResourcePtr<ImageResource> cachedImageOutsideCrop = new ImageResource(BitmapImage::create(NativeImageSkia::create(m_bitmap)).get()); imageOutsideCrop->setImageResource(cachedImageOutsideCrop.get()); MockImageResourceClient mockClient1, mockClient2, mockClient3, mockClient4; cachedImageNoCrop->addClient(&mockClient1); cachedImageInteriorCrop->addClient(&mockClient2); cachedImageExteriorCrop->addClient(&mockClient3); cachedImageOutsideCrop->addClient(&mockClient4); memoryCache()->add(cachedImageNoCrop.get()); memoryCache()->add(cachedImageInteriorCrop.get()); memoryCache()->add(cachedImageExteriorCrop.get()); memoryCache()->add(cachedImageOutsideCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageNoCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageInteriorCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageExteriorCrop.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageOutsideCrop.get()); // HTMLImageElements should default to CacheLiveResourcePriorityLow. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); RefPtr<ImageBitmap> imageBitmapInteriorCrop = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); { RefPtr<ImageBitmap> imageBitmapNoCrop = ImageBitmap::create(imageNoCrop.get(), IntRect(0, 0, m_bitmap.width(), m_bitmap.height())); RefPtr<ImageBitmap> imageBitmapInteriorCrop2 = ImageBitmap::create(imageInteriorCrop.get(), IntRect(m_bitmap.width() / 2, m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtr<ImageBitmap> imageBitmapExteriorCrop = ImageBitmap::create(imageExteriorCrop.get(), IntRect(-m_bitmap.width() / 2, -m_bitmap.height() / 2, m_bitmap.width(), m_bitmap.height())); RefPtr<ImageBitmap> imageBitmapOutsideCrop = ImageBitmap::create(imageOutsideCrop.get(), IntRect(-m_bitmap.width(), -m_bitmap.height(), m_bitmap.width(), m_bitmap.height())); // Images that are referenced by ImageBitmaps have CacheLiveResourcePriorityHigh. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); // ImageBitmaps that do not contain any of the source image do not elevate CacheLiveResourcePriority. ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); } // CacheLiveResourcePriroity should return to CacheLiveResourcePriorityLow when no ImageBitmaps reference the image. ASSERT_EQ(imageNoCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageExteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); ASSERT_EQ(imageOutsideCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityLow); // There is still an ImageBitmap that references this image. ASSERT_EQ(imageInteriorCrop->cachedImage()->cacheLiveResourcePriority(), Resource::CacheLiveResourcePriorityHigh); }
void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, UpdateFromElementBehavior updateBehavior) { // FIXME: According to // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html#the-img-element:the-img-element-55 // When "update image" is called due to environment changes and the load fails, onerror should not be called. // That is currently not the case. // // We don't need to call clearLoader here: Either we were called from the // task, or our caller updateFromElement cleared the task's loader (and set // m_pendingTask to null). m_pendingTask.clear(); // Make sure to only decrement the count when we exit this function OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter; loadDelayCounter.swap(m_loadDelayCounter); Document& document = m_element->document(); if (!document.isActive()) return; AtomicString imageSourceURL = m_element->imageSourceURL(); KURL url = imageSourceToKURL(imageSourceURL); ResourcePtr<ImageResource> newImage = 0; RefPtrWillBeRawPtr<Element> protectElement(m_element.get()); if (!url.isNull()) { // Unlike raw <img>, we block mixed content inside of <picture> or <img srcset>. ResourceLoaderOptions resourceLoaderOptions = ResourceFetcher::defaultResourceOptions(); ResourceRequest resourceRequest(url); resourceRequest.setFetchCredentialsMode(WebURLRequest::FetchCredentialsModeSameOrigin); if (updateBehavior == UpdateForcedReload) { resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReloadBypassingCache); // ImageLoader defers the load of images when in an ImageDocument. Don't defer this load on a forced reload. m_loadingImageDocument = false; } if (isHTMLPictureElement(element()->parentNode()) || !element()->fastGetAttribute(HTMLNames::srcsetAttr).isNull()) resourceRequest.setRequestContext(WebURLRequest::RequestContextImageSet); FetchRequest request(resourceRequest, element()->localName(), resourceLoaderOptions); configureRequest(request, bypassBehavior, *m_element, document.clientHintsPreferences()); // Prevent the immediate creation of a ResourceLoader (and therefore a network // request) for ImageDocument loads. In this case, the image contents have already // been requested as a main resource and ImageDocumentParser will take care of // funneling the main resource bytes into the ImageResource. if (m_loadingImageDocument) { request.setDefer(FetchRequest::DeferredByClient); request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); } newImage = ImageResource::fetch(request, document.fetcher()); if (m_loadingImageDocument && newImage) newImage->setLoading(true); if (!newImage && !pageIsBeingDismissed(&document)) { crossSiteOrCSPViolationOccurred(imageSourceURL); dispatchErrorEvent(); } else { clearFailedLoadURL(); } } else { if (!imageSourceURL.isNull()) { // Fire an error event if the url string is not empty, but the KURL is. dispatchErrorEvent(); } noImageResourceToLoad(); } ImageResource* oldImage = m_image.get(); if (updateBehavior == UpdateSizeChanged && m_element->layoutObject() && m_element->layoutObject()->isImage() && newImage == oldImage) { toLayoutImage(m_element->layoutObject())->intrinsicSizeChanged(); } else { if (newImage != oldImage) sourceImageChanged(); if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; } // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by // this load and we should not cancel the event. // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. if (m_hasPendingErrorEvent && newImage) { errorEventSender().cancelEvent(this); m_hasPendingErrorEvent = false; } m_image = newImage; m_hasPendingLoadEvent = newImage; m_imageComplete = !newImage; updateLayoutObject(); // If newImage exists and is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is dispatched. if (newImage) newImage->addClient(this); if (oldImage) oldImage->removeClient(this); } if (LayoutImageResource* imageResource = layoutImageResource()) imageResource->resetAnimation(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); }
void ImageLoader::doUpdateFromElement(UpdateFromElementBehavior updateBehavior) { // FIXME: According to // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html#the-img-element:the-img-element-55 // When "update image" is called due to environment changes and the load fails, onerror should not be called. // That is currently not the case. // // We don't need to call clearLoader here: Either we were called from the // task, or our caller updateFromElement cleared the task's loader (and set // m_pendingTask to null). m_pendingTask.clear(); // Make sure to only decrement the count when we exit this function OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter; loadDelayCounter.swap(m_loadDelayCounter); Document& document = m_element->document(); if (!document.isActive()) return; AtomicString imageSourceURL = m_element->imageSourceURL(); KURL url = imageSourceToKURL(imageSourceURL); ResourcePtr<ImageResource> newImage = 0; if (!url.isNull()) { // Unlike raw <img>, we block mixed content inside of <picture> or <img srcset>. ResourceLoaderOptions resourceLoaderOptions = ResourceFetcher::defaultResourceOptions(); ResourceRequest resourceRequest(url); FetchRequest request(ResourceRequest(url), element()->localName(), resourceLoaderOptions); newImage = document.fetcher()->fetchImage(request); if (!newImage && !pageIsBeingDismissed(&document)) crossSiteOrCSPViolationOccured(imageSourceURL); else clearFailedLoadURL(); } else if (!imageSourceURL.isNull()) { // Fire an error event if the url string is not empty, but the KURL is. m_hasPendingErrorEvent = true; errorEventSender().dispatchEventSoon(this); } ImageResource* oldImage = m_image.get(); if (newImage != oldImage) { sourceImageChanged(); if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; } // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by // this load and we should not cancel the event. // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. if (m_hasPendingErrorEvent && newImage) { errorEventSender().cancelEvent(this); m_hasPendingErrorEvent = false; } m_image = newImage; m_hasPendingLoadEvent = newImage; m_imageComplete = !newImage; updateRenderer(); // If newImage exists and is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is dispatched. if (newImage) newImage->addClient(this); if (oldImage) oldImage->removeClient(this); } else if (updateBehavior == UpdateSizeChanged && m_element->renderer() && m_element->renderer()->isImage()) { toRenderImage(m_element->renderer())->intrinsicSizeChanged(); } if (RenderImageResource* imageResource = renderImageResource()) imageResource->resetAnimation(); // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); }
// Verifies that CachedResources are evicted from the decode cache // according to their DecodeCachePriority. TEST_F(MemoryCacheTest, DecodeCacheOrder) { memoryCache()->setDelayBeforeLiveDecodedPrune(0); ResourcePtr<MockImageResource> cachedImageLowPriority = new MockImageResource(ResourceRequest(""), Resource::Raw); ResourcePtr<MockImageResource> cachedImageHighPriority = new MockImageResource(ResourceRequest(""), Resource::Raw); MockImageResourceClient clientLowPriority; MockImageResourceClient clientHighPriority; cachedImageLowPriority->addClient(&clientLowPriority); cachedImageHighPriority->addClient(&clientHighPriority); const char data[5] = "abcd"; cachedImageLowPriority->appendData(data, 1); cachedImageHighPriority->appendData(data, 4); const unsigned lowPrioritySize = cachedImageLowPriority->size(); const unsigned highPrioritySize = cachedImageHighPriority->size(); const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize(); const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize(); const unsigned totalSize = lowPrioritySize + highPrioritySize; // Verify that the sizes are different to ensure that we can test eviction order. ASSERT_GT(lowPrioritySize, 0u); ASSERT_NE(lowPrioritySize, highPrioritySize); ASSERT_GT(lowPriorityMockDecodeSize, 0u); ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), 0u); // Add the items. The item added first would normally be evicted first. memoryCache()->add(cachedImageHighPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize); memoryCache()->add(cachedImageLowPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize); // Insert all items in the decoded items list with the same priority memoryCache()->insertInLiveDecodedResourcesList(cachedImageHighPriority.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageLowPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize); // Now we will assign their priority and make sure they are moved to the correct buckets. cachedImageLowPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow); cachedImageHighPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh); // Should first prune the LowPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize); // Should prune the HighPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize); }