Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / local / local_file_sync_context.cc
blob0aafc6d6c44b400f8df387ebb35ec5f8d86e0a1c
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"
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/stl_util.h"
12 #include "base/task_runner_util.h"
13 #include "chrome/browser/sync_file_system/file_change.h"
14 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
15 #include "chrome/browser/sync_file_system/local/local_origin_change_observer.h"
16 #include "chrome/browser/sync_file_system/local/root_delete_helper.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/logger.h"
20 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
21 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
22 #include "storage/browser/blob/scoped_file.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/file_system_file_util.h"
25 #include "storage/browser/fileapi/file_system_operation_context.h"
26 #include "storage/browser/fileapi/file_system_operation_runner.h"
27 #include "storage/common/fileapi/file_system_util.h"
29 using storage::FileSystemContext;
30 using storage::FileSystemFileUtil;
31 using storage::FileSystemOperation;
32 using storage::FileSystemOperationContext;
33 using storage::FileSystemURL;
35 namespace sync_file_system {
37 namespace {
39 const int kMaxConcurrentSyncableOperation = 3;
40 const int kNotifyChangesDurationInSec = 1;
41 const int kMaxURLsToFetchForLocalSync = 5;
43 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots");
45 } // namespace
47 LocalFileSyncContext::LocalFileSyncContext(
48 const base::FilePath& base_path,
49 leveldb::Env* env_override,
50 base::SingleThreadTaskRunner* ui_task_runner,
51 base::SingleThreadTaskRunner* io_task_runner)
52 : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))),
53 env_override_(env_override),
54 ui_task_runner_(ui_task_runner),
55 io_task_runner_(io_task_runner),
56 shutdown_on_ui_(false),
57 shutdown_on_io_(false),
58 mock_notify_changes_duration_in_sec_(-1) {
59 DCHECK(base_path.IsAbsolute());
60 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
63 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
64 const GURL& source_url,
65 FileSystemContext* file_system_context,
66 const SyncStatusCallback& callback) {
67 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
68 if (ContainsKey(file_system_contexts_, file_system_context)) {
69 // The context has been already initialized. Just dispatch the callback
70 // with SYNC_STATUS_OK.
71 ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
72 return;
75 StatusCallbackQueue& callback_queue =
76 pending_initialize_callbacks_[file_system_context];
77 callback_queue.push_back(callback);
78 if (callback_queue.size() > 1)
79 return;
81 // The sync service always expects the origin (app) is initialized
82 // for writable way (even when MaybeInitializeFileSystemContext is called
83 // from read-only OpenFileSystem), so open the filesystem with
84 // CREATE_IF_NONEXISTENT here.
85 storage::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback =
86 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread,
87 this,
88 source_url,
89 make_scoped_refptr(file_system_context));
90 io_task_runner_->PostTask(
91 FROM_HERE,
92 base::Bind(&storage::SandboxFileSystemBackendDelegate::OpenFileSystem,
93 base::Unretained(file_system_context->sandbox_delegate()),
94 source_url,
95 storage::kFileSystemTypeSyncable,
96 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
97 open_filesystem_callback,
98 GURL()));
101 void LocalFileSyncContext::ShutdownOnUIThread() {
102 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
103 shutdown_on_ui_ = true;
104 io_task_runner_->PostTask(
105 FROM_HERE,
106 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread, this));
109 void LocalFileSyncContext::GetFileForLocalSync(
110 FileSystemContext* file_system_context,
111 const LocalFileSyncInfoCallback& callback) {
112 DCHECK(file_system_context);
113 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
115 base::PostTaskAndReplyWithResult(
116 file_system_context->default_file_task_runner(),
117 FROM_HERE,
118 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread,
119 this, make_scoped_refptr(file_system_context)),
120 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync,
121 this, make_scoped_refptr(file_system_context), callback));
124 void LocalFileSyncContext::ClearChangesForURL(
125 FileSystemContext* file_system_context,
126 const FileSystemURL& url,
127 const base::Closure& done_callback) {
128 // This is initially called on UI thread and to be relayed to FILE thread.
129 DCHECK(file_system_context);
130 if (!file_system_context->default_file_task_runner()->
131 RunsTasksOnCurrentThread()) {
132 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
133 file_system_context->default_file_task_runner()->PostTask(
134 FROM_HERE,
135 base::Bind(&LocalFileSyncContext::ClearChangesForURL,
136 this, make_scoped_refptr(file_system_context),
137 url, done_callback));
138 return;
141 SyncFileSystemBackend* backend =
142 SyncFileSystemBackend::GetBackend(file_system_context);
143 DCHECK(backend);
144 DCHECK(backend->change_tracker());
145 backend->change_tracker()->ClearChangesForURL(url);
147 // Call the completion callback on UI thread.
148 ui_task_runner_->PostTask(FROM_HERE, done_callback);
151 void LocalFileSyncContext::FinalizeSnapshotSync(
152 storage::FileSystemContext* file_system_context,
153 const storage::FileSystemURL& url,
154 SyncStatusCode sync_finish_status,
155 const base::Closure& done_callback) {
156 DCHECK(file_system_context);
157 DCHECK(url.is_valid());
158 if (!file_system_context->default_file_task_runner()->
159 RunsTasksOnCurrentThread()) {
160 file_system_context->default_file_task_runner()->PostTask(
161 FROM_HERE,
162 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync,
163 this, make_scoped_refptr(file_system_context),
164 url, sync_finish_status, done_callback));
165 return;
168 SyncFileSystemBackend* backend =
169 SyncFileSystemBackend::GetBackend(file_system_context);
170 DCHECK(backend);
171 DCHECK(backend->change_tracker());
173 if (sync_finish_status == SYNC_STATUS_OK ||
174 sync_finish_status == SYNC_STATUS_HAS_CONFLICT) {
175 // Commit the in-memory mirror change.
176 backend->change_tracker()->ResetToMirrorAndCommitChangesForURL(url);
177 } else {
178 // Abort in-memory mirror change.
179 backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url);
182 // We've been keeping it in writing mode, so clear the writing counter
183 // to unblock sync activities.
184 io_task_runner_->PostTask(
185 FROM_HERE, base::Bind(
186 &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread, this, url));
188 // Call the completion callback on UI thread.
189 ui_task_runner_->PostTask(FROM_HERE, done_callback);
192 void LocalFileSyncContext::FinalizeExclusiveSync(
193 storage::FileSystemContext* file_system_context,
194 const storage::FileSystemURL& url,
195 bool clear_local_changes,
196 const base::Closure& done_callback) {
197 DCHECK(file_system_context);
198 if (!url.is_valid()) {
199 done_callback.Run();
200 return;
203 if (clear_local_changes) {
204 ClearChangesForURL(file_system_context, url,
205 base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync,
206 this, make_scoped_refptr(file_system_context),
207 url, false, done_callback));
208 return;
211 io_task_runner_->PostTask(
212 FROM_HERE,
213 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
214 this, url, false /* for_snapshot_sync */));
216 done_callback.Run();
219 void LocalFileSyncContext::PrepareForSync(
220 FileSystemContext* file_system_context,
221 const FileSystemURL& url,
222 SyncMode sync_mode,
223 const LocalFileSyncInfoCallback& callback) {
224 // This is initially called on UI thread and to be relayed to IO thread.
225 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
226 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
227 io_task_runner_->PostTask(
228 FROM_HERE,
229 base::Bind(&LocalFileSyncContext::PrepareForSync, this,
230 make_scoped_refptr(file_system_context), url,
231 sync_mode, callback));
232 return;
234 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
235 const bool syncable = sync_status()->IsSyncable(url);
236 // Disable writing if it's ready to be synced.
237 if (syncable)
238 sync_status()->StartSyncing(url);
239 ui_task_runner_->PostTask(
240 FROM_HERE,
241 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
242 this, make_scoped_refptr(file_system_context),
243 syncable ? SYNC_STATUS_OK :
244 SYNC_STATUS_FILE_BUSY,
245 url, sync_mode, callback));
248 void LocalFileSyncContext::RegisterURLForWaitingSync(
249 const FileSystemURL& url,
250 const base::Closure& on_syncable_callback) {
251 // This is initially called on UI thread and to be relayed to IO thread.
252 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
253 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
254 io_task_runner_->PostTask(
255 FROM_HERE,
256 base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync,
257 this, url, on_syncable_callback));
258 return;
260 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
261 if (shutdown_on_io_)
262 return;
263 if (sync_status()->IsSyncable(url)) {
264 // No need to register; fire the callback now.
265 ui_task_runner_->PostTask(FROM_HERE, on_syncable_callback);
266 return;
268 url_waiting_sync_on_io_ = url;
269 url_syncable_callback_ = on_syncable_callback;
272 void LocalFileSyncContext::ApplyRemoteChange(
273 FileSystemContext* file_system_context,
274 const FileChange& change,
275 const base::FilePath& local_path,
276 const FileSystemURL& url,
277 const SyncStatusCallback& callback) {
278 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
279 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
280 io_task_runner_->PostTask(
281 FROM_HERE,
282 base::Bind(&LocalFileSyncContext::ApplyRemoteChange, this,
283 make_scoped_refptr(file_system_context),
284 change, local_path, url, callback));
285 return;
287 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
288 DCHECK(!sync_status()->IsWritable(url));
289 DCHECK(!sync_status()->IsWriting(url));
291 FileSystemOperation::StatusCallback operation_callback;
292 switch (change.change()) {
293 case FileChange::FILE_CHANGE_DELETE:
294 HandleRemoteDelete(file_system_context, url, callback);
295 return;
296 case FileChange::FILE_CHANGE_ADD_OR_UPDATE:
297 HandleRemoteAddOrUpdate(
298 file_system_context, change, local_path, url, callback);
299 return;
301 NOTREACHED();
302 callback.Run(SYNC_STATUS_FAILED);
305 void LocalFileSyncContext::HandleRemoteDelete(
306 FileSystemContext* file_system_context,
307 const FileSystemURL& url,
308 const SyncStatusCallback& callback) {
309 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
310 file_system_context, url);
312 // Handle root directory case differently.
313 if (storage::VirtualPath::IsRootPath(url.path())) {
314 DCHECK(!root_delete_helper_);
315 root_delete_helper_.reset(new RootDeleteHelper(
316 file_system_context, sync_status(), url,
317 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
318 this, url, callback)));
319 root_delete_helper_->Run();
320 return;
323 file_system_context->operation_runner()->Remove(
324 url_for_sync, true /* recursive */,
325 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
326 this, url, callback));
329 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
330 FileSystemContext* file_system_context,
331 const FileChange& change,
332 const base::FilePath& local_path,
333 const FileSystemURL& url,
334 const SyncStatusCallback& callback) {
335 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
336 file_system_context, url);
338 if (storage::VirtualPath::IsRootPath(url.path())) {
339 DidApplyRemoteChange(url, callback, base::File::FILE_OK);
340 return;
343 file_system_context->operation_runner()->Remove(
344 url_for_sync, true /* recursive */,
345 base::Bind(
346 &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate,
347 this,
348 make_scoped_refptr(file_system_context),
349 change,
350 local_path,
351 url,
352 callback));
355 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
356 FileSystemContext* file_system_context,
357 const FileChange& change,
358 const base::FilePath& local_path,
359 const FileSystemURL& url,
360 const SyncStatusCallback& callback,
361 base::File::Error error) {
362 // Remove() may fail if the target entry does not exist (which is ok),
363 // so we ignore |error| here.
365 if (shutdown_on_io_) {
366 callback.Run(SYNC_FILE_ERROR_ABORT);
367 return;
370 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
371 DCHECK(!sync_status()->IsWritable(url));
372 DCHECK(!sync_status()->IsWriting(url));
374 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
375 file_system_context, url);
376 FileSystemOperation::StatusCallback operation_callback = base::Bind(
377 &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
379 DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE, change.change());
380 switch (change.file_type()) {
381 case SYNC_FILE_TYPE_FILE: {
382 DCHECK(!local_path.empty());
383 base::FilePath dir_path = storage::VirtualPath::DirName(url.path());
384 if (dir_path.empty() ||
385 storage::VirtualPath::DirName(dir_path) == dir_path) {
386 // Copying into the root directory.
387 file_system_context->operation_runner()->CopyInForeignFile(
388 local_path, url_for_sync, operation_callback);
389 } else {
390 FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
391 url_for_sync.origin(),
392 url_for_sync.mount_type(),
393 storage::VirtualPath::DirName(url_for_sync.virtual_path()));
394 file_system_context->operation_runner()->CreateDirectory(
395 dir_url,
396 false /* exclusive */,
397 true /* recursive */,
398 base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn,
399 this,
400 make_scoped_refptr(file_system_context),
401 local_path,
402 url,
403 operation_callback));
405 break;
407 case SYNC_FILE_TYPE_DIRECTORY:
408 file_system_context->operation_runner()->CreateDirectory(
409 url_for_sync, false /* exclusive */, true /* recursive */,
410 operation_callback);
411 break;
412 case SYNC_FILE_TYPE_UNKNOWN:
413 NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
417 void LocalFileSyncContext::RecordFakeLocalChange(
418 FileSystemContext* file_system_context,
419 const FileSystemURL& url,
420 const FileChange& change,
421 const SyncStatusCallback& callback) {
422 // This is called on UI thread and to be relayed to FILE thread.
423 DCHECK(file_system_context);
424 if (!file_system_context->default_file_task_runner()->
425 RunsTasksOnCurrentThread()) {
426 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
427 file_system_context->default_file_task_runner()->PostTask(
428 FROM_HERE,
429 base::Bind(&LocalFileSyncContext::RecordFakeLocalChange,
430 this, make_scoped_refptr(file_system_context),
431 url, change, callback));
432 return;
435 SyncFileSystemBackend* backend =
436 SyncFileSystemBackend::GetBackend(file_system_context);
437 DCHECK(backend);
438 DCHECK(backend->change_tracker());
439 backend->change_tracker()->MarkDirtyOnDatabase(url);
440 backend->change_tracker()->RecordChange(url, change);
442 // Fire the callback on UI thread.
443 ui_task_runner_->PostTask(FROM_HERE,
444 base::Bind(callback,
445 SYNC_STATUS_OK));
448 void LocalFileSyncContext::GetFileMetadata(
449 FileSystemContext* file_system_context,
450 const FileSystemURL& url,
451 const SyncFileMetadataCallback& callback) {
452 // This is initially called on UI thread and to be relayed to IO thread.
453 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
454 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
455 io_task_runner_->PostTask(
456 FROM_HERE,
457 base::Bind(&LocalFileSyncContext::GetFileMetadata, this,
458 make_scoped_refptr(file_system_context), url, callback));
459 return;
461 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
463 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
464 file_system_context, url);
465 file_system_context->operation_runner()->GetMetadata(
466 url_for_sync, base::Bind(&LocalFileSyncContext::DidGetFileMetadata,
467 this, callback));
470 void LocalFileSyncContext::HasPendingLocalChanges(
471 FileSystemContext* file_system_context,
472 const FileSystemURL& url,
473 const HasPendingLocalChangeCallback& callback) {
474 // This gets called on UI thread and relays the task on FILE thread.
475 DCHECK(file_system_context);
476 if (!file_system_context->default_file_task_runner()->
477 RunsTasksOnCurrentThread()) {
478 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
479 file_system_context->default_file_task_runner()->PostTask(
480 FROM_HERE,
481 base::Bind(&LocalFileSyncContext::HasPendingLocalChanges,
482 this, make_scoped_refptr(file_system_context),
483 url, callback));
484 return;
487 SyncFileSystemBackend* backend =
488 SyncFileSystemBackend::GetBackend(file_system_context);
489 DCHECK(backend);
490 DCHECK(backend->change_tracker());
491 FileChangeList changes;
492 backend->change_tracker()->GetChangesForURL(url, &changes);
494 // Fire the callback on UI thread.
495 ui_task_runner_->PostTask(FROM_HERE,
496 base::Bind(callback,
497 SYNC_STATUS_OK,
498 !changes.empty()));
501 void LocalFileSyncContext::PromoteDemotedChanges(
502 const GURL& origin,
503 storage::FileSystemContext* file_system_context,
504 const base::Closure& callback) {
505 // This is initially called on UI thread and to be relayed to FILE thread.
506 DCHECK(file_system_context);
507 if (!file_system_context->default_file_task_runner()->
508 RunsTasksOnCurrentThread()) {
509 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
510 file_system_context->default_file_task_runner()->PostTask(
511 FROM_HERE,
512 base::Bind(&LocalFileSyncContext::PromoteDemotedChanges,
513 this, origin, make_scoped_refptr(file_system_context),
514 callback));
515 return;
518 SyncFileSystemBackend* backend =
519 SyncFileSystemBackend::GetBackend(file_system_context);
520 DCHECK(backend);
521 DCHECK(backend->change_tracker());
522 if (!backend->change_tracker()->PromoteDemotedChanges()) {
523 ui_task_runner_->PostTask(FROM_HERE, callback);
524 return;
527 io_task_runner_->PostTask(
528 FROM_HERE,
529 base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin,
530 this, origin, callback));
533 void LocalFileSyncContext::UpdateChangesForOrigin(
534 const GURL& origin,
535 const base::Closure& callback) {
536 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
537 if (shutdown_on_io_)
538 return;
539 origins_with_pending_changes_.insert(origin);
540 ScheduleNotifyChangesUpdatedOnIOThread(callback);
543 void LocalFileSyncContext::AddOriginChangeObserver(
544 LocalOriginChangeObserver* observer) {
545 origin_change_observers_.AddObserver(observer);
548 void LocalFileSyncContext::RemoveOriginChangeObserver(
549 LocalOriginChangeObserver* observer) {
550 origin_change_observers_.RemoveObserver(observer);
553 base::WeakPtr<SyncableFileOperationRunner>
554 LocalFileSyncContext::operation_runner() const {
555 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
556 if (operation_runner_)
557 return operation_runner_->AsWeakPtr();
558 return base::WeakPtr<SyncableFileOperationRunner>();
561 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
562 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
563 return sync_status_.get();
566 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
567 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
568 if (shutdown_on_io_)
569 return;
570 UpdateChangesForOrigin(url.origin(), NoopClosure());
571 if (url_syncable_callback_.is_null() ||
572 sync_status()->IsWriting(url_waiting_sync_on_io_)) {
573 return;
575 // TODO(kinuko): may want to check how many pending tasks we have.
576 ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
577 url_syncable_callback_.Reset();
580 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
581 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
582 // Nothing to do for now.
585 LocalFileSyncContext::~LocalFileSyncContext() {
588 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread(
589 const base::Closure& callback) {
590 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
591 if (shutdown_on_io_)
592 return;
593 pending_completion_callbacks_.push_back(callback);
594 if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
595 NotifyAvailableChangesOnIOThread();
596 } else if (!timer_on_io_->IsRunning()) {
597 timer_on_io_->Start(
598 FROM_HERE, NotifyChangesDuration(),
599 base::Bind(&LocalFileSyncContext::NotifyAvailableChangesOnIOThread,
600 base::Unretained(this)));
604 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
605 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
606 if (shutdown_on_io_)
607 return;
609 std::vector<base::Closure> completion_callbacks;
610 completion_callbacks.swap(pending_completion_callbacks_);
612 ui_task_runner_->PostTask(
613 FROM_HERE,
614 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
615 this, origins_with_pending_changes_,
616 completion_callbacks));
617 last_notified_changes_ = base::Time::Now();
618 origins_with_pending_changes_.clear();
621 void LocalFileSyncContext::NotifyAvailableChanges(
622 const std::set<GURL>& origins,
623 const std::vector<base::Closure>& callbacks) {
624 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
625 OnChangesAvailableInOrigins(origins));
626 for (const auto& callback : callbacks)
627 callback.Run();
630 void LocalFileSyncContext::ShutdownOnIOThread() {
631 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
632 shutdown_on_io_ = true;
633 operation_runner_.reset();
634 root_delete_helper_.reset();
635 sync_status_.reset();
636 timer_on_io_.reset();
639 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
640 const GURL& source_url,
641 FileSystemContext* file_system_context,
642 const GURL& /* root */,
643 const std::string& /* name */,
644 base::File::Error error) {
645 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
646 if (shutdown_on_io_)
647 error = base::File::FILE_ERROR_ABORT;
648 if (error != base::File::FILE_OK) {
649 DidInitialize(source_url, file_system_context,
650 FileErrorToSyncStatusCode(error));
651 return;
653 DCHECK(file_system_context);
654 SyncFileSystemBackend* backend =
655 SyncFileSystemBackend::GetBackend(file_system_context);
656 DCHECK(backend);
657 if (!backend->change_tracker()) {
658 // Create and initialize LocalFileChangeTracker and call back this method
659 // later again.
660 std::set<GURL>* origins_with_changes = new std::set<GURL>;
661 scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
662 new scoped_ptr<LocalFileChangeTracker>);
663 base::PostTaskAndReplyWithResult(
664 file_system_context->default_file_task_runner(),
665 FROM_HERE,
666 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
667 this, tracker_ptr,
668 make_scoped_refptr(file_system_context),
669 origins_with_changes),
670 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
671 this, base::Owned(tracker_ptr),
672 source_url,
673 make_scoped_refptr(file_system_context),
674 base::Owned(origins_with_changes)));
675 return;
677 if (!operation_runner_) {
678 DCHECK(!sync_status_);
679 DCHECK(!timer_on_io_);
680 sync_status_.reset(new LocalFileSyncStatus);
681 timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
682 operation_runner_.reset(new SyncableFileOperationRunner(
683 kMaxConcurrentSyncableOperation,
684 sync_status_.get()));
685 sync_status_->AddObserver(this);
687 backend->set_sync_context(this);
688 DidInitialize(source_url, file_system_context,
689 SYNC_STATUS_OK);
692 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
693 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
694 FileSystemContext* file_system_context,
695 std::set<GURL>* origins_with_changes) {
696 DCHECK(file_system_context);
697 DCHECK(tracker_ptr);
698 DCHECK(origins_with_changes);
699 tracker_ptr->reset(new LocalFileChangeTracker(
700 file_system_context->partition_path(),
701 env_override_,
702 file_system_context->default_file_task_runner()));
703 const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
704 if (status != SYNC_STATUS_OK)
705 return status;
707 // Get all origins that have pending changes.
708 FileSystemURLQueue urls;
709 (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
710 for (FileSystemURLQueue::iterator iter = urls.begin();
711 iter != urls.end(); ++iter) {
712 origins_with_changes->insert(iter->origin());
715 // Creates snapshot directory.
716 base::CreateDirectory(local_base_path_.Append(kSnapshotDir));
718 return status;
721 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
722 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
723 const GURL& source_url,
724 FileSystemContext* file_system_context,
725 std::set<GURL>* origins_with_changes,
726 SyncStatusCode status) {
727 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
728 DCHECK(file_system_context);
729 DCHECK(origins_with_changes);
730 if (shutdown_on_io_)
731 status = SYNC_STATUS_ABORT;
732 if (status != SYNC_STATUS_OK) {
733 DidInitialize(source_url, file_system_context, status);
734 return;
737 SyncFileSystemBackend* backend =
738 SyncFileSystemBackend::GetBackend(file_system_context);
739 DCHECK(backend);
740 backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
742 origins_with_pending_changes_.insert(origins_with_changes->begin(),
743 origins_with_changes->end());
744 ScheduleNotifyChangesUpdatedOnIOThread(NoopClosure());
746 InitializeFileSystemContextOnIOThread(source_url, file_system_context,
747 GURL(), std::string(),
748 base::File::FILE_OK);
751 void LocalFileSyncContext::DidInitialize(
752 const GURL& source_url,
753 FileSystemContext* file_system_context,
754 SyncStatusCode status) {
755 if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
756 ui_task_runner_->PostTask(
757 FROM_HERE,
758 base::Bind(&LocalFileSyncContext::DidInitialize,
759 this, source_url,
760 make_scoped_refptr(file_system_context), status));
761 return;
763 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
764 DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
765 DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
767 SyncFileSystemBackend* backend =
768 SyncFileSystemBackend::GetBackend(file_system_context);
769 DCHECK(backend);
770 DCHECK(backend->change_tracker());
772 file_system_contexts_.insert(file_system_context);
774 StatusCallbackQueue& callback_queue =
775 pending_initialize_callbacks_[file_system_context];
776 for (StatusCallbackQueue::iterator iter = callback_queue.begin();
777 iter != callback_queue.end(); ++iter) {
778 ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
780 pending_initialize_callbacks_.erase(file_system_context);
783 scoped_ptr<LocalFileSyncContext::FileSystemURLQueue>
784 LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
785 FileSystemContext* file_system_context) {
786 DCHECK(file_system_context);
787 DCHECK(file_system_context->default_file_task_runner()->
788 RunsTasksOnCurrentThread());
789 SyncFileSystemBackend* backend =
790 SyncFileSystemBackend::GetBackend(file_system_context);
791 DCHECK(backend);
792 DCHECK(backend->change_tracker());
793 scoped_ptr<FileSystemURLQueue> urls(new FileSystemURLQueue);
794 backend->change_tracker()->GetNextChangedURLs(
795 urls.get(), kMaxURLsToFetchForLocalSync);
796 for (FileSystemURLQueue::iterator iter = urls->begin();
797 iter != urls->end(); ++iter)
798 backend->change_tracker()->DemoteChangesForURL(*iter);
800 return urls.Pass();
803 void LocalFileSyncContext::TryPrepareForLocalSync(
804 FileSystemContext* file_system_context,
805 const LocalFileSyncInfoCallback& callback,
806 scoped_ptr<FileSystemURLQueue> urls) {
807 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
808 DCHECK(urls);
810 if (shutdown_on_ui_) {
811 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(), storage::ScopedFile());
812 return;
815 if (urls->empty()) {
816 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC,
817 LocalFileSyncInfo(),
818 storage::ScopedFile());
819 return;
822 const FileSystemURL url = urls->front();
823 urls->pop_front();
825 PrepareForSync(
826 file_system_context, url, SYNC_SNAPSHOT,
827 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
828 this, make_scoped_refptr(file_system_context),
829 base::Passed(&urls), callback));
832 void LocalFileSyncContext::DidTryPrepareForLocalSync(
833 FileSystemContext* file_system_context,
834 scoped_ptr<FileSystemURLQueue> remaining_urls,
835 const LocalFileSyncInfoCallback& callback,
836 SyncStatusCode status,
837 const LocalFileSyncInfo& sync_file_info,
838 storage::ScopedFile snapshot) {
839 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
840 if (status != SYNC_STATUS_FILE_BUSY) {
841 PromoteDemotedChangesForURLs(file_system_context,
842 remaining_urls.Pass());
843 callback.Run(status, sync_file_info, snapshot.Pass());
844 return;
847 PromoteDemotedChangesForURL(file_system_context, sync_file_info.url);
849 // Recursively call TryPrepareForLocalSync with remaining_urls.
850 TryPrepareForLocalSync(file_system_context, callback, remaining_urls.Pass());
853 void LocalFileSyncContext::PromoteDemotedChangesForURL(
854 FileSystemContext* file_system_context,
855 const FileSystemURL& url) {
856 DCHECK(file_system_context);
857 if (!file_system_context->default_file_task_runner()->
858 RunsTasksOnCurrentThread()) {
859 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
860 if (shutdown_on_ui_)
861 return;
862 file_system_context->default_file_task_runner()->PostTask(
863 FROM_HERE,
864 base::Bind(&LocalFileSyncContext::PromoteDemotedChangesForURL,
865 this, make_scoped_refptr(file_system_context), url));
866 return;
869 SyncFileSystemBackend* backend =
870 SyncFileSystemBackend::GetBackend(file_system_context);
871 DCHECK(backend);
872 DCHECK(backend->change_tracker());
873 backend->change_tracker()->PromoteDemotedChangesForURL(url);
876 void LocalFileSyncContext::PromoteDemotedChangesForURLs(
877 FileSystemContext* file_system_context,
878 scoped_ptr<FileSystemURLQueue> urls) {
879 DCHECK(file_system_context);
880 if (!file_system_context->default_file_task_runner()->
881 RunsTasksOnCurrentThread()) {
882 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
883 if (shutdown_on_ui_)
884 return;
885 file_system_context->default_file_task_runner()->PostTask(
886 FROM_HERE,
887 base::Bind(&LocalFileSyncContext::PromoteDemotedChangesForURLs,
888 this, make_scoped_refptr(file_system_context),
889 base::Passed(&urls)));
890 return;
893 for (FileSystemURLQueue::iterator iter = urls->begin();
894 iter != urls->end(); ++iter)
895 PromoteDemotedChangesForURL(file_system_context, *iter);
898 void LocalFileSyncContext::DidGetWritingStatusForSync(
899 FileSystemContext* file_system_context,
900 SyncStatusCode status,
901 const FileSystemURL& url,
902 SyncMode sync_mode,
903 const LocalFileSyncInfoCallback& callback) {
904 // This gets called on UI thread and relays the task on FILE thread.
905 DCHECK(file_system_context);
906 if (!file_system_context->default_file_task_runner()->
907 RunsTasksOnCurrentThread()) {
908 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
909 if (shutdown_on_ui_) {
910 callback.Run(
911 SYNC_STATUS_ABORT, LocalFileSyncInfo(), storage::ScopedFile());
912 return;
914 file_system_context->default_file_task_runner()->PostTask(
915 FROM_HERE,
916 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
917 this, make_scoped_refptr(file_system_context),
918 status, url, sync_mode, callback));
919 return;
922 SyncFileSystemBackend* backend =
923 SyncFileSystemBackend::GetBackend(file_system_context);
924 DCHECK(backend);
925 DCHECK(backend->change_tracker());
926 FileChangeList changes;
927 backend->change_tracker()->GetChangesForURL(url, &changes);
929 base::FilePath platform_path;
930 base::File::Info file_info;
931 FileSystemFileUtil* file_util =
932 file_system_context->sandbox_delegate()->sync_file_util();
933 DCHECK(file_util);
935 base::File::Error file_error = file_util->GetFileInfo(
936 make_scoped_ptr(
937 new FileSystemOperationContext(file_system_context)).get(),
938 url,
939 &file_info,
940 &platform_path);
942 storage::ScopedFile snapshot;
943 if (file_error == base::File::FILE_OK && sync_mode == SYNC_SNAPSHOT) {
944 base::FilePath snapshot_path;
945 base::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
946 &snapshot_path);
947 if (base::CopyFile(platform_path, snapshot_path)) {
948 platform_path = snapshot_path;
949 snapshot =
950 storage::ScopedFile(snapshot_path,
951 storage::ScopedFile::DELETE_ON_SCOPE_OUT,
952 file_system_context->default_file_task_runner());
956 if (status == SYNC_STATUS_OK &&
957 file_error != base::File::FILE_OK &&
958 file_error != base::File::FILE_ERROR_NOT_FOUND) {
959 status = FileErrorToSyncStatusCode(file_error);
962 DCHECK(!file_info.is_symbolic_link);
964 SyncFileType file_type = SYNC_FILE_TYPE_FILE;
965 if (file_error == base::File::FILE_ERROR_NOT_FOUND)
966 file_type = SYNC_FILE_TYPE_UNKNOWN;
967 else if (file_info.is_directory)
968 file_type = SYNC_FILE_TYPE_DIRECTORY;
970 LocalFileSyncInfo sync_file_info;
971 sync_file_info.url = url;
972 sync_file_info.local_file_path = platform_path;
973 sync_file_info.metadata.file_type = file_type;
974 sync_file_info.metadata.size = file_info.size;
975 sync_file_info.metadata.last_modified = file_info.last_modified;
976 sync_file_info.changes = changes;
978 if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
979 if (!changes.empty()) {
980 // Now we create an empty mirror change record for URL (and we record
981 // changes to both mirror and original records during sync), so that
982 // we can reset to the mirror when the sync succeeds.
983 backend->change_tracker()->CreateFreshMirrorForURL(url);
986 // 'Unlock' the file for snapshot sync.
987 // (But keep it in writing status so that no other sync starts on
988 // the same URL)
989 io_task_runner_->PostTask(
990 FROM_HERE,
991 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
992 this, url, true /* for_snapshot_sync */));
995 ui_task_runner_->PostTask(FROM_HERE,
996 base::Bind(callback, status, sync_file_info,
997 base::Passed(&snapshot)));
1000 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
1001 const FileSystemURL& url,
1002 bool for_snapshot_sync) {
1003 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
1004 if (shutdown_on_io_)
1005 return;
1006 sync_status()->EndSyncing(url);
1008 if (for_snapshot_sync) {
1009 // The caller will hold shared lock on this one.
1010 sync_status()->StartWriting(url);
1011 return;
1014 // Since a sync has finished the number of changes must have been updated.
1015 UpdateChangesForOrigin(url.origin(), NoopClosure());
1018 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
1019 const FileSystemURL& url) {
1020 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
1021 if (shutdown_on_io_)
1022 return;
1023 sync_status()->EndWriting(url);
1025 // Since a sync has finished the number of changes must have been updated.
1026 UpdateChangesForOrigin(url.origin(), NoopClosure());
1029 void LocalFileSyncContext::DidApplyRemoteChange(
1030 const FileSystemURL& url,
1031 const SyncStatusCallback& callback_on_ui,
1032 base::File::Error file_error) {
1033 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
1034 root_delete_helper_.reset();
1035 ui_task_runner_->PostTask(
1036 FROM_HERE,
1037 base::Bind(callback_on_ui, FileErrorToSyncStatusCode(file_error)));
1040 void LocalFileSyncContext::DidGetFileMetadata(
1041 const SyncFileMetadataCallback& callback,
1042 base::File::Error file_error,
1043 const base::File::Info& file_info) {
1044 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
1045 SyncFileMetadata metadata;
1046 if (file_error == base::File::FILE_OK) {
1047 metadata.file_type = file_info.is_directory ?
1048 SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
1049 metadata.size = file_info.size;
1050 metadata.last_modified = file_info.last_modified;
1052 ui_task_runner_->PostTask(
1053 FROM_HERE,
1054 base::Bind(callback, FileErrorToSyncStatusCode(file_error), metadata));
1057 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
1058 if (mock_notify_changes_duration_in_sec_ >= 0)
1059 return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
1060 return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
1063 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
1064 FileSystemContext* file_system_context,
1065 const base::FilePath& local_path,
1066 const FileSystemURL& dest_url,
1067 const StatusCallback& callback,
1068 base::File::Error error) {
1069 if (error != base::File::FILE_OK) {
1070 callback.Run(error);
1071 return;
1074 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
1075 file_system_context, dest_url);
1076 file_system_context->operation_runner()->CopyInForeignFile(
1077 local_path, url_for_sync, callback);
1080 } // namespace sync_file_system