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.
5 #include "chrome/browser/chromeos/drive/sync/remove_performer.h"
7 #include "base/sequenced_task_runner.h"
8 #include "chrome/browser/chromeos/drive/drive.pb.h"
9 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
10 #include "chrome/browser/chromeos/drive/file_system_util.h"
11 #include "chrome/browser/chromeos/drive/job_scheduler.h"
12 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
13 #include "chrome/browser/chromeos/drive/resource_metadata.h"
14 #include "chrome/browser/chromeos/drive/sync/entry_revert_performer.h"
15 #include "chrome/browser/drive/drive_api_util.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "google_apis/drive/drive_api_parser.h"
19 using content::BrowserThread;
26 // Updates local metadata and after remote unparenting.
27 FileError UpdateLocalStateAfterUnparent(ResourceMetadata* metadata,
28 const std::string& local_id) {
30 FileError error = metadata->GetResourceEntryById(local_id, &entry);
31 if (error != FILE_ERROR_OK)
33 entry.set_parent_local_id(util::kDriveOtherDirLocalId);
34 return metadata->RefreshEntry(entry);
37 // Utility function to run ResourceMetadata::RemoveEntry from UI thread.
38 void RemoveEntryOnUIThread(base::SequencedTaskRunner* blocking_task_runner,
39 ResourceMetadata* metadata,
40 const std::string& local_id,
41 const FileOperationCallback& callback) {
42 base::PostTaskAndReplyWithResult(
45 base::Bind(&ResourceMetadata::RemoveEntry,
46 base::Unretained(metadata), local_id),
52 RemovePerformer::RemovePerformer(
53 base::SequencedTaskRunner* blocking_task_runner,
54 file_system::OperationDelegate* delegate,
55 JobScheduler* scheduler,
56 ResourceMetadata* metadata)
57 : blocking_task_runner_(blocking_task_runner),
59 scheduler_(scheduler),
61 entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner,
65 weak_ptr_factory_(this) {
66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
69 RemovePerformer::~RemovePerformer() {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73 // Returns |entry| corresponding to |local_id|.
74 // Adding to that, removes the entry when it does not exist on the server.
75 FileError TryToRemoveLocally(ResourceMetadata* metadata,
76 const std::string& local_id,
77 ResourceEntry* entry) {
78 FileError error = metadata->GetResourceEntryById(local_id, entry);
79 if (error != FILE_ERROR_OK || !entry->resource_id().empty())
81 return metadata->RemoveEntry(local_id);
84 void RemovePerformer::Remove(const std::string& local_id,
85 const ClientContext& context,
86 const FileOperationCallback& callback) {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88 DCHECK(!callback.is_null());
90 ResourceEntry* entry = new ResourceEntry;
91 base::PostTaskAndReplyWithResult(
92 blocking_task_runner_.get(),
94 base::Bind(&TryToRemoveLocally, metadata_, local_id, entry),
95 base::Bind(&RemovePerformer::RemoveAfterGetResourceEntry,
96 weak_ptr_factory_.GetWeakPtr(),
102 void RemovePerformer::RemoveAfterGetResourceEntry(
103 const ClientContext& context,
104 const FileOperationCallback& callback,
105 const ResourceEntry* entry,
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108 DCHECK(!callback.is_null());
110 if (error != FILE_ERROR_OK || entry->resource_id().empty()) {
115 // To match with the behavior of drive.google.com:
116 // Removal of shared entries under MyDrive is just removing from the parent.
117 // The entry will stay in shared-with-me (in other words, in "drive/other".)
119 // TODO(kinaba): to be more precise, we might be better to branch by whether
120 // or not the current account is an owner of the file. The code below is
121 // written under the assumption that |shared_with_me| coincides with that.
122 if (entry->shared_with_me()) {
123 UnparentResource(context, callback, entry->resource_id(),
126 // Otherwise try sending the entry to trash.
127 TrashResource(context, callback, entry->resource_id(), entry->local_id());
131 void RemovePerformer::TrashResource(const ClientContext& context,
132 const FileOperationCallback& callback,
133 const std::string& resource_id,
134 const std::string& local_id) {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
136 DCHECK(!callback.is_null());
138 scheduler_->TrashResource(
141 base::Bind(&RemovePerformer::TrashResourceAfterUpdateRemoteState,
142 weak_ptr_factory_.GetWeakPtr(), context, callback, local_id));
145 void RemovePerformer::TrashResourceAfterUpdateRemoteState(
146 const ClientContext& context,
147 const FileOperationCallback& callback,
148 const std::string& local_id,
149 google_apis::GDataErrorCode status) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151 DCHECK(!callback.is_null());
153 if (status == google_apis::HTTP_FORBIDDEN) {
154 // Editing this entry is not allowed, revert local changes.
155 entry_revert_performer_->RevertEntry(local_id, context, callback);
156 delegate_->OnDriveSyncError(
157 file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION, local_id);
161 FileError error = GDataToFileError(status);
162 if (error == FILE_ERROR_NOT_FOUND) { // Remove local entry when not found.
163 RemoveEntryOnUIThread(blocking_task_runner_.get(), metadata_, local_id,
168 // Now we're done. If the entry is trashed on the server, it'll be also
169 // deleted locally on the next update.
173 void RemovePerformer::UnparentResource(const ClientContext& context,
174 const FileOperationCallback& callback,
175 const std::string& resource_id,
176 const std::string& local_id) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
178 DCHECK(!callback.is_null());
180 scheduler_->GetFileResource(
183 base::Bind(&RemovePerformer::UnparentResourceAfterGetFileResource,
184 weak_ptr_factory_.GetWeakPtr(), context, callback, local_id));
187 void RemovePerformer::UnparentResourceAfterGetFileResource(
188 const ClientContext& context,
189 const FileOperationCallback& callback,
190 const std::string& local_id,
191 google_apis::GDataErrorCode status,
192 scoped_ptr<google_apis::FileResource> file_resource) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
194 DCHECK(!callback.is_null());
196 FileError error = GDataToFileError(status);
197 if (error == FILE_ERROR_NOT_FOUND) { // Remove local entry when not found.
198 RemoveEntryOnUIThread(blocking_task_runner_.get(), metadata_, local_id,
203 if (error != FILE_ERROR_OK) {
209 std::string parent_resource_id;
210 if (!ConvertFileResourceToResourceEntry(*file_resource, &entry,
211 &parent_resource_id)) {
212 callback.Run(FILE_ERROR_NOT_A_FILE);
216 if (!entry.shared_with_me()) {
217 // shared_with_me() has changed on the server.
218 UnparentResourceAfterUpdateRemoteState(callback, local_id,
219 google_apis::HTTP_CONFLICT);
223 if (parent_resource_id.empty()) {
224 // This entry is unparented already.
225 UnparentResourceAfterUpdateRemoteState(callback, local_id,
226 google_apis::HTTP_NO_CONTENT);
230 scheduler_->RemoveResourceFromDirectory(
234 base::Bind(&RemovePerformer::UnparentResourceAfterUpdateRemoteState,
235 weak_ptr_factory_.GetWeakPtr(), callback, local_id));
238 void RemovePerformer::UnparentResourceAfterUpdateRemoteState(
239 const FileOperationCallback& callback,
240 const std::string& local_id,
241 google_apis::GDataErrorCode status) {
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 DCHECK(!callback.is_null());
245 FileError error = GDataToFileError(status);
246 if (error != FILE_ERROR_OK) {
251 base::PostTaskAndReplyWithResult(
252 blocking_task_runner_.get(),
254 base::Bind(&UpdateLocalStateAfterUnparent, metadata_, local_id),
258 } // namespace internal