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