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