blob: 19ccf7333b1ea241af58a0a0258354af7cc72c89 [file] [log] [blame]
// Copyright 2018 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_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_
#define CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/containers/linked_list.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "content/browser/cookie_store/cookie_change_subscription.h"
#include "content/browser/cookie_store/cookie_store_host.h"
#include "content/browser/service_worker/service_worker_context_core_observer.h"
#include "mojo/public/cpp/bindings/strong_binding_set.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "third_party/blink/public/mojom/cookie_store/cookie_store.mojom.h"
#include "url/origin.h"
class GURL;
namespace content {
class ServiceWorkerContextWrapper;
class ServiceWorkerRegistration;
// Manages cookie change subscriptions for a StoragePartition's service workers.
//
// Subscriptions are stored along with their associated service worker
// registrations in ServiceWorkerStorage, as user data. When a service worker is
// unregistered, its cookie change subscriptions are removed. The storage method
// (user data) is an implementation detail. Callers should not rely on it, as
// the storage method may change in the future.
//
// Instances of this class must be accessed exclusively on the IO thread,
// because they call into ServiceWorkerContextWrapper methods that are
// restricted to the IO thread.
class CookieStoreManager : public ServiceWorkerContextCoreObserver,
public ::network::mojom::CookieChangeListener {
public:
// Creates a CookieStoreManager with an empty in-memory subscription database.
//
// The in-memory subscription database must be populated with data from disk,
// by calling ReadAllSubscriptions().
CookieStoreManager(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
~CookieStoreManager() override;
// Creates a mojo connection to a service worker.
//
// This is called when service workers use the Cookie Store API to subscribe
// to cookie changes or obtain the list of cookie changes.
void CreateService(blink::mojom::CookieStoreRequest request,
const url::Origin& origin);
// Starts loading the on-disk subscription data.
//
// Returns after scheduling the work. The callback is called with a boolean
// that indicates if the load operation succeeded.
//
// It is safe to call all the other CookieStoreManager methods during the
// loading operation. The CookieStoreManager has well-defined semantics if
// loading fails, so it is not necessary to handle loading errors.
void LoadAllSubscriptions(base::OnceCallback<void(bool)> callback);
// Processes cookie changes from a network service instance.
void ListenToCookieChanges(::network::mojom::CookieManagerPtr cookie_manager,
base::OnceCallback<void(bool)> callback);
// content::mojom::CookieStore implementation
void AppendSubscriptions(
int64_t service_worker_registration_id,
const url::Origin& origin,
std::vector<blink::mojom::CookieChangeSubscriptionPtr> mojo_subscriptions,
blink::mojom::CookieStore::AppendSubscriptionsCallback callback);
void GetSubscriptions(
int64_t service_worker_registration_id,
const url::Origin& origin,
blink::mojom::CookieStore::GetSubscriptionsCallback callback);
// ServiceWorkerContextCoreObserver
void OnRegistrationStored(int64_t service_worker_registration_id,
const GURL& pattern) override;
void OnRegistrationDeleted(int64_t service_worker_registration_id,
const GURL& pattern) override;
void OnNewLiveRegistration(int64_t service_worker_registration_id,
const GURL& pattern) override;
void OnStorageWiped() override;
// ::network::mojom::CookieChangeListener
void OnCookieChange(const net::CanonicalCookie& cookie,
::network::mojom::CookieChangeCause cause) override;
private:
// Updates internal state with the result of loading disk subscription data.
//
// Called exactly once.
void ProcessOnDiskSubscriptions(
base::OnceCallback<void(bool)> load_callback,
const std::vector<std::pair<int64_t, std::string>>& user_data,
ServiceWorkerStatusCode status);
// Runs all the callbacks waiting for on-disk subscription data.
//
// Called exactly once, after on-disk subcriptions have been loaded.
void DidLoadAllSubscriptions(bool succeeded,
base::OnceCallback<void(bool)> load_callback);
// Starts sending cookie change events to a service worker.
//
// All subscriptions must belong to the same service worker registration. This
// method is not idempotent.
void ActivateSubscriptions(
std::vector<CookieChangeSubscription>* subscriptions);
// Stops sending cookie change events to a service worker.
//
// All subscriptions must belong to the same service worker registration. This
// method is not idempotent.
void DeactivateSubscriptions(
std::vector<CookieChangeSubscription>* subscriptions);
// Sends a cookie change to interested service workers.
//
// Must only be called after the on-disk subscription data is successfully
// loaded.
void DispatchCookieChange(const net::CanonicalCookie& cookie,
::network::mojom::CookieChangeCause cause);
// Sends a cookie change event to one service worker.
void DispatchChangeEvent(
scoped_refptr<ServiceWorkerRegistration> registration,
const net::CanonicalCookie& cookie,
::network::mojom::CookieChangeCause cause);
// Called after a service worker was started so it can get a cookie change.
void DidStartWorkerForChangeEvent(
scoped_refptr<ServiceWorkerRegistration> registration,
const net::CanonicalCookie& cookie,
::network::mojom::CookieChangeCause cause,
ServiceWorkerStatusCode start_worker_status);
// Used to efficiently implement OnRegistrationDeleted().
//
// When a service worker registration is removed from the system, the
// CookieStoreManager needs to remove all the cookie change subscriptions
// associated with the registration. Looking up the registration ID in the
// |subscriptions_by_registration_| map is done in O(1) time, and then each
// subscription is removed from a LinkedList in |subscription_by_url_key_| in
// O(1) time.
std::unordered_map<int64_t, std::vector<CookieChangeSubscription>>
subscriptions_by_registration_;
// Used to efficiently implement DispatchCookieChange().
//
// When a cookie change notification comes from the network service, the
// CookieStoreManager needs to dispatch events to the workers with relevant
// subscriptions. |subscriptions_by_url_key_| indexes change subscriptions
// according to the eTLD+1 of the subscription's scope URL, so each cookie
// change only needs to be checked against the subscriptions of the service
// workers in the same eTLD+1. The reduction in work is signficant, given that
// checking whether a subscription matches a cookie isn't very cheap.
//
// The current implementation's performance profile could have been achieved
// with a map from eTLD+1 to registration IDs, which would not have required
// linked lists. However, the current approach is more amenable to future
// optimizations, such as partitioning by (eTLD+1, cookie name).
std::map<std::string, base::LinkedList<CookieChangeSubscription>>
subscriptions_by_url_key_;
// Used to look up and modify service worker registration data.
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
// Tracks the open mojo pipes created by CreateService().
//
// Each pipe is associated with the CookieStoreHost instance that it is
// connected to. When the pipe is closed, the StrongBindingSet automatically
// deletes the CookieStoreHost.
mojo::StrongBindingSet<blink::mojom::CookieStore> bindings_;
// Used to receive cookie changes from the network service.
::network::mojom::CookieManagerPtr cookie_manager_;
mojo::Binding<::network::mojom::CookieChangeListener>
cookie_change_listener_binding_;
// The service worker registration user data key for subscription data.
//
// All the subscriptions associated with a registration are stored in a single
// user data entry whose key is |registration_user_data_key_|, and whose value
// is a serialized CookieChangeSubscriptionsProto.
const std::string registration_user_data_key_;
// Called after all subscriptions have been loaded.
//
// Callbacks can assume that |done_loading_subscriptions_| is true
// and |succeeded_loading_subscriptions_| is set. If the latter is true,
// |subscriptions_by_registration_| and |subscriptions_by_url_key_| will also
// be populated.
std::vector<base::OnceClosure> subscriptions_loaded_callbacks_;
// Set to true once all subscriptions have been loaded.
bool done_loading_subscriptions_ = false;
// Only defined when |done_loading_subscriptions_| is true.
bool succeeded_loading_subscriptions_ = false;
// Instances of this class are currently bound to the IO thread, because they
// call ServiceWorkerContextWrapper methods that are restricted to the IO
// thread. However, the class implementation itself is thread-friendly, so it
// only checks that methods are called on the same sequence.
SEQUENCE_CHECKER(sequence_checker_);
// Supports having the manager destroyed while waiting for disk I/O.
base::WeakPtrFactory<CookieStoreManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CookieStoreManager);
};
} // namespace content
#endif // CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_