Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / sync / entry_update_performer.cc
blob8cf208dcca878969da5650a0970d7d9e98218fcf
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 <set>
9 #include "base/callback_helpers.h"
10 #include "base/files/file_util.h"
11 #include "chrome/browser/chromeos/drive/change_list_loader.h"
12 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
13 #include "chrome/browser/chromeos/drive/sync/entry_revert_performer.h"
14 #include "chrome/browser/chromeos/drive/sync/remove_performer.h"
15 #include "components/drive/drive.pb.h"
16 #include "components/drive/file_cache.h"
17 #include "components/drive/file_change.h"
18 #include "components/drive/file_system_core_util.h"
19 #include "components/drive/job_scheduler.h"
20 #include "components/drive/resource_metadata.h"
21 #include "google_apis/drive/drive_api_parser.h"
23 namespace drive {
24 namespace internal {
26 struct EntryUpdatePerformer::LocalState {
27 LocalState() : cache_file_size(0), should_content_update(false) {}
29 ResourceEntry entry;
30 ResourceEntry parent_entry;
31 base::FilePath drive_file_path;
32 base::FilePath cache_file_path;
33 int64 cache_file_size;
34 bool should_content_update;
37 namespace {
39 struct PropertyCompare {
40 bool operator()(const drive::Property& x, const drive::Property& y) const {
41 if (x.key() < y.key())
42 return true;
43 if (x.key() > y.key())
44 return false;
45 if (x.value() < y.value())
46 return true;
47 if (y.value() > y.value())
48 return false;
49 return x.visibility() < y.visibility();
53 // Looks up ResourceEntry for source entry and its parent.
54 FileError PrepareUpdate(ResourceMetadata* metadata,
55 FileCache* cache,
56 const std::string& local_id,
57 EntryUpdatePerformer::LocalState* local_state) {
58 FileError error = metadata->GetResourceEntryById(local_id,
59 &local_state->entry);
60 if (error != FILE_ERROR_OK)
61 return error;
63 error = metadata->GetResourceEntryById(local_state->entry.parent_local_id(),
64 &local_state->parent_entry);
65 if (error != FILE_ERROR_OK)
66 return error;
68 error = metadata->GetFilePath(local_id, &local_state->drive_file_path);
69 if (error != FILE_ERROR_OK)
70 return error;
72 if (!local_state->entry.file_info().is_directory() &&
73 !local_state->entry.file_specific_info().cache_state().is_present() &&
74 local_state->entry.resource_id().empty()) {
75 // Locally created file with no cache file, store an empty file.
76 base::FilePath empty_file;
77 if (!base::CreateTemporaryFile(&empty_file))
78 return FILE_ERROR_FAILED;
79 error = cache->Store(local_id, std::string(), empty_file,
80 FileCache::FILE_OPERATION_MOVE);
81 if (error != FILE_ERROR_OK)
82 return error;
83 error = metadata->GetResourceEntryById(local_id, &local_state->entry);
84 if (error != FILE_ERROR_OK)
85 return error;
88 // Check if content update is needed or not.
89 if (local_state->entry.file_specific_info().cache_state().is_dirty() &&
90 !cache->IsOpenedForWrite(local_id)) {
91 // Update cache entry's MD5 if needed.
92 if (local_state->entry.file_specific_info().cache_state().md5().empty()) {
93 error = cache->UpdateMd5(local_id);
94 if (error != FILE_ERROR_OK)
95 return error;
96 error = metadata->GetResourceEntryById(local_id, &local_state->entry);
97 if (error != FILE_ERROR_OK)
98 return error;
101 if (local_state->entry.file_specific_info().cache_state().md5() ==
102 local_state->entry.file_specific_info().md5()) {
103 error = cache->ClearDirty(local_id);
104 if (error != FILE_ERROR_OK)
105 return error;
106 } else {
107 error = cache->GetFile(local_id, &local_state->cache_file_path);
108 if (error != FILE_ERROR_OK)
109 return error;
110 const bool result = base::GetFileSize(local_state->cache_file_path,
111 &local_state->cache_file_size);
112 if (!result)
113 return FILE_ERROR_FAILED;
114 local_state->should_content_update = true;
118 // Update metadata_edit_state.
119 switch (local_state->entry.metadata_edit_state()) {
120 case ResourceEntry::CLEAN: // Nothing to do.
121 case ResourceEntry::SYNCING: // Error during the last update. Go ahead.
122 break;
124 case ResourceEntry::DIRTY:
125 local_state->entry.set_metadata_edit_state(ResourceEntry::SYNCING);
126 error = metadata->RefreshEntry(local_state->entry);
127 if (error != FILE_ERROR_OK)
128 return error;
129 break;
131 return FILE_ERROR_OK;
134 FileError FinishUpdate(ResourceMetadata* metadata,
135 FileCache* cache,
136 scoped_ptr<EntryUpdatePerformer::LocalState> local_state,
137 scoped_ptr<google_apis::FileResource> file_resource,
138 FileChange* changed_files) {
139 ResourceEntry entry;
140 FileError error =
141 metadata->GetResourceEntryById(local_state->entry.local_id(), &entry);
142 if (error != FILE_ERROR_OK)
143 return error;
145 // When creating new entries, update check may add a new entry with the same
146 // resource ID before us. If such an entry exists, remove it.
147 std::string existing_local_id;
148 error =
149 metadata->GetIdByResourceId(file_resource->file_id(), &existing_local_id);
151 switch (error) {
152 case FILE_ERROR_OK:
153 if (existing_local_id != local_state->entry.local_id()) {
154 base::FilePath existing_entry_path;
155 error = metadata->GetFilePath(existing_local_id, &existing_entry_path);
156 if (error != FILE_ERROR_OK)
157 return error;
158 error = metadata->RemoveEntry(existing_local_id);
159 if (error != FILE_ERROR_OK)
160 return error;
161 changed_files->Update(existing_entry_path, entry,
162 FileChange::CHANGE_TYPE_DELETE);
164 break;
165 case FILE_ERROR_NOT_FOUND:
166 break;
167 default:
168 return error;
171 // Update metadata_edit_state and MD5.
172 switch (entry.metadata_edit_state()) {
173 case ResourceEntry::CLEAN: // Nothing to do.
174 case ResourceEntry::DIRTY: // Entry was edited again during the update.
175 break;
177 case ResourceEntry::SYNCING:
178 entry.set_metadata_edit_state(ResourceEntry::CLEAN);
179 break;
181 if (!entry.file_info().is_directory())
182 entry.mutable_file_specific_info()->set_md5(file_resource->md5_checksum());
183 entry.set_resource_id(file_resource->file_id());
185 // Keep only those properties which have been added or changed in the proto
186 // during the update.
187 std::set<drive::Property, PropertyCompare> synced_properties(
188 local_state->entry.new_properties().begin(),
189 local_state->entry.new_properties().end());
191 google::protobuf::RepeatedPtrField<drive::Property> not_synced_properties;
192 for (const auto& property : entry.new_properties()) {
193 if (!synced_properties.count(property)) {
194 Property* const not_synced_property = not_synced_properties.Add();
195 not_synced_property->CopyFrom(property);
198 entry.mutable_new_properties()->Swap(&not_synced_properties);
200 error = metadata->RefreshEntry(entry);
201 if (error != FILE_ERROR_OK)
202 return error;
203 base::FilePath entry_path;
204 error = metadata->GetFilePath(local_state->entry.local_id(), &entry_path);
205 if (error != FILE_ERROR_OK)
206 return error;
207 changed_files->Update(entry_path, entry,
208 FileChange::CHANGE_TYPE_ADD_OR_UPDATE);
210 // Clear dirty bit unless the file has been edited during update.
211 if (entry.file_specific_info().cache_state().is_dirty() &&
212 entry.file_specific_info().cache_state().md5() ==
213 entry.file_specific_info().md5()) {
214 error = cache->ClearDirty(local_state->entry.local_id());
215 if (error != FILE_ERROR_OK)
216 return error;
218 return FILE_ERROR_OK;
221 } // namespace
223 EntryUpdatePerformer::EntryUpdatePerformer(
224 base::SequencedTaskRunner* blocking_task_runner,
225 file_system::OperationDelegate* delegate,
226 JobScheduler* scheduler,
227 ResourceMetadata* metadata,
228 FileCache* cache,
229 LoaderController* loader_controller)
230 : blocking_task_runner_(blocking_task_runner),
231 delegate_(delegate),
232 scheduler_(scheduler),
233 metadata_(metadata),
234 cache_(cache),
235 loader_controller_(loader_controller),
236 remove_performer_(new RemovePerformer(blocking_task_runner,
237 delegate,
238 scheduler,
239 metadata)),
240 entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner,
241 delegate,
242 scheduler,
243 metadata)),
244 weak_ptr_factory_(this) {
247 EntryUpdatePerformer::~EntryUpdatePerformer() {
248 DCHECK(thread_checker_.CalledOnValidThread());
251 void EntryUpdatePerformer::UpdateEntry(const std::string& local_id,
252 const ClientContext& context,
253 const FileOperationCallback& callback) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255 DCHECK(!callback.is_null());
257 scoped_ptr<LocalState> local_state(new LocalState);
258 LocalState* const local_state_ptr = local_state.get();
259 base::PostTaskAndReplyWithResult(
260 blocking_task_runner_.get(),
261 FROM_HERE,
262 base::Bind(&PrepareUpdate, metadata_, cache_, local_id, local_state_ptr),
263 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterPrepare,
264 weak_ptr_factory_.GetWeakPtr(), context, callback,
265 base::Passed(&local_state)));
268 void EntryUpdatePerformer::UpdateEntryAfterPrepare(
269 const ClientContext& context,
270 const FileOperationCallback& callback,
271 scoped_ptr<LocalState> local_state,
272 FileError error) {
273 DCHECK(thread_checker_.CalledOnValidThread());
274 DCHECK(!callback.is_null());
276 if (error != FILE_ERROR_OK) {
277 callback.Run(error);
278 return;
281 // Trashed entry should be removed.
282 if (local_state->entry.parent_local_id() == util::kDriveTrashDirLocalId) {
283 remove_performer_->Remove(local_state->entry.local_id(), context, callback);
284 return;
287 // Parent was locally created and needs update. Just return for now.
288 // This entry should be updated again after the parent update completes.
289 if (local_state->parent_entry.resource_id().empty() &&
290 local_state->parent_entry.metadata_edit_state() != ResourceEntry::CLEAN) {
291 callback.Run(FILE_ERROR_OK);
292 return;
295 base::Time last_modified = base::Time::FromInternalValue(
296 local_state->entry.file_info().last_modified());
297 base::Time last_accessed = base::Time::FromInternalValue(
298 local_state->entry.file_info().last_accessed());
300 // Compose a list of new properties from the proto.
301 google_apis::drive::Properties properties;
302 for (const auto& proto_property : local_state->entry.new_properties()) {
303 google_apis::drive::Property property;
304 switch (proto_property.visibility()) {
305 case Property_Visibility_PRIVATE:
306 property.set_visibility(
307 google_apis::drive::Property::VISIBILITY_PRIVATE);
308 break;
309 case Property_Visibility_PUBLIC:
310 property.set_visibility(
311 google_apis::drive::Property::VISIBILITY_PUBLIC);
312 break;
314 property.set_key(proto_property.key());
315 property.set_value(proto_property.value());
316 properties.push_back(property);
319 // Perform content update.
320 if (local_state->should_content_update) {
321 if (local_state->entry.resource_id().empty()) {
322 // Not locking the loader intentionally here to avoid making the UI
323 // unresponsive while uploading large files.
324 // FinishUpdate() is responsible to resolve conflicts caused by this.
325 scoped_ptr<base::ScopedClosureRunner> null_loader_lock;
327 UploadNewFileOptions options;
328 options.modified_date = last_modified;
329 options.last_viewed_by_me_date = last_accessed;
330 options.properties = properties;
331 LocalState* const local_state_ptr = local_state.get();
332 scheduler_->UploadNewFile(
333 local_state_ptr->parent_entry.resource_id(),
334 local_state_ptr->cache_file_size, local_state_ptr->drive_file_path,
335 local_state_ptr->cache_file_path, local_state_ptr->entry.title(),
336 local_state_ptr->entry.file_specific_info().content_mime_type(),
337 options, context,
338 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
339 weak_ptr_factory_.GetWeakPtr(), context, callback,
340 base::Passed(&local_state),
341 base::Passed(&null_loader_lock)));
342 } else {
343 UploadExistingFileOptions options;
344 options.title = local_state->entry.title();
345 options.parent_resource_id = local_state->parent_entry.resource_id();
346 options.modified_date = last_modified;
347 options.last_viewed_by_me_date = last_accessed;
348 options.properties = properties;
349 LocalState* const local_state_ptr = local_state.get();
350 scheduler_->UploadExistingFile(
351 local_state_ptr->entry.resource_id(),
352 local_state_ptr->cache_file_size, local_state_ptr->drive_file_path,
353 local_state_ptr->cache_file_path,
354 local_state_ptr->entry.file_specific_info().content_mime_type(),
355 options, context,
356 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
357 weak_ptr_factory_.GetWeakPtr(), context, callback,
358 base::Passed(&local_state),
359 base::Passed(scoped_ptr<base::ScopedClosureRunner>())));
361 return;
364 // Create directory.
365 if (local_state->entry.file_info().is_directory() &&
366 local_state->entry.resource_id().empty()) {
367 // Lock the loader to avoid race conditions.
368 scoped_ptr<base::ScopedClosureRunner> loader_lock =
369 loader_controller_->GetLock();
371 AddNewDirectoryOptions options;
372 options.modified_date = last_modified;
373 options.last_viewed_by_me_date = last_accessed;
374 options.properties = properties;
375 LocalState* const local_state_ptr = local_state.get();
376 scheduler_->AddNewDirectory(
377 local_state_ptr->parent_entry.resource_id(),
378 local_state_ptr->entry.title(), options, context,
379 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
380 weak_ptr_factory_.GetWeakPtr(), context, callback,
381 base::Passed(&local_state), base::Passed(&loader_lock)));
382 return;
385 // No need to perform update.
386 if (local_state->entry.metadata_edit_state() == ResourceEntry::CLEAN ||
387 local_state->entry.resource_id().empty()) {
388 callback.Run(FILE_ERROR_OK);
389 return;
392 // Perform metadata update.
393 LocalState* const local_state_ptr = local_state.get();
394 scheduler_->UpdateResource(
395 local_state_ptr->entry.resource_id(),
396 local_state_ptr->parent_entry.resource_id(),
397 local_state_ptr->entry.title(), last_modified, last_accessed, properties,
398 context,
399 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
400 weak_ptr_factory_.GetWeakPtr(), context, callback,
401 base::Passed(&local_state),
402 base::Passed(scoped_ptr<base::ScopedClosureRunner>())));
405 void EntryUpdatePerformer::UpdateEntryAfterUpdateResource(
406 const ClientContext& context,
407 const FileOperationCallback& callback,
408 scoped_ptr<LocalState> local_state,
409 scoped_ptr<base::ScopedClosureRunner> loader_lock,
410 google_apis::DriveApiErrorCode status,
411 scoped_ptr<google_apis::FileResource> entry) {
412 DCHECK(thread_checker_.CalledOnValidThread());
413 DCHECK(!callback.is_null());
415 if (status == google_apis::HTTP_FORBIDDEN) {
416 // Editing this entry is not allowed, revert local changes.
417 entry_revert_performer_->RevertEntry(local_state->entry.local_id(), context,
418 callback);
419 return;
422 FileError error = GDataToFileError(status);
423 if (error != FILE_ERROR_OK) {
424 callback.Run(error);
425 return;
428 FileChange* changed_files = new FileChange;
429 base::PostTaskAndReplyWithResult(
430 blocking_task_runner_.get(), FROM_HERE,
431 base::Bind(&FinishUpdate, metadata_, cache_, base::Passed(&local_state),
432 base::Passed(&entry), changed_files),
433 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterFinish,
434 weak_ptr_factory_.GetWeakPtr(), callback,
435 base::Owned(changed_files)));
438 void EntryUpdatePerformer::UpdateEntryAfterFinish(
439 const FileOperationCallback& callback,
440 const FileChange* changed_files,
441 FileError error) {
442 DCHECK(thread_checker_.CalledOnValidThread());
443 DCHECK(!callback.is_null());
445 delegate_->OnFileChangedByOperation(*changed_files);
446 callback.Run(error);
449 } // namespace internal
450 } // namespace drive