- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / sync_notifier / chrome_notifier_service.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // The ChromeNotifierService works together with sync to maintain the state of
6 // user notifications, which can then be presented in the notification center,
7 // via the Notification UI Manager.
8
9 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
10
11 #include <string>
12 #include <vector>
13
14 #include "base/command_line.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/values.h"
18 #include "chrome/browser/notifications/desktop_notification_service.h"
19 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
20 #include "chrome/browser/notifications/notification.h"
21 #include "chrome/browser/notifications/notification_ui_manager.h"
22 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/pref_names.h"
25 #include "components/user_prefs/pref_registry_syncable.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/user_metrics.h"
28 #include "grit/generated_resources.h"
29 #include "grit/theme_resources.h"
30 #include "sync/api/sync_change.h"
31 #include "sync/api/sync_change_processor.h"
32 #include "sync/api/sync_error_factory.h"
33 #include "sync/protocol/sync.pb.h"
34 #include "sync/protocol/synced_notification_specifics.pb.h"
35 #include "third_party/WebKit/public/web/WebTextDirection.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/message_center/notifier_settings.h"
39 #include "url/gurl.h"
40
41 using content::UserMetricsAction;
42
43 namespace notifier {
44
45 const char kFirstSyncedNotificationServiceId[] = "Google+";
46
47 bool ChromeNotifierService::avoid_bitmap_fetching_for_test_ = false;
48
49 ChromeNotifierService::ChromeNotifierService(Profile* profile,
50                                              NotificationUIManager* manager)
51     : profile_(profile), notification_manager_(manager),
52       synced_notification_first_run_(false) {
53   InitializePrefs();
54   AddNewSendingServices();
55 }
56 ChromeNotifierService::~ChromeNotifierService() {}
57
58 // Methods from BrowserContextKeyedService.
59 void ChromeNotifierService::Shutdown() {}
60
61 // syncer::SyncableService implementation.
62
63 // This is called at startup to sync with the server.
64 // This code is not thread safe.
65 syncer::SyncMergeResult ChromeNotifierService::MergeDataAndStartSyncing(
66       syncer::ModelType type,
67       const syncer::SyncDataList& initial_sync_data,
68       scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
69       scoped_ptr<syncer::SyncErrorFactory> error_handler) {
70   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
71   DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
72   syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATIONS);
73   // A list of local changes to send up to the sync server.
74   syncer::SyncChangeList new_changes;
75   sync_processor_ = sync_processor.Pass();
76
77   for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
78        it != initial_sync_data.end(); ++it) {
79     const syncer::SyncData& sync_data = *it;
80     DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
81
82     // Build a local notification object from the sync data.
83     scoped_ptr<SyncedNotification> incoming(CreateNotificationFromSyncData(
84         sync_data));
85     if (!incoming) {
86       // TODO(petewil): Turn this into a NOTREACHED() call once we fix the
87       // underlying problem causing bad data.
88       LOG(WARNING) << "Badly formed sync data in incoming notification";
89       continue;
90     }
91
92     // Process each incoming remote notification.
93     const std::string& key = incoming->GetKey();
94     DCHECK_GT(key.length(), 0U);
95     SyncedNotification* found = FindNotificationById(key);
96
97     if (NULL == found) {
98       // If there are no conflicts, copy in the data from remote.
99       Add(incoming.Pass());
100     } else {
101       // If the incoming (remote) and stored (local) notifications match
102       // in all fields, we don't need to do anything here.
103       if (incoming->EqualsIgnoringReadState(*found)) {
104
105         if (incoming->GetReadState() == found->GetReadState()) {
106           // Notification matches on the client and the server, nothing to do.
107           continue;
108         } else  {
109           // If the read state is different, read wins for both places.
110           if (incoming->GetReadState() == SyncedNotification::kDismissed) {
111             // If it is marked as read on the server, but not the client.
112             found->NotificationHasBeenDismissed();
113             // Tell the Notification UI Manager to remove it.
114             notification_manager_->CancelById(found->GetKey());
115           } else if (incoming->GetReadState() == SyncedNotification::kRead) {
116             // If it is marked as read on the server, but not the client.
117             found->NotificationHasBeenRead();
118             // Tell the Notification UI Manager to remove it.
119             notification_manager_->CancelById(found->GetKey());
120           } else {
121             // If it is marked as read on the client, but not the server.
122             syncer::SyncData sync_data = CreateSyncDataFromNotification(*found);
123             new_changes.push_back(
124                 syncer::SyncChange(FROM_HERE,
125                                    syncer::SyncChange::ACTION_UPDATE,
126                                    sync_data));
127           }
128           // If local state changed, notify Notification UI Manager.
129         }
130       // For any other conflict besides read state, treat it as an update.
131       } else {
132         // If different, just replace the local with the remote.
133         // TODO(petewil): Someday we may allow changes from the client to
134         // flow upwards, when we do, we will need better merge resolution.
135         found->Update(sync_data);
136
137         // Tell the notification manager to update the notification.
138         UpdateInMessageCenter(found);
139       }
140     }
141   }
142
143   // Send up the changes that were made locally.
144   if (new_changes.size() > 0) {
145     merge_result.set_error(sync_processor_->ProcessSyncChanges(
146         FROM_HERE, new_changes));
147   }
148
149   // Once we complete our first sync, we mark "first run" as false,
150   // subsequent runs of Synced Notifications will get normal treatment.
151   if (synced_notification_first_run_) {
152     synced_notification_first_run_ = false;
153     profile_->GetPrefs()->SetBoolean(prefs::kSyncedNotificationFirstRun, false);
154   }
155
156   return merge_result;
157 }
158
159 void ChromeNotifierService::StopSyncing(syncer::ModelType type) {
160   DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
161   // Since this data type is not user-unselectable, we chose not to implement
162   // the stop syncing method, and instead do nothing here.
163 }
164
165 syncer::SyncDataList ChromeNotifierService::GetAllSyncData(
166       syncer::ModelType type) const {
167   DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
168   syncer::SyncDataList sync_data;
169
170   // Copy our native format data into a SyncDataList format.
171   ScopedVector<SyncedNotification>::const_iterator it =
172       notification_data_.begin();
173   for (; it != notification_data_.end(); ++it) {
174     sync_data.push_back(CreateSyncDataFromNotification(**it));
175   }
176
177   return sync_data;
178 }
179
180 // This method is called when there is an incoming sync change from the server.
181 syncer::SyncError ChromeNotifierService::ProcessSyncChanges(
182       const tracked_objects::Location& from_here,
183       const syncer::SyncChangeList& change_list) {
184   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
185   syncer::SyncError error;
186
187   for (syncer::SyncChangeList::const_iterator it = change_list.begin();
188        it != change_list.end(); ++it) {
189     syncer::SyncData sync_data = it->sync_data();
190     DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
191     syncer::SyncChange::SyncChangeType change_type = it->change_type();
192
193     scoped_ptr<SyncedNotification> new_notification(
194         CreateNotificationFromSyncData(sync_data));
195     if (!new_notification.get()) {
196       NOTREACHED() << "Failed to read notification.";
197       continue;
198     }
199
200     const std::string& key = new_notification->GetKey();
201     DCHECK_GT(key.length(), 0U);
202     SyncedNotification* found = FindNotificationById(key);
203
204     switch (change_type) {
205       case syncer::SyncChange::ACTION_ADD:
206         // Intentional fall through, cases are identical.
207       case syncer::SyncChange::ACTION_UPDATE:
208         if (found == NULL) {
209           Add(new_notification.Pass());
210           break;
211         }
212         // Update it in our store.
213         found->Update(sync_data);
214         // Tell the notification manager to update the notification.
215         UpdateInMessageCenter(found);
216         break;
217
218       case syncer::SyncChange::ACTION_DELETE:
219         if (found == NULL) {
220           break;
221         }
222         // Remove it from our store.
223         FreeNotificationById(key);
224         // Remove it from the message center.
225         UpdateInMessageCenter(new_notification.get());
226         // TODO(petewil): Do I need to remember that it was deleted in case the
227         // add arrives after the delete?  If so, how long do I need to remember?
228         break;
229
230       default:
231         NOTREACHED();
232         break;
233     }
234   }
235
236   return error;
237 }
238
239 // Support functions for data type conversion.
240
241 // Static method.  Get to the sync data in our internal format.
242 syncer::SyncData ChromeNotifierService::CreateSyncDataFromNotification(
243     const SyncedNotification& notification) {
244   // Construct the sync_data using the specifics from the notification.
245   return syncer::SyncData::CreateLocalData(
246       notification.GetKey(), notification.GetKey(),
247       notification.GetEntitySpecifics());
248 }
249
250 // Static Method.  Convert from SyncData to our internal format.
251 scoped_ptr<SyncedNotification>
252     ChromeNotifierService::CreateNotificationFromSyncData(
253         const syncer::SyncData& sync_data) {
254   // Get a pointer to our data within the sync_data object.
255   sync_pb::SyncedNotificationSpecifics specifics =
256       sync_data.GetSpecifics().synced_notification();
257
258   // Check for mandatory fields in the sync_data object.
259   if (!specifics.has_coalesced_notification() ||
260       !specifics.coalesced_notification().has_key() ||
261       !specifics.coalesced_notification().has_read_state()) {
262     DVLOG(1) << "Synced Notification missing mandatory fields "
263              << "has coalesced notification? "
264              << specifics.has_coalesced_notification()
265              << " has key? " << specifics.coalesced_notification().has_key()
266              << " has read state? "
267              << specifics.coalesced_notification().has_read_state();
268     return scoped_ptr<SyncedNotification>();
269   }
270
271   bool is_well_formed_unread_notification =
272       (static_cast<SyncedNotification::ReadState>(
273           specifics.coalesced_notification().read_state()) ==
274        SyncedNotification::kUnread &&
275        specifics.coalesced_notification().has_render_info());
276   bool is_well_formed_read_notification =
277       (static_cast<SyncedNotification::ReadState>(
278           specifics.coalesced_notification().read_state()) ==
279        SyncedNotification::kRead);
280   bool is_well_formed_dismissed_notification =
281       (static_cast<SyncedNotification::ReadState>(
282           specifics.coalesced_notification().read_state()) ==
283        SyncedNotification::kDismissed);
284
285   // If the notification is poorly formed, return a null pointer.
286   if (!is_well_formed_unread_notification &&
287       !is_well_formed_read_notification &&
288       !is_well_formed_dismissed_notification) {
289     DVLOG(1) << "Synced Notification is not well formed."
290              << " unread well formed? "
291              << is_well_formed_unread_notification
292              << " dismissed well formed? "
293              << is_well_formed_dismissed_notification
294              << " read well formed? "
295              << is_well_formed_read_notification;
296     return scoped_ptr<SyncedNotification>();
297   }
298
299   // Create a new notification object based on the supplied sync_data.
300   scoped_ptr<SyncedNotification> notification(
301       new SyncedNotification(sync_data));
302
303   return notification.Pass();
304 }
305
306 // This returns a pointer into a vector that we own.  Caller must not free it.
307 // Returns NULL if no match is found.
308 SyncedNotification* ChromeNotifierService::FindNotificationById(
309     const std::string& notification_id) {
310   // TODO(petewil): We can make a performance trade off here.
311   // While the vector has good locality of reference, a map has faster lookup.
312   // Based on how big we expect this to get, maybe change this to a map.
313   ScopedVector<SyncedNotification>::const_iterator it =
314       notification_data_.begin();
315   for (; it != notification_data_.end(); ++it) {
316     SyncedNotification* notification = *it;
317     if (notification_id == notification->GetKey())
318       return *it;
319   }
320
321   return NULL;
322 }
323
324 void ChromeNotifierService::FreeNotificationById(
325     const std::string& notification_id) {
326   ScopedVector<SyncedNotification>::iterator it = notification_data_.begin();
327   for (; it != notification_data_.end(); ++it) {
328     SyncedNotification* notification = *it;
329     if (notification_id == notification->GetKey()) {
330       notification_data_.erase(it);
331       return;
332     }
333   }
334 }
335
336 void ChromeNotifierService::GetSyncedNotificationServices(
337     std::vector<message_center::Notifier*>* notifiers) {
338   // TODO(mukai|petewil): Check the profile's eligibility before adding the
339   // sample app.
340
341   // TODO(petewil): Really obtain the list of synced notification sending
342   // services from the server and create the list of ids here.  Until then, we
343   // are hardcoding the service names.  Once that is done, remove this
344   // hardcoding.
345   // crbug.com/248337
346   DesktopNotificationService* desktop_notification_service =
347       DesktopNotificationServiceFactory::GetForProfile(profile_);
348   message_center::NotifierId notifier_id(
349       message_center::NotifierId::SYNCED_NOTIFICATION_SERVICE,
350       kFirstSyncedNotificationServiceId);
351   message_center::Notifier* notifier_service = new message_center::Notifier(
352       notifier_id,
353       l10n_util::GetStringUTF16(
354           IDS_FIRST_SYNCED_NOTIFICATION_SERVICE_NAME),
355       desktop_notification_service->IsNotifierEnabled(notifier_id));
356
357   // Add icons for our sending services.
358   // TODO(petewil): Replace this temporary hardcoding with a new sync datatype
359   // to dynamically get the name and icon for each synced notification sending
360   // service.  Until then, we use hardcoded service icons for all services.
361   // crbug.com/248337
362   notifier_service->icon = ui::ResourceBundle::GetSharedInstance().
363       GetImageNamed(IDR_TEMPORARY_GOOGLE_PLUS_ICON);
364
365   // Enable or disable the sending service per saved settings.
366   std::set<std::string>::iterator iter;
367
368   iter = find(enabled_sending_services_.begin(),
369               enabled_sending_services_.end(),
370               notifier_id.id);
371   if (iter != enabled_sending_services_.end())
372     notifier_service->enabled = true;
373   else
374     notifier_service->enabled = false;
375
376   notifiers->push_back(notifier_service);
377 }
378
379 void ChromeNotifierService::MarkNotificationAsRead(
380     const std::string& key) {
381   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
382   SyncedNotification* notification = FindNotificationById(key);
383   CHECK(notification != NULL);
384
385   notification->NotificationHasBeenRead();
386   syncer::SyncChangeList new_changes;
387
388   syncer::SyncData sync_data = CreateSyncDataFromNotification(*notification);
389   new_changes.push_back(
390       syncer::SyncChange(FROM_HERE,
391                          syncer::SyncChange::ACTION_UPDATE,
392                          sync_data));
393
394   // Send up the changes that were made locally.
395   sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
396 }
397
398 // Add a new notification to our data structure.  This takes ownership
399 // of the passed in pointer.
400 void ChromeNotifierService::Add(scoped_ptr<SyncedNotification> notification) {
401   SyncedNotification* notification_copy = notification.get();
402   // Take ownership of the object and put it into our local storage.
403   notification_data_.push_back(notification.release());
404
405   // If the user is not interested in this type of notification, ignore it.
406   std::set<std::string>::iterator iter =
407       find(enabled_sending_services_.begin(),
408            enabled_sending_services_.end(),
409            notification_copy->GetSendingServiceId());
410   if (iter == enabled_sending_services_.end()) {
411     return;
412   }
413
414   UpdateInMessageCenter(notification_copy);
415 }
416
417 void ChromeNotifierService::AddForTest(
418     scoped_ptr<notifier::SyncedNotification> notification) {
419   notification_data_.push_back(notification.release());
420 }
421
422 void ChromeNotifierService::UpdateInMessageCenter(
423     SyncedNotification* notification) {
424   // If the feature is disabled, exit now.
425   if (!notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
426       CommandLine::ForCurrentProcess()))
427     return;
428
429   notification->LogNotification();
430
431   if (notification->GetReadState() == SyncedNotification::kUnread) {
432     // If the message is unread, update it.
433     Display(notification);
434   } else {
435     // If the message is read or deleted, dismiss it from the center.
436     // We intentionally ignore errors if it is not in the center.
437     notification_manager_->CancelById(notification->GetKey());
438   }
439 }
440
441 void ChromeNotifierService::Display(SyncedNotification* notification) {
442   // Set up to fetch the bitmaps.
443   notification->QueueBitmapFetchJobs(notification_manager_,
444                                      this,
445                                      profile_);
446
447   // Our tests cannot use the network for reliability reasons.
448   if (avoid_bitmap_fetching_for_test_) {
449     return;
450   }
451
452   // If this is the first run for the feature, don't surprise the user.
453   // Instead, place all backlogged notifications into the notification
454   // center.
455   if (synced_notification_first_run_) {
456     // Setting the toast state to false will prevent toasting the notification.
457     notification->SetToastState(false);
458   }
459
460   // Start the bitmap fetching, Show() will be called when the last bitmap
461   // either arrives or times out.
462   notification->StartBitmapFetch();
463 }
464
465 void ChromeNotifierService::OnSyncedNotificationServiceEnabled(
466     const std::string& notifier_id, bool enabled) {
467   std::set<std::string>::iterator iter;
468
469   // Make a copy of the notifier_id, which might not have lifetime long enough
470   // for this function to finish all of its work.
471   std::string notifier_id_copy(notifier_id);
472
473   iter = find(enabled_sending_services_.begin(),
474               enabled_sending_services_.end(),
475               notifier_id_copy);
476
477   base::ListValue synced_notification_services;
478
479   // Add the notifier_id if it is enabled and not already there.
480   if (iter == enabled_sending_services_.end() && enabled) {
481     enabled_sending_services_.insert(notifier_id_copy);
482     // Check now for any outstanding notifications.
483     DisplayUnreadNotificationsFromSource(notifier_id);
484     BuildServiceListValueInplace(enabled_sending_services_,
485                                  &synced_notification_services);
486     // Add this preference to the enabled list.
487     profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
488                               synced_notification_services);
489   // Remove the notifier_id if it is disabled and present.
490   } else if (iter != enabled_sending_services_.end() && !enabled) {
491     enabled_sending_services_.erase(iter);
492     BuildServiceListValueInplace(enabled_sending_services_,
493                                  &synced_notification_services);
494     // Remove this peference from the enabled list.
495     profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
496                               synced_notification_services);
497     RemoveUnreadNotificationsFromSource(notifier_id_copy);
498   }
499
500   // Collect UMA statistics when a service is enabled or disabled.
501   if (enabled) {
502     content::RecordAction(
503         UserMetricsAction("SyncedNotifications.SendingServiceEnabled"));
504   } else {
505     content::RecordAction(
506         UserMetricsAction("SyncedNotifications.SendingServiceDisabled"));
507   }
508
509   // Collect individual service enabling/disabling statistics.
510   CollectPerServiceEnablingStatistics(notifier_id, enabled);
511
512   return;
513 }
514
515 void ChromeNotifierService::CollectPerServiceEnablingStatistics(
516     const std::string& notifier_id,
517     bool enabled) {
518   // TODO(petewil) - This approach does not scale well as we add new services,
519   // but we are limited to using predefined ENUM values in histogram based UMA
520   // data, which does not permit arbitrary strings.
521   // Find a way to make it scale, or remove enum value this when we have enough
522   // data.
523
524   ChromeNotifierServiceActionType action =
525       CHROME_NOTIFIER_SERVICE_ACTION_UNKNOWN;
526
527   // Derive action type from notifier_id and enabled.
528   // TODO(petewil): Add more sending services as they are enabled.
529   if (notifier_id == std::string(kFirstSyncedNotificationServiceId)) {
530     action = enabled
531         ? CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_ENABLED
532         : CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_DISABLED;
533   }
534
535   UMA_HISTOGRAM_ENUMERATION("ChromeNotifierService.Actions",
536                             action,
537                             CHROME_NOTIFIER_SERVICE_ACTION_COUNT);
538 }
539
540 void ChromeNotifierService::BuildServiceListValueInplace(
541     std::set<std::string> services, base::ListValue* list_value) {
542   std::set<std::string>::iterator iter;
543
544   // Iterate over the strings, adding each one to the list value
545   for (iter = services.begin();
546        iter != services.end();
547        ++iter) {
548     base::StringValue* string_value(new base::StringValue(*iter));
549     list_value->Append(string_value);
550
551   }
552 }
553
554 void ChromeNotifierService::DisplayUnreadNotificationsFromSource(
555     const std::string& notifier_id) {
556   for (std::vector<SyncedNotification*>::const_iterator iter =
557           notification_data_.begin();
558        iter != notification_data_.end();
559        ++iter) {
560     if ((*iter)->GetSendingServiceId() == notifier_id &&
561         (*iter)->GetReadState() == SyncedNotification::kUnread)
562       Display(*iter);
563   }
564 }
565
566 void ChromeNotifierService::RemoveUnreadNotificationsFromSource(
567     const std::string& notifier_id) {
568   for (std::vector<SyncedNotification*>::const_iterator iter =
569           notification_data_.begin();
570        iter != notification_data_.end();
571        ++iter) {
572     if ((*iter)->GetSendingServiceId() == notifier_id &&
573         (*iter)->GetReadState() == SyncedNotification::kUnread) {
574       notification_manager_->CancelById((*iter)->GetKey());
575     }
576   }
577 }
578
579 void ChromeNotifierService::OnEnabledSendingServiceListPrefChanged(
580     std::set<std::string>* ids_field) {
581   ids_field->clear();
582   const std::vector<std::string> pref_list =
583       enabled_sending_services_prefs_.GetValue();
584   for (size_t i = 0; i < pref_list.size(); ++i) {
585     std::string element = pref_list[i];
586     if (!element.empty())
587       ids_field->insert(element);
588     else
589       LOG(WARNING) << i << "-th element is not a string "
590                    << prefs::kEnabledSyncedNotificationSendingServices;
591   }
592 }
593
594 void ChromeNotifierService::OnInitializedSendingServiceListPrefChanged(
595     std::set<std::string>* ids_field) {
596   ids_field->clear();
597   const std::vector<std::string> pref_list =
598       initialized_sending_services_prefs_.GetValue();
599   for (size_t i = 0; i < pref_list.size(); ++i) {
600     std::string element = pref_list[i];
601     if (!element.empty())
602       ids_field->insert(element);
603     else
604       LOG(WARNING) << i << "-th element is not a string for "
605                    << prefs::kInitializedSyncedNotificationSendingServices;
606   }
607 }
608
609 void ChromeNotifierService::OnSyncedNotificationFirstRunBooleanPrefChanged(
610     bool* new_value) {
611   synced_notification_first_run_ = *new_value;
612 }
613
614 void ChromeNotifierService::RegisterProfilePrefs(
615     user_prefs::PrefRegistrySyncable* registry) {
616   // Register the pref for the list of enabled services.
617   registry->RegisterListPref(
618       prefs::kEnabledSyncedNotificationSendingServices,
619       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
620   // Register the pref for the list of initialized services.
621   registry->RegisterListPref(
622       prefs::kInitializedSyncedNotificationSendingServices,
623       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
624   // Register the preference for first run status, defaults to "true",
625   // meaning that this is the first run of the Synced Notification feature.
626   registry->RegisterBooleanPref(
627       prefs::kSyncedNotificationFirstRun, true,
628       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
629 }
630
631 void ChromeNotifierService::InitializePrefs() {
632   // Set up any pref changes to update our list of services.
633   enabled_sending_services_prefs_.Init(
634       prefs::kEnabledSyncedNotificationSendingServices,
635       profile_->GetPrefs(),
636       base::Bind(
637           &ChromeNotifierService::OnEnabledSendingServiceListPrefChanged,
638           base::Unretained(this),
639           base::Unretained(&enabled_sending_services_)));
640   initialized_sending_services_prefs_.Init(
641       prefs::kInitializedSyncedNotificationSendingServices,
642       profile_->GetPrefs(),
643       base::Bind(
644           &ChromeNotifierService::OnInitializedSendingServiceListPrefChanged,
645           base::Unretained(this),
646           base::Unretained(&initialized_sending_services_)));
647   synced_notification_first_run_prefs_.Init(
648       prefs::kSyncedNotificationFirstRun,
649       profile_->GetPrefs(),
650       base::Bind(
651           &ChromeNotifierService::
652               OnSyncedNotificationFirstRunBooleanPrefChanged,
653           base::Unretained(this),
654           base::Unretained(&synced_notification_first_run_)));
655
656   // Get the prefs from last session into our memeber varilables
657   OnEnabledSendingServiceListPrefChanged(&enabled_sending_services_);
658   OnInitializedSendingServiceListPrefChanged(&initialized_sending_services_);
659
660   synced_notification_first_run_ =
661       profile_->GetPrefs()->GetBoolean(prefs::kSyncedNotificationFirstRun);
662 }
663
664 void ChromeNotifierService::AddNewSendingServices() {
665   // TODO(petewil): When we have the new sync datatype for senders, use it
666   // instead of hardcoding the service name.
667
668   // Check to see if all known services are in the initialized list.
669   // If so, we can exit.
670   std::set<std::string>::iterator iter;
671   std::string first_synced_notification_service_id(
672       kFirstSyncedNotificationServiceId);
673
674   iter = find(initialized_sending_services_.begin(),
675               initialized_sending_services_.end(),
676               first_synced_notification_service_id);
677   if (initialized_sending_services_.end() != iter)
678     return;
679
680   // Build a ListValue with the list of services to be enabled.
681   base::ListValue enabled_sending_services;
682   base::ListValue initialized_sending_services;
683
684   // Mark any new services as enabled in preferences.
685   enabled_sending_services_.insert(first_synced_notification_service_id);
686   BuildServiceListValueInplace(enabled_sending_services_,
687                                &enabled_sending_services);
688   profile_->GetPrefs()->Set(
689       prefs::kEnabledSyncedNotificationSendingServices,
690       enabled_sending_services);
691   // Mark it as having been initialized, so we don't try to turn it on again.
692   initialized_sending_services_.insert(first_synced_notification_service_id);
693   BuildServiceListValueInplace(initialized_sending_services_,
694                                &initialized_sending_services);
695   profile_->GetPrefs()->Set(
696       prefs::kInitializedSyncedNotificationSendingServices,
697       initialized_sending_services);
698 }
699
700 }  // namespace notifier