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