chrome_notifier_service.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The ChromeNotifierService works together with sync to maintain the state of
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// user notifications, which can then be presented in the notification center,
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// via the Notification UI Manager.
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/utf_string_conversions.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/browser_process.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/notification.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/notification_ui_manager.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "googleurl/src/gurl.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_change.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_change_processor.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_error_factory.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/sync.pb.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/synced_notification_specifics.pb.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextDirection.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace notifier {
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeNotifierService::ChromeNotifierService(Profile* profile,
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             NotificationUIManager* manager)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : profile_(profile), notification_manager_(manager) {}
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeNotifierService::~ChromeNotifierService() {}
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Methods from ProfileKeyedService.
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::Shutdown() {
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// syncer::SyncableService implementation.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This is called at startup to sync with the server.
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This code is not thread safe.
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncMergeResult ChromeNotifierService::MergeDataAndStartSyncing(
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::ModelType type,
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const syncer::SyncDataList& initial_sync_data,
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      scoped_ptr<syncer::SyncErrorFactory> error_handler) {
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): After I add the infrastructure for the test, add a check
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // that we are currently on the UI thread here.
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATIONS);
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // A list of local changes to send up to the sync server.
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList new_changes;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_processor_ = sync_processor.Pass();
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != initial_sync_data.end(); ++it) {
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncData& sync_data = *it;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Build a local notification object from the sync data.
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<SyncedNotification> incoming(CreateNotificationFromSyncData(
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_data));
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(incoming.get());
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Process each incoming remote notification.
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& id = incoming->notification_id();
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_GT(id.length(), 0U);
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedNotification* found = FindNotificationById(id);
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (NULL == found) {
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If there are no conflicts, copy in the data from remote.
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Add(incoming.Pass());
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If the incoming (remote) and stored (local) notifications match
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // in all fields, we don't need to do anything here.
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (incoming->EqualsIgnoringReadState(*found)) {
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (incoming->read_state() == found->read_state()) {
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Notification matches on the client and the server, nothing to do.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          continue;
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else  {
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If the read state is different, read wins for both places.
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (incoming->read_state() == SyncedNotification::kDismissed) {
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If it is marked as read on the server, but not the client.
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            found->NotificationHasBeenDismissed();
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // TODO(petewil): Tell the Notification UI Manager to mark it read.
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If it is marked as read on the client, but not the server.
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            syncer::SyncData sync_data = CreateSyncDataFromNotification(*found);
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            new_changes.push_back(
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                syncer::SyncChange(FROM_HERE,
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   syncer::SyncChange::ACTION_UPDATE,
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   sync_data));
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If local state changed, notify Notification UI Manager.
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // For any other conflict besides read state, treat it as an update.
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // If different, just replace the local with the remote.
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // TODO(petewil): Someday we may allow changes from the client to
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // flow upwards, when we do, we will need better merge resolution.
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        found->Update(sync_data);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Send up the changes that were made locally.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_changes.size() > 0) {
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    merge_result.set_error(sync_processor_->ProcessSyncChanges(
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE, new_changes));
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return merge_result;
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::StopSyncing(syncer::ModelType type) {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): implement
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncDataList ChromeNotifierService::GetAllSyncData(
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::ModelType type) const {
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncDataList sync_data;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Copy our native format data into a SyncDataList format.
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<SyncedNotification*>::const_iterator it =
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          notification_data_.begin();
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      it != notification_data_.end();
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++it) {
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_data.push_back(CreateSyncDataFromNotification(**it));
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return sync_data;
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This method is called when there is an incoming sync change from the server.
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError ChromeNotifierService::ProcessSyncChanges(
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const tracked_objects::Location& from_here,
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const syncer::SyncChangeList& change_list) {
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): add a check that we are called on the thread we expect.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncError error;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncChangeList::const_iterator it = change_list.begin();
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != change_list.end(); ++it) {
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncData sync_data = it->sync_data();
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChange::SyncChangeType change_type = it->change_type();
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<SyncedNotification> new_notification(
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        CreateNotificationFromSyncData(sync_data));
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!new_notification.get()) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED() << "Failed to read notification.";
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    switch (change_type) {
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case syncer::SyncChange::ACTION_ADD:
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // TODO(petewil): Update the notification if it already exists
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // as opposed to adding it.
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        Add(new_notification.Pass());
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // TODO(petewil): Implement code to add delete and update actions.
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      default:
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return error;
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Support functions for data type conversion.
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Static method.  Get to the sync data in our internal format.
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncData ChromeNotifierService::CreateSyncDataFromNotification(
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SyncedNotification& notification) {
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Construct the sync_data using the specifics from the notification.
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return syncer::SyncData::CreateLocalData(
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      notification.notification_id(), notification.notification_id(),
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      notification.GetEntitySpecifics());
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Static Method.  Convert from SyncData to our internal format.
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_ptr<SyncedNotification>
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ChromeNotifierService::CreateNotificationFromSyncData(
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        const syncer::SyncData& sync_data) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get a pointer to our data within the sync_data object.
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::SyncedNotificationSpecifics specifics =
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      sync_data.GetSpecifics().synced_notification();
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check for mandatory fields in the sync_data object.
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!specifics.has_coalesced_notification() ||
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !specifics.coalesced_notification().has_key() ||
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !specifics.coalesced_notification().has_read_state()) {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return scoped_ptr<SyncedNotification>();
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): Is this the right set?  Should I add more?
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_well_formed_unread_notification =
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (static_cast<SyncedNotification::ReadState>(
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          specifics.coalesced_notification().read_state()) ==
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       SyncedNotification::kUnread &&
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       specifics.coalesced_notification().has_render_info());
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_well_formed_dismissed_notification =
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (static_cast<SyncedNotification::ReadState>(
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          specifics.coalesced_notification().read_state()) ==
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       SyncedNotification::kDismissed);
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // if the notification is poorly formed, return a null pointer
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!is_well_formed_unread_notification &&
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !is_well_formed_dismissed_notification)
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return scoped_ptr<SyncedNotification>();
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create a new notification object based on the supplied sync_data.
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<SyncedNotification> notification(
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new SyncedNotification(sync_data));
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return notification.Pass();
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This returns a pointer into a vector that we own.  Caller must not free it.
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns NULL if no match is found.
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This uses the <app_id/coalescing_key> pair as a key.
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SyncedNotification* ChromeNotifierService::FindNotificationById(
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& id) {
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): We can make a performance trade off here.
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // While the vector has good locality of reference, a map has faster lookup.
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Based on how big we expect this to get, maybe change this to a map.
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<SyncedNotification*>::const_iterator it =
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          notification_data_.begin();
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      it != notification_data_.end();
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++it) {
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedNotification* notification = *it;
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (id == notification->notification_id())
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return *it;
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::MarkNotificationAsDismissed(const std::string& id) {
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedNotification* notification = FindNotificationById(id);
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(notification != NULL);
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  notification->NotificationHasBeenDismissed();
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList new_changes;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncData sync_data = CreateSyncDataFromNotification(*notification);
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  new_changes.push_back(
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::SyncChange(FROM_HERE,
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         syncer::SyncChange::ACTION_UPDATE,
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         sync_data));
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Send up the changes that were made locally.
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Add a new notification to our data structure.  This takes ownership
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// of the passed in pointer.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::Add(scoped_ptr<SyncedNotification> notification) {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedNotification* notification_copy = notification.get();
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Take ownership of the object and put it into our local storage.
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  notification_data_.push_back(notification.release());
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Show it to the user.
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Show(notification_copy);
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Send the notification to the NotificationUIManager to show to the user.
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::Show(SyncedNotification* notification) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Set up the fields we need to send and create a Notification object.
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL origin_url(notification->origin_url());
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL app_icon_url(notification->app_icon_url());
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 title = UTF8ToUTF16(notification->title());
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 text = UTF8ToUTF16(notification->text());
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 heading = UTF8ToUTF16(notification->heading());
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 description = UTF8ToUTF16(notification->description());
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): What goes in the display source, is empty OK?
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 display_source;
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 replace_id = UTF8ToUTF16(notification->notification_id());
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): For now, just punt on dismissed notifications until
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // I change the interface to let NotificationUIManager know the right way.
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (SyncedNotification::kRead == notification->read_state() ||
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SyncedNotification::kDismissed == notification->read_state() ) {
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(2) << "Not showing dismissed notification"
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              << notification->title() << " " << notification->text();
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The delegate will eventually catch calls that the notification
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // was read or deleted, and send the changes back to the server.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<NotificationDelegate> delegate =
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new ChromeNotifierDelegate(notification->notification_id(), this);
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Notification ui_notification(origin_url, app_icon_url, heading, description,
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               WebKit::WebTextDirectionDefault,
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               display_source, replace_id, delegate);
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  notification_manager_->Add(ui_notification, profile_);
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Synced Notification arrived! " << title << " " << text
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << " " << app_icon_url << " " << replace_id << " "
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << notification->read_state();
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return;
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace notifier
310