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