blob: c39eda1dc31035c32f8845aca5a3bee4c4b3dbb8 [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.
#ifndef CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_
#define CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_
#include <stdint.h>
#include <memory>
#include <vector>
#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/WebKit/common/service_worker/service_worker_error_type.mojom.h"
#include "third_party/WebKit/common/service_worker/service_worker_object.mojom.h"
#include "third_party/WebKit/common/service_worker/service_worker_registration.mojom.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.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<WebServiceWorkerRegistrationImpl,
WebServiceWorkerRegistrationImpl> {
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<WebServiceWorkerRegistrationImpl>
CreateForServiceWorkerGlobalScope(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
static scoped_refptr<WebServiceWorkerRegistrationImpl>
CreateForServiceWorkerClient(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info,
base::WeakPtr<ServiceWorkerProviderContext> 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<WebServiceWorkerUpdateCallbacks> callbacks) override;
void Unregister(std::unique_ptr<WebServiceWorkerUnregistrationCallbacks>
callbacks) override;
void EnableNavigationPreload(
bool enable,
std::unique_ptr<WebEnableNavigationPreloadCallbacks> callbacks) override;
void GetNavigationPreloadState(
std::unique_ptr<WebGetNavigationPreloadStateCallbacks> callbacks)
override;
void SetNavigationPreloadHeader(
const blink::WebString& value,
std::unique_ptr<WebSetNavigationPreloadHeaderCallbacks> 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<blink::WebServiceWorkerRegistration::Handle>
CreateHandle(scoped_refptr<WebServiceWorkerRegistrationImpl> registration);
private:
class WebCallbacksHolder;
friend class base::RefCounted<WebServiceWorkerRegistrationImpl,
WebServiceWorkerRegistrationImpl>;
WebServiceWorkerRegistrationImpl(
blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info,
base::WeakPtr<ServiceWorkerProviderContext> 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<void(blink::mojom::ServiceWorkerErrorType,
const base::Optional<std::string>&)>;
// |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<WebGetNavigationPreloadStateCallbacks> 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<WebServiceWorkerImpl>& worker);
QueuedTask(const QueuedTask& other);
~QueuedTask();
QueuedTaskType type;
scoped_refptr<WebServiceWorkerImpl> 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<blink::mojom::ServiceWorkerRegistrationObject>
binding_;
scoped_refptr<base::SingleThreadTaskRunner> creation_task_runner_;
LifecycleState state_;
std::vector<QueuedTask> queued_tasks_;
// Owns all WebServiceWorkerXXXCallbacks provided from Blink when calling
// ServiceWorkerRegistrationObjectHost interface methods. Please see comments
// of WrapResponseCallback() for details.
std::unique_ptr<WebCallbacksHolder> 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<ServiceWorkerProviderContext> provider_context_for_client_;
DISALLOW_COPY_AND_ASSIGN(WebServiceWorkerRegistrationImpl);
};
} // namespace content
#endif // CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_REGISTRATION_IMPL_H_