blob: b4054491e18cbca6791bb2539da373b4faeefbf9 [file] [log] [blame]
// Copyright 2013 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/renderer/service_worker/web_service_worker_provider_impl.h"
#include <memory>
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/renderer/service_worker/service_worker_dispatcher.h"
#include "content/renderer/service_worker/service_worker_handle_reference.h"
#include "content/renderer/service_worker/service_worker_provider_context.h"
#include "content/renderer/service_worker/web_service_worker_impl.h"
#include "content/renderer/service_worker/web_service_worker_registration_impl.h"
#include "third_party/WebKit/common/message_port/message_port_channel.h"
#include "third_party/WebKit/common/service_worker/service_worker_provider_type.mojom.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProviderClient.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_registration.mojom.h"
using blink::WebURL;
namespace content {
namespace {
const char kLostConnectionErrorMessage[] =
"Lost connection to the service worker system.";
} // anonymous namespace
WebServiceWorkerProviderImpl::WebServiceWorkerProviderImpl(
ThreadSafeSender* thread_safe_sender,
ServiceWorkerProviderContext* context)
: thread_safe_sender_(thread_safe_sender),
context_(context),
provider_client_(nullptr),
weak_factory_(this) {
DCHECK(context_);
switch (context_->provider_type()) {
case blink::mojom::ServiceWorkerProviderType::kForWindow:
DCHECK(context_->container_host());
context_->SetWebServiceWorkerProvider(weak_factory_.GetWeakPtr());
break;
case blink::mojom::ServiceWorkerProviderType::kForServiceWorker:
// Do nothing.
break;
case blink::mojom::ServiceWorkerProviderType::kForSharedWorker:
case blink::mojom::ServiceWorkerProviderType::kUnknown:
NOTREACHED() << "Unimplemented type: " << context_->provider_type();
break;
}
}
WebServiceWorkerProviderImpl::~WebServiceWorkerProviderImpl() = default;
void WebServiceWorkerProviderImpl::SetClient(
blink::WebServiceWorkerProviderClient* client) {
provider_client_ = client;
if (!provider_client_)
return;
std::unique_ptr<ServiceWorkerHandleReference> controller =
context_->TakeController();
if (!controller)
return;
SetController(std::move(controller), context_->used_features(),
false /* notify_controllerchange */);
}
void WebServiceWorkerProviderImpl::RegisterServiceWorker(
const WebURL& web_pattern,
const WebURL& web_script_url,
blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks) {
DCHECK(callbacks);
GURL pattern(web_pattern);
GURL script_url(web_script_url);
if (pattern.possibly_invalid_spec().size() > url::kMaxURLChars ||
script_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
std::string error_message(kServiceWorkerRegisterErrorPrefix);
error_message += "The provided scriptURL or scope is too long.";
callbacks->OnError(blink::WebServiceWorkerError(
blink::mojom::ServiceWorkerErrorType::kSecurity,
blink::WebString::FromASCII(error_message)));
return;
}
if (!context_->container_host()) {
std::string error_message(kServiceWorkerRegisterErrorPrefix);
error_message += kLostConnectionErrorMessage;
callbacks->OnError(blink::WebServiceWorkerError(
blink::mojom::ServiceWorkerErrorType::kAbort,
blink::WebString::FromASCII(error_message)));
return;
}
TRACE_EVENT_ASYNC_BEGIN2(
"ServiceWorker", "WebServiceWorkerProviderImpl::RegisterServiceWorker",
this, "Scope", pattern.spec(), "Script URL", script_url.spec());
auto options = blink::mojom::ServiceWorkerRegistrationOptions::New(
pattern, update_via_cache);
context_->container_host()->Register(
script_url, std::move(options),
base::BindOnce(&WebServiceWorkerProviderImpl::OnRegistered,
weak_factory_.GetWeakPtr(), std::move(callbacks)));
}
void WebServiceWorkerProviderImpl::GetRegistration(
const blink::WebURL& web_document_url,
std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks) {
DCHECK(callbacks);
GURL document_url(web_document_url);
if (document_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
error_message += "The provided documentURL is too long.";
callbacks->OnError(blink::WebServiceWorkerError(
blink::mojom::ServiceWorkerErrorType::kSecurity,
blink::WebString::FromASCII(error_message)));
return;
}
if (!context_->container_host()) {
std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
error_message += kLostConnectionErrorMessage;
callbacks->OnError(blink::WebServiceWorkerError(
blink::mojom::ServiceWorkerErrorType::kAbort,
blink::WebString::FromASCII(error_message)));
return;
}
TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
"WebServiceWorkerProviderImpl::GetRegistration",
this, "Document URL", document_url.spec());
context_->container_host()->GetRegistration(
document_url,
base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistration,
weak_factory_.GetWeakPtr(), std::move(callbacks)));
}
void WebServiceWorkerProviderImpl::GetRegistrations(
std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks) {
DCHECK(callbacks);
if (!context_->container_host()) {
std::string error_message(kServiceWorkerGetRegistrationsErrorPrefix);
error_message += kLostConnectionErrorMessage;
callbacks->OnError(blink::WebServiceWorkerError(
blink::mojom::ServiceWorkerErrorType::kAbort,
blink::WebString::FromASCII(error_message)));
return;
}
TRACE_EVENT_ASYNC_BEGIN0(
"ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrations", this);
context_->container_host()->GetRegistrations(
base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistrations,
weak_factory_.GetWeakPtr(), std::move(callbacks)));
}
void WebServiceWorkerProviderImpl::GetRegistrationForReady(
std::unique_ptr<WebServiceWorkerGetRegistrationForReadyCallbacks>
callbacks) {
if (!context_->container_host()) {
return;
}
TRACE_EVENT_ASYNC_BEGIN0(
"ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrationForReady",
this);
context_->container_host()->GetRegistrationForReady(base::BindOnce(
&WebServiceWorkerProviderImpl::OnDidGetRegistrationForReady,
weak_factory_.GetWeakPtr(), std::move(callbacks)));
}
bool WebServiceWorkerProviderImpl::ValidateScopeAndScriptURL(
const blink::WebURL& scope,
const blink::WebURL& script_url,
blink::WebString* error_message) {
std::string error;
bool has_error = ServiceWorkerUtils::ContainsDisallowedCharacter(
scope, script_url, &error);
if (has_error)
*error_message = blink::WebString::FromUTF8(error);
return !has_error;
}
void WebServiceWorkerProviderImpl::SetController(
std::unique_ptr<ServiceWorkerHandleReference> controller,
const std::set<blink::mojom::WebFeature>& features,
bool should_notify_controller_change) {
if (!provider_client_)
return;
for (blink::mojom::WebFeature feature : features)
provider_client_->CountFeature(feature);
provider_client_->SetController(
WebServiceWorkerImpl::CreateHandle(
GetDispatcher()->GetOrCreateServiceWorker(std::move(controller))),
should_notify_controller_change);
}
void WebServiceWorkerProviderImpl::PostMessageToClient(
blink::mojom::ServiceWorkerObjectInfoPtr source,
const base::string16& message,
std::vector<mojo::ScopedMessagePipeHandle> message_pipes) {
if (!provider_client_)
return;
scoped_refptr<WebServiceWorkerImpl> worker =
GetDispatcher()->GetOrCreateServiceWorker(
ServiceWorkerHandleReference::Create(std::move(source),
thread_safe_sender_.get()));
auto message_ports =
blink::MessagePortChannel::CreateFromHandles(std::move(message_pipes));
provider_client_->DispatchMessageEvent(
WebServiceWorkerImpl::CreateHandle(std::move(worker)),
blink::WebString::FromUTF16(message), std::move(message_ports));
}
void WebServiceWorkerProviderImpl::CountFeature(
blink::mojom::WebFeature feature) {
if (!provider_client_)
return;
provider_client_->CountFeature(feature);
}
int WebServiceWorkerProviderImpl::provider_id() const {
return context_->provider_id();
}
ServiceWorkerDispatcher* WebServiceWorkerProviderImpl::GetDispatcher() {
return ServiceWorkerDispatcher::GetThreadSpecificInstance();
}
void WebServiceWorkerProviderImpl::OnRegistered(
std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks,
blink::mojom::ServiceWorkerErrorType error,
const base::Optional<std::string>& error_msg,
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
TRACE_EVENT_ASYNC_END2(
"ServiceWorker", "WebServiceWorkerProviderImpl::RegisterServiceWorker",
this, "Error", ServiceWorkerUtils::ErrorTypeToString(error), "Message",
error_msg ? *error_msg : "Success");
if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
DCHECK(error_msg);
DCHECK(!registration);
callbacks->OnError(blink::WebServiceWorkerError(
error, blink::WebString::FromASCII(*error_msg)));
return;
}
DCHECK(!error_msg);
DCHECK(registration);
DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
registration->registration_id);
callbacks->OnSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
context_->GetOrCreateRegistrationForServiceWorkerClient(
std::move(registration))));
}
void WebServiceWorkerProviderImpl::OnDidGetRegistration(
std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks,
blink::mojom::ServiceWorkerErrorType error,
const base::Optional<std::string>& error_msg,
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
TRACE_EVENT_ASYNC_END2("ServiceWorker",
"WebServiceWorkerProviderImpl::GetRegistration", this,
"Error", ServiceWorkerUtils::ErrorTypeToString(error),
"Message", error_msg ? *error_msg : "Success");
if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
DCHECK(error_msg);
DCHECK(!registration);
callbacks->OnError(blink::WebServiceWorkerError(
error, blink::WebString::FromASCII(*error_msg)));
return;
}
DCHECK(!error_msg);
// |registration| is nullptr if there is no registration at the scope or it's
// uninstalling.
if (!registration) {
callbacks->OnSuccess(nullptr);
return;
}
DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
registration->registration_id);
scoped_refptr<WebServiceWorkerRegistrationImpl> impl =
context_->GetOrCreateRegistrationForServiceWorkerClient(
std::move(registration));
DCHECK(impl);
callbacks->OnSuccess(
WebServiceWorkerRegistrationImpl::CreateHandle(std::move(impl)));
}
void WebServiceWorkerProviderImpl::OnDidGetRegistrations(
std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks,
blink::mojom::ServiceWorkerErrorType error,
const base::Optional<std::string>& error_msg,
base::Optional<
std::vector<blink::mojom::ServiceWorkerRegistrationObjectInfoPtr>>
infos) {
TRACE_EVENT_ASYNC_END2("ServiceWorker",
"WebServiceWorkerProviderImpl::GetRegistrations", this,
"Error", ServiceWorkerUtils::ErrorTypeToString(error),
"Message", error_msg ? *error_msg : "Success");
if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
DCHECK(error_msg);
DCHECK(!infos);
callbacks->OnError(blink::WebServiceWorkerError(
error, blink::WebString::FromASCII(*error_msg)));
return;
}
DCHECK(!error_msg);
DCHECK(infos);
using WebServiceWorkerRegistrationHandles =
WebServiceWorkerProvider::WebServiceWorkerRegistrationHandles;
std::unique_ptr<WebServiceWorkerRegistrationHandles> registrations =
std::make_unique<WebServiceWorkerRegistrationHandles>(infos->size());
for (size_t i = 0; i < infos->size(); ++i) {
DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
(*infos)[i]->registration_id);
(*registrations)[i] = WebServiceWorkerRegistrationImpl::CreateHandle(
context_->GetOrCreateRegistrationForServiceWorkerClient(
std::move((*infos)[i])));
}
callbacks->OnSuccess(std::move(registrations));
}
void WebServiceWorkerProviderImpl::OnDidGetRegistrationForReady(
std::unique_ptr<WebServiceWorkerGetRegistrationForReadyCallbacks> callbacks,
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
TRACE_EVENT_ASYNC_END0(
"ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrationForReady",
this);
// TODO(leonhsl): Currently the only reason that we allow nullable
// |registration| is: impl of the mojo method
// GetRegistrationForReady() needs to respond some non-sense params even if it
// has found that the request is a bad message and has called
// mojo::ReportBadMessage(), this is forced by Mojo, please see
// content::ServiceWorkerProviderHost::GetRegistrationForReady(). We'll find a
// better solution once the discussion at
// https://groups.google.com/a/chromium.org/forum/#!topic/chromium-mojo/NNsogKNurlA
// settled.
CHECK(registration);
DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
registration->registration_id);
callbacks->OnSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
context_->GetOrCreateRegistrationForServiceWorkerClient(
std::move(registration))));
}
} // namespace content