blob: 5ee2a27d8c86ad0e3caf98ea4ee2a6a26f150d42 [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 "content/browser/service_worker/service_worker_test_utils.h"
#include <memory>
#include <utility>
#include "base/barrier_closure.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_database.h"
#include "content/browser/service_worker/service_worker_disk_cache.h"
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/public/common/child_process_host.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "net/http/http_response_info.h"
#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
namespace content {
namespace {
// A mock SharedURLLoaderFactory that always fails to start.
// TODO(bashi): Make this factory not to fail when unit tests actually need
// this to be working.
class MockSharedURLLoaderFactory final
: public network::SharedURLLoaderFactory {
public:
MockSharedURLLoaderFactory() = default;
// network::mojom::URLLoaderFactory:
void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
network::mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag&
traffic_annotation) override {
client->OnComplete(
network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED));
}
void Clone(network::mojom::URLLoaderFactoryRequest request) override {
NOTREACHED();
}
// network::SharedURLLoaderFactory:
std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override {
NOTREACHED();
return nullptr;
}
private:
friend class base::RefCounted<MockSharedURLLoaderFactory>;
~MockSharedURLLoaderFactory() override = default;
DISALLOW_COPY_AND_ASSIGN(MockSharedURLLoaderFactory);
};
// Returns MockSharedURLLoaderFactory.
class MockSharedURLLoaderFactoryInfo final
: public network::SharedURLLoaderFactoryInfo {
public:
MockSharedURLLoaderFactoryInfo() = default;
~MockSharedURLLoaderFactoryInfo() override = default;
protected:
scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override {
return base::MakeRefCounted<MockSharedURLLoaderFactory>();
}
private:
DISALLOW_COPY_AND_ASSIGN(MockSharedURLLoaderFactoryInfo);
};
void OnWriteBodyInfoToDiskCache(
std::unique_ptr<ServiceWorkerResponseWriter> writer,
const std::string& body,
base::OnceClosure callback,
int result) {
EXPECT_GE(result, 0);
scoped_refptr<net::IOBuffer> body_buffer =
base::MakeRefCounted<net::StringIOBuffer>(body);
ServiceWorkerResponseWriter* writer_rawptr = writer.get();
writer_rawptr->WriteData(
body_buffer.get(), body.size(),
base::BindOnce(
[](std::unique_ptr<ServiceWorkerResponseWriter> /* unused */,
base::OnceClosure callback, int expected, int result) {
EXPECT_EQ(expected, result);
std::move(callback).Run();
},
std::move(writer), std::move(callback), body.size()));
}
void WriteBodyToDiskCache(std::unique_ptr<ServiceWorkerResponseWriter> writer,
std::unique_ptr<net::HttpResponseInfo> info,
const std::string& body,
base::OnceClosure callback) {
scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(info));
info_buffer->response_data_size = body.size();
ServiceWorkerResponseWriter* writer_rawptr = writer.get();
writer_rawptr->WriteInfo(
info_buffer.get(),
base::BindOnce(&OnWriteBodyInfoToDiskCache, std::move(writer), body,
std::move(callback)));
}
void WriteMetaDataToDiskCache(
std::unique_ptr<ServiceWorkerResponseMetadataWriter> writer,
const std::string& meta_data,
base::OnceClosure callback) {
scoped_refptr<net::IOBuffer> meta_data_buffer =
base::MakeRefCounted<net::StringIOBuffer>(meta_data);
ServiceWorkerResponseMetadataWriter* writer_rawptr = writer.get();
writer_rawptr->WriteMetadata(
meta_data_buffer.get(), meta_data.size(),
base::BindOnce(
[](std::unique_ptr<ServiceWorkerResponseMetadataWriter> /* unused */,
base::OnceClosure callback, int expected, int result) {
EXPECT_EQ(expected, result);
std::move(callback).Run();
},
std::move(writer), std::move(callback), meta_data.size()));
}
} // namespace
ServiceWorkerRemoteProviderEndpoint::ServiceWorkerRemoteProviderEndpoint() {}
ServiceWorkerRemoteProviderEndpoint::ServiceWorkerRemoteProviderEndpoint(
ServiceWorkerRemoteProviderEndpoint&& other)
: host_ptr_(std::move(other.host_ptr_)),
client_request_(std::move(other.client_request_)) {}
ServiceWorkerRemoteProviderEndpoint::~ServiceWorkerRemoteProviderEndpoint() {}
void ServiceWorkerRemoteProviderEndpoint::BindWithProviderHostInfo(
blink::mojom::ServiceWorkerProviderHostInfoPtr* info) {
blink::mojom::ServiceWorkerContainerAssociatedPtr client_ptr;
client_request_ = mojo::MakeRequestAssociatedWithDedicatedPipe(&client_ptr);
(*info)->client_ptr_info = client_ptr.PassInterface();
(*info)->host_request =
mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr_);
}
void ServiceWorkerRemoteProviderEndpoint::BindWithProviderInfo(
blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr info) {
client_request_ = std::move(info->client_request);
host_ptr_.Bind(std::move(info->host_ptr_info));
}
blink::mojom::ServiceWorkerProviderHostInfoPtr CreateProviderHostInfoForWindow(
int provider_id,
int route_id) {
return blink::mojom::ServiceWorkerProviderHostInfo::New(
provider_id, route_id,
blink::mojom::ServiceWorkerProviderType::kForWindow,
true /* is_parent_frame_secure */, nullptr /* host_request */,
nullptr /* client_ptr_info */);
}
std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForWindow(
int process_id,
int provider_id,
bool is_parent_frame_secure,
base::WeakPtr<ServiceWorkerContextCore> context,
ServiceWorkerRemoteProviderEndpoint* output_endpoint) {
blink::mojom::ServiceWorkerProviderHostInfoPtr info =
CreateProviderHostInfoForWindow(provider_id, 1 /* route_id */);
info->is_parent_frame_secure = is_parent_frame_secure;
output_endpoint->BindWithProviderHostInfo(&info);
return ServiceWorkerProviderHost::Create(process_id, std::move(info),
std::move(context));
}
base::WeakPtr<ServiceWorkerProviderHost>
CreateProviderHostForServiceWorkerContext(
int process_id,
bool is_parent_frame_secure,
ServiceWorkerVersion* hosted_version,
base::WeakPtr<ServiceWorkerContextCore> context,
ServiceWorkerRemoteProviderEndpoint* output_endpoint) {
auto provider_info =
blink::mojom::ServiceWorkerProviderInfoForStartWorker::New();
base::WeakPtr<ServiceWorkerProviderHost> host =
ServiceWorkerProviderHost::PreCreateForController(
std::move(context), base::WrapRefCounted(hosted_version),
&provider_info);
scoped_refptr<network::SharedURLLoaderFactory> loader_factory;
if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<MockSharedURLLoaderFactoryInfo>());
}
provider_info = host->CompleteStartWorkerPreparation(
process_id, loader_factory, std::move(provider_info));
output_endpoint->BindWithProviderInfo(std::move(provider_info));
return host;
}
ServiceWorkerDatabase::ResourceRecord WriteToDiskCacheSync(
ServiceWorkerStorage* storage,
const GURL& script_url,
int64_t resource_id,
const std::vector<std::pair<std::string, std::string>>& headers,
const std::string& body,
const std::string& meta_data) {
base::RunLoop loop;
ServiceWorkerDatabase::ResourceRecord record =
WriteToDiskCacheAsync(storage, script_url, resource_id, headers, body,
meta_data, loop.QuitClosure());
loop.Run();
return record;
}
ServiceWorkerDatabase::ResourceRecord
WriteToDiskCacheWithCustomResponseInfoSync(
ServiceWorkerStorage* storage,
const GURL& script_url,
int64_t resource_id,
std::unique_ptr<net::HttpResponseInfo> http_info,
const std::string& body,
const std::string& meta_data) {
base::RunLoop loop;
ServiceWorkerDatabase::ResourceRecord record =
WriteToDiskCacheWithCustomResponseInfoAsync(
storage, script_url, resource_id, std::move(http_info), body,
meta_data, loop.QuitClosure());
loop.Run();
return record;
}
ServiceWorkerDatabase::ResourceRecord WriteToDiskCacheAsync(
ServiceWorkerStorage* storage,
const GURL& script_url,
int64_t resource_id,
const std::vector<std::pair<std::string, std::string>>& headers,
const std::string& body,
const std::string& meta_data,
base::OnceClosure callback) {
std::unique_ptr<net::HttpResponseInfo> info =
std::make_unique<net::HttpResponseInfo>();
info->request_time = base::Time::Now();
info->response_time = base::Time::Now();
info->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.0 200 OK\0\0");
for (const auto& header : headers)
info->headers->AddHeader(header.first + ": " + header.second);
return WriteToDiskCacheWithCustomResponseInfoAsync(
storage, script_url, resource_id, std::move(info), body, meta_data,
std::move(callback));
}
ServiceWorkerDatabase::ResourceRecord
WriteToDiskCacheWithCustomResponseInfoAsync(
ServiceWorkerStorage* storage,
const GURL& script_url,
int64_t resource_id,
std::unique_ptr<net::HttpResponseInfo> http_info,
const std::string& body,
const std::string& meta_data,
base::OnceClosure callback) {
base::RepeatingClosure barrier = base::BarrierClosure(2, std::move(callback));
auto body_writer = storage->CreateResponseWriter(resource_id);
WriteBodyToDiskCache(std::move(body_writer), std::move(http_info), body,
barrier);
auto metadata_writer = storage->CreateResponseMetadataWriter(resource_id);
WriteMetaDataToDiskCache(std::move(metadata_writer), meta_data,
std::move(barrier));
return ServiceWorkerDatabase::ResourceRecord(resource_id, script_url,
body.size());
}
MockServiceWorkerResponseReader::MockServiceWorkerResponseReader()
: ServiceWorkerResponseReader(/* resource_id=*/0, /*disk_cache=*/nullptr) {}
MockServiceWorkerResponseReader::~MockServiceWorkerResponseReader() {}
void MockServiceWorkerResponseReader::ReadInfo(
HttpResponseInfoIOBuffer* info_buf,
OnceCompletionCallback callback) {
DCHECK(!expected_reads_.empty());
ExpectedRead expected = expected_reads_.front();
EXPECT_TRUE(expected.info);
if (expected.async) {
pending_info_ = info_buf;
pending_callback_ = std::move(callback);
} else {
expected_reads_.pop();
info_buf->response_data_size = expected.len;
std::move(callback).Run(expected.result);
}
}
void MockServiceWorkerResponseReader::ReadData(
net::IOBuffer* buf,
int buf_len,
OnceCompletionCallback callback) {
DCHECK(!expected_reads_.empty());
ExpectedRead expected = expected_reads_.front();
EXPECT_FALSE(expected.info);
if (expected.async) {
pending_callback_ = std::move(callback);
pending_buffer_ = buf;
pending_buffer_len_ = static_cast<size_t>(buf_len);
} else {
expected_reads_.pop();
if (expected.len > 0) {
size_t to_read = std::min(static_cast<size_t>(buf_len), expected.len);
memcpy(buf->data(), expected.data, to_read);
}
std::move(callback).Run(expected.result);
}
}
void MockServiceWorkerResponseReader::ExpectReadInfo(size_t len,
bool async,
int result) {
expected_reads_.push(ExpectedRead(len, async, result));
}
void MockServiceWorkerResponseReader::ExpectReadInfoOk(size_t len, bool async) {
expected_reads_.push(ExpectedRead(len, async, len));
}
void MockServiceWorkerResponseReader::ExpectReadData(const char* data,
size_t len,
bool async,
int result) {
expected_reads_.push(ExpectedRead(data, len, async, result));
}
void MockServiceWorkerResponseReader::ExpectReadDataOk(const std::string& data,
bool async) {
expected_reads_.push(
ExpectedRead(data.data(), data.size(), async, data.size()));
}
void MockServiceWorkerResponseReader::ExpectReadOk(
const std::vector<std::string>& stored_data,
const size_t bytes_stored,
const bool async) {
ExpectReadInfoOk(bytes_stored, async);
for (const auto& data : stored_data)
ExpectReadDataOk(data, async);
}
void MockServiceWorkerResponseReader::CompletePendingRead() {
DCHECK(!expected_reads_.empty());
ExpectedRead expected = expected_reads_.front();
expected_reads_.pop();
EXPECT_TRUE(expected.async);
if (expected.info) {
pending_info_->response_data_size = expected.len;
} else {
size_t to_read = std::min(pending_buffer_len_, expected.len);
if (to_read > 0)
memcpy(pending_buffer_->data(), expected.data, to_read);
}
pending_info_ = nullptr;
pending_buffer_ = nullptr;
OnceCompletionCallback callback = std::move(pending_callback_);
pending_callback_.Reset();
std::move(callback).Run(expected.result);
}
MockServiceWorkerResponseWriter::MockServiceWorkerResponseWriter()
: ServiceWorkerResponseWriter(/*resource_id=*/0, /*disk_cache=*/nullptr),
info_written_(0),
data_written_(0) {}
MockServiceWorkerResponseWriter::~MockServiceWorkerResponseWriter() = default;
void MockServiceWorkerResponseWriter::WriteInfo(
HttpResponseInfoIOBuffer* info_buf,
OnceCompletionCallback callback) {
DCHECK(!expected_writes_.empty());
ExpectedWrite write = expected_writes_.front();
EXPECT_TRUE(write.is_info);
if (write.result > 0) {
EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
info_written_ += info_buf->response_data_size;
}
if (!write.async) {
expected_writes_.pop();
std::move(callback).Run(write.result);
} else {
pending_callback_ = std::move(callback);
}
}
void MockServiceWorkerResponseWriter::WriteData(
net::IOBuffer* buf,
int buf_len,
OnceCompletionCallback callback) {
DCHECK(!expected_writes_.empty());
ExpectedWrite write = expected_writes_.front();
EXPECT_FALSE(write.is_info);
if (write.result > 0) {
EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
data_written_ += buf_len;
}
if (!write.async) {
expected_writes_.pop();
std::move(callback).Run(write.result);
} else {
pending_callback_ = std::move(callback);
}
}
void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length,
bool async) {
ExpectWriteInfo(length, async, length);
}
void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length,
bool async) {
ExpectWriteData(length, async, length);
}
void MockServiceWorkerResponseWriter::ExpectWriteInfo(size_t length,
bool async,
int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
ExpectedWrite expected(true, length, async, result);
expected_writes_.push(expected);
}
void MockServiceWorkerResponseWriter::ExpectWriteData(size_t length,
bool async,
int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
ExpectedWrite expected(false, length, async, result);
expected_writes_.push(expected);
}
void MockServiceWorkerResponseWriter::CompletePendingWrite() {
DCHECK(!expected_writes_.empty());
ExpectedWrite write = expected_writes_.front();
DCHECK(write.async);
expected_writes_.pop();
std::move(pending_callback_).Run(write.result);
}
} // namespace content