Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / local / local_file_sync_context.cc
blob81a83da59e2e6419d363ee97342d8ff26f0df82b
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/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/root_delete_helper.h"
18 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
19 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
20 #include "chrome/browser/sync_file_system/logger.h"
21 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
22 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_file_util.h"
25 #include "webkit/browser/fileapi/file_system_operation_context.h"
26 #include "webkit/browser/fileapi/file_system_operation_runner.h"
27 #include "webkit/common/blob/scoped_file.h"
28 #include "webkit/common/fileapi/file_system_util.h"
30 using fileapi::FileSystemContext;
31 using fileapi::FileSystemFileUtil;
32 using fileapi::FileSystemOperation;
33 using fileapi::FileSystemOperationContext;
34 using fileapi::FileSystemURL;
36 namespace sync_file_system {
38 namespace {
40 const int kMaxConcurrentSyncableOperation = 3;
41 const int kNotifyChangesDurationInSec = 1;
42 const int kMaxURLsToFetchForLocalSync = 5;
44 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots");
46 } // namespace
48 LocalFileSyncContext::LocalFileSyncContext(
49 const base::FilePath& base_path,
50 base::SingleThreadTaskRunner* ui_task_runner,
51 base::SingleThreadTaskRunner* io_task_runner)
52 : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))),
53 ui_task_runner_(ui_task_runner),
54 io_task_runner_(io_task_runner),
55 shutdown_on_ui_(false),
56 shutdown_on_io_(false),
57 mock_notify_changes_duration_in_sec_(-1) {
58 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
61 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
62 const GURL& source_url,
63 FileSystemContext* file_system_context,
64 const SyncStatusCallback& callback) {
65 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
66 if (ContainsKey(file_system_contexts_, file_system_context)) {
67 // The context has been already initialized. Just dispatch the callback
68 // with SYNC_STATUS_OK.
69 ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
70 return;
73 StatusCallbackQueue& callback_queue =
74 pending_initialize_callbacks_[file_system_context];
75 callback_queue.push_back(callback);
76 if (callback_queue.size() > 1)
77 return;
79 // The sync service always expects the origin (app) is initialized
80 // for writable way (even when MaybeInitializeFileSystemContext is called
81 // from read-only OpenFileSystem), so open the filesystem with
82 // CREATE_IF_NONEXISTENT here.
83 fileapi::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback =
84 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread,
85 this, source_url, make_scoped_refptr(file_system_context));
86 io_task_runner_->PostTask(
87 FROM_HERE,
88 base::Bind(&fileapi::SandboxFileSystemBackendDelegate::OpenFileSystem,
89 base::Unretained(file_system_context->sandbox_delegate()),
90 source_url, fileapi::kFileSystemTypeSyncable,
91 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
92 open_filesystem_callback, GURL()));
95 void LocalFileSyncContext::ShutdownOnUIThread() {
96 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
97 shutdown_on_ui_ = true;
98 io_task_runner_->PostTask(
99 FROM_HERE,
100 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread, this));
103 void LocalFileSyncContext::GetFileForLocalSync(
104 FileSystemContext* file_system_context,
105 const LocalFileSyncInfoCallback& callback) {
106 DCHECK(file_system_context);
107 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
109 std::deque<FileSystemURL>* urls = new std::deque<FileSystemURL>;
110 file_system_context->default_file_task_runner()->PostTaskAndReply(
111 FROM_HERE,
112 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread,
113 this, make_scoped_refptr(file_system_context),
114 base::Unretained(urls)),
115 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync,
116 this, make_scoped_refptr(file_system_context),
117 base::Owned(urls), callback));
120 void LocalFileSyncContext::ClearChangesForURL(
121 FileSystemContext* file_system_context,
122 const FileSystemURL& url,
123 const base::Closure& done_callback) {
124 // This is initially called on UI thread and to be relayed to FILE thread.
125 DCHECK(file_system_context);
126 if (!file_system_context->default_file_task_runner()->
127 RunsTasksOnCurrentThread()) {
128 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
129 file_system_context->default_file_task_runner()->PostTask(
130 FROM_HERE,
131 base::Bind(&LocalFileSyncContext::ClearChangesForURL,
132 this, make_scoped_refptr(file_system_context),
133 url, done_callback));
134 return;
137 SyncFileSystemBackend* backend =
138 SyncFileSystemBackend::GetBackend(file_system_context);
139 DCHECK(backend);
140 DCHECK(backend->change_tracker());
141 backend->change_tracker()->ClearChangesForURL(url);
143 // Call the completion callback on UI thread.
144 ui_task_runner_->PostTask(FROM_HERE, done_callback);
147 void LocalFileSyncContext::FinalizeSnapshotSync(
148 fileapi::FileSystemContext* file_system_context,
149 const fileapi::FileSystemURL& url,
150 SyncStatusCode sync_finish_status,
151 const base::Closure& done_callback) {
152 DCHECK(file_system_context);
153 DCHECK(url.is_valid());
154 if (!file_system_context->default_file_task_runner()->
155 RunsTasksOnCurrentThread()) {
156 file_system_context->default_file_task_runner()->PostTask(
157 FROM_HERE,
158 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync,
159 this, make_scoped_refptr(file_system_context),
160 url, sync_finish_status, done_callback));
161 return;
164 SyncFileSystemBackend* backend =
165 SyncFileSystemBackend::GetBackend(file_system_context);
166 DCHECK(backend);
167 DCHECK(backend->change_tracker());
169 if (sync_finish_status == SYNC_STATUS_OK ||
170 sync_finish_status == SYNC_STATUS_HAS_CONFLICT) {
171 // Commit the in-memory mirror change.
172 backend->change_tracker()->ResetToMirrorAndCommitChangesForURL(url);
173 } else {
174 // Abort in-memory mirror change.
175 backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url);
176 if (sync_finish_status == SYNC_STATUS_FILE_BUSY)
177 backend->change_tracker()->DemoteChangesForURL(url);
180 // We've been keeping it in writing mode, so clear the writing counter
181 // to unblock sync activities.
182 io_task_runner_->PostTask(
183 FROM_HERE, base::Bind(
184 &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread, this, url));
186 // Call the completion callback on UI thread.
187 ui_task_runner_->PostTask(FROM_HERE, done_callback);
190 void LocalFileSyncContext::FinalizeExclusiveSync(
191 fileapi::FileSystemContext* file_system_context,
192 const fileapi::FileSystemURL& url,
193 bool clear_local_changes,
194 const base::Closure& done_callback) {
195 DCHECK(file_system_context);
196 if (!url.is_valid()) {
197 done_callback.Run();
198 return;
201 if (clear_local_changes) {
202 ClearChangesForURL(file_system_context, url,
203 base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync,
204 this, make_scoped_refptr(file_system_context),
205 url, false, done_callback));
206 return;
209 io_task_runner_->PostTask(
210 FROM_HERE,
211 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
212 this, url, false /* for_snapshot_sync */));
214 done_callback.Run();
217 void LocalFileSyncContext::PrepareForSync(
218 FileSystemContext* file_system_context,
219 const FileSystemURL& url,
220 SyncMode sync_mode,
221 const LocalFileSyncInfoCallback& callback) {
222 // This is initially called on UI thread and to be relayed to IO thread.
223 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
224 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
225 io_task_runner_->PostTask(
226 FROM_HERE,
227 base::Bind(&LocalFileSyncContext::PrepareForSync, this,
228 make_scoped_refptr(file_system_context), url,
229 sync_mode, callback));
230 return;
232 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
233 const bool syncable = sync_status()->IsSyncable(url);
234 // Disable writing if it's ready to be synced.
235 if (syncable)
236 sync_status()->StartSyncing(url);
237 ui_task_runner_->PostTask(
238 FROM_HERE,
239 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
240 this, make_scoped_refptr(file_system_context),
241 syncable ? SYNC_STATUS_OK :
242 SYNC_STATUS_FILE_BUSY,
243 url, sync_mode, callback));
246 void LocalFileSyncContext::RegisterURLForWaitingSync(
247 const FileSystemURL& url,
248 const base::Closure& on_syncable_callback) {
249 // This is initially called on UI thread and to be relayed to IO thread.
250 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
251 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
252 io_task_runner_->PostTask(
253 FROM_HERE,
254 base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync,
255 this, url, on_syncable_callback));
256 return;
258 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
259 if (shutdown_on_io_)
260 return;
261 if (sync_status()->IsSyncable(url)) {
262 // No need to register; fire the callback now.
263 ui_task_runner_->PostTask(FROM_HERE, on_syncable_callback);
264 return;
266 url_waiting_sync_on_io_ = url;
267 url_syncable_callback_ = on_syncable_callback;
270 void LocalFileSyncContext::ApplyRemoteChange(
271 FileSystemContext* file_system_context,
272 const FileChange& change,
273 const base::FilePath& local_path,
274 const FileSystemURL& url,
275 const SyncStatusCallback& callback) {
276 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
277 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
278 io_task_runner_->PostTask(
279 FROM_HERE,
280 base::Bind(&LocalFileSyncContext::ApplyRemoteChange, this,
281 make_scoped_refptr(file_system_context),
282 change, local_path, url, callback));
283 return;
285 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
286 DCHECK(!sync_status()->IsWritable(url));
287 DCHECK(!sync_status()->IsWriting(url));
289 FileSystemOperation::StatusCallback operation_callback;
290 switch (change.change()) {
291 case FileChange::FILE_CHANGE_DELETE:
292 HandleRemoteDelete(file_system_context, url, callback);
293 return;
294 case FileChange::FILE_CHANGE_ADD_OR_UPDATE:
295 HandleRemoteAddOrUpdate(
296 file_system_context, change, local_path, url, callback);
297 return;
299 NOTREACHED();
300 callback.Run(SYNC_STATUS_FAILED);
303 void LocalFileSyncContext::HandleRemoteDelete(
304 FileSystemContext* file_system_context,
305 const FileSystemURL& url,
306 const SyncStatusCallback& callback) {
307 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
308 file_system_context, url);
310 // Handle root directory case differently.
311 if (fileapi::VirtualPath::IsRootPath(url.path())) {
312 DCHECK(!root_delete_helper_);
313 root_delete_helper_.reset(new RootDeleteHelper(
314 file_system_context, sync_status(), url,
315 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
316 this, url, callback)));
317 root_delete_helper_->Run();
318 return;
321 file_system_context->operation_runner()->Remove(
322 url_for_sync, true /* recursive */,
323 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
324 this, url, callback));
327 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
328 FileSystemContext* file_system_context,
329 const FileChange& change,
330 const base::FilePath& local_path,
331 const FileSystemURL& url,
332 const SyncStatusCallback& callback) {
333 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
334 file_system_context, url);
336 if (fileapi::VirtualPath::IsRootPath(url.path())) {
337 DidApplyRemoteChange(url, callback, base::PLATFORM_FILE_OK);
338 return;
341 file_system_context->operation_runner()->Remove(
342 url_for_sync, true /* recursive */,
343 base::Bind(
344 &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate,
345 this,
346 make_scoped_refptr(file_system_context),
347 change,
348 local_path,
349 url,
350 callback));
353 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
354 FileSystemContext* file_system_context,
355 const FileChange& change,
356 const base::FilePath& local_path,
357 const FileSystemURL& url,
358 const SyncStatusCallback& callback,
359 base::PlatformFileError error) {
360 // Remove() may fail if the target entry does not exist (which is ok),
361 // so we ignore |error| here.
363 if (shutdown_on_io_) {
364 callback.Run(SYNC_FILE_ERROR_ABORT);
365 return;
368 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
369 DCHECK(!sync_status()->IsWritable(url));
370 DCHECK(!sync_status()->IsWriting(url));
372 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
373 file_system_context, url);
374 FileSystemOperation::StatusCallback operation_callback = base::Bind(
375 &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
377 DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE, change.change());
378 switch (change.file_type()) {
379 case SYNC_FILE_TYPE_FILE: {
380 DCHECK(!local_path.empty());
381 base::FilePath dir_path = fileapi::VirtualPath::DirName(url.path());
382 if (dir_path.empty() ||
383 fileapi::VirtualPath::DirName(dir_path) == dir_path) {
384 // Copying into the root directory.
385 file_system_context->operation_runner()->CopyInForeignFile(
386 local_path, url_for_sync, operation_callback);
387 } else {
388 FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
389 url_for_sync.origin(),
390 url_for_sync.mount_type(),
391 fileapi::VirtualPath::DirName(url_for_sync.virtual_path()));
392 file_system_context->operation_runner()->CreateDirectory(
393 dir_url,
394 false /* exclusive */,
395 true /* recursive */,
396 base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn,
397 this,
398 make_scoped_refptr(file_system_context),
399 local_path,
400 url,
401 operation_callback));
403 break;
405 case SYNC_FILE_TYPE_DIRECTORY:
406 file_system_context->operation_runner()->CreateDirectory(
407 url_for_sync, false /* exclusive */, true /* recursive */,
408 operation_callback);
409 break;
410 case SYNC_FILE_TYPE_UNKNOWN:
411 NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
415 void LocalFileSyncContext::RecordFakeLocalChange(
416 FileSystemContext* file_system_context,
417 const FileSystemURL& url,
418 const FileChange& change,
419 const SyncStatusCallback& callback) {
420 // This is called on UI thread and to be relayed to FILE thread.
421 DCHECK(file_system_context);
422 if (!file_system_context->default_file_task_runner()->
423 RunsTasksOnCurrentThread()) {
424 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
425 file_system_context->default_file_task_runner()->PostTask(
426 FROM_HERE,
427 base::Bind(&LocalFileSyncContext::RecordFakeLocalChange,
428 this, make_scoped_refptr(file_system_context),
429 url, change, callback));
430 return;
433 SyncFileSystemBackend* backend =
434 SyncFileSystemBackend::GetBackend(file_system_context);
435 DCHECK(backend);
436 DCHECK(backend->change_tracker());
437 backend->change_tracker()->MarkDirtyOnDatabase(url);
438 backend->change_tracker()->RecordChange(url, change);
440 // Fire the callback on UI thread.
441 ui_task_runner_->PostTask(FROM_HERE,
442 base::Bind(callback,
443 SYNC_STATUS_OK));
446 void LocalFileSyncContext::GetFileMetadata(
447 FileSystemContext* file_system_context,
448 const FileSystemURL& url,
449 const SyncFileMetadataCallback& callback) {
450 // This is initially called on UI thread and to be relayed to IO thread.
451 if (!io_task_runner_->RunsTasksOnCurrentThread()) {
452 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
453 io_task_runner_->PostTask(
454 FROM_HERE,
455 base::Bind(&LocalFileSyncContext::GetFileMetadata, this,
456 make_scoped_refptr(file_system_context), url, callback));
457 return;
459 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
461 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
462 file_system_context, url);
463 file_system_context->operation_runner()->GetMetadata(
464 url_for_sync, base::Bind(&LocalFileSyncContext::DidGetFileMetadata,
465 this, callback));
468 void LocalFileSyncContext::HasPendingLocalChanges(
469 FileSystemContext* file_system_context,
470 const FileSystemURL& url,
471 const HasPendingLocalChangeCallback& callback) {
472 // This gets called on UI thread and relays the task on FILE thread.
473 DCHECK(file_system_context);
474 if (!file_system_context->default_file_task_runner()->
475 RunsTasksOnCurrentThread()) {
476 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
477 file_system_context->default_file_task_runner()->PostTask(
478 FROM_HERE,
479 base::Bind(&LocalFileSyncContext::HasPendingLocalChanges,
480 this, make_scoped_refptr(file_system_context),
481 url, callback));
482 return;
485 SyncFileSystemBackend* backend =
486 SyncFileSystemBackend::GetBackend(file_system_context);
487 DCHECK(backend);
488 DCHECK(backend->change_tracker());
489 FileChangeList changes;
490 backend->change_tracker()->GetChangesForURL(url, &changes);
492 // Fire the callback on UI thread.
493 ui_task_runner_->PostTask(FROM_HERE,
494 base::Bind(callback,
495 SYNC_STATUS_OK,
496 !changes.empty()));
499 void LocalFileSyncContext::PromoteDemotedChanges(
500 const GURL& origin,
501 fileapi::FileSystemContext* file_system_context) {
502 // This is initially called on UI thread and to be relayed to FILE thread.
503 DCHECK(file_system_context);
504 if (!file_system_context->default_file_task_runner()->
505 RunsTasksOnCurrentThread()) {
506 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
507 file_system_context->default_file_task_runner()->PostTask(
508 FROM_HERE,
509 base::Bind(&LocalFileSyncContext::PromoteDemotedChanges,
510 this, origin, make_scoped_refptr(file_system_context)));
511 return;
514 SyncFileSystemBackend* backend =
515 SyncFileSystemBackend::GetBackend(file_system_context);
516 DCHECK(backend);
517 DCHECK(backend->change_tracker());
518 if (!backend->change_tracker()->PromoteDemotedChanges())
519 return;
521 io_task_runner_->PostTask(
522 FROM_HERE,
523 base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin,
524 this, origin));
527 void LocalFileSyncContext::UpdateChangesForOrigin(const GURL& origin) {
528 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
529 if (shutdown_on_io_)
530 return;
531 origins_with_pending_changes_.insert(origin);
532 ScheduleNotifyChangesUpdatedOnIOThread();
535 void LocalFileSyncContext::AddOriginChangeObserver(
536 LocalOriginChangeObserver* observer) {
537 origin_change_observers_.AddObserver(observer);
540 void LocalFileSyncContext::RemoveOriginChangeObserver(
541 LocalOriginChangeObserver* observer) {
542 origin_change_observers_.RemoveObserver(observer);
545 base::WeakPtr<SyncableFileOperationRunner>
546 LocalFileSyncContext::operation_runner() const {
547 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
548 if (operation_runner_)
549 return operation_runner_->AsWeakPtr();
550 return base::WeakPtr<SyncableFileOperationRunner>();
553 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
554 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
555 return sync_status_.get();
558 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
559 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
560 if (shutdown_on_io_)
561 return;
562 UpdateChangesForOrigin(url.origin());
563 if (url_syncable_callback_.is_null() ||
564 sync_status()->IsWriting(url_waiting_sync_on_io_)) {
565 return;
567 // TODO(kinuko): may want to check how many pending tasks we have.
568 ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
569 url_syncable_callback_.Reset();
572 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
573 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
574 // Nothing to do for now.
577 LocalFileSyncContext::~LocalFileSyncContext() {
580 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
581 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
582 if (shutdown_on_io_)
583 return;
584 if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
585 NotifyAvailableChangesOnIOThread();
586 } else if (!timer_on_io_->IsRunning()) {
587 timer_on_io_->Start(
588 FROM_HERE, NotifyChangesDuration(), this,
589 &LocalFileSyncContext::NotifyAvailableChangesOnIOThread);
593 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
594 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
595 if (shutdown_on_io_)
596 return;
597 ui_task_runner_->PostTask(
598 FROM_HERE,
599 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
600 this, origins_with_pending_changes_));
601 last_notified_changes_ = base::Time::Now();
602 origins_with_pending_changes_.clear();
605 void LocalFileSyncContext::NotifyAvailableChanges(
606 const std::set<GURL>& origins) {
607 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
608 OnChangesAvailableInOrigins(origins));
611 void LocalFileSyncContext::ShutdownOnIOThread() {
612 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
613 shutdown_on_io_ = true;
614 operation_runner_.reset();
615 root_delete_helper_.reset();
616 sync_status_.reset();
617 timer_on_io_.reset();
620 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
621 const GURL& source_url,
622 FileSystemContext* file_system_context,
623 const GURL& /* root */,
624 const std::string& /* name */,
625 base::PlatformFileError error) {
626 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
627 if (shutdown_on_io_)
628 error = base::PLATFORM_FILE_ERROR_ABORT;
629 if (error != base::PLATFORM_FILE_OK) {
630 DidInitialize(source_url, file_system_context,
631 PlatformFileErrorToSyncStatusCode(error));
632 return;
634 DCHECK(file_system_context);
635 SyncFileSystemBackend* backend =
636 SyncFileSystemBackend::GetBackend(file_system_context);
637 DCHECK(backend);
638 if (!backend->change_tracker()) {
639 // Create and initialize LocalFileChangeTracker and call back this method
640 // later again.
641 std::set<GURL>* origins_with_changes = new std::set<GURL>;
642 scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
643 new scoped_ptr<LocalFileChangeTracker>);
644 base::PostTaskAndReplyWithResult(
645 file_system_context->default_file_task_runner(),
646 FROM_HERE,
647 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
648 this, tracker_ptr,
649 make_scoped_refptr(file_system_context),
650 origins_with_changes),
651 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
652 this, base::Owned(tracker_ptr),
653 source_url,
654 make_scoped_refptr(file_system_context),
655 base::Owned(origins_with_changes)));
656 return;
658 if (!operation_runner_) {
659 DCHECK(!sync_status_);
660 DCHECK(!timer_on_io_);
661 sync_status_.reset(new LocalFileSyncStatus);
662 timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
663 operation_runner_.reset(new SyncableFileOperationRunner(
664 kMaxConcurrentSyncableOperation,
665 sync_status_.get()));
666 sync_status_->AddObserver(this);
668 backend->set_sync_context(this);
669 DidInitialize(source_url, file_system_context,
670 SYNC_STATUS_OK);
673 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
674 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
675 FileSystemContext* file_system_context,
676 std::set<GURL>* origins_with_changes) {
677 DCHECK(file_system_context);
678 DCHECK(tracker_ptr);
679 DCHECK(origins_with_changes);
680 tracker_ptr->reset(new LocalFileChangeTracker(
681 file_system_context->partition_path(),
682 file_system_context->default_file_task_runner()));
683 const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
684 if (status != SYNC_STATUS_OK)
685 return status;
687 // Get all origins that have pending changes.
688 std::deque<FileSystemURL> urls;
689 (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
690 for (std::deque<FileSystemURL>::iterator iter = urls.begin();
691 iter != urls.end(); ++iter) {
692 origins_with_changes->insert(iter->origin());
695 // Creates snapshot directory.
696 base::CreateDirectory(local_base_path_.Append(kSnapshotDir));
698 return status;
701 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
702 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
703 const GURL& source_url,
704 FileSystemContext* file_system_context,
705 std::set<GURL>* origins_with_changes,
706 SyncStatusCode status) {
707 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
708 DCHECK(file_system_context);
709 DCHECK(origins_with_changes);
710 if (shutdown_on_io_)
711 status = SYNC_STATUS_ABORT;
712 if (status != SYNC_STATUS_OK) {
713 DidInitialize(source_url, file_system_context, status);
714 return;
717 SyncFileSystemBackend* backend =
718 SyncFileSystemBackend::GetBackend(file_system_context);
719 DCHECK(backend);
720 backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
722 origins_with_pending_changes_.insert(origins_with_changes->begin(),
723 origins_with_changes->end());
724 ScheduleNotifyChangesUpdatedOnIOThread();
726 InitializeFileSystemContextOnIOThread(source_url, file_system_context,
727 GURL(), std::string(),
728 base::PLATFORM_FILE_OK);
731 void LocalFileSyncContext::DidInitialize(
732 const GURL& source_url,
733 FileSystemContext* file_system_context,
734 SyncStatusCode status) {
735 if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
736 ui_task_runner_->PostTask(
737 FROM_HERE,
738 base::Bind(&LocalFileSyncContext::DidInitialize,
739 this, source_url,
740 make_scoped_refptr(file_system_context), status));
741 return;
743 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
744 DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
745 DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
747 SyncFileSystemBackend* backend =
748 SyncFileSystemBackend::GetBackend(file_system_context);
749 DCHECK(backend);
750 DCHECK(backend->change_tracker());
752 file_system_contexts_.insert(file_system_context);
754 StatusCallbackQueue& callback_queue =
755 pending_initialize_callbacks_[file_system_context];
756 for (StatusCallbackQueue::iterator iter = callback_queue.begin();
757 iter != callback_queue.end(); ++iter) {
758 ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
760 pending_initialize_callbacks_.erase(file_system_context);
763 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
764 FileSystemContext* file_system_context,
765 std::deque<FileSystemURL>* urls) {
766 DCHECK(file_system_context);
767 DCHECK(file_system_context->default_file_task_runner()->
768 RunsTasksOnCurrentThread());
769 SyncFileSystemBackend* backend =
770 SyncFileSystemBackend::GetBackend(file_system_context);
771 DCHECK(backend);
772 DCHECK(backend->change_tracker());
773 backend->change_tracker()->GetNextChangedURLs(
774 urls, kMaxURLsToFetchForLocalSync);
777 void LocalFileSyncContext::TryPrepareForLocalSync(
778 FileSystemContext* file_system_context,
779 std::deque<FileSystemURL>* urls,
780 const LocalFileSyncInfoCallback& callback) {
781 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
782 DCHECK(urls);
784 if (shutdown_on_ui_) {
785 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
786 webkit_blob::ScopedFile());
787 return;
790 if (urls->empty()) {
791 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(),
792 webkit_blob::ScopedFile());
793 return;
796 const FileSystemURL url = urls->front();
797 urls->pop_front();
798 std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>;
799 remaining->swap(*urls);
801 PrepareForSync(
802 file_system_context, url, SYNC_SNAPSHOT,
803 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
804 this, make_scoped_refptr(file_system_context),
805 base::Owned(remaining), callback));
808 void LocalFileSyncContext::DidTryPrepareForLocalSync(
809 FileSystemContext* file_system_context,
810 std::deque<FileSystemURL>* remaining_urls,
811 const LocalFileSyncInfoCallback& callback,
812 SyncStatusCode status,
813 const LocalFileSyncInfo& sync_file_info,
814 webkit_blob::ScopedFile snapshot) {
815 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
816 if (status != SYNC_STATUS_FILE_BUSY) {
817 callback.Run(status, sync_file_info, snapshot.Pass());
818 return;
820 // Recursively call TryPrepareForLocalSync with remaining_urls.
821 TryPrepareForLocalSync(file_system_context, remaining_urls, callback);
824 void LocalFileSyncContext::DidGetWritingStatusForSync(
825 FileSystemContext* file_system_context,
826 SyncStatusCode status,
827 const FileSystemURL& url,
828 SyncMode sync_mode,
829 const LocalFileSyncInfoCallback& callback) {
830 // This gets called on UI thread and relays the task on FILE thread.
831 DCHECK(file_system_context);
832 if (!file_system_context->default_file_task_runner()->
833 RunsTasksOnCurrentThread()) {
834 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
835 if (shutdown_on_ui_) {
836 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
837 webkit_blob::ScopedFile());
838 return;
840 file_system_context->default_file_task_runner()->PostTask(
841 FROM_HERE,
842 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
843 this, make_scoped_refptr(file_system_context),
844 status, url, sync_mode, callback));
845 return;
848 SyncFileSystemBackend* backend =
849 SyncFileSystemBackend::GetBackend(file_system_context);
850 DCHECK(backend);
851 DCHECK(backend->change_tracker());
852 FileChangeList changes;
853 backend->change_tracker()->GetChangesForURL(url, &changes);
855 base::FilePath platform_path;
856 base::PlatformFileInfo file_info;
857 FileSystemFileUtil* file_util =
858 file_system_context->sandbox_delegate()->sync_file_util();
859 DCHECK(file_util);
861 base::PlatformFileError file_error = file_util->GetFileInfo(
862 make_scoped_ptr(
863 new FileSystemOperationContext(file_system_context)).get(),
864 url,
865 &file_info,
866 &platform_path);
868 webkit_blob::ScopedFile snapshot;
869 if (file_error == base::PLATFORM_FILE_OK && sync_mode == SYNC_SNAPSHOT) {
870 base::FilePath snapshot_path;
871 base::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
872 &snapshot_path);
873 if (base::CopyFile(platform_path, snapshot_path)) {
874 platform_path = snapshot_path;
875 snapshot = webkit_blob::ScopedFile(
876 snapshot_path,
877 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
878 file_system_context->default_file_task_runner());
882 if (status == SYNC_STATUS_OK &&
883 file_error != base::PLATFORM_FILE_OK &&
884 file_error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
885 status = PlatformFileErrorToSyncStatusCode(file_error);
887 DCHECK(!file_info.is_symbolic_link);
889 SyncFileType file_type = SYNC_FILE_TYPE_FILE;
890 if (file_error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
891 file_type = SYNC_FILE_TYPE_UNKNOWN;
892 else if (file_info.is_directory)
893 file_type = SYNC_FILE_TYPE_DIRECTORY;
895 LocalFileSyncInfo sync_file_info;
896 sync_file_info.url = url;
897 sync_file_info.local_file_path = platform_path;
898 sync_file_info.metadata.file_type = file_type;
899 sync_file_info.metadata.size = file_info.size;
900 sync_file_info.metadata.last_modified = file_info.last_modified;
901 sync_file_info.changes = changes;
903 if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
904 if (!changes.empty()) {
905 // Now we create an empty mirror change record for URL (and we record
906 // changes to both mirror and original records during sync), so that
907 // we can reset to the mirror when the sync succeeds.
908 backend->change_tracker()->CreateFreshMirrorForURL(url);
911 // 'Unlock' the file for snapshot sync.
912 // (But keep it in writing status so that no other sync starts on
913 // the same URL)
914 io_task_runner_->PostTask(
915 FROM_HERE,
916 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
917 this, url, true /* for_snapshot_sync */));
920 ui_task_runner_->PostTask(FROM_HERE,
921 base::Bind(callback, status, sync_file_info,
922 base::Passed(&snapshot)));
925 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
926 const FileSystemURL& url,
927 bool for_snapshot_sync) {
928 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
929 if (shutdown_on_io_)
930 return;
931 sync_status()->EndSyncing(url);
933 if (for_snapshot_sync) {
934 // The caller will hold shared lock on this one.
935 sync_status()->StartWriting(url);
936 return;
939 // Since a sync has finished the number of changes must have been updated.
940 UpdateChangesForOrigin(url.origin());
943 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
944 const FileSystemURL& url) {
945 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
946 if (shutdown_on_io_)
947 return;
948 sync_status()->EndWriting(url);
950 // Since a sync has finished the number of changes must have been updated.
951 UpdateChangesForOrigin(url.origin());
954 void LocalFileSyncContext::DidApplyRemoteChange(
955 const FileSystemURL& url,
956 const SyncStatusCallback& callback_on_ui,
957 base::PlatformFileError file_error) {
958 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
959 root_delete_helper_.reset();
960 ui_task_runner_->PostTask(
961 FROM_HERE,
962 base::Bind(callback_on_ui,
963 PlatformFileErrorToSyncStatusCode(file_error)));
966 void LocalFileSyncContext::DidGetFileMetadata(
967 const SyncFileMetadataCallback& callback,
968 base::PlatformFileError file_error,
969 const base::PlatformFileInfo& file_info) {
970 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
971 SyncFileMetadata metadata;
972 if (file_error == base::PLATFORM_FILE_OK) {
973 metadata.file_type = file_info.is_directory ?
974 SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
975 metadata.size = file_info.size;
976 metadata.last_modified = file_info.last_modified;
978 ui_task_runner_->PostTask(
979 FROM_HERE,
980 base::Bind(callback,
981 PlatformFileErrorToSyncStatusCode(file_error),
982 metadata));
985 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
986 if (mock_notify_changes_duration_in_sec_ >= 0)
987 return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
988 return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
991 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
992 FileSystemContext* file_system_context,
993 const base::FilePath& local_path,
994 const FileSystemURL& dest_url,
995 const StatusCallback& callback,
996 base::PlatformFileError error) {
997 if (error != base::PLATFORM_FILE_OK) {
998 callback.Run(error);
999 return;
1002 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
1003 file_system_context, dest_url);
1004 file_system_context->operation_runner()->CopyInForeignFile(
1005 local_path, url_for_sync, callback);
1008 } // namespace sync_file_system