// 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. #ifndef CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_ #define CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_ #include #include #include #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" #include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_registration.h" namespace base { class SingleThreadTaskRunner; } namespace blink { class WebServiceWorkerRegistrationProxy; } namespace content { class WebServiceWorkerImpl; class ServiceWorkerProviderContext; // WebServiceWorkerRegistrationImpl corresponds to one ServiceWorkerRegistration // object in JavaScript. It is owned by // content::ServiceWorkerRegistrationObjectHost in the browser process, but also // is refcounted by WebServiceWorkerRegistration::Handles in the renderer // process. See the detailed lifecycle explanation below. // // A WebServiceWorkerRegistrationImpl is created when the browser process sends // the first ServiceWorkerRegistrationObjectInfo to the renderer process that // describes the desired JavaScript object. The instance is created and takes // ownership of the object info. The object info has a Mojo connection to a // ServiceWorkerRegistrationObjectHost in the browser process // ((|this->info_.host_ptr_info|) . In addition, The // WebServiceWorkerRegistrationImpl itself is connected with the // ServiceWorkerRegistrationObjectHost (|this->binding_|). Creation always // happens in order to create a ServiceWorkerRegistration JavaScript object in // Blink. The instance is shared with Blink via // WebServiceWorkerRegistration::Handle. As long as a handle is alive in Blink, // this instance should not die. // // During the lifetime of WebServiceWorkerRegistrationImpl, multiple // WebServiceWorkerRegistration::Handles may be created and held by Blink. If // the browser process sends another ServiceWorkerRegistrationObjectInfo to the // renderer process for this same JavaScript object, the renderer reuses the // existing WebServiceWorkerRegistrationImpl instance and creates a new // WebServiceWorkerRegistration::Handle to share with Blink. // // If all WebServiceWorkerRegistration::Handles are destroyed, the // WebServiceWorkerRegistrationImpl clears |info_|, which informs the // ServiceWorkerRegistrationObjectHost in the browser process that this instance // is ready to be destroyed. If there was no ServiceWorkerRegistrationObjectInfo // inflight, the browser process destroys the Mojo connection to this instance, // which finally destroys it. // // Another destruction scenario is that the browser process destroys the // ServiceWorkerRegistrationObject Mojo connection while some // WebServiceWorkerRegistration::Handles are still held by Blink. In such a case // this instance will finally be destroyed after all Blink destroys all the // WebServiceWorkerRegistration::Handles. class CONTENT_EXPORT WebServiceWorkerRegistrationImpl : public blink::mojom::ServiceWorkerRegistrationObject, public blink::WebServiceWorkerRegistration, public base::RefCounted { public: // |io_task_runner| is used to bind |host_for_global_scope_| and |binding_| // for service worker execution context, as both of // ServiceWorkerRegistrationObjectHost and ServiceWorkerRegistrationObject are // Channel-associated interfaces and need to be bound on either the main or IO // thread. static scoped_refptr CreateForServiceWorkerGlobalScope( blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info, scoped_refptr io_task_runner); static scoped_refptr CreateForServiceWorkerClient( blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info, base::WeakPtr provider_context); void AttachForServiceWorkerClient( blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info); // blink::WebServiceWorkerRegistration overrides. void SetProxy(blink::WebServiceWorkerRegistrationProxy* proxy) override; blink::WebServiceWorkerRegistrationProxy* Proxy() override; blink::WebURL Scope() const override; blink::mojom::ServiceWorkerUpdateViaCache UpdateViaCache() const override; void Update( std::unique_ptr callbacks) override; void Unregister(std::unique_ptr callbacks) override; void EnableNavigationPreload( bool enable, std::unique_ptr callbacks) override; void GetNavigationPreloadState( std::unique_ptr callbacks) override; void SetNavigationPreloadHeader( const blink::WebString& value, std::unique_ptr callbacks) override; int64_t RegistrationId() const override; // Creates blink::WebServiceWorkerRegistration::Handle object that owns a // reference to the given WebServiceWorkerRegistrationImpl object. static std::unique_ptr CreateHandle(scoped_refptr registration); private: class WebCallbacksHolder; friend class base::RefCounted; WebServiceWorkerRegistrationImpl( blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info, base::WeakPtr provider_context); ~WebServiceWorkerRegistrationImpl() override; void SetInstalling(blink::mojom::ServiceWorkerObjectInfoPtr info); void SetWaiting(blink::mojom::ServiceWorkerObjectInfoPtr info); void SetActive(blink::mojom::ServiceWorkerObjectInfoPtr info); // Refreshes the JavaScript ServiceWorkerRegistration object (|proxy_|) with // the {installing,waiting,active} service worker object infos from |info_|. void RefreshVersionAttributes(); // Implements blink::mojom::ServiceWorkerRegistrationObject. void SetVersionAttributes( int changed_mask, blink::mojom::ServiceWorkerObjectInfoPtr installing, blink::mojom::ServiceWorkerObjectInfoPtr waiting, blink::mojom::ServiceWorkerObjectInfoPtr active) override; void UpdateFound() override; using ResponseCallback = base::OnceCallback&)>; // |callback| owns a Web*Callbacks instance which constrains itself to be // destroyed on the same thread that created it (the worker thread). // // But our |host_for_global_scope_| is a Mojo thread safe ptr, which is used // to make Mojo calls on the worker thread but its underlying Mojo connection // is bound on the IO thread. Currently when we call |host_for_global_scope_| // methods, we're moving |callback| along the way from the worker thread to // the IO thread, then waiting until |callback| is posted back to the worker // thread. However, the post back operation will fail if the worker thread has // already been destroyed for some reasons, in such a case the poster on the // IO thread has to destroy |callback| there, then causing a crash. // // To solve the above problem, this function creates a wrapper response // callback adapting to run |callback|, since the Web*Callbacks are stored // separately outside it, this wrapper callback is thread-safe and can be // destroyed on any thread, thus, we can pass this wrapper callback to // |host_for_global_scope_| which can move this wrapper between the worker // thread and IO thread freely. // // TODO(leonhsl): Once we can avoid using Mojo thread safe ptr for // |host_for_global_scope_|, we can eliminate this wrapping mechanism. ResponseCallback WrapResponseCallback(ResponseCallback callback); blink::mojom::ServiceWorkerRegistrationObjectHost:: GetNavigationPreloadStateCallback WrapWebGetNavigationPreloadStateCallbacks( std::unique_ptr callbacks); // RefCounted traits implementation, rather than delete |impl| directly, calls // |impl->DetachAndMaybeDestroy()| to notify that the last reference to it has // gone away. static void Destruct(const WebServiceWorkerRegistrationImpl* impl); // Enumeration of the possible |state_| during lifetime of this class. // - |kInitial| --> |kAttachedAndBound| // |this| is initialized with |kInitial| state by the ctor then it is // set to |kAttachedAndBound| state soon in the factory methods // (CreateForServiceWorkerGlobalScope() or CreateForServiceWorkerClient()). // From the beginning |this| is referenced by // blink::WebServiceWorkerRegistration::Handle impl and the |binding_| Mojo // connection has been established. // - |kAttachedAndBound| --> |kDetached| // When all references to |this| have been released by Blink, // DetachAndMaybeDestroy() will be triggered to change |state_| from // |kAttachedAndBound| to |kDetached|. // - |kAttachedAndBound| --> |kUnbound| // When |binding_| Mojo connection gets broken, OnConnectionError() will be // triggered to change |state_| from |kAttachedAndBound| to |kUnbound|. // - {|kUnbound|, |kDetached|} --> |kDead| // But if DetachAndMaybeDestroy() saw that |state_| is already |kUnbound| or // OnConnectionError() saw that |state_| is already |kDetached|, they will // just set |state_| to |kDead| and delete |this| immediately. // - |kDetached| --> |kAttachedAndBound| // When |this| is in |kDetached| state, if an inflight // ServiceWorkerRegistrationObjectInfo for the same JavaScript registration // object arrived, |this| is reused to be provided to Blink. In such a case // AttachForServiceWorkerClient() sets |state_| to |kAttachedAndBound|. enum class LifecycleState { kInitial, kAttachedAndBound, kUnbound, kDetached, kDead }; enum QueuedTaskType { INSTALLING, WAITING, ACTIVE, UPDATE_FOUND, }; struct QueuedTask { QueuedTask(QueuedTaskType type, const scoped_refptr& worker); QueuedTask(const QueuedTask& other); ~QueuedTask(); QueuedTaskType type; scoped_refptr worker; }; void RunQueuedTasks(); blink::mojom::ServiceWorkerRegistrationObjectHost* GetRegistrationObjectHost(); void Attach(blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info); void DetachAndMaybeDestroy(); void BindRequest( blink::mojom::ServiceWorkerRegistrationObjectAssociatedRequest request); void OnConnectionError(); // |registration_id_| is the id of the corresponding // content::ServiceWorkerRegistration in the browser process. const int64_t registration_id_; // |info_| is initialized by the contructor with |info| passed from the remote // content::ServiceWorkerRegistrationObjectHost just created in the browser // process. It will be reset to nullptr by DetachAndMaybeDestroy() when // there is no any blink::WebServiceWorkerRegistration::Handle referencing // |this|. After that if another Mojo connection from the same remote // content::ServiceWorkerRegistrationObjectHost is passed here again (e.g. // WebServiceWorkerProviderImpl::OnDidGetRegistration()), |info_| will be set // to the valid value again by Attach(). // |info_->host_ptr_info| is taken/bound by |host_for_global_scope_| or // |host_for_client_| which holds the Mojo connection caller end point // retaining an reference to the remote // content::ServiceWorkerRegistrationObjectHost to control its lifetime. // |info_->request| is bound on |binding_|. blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info_; blink::WebServiceWorkerRegistrationProxy* proxy_; // Either |host_for_global_scope_| or |host_for_client_| is non-null. // // |host_for_global_scope_| is for service worker execution contexts. It is // used on the worker thread but bound on the IO thread, because it's a // channel- associated interface which can be bound only on the main or IO // thread. // TODO(leonhsl): Once we can detach this interface out from the legacy IPC // channel-associated interfaces world, we should bind it always on the worker // thread on which |this| lives. // Although it is a scoped_refptr, the only one owner is |this|. scoped_refptr< blink::mojom::ThreadSafeServiceWorkerRegistrationObjectHostAssociatedPtr> host_for_global_scope_; // |host_for_client_| is for service worker clients (document, shared worker). // It is bound and used on the main thread. blink::mojom::ServiceWorkerRegistrationObjectHostAssociatedPtr host_for_client_; // |binding_| keeps the Mojo binding to serve its other Mojo endpoint (i.e. // the caller end) held by the content::ServiceWorkerRegistrationObjectHost in // the browser process, is bound with |info_->request| by BindRequest() // function. This also controls lifetime of |this|, its connection error // handler will delete |this|. It is bound on the main thread for service // worker clients (document, shared worker). It is bound on the IO thread for // service worker execution contexts, but always uses PostTask to handle // received messages actually on the worker thread, because it's a // channel-associated interface which can be bound only on the main or IO // thread. // TODO(leonhsl): Once we can detach this interface out from the legacy IPC // channel-associated interfaces world, for service worker execution context // we should bind it always on the worker thread on which |this| lives. mojo::AssociatedBinding binding_; scoped_refptr creation_task_runner_; LifecycleState state_; std::vector queued_tasks_; // Owns all WebServiceWorkerXXXCallbacks provided from Blink when calling // ServiceWorkerRegistrationObjectHost interface methods. Please see comments // of WrapResponseCallback() for details. std::unique_ptr web_callbacks_holder_; // For service worker client contexts, |this| is tracked (not owned) in // |provider_context_for_client_->controllee_state_->registrations_|. // For service worker execution contexts, |provider_context_for_client_| is // null. base::WeakPtr provider_context_for_client_; DISALLOW_COPY_AND_ASSIGN(WebServiceWorkerRegistrationImpl); }; } // namespace content #endif // CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_