1// Copyright 2014 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#include "chrome/browser/extensions/api/synced_notifications_private/synced_notifications_shim.h" 6 7#include "extensions/browser/event_router.h" 8#include "sync/api/sync_change.h" 9#include "sync/api/sync_data.h" 10#include "sync/api/sync_error_factory.h" 11#include "sync/protocol/sync.pb.h" 12 13using namespace extensions; 14using namespace extensions::api; 15 16namespace { 17 18synced_notifications_private::ChangeType SyncerChangeTypeToJS( 19 syncer::SyncChange::SyncChangeType change_type) { 20 switch (change_type) { 21 case syncer::SyncChange::ACTION_UPDATE: 22 return synced_notifications_private::CHANGE_TYPE_UPDATED; 23 case syncer::SyncChange::ACTION_DELETE: 24 return synced_notifications_private::CHANGE_TYPE_DELETED; 25 case syncer::SyncChange::ACTION_ADD: 26 return synced_notifications_private::CHANGE_TYPE_ADDED; 27 case syncer::SyncChange::ACTION_INVALID: 28 return synced_notifications_private::CHANGE_TYPE_NONE; 29 } 30 NOTREACHED(); 31 return synced_notifications_private::CHANGE_TYPE_NONE; 32} 33 34syncer::ModelType JSDataTypeToSyncer( 35 synced_notifications_private::SyncDataType data_type) { 36 switch (data_type) { 37 case synced_notifications_private::SYNC_DATA_TYPE_APP_INFO: 38 return syncer::SYNCED_NOTIFICATION_APP_INFO; 39 case synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION: 40 return syncer::SYNCED_NOTIFICATIONS; 41 default: 42 NOTREACHED(); 43 return syncer::UNSPECIFIED; 44 } 45} 46 47synced_notifications_private::SyncDataType SyncerModelTypeToJS( 48 syncer::ModelType model_type) { 49 switch (model_type) { 50 case syncer::SYNCED_NOTIFICATION_APP_INFO: 51 return synced_notifications_private::SYNC_DATA_TYPE_APP_INFO; 52 case syncer::SYNCED_NOTIFICATIONS: 53 return synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION; 54 default: 55 NOTREACHED(); 56 return synced_notifications_private::SYNC_DATA_TYPE_NONE; 57 } 58} 59 60bool BuildNewSyncUpdate( 61 const tracked_objects::Location& from_here, 62 const std::string& changed_notification, 63 syncer::SyncChange* sync_change) { 64 sync_pb::EntitySpecifics specifics; 65 sync_pb::SyncedNotificationSpecifics* notification_specifics = 66 specifics.mutable_synced_notification(); 67 if (!notification_specifics->ParseFromArray( 68 changed_notification.c_str(), changed_notification.size())) { 69 return false; 70 } 71 72 // TODO(synced notifications): pass the tag via the JS API. 73 const std::string& tag = 74 notification_specifics->coalesced_notification().key(); 75 syncer::SyncData sync_data = 76 syncer::SyncData::CreateLocalData(tag, tag, specifics); 77 *sync_change = syncer::SyncChange( 78 from_here, syncer::SyncChange::ACTION_UPDATE, sync_data); 79 return true; 80} 81 82linked_ptr<synced_notifications_private::SyncChange> BuildNewJSSyncChange( 83 const syncer::SyncChange& change) { 84 linked_ptr<synced_notifications_private::SyncChange> js_change = 85 make_linked_ptr<synced_notifications_private::SyncChange>( 86 new synced_notifications_private::SyncChange()); 87 js_change->change_type = SyncerChangeTypeToJS(change.change_type()); 88 js_change->data.datatype = 89 SyncerModelTypeToJS(change.sync_data().GetDataType()); 90 if (change.sync_data().GetDataType() == syncer::SYNCED_NOTIFICATIONS) { 91 const sync_pb::SyncedNotificationSpecifics& specifics = 92 change.sync_data().GetSpecifics().synced_notification(); 93 js_change->data.data_item = specifics.SerializeAsString(); 94 } else { 95 DCHECK_EQ(change.sync_data().GetDataType(), 96 syncer::SYNCED_NOTIFICATION_APP_INFO); 97 const sync_pb::SyncedNotificationAppInfoSpecifics& specifics = 98 change.sync_data().GetSpecifics().synced_notification_app_info(); 99 js_change->data.data_item = specifics.SerializeAsString(); 100 } 101 return js_change; 102} 103 104bool PopulateJSDataListFromSync( 105 const syncer::SyncDataList& sync_data_list, 106 std::vector<linked_ptr<synced_notifications_private::SyncData> >* 107 js_data_list) { 108 for (size_t i = 0; i < sync_data_list.size(); ++i) { 109 linked_ptr<synced_notifications_private::SyncData> js_data( 110 new synced_notifications_private::SyncData()); 111 syncer::ModelType data_type = sync_data_list[i].GetDataType(); 112 js_data->datatype = SyncerModelTypeToJS(data_type); 113 if (data_type == syncer::SYNCED_NOTIFICATIONS) { 114 const sync_pb::SyncedNotificationSpecifics& specifics = 115 sync_data_list[i].GetSpecifics().synced_notification(); 116 js_data->data_item = specifics.SerializeAsString(); 117 } else if (data_type == syncer::SYNCED_NOTIFICATION_APP_INFO) { 118 const sync_pb::SyncedNotificationAppInfoSpecifics& specifics = 119 sync_data_list[i].GetSpecifics().synced_notification_app_info(); 120 js_data->data_item = specifics.SerializeAsString(); 121 } else { 122 return false; 123 } 124 js_data_list->push_back(js_data); 125 } 126 return true; 127} 128 129} // namespace 130 131SyncedNotificationsShim::SyncedNotificationsShim( 132 const EventLauncher& event_launcher, 133 const base::Closure& refresh_request) 134 : event_launcher_(event_launcher), 135 refresh_request_(refresh_request) { 136} 137 138SyncedNotificationsShim::~SyncedNotificationsShim() { 139} 140 141syncer::SyncMergeResult SyncedNotificationsShim::MergeDataAndStartSyncing( 142 syncer::ModelType type, 143 const syncer::SyncDataList& initial_sync_data, 144 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 145 scoped_ptr<syncer::SyncErrorFactory> error_handler) { 146 if (type == syncer::SYNCED_NOTIFICATIONS) 147 notifications_change_processor_ = sync_processor.Pass(); 148 else if (type == syncer::SYNCED_NOTIFICATION_APP_INFO) 149 app_info_change_processor_ = sync_processor.Pass(); 150 else 151 NOTREACHED(); 152 153 // Only wake up the extension if both sync data types are ready. 154 if (notifications_change_processor_ && app_info_change_processor_) { 155 scoped_ptr<Event> event(new Event( 156 synced_notifications_private::OnSyncStartup::kEventName, 157 synced_notifications_private::OnSyncStartup::Create())); 158 event_launcher_.Run(event.Pass()); 159 } 160 161 return syncer::SyncMergeResult(type); 162} 163 164void SyncedNotificationsShim::StopSyncing(syncer::ModelType type) { 165 if (type == syncer::SYNCED_NOTIFICATIONS) 166 notifications_change_processor_.reset(); 167 else if (type == syncer::SYNCED_NOTIFICATION_APP_INFO) 168 app_info_change_processor_.reset(); 169 else 170 NOTREACHED(); 171} 172 173syncer::SyncError SyncedNotificationsShim::ProcessSyncChanges( 174 const tracked_objects::Location& from_here, 175 const syncer::SyncChangeList& changes) { 176 std::vector<linked_ptr<synced_notifications_private::SyncChange> > js_changes; 177 for (size_t i = 0; i < changes.size(); ++i) 178 js_changes.push_back(BuildNewJSSyncChange(changes[i])); 179 180 scoped_ptr<base::ListValue> args( 181 synced_notifications_private::OnDataChanges::Create(js_changes)); 182 scoped_ptr<Event> event(new Event( 183 synced_notifications_private::OnDataChanges::kEventName, args.Pass())); 184 event_launcher_.Run(event.Pass()); 185 return syncer::SyncError(); 186} 187 188syncer::SyncDataList SyncedNotificationsShim::GetAllSyncData( 189 syncer::ModelType type) const { 190 NOTIMPLEMENTED(); 191 return syncer::SyncDataList(); 192} 193 194bool SyncedNotificationsShim::GetInitialData( 195 synced_notifications_private::SyncDataType data_type, 196 std::vector<linked_ptr<synced_notifications_private::SyncData> >* 197 js_data_list) const { 198 if (!IsSyncReady()) 199 return false; 200 201 syncer::SyncDataList sync_data_list; 202 if (JSDataTypeToSyncer(data_type) == syncer::SYNCED_NOTIFICATIONS) { 203 sync_data_list = notifications_change_processor_->GetAllSyncData( 204 syncer::SYNCED_NOTIFICATIONS); 205 if (PopulateJSDataListFromSync(sync_data_list, js_data_list)) 206 return true; 207 } else if (JSDataTypeToSyncer(data_type) == 208 syncer::SYNCED_NOTIFICATION_APP_INFO) { 209 sync_data_list = app_info_change_processor_->GetAllSyncData( 210 syncer::SYNCED_NOTIFICATION_APP_INFO); 211 if (PopulateJSDataListFromSync(sync_data_list, js_data_list)) 212 return true; 213 } 214 return false; 215} 216 217bool SyncedNotificationsShim::UpdateNotification( 218 const std::string& changed_notification) { 219 if (!IsSyncReady()) 220 return false; 221 222 syncer::SyncChange sync_change; 223 if (!BuildNewSyncUpdate(FROM_HERE, changed_notification, &sync_change)) 224 return false; 225 syncer::SyncError error = notifications_change_processor_->ProcessSyncChanges( 226 FROM_HERE, 227 syncer::SyncChangeList(1, sync_change)); 228 return !error.IsSet(); 229} 230 231bool SyncedNotificationsShim::SetRenderContext( 232 synced_notifications_private::RefreshRequest refresh_request, 233 const std::string& new_context) { 234 if (!IsSyncReady()) 235 return false; 236 237 syncer::SyncChangeProcessor::ContextRefreshStatus sync_refresh_status = 238 refresh_request == 239 synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED 240 ? syncer::SyncChangeProcessor::REFRESH_NEEDED 241 : syncer::SyncChangeProcessor::NO_REFRESH; 242 syncer::SyncError error = 243 notifications_change_processor_->UpdateDataTypeContext( 244 syncer::SYNCED_NOTIFICATIONS, sync_refresh_status, new_context); 245 246 if (sync_refresh_status == syncer::SyncChangeProcessor::REFRESH_NEEDED && 247 !refresh_request_.is_null()) { 248 refresh_request_.Run(); 249 } 250 251 return !error.IsSet(); 252} 253 254bool SyncedNotificationsShim::IsSyncReady() const { 255 return notifications_change_processor_ && app_info_change_processor_; 256} 257