| // 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 |