blob: ec34e518f7d73493e7e24ac3cf405fa551e0a823 [file] [log] [blame]
/*
* Copyright (c) 2013, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/loader/resource/image_resource.h"
#include <memory>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/loader/resource/mock_image_resource_observer.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
#include "third_party/blink/renderer/platform/graphics/image.h"
#include "third_party/blink/renderer/platform/instance_counters.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
#include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
#include "third_party/blink/renderer/platform/shared_buffer.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/scoped_mocked_url.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/base64.h"
namespace blink {
using test::ScopedMockedURLLoad;
namespace {
// An image of size 1x1.
constexpr unsigned char kJpegImage[] = {
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x13,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68,
0x20, 0x47, 0x49, 0x4d, 0x50, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x05, 0x03,
0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06,
0x07, 0x0c, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09, 0x0c,
0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, 0x11, 0x11, 0x13, 0x16, 0x1c, 0x17,
0x13, 0x14, 0x1a, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1a, 0x1d, 0x1d,
0x1f, 0x1f, 0x1f, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1e, 0x24, 0x1c, 0x1e,
0x1f, 0x1e, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x05, 0x05, 0x05, 0x07, 0x06,
0x07, 0x0e, 0x08, 0x08, 0x0e, 0x1e, 0x14, 0x11, 0x14, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0xff,
0xc0, 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x22, 0x00,
0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x15, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xc4, 0x00, 0x14, 0x10, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f,
0x00, 0xb2, 0xc0, 0x07, 0xff, 0xd9};
constexpr int kJpegImageWidth = 1;
constexpr int kJpegImageHeight = 1;
constexpr size_t kJpegImageSubrangeWithDimensionsLength =
sizeof(kJpegImage) - 1;
constexpr size_t kJpegImageSubrangeWithoutDimensionsLength = 3;
// Ensure that the image decoder can determine the dimensions of kJpegImage from
// just the first kJpegImageSubrangeWithDimensionsLength bytes. If this test
// fails, then the test data here probably needs to be updated.
TEST(ImageResourceTest, DimensionsDecodableFromPartialTestImage) {
scoped_refptr<Image> image = BitmapImage::Create();
EXPECT_EQ(
Image::kSizeAvailable,
image->SetData(SharedBuffer::Create(
kJpegImage, kJpegImageSubrangeWithDimensionsLength),
true));
EXPECT_TRUE(image->IsBitmapImage());
EXPECT_EQ(1, image->width());
EXPECT_EQ(1, image->height());
}
// An image of size 50x50.
constexpr unsigned char kJpegImage2[] = {
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0x00, 0x43, 0x01, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x32, 0x03,
0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,
0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x10,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x15, 0x01, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03,
0x11, 0x00, 0x3f, 0x00, 0x00, 0x94, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xd9};
constexpr char kSvgImage[] =
"<svg width=\"200\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
"<rect x=\"0\" y=\"0\" width=\"100px\" height=\"100px\" fill=\"red\"/>"
"</svg>";
constexpr char kSvgImage2[] =
"<svg width=\"300\" height=\"300\" xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
"<rect x=\"0\" y=\"0\" width=\"200px\" height=\"200px\" fill=\"green\"/>"
"</svg>";
constexpr char kTestURL[] = "http://www.test.com/cancelTest.html";
String GetTestFilePath() {
return test::CoreTestDataPath("cancelTest.html");
}
constexpr char kSvgImageWithSubresource[] =
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"198\" height=\"100\">"
"<style>"
" <![CDATA[@font-face{font-family:\"test\"; "
" src:url('data:font/ttf;base64,invalidFontData');}]]>"
"</style>"
"<text x=\"50\" y=\"50\" font-family=\"test\" font-size=\"16\">Fox</text>"
"</svg>";
void ReceiveResponse(ImageResource* image_resource,
const KURL& url,
const AtomicString& mime_type,
const char* data,
size_t data_size) {
ResourceResponse resource_response(url);
resource_response.SetMimeType(mime_type);
resource_response.SetHTTPStatusCode(200);
image_resource->NotifyStartLoad();
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(data, data_size);
image_resource->FinishForTest();
}
void TestThatReloadIsStartedThenServeReload(
const KURL& test_url,
ImageResource* image_resource,
ImageResourceContent* content,
MockImageResourceObserver* observer,
bool placeholder_before_reload) {
const char* data = reinterpret_cast<const char*>(kJpegImage2);
constexpr size_t kDataLength = sizeof(kJpegImage2);
constexpr int kImageWidth = 50;
constexpr int kImageHeight = 50;
// Checks that |imageResource| and |content| are ready for non-placeholder
// reloading.
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
EXPECT_FALSE(image_resource->ResourceBuffer());
EXPECT_EQ(placeholder_before_reload, image_resource->ShouldShowPlaceholder());
EXPECT_EQ(g_null_atom,
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_EQ(content, image_resource->GetContent());
EXPECT_FALSE(content->HasImage());
// Checks |observer| before reloading.
const int original_image_changed_count = observer->ImageChangedCount();
const bool already_notified_finish = observer->ImageNotifyFinishedCalled();
const int image_width_on_image_notify_finished =
observer->ImageWidthOnImageNotifyFinished();
ASSERT_NE(kImageWidth, image_width_on_image_notify_finished);
// Does Reload.
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(kDataLength);
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(data, kDataLength);
image_resource->Loader()->DidFinishLoading(
TimeTicks(), kDataLength, kDataLength, kDataLength, false,
std::vector<network::cors::PreflightTimingInfo>());
// Checks |imageResource|'s status after reloading.
EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
EXPECT_FALSE(image_resource->ErrorOccurred());
EXPECT_EQ(kDataLength, image_resource->EncodedSize());
// Checks |observer| after reloading that it is notified of updates/finish.
EXPECT_LT(original_image_changed_count, observer->ImageChangedCount());
EXPECT_EQ(kImageWidth, observer->ImageWidthOnLastImageChanged());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
if (!already_notified_finish) {
// If imageNotifyFinished() has not been called before the reloaded
// response is served, then imageNotifyFinished() should be called with
// the new image (of width |imageWidth|).
EXPECT_EQ(kImageWidth, observer->ImageWidthOnImageNotifyFinished());
}
// Checks |content| receives the correct image.
EXPECT_TRUE(content->HasImage());
EXPECT_FALSE(content->GetImage()->IsNull());
EXPECT_EQ(kImageWidth, content->GetImage()->width());
EXPECT_EQ(kImageHeight, content->GetImage()->height());
EXPECT_FALSE(content->GetImage()->PaintImageForCurrentFrame().is_multipart());
}
AtomicString BuildContentRange(size_t range_length, size_t total_length) {
return AtomicString(String("bytes 0-" + String::Number(range_length - 1) +
"/" + String::Number(total_length)));
}
void TestThatIsPlaceholderRequestAndServeResponse(
const KURL& url,
ImageResource* image_resource,
MockImageResourceObserver* observer) {
// Checks that |imageResource| is requesting for placeholder.
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_EQ(0, observer->ImageChangedCount());
// Serves partial response that is sufficient for creating a placeholder.
ResourceResponse resource_response(url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(
kJpegImageSubrangeWithDimensionsLength);
resource_response.SetHTTPStatusCode(206);
resource_response.SetHTTPHeaderField(
"content-range", BuildContentRange(kJpegImageSubrangeWithDimensionsLength,
sizeof(kJpegImage)));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage),
kJpegImageSubrangeWithDimensionsLength);
image_resource->Loader()->DidFinishLoading(
TimeTicks(), kJpegImageSubrangeWithDimensionsLength,
kJpegImageSubrangeWithDimensionsLength,
kJpegImageSubrangeWithDimensionsLength, false,
std::vector<network::cors::PreflightTimingInfo>());
// Checks that |imageResource| is successfully loaded, showing a placeholder.
EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
EXPECT_EQ(kJpegImageSubrangeWithDimensionsLength,
image_resource->EncodedSize());
EXPECT_LT(0, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// A placeholder image.
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsSVGImage());
}
void TestThatIsNotPlaceholderRequestAndServeResponse(
const KURL& url,
ImageResource* image_resource,
MockImageResourceObserver* observer) {
// Checks that |imageResource| is NOT requesting for placeholder.
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
EXPECT_EQ(g_null_atom,
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_EQ(0, observer->ImageChangedCount());
// Serves full response.
ResourceResponse resource_response(url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
image_resource->Loader()->DidFinishLoading(
TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
false, std::vector<network::cors::PreflightTimingInfo>());
// Checks that |imageResource| is successfully loaded,
// showing a non-placeholder image.
EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
EXPECT_EQ(sizeof(kJpegImage), image_resource->EncodedSize());
EXPECT_LT(0, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// A non-placeholder bitmap image.
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsSVGImage());
}
ResourceFetcher* CreateFetcher() {
auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
return MakeGarbageCollected<ResourceFetcher>(
ResourceFetcherInit(*properties, MakeGarbageCollected<MockFetchContext>(),
base::MakeRefCounted<scheduler::FakeTaskRunner>()));
}
TEST(ImageResourceTest, MultipartImage) {
ResourceFetcher* fetcher = CreateFetcher();
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
// Emulate starting a real load, but don't expect any "real"
// WebURLLoaderClient callbacks.
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
image_resource->SetIdentifier(CreateUniqueIdentifier());
fetcher->StartLoad(image_resource);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
// Send the multipart response. No image or data buffer is created. Note that
// the response must be routed through ResourceLoader to ensure the load is
// flagged as multipart.
ResourceResponse multipart_response(NullURL());
multipart_response.SetMimeType("multipart/x-mixed-replace");
multipart_response.SetHTTPHeaderField(
http_names::kContentType, "multipart/x-mixed-replace; boundary=boundary");
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(multipart_response), nullptr);
EXPECT_FALSE(image_resource->ResourceBuffer());
EXPECT_FALSE(image_resource->GetContent()->HasImage());
EXPECT_EQ(0, observer->ImageChangedCount());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ("multipart/x-mixed-replace",
image_resource->GetResponse().MimeType());
const char kFirstPart[] =
"--boundary\n"
"Content-Type: image/svg+xml\n\n";
image_resource->AppendData(kFirstPart, strlen(kFirstPart));
// Send the response for the first real part. No image or data buffer is
// created.
EXPECT_FALSE(image_resource->ResourceBuffer());
EXPECT_FALSE(image_resource->GetContent()->HasImage());
EXPECT_EQ(0, observer->ImageChangedCount());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ("image/svg+xml", image_resource->GetResponse().MimeType());
const char kSecondPart[] =
"<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><rect "
"width='1' height='1' fill='green'/></svg>\n";
// The first bytes arrive. The data buffer is created, but no image is
// created.
image_resource->AppendData(kSecondPart, strlen(kSecondPart));
EXPECT_TRUE(image_resource->ResourceBuffer());
EXPECT_FALSE(image_resource->GetContent()->HasImage());
EXPECT_EQ(0, observer->ImageChangedCount());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
// Add an observer to check an assertion error doesn't happen
// (crbug.com/630983).
std::unique_ptr<MockImageResourceObserver> observer2 =
MockImageResourceObserver::Create(image_resource->GetContent());
EXPECT_EQ(0, observer2->ImageChangedCount());
EXPECT_FALSE(observer2->ImageNotifyFinishedCalled());
const char kThirdPart[] = "--boundary";
image_resource->AppendData(kThirdPart, strlen(kThirdPart));
ASSERT_TRUE(image_resource->ResourceBuffer());
EXPECT_EQ(strlen(kSecondPart) - 1, image_resource->ResourceBuffer()->size());
// This part finishes. The image is created, callbacks are sent, and the data
// buffer is cleared.
image_resource->Loader()->DidFinishLoading(
TimeTicks(), 0, 0, 0, false,
std::vector<network::cors::PreflightTimingInfo>());
EXPECT_TRUE(image_resource->ResourceBuffer());
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsSVGImage());
EXPECT_TRUE(image_resource->GetContent()
->GetImage()
->PaintImageForCurrentFrame()
.is_multipart());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(1, observer2->ImageChangedCount());
EXPECT_TRUE(observer2->ImageNotifyFinishedCalled());
}
TEST(ImageResourceTest, BitmapMultipartImage) {
ResourceFetcher* fetcher = CreateFetcher();
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ImageResource* image_resource =
ImageResource::Create(ResourceRequest(test_url));
image_resource->SetIdentifier(CreateUniqueIdentifier());
fetcher->StartLoad(image_resource);
ResourceResponse multipart_response(NullURL());
multipart_response.SetMimeType("multipart/x-mixed-replace");
multipart_response.SetHTTPHeaderField(
http_names::kContentType, "multipart/x-mixed-replace; boundary=boundary");
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(multipart_response), nullptr);
EXPECT_FALSE(image_resource->GetContent()->HasImage());
const char kBoundary[] = "--boundary\n";
const char kContentType[] = "Content-Type: image/jpeg\n\n";
image_resource->AppendData(kBoundary, strlen(kBoundary));
image_resource->AppendData(kContentType, strlen(kContentType));
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
image_resource->AppendData(kBoundary, strlen(kBoundary));
image_resource->Loader()->DidFinishLoading(
TimeTicks(), 0, 0, 0, false,
std::vector<network::cors::PreflightTimingInfo>());
EXPECT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_TRUE(image_resource->GetContent()
->GetImage()
->PaintImageForCurrentFrame()
.is_multipart());
}
TEST(ImageResourceTest, CancelOnRemoveObserver) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
scheduler::FakeTaskRunner* task_runner =
static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get());
task_runner->SetTime(1);
// Emulate starting a real load.
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
image_resource->SetIdentifier(CreateUniqueIdentifier());
fetcher->StartLoad(image_resource);
GetMemoryCache()->Add(image_resource);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
// The load should still be alive, but a timer should be started to cancel the
// load inside removeClient().
observer->RemoveAsObserver();
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
EXPECT_TRUE(GetMemoryCache()->ResourceForURL(test_url));
// Trigger the cancel timer, ensure the load was cancelled and the resource
// was evicted from the cache.
task_runner->RunUntilIdle();
EXPECT_EQ(ResourceStatus::kLoadError, image_resource->GetStatus());
EXPECT_FALSE(GetMemoryCache()->ResourceForURL(test_url));
}
class MockFinishObserver : public GarbageCollectedFinalized<MockFinishObserver>,
public ResourceFinishObserver {
USING_GARBAGE_COLLECTED_MIXIN(MockFinishObserver);
public:
static MockFinishObserver* Create() {
return
MakeGarbageCollected<testing::StrictMock<MockFinishObserver>>();
}
MOCK_METHOD0(NotifyFinished, void());
String DebugName() const override { return "MockFinishObserver"; }
void Trace(blink::Visitor* visitor) override {
blink::ResourceFinishObserver::Trace(visitor);
}
protected:
MockFinishObserver() = default;
};
TEST(ImageResourceTest, CancelWithImageAndFinishObserver) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
// Emulate starting a real load.
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
image_resource->SetIdentifier(CreateUniqueIdentifier());
fetcher->StartLoad(image_resource);
GetMemoryCache()->Add(image_resource);
Persistent<MockFinishObserver> finish_observer = MockFinishObserver::Create();
image_resource->AddFinishObserver(finish_observer,
fetcher->GetTaskRunner().get());
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
// This shouldn't crash. crbug.com/701723
image_resource->Loader()->Cancel();
EXPECT_EQ(ResourceStatus::kLoadError, image_resource->GetStatus());
EXPECT_FALSE(GetMemoryCache()->ResourceForURL(test_url));
// ResourceFinishObserver is notified asynchronously.
EXPECT_CALL(*finish_observer, NotifyFinished());
blink::test::RunPendingTasks();
}
TEST(ImageResourceTest, DecodedDataRemainsWhileHasClients) {
ImageResource* image_resource = ImageResource::CreateForTest(NullURL());
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("multipart/x-mixed-replace");
image_resource->ResponseReceived(resource_response, nullptr);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_NE(0u, image_resource->EncodedSizeMemoryUsageForTesting());
image_resource->FinishForTest();
EXPECT_EQ(0u, image_resource->EncodedSizeMemoryUsageForTesting());
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
// The prune comes when the ImageResource still has observers. The image
// should not be deleted.
image_resource->Prune();
EXPECT_TRUE(image_resource->IsAlive());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
// The ImageResource no longer has observers. The decoded image data should be
// deleted by prune.
observer->RemoveAsObserver();
image_resource->Prune();
EXPECT_FALSE(image_resource->IsAlive());
EXPECT_TRUE(image_resource->GetContent()->HasImage());
// TODO(hajimehoshi): Should check imageResource doesn't have decoded image
// data.
}
TEST(ImageResourceTest, UpdateBitmapImages) {
ImageResource* image_resource = ImageResource::CreateForTest(NullURL());
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
image_resource->FinishForTest();
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
}
class ImageResourceReloadTest
: public testing::TestWithParam<bool>,
private ScopedClientPlaceholdersForServerLoFiForTest {
public:
ImageResourceReloadTest()
: ScopedClientPlaceholdersForServerLoFiForTest(GetParam()) {}
~ImageResourceReloadTest() override = default;
bool IsClientPlaceholderForServerLoFiEnabled() const { return GetParam(); }
void SetUp() override {
}
};
TEST_P(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderAfterFinished) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceFetcher* fetcher = CreateFetcher();
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
resource_response.AddHTTPHeaderField("chrome-proxy-content-transform",
"empty-image");
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
image_resource->FinishForTest();
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
// The observer should have been notified that the image load completed.
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
EXPECT_NE(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->ShouldShowPlaceholder());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// Call reloadIfLoFiOrPlaceholderImage() after the image has finished loading.
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
EXPECT_EQ(3, observer->ImageChangedCount());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST_P(ImageResourceReloadTest,
ReloadIfLoFiOrPlaceholderAfterFinishedWithOldHeaders) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceFetcher* fetcher = CreateFetcher();
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
resource_response.AddHTTPHeaderField("chrome-proxy", "q=low");
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
image_resource->FinishForTest();
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
// The observer should have been notified that the image load completed.
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
EXPECT_NE(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->ShouldShowPlaceholder());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// Call reloadIfLoFiOrPlaceholderImage() after the image has finished loading.
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
EXPECT_EQ(3, observer->ImageChangedCount());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST_P(ImageResourceReloadTest,
ReloadIfLoFiOrPlaceholderAfterFinishedWithoutLoFiHeaders) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest request(test_url);
request.SetPreviewsState(WebURLRequest::kServerLoFiOn);
request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
ImageResource* image_resource = ImageResource::Create(request);
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceFetcher* fetcher = CreateFetcher();
// Send the image response, without any LoFi image response headers.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
image_resource->ResponseReceived(resource_response, nullptr);
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
image_resource->FinishForTest();
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
// The observer should have been notified that the image load completed.
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// Call reloadIfLoFiOrPlaceholderImage() after the image has finished loading.
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
// The image should not have been reloaded, since it didn't have the LoFi
// image response headers.
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(image_resource->IsLoaded());
}
TEST_P(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderViaResourceFetcher) {
ResourceFetcher* fetcher = CreateFetcher();
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest request = ResourceRequest(test_url);
request.SetPreviewsState(WebURLRequest::kServerLoFiOn);
FetchParameters fetch_params(request);
ImageResource* image_resource = ImageResource::Fetch(fetch_params, fetcher);
ImageResourceContent* content = image_resource->GetContent();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(content);
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
resource_response.AddHTTPHeaderField("chrome-proxy-content-transform",
"empty-image");
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
image_resource->Loader()->DidFinishLoading(
TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
false, std::vector<network::cors::PreflightTimingInfo>());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(image_resource, fetcher->CachedResource(test_url));
fetcher->ReloadLoFiImages();
EXPECT_EQ(3, observer->ImageChangedCount());
TestThatReloadIsStartedThenServeReload(test_url, image_resource, content,
observer.get(), false);
GetMemoryCache()->Remove(image_resource);
}
TEST_P(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderBeforeResponse) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest request(test_url);
request.SetPreviewsState(WebURLRequest::kServerLoFiOn);
FetchParameters fetch_params(request);
ResourceFetcher* fetcher = CreateFetcher();
ImageResource* image_resource = ImageResource::Fetch(fetch_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
EXPECT_FALSE(image_resource->ErrorOccurred());
EXPECT_EQ(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->ShouldShowPlaceholder());
// Call reloadIfLoFiOrPlaceholderImage() while the image is still loading.
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_EQ(0, observer->ImageWidthOnLastImageChanged());
// The observer should not have been notified of completion yet, since the
// image is still loading.
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST_P(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderDuringResponse) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest request(test_url);
request.SetPreviewsState(WebURLRequest::kServerLoFiOn);
FetchParameters fetch_params(request);
ResourceFetcher* fetcher = CreateFetcher();
ImageResource* image_resource = ImageResource::Fetch(fetch_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
// Send the image response.
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
resource_response.AddHTTPHeaderField("chrome-proxy-content-transform",
"empty-image");
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(IsClientPlaceholderForServerLoFiEnabled(),
image_resource->ShouldShowPlaceholder());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
// Call reloadIfLoFiOrPlaceholderImage() while the image is still loading.
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_EQ(0, observer->ImageWidthOnLastImageChanged());
// The observer should not have been notified of completion yet, since the
// image is still loading.
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST_P(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderForPlaceholder) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, fetcher);
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
TestThatIsPlaceholderRequestAndServeResponse(test_url, image_resource,
observer.get());
image_resource->ReloadIfLoFiOrPlaceholderImage(fetcher,
Resource::kReloadAlways);
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST_P(ImageResourceReloadTest, ReloadLoFiImagesWithDuplicateURLs) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters placeholder_params{ResourceRequest(test_url)};
placeholder_params.SetAllowImagePlaceholder();
ImageResource* placeholder_resource =
ImageResource::Fetch(placeholder_params, fetcher);
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
placeholder_params.GetImageRequestOptimization());
EXPECT_TRUE(placeholder_resource->ShouldShowPlaceholder());
FetchParameters full_image_params{ResourceRequest(test_url)};
ImageResource* full_image_resource =
ImageResource::Fetch(full_image_params, fetcher);
EXPECT_EQ(FetchParameters::kNone,
full_image_params.GetImageRequestOptimization());
EXPECT_FALSE(full_image_resource->ShouldShowPlaceholder());
// The |placeholder_resource| should not be reused for the
// |full_image_resource|.
EXPECT_NE(placeholder_resource, full_image_resource);
fetcher->ReloadLoFiImages();
EXPECT_FALSE(placeholder_resource->ShouldShowPlaceholder());
EXPECT_FALSE(full_image_resource->ShouldShowPlaceholder());
}
INSTANTIATE_TEST_CASE_P(/* no prefix */,
ImageResourceReloadTest,
testing::Bool());
TEST(ImageResourceTest, SVGImage) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
strlen(kSvgImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
}
TEST(ImageResourceTest, SVGImageWithSubresource) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/svg+xml",
kSvgImageWithSubresource, strlen(kSvgImageWithSubresource));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
// At this point, image is (mostly) available but the loading is not yet
// finished because of SVG's subresources, and thus ImageChanged() or
// ImageNotifyFinished() are not called.
EXPECT_EQ(ResourceStatus::kPending,
image_resource->GetContent()->GetContentStatus());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(198, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(100, image_resource->GetContent()->GetImage()->height());
// A new client added here shouldn't notified of finish.
std::unique_ptr<MockImageResourceObserver> observer2 =
MockImageResourceObserver::Create(image_resource->GetContent());
EXPECT_EQ(1, observer2->ImageChangedCount());
EXPECT_FALSE(observer2->ImageNotifyFinishedCalled());
// After asynchronous tasks are executed, the loading of SVG document is
// completed and ImageNotifyFinished() is called.
test::RunPendingTasks();
EXPECT_EQ(ResourceStatus::kCached,
image_resource->GetContent()->GetContentStatus());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(2, observer2->ImageChangedCount());
EXPECT_TRUE(observer2->ImageNotifyFinishedCalled());
EXPECT_EQ(198, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(100, image_resource->GetContent()->GetImage()->height());
GetMemoryCache()->EvictResources();
}
TEST(ImageResourceTest, SuccessfulRevalidationJpeg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ResourceResponse resource_response(url);
resource_response.SetHTTPStatusCode(304);
image_resource->ResponseReceived(resource_response, nullptr);
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, SuccessfulRevalidationSvg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
strlen(kSvgImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ResourceResponse resource_response(url);
resource_response.SetHTTPStatusCode(304);
image_resource->ResponseReceived(resource_response, nullptr);
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, FailedRevalidationJpegToJpeg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage2),
sizeof(kJpegImage2));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(4, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(50, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(50, image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, FailedRevalidationJpegToSvg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
strlen(kSvgImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(3, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, FailedRevalidationSvgToJpeg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
strlen(kSvgImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(3, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, FailedRevalidationSvgToSvg) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
strlen(kSvgImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
image_resource->SetRevalidatingRequest(ResourceRequest(url));
ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage2,
strlen(kSvgImage2));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(300, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(300, image_resource->GetContent()->GetImage()->height());
}
// Tests for pruning.
TEST(ImageResourceTest, Prune) {
KURL url("http://127.0.0.1:8000/foo");
ImageResource* image_resource = ImageResource::CreateForTest(url);
ReceiveResponse(image_resource, url, "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
EXPECT_FALSE(image_resource->IsAlive());
image_resource->Prune();
EXPECT_TRUE(image_resource->GetContent()->HasImage());
blink::test::RunPendingTasks();
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, CancelOnDecodeError) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters params{ResourceRequest(test_url)};
ImageResource* image_resource = ImageResource::Fetch(params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(18);
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response), nullptr);
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidReceiveData("notactuallyanimage", 18);
EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(ResourceStatus::kDecodeError,
observer->StatusOnImageNotifyFinished());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_FALSE(image_resource->IsLoading());
}
TEST(ImageResourceTest, DecodeErrorWithEmptyBody) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters params{ResourceRequest(test_url)};
ImageResource* image_resource = ImageResource::Fetch(params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response), nullptr);
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidFinishLoading(
TimeTicks(), 0, 0, 0, false,
std::vector<network::cors::PreflightTimingInfo>());
EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(ResourceStatus::kDecodeError,
observer->StatusOnImageNotifyFinished());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_FALSE(image_resource->IsLoading());
}
// Testing DecodeError that occurs in didFinishLoading().
// This is similar to DecodeErrorWithEmptyBody, but with non-empty body.
TEST(ImageResourceTest, PartialContentWithoutDimensions) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest resource_request(test_url);
resource_request.SetHTTPHeaderField("range", "bytes=0-2");
FetchParameters params(resource_request);
ResourceFetcher* fetcher = CreateFetcher();
ImageResource* image_resource = ImageResource::Fetch(params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceResponse partial_response(test_url);
partial_response.SetMimeType("image/jpeg");
partial_response.SetExpectedContentLength(
kJpegImageSubrangeWithoutDimensionsLength);
partial_response.SetHTTPStatusCode(206);
partial_response.SetHTTPHeaderField(
"content-range",
BuildContentRange(kJpegImageSubrangeWithoutDimensionsLength,
sizeof(kJpegImage)));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(partial_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage),
kJpegImageSubrangeWithoutDimensionsLength);
EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidFinishLoading(
TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
kJpegImageSubrangeWithoutDimensionsLength,
kJpegImageSubrangeWithoutDimensionsLength, false,
std::vector<network::cors::PreflightTimingInfo>());
EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(ResourceStatus::kDecodeError,
observer->StatusOnImageNotifyFinished());
EXPECT_EQ(1, observer->ImageChangedCount());
EXPECT_FALSE(image_resource->IsLoading());
}
TEST(ImageResourceTest, FetchDisallowPlaceholder) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kNone, params.GetImageRequestOptimization());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
TestThatIsNotPlaceholderRequestAndServeResponse(test_url, image_resource,
observer.get());
}
TEST(ImageResourceTest, FetchAllowPlaceholderDataURL) {
KURL test_url("data:image/jpeg;base64," +
Base64Encode(reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage)));
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kNone, params.GetImageRequestOptimization());
EXPECT_EQ(g_null_atom,
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
}
TEST(ImageResourceTest, FetchAllowPlaceholderPostRequest) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest resource_request(test_url);
resource_request.SetHTTPMethod(http_names::kPOST);
FetchParameters params(resource_request);
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kNone, params.GetImageRequestOptimization());
EXPECT_EQ(g_null_atom,
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
image_resource->Loader()->Cancel();
}
TEST(ImageResourceTest, FetchAllowPlaceholderExistingRangeHeader) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest resource_request(test_url);
resource_request.SetHTTPHeaderField("range", "bytes=128-255");
FetchParameters params(resource_request);
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kNone, params.GetImageRequestOptimization());
EXPECT_EQ("bytes=128-255",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
image_resource->Loader()->Cancel();
}
TEST(ImageResourceTest, FetchAllowPlaceholderSuccessful) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
TestThatIsPlaceholderRequestAndServeResponse(test_url, image_resource,
observer.get());
}
TEST(ImageResourceTest, FetchAllowPlaceholderUnsuccessful) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
const char kBadData[] = "notanimageresponse";
ResourceResponse bad_response(test_url);
bad_response.SetMimeType("image/jpeg");
bad_response.SetExpectedContentLength(sizeof(kBadData));
bad_response.SetHTTPStatusCode(206);
bad_response.SetHTTPHeaderField(
"content-range", BuildContentRange(sizeof(kBadData), sizeof(kJpegImage)));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(bad_response));
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidReceiveData(kBadData, sizeof(kBadData));
// The dimensions could not be extracted, so the full original image should be
// loading.
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(2, observer->ImageChangedCount());
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
TEST(ImageResourceTest, FetchAllowPlaceholderUnsuccessfulClientLoFi) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest request = ResourceRequest(test_url);
request.SetPreviewsState(WebURLRequest::kClientLoFiOn);
FetchParameters params{request};
params.SetAllowImagePlaceholder();
ImageResource* image_resource = ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
const char kBadData[] = "notanimageresponse";
ResourceResponse bad_response(test_url);
bad_response.SetMimeType("image/jpeg");
bad_response.SetExpectedContentLength(sizeof(kBadData));
bad_response.SetHTTPStatusCode(206);
bad_response.SetHTTPHeaderField(
"content-range", BuildContentRange(sizeof(kBadData), sizeof(kJpegImage)));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(bad_response));
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidReceiveData(kBadData, sizeof(kBadData));
// The dimensions could not be extracted, so the full original image should be
// loading.
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(2, observer->ImageChangedCount());
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), true);
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
}
TEST(ImageResourceTest, FetchAllowPlaceholderPartialContentWithoutDimensions) {
const struct {
WebURLRequest::PreviewsState initial_previews_state;
WebURLRequest::PreviewsState expected_reload_previews_state;
bool placeholder_before_reload;
bool placeholder_after_reload;
} tests[] = {
{WebURLRequest::kPreviewsUnspecified, WebURLRequest::kPreviewsNoTransform,
false},
{WebURLRequest::kClientLoFiOn,
WebURLRequest::kPreviewsNoTransform |
WebURLRequest::kClientLoFiAutoReload,
true},
};
for (const auto& test : tests) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceRequest resource_request(test_url);
resource_request.SetPreviewsState(test.initial_previews_state);
FetchParameters params(resource_request);
params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
// TODO(hiroshige): Make the range request header and partial content length
// consistent. https://crbug.com/689760.
ResourceResponse partial_response(test_url);
partial_response.SetMimeType("image/jpeg");
partial_response.SetExpectedContentLength(
kJpegImageSubrangeWithoutDimensionsLength);
partial_response.SetHTTPStatusCode(206);
partial_response.SetHTTPHeaderField(
"content-range",
BuildContentRange(kJpegImageSubrangeWithoutDimensionsLength,
sizeof(kJpegImage)));
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(partial_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage),
kJpegImageSubrangeWithoutDimensionsLength);
EXPECT_EQ(0, observer->ImageChangedCount());
image_resource->Loader()->DidFinishLoading(
TimeTicks(), kJpegImageSubrangeWithoutDimensionsLength,
kJpegImageSubrangeWithoutDimensionsLength,
kJpegImageSubrangeWithoutDimensionsLength, false,
std::vector<network::cors::PreflightTimingInfo>());
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(2, observer->ImageChangedCount());
TestThatReloadIsStartedThenServeReload(
test_url, image_resource, image_resource->GetContent(), observer.get(),
test.placeholder_before_reload);
EXPECT_EQ(test.expected_reload_previews_state,
image_resource->GetResourceRequest().GetPreviewsState());
}
}
TEST(ImageResourceTest, FetchAllowPlaceholderThenDisallowPlaceholder) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters placeholder_params{ResourceRequest(test_url)};
placeholder_params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
FetchParameters non_placeholder_params{ResourceRequest(test_url)};
ImageResource* image_resource2 =
ImageResource::Fetch(non_placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer2 =
MockImageResourceObserver::Create(image_resource2->GetContent());
ImageResource* image_resource3 =
ImageResource::Fetch(non_placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer3 =
MockImageResourceObserver::Create(image_resource3->GetContent());
// |imageResource| remains a placeholder, while following non-placeholder
// requests start non-placeholder loading with a separate ImageResource.
ASSERT_NE(image_resource, image_resource2);
ASSERT_NE(image_resource->Loader(), image_resource2->Loader());
ASSERT_NE(image_resource->GetContent(), image_resource2->GetContent());
ASSERT_EQ(image_resource2, image_resource3);
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
EXPECT_FALSE(observer2->ImageNotifyFinishedCalled());
EXPECT_FALSE(observer3->ImageNotifyFinishedCalled());
// Checks that |imageResource2| (and |imageResource3|) loads a
// non-placeholder image.
TestThatIsNotPlaceholderRequestAndServeResponse(test_url, image_resource2,
observer2.get());
EXPECT_TRUE(observer3->ImageNotifyFinishedCalled());
// Checks that |imageResource| will loads a placeholder image.
TestThatIsPlaceholderRequestAndServeResponse(test_url, image_resource,
observer.get());
// |imageResource2| is still a non-placeholder image.
EXPECT_FALSE(image_resource2->ShouldShowPlaceholder());
EXPECT_TRUE(image_resource2->GetContent()->GetImage()->IsBitmapImage());
}
TEST(ImageResourceTest,
FetchAllowPlaceholderThenDisallowPlaceholderAfterLoaded) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
ResourceFetcher* fetcher = CreateFetcher();
FetchParameters placeholder_params{ResourceRequest(test_url)};
placeholder_params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
TestThatIsPlaceholderRequestAndServeResponse(test_url, image_resource,
observer.get());
FetchParameters non_placeholder_params{ResourceRequest(test_url)};
ImageResource* image_resource2 =
ImageResource::Fetch(non_placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer2 =
MockImageResourceObserver::Create(image_resource2->GetContent());
ImageResource* image_resource3 =
ImageResource::Fetch(non_placeholder_params, fetcher);
std::unique_ptr<MockImageResourceObserver> observer3 =
MockImageResourceObserver::Create(image_resource3->GetContent());
EXPECT_FALSE(observer2->ImageNotifyFinishedCalled());
EXPECT_FALSE(observer3->ImageNotifyFinishedCalled());
// |imageResource| remains a placeholder, while following non-placeholder
// requests start non-placeholder loading with a separate ImageResource.
ASSERT_NE(image_resource, image_resource2);
ASSERT_EQ(image_resource2, image_resource3);
TestThatIsNotPlaceholderRequestAndServeResponse(test_url, image_resource2,
observer2.get());
EXPECT_TRUE(observer3->ImageNotifyFinishedCalled());
}
TEST(ImageResourceTest, FetchAllowPlaceholderFullResponseDecodeSuccess) {
const struct {
int status_code;
AtomicString content_range;
} tests[] = {
{200, g_null_atom},
{404, g_null_atom},
{206, BuildContentRange(sizeof(kJpegImage), sizeof(kJpegImage))},
};
for (const auto& test : tests) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("imapge/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage));
resource_response.SetHTTPStatusCode(test.status_code);
if (test.content_range != g_null_atom)
resource_response.SetHTTPHeaderField("content-range", test.content_range);
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(
reinterpret_cast<const char*>(kJpegImage), sizeof(kJpegImage));
image_resource->Loader()->DidFinishLoading(
TimeTicks(), sizeof(kJpegImage), sizeof(kJpegImage), sizeof(kJpegImage),
false, std::vector<network::cors::PreflightTimingInfo>());
EXPECT_EQ(ResourceStatus::kCached, image_resource->GetStatus());
EXPECT_EQ(sizeof(kJpegImage), image_resource->EncodedSize());
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
EXPECT_LT(0, observer->ImageChangedCount());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnLastImageChanged());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_EQ(kJpegImageWidth, observer->ImageWidthOnImageNotifyFinished());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(kJpegImageWidth,
image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(kJpegImageHeight,
image_resource->GetContent()->GetImage()->height());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
}
}
TEST(ImageResourceTest,
FetchAllowPlaceholderFullResponseDecodeFailureNoReload) {
static const char kBadImageData[] = "bad image data";
const struct {
int status_code;
AtomicString content_range;
uint32_t data_size;
} tests[] = {
{200, g_null_atom, sizeof(kBadImageData)},
{206, BuildContentRange(sizeof(kBadImageData), sizeof(kBadImageData)),
sizeof(kBadImageData)},
{204, g_null_atom, 0},
};
for (const auto& test : tests) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(test.data_size);
resource_response.SetHTTPStatusCode(test.status_code);
if (test.content_range != g_null_atom)
resource_response.SetHTTPHeaderField("content-range", test.content_range);
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(kBadImageData, test.data_size);
EXPECT_EQ(ResourceStatus::kDecodeError, image_resource->GetStatus());
EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
}
}
TEST(ImageResourceTest,
FetchAllowPlaceholderFullResponseDecodeFailureWithReload) {
const int kStatusCodes[] = {404, 500};
for (int status_code : kStatusCodes) {
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
FetchParameters params{ResourceRequest(test_url)};
params.SetAllowImagePlaceholder();
ImageResource* image_resource =
ImageResource::Fetch(params, CreateFetcher());
EXPECT_EQ(FetchParameters::kAllowPlaceholder,
params.GetImageRequestOptimization());
EXPECT_EQ("bytes=0-2047",
image_resource->GetResourceRequest().HttpHeaderField("range"));
EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
static const char kBadImageData[] = "bad image data";
ResourceResponse resource_response(test_url);
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kBadImageData));
resource_response.SetHTTPStatusCode(status_code);
image_resource->Loader()->DidReceiveResponse(
WrappedResourceResponse(resource_response));
image_resource->Loader()->DidReceiveData(kBadImageData,
sizeof(kBadImageData));
EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
// The dimensions could not be extracted, and the response code was a 4xx
// error, so the full original image should be loading.
TestThatReloadIsStartedThenServeReload(test_url, image_resource,
image_resource->GetContent(),
observer.get(), false);
}
}
TEST(ImageResourceTest, PeriodicFlushTest) {
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform;
EmptyChromeClient* chrome_client = MakeGarbageCollected<EmptyChromeClient>();
Page::PageClients clients;
FillWithEmptyClients(clients);
clients.chrome_client = chrome_client;
std::unique_ptr<DummyPageHolder> page_holder = DummyPageHolder::Create(
IntSize(800, 600), &clients, EmptyLocalFrameClient::Create(), nullptr);
KURL test_url(kTestURL);
ScopedMockedURLLoad scoped_mocked_url_load(test_url, GetTestFilePath());
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
page_holder->GetFrame().GetTaskRunner(TaskType::kInternalTest);
MockFetchContext* context =
MakeGarbageCollected<MockFetchContext>(task_runner);
auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
ResourceFetcherInit(*properties, context, task_runner));
auto* scheduler = MakeGarbageCollected<ResourceLoadScheduler>(
ResourceLoadScheduler::ThrottlingPolicy::kNormal, context);
ImageResource* image_resource = ImageResource::CreateForTest(test_url);
// Ensure that |image_resource| has a loader.
ResourceLoader* loader =
ResourceLoader::Create(fetcher, scheduler, image_resource);
ALLOW_UNUSED_LOCAL(loader);
image_resource->NotifyStartLoad();
std::unique_ptr<MockImageResourceObserver> observer =
MockImageResourceObserver::Create(image_resource->GetContent());
// Send the image response.
ResourceResponse resource_response(NullURL());
resource_response.SetMimeType("image/jpeg");
resource_response.SetExpectedContentLength(sizeof(kJpegImage2));
image_resource->ResponseReceived(resource_response, nullptr);
// This is number is sufficiently large amount of bytes necessary for the
// image to be created (since the size is known). This was determined by
// appending one byte at a time (with flushes) until the image was decoded.
size_t meaningful_image_size = 280;
image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage2),
meaningful_image_size);
size_t bytes_sent = meaningful_image_size;
EXPECT_FALSE(image_resource->ErrorOccurred());
EXPECT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(1, observer->ImageChangedCount());
platform->RunForPeriodSeconds(1.);
platform->AdvanceClockSeconds(1.);
// Sanity check that we created an image after appending |meaningfulImageSize|
// bytes just once.
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(1, observer->ImageChangedCount());
for (int flush_count = 1; flush_count <= 3; ++flush_count) {
// For each of the iteration that appends data, we don't expect
// |imageChangeCount()| to change, since the time is adjusted by 0.2001
// seconds (it's greater than 0.2 to avoid double precision problems).
// After 5 appends, we breach the flush interval and the flush count
// increases.
for (int i = 0; i < 5; ++i) {
SCOPED_TRACE(i);
image_resource->AppendData(
reinterpret_cast<const char*>(kJpegImage2) + bytes_sent, 1);
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_EQ(flush_count, observer->ImageChangedCount());
++bytes_sent;
platform->RunForPeriodSeconds(0.2001);
}
}
// Increasing time by a large number only causes one extra flush.
platform->RunForPeriodSeconds(10.);
platform->AdvanceClockSeconds(10.);
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(4, observer->ImageChangedCount());
// Append the rest of the data and finish (which causes another flush).
image_resource->AppendData(
reinterpret_cast<const char*>(kJpegImage2) + bytes_sent,
sizeof(kJpegImage2) - bytes_sent);
image_resource->FinishForTest();
EXPECT_FALSE(image_resource->ErrorOccurred());
ASSERT_TRUE(image_resource->GetContent()->HasImage());
EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
EXPECT_EQ(5, observer->ImageChangedCount());
EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
EXPECT_EQ(50, image_resource->GetContent()->GetImage()->width());
EXPECT_EQ(50, image_resource->GetContent()->GetImage()->height());
}
TEST(ImageResourceTest, DeferredInvalidation) {
ImageResource* image_resource = ImageResource::CreateForTest(NullURL());
std::unique_ptr<MockImageResourceObserver> obs =
MockImageResourceObserver::Create(image_resource->GetContent());
// Image loaded.
ReceiveResponse(image_resource, NullURL(), "image/jpeg",
reinterpret_cast<const char*>(kJpegImage),
sizeof(kJpegImage));
EXPECT_EQ(obs->ImageChangedCount(), 2);
EXPECT_EQ(obs->Defer(), ImageResourceObserver::CanDeferInvalidation::kNo);
// Image animated.
static_cast<ImageObserver*>(image_resource->GetContent())
->Changed(image_resource->GetContent()->GetImage());
EXPECT_EQ(obs->ImageChangedCount(), 3);
EXPECT_EQ(obs->Defer(), ImageResourceObserver::CanDeferInvalidation::kYes);
}
} // namespace
class ImageResourceCounterTest : public testing::Test {
public:
ImageResourceCounterTest() = default;
~ImageResourceCounterTest() override = default;
void CreateImageResource(const char* url_part, bool ua_resource) {
// Create a unique fake data url.
String url("data:image/png;base64,");
url.append(url_part);
// Setup the fetcher and request.
ResourceFetcher* fetcher = CreateFetcher();
KURL test_url(url);
ResourceRequest request = ResourceRequest(test_url);
FetchParameters fetch_params(request);
scheduler::FakeTaskRunner* task_runner =
static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get());
task_runner->SetTime(1);
// Mark it as coming from a UA stylesheet (if needed).
if (ua_resource) {
fetch_params.MutableOptions().initiator_info.name =
fetch_initiator_type_names::kUacss;
}
// Fetch the ImageResource.
ImageResource::Fetch(fetch_params, fetcher);
task_runner->RunUntilIdle();
}
int GetResourceCount() const {
return InstanceCounters::CounterValue(InstanceCounters::kResourceCounter);
}
int GetUACSSResourceCount() const {
return InstanceCounters::CounterValue(
InstanceCounters::kUACSSResourceCounter);
}
};
TEST_F(ImageResourceCounterTest, InstanceCounters) {
// Get the current resource count.
int current_count = GetResourceCount();
int current_ua_count = GetUACSSResourceCount();
// Create a non-UA sourced image.
CreateImageResource("a", false);
// Check the instance counters have been updated.
EXPECT_EQ(++current_count, GetResourceCount());
EXPECT_EQ(current_ua_count, GetUACSSResourceCount());
// Create another non-UA sourced image.
CreateImageResource("b", false);
// Check the instance counters have been updated.
EXPECT_EQ(++current_count, GetResourceCount());
EXPECT_EQ(current_ua_count, GetUACSSResourceCount());
}
TEST_F(ImageResourceCounterTest, InstanceCounters_UserAgent) {
// Get the current resource count.
int current_count = GetResourceCount();
int current_ua_count = GetUACSSResourceCount();
// Create a non-UA sourced image.
CreateImageResource("c", false);
// Check the instance counters have been updated.
EXPECT_EQ(++current_count, GetResourceCount());
EXPECT_EQ(current_ua_count, GetUACSSResourceCount());
// Create a UA sourced image.
CreateImageResource("d", true);
// Check the instance counters have been updated.
EXPECT_EQ(++current_count, GetResourceCount());
EXPECT_EQ(++current_ua_count, GetUACSSResourceCount());
}
} // namespace blink