| 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/fileapi/file_system_context.h" | |
| 14 #include "webkit/fileapi/file_system_file_util.h" | |
| 15 #include "webkit/fileapi/file_system_operation_context.h" | |
| 16 #include "webkit/fileapi/file_system_task_runners.h" | |
| 17 #include "webkit/fileapi/file_system_util.h" | |
| 18 #include "webkit/fileapi/local_file_system_operation.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 |