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