blob: 976f85b2aaf2a9016a2e6cd9f0d5492c92585fdb [file] [log] [blame]
// Copyright 2014 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/embedded_worker_test_helper.h"
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_test_utils.h"
#include "content/common/renderer.mojom.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "mojo/public/cpp/bindings/associated_binding_set.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "net/http/http_util.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
namespace content {
// A URLLoaderFactory that returns 200 OK with a simple body to any request.
class EmbeddedWorkerTestHelper::MockNetworkURLLoaderFactory final
: public network::mojom::URLLoaderFactory {
public:
MockNetworkURLLoaderFactory() = default;
// network::mojom::URLLoaderFactory implementation.
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 {
std::string headers = "HTTP/1.1 200 OK\n\n";
net::HttpResponseInfo info;
info.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
net::HttpUtil::AssembleRawHeaders(headers));
network::ResourceResponseHead response;
response.headers = info.headers;
response.headers->GetMimeType(&response.mime_type);
client->OnReceiveResponse(response);
std::string body = "this body came from the network";
uint32_t bytes_written = body.size();
mojo::DataPipe data_pipe;
data_pipe.producer_handle->WriteData(body.data(), &bytes_written,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
network::URLLoaderCompletionStatus status;
status.error_code = net::OK;
client->OnComplete(status);
}
void Clone(network::mojom::URLLoaderFactoryRequest request) override {
bindings_.AddBinding(this, std::move(request));
}
private:
mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
DISALLOW_COPY_AND_ASSIGN(MockNetworkURLLoaderFactory);
};
class EmbeddedWorkerTestHelper::MockRendererInterface : public mojom::Renderer {
public:
// |helper| must outlive this.
explicit MockRendererInterface(EmbeddedWorkerTestHelper* helper)
: helper_(helper) {}
void AddBinding(mojom::RendererAssociatedRequest request) {
bindings_.AddBinding(this, std::move(request));
}
private:
void CreateEmbedderRendererService(
service_manager::mojom::ServiceRequest service_request) override {
NOTREACHED();
}
void CreateView(mojom::CreateViewParamsPtr) override { NOTREACHED(); }
void CreateFrame(mojom::CreateFrameParamsPtr) override { NOTREACHED(); }
void SetUpEmbeddedWorkerChannelForServiceWorker(
blink::mojom::EmbeddedWorkerInstanceClientRequest client_request)
override {
helper_->OnInstanceClientRequest(std::move(client_request));
}
void CreateFrameProxy(
int32_t routing_id,
int32_t render_view_routing_id,
int32_t opener_routing_id,
int32_t parent_routing_id,
const FrameReplicationState& replicated_state,
const base::UnguessableToken& devtools_frame_token) override {
NOTREACHED();
}
void OnNetworkConnectionChanged(
net::NetworkChangeNotifier::ConnectionType type,
double max_bandwidth_mbps) override {
NOTREACHED();
}
void OnNetworkQualityChanged(net::EffectiveConnectionType type,
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
double bandwidth_kbps) override {
NOTREACHED();
}
void SetWebKitSharedTimersSuspended(bool suspend) override { NOTREACHED(); }
void SetUserAgent(const std::string& user_agent) override { NOTREACHED(); }
void SetUserAgentMetadata(const blink::UserAgentMetadata& metadata) override {
NOTREACHED();
}
void UpdateScrollbarTheme(
mojom::UpdateScrollbarThemeParamsPtr params) override {
NOTREACHED();
}
void OnSystemColorsChanged(int32_t aqua_color_variant,
const std::string& highlight_text_color,
const std::string& highlight_color) override {
NOTREACHED();
}
void PurgePluginListCache(bool reload_pages) override { NOTREACHED(); }
void SetProcessState(mojom::RenderProcessState process_state) override {
NOTREACHED();
}
void SetSchedulerKeepActive(bool keep_active) override { NOTREACHED(); }
void SetIsLockedToSite() override { NOTREACHED(); }
void EnableV8LowMemoryMode() override { NOTREACHED(); }
EmbeddedWorkerTestHelper* helper_;
mojo::AssociatedBindingSet<mojom::Renderer> bindings_;
};
EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(
const base::FilePath& user_data_directory)
: browser_context_(std::make_unique<TestBrowserContext>()),
render_process_host_(
std::make_unique<MockRenderProcessHost>(browser_context_.get())),
new_render_process_host_(
std::make_unique<MockRenderProcessHost>(browser_context_.get())),
wrapper_(base::MakeRefCounted<ServiceWorkerContextWrapper>(
browser_context_.get())),
next_thread_id_(0),
mock_render_process_id_(render_process_host_->GetID()),
new_mock_render_process_id_(new_render_process_host_->GetID()),
url_loader_factory_getter_(
base::MakeRefCounted<URLLoaderFactoryGetter>()) {
scoped_refptr<base::SequencedTaskRunner> database_task_runner =
base::ThreadTaskRunnerHandle::Get();
wrapper_->InitInternal(user_data_directory, std::move(database_task_runner),
nullptr, nullptr, nullptr,
url_loader_factory_getter_.get());
wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id());
wrapper_->process_manager()->SetNewProcessIdForTest(new_render_process_id());
wrapper_->InitializeResourceContext(browser_context_->GetResourceContext());
// Install a mocked mojom::Renderer interface to catch requests to
// establish Mojo connection for EWInstanceClient.
mock_renderer_interface_ = std::make_unique<MockRendererInterface>(this);
auto renderer_interface_ptr =
std::make_unique<mojom::RendererAssociatedPtr>();
mock_renderer_interface_->AddBinding(
mojo::MakeRequestAssociatedWithDedicatedPipe(
renderer_interface_ptr.get()));
render_process_host_->OverrideRendererInterfaceForTesting(
std::move(renderer_interface_ptr));
auto new_renderer_interface_ptr =
std::make_unique<mojom::RendererAssociatedPtr>();
mock_renderer_interface_->AddBinding(
mojo::MakeRequestAssociatedWithDedicatedPipe(
new_renderer_interface_ptr.get()));
new_render_process_host_->OverrideRendererInterfaceForTesting(
std::move(new_renderer_interface_ptr));
default_network_loader_factory_ =
std::make_unique<MockNetworkURLLoaderFactory>();
SetNetworkFactory(default_network_loader_factory_.get());
}
void EmbeddedWorkerTestHelper::SetNetworkFactory(
network::mojom::URLLoaderFactory* factory) {
if (!factory)
factory = default_network_loader_factory_.get();
// Reset factory in URLLoaderFactoryGetter so that we don't hit DCHECK()
// there.
url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr);
url_loader_factory_getter_->SetNetworkFactoryForTesting(factory);
render_process_host_->OverrideURLLoaderFactory(factory);
new_render_process_host_->OverrideURLLoaderFactory(factory);
}
void EmbeddedWorkerTestHelper::AddPendingInstanceClient(
std::unique_ptr<FakeEmbeddedWorkerInstanceClient> client) {
pending_embedded_worker_instance_clients_.push(std::move(client));
}
void EmbeddedWorkerTestHelper::AddPendingServiceWorker(
std::unique_ptr<FakeServiceWorker> service_worker) {
pending_service_workers_.push(std::move(service_worker));
}
void EmbeddedWorkerTestHelper::OnInstanceClientRequest(
blink::mojom::EmbeddedWorkerInstanceClientRequest request) {
std::unique_ptr<FakeEmbeddedWorkerInstanceClient> client;
if (!pending_embedded_worker_instance_clients_.empty()) {
// Use the instance client that was registered for this message.
client = std::move(pending_embedded_worker_instance_clients_.front());
pending_embedded_worker_instance_clients_.pop();
if (!client) {
// Some tests provide a nullptr to drop the request.
return;
}
} else {
client = CreateInstanceClient();
}
client->Bind(std::move(request));
instance_clients_.insert(std::move(client));
}
void EmbeddedWorkerTestHelper::OnServiceWorkerRequest(
blink::mojom::ServiceWorkerRequest request) {
std::unique_ptr<FakeServiceWorker> service_worker;
if (!pending_service_workers_.empty()) {
// Use the service worker that was registered for this message.
service_worker = std::move(pending_service_workers_.front());
pending_service_workers_.pop();
if (!service_worker) {
// Some tests provide a nullptr to drop the request.
return;
}
} else {
service_worker = CreateServiceWorker();
}
service_worker->Bind(std::move(request));
service_workers_.insert(std::move(service_worker));
}
void EmbeddedWorkerTestHelper::RemoveInstanceClient(
FakeEmbeddedWorkerInstanceClient* instance_client) {
auto it = instance_clients_.find(instance_client);
instance_clients_.erase(it);
}
void EmbeddedWorkerTestHelper::RemoveServiceWorker(
FakeServiceWorker* service_worker) {
auto it = service_workers_.find(service_worker);
service_workers_.erase(it);
}
EmbeddedWorkerTestHelper::~EmbeddedWorkerTestHelper() {
if (wrapper_.get())
wrapper_->Shutdown();
}
ServiceWorkerContextCore* EmbeddedWorkerTestHelper::context() {
return wrapper_->context();
}
void EmbeddedWorkerTestHelper::ShutdownContext() {
wrapper_->Shutdown();
wrapper_ = nullptr;
}
// static
net::HttpResponseInfo EmbeddedWorkerTestHelper::CreateHttpResponseInfo() {
net::HttpResponseInfo info;
const char data[] =
"HTTP/1.1 200 OK\0"
"Content-Type: application/javascript\0"
"\0";
info.headers =
new net::HttpResponseHeaders(std::string(data, base::size(data)));
return info;
}
void EmbeddedWorkerTestHelper::PopulateScriptCacheMap(
int64_t version_id,
base::OnceClosure callback) {
ServiceWorkerVersion* version = context()->GetLiveVersion(version_id);
if (!version) {
std::move(callback).Run();
return;
}
if (!version->script_cache_map()->size()) {
std::vector<ServiceWorkerDatabase::ResourceRecord> records;
// Add a dummy ResourceRecord for the main script to the script cache map of
// the ServiceWorkerVersion.
records.push_back(WriteToDiskCacheAsync(
context()->storage(), version->script_url(),
context()->storage()->NewResourceId(), {} /* headers */, "I'm a body",
"I'm a meta data", std::move(callback)));
version->script_cache_map()->SetResources(records);
}
if (!version->GetMainScriptHttpResponseInfo())
version->SetMainScriptHttpResponseInfo(CreateHttpResponseInfo());
// Call |callback| if |version| already has ResourceRecords.
if (!callback.is_null())
std::move(callback).Run();
}
std::unique_ptr<FakeEmbeddedWorkerInstanceClient>
EmbeddedWorkerTestHelper::CreateInstanceClient() {
return std::make_unique<FakeEmbeddedWorkerInstanceClient>(this);
}
std::unique_ptr<FakeServiceWorker>
EmbeddedWorkerTestHelper::CreateServiceWorker() {
return std::make_unique<FakeServiceWorker>(this);
}
} // namespace content