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_observer.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/sync/entry_revert_performer.h"
14 #include "content/public/browser/browser_thread.h"
16 using content::BrowserThread
;
23 // Updates local metadata and after remote unparenting.
24 FileError
UpdateLocalStateAfterUnparent(ResourceMetadata
* metadata
,
25 const std::string
& local_id
) {
27 FileError error
= metadata
->GetResourceEntryById(local_id
, &entry
);
28 if (error
!= FILE_ERROR_OK
)
30 entry
.set_parent_local_id(util::kDriveOtherDirLocalId
);
31 return metadata
->RefreshEntry(entry
);
34 // Utility function to run ResourceMetadata::RemoveEntry from UI thread.
35 void RemoveEntryOnUIThread(base::SequencedTaskRunner
* blocking_task_runner
,
36 ResourceMetadata
* metadata
,
37 const std::string
& local_id
,
38 const FileOperationCallback
& callback
) {
39 base::PostTaskAndReplyWithResult(
42 base::Bind(&ResourceMetadata::RemoveEntry
,
43 base::Unretained(metadata
), local_id
),
49 RemovePerformer::RemovePerformer(
50 base::SequencedTaskRunner
* blocking_task_runner
,
51 file_system::OperationObserver
* observer
,
52 JobScheduler
* scheduler
,
53 ResourceMetadata
* metadata
)
54 : blocking_task_runner_(blocking_task_runner
),
56 scheduler_(scheduler
),
58 entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner
,
62 weak_ptr_factory_(this) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
66 RemovePerformer::~RemovePerformer() {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
70 void RemovePerformer::Remove(const std::string
& local_id
,
71 const FileOperationCallback
& callback
) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
73 DCHECK(!callback
.is_null());
75 ResourceEntry
* entry
= new ResourceEntry
;
76 base::PostTaskAndReplyWithResult(
77 blocking_task_runner_
.get(),
79 base::Bind(&ResourceMetadata::GetResourceEntryById
,
80 base::Unretained(metadata_
),
83 base::Bind(&RemovePerformer::RemoveAfterGetResourceEntry
,
84 weak_ptr_factory_
.GetWeakPtr(),
89 void RemovePerformer::RemoveAfterGetResourceEntry(
90 const FileOperationCallback
& callback
,
91 const ResourceEntry
* entry
,
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
94 DCHECK(!callback
.is_null());
96 if (error
!= FILE_ERROR_OK
) {
101 // To match with the behavior of drive.google.com:
102 // Removal of shared entries under MyDrive is just removing from the parent.
103 // The entry will stay in shared-with-me (in other words, in "drive/other".)
105 // TODO(kinaba): to be more precise, we might be better to branch by whether
106 // or not the current account is an owner of the file. The code below is
107 // written under the assumption that |shared_with_me| coincides with that.
108 if (entry
->shared_with_me()) {
109 UnparentResource(callback
, entry
->resource_id(), entry
->local_id());
111 // Otherwise try sending the entry to trash.
112 TrashResource(callback
, entry
->resource_id(), entry
->local_id());
116 void RemovePerformer::TrashResource(const FileOperationCallback
& callback
,
117 const std::string
& resource_id
,
118 const std::string
& local_id
) {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
120 DCHECK(!callback
.is_null());
122 scheduler_
->TrashResource(
124 ClientContext(BACKGROUND
),
125 base::Bind(&RemovePerformer::TrashResourceAfterUpdateRemoteState
,
126 weak_ptr_factory_
.GetWeakPtr(), callback
, local_id
));
129 void RemovePerformer::TrashResourceAfterUpdateRemoteState(
130 const FileOperationCallback
& callback
,
131 const std::string
& local_id
,
132 google_apis::GDataErrorCode status
) {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
134 DCHECK(!callback
.is_null());
136 if (status
== google_apis::HTTP_FORBIDDEN
) {
137 // Editing this entry is not allowed, revert local changes.
138 entry_revert_performer_
->RevertEntry(local_id
, callback
);
139 observer_
->OnDriveSyncError(
140 file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION
, local_id
);
144 FileError error
= GDataToFileError(status
);
145 if (error
== FILE_ERROR_NOT_FOUND
) { // Remove local entry when not found.
146 RemoveEntryOnUIThread(blocking_task_runner_
.get(), metadata_
, local_id
,
151 // Now we're done. If the entry is trashed on the server, it'll be also
152 // deleted locally on the next update.
156 void RemovePerformer::UnparentResource(
157 const FileOperationCallback
& callback
,
158 const std::string
& resource_id
,
159 const std::string
& local_id
) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
161 DCHECK(!callback
.is_null());
163 scheduler_
->GetResourceEntry(
165 ClientContext(BACKGROUND
),
166 base::Bind(&RemovePerformer::UnparentResourceAfterGetResourceEntry
,
167 weak_ptr_factory_
.GetWeakPtr(), callback
, local_id
));
170 void RemovePerformer::UnparentResourceAfterGetResourceEntry(
171 const FileOperationCallback
& callback
,
172 const std::string
& local_id
,
173 google_apis::GDataErrorCode status
,
174 scoped_ptr
<google_apis::ResourceEntry
> resource_entry
) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
176 DCHECK(!callback
.is_null());
178 FileError error
= GDataToFileError(status
);
179 if (error
== FILE_ERROR_NOT_FOUND
) { // Remove local entry when not found.
180 RemoveEntryOnUIThread(blocking_task_runner_
.get(), metadata_
, local_id
,
185 if (error
!= FILE_ERROR_OK
) {
191 std::string parent_resource_id
;
192 if (!ConvertToResourceEntry(*resource_entry
, &entry
, &parent_resource_id
)) {
193 callback
.Run(FILE_ERROR_NOT_A_FILE
);
197 if (!entry
.shared_with_me()) {
198 // shared_with_me() has changed on the server.
199 UnparentResourceAfterUpdateRemoteState(callback
, local_id
,
200 google_apis::HTTP_CONFLICT
);
204 if (parent_resource_id
.empty()) {
205 // This entry is unparented already.
206 UnparentResourceAfterUpdateRemoteState(callback
, local_id
,
207 google_apis::HTTP_NO_CONTENT
);
211 scheduler_
->RemoveResourceFromDirectory(
214 ClientContext(BACKGROUND
),
215 base::Bind(&RemovePerformer::UnparentResourceAfterUpdateRemoteState
,
216 weak_ptr_factory_
.GetWeakPtr(), callback
, local_id
));
219 void RemovePerformer::UnparentResourceAfterUpdateRemoteState(
220 const FileOperationCallback
& callback
,
221 const std::string
& local_id
,
222 google_apis::GDataErrorCode status
) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
224 DCHECK(!callback
.is_null());
226 FileError error
= GDataToFileError(status
);
227 if (error
!= FILE_ERROR_OK
) {
232 base::PostTaskAndReplyWithResult(
233 blocking_task_runner_
.get(),
235 base::Bind(&UpdateLocalStateAfterUnparent
, metadata_
, local_id
),
239 } // namespace internal