blob: ae66f3e79527ee5ee4bcdf2bd8b18d0bf8d1f746 [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/platform/loader/fetch/resource_fetcher.h"
#include <memory>
#include "base/optional.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_url_loader.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/platform/exported/wrapped_resource_response.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/loader/fetch/console_logger.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/raw_resource.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
#include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h"
#include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h"
#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
#include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.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.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
namespace {
constexpr char kTestResourceFilename[] = "white-1x1.png";
constexpr char kTestResourceMimeType[] = "image/png";
constexpr uint32_t kTestResourceSize = 103; // size of white-1x1.png
const FetchClientSettingsObjectSnapshot& CreateFetchClientSettingsObject(
network::mojom::IPAddressSpace address_space) {
return *MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
KURL("https://example.com/foo.html"),
KURL("https://example.com/foo.html"),
SecurityOrigin::Create(KURL("https://example.com/")),
network::mojom::ReferrerPolicy::kDefault, "https://example.com/foo.html",
HttpsState::kModern, AllowedByNosniff::MimeTypeCheck::kStrict,
address_space,
mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
FetchClientSettingsObject::InsecureNavigationsSet());
}
class PartialResourceRequest {
public:
PartialResourceRequest() : PartialResourceRequest(ResourceRequest()) {}
PartialResourceRequest(const ResourceRequest& request)
: is_ad_resource_(request.IsAdResource()),
priority_(request.Priority()) {}
bool IsAdResource() const { return is_ad_resource_; }
ResourceLoadPriority Priority() const { return priority_; }
private:
bool is_ad_resource_;
ResourceLoadPriority priority_;
};
} // namespace
class ResourceFetcherTest : public testing::Test {
public:
ResourceFetcherTest() = default;
~ResourceFetcherTest() override { GetMemoryCache()->EvictResources(); }
class TestResourceLoadObserver final : public ResourceLoadObserver {
public:
// ResourceLoadObserver implementation.
void DidStartRequest(const FetchParameters&, ResourceType) override {}
void WillSendRequest(uint64_t identifier,
const ResourceRequest& request,
const ResourceResponse& redirect_response,
ResourceType,
const FetchInitiatorInfo&,
RenderBlockingBehavior) override {
request_ = PartialResourceRequest(request);
}
void DidChangePriority(uint64_t identifier,
ResourceLoadPriority,
int intra_priority_value) override {}
void DidReceiveResponse(uint64_t identifier,
const ResourceRequest& request,
const ResourceResponse& response,
const Resource* resource,
ResponseSource source) override {}
void DidReceiveData(uint64_t identifier,
base::span<const char> chunk) override {}
void DidReceiveTransferSizeUpdate(uint64_t identifier,
int transfer_size_diff) override {}
void DidDownloadToBlob(uint64_t identifier, BlobDataHandle*) override {}
void DidFinishLoading(uint64_t identifier,
base::TimeTicks finish_time,
int64_t encoded_data_length,
int64_t decoded_body_length,
bool should_report_corb_blocking) override {}
void DidFailLoading(const KURL&,
uint64_t identifier,
const ResourceError&,
int64_t encoded_data_length,
IsInternalRequest is_internal_request) override {}
const base::Optional<PartialResourceRequest>& GetLastRequest() const {
return request_;
}
private:
base::Optional<PartialResourceRequest> request_;
};
protected:
scoped_refptr<scheduler::FakeTaskRunner> CreateTaskRunner() {
return base::MakeRefCounted<scheduler::FakeTaskRunner>();
}
ResourceFetcher* CreateFetcher(
const TestResourceFetcherProperties& properties,
FetchContext* context) {
return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
properties.MakeDetachable(), context, CreateTaskRunner(),
CreateTaskRunner(),
MakeGarbageCollected<TestLoaderFactory>(
platform_->GetURLLoaderMockFactory()),
MakeGarbageCollected<MockContextLifecycleNotifier>(),
nullptr /* back_forward_cache_loader_helper */));
}
ResourceFetcher* CreateFetcher(
const TestResourceFetcherProperties& properties) {
return CreateFetcher(properties, MakeGarbageCollected<MockFetchContext>());
}
ResourceFetcher* CreateFetcher() {
return CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>());
}
void AddResourceToMemoryCache(Resource* resource) {
GetMemoryCache()->Add(resource);
}
void RegisterMockedURLLoad(const KURL& url) {
url_test_helpers::RegisterMockedURLLoad(
url, test::PlatformTestDataPath(kTestResourceFilename),
kTestResourceMimeType, platform_->GetURLLoaderMockFactory());
}
ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
private:
DISALLOW_COPY_AND_ASSIGN(ResourceFetcherTest);
};
TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) {
KURL secure_url("https://secureorigin.test/image.png");
// Try to request a url. The request should fail, and a resource in an error
// state should be returned, and no resource should be present in the cache.
auto* fetcher = CreateFetcher();
fetcher->ClearContext();
ResourceRequest resource_request(secure_url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->ErrorOccurred());
EXPECT_TRUE(resource->GetResourceError().IsAccessCheck());
EXPECT_FALSE(GetMemoryCache()->ResourceForURL(secure_url));
// Start by calling StartLoad() directly, rather than via RequestResource().
// This shouldn't crash. Setting the resource type to image, as StartLoad with
// a single argument is only called on images or fonts.
fetcher->StartLoad(RawResource::CreateForTest(
secure_url, SecurityOrigin::CreateUniqueOpaque(), ResourceType::kImage));
}
TEST_F(ResourceFetcherTest, UseExistingResource) {
blink::HistogramTester histogram_tester;
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(resource);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource));
Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
// Test histograms.
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
2);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 1);
// Create a new fetcher and load the same resource.
auto* new_fetcher = CreateFetcher();
Resource* new_fetcher_resource =
MockResource::Fetch(fetch_params, new_fetcher, nullptr);
EXPECT_EQ(resource, new_fetcher_resource);
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
3);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 2);
}
TEST_F(ResourceFetcherTest, MemoryCachePerContextUseExistingResource) {
blink::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kScopeMemoryCachePerContext);
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
auto* fetcher_a = CreateFetcher();
Resource* resource_a = MockResource::Fetch(fetch_params, fetcher_a, nullptr);
ASSERT_TRUE(resource_a);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource_a->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource_a));
Resource* resource_a1 = MockResource::Fetch(fetch_params, fetcher_a, nullptr);
ASSERT_TRUE(resource_a1);
EXPECT_EQ(resource_a, resource_a1);
// Test histograms.
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
2);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 1);
// Create a new fetcher and load the same resource. It should be loaded again.
auto* fetcher_b = CreateFetcher();
Resource* resource_b = MockResource::Fetch(fetch_params, fetcher_b, nullptr);
EXPECT_NE(resource_a1, resource_b);
ASSERT_TRUE(resource_b);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource_b->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource_b));
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
3);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 2);
// (TODO: crbug.com/1127971) Using the first fetcher now should reuse the same
// resource as was earlier loaded by the same fetcher.
// EXPECT_EQ(resource_a1, resource_a2);
Resource* resource_a2 = MockResource::Fetch(fetch_params, fetcher_a, nullptr);
EXPECT_EQ(resource_b, resource_a2);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 2);
// Using the second fetcher now should reuse the same resource as was earlier
// loaded by the same fetcher.
Resource* resource_b1 = MockResource::Fetch(fetch_params, fetcher_b, nullptr);
EXPECT_EQ(resource_b, resource_b1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 3);
}
TEST_F(ResourceFetcherTest, MetricsPerTopFrameSite) {
blink::HistogramTester histogram_tester;
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
ResourceRequestHead request_head(url);
scoped_refptr<const SecurityOrigin> origin_a =
SecurityOrigin::Create(KURL("https://a.test"));
request_head.SetTopFrameOrigin(origin_a);
request_head.SetRequestorOrigin(origin_a);
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(request_head));
auto* fetcher_1 = CreateFetcher();
Resource* resource_1 = MockResource::Fetch(fetch_params, fetcher_1, nullptr);
ASSERT_TRUE(resource_1);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource_1->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource_1));
auto* fetcher_2 = CreateFetcher();
ResourceRequestHead request_head_2(url);
scoped_refptr<const SecurityOrigin> origin_b =
SecurityOrigin::Create(KURL("https://b.test"));
request_head_2.SetTopFrameOrigin(origin_b);
request_head_2.SetRequestorOrigin(origin_a);
FetchParameters fetch_params_2 =
FetchParameters::CreateForTest(ResourceRequest(request_head_2));
Resource* resource_2 =
MockResource::Fetch(fetch_params_2, fetcher_2, nullptr);
EXPECT_EQ(resource_1, resource_2);
// Test histograms.
histogram_tester.ExpectTotalCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 0);
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
2);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 1);
// Now load the same resource with origin_b as top-frame site. The
// PerTopFrameSite histogram should be incremented.
auto* fetcher_3 = CreateFetcher();
ResourceRequestHead request_head_3(url);
scoped_refptr<const SecurityOrigin> foo_origin_b =
SecurityOrigin::Create(KURL("https://foo.b.test"));
request_head_3.SetTopFrameOrigin(foo_origin_b);
request_head_3.SetRequestorOrigin(origin_a);
FetchParameters fetch_params_3 =
FetchParameters::CreateForTest(ResourceRequest(request_head_3));
Resource* resource_3 =
MockResource::Fetch(fetch_params_2, fetcher_3, nullptr);
EXPECT_EQ(resource_1, resource_3);
histogram_tester.ExpectTotalCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock",
0 /* RevalidationPolicy::kUse */, 1);
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
3);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 2);
}
TEST_F(ResourceFetcherTest, MetricsPerTopFrameSiteOpaqueOrigins) {
blink::HistogramTester histogram_tester;
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
ResourceRequestHead request_head(url);
scoped_refptr<const SecurityOrigin> origin_a =
SecurityOrigin::Create(KURL("https://a.test"));
scoped_refptr<const SecurityOrigin> opaque_origin1 =
SecurityOrigin::CreateUniqueOpaque();
request_head.SetTopFrameOrigin(opaque_origin1);
request_head.SetRequestorOrigin(origin_a);
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(request_head));
auto* fetcher_1 = CreateFetcher();
Resource* resource_1 = MockResource::Fetch(fetch_params, fetcher_1, nullptr);
ASSERT_TRUE(resource_1);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource_1->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource_1));
// Create a 2nd opaque top-level origin.
auto* fetcher_2 = CreateFetcher();
ResourceRequestHead request_head_2(url);
scoped_refptr<const SecurityOrigin> opaque_origin2 =
SecurityOrigin::CreateUniqueOpaque();
request_head_2.SetTopFrameOrigin(opaque_origin2);
request_head_2.SetRequestorOrigin(origin_a);
FetchParameters fetch_params_2 =
FetchParameters::CreateForTest(ResourceRequest(request_head_2));
Resource* resource_2 =
MockResource::Fetch(fetch_params_2, fetcher_2, nullptr);
EXPECT_EQ(resource_1, resource_2);
// Test histograms.
histogram_tester.ExpectTotalCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 0);
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
2);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
3 /* RevalidationPolicy::kLoad */, 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 1);
// Now load the same resource with opaque_origin1 as top-frame site. The
// PerTopFrameSite histogram should be incremented.
auto* fetcher_3 = CreateFetcher();
ResourceRequestHead request_head_3(url);
request_head_3.SetTopFrameOrigin(opaque_origin2);
request_head_3.SetRequestorOrigin(origin_a);
FetchParameters fetch_params_3 =
FetchParameters::CreateForTest(ResourceRequest(request_head_3));
Resource* resource_3 =
MockResource::Fetch(fetch_params_2, fetcher_3, nullptr);
EXPECT_EQ(resource_1, resource_3);
histogram_tester.ExpectTotalCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 1);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock",
0 /* RevalidationPolicy::kUse */, 1);
histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock",
3);
histogram_tester.ExpectBucketCount(
"Blink.MemoryCache.RevalidationPolicy.Mock",
0 /* RevalidationPolicy::kUse */, 2);
}
// Verify that the ad bit is copied to WillSendRequest's request when the
// response is served from the memory cache.
TEST_F(ResourceFetcherTest, WillSendRequestAdBit) {
// Add a resource to the memory cache.
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
auto* properties =
MakeGarbageCollected<TestResourceFetcherProperties>(source_origin);
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
KURL url("http://127.0.0.1:8000/foo.html");
Resource* resource =
RawResource::CreateForTest(url, source_origin, ResourceType::kRaw);
AddResourceToMemoryCache(resource);
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
resource->ResponseReceived(response);
resource->FinishForTest();
auto* observer = MakeGarbageCollected<TestResourceLoadObserver>();
// Fetch the cached resource. The request to DispatchWillSendRequest should
// preserve the ad bit.
auto* fetcher = CreateFetcher(*properties, context);
fetcher->SetResourceLoadObserver(observer);
ResourceRequest resource_request(url);
resource_request.SetIsAdResource();
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
base::Optional<PartialResourceRequest> new_request =
observer->GetLastRequest();
EXPECT_TRUE(new_request.has_value());
EXPECT_TRUE(new_request.value().IsAdResource());
}
TEST_F(ResourceFetcherTest, Vary) {
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
KURL url("http://127.0.0.1:8000/foo.html");
Resource* resource =
RawResource::CreateForTest(url, source_origin, ResourceType::kRaw);
AddResourceToMemoryCache(resource);
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kVary, "*");
resource->ResponseReceived(response);
resource->FinishForTest();
ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
auto* fetcher = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin));
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_NE(resource, new_resource);
new_resource->Loader()->Cancel();
}
TEST_F(ResourceFetcherTest, ResourceTimingInfo) {
auto info = ResourceTimingInfo::Create(
fetch_initiator_type_names::kDocument, base::TimeTicks::Now(),
mojom::blink::RequestContextType::UNSPECIFIED,
network::mojom::RequestDestination::kEmpty);
info->AddFinalTransferSize(5);
EXPECT_EQ(info->TransferSize(), static_cast<uint64_t>(5));
ResourceResponse redirect_response(KURL("https://example.com/original"));
redirect_response.SetHttpStatusCode(200);
redirect_response.SetEncodedDataLength(7);
info->AddRedirect(redirect_response, KURL("https://example.com/redirect"));
EXPECT_EQ(info->TransferSize(), static_cast<uint64_t>(12));
}
TEST_F(ResourceFetcherTest, VaryOnBack) {
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
auto* fetcher = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin));
KURL url("http://127.0.0.1:8000/foo.html");
Resource* resource =
RawResource::CreateForTest(url, source_origin, ResourceType::kRaw);
AddResourceToMemoryCache(resource);
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kVary, "*");
resource->ResponseReceived(response);
resource->FinishForTest();
ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
ResourceRequest resource_request(url);
resource_request.SetCacheMode(mojom::FetchCacheMode::kForceCache);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
}
TEST_F(ResourceFetcherTest, VaryResource) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kVary, "*");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
}
class RequestSameResourceOnComplete
: public GarbageCollected<RequestSameResourceOnComplete>,
public RawResourceClient {
public:
RequestSameResourceOnComplete(WebURLLoaderMockFactory* mock_factory,
FetchParameters& params,
ResourceFetcher* fetcher)
: mock_factory_(mock_factory),
notify_finished_called_(false),
source_origin_(fetcher->GetProperties()
.GetFetchClientSettingsObject()
.GetSecurityOrigin()) {
MockResource::Fetch(params, fetcher, this);
}
void NotifyFinished(Resource* resource) override {
EXPECT_EQ(GetResource(), resource);
auto* properties =
MakeGarbageCollected<TestResourceFetcherProperties>(source_origin_);
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
auto* fetcher2 = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
properties->MakeDetachable(), context,
base::MakeRefCounted<scheduler::FakeTaskRunner>(),
base::MakeRefCounted<scheduler::FakeTaskRunner>(),
MakeGarbageCollected<TestLoaderFactory>(mock_factory_),
MakeGarbageCollected<MockContextLifecycleNotifier>(),
nullptr /* back_forward_cache_loader_helper */));
ResourceRequest resource_request2(GetResource()->Url());
resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache);
FetchParameters fetch_params2 =
FetchParameters::CreateForTest(std::move(resource_request2));
Resource* resource2 = MockResource::Fetch(fetch_params2, fetcher2, nullptr);
EXPECT_EQ(GetResource(), resource2);
notify_finished_called_ = true;
ClearResource();
}
bool NotifyFinishedCalled() const { return notify_finished_called_; }
void Trace(Visitor* visitor) const override {
RawResourceClient::Trace(visitor);
}
String DebugName() const override { return "RequestSameResourceOnComplete"; }
private:
WebURLLoaderMockFactory* mock_factory_;
bool notify_finished_called_;
scoped_refptr<const SecurityOrigin> source_origin_;
};
TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) {
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
KURL url("http://127.0.0.1:8000/foo.png");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kETag, "1234567890");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
ResourceFetcher* fetcher1 = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin));
ResourceRequest request1(url);
request1.SetHttpHeaderField(http_names::kCacheControl, "no-cache");
FetchParameters fetch_params1 =
FetchParameters::CreateForTest(std::move(request1));
Persistent<RequestSameResourceOnComplete> client =
MakeGarbageCollected<RequestSameResourceOnComplete>(
platform_->GetURLLoaderMockFactory(), fetch_params1, fetcher1);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(client->NotifyFinishedCalled());
}
// TODO(crbug.com/850785): Reenable this.
#if defined(OS_ANDROID)
#define MAYBE_DontReuseMediaDataUrl DISABLED_DontReuseMediaDataUrl
#else
#define MAYBE_DontReuseMediaDataUrl DontReuseMediaDataUrl
#endif
TEST_F(ResourceFetcherTest, MAYBE_DontReuseMediaDataUrl) {
auto* fetcher = CreateFetcher();
ResourceRequest request(KURL("data:text/html,foo"));
request.SetRequestContext(mojom::blink::RequestContextType::VIDEO);
ResourceLoaderOptions options(nullptr /* world */);
options.data_buffering_policy = kDoNotBufferData;
options.initiator_info.name = fetch_initiator_type_names::kInternal;
FetchParameters fetch_params(std::move(request), options);
Resource* resource1 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
Resource* resource2 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
EXPECT_NE(resource1, resource2);
}
class ServeRequestsOnCompleteClient final
: public GarbageCollected<ServeRequestsOnCompleteClient>,
public RawResourceClient {
public:
explicit ServeRequestsOnCompleteClient(WebURLLoaderMockFactory* mock_factory)
: mock_factory_(mock_factory) {}
void NotifyFinished(Resource*) override {
mock_factory_->ServeAsynchronousRequests();
ClearResource();
}
// No callbacks should be received except for the NotifyFinished() triggered
// by ResourceLoader::Cancel().
void DataSent(Resource*, uint64_t, uint64_t) override { ASSERT_TRUE(false); }
void ResponseReceived(Resource*, const ResourceResponse&) override {
ASSERT_TRUE(false);
}
void CachedMetadataReceived(Resource*, mojo_base::BigBuffer) override {
ASSERT_TRUE(false);
}
void DataReceived(Resource*, const char*, size_t) override {
ASSERT_TRUE(false);
}
bool RedirectReceived(Resource*,
const ResourceRequest&,
const ResourceResponse&) override {
ADD_FAILURE();
return true;
}
void DataDownloaded(Resource*, uint64_t) override { ASSERT_TRUE(false); }
void Trace(Visitor* visitor) const override {
RawResourceClient::Trace(visitor);
}
String DebugName() const override { return "ServeRequestsOnCompleteClient"; }
private:
WebURLLoaderMockFactory* mock_factory_;
};
// Regression test for http://crbug.com/594072.
// This emulates a modal dialog triggering a nested run loop inside
// ResourceLoader::Cancel(). If the ResourceLoader doesn't promptly cancel its
// WebURLLoader before notifying its clients, a nested run loop may send a
// network response, leading to an invalid state transition in ResourceLoader.
TEST_F(ResourceFetcherTest, ResponseOnCancel) {
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
auto* fetcher = CreateFetcher();
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
Persistent<ServeRequestsOnCompleteClient> client =
MakeGarbageCollected<ServeRequestsOnCompleteClient>(
platform_->GetURLLoaderMockFactory());
Resource* resource = RawResource::Fetch(fetch_params, fetcher, client);
resource->Loader()->Cancel();
}
class ScopedMockRedirectRequester {
STACK_ALLOCATED();
public:
ScopedMockRedirectRequester(
WebURLLoaderMockFactory* mock_factory,
MockFetchContext* context,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: mock_factory_(mock_factory),
context_(context),
task_runner_(std::move(task_runner)) {}
void RegisterRedirect(const WebString& from_url, const WebString& to_url) {
KURL redirect_url(from_url);
WebURLResponse redirect_response;
redirect_response.SetCurrentRequestUrl(redirect_url);
redirect_response.SetHttpStatusCode(301);
redirect_response.SetHttpHeaderField(http_names::kLocation, to_url);
redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes);
mock_factory_->RegisterURL(redirect_url, redirect_response, "");
}
void RegisterFinalResource(const WebString& url) {
url_test_helpers::RegisterMockedURLLoad(
KURL(url), test::PlatformTestDataPath(kTestResourceFilename),
kTestResourceMimeType, mock_factory_);
}
void Request(const WebString& url) {
auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
properties->MakeDetachable(), context_, task_runner_,
base::MakeRefCounted<scheduler::FakeTaskRunner>(),
MakeGarbageCollected<TestLoaderFactory>(mock_factory_),
MakeGarbageCollected<MockContextLifecycleNotifier>(),
nullptr /* back_forward_cache_loader_helper */));
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
RawResource::Fetch(fetch_params, fetcher, nullptr);
mock_factory_->ServeAsynchronousRequests();
}
private:
WebURLLoaderMockFactory* mock_factory_;
MockFetchContext* context_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(ScopedMockRedirectRequester);
};
TEST_F(ResourceFetcherTest, SameOriginRedirect) {
const char kRedirectURL[] = "http://127.0.0.1:8000/redirect.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL, kFinalURL);
requester.RegisterFinalResource(kFinalURL);
requester.Request(kRedirectURL);
EXPECT_EQ(kRedirectResponseOverheadBytes + kTestResourceSize,
context->GetTransferSize());
}
TEST_F(ResourceFetcherTest, CrossOriginRedirect) {
const char kRedirectURL[] = "http://otherorigin.test/redirect.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL, kFinalURL);
requester.RegisterFinalResource(kFinalURL);
requester.Request(kRedirectURL);
EXPECT_EQ(kTestResourceSize, context->GetTransferSize());
}
TEST_F(ResourceFetcherTest, ComplexCrossOriginRedirect) {
const char kRedirectURL1[] = "http://127.0.0.1:8000/redirect1.html";
const char kRedirectURL2[] = "http://otherorigin.test/redirect2.html";
const char kRedirectURL3[] = "http://127.0.0.1:8000/redirect3.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL1, kRedirectURL2);
requester.RegisterRedirect(kRedirectURL2, kRedirectURL3);
requester.RegisterRedirect(kRedirectURL3, kFinalURL);
requester.RegisterFinalResource(kFinalURL);
requester.Request(kRedirectURL1);
EXPECT_EQ(kTestResourceSize, context->GetTransferSize());
}
TEST_F(ResourceFetcherTest, SynchronousRequest) {
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
auto* fetcher = CreateFetcher();
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
fetch_params.MakeSynchronous();
Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_TRUE(resource->IsLoaded());
EXPECT_EQ(ResourceLoadPriority::kHighest,
resource->GetResourceRequest().Priority());
}
TEST_F(ResourceFetcherTest, PingPriority) {
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
auto* fetcher = CreateFetcher();
ResourceRequest resource_request(url);
resource_request.SetRequestContext(mojom::blink::RequestContextType::PING);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(ResourceLoadPriority::kVeryLow,
resource->GetResourceRequest().Priority());
}
TEST_F(ResourceFetcherTest, PreloadResourceTwice) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_original.SetLinkPreload(true);
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->IsLinkPreload());
EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params.SetLinkPreload(true);
Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
fetcher->ClearPreloads(ResourceFetcher::kClearAllPreloads);
EXPECT_FALSE(fetcher->ContainsAsPreload(resource));
EXPECT_FALSE(GetMemoryCache()->Contains(resource));
EXPECT_TRUE(resource->IsUnusedPreload());
}
TEST_F(ResourceFetcherTest, LinkPreloadResourceAndUse) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
// Link preload preload scanner
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_original.SetLinkPreload(true);
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->IsLinkPreload());
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
// Resource created by preload scanner
FetchParameters fetch_params_preload_scanner =
FetchParameters::CreateForTest(ResourceRequest(url));
Resource* preload_scanner_resource =
MockResource::Fetch(fetch_params_preload_scanner, fetcher, nullptr);
EXPECT_EQ(resource, preload_scanner_resource);
EXPECT_TRUE(resource->IsLinkPreload());
// Resource created by parser
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
Persistent<MockResourceClient> client =
MakeGarbageCollected<MockResourceClient>();
Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, client);
EXPECT_EQ(resource, new_resource);
EXPECT_TRUE(resource->IsLinkPreload());
// DCL reached
fetcher->ClearPreloads(ResourceFetcher::kClearSpeculativeMarkupPreloads);
EXPECT_TRUE(GetMemoryCache()->Contains(resource));
EXPECT_FALSE(resource->IsUnusedPreload());
}
TEST_F(ResourceFetcherTest, PreloadMatchWithBypassingCache) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_original.SetLinkPreload(true);
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->IsLinkPreload());
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
FetchParameters fetch_params_second =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_second.MutableResourceRequest().SetCacheMode(
mojom::FetchCacheMode::kBypassCache);
Resource* second_resource =
MockResource::Fetch(fetch_params_second, fetcher, nullptr);
EXPECT_EQ(resource, second_resource);
EXPECT_TRUE(resource->IsLinkPreload());
}
TEST_F(ResourceFetcherTest, CrossFramePreloadMatchIsNotAllowed) {
auto* fetcher = CreateFetcher();
auto* fetcher2 = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_original.SetLinkPreload(true);
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->IsLinkPreload());
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
FetchParameters fetch_params_second =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_second.MutableResourceRequest().SetCacheMode(
mojom::FetchCacheMode::kBypassCache);
Resource* second_resource =
MockResource::Fetch(fetch_params_second, fetcher2, nullptr);
EXPECT_NE(resource, second_resource);
EXPECT_TRUE(resource->IsLinkPreload());
}
TEST_F(ResourceFetcherTest, RepetitiveLinkPreloadShouldBeMerged) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_for_request =
FetchParameters::CreateForTest(ResourceRequest(url));
FetchParameters fetch_params_for_preload =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_for_preload.SetLinkPreload(true);
Resource* resource1 =
MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
ASSERT_TRUE(resource1);
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
// The second preload fetch returns the first preload.
Resource* resource2 =
MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_EQ(resource1, resource2);
// preload matching
Resource* resource3 =
MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
EXPECT_EQ(resource1, resource3);
EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
EXPECT_FALSE(resource1->IsUnusedPreload());
}
TEST_F(ResourceFetcherTest, RepetitiveSpeculativePreloadShouldBeMerged) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_for_request =
FetchParameters::CreateForTest(ResourceRequest(url));
FetchParameters fetch_params_for_preload =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_for_preload.SetSpeculativePreloadType(
FetchParameters::SpeculativePreloadType::kInDocument);
Resource* resource1 =
MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
ASSERT_TRUE(resource1);
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
// The second preload fetch returns the first preload.
Resource* resource2 =
MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_EQ(resource1, resource2);
// preload matching
Resource* resource3 =
MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
EXPECT_EQ(resource1, resource3);
EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
EXPECT_FALSE(resource1->IsUnusedPreload());
}
TEST_F(ResourceFetcherTest, SpeculativePreloadShouldBePromotedToLinkPreload) {
auto* fetcher = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_for_request =
FetchParameters::CreateForTest(ResourceRequest(url));
FetchParameters fetch_params_for_speculative_preload =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_for_speculative_preload.SetSpeculativePreloadType(
FetchParameters::SpeculativePreloadType::kInDocument);
FetchParameters fetch_params_for_link_preload =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_for_link_preload.SetLinkPreload(true);
Resource* resource1 = MockResource::Fetch(
fetch_params_for_speculative_preload, fetcher, nullptr);
ASSERT_TRUE(resource1);
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_FALSE(resource1->IsLinkPreload());
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
// The second preload fetch returns the first preload.
Resource* resource2 =
MockResource::Fetch(fetch_params_for_link_preload, fetcher, nullptr);
EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
EXPECT_TRUE(resource1->IsUnusedPreload());
EXPECT_TRUE(resource1->IsLinkPreload());
EXPECT_EQ(resource1, resource2);
// preload matching
Resource* resource3 =
MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
EXPECT_EQ(resource1, resource3);
EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
EXPECT_FALSE(resource1->IsUnusedPreload());
EXPECT_TRUE(resource1->IsLinkPreload());
}
TEST_F(ResourceFetcherTest, Revalidate304) {
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
KURL url("http://127.0.0.1:8000/foo.html");
Resource* resource =
RawResource::CreateForTest(url, source_origin, ResourceType::kRaw);
AddResourceToMemoryCache(resource);
ResourceResponse response(url);
response.SetHttpStatusCode(304);
response.SetHttpHeaderField("etag", "1234567890");
resource->ResponseReceived(response);
resource->FinishForTest();
auto* fetcher = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin));
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
fetcher->StopFetching();
EXPECT_NE(resource, new_resource);
}
TEST_F(ResourceFetcherTest, LinkPreloadResourceMultipleFetchersAndMove) {
auto* fetcher = CreateFetcher();
auto* fetcher2 = CreateFetcher();
KURL url("http://127.0.0.1:8000/foo.png");
RegisterMockedURLLoad(url);
FetchParameters fetch_params_original =
FetchParameters::CreateForTest(ResourceRequest(url));
fetch_params_original.SetLinkPreload(true);
Resource* resource =
MockResource::Fetch(fetch_params_original, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_TRUE(resource->IsLinkPreload());
EXPECT_EQ(0, fetcher->BlockingRequestCount());
// Resource created by parser on the second fetcher
FetchParameters fetch_params2 =
FetchParameters::CreateForTest(ResourceRequest(url));
Persistent<MockResourceClient> client2 =
MakeGarbageCollected<MockResourceClient>();
Resource* new_resource2 =
MockResource::Fetch(fetch_params2, fetcher2, client2);
EXPECT_NE(resource, new_resource2);
EXPECT_EQ(0, fetcher2->BlockingRequestCount());
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
}
// TODO(crbug.com/850785): Reenable this.
#if defined(OS_ANDROID)
#define MAYBE_ContentTypeDataURL DISABLED_ContentTypeDataURL
#else
#define MAYBE_ContentTypeDataURL ContentTypeDataURL
#endif
TEST_F(ResourceFetcherTest, MAYBE_ContentTypeDataURL) {
auto* fetcher = CreateFetcher();
FetchParameters fetch_params = FetchParameters::CreateForTest(
ResourceRequest("data:text/testmimetype,foo"));
Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(resource);
EXPECT_EQ(ResourceStatus::kCached, resource->GetStatus());
EXPECT_EQ("text/testmimetype", resource->GetResponse().MimeType());
EXPECT_EQ("text/testmimetype", resource->GetResponse().HttpContentType());
}
// Request with the Content-ID scheme must not be canceled, even if there is no
// MHTMLArchive to serve them.
// Note: Not blocking it is important because there are some embedders of
// Android WebView that are intercepting Content-ID URLs and serve their own
// resources. Please see https://crbug.com/739658.
TEST_F(ResourceFetcherTest, ContentIdURL) {
KURL url("cid:0123456789@example.com");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
auto* fetcher = CreateFetcher();
// Subresource case.
{
ResourceRequest resource_request(url);
resource_request.SetRequestContext(mojom::blink::RequestContextType::VIDEO);
FetchParameters fetch_params =
FetchParameters::CreateForTest(std::move(resource_request));
RawResource* resource =
RawResource::FetchMedia(fetch_params, fetcher, nullptr);
ASSERT_NE(nullptr, resource);
EXPECT_FALSE(resource->ErrorOccurred());
}
}
TEST_F(ResourceFetcherTest, StaleWhileRevalidate) {
scoped_refptr<const SecurityOrigin> source_origin =
SecurityOrigin::CreateUniqueOpaque();
auto* observer = MakeGarbageCollected<TestResourceLoadObserver>();
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
auto* fetcher = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin),
context);
fetcher->SetResourceLoadObserver(observer);
KURL url("http://127.0.0.1:8000/foo.html");
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl,
"max-age=0, stale-while-revalidate=40");
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(resource);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource->IsLoaded());
EXPECT_TRUE(GetMemoryCache()->Contains(resource));
ResourceRequest resource_request(url);
resource_request.SetRequestContext(
mojom::blink::RequestContextType::INTERNAL);
FetchParameters fetch_params2 =
FetchParameters::CreateForTest(std::move(resource_request));
Resource* new_resource = MockResource::Fetch(fetch_params2, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_TRUE(resource->IsLoaded());
// Advance the clock, make sure the original resource gets removed from the
// memory cache after the revalidation completes.
platform_->AdvanceClockSeconds(1);
ResourceResponse revalidate_response(url);
revalidate_response.SetHttpStatusCode(200);
platform_->GetURLLoaderMockFactory()->UnregisterURL(url);
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(revalidate_response),
test::PlatformTestDataPath(kTestResourceFilename));
new_resource = MockResource::Fetch(fetch_params2, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
EXPECT_TRUE(GetMemoryCache()->Contains(resource));
static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get())
->RunUntilIdle();
base::Optional<PartialResourceRequest> swr_request =
observer->GetLastRequest();
ASSERT_TRUE(swr_request.has_value());
EXPECT_EQ(ResourceLoadPriority::kVeryLow, swr_request->Priority());
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
EXPECT_FALSE(GetMemoryCache()->Contains(resource));
}
TEST_F(ResourceFetcherTest, CachedResourceShouldNotCrashByNullURL) {
auto* fetcher = CreateFetcher();
// Make sure |cached_resources_map_| is not empty, so that HashMap lookup
// won't take a fast path.
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
platform_->GetURLLoaderMockFactory()->RegisterURL(
url, WrappedResourceResponse(response),
test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(url));
MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_NE(fetcher->CachedResource(url), nullptr);
ASSERT_EQ(fetcher->CachedResource(KURL()), nullptr);
}
TEST_F(ResourceFetcherTest, DeprioritizeSubframe) {
auto& properties = *MakeGarbageCollected<TestResourceFetcherProperties>();
auto* fetcher = CreateFetcher(properties);
ResourceRequest request(KURL("https://www.example.com/"));
{
// Subframe deprioritization is disabled (main frame case).
properties.SetIsMainFrame(true);
properties.SetIsSubframeDeprioritizationEnabled(false);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kScript, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kHigh);
}
{
// Subframe deprioritization is disabled (nested frame case).
properties.SetIsMainFrame(false);
properties.SetIsSubframeDeprioritizationEnabled(false);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kScript, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kHigh);
}
{
// Subframe deprioritization is enabled (main frame case), kHigh.
properties.SetIsMainFrame(true);
properties.SetIsSubframeDeprioritizationEnabled(true);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kScript, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kHigh);
}
{
// Subframe deprioritization is enabled (nested frame case), kHigh => kLow.
properties.SetIsMainFrame(false);
properties.SetIsSubframeDeprioritizationEnabled(true);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kScript, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kLow);
}
{
// Subframe deprioritization is enabled (main frame case), kMedium.
properties.SetIsMainFrame(true);
properties.SetIsSubframeDeprioritizationEnabled(true);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kMock, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kMedium);
}
{
// Subframe deprioritization is enabled (nested frame case), kMedium =>
// kLowest.
properties.SetIsMainFrame(false);
properties.SetIsSubframeDeprioritizationEnabled(true);
const auto priority = fetcher->ComputeLoadPriorityForTesting(
ResourceType::kMock, request, ResourcePriority::kNotVisible,
FetchParameters::DeferOption::kNoDefer,
FetchParameters::SpeculativePreloadType::kNotSpeculative,
false /* is_link_preload */);
EXPECT_EQ(priority, ResourceLoadPriority::kLowest);
}
}
TEST_F(ResourceFetcherTest, Detach) {
DetachableResourceFetcherProperties& properties =
MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable();
auto* const fetcher = MakeGarbageCollected<ResourceFetcher>(
ResourceFetcherInit(properties, MakeGarbageCollected<MockFetchContext>(),
CreateTaskRunner(), CreateTaskRunner(),
MakeGarbageCollected<TestLoaderFactory>(
platform_->GetURLLoaderMockFactory()),
MakeGarbageCollected<MockContextLifecycleNotifier>(),
nullptr /* back_forward_cache_loader_helper */));
EXPECT_EQ(&properties, &fetcher->GetProperties());
EXPECT_FALSE(properties.IsDetached());
fetcher->ClearContext();
// ResourceFetcher::GetProperties always returns the same object.
EXPECT_EQ(&properties, &fetcher->GetProperties());
EXPECT_TRUE(properties.IsDetached());
}
// TODO(crbug.com/850785): Reenable this.
#if defined(OS_ANDROID)
#define MAYBE_SetIsExternalRequest DISABLED_SetIsExternalRequest
#else
#define MAYBE_SetIsExternalRequest SetIsExternalRequest
#endif
TEST_F(ResourceFetcherTest, MAYBE_SetIsExternalRequest) {
struct TestCase {
const char* url;
bool is_external_expectation_public;
bool is_external_expectation_private;
bool is_external_expectation_local;
} cases[] = {{"data:text/html,whatever", false, false, false},
{"file:///etc/passwd", false, false, false},
{"blob:http://example.com/", false, false, false},
{"http://example.com/", false, false, false},
{"https://example.com/", false, false, false},
{"http://192.168.1.1:8000/", true, false, false},
{"http://10.1.1.1:8000/", true, false, false},
{"http://localhost/", true, true, false},
{"http://127.0.0.1/", true, true, false},
{"http://127.0.0.1:8000/", true, true, false}};
for (const auto address_space : {network::mojom::IPAddressSpace::kPublic,
network::mojom::IPAddressSpace::kPrivate,
network::mojom::IPAddressSpace::kLocal}) {
auto* fetcher =
CreateFetcher(*MakeGarbageCollected<TestResourceFetcherProperties>(
CreateFetchClientSettingsObject(address_space)),
MakeGarbageCollected<MockFetchContext>());
EXPECT_EQ(address_space, fetcher->GetProperties()
.GetFetchClientSettingsObject()
.GetAddressSpace());
for (const bool enabled : {false, true}) {
ScopedCorsRFC1918ForTest cors_rfc1918(enabled);
for (const auto& test : cases) {
SCOPED_TRACE(test.url);
FetchParameters fetch_params =
FetchParameters::CreateForTest(ResourceRequest(test.url));
RegisterMockedURLLoad(fetch_params.Url());
Resource* new_resource =
MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(new_resource);
if (enabled) {
bool expected;
switch (address_space) {
case network::mojom::IPAddressSpace::kPublic:
expected = test.is_external_expectation_public;
break;
case network::mojom::IPAddressSpace::kPrivate:
expected = test.is_external_expectation_private;
break;
case network::mojom::IPAddressSpace::kLocal:
expected = test.is_external_expectation_local;
break;
case network::mojom::IPAddressSpace::kUnknown:
NOTREACHED();
break;
}
EXPECT_EQ(expected,
new_resource->GetResourceRequest().IsExternalRequest());
} else {
EXPECT_FALSE(new_resource->GetResourceRequest().IsExternalRequest());
}
// Clear mocked URLs from mock loader and MemoryCache.
platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
platform_->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
}
}
}
} // namespace blink