blob: 98550bad89ab2d4c96d04449d444d0b7b78584da [file] [log] [blame]
// 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 "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_runtime_features.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/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/mock_fetch_context.h"
#include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.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 {
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 FetchRequestMode = network::mojom::FetchRequestMode;
using FetchResponseType = network::mojom::FetchResponseType;
struct TestCase {
const KURL url;
const FetchRequestMode 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 TestWebURLLoaderFactory final : public WebURLLoaderFactory {
std::unique_ptr<WebURLLoader> CreateURLLoader(
const WebURLRequest& request,
std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle>)
override {
return std::make_unique<TestWebURLLoader>();
}
};
private:
class TestWebURLLoader final : public WebURLLoader {
public:
~TestWebURLLoader() override = default;
void LoadSynchronously(const WebURLRequest&,
WebURLLoaderClient*,
WebURLResponse&,
base::Optional<WebURLError>&,
WebData&,
int64_t& encoded_data_length,
int64_t& encoded_body_length,
WebBlobInfo& downloaded_blob) override {
NOTREACHED();
}
void LoadAsynchronously(const WebURLRequest&,
WebURLLoaderClient*) override {}
void Cancel() override {}
void SetDefersLoading(bool) override {}
void DidChangePriority(WebURLRequest::Priority, int) override {
NOTREACHED();
}
};
};
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, ResponseType) {
// This test will be trivial if EnableOutOfBlinkCors is enabled.
WebRuntimeFeatures::EnableOutOfBlinkCors(false);
const scoped_refptr<const SecurityOrigin> origin =
SecurityOrigin::Create(foo_url_);
const scoped_refptr<const SecurityOrigin> no_origin = nullptr;
const KURL same_origin_url = foo_url_;
const KURL cross_origin_url = bar_url_;
TestCase cases[] = {
// Same origin response:
{same_origin_url, FetchRequestMode::kNoCors, From::kNetwork, no_origin,
FetchResponseType::kDefault, FetchResponseType::kBasic},
{same_origin_url, FetchRequestMode::kCors, From::kNetwork, no_origin,
FetchResponseType::kDefault, FetchResponseType::kBasic},
// Cross origin, no-cors:
{cross_origin_url, FetchRequestMode::kNoCors, From::kNetwork, no_origin,
FetchResponseType::kDefault, FetchResponseType::kOpaque},
// Cross origin, cors:
{cross_origin_url, FetchRequestMode::kCors, From::kNetwork, origin,
FetchResponseType::kDefault, FetchResponseType::kCors},
{cross_origin_url, FetchRequestMode::kCors, From::kNetwork, no_origin,
FetchResponseType::kDefault, FetchResponseType::kError},
// From service worker, no-cors:
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kBasic, FetchResponseType::kBasic},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kCors, FetchResponseType::kCors},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kDefault, FetchResponseType::kDefault},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kOpaque, FetchResponseType::kOpaque},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kOpaqueRedirect,
FetchResponseType::kOpaqueRedirect},
// From service worker, cors:
{same_origin_url, FetchRequestMode::kCors, From::kServiceWorker,
no_origin, FetchResponseType::kBasic, FetchResponseType::kBasic},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kCors, FetchResponseType::kCors},
{same_origin_url, FetchRequestMode::kNoCors, From::kServiceWorker,
no_origin, FetchResponseType::kDefault, FetchResponseType::kDefault},
};
for (const auto& test : cases) {
SCOPED_TRACE(testing::Message()
<< "url: " << test.url.GetString()
<< ", requets mode: " << test.request_mode
<< ", from: " << test.from << ", allowed_origin: "
<< (test.allowed_origin ? test.allowed_origin->ToString()
: String("<no allowed origin>"))
<< ", original_response_type: "
<< test.original_response_type);
auto* properties =
MakeGarbageCollected<TestResourceFetcherProperties>(origin);
FetchContext* context = MakeGarbageCollected<MockFetchContext>(
nullptr, std::make_unique<TestWebURLLoaderFactory>());
auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
ResourceRequest request;
request.SetURL(test.url);
request.SetFetchRequestMode(test.request_mode);
request.SetRequestContext(mojom::RequestContextType::FETCH);
FetchParameters fetch_parameters(request);
if (test.request_mode == network::mojom::FetchRequestMode::kCors) {
fetch_parameters.SetCrossOriginAccessControl(
origin.get(), network::mojom::FetchCredentialsMode::kOmit);
}
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
ResourceLoader* loader = resource->Loader();
ResourceResponse response(test.url);
response.SetHTTPStatusCode(200);
response.SetType(test.original_response_type);
response.SetWasFetchedViaServiceWorker(test.from == From::kServiceWorker);
if (test.allowed_origin) {
response.SetHTTPHeaderField("access-control-allow-origin",
test.allowed_origin->ToAtomicString());
}
response.SetType(test.original_response_type);
loader->DidReceiveResponse(WrappedResourceResponse(response));
EXPECT_EQ(test.expectation, resource->GetResponse().GetType());
}
}
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>(
nullptr, std::make_unique<TestWebURLLoaderFactory>());
auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
ResourceRequest request;
request.SetURL(foo_url_);
request.SetRequestContext(mojom::RequestContextType::FETCH);
FetchParameters fetch_parameters(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));
}
} // namespace blink