Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / sync_notifier / synced_notification_app_info_service.cc
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 // The SyncedNotificationAppInfoService brings down read only metadata from the
6 // sync server with information about the services sending synced notifications.
7
8 #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h"
9
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
12 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "sync/api/sync_change.h"
15 #include "sync/api/sync_change_processor.h"
16 #include "sync/api/sync_error_factory.h"
17 #include "sync/protocol/sync.pb.h"
18 #include "sync/protocol/synced_notification_app_info_specifics.pb.h"
19 #include "url/gurl.h"
20
21 namespace notifier {
22
23 SyncedNotificationSendingServiceSettingsData::
24     SyncedNotificationSendingServiceSettingsData(
25         std::string settings_display_name_param,
26         gfx::Image settings_icon_param,
27         message_center::NotifierId notifier_id_param)
28         : settings_display_name(settings_display_name_param),
29           settings_icon(settings_icon_param),
30           notifier_id(notifier_id_param) {}
31
32 bool SyncedNotificationAppInfoService::avoid_bitmap_fetching_for_test_ = false;
33
34 SyncedNotificationAppInfoService::SyncedNotificationAppInfoService(
35     Profile* profile)
36     : profile_(profile), chrome_notifier_service_(NULL) {}
37
38 SyncedNotificationAppInfoService::~SyncedNotificationAppInfoService() {}
39
40 // Methods from KeyedService.
41 void SyncedNotificationAppInfoService::Shutdown() {}
42
43 // syncer::SyncableService implementation.
44
45 // This is called at startup to sync with the sync data. This code is not thread
46 // safe, it should only be called by sync on the browser thread.
47 syncer::SyncMergeResult
48 SyncedNotificationAppInfoService::MergeDataAndStartSyncing(
49     syncer::ModelType type,
50     const syncer::SyncDataList& initial_sync_data,
51     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
52     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
53   thread_checker_.CalledOnValidThread();
54   DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
55   syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATION_APP_INFO);
56
57   // There should only be one sync data in the list for this data type.
58   if (initial_sync_data.size() > 1) {
59     LOG(ERROR) << "Too many app infos over sync";
60   }
61
62   // TODO(petewil): Today we can only handle a single object, so we simply check
63   // for a non-empty list.  If in the future we can ever handle more, convert
64   // this whole block to be a loop over all of |initial_sync_data|.
65   if (!initial_sync_data.empty()) {
66     const syncer::SyncData& sync_data = initial_sync_data.front();
67     DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType());
68
69     const sync_pb::SyncedNotificationAppInfoSpecifics& specifics =
70         sync_data.GetSpecifics().synced_notification_app_info();
71
72     // Store our sync data, so GetAllSyncData can give it back later.
73     sync_data_ = sync_data;
74
75     size_t app_info_count = specifics.synced_notification_app_info_size();
76
77     // The SyncedNotificationAppInfo is a repeated field, process each one.
78     for (size_t app_info_index = 0;
79          app_info_index < app_info_count;
80          ++app_info_index) {
81       const sync_pb::SyncedNotificationAppInfo app_info(
82           specifics.synced_notification_app_info(app_info_index));
83
84       ProcessIncomingAppInfoProtobuf(app_info);
85     }
86   }
87
88   return merge_result;
89 }
90
91 void SyncedNotificationAppInfoService::StopSyncing(syncer::ModelType type) {
92   DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
93   // Implementation is not required, since this is not a user selectable sync
94   // type.
95 }
96
97 // This method is called when there is an incoming sync change from the server.
98 syncer::SyncError SyncedNotificationAppInfoService::ProcessSyncChanges(
99     const tracked_objects::Location& from_here,
100     const syncer::SyncChangeList& change_list) {
101   thread_checker_.CalledOnValidThread();
102   syncer::SyncError error;
103
104   for (syncer::SyncChangeList::const_iterator it = change_list.begin();
105        it != change_list.end();
106        ++it) {
107     syncer::SyncData sync_data = it->sync_data();
108     DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType());
109     syncer::SyncChange::SyncChangeType change_type = it->change_type();
110     DCHECK(change_type == syncer::SyncChange::ACTION_UPDATE ||
111            change_type == syncer::SyncChange::ACTION_ADD);
112
113     sync_pb::SyncedNotificationAppInfoSpecifics specifics =
114         sync_data.GetSpecifics().synced_notification_app_info();
115
116     // Copy over the sync data with the new one.
117     sync_data_ = sync_data;
118
119     size_t app_info_count = specifics.synced_notification_app_info_size();
120     if (app_info_count == 0) {
121       NOTREACHED() << "Bad notification app info change from the server.";
122       continue;
123     }
124
125     // The SyncedNotificationAppInfo is a repeated field, process each one.
126     for (size_t app_info_index = 0;
127          app_info_index < app_info_count;
128          ++app_info_index) {
129       const sync_pb::SyncedNotificationAppInfo app_info(
130           specifics.synced_notification_app_info(app_info_index));
131       ProcessIncomingAppInfoProtobuf(app_info);
132     }
133   }
134
135   return error;
136 }
137
138 syncer::SyncDataList SyncedNotificationAppInfoService::GetAllSyncData(
139     syncer::ModelType type) const {
140   DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
141   syncer::SyncDataList sync_data_list;
142
143   // TODO(petewil): Recreate this from internal data instead,
144   // it improves code maintainability.
145
146   // make a copy for the list we give back to the caller.
147   sync_data_list.push_back(sync_data_);
148
149   return sync_data_list;
150 }
151
152 // When a new protobuf comes in, we will fetch all the bitmaps before reporting
153 // the new protobufs to the ChromeNotifierService.  This helps insure that new
154 // protobufs appear at the same time old ones disappear, and that no protobuf
155 // appears before its bitmaps are present.
156 // We also check for duplicate bitmap URLs when loading the bitmaps for a
157 // protobuf, so we aren't left waiting for a second copy of the bitmap to be
158 // fetched before we call the protobuf done.
159 void SyncedNotificationAppInfoService::ProcessIncomingAppInfoProtobuf(
160     const sync_pb::SyncedNotificationAppInfo& app_info) {
161   // Build a local app_info object from the sync data.
162   scoped_ptr<SyncedNotificationAppInfo> incoming(
163       CreateSyncedNotificationAppInfoFromProtobuf(app_info));
164   if (incoming.get() == NULL) {
165     LOG(ERROR) << "Invalid Synced No5tification App Info protobuf";
166     return;
167   }
168
169   // Process each incoming app_info protobuf.
170   std::string app_info_name = incoming->settings_display_name();
171   DCHECK_GT(app_info_name.length(), 0U);
172   if (app_info_name.length() == 0) {
173     // If there is no unique id (name), there is nothing we can do.
174     return;
175   }
176
177   SyncedNotificationAppInfo* found =
178       FindSyncedNotificationAppInfoByName(app_info_name);
179
180   std::vector<std::string> old_app_ids;
181   std::vector<std::string> new_app_ids;
182   std::vector<std::string> added_app_ids;
183   std::vector<std::string> removed_app_ids;
184
185   new_app_ids = incoming->GetAppIdList();
186
187   if (NULL == found) {
188     added_app_ids = new_app_ids;
189   } else {
190     // When we have an update, some app id types may be added or removed.
191     // Append to lists of added and removed types.
192     old_app_ids = found->GetAppIdList();
193     new_app_ids = incoming->GetAppIdList();
194     FreeSyncedNotificationAppInfoByName(app_info_name);
195
196     // Set up for a set difference by sorting the lists.
197     std::sort(old_app_ids.begin(), old_app_ids.end());
198     std::sort(new_app_ids.begin(), new_app_ids.end());
199
200     // Calculate which app ids are removed (in old, but not in new app ids).
201     std::set_difference(old_app_ids.begin(),
202                         old_app_ids.end(),
203                         new_app_ids.begin(),
204                         new_app_ids.end(),
205                         std::back_inserter(removed_app_ids));
206
207     // Calculate which app_ids are added (in new, but not in old app ids).
208     std::set_difference(new_app_ids.begin(),
209                         new_app_ids.end(),
210                         old_app_ids.begin(),
211                         old_app_ids.end(),
212                         std::back_inserter(added_app_ids));
213   }
214
215   // Put these lists into the app_info object.
216   incoming->set_added_app_ids(added_app_ids);
217   incoming->set_removed_app_ids(removed_app_ids);
218
219   // Start bitmap fetch.
220   if (!avoid_bitmap_fetching_for_test_) {
221     incoming->QueueBitmapFetchJobs();
222     incoming->StartBitmapFetch();
223   } else {
224     OnBitmapFetchesDone(incoming->added_app_ids(), incoming->removed_app_ids());
225   }
226
227   sending_service_infos_.push_back(incoming.release());
228 }
229
230 void SyncedNotificationAppInfoService::OnBitmapFetchesDone(
231     std::vector<std::string> added_app_ids,
232     std::vector<std::string> removed_app_ids) {
233   // Tell the Chrome Notifier Service so it can show any notifications that were
234   // waiting for the app id to arrive, and to remave any notifications that are
235   // no longer supported.
236   if (chrome_notifier_service_ != NULL) {
237     chrome_notifier_service_->OnAddedAppIds(added_app_ids);
238     chrome_notifier_service_->OnRemovedAppIds(removed_app_ids);
239   }
240 }
241
242 // Static Method.  Convert from a server protobuf to our internal format.
243 scoped_ptr<SyncedNotificationAppInfo>
244 SyncedNotificationAppInfoService::CreateSyncedNotificationAppInfoFromProtobuf(
245     const sync_pb::SyncedNotificationAppInfo& server_app_info) {
246
247   // Check for mandatory fields in the sync_data object.
248   std::string display_name;
249   if (server_app_info.has_app_name()) {
250     display_name = server_app_info.app_name();
251   } else if (server_app_info.has_settings_display_name()) {
252     display_name = server_app_info.settings_display_name();
253   }
254
255   scoped_ptr<SyncedNotificationAppInfo> app_info;
256   if (display_name.length() == 0)
257     return app_info.Pass();
258
259   // Create a new app info object based on the supplied protobuf.
260   app_info.reset(new SyncedNotificationAppInfo(profile_, display_name, this));
261
262   // This URL is used whenever the user clicks on the body of the app's welcome
263   // notification.
264   if (server_app_info.has_info_url()) {
265     app_info->SetWelcomeLinkUrl(GURL(server_app_info.info_url()));
266   }
267
268   // TODO(petewil): Eventually we will add the monochrome icon here, and we may
269   // need to fetch the correct url for the current DPI.
270   // Add the icon URL, if any.
271   if (server_app_info.has_icon()) {
272     std::string icon_url = server_app_info.icon().url();
273     // Set the URLs for the low and high DPI images.
274     // TODO(petewil): Since the high DPI image is not available yet, we just
275     // pass an empty URL for now.  Fix this once the high DPI URL is available.
276     app_info->SetSettingsURLs(GURL(icon_url), GURL());
277   }
278
279   // Add all the AppIds from the protobuf.
280   size_t app_id_count = server_app_info.app_id_size();
281   for (size_t ii = 0; ii < app_id_count; ++ii) {
282     app_info->AddAppId(server_app_info.app_id(ii));
283   }
284
285   return app_info.Pass();
286 }
287
288 // This returns a pointer into a vector that we own.  Caller must not free it.
289 // Returns NULL if no match is found.
290 notifier::SyncedNotificationAppInfo*
291 SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByName(
292     const std::string& name) {
293   for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
294            sending_service_infos_.begin();
295        it != sending_service_infos_.end();
296        ++it) {
297     SyncedNotificationAppInfo* app_info = *it;
298     if (name == app_info->settings_display_name())
299       return *it;
300   }
301
302   return NULL;
303 }
304
305 // This returns a pointer into a vector that we own.  Caller must not free it.
306 // Returns NULL if no match is found.
307 notifier::SyncedNotificationAppInfo*
308 SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByAppId(
309     const std::string& app_id) {
310   for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
311            sending_service_infos_.begin();
312        it != sending_service_infos_.end();
313        ++it) {
314     SyncedNotificationAppInfo* app_info = *it;
315     if (app_info->HasAppId(app_id))
316       return *it;
317   }
318
319   return NULL;
320 }
321
322 // Lookup the sending service name for a given app id.
323 std::string SyncedNotificationAppInfoService::FindSendingServiceNameFromAppId(
324     const std::string app_id) {
325   for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
326            sending_service_infos_.begin();
327        it != sending_service_infos_.end();
328        ++it) {
329     SyncedNotificationAppInfo* app_info = *it;
330     if (app_info->HasAppId(app_id))
331       return app_info->settings_display_name();
332   }
333
334   return std::string();
335 }
336
337 void SyncedNotificationAppInfoService::FreeSyncedNotificationAppInfoByName(
338     const std::string& name) {
339   ScopedVector<SyncedNotificationAppInfo>::iterator it =
340       sending_service_infos_.begin();
341   for (; it != sending_service_infos_.end(); ++it) {
342     SyncedNotificationAppInfo* app_info = *it;
343     if (name == app_info->settings_display_name()) {
344       sending_service_infos_.erase(it);
345       return;
346     }
347   }
348 }
349
350 std::vector<SyncedNotificationSendingServiceSettingsData>
351 SyncedNotificationAppInfoService::GetAllSendingServiceSettingsData() {
352   std::vector<SyncedNotificationSendingServiceSettingsData> settings_data;
353   ScopedVector<SyncedNotificationAppInfo>::iterator it =
354       sending_service_infos_.begin();
355
356   for (; it != sending_service_infos_.end(); ++it) {
357     SyncedNotificationSendingServiceSettingsData this_service(
358         (*it)->settings_display_name(),
359         (*it)->icon(),
360         (*it)->GetNotifierId());
361     settings_data.push_back(this_service);
362   }
363
364   return settings_data;
365 }
366
367 // Add a new app info to our data structure.  This takes ownership
368 // of the passed in pointer.
369 void SyncedNotificationAppInfoService::Add(
370     scoped_ptr<SyncedNotificationAppInfo> app_info) {
371   sending_service_infos_.push_back(app_info.release());
372 }
373
374 }  // namespace notifier