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