| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "mojo/public/c/system/data_pipe.h" |
| #include "services/network/public/mojom/fetch_api.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/platform/resource_load_info_notifier_wrapper.h" |
| #include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" |
| #include "third_party/blink/public/platform/web_url_loader.h" |
| #include "third_party/blink/public/platform/web_url_loader_factory.h" |
| #include "third_party/blink/public/platform/web_url_request_extra_data.h" |
| #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" |
| #include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h" |
| #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h" |
| #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h" |
| #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h" |
| #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" |
| |
| namespace blink { |
| |
| const char kCnameAliasHadAliasesHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.HadAliases"; |
| const char kCnameAliasIsInvalidCountHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.InvalidCount"; |
| const char kCnameAliasIsRedundantCountHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.RedundantCount"; |
| const char kCnameAliasListLengthHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.ListLength"; |
| const char kCnameAliasWasAdTaggedHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.WasAdTaggedBasedOnAlias"; |
| const char kCnameAliasWasBlockedHistogram[] = |
| "SubresourceFilter.CnameAlias.Renderer.WasBlockedBasedOnAlias"; |
| |
| class ResourceLoaderTest : public testing::Test { |
| DISALLOW_COPY_AND_ASSIGN(ResourceLoaderTest); |
| |
| public: |
| enum class From { |
| kServiceWorker, |
| kNetwork, |
| }; |
| |
| ResourceLoaderTest() |
| : foo_url_("https://foo.test"), bar_url_("https://bar.test") {} |
| |
| protected: |
| using RequestMode = network::mojom::RequestMode; |
| using FetchResponseType = network::mojom::FetchResponseType; |
| |
| struct TestCase { |
| const KURL url; |
| const RequestMode request_mode; |
| const From from; |
| const scoped_refptr<const SecurityOrigin> allowed_origin; |
| const FetchResponseType original_response_type; |
| const FetchResponseType expectation; |
| }; |
| |
| ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> |
| platform_; |
| const KURL foo_url_; |
| const KURL bar_url_; |
| |
| class NoopLoaderFactory final : public ResourceFetcher::LoaderFactory { |
| std::unique_ptr<WebURLLoader> CreateURLLoader( |
| const ResourceRequest& request, |
| const ResourceLoaderOptions& options, |
| scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, |
| scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, |
| WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) |
| override { |
| return std::make_unique<NoopWebURLLoader>( |
| std::move(freezable_task_runner)); |
| } |
| std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() override { |
| return Platform::Current()->CreateCodeCacheLoader(); |
| } |
| }; |
| |
| static scoped_refptr<base::SingleThreadTaskRunner> CreateTaskRunner() { |
| return base::MakeRefCounted<scheduler::FakeTaskRunner>(); |
| } |
| |
| ResourceFetcher* MakeResourceFetcher( |
| TestResourceFetcherProperties* properties, |
| FetchContext* context) { |
| return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( |
| properties->MakeDetachable(), context, CreateTaskRunner(), |
| CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), |
| MakeGarbageCollected<MockContextLifecycleNotifier>(), |
| nullptr /* back_forward_cache_loader_helper */)); |
| } |
| |
| private: |
| class NoopWebURLLoader final : public WebURLLoader { |
| public: |
| explicit NoopWebURLLoader( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : task_runner_(task_runner) {} |
| ~NoopWebURLLoader() override = default; |
| void LoadSynchronously( |
| std::unique_ptr<network::ResourceRequest> request, |
| scoped_refptr<WebURLRequestExtraData> url_request_extra_data, |
| int requestor_id, |
| bool pass_response_pipe_to_client, |
| bool no_mime_sniffing, |
| base::TimeDelta timeout_interval, |
| WebURLLoaderClient*, |
| WebURLResponse&, |
| base::Optional<WebURLError>&, |
| WebData&, |
| int64_t& encoded_data_length, |
| int64_t& encoded_body_length, |
| WebBlobInfo& downloaded_blob, |
| std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper> |
| resource_load_info_notifier_wrapper) override { |
| NOTREACHED(); |
| } |
| void LoadAsynchronously( |
| std::unique_ptr<network::ResourceRequest> request, |
| scoped_refptr<WebURLRequestExtraData> url_request_extra_data, |
| int requestor_id, |
| bool no_mime_sniffing, |
| std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper> |
| resource_load_info_notifier_wrapper, |
| WebURLLoaderClient*) override {} |
| |
| void SetDefersLoading(WebURLLoader::DeferType) override {} |
| void DidChangePriority(WebURLRequest::Priority, int) override { |
| NOTREACHED(); |
| } |
| scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForBodyLoader() |
| override { |
| return task_runner_; |
| } |
| |
| private: |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| }; |
| }; |
| |
| std::ostream& operator<<(std::ostream& o, const ResourceLoaderTest::From& f) { |
| switch (f) { |
| case ResourceLoaderTest::From::kServiceWorker: |
| o << "service worker"; |
| break; |
| case ResourceLoaderTest::From::kNetwork: |
| o << "network"; |
| break; |
| } |
| return o; |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadResponseBody) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| |
| KURL url("https://www.example.com/"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = RawResource::Fetch(params, fetcher, nullptr); |
| ResourceLoader* loader = resource->Loader(); |
| |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| mojo::ScopedDataPipeProducerHandle producer; |
| mojo::ScopedDataPipeConsumerHandle consumer; |
| MojoCreateDataPipeOptions options; |
| options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; |
| options.element_num_bytes = 1; |
| options.capacity_num_bytes = 3; |
| |
| MojoResult result = CreateDataPipe(&options, producer, consumer); |
| ASSERT_EQ(result, MOJO_RESULT_OK); |
| |
| loader->DidReceiveResponse(WrappedResourceResponse(response)); |
| loader->DidStartLoadingResponseBody(std::move(consumer)); |
| loader->DidFinishLoading(base::TimeTicks(), 0, 0, 0, false); |
| |
| uint32_t num_bytes = 2; |
| result = producer->WriteData("he", &num_bytes, MOJO_WRITE_DATA_FLAG_NONE); |
| ASSERT_EQ(result, MOJO_RESULT_OK); |
| ASSERT_EQ(num_bytes, 2u); |
| |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) |
| ->RunUntilIdle(); |
| |
| num_bytes = 3; |
| result = producer->WriteData("llo", &num_bytes, MOJO_WRITE_DATA_FLAG_NONE); |
| ASSERT_EQ(result, MOJO_RESULT_OK); |
| ASSERT_EQ(num_bytes, 3u); |
| |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) |
| ->RunUntilIdle(); |
| |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| producer.reset(); |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) |
| ->RunUntilIdle(); |
| |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| StringBuilder data; |
| for (const auto& span : *buffer) { |
| data.Append(span.data(), span.size()); |
| } |
| EXPECT_EQ(data.ToString(), "hello"); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndNonStream) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| |
| // Fetch a data url. |
| KURL url("data:text/plain,Hello%20World!"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = RawResource::Fetch(params, fetcher, nullptr); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) |
| ->RunUntilIdle(); |
| |
| // The resource has a parsed body. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| StringBuilder data; |
| for (const auto& span : *buffer) { |
| data.Append(span.data(), span.size()); |
| } |
| EXPECT_EQ(data.ToString(), "Hello World!"); |
| } |
| |
| // Helper class which stores a BytesConsumer passed by RawResource and reads the |
| // bytes when ReadThroughBytesConsumer is called. |
| class TestRawResourceClient final |
| : public GarbageCollected<TestRawResourceClient>, |
| public RawResourceClient { |
| public: |
| TestRawResourceClient() = default; |
| |
| // Implements RawResourceClient. |
| void ResponseBodyReceived(Resource* resource, |
| BytesConsumer& bytes_consumer) override { |
| body_ = &bytes_consumer; |
| } |
| String DebugName() const override { return "TestRawResourceClient"; } |
| |
| void Trace(Visitor* visitor) const override { |
| visitor->Trace(body_); |
| RawResourceClient::Trace(visitor); |
| } |
| |
| BytesConsumer* body() { return body_; } |
| |
| private: |
| Member<BytesConsumer> body_; |
| }; |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndStream) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| scheduler::FakeTaskRunner* task_runner = |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); |
| |
| // Fetch a data url as a stream on response. |
| KURL url("data:text/plain,Hello%20World!"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| request.SetUseStreamOnResponse(true); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| auto* raw_resource_client = MakeGarbageCollected<TestRawResourceClient>(); |
| Resource* resource = RawResource::Fetch(params, fetcher, raw_resource_client); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| task_runner->RunUntilIdle(); |
| |
| // It's still pending because we don't read the body yet. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| // Read through the bytes consumer passed back from the ResourceLoader. |
| auto* test_reader = MakeGarbageCollected<BytesConsumerTestReader>( |
| raw_resource_client->body()); |
| Vector<char> body; |
| BytesConsumer::Result result; |
| std::tie(result, body) = test_reader->Run(task_runner); |
| EXPECT_EQ(result, BytesConsumer::Result::kDone); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| EXPECT_EQ(std::string(body.data(), body.size()), "Hello World!"); |
| |
| // The body is not set to ResourceBuffer since the response body is requested |
| // as a stream. |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| EXPECT_FALSE(buffer); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_AsyncEmptyData) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| |
| // Fetch an empty data url. |
| KURL url("data:text/html,"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = RawResource::Fetch(params, fetcher, nullptr); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) |
| ->RunUntilIdle(); |
| |
| // It successfully finishes, and no buffer is propagated. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| EXPECT_FALSE(buffer); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_Sync) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| |
| // Fetch a data url synchronously. |
| KURL url("data:text/plain,Hello%20World!"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = |
| RawResource::FetchSynchronously(params, fetcher, nullptr); |
| |
| // The resource has a parsed body. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| StringBuilder data; |
| for (const auto& span : *buffer) { |
| data.Append(span.data(), span.size()); |
| } |
| EXPECT_EQ(data.ToString(), "Hello World!"); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_SyncEmptyData) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| |
| // Fetch an empty data url synchronously. |
| KURL url("data:text/html,"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = |
| RawResource::FetchSynchronously(params, fetcher, nullptr); |
| |
| // It successfully finishes, and no buffer is propagated. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| EXPECT_FALSE(buffer); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndNonStream) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| scheduler::FakeTaskRunner* task_runner = |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); |
| |
| // Fetch a data url. |
| KURL url("data:text/plain,Hello%20World!"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = RawResource::Fetch(params, fetcher, nullptr); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| // The resource should still be pending since it's deferred. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| // The resource should still be pending since it's deferred again. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| // The resource should still be pending if it's unset and set in a single |
| // task. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred); |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| |
| // The resource has a parsed body. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| scoped_refptr<const SharedBuffer> buffer = resource->ResourceBuffer(); |
| StringBuilder data; |
| for (const auto& span : *buffer) { |
| data.Append(span.data(), span.size()); |
| } |
| EXPECT_EQ(data.ToString(), "Hello World!"); |
| } |
| |
| TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndStream) { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| scheduler::FakeTaskRunner* task_runner = |
| static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); |
| |
| // Fetch a data url as a stream on response. |
| KURL url("data:text/plain,Hello%20World!"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| request.SetUseStreamOnResponse(true); |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| auto* raw_resource_client = MakeGarbageCollected<TestRawResourceClient>(); |
| Resource* resource = RawResource::Fetch(params, fetcher, raw_resource_client); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| |
| // It's still pending because the body should not provided yet. |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| EXPECT_FALSE(raw_resource_client->body()); |
| |
| // The body should be provided since not deferring now, but it's still pending |
| // since we haven't read the body yet. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| EXPECT_TRUE(raw_resource_client->body()); |
| |
| // The resource should still be pending when it's set to deferred again. No |
| // body is provided when deferred. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| const char* buffer; |
| size_t available; |
| BytesConsumer::Result result = |
| raw_resource_client->body()->BeginRead(&buffer, &available); |
| EXPECT_EQ(BytesConsumer::Result::kShouldWait, result); |
| |
| // The resource should still be pending if it's unset and set in a single |
| // task. No body is provided when deferred. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred); |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kDeferred); |
| task_runner->RunUntilIdle(); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); |
| result = raw_resource_client->body()->BeginRead(&buffer, &available); |
| EXPECT_EQ(BytesConsumer::Result::kShouldWait, result); |
| |
| // Read through the bytes consumer passed back from the ResourceLoader. |
| fetcher->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred); |
| task_runner->RunUntilIdle(); |
| auto* test_reader = MakeGarbageCollected<BytesConsumerTestReader>( |
| raw_resource_client->body()); |
| Vector<char> body; |
| std::tie(result, body) = test_reader->Run(task_runner); |
| EXPECT_EQ(resource->GetStatus(), ResourceStatus::kCached); |
| EXPECT_EQ(std::string(body.data(), body.size()), "Hello World!"); |
| |
| // The body is not set to ResourceBuffer since the response body is requested |
| // as a stream. |
| EXPECT_FALSE(resource->ResourceBuffer()); |
| } |
| |
| class ResourceLoaderIsolatedCodeCacheTest : public ResourceLoaderTest { |
| protected: |
| bool LoadAndCheckIsolatedCodeCache(ResourceResponse response) { |
| const scoped_refptr<const SecurityOrigin> origin = |
| SecurityOrigin::Create(foo_url_); |
| |
| auto* properties = |
| MakeGarbageCollected<TestResourceFetcherProperties>(origin); |
| FetchContext* context = MakeGarbageCollected<MockFetchContext>(); |
| auto* fetcher = MakeResourceFetcher(properties, context); |
| ResourceRequest request; |
| request.SetUrl(foo_url_); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| FetchParameters fetch_parameters = |
| FetchParameters::CreateForTest(std::move(request)); |
| Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); |
| ResourceLoader* loader = resource->Loader(); |
| |
| loader->DidReceiveResponse(WrappedResourceResponse(response)); |
| return loader->should_use_isolated_code_cache_; |
| } |
| }; |
| |
| TEST_F(ResourceLoaderIsolatedCodeCacheTest, ResponseFromNetwork) { |
| ResourceResponse response(foo_url_); |
| response.SetHttpStatusCode(200); |
| EXPECT_EQ(true, LoadAndCheckIsolatedCodeCache(response)); |
| } |
| |
| TEST_F(ResourceLoaderIsolatedCodeCacheTest, |
| SyntheticResponseFromServiceWorker) { |
| ResourceResponse response(foo_url_); |
| response.SetHttpStatusCode(200); |
| response.SetWasFetchedViaServiceWorker(true); |
| EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response)); |
| } |
| |
| TEST_F(ResourceLoaderIsolatedCodeCacheTest, |
| PassThroughResponseFromServiceWorker) { |
| ResourceResponse response(foo_url_); |
| response.SetHttpStatusCode(200); |
| response.SetWasFetchedViaServiceWorker(true); |
| response.SetUrlListViaServiceWorker(Vector<KURL>(1, foo_url_)); |
| EXPECT_EQ(true, LoadAndCheckIsolatedCodeCache(response)); |
| } |
| |
| TEST_F(ResourceLoaderIsolatedCodeCacheTest, |
| DifferentUrlResponseFromServiceWorker) { |
| ResourceResponse response(foo_url_); |
| response.SetHttpStatusCode(200); |
| response.SetWasFetchedViaServiceWorker(true); |
| response.SetUrlListViaServiceWorker(Vector<KURL>(1, bar_url_)); |
| EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response)); |
| } |
| |
| TEST_F(ResourceLoaderIsolatedCodeCacheTest, CacheResponseFromServiceWorker) { |
| ResourceResponse response(foo_url_); |
| response.SetHttpStatusCode(200); |
| response.SetWasFetchedViaServiceWorker(true); |
| response.SetCacheStorageCacheName("dummy"); |
| // The browser does support code cache for cache_storage Responses, but they |
| // are loaded via a different mechanism. So the ResourceLoader code caching |
| // value should be false here. |
| EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response)); |
| } |
| |
| class ResourceLoaderSubresourceFilterCnameAliasTest |
| : public ResourceLoaderTest { |
| public: |
| ResourceLoaderSubresourceFilterCnameAliasTest() = default; |
| ~ResourceLoaderSubresourceFilterCnameAliasTest() override = default; |
| |
| void SetUp() override { |
| feature_list_.InitAndEnableFeature( |
| features::kSendCnameAliasesToSubresourceFilterFromRenderer); |
| ResourceLoaderTest::SetUp(); |
| } |
| |
| base::HistogramTester* histogram_tester() { return &histogram_tester_; } |
| |
| void SetMockSubresourceFilterBlockLists(Vector<String> blocked_urls, |
| Vector<String> tagged_urls) { |
| blocked_urls_ = blocked_urls; |
| tagged_urls_ = tagged_urls; |
| } |
| |
| Resource* CreateResource(ResourceRequest request) { |
| FetchParameters params = FetchParameters::CreateForTest(std::move(request)); |
| auto* fetcher = MakeResourceFetcherWithMockSubresourceFilter(); |
| return RawResource::Fetch(params, fetcher, nullptr); |
| } |
| |
| void GiveResponseToLoader(ResourceResponse response, ResourceLoader* loader) { |
| CreateMojoDataPipe(); |
| loader->DidReceiveResponse(WrappedResourceResponse(response)); |
| } |
| |
| protected: |
| FetchContext* MakeFetchContextWithMockSubresourceFilter( |
| Vector<String> blocked_urls, |
| Vector<String> tagged_urls) { |
| auto* context = MakeGarbageCollected<MockFetchContext>(); |
| context->set_blocked_urls(blocked_urls); |
| context->set_tagged_urls(tagged_urls); |
| return context; |
| } |
| |
| ResourceFetcher* MakeResourceFetcherWithMockSubresourceFilter() { |
| auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); |
| FetchContext* context = |
| MakeFetchContextWithMockSubresourceFilter(blocked_urls_, tagged_urls_); |
| return MakeResourceFetcher(properties, context); |
| } |
| |
| void CreateMojoDataPipe() { |
| mojo::ScopedDataPipeProducerHandle producer; |
| mojo::ScopedDataPipeConsumerHandle consumer; |
| MojoCreateDataPipeOptions options; |
| options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; |
| options.element_num_bytes = 1; |
| options.capacity_num_bytes = 3; |
| |
| MojoResult result = CreateDataPipe(&options, producer, consumer); |
| ASSERT_EQ(result, MOJO_RESULT_OK); |
| } |
| |
| void ExpectHistogramsMatching(CnameAliasMetricInfo info) { |
| histogram_tester()->ExpectUniqueSample(kCnameAliasHadAliasesHistogram, |
| info.has_aliases, 1); |
| |
| if (info.has_aliases) { |
| histogram_tester()->ExpectUniqueSample(kCnameAliasWasAdTaggedHistogram, |
| info.was_ad_tagged_based_on_alias, |
| 1); |
| histogram_tester()->ExpectUniqueSample( |
| kCnameAliasWasBlockedHistogram, info.was_blocked_based_on_alias, 1); |
| histogram_tester()->ExpectUniqueSample(kCnameAliasListLengthHistogram, |
| info.list_length, 1); |
| histogram_tester()->ExpectUniqueSample(kCnameAliasIsInvalidCountHistogram, |
| info.invalid_count, 1); |
| histogram_tester()->ExpectUniqueSample( |
| kCnameAliasIsRedundantCountHistogram, info.redundant_count, 1); |
| } |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| base::HistogramTester histogram_tester_; |
| Vector<String> blocked_urls_; |
| Vector<String> tagged_urls_; |
| }; |
| |
| TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, |
| DnsAliasesCheckedBySubresourceFilterDisallowed_TaggedAndBlocked) { |
| // Set the blocklists: the first for blocking, the second for ad-tagging. |
| Vector<String> blocked_urls = {"https://bad-ad.com/some_path.html"}; |
| Vector<String> tagged_urls = {"https://ad.com/some_path.html"}; |
| SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); |
| |
| // Create the request. |
| KURL url("https://www.example.com/some_path.html"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| // Create the resource and loader. |
| Resource* resource = CreateResource(std::move(request)); |
| ResourceLoader* loader = resource->Loader(); |
| |
| // Create the response. |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| // Set the CNAME aliases. |
| Vector<String> aliases({"ad.com", "bad-ad.com", "alias3.com"}); |
| response.SetDnsAliases(aliases); |
| |
| // Give the response to the loader. |
| GiveResponseToLoader(response, loader); |
| |
| // Test the histograms to verify that the CNAME aliases were detected. |
| // Expect that the resource was tagged as a ad, due to first alias. |
| // Expect that the resource was blocked, due to second alias. |
| CnameAliasMetricInfo info = {.has_aliases = true, |
| .was_ad_tagged_based_on_alias = true, |
| .was_blocked_based_on_alias = true, |
| .list_length = 3, |
| .invalid_count = 0, |
| .redundant_count = 0}; |
| |
| ExpectHistogramsMatching(info); |
| } |
| |
| TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, |
| DnsAliasesCheckedBySubresourceFilterDisallowed_BlockedOnly) { |
| // Set the blocklists: the first for blocking, the second for ad-tagging. |
| Vector<String> blocked_urls = {"https://bad-ad.com/some_path.html"}; |
| Vector<String> tagged_urls = {}; |
| SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); |
| |
| // Create the request. |
| KURL url("https://www.example.com/some_path.html"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| // Create the resource and loader. |
| Resource* resource = CreateResource(std::move(request)); |
| ResourceLoader* loader = resource->Loader(); |
| |
| // Create the response. |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| // Set the CNAME aliases. |
| Vector<String> aliases({"ad.com", "bad-ad.com", "alias3.com"}); |
| response.SetDnsAliases(aliases); |
| |
| // Give the response to the loader. |
| GiveResponseToLoader(response, loader); |
| |
| // Test the histograms to verify that the CNAME aliases were detected. |
| // Expect that the resource was blocked, due to second alias. |
| CnameAliasMetricInfo info = {.has_aliases = true, |
| .was_ad_tagged_based_on_alias = false, |
| .was_blocked_based_on_alias = true, |
| .list_length = 3, |
| .invalid_count = 0, |
| .redundant_count = 0}; |
| |
| ExpectHistogramsMatching(info); |
| } |
| |
| TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, |
| DnsAliasesCheckedBySubresourceFilterDisallowed_TaggedOnly) { |
| // Set the blocklists: the first for blocking, the second for ad-tagging. |
| Vector<String> blocked_urls = {}; |
| Vector<String> tagged_urls = {"https://bad-ad.com/some_path.html"}; |
| SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); |
| |
| // Create the request. |
| KURL url("https://www.example.com/some_path.html"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| // Create the resource and loader. |
| Resource* resource = CreateResource(std::move(request)); |
| ResourceLoader* loader = resource->Loader(); |
| |
| // Create the response. |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| // Set the CNAME aliases. |
| Vector<String> aliases({"ad.com", "", "alias3.com", "bad-ad.com"}); |
| response.SetDnsAliases(aliases); |
| |
| // Give the response to the loader. |
| GiveResponseToLoader(response, loader); |
| |
| // Test the histograms to verify that the CNAME aliases were detected. |
| // Expect that the resource was tagged, due to fourth alias. |
| // Expect that the invalid empty alias is counted as such. |
| CnameAliasMetricInfo info = {.has_aliases = true, |
| .was_ad_tagged_based_on_alias = true, |
| .was_blocked_based_on_alias = false, |
| .list_length = 4, |
| .invalid_count = 1, |
| .redundant_count = 0}; |
| |
| ExpectHistogramsMatching(info); |
| } |
| |
| TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, |
| DnsAliasesCheckedBySubresourceFilterAllowed_NotBlockedOrTagged) { |
| // Set the blocklists: the first for blocking, the second for ad-tagging. |
| Vector<String> blocked_urls = {}; |
| Vector<String> tagged_urls = {}; |
| SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); |
| |
| // Create the request. |
| KURL url("https://www.example.com/some_path.html"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| // Create the resource and loader. |
| Resource* resource = CreateResource(std::move(request)); |
| ResourceLoader* loader = resource->Loader(); |
| |
| // Create the response. |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| // Set the CNAME aliases. |
| Vector<String> aliases( |
| {"non-ad.com", "?", "alias3.com", "not-an-ad.com", "www.example.com"}); |
| response.SetDnsAliases(aliases); |
| |
| // Give the response to the loader. |
| GiveResponseToLoader(response, loader); |
| |
| // Test the histograms to verify that the CNAME aliases were detected. |
| // Expect that the resource was neither tagged nor blocked. |
| // Expect that the invalid alias is counted as such. |
| // Expect that the redundant (i.e. matching the request URL) fifth alias to be |
| // counted as such. |
| CnameAliasMetricInfo info = {.has_aliases = true, |
| .was_ad_tagged_based_on_alias = false, |
| .was_blocked_based_on_alias = false, |
| .list_length = 5, |
| .invalid_count = 1, |
| .redundant_count = 1}; |
| |
| ExpectHistogramsMatching(info); |
| } |
| |
| TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, |
| DnsAliasesCheckedBySubresourceFilterNoAliases_NoneDetected) { |
| // Set the blocklists: the first for blocking, the second for ad-tagging. |
| Vector<String> blocked_urls = {}; |
| Vector<String> tagged_urls = {}; |
| SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); |
| |
| // Create the request. |
| KURL url("https://www.example.com/some_path.html"); |
| ResourceRequest request(url); |
| request.SetRequestContext(mojom::blink::RequestContextType::FETCH); |
| |
| // Create the resource and loader. |
| Resource* resource = CreateResource(std::move(request)); |
| ResourceLoader* loader = resource->Loader(); |
| |
| // Create the response. |
| ResourceResponse response(url); |
| response.SetHttpStatusCode(200); |
| |
| // Set the CNAME aliases. |
| Vector<String> aliases; |
| response.SetDnsAliases(aliases); |
| |
| // Give the response to the loader. |
| GiveResponseToLoader(response, loader); |
| |
| // Test the histogram to verify that no aliases were detected. |
| CnameAliasMetricInfo info = {.has_aliases = false}; |
| |
| ExpectHistogramsMatching(info); |
| } |
| |
| } // namespace blink |