| Index: chrome/browser/sync_file_system/local/local_file_sync_context.cc | 
| diff --git a/chrome/browser/sync_file_system/local/local_file_sync_context.cc b/chrome/browser/sync_file_system/local/local_file_sync_context.cc | 
| index c8b937a01a57f0ec9610df03dea90f165e626c51..b4f3a030a640e65dc6e27362bc1e1a8fb2c90268 100644 | 
| --- a/chrome/browser/sync_file_system/local/local_file_sync_context.cc | 
| +++ b/chrome/browser/sync_file_system/local/local_file_sync_context.cc | 
| @@ -5,6 +5,7 @@ | 
| #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" | 
|  | 
| #include "base/bind.h" | 
| +#include "base/file_util.h" | 
| #include "base/location.h" | 
| #include "base/platform_file.h" | 
| #include "base/single_thread_task_runner.h" | 
| @@ -21,6 +22,7 @@ | 
| #include "webkit/browser/fileapi/file_system_file_util.h" | 
| #include "webkit/browser/fileapi/file_system_operation_context.h" | 
| #include "webkit/browser/fileapi/file_system_operation_runner.h" | 
| +#include "webkit/common/blob/scoped_file.h" | 
| #include "webkit/common/fileapi/file_system_util.h" | 
|  | 
| using fileapi::FileSystemContext; | 
| @@ -37,12 +39,16 @@ const int kMaxConcurrentSyncableOperation = 3; | 
| const int kNotifyChangesDurationInSec = 1; | 
| const int kMaxURLsToFetchForLocalSync = 5; | 
|  | 
| +const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots"); | 
| + | 
| }  // namespace | 
|  | 
| LocalFileSyncContext::LocalFileSyncContext( | 
| +    const base::FilePath& base_path, | 
| base::SingleThreadTaskRunner* ui_task_runner, | 
| base::SingleThreadTaskRunner* io_task_runner) | 
| -    : ui_task_runner_(ui_task_runner), | 
| +    : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))), | 
| +      ui_task_runner_(ui_task_runner), | 
| io_task_runner_(io_task_runner), | 
| shutdown_on_ui_(false), | 
| mock_notify_changes_duration_in_sec_(-1) { | 
| @@ -159,6 +165,12 @@ void LocalFileSyncContext::CommitChangeStatusForURL( | 
| backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url); | 
| } | 
|  | 
| +  // We've been keeping it in writing mode, so clear the writing counter | 
| +  // to unblock sync activities. | 
| +  io_task_runner_->PostTask( | 
| +      FROM_HERE, base::Bind(&LocalFileSyncContext::EndWritingOnIOThread, | 
| +                            this, url)); | 
| + | 
| // Call the completion callback on UI thread. | 
| ui_task_runner_->PostTask(FROM_HERE, done_callback); | 
| } | 
| @@ -167,8 +179,8 @@ void LocalFileSyncContext::ClearSyncFlagForURL(const FileSystemURL& url) { | 
| // This is initially called on UI thread and to be relayed to IO thread. | 
| io_task_runner_->PostTask( | 
| FROM_HERE, | 
| -      base::Bind(&LocalFileSyncContext::EnableWritingOnIOThread, | 
| -                 this, url, true /* may_have_updates */)); | 
| +      base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread, | 
| +                 this, url, false /* keep_url_in_writing */)); | 
| } | 
|  | 
| void LocalFileSyncContext::PrepareForSync( | 
| @@ -550,6 +562,10 @@ SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread( | 
| iter != urls.end(); ++iter) { | 
| origins_with_changes->insert(iter->origin()); | 
| } | 
| + | 
| +  // Creates snapshot directory. | 
| +  file_util::CreateDirectory(local_base_path_.Append(kSnapshotDir)); | 
| + | 
| return status; | 
| } | 
|  | 
| @@ -633,13 +649,14 @@ void LocalFileSyncContext::TryPrepareForLocalSync( | 
| DCHECK(urls); | 
|  | 
| if (shutdown_on_ui_) { | 
| -    callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo()); | 
| +    callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(), | 
| +                 scoped_ptr<webkit_blob::ScopedFile>()); | 
| return; | 
| } | 
|  | 
| if (urls->empty()) { | 
| -    callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, | 
| -                 LocalFileSyncInfo()); | 
| +    callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(), | 
| +                 scoped_ptr<webkit_blob::ScopedFile>()); | 
| return; | 
| } | 
|  | 
| @@ -648,9 +665,8 @@ void LocalFileSyncContext::TryPrepareForLocalSync( | 
| std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>; | 
| remaining->swap(*urls); | 
|  | 
| -  // TODO(kinuko): Call PrepareForSync with SYNC_SNAPSHOT when it becomes ready. | 
| PrepareForSync( | 
| -      file_system_context, url, SYNC_EXCLUSIVE, | 
| +      file_system_context, url, SYNC_SNAPSHOT, | 
| base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync, | 
| this, make_scoped_refptr(file_system_context), | 
| base::Owned(remaining), callback)); | 
| @@ -661,10 +677,11 @@ void LocalFileSyncContext::DidTryPrepareForLocalSync( | 
| std::deque<FileSystemURL>* remaining_urls, | 
| const LocalFileSyncInfoCallback& callback, | 
| SyncStatusCode status, | 
| -    const LocalFileSyncInfo& sync_file_info) { | 
| +    const LocalFileSyncInfo& sync_file_info, | 
| +    scoped_ptr<webkit_blob::ScopedFile> snapshot) { | 
| DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); | 
| if (status != SYNC_STATUS_FILE_BUSY) { | 
| -    callback.Run(status, sync_file_info); | 
| +    callback.Run(status, sync_file_info, snapshot.Pass()); | 
| return; | 
| } | 
| // Recursively call TryPrepareForLocalSync with remaining_urls. | 
| @@ -683,7 +700,8 @@ void LocalFileSyncContext::DidGetWritingStatusForSync( | 
| RunsTasksOnCurrentThread()) { | 
| DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); | 
| if (shutdown_on_ui_) { | 
| -      callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo()); | 
| +      callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(), | 
| +                   scoped_ptr<webkit_blob::ScopedFile>()); | 
| return; | 
| } | 
| file_system_context->default_file_task_runner()->PostTask( | 
| @@ -713,9 +731,21 @@ void LocalFileSyncContext::DidGetWritingStatusForSync( | 
| url, | 
| &file_info, | 
| &platform_path); | 
| + | 
| +  scoped_ptr<webkit_blob::ScopedFile> snapshot; | 
| if (file_error == base::PLATFORM_FILE_OK && sync_mode == SYNC_SNAPSHOT) { | 
| -    // TODO(kinuko): creates a snapshot file. | 
| +    base::FilePath snapshot_path; | 
| +    file_util::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir), | 
| +                                        &snapshot_path); | 
| +    if (base::CopyFile(platform_path, snapshot_path)) { | 
| +      platform_path = snapshot_path; | 
| +      snapshot.reset(new webkit_blob::ScopedFile( | 
| +          snapshot_path, | 
| +          webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT, | 
| +          file_system_context->default_file_task_runner())); | 
| +    } | 
| } | 
| + | 
| if (status == SYNC_STATUS_OK && | 
| file_error != base::PLATFORM_FILE_OK && | 
| file_error != base::PLATFORM_FILE_ERROR_NOT_FOUND) | 
| @@ -729,7 +759,6 @@ void LocalFileSyncContext::DidGetWritingStatusForSync( | 
| else if (file_info.is_directory) | 
| file_type = SYNC_FILE_TYPE_DIRECTORY; | 
|  | 
| -  // TODO(kinuko): returns the snapshot file path if snapshot is available. | 
| LocalFileSyncInfo sync_file_info; | 
| sync_file_info.url = url; | 
| sync_file_info.local_file_path = platform_path; | 
| @@ -747,21 +776,24 @@ void LocalFileSyncContext::DidGetWritingStatusForSync( | 
| } | 
|  | 
| // 'Unlock' the file if sync_mode is not SYNC_EXCLUSIVE. | 
| +    // (But keep it in writing status so that no other sync starts on | 
| +    // the same URL) | 
| if (sync_mode != SYNC_EXCLUSIVE) { | 
| io_task_runner_->PostTask( | 
| FROM_HERE, | 
| -          base::Bind(&LocalFileSyncContext::EnableWritingOnIOThread, | 
| -                    this, url, false /* may_have_updates */)); | 
| +          base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread, | 
| +                     this, url, true /* keep_url_in_writing */)); | 
| } | 
| } | 
|  | 
| ui_task_runner_->PostTask(FROM_HERE, | 
| -                            base::Bind(callback, status, sync_file_info)); | 
| +                            base::Bind(callback, status, sync_file_info, | 
| +                                       base::Passed(&snapshot))); | 
| } | 
|  | 
| -void LocalFileSyncContext::EnableWritingOnIOThread( | 
| +void LocalFileSyncContext::ClearSyncFlagOnIOThread( | 
| const FileSystemURL& url, | 
| -    bool may_have_updates) { | 
| +    bool keep_url_in_writing) { | 
| DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); | 
| if (!sync_status()) { | 
| // The service might have been shut down. | 
| @@ -769,14 +801,27 @@ void LocalFileSyncContext::EnableWritingOnIOThread( | 
| } | 
| sync_status()->EndSyncing(url); | 
|  | 
| -  if (!may_have_updates) | 
| +  if (keep_url_in_writing) { | 
| +    // The caller will hold shared lock on this one. | 
| +    sync_status()->StartWriting(url); | 
| return; | 
| +  } | 
|  | 
| // Since a sync has finished the number of changes must have been updated. | 
| origins_with_pending_changes_.insert(url.origin()); | 
| ScheduleNotifyChangesUpdatedOnIOThread(); | 
| } | 
|  | 
| +void LocalFileSyncContext::EndWritingOnIOThread( | 
| +    const FileSystemURL& url) { | 
| +  DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); | 
| +  if (!sync_status()) { | 
| +    // The service might have been shut down. | 
| +    return; | 
| +  } | 
| +  sync_status()->EndWriting(url); | 
| +} | 
| + | 
| void LocalFileSyncContext::DidApplyRemoteChange( | 
| const FileSystemURL& url, | 
| const SyncStatusCallback& callback_on_ui, | 
| @@ -786,7 +831,7 @@ void LocalFileSyncContext::DidApplyRemoteChange( | 
| FROM_HERE, | 
| base::Bind(callback_on_ui, | 
| PlatformFileErrorToSyncStatusCode(file_error))); | 
| -  EnableWritingOnIOThread(url, true /* may_have_updates */); | 
| +  ClearSyncFlagOnIOThread(url, false /* keep_url_in_writing */); | 
| } | 
|  | 
| void LocalFileSyncContext::DidGetFileMetadata( | 
|  |