Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / file_system / copy_operation.cc
blob39b959ac0f197ea45e8b22b80586a3a8beb414a6
1 // Copyright (c) 2012 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/file_system/copy_operation.h"
7 #include <string>
9 #include "base/task_runner_util.h"
10 #include "chrome/browser/chromeos/drive/drive.pb.h"
11 #include "chrome/browser/chromeos/drive/file_cache.h"
12 #include "chrome/browser/chromeos/drive/file_change.h"
13 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
14 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
15 #include "chrome/browser/chromeos/drive/file_system_util.h"
16 #include "chrome/browser/chromeos/drive/job_scheduler.h"
17 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
18 #include "chrome/browser/chromeos/drive/resource_metadata.h"
19 #include "chrome/browser/drive/drive_api_util.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "google_apis/drive/drive_api_parser.h"
23 using content::BrowserThread;
25 namespace drive {
26 namespace file_system {
28 struct CopyOperation::CopyParams {
29 base::FilePath src_file_path;
30 base::FilePath dest_file_path;
31 bool preserve_last_modified;
32 FileOperationCallback callback;
33 ResourceEntry src_entry;
34 ResourceEntry parent_entry;
37 // Enum for categorizing where a gdoc represented by a JSON file exists.
38 enum JsonGdocLocationType {
39 NOT_IN_METADATA,
40 IS_ORPHAN,
41 HAS_PARENT,
44 struct CopyOperation::TransferJsonGdocParams {
45 TransferJsonGdocParams(const FileOperationCallback& callback,
46 const std::string& resource_id,
47 const ResourceEntry& parent_entry,
48 const std::string& new_title)
49 : callback(callback),
50 resource_id(resource_id),
51 parent_resource_id(parent_entry.resource_id()),
52 parent_local_id(parent_entry.local_id()),
53 new_title(new_title),
54 location_type(NOT_IN_METADATA) {
56 // Parameters supplied or calculated from operation arguments.
57 const FileOperationCallback callback;
58 const std::string resource_id;
59 const std::string parent_resource_id;
60 const std::string parent_local_id;
61 const std::string new_title;
63 // Values computed during operation.
64 JsonGdocLocationType location_type; // types where the gdoc file is located.
65 std::string local_id; // the local_id of the file (if exists in metadata.)
66 base::FilePath changed_path;
69 namespace {
71 FileError TryToCopyLocally(internal::ResourceMetadata* metadata,
72 internal::FileCache* cache,
73 CopyOperation::CopyParams* params,
74 std::vector<std::string>* updated_local_ids,
75 bool* directory_changed,
76 bool* should_copy_on_server) {
77 FileError error = metadata->GetResourceEntryByPath(params->src_file_path,
78 &params->src_entry);
79 if (error != FILE_ERROR_OK)
80 return error;
82 error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(),
83 &params->parent_entry);
84 if (error != FILE_ERROR_OK)
85 return error;
87 if (!params->parent_entry.file_info().is_directory())
88 return FILE_ERROR_NOT_A_DIRECTORY;
90 // Drive File System doesn't support recursive copy.
91 if (params->src_entry.file_info().is_directory())
92 return FILE_ERROR_NOT_A_FILE;
94 // Check destination.
95 ResourceEntry dest_entry;
96 error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry);
97 switch (error) {
98 case FILE_ERROR_OK:
99 // File API spec says it is an error to try to "copy a file to a path
100 // occupied by a directory".
101 if (dest_entry.file_info().is_directory())
102 return FILE_ERROR_INVALID_OPERATION;
104 // Move the existing entry to the trash.
105 dest_entry.set_parent_local_id(util::kDriveTrashDirLocalId);
106 error = metadata->RefreshEntry(dest_entry);
107 if (error != FILE_ERROR_OK)
108 return error;
109 updated_local_ids->push_back(dest_entry.local_id());
110 *directory_changed = true;
111 break;
112 case FILE_ERROR_NOT_FOUND:
113 break;
114 default:
115 return error;
118 // If the cache file is not present and the entry exists on the server,
119 // server side copy should be used.
120 if (!params->src_entry.file_specific_info().cache_state().is_present() &&
121 !params->src_entry.resource_id().empty()) {
122 *should_copy_on_server = true;
123 return FILE_ERROR_OK;
126 // Copy locally.
127 ResourceEntry entry;
128 const int64 now = base::Time::Now().ToInternalValue();
129 entry.set_title(params->dest_file_path.BaseName().AsUTF8Unsafe());
130 entry.set_parent_local_id(params->parent_entry.local_id());
131 entry.mutable_file_specific_info()->set_content_mime_type(
132 params->src_entry.file_specific_info().content_mime_type());
133 entry.set_metadata_edit_state(ResourceEntry::DIRTY);
134 entry.set_modification_date(base::Time::Now().ToInternalValue());
135 entry.mutable_file_info()->set_last_modified(
136 params->preserve_last_modified ?
137 params->src_entry.file_info().last_modified() : now);
138 entry.mutable_file_info()->set_last_accessed(now);
140 std::string local_id;
141 error = metadata->AddEntry(entry, &local_id);
142 if (error != FILE_ERROR_OK)
143 return error;
144 updated_local_ids->push_back(local_id);
145 *directory_changed = true;
147 if (!params->src_entry.file_specific_info().cache_state().is_present()) {
148 DCHECK(params->src_entry.resource_id().empty());
149 // Locally created empty file may have no cache file.
150 return FILE_ERROR_OK;
153 base::FilePath cache_file_path;
154 error = cache->GetFile(params->src_entry.local_id(), &cache_file_path);
155 if (error != FILE_ERROR_OK)
156 return error;
158 return cache->Store(local_id, std::string(), cache_file_path,
159 internal::FileCache::FILE_OPERATION_COPY);
162 // Stores the entry returned from the server and returns its path.
163 FileError UpdateLocalStateForServerSideOperation(
164 internal::ResourceMetadata* metadata,
165 scoped_ptr<google_apis::FileResource> file_resource,
166 ResourceEntry* entry,
167 base::FilePath* file_path) {
168 DCHECK(file_resource);
170 std::string parent_resource_id;
171 if (!ConvertFileResourceToResourceEntry(
172 *file_resource, entry, &parent_resource_id) ||
173 parent_resource_id.empty())
174 return FILE_ERROR_NOT_A_FILE;
176 std::string parent_local_id;
177 FileError error = metadata->GetIdByResourceId(parent_resource_id,
178 &parent_local_id);
179 if (error != FILE_ERROR_OK)
180 return error;
181 entry->set_parent_local_id(parent_local_id);
183 std::string local_id;
184 error = metadata->AddEntry(*entry, &local_id);
185 // Depending on timing, the metadata may have inserted via change list
186 // already. So, FILE_ERROR_EXISTS is not an error.
187 if (error == FILE_ERROR_EXISTS)
188 error = metadata->GetIdByResourceId(entry->resource_id(), &local_id);
190 if (error != FILE_ERROR_OK)
191 return error;
193 return metadata->GetFilePath(local_id, file_path);
196 // Stores the file at |local_file_path| to the cache as a content of entry at
197 // |remote_dest_path|, and marks it dirty.
198 FileError UpdateLocalStateForScheduleTransfer(
199 internal::ResourceMetadata* metadata,
200 internal::FileCache* cache,
201 const base::FilePath& local_src_path,
202 const base::FilePath& remote_dest_path,
203 ResourceEntry* entry,
204 std::string* local_id) {
205 FileError error = metadata->GetIdByPath(remote_dest_path, local_id);
206 if (error != FILE_ERROR_OK)
207 return error;
209 error = metadata->GetResourceEntryById(*local_id, entry);
210 if (error != FILE_ERROR_OK)
211 return error;
213 return cache->Store(*local_id, std::string(), local_src_path,
214 internal::FileCache::FILE_OPERATION_COPY);
217 // Gets the file size of the |local_path|, and the ResourceEntry for the parent
218 // of |remote_path| to prepare the necessary information for transfer.
219 FileError PrepareTransferFileFromLocalToRemote(
220 internal::ResourceMetadata* metadata,
221 const base::FilePath& local_src_path,
222 const base::FilePath& remote_dest_path,
223 std::string* gdoc_resource_id,
224 ResourceEntry* parent_entry) {
225 FileError error = metadata->GetResourceEntryByPath(
226 remote_dest_path.DirName(), parent_entry);
227 if (error != FILE_ERROR_OK)
228 return error;
230 // The destination's parent must be a directory.
231 if (!parent_entry->file_info().is_directory())
232 return FILE_ERROR_NOT_A_DIRECTORY;
234 // Try to parse GDoc File and extract the resource id, if necessary.
235 // Failing isn't problem. It'd be handled as a regular file, then.
236 if (util::HasHostedDocumentExtension(local_src_path))
237 *gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
238 return FILE_ERROR_OK;
241 // Performs local work before server-side work for transferring JSON-represented
242 // gdoc files.
243 FileError LocalWorkForTransferJsonGdocFile(
244 internal::ResourceMetadata* metadata,
245 CopyOperation::TransferJsonGdocParams* params) {
246 std::string local_id;
247 FileError error = metadata->GetIdByResourceId(params->resource_id, &local_id);
248 if (error != FILE_ERROR_OK) {
249 params->location_type = NOT_IN_METADATA;
250 return error == FILE_ERROR_NOT_FOUND ? FILE_ERROR_OK : error;
253 ResourceEntry entry;
254 error = metadata->GetResourceEntryById(local_id, &entry);
255 if (error != FILE_ERROR_OK)
256 return error;
257 params->local_id = entry.local_id();
259 if (entry.parent_local_id() == util::kDriveOtherDirLocalId) {
260 params->location_type = IS_ORPHAN;
261 entry.set_title(params->new_title);
262 entry.set_parent_local_id(params->parent_local_id);
263 entry.set_metadata_edit_state(ResourceEntry::DIRTY);
264 entry.set_modification_date(base::Time::Now().ToInternalValue());
265 error = metadata->RefreshEntry(entry);
266 if (error != FILE_ERROR_OK)
267 return error;
268 return metadata->GetFilePath(local_id, &params->changed_path);
271 params->location_type = HAS_PARENT;
272 return FILE_ERROR_OK;
275 } // namespace
277 CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
278 OperationDelegate* delegate,
279 JobScheduler* scheduler,
280 internal::ResourceMetadata* metadata,
281 internal::FileCache* cache)
282 : blocking_task_runner_(blocking_task_runner),
283 delegate_(delegate),
284 scheduler_(scheduler),
285 metadata_(metadata),
286 cache_(cache),
287 create_file_operation_(new CreateFileOperation(blocking_task_runner,
288 delegate,
289 metadata)),
290 weak_ptr_factory_(this) {
291 DCHECK_CURRENTLY_ON(BrowserThread::UI);
294 CopyOperation::~CopyOperation() {
295 DCHECK_CURRENTLY_ON(BrowserThread::UI);
298 void CopyOperation::Copy(const base::FilePath& src_file_path,
299 const base::FilePath& dest_file_path,
300 bool preserve_last_modified,
301 const FileOperationCallback& callback) {
302 DCHECK_CURRENTLY_ON(BrowserThread::UI);
303 DCHECK(!callback.is_null());
305 CopyParams* params = new CopyParams;
306 params->src_file_path = src_file_path;
307 params->dest_file_path = dest_file_path;
308 params->preserve_last_modified = preserve_last_modified;
309 params->callback = callback;
311 std::vector<std::string>* updated_local_ids = new std::vector<std::string>;
312 bool* directory_changed = new bool(false);
313 bool* should_copy_on_server = new bool(false);
314 base::PostTaskAndReplyWithResult(
315 blocking_task_runner_.get(),
316 FROM_HERE,
317 base::Bind(&TryToCopyLocally, metadata_, cache_, params,
318 updated_local_ids, directory_changed, should_copy_on_server),
319 base::Bind(&CopyOperation::CopyAfterTryToCopyLocally,
320 weak_ptr_factory_.GetWeakPtr(), base::Owned(params),
321 base::Owned(updated_local_ids), base::Owned(directory_changed),
322 base::Owned(should_copy_on_server)));
325 void CopyOperation::CopyAfterTryToCopyLocally(
326 const CopyParams* params,
327 const std::vector<std::string>* updated_local_ids,
328 const bool* directory_changed,
329 const bool* should_copy_on_server,
330 FileError error) {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI);
332 DCHECK(!params->callback.is_null());
334 for (const auto& id : *updated_local_ids) {
335 // Syncing for copy should be done in background, so pass the BACKGROUND
336 // context. See: crbug.com/420278.
337 delegate_->OnEntryUpdatedByOperation(ClientContext(BACKGROUND), id);
340 if (*directory_changed) {
341 FileChange changed_file;
342 DCHECK(!params->src_entry.file_info().is_directory());
343 changed_file.Update(params->dest_file_path,
344 FileChange::FILE_TYPE_FILE,
345 FileChange::ADD_OR_UPDATE);
346 delegate_->OnFileChangedByOperation(changed_file);
349 if (error != FILE_ERROR_OK || !*should_copy_on_server) {
350 params->callback.Run(error);
351 return;
354 if (params->parent_entry.resource_id().empty()) {
355 // Parent entry may be being synced.
356 const bool waiting = delegate_->WaitForSyncComplete(
357 params->parent_entry.local_id(),
358 base::Bind(&CopyOperation::CopyAfterParentSync,
359 weak_ptr_factory_.GetWeakPtr(), *params));
360 if (!waiting)
361 params->callback.Run(FILE_ERROR_NOT_FOUND);
362 } else {
363 CopyAfterGetParentResourceId(*params, &params->parent_entry, FILE_ERROR_OK);
367 void CopyOperation::CopyAfterParentSync(const CopyParams& params,
368 FileError error) {
369 DCHECK_CURRENTLY_ON(BrowserThread::UI);
370 DCHECK(!params.callback.is_null());
372 if (error != FILE_ERROR_OK) {
373 params.callback.Run(error);
374 return;
377 ResourceEntry* parent = new ResourceEntry;
378 base::PostTaskAndReplyWithResult(
379 blocking_task_runner_.get(),
380 FROM_HERE,
381 base::Bind(&internal::ResourceMetadata::GetResourceEntryById,
382 base::Unretained(metadata_),
383 params.parent_entry.local_id(),
384 parent),
385 base::Bind(&CopyOperation::CopyAfterGetParentResourceId,
386 weak_ptr_factory_.GetWeakPtr(),
387 params,
388 base::Owned(parent)));
391 void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params,
392 const ResourceEntry* parent,
393 FileError error) {
394 DCHECK_CURRENTLY_ON(BrowserThread::UI);
395 DCHECK(!params.callback.is_null());
397 if (error != FILE_ERROR_OK) {
398 params.callback.Run(error);
399 return;
402 base::FilePath new_title = params.dest_file_path.BaseName();
403 if (params.src_entry.file_specific_info().is_hosted_document()) {
404 // Drop the document extension, which should not be in the title.
405 // TODO(yoshiki): Remove this code with crbug.com/223304.
406 new_title = new_title.RemoveExtension();
409 base::Time last_modified =
410 params.preserve_last_modified ?
411 base::Time::FromInternalValue(
412 params.src_entry.file_info().last_modified()) : base::Time();
414 CopyResourceOnServer(
415 params.src_entry.resource_id(), parent->resource_id(),
416 new_title.AsUTF8Unsafe(), last_modified, params.callback);
419 void CopyOperation::TransferFileFromLocalToRemote(
420 const base::FilePath& local_src_path,
421 const base::FilePath& remote_dest_path,
422 const FileOperationCallback& callback) {
423 DCHECK_CURRENTLY_ON(BrowserThread::UI);
424 DCHECK(!callback.is_null());
426 std::string* gdoc_resource_id = new std::string;
427 ResourceEntry* parent_entry = new ResourceEntry;
428 base::PostTaskAndReplyWithResult(
429 blocking_task_runner_.get(),
430 FROM_HERE,
431 base::Bind(
432 &PrepareTransferFileFromLocalToRemote,
433 metadata_, local_src_path, remote_dest_path,
434 gdoc_resource_id, parent_entry),
435 base::Bind(
436 &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
437 weak_ptr_factory_.GetWeakPtr(),
438 local_src_path, remote_dest_path, callback,
439 base::Owned(gdoc_resource_id), base::Owned(parent_entry)));
442 void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
443 const base::FilePath& local_src_path,
444 const base::FilePath& remote_dest_path,
445 const FileOperationCallback& callback,
446 std::string* gdoc_resource_id,
447 ResourceEntry* parent_entry,
448 FileError error) {
449 DCHECK_CURRENTLY_ON(BrowserThread::UI);
450 DCHECK(!callback.is_null());
452 if (error != FILE_ERROR_OK) {
453 callback.Run(error);
454 return;
457 // For regular files, schedule the transfer.
458 if (gdoc_resource_id->empty()) {
459 ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
460 return;
463 // GDoc file may contain a resource ID in the old format.
464 const std::string canonicalized_resource_id =
465 util::CanonicalizeResourceId(*gdoc_resource_id);
467 // Drop the document extension, which should not be in the title.
468 // TODO(yoshiki): Remove this code with crbug.com/223304.
469 const std::string new_title =
470 remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe();
472 // This is uploading a JSON file representing a hosted document.
473 TransferJsonGdocParams* params = new TransferJsonGdocParams(
474 callback, canonicalized_resource_id, *parent_entry, new_title);
475 base::PostTaskAndReplyWithResult(
476 blocking_task_runner_.get(),
477 FROM_HERE,
478 base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params),
479 base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork,
480 weak_ptr_factory_.GetWeakPtr(), base::Owned(params)));
483 void CopyOperation::TransferJsonGdocFileAfterLocalWork(
484 TransferJsonGdocParams* params,
485 FileError error) {
486 DCHECK_CURRENTLY_ON(BrowserThread::UI);
488 if (error != FILE_ERROR_OK) {
489 params->callback.Run(error);
490 return;
493 switch (params->location_type) {
494 // When |resource_id| is found in the local metadata and it has a specific
495 // parent folder, we assume the user's intention is to copy the document and
496 // thus perform the server-side copy operation.
497 case HAS_PARENT:
498 CopyResourceOnServer(params->resource_id,
499 params->parent_resource_id,
500 params->new_title,
501 base::Time(),
502 params->callback);
503 break;
504 // When |resource_id| has no parent, we just set the new destination folder
505 // as the parent, for sharing the document between the original source.
506 // This reparenting is already done in LocalWorkForTransferJsonGdocFile().
507 case IS_ORPHAN: {
508 DCHECK(!params->changed_path.empty());
509 // Syncing for copy should be done in background, so pass the BACKGROUND
510 // context. See: crbug.com/420278.
511 delegate_->OnEntryUpdatedByOperation(ClientContext(BACKGROUND),
512 params->local_id);
514 FileChange changed_file;
515 changed_file.Update(
516 params->changed_path,
517 FileChange::FILE_TYPE_FILE, // This must be a hosted document.
518 FileChange::ADD_OR_UPDATE);
519 delegate_->OnFileChangedByOperation(changed_file);
520 params->callback.Run(error);
521 break;
523 // When the |resource_id| is not in the local metadata, assume it to be a
524 // document just now shared on the server but not synced locally.
525 // Same as the IS_ORPHAN case, we want to deal the case by setting parent,
526 // but this time we need to resort to server side operation.
527 case NOT_IN_METADATA:
528 scheduler_->UpdateResource(
529 params->resource_id, params->parent_resource_id, params->new_title,
530 base::Time(), base::Time(), google_apis::drive::Properties(),
531 ClientContext(USER_INITIATED),
532 base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
533 weak_ptr_factory_.GetWeakPtr(), params->callback));
534 break;
538 void CopyOperation::CopyResourceOnServer(
539 const std::string& resource_id,
540 const std::string& parent_resource_id,
541 const std::string& new_title,
542 const base::Time& last_modified,
543 const FileOperationCallback& callback) {
544 DCHECK_CURRENTLY_ON(BrowserThread::UI);
545 DCHECK(!callback.is_null());
547 scheduler_->CopyResource(
548 resource_id, parent_resource_id, new_title, last_modified,
549 base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
550 weak_ptr_factory_.GetWeakPtr(),
551 callback));
554 void CopyOperation::UpdateAfterServerSideOperation(
555 const FileOperationCallback& callback,
556 google_apis::DriveApiErrorCode status,
557 scoped_ptr<google_apis::FileResource> entry) {
558 DCHECK_CURRENTLY_ON(BrowserThread::UI);
559 DCHECK(!callback.is_null());
561 FileError error = GDataToFileError(status);
562 if (error != FILE_ERROR_OK) {
563 callback.Run(error);
564 return;
567 ResourceEntry* resource_entry = new ResourceEntry;
569 // The copy on the server side is completed successfully. Update the local
570 // metadata.
571 base::FilePath* file_path = new base::FilePath;
572 base::PostTaskAndReplyWithResult(
573 blocking_task_runner_.get(),
574 FROM_HERE,
575 base::Bind(&UpdateLocalStateForServerSideOperation,
576 metadata_,
577 base::Passed(&entry),
578 resource_entry,
579 file_path),
580 base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate,
581 weak_ptr_factory_.GetWeakPtr(),
582 callback,
583 base::Owned(file_path),
584 base::Owned(resource_entry)));
587 void CopyOperation::UpdateAfterLocalStateUpdate(
588 const FileOperationCallback& callback,
589 base::FilePath* file_path,
590 const ResourceEntry* entry,
591 FileError error) {
592 DCHECK_CURRENTLY_ON(BrowserThread::UI);
593 DCHECK(!callback.is_null());
595 if (error == FILE_ERROR_OK) {
596 FileChange changed_file;
597 changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE);
598 delegate_->OnFileChangedByOperation(changed_file);
600 callback.Run(error);
603 void CopyOperation::ScheduleTransferRegularFile(
604 const base::FilePath& local_src_path,
605 const base::FilePath& remote_dest_path,
606 const FileOperationCallback& callback) {
607 DCHECK_CURRENTLY_ON(BrowserThread::UI);
608 DCHECK(!callback.is_null());
610 create_file_operation_->CreateFile(
611 remote_dest_path,
612 false, // Not exclusive (OK even if a file already exists).
613 std::string(), // no specific mime type; CreateFile should guess it.
614 base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
615 weak_ptr_factory_.GetWeakPtr(),
616 local_src_path, remote_dest_path, callback));
619 void CopyOperation::ScheduleTransferRegularFileAfterCreate(
620 const base::FilePath& local_src_path,
621 const base::FilePath& remote_dest_path,
622 const FileOperationCallback& callback,
623 FileError error) {
624 DCHECK_CURRENTLY_ON(BrowserThread::UI);
625 DCHECK(!callback.is_null());
627 if (error != FILE_ERROR_OK) {
628 callback.Run(error);
629 return;
632 std::string* local_id = new std::string;
633 ResourceEntry* entry = new ResourceEntry;
634 base::PostTaskAndReplyWithResult(
635 blocking_task_runner_.get(),
636 FROM_HERE,
637 base::Bind(&UpdateLocalStateForScheduleTransfer,
638 metadata_,
639 cache_,
640 local_src_path,
641 remote_dest_path,
642 entry,
643 local_id),
644 base::Bind(
645 &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
646 weak_ptr_factory_.GetWeakPtr(),
647 callback,
648 remote_dest_path,
649 base::Owned(entry),
650 base::Owned(local_id)));
653 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
654 const FileOperationCallback& callback,
655 const base::FilePath& remote_dest_path,
656 const ResourceEntry* entry,
657 std::string* local_id,
658 FileError error) {
659 DCHECK_CURRENTLY_ON(BrowserThread::UI);
660 DCHECK(!callback.is_null());
662 if (error == FILE_ERROR_OK) {
663 FileChange changed_file;
664 changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE);
665 delegate_->OnFileChangedByOperation(changed_file);
666 // Syncing for copy should be done in background, so pass the BACKGROUND
667 // context. See: crbug.com/420278.
668 delegate_->OnEntryUpdatedByOperation(ClientContext(BACKGROUND), *local_id);
670 callback.Run(error);
673 } // namespace file_system
674 } // namespace drive