blob: a4f285a969d8e5c13b5acc7e0f7ac9fddf188d84 [file] [log] [blame]
// Copyright 2018 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 "base/bind.h"
#include "base/debug/stack_trace.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "testing/gtest/include/gtest/gtest.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_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/public/platform/web_url_request_extra_data.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/unique_identifier.h"
#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.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/mock_context_lifecycle_notifier.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
namespace blink {
namespace {
using ProcessCodeCacheRequestCallback =
base::RepeatingCallback<void(WebCodeCacheLoader::FetchCodeCacheCallback)>;
// A mock code cache loader that calls the processing function whenever it
// receives fetch requests.
class TestCodeCacheLoader : public WebCodeCacheLoader {
public:
explicit TestCodeCacheLoader(ProcessCodeCacheRequestCallback callback)
: process_request_(callback) {}
~TestCodeCacheLoader() override = default;
// WebCodeCacheLoader methods:
void FetchFromCodeCacheSynchronously(
const WebURL& url,
base::Time* response_time_out,
mojo_base::BigBuffer* buffer_out) override {}
void FetchFromCodeCache(
blink::mojom::CodeCacheType cache_type,
const WebURL& url,
WebCodeCacheLoader::FetchCodeCacheCallback callback) override {
process_request_.Run(std::move(callback));
}
private:
ProcessCodeCacheRequestCallback process_request_;
};
// A mock WebURLLoader to know the status of defers flag.
class TestWebURLLoader final : public WebURLLoader {
public:
explicit TestWebURLLoader(WebURLLoader::DeferType* const defers_flag_ptr)
: defers_flag_ptr_(defers_flag_ptr) {}
~TestWebURLLoader() 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 defers) override {
*defers_flag_ptr_ = defers;
}
void DidChangePriority(WebURLRequest::Priority, int) override {
NOTREACHED();
}
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForBodyLoader()
override {
return base::MakeRefCounted<scheduler::FakeTaskRunner>();
}
private:
// Points to |ResourceLoaderDefersLoadingTest::web_url_loader_defers_|.
WebURLLoader::DeferType* const defers_flag_ptr_;
};
class DeferTestLoaderFactory final : public ResourceFetcher::LoaderFactory {
public:
DeferTestLoaderFactory(
WebURLLoader::DeferType* const defers_flag,
ProcessCodeCacheRequestCallback process_code_cache_request_callback)
: defers_flag_(defers_flag),
process_code_cache_request_callback_(
process_code_cache_request_callback) {}
// LoaderFactory implementations
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<TestWebURLLoader>(defers_flag_);
}
std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() override {
return std::make_unique<TestCodeCacheLoader>(
process_code_cache_request_callback_);
}
private:
// Points to |ResourceLoaderDefersLoadingTest::web_url_loader_defers_|.
WebURLLoader::DeferType* const defers_flag_;
ProcessCodeCacheRequestCallback process_code_cache_request_callback_;
};
} // namespace
class ResourceLoaderDefersLoadingTest : public testing::Test {
public:
ResourceLoaderDefersLoadingTest() {
SetCodeCacheProcessFunction(base::BindRepeating(
&ResourceLoaderDefersLoadingTest::SaveCodeCacheCallback,
base::Unretained(this)));
}
void SaveCodeCacheCallback(
WebCodeCacheLoader::FetchCodeCacheCallback callback) {
// Store the callback to send back a response.
code_cache_response_callback_ = std::move(callback);
}
ResourceFetcher* CreateFetcher() {
return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable(),
MakeGarbageCollected<MockFetchContext>(),
base::MakeRefCounted<scheduler::FakeTaskRunner>(),
base::MakeRefCounted<scheduler::FakeTaskRunner>(),
MakeGarbageCollected<DeferTestLoaderFactory>(
&web_url_loader_defers_, process_code_cache_request_callback_),
MakeGarbageCollected<MockContextLifecycleNotifier>(),
nullptr /* back_forward_cache_loader_helper */));
}
void SetCodeCacheProcessFunction(ProcessCodeCacheRequestCallback callback) {
process_code_cache_request_callback_ = callback;
}
ProcessCodeCacheRequestCallback process_code_cache_request_callback_;
WebCodeCacheLoader::FetchCodeCacheCallback code_cache_response_callback_;
// Passed to TestWebURLLoader (via |platform_|) and updated when its
// SetDefersLoading method is called.
WebURLLoader::DeferType web_url_loader_defers_ =
WebURLLoader::DeferType::kNotDeferred;
const KURL test_url_ = KURL("http://example.com/");
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform_;
};
TEST_F(ResourceLoaderDefersLoadingTest, CodeCacheFetchCheckDefers) {
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
// After code cache fetch it should have deferred WebURLLoader.
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
DCHECK(resource);
std::move(code_cache_response_callback_).Run(base::Time(), {});
// Once the response is received it should be reset.
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kNotDeferred);
}
TEST_F(ResourceLoaderDefersLoadingTest, CodeCacheFetchSyncReturn) {
SetCodeCacheProcessFunction(base::BindRepeating(
[](WebCodeCacheLoader::FetchCodeCacheCallback callback) {
std::move(callback).Run(base::Time(), {});
}));
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
DCHECK(resource);
// The callback would be called so it should not be deferred.
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kNotDeferred);
}
TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToFalse) {
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
// Change Defers loading to false. This should not be sent to
// WebURLLoader since a code cache request is still pending.
ResourceLoader* loader = resource->Loader();
loader->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
}
TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToTrue) {
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
ResourceLoader* loader = resource->Loader();
loader->SetDefersLoading(WebURLLoader::DeferType::kDeferred);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
std::move(code_cache_response_callback_).Run(base::Time(), {});
// Since it was requested to be deferred, it should be reset to the
// correct value.
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
}
TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToBfcacheDefer) {
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
ResourceLoader* loader = resource->Loader();
loader->SetDefersLoading(
WebURLLoader::DeferType::kDeferredWithBackForwardCache);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
std::move(code_cache_response_callback_).Run(base::Time(), {});
// Since it was requested to be deferred, it should be reset to the
// correct value.
DCHECK_EQ(web_url_loader_defers_,
WebURLLoader::DeferType::kDeferredWithBackForwardCache);
}
TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersMultipleTimes) {
auto* fetcher = CreateFetcher();
ResourceRequest request;
request.SetUrl(test_url_);
request.SetRequestContext(mojom::blink::RequestContextType::FETCH);
FetchParameters fetch_parameters =
FetchParameters::CreateForTest(std::move(request));
Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
ResourceLoader* loader = resource->Loader();
loader->SetDefersLoading(WebURLLoader::DeferType::kDeferred);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
loader->SetDefersLoading(WebURLLoader::DeferType::kNotDeferred);
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kDeferred);
std::move(code_cache_response_callback_).Run(base::Time(), {});
DCHECK_EQ(web_url_loader_defers_, WebURLLoader::DeferType::kNotDeferred);
}
} // namespace blink