summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-16 01:26:17 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-16 01:26:17 +0000
commit6517c45b4e76fe4ef381da7b3559db8afdf65fc2 (patch)
tree2c650653d6ee941ca671d3215b4abdea0f301415
parent257745de825487cc6bb520e0f420ffa51003e0a1 (diff)
downloadchromium_src-6517c45b4e76fe4ef381da7b3559db8afdf65fc2.zip
chromium_src-6517c45b4e76fe4ef381da7b3559db8afdf65fc2.tar.gz
chromium_src-6517c45b4e76fe4ef381da7b3559db8afdf65fc2.tar.bz2
[Sync] (Merge to 1229) Avoid unregistering object IDs on shutdown
Add RegisterHandler() and UnregisterHandler(), which should be called before and after calls to UpdateRegisteredIds(). Use UnregisterHandler() on shutdown instead of UpdateRegisteredIds(_, ObjectIdSet()). Make SyncNotifierHelper non-thread-safe. Fix test breakages that this revealed. Also add GetAllRegisteredIds() instead of making it the return value of UpdateRegisteredIds(). Propagate UpdateRegisteredIds()/RegisterHandler()/UnregisterHandler() all the way up to ProfileSyncService. Make FakeSyncManager be created on the sync thread. Clean up SyncBackendHost startup/shutdown behavior a bit. BUG=140325 TBR=tim@chromium.org Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=150990 Review URL: https://chromiumcodereview.appspot.com/10824161 Review URL: https://chromiumcodereview.appspot.com/10825379 git-svn-id: svn://svn.chromium.org/chrome/branches/1229/src@151820 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/sync/glue/bridged_sync_notifier.cc16
-rw-r--r--chrome/browser/sync/glue/bridged_sync_notifier.h6
-rw-r--r--chrome/browser/sync/glue/bridged_sync_notifier_unittest.cc38
-rw-r--r--chrome/browser/sync/glue/chrome_sync_notification_bridge.cc70
-rw-r--r--chrome/browser/sync/glue/chrome_sync_notification_bridge.h7
-rw-r--r--chrome/browser/sync/glue/chrome_sync_notification_bridge_unittest.cc6
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc86
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h9
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_unittest.cc106
-rw-r--r--chrome/browser/sync/profile_sync_service.cc26
-rw-r--r--chrome/browser/sync/profile_sync_service.h58
-rw-r--r--chrome/browser/sync/profile_sync_service_unittest.cc8
-rw-r--r--sync/internal_api/public/sync_manager.h11
-rw-r--r--sync/internal_api/public/test/fake_sync_manager.h43
-rw-r--r--sync/internal_api/sync_manager_impl.cc30
-rw-r--r--sync/internal_api/sync_manager_impl.h4
-rw-r--r--sync/internal_api/sync_manager_impl_unittest.cc21
-rw-r--r--sync/internal_api/test/fake_sync_manager.cc56
-rw-r--r--sync/notifier/invalidation_notifier.cc19
-rw-r--r--sync/notifier/invalidation_notifier.h6
-rw-r--r--sync/notifier/invalidation_notifier_unittest.cc26
-rw-r--r--sync/notifier/non_blocking_invalidation_notifier.cc32
-rw-r--r--sync/notifier/non_blocking_invalidation_notifier.h8
-rw-r--r--sync/notifier/non_blocking_invalidation_notifier_unittest.cc13
-rw-r--r--sync/notifier/p2p_notifier.cc23
-rw-r--r--sync/notifier/p2p_notifier.h6
-rw-r--r--sync/notifier/p2p_notifier_unittest.cc29
-rw-r--r--sync/notifier/sync_notifier.h40
-rw-r--r--sync/notifier/sync_notifier_factory_unittest.cc6
-rw-r--r--sync/notifier/sync_notifier_helper.cc95
-rw-r--r--sync/notifier/sync_notifier_helper.h55
-rw-r--r--sync/notifier/sync_notifier_helper_unittest.cc156
-rw-r--r--sync/notifier/sync_notifier_registrar.cc129
-rw-r--r--sync/notifier/sync_notifier_registrar.h82
-rw-r--r--sync/notifier/sync_notifier_registrar_unittest.cc248
-rw-r--r--sync/sync.gyp6
-rw-r--r--sync/tools/sync_listen_notifications.cc5
37 files changed, 1072 insertions, 513 deletions
diff --git a/chrome/browser/sync/glue/bridged_sync_notifier.cc b/chrome/browser/sync/glue/bridged_sync_notifier.cc
index 5e3f669..f783418 100644
--- a/chrome/browser/sync/glue/bridged_sync_notifier.cc
+++ b/chrome/browser/sync/glue/bridged_sync_notifier.cc
@@ -18,14 +18,28 @@ BridgedSyncNotifier::BridgedSyncNotifier(
BridgedSyncNotifier::~BridgedSyncNotifier() {
}
+void BridgedSyncNotifier::RegisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ if (delegate_.get())
+ delegate_->RegisterHandler(handler);
+ bridge_->RegisterHandler(handler);
+}
+
void BridgedSyncNotifier::UpdateRegisteredIds(
syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids) {
if (delegate_.get())
- delegate_->UpdateRegisteredIds(handler, ids);
+ delegate_->UpdateRegisteredIds(handler, ids);
bridge_->UpdateRegisteredIds(handler, ids);
}
+void BridgedSyncNotifier::UnregisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ if (delegate_.get())
+ delegate_->UnregisterHandler(handler);
+ bridge_->UnregisterHandler(handler);
+}
+
void BridgedSyncNotifier::SetUniqueId(const std::string& unique_id) {
if (delegate_.get())
delegate_->SetUniqueId(unique_id);
diff --git a/chrome/browser/sync/glue/bridged_sync_notifier.h b/chrome/browser/sync/glue/bridged_sync_notifier.h
index f48d0f9..616e16d 100644
--- a/chrome/browser/sync/glue/bridged_sync_notifier.h
+++ b/chrome/browser/sync/glue/bridged_sync_notifier.h
@@ -28,9 +28,13 @@ class BridgedSyncNotifier : public syncer::SyncNotifier {
virtual ~BridgedSyncNotifier();
// SyncNotifier implementation. Passes through all calls to the delegate.
- // UpdateRegisteredIds calls will also be forwarded to the bridge.
+ // RegisterHandler, UnregisterHandler, and UpdateRegisteredIds calls will
+ // also be forwarded to the bridge.
+ virtual void RegisterHandler(syncer::SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredIds(syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterHandler(
+ syncer::SyncNotifierObserver* handler) OVERRIDE;
virtual void SetUniqueId(const std::string& unique_id) OVERRIDE;
virtual void SetStateDeprecated(const std::string& state) OVERRIDE;
virtual void UpdateCredentials(
diff --git a/chrome/browser/sync/glue/bridged_sync_notifier_unittest.cc b/chrome/browser/sync/glue/bridged_sync_notifier_unittest.cc
index 7347eac..8863be4 100644
--- a/chrome/browser/sync/glue/bridged_sync_notifier_unittest.cc
+++ b/chrome/browser/sync/glue/bridged_sync_notifier_unittest.cc
@@ -35,8 +35,11 @@ class MockChromeSyncNotificationBridge : public ChromeSyncNotificationBridge {
: ChromeSyncNotificationBridge(profile, sync_task_runner) {}
virtual ~MockChromeSyncNotificationBridge() {}
- MOCK_METHOD2(UpdateRegisteredIds, void(syncer::SyncNotifierObserver*,
- const syncer::ObjectIdSet&));
+ MOCK_METHOD1(RegisterHandler, void(syncer::SyncNotifierObserver*));
+ MOCK_METHOD2(UpdateRegisteredIds,
+ void(syncer::SyncNotifierObserver*,
+ const syncer::ObjectIdSet&));
+ MOCK_METHOD1(UnregisterHandler, void(syncer::SyncNotifierObserver*));
};
class MockSyncNotifier : public syncer::SyncNotifier {
@@ -44,8 +47,11 @@ class MockSyncNotifier : public syncer::SyncNotifier {
MockSyncNotifier() {}
virtual ~MockSyncNotifier() {}
+ MOCK_METHOD1(RegisterHandler, void(syncer::SyncNotifierObserver*));
MOCK_METHOD2(UpdateRegisteredIds,
- void(syncer::SyncNotifierObserver*, const syncer::ObjectIdSet&));
+ void(syncer::SyncNotifierObserver*,
+ const syncer::ObjectIdSet&));
+ MOCK_METHOD1(UnregisterHandler, void(syncer::SyncNotifierObserver*));
MOCK_METHOD1(SetUniqueId, void(const std::string&));
MOCK_METHOD1(SetStateDeprecated, void(const std::string&));
MOCK_METHOD2(UpdateCredentials, void(const std::string&, const std::string&));
@@ -53,8 +59,9 @@ class MockSyncNotifier : public syncer::SyncNotifier {
};
// All tests just verify that each call is passed through to the delegate, with
-// the exception of UpdateRegisteredIds, which also verifies the call is
-// forwarded to the bridge.
+// the exception of RegisterHandler, UnregisterHandler, and
+// UpdateRegisteredIds, which also verifies the call is forwarded to the
+// bridge.
class BridgedSyncNotifierTest : public testing::Test {
public:
BridgedSyncNotifierTest()
@@ -73,13 +80,26 @@ class BridgedSyncNotifierTest : public testing::Test {
BridgedSyncNotifier bridged_notifier_;
};
-TEST_F(BridgedSyncNotifierTest, UpdateRegisteredIds) {
+TEST_F(BridgedSyncNotifierTest, RegisterHandler) {
syncer::MockSyncNotifierObserver observer;
+ EXPECT_CALL(mock_bridge_, RegisterHandler(&observer));
+ EXPECT_CALL(*mock_delegate_, RegisterHandler(&observer));
+ bridged_notifier_.RegisterHandler(&observer);
+}
+
+TEST_F(BridgedSyncNotifierTest, UpdateRegisteredIds) {
EXPECT_CALL(mock_bridge_, UpdateRegisteredIds(
- &observer, syncer::ObjectIdSet()));
+ NULL, syncer::ObjectIdSet()));
EXPECT_CALL(*mock_delegate_, UpdateRegisteredIds(
- &observer, syncer::ObjectIdSet()));
- bridged_notifier_.UpdateRegisteredIds(&observer, syncer::ObjectIdSet());
+ NULL, syncer::ObjectIdSet()));
+ bridged_notifier_.UpdateRegisteredIds(NULL, syncer::ObjectIdSet());
+}
+
+TEST_F(BridgedSyncNotifierTest, UnregisterHandler) {
+ syncer::MockSyncNotifierObserver observer;
+ EXPECT_CALL(mock_bridge_, UnregisterHandler(&observer));
+ EXPECT_CALL(*mock_delegate_, UnregisterHandler(&observer));
+ bridged_notifier_.UnregisterHandler(&observer);
}
TEST_F(BridgedSyncNotifierTest, SetUniqueId) {
diff --git a/chrome/browser/sync/glue/chrome_sync_notification_bridge.cc b/chrome/browser/sync/glue/chrome_sync_notification_bridge.cc
index 31db8e2..038d4f2 100644
--- a/chrome/browser/sync/glue/chrome_sync_notification_bridge.cc
+++ b/chrome/browser/sync/glue/chrome_sync_notification_bridge.cc
@@ -6,11 +6,13 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/common/chrome_notification_types.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
-#include "sync/notifier/sync_notifier_helper.h"
#include "sync/notifier/sync_notifier_observer.h"
+#include "sync/notifier/sync_notifier_registrar.h"
using content::BrowserThread;
@@ -25,9 +27,14 @@ class ChromeSyncNotificationBridge::Core
// All member functions below must be called on the sync task runner.
+ void InitializeOnSyncThread();
+ void CleanupOnSyncThread();
+
void UpdateEnabledTypes(syncer::ModelTypeSet enabled_types);
+ void RegisterHandler(syncer::SyncNotifierObserver* handler);
void UpdateRegisteredIds(syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids);
+ void UnregisterHandler(syncer::SyncNotifierObserver* handler);
void EmitNotification(
const syncer::ModelTypePayloadMap& payload_map,
@@ -43,7 +50,7 @@ class ChromeSyncNotificationBridge::Core
// Used only on |sync_task_runner_|.
syncer::ModelTypeSet enabled_types_;
- syncer::SyncNotifierHelper helper_;
+ scoped_ptr<syncer::SyncNotifierRegistrar> notifier_registrar_;
};
ChromeSyncNotificationBridge::Core::Core(
@@ -56,6 +63,15 @@ ChromeSyncNotificationBridge::Core::Core(
ChromeSyncNotificationBridge::Core::~Core() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
sync_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!notifier_registrar_.get());
+}
+
+void ChromeSyncNotificationBridge::Core::InitializeOnSyncThread() {
+ notifier_registrar_.reset(new syncer::SyncNotifierRegistrar());
+}
+
+void ChromeSyncNotificationBridge::Core::CleanupOnSyncThread() {
+ notifier_registrar_.reset();
}
void ChromeSyncNotificationBridge::Core::UpdateEnabledTypes(
@@ -64,11 +80,23 @@ void ChromeSyncNotificationBridge::Core::UpdateEnabledTypes(
enabled_types_ = types;
}
+void ChromeSyncNotificationBridge::Core::RegisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
+ notifier_registrar_->RegisterHandler(handler);
+}
+
void ChromeSyncNotificationBridge::Core::UpdateRegisteredIds(
syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids) {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
- helper_.UpdateRegisteredIds(handler, ids);
+ notifier_registrar_->UpdateRegisteredIds(handler, ids);
+}
+
+void ChromeSyncNotificationBridge::Core::UnregisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
+ notifier_registrar_->UnregisterHandler(handler);
}
void ChromeSyncNotificationBridge::Core::EmitNotification(
@@ -80,7 +108,7 @@ void ChromeSyncNotificationBridge::Core::EmitNotification(
syncer::ModelTypePayloadMapFromEnumSet(enabled_types_, std::string()) :
payload_map;
- helper_.DispatchInvalidationsToHandlers(
+ notifier_registrar_->DispatchInvalidationsToHandlers(
ModelTypePayloadMapToObjectIdPayloadMap(effective_payload_map),
notification_source);
}
@@ -96,16 +124,34 @@ ChromeSyncNotificationBridge::ChromeSyncNotificationBridge(
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_REMOTE,
content::Source<Profile>(profile));
+
+ if (!sync_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::InitializeOnSyncThread, core_))) {
+ NOTREACHED();
+ }
}
ChromeSyncNotificationBridge::~ChromeSyncNotificationBridge() {}
+void ChromeSyncNotificationBridge::StopForShutdown() {
+ if (!sync_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Core::CleanupOnSyncThread, core_))) {
+ NOTREACHED();
+ }
+}
+
void ChromeSyncNotificationBridge::UpdateEnabledTypes(
syncer::ModelTypeSet types) {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
core_->UpdateEnabledTypes(types);
}
+void ChromeSyncNotificationBridge::RegisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
+ core_->RegisterHandler(handler);
+}
+
void ChromeSyncNotificationBridge::UpdateRegisteredIds(
syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids) {
@@ -113,6 +159,12 @@ void ChromeSyncNotificationBridge::UpdateRegisteredIds(
core_->UpdateRegisteredIds(handler, ids);
}
+void ChromeSyncNotificationBridge::UnregisterHandler(
+ syncer::SyncNotifierObserver* handler) {
+ DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
+ core_->UnregisterHandler(handler);
+}
+
void ChromeSyncNotificationBridge::Observe(
int type,
const content::NotificationSource& source,
@@ -132,10 +184,12 @@ void ChromeSyncNotificationBridge::Observe(
content::Details<const syncer::ModelTypePayloadMap>
payload_details(details);
const syncer::ModelTypePayloadMap& payload_map = *(payload_details.ptr());
- sync_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Core::EmitNotification,
- core_, payload_map, notification_source));
+ if (!sync_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Core::EmitNotification,
+ core_, payload_map, notification_source))) {
+ NOTREACHED();
+ }
}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/chrome_sync_notification_bridge.h b/chrome/browser/sync/glue/chrome_sync_notification_bridge.h
index a878ff7..0216df9 100644
--- a/chrome/browser/sync/glue/chrome_sync_notification_bridge.h
+++ b/chrome/browser/sync/glue/chrome_sync_notification_bridge.h
@@ -36,11 +36,18 @@ class ChromeSyncNotificationBridge : public content::NotificationObserver {
const scoped_refptr<base::SequencedTaskRunner>& sync_task_runner);
virtual ~ChromeSyncNotificationBridge();
+ // Must be called on the UI thread while the sync task runner is still
+ // around. No other member functions on the sync thread may be called after
+ // this is called.
+ void StopForShutdown();
+
// Must be called on the sync task runner.
void UpdateEnabledTypes(syncer::ModelTypeSet enabled_types);
// Marked virtual for tests.
+ virtual void RegisterHandler(syncer::SyncNotifierObserver* handler);
virtual void UpdateRegisteredIds(syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids);
+ virtual void UnregisterHandler(syncer::SyncNotifierObserver* handler);
// NotificationObserver implementation. Called on UI thread.
virtual void Observe(int type,
diff --git a/chrome/browser/sync/glue/chrome_sync_notification_bridge_unittest.cc b/chrome/browser/sync/glue/chrome_sync_notification_bridge_unittest.cc
index 991b774..ed50b9e 100644
--- a/chrome/browser/sync/glue/chrome_sync_notification_bridge_unittest.cc
+++ b/chrome/browser/sync/glue/chrome_sync_notification_bridge_unittest.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/sync/glue/chrome_sync_notification_bridge.h"
+#include <cstddef>
+
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@@ -53,6 +55,7 @@ class FakeSyncNotifierObserver : public syncer::SyncNotifierObserver {
expected_payloads_(expected_payloads),
expected_source_(expected_source) {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
+ bridge_->RegisterHandler(this);
const syncer::ObjectIdSet& ids =
syncer::ObjectIdPayloadMapToSet(expected_payloads);
bridge_->UpdateRegisteredIds(this, ids);
@@ -60,7 +63,7 @@ class FakeSyncNotifierObserver : public syncer::SyncNotifierObserver {
virtual ~FakeSyncNotifierObserver() {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
- bridge_->UpdateRegisteredIds(this, syncer::ObjectIdSet());
+ bridge_->UnregisterHandler(this);
}
// SyncNotifierObserver implementation.
@@ -120,6 +123,7 @@ class ChromeSyncNotificationBridgeTest : public testing::Test {
}
virtual void TearDown() OVERRIDE {
+ bridge_->StopForShutdown();
sync_thread_.Stop();
// Must be reset only after the sync thread is stopped.
bridge_.reset();
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 9b7d19d..e3dc7cf 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -170,6 +170,7 @@ class SyncBackendHost::Core
// (in step 2).
void DoStopSyncManagerForShutdown(const base::Closure& closure);
void DoShutdown(bool stopping_sync);
+ void DoDestroySyncManager();
// Configuration methods that must execute on sync loop.
void DoConfigureSyncer(
@@ -245,6 +246,13 @@ class SyncBackendHost::Core
// The top-level syncapi entry point. Lives on the sync thread.
scoped_ptr<syncer::SyncManager> sync_manager_;
+ // Whether or not we registered with |sync_manager_| as an invalidation
+ // handler. Necessary since we may end up trying to unregister before we
+ // register in tests (in synchronous initialization mode).
+ //
+ // TODO(akalin): Fix this behavior (see http://crbug.com/140354).
+ bool registered_as_invalidation_handler_;
+
DISALLOW_COPY_AND_ASSIGN(Core);
};
@@ -541,11 +549,15 @@ void SyncBackendHost::StopSyncManagerForShutdown(
void SyncBackendHost::StopSyncingForShutdown() {
DCHECK_EQ(MessageLoop::current(), frontend_loop_);
+
+ // Immediately stop sending messages to the frontend.
+ frontend_ = NULL;
+
// Thread shutdown should occur in the following order:
// - Sync Thread
// - UI Thread (stops some time after we return from this call).
//
- // In order to acheive this, we first shutdown components from the UI thread
+ // In order to achieve this, we first shutdown components from the UI thread
// and send signals to abort components that may be busy on the sync thread.
// The callback (OnSyncerShutdownComplete) will happen on the sync thread,
// after which we'll shutdown components on the sync thread, and then be
@@ -570,17 +582,23 @@ void SyncBackendHost::StopSyncingForShutdown() {
// If the sync thread isn't running, then the syncer is effectively
// stopped. Moreover, it implies that we never attempted initialization,
// so the registrar won't need stopping either.
- DCHECK_EQ(NOT_ATTEMPTED, initialization_state_);
+ DCHECK_EQ(initialization_state_, NOT_ATTEMPTED);
DCHECK(!registrar_.get());
}
}
void SyncBackendHost::Shutdown(bool sync_disabled) {
+ // StopSyncingForShutdown() (which nulls out |frontend_|) should be
+ // called first.
+ DCHECK(!frontend_);
// TODO(tim): DCHECK(registrar_->StoppedOnUIThread()) would be nice.
if (sync_thread_.IsRunning()) {
sync_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&SyncBackendHost::Core::DoShutdown, core_.get(),
sync_disabled));
+
+ if (chrome_sync_notification_bridge_.get())
+ chrome_sync_notification_bridge_->StopForShutdown();
}
// Stop will return once the thread exits, which will be after DoShutdown
@@ -605,7 +623,6 @@ void SyncBackendHost::Shutdown(bool sync_disabled) {
stop_sync_thread_time);
registrar_.reset();
- frontend_ = NULL;
chrome_sync_notification_bridge_.reset();
core_ = NULL; // Releases reference to core_.
}
@@ -846,7 +863,8 @@ SyncBackendHost::Core::Core(const std::string& name,
host_(backend),
sync_loop_(NULL),
registrar_(NULL),
- chrome_sync_notification_bridge_(NULL) {
+ chrome_sync_notification_bridge_(NULL),
+ registered_as_invalidation_handler_(false) {
DCHECK(backend.get());
}
@@ -892,11 +910,7 @@ void SyncBackendHost::Core::OnInitializationComplete(
DCHECK_EQ(MessageLoop::current(), sync_loop_);
if (!success) {
- sync_manager_->RemoveObserver(this);
- sync_manager_->UpdateRegisteredInvalidationIds(
- this, syncer::ObjectIdSet());
- sync_manager_->ShutdownOnSyncThread();
- sync_manager_.reset();
+ DoDestroySyncManager();
}
host_.Call(
@@ -1085,15 +1099,24 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) {
options.report_unrecoverable_error_function);
LOG_IF(ERROR, !success) << "Sync manager initialization failed!";
- // Now check the command line to see if we need to simulate an
- // unrecoverable error for testing purpose. Note the error is thrown
- // only if the initialization succeeded. Also it makes sense to use this
- // flag only when restarting the browser with an account already setup. If
- // you use this before setting up the setup would not succeed as an error
- // would be encountered.
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kSyncThrowUnrecoverableError)) {
- sync_manager_->ThrowUnrecoverableError();
+ // |sync_manager_| may end up being NULL here in tests (in
+ // synchronous initialization mode).
+ //
+ // TODO(akalin): Fix this behavior (see http://crbug.com/140354).
+ if (sync_manager_.get()) {
+ sync_manager_->RegisterInvalidationHandler(this);
+ registered_as_invalidation_handler_ = true;
+
+ // Now check the command line to see if we need to simulate an
+ // unrecoverable error for testing purpose. Note the error is thrown
+ // only if the initialization succeeded. Also it makes sense to use this
+ // flag only when restarting the browser with an account already setup. If
+ // you use this before setting up the setup would not succeed as an error
+ // would be encountered.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSyncThrowUnrecoverableError)) {
+ sync_manager_->ThrowUnrecoverableError();
+ }
}
}
@@ -1110,7 +1133,7 @@ void SyncBackendHost::Core::DoUpdateRegisteredInvalidationIds(
// synchronous initialization mode) since this is called during
// shutdown.
//
- // TODO(akalin): Fix this behavior.
+ // TODO(akalin): Fix this behavior (see http://crbug.com/140354).
if (sync_manager_.get()) {
sync_manager_->UpdateRegisteredInvalidationIds(this, ids);
}
@@ -1159,14 +1182,7 @@ void SyncBackendHost::Core::DoStopSyncManagerForShutdown(
void SyncBackendHost::Core::DoShutdown(bool sync_disabled) {
DCHECK_EQ(MessageLoop::current(), sync_loop_);
- if (sync_manager_.get()) {
- save_changes_timer_.reset();
- sync_manager_->UpdateRegisteredInvalidationIds(
- this, syncer::ObjectIdSet());
- sync_manager_->ShutdownOnSyncThread();
- sync_manager_->RemoveObserver(this);
- sync_manager_.reset();
- }
+ DoDestroySyncManager();
chrome_sync_notification_bridge_ = NULL;
registrar_ = NULL;
@@ -1179,6 +1195,20 @@ void SyncBackendHost::Core::DoShutdown(bool sync_disabled) {
host_.Reset();
}
+void SyncBackendHost::Core::DoDestroySyncManager() {
+ DCHECK_EQ(MessageLoop::current(), sync_loop_);
+ if (sync_manager_.get()) {
+ save_changes_timer_.reset();
+ if (registered_as_invalidation_handler_) {
+ sync_manager_->UnregisterInvalidationHandler(this);
+ registered_as_invalidation_handler_ = false;
+ }
+ sync_manager_->RemoveObserver(this);
+ sync_manager_->ShutdownOnSyncThread();
+ sync_manager_.reset();
+ }
+}
+
void SyncBackendHost::Core::DoConfigureSyncer(
syncer::ConfigureReason reason,
syncer::ModelTypeSet types_to_config,
@@ -1269,7 +1299,7 @@ void SyncBackendHost::OnNigoriDownloadRetry() {
void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop(
const syncer::WeakHandle<syncer::JsBackend>& js_backend, bool success) {
- DCHECK_NE(NOT_ATTEMPTED, initialization_state_);
+ DCHECK_NE(initialization_state_, NOT_ATTEMPTED);
if (!frontend_)
return;
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index bafe140..dd703b4 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -179,8 +179,8 @@ class SyncBackendHost : public BackendDataTypeConfigurer {
// Called on |frontend_loop| to update SyncCredentials.
void UpdateCredentials(const syncer::SyncCredentials& credentials);
- // Registers the underlying frontend for the given IDs to the
- // underlying notifier.
+ // Registers the underlying frontend for the given IDs to the underlying
+ // notifier. This lasts until StopSyncingForShutdown() is called.
void UpdateRegisteredInvalidationIds(const syncer::ObjectIdSet& ids);
// This starts the SyncerThread running a Syncer object to communicate with
@@ -211,9 +211,10 @@ class SyncBackendHost : public BackendDataTypeConfigurer {
bool SetDecryptionPassphrase(const std::string& passphrase)
WARN_UNUSED_RESULT;
- // Called on |frontend_loop_| to kick off shutdown procedure. After this,
- // no further sync activity will occur with the sync server and no further
+ // Called on |frontend_loop_| to kick off shutdown procedure. After this, no
+ // further sync activity will occur with the sync server and no further
// change applications will occur from changes already downloaded.
+ // Furthermore, no notifications will be sent to any invalidation handler.
virtual void StopSyncingForShutdown();
// Called on |frontend_loop_| to kick off shutdown.
diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
index 2145da1..16770e7d 100644
--- a/chrome/browser/sync/glue/sync_backend_host_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
@@ -89,24 +89,44 @@ class MockSyncFrontend : public SyncFrontend {
class FakeSyncManagerFactory : public syncer::SyncManagerFactory {
public:
- FakeSyncManagerFactory() : manager_(NULL) {}
+ FakeSyncManagerFactory() : fake_manager_(NULL) {}
virtual ~FakeSyncManagerFactory() {}
- // Takes ownership of |manager|.
- void SetSyncManager(FakeSyncManager* manager) {
- DCHECK(!manager_.get());
- manager_.reset(manager);
+ // SyncManagerFactory implementation. Called on the sync thread.
+ virtual scoped_ptr<SyncManager> CreateSyncManager(
+ std::string name) OVERRIDE {
+ DCHECK(!fake_manager_);
+ fake_manager_ = new FakeSyncManager(initial_sync_ended_types_,
+ progress_marker_types_,
+ configure_fail_types_);
+ return scoped_ptr<SyncManager>(fake_manager_);
}
- // Passes ownership of |manager_|.
- // SyncManagerFactory implementation.
- virtual scoped_ptr<SyncManager> CreateSyncManager(std::string name) OVERRIDE {
- DCHECK(manager_.get());
- return manager_.Pass();
+ // Returns NULL until CreateSyncManager() is called on the sync
+ // thread. Called on the main thread, but only after
+ // OnBackendInitialized() is called (which is strictly after
+ // CreateSyncManager is called on the sync thread).
+ FakeSyncManager* fake_manager() {
+ return fake_manager_;
+ }
+
+ void set_initial_sync_ended_types(syncer::ModelTypeSet types) {
+ initial_sync_ended_types_ = types;
+ }
+
+ void set_progress_marker_types(syncer::ModelTypeSet types) {
+ progress_marker_types_ = types;
+ }
+
+ void set_configure_fail_types(syncer::ModelTypeSet types) {
+ configure_fail_types_ = types;
}
private:
- scoped_ptr<SyncManager> manager_;
+ syncer::ModelTypeSet initial_sync_ended_types_;
+ syncer::ModelTypeSet progress_marker_types_;
+ syncer::ModelTypeSet configure_fail_types_;
+ FakeSyncManager* fake_manager_;
};
class SyncBackendHostTest : public testing::Test {
@@ -131,8 +151,6 @@ class SyncBackendHostTest : public testing::Test {
invalidator_storage_->AsWeakPtr()));
credentials_.email = "user@example.com";
credentials_.sync_token = "sync_token";
- fake_manager_ = new FakeSyncManager();
- fake_sync_manager_factory_.SetSyncManager(fake_manager_);
// NOTE: We can't include Passwords or Typed URLs due to the Sync Backend
// Registrar removing them if it can't find their model workers.
@@ -145,8 +163,10 @@ class SyncBackendHostTest : public testing::Test {
}
virtual void TearDown() OVERRIDE {
- backend_->StopSyncingForShutdown();
- backend_->Shutdown(false);
+ if (backend_.get()) {
+ backend_->StopSyncingForShutdown();
+ backend_->Shutdown(false);
+ }
backend_.reset();
sync_prefs_.reset();
invalidator_storage_.reset();
@@ -168,12 +188,17 @@ class SyncBackendHostTest : public testing::Test {
GURL(""),
credentials_,
true,
- &fake_sync_manager_factory_,
+ &fake_manager_factory_,
&handler_,
NULL);
ui_loop_.PostDelayedTask(FROM_HERE,
ui_loop_.QuitClosure(), TestTimeouts::action_timeout());
ui_loop_.Run();
+ // |fake_manager_factory_|'s fake_manager() is set on the sync
+ // thread, but we can rely on the message loop barriers to
+ // guarantee that we see the updated value.
+ fake_manager_ = fake_manager_factory_.fake_manager();
+ DCHECK(fake_manager_);
}
// Synchronously configures the backend's datatypes.
@@ -206,15 +231,15 @@ class SyncBackendHostTest : public testing::Test {
MessageLoop ui_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread io_thread_;
- MockSyncFrontend mock_frontend_;
+ StrictMock<MockSyncFrontend> mock_frontend_;
syncer::SyncCredentials credentials_;
syncer::TestUnrecoverableErrorHandler handler_;
scoped_ptr<TestingProfile> profile_;
scoped_ptr<SyncPrefs> sync_prefs_;
scoped_ptr<InvalidatorStorage> invalidator_storage_;
scoped_ptr<SyncBackendHost> backend_;
- FakeSyncManagerFactory fake_sync_manager_factory_;
FakeSyncManager* fake_manager_;
+ FakeSyncManagerFactory fake_manager_factory_;
syncer::ModelTypeSet enabled_types_;
};
@@ -257,8 +282,8 @@ TEST_F(SyncBackendHostTest, FirstTimeSync) {
TEST_F(SyncBackendHostTest, Restart) {
sync_prefs_->SetSyncSetupCompleted();
syncer::ModelTypeSet all_but_nigori = enabled_types_;
- fake_manager_->set_progress_marker_types(enabled_types_);
- fake_manager_->set_initial_sync_ended_types(enabled_types_);
+ fake_manager_factory_.set_progress_marker_types(enabled_types_);
+ fake_manager_factory_.set_initial_sync_ended_types(enabled_types_);
InitializeBackend();
EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
EXPECT_TRUE(Intersection(fake_manager_->GetAndResetCleanedTypes(),
@@ -289,8 +314,8 @@ TEST_F(SyncBackendHostTest, PartialTypes) {
syncer::ModelTypeSet partial_types(syncer::NIGORI, syncer::BOOKMARKS);
syncer::ModelTypeSet full_types =
Difference(enabled_types_, partial_types);
- fake_manager_->set_progress_marker_types(enabled_types_);
- fake_manager_->set_initial_sync_ended_types(full_types);
+ fake_manager_factory_.set_progress_marker_types(enabled_types_);
+ fake_manager_factory_.set_initial_sync_ended_types(full_types);
// Bringing up the backend should purge all partial types, then proceed to
// download the Nigori.
@@ -476,8 +501,8 @@ TEST_F(SyncBackendHostTest, NewlySupportedTypes) {
// Set sync manager behavior before passing it down. All types have progress
// markers and initial sync ended except the new types.
syncer::ModelTypeSet old_types = enabled_types_;
- fake_manager_->set_progress_marker_types(old_types);
- fake_manager_->set_initial_sync_ended_types(old_types);
+ fake_manager_factory_.set_progress_marker_types(old_types);
+ fake_manager_factory_.set_initial_sync_ended_types(old_types);
syncer::ModelTypeSet new_types(syncer::APP_SETTINGS,
syncer::EXTENSION_SETTINGS);
enabled_types_.PutAll(new_types);
@@ -517,8 +542,8 @@ TEST_F(SyncBackendHostTest, NewlySupportedTypesWithPartialTypes) {
syncer::ModelTypeSet partial_types(syncer::NIGORI, syncer::BOOKMARKS);
syncer::ModelTypeSet full_types =
Difference(enabled_types_, partial_types);
- fake_manager_->set_progress_marker_types(old_types);
- fake_manager_->set_initial_sync_ended_types(full_types);
+ fake_manager_factory_.set_progress_marker_types(old_types);
+ fake_manager_factory_.set_initial_sync_ended_types(full_types);
syncer::ModelTypeSet new_types(syncer::APP_SETTINGS,
syncer::EXTENSION_SETTINGS);
enabled_types_.PutAll(new_types);
@@ -609,6 +634,35 @@ TEST_F(SyncBackendHostTest, DisableNotifications) {
ui_loop_.Run();
}
+// Call StopSyncingForShutdown() on the backend and fire some notifications
+// before calling Shutdown(). Then start up and shut down the backend again.
+// Those notifications shouldn't propagate to the frontend.
+TEST_F(SyncBackendHostTest, NotificationsAfterStopSyncingForShutdown) {
+ InitializeBackend();
+
+ syncer::ObjectIdSet ids;
+ ids.insert(invalidation::ObjectId(5, "id5"));
+ backend_->UpdateRegisteredInvalidationIds(ids);
+
+ backend_->StopSyncingForShutdown();
+
+ // Should not trigger anything.
+ fake_manager_->DisableNotifications(syncer::TRANSIENT_NOTIFICATION_ERROR);
+ fake_manager_->EnableNotifications();
+ const syncer::ObjectIdPayloadMap& id_payloads =
+ syncer::ObjectIdSetToPayloadMap(ids, "payload");
+ fake_manager_->Invalidate(id_payloads, syncer::REMOTE_NOTIFICATION);
+
+ // Make sure the above calls take effect before we continue.
+ fake_manager_->WaitForSyncThread();
+
+ backend_->Shutdown(false);
+ backend_.reset();
+
+ TearDown();
+ SetUp();
+}
+
} // namespace
} // namespace browser_sync
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index bc72dcc..5e83b20 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -428,7 +428,8 @@ void ProfileSyncService::StartUp() {
// TODO(akalin): Fix this horribly non-intuitive behavior (see
// http://crbug.com/140354).
if (backend_.get()) {
- backend_->UpdateRegisteredInvalidationIds(all_registered_ids_);
+ backend_->UpdateRegisteredInvalidationIds(
+ notifier_registrar_.GetAllRegisteredIds());
}
if (!sync_global_error_.get()) {
@@ -441,17 +442,29 @@ void ProfileSyncService::StartUp() {
}
}
+void ProfileSyncService::RegisterInvalidationHandler(
+ syncer::SyncNotifierObserver* handler) {
+ notifier_registrar_.RegisterHandler(handler);
+}
+
void ProfileSyncService::UpdateRegisteredInvalidationIds(
syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids) {
- all_registered_ids_ = notifier_helper_.UpdateRegisteredIds(handler, ids);
+ notifier_registrar_.UpdateRegisteredIds(handler, ids);
+
// If |backend_| is NULL, its registered IDs will be updated when
// it's created and initialized.
if (backend_.get()) {
- backend_->UpdateRegisteredInvalidationIds(all_registered_ids_);
+ backend_->UpdateRegisteredInvalidationIds(
+ notifier_registrar_.GetAllRegisteredIds());
}
}
+void ProfileSyncService::UnregisterInvalidationHandler(
+ syncer::SyncNotifierObserver* handler) {
+ notifier_registrar_.UnregisterHandler(handler);
+}
+
void ProfileSyncService::Shutdown() {
ShutdownImpl(false);
}
@@ -464,7 +477,6 @@ void ProfileSyncService::ShutdownImpl(bool sync_disabled) {
base::Time shutdown_start_time = base::Time::Now();
if (backend_.get()) {
backend_->StopSyncingForShutdown();
- backend_->UpdateRegisteredInvalidationIds(syncer::ObjectIdSet());
}
// Stop all data type controllers, if needed. Note that until Stop
@@ -659,18 +671,18 @@ void ProfileSyncService::DisableBrokenDatatype(
}
void ProfileSyncService::OnNotificationsEnabled() {
- notifier_helper_.EmitOnNotificationsEnabled();
+ notifier_registrar_.EmitOnNotificationsEnabled();
}
void ProfileSyncService::OnNotificationsDisabled(
syncer::NotificationsDisabledReason reason) {
- notifier_helper_.EmitOnNotificationsDisabled(reason);
+ notifier_registrar_.EmitOnNotificationsDisabled(reason);
}
void ProfileSyncService::OnIncomingNotification(
const syncer::ObjectIdPayloadMap& id_payloads,
syncer::IncomingNotificationSource source) {
- notifier_helper_.DispatchInvalidationsToHandlers(id_payloads, source);
+ notifier_registrar_.DispatchInvalidationsToHandlers(id_payloads, source);
}
void ProfileSyncService::OnBackendInitialized(
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 3e3e14c..6e8ea6e 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -38,7 +38,7 @@
#include "sync/internal_api/public/util/experiments.h"
#include "sync/internal_api/public/util/unrecoverable_error_handler.h"
#include "sync/js/sync_js_controller.h"
-#include "sync/notifier/sync_notifier_helper.h"
+#include "sync/notifier/sync_notifier_registrar.h"
class Profile;
class ProfileSyncComponentsFactory;
@@ -552,15 +552,56 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// been cleared yet. Virtual for testing purposes.
virtual bool waiting_for_auth() const;
- // Updates the set of ObjectIds associated with a given |handler|.
- // Passing an empty ObjectIdSet will unregister |handler|.
- // There should be at most one handler registered per object id.
+ // Invalidation clients should follow the pattern below:
//
- // The handler -> registered ids map is persisted across restarts of
- // sync.
+ // When starting the client:
+ //
+ // pss->RegisterInvalidationHandler(client_handler);
+ //
+ // When the set of IDs to register changes for the client during its lifetime
+ // (i.e., between calls to RegisterInvalidationHandler(client_handler) and
+ // UnregisterInvalidationHandler(client_handler):
+ //
+ // pss->UpdateRegisteredInvalidationIds(client_handler, client_ids);
+ //
+ // When shutting down the client for browser shutdown:
+ //
+ // pss->UnregisterInvalidationHandler(client_handler);
+ //
+ // Note that there's no call to UpdateRegisteredIds() -- this is because the
+ // invalidation API persists registrations across browser restarts.
+ //
+ // When permanently shutting down the client, e.g. when disabling the related
+ // feature:
+ //
+ // pss->UpdateRegisteredInvalidationIds(client_handler, ObjectIdSet());
+ // pss->UnregisterInvalidationHandler(client_handler);
+
+ // NOTE(akalin): Invalidations that come in during browser shutdown may get
+ // dropped. This won't matter once we have an Acknowledge API, though: see
+ // http://crbug.com/78462 and http://crbug.com/124149.
+
+ // Starts sending notifications to |handler|. |handler| must not be NULL,
+ // and it must already be registered.
+ //
+ // Handler registrations are persisted across restarts of sync.
+ void RegisterInvalidationHandler(syncer::SyncNotifierObserver* handler);
+
+ // Updates the set of ObjectIds associated with |handler|. |handler| must
+ // not be NULL, and must already be registered. An ID must be registered for
+ // at most one handler.
+ //
+ // Registered IDs are persisted across restarts of sync.
void UpdateRegisteredInvalidationIds(syncer::SyncNotifierObserver* handler,
const syncer::ObjectIdSet& ids);
+ // Stops sending notifications to |handler|. |handler| must not be NULL, and
+ // it must already be registered. Note that this doesn't unregister the IDs
+ // associated with |handler|.
+ //
+ // Handler registrations are persisted across restarts of sync.
+ void UnregisterInvalidationHandler(syncer::SyncNotifierObserver* handler);
+
// ProfileKeyedService implementation.
virtual void Shutdown() OVERRIDE;
@@ -835,11 +876,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// Factory the backend will use to build the SyncManager.
syncer::SyncManagerFactory sync_manager_factory_;
- // The set of all registered IDs.
- syncer::ObjectIdSet all_registered_ids_;
-
// Dispatches invalidations to handlers.
- syncer::SyncNotifierHelper notifier_helper_;
+ syncer::SyncNotifierRegistrar notifier_registrar_;
DISALLOW_COPY_AND_ASSIGN(ProfileSyncService);
};
diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc
index 78ac1f4..1a2fe90 100644
--- a/chrome/browser/sync/profile_sync_service_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_unittest.cc
@@ -389,6 +389,7 @@ TEST_F(ProfileSyncServiceTest, UpdateRegisteredInvalidationIds) {
EXPECT_CALL(observer, OnNotificationsDisabled(
syncer::TRANSIENT_NOTIFICATION_ERROR));
+ service_->RegisterInvalidationHandler(&observer);
service_->UpdateRegisteredInvalidationIds(&observer, ids);
SyncBackendHostForProfileSyncTest* const backend =
@@ -400,7 +401,7 @@ TEST_F(ProfileSyncServiceTest, UpdateRegisteredInvalidationIds) {
Mock::VerifyAndClearExpectations(&observer);
- service_->UpdateRegisteredInvalidationIds(&observer, syncer::ObjectIdSet());
+ service_->UnregisterInvalidationHandler(&observer);
backend->EmitOnNotificationsEnabled();
backend->EmitOnIncomingNotification(payloads, syncer::REMOTE_NOTIFICATION);
@@ -422,9 +423,12 @@ TEST_F(ProfileSyncServiceTest, UpdateRegisteredInvalidationIdsPersistence) {
EXPECT_CALL(observer, OnNotificationsEnabled());
EXPECT_CALL(observer, OnIncomingNotification(
payloads, syncer::REMOTE_NOTIFICATION));
+ // This may get called more than once, as a real notifier is
+ // created.
EXPECT_CALL(observer, OnNotificationsDisabled(
- syncer::TRANSIENT_NOTIFICATION_ERROR));
+ syncer::TRANSIENT_NOTIFICATION_ERROR)).Times(AtLeast(1));
+ service_->RegisterInvalidationHandler(&observer);
service_->UpdateRegisteredInvalidationIds(&observer, ids);
service_->StopAndSuppress();
diff --git a/sync/internal_api/public/sync_manager.h b/sync/internal_api/public/sync_manager.h
index 1fccac4..b08810c 100644
--- a/sync/internal_api/public/sync_manager.h
+++ b/sync/internal_api/public/sync_manager.h
@@ -407,12 +407,19 @@ class SyncManager {
virtual void UpdateEnabledTypes(
const ModelTypeSet& enabled_types) = 0;
- // Forwards to the underlying notifier (see
- // SyncNotifier::UpdateRegisteredIds()).
+ // Forwards to the underlying notifier (see comments in sync_notifier.h).
+ virtual void RegisterInvalidationHandler(
+ SyncNotifierObserver* handler) = 0;
+
+ // Forwards to the underlying notifier (see comments in sync_notifier.h).
virtual void UpdateRegisteredInvalidationIds(
SyncNotifierObserver* handler,
const ObjectIdSet& ids) = 0;
+ // Forwards to the underlying notifier (see comments in sync_notifier.h).
+ virtual void UnregisterInvalidationHandler(
+ SyncNotifierObserver* handler) = 0;
+
// Put the syncer in normal mode ready to perform nudges and polls.
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) = 0;
diff --git a/sync/internal_api/public/test/fake_sync_manager.h b/sync/internal_api/public/test/fake_sync_manager.h
index cbc8b56..8607712 100644
--- a/sync/internal_api/public/test/fake_sync_manager.h
+++ b/sync/internal_api/public/test/fake_sync_manager.h
@@ -10,7 +10,7 @@
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "sync/internal_api/public/sync_manager.h"
-#include "sync/notifier/sync_notifier_helper.h"
+#include "sync/notifier/sync_notifier_registrar.h"
namespace base {
class SequencedTaskRunner;
@@ -20,25 +20,23 @@ namespace syncer {
class FakeSyncManager : public SyncManager {
public:
- explicit FakeSyncManager();
+ // |initial_sync_ended_types|: The set of types that have initial_sync_ended
+ // set to true. This value will be used by InitialSyncEndedTypes() until the
+ // next configuration is performed.
+ //
+ // |progress_marker_types|: The set of types that have valid progress
+ // markers. This will be used by GetTypesWithEmptyProgressMarkerToken() until
+ // the next configuration is performed.
+ //
+ // |configure_fail_types|: The set of types that will fail
+ // configuration. Once ConfigureSyncer is called, the
+ // |initial_sync_ended_types_| and |progress_marker_types_| will be updated
+ // to include those types that didn't fail.
+ FakeSyncManager(ModelTypeSet initial_sync_ended_types,
+ ModelTypeSet progress_marker_types,
+ ModelTypeSet configure_fail_types);
virtual ~FakeSyncManager();
- // The set of types that have initial_sync_ended set to true. This value will
- // be used by InitialSyncEndedTypes() until the next configuration is
- // performed.
- void set_initial_sync_ended_types(ModelTypeSet types);
-
- // The set of types that have valid progress markers. This will be used by
- // GetTypesWithEmptyProgressMarkerToken() until the next configuration is
- // performed.
- void set_progress_marker_types(ModelTypeSet types);
-
- // The set of types that will fail configuration. Once ConfigureSyncer is
- // called, the |initial_sync_ended_types_| and
- // |progress_marker_types_| will be updated to include those types
- // that didn't fail.
- void set_configure_fail_types(ModelTypeSet types);
-
// Returns those types that have been cleaned (purged from the directory)
// since the last call to GetAndResetCleanedTypes(), or since startup if never
// called.
@@ -63,6 +61,9 @@ class FakeSyncManager : public SyncManager {
// Posts a method to disable notifications on the sync thread.
void DisableNotifications(NotificationsDisabledReason reason);
+ // Block until the sync thread has finished processing any pending messages.
+ void WaitForSyncThread();
+
// SyncManager implementation.
// Note: we treat whatever message loop this is called from as the sync
// loop for purposes of callbacks.
@@ -94,9 +95,13 @@ class FakeSyncManager : public SyncManager {
virtual bool PurgePartiallySyncedTypes() OVERRIDE;
virtual void UpdateCredentials(const SyncCredentials& credentials) OVERRIDE;
virtual void UpdateEnabledTypes(const ModelTypeSet& types) OVERRIDE;
+ virtual void RegisterInvalidationHandler(
+ SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredInvalidationIds(
SyncNotifierObserver* handler,
const ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterInvalidationHandler(
+ SyncNotifierObserver* handler) OVERRIDE;
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) OVERRIDE;
virtual void SetEncryptionPassphrase(const std::string& passphrase,
@@ -150,7 +155,7 @@ class FakeSyncManager : public SyncManager {
ModelTypeSet enabled_types_;
// Faked notifier state.
- SyncNotifierHelper notifier_helper_;
+ SyncNotifierRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(FakeSyncManager);
};
diff --git a/sync/internal_api/sync_manager_impl.cc b/sync/internal_api/sync_manager_impl.cc
index 72f936f..7e8c17f 100644
--- a/sync/internal_api/sync_manager_impl.cc
+++ b/sync/internal_api/sync_manager_impl.cc
@@ -396,6 +396,7 @@ bool SyncManagerImpl::Init(
change_delegate_ = change_delegate;
sync_notifier_ = sync_notifier.Pass();
+ sync_notifier_->RegisterHandler(this);
AddObserver(&js_sync_manager_observer_);
SetJsEventHandler(event_handler);
@@ -733,18 +734,34 @@ void SyncManagerImpl::UpdateCredentials(
void SyncManagerImpl::UpdateEnabledTypes(
const ModelTypeSet& enabled_types) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(initialized_);
sync_notifier_->UpdateRegisteredIds(
this,
ModelTypeSetToObjectIdSet(enabled_types));
}
+void SyncManagerImpl::RegisterInvalidationHandler(
+ SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(initialized_);
+ sync_notifier_->RegisterHandler(handler);
+}
+
void SyncManagerImpl::UpdateRegisteredInvalidationIds(
SyncNotifierObserver* handler,
const ObjectIdSet& ids) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(initialized_);
sync_notifier_->UpdateRegisteredIds(handler, ids);
}
+void SyncManagerImpl::UnregisterInvalidationHandler(
+ SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(initialized_);
+ sync_notifier_->UnregisterHandler(handler);
+}
+
void SyncManagerImpl::SetEncryptionPassphrase(
const std::string& passphrase,
bool is_explicit) {
@@ -1216,14 +1233,17 @@ void SyncManagerImpl::ShutdownOnSyncThread() {
RemoveObserver(&debug_info_event_listener_);
- if (sync_notifier_.get()) {
- sync_notifier_->UpdateRegisteredIds(this, ObjectIdSet());
- }
+ // |sync_notifier_| and |connection_manager_| may end up being NULL here in
+ // tests (in synchronous initialization mode).
+ //
+ // TODO(akalin): Fix this behavior.
+
+ if (sync_notifier_.get())
+ sync_notifier_->UnregisterHandler(this);
sync_notifier_.reset();
- if (connection_manager_.get()) {
+ if (connection_manager_.get())
connection_manager_->RemoveListener(this);
- }
connection_manager_.reset();
net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
diff --git a/sync/internal_api/sync_manager_impl.h b/sync/internal_api/sync_manager_impl.h
index c09a498..501c1cd 100644
--- a/sync/internal_api/sync_manager_impl.h
+++ b/sync/internal_api/sync_manager_impl.h
@@ -87,9 +87,13 @@ class SyncManagerImpl : public SyncManager,
virtual void UpdateCredentials(const SyncCredentials& credentials) OVERRIDE;
virtual void UpdateEnabledTypes(
const ModelTypeSet& enabled_types) OVERRIDE;
+ virtual void RegisterInvalidationHandler(
+ SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredInvalidationIds(
SyncNotifierObserver* handler,
const ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterInvalidationHandler(
+ SyncNotifierObserver* handler) OVERRIDE;
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) OVERRIDE;
virtual void SetEncryptionPassphrase(const std::string& passphrase,
diff --git a/sync/internal_api/sync_manager_impl_unittest.cc b/sync/internal_api/sync_manager_impl_unittest.cc
index bd2ffef..841ffd0 100644
--- a/sync/internal_api/sync_manager_impl_unittest.cc
+++ b/sync/internal_api/sync_manager_impl_unittest.cc
@@ -696,8 +696,10 @@ class SyncManagerObserverMock : public SyncManager::Observer {
class SyncNotifierMock : public SyncNotifier {
public:
+ MOCK_METHOD1(RegisterHandler, void(SyncNotifierObserver*));
MOCK_METHOD2(UpdateRegisteredIds,
void(SyncNotifierObserver*, const ObjectIdSet&));
+ MOCK_METHOD1(UnregisterHandler, void(SyncNotifierObserver*));
MOCK_METHOD1(SetUniqueId, void(const std::string&));
MOCK_METHOD1(SetStateDeprecated, void(const std::string&));
MOCK_METHOD2(UpdateCredentials,
@@ -742,6 +744,10 @@ class SyncManagerTest : public testing::Test,
EXPECT_CALL(*sync_notifier_mock_, SetStateDeprecated(""));
EXPECT_CALL(*sync_notifier_mock_,
UpdateCredentials(credentials.email, credentials.sync_token));
+ EXPECT_CALL(*sync_notifier_mock_, RegisterHandler(_));
+
+ // Called by ShutdownOnSyncThread().
+ EXPECT_CALL(*sync_notifier_mock_, UnregisterHandler(_));
sync_manager_.AddObserver(&observer_);
EXPECT_CALL(observer_, OnInitializationComplete(_, _, _)).
@@ -782,7 +788,8 @@ class SyncManagerTest : public testing::Test,
void TearDown() {
sync_manager_.RemoveObserver(&observer_);
- EXPECT_CALL(*sync_notifier_mock_, UpdateRegisteredIds(_, ObjectIdSet()));
+ // |sync_notifier_mock_| is strict, which ensures we don't do anything but
+ // unregister |sync_manager_| as a handler on shutdown.
sync_manager_.ShutdownOnSyncThread();
sync_notifier_mock_ = NULL;
PumpLoop();
@@ -956,18 +963,28 @@ TEST_F(SyncManagerTest, UpdateEnabledTypes) {
ModelSafeRoutingInfo routes;
GetModelSafeRoutingInfo(&routes);
const ModelTypeSet enabled_types = GetRoutingInfoTypes(routes);
-
EXPECT_CALL(*sync_notifier_mock_,
UpdateRegisteredIds(
_, ModelTypeSetToObjectIdSet(enabled_types)));
+
sync_manager_.UpdateEnabledTypes(enabled_types);
}
+TEST_F(SyncManagerTest, RegisterInvalidationHandler) {
+ EXPECT_CALL(*sync_notifier_mock_, RegisterHandler(NULL));
+ sync_manager_.RegisterInvalidationHandler(NULL);
+}
+
TEST_F(SyncManagerTest, UpdateRegisteredInvalidationIds) {
EXPECT_CALL(*sync_notifier_mock_, UpdateRegisteredIds(NULL, ObjectIdSet()));
sync_manager_.UpdateRegisteredInvalidationIds(NULL, ObjectIdSet());
}
+TEST_F(SyncManagerTest, UnregisterInvalidationHandler) {
+ EXPECT_CALL(*sync_notifier_mock_, UnregisterHandler(NULL));
+ sync_manager_.UnregisterInvalidationHandler(NULL);
+}
+
TEST_F(SyncManagerTest, ProcessJsMessage) {
const JsArgList kNoArgs;
diff --git a/sync/internal_api/test/fake_sync_manager.cc b/sync/internal_api/test/fake_sync_manager.cc
index c9a742d..4028040 100644
--- a/sync/internal_api/test/fake_sync_manager.cc
+++ b/sync/internal_api/test/fake_sync_manager.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
+#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
@@ -21,22 +22,15 @@
namespace syncer {
-FakeSyncManager::FakeSyncManager() {}
+FakeSyncManager::FakeSyncManager(ModelTypeSet initial_sync_ended_types,
+ ModelTypeSet progress_marker_types,
+ ModelTypeSet configure_fail_types) :
+ initial_sync_ended_types_(initial_sync_ended_types),
+ progress_marker_types_(progress_marker_types),
+ configure_fail_types_(configure_fail_types) {}
FakeSyncManager::~FakeSyncManager() {}
-void FakeSyncManager::set_initial_sync_ended_types(ModelTypeSet types) {
- initial_sync_ended_types_ = types;
-}
-
-void FakeSyncManager::set_progress_marker_types(ModelTypeSet types) {
- progress_marker_types_ = types;
-}
-
-void FakeSyncManager::set_configure_fail_types(ModelTypeSet types) {
- configure_fail_types_ = types;
-}
-
ModelTypeSet FakeSyncManager::GetAndResetCleanedTypes() {
ModelTypeSet cleaned_types = cleaned_types_;
cleaned_types_.Clear();
@@ -84,6 +78,24 @@ void FakeSyncManager::DisableNotifications(
}
}
+namespace {
+
+void DoNothing() {}
+
+} // namespace
+
+void FakeSyncManager::WaitForSyncThread() {
+ // Post a task to |sync_task_runner_| and block until it runs.
+ base::RunLoop run_loop;
+ if (!sync_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DoNothing),
+ run_loop.QuitClosure())) {
+ NOTREACHED();
+ }
+ run_loop.Run();
+}
+
bool FakeSyncManager::Init(
const FilePath& database_location,
const WeakHandle<JsEventHandler>& event_handler,
@@ -149,10 +161,20 @@ void FakeSyncManager::UpdateEnabledTypes(const ModelTypeSet& types) {
enabled_types_ = types;
}
+void FakeSyncManager::RegisterInvalidationHandler(
+ SyncNotifierObserver* handler) {
+ registrar_.RegisterHandler(handler);
+}
+
void FakeSyncManager::UpdateRegisteredInvalidationIds(
SyncNotifierObserver* handler,
const ObjectIdSet& ids) {
- notifier_helper_.UpdateRegisteredIds(handler, ids);
+ registrar_.UpdateRegisteredIds(handler, ids);
+}
+
+void FakeSyncManager::UnregisterInvalidationHandler(
+ SyncNotifierObserver* handler) {
+ registrar_.UnregisterHandler(handler);
}
void FakeSyncManager::StartSyncingNormally(
@@ -265,18 +287,18 @@ void FakeSyncManager::InvalidateOnSyncThread(
const ObjectIdPayloadMap& id_payloads,
IncomingNotificationSource source) {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
- notifier_helper_.DispatchInvalidationsToHandlers(id_payloads, source);
+ registrar_.DispatchInvalidationsToHandlers(id_payloads, source);
}
void FakeSyncManager::EnableNotificationsOnSyncThread() {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
- notifier_helper_.EmitOnNotificationsEnabled();
+ registrar_.EmitOnNotificationsEnabled();
}
void FakeSyncManager::DisableNotificationsOnSyncThread(
NotificationsDisabledReason reason) {
DCHECK(sync_task_runner_->RunsTasksOnCurrentThread());
- notifier_helper_.EmitOnNotificationsDisabled(reason);
+ registrar_.EmitOnNotificationsDisabled(reason);
}
} // namespace syncer
diff --git a/sync/notifier/invalidation_notifier.cc b/sync/notifier/invalidation_notifier.cc
index 3a72f32..91a59ad 100644
--- a/sync/notifier/invalidation_notifier.cc
+++ b/sync/notifier/invalidation_notifier.cc
@@ -34,10 +34,21 @@ InvalidationNotifier::~InvalidationNotifier() {
DCHECK(CalledOnValidThread());
}
+void InvalidationNotifier::RegisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(CalledOnValidThread());
+ registrar_.RegisterHandler(handler);
+}
+
void InvalidationNotifier::UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) {
DCHECK(CalledOnValidThread());
- invalidation_client_.RegisterIds(helper_.UpdateRegisteredIds(handler, ids));
+ registrar_.UpdateRegisteredIds(handler, ids);
+ invalidation_client_.RegisterIds(registrar_.GetAllRegisteredIds());
+}
+
+void InvalidationNotifier::UnregisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(CalledOnValidThread());
+ registrar_.UnregisterHandler(handler);
}
void InvalidationNotifier::SetUniqueId(const std::string& unique_id) {
@@ -88,18 +99,18 @@ void InvalidationNotifier::SendNotification(ModelTypeSet changed_types) {
void InvalidationNotifier::OnInvalidate(const ObjectIdPayloadMap& id_payloads) {
DCHECK(CalledOnValidThread());
- helper_.DispatchInvalidationsToHandlers(id_payloads, REMOTE_NOTIFICATION);
+ registrar_.DispatchInvalidationsToHandlers(id_payloads, REMOTE_NOTIFICATION);
}
void InvalidationNotifier::OnNotificationsEnabled() {
DCHECK(CalledOnValidThread());
- helper_.EmitOnNotificationsEnabled();
+ registrar_.EmitOnNotificationsEnabled();
}
void InvalidationNotifier::OnNotificationsDisabled(
NotificationsDisabledReason reason) {
DCHECK(CalledOnValidThread());
- helper_.EmitOnNotificationsDisabled(reason);
+ registrar_.EmitOnNotificationsDisabled(reason);
}
} // namespace syncer
diff --git a/sync/notifier/invalidation_notifier.h b/sync/notifier/invalidation_notifier.h
index 8d74552..6cbe44d 100644
--- a/sync/notifier/invalidation_notifier.h
+++ b/sync/notifier/invalidation_notifier.h
@@ -23,7 +23,7 @@
#include "sync/notifier/chrome_invalidation_client.h"
#include "sync/notifier/invalidation_state_tracker.h"
#include "sync/notifier/sync_notifier.h"
-#include "sync/notifier/sync_notifier_helper.h"
+#include "sync/notifier/sync_notifier_registrar.h"
namespace notifier {
class PushClient;
@@ -49,8 +49,10 @@ class InvalidationNotifier
virtual ~InvalidationNotifier();
// SyncNotifier implementation.
+ virtual void RegisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void SetUniqueId(const std::string& unique_id) OVERRIDE;
virtual void SetStateDeprecated(const std::string& state) OVERRIDE;
virtual void UpdateCredentials(
@@ -75,7 +77,7 @@ class InvalidationNotifier
};
State state_;
- SyncNotifierHelper helper_;
+ SyncNotifierRegistrar registrar_;
// Passed to |invalidation_client_|.
const InvalidationVersionMap initial_max_invalidation_versions_;
diff --git a/sync/notifier/invalidation_notifier_unittest.cc b/sync/notifier/invalidation_notifier_unittest.cc
index 4a07678..5f41428 100644
--- a/sync/notifier/invalidation_notifier_unittest.cc
+++ b/sync/notifier/invalidation_notifier_unittest.cc
@@ -33,11 +33,10 @@ class InvalidationNotifierTest : public testing::Test {
ResetNotifier();
}
- // Constructs an InvalidationNotifier, places it in
- // |invalidation_notifier_|, and adds |mock_observer_| as an observer. This
- // remains in place until either TearDown (automatic) or ResetNotifier
- // (manual) is called.
- void CreateAndObserveNotifier(
+ // Constructs an InvalidationNotifier, places it in |invalidation_notifier_|,
+ // and registers |mock_observer_| as a handler. This remains in place until
+ // either TearDown (automatic) or ResetNotifier (manual) is called.
+ void CreateNotifier(
const std::string& initial_invalidation_state) {
notifier::NotifierOptions notifier_options;
// Note: URLRequestContextGetters are ref-counted.
@@ -50,10 +49,11 @@ class InvalidationNotifierTest : public testing::Test {
initial_invalidation_state,
MakeWeakHandle(mock_tracker_.AsWeakPtr()),
"fake_client_info"));
+ invalidation_notifier_->RegisterHandler(&mock_observer_);
}
void ResetNotifier() {
- invalidation_notifier_->UpdateRegisteredIds(&mock_observer_, ObjectIdSet());
+ invalidation_notifier_->UnregisterHandler(&mock_observer_);
// Stopping the invalidation notifier stops its scheduler, which deletes any
// pending tasks without running them. Some tasks "run and delete" another
// task, so they must be run in order to avoid leaking the inner task.
@@ -71,13 +71,11 @@ class InvalidationNotifierTest : public testing::Test {
};
TEST_F(InvalidationNotifierTest, Basic) {
- CreateAndObserveNotifier("fake_state");
InSequence dummy;
- ModelTypeSet models(PREFERENCES, BOOKMARKS, AUTOFILL);
- invalidation_notifier_->UpdateRegisteredIds(
- &mock_observer_, ModelTypeSetToObjectIdSet(models));
+ CreateNotifier("fake_state");
+ const ModelTypeSet models(PREFERENCES, BOOKMARKS, AUTOFILL);
const ModelTypePayloadMap& type_payloads =
ModelTypePayloadMapFromEnumSet(models, "payload");
EXPECT_CALL(mock_observer_, OnNotificationsEnabled());
@@ -91,6 +89,9 @@ TEST_F(InvalidationNotifierTest, Basic) {
// Note no expectation on mock_tracker_, as we initialized with
// non-empty initial_invalidation_state above.
+ invalidation_notifier_->UpdateRegisteredIds(
+ &mock_observer_, ModelTypeSetToObjectIdSet(models));
+
// TODO(tim): This call should be a no-op, Remove once bug 124140 and
// associated issues are fixed.
invalidation_notifier_->SetStateDeprecated("fake_state");
@@ -109,8 +110,7 @@ TEST_F(InvalidationNotifierTest, Basic) {
}
TEST_F(InvalidationNotifierTest, MigrateState) {
- CreateAndObserveNotifier(std::string());
- InSequence dummy;
+ CreateNotifier(std::string());
EXPECT_CALL(mock_tracker_, SetInvalidationState("fake_state"));
invalidation_notifier_->SetStateDeprecated("fake_state");
@@ -121,7 +121,7 @@ TEST_F(InvalidationNotifierTest, MigrateState) {
// Pretend Chrome shut down.
ResetNotifier();
- CreateAndObserveNotifier("fake_state");
+ CreateNotifier("fake_state");
// Should do nothing.
invalidation_notifier_->SetStateDeprecated("more_spurious_fake_state");
}
diff --git a/sync/notifier/non_blocking_invalidation_notifier.cc b/sync/notifier/non_blocking_invalidation_notifier.cc
index ac0f76a..c561c35 100644
--- a/sync/notifier/non_blocking_invalidation_notifier.cc
+++ b/sync/notifier/non_blocking_invalidation_notifier.cc
@@ -4,6 +4,8 @@
#include "sync/notifier/non_blocking_invalidation_notifier.h"
+#include <cstddef>
+
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -89,12 +91,12 @@ void NonBlockingInvalidationNotifier::Core::Initialize(
initial_invalidation_state,
invalidation_state_tracker,
client_info));
+ invalidation_notifier_->RegisterHandler(this);
}
-
void NonBlockingInvalidationNotifier::Core::Teardown() {
DCHECK(network_task_runner_->BelongsToCurrentThread());
- invalidation_notifier_->UpdateRegisteredIds(this, ObjectIdSet());
+ invalidation_notifier_->UnregisterHandler(this);
invalidation_notifier_.reset();
network_task_runner_ = NULL;
}
@@ -183,21 +185,33 @@ NonBlockingInvalidationNotifier::~NonBlockingInvalidationNotifier() {
}
}
+void NonBlockingInvalidationNotifier::RegisterHandler(
+ SyncNotifierObserver* handler) {
+ DCHECK(parent_task_runner_->BelongsToCurrentThread());
+ registrar_.RegisterHandler(handler);
+}
+
void NonBlockingInvalidationNotifier::UpdateRegisteredIds(
- SyncNotifierObserver* handler, const ObjectIdSet& ids) {
+ SyncNotifierObserver* handler,
+ const ObjectIdSet& ids) {
DCHECK(parent_task_runner_->BelongsToCurrentThread());
- const ObjectIdSet& all_registered_ids =
- helper_.UpdateRegisteredIds(handler, ids);
+ registrar_.UpdateRegisteredIds(handler, ids);
if (!network_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&NonBlockingInvalidationNotifier::Core::UpdateRegisteredIds,
core_.get(),
- all_registered_ids))) {
+ registrar_.GetAllRegisteredIds()))) {
NOTREACHED();
}
}
+void NonBlockingInvalidationNotifier::UnregisterHandler(
+ SyncNotifierObserver* handler) {
+ DCHECK(parent_task_runner_->BelongsToCurrentThread());
+ registrar_.UnregisterHandler(handler);
+}
+
void NonBlockingInvalidationNotifier::SetUniqueId(
const std::string& unique_id) {
DCHECK(parent_task_runner_->BelongsToCurrentThread());
@@ -241,20 +255,20 @@ void NonBlockingInvalidationNotifier::SendNotification(
void NonBlockingInvalidationNotifier::OnNotificationsEnabled() {
DCHECK(parent_task_runner_->BelongsToCurrentThread());
- helper_.EmitOnNotificationsEnabled();
+ registrar_.EmitOnNotificationsEnabled();
}
void NonBlockingInvalidationNotifier::OnNotificationsDisabled(
NotificationsDisabledReason reason) {
DCHECK(parent_task_runner_->BelongsToCurrentThread());
- helper_.EmitOnNotificationsDisabled(reason);
+ registrar_.EmitOnNotificationsDisabled(reason);
}
void NonBlockingInvalidationNotifier::OnIncomingNotification(
const ObjectIdPayloadMap& id_payloads,
IncomingNotificationSource source) {
DCHECK(parent_task_runner_->BelongsToCurrentThread());
- helper_.DispatchInvalidationsToHandlers(id_payloads, source);
+ registrar_.DispatchInvalidationsToHandlers(id_payloads, source);
}
} // namespace syncer
diff --git a/sync/notifier/non_blocking_invalidation_notifier.h b/sync/notifier/non_blocking_invalidation_notifier.h
index f76b37b..9577297 100644
--- a/sync/notifier/non_blocking_invalidation_notifier.h
+++ b/sync/notifier/non_blocking_invalidation_notifier.h
@@ -18,8 +18,8 @@
#include "sync/internal_api/public/util/weak_handle.h"
#include "sync/notifier/invalidation_state_tracker.h"
#include "sync/notifier/sync_notifier.h"
-#include "sync/notifier/sync_notifier_helper.h"
#include "sync/notifier/sync_notifier_observer.h"
+#include "sync/notifier/sync_notifier_registrar.h"
namespace base {
class SingleThreadTaskRunner;
@@ -27,6 +27,8 @@ class SingleThreadTaskRunner;
namespace syncer {
+// TODO(akalin): Generalize to NonBlockingSyncNotifier
+// (http://crbug.com/140409).
class NonBlockingInvalidationNotifier
: public SyncNotifier,
// SyncNotifierObserver to "observe" our Core via WeakHandle.
@@ -44,8 +46,10 @@ class NonBlockingInvalidationNotifier
virtual ~NonBlockingInvalidationNotifier();
// SyncNotifier implementation.
+ virtual void RegisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void SetUniqueId(const std::string& unique_id) OVERRIDE;
virtual void SetStateDeprecated(const std::string& state) OVERRIDE;
virtual void UpdateCredentials(
@@ -65,7 +69,7 @@ class NonBlockingInvalidationNotifier
base::WeakPtrFactory<NonBlockingInvalidationNotifier> weak_ptr_factory_;
- SyncNotifierHelper helper_;
+ SyncNotifierRegistrar registrar_;
// The real guts of NonBlockingInvalidationNotifier, which allows
// this class to live completely on the parent thread.
diff --git a/sync/notifier/non_blocking_invalidation_notifier_unittest.cc b/sync/notifier/non_blocking_invalidation_notifier_unittest.cc
index 0cadd4a..f237cdb 100644
--- a/sync/notifier/non_blocking_invalidation_notifier_unittest.cc
+++ b/sync/notifier/non_blocking_invalidation_notifier_unittest.cc
@@ -46,10 +46,11 @@ class NonBlockingInvalidationNotifierTest : public testing::Test {
std::string(), // initial_invalidation_state
MakeWeakHandle(base::WeakPtr<InvalidationStateTracker>()),
"fake_client_info"));
+ invalidation_notifier_->RegisterHandler(&mock_observer_);
}
virtual void TearDown() {
- invalidation_notifier_->UpdateRegisteredIds(&mock_observer_, ObjectIdSet());
+ invalidation_notifier_->UnregisterHandler(&mock_observer_);
invalidation_notifier_.reset();
request_context_getter_ = NULL;
io_thread_.Stop();
@@ -64,13 +65,12 @@ class NonBlockingInvalidationNotifierTest : public testing::Test {
notifier::FakeBaseTask fake_base_task_;
};
+// TODO(akalin): Add real unit tests (http://crbug.com/140410).
+
TEST_F(NonBlockingInvalidationNotifierTest, Basic) {
InSequence dummy;
- ModelTypeSet models(PREFERENCES, BOOKMARKS, AUTOFILL);
- invalidation_notifier_->UpdateRegisteredIds(
- &mock_observer_, ModelTypeSetToObjectIdSet(models));
-
+ const ModelTypeSet models(PREFERENCES, BOOKMARKS, AUTOFILL);
const ModelTypePayloadMap& type_payloads =
ModelTypePayloadMapFromEnumSet(models, "payload");
EXPECT_CALL(mock_observer_, OnNotificationsEnabled());
@@ -82,6 +82,9 @@ TEST_F(NonBlockingInvalidationNotifierTest, Basic) {
EXPECT_CALL(mock_observer_,
OnNotificationsDisabled(NOTIFICATION_CREDENTIALS_REJECTED));
+ invalidation_notifier_->UpdateRegisteredIds(
+ &mock_observer_, ModelTypeSetToObjectIdSet(models));
+
invalidation_notifier_->SetStateDeprecated("fake_state");
invalidation_notifier_->SetUniqueId("fake_id");
invalidation_notifier_->UpdateCredentials("foo@bar.com", "fake_token");
diff --git a/sync/notifier/p2p_notifier.cc b/sync/notifier/p2p_notifier.cc
index 5d5aeff..dab7221 100644
--- a/sync/notifier/p2p_notifier.cc
+++ b/sync/notifier/p2p_notifier.cc
@@ -157,10 +157,18 @@ P2PNotifier::~P2PNotifier() {
push_client_->RemoveObserver(this);
}
+void P2PNotifier::RegisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ registrar_.RegisterHandler(handler);
+}
+
void P2PNotifier::UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) {
- const ModelTypeSet enabled_types = ObjectIdSetToModelTypeSet(
- helper_.UpdateRegisteredIds(handler, ids));
+ // TODO(akalin): Handle arbitrary object IDs (http://crbug.com/140411).
+ DCHECK(thread_checker_.CalledOnValidThread());
+ registrar_.UpdateRegisteredIds(handler, ids);
+ const ModelTypeSet enabled_types =
+ ObjectIdSetToModelTypeSet(registrar_.GetAllRegisteredIds());
const ModelTypeSet new_enabled_types =
Difference(enabled_types, enabled_types_);
const P2PNotificationData notification_data(
@@ -169,6 +177,11 @@ void P2PNotifier::UpdateRegisteredIds(SyncNotifierObserver* handler,
enabled_types_ = enabled_types;
}
+void P2PNotifier::UnregisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ registrar_.UnregisterHandler(handler);
+}
+
void P2PNotifier::SetUniqueId(const std::string& unique_id) {
DCHECK(thread_checker_.CalledOnValidThread());
unique_id_ = unique_id;
@@ -207,7 +220,7 @@ void P2PNotifier::OnNotificationsEnabled() {
DCHECK(thread_checker_.CalledOnValidThread());
bool just_turned_on = (notifications_enabled_ == false);
notifications_enabled_ = true;
- helper_.EmitOnNotificationsEnabled();
+ registrar_.EmitOnNotificationsEnabled();
if (just_turned_on) {
const P2PNotificationData notification_data(
unique_id_, NOTIFY_SELF, enabled_types_);
@@ -218,7 +231,7 @@ void P2PNotifier::OnNotificationsEnabled() {
void P2PNotifier::OnNotificationsDisabled(
notifier::NotificationsDisabledReason reason) {
DCHECK(thread_checker_.CalledOnValidThread());
- helper_.EmitOnNotificationsDisabled(FromNotifierReason(reason));
+ registrar_.EmitOnNotificationsDisabled(FromNotifierReason(reason));
}
void P2PNotifier::OnIncomingNotification(
@@ -257,7 +270,7 @@ void P2PNotifier::OnIncomingNotification(
}
const ModelTypePayloadMap& type_payloads = ModelTypePayloadMapFromEnumSet(
notification_data.GetChangedTypes(), std::string());
- helper_.DispatchInvalidationsToHandlers(
+ registrar_.DispatchInvalidationsToHandlers(
ModelTypePayloadMapToObjectIdPayloadMap(type_payloads),
REMOTE_NOTIFICATION);
}
diff --git a/sync/notifier/p2p_notifier.h b/sync/notifier/p2p_notifier.h
index d2de89d..4457c6f 100644
--- a/sync/notifier/p2p_notifier.h
+++ b/sync/notifier/p2p_notifier.h
@@ -20,7 +20,7 @@
#include "sync/internal_api/public/base/model_type.h"
#include "sync/notifier/notifications_disabled_reason.h"
#include "sync/notifier/sync_notifier.h"
-#include "sync/notifier/sync_notifier_helper.h"
+#include "sync/notifier/sync_notifier_registrar.h"
namespace notifier {
class PushClient;
@@ -96,8 +96,10 @@ class P2PNotifier : public SyncNotifier,
virtual ~P2PNotifier();
// SyncNotifier implementation
+ virtual void RegisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) OVERRIDE;
+ virtual void UnregisterHandler(SyncNotifierObserver* handler) OVERRIDE;
virtual void SetUniqueId(const std::string& unique_id) OVERRIDE;
virtual void SetStateDeprecated(const std::string& state) OVERRIDE;
virtual void UpdateCredentials(
@@ -119,7 +121,7 @@ class P2PNotifier : public SyncNotifier,
base::ThreadChecker thread_checker_;
- SyncNotifierHelper helper_;
+ SyncNotifierRegistrar registrar_;
// The push client.
scoped_ptr<notifier::PushClient> push_client_;
diff --git a/sync/notifier/p2p_notifier_unittest.cc b/sync/notifier/p2p_notifier_unittest.cc
index 28a73c9..97033ea 100644
--- a/sync/notifier/p2p_notifier_unittest.cc
+++ b/sync/notifier/p2p_notifier_unittest.cc
@@ -28,10 +28,11 @@ class P2PNotifierTest : public testing::Test {
scoped_ptr<notifier::PushClient>(fake_push_client_),
NOTIFY_OTHERS),
next_sent_notification_to_reflect_(0) {
+ p2p_notifier_.RegisterHandler(&mock_observer_);
}
virtual ~P2PNotifierTest() {
- p2p_notifier_.UpdateRegisteredIds(&mock_observer_, ObjectIdSet());
+ p2p_notifier_.UnregisterHandler(&mock_observer_);
}
ModelTypePayloadMap MakePayloadMap(ModelTypeSet types) {
@@ -140,16 +141,16 @@ TEST_F(P2PNotifierTest, P2PNotificationDataNonDefault) {
// observer should receive only a notification from the call to
// UpdateEnabledTypes().
TEST_F(P2PNotifierTest, NotificationsBasic) {
- ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES);
-
- p2p_notifier_.UpdateRegisteredIds(&mock_observer_,
- ModelTypeSetToObjectIdSet(enabled_types));
+ const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES);
EXPECT_CALL(mock_observer_, OnNotificationsEnabled());
EXPECT_CALL(mock_observer_, OnIncomingNotification(
ModelTypePayloadMapToObjectIdPayloadMap(MakePayloadMap(enabled_types)),
REMOTE_NOTIFICATION));
+ p2p_notifier_.UpdateRegisteredIds(&mock_observer_,
+ ModelTypeSetToObjectIdSet(enabled_types));
+
p2p_notifier_.SetUniqueId("sender");
const char kEmail[] = "foo@bar.com";
@@ -183,15 +184,9 @@ TEST_F(P2PNotifierTest, NotificationsBasic) {
// target settings. The notifications received by the observer should
// be consistent with the target settings.
TEST_F(P2PNotifierTest, SendNotificationData) {
- ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES, THEMES);
- ModelTypeSet changed_types(THEMES, APPS);
- ModelTypeSet expected_types(THEMES);
-
- p2p_notifier_.UpdateRegisteredIds(&mock_observer_,
- ModelTypeSetToObjectIdSet(enabled_types));
-
- const ModelTypePayloadMap& expected_payload_map =
- MakePayloadMap(expected_types);
+ const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES, THEMES);
+ const ModelTypeSet changed_types(THEMES, APPS);
+ const ModelTypeSet expected_types(THEMES);
EXPECT_CALL(mock_observer_, OnNotificationsEnabled());
EXPECT_CALL(mock_observer_,
@@ -200,6 +195,12 @@ TEST_F(P2PNotifierTest, SendNotificationData) {
MakePayloadMap(enabled_types)),
REMOTE_NOTIFICATION));
+ p2p_notifier_.UpdateRegisteredIds(&mock_observer_,
+ ModelTypeSetToObjectIdSet(enabled_types));
+
+ const ModelTypePayloadMap& expected_payload_map =
+ MakePayloadMap(expected_types);
+
p2p_notifier_.SetUniqueId("sender");
p2p_notifier_.UpdateCredentials("foo@bar.com", "fake_token");
diff --git a/sync/notifier/sync_notifier.h b/sync/notifier/sync_notifier.h
index 80b97b8..9191b90 100644
--- a/sync/notifier/sync_notifier.h
+++ b/sync/notifier/sync_notifier.h
@@ -22,12 +22,46 @@ class SyncNotifier {
SyncNotifier() {}
virtual ~SyncNotifier() {}
- // Updates the set of ObjectIds associated with a given |handler|.
- // Passing an empty ObjectIdSet will unregister |handler|.
- // There should be at most one handler registered per object id.
+ // Clients should follow the pattern below:
+ //
+ // When starting the client:
+ //
+ // notifier->RegisterHandler(client_handler);
+ //
+ // When the set of IDs to register changes for the client during its lifetime
+ // (i.e., between calls to RegisterHandler(client_handler) and
+ // UnregisterHandler(client_handler):
+ //
+ // notifier->UpdateRegisteredIds(client_handler, client_ids);
+ //
+ // When shutting down the client for browser shutdown:
+ //
+ // notifier->UnregisterHandler(client_handler);
+ //
+ // Note that there's no call to UpdateRegisteredIds() -- this is because the
+ // invalidation API persists registrations across browser restarts.
+ //
+ // When permanently shutting down the client, e.g. when disabling the related
+ // feature:
+ //
+ // notifier->UpdateRegisteredIds(client_handler, ObjectIdSet());
+ // notifier->UnregisterHandler(client_handler);
+
+ // Starts sending notifications to |handler|. |handler| must not be NULL,
+ // and it must already be registered.
+ virtual void RegisterHandler(SyncNotifierObserver* handler) = 0;
+
+ // Updates the set of ObjectIds associated with |handler|. |handler| must
+ // not be NULL, and must already be registered. An ID must be registered for
+ // at most one handler.
virtual void UpdateRegisteredIds(SyncNotifierObserver* handler,
const ObjectIdSet& ids) = 0;
+ // Stops sending notifications to |handler|. |handler| must not be NULL, and
+ // it must already be registered. Note that this doesn't unregister the IDs
+ // associated with |handler|.
+ virtual void UnregisterHandler(SyncNotifierObserver* handler) = 0;
+
// SetUniqueId must be called once, before any call to
// UpdateCredentials. |unique_id| should be a non-empty globally
// unique string.
diff --git a/sync/notifier/sync_notifier_factory_unittest.cc b/sync/notifier/sync_notifier_factory_unittest.cc
index 8036c1a..50d923b 100644
--- a/sync/notifier/sync_notifier_factory_unittest.cc
+++ b/sync/notifier/sync_notifier_factory_unittest.cc
@@ -61,8 +61,9 @@ TEST_F(SyncNotifierFactoryTest, Basic) {
#else
ASSERT_TRUE(notifier.get());
ObjectIdSet ids = ModelTypeSetToObjectIdSet(ModelTypeSet(syncer::BOOKMARKS));
+ notifier->RegisterHandler(&mock_observer_);
notifier->UpdateRegisteredIds(&mock_observer_, ids);
- notifier->UpdateRegisteredIds(&mock_observer_, ObjectIdSet());
+ notifier->UnregisterHandler(&mock_observer_);
#endif
}
@@ -79,8 +80,9 @@ TEST_F(SyncNotifierFactoryTest, Basic_P2P) {
#else
ASSERT_TRUE(notifier.get());
ObjectIdSet ids = ModelTypeSetToObjectIdSet(ModelTypeSet(syncer::BOOKMARKS));
+ notifier->RegisterHandler(&mock_observer_);
notifier->UpdateRegisteredIds(&mock_observer_, ids);
- notifier->UpdateRegisteredIds(&mock_observer_, ObjectIdSet());
+ notifier->UnregisterHandler(&mock_observer_);
#endif
}
diff --git a/sync/notifier/sync_notifier_helper.cc b/sync/notifier/sync_notifier_helper.cc
deleted file mode 100644
index af3b2ea..0000000
--- a/sync/notifier/sync_notifier_helper.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2012 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 "sync/notifier/sync_notifier_helper.h"
-
-#include "base/logging.h"
-
-namespace syncer {
-
-SyncNotifierHelper::SyncNotifierHelper() {}
-SyncNotifierHelper::~SyncNotifierHelper() {}
-
-ObjectIdSet SyncNotifierHelper::UpdateRegisteredIds(
- SyncNotifierObserver* handler, const ObjectIdSet& ids) {
- if (ids.empty()) {
- handlers_.RemoveObserver(handler);
- } else if (!handlers_.HasObserver(handler)) {
- handlers_.AddObserver(handler);
- }
-
- ObjectIdSet registered_ids(ids);
- // Remove all existing entries for |handler| and fill |registered_ids| with
- // the rest.
- for (ObjectIdHandlerMap::iterator it = id_to_handler_map_.begin();
- it != id_to_handler_map_.end(); ) {
- if (it->second == handler) {
- ObjectIdHandlerMap::iterator erase_it = it;
- ++it;
- id_to_handler_map_.erase(erase_it);
- } else {
- registered_ids.insert(it->first);
- ++it;
- }
- }
-
- // Now add the entries for |handler|. We keep track of the last insertion
- // point so we only traverse the map once to insert all the new entries.
- ObjectIdHandlerMap::iterator insert_it = id_to_handler_map_.begin();
- for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
- insert_it = id_to_handler_map_.insert(insert_it,
- std::make_pair(*it, handler));
- CHECK_EQ(handler, insert_it->second) << "Duplicate registration for "
- << ObjectIdToString(insert_it->first);
- }
- if (logging::DEBUG_MODE) {
- // The mapping shouldn't contain any handlers that aren't in |handlers_|.
- for (ObjectIdHandlerMap::const_iterator it = id_to_handler_map_.begin();
- it != id_to_handler_map_.end(); ++it) {
- CHECK(handlers_.HasObserver(it->second));
- }
- }
- return registered_ids;
-}
-
-void SyncNotifierHelper::DispatchInvalidationsToHandlers(
- const ObjectIdPayloadMap& id_payloads,
- IncomingNotificationSource source) {
- typedef std::map<SyncNotifierObserver*, ObjectIdPayloadMap> DispatchMap;
- DispatchMap dispatch_map;
- for (ObjectIdPayloadMap::const_iterator it = id_payloads.begin();
- it != id_payloads.end(); ++it) {
- ObjectIdHandlerMap::const_iterator find_it =
- id_to_handler_map_.find(it->first);
- // If we get an invalidation with a source type that we can't map to an
- // observer, just drop it--the observer was unregistered while the
- // invalidation was in flight.
- if (find_it == id_to_handler_map_.end())
- continue;
- dispatch_map[find_it->second].insert(*it);
- }
-
- if (handlers_.might_have_observers()) {
- ObserverListBase<SyncNotifierObserver>::Iterator it(handlers_);
- SyncNotifierObserver* handler = NULL;
- while ((handler = it.GetNext()) != NULL) {
- DispatchMap::const_iterator dispatch_it = dispatch_map.find(handler);
- if (dispatch_it != dispatch_map.end()) {
- handler->OnIncomingNotification(dispatch_it->second, source);
- }
- }
- }
-}
-
-void SyncNotifierHelper::EmitOnNotificationsEnabled() {
- FOR_EACH_OBSERVER(SyncNotifierObserver, handlers_, OnNotificationsEnabled());
-}
-
-void SyncNotifierHelper::EmitOnNotificationsDisabled(
- NotificationsDisabledReason reason) {
- FOR_EACH_OBSERVER(SyncNotifierObserver, handlers_,
- OnNotificationsDisabled(reason));
-}
-
-} // namespace syncer
diff --git a/sync/notifier/sync_notifier_helper.h b/sync/notifier/sync_notifier_helper.h
deleted file mode 100644
index 28ff187..0000000
--- a/sync/notifier/sync_notifier_helper.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2012 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 SYNC_NOTIFIER_SYNC_NOTIFIER_HELPER_H_
-#define SYNC_NOTIFIER_SYNC_NOTIFIER_HELPER_H_
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "base/observer_list.h"
-#include "sync/notifier/invalidation_util.h"
-#include "sync/notifier/object_id_payload_map.h"
-#include "sync/notifier/sync_notifier_observer.h"
-
-namespace syncer {
-
-// A helper class for classes that want to implement the SyncNotifier interface.
-// It helps keep track of registered handlers and which object ID registrations
-// are associated with which handlers, so implementors can just reuse the logic
-// here to dispatch invalidations and other interesting notifications.
-class SyncNotifierHelper {
- public:
- SyncNotifierHelper();
- ~SyncNotifierHelper();
-
- // Updates the set of ObjectIds associated with a given |handler|. Passing an
- // empty ObjectIdSet will unregister |handler|. The return value is an
- // ObjectIdSet containing values for all existing handlers.
- ObjectIdSet UpdateRegisteredIds(SyncNotifierObserver* handler,
- const ObjectIdSet& ids);
-
- // Helper that sorts incoming invalidations into a bucket for each handler
- // and then dispatches the batched invalidations to the corresponding handler.
- void DispatchInvalidationsToHandlers(const ObjectIdPayloadMap& id_payloads,
- IncomingNotificationSource source);
-
- // Calls the given handler method for each handler that has registered IDs.
- void EmitOnNotificationsEnabled();
- void EmitOnNotificationsDisabled(NotificationsDisabledReason reason);
-
- private:
- typedef std::map<invalidation::ObjectId,
- SyncNotifierObserver*,
- ObjectIdLessThan> ObjectIdHandlerMap;
-
- ObserverList<SyncNotifierObserver> handlers_;
- ObjectIdHandlerMap id_to_handler_map_;
-
- DISALLOW_COPY_AND_ASSIGN(SyncNotifierHelper);
-};
-
-} // namespace syncer
-
-#endif // SYNC_NOTIFIER_SYNC_NOTIFIER_HELPER_H_
diff --git a/sync/notifier/sync_notifier_helper_unittest.cc b/sync/notifier/sync_notifier_helper_unittest.cc
deleted file mode 100644
index 24d8871..0000000
--- a/sync/notifier/sync_notifier_helper_unittest.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2012 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 "google/cacheinvalidation/types.pb.h"
-#include "sync/notifier/sync_notifier_helper.h"
-#include "sync/notifier/mock_sync_notifier_observer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::StrictMock;
-
-namespace syncer {
-
-class SyncNotifierHelperTest : public testing::Test {
- protected:
- SyncNotifierHelperTest()
- : kObjectId1(ipc::invalidation::ObjectSource::TEST, "a"),
- kObjectId2(ipc::invalidation::ObjectSource::TEST, "b"),
- kObjectId3(ipc::invalidation::ObjectSource::TEST, "c") {
- }
-
- invalidation::ObjectId kObjectId1;
- invalidation::ObjectId kObjectId2;
- invalidation::ObjectId kObjectId3;
-};
-
-// Basic check that registrations are correctly updated for one handler.
-TEST_F(SyncNotifierHelperTest, Basic) {
- SyncNotifierHelper helper;
- StrictMock<MockSyncNotifierObserver> observer;
- ObjectIdSet ids;
- ids.insert(kObjectId1);
- ids.insert(kObjectId2);
- helper.UpdateRegisteredIds(&observer, ids);
-
- ObjectIdPayloadMap dispatched_payloads;
- dispatched_payloads[kObjectId1] = "1";
- dispatched_payloads[kObjectId2] = "2";
- dispatched_payloads[kObjectId3] = "3";
-
- // A object ID with no registration should be ignored.
- ObjectIdPayloadMap expected_payload1;
- expected_payload1[kObjectId1] = "1";
- expected_payload1[kObjectId2] = "2";
- EXPECT_CALL(observer, OnIncomingNotification(expected_payload1,
- REMOTE_NOTIFICATION));
- helper.DispatchInvalidationsToHandlers(dispatched_payloads,
- REMOTE_NOTIFICATION);
-
- // Removed object IDs should not be notified, newly-added ones should.
- ids.erase(kObjectId1);
- ids.insert(kObjectId3);
- helper.UpdateRegisteredIds(&observer, ids);
-
- ObjectIdPayloadMap expected_payload2;
- expected_payload2[kObjectId2] = "2";
- expected_payload2[kObjectId3] = "3";
- EXPECT_CALL(observer, OnIncomingNotification(expected_payload2,
- REMOTE_NOTIFICATION));
- helper.DispatchInvalidationsToHandlers(dispatched_payloads,
- REMOTE_NOTIFICATION);
-}
-
-// Tests that we correctly bucket and dispatch invalidations on multiple objects
-// to the corresponding handlers.
-TEST_F(SyncNotifierHelperTest, MultipleHandlers) {
- SyncNotifierHelper helper;
- StrictMock<MockSyncNotifierObserver> observer;
- ObjectIdSet ids;
- ids.insert(kObjectId1);
- ids.insert(kObjectId2);
- helper.UpdateRegisteredIds(&observer, ids);
- StrictMock<MockSyncNotifierObserver> observer2;
- ObjectIdSet ids2;
- ids2.insert(kObjectId3);
- helper.UpdateRegisteredIds(&observer2, ids2);
-
- ObjectIdPayloadMap expected_payload1;
- expected_payload1[kObjectId1] = "1";
- expected_payload1[kObjectId2] = "2";
- EXPECT_CALL(observer, OnIncomingNotification(expected_payload1,
- REMOTE_NOTIFICATION));
- ObjectIdPayloadMap expected_payload2;
- expected_payload2[kObjectId3] = "3";
- EXPECT_CALL(observer2, OnIncomingNotification(expected_payload2,
- REMOTE_NOTIFICATION));
-
- ObjectIdPayloadMap dispatched_payloads;
- dispatched_payloads[kObjectId1] = "1";
- dispatched_payloads[kObjectId2] = "2";
- dispatched_payloads[kObjectId3] = "3";
- helper.DispatchInvalidationsToHandlers(dispatched_payloads,
- REMOTE_NOTIFICATION);
-
- // Also verify that the callbacks for OnNotificationsEnabled/Disabled work.
- EXPECT_CALL(observer, OnNotificationsEnabled());
- EXPECT_CALL(observer2, OnNotificationsEnabled());
- EXPECT_CALL(observer,
- OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
- EXPECT_CALL(observer2,
- OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
- helper.EmitOnNotificationsEnabled();
- helper.EmitOnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR);
-}
-
-// Multiple registrations by different handlers on the same object ID should
-// cause a CHECK.
-TEST_F(SyncNotifierHelperTest, MultipleRegistration) {
- SyncNotifierHelper helper;
- StrictMock<MockSyncNotifierObserver> observer;
- ObjectIdSet ids;
- ids.insert(kObjectId1);
- ids.insert(kObjectId2);
- helper.UpdateRegisteredIds(&observer, ids);
-
- StrictMock<MockSyncNotifierObserver> observer2;
- EXPECT_DEATH({ helper.UpdateRegisteredIds(&observer2, ids); },
- "Duplicate registration for .*");
-}
-
-// Make sure that passing an empty set to UpdateRegisteredIds clears the
-// corresponding entries for the handler.
-TEST_F(SyncNotifierHelperTest, EmptySetUnregisters) {
- SyncNotifierHelper helper;
- StrictMock<MockSyncNotifierObserver> observer;
- ObjectIdSet ids;
- ids.insert(kObjectId1);
- ids.insert(kObjectId2);
- helper.UpdateRegisteredIds(&observer, ids);
- // Control observer.
- StrictMock<MockSyncNotifierObserver> observer2;
- ObjectIdSet ids2;
- ids2.insert(kObjectId3);
- helper.UpdateRegisteredIds(&observer2, ids2);
- // Unregister the first observer. It should not receive any further callbacks.
- helper.UpdateRegisteredIds(&observer, ObjectIdSet());
-
- ObjectIdPayloadMap expected_payload2;
- expected_payload2[kObjectId3] = "3";
- EXPECT_CALL(observer2, OnIncomingNotification(expected_payload2,
- REMOTE_NOTIFICATION));
- EXPECT_CALL(observer2, OnNotificationsEnabled());
- EXPECT_CALL(observer2,
- OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
-
- ObjectIdPayloadMap dispatched_payloads;
- dispatched_payloads[kObjectId1] = "1";
- dispatched_payloads[kObjectId2] = "2";
- dispatched_payloads[kObjectId3] = "3";
- helper.DispatchInvalidationsToHandlers(dispatched_payloads,
- REMOTE_NOTIFICATION);
- helper.EmitOnNotificationsEnabled();
- helper.EmitOnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR);
-}
-
-} // namespace syncer
diff --git a/sync/notifier/sync_notifier_registrar.cc b/sync/notifier/sync_notifier_registrar.cc
new file mode 100644
index 0000000..02c00b6
--- /dev/null
+++ b/sync/notifier/sync_notifier_registrar.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2012 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 "sync/notifier/sync_notifier_registrar.h"
+
+#include <cstddef>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace syncer {
+
+SyncNotifierRegistrar::SyncNotifierRegistrar() {}
+
+SyncNotifierRegistrar::~SyncNotifierRegistrar() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void SyncNotifierRegistrar::RegisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CHECK(handler);
+ CHECK(!handlers_.HasObserver(handler));
+ handlers_.AddObserver(handler);
+}
+
+void SyncNotifierRegistrar::UpdateRegisteredIds(
+ SyncNotifierObserver* handler,
+ const ObjectIdSet& ids) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CHECK(handler);
+ CHECK(handlers_.HasObserver(handler));
+ // Remove all existing entries for |handler|.
+ for (IdHandlerMap::iterator it = id_to_handler_map_.begin();
+ it != id_to_handler_map_.end(); ) {
+ if (it->second == handler) {
+ IdHandlerMap::iterator erase_it = it;
+ ++it;
+ id_to_handler_map_.erase(erase_it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Now add the entries for |handler|. We keep track of the last insertion
+ // point so we only traverse the map once to insert all the new entries.
+ IdHandlerMap::iterator insert_it = id_to_handler_map_.begin();
+ for (ObjectIdSet::const_iterator it = ids.begin(); it != ids.end(); ++it) {
+ insert_it =
+ id_to_handler_map_.insert(insert_it, std::make_pair(*it, handler));
+ CHECK_EQ(handler, insert_it->second)
+ << "Duplicate registration: trying to register "
+ << ObjectIdToString(insert_it->first) << " for "
+ << handler << " when it's already registered for "
+ << insert_it->second;
+ }
+}
+
+void SyncNotifierRegistrar::UnregisterHandler(SyncNotifierObserver* handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CHECK(handler);
+ CHECK(handlers_.HasObserver(handler));
+ handlers_.RemoveObserver(handler);
+}
+
+ObjectIdSet SyncNotifierRegistrar::GetAllRegisteredIds() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ObjectIdSet registered_ids;
+ for (IdHandlerMap::const_iterator it = id_to_handler_map_.begin();
+ it != id_to_handler_map_.end(); ++it) {
+ registered_ids.insert(it->first);
+ }
+ return registered_ids;
+}
+
+void SyncNotifierRegistrar::DispatchInvalidationsToHandlers(
+ const ObjectIdPayloadMap& id_payloads,
+ IncomingNotificationSource source) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // If we have no handlers, there's nothing to do.
+ if (!handlers_.might_have_observers()) {
+ return;
+ }
+
+ typedef std::map<SyncNotifierObserver*, ObjectIdPayloadMap> DispatchMap;
+ DispatchMap dispatch_map;
+ for (ObjectIdPayloadMap::const_iterator it = id_payloads.begin();
+ it != id_payloads.end(); ++it) {
+ SyncNotifierObserver* const handler = ObjectIdToHandler(it->first);
+ // Filter out invalidations for IDs with no handler.
+ if (handler)
+ dispatch_map[handler].insert(*it);
+ }
+
+ // Emit invalidations only for handlers in |handlers_|.
+ ObserverListBase<SyncNotifierObserver>::Iterator it(handlers_);
+ SyncNotifierObserver* handler = NULL;
+ while ((handler = it.GetNext()) != NULL) {
+ DispatchMap::const_iterator dispatch_it = dispatch_map.find(handler);
+ if (dispatch_it != dispatch_map.end())
+ handler->OnIncomingNotification(dispatch_it->second, source);
+ }
+}
+
+void SyncNotifierRegistrar::EmitOnNotificationsEnabled() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ FOR_EACH_OBSERVER(SyncNotifierObserver, handlers_, OnNotificationsEnabled());
+}
+
+void SyncNotifierRegistrar::EmitOnNotificationsDisabled(
+ NotificationsDisabledReason reason) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ FOR_EACH_OBSERVER(SyncNotifierObserver, handlers_,
+ OnNotificationsDisabled(reason));
+}
+
+void SyncNotifierRegistrar::DetachFromThreadForTest() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ thread_checker_.DetachFromThread();
+}
+
+SyncNotifierObserver* SyncNotifierRegistrar::ObjectIdToHandler(
+ const invalidation::ObjectId& id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ IdHandlerMap::const_iterator it = id_to_handler_map_.find(id);
+ return (it == id_to_handler_map_.end()) ? NULL : it->second;
+}
+
+} // namespace syncer
diff --git a/sync/notifier/sync_notifier_registrar.h b/sync/notifier/sync_notifier_registrar.h
new file mode 100644
index 0000000..949e3ca
--- /dev/null
+++ b/sync/notifier/sync_notifier_registrar.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 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 SYNC_NOTIFIER_SYNC_NOTIFIER_REGISTRAR_H_
+#define SYNC_NOTIFIER_SYNC_NOTIFIER_REGISTRAR_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "sync/notifier/invalidation_util.h"
+#include "sync/notifier/object_id_payload_map.h"
+#include "sync/notifier/sync_notifier_observer.h"
+
+namespace invalidation {
+class ObjectId;
+} // namespace invalidation
+
+namespace syncer {
+
+// A helper class for implementations of the SyncNotifier interface. It helps
+// keep track of registered handlers and which object ID registrations are
+// associated with which handlers, so implementors can just reuse the logic
+// here to dispatch invalidations and other interesting notifications.
+class SyncNotifierRegistrar {
+ public:
+ SyncNotifierRegistrar();
+ ~SyncNotifierRegistrar();
+
+ // Starts sending notifications to |handler|. |handler| must not be NULL,
+ // and it must already be registered.
+ void RegisterHandler(SyncNotifierObserver* handler);
+
+
+ // Updates the set of ObjectIds associated with |handler|. |handler| must
+ // not be NULL, and must already be registered. An ID must be registered for
+ // at most one handler.
+ void UpdateRegisteredIds(SyncNotifierObserver* handler,
+ const ObjectIdSet& ids);
+
+ // Stops sending notifications to |handler|. |handler| must not be NULL, and
+ // it must already be registered. Note that this doesn't unregister the IDs
+ // associated with |handler|.
+ void UnregisterHandler(SyncNotifierObserver* handler);
+
+ // Returns the set of all IDs that are registered to some handler (even
+ // handlers that have been unregistered).
+ ObjectIdSet GetAllRegisteredIds() const;
+
+ // Sorts incoming invalidations into a bucket for each handler and then
+ // dispatches the batched invalidations to the corresponding handler.
+ // Invalidations for IDs with no corresponding handler are dropped, as are
+ // invalidations for handlers that are not added.
+ void DispatchInvalidationsToHandlers(const ObjectIdPayloadMap& id_payloads,
+ IncomingNotificationSource source);
+
+ // Calls the given handler method for each handler that has registered IDs.
+ void EmitOnNotificationsEnabled();
+ void EmitOnNotificationsDisabled(NotificationsDisabledReason reason);
+
+ // Needed for death tests.
+ void DetachFromThreadForTest();
+
+ private:
+ typedef std::map<invalidation::ObjectId, SyncNotifierObserver*,
+ ObjectIdLessThan>
+ IdHandlerMap;
+
+ SyncNotifierObserver* ObjectIdToHandler(const invalidation::ObjectId& id);
+
+ base::ThreadChecker thread_checker_;
+ ObserverList<SyncNotifierObserver> handlers_;
+ IdHandlerMap id_to_handler_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncNotifierRegistrar);
+};
+
+} // namespace syncer
+
+#endif // SYNC_NOTIFIER_SYNC_NOTIFIER_REGISTRAR_H_
diff --git a/sync/notifier/sync_notifier_registrar_unittest.cc b/sync/notifier/sync_notifier_registrar_unittest.cc
new file mode 100644
index 0000000..b30252b
--- /dev/null
+++ b/sync/notifier/sync_notifier_registrar_unittest.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2012 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 "google/cacheinvalidation/types.pb.h"
+#include "sync/notifier/mock_sync_notifier_observer.h"
+#include "sync/notifier/sync_notifier_registrar.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using testing::InSequence;
+using testing::Mock;
+using testing::StrictMock;
+
+class SyncNotifierRegistrarTest : public testing::Test {
+ protected:
+ SyncNotifierRegistrarTest()
+ : kObjectId1(ipc::invalidation::ObjectSource::TEST, "a"),
+ kObjectId2(ipc::invalidation::ObjectSource::TEST, "b"),
+ kObjectId3(ipc::invalidation::ObjectSource::TEST, "c"),
+ kObjectId4(ipc::invalidation::ObjectSource::TEST, "d") {
+ }
+
+ const invalidation::ObjectId kObjectId1;
+ const invalidation::ObjectId kObjectId2;
+ const invalidation::ObjectId kObjectId3;
+ const invalidation::ObjectId kObjectId4;
+};
+
+// Register a handler, register some IDs for that handler, and then unregister
+// the handler, dispatching invalidations in between. The handler should only
+// see invalidations when its registered and its IDs are registered.
+TEST_F(SyncNotifierRegistrarTest, Basic) {
+ StrictMock<MockSyncNotifierObserver> handler;
+
+ SyncNotifierRegistrar registrar;
+
+ registrar.RegisterHandler(&handler);
+
+ ObjectIdPayloadMap payloads;
+ payloads[kObjectId1] = "1";
+ payloads[kObjectId2] = "2";
+ payloads[kObjectId3] = "3";
+
+ // Should be ignored since no IDs are registered to |handler|.
+ registrar.DispatchInvalidationsToHandlers(payloads, REMOTE_NOTIFICATION);
+
+ Mock::VerifyAndClearExpectations(&handler);
+
+ ObjectIdSet ids;
+ ids.insert(kObjectId1);
+ ids.insert(kObjectId2);
+ registrar.UpdateRegisteredIds(&handler, ids);
+
+ {
+ ObjectIdPayloadMap expected_payloads;
+ expected_payloads[kObjectId1] = "1";
+ expected_payloads[kObjectId2] = "2";
+ EXPECT_CALL(handler, OnIncomingNotification(expected_payloads,
+ REMOTE_NOTIFICATION));
+ }
+
+ registrar.DispatchInvalidationsToHandlers(payloads, REMOTE_NOTIFICATION);
+
+ Mock::VerifyAndClearExpectations(&handler);
+
+ ids.erase(kObjectId1);
+ ids.insert(kObjectId3);
+ registrar.UpdateRegisteredIds(&handler, ids);
+
+ {
+ ObjectIdPayloadMap expected_payloads;
+ expected_payloads[kObjectId2] = "2";
+ expected_payloads[kObjectId3] = "3";
+ EXPECT_CALL(handler, OnIncomingNotification(expected_payloads,
+ REMOTE_NOTIFICATION));
+ }
+
+ // Removed object IDs should not be notified, newly-added ones should.
+ registrar.DispatchInvalidationsToHandlers(payloads, REMOTE_NOTIFICATION);
+
+ Mock::VerifyAndClearExpectations(&handler);
+
+ registrar.UnregisterHandler(&handler);
+
+ // Should be ignored since |handler| isn't registered anymore.
+ registrar.DispatchInvalidationsToHandlers(payloads, REMOTE_NOTIFICATION);
+}
+
+// Register handlers and some IDs for those handlers, register a handler with
+// no IDs, and register a handler with some IDs but unregister it. Then,
+// dispatch some notifications and invalidations. Handlers that are registered
+// should get notifications, and the ones that have registered IDs should
+// receive invalidations for those IDs.
+TEST_F(SyncNotifierRegistrarTest, MultipleHandlers) {
+ StrictMock<MockSyncNotifierObserver> handler1;
+ EXPECT_CALL(handler1, OnNotificationsEnabled());
+ {
+ ObjectIdPayloadMap expected_payloads;
+ expected_payloads[kObjectId1] = "1";
+ expected_payloads[kObjectId2] = "2";
+ EXPECT_CALL(handler1, OnIncomingNotification(expected_payloads,
+ REMOTE_NOTIFICATION));
+ }
+ EXPECT_CALL(handler1,
+ OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
+
+ StrictMock<MockSyncNotifierObserver> handler2;
+ EXPECT_CALL(handler2, OnNotificationsEnabled());
+ {
+ ObjectIdPayloadMap expected_payloads;
+ expected_payloads[kObjectId3] = "3";
+ EXPECT_CALL(handler2, OnIncomingNotification(expected_payloads,
+ REMOTE_NOTIFICATION));
+ }
+ EXPECT_CALL(handler2,
+ OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
+
+ StrictMock<MockSyncNotifierObserver> handler3;
+ EXPECT_CALL(handler3, OnNotificationsEnabled());
+ EXPECT_CALL(handler3,
+ OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
+
+ StrictMock<MockSyncNotifierObserver> handler4;
+
+ SyncNotifierRegistrar registrar;
+
+ registrar.RegisterHandler(&handler1);
+ registrar.RegisterHandler(&handler2);
+ registrar.RegisterHandler(&handler3);
+ registrar.RegisterHandler(&handler4);
+
+ {
+ ObjectIdSet ids;
+ ids.insert(kObjectId1);
+ ids.insert(kObjectId2);
+ registrar.UpdateRegisteredIds(&handler1, ids);
+ }
+
+ {
+ ObjectIdSet ids;
+ ids.insert(kObjectId3);
+ registrar.UpdateRegisteredIds(&handler2, ids);
+ }
+
+ // Don't register any IDs for handler3.
+
+ {
+ ObjectIdSet ids;
+ ids.insert(kObjectId4);
+ registrar.UpdateRegisteredIds(&handler4, ids);
+ }
+
+ registrar.UnregisterHandler(&handler4);
+
+ registrar.EmitOnNotificationsEnabled();
+ {
+ ObjectIdPayloadMap payloads;
+ payloads[kObjectId1] = "1";
+ payloads[kObjectId2] = "2";
+ payloads[kObjectId3] = "3";
+ payloads[kObjectId4] = "4";
+ registrar.DispatchInvalidationsToHandlers(payloads, REMOTE_NOTIFICATION);
+ }
+ registrar.EmitOnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR);
+}
+
+// Multiple registrations by different handlers on the same object ID should
+// cause a CHECK.
+TEST_F(SyncNotifierRegistrarTest, MultipleRegistration) {
+ SyncNotifierRegistrar registrar;
+
+ StrictMock<MockSyncNotifierObserver> handler1;
+ registrar.RegisterHandler(&handler1);
+
+ MockSyncNotifierObserver handler2;
+ registrar.RegisterHandler(&handler2);
+
+ ObjectIdSet ids;
+ ids.insert(kObjectId1);
+ ids.insert(kObjectId2);
+ registrar.UpdateRegisteredIds(&handler1, ids);
+
+ registrar.DetachFromThreadForTest();
+ EXPECT_DEATH({ registrar.UpdateRegisteredIds(&handler2, ids); },
+ "Duplicate registration: .*");
+}
+
+// Make sure that passing an empty set to UpdateRegisteredIds clears the
+// corresponding entries for the handler.
+TEST_F(SyncNotifierRegistrarTest, EmptySetUnregisters) {
+ StrictMock<MockSyncNotifierObserver> handler1;
+ EXPECT_CALL(handler1, OnNotificationsEnabled());
+ EXPECT_CALL(handler1,
+ OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
+
+ // Control observer.
+ StrictMock<MockSyncNotifierObserver> handler2;
+ EXPECT_CALL(handler2, OnNotificationsEnabled());
+ {
+ ObjectIdPayloadMap expected_payloads;
+ expected_payloads[kObjectId3] = "3";
+ EXPECT_CALL(handler2, OnIncomingNotification(expected_payloads,
+ REMOTE_NOTIFICATION));
+ }
+ EXPECT_CALL(handler2,
+ OnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR));
+
+ SyncNotifierRegistrar registrar;
+
+ registrar.RegisterHandler(&handler1);
+ registrar.RegisterHandler(&handler2);
+
+ {
+ ObjectIdSet ids;
+ ids.insert(kObjectId1);
+ ids.insert(kObjectId2);
+ registrar.UpdateRegisteredIds(&handler1, ids);
+ }
+
+ {
+ ObjectIdSet ids;
+ ids.insert(kObjectId3);
+ registrar.UpdateRegisteredIds(&handler2, ids);
+ }
+
+ // Unregister the IDs for the first observer. It should not receive any
+ // further invalidations.
+ registrar.UpdateRegisteredIds(&handler1, ObjectIdSet());
+
+ registrar.EmitOnNotificationsEnabled();
+ {
+ ObjectIdPayloadMap payloads;
+ payloads[kObjectId1] = "1";
+ payloads[kObjectId2] = "2";
+ payloads[kObjectId3] = "3";
+ registrar.DispatchInvalidationsToHandlers(payloads,
+ REMOTE_NOTIFICATION);
+ }
+ registrar.EmitOnNotificationsDisabled(TRANSIENT_NOTIFICATION_ERROR);
+}
+
+} // namespace
+
+} // namespace syncer
diff --git a/sync/sync.gyp b/sync/sync.gyp
index 3583c87..d559f1b 100644
--- a/sync/sync.gyp
+++ b/sync/sync.gyp
@@ -257,9 +257,9 @@
'notifier/sync_notifier.h',
'notifier/sync_notifier_factory.cc',
'notifier/sync_notifier_factory.h',
- 'notifier/sync_notifier_helper.cc',
- 'notifier/sync_notifier_helper.h',
'notifier/sync_notifier_observer.h',
+ 'notifier/sync_notifier_registrar.cc',
+ 'notifier/sync_notifier_registrar.h',
],
'conditions': [
['OS != "android"', {
@@ -654,7 +654,7 @@
'notifier/p2p_notifier_unittest.cc',
'notifier/push_client_channel_unittest.cc',
'notifier/registration_manager_unittest.cc',
- 'notifier/sync_notifier_helper_unittest.cc',
+ 'notifier/sync_notifier_registrar_unittest.cc',
],
}],
],
diff --git a/sync/tools/sync_listen_notifications.cc b/sync/tools/sync_listen_notifications.cc
index 5eb05b6..6d8adb0 100644
--- a/sync/tools/sync_listen_notifications.cc
+++ b/sync/tools/sync_listen_notifications.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <cstddef>
#include <cstdio>
#include <string>
@@ -242,13 +243,15 @@ int SyncListenNotificationsMain(int argc, char* argv[]) {
const char kUniqueId[] = "fake_unique_id";
sync_notifier->SetUniqueId(kUniqueId);
sync_notifier->UpdateCredentials(email, token);
+
// Listen for notifications for all known types.
+ sync_notifier->RegisterHandler(&notification_printer);
sync_notifier->UpdateRegisteredIds(
&notification_printer, ModelTypeSetToObjectIdSet(ModelTypeSet::All()));
ui_loop.Run();
- sync_notifier->UpdateRegisteredIds(&notification_printer, ObjectIdSet());
+ sync_notifier->UnregisterHandler(&notification_printer);
io_thread.Stop();
return 0;
}