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/sync_file_system/local/local_file_sync_context.h"
8 #include "base/file_util.h"
9 #include "base/location.h"
10 #include "base/platform_file.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/task_runner_util.h"
14 #include "chrome/browser/sync_file_system/file_change.h"
15 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
16 #include "chrome/browser/sync_file_system/local/local_origin_change_observer.h"
17 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
18 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
19 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21 #include "webkit/browser/fileapi/file_system_context.h"
22 #include "webkit/browser/fileapi/file_system_file_util.h"
23 #include "webkit/browser/fileapi/file_system_operation_context.h"
24 #include "webkit/browser/fileapi/file_system_operation_runner.h"
25 #include "webkit/common/blob/scoped_file.h"
26 #include "webkit/common/fileapi/file_system_util.h"
28 using fileapi::FileSystemContext;
29 using fileapi::FileSystemFileUtil;
30 using fileapi::FileSystemOperation;
31 using fileapi::FileSystemOperationContext;
32 using fileapi::FileSystemURL;
34 namespace sync_file_system {
38 const int kMaxConcurrentSyncableOperation = 3;
39 const int kNotifyChangesDurationInSec = 1;
40 const int kMaxURLsToFetchForLocalSync = 5;
42 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots");
46 LocalFileSyncContext::LocalFileSyncContext(
47 const base::FilePath& base_path,
48 base::SingleThreadTaskRunner* ui_task_runner,
49 base::SingleThreadTaskRunner* io_task_runner)
50 : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))),
51 ui_task_runner_(ui_task_runner),
52 io_task_runner_(io_task_runner),
53 shutdown_on_ui_(false),
54 mock_notify_changes_duration_in_sec_(-1) {
55 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
58 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
59 const GURL& source_url,
60 FileSystemContext* file_system_context,
61 const SyncStatusCallback& callback) {
62 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
63 if (ContainsKey(file_system_contexts_, file_system_context)) {
64 // The context has been already initialized. Just dispatch the callback
65 // with SYNC_STATUS_OK.
66 ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
70 StatusCallbackQueue& callback_queue =
71 pending_initialize_callbacks_[file_system_context];
72 callback_queue.push_back(callback);
73 if (callback_queue.size() > 1)
76 io_task_runner_->PostTask(
78 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread,
80 make_scoped_refptr(file_system_context)));
83 void LocalFileSyncContext::ShutdownOnUIThread() {
84 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
85 shutdown_on_ui_ = true;
86 io_task_runner_->PostTask(
88 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread, this));
91 void LocalFileSyncContext::GetFileForLocalSync(
92 FileSystemContext* file_system_context,
93 const LocalFileSyncInfoCallback& callback) {
94 DCHECK(file_system_context);
95 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
97 std::deque<FileSystemURL>* urls = new std::deque<FileSystemURL>;
98 file_system_context->default_file_task_runner()->PostTaskAndReply(
100 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread,
101 this, make_scoped_refptr(file_system_context),
102 base::Unretained(urls)),
103 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync,
104 this, make_scoped_refptr(file_system_context),
105 base::Owned(urls), callback));
108 void LocalFileSyncContext::ClearChangesForURL(
109 FileSystemContext* file_system_context,
110 const FileSystemURL& url,
111 const base::Closure& done_callback) {
112 // This is initially called on UI thread and to be relayed to FILE thread.
113 DCHECK(file_system_context);
114 if (!file_system_context->default_file_task_runner()->
115 RunsTasksOnCurrentThread()) {
116 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
117 file_system_context->default_file_task_runner()->PostTask(
119 base::Bind(&LocalFileSyncContext::ClearChangesForURL,
120 this, make_scoped_refptr(file_system_context),
121 url, done_callback));
125 SyncFileSystemBackend* backend =
126 SyncFileSystemBackend::GetBackend(file_system_context);
128 DCHECK(backend->change_tracker());
129 backend->change_tracker()->ClearChangesForURL(url);
131 // Call the completion callback on UI thread.
132 ui_task_runner_->PostTask(FROM_HERE, done_callback);
135 void LocalFileSyncContext::FinalizeSnapshotSync(
136 fileapi::FileSystemContext* file_system_context,
137 const fileapi::FileSystemURL& url,
138 SyncStatusCode sync_finish_status,
139 const base::Closure& done_callback) {
140 DCHECK(file_system_context);
141 DCHECK(url.is_valid());
142 if (!file_system_context->default_file_task_runner()->
143 RunsTasksOnCurrentThread()) {
144 file_system_context->default_file_task_runner()->PostTask(
146 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync,
147 this, make_scoped_refptr(file_system_context),
148 url, sync_finish_status, done_callback));
152 SyncFileSystemBackend* backend =
153 SyncFileSystemBackend::GetBackend(file_system_context);
155 DCHECK(backend->change_tracker());
157 if (sync_finish_status == SYNC_STATUS_OK ||
158 sync_finish_status == SYNC_STATUS_HAS_CONFLICT) {
159 // Commit the in-memory mirror change.
160 backend->change_tracker()->ResetToMirrorAndCommitChangesForURL(url);
162 // Abort in-memory mirror change.
163 backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url);
166 // We've been keeping it in writing mode, so clear the writing counter
167 // to unblock sync activities.
168 io_task_runner_->PostTask(
169 FROM_HERE, base::Bind(
170 &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread, this, url));
172 // Call the completion callback on UI thread.
173 ui_task_runner_->PostTask(FROM_HERE, done_callback);
176 void LocalFileSyncContext::FinalizeExclusiveSync(
177 fileapi::FileSystemContext* file_system_context,
178 const fileapi::FileSystemURL& url,
179 bool clear_local_changes,
180 const base::Closure& done_callback) {
181 DCHECK(file_system_context);
182 if (!url.is_valid()) {
187 if (clear_local_changes) {
188 ClearChangesForURL(file_system_context, url,
189 base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync,
190 this, make_scoped_refptr(file_system_context),
191 url, false, done_callback));
195 io_task_runner_->PostTask(
197 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
198 this, url, false /* for_snapshot_sync */));
203 void LocalFileSyncContext::PrepareForSync(
204 FileSystemContext* file_system_context,
205 const FileSystemURL& url,
207 const LocalFileSyncInfoCallback& callback) {
208 // This is initially called on UI thread and to be relayed to IO thread.
209 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
210 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
211 io_task_runner_->PostTask(
213 base::Bind(&LocalFileSyncContext::PrepareForSync, this,
214 make_scoped_refptr(file_system_context), url,
215 sync_mode, callback));
218 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
219 const bool syncable = sync_status()->IsSyncable(url);
220 // Disable writing if it's ready to be synced.
222 sync_status()->StartSyncing(url);
223 ui_task_runner_->PostTask(
225 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
226 this, make_scoped_refptr(file_system_context),
227 syncable ? SYNC_STATUS_OK :
228 SYNC_STATUS_FILE_BUSY,
229 url, sync_mode, callback));
232 void LocalFileSyncContext::RegisterURLForWaitingSync(
233 const FileSystemURL& url,
234 const base::Closure& on_syncable_callback) {
235 // This is initially called on UI thread and to be relayed to IO thread.
236 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
237 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
238 io_task_runner_->PostTask(
240 base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync,
241 this, url, on_syncable_callback));
244 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
245 if (sync_status()->IsSyncable(url)) {
246 // No need to register; fire the callback now.
247 ui_task_runner_->PostTask(FROM_HERE, on_syncable_callback);
250 url_waiting_sync_on_io_ = url;
251 url_syncable_callback_ = on_syncable_callback;
254 void LocalFileSyncContext::ApplyRemoteChange(
255 FileSystemContext* file_system_context,
256 const FileChange& change,
257 const base::FilePath& local_path,
258 const FileSystemURL& url,
259 const SyncStatusCallback& callback) {
260 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
261 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
262 io_task_runner_->PostTask(
264 base::Bind(&LocalFileSyncContext::ApplyRemoteChange, this,
265 make_scoped_refptr(file_system_context),
266 change, local_path, url, callback));
269 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
270 DCHECK(!sync_status()->IsWritable(url));
271 DCHECK(!sync_status()->IsWriting(url));
273 FileSystemOperation::StatusCallback operation_callback;
274 if (change.change() == FileChange::FILE_CHANGE_ADD_OR_UPDATE) {
275 operation_callback = base::Bind(
276 &LocalFileSyncContext::DidRemoveExistingEntryForApplyRemoteChange,
278 make_scoped_refptr(file_system_context),
284 DCHECK_EQ(FileChange::FILE_CHANGE_DELETE, change.change());
285 operation_callback = base::Bind(
286 &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
288 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
289 file_system_context, url);
290 file_system_context->operation_runner()->Remove(
291 url_for_sync, true /* recursive */, operation_callback);
294 void LocalFileSyncContext::DidRemoveExistingEntryForApplyRemoteChange(
295 FileSystemContext* file_system_context,
296 const FileChange& change,
297 const base::FilePath& local_path,
298 const FileSystemURL& url,
299 const SyncStatusCallback& callback,
300 base::PlatformFileError error) {
301 // Remove() may fail if the target entry does not exist (which is ok),
302 // so we ignore |error| here.
304 if (!sync_status()) {
305 callback.Run(SYNC_FILE_ERROR_ABORT);
309 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
310 DCHECK(!sync_status()->IsWritable(url));
311 DCHECK(!sync_status()->IsWriting(url));
313 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
314 file_system_context, url);
315 FileSystemOperation::StatusCallback operation_callback = base::Bind(
316 &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
318 DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE, change.change());
319 switch (change.file_type()) {
320 case SYNC_FILE_TYPE_FILE: {
321 DCHECK(!local_path.empty());
322 base::FilePath dir_path = fileapi::VirtualPath::DirName(url.path());
323 if (dir_path.empty() ||
324 fileapi::VirtualPath::DirName(dir_path) == dir_path) {
325 // Copying into the root directory.
326 file_system_context->operation_runner()->CopyInForeignFile(
327 local_path, url_for_sync, operation_callback);
329 FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
330 url_for_sync.origin(),
331 url_for_sync.mount_type(),
332 fileapi::VirtualPath::DirName(url_for_sync.virtual_path()));
333 file_system_context->operation_runner()->CreateDirectory(
335 false /* exclusive */,
336 true /* recursive */,
337 base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn,
339 make_scoped_refptr(file_system_context),
342 operation_callback));
346 case SYNC_FILE_TYPE_DIRECTORY:
347 file_system_context->operation_runner()->CreateDirectory(
348 url_for_sync, false /* exclusive */, true /* recursive */,
351 case SYNC_FILE_TYPE_UNKNOWN:
352 NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
356 void LocalFileSyncContext::RecordFakeLocalChange(
357 FileSystemContext* file_system_context,
358 const FileSystemURL& url,
359 const FileChange& change,
360 const SyncStatusCallback& callback) {
361 // This is called on UI thread and to be relayed to FILE thread.
362 DCHECK(file_system_context);
363 if (!file_system_context->default_file_task_runner()->
364 RunsTasksOnCurrentThread()) {
365 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
366 file_system_context->default_file_task_runner()->PostTask(
368 base::Bind(&LocalFileSyncContext::RecordFakeLocalChange,
369 this, make_scoped_refptr(file_system_context),
370 url, change, callback));
374 SyncFileSystemBackend* backend =
375 SyncFileSystemBackend::GetBackend(file_system_context);
377 DCHECK(backend->change_tracker());
378 backend->change_tracker()->MarkDirtyOnDatabase(url);
379 backend->change_tracker()->RecordChange(url, change);
381 // Fire the callback on UI thread.
382 ui_task_runner_->PostTask(FROM_HERE,
387 void LocalFileSyncContext::GetFileMetadata(
388 FileSystemContext* file_system_context,
389 const FileSystemURL& url,
390 const SyncFileMetadataCallback& callback) {
391 // This is initially called on UI thread and to be relayed to IO thread.
392 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
393 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
394 io_task_runner_->PostTask(
396 base::Bind(&LocalFileSyncContext::GetFileMetadata, this,
397 make_scoped_refptr(file_system_context), url, callback));
400 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
402 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
403 file_system_context, url);
404 file_system_context->operation_runner()->GetMetadata(
405 url_for_sync, base::Bind(&LocalFileSyncContext::DidGetFileMetadata,
409 void LocalFileSyncContext::HasPendingLocalChanges(
410 FileSystemContext* file_system_context,
411 const FileSystemURL& url,
412 const HasPendingLocalChangeCallback& callback) {
413 // This gets called on UI thread and relays the task on FILE thread.
414 DCHECK(file_system_context);
415 if (!file_system_context->default_file_task_runner()->
416 RunsTasksOnCurrentThread()) {
417 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
418 file_system_context->default_file_task_runner()->PostTask(
420 base::Bind(&LocalFileSyncContext::HasPendingLocalChanges,
421 this, make_scoped_refptr(file_system_context),
426 SyncFileSystemBackend* backend =
427 SyncFileSystemBackend::GetBackend(file_system_context);
429 DCHECK(backend->change_tracker());
430 FileChangeList changes;
431 backend->change_tracker()->GetChangesForURL(url, &changes);
433 // Fire the callback on UI thread.
434 ui_task_runner_->PostTask(FROM_HERE,
440 void LocalFileSyncContext::AddOriginChangeObserver(
441 LocalOriginChangeObserver* observer) {
442 origin_change_observers_.AddObserver(observer);
445 void LocalFileSyncContext::RemoveOriginChangeObserver(
446 LocalOriginChangeObserver* observer) {
447 origin_change_observers_.RemoveObserver(observer);
450 base::WeakPtr<SyncableFileOperationRunner>
451 LocalFileSyncContext::operation_runner() const {
452 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
453 if (operation_runner_)
454 return operation_runner_->AsWeakPtr();
455 return base::WeakPtr<SyncableFileOperationRunner>();
458 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
459 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
460 return sync_status_.get();
463 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
464 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
465 origins_with_pending_changes_.insert(url.origin());
466 ScheduleNotifyChangesUpdatedOnIOThread();
467 if (url_syncable_callback_.is_null() ||
468 sync_status()->IsWriting(url_waiting_sync_on_io_)) {
471 // TODO(kinuko): may want to check how many pending tasks we have.
472 ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
473 url_syncable_callback_.Reset();
476 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
477 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
478 // Nothing to do for now.
481 LocalFileSyncContext::~LocalFileSyncContext() {
484 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
485 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
486 if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
487 NotifyAvailableChangesOnIOThread();
488 } else if (!timer_on_io_->IsRunning()) {
490 FROM_HERE, NotifyChangesDuration(), this,
491 &LocalFileSyncContext::NotifyAvailableChangesOnIOThread);
495 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
496 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
497 ui_task_runner_->PostTask(
499 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
500 this, origins_with_pending_changes_));
501 last_notified_changes_ = base::Time::Now();
502 origins_with_pending_changes_.clear();
505 void LocalFileSyncContext::NotifyAvailableChanges(
506 const std::set<GURL>& origins) {
507 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
508 OnChangesAvailableInOrigins(origins));
511 void LocalFileSyncContext::ShutdownOnIOThread() {
512 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
513 operation_runner_.reset();
514 sync_status_.reset();
515 timer_on_io_.reset();
518 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
519 const GURL& source_url,
520 FileSystemContext* file_system_context) {
521 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
522 DCHECK(file_system_context);
523 SyncFileSystemBackend* backend =
524 SyncFileSystemBackend::GetBackend(file_system_context);
526 if (!backend->change_tracker()) {
527 // Create and initialize LocalFileChangeTracker and call back this method
529 std::set<GURL>* origins_with_changes = new std::set<GURL>;
530 scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
531 new scoped_ptr<LocalFileChangeTracker>);
532 base::PostTaskAndReplyWithResult(
533 file_system_context->default_file_task_runner(),
535 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
537 make_scoped_refptr(file_system_context),
538 origins_with_changes),
539 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
540 this, base::Owned(tracker_ptr),
542 make_scoped_refptr(file_system_context),
543 base::Owned(origins_with_changes)));
546 if (!operation_runner_) {
547 DCHECK(!sync_status_);
548 DCHECK(!timer_on_io_);
549 sync_status_.reset(new LocalFileSyncStatus);
550 timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
551 operation_runner_.reset(new SyncableFileOperationRunner(
552 kMaxConcurrentSyncableOperation,
553 sync_status_.get()));
554 sync_status_->AddObserver(this);
556 backend->set_sync_context(this);
557 DidInitialize(source_url, file_system_context,
561 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
562 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
563 FileSystemContext* file_system_context,
564 std::set<GURL>* origins_with_changes) {
565 DCHECK(file_system_context);
567 DCHECK(origins_with_changes);
568 tracker_ptr->reset(new LocalFileChangeTracker(
569 file_system_context->partition_path(),
570 file_system_context->default_file_task_runner()));
571 const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
572 if (status != SYNC_STATUS_OK)
575 // Get all origins that have pending changes.
576 std::deque<FileSystemURL> urls;
577 (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
578 for (std::deque<FileSystemURL>::iterator iter = urls.begin();
579 iter != urls.end(); ++iter) {
580 origins_with_changes->insert(iter->origin());
583 // Creates snapshot directory.
584 file_util::CreateDirectory(local_base_path_.Append(kSnapshotDir));
589 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
590 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
591 const GURL& source_url,
592 FileSystemContext* file_system_context,
593 std::set<GURL>* origins_with_changes,
594 SyncStatusCode status) {
595 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
596 DCHECK(file_system_context);
597 DCHECK(origins_with_changes);
598 if (status != SYNC_STATUS_OK) {
599 DidInitialize(source_url, file_system_context, status);
603 SyncFileSystemBackend* backend =
604 SyncFileSystemBackend::GetBackend(file_system_context);
606 backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
608 origins_with_pending_changes_.insert(origins_with_changes->begin(),
609 origins_with_changes->end());
610 ScheduleNotifyChangesUpdatedOnIOThread();
612 InitializeFileSystemContextOnIOThread(source_url, file_system_context);
615 void LocalFileSyncContext::DidInitialize(
616 const GURL& source_url,
617 FileSystemContext* file_system_context,
618 SyncStatusCode status) {
619 if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
620 ui_task_runner_->PostTask(
622 base::Bind(&LocalFileSyncContext::DidInitialize,
624 make_scoped_refptr(file_system_context), status));
627 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
628 DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
629 DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
631 SyncFileSystemBackend* backend =
632 SyncFileSystemBackend::GetBackend(file_system_context);
634 DCHECK(backend->change_tracker());
636 file_system_contexts_.insert(file_system_context);
638 StatusCallbackQueue& callback_queue =
639 pending_initialize_callbacks_[file_system_context];
640 for (StatusCallbackQueue::iterator iter = callback_queue.begin();
641 iter != callback_queue.end(); ++iter) {
642 ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
644 pending_initialize_callbacks_.erase(file_system_context);
647 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
648 FileSystemContext* file_system_context,
649 std::deque<FileSystemURL>* urls) {
650 DCHECK(file_system_context);
651 DCHECK(file_system_context->default_file_task_runner()->
652 RunsTasksOnCurrentThread());
653 SyncFileSystemBackend* backend =
654 SyncFileSystemBackend::GetBackend(file_system_context);
656 DCHECK(backend->change_tracker());
657 backend->change_tracker()->GetNextChangedURLs(
658 urls, kMaxURLsToFetchForLocalSync);
661 void LocalFileSyncContext::TryPrepareForLocalSync(
662 FileSystemContext* file_system_context,
663 std::deque<FileSystemURL>* urls,
664 const LocalFileSyncInfoCallback& callback) {
665 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
668 if (shutdown_on_ui_) {
669 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
670 webkit_blob::ScopedFile());
675 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(),
676 webkit_blob::ScopedFile());
680 const FileSystemURL url = urls->front();
682 std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>;
683 remaining->swap(*urls);
686 file_system_context, url, SYNC_SNAPSHOT,
687 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
688 this, make_scoped_refptr(file_system_context),
689 base::Owned(remaining), callback));
692 void LocalFileSyncContext::DidTryPrepareForLocalSync(
693 FileSystemContext* file_system_context,
694 std::deque<FileSystemURL>* remaining_urls,
695 const LocalFileSyncInfoCallback& callback,
696 SyncStatusCode status,
697 const LocalFileSyncInfo& sync_file_info,
698 webkit_blob::ScopedFile snapshot) {
699 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
700 if (status != SYNC_STATUS_FILE_BUSY) {
701 callback.Run(status, sync_file_info, snapshot.Pass());
704 // Recursively call TryPrepareForLocalSync with remaining_urls.
705 TryPrepareForLocalSync(file_system_context, remaining_urls, callback);
708 void LocalFileSyncContext::DidGetWritingStatusForSync(
709 FileSystemContext* file_system_context,
710 SyncStatusCode status,
711 const FileSystemURL& url,
713 const LocalFileSyncInfoCallback& callback) {
714 // This gets called on UI thread and relays the task on FILE thread.
715 DCHECK(file_system_context);
716 if (!file_system_context->default_file_task_runner()->
717 RunsTasksOnCurrentThread()) {
718 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
719 if (shutdown_on_ui_) {
720 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
721 webkit_blob::ScopedFile());
724 file_system_context->default_file_task_runner()->PostTask(
726 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
727 this, make_scoped_refptr(file_system_context),
728 status, url, sync_mode, callback));
732 SyncFileSystemBackend* backend =
733 SyncFileSystemBackend::GetBackend(file_system_context);
735 DCHECK(backend->change_tracker());
736 FileChangeList changes;
737 backend->change_tracker()->GetChangesForURL(url, &changes);
739 base::FilePath platform_path;
740 base::PlatformFileInfo file_info;
741 FileSystemFileUtil* file_util =
742 file_system_context->sandbox_delegate()->sync_file_util();
745 base::PlatformFileError file_error = file_util->GetFileInfo(
747 new FileSystemOperationContext(file_system_context)).get(),
752 webkit_blob::ScopedFile snapshot;
753 if (file_error == base::PLATFORM_FILE_OK && sync_mode == SYNC_SNAPSHOT) {
754 base::FilePath snapshot_path;
755 file_util::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
757 if (base::CopyFile(platform_path, snapshot_path)) {
758 platform_path = snapshot_path;
759 snapshot = webkit_blob::ScopedFile(
761 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
762 file_system_context->default_file_task_runner());
766 if (status == SYNC_STATUS_OK &&
767 file_error != base::PLATFORM_FILE_OK &&
768 file_error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
769 status = PlatformFileErrorToSyncStatusCode(file_error);
771 DCHECK(!file_info.is_symbolic_link);
773 SyncFileType file_type = SYNC_FILE_TYPE_FILE;
774 if (file_error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
775 file_type = SYNC_FILE_TYPE_UNKNOWN;
776 else if (file_info.is_directory)
777 file_type = SYNC_FILE_TYPE_DIRECTORY;
779 LocalFileSyncInfo sync_file_info;
780 sync_file_info.url = url;
781 sync_file_info.local_file_path = platform_path;
782 sync_file_info.metadata.file_type = file_type;
783 sync_file_info.metadata.size = file_info.size;
784 sync_file_info.metadata.last_modified = file_info.last_modified;
785 sync_file_info.changes = changes;
787 if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
788 if (!changes.empty()) {
789 // Now we create an empty mirror change record for URL (and we record
790 // changes to both mirror and original records during sync), so that
791 // we can reset to the mirror when the sync succeeds.
792 backend->change_tracker()->CreateFreshMirrorForURL(url);
795 // 'Unlock' the file for snapshot sync.
796 // (But keep it in writing status so that no other sync starts on
798 io_task_runner_->PostTask(
800 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
801 this, url, true /* for_snapshot_sync */));
804 ui_task_runner_->PostTask(FROM_HERE,
805 base::Bind(callback, status, sync_file_info,
806 base::Passed(&snapshot)));
809 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
810 const FileSystemURL& url,
811 bool for_snapshot_sync) {
812 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
813 if (!sync_status()) {
814 // The service might have been shut down.
817 sync_status()->EndSyncing(url);
819 if (for_snapshot_sync) {
820 // The caller will hold shared lock on this one.
821 sync_status()->StartWriting(url);
825 // Since a sync has finished the number of changes must have been updated.
826 origins_with_pending_changes_.insert(url.origin());
827 ScheduleNotifyChangesUpdatedOnIOThread();
830 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
831 const FileSystemURL& url) {
832 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
833 if (!sync_status()) {
834 // The service might have been shut down.
837 sync_status()->EndWriting(url);
839 // Since a sync has finished the number of changes must have been updated.
840 origins_with_pending_changes_.insert(url.origin());
841 ScheduleNotifyChangesUpdatedOnIOThread();
844 void LocalFileSyncContext::DidApplyRemoteChange(
845 const FileSystemURL& url,
846 const SyncStatusCallback& callback_on_ui,
847 base::PlatformFileError file_error) {
848 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
849 ui_task_runner_->PostTask(
851 base::Bind(callback_on_ui,
852 PlatformFileErrorToSyncStatusCode(file_error)));
855 void LocalFileSyncContext::DidGetFileMetadata(
856 const SyncFileMetadataCallback& callback,
857 base::PlatformFileError file_error,
858 const base::PlatformFileInfo& file_info) {
859 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
860 SyncFileMetadata metadata;
861 if (file_error == base::PLATFORM_FILE_OK) {
862 metadata.file_type = file_info.is_directory ?
863 SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
864 metadata.size = file_info.size;
865 metadata.last_modified = file_info.last_modified;
867 ui_task_runner_->PostTask(
870 PlatformFileErrorToSyncStatusCode(file_error),
874 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
875 if (mock_notify_changes_duration_in_sec_ >= 0)
876 return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
877 return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
880 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
881 FileSystemContext* file_system_context,
882 const base::FilePath& local_path,
883 const FileSystemURL& dest_url,
884 const StatusCallback& callback,
885 base::PlatformFileError error) {
886 if (error != base::PLATFORM_FILE_OK) {
891 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
892 file_system_context, dest_url);
893 file_system_context->operation_runner()->CopyInForeignFile(
894 local_path, url_for_sync, callback);
897 } // namespace sync_file_system