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/entry_update_performer.h"
7 #include "chrome/browser/chromeos/drive/drive.pb.h"
8 #include "chrome/browser/chromeos/drive/file_cache.h"
9 #include "chrome/browser/chromeos/drive/file_system_util.h"
10 #include "chrome/browser/chromeos/drive/job_scheduler.h"
11 #include "chrome/browser/chromeos/drive/resource_metadata.h"
12 #include "chrome/browser/chromeos/drive/sync/entry_revert_performer.h"
13 #include "chrome/browser/chromeos/drive/sync/remove_performer.h"
14 #include "content/public/browser/browser_thread.h"
16 using content::BrowserThread;
21 struct EntryUpdatePerformer::LocalState {
22 LocalState() : should_content_update(false) {
26 ResourceEntry parent_entry;
27 base::FilePath drive_file_path;
28 base::FilePath cache_file_path;
29 bool should_content_update;
34 // Looks up ResourceEntry for source entry and its parent.
35 FileError PrepareUpdate(ResourceMetadata* metadata,
37 const std::string& local_id,
38 EntryUpdatePerformer::LocalState* local_state) {
39 FileError error = metadata->GetResourceEntryById(local_id,
41 if (error != FILE_ERROR_OK)
44 error = metadata->GetResourceEntryById(local_state->entry.parent_local_id(),
45 &local_state->parent_entry);
46 if (error != FILE_ERROR_OK)
49 local_state->drive_file_path = metadata->GetFilePath(local_id);
50 if (local_state->drive_file_path.empty())
51 return FILE_ERROR_NOT_FOUND;
53 // Check if content update is needed or not.
54 FileCacheEntry cache_entry;
55 if (cache->GetCacheEntry(local_id, &cache_entry) &&
56 cache_entry.is_dirty() &&
57 !cache->IsOpenedForWrite(local_id)) {
58 // Update cache entry's MD5 if needed.
59 if (cache_entry.md5().empty()) {
60 error = cache->UpdateMd5(local_id);
61 if (error != FILE_ERROR_OK)
63 if (!cache->GetCacheEntry(local_id, &cache_entry))
64 return FILE_ERROR_NOT_FOUND;
67 if (cache_entry.md5() == local_state->entry.file_specific_info().md5()) {
68 error = cache->ClearDirty(local_id);
69 if (error != FILE_ERROR_OK)
72 error = cache->GetFile(local_id, &local_state->cache_file_path);
73 if (error != FILE_ERROR_OK)
76 local_state->should_content_update = true;
80 // Update metadata_edit_state.
81 switch (local_state->entry.metadata_edit_state()) {
82 case ResourceEntry::CLEAN: // Nothing to do.
83 case ResourceEntry::SYNCING: // Error during the last update. Go ahead.
86 case ResourceEntry::DIRTY:
87 local_state->entry.set_metadata_edit_state(ResourceEntry::SYNCING);
88 error = metadata->RefreshEntry(local_state->entry);
89 if (error != FILE_ERROR_OK)
96 FileError FinishUpdate(ResourceMetadata* metadata,
98 const std::string& local_id,
99 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
101 FileError error = metadata->GetResourceEntryById(local_id, &entry);
102 if (error != FILE_ERROR_OK)
105 // Update metadata_edit_state and MD5.
106 switch (entry.metadata_edit_state()) {
107 case ResourceEntry::CLEAN: // Nothing to do.
108 case ResourceEntry::DIRTY: // Entry was edited again during the update.
111 case ResourceEntry::SYNCING:
112 entry.set_metadata_edit_state(ResourceEntry::CLEAN);
115 if (!entry.file_info().is_directory())
116 entry.mutable_file_specific_info()->set_md5(resource_entry->file_md5());
117 error = metadata->RefreshEntry(entry);
118 if (error != FILE_ERROR_OK)
121 // Clear dirty bit unless the file has been edited during update.
122 FileCacheEntry cache_entry;
123 if (cache->GetCacheEntry(local_id, &cache_entry) &&
124 cache_entry.md5() == entry.file_specific_info().md5()) {
125 error = cache->ClearDirty(local_id);
126 if (error != FILE_ERROR_OK)
129 return FILE_ERROR_OK;
134 EntryUpdatePerformer::EntryUpdatePerformer(
135 base::SequencedTaskRunner* blocking_task_runner,
136 file_system::OperationObserver* observer,
137 JobScheduler* scheduler,
138 ResourceMetadata* metadata,
140 : blocking_task_runner_(blocking_task_runner),
141 scheduler_(scheduler),
144 remove_performer_(new RemovePerformer(blocking_task_runner,
148 entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner,
152 weak_ptr_factory_(this) {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 EntryUpdatePerformer::~EntryUpdatePerformer() {
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
160 void EntryUpdatePerformer::UpdateEntry(const std::string& local_id,
161 const ClientContext& context,
162 const FileOperationCallback& callback) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164 DCHECK(!callback.is_null());
166 scoped_ptr<LocalState> local_state(new LocalState);
167 LocalState* local_state_ptr = local_state.get();
168 base::PostTaskAndReplyWithResult(
169 blocking_task_runner_.get(),
171 base::Bind(&PrepareUpdate, metadata_, cache_, local_id, local_state_ptr),
172 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterPrepare,
173 weak_ptr_factory_.GetWeakPtr(), context, callback,
174 base::Passed(&local_state)));
177 void EntryUpdatePerformer::UpdateEntryAfterPrepare(
178 const ClientContext& context,
179 const FileOperationCallback& callback,
180 scoped_ptr<LocalState> local_state,
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 DCHECK(!callback.is_null());
185 if (error != FILE_ERROR_OK) {
190 // Trashed entry should be removed.
191 if (local_state->entry.parent_local_id() == util::kDriveTrashDirLocalId) {
192 remove_performer_->Remove(local_state->entry.local_id(), context, callback);
196 base::Time last_modified = base::Time::FromInternalValue(
197 local_state->entry.file_info().last_modified());
198 base::Time last_accessed = base::Time::FromInternalValue(
199 local_state->entry.file_info().last_accessed());
201 // Perform content update.
202 if (local_state->should_content_update) {
203 drive::DriveUploader::UploadExistingFileOptions options;
204 options.title = local_state->entry.title();
205 options.parent_resource_id = local_state->parent_entry.resource_id();
206 options.modified_date = last_modified;
207 options.last_viewed_by_me_date = last_accessed;
208 scheduler_->UploadExistingFile(
209 local_state->entry.resource_id(),
210 local_state->drive_file_path,
211 local_state->cache_file_path,
212 local_state->entry.file_specific_info().content_mime_type(),
215 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
216 weak_ptr_factory_.GetWeakPtr(),
219 local_state->entry.local_id()));
223 // No need to perform update.
224 if (local_state->entry.metadata_edit_state() == ResourceEntry::CLEAN) {
225 callback.Run(FILE_ERROR_OK);
229 // Perform metadata update.
230 scheduler_->UpdateResource(
231 local_state->entry.resource_id(), local_state->parent_entry.resource_id(),
232 local_state->entry.title(), last_modified, last_accessed,
234 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
235 weak_ptr_factory_.GetWeakPtr(),
236 context, callback, local_state->entry.local_id()));
239 void EntryUpdatePerformer::UpdateEntryAfterUpdateResource(
240 const ClientContext& context,
241 const FileOperationCallback& callback,
242 const std::string& local_id,
243 google_apis::GDataErrorCode status,
244 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247 if (status == google_apis::HTTP_FORBIDDEN) {
248 // Editing this entry is not allowed, revert local changes.
249 entry_revert_performer_->RevertEntry(local_id, context, callback);
253 FileError error = GDataToFileError(status);
254 if (error != FILE_ERROR_OK) {
259 base::PostTaskAndReplyWithResult(
260 blocking_task_runner_.get(),
262 base::Bind(&FinishUpdate,
263 metadata_, cache_, local_id, base::Passed(&resource_entry)),
267 } // namespace internal