diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc new file mode 100644 index 00000000000..679e35ddf20 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc @@ -0,0 +1,852 @@ +/* + * 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 "services/network/public/mojom/request_context_frame_type.mojom-shared.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.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/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_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/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/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.h" +#include "third_party/blink/renderer/platform/wtf/optional.h" + +namespace blink { + +namespace { + +constexpr char kTestResourceFilename[] = "white-1x1.png"; +constexpr char kTestResourceMimeType[] = "image/png"; +constexpr int kTestResourceSize = 103; // size of white-1x1.png + +void RegisterMockedURLLoadWithCustomResponse(const KURL& url, + const ResourceResponse& response) { + URLTestHelpers::RegisterMockedURLLoadWithCustomResponse( + url, test::PlatformTestDataPath(kTestResourceFilename), + WrappedResourceResponse(response)); +} + +void RegisterMockedURLLoad(const KURL& url) { + URLTestHelpers::RegisterMockedURLLoad( + url, test::PlatformTestDataPath(kTestResourceFilename), + kTestResourceMimeType); +} + +} // namespace + +class ResourceFetcherTest : public testing::Test { + public: + ResourceFetcherTest() = default; + ~ResourceFetcherTest() override { GetMemoryCache()->EvictResources(); } + + protected: + MockFetchContext* Context() { return platform_->Context(); } + void AddResourceToMemoryCache( + Resource* resource, + scoped_refptr<const SecurityOrigin> source_origin) { + resource->SetSourceOrigin(source_origin); + GetMemoryCache()->Add(resource); + } + + 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. + ResourceFetcher* fetcher = + ResourceFetcher::Create(&FetchContext::NullInstance()); + + ResourceRequest resource_request(secure_url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(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. + fetcher->StartLoad(RawResource::CreateForTest(secure_url, Resource::kRaw)); +} + +TEST_F(ResourceFetcherTest, UseExistingResource) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + RegisterMockedURLLoadWithCustomResponse(url, response); + + FetchParameters fetch_params{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); +} + +// 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::CreateUnique(); + Context()->SetSecurityOrigin(source_origin); + KURL url("http://127.0.0.1:8000/foo.html"); + Resource* resource = RawResource::CreateForTest(url, Resource::kRaw); + AddResourceToMemoryCache(resource, source_origin); + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + resource->ResponseReceived(response, nullptr); + resource->FinishForTest(); + + // Fetch the cached resource. The request to DispatchWillSendRequest should + // preserve the ad bit. + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetIsAdResource(); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(resource_request); + platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), ""); + Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); + + EXPECT_EQ(resource, new_resource); + WTF::Optional<ResourceRequest> new_request = + Context()->RequestFromWillSendRequest(); + EXPECT_TRUE(new_request.has_value()); + EXPECT_TRUE(new_request.value().IsAdResource()); +} + +TEST_F(ResourceFetcherTest, Vary) { + scoped_refptr<const SecurityOrigin> source_origin = + SecurityOrigin::CreateUnique(); + Context()->SetSecurityOrigin(source_origin); + + KURL url("http://127.0.0.1:8000/foo.html"); + Resource* resource = RawResource::CreateForTest(url, Resource::kRaw); + AddResourceToMemoryCache(resource, source_origin); + + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + response.SetHTTPHeaderField(HTTPNames::Vary, "*"); + resource->ResponseReceived(response, nullptr); + resource->FinishForTest(); + ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url))); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(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, NavigationTimingInfo) { + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetFrameType( + network::mojom::RequestContextFrameType::kNested); + resource_request.SetRequestContext(WebURLRequest::kRequestContextForm); + FetchParameters fetch_params(resource_request); + platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), ""); + Resource* resource = RawResource::FetchMainResource( + fetch_params, fetcher, nullptr, SubstituteData()); + resource->ResponseReceived(response, nullptr); + EXPECT_EQ(resource->GetType(), Resource::kMainResource); + + ResourceTimingInfo* navigation_timing_info = + fetcher->GetNavigationTimingInfo(); + ASSERT_TRUE(navigation_timing_info); + long long encoded_data_length = 123; + resource->Loader()->DidFinishLoading(0.0, encoded_data_length, 0, 0, false); + EXPECT_EQ(navigation_timing_info->TransferSize(), encoded_data_length); + + // When there are redirects. + KURL redirect_url("http://127.0.0.1:8000/redirect.html"); + ResourceResponse redirect_response(redirect_url); + redirect_response.SetHTTPStatusCode(200); + long long redirect_encoded_data_length = 123; + redirect_response.SetEncodedDataLength(redirect_encoded_data_length); + ResourceRequest redirect_resource_request(url); + fetcher->RecordResourceTimingOnRedirect(resource, redirect_response, false); + EXPECT_EQ(navigation_timing_info->TransferSize(), + encoded_data_length + redirect_encoded_data_length); +} + +TEST_F(ResourceFetcherTest, VaryOnBack) { + scoped_refptr<const SecurityOrigin> source_origin = + SecurityOrigin::CreateUnique(); + Context()->SetSecurityOrigin(source_origin); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.html"); + Resource* resource = RawResource::CreateForTest(url, Resource::kRaw); + AddResourceToMemoryCache(resource, source_origin); + + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + response.SetHTTPHeaderField(HTTPNames::Vary, "*"); + resource->ResponseReceived(response, nullptr); + resource->FinishForTest(); + ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url))); + + ResourceRequest resource_request(url); + resource_request.SetCacheMode(mojom::FetchCacheMode::kForceCache); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(resource_request); + Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); + EXPECT_EQ(resource, new_resource); +} + +TEST_F(ResourceFetcherTest, VaryResource) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + response.SetHTTPHeaderField(HTTPNames::Vary, "*"); + RegisterMockedURLLoadWithCustomResponse(url, response); + + FetchParameters fetch_params_original{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{ResourceRequest(url)}; + Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr); + EXPECT_EQ(resource, new_resource); +} + +class RequestSameResourceOnComplete + : public GarbageCollectedFinalized<RequestSameResourceOnComplete>, + public RawResourceClient { + USING_GARBAGE_COLLECTED_MIXIN(RequestSameResourceOnComplete); + + public: + explicit RequestSameResourceOnComplete(FetchParameters& params, + ResourceFetcher* fetcher) + : notify_finished_called_(false), + source_origin_(fetcher->Context().GetSecurityOrigin()) { + MockResource::Fetch(params, fetcher, this); + } + + void NotifyFinished(Resource* resource) override { + EXPECT_EQ(GetResource(), resource); + MockFetchContext* context = + MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource); + context->SetSecurityOrigin(source_origin_); + ResourceFetcher* fetcher2 = ResourceFetcher::Create(context); + ResourceRequest resource_request2(GetResource()->Url()); + resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache); + FetchParameters fetch_params2(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(blink::Visitor* visitor) override { + RawResourceClient::Trace(visitor); + } + + String DebugName() const override { return "RequestSameResourceOnComplete"; } + + private: + bool notify_finished_called_; + scoped_refptr<const SecurityOrigin> source_origin_; +}; + +TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) { + scoped_refptr<const SecurityOrigin> source_origin = + SecurityOrigin::CreateUnique(); + Context()->SetSecurityOrigin(source_origin); + + KURL url("http://127.0.0.1:8000/foo.png"); + + ResourceResponse response(url); + response.SetHTTPStatusCode(200); + response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600"); + response.SetHTTPHeaderField(HTTPNames::ETag, "1234567890"); + RegisterMockedURLLoadWithCustomResponse(url, response); + + ResourceFetcher* fetcher1 = ResourceFetcher::Create(Context()); + ResourceRequest request1(url); + request1.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); + FetchParameters fetch_params1(request1); + Persistent<RequestSameResourceOnComplete> client = + new RequestSameResourceOnComplete(fetch_params1, fetcher1); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + EXPECT_TRUE(client->NotifyFinishedCalled()); +} + +TEST_F(ResourceFetcherTest, DontReuseMediaDataUrl) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest request(KURL("data:text/html,foo")); + request.SetRequestContext(WebURLRequest::kRequestContextVideo); + request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); + ResourceLoaderOptions options; + options.data_buffering_policy = kDoNotBufferData; + options.initiator_info.name = FetchInitiatorTypeNames::internal; + FetchParameters fetch_params(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 GarbageCollectedFinalized<ServeRequestsOnCompleteClient>, + public RawResourceClient { + USING_GARBAGE_COLLECTED_MIXIN(ServeRequestsOnCompleteClient); + + public: + void NotifyFinished(Resource*) override { + Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + ClearResource(); + } + + // No callbacks should be received except for the NotifyFinished() triggered + // by ResourceLoader::Cancel(). + void DataSent(Resource*, unsigned long long, unsigned long long) override { + ASSERT_TRUE(false); + } + void ResponseReceived(Resource*, + const ResourceResponse&, + std::unique_ptr<WebDataConsumerHandle>) override { + ASSERT_TRUE(false); + } + void SetSerializedCachedMetadata(Resource*, const char*, size_t) 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*, int) override { ASSERT_TRUE(false); } + void DidReceiveResourceTiming(Resource*, const ResourceTimingInfo&) override { + ASSERT_TRUE(false); + } + + void Trace(blink::Visitor* visitor) override { + RawResourceClient::Trace(visitor); + } + + String DebugName() const override { return "ServeRequestsOnCompleteClient"; } +}; + +// 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); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(resource_request); + Persistent<ServeRequestsOnCompleteClient> client = + new ServeRequestsOnCompleteClient(); + Resource* resource = RawResource::Fetch(fetch_params, fetcher, client); + resource->Loader()->Cancel(); +} + +class ScopedMockRedirectRequester { + STACK_ALLOCATED(); + WTF_MAKE_NONCOPYABLE(ScopedMockRedirectRequester); + + public: + explicit ScopedMockRedirectRequester(MockFetchContext* context) + : context_(context) {} + + void RegisterRedirect(const WebString& from_url, const WebString& to_url) { + KURL redirect_url(from_url); + WebURLResponse redirect_response; + redirect_response.SetURL(redirect_url); + redirect_response.SetHTTPStatusCode(301); + redirect_response.SetHTTPHeaderField(HTTPNames::Location, to_url); + redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes); + Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( + redirect_url, redirect_response, ""); + } + + void RegisterFinalResource(const WebString& url) { + KURL final_url(url); + RegisterMockedURLLoad(final_url); + } + + void Request(const WebString& url) { + ResourceFetcher* fetcher = ResourceFetcher::Create(context_); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(resource_request); + RawResource::Fetch(fetch_params, fetcher, nullptr); + Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + } + + private: + Member<MockFetchContext> context_; +}; + +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"; + ScopedMockRedirectRequester requester(Context()); + 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"; + ScopedMockRedirectRequester requester(Context()); + 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"; + ScopedMockRedirectRequester requester(Context()); + 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); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(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); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextPing); + FetchParameters fetch_params(resource_request); + Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr); + EXPECT_EQ(ResourceLoadPriority::kVeryLow, + resource->GetResourceRequest().Priority()); +} + +TEST_F(ResourceFetcherTest, PreloadResourceTwice) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_original{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{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) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + // Link preload preload scanner + FetchParameters fetch_params_original{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{ResourceRequest(url)}; + Resource* preload_scanner_resource = + MockResource::Fetch(fetch_params_preload_scanner, fetcher, nullptr); + EXPECT_EQ(resource, preload_scanner_resource); + EXPECT_FALSE(resource->IsLinkPreload()); + + // Resource created by parser + FetchParameters fetch_params{ResourceRequest(url)}; + Persistent<MockResourceClient> client = new MockResourceClient; + Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, client); + EXPECT_EQ(resource, new_resource); + EXPECT_FALSE(resource->IsLinkPreload()); + + // DCL reached + fetcher->ClearPreloads(ResourceFetcher::kClearSpeculativeMarkupPreloads); + EXPECT_TRUE(GetMemoryCache()->Contains(resource)); + EXPECT_FALSE(resource->IsUnusedPreload()); +} + +TEST_F(ResourceFetcherTest, PreloadMatchWithBypassingCache) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_original{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{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_FALSE(resource->IsLinkPreload()); +} + +TEST_F(ResourceFetcherTest, CrossFramePreloadMatchIsNotAllowed) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_original{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{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) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_for_request{ResourceRequest(url)}; + FetchParameters fetch_params_for_preload{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) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_for_request{ResourceRequest(url)}; + FetchParameters fetch_params_for_preload{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, SpeculativePreloadShouldBePromotedToLinkePreload) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_for_request{ResourceRequest(url)}; + FetchParameters fetch_params_for_speculative_preload{ResourceRequest(url)}; + fetch_params_for_speculative_preload.SetSpeculativePreloadType( + FetchParameters::SpeculativePreloadType::kInDocument); + FetchParameters fetch_params_for_link_preload{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_FALSE(resource1->IsLinkPreload()); +} + +TEST_F(ResourceFetcherTest, Revalidate304) { + scoped_refptr<const SecurityOrigin> source_origin = + SecurityOrigin::CreateUnique(); + Context()->SetSecurityOrigin(source_origin); + + KURL url("http://127.0.0.1:8000/foo.html"); + Resource* resource = RawResource::CreateForTest(url, Resource::kRaw); + AddResourceToMemoryCache(resource, source_origin); + + ResourceResponse response(url); + response.SetHTTPStatusCode(304); + response.SetHTTPHeaderField("etag", "1234567890"); + resource->ResponseReceived(response, nullptr); + resource->FinishForTest(); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal); + FetchParameters fetch_params(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) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context()); + + KURL url("http://127.0.0.1:8000/foo.png"); + RegisterMockedURLLoad(url); + + FetchParameters fetch_params_original{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{ResourceRequest(url)}; + Persistent<MockResourceClient> client2 = new MockResourceClient; + Resource* new_resource2 = + MockResource::Fetch(fetch_params2, fetcher2, client2); + EXPECT_NE(resource, new_resource2); + EXPECT_EQ(0, fetcher2->BlockingRequestCount()); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); +} + +TEST_F(ResourceFetcherTest, ContentTypeDataURL) { + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + FetchParameters fetch_params{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); + RegisterMockedURLLoadWithCustomResponse(url, response); + + ResourceFetcher* fetcher = ResourceFetcher::Create(Context()); + + // Main resource case. + { + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextIframe); + resource_request.SetFrameType( + network::mojom::RequestContextFrameType::kNested); + FetchParameters fetch_params(resource_request); + RawResource* resource = RawResource::FetchMainResource( + fetch_params, fetcher, nullptr, SubstituteData()); + ASSERT_NE(nullptr, resource); + EXPECT_FALSE(resource->ErrorOccurred()); + } + + // Subresource case. + { + ResourceRequest resource_request(url); + resource_request.SetRequestContext(WebURLRequest::kRequestContextVideo); + FetchParameters fetch_params(resource_request); + RawResource* resource = + RawResource::FetchMedia(fetch_params, fetcher, nullptr); + ASSERT_NE(nullptr, resource); + EXPECT_FALSE(resource->ErrorOccurred()); + } +} + +} // namespace blink |