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_service.h" 6 7 #include "base/stl_util.h" 8 #include "chrome/browser/extensions/extension_service.h" 9 #include "chrome/browser/extensions/extension_system.h" 10 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/sync_file_system/file_change.h" 12 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 13 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" 14 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 15 #include "chrome/browser/sync_file_system/local_change_processor.h" 16 #include "chrome/browser/sync_file_system/logger.h" 17 #include "chrome/browser/sync_file_system/sync_file_metadata.h" 18 #include "content/public/browser/browser_context.h" 19 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/site_instance.h" 21 #include "content/public/browser/storage_partition.h" 22 #include "url/gurl.h" 23 #include "webkit/browser/fileapi/file_system_context.h" 24 #include "webkit/browser/fileapi/file_system_url.h" 25 #include "webkit/common/blob/scoped_file.h" 26 27 using content::BrowserThread; 28 using fileapi::FileSystemURL; 29 30 namespace sync_file_system { 31 32 namespace { 33 34 void PrepareForProcessRemoteChangeCallbackAdapter( 35 const RemoteChangeProcessor::PrepareChangeCallback& callback, 36 SyncStatusCode status, 37 const LocalFileSyncInfo& sync_file_info, 38 webkit_blob::ScopedFile snapshot) { 39 callback.Run(status, sync_file_info.metadata, sync_file_info.changes); 40 } 41 42 } // namespace 43 44 LocalFileSyncService::OriginChangeMap::OriginChangeMap() 45 : next_(change_count_map_.end()) {} 46 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {} 47 48 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) { 49 DCHECK(origin); 50 if (change_count_map_.empty()) 51 return false; 52 Map::iterator begin = next_; 53 do { 54 if (next_ == change_count_map_.end()) 55 next_ = change_count_map_.begin(); 56 DCHECK_NE(0, next_->second); 57 *origin = next_++->first; 58 if (!ContainsKey(disabled_origins_, *origin)) 59 return true; 60 } while (next_ != begin); 61 return false; 62 } 63 64 int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const { 65 int64 num_changes = 0; 66 for (Map::const_iterator iter = change_count_map_.begin(); 67 iter != change_count_map_.end(); ++iter) { 68 if (ContainsKey(disabled_origins_, iter->first)) 69 continue; 70 num_changes += iter->second; 71 } 72 return num_changes; 73 } 74 75 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount( 76 const GURL& origin, int64 changes) { 77 if (changes != 0) { 78 change_count_map_[origin] = changes; 79 return; 80 } 81 Map::iterator found = change_count_map_.find(origin); 82 if (found != change_count_map_.end()) { 83 if (next_ == found) 84 ++next_; 85 change_count_map_.erase(found); 86 } 87 } 88 89 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled( 90 const GURL& origin, bool enabled) { 91 if (enabled) 92 disabled_origins_.erase(origin); 93 else 94 disabled_origins_.insert(origin); 95 } 96 97 // LocalFileSyncService ------------------------------------------------------- 98 99 LocalFileSyncService::LocalFileSyncService(Profile* profile) 100 : profile_(profile), 101 sync_context_(new LocalFileSyncContext( 102 profile_->GetPath(), 103 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(), 104 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO) 105 .get())), 106 local_change_processor_(NULL) { 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 108 sync_context_->AddOriginChangeObserver(this); 109 } 110 111 LocalFileSyncService::~LocalFileSyncService() { 112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 113 } 114 115 void LocalFileSyncService::Shutdown() { 116 sync_context_->RemoveOriginChangeObserver(this); 117 sync_context_->ShutdownOnUIThread(); 118 profile_ = NULL; 119 } 120 121 void LocalFileSyncService::MaybeInitializeFileSystemContext( 122 const GURL& app_origin, 123 fileapi::FileSystemContext* file_system_context, 124 const SyncStatusCallback& callback) { 125 sync_context_->MaybeInitializeFileSystemContext( 126 app_origin, file_system_context, 127 base::Bind(&LocalFileSyncService::DidInitializeFileSystemContext, 128 AsWeakPtr(), app_origin, 129 make_scoped_refptr(file_system_context), callback)); 130 } 131 132 void LocalFileSyncService::AddChangeObserver(Observer* observer) { 133 change_observers_.AddObserver(observer); 134 } 135 136 void LocalFileSyncService::RegisterURLForWaitingSync( 137 const FileSystemURL& url, 138 const base::Closure& on_syncable_callback) { 139 sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback); 140 } 141 142 void LocalFileSyncService::ProcessLocalChange( 143 const SyncFileCallback& callback) { 144 // Pick an origin to process next. 145 GURL origin; 146 if (!origin_change_map_.NextOriginToProcess(&origin)) { 147 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL()); 148 return; 149 } 150 DCHECK(local_sync_callback_.is_null()); 151 DCHECK(!origin.is_empty()); 152 DCHECK(ContainsKey(origin_to_contexts_, origin)); 153 154 DVLOG(1) << "Starting ProcessLocalChange"; 155 156 local_sync_callback_ = callback; 157 158 sync_context_->GetFileForLocalSync( 159 origin_to_contexts_[origin], 160 base::Bind(&LocalFileSyncService::DidGetFileForLocalSync, 161 AsWeakPtr())); 162 } 163 164 void LocalFileSyncService::SetLocalChangeProcessor( 165 LocalChangeProcessor* local_change_processor) { 166 local_change_processor_ = local_change_processor; 167 } 168 169 void LocalFileSyncService::SetLocalChangeProcessorCallback( 170 const GetLocalChangeProcessorCallback& get_local_change_processor) { 171 get_local_change_processor_ = get_local_change_processor; 172 } 173 174 void LocalFileSyncService::HasPendingLocalChanges( 175 const FileSystemURL& url, 176 const HasPendingLocalChangeCallback& callback) { 177 if (!ContainsKey(origin_to_contexts_, url.origin())) { 178 base::MessageLoopProxy::current()->PostTask( 179 FROM_HERE, 180 base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false)); 181 return; 182 } 183 sync_context_->HasPendingLocalChanges( 184 origin_to_contexts_[url.origin()], url, callback); 185 } 186 187 void LocalFileSyncService::GetLocalFileMetadata( 188 const FileSystemURL& url, const SyncFileMetadataCallback& callback) { 189 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 190 sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()], 191 url, callback); 192 } 193 194 void LocalFileSyncService::PrepareForProcessRemoteChange( 195 const FileSystemURL& url, 196 const PrepareChangeCallback& callback) { 197 DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString(); 198 199 if (!ContainsKey(origin_to_contexts_, url.origin())) { 200 // This could happen if a remote sync is triggered for the app that hasn't 201 // been initialized in this service. 202 DCHECK(profile_); 203 // The given url.origin() must be for valid installed app. 204 ExtensionService* extension_service = 205 extensions::ExtensionSystem::Get(profile_)->extension_service(); 206 const extensions::Extension* extension = extension_service->GetInstalledApp( 207 url.origin()); 208 if (!extension) { 209 util::Log( 210 logging::LOG_WARNING, 211 FROM_HERE, 212 "PrepareForProcessRemoteChange called for non-existing origin: %s", 213 url.origin().spec().c_str()); 214 215 // The extension has been uninstalled and this method is called 216 // before the remote changes for the origin are removed. 217 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, 218 SyncFileMetadata(), FileChangeList()); 219 return; 220 } 221 GURL site_url = extension_service->GetSiteForExtensionId(extension->id()); 222 DCHECK(!site_url.is_empty()); 223 scoped_refptr<fileapi::FileSystemContext> file_system_context = 224 content::BrowserContext::GetStoragePartitionForSite( 225 profile_, site_url)->GetFileSystemContext(); 226 MaybeInitializeFileSystemContext( 227 url.origin(), 228 file_system_context.get(), 229 base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync, 230 AsWeakPtr(), 231 url, 232 file_system_context, 233 callback)); 234 return; 235 } 236 237 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 238 sync_context_->PrepareForSync( 239 origin_to_contexts_[url.origin()], url, 240 LocalFileSyncContext::SYNC_EXCLUSIVE, 241 base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback)); 242 } 243 244 void LocalFileSyncService::ApplyRemoteChange( 245 const FileChange& change, 246 const base::FilePath& local_path, 247 const FileSystemURL& url, 248 const SyncStatusCallback& callback) { 249 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 250 util::Log(logging::LOG_VERBOSE, FROM_HERE, 251 "[Remote->Local] ApplyRemoteChange: %s on %s", 252 change.DebugString().c_str(), 253 url.DebugString().c_str()); 254 255 sync_context_->ApplyRemoteChange( 256 origin_to_contexts_[url.origin()], 257 change, local_path, url, 258 base::Bind(&LocalFileSyncService::DidApplyRemoteChange, AsWeakPtr(), 259 callback)); 260 } 261 262 void LocalFileSyncService::FinalizeRemoteSync( 263 const FileSystemURL& url, 264 bool clear_local_changes, 265 const base::Closure& completion_callback) { 266 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 267 sync_context_->FinalizeExclusiveSync( 268 origin_to_contexts_[url.origin()], 269 url, clear_local_changes, completion_callback); 270 } 271 272 void LocalFileSyncService::RecordFakeLocalChange( 273 const FileSystemURL& url, 274 const FileChange& change, 275 const SyncStatusCallback& callback) { 276 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 277 sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()], 278 url, change, callback); 279 } 280 281 void LocalFileSyncService::OnChangesAvailableInOrigins( 282 const std::set<GURL>& origins) { 283 bool need_notification = false; 284 for (std::set<GURL>::const_iterator iter = origins.begin(); 285 iter != origins.end(); ++iter) { 286 const GURL& origin = *iter; 287 if (!ContainsKey(origin_to_contexts_, origin)) { 288 // This could happen if this is called for apps/origins that haven't 289 // been initialized yet, or for apps/origins that are disabled. 290 // (Local change tracker could call this for uninitialized origins 291 // while it's reading dirty files from the database in the 292 // initialization phase.) 293 pending_origins_with_changes_.insert(origin); 294 continue; 295 } 296 need_notification = true; 297 SyncFileSystemBackend* backend = 298 SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]); 299 DCHECK(backend); 300 DCHECK(backend->change_tracker()); 301 origin_change_map_.SetOriginChangeCount( 302 origin, backend->change_tracker()->num_changes()); 303 } 304 if (!need_notification) 305 return; 306 int64 num_changes = origin_change_map_.GetTotalChangeCount(); 307 FOR_EACH_OBSERVER(Observer, change_observers_, 308 OnLocalChangeAvailable(num_changes)); 309 } 310 311 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) { 312 if (!ContainsKey(origin_to_contexts_, origin)) 313 return; 314 origin_change_map_.SetOriginEnabled(origin, enabled); 315 } 316 317 void LocalFileSyncService::DidInitializeFileSystemContext( 318 const GURL& app_origin, 319 fileapi::FileSystemContext* file_system_context, 320 const SyncStatusCallback& callback, 321 SyncStatusCode status) { 322 if (status != SYNC_STATUS_OK) { 323 callback.Run(status); 324 return; 325 } 326 DCHECK(file_system_context); 327 origin_to_contexts_[app_origin] = file_system_context; 328 329 if (pending_origins_with_changes_.find(app_origin) != 330 pending_origins_with_changes_.end()) { 331 // We have remaining changes for the origin. 332 pending_origins_with_changes_.erase(app_origin); 333 SyncFileSystemBackend* backend = 334 SyncFileSystemBackend::GetBackend(file_system_context); 335 DCHECK(backend); 336 DCHECK(backend->change_tracker()); 337 origin_change_map_.SetOriginChangeCount( 338 app_origin, backend->change_tracker()->num_changes()); 339 int64 num_changes = origin_change_map_.GetTotalChangeCount(); 340 FOR_EACH_OBSERVER(Observer, change_observers_, 341 OnLocalChangeAvailable(num_changes)); 342 } 343 callback.Run(status); 344 } 345 346 void LocalFileSyncService::DidInitializeForRemoteSync( 347 const FileSystemURL& url, 348 fileapi::FileSystemContext* file_system_context, 349 const PrepareChangeCallback& callback, 350 SyncStatusCode status) { 351 if (status != SYNC_STATUS_OK) { 352 DVLOG(1) << "FileSystemContext initialization failed for remote sync:" 353 << url.DebugString() << " status=" << status 354 << " (" << SyncStatusCodeToString(status) << ")"; 355 callback.Run(status, SyncFileMetadata(), FileChangeList()); 356 return; 357 } 358 origin_to_contexts_[url.origin()] = file_system_context; 359 PrepareForProcessRemoteChange(url, callback); 360 } 361 362 void LocalFileSyncService::RunLocalSyncCallback( 363 SyncStatusCode status, 364 const FileSystemURL& url) { 365 DVLOG(1) << "Local sync is finished with: " << status 366 << " on " << url.DebugString(); 367 DCHECK(!local_sync_callback_.is_null()); 368 SyncFileCallback callback = local_sync_callback_; 369 local_sync_callback_.Reset(); 370 callback.Run(status, url); 371 } 372 373 void LocalFileSyncService::DidApplyRemoteChange( 374 const SyncStatusCallback& callback, 375 SyncStatusCode status) { 376 util::Log(logging::LOG_VERBOSE, FROM_HERE, 377 "[Remote->Local] ApplyRemoteChange finished --> %s", 378 SyncStatusCodeToString(status)); 379 callback.Run(status); 380 } 381 382 void LocalFileSyncService::DidGetFileForLocalSync( 383 SyncStatusCode status, 384 const LocalFileSyncInfo& sync_file_info, 385 webkit_blob::ScopedFile snapshot) { 386 DCHECK(!local_sync_callback_.is_null()); 387 if (status != SYNC_STATUS_OK) { 388 RunLocalSyncCallback(status, sync_file_info.url); 389 return; 390 } 391 if (sync_file_info.changes.empty()) { 392 // There's a slight chance this could happen. 393 SyncFileCallback callback = local_sync_callback_; 394 local_sync_callback_.Reset(); 395 ProcessLocalChange(callback); 396 return; 397 } 398 399 FileChange next_change = sync_file_info.changes.front(); 400 DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString() 401 << " change:" << next_change.DebugString(); 402 403 GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange( 404 next_change, 405 sync_file_info.local_file_path, 406 sync_file_info.metadata, 407 sync_file_info.url, 408 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 409 AsWeakPtr(), base::Passed(&snapshot), sync_file_info, 410 next_change, sync_file_info.changes.PopAndGetNewList())); 411 } 412 413 void LocalFileSyncService::ProcessNextChangeForURL( 414 webkit_blob::ScopedFile snapshot, 415 const LocalFileSyncInfo& sync_file_info, 416 const FileChange& processed_change, 417 const FileChangeList& changes, 418 SyncStatusCode status) { 419 DVLOG(1) << "Processed one local change: " 420 << sync_file_info.url.DebugString() 421 << " change:" << processed_change.DebugString() 422 << " status:" << status; 423 424 if (status == SYNC_STATUS_RETRY) { 425 GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange( 426 processed_change, 427 sync_file_info.local_file_path, 428 sync_file_info.metadata, 429 sync_file_info.url, 430 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 431 AsWeakPtr(), base::Passed(&snapshot), 432 sync_file_info, processed_change, changes)); 433 return; 434 } 435 436 if (status == SYNC_FILE_ERROR_NOT_FOUND && 437 processed_change.change() == FileChange::FILE_CHANGE_DELETE) { 438 // This must be ok (and could happen). 439 status = SYNC_STATUS_OK; 440 } 441 442 const FileSystemURL& url = sync_file_info.url; 443 if (status != SYNC_STATUS_OK || changes.empty()) { 444 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 445 sync_context_->FinalizeSnapshotSync( 446 origin_to_contexts_[url.origin()], url, status, 447 base::Bind(&LocalFileSyncService::RunLocalSyncCallback, 448 AsWeakPtr(), status, url)); 449 return; 450 } 451 452 FileChange next_change = changes.front(); 453 GetLocalChangeProcessor(url)->ApplyLocalChange( 454 changes.front(), 455 sync_file_info.local_file_path, 456 sync_file_info.metadata, 457 url, 458 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 459 AsWeakPtr(), base::Passed(&snapshot), sync_file_info, 460 next_change, changes.PopAndGetNewList())); 461 } 462 463 LocalChangeProcessor* LocalFileSyncService::GetLocalChangeProcessor( 464 const FileSystemURL& url) { 465 if (!get_local_change_processor_.is_null()) 466 return get_local_change_processor_.Run(url.origin()); 467 DCHECK(local_change_processor_); 468 return local_change_processor_; 469 } 470 471 } // namespace sync_file_system 472