b28e94194336f33b7384da7eddbadfaeecfe406b
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / sync_engine_initializer.cc
1 // Copyright 2013 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/sync_file_system/drive_backend/sync_engine_initializer.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "chrome/browser/drive/drive_api_service.h"
11 #include "chrome/browser/drive/drive_api_util.h"
12 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
13 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
14 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
15 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
16 #include "chrome/browser/sync_file_system/logger.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "google_apis/drive/gdata_wapi_parser.h"
19
20 namespace sync_file_system {
21 namespace drive_backend {
22
23 namespace {
24
25 ////////////////////////////////////////////////////////////////////////////////
26 // Functions below are for wrapping the access to legacy GData WAPI classes.
27
28 bool IsDeleted(const google_apis::ResourceEntry& entry) {
29   return entry.deleted();
30 }
31
32 bool HasNoParents(const google_apis::ResourceEntry& entry) {
33   return !entry.GetLinkByType(google_apis::Link::LINK_PARENT);
34 }
35
36 bool HasFolderAsParent(const google_apis::ResourceEntry& entry,
37                        const std::string& parent_id) {
38   const ScopedVector<google_apis::Link>& links = entry.links();
39   for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
40        itr != links.end(); ++itr) {
41     const google_apis::Link& link = **itr;
42     if (link.type() != google_apis::Link::LINK_PARENT)
43       continue;
44     if (drive::util::ExtractResourceIdFromUrl(link.href()) == parent_id)
45       return true;
46   }
47   return false;
48 }
49
50 bool LessOnCreationTime(const google_apis::ResourceEntry& left,
51                         const google_apis::ResourceEntry& right) {
52   return left.published_time() < right.published_time();
53 }
54
55 // Posts a request to continue listing.  Returns false if the list doesn't need
56 // listing anymore.
57 bool GetRemainingFileList(
58     google_apis::CancelCallback* cancel_callback,
59     drive::DriveServiceInterface* api_service,
60     const google_apis::ResourceList& resource_list,
61     const google_apis::GetResourceListCallback& callback) {
62   GURL next_url;
63   if (!resource_list.GetNextFeedURL(&next_url))
64     return false;
65
66   *cancel_callback = api_service->GetRemainingFileList(next_url, callback);
67   return true;
68 }
69
70 std::string GetID(const google_apis::ResourceEntry& entry) {
71   return entry.resource_id();
72 }
73
74 ScopedVector<google_apis::FileResource> ConvertResourceEntriesToFileResources(
75     const ScopedVector<google_apis::ResourceEntry>& entries) {
76   ScopedVector<google_apis::FileResource> resources;
77   for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr =
78            entries.begin();
79        itr != entries.end();
80        ++itr) {
81     resources.push_back(
82         drive::util::ConvertResourceEntryToFileResource(
83             **itr).release());
84   }
85   return resources.Pass();
86 }
87
88 // Functions above are for wrapping the access to legacy GData WAPI classes.
89 ////////////////////////////////////////////////////////////////////////////////
90
91 }  // namespace
92
93 SyncEngineInitializer::SyncEngineInitializer(
94     SyncEngineContext* sync_context,
95     base::SequencedTaskRunner* task_runner,
96     drive::DriveServiceInterface* drive_service,
97     const base::FilePath& database_path)
98     : sync_context_(sync_context),
99       task_runner_(task_runner),
100       drive_service_(drive_service),
101       database_path_(database_path),
102       find_sync_root_retry_count_(0),
103       largest_change_id_(0),
104       weak_ptr_factory_(this) {
105   DCHECK(task_runner);
106   DCHECK(drive_service_);
107 }
108
109 SyncEngineInitializer::~SyncEngineInitializer() {
110   if (!cancel_callback_.is_null())
111     cancel_callback_.Run();
112 }
113
114 void SyncEngineInitializer::Run(const SyncStatusCallback& callback) {
115   util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
116
117   // The metadata seems to have been already initialized. Just return with OK.
118   if (sync_context_ && sync_context_->GetMetadataDatabase()) {
119     util::Log(logging::LOG_VERBOSE, FROM_HERE,
120               "[Initialize] Already initialized.");
121     callback.Run(SYNC_STATUS_OK);
122     return;
123   }
124
125   MetadataDatabase::Create(
126       task_runner_.get(), database_path_,
127       base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
128                  weak_ptr_factory_.GetWeakPtr(), callback));
129 }
130
131 scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
132   return metadata_database_.Pass();
133 }
134
135 void SyncEngineInitializer::DidCreateMetadataDatabase(
136     const SyncStatusCallback& callback,
137     SyncStatusCode status,
138     scoped_ptr<MetadataDatabase> instance) {
139   if (status != SYNC_STATUS_OK) {
140     util::Log(logging::LOG_VERBOSE, FROM_HERE,
141               "[Initialize] Failed to initialize MetadataDatabase.");
142     callback.Run(status);
143     return;
144   }
145
146   DCHECK(instance);
147   metadata_database_ = instance.Pass();
148   if (metadata_database_->HasSyncRoot()) {
149     util::Log(logging::LOG_VERBOSE, FROM_HERE,
150               "[Initialize] Found local cache of sync-root.");
151     callback.Run(SYNC_STATUS_OK);
152     return;
153   }
154
155   GetAboutResource(callback);
156 }
157
158 void SyncEngineInitializer::GetAboutResource(
159     const SyncStatusCallback& callback) {
160   set_used_network(true);
161   drive_service_->GetAboutResource(
162       base::Bind(&SyncEngineInitializer::DidGetAboutResource,
163                  weak_ptr_factory_.GetWeakPtr(), callback));
164 }
165
166 void SyncEngineInitializer::DidGetAboutResource(
167     const SyncStatusCallback& callback,
168     google_apis::GDataErrorCode error,
169     scoped_ptr<google_apis::AboutResource> about_resource) {
170   cancel_callback_.Reset();
171
172   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
173   if (status != SYNC_STATUS_OK) {
174     util::Log(logging::LOG_VERBOSE, FROM_HERE,
175               "[Initialize] Failed to get AboutResource.");
176     callback.Run(status);
177     return;
178   }
179
180   DCHECK(about_resource);
181   root_folder_id_ = about_resource->root_folder_id();
182   largest_change_id_ = about_resource->largest_change_id();
183
184   DCHECK(!root_folder_id_.empty());
185   FindSyncRoot(callback);
186 }
187
188 void SyncEngineInitializer::FindSyncRoot(const SyncStatusCallback& callback) {
189   if (find_sync_root_retry_count_++ >= kMaxRetry) {
190     util::Log(logging::LOG_VERBOSE, FROM_HERE,
191               "[Initialize] Reached max retry count.");
192     callback.Run(SYNC_STATUS_FAILED);
193     return;
194   }
195
196   set_used_network(true);
197   cancel_callback_ = drive_service_->SearchByTitle(
198       kSyncRootFolderTitle,
199       std::string(), // parent_folder_id
200       base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
201                  weak_ptr_factory_.GetWeakPtr(),
202                  callback));
203 }
204
205 void SyncEngineInitializer::DidFindSyncRoot(
206     const SyncStatusCallback& callback,
207     google_apis::GDataErrorCode error,
208     scoped_ptr<google_apis::ResourceList> resource_list) {
209   cancel_callback_.Reset();
210
211   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
212   if (status != SYNC_STATUS_OK) {
213     util::Log(logging::LOG_VERBOSE, FROM_HERE,
214               "[Initialize] Failed to find sync root.");
215     callback.Run(status);
216     return;
217   }
218
219   if (!resource_list) {
220     NOTREACHED();
221     util::Log(logging::LOG_VERBOSE, FROM_HERE,
222               "[Initialize] Got invalid resource list.");
223     callback.Run(SYNC_STATUS_FAILED);
224     return;
225   }
226
227   ScopedVector<google_apis::ResourceEntry>* entries =
228       resource_list->mutable_entries();
229   for (ScopedVector<google_apis::ResourceEntry>::iterator itr =
230            entries->begin();
231        itr != entries->end(); ++itr) {
232     google_apis::ResourceEntry* entry = *itr;
233
234     // Ignore deleted folder.
235     if (IsDeleted(*entry))
236       continue;
237
238     // Pick an orphaned folder or a direct child of the root folder and
239     // ignore others.
240     DCHECK(!root_folder_id_.empty());
241     if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
242       continue;
243
244     if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
245       sync_root_folder_.reset(entry);
246       *itr = NULL;
247     }
248   }
249
250   set_used_network(true);
251   // If there are more results, retrieve them.
252   if (GetRemainingFileList(
253           &cancel_callback_,
254           drive_service_, *resource_list,
255           base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
256                      weak_ptr_factory_.GetWeakPtr(),
257                      callback)))
258     return;
259
260   if (!sync_root_folder_) {
261     CreateSyncRoot(callback);
262     return;
263   }
264
265   if (!HasNoParents(*sync_root_folder_)) {
266     DetachSyncRoot(callback);
267     return;
268   }
269
270   ListAppRootFolders(callback);
271 }
272
273 void SyncEngineInitializer::CreateSyncRoot(const SyncStatusCallback& callback) {
274   DCHECK(!sync_root_folder_);
275   set_used_network(true);
276   cancel_callback_ = drive_service_->AddNewDirectory(
277       root_folder_id_, kSyncRootFolderTitle,
278       base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
279                  weak_ptr_factory_.GetWeakPtr(),
280                  callback));
281 }
282
283 void SyncEngineInitializer::DidCreateSyncRoot(
284     const SyncStatusCallback& callback,
285     google_apis::GDataErrorCode error,
286     scoped_ptr<google_apis::ResourceEntry> entry) {
287   DCHECK(!sync_root_folder_);
288   cancel_callback_.Reset();
289
290   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
291   if (status != SYNC_STATUS_OK) {
292     util::Log(logging::LOG_VERBOSE, FROM_HERE,
293               "[Initialize] Failed to create sync root.");
294     callback.Run(status);
295     return;
296   }
297
298   FindSyncRoot(callback);
299 }
300
301 void SyncEngineInitializer::DetachSyncRoot(const SyncStatusCallback& callback) {
302   DCHECK(sync_root_folder_);
303   set_used_network(true);
304   cancel_callback_ = drive_service_->RemoveResourceFromDirectory(
305       root_folder_id_, GetID(*sync_root_folder_),
306       base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
307                  weak_ptr_factory_.GetWeakPtr(),
308                  callback));
309 }
310
311 void SyncEngineInitializer::DidDetachSyncRoot(
312     const SyncStatusCallback& callback,
313     google_apis::GDataErrorCode error) {
314   cancel_callback_.Reset();
315
316   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
317   if (status != SYNC_STATUS_OK) {
318     util::Log(logging::LOG_VERBOSE, FROM_HERE,
319               "[Initialize] Failed to detach sync root.");
320     callback.Run(status);
321     return;
322   }
323
324   ListAppRootFolders(callback);
325 }
326
327 void SyncEngineInitializer::ListAppRootFolders(
328     const SyncStatusCallback& callback) {
329   DCHECK(sync_root_folder_);
330   set_used_network(true);
331   cancel_callback_ = drive_service_->GetResourceListInDirectory(
332       GetID(*sync_root_folder_),
333       base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
334                  weak_ptr_factory_.GetWeakPtr(),
335                  callback));
336 }
337
338 void SyncEngineInitializer::DidListAppRootFolders(
339     const SyncStatusCallback& callback,
340     google_apis::GDataErrorCode error,
341     scoped_ptr<google_apis::ResourceList> resource_list) {
342   cancel_callback_.Reset();
343
344   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
345   if (status != SYNC_STATUS_OK) {
346     util::Log(logging::LOG_VERBOSE, FROM_HERE,
347               "[Initialize] Failed to get initial app-root folders.");
348     callback.Run(status);
349     return;
350   }
351
352   if (!resource_list) {
353     NOTREACHED();
354     util::Log(logging::LOG_VERBOSE, FROM_HERE,
355               "[Initialize] Got invalid initial app-root list.");
356     callback.Run(SYNC_STATUS_FAILED);
357     return;
358   }
359
360   ScopedVector<google_apis::ResourceEntry>* new_entries =
361       resource_list->mutable_entries();
362   app_root_folders_.insert(app_root_folders_.end(),
363                            new_entries->begin(), new_entries->end());
364   new_entries->weak_clear();
365
366   set_used_network(true);
367   if (GetRemainingFileList(
368           &cancel_callback_,
369           drive_service_,
370           *resource_list,
371           base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
372                      weak_ptr_factory_.GetWeakPtr(), callback)))
373     return;
374
375   PopulateDatabase(callback);
376 }
377
378 void SyncEngineInitializer::PopulateDatabase(
379     const SyncStatusCallback& callback) {
380   DCHECK(sync_root_folder_);
381   metadata_database_->PopulateInitialData(
382       largest_change_id_,
383       *drive::util::ConvertResourceEntryToFileResource(
384           *sync_root_folder_),
385       ConvertResourceEntriesToFileResources(app_root_folders_),
386       base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
387                  weak_ptr_factory_.GetWeakPtr(),
388                  callback));
389 }
390
391 void SyncEngineInitializer::DidPopulateDatabase(
392     const SyncStatusCallback& callback,
393     SyncStatusCode status) {
394   if (status != SYNC_STATUS_OK) {
395     util::Log(logging::LOG_VERBOSE, FROM_HERE,
396               "[Initialize] Failed to populate initial data"
397               " to MetadataDatabase.");
398     callback.Run(status);
399     return;
400   }
401
402   util::Log(logging::LOG_VERBOSE, FROM_HERE,
403             "[Initialize] Completed successfully.");
404   callback.Run(SYNC_STATUS_OK);
405 }
406
407 }  // namespace drive_backend
408 }  // namespace sync_file_system