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