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