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_CURRENTLY_ON(BrowserThread::UI
);
69 RemovePerformer::~RemovePerformer() {
70 DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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::DriveApiErrorCode status
) {
150 DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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::DriveApiErrorCode status
,
192 scoped_ptr
<google_apis::FileResource
> file_resource
) {
193 DCHECK_CURRENTLY_ON(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::DriveApiErrorCode status
) {
242 DCHECK_CURRENTLY_ON(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