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