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