blob: 29bb14d743d0f5fa44ff03b9d400e87c02c88cb7 [file] [log] [blame]
// Copyright 2016 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 "core/loader/ThreadableLoader.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "core/loader/DocumentThreadableLoader.h"
#include "core/loader/ThreadableLoaderClient.h"
#include "core/loader/ThreadableLoadingContext.h"
#include "core/loader/WorkerFetchContext.h"
#include "core/loader/WorkerThreadableLoader.h"
#include "core/testing/DummyPageHolder.h"
#include "core/workers/WorkerReportingProxy.h"
#include "core/workers/WorkerThreadTestHelper.h"
#include "platform/WaitableEvent.h"
#include "platform/geometry/IntSize.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "platform/loader/fetch/ResourceError.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/loader/fetch/ResourceRequest.h"
#include "platform/loader/fetch/ResourceResponse.h"
#include "platform/loader/fetch/ResourceTimingInfo.h"
#include "platform/loader/testing/WebURLLoaderFactoryWithMock.h"
#include "platform/testing/URLTestHelpers.h"
#include "platform/testing/UnitTestHelpers.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/Assertions.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/text/WTFString.h"
#include "public/platform/Platform.h"
#include "public/platform/TaskType.h"
#include "public/platform/WebURLLoadTiming.h"
#include "public/platform/WebURLLoaderMockFactory.h"
#include "public/platform/WebURLRequest.h"
#include "public/platform/WebURLResponse.h"
#include "public/platform/WebWorkerFetchContext.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
using testing::_;
using testing::InSequence;
using testing::InvokeWithoutArgs;
using testing::StrEq;
using testing::Truly;
using Checkpoint = testing::StrictMock<testing::MockFunction<void(int)>>;
constexpr char kFileName[] = "fox-null-terminated.html";
class MockThreadableLoaderClient : public ThreadableLoaderClient {
public:
static std::unique_ptr<MockThreadableLoaderClient> Create() {
return base::WrapUnique(
new testing::StrictMock<MockThreadableLoaderClient>);
}
MOCK_METHOD2(DidSendData, void(unsigned long long, unsigned long long));
MOCK_METHOD3(DidReceiveResponseMock,
void(unsigned long,
const ResourceResponse&,
WebDataConsumerHandle*));
void DidReceiveResponse(unsigned long identifier,
const ResourceResponse& response,
std::unique_ptr<WebDataConsumerHandle> handle) {
DidReceiveResponseMock(identifier, response, handle.get());
}
MOCK_METHOD2(DidReceiveData, void(const char*, unsigned));
MOCK_METHOD2(DidReceiveCachedMetadata, void(const char*, int));
MOCK_METHOD2(DidFinishLoading, void(unsigned long, double));
MOCK_METHOD1(DidFail, void(const ResourceError&));
MOCK_METHOD0(DidFailRedirectCheck, void());
MOCK_METHOD1(DidReceiveResourceTiming, void(const ResourceTimingInfo&));
MOCK_METHOD1(DidDownloadData, void(int));
protected:
MockThreadableLoaderClient() = default;
};
bool IsCancellation(const ResourceError& error) {
return error.IsCancellation();
}
bool IsNotCancellation(const ResourceError& error) {
return !error.IsCancellation();
}
KURL SuccessURL() {
return KURL("http://example.com/success").Copy();
}
KURL ErrorURL() {
return KURL("http://example.com/error").Copy();
}
KURL RedirectURL() {
return KURL("http://example.com/redirect").Copy();
}
KURL RedirectLoopURL() {
return KURL("http://example.com/loop").Copy();
}
void ServeAsynchronousRequests() {
Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
}
void UnregisterAllURLsAndClearMemoryCache() {
Platform::Current()
->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
void SetUpSuccessURL() {
URLTestHelpers::RegisterMockedURLLoad(
SuccessURL(), test::CoreTestDataPath(kFileName), "text/html");
}
void SetUpErrorURL() {
URLTestHelpers::RegisterMockedErrorURLLoad(ErrorURL());
}
void SetUpRedirectURL() {
KURL url = RedirectURL();
WebURLLoadTiming timing;
timing.Initialize();
WebURLResponse response;
response.SetURL(url);
response.SetHTTPStatusCode(301);
response.SetLoadTiming(timing);
response.AddHTTPHeaderField("Location", SuccessURL().GetString());
response.AddHTTPHeaderField("Access-Control-Allow-Origin", "null");
URLTestHelpers::RegisterMockedURLLoadWithCustomResponse(
url, test::CoreTestDataPath(kFileName), response);
}
void SetUpRedirectLoopURL() {
KURL url = RedirectLoopURL();
WebURLLoadTiming timing;
timing.Initialize();
WebURLResponse response;
response.SetURL(url);
response.SetHTTPStatusCode(301);
response.SetLoadTiming(timing);
response.AddHTTPHeaderField("Location", RedirectLoopURL().GetString());
response.AddHTTPHeaderField("Access-Control-Allow-Origin", "null");
URLTestHelpers::RegisterMockedURLLoadWithCustomResponse(
url, test::CoreTestDataPath(kFileName), response);
}
void SetUpMockURLs() {
SetUpSuccessURL();
SetUpErrorURL();
SetUpRedirectURL();
SetUpRedirectLoopURL();
}
enum ThreadableLoaderToTest {
kDocumentThreadableLoaderTest,
kWorkerThreadableLoaderTest,
};
class ThreadableLoaderTestHelper {
public:
virtual ~ThreadableLoaderTestHelper() = default;
virtual void CreateLoader(ThreadableLoaderClient*) = 0;
virtual void StartLoader(const ResourceRequest&) = 0;
virtual void CancelLoader() = 0;
virtual void CancelAndClearLoader() = 0;
virtual void ClearLoader() = 0;
virtual Checkpoint& GetCheckpoint() = 0;
virtual void CallCheckpoint(int) = 0;
virtual void OnSetUp() = 0;
virtual void OnServeRequests() = 0;
virtual void OnTearDown() = 0;
};
class DocumentThreadableLoaderTestHelper : public ThreadableLoaderTestHelper {
public:
DocumentThreadableLoaderTestHelper()
: dummy_page_holder_(DummyPageHolder::Create(IntSize(1, 1))) {}
void CreateLoader(ThreadableLoaderClient* client) override {
ThreadableLoaderOptions options;
ResourceLoaderOptions resource_loader_options;
loader_ = DocumentThreadableLoader::Create(
*ThreadableLoadingContext::Create(GetDocument()), client, options,
resource_loader_options);
}
void StartLoader(const ResourceRequest& request) override {
loader_->Start(request);
}
void CancelLoader() override { loader_->Cancel(); }
void CancelAndClearLoader() override {
loader_->Cancel();
loader_ = nullptr;
}
void ClearLoader() override { loader_ = nullptr; }
Checkpoint& GetCheckpoint() override { return checkpoint_; }
void CallCheckpoint(int n) override { checkpoint_.Call(n); }
void OnSetUp() override { SetUpMockURLs(); }
void OnServeRequests() override { ServeAsynchronousRequests(); }
void OnTearDown() override {
if (loader_) {
loader_->Cancel();
loader_ = nullptr;
}
UnregisterAllURLsAndClearMemoryCache();
}
private:
Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
Checkpoint checkpoint_;
Persistent<DocumentThreadableLoader> loader_;
};
class WebWorkerFetchContextForTest : public WebWorkerFetchContext {
public:
WebWorkerFetchContextForTest(KURL site_for_cookies)
: site_for_cookies_(site_for_cookies.Copy()) {}
void SetTerminateSyncLoadEvent(base::WaitableEvent*) override {}
void InitializeOnWorkerThread() override {}
std::unique_ptr<WebURLLoaderFactory> CreateURLLoaderFactory() override {
return std::make_unique<WebURLLoaderFactoryWithMock>(
Platform::Current()->GetURLLoaderMockFactory());
}
std::unique_ptr<WebURLLoaderFactory> WrapURLLoaderFactory(
mojo::ScopedMessagePipeHandle) override {
return std::make_unique<WebURLLoaderFactoryWithMock>(
Platform::Current()->GetURLLoaderMockFactory());
}
void WillSendRequest(WebURLRequest&) override {}
bool IsControlledByServiceWorker() const override { return false; }
WebURL SiteForCookies() const override { return site_for_cookies_; }
private:
WebURL site_for_cookies_;
DISALLOW_COPY_AND_ASSIGN(WebWorkerFetchContextForTest);
};
class WorkerThreadableLoaderTestHelper : public ThreadableLoaderTestHelper {
public:
WorkerThreadableLoaderTestHelper()
: dummy_page_holder_(DummyPageHolder::Create(IntSize(1, 1))) {}
void CreateLoader(ThreadableLoaderClient* client) override {
std::unique_ptr<WaitableEvent> completion_event =
std::make_unique<WaitableEvent>();
PostCrossThreadTask(
*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&WorkerThreadableLoaderTestHelper::WorkerCreateLoader,
CrossThreadUnretained(this),
CrossThreadUnretained(client),
CrossThreadUnretained(completion_event.get())));
completion_event->Wait();
}
void StartLoader(const ResourceRequest& request) override {
std::unique_ptr<WaitableEvent> completion_event =
std::make_unique<WaitableEvent>();
PostCrossThreadTask(
*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&WorkerThreadableLoaderTestHelper::WorkerStartLoader,
CrossThreadUnretained(this),
CrossThreadUnretained(completion_event.get()),
request));
completion_event->Wait();
}
// Must be called on the worker thread.
void CancelLoader() override {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
loader_->Cancel();
}
void CancelAndClearLoader() override {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
loader_->Cancel();
loader_ = nullptr;
}
// Must be called on the worker thread.
void ClearLoader() override {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
loader_ = nullptr;
}
Checkpoint& GetCheckpoint() override { return checkpoint_; }
void CallCheckpoint(int n) override {
test::RunPendingTasks();
std::unique_ptr<WaitableEvent> completion_event =
std::make_unique<WaitableEvent>();
PostCrossThreadTask(
*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&WorkerThreadableLoaderTestHelper::WorkerCallCheckpoint,
CrossThreadUnretained(this),
CrossThreadUnretained(completion_event.get()), n));
completion_event->Wait();
}
void OnSetUp() override {
reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
security_origin_ = GetDocument().GetSecurityOrigin();
parent_frame_task_runners_ = ParentFrameTaskRunners::Create(&GetDocument());
worker_thread_ = std::make_unique<WorkerThreadForTest>(
ThreadableLoadingContext::Create(GetDocument()), *reporting_proxy_);
WorkerClients* worker_clients = WorkerClients::Create();
ProvideWorkerFetchContextToWorker(
worker_clients, std::make_unique<WebWorkerFetchContextForTest>(
GetDocument().SiteForCookies()));
worker_thread_->StartWithSourceCode(
security_origin_.get(), "//fake source code",
parent_frame_task_runners_.Get(), GetDocument().Url(), worker_clients);
worker_thread_->WaitForInit();
worker_loading_task_runner_ =
worker_thread_->GetTaskRunner(TaskType::kInternalTest);
PostCrossThreadTask(*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&SetUpMockURLs));
WaitForWorkerThreadSignal();
}
void OnServeRequests() override {
test::RunPendingTasks();
PostCrossThreadTask(*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&ServeAsynchronousRequests));
WaitForWorkerThreadSignal();
}
void OnTearDown() override {
PostCrossThreadTask(
*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&WorkerThreadableLoaderTestHelper::ClearLoader,
CrossThreadUnretained(this)));
WaitForWorkerThreadSignal();
PostCrossThreadTask(*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&UnregisterAllURLsAndClearMemoryCache));
WaitForWorkerThreadSignal();
worker_thread_->Terminate();
worker_thread_->WaitForShutdownForTesting();
// Needed to clean up the things on the main thread side and
// avoid Resource leaks.
test::RunPendingTasks();
}
private:
Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
void WorkerCreateLoader(ThreadableLoaderClient* client,
WaitableEvent* event) {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
ThreadableLoaderOptions options;
ResourceLoaderOptions resource_loader_options;
// Ensure that WorkerThreadableLoader is created.
// ThreadableLoader::create() determines whether it should create
// a DocumentThreadableLoader or WorkerThreadableLoader based on
// isWorkerGlobalScope().
DCHECK(worker_thread_->GlobalScope()->IsWorkerGlobalScope());
loader_ = ThreadableLoader::Create(*worker_thread_->GlobalScope(), client,
options, resource_loader_options);
DCHECK(loader_);
event->Signal();
}
void WorkerStartLoader(
WaitableEvent* event,
std::unique_ptr<CrossThreadResourceRequestData> request_data) {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
ResourceRequest request(request_data.get());
request.SetFetchCredentialsMode(
network::mojom::FetchCredentialsMode::kOmit);
loader_->Start(request);
event->Signal();
}
void WorkerCallCheckpoint(WaitableEvent* event, int n) {
DCHECK(worker_thread_);
DCHECK(worker_thread_->IsCurrentThread());
checkpoint_.Call(n);
event->Signal();
}
void WaitForWorkerThreadSignal() {
WaitableEvent event;
PostCrossThreadTask(
*worker_loading_task_runner_, FROM_HERE,
CrossThreadBind(&WaitableEvent::Signal, CrossThreadUnretained(&event)));
event.Wait();
}
scoped_refptr<const SecurityOrigin> security_origin_;
std::unique_ptr<WorkerReportingProxy> reporting_proxy_;
std::unique_ptr<WorkerThreadForTest> worker_thread_;
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
// Accessed cross-thread when worker thread posts tasks to the parent.
CrossThreadPersistent<ParentFrameTaskRunners> parent_frame_task_runners_;
scoped_refptr<base::SingleThreadTaskRunner> worker_loading_task_runner_;
Checkpoint checkpoint_;
// |m_loader| must be touched only from the worker thread only.
CrossThreadPersistent<ThreadableLoader> loader_;
};
class ThreadableLoaderTest
: public testing::TestWithParam<ThreadableLoaderToTest> {
public:
ThreadableLoaderTest() {
switch (GetParam()) {
case kDocumentThreadableLoaderTest:
helper_ = std::make_unique<DocumentThreadableLoaderTestHelper>();
break;
case kWorkerThreadableLoaderTest:
helper_ = std::make_unique<WorkerThreadableLoaderTestHelper>();
break;
}
}
void StartLoader(const KURL& url,
network::mojom::FetchRequestMode fetch_request_mode =
network::mojom::FetchRequestMode::kNoCORS) {
ResourceRequest request(url);
request.SetRequestContext(WebURLRequest::kRequestContextObject);
request.SetFetchRequestMode(fetch_request_mode);
request.SetFetchCredentialsMode(
network::mojom::FetchCredentialsMode::kOmit);
helper_->StartLoader(request);
}
void CancelLoader() { helper_->CancelLoader(); }
void CancelAndClearLoader() { helper_->CancelAndClearLoader(); }
void ClearLoader() { helper_->ClearLoader(); }
Checkpoint& GetCheckpoint() { return helper_->GetCheckpoint(); }
void CallCheckpoint(int n) { helper_->CallCheckpoint(n); }
void ServeRequests() {
helper_->OnServeRequests();
}
void CreateLoader() { helper_->CreateLoader(Client()); }
MockThreadableLoaderClient* Client() const { return client_.get(); }
private:
void SetUp() override {
client_ = MockThreadableLoaderClient::Create();
helper_->OnSetUp();
}
void TearDown() override {
helper_->OnTearDown();
client_.reset();
}
std::unique_ptr<MockThreadableLoaderClient> client_;
std::unique_ptr<ThreadableLoaderTestHelper> helper_;
};
INSTANTIATE_TEST_CASE_P(Document,
ThreadableLoaderTest,
testing::Values(kDocumentThreadableLoaderTest));
INSTANTIATE_TEST_CASE_P(Worker,
ThreadableLoaderTest,
testing::Values(kWorkerThreadableLoaderTest));
TEST_P(ThreadableLoaderTest, StartAndStop) {}
TEST_P(ThreadableLoaderTest, CancelAfterStart) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
EXPECT_CALL(GetCheckpoint(), Call(3));
StartLoader(SuccessURL());
CallCheckpoint(2);
CallCheckpoint(3);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelAndClearAfterStart) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2))
.WillOnce(
InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelAndClearLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
EXPECT_CALL(GetCheckpoint(), Call(3));
StartLoader(SuccessURL());
CallCheckpoint(2);
CallCheckpoint(3);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidReceiveResponse) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelAndClearInDidReceiveResponse) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _))
.WillOnce(
InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelAndClearLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidReceiveData) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(_, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelAndClearInDidReceiveData) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(_, _))
.WillOnce(
InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelAndClearLoader));
EXPECT_CALL(*Client(), DidFail(Truly(IsCancellation)));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, DidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(StrEq("fox"), 4));
// We expect didReceiveResourceTiming() calls in DocumentThreadableLoader;
// it's used to connect DocumentThreadableLoader to WorkerThreadableLoader,
// not to ThreadableLoaderClient.
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(_, _));
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, ClearInDidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(_, _));
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::ClearLoader));
StartLoader(SuccessURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, DidFail) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidFail(Truly(IsNotCancellation)));
StartLoader(ErrorURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidFail) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidFail(_))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
StartLoader(ErrorURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, ClearInDidFail) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidFail(_))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::ClearLoader));
StartLoader(ErrorURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, DidFailInStart) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
String error_message = String::Format(
"Failed to load '%s': Cross origin requests are not allowed by request "
"mode.",
ErrorURL().GetString().Utf8().data());
EXPECT_CALL(*Client(), DidFail(ResourceError::CancelledDueToAccessCheckError(
ErrorURL(), ResourceRequestBlockedReason::kOther,
error_message)));
EXPECT_CALL(GetCheckpoint(), Call(2));
StartLoader(ErrorURL(), network::mojom::FetchRequestMode::kSameOrigin);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidFailInStart) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(*Client(), DidFail(_))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
EXPECT_CALL(GetCheckpoint(), Call(2));
StartLoader(ErrorURL(), network::mojom::FetchRequestMode::kSameOrigin);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, ClearInDidFailInStart) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(*Client(), DidFail(_))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::ClearLoader));
EXPECT_CALL(GetCheckpoint(), Call(2));
StartLoader(ErrorURL(), network::mojom::FetchRequestMode::kSameOrigin);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, DidFailAccessControlCheck) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(
*Client(),
DidFail(ResourceError::CancelledDueToAccessCheckError(
SuccessURL(), ResourceRequestBlockedReason::kOther,
"No 'Access-Control-Allow-Origin' header is present on the requested "
"resource. Origin 'null' is therefore not allowed access.")));
StartLoader(SuccessURL(), network::mojom::FetchRequestMode::kCORS);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, RedirectDidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(StrEq("fox"), 4));
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _));
StartLoader(RedirectURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInRedirectDidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(StrEq("fox"), 4));
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
StartLoader(RedirectURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, ClearInRedirectDidFinishLoading) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidReceiveResponseMock(_, _, _));
EXPECT_CALL(*Client(), DidReceiveData(StrEq("fox"), 4));
EXPECT_CALL(*Client(), DidReceiveResourceTiming(_));
EXPECT_CALL(*Client(), DidFinishLoading(_, _))
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::ClearLoader));
StartLoader(RedirectURL());
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, DidFailRedirectCheck) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidFailRedirectCheck());
StartLoader(RedirectLoopURL(), network::mojom::FetchRequestMode::kCORS);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, CancelInDidFailRedirectCheck) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidFailRedirectCheck())
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::CancelLoader));
StartLoader(RedirectLoopURL(), network::mojom::FetchRequestMode::kCORS);
CallCheckpoint(2);
ServeRequests();
}
TEST_P(ThreadableLoaderTest, ClearInDidFailRedirectCheck) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(GetCheckpoint(), Call(2));
EXPECT_CALL(*Client(), DidFailRedirectCheck())
.WillOnce(InvokeWithoutArgs(this, &ThreadableLoaderTest::ClearLoader));
StartLoader(RedirectLoopURL(), network::mojom::FetchRequestMode::kCORS);
CallCheckpoint(2);
ServeRequests();
}
// This test case checks blink doesn't crash even when the response arrives
// synchronously.
TEST_P(ThreadableLoaderTest, GetResponseSynchronously) {
InSequence s;
EXPECT_CALL(GetCheckpoint(), Call(1));
CreateLoader();
CallCheckpoint(1);
EXPECT_CALL(*Client(), DidFail(_));
EXPECT_CALL(GetCheckpoint(), Call(2));
// Currently didFailAccessControlCheck is dispatched synchronously. This
// test is not saying that didFailAccessControlCheck should be dispatched
// synchronously, but is saying that even when a response is served
// synchronously it should not lead to a crash.
StartLoader(KURL("about:blank"), network::mojom::FetchRequestMode::kCORS);
CallCheckpoint(2);
}
} // namespace
} // namespace blink