summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormunjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-21 21:59:26 +0000
committermunjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-21 21:59:26 +0000
commitaae9eeb10159ca2e345408f74d8425a41bb81ab4 (patch)
treea1843a6f019e1f08f214ac4e11e8346a6614ec8a
parent4e57c88872d08f8e98ee1bae3258ba38e4694d87 (diff)
downloadchromium_src-aae9eeb10159ca2e345408f74d8425a41bb81ab4.zip
chromium_src-aae9eeb10159ca2e345408f74d8425a41bb81ab4.tar.gz
chromium_src-aae9eeb10159ca2e345408f74d8425a41bb81ab4.tar.bz2
Implement sync data type controller and UI for syncing notifications:
- Add class AppNotificationDataTypeController - Add resources and other things needed for sync UI for app notifications - Add command line flag to enable/disable app notifications sync. Review URL: http://codereview.chromium.org/8320017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106786 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd9
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/resources/sync_setup_overlay.html8
-rw-r--r--chrome/browser/resources/sync_setup_overlay.js8
-rw-r--r--chrome/browser/sync/glue/app_notification_data_type_controller.cc97
-rw-r--r--chrome/browser/sync/glue/app_notification_data_type_controller.h59
-rw-r--r--chrome/browser/sync/glue/app_notification_data_type_controller_unittest.cc250
-rw-r--r--chrome/browser/sync/profile_sync_factory.h7
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.cc29
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.h4
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.cc5
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.h4
-rw-r--r--chrome/browser/sync/resources/configure.html16
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc4
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc5
-rw-r--r--chrome/browser/ui/webui/ntp/ntp_resource_cache.cc2
-rw-r--r--chrome/browser/ui/webui/options/personal_options_handler.cc2
-rw-r--r--chrome/browser/ui/webui/sync_setup_handler.cc7
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/pref_names.cc2
-rw-r--r--chrome/common/pref_names.h2
24 files changed, 530 insertions, 4 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 26c5bd7..96bfaeb 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4696,6 +4696,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_SYNC_TABS_DESCRIPTION" desc="Description for the flag to enable syncing the open tabs datatype">
Enable open tabs in the sync options. This allows syncing your open tabs to other clients.
</message>
+ <message name="IDS_FLAGS_SYNC_APP_NOTIFICATIONS_NAME" desc="Title for the flag to enable syncing the app notifications datatype">
+ Enable syncing app notifications
+ </message>
+ <message name="IDS_FLAGS_SYNC_APP_NOTIFICATIONS_DESCRIPTION" desc="Description for the flag to enable syncing the app notifications datatype">
+ Enable app notifications in the sync options. This allows syncing notifications received from your apps to other clients.
+ </message>
<if expr="pp_ifdef('android')">
<message name="IDS_FLAGS_SYNC_TYPED_URLS_NAME" desc="Title for the flag to enable syncing the TypedUrl datatype">
Enable syncing typed URLs
@@ -10325,6 +10331,9 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_SYNC_DATATYPE_TABS" desc="Open Tabs, one of the data types that we allow syncing.">
Open Tabs
</message>
+ <message name="IDS_SYNC_DATATYPE_APP_NOTIFICATIONS" desc="App notifications, one of the data types that we allow syncing.">
+ App Notifications
+ </message>
<!-- Encryption tab of the configure sync dialog -->
<if expr="not pp_ifdef('use_titlecase')">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 19780ef..2ad644f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -308,6 +308,13 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(switches::kEnableSyncSearchEngines)
},
{
+ "sync-app-notifications",
+ IDS_FLAGS_SYNC_APP_NOTIFICATIONS_NAME,
+ IDS_FLAGS_SYNC_APP_NOTIFICATIONS_DESCRIPTION,
+ kOsAll,
+ SINGLE_VALUE_TYPE(switches::kEnableSyncAppNotifications)
+ },
+ {
"enable-smooth-scrolling", // FLAGS:RECORD_UMA
IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_NAME,
IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_DESCRIPTION,
diff --git a/chrome/browser/resources/sync_setup_overlay.html b/chrome/browser/resources/sync_setup_overlay.html
index da782e8..2002e22 100644
--- a/chrome/browser/resources/sync_setup_overlay.html
+++ b/chrome/browser/resources/sync_setup_overlay.html
@@ -238,6 +238,14 @@
il8n-values="title:searchEngines"
name="dataTypeLabel"></label>
</div>
+ <div id="app-notifications-item" class="sync-item-show">
+ <input id="app-notifications-checkbox" type="checkbox"
+ name="dataTypeCheckbox">
+ <label for="app-notifications-checkbox"
+ i18n-content="appNotifications"
+ il8n-values="title:appNotifications"
+ name="dataTypeLabel"></label>
+ </div>
</div>
</div>
</div>
diff --git a/chrome/browser/resources/sync_setup_overlay.js b/chrome/browser/resources/sync_setup_overlay.js
index a60b29b..2876331 100644
--- a/chrome/browser/resources/sync_setup_overlay.js
+++ b/chrome/browser/resources/sync_setup_overlay.js
@@ -250,6 +250,8 @@ cr.define('options', function() {
"syncTypedUrls": syncAll || $('typed-urls-checkbox').checked,
"syncApps": syncAll || $('apps-checkbox').checked,
"syncSearchEngines": syncAll || $('search-engines-checkbox').checked,
+ "syncAppNotifications": syncAll ||
+ $('app-notifications-checkbox').checked,
"syncSessions": syncAll || $('sessions-checkbox').checked,
"encryptAllData": encryptAllData,
"usePassphrase": usePassphrase,
@@ -356,6 +358,12 @@ cr.define('options', function() {
} else {
$('sessions-item').className = "sync-item-hide";
}
+ if (args.appNotificationsRegistered) {
+ $('app-notifications-checkbox').checked = args.syncAppNotifications;
+ $('app-notifications-item').className = "sync-item-show";
+ } else {
+ $('app-notifications-item').className = "sync-item-hide";
+ }
this.setCheckboxesToKeepEverythingSynced_(args.syncAllDataTypes);
},
diff --git a/chrome/browser/sync/glue/app_notification_data_type_controller.cc b/chrome/browser/sync/glue/app_notification_data_type_controller.cc
new file mode 100644
index 0000000..e1a272d
--- /dev/null
+++ b/chrome/browser/sync/glue/app_notification_data_type_controller.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 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/glue/app_notification_data_type_controller.h"
+
+#include "base/metrics/histogram.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/extensions/app_notification_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/sync/api/syncable_service.h"
+#include "chrome/browser/sync/glue/generic_change_processor.h"
+#include "chrome/browser/sync/profile_sync_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/notification_source.h"
+
+namespace browser_sync {
+
+AppNotificationDataTypeController::AppNotificationDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service)
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
+}
+
+AppNotificationDataTypeController::~AppNotificationDataTypeController() {
+ CleanUpState();
+}
+
+syncable::ModelType AppNotificationDataTypeController::type() const {
+ return syncable::APP_NOTIFICATIONS;
+}
+
+void AppNotificationDataTypeController::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED, type);
+ registrar_.RemoveAll();
+ DCHECK_EQ(state_, MODEL_STARTING);
+ state_ = ASSOCIATING;
+ Associate();
+}
+
+// We want to start the AppNotificationManager before we begin associating.
+bool AppNotificationDataTypeController::StartModels() {
+ AppNotificationManager* manager = GetAppNotificationManager();
+ DCHECK(manager);
+ if (manager->loaded())
+ return true; // Continue to Associate().
+
+ // Add an observer and continue when the AppNotificationManager is loaded.
+ registrar_.Add(this, chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED,
+ content::Source<AppNotificationManager>(manager));
+ return false; // Don't continue Associate().
+}
+
+// Cleanup for our extra registrar usage.
+void AppNotificationDataTypeController::CleanUpState() {
+ registrar_.RemoveAll();
+}
+
+void AppNotificationDataTypeController::CreateSyncComponents() {
+ ProfileSyncFactory::SyncComponents sync_components =
+ profile_sync_factory_->CreateAppNotificationSyncComponents(
+ sync_service_, this);
+ set_model_associator(sync_components.model_associator);
+ set_change_processor(sync_components.change_processor);
+}
+
+void AppNotificationDataTypeController::RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) {
+ UMA_HISTOGRAM_COUNTS("Sync.AppNotificationRunFailures", 1);
+}
+
+void AppNotificationDataTypeController::RecordAssociationTime(
+ base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.AppNotificationAssociationTime", time);
+}
+
+void AppNotificationDataTypeController::RecordStartFailure(StartResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Sync.AppNotificationStartFailures",
+ result,
+ MAX_START_RESULT);
+}
+
+AppNotificationManager*
+AppNotificationDataTypeController::GetAppNotificationManager() {
+ return profile_->GetExtensionService()->app_notification_manager();
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/app_notification_data_type_controller.h b/chrome/browser/sync/glue/app_notification_data_type_controller.h
new file mode 100644
index 0000000..407d9c9
--- /dev/null
+++ b/chrome/browser/sync/glue/app_notification_data_type_controller.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 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_GLUE_APP_NOTIFICATION_DATA_TYPE_CONTROLLER_H__
+#define CHROME_BROWSER_SYNC_GLUE_APP_NOTIFICATION_DATA_TYPE_CONTROLLER_H__
+#pragma once
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class AppNotificationManager;
+
+namespace browser_sync {
+
+class AppNotificationDataTypeController
+ : public FrontendDataTypeController,
+ public content::NotificationObserver {
+ public:
+ AppNotificationDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service);
+ virtual ~AppNotificationDataTypeController();
+
+ // FrontendDataTypeController implementation.
+ virtual syncable::ModelType type() const OVERRIDE;
+
+ // NotificationObserver interface.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Overridden in test to control creation and init order.
+ virtual AppNotificationManager* GetAppNotificationManager();
+
+ private:
+ // FrontendDataTypeController implementations.
+ virtual bool StartModels() OVERRIDE;
+ virtual void CleanUpState() OVERRIDE;
+ virtual void CreateSyncComponents() OVERRIDE;
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) OVERRIDE;
+ virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE;
+ virtual void RecordStartFailure(StartResult result) OVERRIDE;
+
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppNotificationDataTypeController);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_APP_NOTIFICATION_DATA_TYPE_CONTROLLER_H__
diff --git a/chrome/browser/sync/glue/app_notification_data_type_controller_unittest.cc b/chrome/browser/sync/glue/app_notification_data_type_controller_unittest.cc
new file mode 100644
index 0000000..466717f
--- /dev/null
+++ b/chrome/browser/sync/glue/app_notification_data_type_controller_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2011 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 "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/task.h"
+#include "base/tracked_objects.h"
+#include "chrome/browser/extensions/app_notification_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/sync/glue/app_notification_data_type_controller.h"
+#include "chrome/browser/sync/glue/change_processor_mock.h"
+#include "chrome/browser/sync/glue/data_type_controller_mock.h"
+#include "chrome/browser/sync/glue/model_associator_mock.h"
+#include "chrome/browser/sync/profile_sync_factory_mock.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/notification_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using browser_sync::AppNotificationDataTypeController;
+using browser_sync::ChangeProcessorMock;
+using browser_sync::DataTypeController;
+using browser_sync::ModelAssociatorMock;
+using browser_sync::StartCallback;
+using testing::_;
+using testing::DoAll;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::SetArgumentPointee;
+
+class TestAppNotificationDataTypeController
+ : public AppNotificationDataTypeController {
+ public:
+ TestAppNotificationDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service)
+ : AppNotificationDataTypeController(profile_sync_factory,
+ profile,
+ sync_service),
+ manager_(new AppNotificationManager(profile_)) {
+ }
+
+ virtual AppNotificationManager* GetAppNotificationManager() {
+ return manager_.get();
+ }
+
+ private:
+ scoped_refptr<AppNotificationManager> manager_;
+};
+
+class AppNotificationDataTypeControllerTest
+ : public testing::Test {
+ public:
+ AppNotificationDataTypeControllerTest()
+ : ui_thread_(BrowserThread::UI, &ui_loop_),
+ file_thread_(BrowserThread::FILE) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ file_thread_.Start();
+
+ profile_.reset(new TestingProfile());
+ model_associator_ = new ModelAssociatorMock();
+ change_processor_ = new ChangeProcessorMock();
+ profile_sync_factory_.reset(new ProfileSyncFactoryMock(
+ model_associator_, change_processor_));
+ app_notif_dtc_ = new TestAppNotificationDataTypeController(
+ profile_sync_factory_.get(),
+ profile_.get(),
+ &service_);
+ }
+
+ virtual void TearDown() { }
+
+ protected:
+ // Waits until the file thread executes all tasks queued up so far.
+ static void WaitForFileThread() {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Post a task on the file thread that will post a task back on the
+ // UI thread to quit the loop.
+ BrowserThread::PostTask(BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&PostQuitToUIThread));
+ // Now run the message loop until quit is called.
+ MessageLoop::current()->Run();
+ }
+
+ // Posts quit task on the UI thread.
+ static void PostQuitToUIThread() {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ new MessageLoop::QuitTask());
+ }
+
+ void InitAndLoadManager() {
+ app_notif_dtc_->GetAppNotificationManager()->Init();
+ WaitForFileThread();
+ }
+
+ void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
+ EXPECT_CALL(*profile_sync_factory_,
+ CreateAppNotificationSyncComponents(_, _));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(*model_associator_, AssociateModels(_)).
+ WillRepeatedly(Return(true));
+ EXPECT_CALL(service_, ActivateDataType(_, _, _));
+ }
+
+ void SetStopExpectations() {
+ EXPECT_CALL(service_, DeactivateDataType(_));
+ EXPECT_CALL(*model_associator_, DisassociateModels(_));
+ }
+
+ void PumpLoop() {
+ ui_loop_.RunAllPending();
+ }
+
+ MessageLoop ui_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread file_thread_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_refptr<TestAppNotificationDataTypeController> app_notif_dtc_;
+ scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_;
+ ProfileSyncServiceMock service_;
+ ModelAssociatorMock* model_associator_;
+ ChangeProcessorMock* change_processor_;
+ StartCallback start_callback_;
+};
+
+// When notification manager is ready, sync assocation should happen
+// successfully.
+TEST_F(AppNotificationDataTypeControllerTest, StartManagerReady) {
+ InitAndLoadManager();
+
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, app_notif_dtc_->state());
+ SetAssociateExpectations();
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, app_notif_dtc_->state());
+}
+
+// When notification manager is not ready, sync assocation should wait
+// until loaded event is seen.
+TEST_F(AppNotificationDataTypeControllerTest, StartManagerNotReady) {
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, app_notif_dtc_->state());
+ SetAssociateExpectations();
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::MODEL_STARTING, app_notif_dtc_->state());
+
+ // Unblock file thread and wait for it to finish all tasks.
+ // Send the notification that the TemplateURLService has started.
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED,
+ content::Source<AppNotificationManager>(
+ app_notif_dtc_->GetAppNotificationManager()),
+ content::NotificationService::NoDetails());
+ EXPECT_EQ(DataTypeController::RUNNING, app_notif_dtc_->state());
+}
+
+TEST_F(AppNotificationDataTypeControllerTest, StartFirstRun) {
+ InitAndLoadManager();
+ SetAssociateExpectations();
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+}
+
+TEST_F(AppNotificationDataTypeControllerTest, StartOk) {
+ InitAndLoadManager();
+ SetAssociateExpectations();
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+}
+
+TEST_F(AppNotificationDataTypeControllerTest, StartAssociationFailed) {
+ InitAndLoadManager();
+ EXPECT_CALL(*profile_sync_factory_,
+ CreateAppNotificationSyncComponents(_, _));
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(*model_associator_, AssociateModels(_)).
+ WillRepeatedly(DoAll(
+ browser_sync::SetSyncError(syncable::APP_NOTIFICATIONS),
+ Return(false)));
+
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::DISABLED, app_notif_dtc_->state());
+}
+
+TEST_F(AppNotificationDataTypeControllerTest,
+ StartAssociationTriggersUnrecoverableError) {
+ InitAndLoadManager();
+ // Set up association to fail with an unrecoverable error.
+ EXPECT_CALL(*profile_sync_factory_,
+ CreateAppNotificationSyncComponents(_, _));
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, app_notif_dtc_->state());
+}
+
+TEST_F(AppNotificationDataTypeControllerTest, Stop) {
+ InitAndLoadManager();
+ SetAssociateExpectations();
+ SetStopExpectations();
+
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, app_notif_dtc_->state());
+
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, app_notif_dtc_->state());
+ app_notif_dtc_->Stop();
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, app_notif_dtc_->state());
+}
+
+TEST_F(AppNotificationDataTypeControllerTest, OnUnrecoverableError) {
+ InitAndLoadManager();
+ SetAssociateExpectations();
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(service_, OnUnrecoverableError(_, _)).
+ WillOnce(InvokeWithoutArgs(app_notif_dtc_.get(),
+ &AppNotificationDataTypeController::Stop));
+ SetStopExpectations();
+
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
+ app_notif_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ // This should cause app_notif_dtc_->Stop() to be called.
+ app_notif_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
+ PumpLoop();
+}
diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h
index b65fe21..d956d3c 100644
--- a/chrome/browser/sync/profile_sync_factory.h
+++ b/chrome/browser/sync/profile_sync_factory.h
@@ -172,6 +172,13 @@ class ProfileSyncFactory {
virtual SyncComponents CreateSearchEngineSyncComponents(
ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler) = 0;
+
+ // Instantiates both a model associator and change processor for the app
+ // notification data type. The pointers in the return struct are owned by the
+ // caller.
+ virtual SyncComponents CreateAppNotificationSyncComponents(
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler) = 0;
};
#endif // CHROME_BROWSER_SYNC_PROFILE_SYNC_FACTORY_H__
diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc
index ede961c..c219624 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_factory_impl.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/command_line.h"
+#include "chrome/browser/extensions/app_notification_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_settings_backend.h"
#include "chrome/browser/prefs/pref_model_associator.h"
@@ -11,6 +12,7 @@
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/sync/api/syncable_service.h"
#include "chrome/browser/sync/glue/app_data_type_controller.h"
+#include "chrome/browser/sync/glue/app_notification_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
#include "chrome/browser/sync/glue/autofill_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_model_associator.h"
@@ -49,6 +51,7 @@
#include "content/browser/browser_thread.h"
using browser_sync::AppDataTypeController;
+using browser_sync::AppNotificationDataTypeController;
using browser_sync::AutofillChangeProcessor;
using browser_sync::AutofillDataTypeController;
using browser_sync::AutofillProfileDataTypeController;
@@ -177,6 +180,13 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) {
pss->RegisterDataTypeController(
new SearchEngineDataTypeController(this, profile_, pss));
}
+
+ // App notifications sync is disabled by default. Register only if
+ // explicitly enabled.
+ if (command_line_->HasSwitch(switches::kEnableSyncAppNotifications)) {
+ pss->RegisterDataTypeController(
+ new AppNotificationDataTypeController(this, profile_, pss));
+ }
}
DataTypeManager* ProfileSyncFactoryImpl::CreateDataTypeManager(
@@ -383,3 +393,22 @@ ProfileSyncFactoryImpl::CreateSearchEngineSyncComponents(
change_processor);
return SyncComponents(sync_service_adapter, change_processor);
}
+
+ProfileSyncFactory::SyncComponents
+ProfileSyncFactoryImpl::CreateAppNotificationSyncComponents(
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler) {
+ base::WeakPtr<SyncableService> notif_sync_service =
+ profile_->GetExtensionService()->app_notification_manager()->AsWeakPtr();
+ DCHECK(notif_sync_service);
+ sync_api::UserShare* user_share = profile_sync_service->GetUserShare();
+ GenericChangeProcessor* change_processor =
+ new GenericChangeProcessor(error_handler,
+ notif_sync_service,
+ user_share);
+ SyncableServiceAdapter* sync_service_adapter =
+ new SyncableServiceAdapter(syncable::APP_NOTIFICATIONS,
+ notif_sync_service,
+ change_processor);
+ return SyncComponents(sync_service_adapter, change_processor);
+}
diff --git a/chrome/browser/sync/profile_sync_factory_impl.h b/chrome/browser/sync/profile_sync_factory_impl.h
index 27063a5..f7e7494 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.h
+++ b/chrome/browser/sync/profile_sync_factory_impl.h
@@ -88,6 +88,10 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory {
ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler);
+ virtual SyncComponents CreateAppNotificationSyncComponents(
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler);
+
private:
Profile* profile_;
CommandLine* command_line_;
diff --git a/chrome/browser/sync/profile_sync_factory_mock.cc b/chrome/browser/sync/profile_sync_factory_mock.cc
index 46bcddc..fc3af4f 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.cc
+++ b/chrome/browser/sync/profile_sync_factory_mock.cc
@@ -27,6 +27,11 @@ ProfileSyncFactoryMock::ProfileSyncFactoryMock(
InvokeWithoutArgs(
this,
&ProfileSyncFactoryMock::MakeSyncComponents));
+ ON_CALL(*this, CreateAppNotificationSyncComponents(_, _)).
+ WillByDefault(
+ InvokeWithoutArgs(
+ this,
+ &ProfileSyncFactoryMock::MakeSyncComponents));
}
ProfileSyncFactoryMock::~ProfileSyncFactoryMock() {}
diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h
index efbb7b9..0d184d1 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.h
+++ b/chrome/browser/sync/profile_sync_factory_mock.h
@@ -84,6 +84,10 @@ class ProfileSyncFactoryMock : public ProfileSyncFactory {
SyncComponents(
ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler));
+ MOCK_METHOD2(CreateAppNotificationSyncComponents,
+ SyncComponents(
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler));
private:
SyncComponents MakeSyncComponents();
diff --git a/chrome/browser/sync/resources/configure.html b/chrome/browser/sync/resources/configure.html
index 2ce7d3b..6bb13e6 100644
--- a/chrome/browser/sync/resources/configure.html
+++ b/chrome/browser/sync/resources/configure.html
@@ -373,6 +373,13 @@ html[os='mac'] input[type='submit'] {
} else {
document.getElementById("sessionsItem").className = "sync-item-hide";
}
+ if (args.appNotificationsRegistered) {
+ document.getElementById("appNotificationsCheckbox").checked =
+ args.syncAppNotifications;
+ document.getElementById("appNotificationsItem").className = "sync-item-show";
+ } else {
+ document.getElementById("appNotificationsItem").className = "sync-item-hide";
+ }
setCheckboxesToKeepEverythingSynced(args.syncAllDataTypes);
}
@@ -465,6 +472,7 @@ html[os='mac'] input[type='submit'] {
"syncApps": syncAll || f.appsCheckbox.checked,
"syncSearchEngines": syncAll || f.searchEnginesCheckbox.checked,
"syncSessions": syncAll || f.sessionsCheckbox.checked,
+ "syncAppNotifications": syncAll || f.appNotificationsCheckbox.checked,
"usePassphrase": (getRadioCheckedValue() == 'explicit'),
"passphrase": f.passphrase.value
});
@@ -625,7 +633,13 @@ html[os='mac'] input[type='submit'] {
<label id="searchEnginesCheckboxLabel" name="dataTypeLabel"
for="searchEnginesCheckbox" i18n-content="searchengines"
il8n-values="title:searchengines"></label>
- </div>
+ </div>
+ <div class="sync-item-show" id="appNotificationsItem">
+ <input id="appNotificationsCheckbox" name="dataTypeCheckbox" type="checkbox">
+ <label id="appNotificationsCheckboxLabel" name="dataTypeLabel"
+ for="appNotificationsCheckbox" i18n-content="appnotifications"
+ il8n-values="title:appnotifications"></label>
+ </div>
</div>
</div>
</div>
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index e831831..a4d13b0 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -142,6 +142,8 @@ void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
registered_types.count(syncable::SEARCH_ENGINES) > 0);
args->SetBoolean("sessionsRegistered",
registered_types.count(syncable::SESSIONS) > 0);
+ args->SetBoolean("appNotificationsRegistered",
+ registered_types.count(syncable::APP_NOTIFICATIONS) > 0);
args->SetBoolean("syncBookmarks",
service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
args->SetBoolean("syncPreferences",
@@ -162,6 +164,8 @@ void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
args->SetBoolean("syncApps",
service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
+ args->SetBoolean("syncAppNotifications",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAppNotifications));
args->SetBoolean("encryptionEnabled",
!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableSyncEncryption));
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
index bbe7673..e08c91d 100644
--- a/chrome/browser/sync/sync_setup_wizard_unittest.cc
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -314,7 +314,8 @@ TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
"\"syncPreferences\":true,\"syncThemes\":false,\"syncPasswords\":false,"
"\"syncAutofill\":false,\"syncExtensions\":false,\"syncTypedUrls\":true,"
"\"syncApps\":true,\"syncSearchEngines\":false,\"syncSessions\":false,"
- "\"usePassphrase\":false,\"encryptAllData\":false}";
+ "\"syncAppNotifications\":false,\"usePassphrase\":false,"
+ "\"encryptAllData\":false}";
data_type_choices_value.Append(new StringValue(data_type_choices));
// Simulate the user choosing data types; bookmarks, prefs, typed URLS, and
@@ -333,6 +334,8 @@ TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
EXPECT_EQ(1U, service_->chosen_data_types_.count(syncable::TYPED_URLS));
EXPECT_EQ(1U, service_->chosen_data_types_.count(syncable::APPS));
EXPECT_EQ(0U, service_->chosen_data_types_.count(syncable::SEARCH_ENGINES));
+ EXPECT_EQ(0U, service_->chosen_data_types_.count(
+ syncable::APP_NOTIFICATIONS));
}
TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 6ba45e8..ff6e9df 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -308,6 +308,8 @@ void NTPResourceCache::CreateNewTabHTML() {
l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_SEARCH_ENGINES));
localized_strings.SetString("foreignsessions",
l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_TABS));
+ localized_strings.SetString("appnotifications",
+ l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_APP_NOTIFICATIONS));
localized_strings.SetString("closedwindowmultiple",
l10n_util::GetStringUTF16(IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE));
localized_strings.SetString("attributionintro",
diff --git a/chrome/browser/ui/webui/options/personal_options_handler.cc b/chrome/browser/ui/webui/options/personal_options_handler.cc
index 80bdd68..acc3da7 100644
--- a/chrome/browser/ui/webui/options/personal_options_handler.cc
+++ b/chrome/browser/ui/webui/options/personal_options_handler.cc
@@ -189,6 +189,8 @@ void PersonalOptionsHandler::GetLocalizedValues(
l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_SEARCH_ENGINES));
localized_strings->SetString("syncsessions",
l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_TABS));
+ localized_strings->SetString("syncappnotifications",
+ l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_APP_NOTIFICATIONS));
#if defined(OS_CHROMEOS)
localized_strings->SetString("account",
diff --git a/chrome/browser/ui/webui/sync_setup_handler.cc b/chrome/browser/ui/webui/sync_setup_handler.cc
index 414743b..82cc1f5 100644
--- a/chrome/browser/ui/webui/sync_setup_handler.cc
+++ b/chrome/browser/ui/webui/sync_setup_handler.cc
@@ -138,6 +138,12 @@ bool GetConfiguration(const std::string& json, SyncConfiguration* config) {
if (sync_apps)
config->data_types.insert(syncable::APPS);
+ bool sync_app_notifications;
+ if (!result->GetBoolean("syncAppNotifications", &sync_app_notifications))
+ return false;
+ if (sync_app_notifications)
+ config->data_types.insert(syncable::APP_NOTIFICATIONS);
+
// Encryption settings.
if (!result->GetBoolean("encryptAllData", &config->encrypt_all))
return false;
@@ -279,6 +285,7 @@ void SyncSetupHandler::GetStaticLocalizedValues(
{ "apps", IDS_SYNC_DATATYPE_APPS },
{ "searchEngines", IDS_SYNC_DATATYPE_SEARCH_ENGINES },
{ "openTabs", IDS_SYNC_DATATYPE_TABS },
+ { "appNotifications", IDS_SYNC_DATATYPE_APP_NOTIFICATIONS },
{ "syncZeroDataTypesError", IDS_SYNC_ZERO_DATA_TYPES_ERROR },
{ "serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR },
{ "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL },
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b251337..95ee107 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2137,6 +2137,8 @@
'browser/status_icons/status_tray.h',
'browser/sync/glue/app_data_type_controller.cc',
'browser/sync/glue/app_data_type_controller.h',
+ 'browser/sync/glue/app_notification_data_type_controller.cc',
+ 'browser/sync/glue/app_notification_data_type_controller.h',
'browser/sync/glue/autofill_change_processor.cc',
'browser/sync/glue/autofill_change_processor.h',
'browser/sync/glue/autofill_data_type_controller.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 425ee2b..bc8ee41 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1523,6 +1523,7 @@
'browser/sync/backend_migrator_unittest.cc',
'browser/sync/internal_api/read_node_mock.cc',
'browser/sync/internal_api/read_node_mock.h',
+ 'browser/sync/glue/app_notification_data_type_controller_unittest.cc',
'browser/sync/glue/autofill_data_type_controller_unittest.cc',
'browser/sync/glue/autofill_model_associator_unittest.cc',
'browser/sync/glue/bookmark_data_type_controller_unittest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 283849c..fe98f7f 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -537,6 +537,9 @@ const char kEnableSyncTabs[] = "enable-sync-tabs";
const char kEnableSyncTabsForOtherClients[] =
"enable-sync-tabs-for-other-clients";
+// Enable syncing app notifications.
+const char kEnableSyncAppNotifications[] = "enable-sync-app-notifications";
+
// Enables context menu for selecting groups of tabs.
const char kEnableTabGroupsContextMenu[] = "enable-tab-groups-context-menu";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index e2b13f4..1d07572 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -156,6 +156,7 @@ extern const char kEnableSyncOAuth[];
extern const char kEnableSyncSearchEngines[];
extern const char kEnableSyncTabs[];
extern const char kEnableSyncTabsForOtherClients[];
+extern const char kEnableSyncAppNotifications[];
extern const char kEnableSyncedBookmarksFolder[];
extern const char kEnableTabGroupsContextMenu[];
extern const char kEnableTopSites[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 0e9dfe4..e51f1c1 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1375,6 +1375,7 @@ const char kSyncBookmarks[] = "sync.bookmarks";
const char kSyncPasswords[] = "sync.passwords";
const char kSyncPreferences[] = "sync.preferences";
const char kSyncApps[] = "sync.apps";
+const char kSyncAppNotifications[] = "sync.app_notifications";
const char kSyncAutofill[] = "sync.autofill";
const char kSyncAutofillProfile[] = "sync.autofill_profile";
const char kSyncThemes[] = "sync.themes";
@@ -1383,7 +1384,6 @@ const char kSyncExtensions[] = "sync.extensions";
const char kSyncExtensionSettings[] = "sync.extension_settings";
const char kSyncSearchEngines[] = "sync.search_engines";
const char kSyncSessions[] = "sync.sessions";
-const char kSyncAppNotifications[] = "sync.app_notifications";
// Boolean used by enterprise configuration management in order to lock down
// sync.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 12da82a..6d78ba1 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -492,6 +492,7 @@ extern const char kSyncKeepEverythingSynced[];
extern const char kSyncBookmarks[];
extern const char kSyncPasswords[];
extern const char kSyncPreferences[];
+extern const char kSyncAppNotifications[];
extern const char kSyncApps[];
extern const char kSyncAutofill[];
extern const char kSyncAutofillProfile[];
@@ -502,7 +503,6 @@ extern const char kSyncExtensionSettings[];
extern const char kSyncManaged[];
extern const char kSyncSearchEngines[];
extern const char kSyncSessions[];
-extern const char kSyncAppNotifications[];
extern const char kSyncSuppressStart[];
extern const char kGoogleServicesUsername[];
extern const char kSyncUsingOAuth[];