diff options
35 files changed, 898 insertions, 96 deletions
diff --git a/chrome/browser/sync/engine/auth_watcher_unittest.cc b/chrome/browser/sync/engine/auth_watcher_unittest.cc index 0e02f70..8de43e0 100644 --- a/chrome/browser/sync/engine/auth_watcher_unittest.cc +++ b/chrome/browser/sync/engine/auth_watcher_unittest.cc @@ -89,7 +89,8 @@ class AuthWatcherTest : public testing::Test { FilePath user_settings_path = temp_dir_.path().Append(kUserSettingsDB); user_settings_->Init(user_settings_path); gaia_auth_ = new GaiaAuthMockForAuthWatcher(); - talk_mediator_.reset(new TalkMediatorImpl()); + talk_mediator_.reset(new TalkMediatorImpl( + browser_sync::kDefaultNotificationMethod)); auth_watcher_ = new AuthWatcher(metadb_.manager(), connection_.get(), allstatus_.get(), kTestUserAgent, kTestServiceId, kTestGaiaURL, user_settings_.get(), gaia_auth_, talk_mediator_.get()); diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 4ae26a2..0fa7362 100755 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -918,7 +918,8 @@ class SyncManager::SyncInternal { bool attempt_last_user_authentication, bool invalidate_last_user_auth_token, const char* user_agent, - const std::string& lsid); + const std::string& lsid, + browser_sync::NotificationMethod notification_method); // Tell sync engine to submit credentials to GAIA for verification and start // the syncing process on success. Successful GAIA authentication will kick @@ -1159,7 +1160,8 @@ bool SyncManager::Init(const FilePath& database_location, bool attempt_last_user_authentication, bool invalidate_last_user_auth_token, const char* user_agent, - const char* lsid) { + const char* lsid, + browser_sync::NotificationMethod notification_method) { DCHECK(post_factory); string server_string(sync_server_and_path); @@ -1175,7 +1177,8 @@ bool SyncManager::Init(const FilePath& database_location, attempt_last_user_authentication, invalidate_last_user_auth_token, user_agent, - lsid); + lsid, + notification_method); } void SyncManager::Authenticate(const char* username, const char* password, @@ -1201,8 +1204,8 @@ bool SyncManager::SyncInternal::Init( bool attempt_last_user_authentication, bool invalidate_last_user_auth_token, const char* user_agent, - const std::string& lsid) { - + const std::string& lsid, + browser_sync::NotificationMethod notification_method) { // Set up UserSettings, creating the db if necessary. We need this to // instantiate a URLFactory to give to the Syncer. FilePath settings_db_file = @@ -1254,7 +1257,7 @@ bool SyncManager::SyncInternal::Init( const char* service_id = gaia_service_id ? gaia_service_id : SYNC_SERVICE_NAME; - talk_mediator_.reset(new TalkMediatorImpl()); + talk_mediator_.reset(new TalkMediatorImpl(notification_method)); allstatus()->WatchTalkMediator(talk_mediator()); BridgedGaiaAuthenticator* gaia_auth = new BridgedGaiaAuthenticator( diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index 96b869b..5307f7b 100755 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -46,6 +46,7 @@ #include "base/scoped_ptr.h" #include "build/build_config.h" #include "chrome/browser/google_service_auth_error.h" +#include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/syncable/model_type.h" #include "googleurl/src/gurl.h" @@ -592,7 +593,8 @@ class SyncManager { bool attempt_last_user_authentication, bool invalidate_last_user_auth_token, const char* user_agent, - const char* lsid); + const char* lsid, + browser_sync::NotificationMethod notification_method); // Returns the username last used for a successful authentication. // Returns empty if there is no such username. diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index 7206b1c..c72567a 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -44,7 +44,8 @@ void SyncBackendHost::Initialize( URLRequestContextGetter* baseline_context_getter, const std::string& lsid, bool delete_sync_data_folder, - bool invalidate_sync_login) { + bool invalidate_sync_login, + NotificationMethod notification_method) { if (!core_thread_.Start()) return; @@ -64,12 +65,14 @@ void SyncBackendHost::Initialize( core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, - sync_service_url, true, - new HttpBridgeFactory(baseline_context_getter), - new HttpBridgeFactory(baseline_context_getter), - lsid, - delete_sync_data_folder, - invalidate_sync_login)); + Core::DoInitializeOptions( + sync_service_url, true, + new HttpBridgeFactory(baseline_context_getter), + new HttpBridgeFactory(baseline_context_getter), + lsid, + delete_sync_data_folder, + invalidate_sync_login, + notification_method))); } void SyncBackendHost::Authenticate(const std::string& username, @@ -232,19 +235,12 @@ std::string MakeUserAgentForSyncapi() { return user_agent; } -void SyncBackendHost::Core::DoInitialize( - const GURL& service_url, - bool attempt_last_user_authentication, - sync_api::HttpPostProviderFactory* http_provider_factory, - sync_api::HttpPostProviderFactory* auth_http_provider_factory, - const std::string& lsid, - bool delete_sync_data_folder, - bool invalidate_sync_login) { +void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); // Blow away the partial or corrupt sync data folder before doing any more // initialization, if necessary. - if (delete_sync_data_folder) + if (options.delete_sync_data_folder) DeleteSyncDataFolder(); // Make sure that the directory exists before initializing the backend. @@ -255,18 +251,19 @@ void SyncBackendHost::Core::DoInitialize( syncapi_->SetObserver(this); const FilePath& path_str = host_->sync_data_folder_path(); success = syncapi_->Init(path_str, - (service_url.host() + service_url.path()).c_str(), - service_url.EffectiveIntPort(), + (options.service_url.host() + options.service_url.path()).c_str(), + options.service_url.EffectiveIntPort(), kGaiaServiceId, kGaiaSourceForChrome, - service_url.SchemeIsSecure(), - http_provider_factory, - auth_http_provider_factory, + options.service_url.SchemeIsSecure(), + options.http_bridge_factory, + options.auth_http_bridge_factory, host_, // ModelSafeWorkerRegistrar. - attempt_last_user_authentication, - invalidate_sync_login, + options.attempt_last_user_authentication, + options.invalidate_sync_login, MakeUserAgentForSyncapi().c_str(), - lsid.c_str()); + options.lsid.c_str(), + options.notification_method); DCHECK(success) << "Syncapi initialization failed!"; } diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index c7c145b..fe832c5 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -17,6 +17,7 @@ #include "base/timer.h" #include "chrome/browser/google_service_auth_error.h" #include "chrome/browser/net/url_request_context_getter.h" +#include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/engine/model_safe_worker.h" #include "chrome/browser/sync/glue/ui_model_worker.h" @@ -82,7 +83,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { URLRequestContextGetter* baseline_context_getter, const std::string& lsid, bool delete_sync_data_folder, - bool invalidate_sync_login); + bool invalidate_sync_login, + NotificationMethod notification_method); // Called on |frontend_loop_| to kick off asynchronous authentication. void Authenticate(const std::string& username, const std::string& password, @@ -133,7 +135,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void InitializeForTestMode(const std::wstring& test_user, sync_api::HttpPostProviderFactory* factory, sync_api::HttpPostProviderFactory* auth_factory, - bool delete_sync_data_folder) { + bool delete_sync_data_folder, + NotificationMethod notification_method) { if (!core_thread_.Start()) return; registrar_.workers[GROUP_UI] = new UIModelWorker(frontend_loop_); @@ -145,7 +148,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { test_user, factory, auth_factory, - delete_sync_data_folder)); + delete_sync_data_folder, + notification_method)); } #endif @@ -167,6 +171,35 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { virtual void OnInitializationComplete(); virtual void OnAuthError(const GoogleServiceAuthError& auth_error); + struct DoInitializeOptions { + DoInitializeOptions( + const GURL& service_url, + bool attempt_last_user_authentication, + sync_api::HttpPostProviderFactory* http_bridge_factory, + sync_api::HttpPostProviderFactory* auth_http_bridge_factory, + const std::string& lsid, + bool delete_sync_data_folder, + bool invalidate_sync_login, + NotificationMethod notification_method) + : service_url(service_url), + attempt_last_user_authentication(attempt_last_user_authentication), + http_bridge_factory(http_bridge_factory), + auth_http_bridge_factory(auth_http_bridge_factory), + lsid(lsid), + delete_sync_data_folder(delete_sync_data_folder), + invalidate_sync_login(invalidate_sync_login), + notification_method(notification_method) {} + + GURL service_url; + bool attempt_last_user_authentication; + sync_api::HttpPostProviderFactory* http_bridge_factory; + sync_api::HttpPostProviderFactory* auth_http_bridge_factory; + std::string lsid; + bool delete_sync_data_folder; + bool invalidate_sync_login; + NotificationMethod notification_method; + }; + // Note: // // The Do* methods are the various entry points from our SyncBackendHost. @@ -175,13 +208,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // // Called on the SyncBackendHost core_thread_ to perform initialization // of the syncapi on behalf of SyncBackendHost::Initialize. - void DoInitialize(const GURL& service_url, - bool attempt_last_user_authentication, - sync_api::HttpPostProviderFactory* http_bridge_factory, - sync_api::HttpPostProviderFactory* auth_http_bridge_factory, - const std::string& lsid, - bool delete_sync_data_folder, - bool invalidate_sync_login); + void DoInitialize(const DoInitializeOptions& options); // Called on our SyncBackendHost's core_thread_ to perform authentication // on behalf of SyncBackendHost::Authenticate. @@ -218,9 +245,12 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void DoInitializeForTest(const std::wstring& test_user, sync_api::HttpPostProviderFactory* factory, sync_api::HttpPostProviderFactory* auth_factory, - bool delete_sync_data_folder) { - DoInitialize(GURL(), false, factory, auth_factory, std::string(), - delete_sync_data_folder, false); + bool delete_sync_data_folder, + NotificationMethod notification_method) { + DoInitialize( + DoInitializeOptions(GURL(), false, factory, auth_factory, + std::string(), delete_sync_data_folder, false, + notification_method)); syncapi_->SetupForTestMode(test_user); } #endif diff --git a/chrome/browser/sync/notification_method.cc b/chrome/browser/sync/notification_method.cc new file mode 100644 index 0000000..28f6c15 --- /dev/null +++ b/chrome/browser/sync/notification_method.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notification_method.h" + +#include "base/logging.h" + +namespace browser_sync { + +// TODO(akalin): Eventually change this to NOTIFICATION_TRANSITIONAL, +// then NOTIFICATION_NEW. +const NotificationMethod kDefaultNotificationMethod = NOTIFICATION_LEGACY; + +std::string NotificationMethodToString( + NotificationMethod notification_method) { + switch (notification_method) { + case NOTIFICATION_LEGACY: + return "NOTIFICATION_LEGACY"; + break; + case NOTIFICATION_TRANSITIONAL: + return "NOTIFICATION_TRANSITIONAL"; + break; + case NOTIFICATION_NEW: + return "NOTIFICATION_NEW"; + break; + default: + LOG(WARNING) << "Unknown value for notification method: " + << notification_method; + break; + } + return "<unknown notification method>"; +} + +NotificationMethod StringToNotificationMethod(const std::string& str) { + if (str == "legacy") { + return NOTIFICATION_LEGACY; + } else if (str == "transitional") { + return NOTIFICATION_TRANSITIONAL; + } else if (str == "new") { + return NOTIFICATION_NEW; + } + LOG(WARNING) << "Unknown notification method \"" << str + << "\"; using method " + << NotificationMethodToString(kDefaultNotificationMethod); + return kDefaultNotificationMethod; +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/notification_method.h b/chrome/browser/sync/notification_method.h new file mode 100644 index 0000000..569f8d2 --- /dev/null +++ b/chrome/browser/sync/notification_method.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_SYNC_NOTIFICATION_METHOD_H_ +#define CHROME_BROWSER_SYNC_NOTIFICATION_METHOD_H_ + +#include <string> + +namespace browser_sync { + +// This is the matrix for the interaction between clients with +// different notification methods: +// +// Listen +// L T N +// +-------+ +// L | E E E | +// Send T | Y Y Y | +// N | E Y Y | +// +-------+ +// +// 'Y' means a client listening with the column notification method +// will receive notifications from a client sending with the row +// notification method. 'E' means means that the notification will be +// an empty one, which may be dropped by the server in the future. + +enum NotificationMethod { + // Old, broken notification method. Works only if notification + // servers don't drop empty notifications. + NOTIFICATION_LEGACY, + // Compatible with new notifications. Also compatible with legacy + // notifications if the notification servers don't drop empty + // notifications. + NOTIFICATION_TRANSITIONAL, + // New, ideal notification method. Compatible only with + // transitional notifications. + NOTIFICATION_NEW, +}; + +extern const NotificationMethod kDefaultNotificationMethod; + +std::string NotificationMethodToString( + NotificationMethod notification_method); + +// If the given string is not one of "legacy", "transitional", or +// "new", returns kDefaultNotificationMethod. +NotificationMethod StringToNotificationMethod(const std::string& str); + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NOTIFICATION_METHOD_H_ + diff --git a/chrome/browser/sync/notifier/listener/listen_task.cc b/chrome/browser/sync/notifier/listener/listen_task.cc index 70fd081..48468a8 100644 --- a/chrome/browser/sync/notifier/listener/listen_task.cc +++ b/chrome/browser/sync/notifier/listener/listen_task.cc @@ -5,7 +5,9 @@ #include "chrome/browser/sync/notifier/listener/listen_task.h" #include "base/logging.h" -#include "base/string_util.h" +#include "chrome/browser/sync/notification_method.h" +#include "chrome/browser/sync/notifier/listener/notification_constants.h" +#include "chrome/browser/sync/notifier/listener/xml_element_util.h" #include "talk/base/task.h" #include "talk/xmllite/qname.h" #include "talk/xmllite/xmlelement.h" @@ -15,8 +17,9 @@ namespace browser_sync { -ListenTask::ListenTask(Task* parent) - : buzz::XmppTask(parent, buzz::XmppEngine::HL_TYPE) { +ListenTask::ListenTask(Task* parent, NotificationMethod notification_method) + : buzz::XmppTask(parent, buzz::XmppEngine::HL_TYPE), + notification_method_(notification_method) { } ListenTask::~ListenTask() { @@ -33,7 +36,7 @@ int ListenTask::ProcessResponse() { if (stanza == NULL) { return STATE_BLOCKED; } - // Acknowledge receipt of the notificaiton to the buzz server. + // Acknowledge receipt of the notification to the buzz server. scoped_ptr<buzz::XmlElement> response_stanza(MakeIqResult(stanza)); SendStanza(response_stanza.get()); @@ -43,6 +46,9 @@ int ListenTask::ProcessResponse() { } bool ListenTask::HandleStanza(const buzz::XmlElement* stanza) { + LOG(INFO) << "P2P: Stanza received: " << XmlElementToString(*stanza); + // TODO(akalin): Do more verification on stanza depending on + // notification_method_. if (IsValidNotification(stanza)) { QueueStanza(stanza); return true; @@ -51,8 +57,8 @@ bool ListenTask::HandleStanza(const buzz::XmlElement* stanza) { } bool ListenTask::IsValidNotification(const buzz::XmlElement* stanza) { - static const std::string kNSNotifier("google:notifier"); - static const buzz::QName kQnNotifierGetAll(true, kNSNotifier, "getAll"); + static const buzz::QName kQnNotifierGetAll( + true, kNotifierNamespace, "getAll"); // An update notificaiton has the following form. // <cli:iq from="{bare_jid}" to="{full_jid}" // id="#" type="set" xmlns:cli="jabber:client"> @@ -60,14 +66,10 @@ bool ListenTask::IsValidNotification(const buzz::XmlElement* stanza) { // <Timestamp long="#" xmlns=""/> // </not:getAll> // </cli:iq> - if (MatchRequestIq(stanza, buzz::STR_SET, kQnNotifierGetAll) && - !base::strcasecmp(stanza->Attr(buzz::QN_TO).c_str(), - GetClient()->jid().Str().c_str()) && - !base::strcasecmp(stanza->Attr(buzz::QN_FROM).c_str(), - GetClient()->jid().BareJid().Str().c_str())) { - return true; - } - return false; + return + (MatchRequestIq(stanza, buzz::STR_SET, kQnNotifierGetAll) && + (stanza->Attr(buzz::QN_TO) == GetClient()->jid().Str()) && + (stanza->Attr(buzz::QN_FROM) == GetClient()->jid().BareJid().Str())); } } // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/listen_task.h b/chrome/browser/sync/notifier/listener/listen_task.h index f8b32ce89..8f5bcd3 100644 --- a/chrome/browser/sync/notifier/listener/listen_task.h +++ b/chrome/browser/sync/notifier/listener/listen_task.h @@ -12,6 +12,7 @@ #ifndef CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_LISTEN_TASK_H_ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_LISTEN_TASK_H_ +#include "chrome/browser/sync/notification_method.h" #include "talk/xmpp/xmpptask.h" namespace buzz { @@ -23,7 +24,7 @@ namespace browser_sync { class ListenTask : public buzz::XmppTask { public: - explicit ListenTask(Task* parent); + ListenTask(Task* parent, NotificationMethod notification_method); virtual ~ListenTask(); // Overriden from buzz::XmppTask. @@ -39,6 +40,8 @@ class ListenTask : public buzz::XmppTask { // this notification came from our own Jid(). bool IsValidNotification(const buzz::XmlElement* stanza); + NotificationMethod notification_method_; + DISALLOW_COPY_AND_ASSIGN(ListenTask); }; diff --git a/chrome/browser/sync/notifier/listener/mediator_thread.h b/chrome/browser/sync/notifier/listener/mediator_thread.h index 1e6d89a..66682929 100644 --- a/chrome/browser/sync/notifier/listener/mediator_thread.h +++ b/chrome/browser/sync/notifier/listener/mediator_thread.h @@ -8,6 +8,8 @@ #ifndef CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_MEDIATOR_THREAD_H_ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_MEDIATOR_THREAD_H_ +#include "base/logging.h" +#include "chrome/browser/sync/notification_method.h" #include "talk/xmpp/xmppclientsettings.h" namespace browser_sync { @@ -23,7 +25,6 @@ class MediatorThread { MSG_NOTIFICATION_SENT }; - MediatorThread() {} virtual ~MediatorThread() {} virtual void Login(const buzz::XmppClientSettings& settings) = 0; @@ -35,6 +36,15 @@ class MediatorThread { // Connect to this for messages about talk events. sigslot::signal1<MediatorMessage> SignalStateChange; + + protected: + explicit MediatorThread(NotificationMethod notification_method) + : notification_method_(notification_method) {} + + const NotificationMethod notification_method_; + + private: + DISALLOW_COPY_AND_ASSIGN(MediatorThread); }; } // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc b/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc index d688b7e..a6df235 100644 --- a/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc +++ b/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc @@ -28,8 +28,9 @@ using std::string; namespace browser_sync { -MediatorThreadImpl::MediatorThreadImpl() { -} +MediatorThreadImpl::MediatorThreadImpl( + NotificationMethod notification_method) + : MediatorThread(notification_method) {} MediatorThreadImpl::~MediatorThreadImpl() { } @@ -43,6 +44,9 @@ void MediatorThreadImpl::Run() { // For win32, this sets up the win32socketserver. Note that it needs to // dispatch windows messages since that is what the win32 socket server uses. + LOG(INFO) << "Running mediator thread with notification method " + << NotificationMethodToString(notification_method_); + MessageLoop message_loop; #if defined(OS_WIN) scoped_ptr<talk_base::SocketServer> socket_server( @@ -211,7 +215,8 @@ void MediatorThreadImpl::DoSubscribeForUpdates() { if (!client) { return; } - SubscribeTask* subscription = new SubscribeTask(client); + SubscribeTask* subscription = + new SubscribeTask(client, notification_method_); subscription->SignalStatusUpdate.connect( this, &MediatorThreadImpl::OnSubscriptionStateChange); @@ -224,7 +229,7 @@ void MediatorThreadImpl::DoListenForUpdates() { if (!client) { return; } - ListenTask* listener = new ListenTask(client); + ListenTask* listener = new ListenTask(client, notification_method_); listener->SignalUpdateAvailable.connect( this, &MediatorThreadImpl::OnUpdateListenerMessage); @@ -237,7 +242,7 @@ void MediatorThreadImpl::DoSendNotification() { if (!client) { return; } - SendUpdateTask* task = new SendUpdateTask(client); + SendUpdateTask* task = new SendUpdateTask(client, notification_method_); task->SignalStatusUpdate.connect( this, &MediatorThreadImpl::OnUpdateNotificationSent); diff --git a/chrome/browser/sync/notifier/listener/mediator_thread_impl.h b/chrome/browser/sync/notifier/listener/mediator_thread_impl.h index a31da53..f8cf359 100644 --- a/chrome/browser/sync/notifier/listener/mediator_thread_impl.h +++ b/chrome/browser/sync/notifier/listener/mediator_thread_impl.h @@ -20,6 +20,7 @@ #ifndef CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_MEDIATOR_THREAD_IMPL_H_ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_MEDIATOR_THREAD_IMPL_H_ +#include "base/logging.h" #include "base/scoped_ptr.h" #include "chrome/browser/sync/notifier/communicator/login.h" #include "chrome/browser/sync/notifier/communicator/login_failure.h" @@ -67,7 +68,7 @@ class MediatorThreadImpl public talk_base::MessageHandler, public talk_base::Thread { public: - MediatorThreadImpl(); + explicit MediatorThreadImpl(NotificationMethod notification_method); virtual ~MediatorThreadImpl(); // Start the thread. diff --git a/chrome/browser/sync/notifier/listener/mediator_thread_mock.h b/chrome/browser/sync/notifier/listener/mediator_thread_mock.h index dea8a8e..ee5fde6 100644 --- a/chrome/browser/sync/notifier/listener/mediator_thread_mock.h +++ b/chrome/browser/sync/notifier/listener/mediator_thread_mock.h @@ -10,13 +10,14 @@ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_MEDIATOR_THREAD_MOCK_H_ #include "chrome/browser/sync/notifier/listener/mediator_thread.h" +#include "chrome/browser/sync/notification_method.h" #include "talk/xmpp/xmppclientsettings.h" namespace browser_sync { class MockMediatorThread : public MediatorThread { public: - MockMediatorThread() { + MockMediatorThread() : MediatorThread(kDefaultNotificationMethod) { Reset(); } ~MockMediatorThread() {} diff --git a/chrome/browser/sync/notifier/listener/notification_constants.cc b/chrome/browser/sync/notifier/listener/notification_constants.cc new file mode 100644 index 0000000..bd253e5 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/notification_constants.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notifier/listener/notification_constants.h" + +namespace browser_sync { + +const char kNotifierNamespace[] = "google:notifier"; +const char kSyncLegacyServiceUrl[] = "google:notifier"; +const char kSyncServiceUrl[] = "http://www.google.com/chrome/sync"; +const char kSyncLegacyServiceId[] = "notification"; +const char kSyncServiceId[] = "sync-ping"; +const int kSyncPriority = 200; +const char kSyncServiceSpecificData[] = "sync-ping-p2p"; + +} // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/notification_constants.h b/chrome/browser/sync/notifier/listener/notification_constants.h new file mode 100644 index 0000000..afb40d2 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/notification_constants.h @@ -0,0 +1,20 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_NOTIFICATION_CONSTANTS_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_NOTIFICATION_CONSTANTS_H_ + +namespace browser_sync { + +extern const char kNotifierNamespace[]; +extern const char kSyncLegacyServiceUrl[]; +extern const char kSyncServiceUrl[]; +extern const char kSyncLegacyServiceId[]; +extern const char kSyncServiceId[]; +extern const int kSyncPriority; +extern const char kSyncServiceSpecificData[]; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_NOTIFICATION_CONSTANTS_H_ diff --git a/chrome/browser/sync/notifier/listener/send_update_task.cc b/chrome/browser/sync/notifier/listener/send_update_task.cc index 835b226..6204310 100644 --- a/chrome/browser/sync/notifier/listener/send_update_task.cc +++ b/chrome/browser/sync/notifier/listener/send_update_task.cc @@ -4,16 +4,22 @@ #include "chrome/browser/sync/notifier/listener/send_update_task.h" +#include <string> + #include "base/logging.h" #include "base/scoped_ptr.h" +#include "chrome/browser/sync/notifier/listener/notification_constants.h" +#include "chrome/browser/sync/notifier/listener/xml_element_util.h" #include "talk/xmllite/qname.h" #include "talk/xmpp/xmppclient.h" #include "talk/xmpp/xmppconstants.h" namespace browser_sync { -SendUpdateTask::SendUpdateTask(Task* parent) - : XmppTask(parent, buzz::XmppEngine::HL_SINGLE) { // Watch for one reply. +SendUpdateTask::SendUpdateTask(Task* parent, + NotificationMethod notification_method) + : XmppTask(parent, buzz::XmppEngine::HL_SINGLE), // Watch for one reply. + notification_method_(notification_method) { } SendUpdateTask::~SendUpdateTask() { @@ -28,9 +34,17 @@ bool SendUpdateTask::HandleStanza(const buzz::XmlElement* stanza) { int SendUpdateTask::ProcessStart() { LOG(INFO) << "P2P: Notification task started."; - scoped_ptr<buzz::XmlElement> stanza(NewUpdateMessage()); + scoped_ptr<buzz::XmlElement> stanza( + MakeUpdateMessage(notification_method_, + GetClient()->jid().BareJid(), task_id())); + LOG(INFO) << "P2P: Notification stanza: " + << XmlElementToString(*stanza.get()); + if (SendStanza(stanza.get()) != buzz::XMPP_RETURN_OK) { // TODO(brg) : Retry on error. + // TODO(akalin): Or maybe immediately return STATE_ERROR and let + // retries happen a higher level. In any case, STATE_ERROR should + // eventually be returned. SignalStatusUpdate(false); return STATE_DONE; } @@ -43,6 +57,7 @@ int SendUpdateTask::ProcessResponse() { if (stanza == NULL) { return STATE_BLOCKED; } + LOG(INFO) << "P2P: Notification response: " << XmlElementToString(*stanza); if (stanza->HasAttr(buzz::QN_TYPE) && stanza->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) { // Notify listeners of success. @@ -53,12 +68,32 @@ int SendUpdateTask::ProcessResponse() { // An error response was received. // TODO(brg) : Error handling. SignalStatusUpdate(false); + // TODO(akalin): This should be STATE_ERROR. return STATE_DONE; } -buzz::XmlElement* SendUpdateTask::NewUpdateMessage() { - static const std::string kNSNotifier = "google:notifier"; - static const buzz::QName kQnNotifierSet(true, kNSNotifier, "set"); +buzz::XmlElement* SendUpdateTask::MakeUpdateMessage( + NotificationMethod notification_method, + const buzz::Jid& to_jid_bare, const std::string& task_id) { + switch (notification_method) { + case NOTIFICATION_LEGACY: + return MakeLegacyUpdateMessage(to_jid_bare, task_id); + case NOTIFICATION_TRANSITIONAL: + return MakeNonLegacyUpdateMessage(true, to_jid_bare, task_id); + case NOTIFICATION_NEW: + return MakeNonLegacyUpdateMessage(false, to_jid_bare, task_id); + } + NOTREACHED(); + return NULL; +} + +// TODO(akalin): Remove this once we get all clients on at least +// NOTIFICATION_TRANSITIONAL. + +buzz::XmlElement* SendUpdateTask::MakeLegacyUpdateMessage( + const buzz::Jid& to_jid_bare, const std::string& task_id) { + DCHECK(to_jid_bare.IsBare()); + static const buzz::QName kQnNotifierSet(true, kNotifierNamespace, "set"); static const buzz::QName kQnId(true, buzz::STR_EMPTY, "Id"); static const buzz::QName kQnServiceUrl(true, buzz::STR_EMPTY, "ServiceUrl"); static const buzz::QName kQnData(true, buzz::STR_EMPTY, "data"); @@ -74,8 +109,7 @@ buzz::XmlElement* SendUpdateTask::NewUpdateMessage() { // </Id> // </set> // </iq> - buzz::XmlElement* stanza = - MakeIq(buzz::STR_GET, GetClient()->jid().BareJid(), task_id()); + buzz::XmlElement* stanza = MakeIq(buzz::STR_GET, to_jid_bare, task_id); buzz::XmlElement* notifier_set = new buzz::XmlElement(kQnNotifierSet, true); stanza->AddElement(notifier_set); @@ -83,13 +117,65 @@ buzz::XmlElement* SendUpdateTask::NewUpdateMessage() { notifier_set->AddElement(id); buzz::XmlElement* service_url = new buzz::XmlElement(kQnServiceUrl, true); - service_url->AddAttr(kQnData, kNSNotifier); + service_url->AddAttr(kQnData, kSyncLegacyServiceUrl); id->AddElement(service_url); buzz::XmlElement* service_id = new buzz::XmlElement(kQnServiceId, true); - service_id->AddAttr(kQnData, "notification"); + service_id->AddAttr(kQnData, kSyncLegacyServiceId); id->AddElement(service_id); return stanza; } +// TODO(akalin): Remove the is_transitional switch once we get all +// clients on at least NOTIFICATION_NEW. + +buzz::XmlElement* SendUpdateTask::MakeNonLegacyUpdateMessage( + bool is_transitional, + const buzz::Jid& to_jid_bare, const std::string& task_id) { + DCHECK(to_jid_bare.IsBare()); + static const buzz::QName kQnNotifierSet(true, kNotifierNamespace, "set"); + static const buzz::QName kQnId(true, buzz::STR_EMPTY, "Id"); + static const buzz::QName kQnContent(true, buzz::STR_EMPTY, "Content"); + + // Create our update stanza. In the future this may include the revision id, + // but at the moment simply does a p2p ping. The message is constructed as: + // <iq type='get' from='{fullJid}' to='{bareJid}' id='{#}'> + // <gn:set xmlns:gn="google:notifier" xmlns=""> + // <Id> + // <ServiceUrl data="http://www.google.com/chrome/sync" /> + // <ServiceId data="sync-ping" /> + // </Id> + // <Content> + // <Priority int="200" /> + // <!-- If is_transitional is not set, the bool value is "true". --> + // <RequireSubscription bool="false" /> + // <!-- If is_transitional is set, this is omitted. --> + // <ServiceSpecificData data="sync-ping-p2p" /> + // <WriteToCacheOnly bool="true" /> + // </Content> + // </set> + // </iq> + buzz::XmlElement* iq = MakeIq(buzz::STR_GET, to_jid_bare, task_id); + buzz::XmlElement* set = new buzz::XmlElement(kQnNotifierSet, true); + buzz::XmlElement* id = new buzz::XmlElement(kQnId, true); + buzz::XmlElement* content = new buzz::XmlElement(kQnContent, true); + iq->AddElement(set); + set->AddElement(id); + set->AddElement(content); + + id->AddElement(MakeStringXmlElement("ServiceUrl", kSyncServiceUrl)); + id->AddElement(MakeStringXmlElement("ServiceId", kSyncServiceId)); + + content->AddElement(MakeIntXmlElement("Priority", kSyncPriority)); + content->AddElement( + MakeBoolXmlElement("RequireSubscription", !is_transitional)); + if (!is_transitional) { + content->AddElement( + MakeStringXmlElement("ServiceSpecificData", kSyncServiceSpecificData)); + } + content->AddElement(MakeBoolXmlElement("WriteToCacheOnly", true)); + + return iq; +} + } // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/send_update_task.h b/chrome/browser/sync/notifier/listener/send_update_task.h index de4a518..ae422af 100644 --- a/chrome/browser/sync/notifier/listener/send_update_task.h +++ b/chrome/browser/sync/notifier/listener/send_update_task.h @@ -7,14 +7,18 @@ #ifndef CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_SEND_UPDATE_TASK_H_ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_SEND_UPDATE_TASK_H_ +#include <string> + +#include "chrome/browser/sync/notification_method.h" #include "talk/xmllite/xmlelement.h" #include "talk/xmpp/xmpptask.h" +#include "testing/gtest/include/gtest/gtest_prod.h" namespace browser_sync { class SendUpdateTask : public buzz::XmppTask { public: - explicit SendUpdateTask(Task* parent); + SendUpdateTask(Task* parent, NotificationMethod notification_method); virtual ~SendUpdateTask(); // Overridden from buzz::XmppTask. @@ -27,7 +31,22 @@ class SendUpdateTask : public buzz::XmppTask { private: // Allocates and constructs an buzz::XmlElement containing the update stanza. - buzz::XmlElement* NewUpdateMessage(); + static buzz::XmlElement* MakeUpdateMessage( + NotificationMethod notification_method, + const buzz::Jid& to_jid_bare, const std::string& task_id); + + static buzz::XmlElement* MakeLegacyUpdateMessage( + const buzz::Jid& to_jid_bare, const std::string& task_id); + + static buzz::XmlElement* MakeNonLegacyUpdateMessage( + bool is_transitional, + const buzz::Jid& to_jid_bare, const std::string& task_id); + + NotificationMethod notification_method_; + + FRIEND_TEST(SendUpdateTaskTest, MakeUpdateMessage); + FRIEND_TEST(SendUpdateTaskTest, MakeLegacyUpdateMessage); + FRIEND_TEST(SendUpdateTaskTest, MakeNonLegacyUpdateMessage); DISALLOW_COPY_AND_ASSIGN(SendUpdateTask); }; diff --git a/chrome/browser/sync/notifier/listener/send_update_task_unittest.cc b/chrome/browser/sync/notifier/listener/send_update_task_unittest.cc new file mode 100644 index 0000000..50dd379 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/send_update_task_unittest.cc @@ -0,0 +1,132 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notifier/listener/send_update_task.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/browser/sync/notification_method.h" +#include "chrome/browser/sync/notifier/listener/xml_element_util.h" +#include "talk/xmpp/jid.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace buzz { +class XmlElement; +} + +namespace browser_sync { + +class SendUpdateTaskTest : public testing::Test { + public: + SendUpdateTaskTest() : to_jid_bare_("to@jid.com"), task_id_("taskid") { + EXPECT_EQ(to_jid_bare_.Str(), to_jid_bare_.BareJid().Str()); + } + + protected: + const buzz::Jid to_jid_bare_; + const std::string task_id_; + + private: + DISALLOW_COPY_AND_ASSIGN(SendUpdateTaskTest); +}; + +TEST_F(SendUpdateTaskTest, MakeLegacyUpdateMessage) { + scoped_ptr<buzz::XmlElement> message( + SendUpdateTask::MakeLegacyUpdateMessage(to_jid_bare_, task_id_)); + const std::string expected_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<set xmlns=\"google:notifier\">" + "<Id xmlns=\"\">" + "<ServiceUrl xmlns=\"\" data=\"google:notifier\"/>" + "<ServiceId xmlns=\"\" data=\"notification\"/>" + "</Id>" + "</set>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_xml_string, XmlElementToString(*message)); +} + +TEST_F(SendUpdateTaskTest, MakeNonLegacyUpdateMessage) { + scoped_ptr<buzz::XmlElement> new_message( + SendUpdateTask::MakeNonLegacyUpdateMessage(false, to_jid_bare_, + task_id_)); + const std::string expected_new_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<set xmlns=\"google:notifier\">" + "<Id xmlns=\"\">" + "<ServiceUrl xmlns=\"\" " + "data=\"http://www.google.com/chrome/sync\"/>" + "<ServiceId xmlns=\"\" data=\"sync-ping\"/>" + "</Id>" + "<Content xmlns=\"\">" + "<Priority xmlns=\"\" int=\"200\"/>" + "<RequireSubscription xmlns=\"\" bool=\"true\"/>" + "<ServiceSpecificData xmlns=\"\" " + "data=\"sync-ping-p2p\"/>" + "<WriteToCacheOnly xmlns=\"\" bool=\"true\"/>" + "</Content>" + "</set>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_new_xml_string, XmlElementToString(*new_message)); + + scoped_ptr<buzz::XmlElement> transitional_message( + SendUpdateTask::MakeNonLegacyUpdateMessage(true, to_jid_bare_, + task_id_)); + const std::string expected_transitional_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<set xmlns=\"google:notifier\">" + "<Id xmlns=\"\">" + "<ServiceUrl xmlns=\"\" " + "data=\"http://www.google.com/chrome/sync\"/>" + "<ServiceId xmlns=\"\" data=\"sync-ping\"/>" + "</Id>" + "<Content xmlns=\"\">" + "<Priority xmlns=\"\" int=\"200\"/>" + "<RequireSubscription xmlns=\"\" bool=\"false\"/>" + "<WriteToCacheOnly xmlns=\"\" bool=\"true\"/>" + "</Content>" + "</set>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_transitional_xml_string, + XmlElementToString(*transitional_message)); +} + +TEST_F(SendUpdateTaskTest, MakeUpdateMessage) { + scoped_ptr<buzz::XmlElement> expected_legacy_message( + SendUpdateTask::MakeLegacyUpdateMessage(to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> legacy_message( + SendUpdateTask::MakeUpdateMessage(NOTIFICATION_LEGACY, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_legacy_message), + XmlElementToString(*legacy_message)); + + scoped_ptr<buzz::XmlElement> expected_transitional_message( + SendUpdateTask::MakeNonLegacyUpdateMessage(true, + to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> transitional_message( + SendUpdateTask::MakeUpdateMessage(NOTIFICATION_TRANSITIONAL, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_transitional_message), + XmlElementToString(*transitional_message)); + + scoped_ptr<buzz::XmlElement> expected_new_message( + SendUpdateTask::MakeNonLegacyUpdateMessage(false, + to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> new_message( + SendUpdateTask::MakeUpdateMessage(NOTIFICATION_NEW, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_new_message), + XmlElementToString(*new_message)); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/subscribe_task.cc b/chrome/browser/sync/notifier/listener/subscribe_task.cc index 9fb828b..e0f0bf9 100644 --- a/chrome/browser/sync/notifier/listener/subscribe_task.cc +++ b/chrome/browser/sync/notifier/listener/subscribe_task.cc @@ -7,6 +7,8 @@ #include <string> #include "base/logging.h" +#include "chrome/browser/sync/notifier/listener/notification_constants.h" +#include "chrome/browser/sync/notifier/listener/xml_element_util.h" #include "talk/base/task.h" #include "talk/xmllite/qname.h" #include "talk/xmllite/xmlelement.h" @@ -16,8 +18,10 @@ namespace browser_sync { -SubscribeTask::SubscribeTask(Task* parent) - : XmppTask(parent, buzz::XmppEngine::HL_SINGLE) { +SubscribeTask::SubscribeTask(Task* parent, + NotificationMethod notification_method) + : XmppTask(parent, buzz::XmppEngine::HL_SINGLE), + notification_method_(notification_method) { } SubscribeTask::~SubscribeTask() { @@ -32,10 +36,15 @@ bool SubscribeTask::HandleStanza(const buzz::XmlElement* stanza) { int SubscribeTask::ProcessStart() { LOG(INFO) << "P2P: Subscription task started."; - scoped_ptr<buzz::XmlElement> iq_stanza(NewSubscriptionMessage()); + scoped_ptr<buzz::XmlElement> iq_stanza( + MakeSubscriptionMessage(notification_method_, + GetClient()->jid().BareJid(), task_id())); + LOG(INFO) << "P2P: Subscription stanza: " + << XmlElementToString(*iq_stanza.get()); if (SendStanza(iq_stanza.get()) != buzz::XMPP_RETURN_OK) { SignalStatusUpdate(false); + // TODO(akalin): This should be STATE_ERROR. return STATE_DONE; } return STATE_RESPONSE; @@ -47,6 +56,7 @@ int SubscribeTask::ProcessResponse() { if (stanza == NULL) { return STATE_BLOCKED; } + LOG(INFO) << "P2P: Subscription response: " << XmlElementToString(*stanza); // We've receieved a response to our subscription request. if (stanza->HasAttr(buzz::QN_TYPE) && stanza->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) { @@ -56,25 +66,47 @@ int SubscribeTask::ProcessResponse() { // An error response was received. // TODO(brg) : Error handling. SignalStatusUpdate(false); + // TODO(akalin): This should be STATE_ERROR. return STATE_DONE; } -buzz::XmlElement* SubscribeTask::NewSubscriptionMessage() { - static const buzz::QName kQnNotifierGetAll(true, "google:notifier", "getAll"); +buzz::XmlElement* SubscribeTask::MakeSubscriptionMessage( + NotificationMethod notification_method, + const buzz::Jid& to_jid_bare, const std::string& task_id) { + switch (notification_method) { + case NOTIFICATION_LEGACY: + return MakeLegacySubscriptionMessage(to_jid_bare, task_id); + case NOTIFICATION_TRANSITIONAL: + return MakeNonLegacySubscriptionMessage(true, to_jid_bare, task_id); + case NOTIFICATION_NEW: + return MakeNonLegacySubscriptionMessage(false, to_jid_bare, task_id); + } + NOTREACHED(); + return NULL; +} + +// TODO(akalin): Remove this once we get all clients on at least +// NOTIFICATION_TRANSITIONAL. + +buzz::XmlElement* SubscribeTask::MakeLegacySubscriptionMessage( + const buzz::Jid& to_jid_bare, const std::string& task_id) { + DCHECK(to_jid_bare.IsBare()); + static const buzz::QName kQnNotifierGetAll( + true, kNotifierNamespace, "getAll"); static const buzz::QName kQnNotifierClientActive(true, buzz::STR_EMPTY, "ClientActive"); static const buzz::QName kQnBool(true, buzz::STR_EMPTY, "bool"); static const std::string kTrueString("true"); - // Create the subscription stanza using the notificaitons protocol. + // Create the subscription stanza using the notifications protocol. // <iq type='get' from='{fullJid}' to='{bareJid}' id='{#}'> // <gn:getAll xmlns:gn='google:notifier' xmlns=''> // <ClientActive bool='true'/> // </gn:getAll> // </iq> buzz::XmlElement* get_all_request = - MakeIq(buzz::STR_GET, GetClient()->jid().BareJid(), task_id()); + MakeIq(buzz::STR_GET, to_jid_bare, task_id); buzz::XmlElement* notifier_get = new buzz::XmlElement(kQnNotifierGetAll, true); @@ -88,4 +120,41 @@ buzz::XmlElement* SubscribeTask::NewSubscriptionMessage() { return get_all_request; } +// TODO(akalin): Remove the is_transitional switch once we get all +// clients on at least NOTIFICATION_NEW. + +buzz::XmlElement* SubscribeTask::MakeNonLegacySubscriptionMessage( + bool is_transitional, const buzz::Jid& to_jid_bare, + const std::string& task_id) { + DCHECK(to_jid_bare.IsBare()); + static const buzz::QName kQnNotifierGetAll( + true, kNotifierNamespace, "getAll"); + + // Create the subscription stanza using the notifications protocol. + // <iq type='get' from='{fullJid}' to='{bareJid}' id='{#}'> + // <gn:getAll xmlns:gn="google:notifier" xmlns=""> + // <ClientActive bool="true" /> + // <!-- present only if is_transitional is set --> + // <SubscribedServiceUrl data="google:notifier"> + // <SubscribedServiceUrl data="http://www.google.com/chrome/sync"> + // <FilterNonSubscribed bool="true" /> + // </gn:getAll> + // </iq> + + buzz::XmlElement* iq = MakeIq(buzz::STR_GET, to_jid_bare, task_id); + buzz::XmlElement* get_all = new buzz::XmlElement(kQnNotifierGetAll, true); + iq->AddElement(get_all); + + get_all->AddElement(MakeBoolXmlElement("ClientActive", true)); + if (is_transitional) { + get_all->AddElement( + MakeStringXmlElement("SubscribedServiceUrl", kSyncLegacyServiceUrl)); + } + get_all->AddElement( + MakeStringXmlElement("SubscribedServiceUrl", kSyncServiceUrl)); + get_all->AddElement(MakeBoolXmlElement("FilterNonSubscribed", true)); + + return iq; +} + } // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/subscribe_task.h b/chrome/browser/sync/notifier/listener/subscribe_task.h index 793694e..dbef8d6 100644 --- a/chrome/browser/sync/notifier/listener/subscribe_task.h +++ b/chrome/browser/sync/notifier/listener/subscribe_task.h @@ -9,14 +9,18 @@ #ifndef CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_SUBSCRIBE_TASK_H_ #define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_SUBSCRIBE_TASK_H_ +#include <string> + +#include "chrome/browser/sync/notification_method.h" #include "talk/xmllite/xmlelement.h" #include "talk/xmpp/xmpptask.h" +#include "testing/gtest/include/gtest/gtest_prod.h" namespace browser_sync { class SubscribeTask : public buzz::XmppTask { public: - explicit SubscribeTask(Task* parent); + SubscribeTask(Task* parent, NotificationMethod notification_method); virtual ~SubscribeTask(); // Overridden from XmppTask. @@ -29,7 +33,22 @@ class SubscribeTask : public buzz::XmppTask { private: // Assembles an Xmpp stanza which can be sent to subscribe to notifications. - buzz::XmlElement* NewSubscriptionMessage(); + static buzz::XmlElement* MakeSubscriptionMessage( + NotificationMethod notification_method, + const buzz::Jid& to_jid_bare, const std::string& task_id); + + static buzz::XmlElement* MakeLegacySubscriptionMessage( + const buzz::Jid& to_jid_bare, const std::string& task_id); + + static buzz::XmlElement* MakeNonLegacySubscriptionMessage( + bool is_transitional, + const buzz::Jid& to_jid_bare, const std::string& task_id); + + NotificationMethod notification_method_; + + FRIEND_TEST(SubscribeTaskTest, MakeLegacySubscriptionMessage); + FRIEND_TEST(SubscribeTaskTest, MakeNonLegacySubscriptionMessage); + FRIEND_TEST(SubscribeTaskTest, MakeSubscriptionMessage); DISALLOW_COPY_AND_ASSIGN(SubscribeTask); }; diff --git a/chrome/browser/sync/notifier/listener/subscribe_task_unittest.cc b/chrome/browser/sync/notifier/listener/subscribe_task_unittest.cc new file mode 100644 index 0000000..21bffc2 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/subscribe_task_unittest.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notifier/listener/subscribe_task.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/browser/sync/notification_method.h" +#include "chrome/browser/sync/notifier/listener/xml_element_util.h" +#include "talk/xmpp/jid.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace buzz { +class XmlElement; +} + +namespace browser_sync { + +class SubscribeTaskTest : public testing::Test { + public: + SubscribeTaskTest() : to_jid_bare_("to@jid.com"), task_id_("taskid") { + EXPECT_EQ(to_jid_bare_.Str(), to_jid_bare_.BareJid().Str()); + } + + protected: + const buzz::Jid to_jid_bare_; + const std::string task_id_; + + private: + DISALLOW_COPY_AND_ASSIGN(SubscribeTaskTest); +}; + +TEST_F(SubscribeTaskTest, MakeLegacySubscriptionMessage) { + scoped_ptr<buzz::XmlElement> message( + SubscribeTask::MakeLegacySubscriptionMessage(to_jid_bare_, task_id_)); + const std::string expected_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<getAll xmlns=\"google:notifier\">" + "<ClientActive xmlns=\"\" bool=\"true\"/>" + "</getAll>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_xml_string, XmlElementToString(*message)); +} + +TEST_F(SubscribeTaskTest, MakeNonLegacySubscriptionMessage) { + scoped_ptr<buzz::XmlElement> new_message( + SubscribeTask::MakeNonLegacySubscriptionMessage(false, to_jid_bare_, + task_id_)); + const std::string expected_new_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<getAll xmlns=\"google:notifier\">" + "<ClientActive xmlns=\"\" bool=\"true\"/>" + "<SubscribedServiceUrl " + "xmlns=\"\" data=\"http://www.google.com/chrome/sync\"/>" + "<FilterNonSubscribed xmlns=\"\" bool=\"true\"/>" + "</getAll>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_new_xml_string, XmlElementToString(*new_message)); + + scoped_ptr<buzz::XmlElement> transitional_message( + SubscribeTask::MakeNonLegacySubscriptionMessage(true, to_jid_bare_, + task_id_)); + const std::string expected_transitional_xml_string = + StringPrintf( + "<cli:iq type=\"get\" to=\"%s\" id=\"%s\" " + "xmlns:cli=\"jabber:client\">" + "<getAll xmlns=\"google:notifier\">" + "<ClientActive xmlns=\"\" bool=\"true\"/>" + "<SubscribedServiceUrl xmlns=\"\" data=\"google:notifier\"/>" + "<SubscribedServiceUrl " + "xmlns=\"\" data=\"http://www.google.com/chrome/sync\"/>" + "<FilterNonSubscribed xmlns=\"\" bool=\"true\"/>" + "</getAll>" + "</cli:iq>", + to_jid_bare_.Str().c_str(), task_id_.c_str()); + EXPECT_EQ(expected_transitional_xml_string, + XmlElementToString(*transitional_message)); +} + +TEST_F(SubscribeTaskTest, MakeSubscriptionMessage) { + scoped_ptr<buzz::XmlElement> expected_legacy_message( + SubscribeTask::MakeLegacySubscriptionMessage(to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> legacy_message( + SubscribeTask::MakeSubscriptionMessage(NOTIFICATION_LEGACY, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_legacy_message), + XmlElementToString(*legacy_message)); + + scoped_ptr<buzz::XmlElement> expected_transitional_message( + SubscribeTask::MakeNonLegacySubscriptionMessage(true, + to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> transitional_message( + SubscribeTask::MakeSubscriptionMessage(NOTIFICATION_TRANSITIONAL, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_transitional_message), + XmlElementToString(*transitional_message)); + + scoped_ptr<buzz::XmlElement> expected_new_message( + SubscribeTask::MakeNonLegacySubscriptionMessage(false, + to_jid_bare_, task_id_)); + scoped_ptr<buzz::XmlElement> new_message( + SubscribeTask::MakeSubscriptionMessage(NOTIFICATION_NEW, + to_jid_bare_, task_id_)); + EXPECT_EQ(XmlElementToString(*expected_new_message), + XmlElementToString(*new_message)); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/talk_mediator_impl.cc b/chrome/browser/sync/notifier/listener/talk_mediator_impl.cc index a147e66..f8506fe 100644 --- a/chrome/browser/sync/notifier/listener/talk_mediator_impl.cc +++ b/chrome/browser/sync/notifier/listener/talk_mediator_impl.cc @@ -41,8 +41,8 @@ class SslInitializationSingleton { DISALLOW_COPY_AND_ASSIGN(SslInitializationSingleton); }; -TalkMediatorImpl::TalkMediatorImpl() - : mediator_thread_(new MediatorThreadImpl()) { +TalkMediatorImpl::TalkMediatorImpl(NotificationMethod notification_method) + : mediator_thread_(new MediatorThreadImpl(notification_method)) { // Ensure the SSL library is initialized. SslInitializationSingleton::GetInstance()->RegisterClient(); diff --git a/chrome/browser/sync/notifier/listener/talk_mediator_impl.h b/chrome/browser/sync/notifier/listener/talk_mediator_impl.h index 922624e..6b9ce20 100644 --- a/chrome/browser/sync/notifier/listener/talk_mediator_impl.h +++ b/chrome/browser/sync/notifier/listener/talk_mediator_impl.h @@ -14,6 +14,7 @@ #include "base/lock.h" #include "base/scoped_ptr.h" #include "chrome/browser/sync/engine/auth_watcher.h" +#include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/notifier/listener/mediator_thread.h" #include "chrome/browser/sync/notifier/listener/talk_mediator.h" #include "talk/xmpp/xmppclientsettings.h" @@ -31,7 +32,7 @@ class TalkMediatorImpl : public TalkMediator, public sigslot::has_slots<> { public: - TalkMediatorImpl(); + explicit TalkMediatorImpl(NotificationMethod notification_method); explicit TalkMediatorImpl(MediatorThread* thread); virtual ~TalkMediatorImpl(); diff --git a/chrome/browser/sync/notifier/listener/talk_mediator_unittest.cc b/chrome/browser/sync/notifier/listener/talk_mediator_unittest.cc index 280a8a9..4c37eba 100644 --- a/chrome/browser/sync/notifier/listener/talk_mediator_unittest.cc +++ b/chrome/browser/sync/notifier/listener/talk_mediator_unittest.cc @@ -36,7 +36,8 @@ class TalkMediatorImplTest : public testing::Test { TEST_F(TalkMediatorImplTest, ConstructionOfTheClass) { // Constructing a single talk mediator enables SSL through the singleton. - scoped_ptr<TalkMediatorImpl> talk1(new TalkMediatorImpl()); + scoped_ptr<TalkMediatorImpl> talk1(new TalkMediatorImpl( + browser_sync::kDefaultNotificationMethod)); talk1.reset(NULL); } diff --git a/chrome/browser/sync/notifier/listener/xml_element_util.cc b/chrome/browser/sync/notifier/listener/xml_element_util.cc new file mode 100644 index 0000000..a00f899 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/xml_element_util.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notifier/listener/xml_element_util.h" + +#include <sstream> +#include <string> + +#include "base/string_util.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlconstants.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlprinter.h" + +namespace browser_sync { + +std::string XmlElementToString(const buzz::XmlElement& xml_element) { + std::ostringstream xml_stream; + buzz::XmlPrinter::PrintXml(&xml_stream, &xml_element); + return xml_stream.str(); +} + +buzz::XmlElement* MakeBoolXmlElement(const char* name, bool value) { + const buzz::QName elementQName(true, buzz::STR_EMPTY, name); + const buzz::QName boolAttrQName(true, buzz::STR_EMPTY, "bool"); + buzz::XmlElement* bool_xml_element = + new buzz::XmlElement(elementQName, true); + bool_xml_element->AddAttr(boolAttrQName, value ? "true" : "false"); + return bool_xml_element; +} + +buzz::XmlElement* MakeIntXmlElement(const char* name, int value) { + const buzz::QName elementQName(true, buzz::STR_EMPTY, name); + const buzz::QName intAttrQName(true, buzz::STR_EMPTY, "int"); + buzz::XmlElement* int_xml_element = + new buzz::XmlElement(elementQName, true); + int_xml_element->AddAttr(intAttrQName, IntToString(value)); + return int_xml_element; +} + +buzz::XmlElement* MakeStringXmlElement(const char* name, const char* value) { + const buzz::QName elementQName(true, buzz::STR_EMPTY, name); + const buzz::QName dataAttrQName(true, buzz::STR_EMPTY, "data"); + buzz::XmlElement* data_xml_element = + new buzz::XmlElement(elementQName, true); + data_xml_element->AddAttr(dataAttrQName, value); + return data_xml_element; +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/notifier/listener/xml_element_util.h b/chrome/browser/sync/notifier/listener/xml_element_util.h new file mode 100644 index 0000000..cfd0d05 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/xml_element_util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_XML_ELEMENT_UTIL_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_XML_ELEMENT_UTIL_H_ + +#include <string> + +namespace buzz { +class XmlElement; +} + +namespace browser_sync { + +std::string XmlElementToString(const buzz::XmlElement& xml_element); + +// The functions below are helpful for building notifications-related +// XML stanzas. + +buzz::XmlElement* MakeBoolXmlElement(const char* name, bool value); + +buzz::XmlElement* MakeIntXmlElement(const char* name, int value); + +buzz::XmlElement* MakeStringXmlElement(const char* name, const char* value); + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_LISTENER_XML_ELEMENT_UTIL_H_ diff --git a/chrome/browser/sync/notifier/listener/xml_element_util_unittest.cc b/chrome/browser/sync/notifier/listener/xml_element_util_unittest.cc new file mode 100644 index 0000000..7faf073 --- /dev/null +++ b/chrome/browser/sync/notifier/listener/xml_element_util_unittest.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2010 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 "chrome/browser/sync/notifier/listener/xml_element_util.h" + +#include <sstream> +#include <string> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" +#include "talk/xmllite/xmlprinter.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace buzz { +class XmlElement; +} + +namespace browser_sync { +namespace { + +class XmlElementUtilTest : public testing::Test {}; + +TEST_F(XmlElementUtilTest, XmlElementToString) { + const buzz::QName kQName(true, "namespace", "element"); + const buzz::XmlElement kXmlElement(kQName, true); + std::ostringstream expected_xml_stream; + buzz::XmlPrinter::PrintXml(&expected_xml_stream, &kXmlElement); + EXPECT_EQ(expected_xml_stream.str(), XmlElementToString(kXmlElement)); +} + +TEST_F(XmlElementUtilTest, MakeBoolXmlElement) { + scoped_ptr<buzz::XmlElement> foo_false( + MakeBoolXmlElement("foo", false)); + EXPECT_EQ("<foo xmlns=\"\" bool=\"false\"/>", XmlElementToString(*foo_false)); + + scoped_ptr<buzz::XmlElement> bar_true( + MakeBoolXmlElement("bar", true)); + EXPECT_EQ("<bar xmlns=\"\" bool=\"true\"/>", XmlElementToString(*bar_true)); +} + +TEST_F(XmlElementUtilTest, MakeIntXmlElement) { + scoped_ptr<buzz::XmlElement> int_xml_element( + MakeIntXmlElement("foo", 35)); + EXPECT_EQ("<foo xmlns=\"\" int=\"35\"/>", + XmlElementToString(*int_xml_element)); +} + +TEST_F(XmlElementUtilTest, MakeStringXmlElement) { + scoped_ptr<buzz::XmlElement> string_xml_element( + MakeStringXmlElement("foo", "bar")); + EXPECT_EQ("<foo xmlns=\"\" data=\"bar\"/>", + XmlElementToString(*string_xml_element)); +} + +} // namespace +} // namespace browser_sync diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index df65f79..c6baf7f 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -42,7 +42,8 @@ ProfileSyncService::ProfileSyncService(Profile* profile) expecting_first_run_auth_needed_event_(false), is_auth_in_progress_(false), ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)), - unrecoverable_error_detected_(false) { + unrecoverable_error_detected_(false), + notification_method_(browser_sync::kDefaultNotificationMethod) { } ProfileSyncService::~ProfileSyncService() { @@ -93,6 +94,13 @@ void ProfileSyncService::InitSettings() { } } } + + if (command_line.HasSwitch(switches::kSyncNotificationMethod)) { + const std::string notification_method_str( + command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod)); + notification_method_ = + browser_sync::StringToNotificationMethod(notification_method_str); + } } void ProfileSyncService::RegisterPreferences() { @@ -143,7 +151,7 @@ void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) { #endif backend_->Initialize(sync_service_url_, profile_->GetRequestContext(), GetLsidForAuthBootstraping(), delete_sync_data_folder, - invalidate_sync_login); + invalidate_sync_login, notification_method_); } void ProfileSyncService::StartUp() { diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index 0c1f8ae..349e819f 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -16,6 +16,7 @@ #include "chrome/browser/profile.h" #include "chrome/browser/sync/glue/data_type_controller.h" // For StartResult. #include "chrome/browser/sync/glue/sync_backend_host.h" +#include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/sync_setup_wizard.h" #include "chrome/browser/sync/syncable/model_type.h" #include "googleurl/src/gurl.h" @@ -321,6 +322,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // doing any work that might corrupt things further. bool unrecoverable_error_detected_; + // Which peer-to-peer notification method to use. + browser_sync::NotificationMethod notification_method_; + ObserverList<Observer> observers_; 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 584b77d..2e54c5b 100644 --- a/chrome/browser/sync/profile_sync_service_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_unittest.cc @@ -20,6 +20,7 @@ #include "chrome/browser/sync/glue/bookmark_model_associator.h" #include "chrome/browser/sync/glue/model_associator.h" #include "chrome/browser/sync/glue/sync_backend_host.h" +#include "chrome/browser/sync/notification_method.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/common/chrome_switches.h" @@ -131,7 +132,7 @@ class TestProfileSyncService : public ProfileSyncService { TestHttpBridgeFactory* factory = new TestHttpBridgeFactory(); TestHttpBridgeFactory* factory2 = new TestHttpBridgeFactory(); backend()->InitializeForTestMode(L"testuser", factory, factory2, - delete_sync_data_folder); + delete_sync_data_folder, browser_sync::kDefaultNotificationMethod); // The SyncBackend posts a task to the current loop when initialization // completes. MessageLoop::current()->Run(); diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index f06dd1a..e2199b8 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -801,6 +801,8 @@ 'browser/sync/notifier/listener/mediator_thread_impl.cc', 'browser/sync/notifier/listener/mediator_thread_impl.h', 'browser/sync/notifier/listener/mediator_thread_mock.h', + 'browser/sync/notifier/listener/notification_constants.cc', + 'browser/sync/notifier/listener/notification_constants.h', 'browser/sync/notifier/listener/send_update_task.cc', 'browser/sync/notifier/listener/send_update_task.h', 'browser/sync/notifier/listener/subscribe_task.cc', @@ -808,6 +810,8 @@ 'browser/sync/notifier/listener/talk_mediator.h', 'browser/sync/notifier/listener/talk_mediator_impl.cc', 'browser/sync/notifier/listener/talk_mediator_impl.h', + 'browser/sync/notifier/listener/xml_element_util.cc', + 'browser/sync/notifier/listener/xml_element_util.h', ], 'include_dirs': [ '..', diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 0149981..d049ab4 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1685,6 +1685,8 @@ 'browser/sync/glue/sync_backend_host.h', 'browser/sync/glue/ui_model_worker.cc', 'browser/sync/glue/ui_model_worker.h', + 'browser/sync/notification_method.h', + 'browser/sync/notification_method.cc', 'browser/sync/profile_sync_service.cc', 'browser/sync/profile_sync_service.h', 'browser/sync/profile_sync_factory.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 04a4491..fea4898 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1465,6 +1465,9 @@ 'browser/sync/glue/change_processor_mock.h', 'browser/sync/notifier/base/mac/network_status_detector_task_mac_unittest.cc', 'browser/sync/notifier/listener/talk_mediator_unittest.cc', + 'browser/sync/notifier/listener/send_update_task_unittest.cc', + 'browser/sync/notifier/listener/subscribe_task_unittest.cc', + 'browser/sync/notifier/listener/xml_element_util_unittest.cc', 'browser/sync/profile_sync_factory_mock.cc', 'browser/sync/profile_sync_factory_mock.h', 'browser/sync/sessions/status_controller_unittest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index c4b45b2..eba0a6e 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -604,6 +604,9 @@ const char kStartMaximized[] = "start-maximized"; // Override the default server used for profile sync. const char kSyncServiceURL[] = "sync-url"; +// Override the default notification method for sync. +const char kSyncNotificationMethod[] = "sync-notification-method"; + // Use the SyncerThread implementation that matches up with the old pthread // impl semantics, but using Chrome synchronization primitives. The only // difference between this and the default is that we now have no timeout on diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 5676bf3..0cd2873 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -176,6 +176,7 @@ extern const char kSimpleDataSource[]; extern const char kSingleProcess[]; extern const char kStartMaximized[]; extern const char kSyncServiceURL[]; +extern const char kSyncNotificationMethod[]; extern const char kSyncerThreadTimedStop[]; extern const char kTabCountToLoadOnSessionRestore[]; extern const char kTestName[]; |