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/register_app_task.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "chrome/browser/drive/drive_api_util.h"
10#include "chrome/browser/drive/drive_service_interface.h"
11#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
12#include "chrome/browser/sync_file_system/drive_backend/folder_creator.h"
13#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
14#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
15#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
16#include "chrome/browser/sync_file_system/drive_backend/tracker_id_set.h"
17#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
18#include "google_apis/drive/drive_api_parser.h"
19#include "google_apis/drive/gdata_wapi_parser.h"
20
21namespace sync_file_system {
22namespace drive_backend {
23
24namespace {
25
26bool CompareOnCTime(const FileTracker& left,
27                    const FileTracker& right) {
28  return left.synced_details().creation_time() <
29      right.synced_details().creation_time();
30}
31
32}  // namespace
33
34RegisterAppTask::RegisterAppTask(SyncEngineContext* sync_context,
35                                 const std::string& app_id)
36    : sync_context_(sync_context),
37      create_folder_retry_count_(0),
38      app_id_(app_id),
39      weak_ptr_factory_(this) {
40}
41
42RegisterAppTask::~RegisterAppTask() {
43}
44
45bool RegisterAppTask::CanFinishImmediately() {
46  return metadata_database() &&
47         metadata_database()->FindAppRootTracker(app_id_, NULL);
48}
49
50void RegisterAppTask::RunExclusive(const SyncStatusCallback& callback) {
51  if (create_folder_retry_count_++ >= kMaxRetry) {
52    callback.Run(SYNC_STATUS_FAILED);
53    return;
54  }
55
56  if (!metadata_database() || !drive_service()) {
57    callback.Run(SYNC_STATUS_FAILED);
58    return;
59  }
60
61  int64 sync_root = metadata_database()->GetSyncRootTrackerID();
62  TrackerIDSet trackers;
63  if (!metadata_database()->FindTrackersByParentAndTitle(
64          sync_root, app_id_, &trackers)) {
65    CreateAppRootFolder(callback);
66    return;
67  }
68
69  FileTracker candidate;
70  if (!FilterCandidates(trackers, &candidate)) {
71    CreateAppRootFolder(callback);
72    return;
73  }
74
75  if (candidate.active()) {
76    DCHECK(candidate.tracker_kind() == TRACKER_KIND_APP_ROOT ||
77           candidate.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
78    callback.Run(SYNC_STATUS_OK);
79    return;
80  }
81
82  RegisterAppIntoDatabase(candidate, callback);
83}
84
85void RegisterAppTask::CreateAppRootFolder(const SyncStatusCallback& callback) {
86  int64 sync_root_tracker_id = metadata_database()->GetSyncRootTrackerID();
87  FileTracker sync_root_tracker;
88  bool should_success = metadata_database()->FindTrackerByTrackerID(
89      sync_root_tracker_id,
90      &sync_root_tracker);
91  DCHECK(should_success);
92
93  DCHECK(!folder_creator_);
94  folder_creator_.reset(new FolderCreator(
95      drive_service(), metadata_database(),
96      sync_root_tracker.file_id(), app_id_));
97  folder_creator_->Run(base::Bind(
98      &RegisterAppTask::DidCreateAppRootFolder,
99      weak_ptr_factory_.GetWeakPtr(), callback));
100}
101
102void RegisterAppTask::DidCreateAppRootFolder(
103    const SyncStatusCallback& callback,
104    const std::string& folder_id,
105    SyncStatusCode status) {
106  scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
107  if (status != SYNC_STATUS_OK) {
108    callback.Run(status);
109    return;
110  }
111
112  RunExclusive(callback);
113}
114
115bool RegisterAppTask::FilterCandidates(const TrackerIDSet& trackers,
116                                       FileTracker* candidate) {
117  DCHECK(candidate);
118  if (trackers.has_active()) {
119    if (metadata_database()->FindTrackerByTrackerID(
120            trackers.active_tracker(), candidate)) {
121      return true;
122    }
123    NOTREACHED();
124  }
125
126  scoped_ptr<FileTracker> oldest_tracker;
127  for (TrackerIDSet::const_iterator itr = trackers.begin();
128       itr != trackers.end(); ++itr) {
129    scoped_ptr<FileTracker> tracker(new FileTracker);
130    if (!metadata_database()->FindTrackerByTrackerID(
131            *itr, tracker.get())) {
132      NOTREACHED();
133      continue;
134    }
135
136    FileMetadata file;
137    DCHECK(!tracker->active());
138    DCHECK(tracker->has_synced_details());
139
140    if (!metadata_database()->FindFileByFileID(tracker->file_id(), &file)) {
141      NOTREACHED();
142      // The parent folder is sync-root, whose contents are fetched in
143      // initialization sequence.
144      // So at this point, direct children of sync-root should have
145      // FileMetadata.
146      continue;
147    }
148
149    if (file.details().file_kind() != FILE_KIND_FOLDER)
150      continue;
151
152    if (file.details().missing())
153      continue;
154
155    if (oldest_tracker && CompareOnCTime(*oldest_tracker, *tracker))
156      continue;
157
158    oldest_tracker = tracker.Pass();
159  }
160
161  if (!oldest_tracker)
162    return false;
163
164  *candidate = *oldest_tracker;
165  return true;
166}
167
168void RegisterAppTask::RegisterAppIntoDatabase(
169    const FileTracker& tracker,
170    const SyncStatusCallback& callback) {
171  SyncStatusCode status =
172      metadata_database()->RegisterApp(app_id_, tracker.file_id());
173  callback.Run(status);
174}
175
176MetadataDatabase* RegisterAppTask::metadata_database() {
177  return sync_context_->GetMetadataDatabase();
178}
179
180drive::DriveServiceInterface* RegisterAppTask::drive_service() {
181  return sync_context_->GetDriveService();
182}
183
184}  // namespace drive_backend
185}  // namespace sync_file_system
186