Commit 34ee23cf authored by jkarlin's avatar jkarlin Committed by Commit bot

[BackgroundSync] Initial land of the BackgroundSyncManager

The BackgroundSyncManager is the class that handles registration of
background syncs. This first CL handles storage and retrieval of
registrations and sequential ordering of async operations. Future CLs
will add permission checks and incorporate a scheduler.

Eventually the BackgroundSyncManager will be created and owned by the
BackgroundSyncMessageFilter, which will be owned by the RenderViewHost.

BackgroundSync Design Doc: https://docs.google.com/document/d/1MAuNzV0q5FporLZVJMh7CMrwTHwcZsWX6YUwPwKMGco/

BUG=449443

Review URL: https://codereview.chromium.org/950343006

Cr-Commit-Position: refs/heads/master@{#322375}
parent 556a2bfd
...@@ -22,6 +22,7 @@ source_set("browser") { ...@@ -22,6 +22,7 @@ source_set("browser") {
"//base", "//base",
"//base:base_static", "//base:base_static",
"//content:resources", "//content:resources",
"//content/browser/background_sync:background_sync_proto",
"//content/browser/notifications:notification_proto", "//content/browser/notifications:notification_proto",
"//content/browser/service_worker:service_worker_proto", "//content/browser/service_worker:service_worker_proto",
"//content/browser/speech/proto", "//content/browser/speech/proto",
......
# Copyright 2015 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.
import("//third_party/protobuf/proto_library.gni")
proto_library("background_sync_proto") {
sources = [
"background_sync.proto",
]
}
# Copyright 2015 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.
"""Top-level presubmit script for src/content/browser/background_sync/
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
def CheckChangeOnUpload(input_api, output_api):
return input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
// Copyright 2015 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.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package content;
message BackgroundSyncRegistrationProto {
required int64 id = 1;
required string name = 2;
optional int64 min_period = 3;
}
message BackgroundSyncRegistrationsProto {
repeated BackgroundSyncRegistrationProto registration = 1;
required int64 next_registration_id = 2;
}
\ No newline at end of file
This diff is collapsed.
// Copyright 2015 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_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
#include <list>
#include <map>
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/service_worker/service_worker_cache_scheduler.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/common/content_export.h"
#include "content/common/service_worker/service_worker_status_code.h"
#include "url/gurl.h"
namespace content {
class ServiceWorkerContextWrapper;
// BackgroundSyncManager manages and stores the set of background sync
// registrations across all registered service workers for a profile.
// Registrations are stored along with their associated Service Worker
// registration in ServiceWorkerStorage. If the ServiceWorker is unregistered,
// the sync registrations are removed. This class expects to be run on the IO
// thread. The asynchronous methods are executed sequentially.
// TODO(jkarlin): Check permissions when registering, scheduling, and firing
// background sync. In the meantime, --enable-service-worker-sync is required to
// fire a sync event.
// TODO(jkarlin): Unregister syncs when permission is revoked.
// TODO(jkarlin): Create a background sync scheduler to actually run the
// registered events.
// TODO(jkarlin): Keep the browser alive if "Let Google Chrome Run in the
// Background" is true and a sync is registered.
// TODO(jkarlin): Unregister syncs when storage for an origin is cleared.
// TODO(jkarlin): Detect and handle a corrupt or broken backend.
class CONTENT_EXPORT BackgroundSyncManager {
public:
enum ErrorType {
ERROR_TYPE_OK = 0,
ERROR_TYPE_STORAGE,
ERROR_TYPE_NOT_FOUND
};
// TODO(jkarlin): Remove this and use the struct from IPC messages once it
// lands.
struct CONTENT_EXPORT BackgroundSyncRegistration {
using RegistrationId = int64;
static const RegistrationId kInvalidRegistrationId;
BackgroundSyncRegistration()
: BackgroundSyncRegistration(kInvalidRegistrationId, "") {}
explicit BackgroundSyncRegistration(const std::string& name)
: BackgroundSyncRegistration(kInvalidRegistrationId, name) {}
BackgroundSyncRegistration(int64 id, const std::string& name)
: id(id), min_period(0), name(name) {}
bool Equals(const BackgroundSyncRegistration& other) {
return this->name == other.name && this->min_period == other.min_period;
}
RegistrationId id;
int64 min_period;
std::string name;
};
struct CONTENT_EXPORT BackgroundSyncRegistrations {
using NameToRegistrationMap =
std::map<std::string, BackgroundSyncRegistration>;
static const BackgroundSyncRegistration::RegistrationId kInitialId;
BackgroundSyncRegistrations();
explicit BackgroundSyncRegistrations(
BackgroundSyncRegistration::RegistrationId next_id);
~BackgroundSyncRegistrations();
NameToRegistrationMap name_to_registration_map;
BackgroundSyncRegistration::RegistrationId next_id;
};
using StatusCallback = base::Callback<void(ErrorType)>;
using StatusAndRegistrationCallback =
base::Callback<void(ErrorType, const BackgroundSyncRegistration&)>;
static scoped_ptr<BackgroundSyncManager> Create(
const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context);
virtual ~BackgroundSyncManager();
// Stores the given background sync registration and adds it to the scheduling
// queue. Overwrites any existing registration with the same name but
// different parameters (other than the id). Calls |callback| with ErrorTypeOK
// and the accepted registration on success. The accepted registration will
// have a unique id. It may also have altered parameters if the user or UA
// chose different parameters than those supplied.
void Register(const GURL& origin,
int64 sw_registration_id,
const BackgroundSyncRegistration& sync_registration,
const StatusAndRegistrationCallback& callback);
// Removes the background sync registration with |sync_registration_name| if
// the |sync_registration_id| matches. |sync_registration_id| will not match
// if, for instance, a new registration with the same name has replaced it.
// Calls |callback| with ErrorTypeNotFound if no match is found. Calls
// |callback| with ErrorTypeOK on success.
void Unregister(
const GURL& origin,
int64 sw_registration_id,
const std::string& sync_registration_name,
BackgroundSyncRegistration::RegistrationId sync_registration_id,
const StatusCallback& callback);
// Finds the background sync registration associated with
// |sw_registration_id|. Calls |callback| with ErrorTypeNotFound if it doesn't
// exist. Calls |callback| with ErrorTypeOK on success.
void GetRegistration(const GURL& origin,
int64 sw_registration_id,
const std::string sync_registration_name,
const StatusAndRegistrationCallback& callback);
protected:
explicit BackgroundSyncManager(
const scoped_refptr<ServiceWorkerContextWrapper>& context);
// Init must be called before any public member function. Only call it once.
void Init();
// The following methods are virtual for testing.
virtual void StoreDataInBackend(
int64 sw_registration_id,
const GURL& origin,
const std::string& key,
const std::string& data,
const ServiceWorkerStorage::StatusCallback& callback);
virtual void GetDataFromBackend(
const std::string& key,
const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
callback);
private:
using PermissionStatusCallback = base::Callback<void(bool)>;
using SWIdToRegistrationsMap = std::map<int64, BackgroundSyncRegistrations>;
// Returns the existing registration in |existing_registration| if it is not
// null.
bool LookupRegistration(int64 sw_registration_id,
const std::string& sync_registration_name,
BackgroundSyncRegistration* existing_registration);
// Store all registrations for a given |sw_registration_id|.
void StoreRegistrations(const GURL& origin,
int64 sw_registration_id,
const ServiceWorkerStorage::StatusCallback& callback);
// If the registration is in the map, removes it and returns the removed
// registration in |old_registration|. |old_registration| may be null.
void RemoveRegistrationFromMap(int64 sw_registration_id,
const std::string& sync_registration_name,
BackgroundSyncRegistration* old_registration);
void AddRegistrationToMap(
int64 sw_registration_id,
const BackgroundSyncRegistration& sync_registration);
void InitImpl();
void InitDidGetDataFromBackend(
const std::vector<std::pair<int64, std::string>>& user_data,
ServiceWorkerStatusCode status);
// Register callbacks
void RegisterImpl(const GURL& origin,
int64 sw_registration_id,
const BackgroundSyncRegistration& sync_registration,
const StatusAndRegistrationCallback& callback);
void RegisterDidStore(int64 sw_registration_id,
const BackgroundSyncRegistration& sync_registration,
const BackgroundSyncRegistration& previous_registration,
const StatusAndRegistrationCallback& callback,
ServiceWorkerStatusCode status);
// Unregister callbacks
void UnregisterImpl(
const GURL& origin,
int64 sw_registration_id,
const std::string& sync_registration_name,
BackgroundSyncRegistration::RegistrationId sync_registration_id,
const StatusCallback& callback);
void UnregisterDidStore(
int64 sw_registration_id,
const BackgroundSyncRegistration& old_sync_registration,
const StatusCallback& callback,
ServiceWorkerStatusCode status);
// GetRegistration callbacks
void GetRegistrationImpl(const GURL& origin,
int64 sw_registration_id,
const std::string sync_registration_name,
const StatusAndRegistrationCallback& callback);
// Operation Scheduling callbacks
void PendingStatusAndRegistrationCallback(
const StatusAndRegistrationCallback& callback,
ErrorType error,
const BackgroundSyncRegistration& sync_registration);
void PendingStatusCallback(const StatusCallback& callback, ErrorType error);
SWIdToRegistrationsMap sw_to_registrations_map_;
ServiceWorkerCacheScheduler op_scheduler_;
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
base::WeakPtrFactory<BackgroundSyncManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BackgroundSyncManager);
};
} // namespace content
#endif // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_MANAGER_H_
{
'targets': [
{
# GN version: //content/browser/background_sync:background_sync_proto
'target_name': 'background_sync_proto',
'type': 'static_library',
'sources': [
'background_sync.proto',
],
'variables': {
'proto_in_dir': '.',
'proto_out_dir': 'content/browser/background_sync',
},
'includes': [ '../../../build/protoc.gypi' ]
},
],
}
...@@ -98,6 +98,7 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper ...@@ -98,6 +98,7 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
bool is_incognito() const { return is_incognito_; } bool is_incognito() const { return is_incognito_; }
private: private:
friend class BackgroundSyncManagerTest;
friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>; friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
friend class EmbeddedWorkerTestHelper; friend class EmbeddedWorkerTestHelper;
friend class ServiceWorkerProcessManager; friend class ServiceWorkerProcessManager;
......
...@@ -773,13 +773,17 @@ void ServiceWorkerStorage::GetUserDataForAllRegistrations( ...@@ -773,13 +773,17 @@ void ServiceWorkerStorage::GetUserDataForAllRegistrations(
const std::string& key, const std::string& key,
const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback& const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback&
callback) { callback) {
DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; if (!LazyInitialize(
if (IsDisabled() || !context_) { base::Bind(&ServiceWorkerStorage::GetUserDataForAllRegistrations,
RunSoon(FROM_HERE, weak_factory_.GetWeakPtr(), key, callback))) {
base::Bind(callback, std::vector<std::pair<int64, std::string>>(), if (state_ != INITIALIZING || !context_) {
SERVICE_WORKER_ERROR_FAILED)); RunSoon(FROM_HERE,
base::Bind(callback, std::vector<std::pair<int64, std::string>>(),
SERVICE_WORKER_ERROR_FAILED));
}
return; return;
} }
DCHECK_EQ(INITIALIZED, state_);
if (key.empty()) { if (key.empty()) {
RunSoon(FROM_HERE, RunSoon(FROM_HERE,
......
...@@ -294,6 +294,8 @@ class CONTENT_EXPORT ServiceWorkerStorage ...@@ -294,6 +294,8 @@ class CONTENT_EXPORT ServiceWorkerStorage
base::FilePath GetDatabasePath(); base::FilePath GetDatabasePath();
base::FilePath GetDiskCachePath(); base::FilePath GetDiskCachePath();
// Loads the registration data from backend storage. This must be called
// before any method that requires registration data.
bool LazyInitialize( bool LazyInitialize(
const base::Closure& callback); const base::Closure& callback);
void DidReadInitialData( void DidReadInitialData(
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
'../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/resources/ui_resources.gyp:ui_resources', '../ui/resources/ui_resources.gyp:ui_resources',
'../ui/snapshot/snapshot.gyp:snapshot', '../ui/snapshot/snapshot.gyp:snapshot',
'browser/background_sync/background_sync_proto.gyp:background_sync_proto',
'browser/notifications/notification_proto.gyp:notification_proto', 'browser/notifications/notification_proto.gyp:notification_proto',
'browser/service_worker/service_worker_proto.gyp:service_worker_proto', 'browser/service_worker/service_worker_proto.gyp:service_worker_proto',
'browser/speech/proto/speech_proto.gyp:speech_proto', 'browser/speech/proto/speech_proto.gyp:speech_proto',
...@@ -428,6 +429,8 @@ ...@@ -428,6 +429,8 @@
'browser/appcache/chrome_appcache_service.h', 'browser/appcache/chrome_appcache_service.h',
'browser/appcache/view_appcache_internals_job.cc', 'browser/appcache/view_appcache_internals_job.cc',
'browser/appcache/view_appcache_internals_job.h', 'browser/appcache/view_appcache_internals_job.h',
'browser/background_sync/background_sync_manager.cc',
'browser/background_sync/background_sync_manager.h',
'browser/bad_message.cc', 'browser/bad_message.cc',
'browser/bad_message.h', 'browser/bad_message.h',
'browser/bluetooth/bluetooth_dispatcher_host.cc', 'browser/bluetooth/bluetooth_dispatcher_host.cc',
......
...@@ -333,6 +333,7 @@ ...@@ -333,6 +333,7 @@
'browser/appcache/mock_appcache_storage.cc', 'browser/appcache/mock_appcache_storage.cc',
'browser/appcache/mock_appcache_storage.h', 'browser/appcache/mock_appcache_storage.h',
'browser/appcache/mock_appcache_storage_unittest.cc', 'browser/appcache/mock_appcache_storage_unittest.cc',
'browser/background_sync/background_sync_manager_unittest.cc',
'browser/browser_thread_unittest.cc', 'browser/browser_thread_unittest.cc',
'browser/browser_url_handler_impl_unittest.cc', 'browser/browser_url_handler_impl_unittest.cc',
'browser/byte_stream_unittest.cc', 'browser/byte_stream_unittest.cc',
...@@ -973,6 +974,7 @@ ...@@ -973,6 +974,7 @@
'target_name': 'content_unittests', 'target_name': 'content_unittests',
'type': '<(gtest_target_type)', 'type': '<(gtest_target_type)',
'dependencies': [ 'dependencies': [
'browser/background_sync/background_sync_proto.gyp:background_sync_proto',
'browser/notifications/notification_proto.gyp:notification_proto', 'browser/notifications/notification_proto.gyp:notification_proto',
'browser/service_worker/service_worker_proto.gyp:service_worker_proto', 'browser/service_worker/service_worker_proto.gyp:service_worker_proto',
'browser/speech/proto/speech_proto.gyp:speech_proto', 'browser/speech/proto/speech_proto.gyp:speech_proto',
......
...@@ -448,6 +448,7 @@ if (!is_mac) { # TODO(GYP) enable on Mac once it links. ...@@ -448,6 +448,7 @@ if (!is_mac) { # TODO(GYP) enable on Mac once it links.
":test_support", ":test_support",
"//base/allocator", "//base/allocator",
"//base/test:test_support", "//base/test:test_support",
"//content/browser/background_sync:background_sync_proto",
"//content/browser/notifications:notification_proto", "//content/browser/notifications:notification_proto",
"//content/browser/service_worker:service_worker_proto", "//content/browser/service_worker:service_worker_proto",
"//content/browser/speech/proto", "//content/browser/speech/proto",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment