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