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