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