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
13 using namespace extensions;
14 using namespace extensions::api;
15
16 namespace {
17
SyncerChangeTypeToJS(syncer::SyncChange::SyncChangeType change_type)18 synced_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
JSDataTypeToSyncer(synced_notifications_private::SyncDataType data_type)34 syncer::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
SyncerModelTypeToJS(syncer::ModelType model_type)47 synced_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
BuildNewSyncUpdate(const tracked_objects::Location & from_here,const std::string & changed_notification,syncer::SyncChange * sync_change)60 bool 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
BuildNewJSSyncChange(const syncer::SyncChange & change)82 linked_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
PopulateJSDataListFromSync(const syncer::SyncDataList & sync_data_list,std::vector<linked_ptr<synced_notifications_private::SyncData>> * js_data_list)104 bool 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
SyncedNotificationsShim(const EventLauncher & event_launcher,const base::Closure & refresh_request)131 SyncedNotificationsShim::SyncedNotificationsShim(
132 const EventLauncher& event_launcher,
133 const base::Closure& refresh_request)
134 : event_launcher_(event_launcher),
135 refresh_request_(refresh_request) {
136 }
137
~SyncedNotificationsShim()138 SyncedNotificationsShim::~SyncedNotificationsShim() {
139 }
140
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,scoped_ptr<syncer::SyncChangeProcessor> sync_processor,scoped_ptr<syncer::SyncErrorFactory> error_handler)141 syncer::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
StopSyncing(syncer::ModelType type)164 void 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
ProcessSyncChanges(const tracked_objects::Location & from_here,const syncer::SyncChangeList & changes)173 syncer::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
GetAllSyncData(syncer::ModelType type) const188 syncer::SyncDataList SyncedNotificationsShim::GetAllSyncData(
189 syncer::ModelType type) const {
190 NOTIMPLEMENTED();
191 return syncer::SyncDataList();
192 }
193
GetInitialData(synced_notifications_private::SyncDataType data_type,std::vector<linked_ptr<synced_notifications_private::SyncData>> * js_data_list) const194 bool 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
UpdateNotification(const std::string & changed_notification)217 bool 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
SetRenderContext(synced_notifications_private::RefreshRequest refresh_request,const std::string & new_context)231 bool 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
IsSyncReady() const254 bool SyncedNotificationsShim::IsSyncReady() const {
255 return notifications_change_processor_ && app_info_change_processor_;
256 }
257