Home | History | Annotate | Download | only in local
      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.
      4 
      5 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
      6 
      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"
     29 
     30 using fileapi::FileSystemContext;
     31 using fileapi::FileSystemFileUtil;
     32 using fileapi::FileSystemOperation;
     33 using fileapi::FileSystemOperationContext;
     34 using fileapi::FileSystemURL;
     35 
     36 namespace sync_file_system {
     37 
     38 namespace {
     39 
     40 const int kMaxConcurrentSyncableOperation = 3;
     41 const int kNotifyChangesDurationInSec = 1;
     42 const int kMaxURLsToFetchForLocalSync = 5;
     43 
     44 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots");
     45 
     46 }  // namespace
     47 
     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());
     59 }
     60 
     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;
     71   }
     72 
     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;
     78 
     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()));
     93 }
     94 
     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));
    101 }
    102 
    103 void LocalFileSyncContext::GetFileForLocalSync(
    104     FileSystemContext* file_system_context,
    105     const LocalFileSyncInfoCallback& callback) {
    106   DCHECK(file_system_context);
    107   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    108 
    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));
    118 }
    119 
    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;
    135   }
    136 
    137   SyncFileSystemBackend* backend =
    138       SyncFileSystemBackend::GetBackend(file_system_context);
    139   DCHECK(backend);
    140   DCHECK(backend->change_tracker());
    141   backend->change_tracker()->ClearChangesForURL(url);
    142 
    143   // Call the completion callback on UI thread.
    144   ui_task_runner_->PostTask(FROM_HERE, done_callback);
    145 }
    146 
    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;
    162   }
    163 
    164   SyncFileSystemBackend* backend =
    165       SyncFileSystemBackend::GetBackend(file_system_context);
    166   DCHECK(backend);
    167   DCHECK(backend->change_tracker());
    168 
    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   }
    177 
    178   // We've been keeping it in writing mode, so clear the writing counter
    179   // to unblock sync activities.
    180   io_task_runner_->PostTask(
    181       FROM_HERE, base::Bind(
    182           &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread, this, url));
    183 
    184   // Call the completion callback on UI thread.
    185   ui_task_runner_->PostTask(FROM_HERE, done_callback);
    186 }
    187 
    188 void LocalFileSyncContext::FinalizeExclusiveSync(
    189     fileapi::FileSystemContext* file_system_context,
    190     const fileapi::FileSystemURL& url,
    191     bool clear_local_changes,
    192     const base::Closure& done_callback) {
    193   DCHECK(file_system_context);
    194   if (!url.is_valid()) {
    195     done_callback.Run();
    196     return;
    197   }
    198 
    199   if (clear_local_changes) {
    200     ClearChangesForURL(file_system_context, url,
    201                        base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync,
    202                                   this, make_scoped_refptr(file_system_context),
    203                                   url, false, done_callback));
    204     return;
    205   }
    206 
    207   io_task_runner_->PostTask(
    208       FROM_HERE,
    209       base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
    210                  this, url, false /* for_snapshot_sync */));
    211 
    212   done_callback.Run();
    213 }
    214 
    215 void LocalFileSyncContext::PrepareForSync(
    216     FileSystemContext* file_system_context,
    217     const FileSystemURL& url,
    218     SyncMode sync_mode,
    219     const LocalFileSyncInfoCallback& callback) {
    220   // This is initially called on UI thread and to be relayed to IO thread.
    221   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    222     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    223     io_task_runner_->PostTask(
    224         FROM_HERE,
    225         base::Bind(&LocalFileSyncContext::PrepareForSync, this,
    226                    make_scoped_refptr(file_system_context), url,
    227                    sync_mode, callback));
    228     return;
    229   }
    230   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    231   const bool syncable = sync_status()->IsSyncable(url);
    232   // Disable writing if it's ready to be synced.
    233   if (syncable)
    234     sync_status()->StartSyncing(url);
    235   ui_task_runner_->PostTask(
    236       FROM_HERE,
    237       base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
    238                  this, make_scoped_refptr(file_system_context),
    239                  syncable ? SYNC_STATUS_OK :
    240                             SYNC_STATUS_FILE_BUSY,
    241                  url, sync_mode, callback));
    242 }
    243 
    244 void LocalFileSyncContext::RegisterURLForWaitingSync(
    245     const FileSystemURL& url,
    246     const base::Closure& on_syncable_callback) {
    247   // This is initially called on UI thread and to be relayed to IO thread.
    248   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    249     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    250     io_task_runner_->PostTask(
    251         FROM_HERE,
    252         base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync,
    253                    this, url, on_syncable_callback));
    254     return;
    255   }
    256   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    257   if (shutdown_on_io_)
    258     return;
    259   if (sync_status()->IsSyncable(url)) {
    260     // No need to register; fire the callback now.
    261     ui_task_runner_->PostTask(FROM_HERE, on_syncable_callback);
    262     return;
    263   }
    264   url_waiting_sync_on_io_ = url;
    265   url_syncable_callback_ = on_syncable_callback;
    266 }
    267 
    268 void LocalFileSyncContext::ApplyRemoteChange(
    269     FileSystemContext* file_system_context,
    270     const FileChange& change,
    271     const base::FilePath& local_path,
    272     const FileSystemURL& url,
    273     const SyncStatusCallback& callback) {
    274   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    275     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    276     io_task_runner_->PostTask(
    277         FROM_HERE,
    278         base::Bind(&LocalFileSyncContext::ApplyRemoteChange, this,
    279                    make_scoped_refptr(file_system_context),
    280                    change, local_path, url, callback));
    281     return;
    282   }
    283   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    284   DCHECK(!sync_status()->IsWritable(url));
    285   DCHECK(!sync_status()->IsWriting(url));
    286 
    287   FileSystemOperation::StatusCallback operation_callback;
    288   switch (change.change()) {
    289     case FileChange::FILE_CHANGE_DELETE:
    290       HandleRemoteDelete(file_system_context, url, callback);
    291       return;
    292     case FileChange::FILE_CHANGE_ADD_OR_UPDATE:
    293       HandleRemoteAddOrUpdate(
    294           file_system_context, change, local_path, url, callback);
    295       return;
    296   }
    297   NOTREACHED();
    298   callback.Run(SYNC_STATUS_FAILED);
    299 }
    300 
    301 void LocalFileSyncContext::HandleRemoteDelete(
    302     FileSystemContext* file_system_context,
    303     const FileSystemURL& url,
    304     const SyncStatusCallback& callback) {
    305   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
    306       file_system_context, url);
    307 
    308   // Handle root directory case differently.
    309   if (fileapi::VirtualPath::IsRootPath(url.path())) {
    310     DCHECK(!root_delete_helper_);
    311     root_delete_helper_.reset(new RootDeleteHelper(
    312         file_system_context, sync_status(), url,
    313         base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
    314                    this, url, callback)));
    315     root_delete_helper_->Run();
    316     return;
    317   }
    318 
    319   file_system_context->operation_runner()->Remove(
    320       url_for_sync, true /* recursive */,
    321       base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
    322                  this, url, callback));
    323 }
    324 
    325 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
    326     FileSystemContext* file_system_context,
    327     const FileChange& change,
    328     const base::FilePath& local_path,
    329     const FileSystemURL& url,
    330     const SyncStatusCallback& callback) {
    331   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
    332       file_system_context, url);
    333 
    334   if (fileapi::VirtualPath::IsRootPath(url.path())) {
    335     DidApplyRemoteChange(url, callback, base::PLATFORM_FILE_OK);
    336     return;
    337   }
    338 
    339   file_system_context->operation_runner()->Remove(
    340       url_for_sync, true /* recursive */,
    341       base::Bind(
    342           &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate,
    343           this,
    344           make_scoped_refptr(file_system_context),
    345           change,
    346           local_path,
    347           url,
    348           callback));
    349 }
    350 
    351 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
    352     FileSystemContext* file_system_context,
    353     const FileChange& change,
    354     const base::FilePath& local_path,
    355     const FileSystemURL& url,
    356     const SyncStatusCallback& callback,
    357     base::PlatformFileError error) {
    358   // Remove() may fail if the target entry does not exist (which is ok),
    359   // so we ignore |error| here.
    360 
    361   if (shutdown_on_io_) {
    362     callback.Run(SYNC_FILE_ERROR_ABORT);
    363     return;
    364   }
    365 
    366   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    367   DCHECK(!sync_status()->IsWritable(url));
    368   DCHECK(!sync_status()->IsWriting(url));
    369 
    370   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
    371       file_system_context, url);
    372   FileSystemOperation::StatusCallback operation_callback = base::Bind(
    373       &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
    374 
    375   DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE, change.change());
    376   switch (change.file_type()) {
    377     case SYNC_FILE_TYPE_FILE: {
    378       DCHECK(!local_path.empty());
    379       base::FilePath dir_path = fileapi::VirtualPath::DirName(url.path());
    380       if (dir_path.empty() ||
    381           fileapi::VirtualPath::DirName(dir_path) == dir_path) {
    382         // Copying into the root directory.
    383         file_system_context->operation_runner()->CopyInForeignFile(
    384             local_path, url_for_sync, operation_callback);
    385       } else {
    386         FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
    387             url_for_sync.origin(),
    388             url_for_sync.mount_type(),
    389             fileapi::VirtualPath::DirName(url_for_sync.virtual_path()));
    390         file_system_context->operation_runner()->CreateDirectory(
    391             dir_url,
    392             false /* exclusive */,
    393             true /* recursive */,
    394             base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn,
    395                        this,
    396                        make_scoped_refptr(file_system_context),
    397                        local_path,
    398                        url,
    399                        operation_callback));
    400       }
    401       break;
    402     }
    403     case SYNC_FILE_TYPE_DIRECTORY:
    404       file_system_context->operation_runner()->CreateDirectory(
    405           url_for_sync, false /* exclusive */, true /* recursive */,
    406           operation_callback);
    407       break;
    408     case SYNC_FILE_TYPE_UNKNOWN:
    409       NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
    410   }
    411 }
    412 
    413 void LocalFileSyncContext::RecordFakeLocalChange(
    414     FileSystemContext* file_system_context,
    415     const FileSystemURL& url,
    416     const FileChange& change,
    417     const SyncStatusCallback& callback) {
    418   // This is called on UI thread and to be relayed to FILE thread.
    419   DCHECK(file_system_context);
    420   if (!file_system_context->default_file_task_runner()->
    421           RunsTasksOnCurrentThread()) {
    422     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    423     file_system_context->default_file_task_runner()->PostTask(
    424         FROM_HERE,
    425         base::Bind(&LocalFileSyncContext::RecordFakeLocalChange,
    426                    this, make_scoped_refptr(file_system_context),
    427                    url, change, callback));
    428     return;
    429   }
    430 
    431   SyncFileSystemBackend* backend =
    432       SyncFileSystemBackend::GetBackend(file_system_context);
    433   DCHECK(backend);
    434   DCHECK(backend->change_tracker());
    435   backend->change_tracker()->MarkDirtyOnDatabase(url);
    436   backend->change_tracker()->RecordChange(url, change);
    437 
    438   // Fire the callback on UI thread.
    439   ui_task_runner_->PostTask(FROM_HERE,
    440                             base::Bind(callback,
    441                                        SYNC_STATUS_OK));
    442 }
    443 
    444 void LocalFileSyncContext::GetFileMetadata(
    445     FileSystemContext* file_system_context,
    446     const FileSystemURL& url,
    447     const SyncFileMetadataCallback& callback) {
    448   // This is initially called on UI thread and to be relayed to IO thread.
    449   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    450     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    451     io_task_runner_->PostTask(
    452         FROM_HERE,
    453         base::Bind(&LocalFileSyncContext::GetFileMetadata, this,
    454                    make_scoped_refptr(file_system_context), url, callback));
    455     return;
    456   }
    457   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    458 
    459   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
    460       file_system_context, url);
    461   file_system_context->operation_runner()->GetMetadata(
    462       url_for_sync, base::Bind(&LocalFileSyncContext::DidGetFileMetadata,
    463                       this, callback));
    464 }
    465 
    466 void LocalFileSyncContext::HasPendingLocalChanges(
    467     FileSystemContext* file_system_context,
    468     const FileSystemURL& url,
    469     const HasPendingLocalChangeCallback& callback) {
    470   // This gets called on UI thread and relays the task on FILE thread.
    471   DCHECK(file_system_context);
    472   if (!file_system_context->default_file_task_runner()->
    473           RunsTasksOnCurrentThread()) {
    474     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    475     file_system_context->default_file_task_runner()->PostTask(
    476         FROM_HERE,
    477         base::Bind(&LocalFileSyncContext::HasPendingLocalChanges,
    478                    this, make_scoped_refptr(file_system_context),
    479                    url, callback));
    480     return;
    481   }
    482 
    483   SyncFileSystemBackend* backend =
    484       SyncFileSystemBackend::GetBackend(file_system_context);
    485   DCHECK(backend);
    486   DCHECK(backend->change_tracker());
    487   FileChangeList changes;
    488   backend->change_tracker()->GetChangesForURL(url, &changes);
    489 
    490   // Fire the callback on UI thread.
    491   ui_task_runner_->PostTask(FROM_HERE,
    492                             base::Bind(callback,
    493                                        SYNC_STATUS_OK,
    494                                        !changes.empty()));
    495 }
    496 
    497 void LocalFileSyncContext::AddOriginChangeObserver(
    498     LocalOriginChangeObserver* observer) {
    499   origin_change_observers_.AddObserver(observer);
    500 }
    501 
    502 void LocalFileSyncContext::RemoveOriginChangeObserver(
    503     LocalOriginChangeObserver* observer) {
    504   origin_change_observers_.RemoveObserver(observer);
    505 }
    506 
    507 base::WeakPtr<SyncableFileOperationRunner>
    508 LocalFileSyncContext::operation_runner() const {
    509   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    510   if (operation_runner_)
    511     return operation_runner_->AsWeakPtr();
    512   return base::WeakPtr<SyncableFileOperationRunner>();
    513 }
    514 
    515 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
    516   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    517   return sync_status_.get();
    518 }
    519 
    520 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
    521   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    522   if (shutdown_on_io_)
    523     return;
    524   origins_with_pending_changes_.insert(url.origin());
    525   ScheduleNotifyChangesUpdatedOnIOThread();
    526   if (url_syncable_callback_.is_null() ||
    527       sync_status()->IsWriting(url_waiting_sync_on_io_)) {
    528     return;
    529   }
    530   // TODO(kinuko): may want to check how many pending tasks we have.
    531   ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
    532   url_syncable_callback_.Reset();
    533 }
    534 
    535 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
    536   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    537   // Nothing to do for now.
    538 }
    539 
    540 LocalFileSyncContext::~LocalFileSyncContext() {
    541 }
    542 
    543 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
    544   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    545   if (shutdown_on_io_)
    546     return;
    547   if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
    548     NotifyAvailableChangesOnIOThread();
    549   } else if (!timer_on_io_->IsRunning()) {
    550     timer_on_io_->Start(
    551         FROM_HERE, NotifyChangesDuration(), this,
    552         &LocalFileSyncContext::NotifyAvailableChangesOnIOThread);
    553   }
    554 }
    555 
    556 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
    557   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    558   if (shutdown_on_io_)
    559     return;
    560   ui_task_runner_->PostTask(
    561       FROM_HERE,
    562       base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
    563                  this, origins_with_pending_changes_));
    564   last_notified_changes_ = base::Time::Now();
    565   origins_with_pending_changes_.clear();
    566 }
    567 
    568 void LocalFileSyncContext::NotifyAvailableChanges(
    569     const std::set<GURL>& origins) {
    570   FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
    571                     OnChangesAvailableInOrigins(origins));
    572 }
    573 
    574 void LocalFileSyncContext::ShutdownOnIOThread() {
    575   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    576   shutdown_on_io_ = true;
    577   operation_runner_.reset();
    578   root_delete_helper_.reset();
    579   sync_status_.reset();
    580   timer_on_io_.reset();
    581 }
    582 
    583 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
    584     const GURL& source_url,
    585     FileSystemContext* file_system_context,
    586     const GURL& /* root */,
    587     const std::string& /* name */,
    588     base::PlatformFileError error) {
    589   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    590   if (shutdown_on_io_)
    591     error = base::PLATFORM_FILE_ERROR_ABORT;
    592   if (error != base::PLATFORM_FILE_OK) {
    593     DidInitialize(source_url, file_system_context,
    594                   PlatformFileErrorToSyncStatusCode(error));
    595     return;
    596   }
    597   DCHECK(file_system_context);
    598   SyncFileSystemBackend* backend =
    599       SyncFileSystemBackend::GetBackend(file_system_context);
    600   DCHECK(backend);
    601   if (!backend->change_tracker()) {
    602     // Create and initialize LocalFileChangeTracker and call back this method
    603     // later again.
    604     std::set<GURL>* origins_with_changes = new std::set<GURL>;
    605     scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
    606         new scoped_ptr<LocalFileChangeTracker>);
    607     base::PostTaskAndReplyWithResult(
    608         file_system_context->default_file_task_runner(),
    609         FROM_HERE,
    610         base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
    611                    this, tracker_ptr,
    612                    make_scoped_refptr(file_system_context),
    613                    origins_with_changes),
    614         base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
    615                    this, base::Owned(tracker_ptr),
    616                    source_url,
    617                    make_scoped_refptr(file_system_context),
    618                    base::Owned(origins_with_changes)));
    619     return;
    620   }
    621   if (!operation_runner_) {
    622     DCHECK(!sync_status_);
    623     DCHECK(!timer_on_io_);
    624     sync_status_.reset(new LocalFileSyncStatus);
    625     timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
    626     operation_runner_.reset(new SyncableFileOperationRunner(
    627             kMaxConcurrentSyncableOperation,
    628             sync_status_.get()));
    629     sync_status_->AddObserver(this);
    630   }
    631   backend->set_sync_context(this);
    632   DidInitialize(source_url, file_system_context,
    633                 SYNC_STATUS_OK);
    634 }
    635 
    636 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
    637     scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
    638     FileSystemContext* file_system_context,
    639     std::set<GURL>* origins_with_changes) {
    640   DCHECK(file_system_context);
    641   DCHECK(tracker_ptr);
    642   DCHECK(origins_with_changes);
    643   tracker_ptr->reset(new LocalFileChangeTracker(
    644           file_system_context->partition_path(),
    645           file_system_context->default_file_task_runner()));
    646   const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
    647   if (status != SYNC_STATUS_OK)
    648     return status;
    649 
    650   // Get all origins that have pending changes.
    651   std::deque<FileSystemURL> urls;
    652   (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
    653   for (std::deque<FileSystemURL>::iterator iter = urls.begin();
    654        iter != urls.end(); ++iter) {
    655     origins_with_changes->insert(iter->origin());
    656   }
    657 
    658   // Creates snapshot directory.
    659   base::CreateDirectory(local_base_path_.Append(kSnapshotDir));
    660 
    661   return status;
    662 }
    663 
    664 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
    665     scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
    666     const GURL& source_url,
    667     FileSystemContext* file_system_context,
    668     std::set<GURL>* origins_with_changes,
    669     SyncStatusCode status) {
    670   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    671   DCHECK(file_system_context);
    672   DCHECK(origins_with_changes);
    673   if (shutdown_on_io_)
    674     status = SYNC_STATUS_ABORT;
    675   if (status != SYNC_STATUS_OK) {
    676     DidInitialize(source_url, file_system_context, status);
    677     return;
    678   }
    679 
    680   SyncFileSystemBackend* backend =
    681       SyncFileSystemBackend::GetBackend(file_system_context);
    682   DCHECK(backend);
    683   backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
    684 
    685   origins_with_pending_changes_.insert(origins_with_changes->begin(),
    686                                        origins_with_changes->end());
    687   ScheduleNotifyChangesUpdatedOnIOThread();
    688 
    689   InitializeFileSystemContextOnIOThread(source_url, file_system_context,
    690                                         GURL(), std::string(),
    691                                         base::PLATFORM_FILE_OK);
    692 }
    693 
    694 void LocalFileSyncContext::DidInitialize(
    695     const GURL& source_url,
    696     FileSystemContext* file_system_context,
    697     SyncStatusCode status) {
    698   if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
    699     ui_task_runner_->PostTask(
    700         FROM_HERE,
    701         base::Bind(&LocalFileSyncContext::DidInitialize,
    702                    this, source_url,
    703                    make_scoped_refptr(file_system_context), status));
    704     return;
    705   }
    706   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    707   DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
    708   DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
    709 
    710   SyncFileSystemBackend* backend =
    711       SyncFileSystemBackend::GetBackend(file_system_context);
    712   DCHECK(backend);
    713   DCHECK(backend->change_tracker());
    714 
    715   file_system_contexts_.insert(file_system_context);
    716 
    717   StatusCallbackQueue& callback_queue =
    718       pending_initialize_callbacks_[file_system_context];
    719   for (StatusCallbackQueue::iterator iter = callback_queue.begin();
    720        iter != callback_queue.end(); ++iter) {
    721     ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
    722   }
    723   pending_initialize_callbacks_.erase(file_system_context);
    724 }
    725 
    726 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
    727     FileSystemContext* file_system_context,
    728     std::deque<FileSystemURL>* urls) {
    729   DCHECK(file_system_context);
    730   DCHECK(file_system_context->default_file_task_runner()->
    731              RunsTasksOnCurrentThread());
    732   SyncFileSystemBackend* backend =
    733       SyncFileSystemBackend::GetBackend(file_system_context);
    734   DCHECK(backend);
    735   DCHECK(backend->change_tracker());
    736   backend->change_tracker()->GetNextChangedURLs(
    737       urls, kMaxURLsToFetchForLocalSync);
    738 }
    739 
    740 void LocalFileSyncContext::TryPrepareForLocalSync(
    741     FileSystemContext* file_system_context,
    742     std::deque<FileSystemURL>* urls,
    743     const LocalFileSyncInfoCallback& callback) {
    744   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    745   DCHECK(urls);
    746 
    747   if (shutdown_on_ui_) {
    748     callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
    749                  webkit_blob::ScopedFile());
    750     return;
    751   }
    752 
    753   if (urls->empty()) {
    754     callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(),
    755                  webkit_blob::ScopedFile());
    756     return;
    757   }
    758 
    759   const FileSystemURL url = urls->front();
    760   urls->pop_front();
    761   std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>;
    762   remaining->swap(*urls);
    763 
    764   PrepareForSync(
    765       file_system_context, url, SYNC_SNAPSHOT,
    766       base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
    767                  this, make_scoped_refptr(file_system_context),
    768                  base::Owned(remaining), callback));
    769 }
    770 
    771 void LocalFileSyncContext::DidTryPrepareForLocalSync(
    772     FileSystemContext* file_system_context,
    773     std::deque<FileSystemURL>* remaining_urls,
    774     const LocalFileSyncInfoCallback& callback,
    775     SyncStatusCode status,
    776     const LocalFileSyncInfo& sync_file_info,
    777     webkit_blob::ScopedFile snapshot) {
    778   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    779   if (status != SYNC_STATUS_FILE_BUSY) {
    780     callback.Run(status, sync_file_info, snapshot.Pass());
    781     return;
    782   }
    783   // Recursively call TryPrepareForLocalSync with remaining_urls.
    784   TryPrepareForLocalSync(file_system_context, remaining_urls, callback);
    785 }
    786 
    787 void LocalFileSyncContext::DidGetWritingStatusForSync(
    788     FileSystemContext* file_system_context,
    789     SyncStatusCode status,
    790     const FileSystemURL& url,
    791     SyncMode sync_mode,
    792     const LocalFileSyncInfoCallback& callback) {
    793   // This gets called on UI thread and relays the task on FILE thread.
    794   DCHECK(file_system_context);
    795   if (!file_system_context->default_file_task_runner()->
    796           RunsTasksOnCurrentThread()) {
    797     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    798     if (shutdown_on_ui_) {
    799       callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
    800                    webkit_blob::ScopedFile());
    801       return;
    802     }
    803     file_system_context->default_file_task_runner()->PostTask(
    804         FROM_HERE,
    805         base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
    806                    this, make_scoped_refptr(file_system_context),
    807                    status, url, sync_mode, callback));
    808     return;
    809   }
    810 
    811   SyncFileSystemBackend* backend =
    812       SyncFileSystemBackend::GetBackend(file_system_context);
    813   DCHECK(backend);
    814   DCHECK(backend->change_tracker());
    815   FileChangeList changes;
    816   backend->change_tracker()->GetChangesForURL(url, &changes);
    817 
    818   base::FilePath platform_path;
    819   base::PlatformFileInfo file_info;
    820   FileSystemFileUtil* file_util =
    821       file_system_context->sandbox_delegate()->sync_file_util();
    822   DCHECK(file_util);
    823 
    824   base::PlatformFileError file_error = file_util->GetFileInfo(
    825       make_scoped_ptr(
    826           new FileSystemOperationContext(file_system_context)).get(),
    827       url,
    828       &file_info,
    829       &platform_path);
    830 
    831   webkit_blob::ScopedFile snapshot;
    832   if (file_error == base::PLATFORM_FILE_OK && sync_mode == SYNC_SNAPSHOT) {
    833     base::FilePath snapshot_path;
    834     base::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
    835                                    &snapshot_path);
    836     if (base::CopyFile(platform_path, snapshot_path)) {
    837       platform_path = snapshot_path;
    838       snapshot = webkit_blob::ScopedFile(
    839           snapshot_path,
    840           webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
    841           file_system_context->default_file_task_runner());
    842     }
    843   }
    844 
    845   if (status == SYNC_STATUS_OK &&
    846       file_error != base::PLATFORM_FILE_OK &&
    847       file_error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
    848     status = PlatformFileErrorToSyncStatusCode(file_error);
    849 
    850   DCHECK(!file_info.is_symbolic_link);
    851 
    852   SyncFileType file_type = SYNC_FILE_TYPE_FILE;
    853   if (file_error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    854     file_type = SYNC_FILE_TYPE_UNKNOWN;
    855   else if (file_info.is_directory)
    856     file_type = SYNC_FILE_TYPE_DIRECTORY;
    857 
    858   LocalFileSyncInfo sync_file_info;
    859   sync_file_info.url = url;
    860   sync_file_info.local_file_path = platform_path;
    861   sync_file_info.metadata.file_type = file_type;
    862   sync_file_info.metadata.size = file_info.size;
    863   sync_file_info.metadata.last_modified = file_info.last_modified;
    864   sync_file_info.changes = changes;
    865 
    866   if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
    867     if (!changes.empty()) {
    868       // Now we create an empty mirror change record for URL (and we record
    869       // changes to both mirror and original records during sync), so that
    870       // we can reset to the mirror when the sync succeeds.
    871       backend->change_tracker()->CreateFreshMirrorForURL(url);
    872     }
    873 
    874     // 'Unlock' the file for snapshot sync.
    875     // (But keep it in writing status so that no other sync starts on
    876     // the same URL)
    877     io_task_runner_->PostTask(
    878         FROM_HERE,
    879         base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
    880                    this, url, true /* for_snapshot_sync */));
    881   }
    882 
    883   ui_task_runner_->PostTask(FROM_HERE,
    884                             base::Bind(callback, status, sync_file_info,
    885                                        base::Passed(&snapshot)));
    886 }
    887 
    888 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
    889     const FileSystemURL& url,
    890     bool for_snapshot_sync) {
    891   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    892   if (shutdown_on_io_)
    893     return;
    894   sync_status()->EndSyncing(url);
    895 
    896   if (for_snapshot_sync) {
    897     // The caller will hold shared lock on this one.
    898     sync_status()->StartWriting(url);
    899     return;
    900   }
    901 
    902   // Since a sync has finished the number of changes must have been updated.
    903   origins_with_pending_changes_.insert(url.origin());
    904   ScheduleNotifyChangesUpdatedOnIOThread();
    905 }
    906 
    907 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
    908     const FileSystemURL& url) {
    909   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    910   if (shutdown_on_io_)
    911     return;
    912   sync_status()->EndWriting(url);
    913 
    914   // Since a sync has finished the number of changes must have been updated.
    915   origins_with_pending_changes_.insert(url.origin());
    916   ScheduleNotifyChangesUpdatedOnIOThread();
    917 }
    918 
    919 void LocalFileSyncContext::DidApplyRemoteChange(
    920     const FileSystemURL& url,
    921     const SyncStatusCallback& callback_on_ui,
    922     base::PlatformFileError file_error) {
    923   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    924   root_delete_helper_.reset();
    925   ui_task_runner_->PostTask(
    926       FROM_HERE,
    927       base::Bind(callback_on_ui,
    928                  PlatformFileErrorToSyncStatusCode(file_error)));
    929 }
    930 
    931 void LocalFileSyncContext::DidGetFileMetadata(
    932     const SyncFileMetadataCallback& callback,
    933     base::PlatformFileError file_error,
    934     const base::PlatformFileInfo& file_info) {
    935   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    936   SyncFileMetadata metadata;
    937   if (file_error == base::PLATFORM_FILE_OK) {
    938     metadata.file_type = file_info.is_directory ?
    939         SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
    940     metadata.size = file_info.size;
    941     metadata.last_modified = file_info.last_modified;
    942   }
    943   ui_task_runner_->PostTask(
    944       FROM_HERE,
    945       base::Bind(callback,
    946                  PlatformFileErrorToSyncStatusCode(file_error),
    947                  metadata));
    948 }
    949 
    950 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
    951   if (mock_notify_changes_duration_in_sec_ >= 0)
    952     return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
    953   return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
    954 }
    955 
    956 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
    957     FileSystemContext* file_system_context,
    958     const base::FilePath& local_path,
    959     const FileSystemURL& dest_url,
    960     const StatusCallback& callback,
    961     base::PlatformFileError error) {
    962   if (error != base::PLATFORM_FILE_OK) {
    963     callback.Run(error);
    964     return;
    965   }
    966 
    967   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
    968       file_system_context, dest_url);
    969   file_system_context->operation_runner()->CopyInForeignFile(
    970       local_path, url_for_sync, callback);
    971 }
    972 
    973 }  // namespace sync_file_system
    974