local_file_sync_service.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 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 99LocalFileSyncService::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 111LocalFileSyncService::~LocalFileSyncService() { 112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 113} 114 115void LocalFileSyncService::Shutdown() { 116 sync_context_->RemoveOriginChangeObserver(this); 117 sync_context_->ShutdownOnUIThread(); 118 profile_ = NULL; 119} 120 121void 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 132void LocalFileSyncService::AddChangeObserver(Observer* observer) { 133 change_observers_.AddObserver(observer); 134} 135 136void LocalFileSyncService::RegisterURLForWaitingSync( 137 const FileSystemURL& url, 138 const base::Closure& on_syncable_callback) { 139 sync_context_->RegisterURLForWaitingSync(url, on_syncable_callback); 140} 141 142void LocalFileSyncService::ProcessLocalChange( 143 const SyncFileCallback& callback) { 144 DCHECK(local_change_processor_); 145 // Pick an origin to process next. 146 GURL origin; 147 if (!origin_change_map_.NextOriginToProcess(&origin)) { 148 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL()); 149 return; 150 } 151 DCHECK(local_sync_callback_.is_null()); 152 DCHECK(!origin.is_empty()); 153 DCHECK(ContainsKey(origin_to_contexts_, origin)); 154 155 DVLOG(1) << "Starting ProcessLocalChange"; 156 157 local_sync_callback_ = callback; 158 159 sync_context_->GetFileForLocalSync( 160 origin_to_contexts_[origin], 161 base::Bind(&LocalFileSyncService::DidGetFileForLocalSync, 162 AsWeakPtr())); 163} 164 165void LocalFileSyncService::SetLocalChangeProcessor( 166 LocalChangeProcessor* processor) { 167 local_change_processor_ = processor; 168} 169 170void LocalFileSyncService::HasPendingLocalChanges( 171 const FileSystemURL& url, 172 const HasPendingLocalChangeCallback& callback) { 173 if (!ContainsKey(origin_to_contexts_, url.origin())) { 174 base::MessageLoopProxy::current()->PostTask( 175 FROM_HERE, 176 base::Bind(callback, SYNC_FILE_ERROR_INVALID_URL, false)); 177 return; 178 } 179 sync_context_->HasPendingLocalChanges( 180 origin_to_contexts_[url.origin()], url, callback); 181} 182 183void LocalFileSyncService::GetLocalFileMetadata( 184 const FileSystemURL& url, const SyncFileMetadataCallback& callback) { 185 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 186 sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()], 187 url, callback); 188} 189 190void LocalFileSyncService::PrepareForProcessRemoteChange( 191 const FileSystemURL& url, 192 const PrepareChangeCallback& callback) { 193 DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString(); 194 195 if (!ContainsKey(origin_to_contexts_, url.origin())) { 196 // This could happen if a remote sync is triggered for the app that hasn't 197 // been initialized in this service. 198 DCHECK(profile_); 199 // The given url.origin() must be for valid installed app. 200 ExtensionService* extension_service = 201 extensions::ExtensionSystem::Get(profile_)->extension_service(); 202 const extensions::Extension* extension = extension_service->GetInstalledApp( 203 url.origin()); 204 if (!extension) { 205 util::Log( 206 logging::LOG_WARNING, 207 FROM_HERE, 208 "PrepareForProcessRemoteChange called for non-existing origin: %s", 209 url.origin().spec().c_str()); 210 211 // The extension has been uninstalled and this method is called 212 // before the remote changes for the origin are removed. 213 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, 214 SyncFileMetadata(), FileChangeList()); 215 return; 216 } 217 GURL site_url = extension_service->GetSiteForExtensionId(extension->id()); 218 DCHECK(!site_url.is_empty()); 219 scoped_refptr<fileapi::FileSystemContext> file_system_context = 220 content::BrowserContext::GetStoragePartitionForSite( 221 profile_, site_url)->GetFileSystemContext(); 222 MaybeInitializeFileSystemContext( 223 url.origin(), 224 file_system_context.get(), 225 base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync, 226 AsWeakPtr(), 227 url, 228 file_system_context, 229 callback)); 230 return; 231 } 232 233 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 234 sync_context_->PrepareForSync( 235 origin_to_contexts_[url.origin()], url, 236 LocalFileSyncContext::SYNC_EXCLUSIVE, 237 base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback)); 238} 239 240void LocalFileSyncService::ApplyRemoteChange( 241 const FileChange& change, 242 const base::FilePath& local_path, 243 const FileSystemURL& url, 244 const SyncStatusCallback& callback) { 245 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 246 sync_context_->ApplyRemoteChange( 247 origin_to_contexts_[url.origin()], 248 change, local_path, url, callback); 249} 250 251void LocalFileSyncService::FinalizeRemoteSync( 252 const FileSystemURL& url, 253 bool clear_local_changes, 254 const base::Closure& completion_callback) { 255 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 256 sync_context_->FinalizeExclusiveSync( 257 origin_to_contexts_[url.origin()], 258 url, clear_local_changes, completion_callback); 259} 260 261void LocalFileSyncService::RecordFakeLocalChange( 262 const FileSystemURL& url, 263 const FileChange& change, 264 const SyncStatusCallback& callback) { 265 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 266 sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()], 267 url, change, callback); 268} 269 270void LocalFileSyncService::OnChangesAvailableInOrigins( 271 const std::set<GURL>& origins) { 272 bool need_notification = false; 273 for (std::set<GURL>::const_iterator iter = origins.begin(); 274 iter != origins.end(); ++iter) { 275 const GURL& origin = *iter; 276 if (!ContainsKey(origin_to_contexts_, origin)) { 277 // This could happen if this is called for apps/origins that haven't 278 // been initialized yet, or for apps/origins that are disabled. 279 // (Local change tracker could call this for uninitialized origins 280 // while it's reading dirty files from the database in the 281 // initialization phase.) 282 pending_origins_with_changes_.insert(origin); 283 continue; 284 } 285 need_notification = true; 286 SyncFileSystemBackend* backend = 287 SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]); 288 DCHECK(backend); 289 DCHECK(backend->change_tracker()); 290 origin_change_map_.SetOriginChangeCount( 291 origin, backend->change_tracker()->num_changes()); 292 } 293 if (!need_notification) 294 return; 295 int64 num_changes = origin_change_map_.GetTotalChangeCount(); 296 FOR_EACH_OBSERVER(Observer, change_observers_, 297 OnLocalChangeAvailable(num_changes)); 298} 299 300void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) { 301 if (!ContainsKey(origin_to_contexts_, origin)) 302 return; 303 origin_change_map_.SetOriginEnabled(origin, enabled); 304} 305 306void LocalFileSyncService::DidInitializeFileSystemContext( 307 const GURL& app_origin, 308 fileapi::FileSystemContext* file_system_context, 309 const SyncStatusCallback& callback, 310 SyncStatusCode status) { 311 if (status != SYNC_STATUS_OK) { 312 callback.Run(status); 313 return; 314 } 315 DCHECK(file_system_context); 316 origin_to_contexts_[app_origin] = file_system_context; 317 318 if (pending_origins_with_changes_.find(app_origin) != 319 pending_origins_with_changes_.end()) { 320 // We have remaining changes for the origin. 321 pending_origins_with_changes_.erase(app_origin); 322 SyncFileSystemBackend* backend = 323 SyncFileSystemBackend::GetBackend(file_system_context); 324 DCHECK(backend); 325 DCHECK(backend->change_tracker()); 326 origin_change_map_.SetOriginChangeCount( 327 app_origin, backend->change_tracker()->num_changes()); 328 int64 num_changes = origin_change_map_.GetTotalChangeCount(); 329 FOR_EACH_OBSERVER(Observer, change_observers_, 330 OnLocalChangeAvailable(num_changes)); 331 } 332 callback.Run(status); 333} 334 335void LocalFileSyncService::DidInitializeForRemoteSync( 336 const FileSystemURL& url, 337 fileapi::FileSystemContext* file_system_context, 338 const PrepareChangeCallback& callback, 339 SyncStatusCode status) { 340 if (status != SYNC_STATUS_OK) { 341 DVLOG(1) << "FileSystemContext initialization failed for remote sync:" 342 << url.DebugString() << " status=" << status 343 << " (" << SyncStatusCodeToString(status) << ")"; 344 callback.Run(status, SyncFileMetadata(), FileChangeList()); 345 return; 346 } 347 origin_to_contexts_[url.origin()] = file_system_context; 348 PrepareForProcessRemoteChange(url, callback); 349} 350 351void LocalFileSyncService::RunLocalSyncCallback( 352 SyncStatusCode status, 353 const FileSystemURL& url) { 354 DVLOG(1) << "Local sync is finished with: " << status 355 << " on " << url.DebugString(); 356 DCHECK(!local_sync_callback_.is_null()); 357 SyncFileCallback callback = local_sync_callback_; 358 local_sync_callback_.Reset(); 359 callback.Run(status, url); 360} 361 362void LocalFileSyncService::DidGetFileForLocalSync( 363 SyncStatusCode status, 364 const LocalFileSyncInfo& sync_file_info, 365 webkit_blob::ScopedFile snapshot) { 366 DCHECK(!local_sync_callback_.is_null()); 367 if (status != SYNC_STATUS_OK) { 368 RunLocalSyncCallback(status, sync_file_info.url); 369 return; 370 } 371 if (sync_file_info.changes.empty()) { 372 // There's a slight chance this could happen. 373 SyncFileCallback callback = local_sync_callback_; 374 local_sync_callback_.Reset(); 375 ProcessLocalChange(callback); 376 return; 377 } 378 379 FileChange next_change = sync_file_info.changes.front(); 380 DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString() 381 << " change:" << next_change.DebugString(); 382 383 local_change_processor_->ApplyLocalChange( 384 next_change, 385 sync_file_info.local_file_path, 386 sync_file_info.metadata, 387 sync_file_info.url, 388 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 389 AsWeakPtr(), base::Passed(&snapshot), sync_file_info, 390 next_change, sync_file_info.changes.PopAndGetNewList())); 391} 392 393void LocalFileSyncService::ProcessNextChangeForURL( 394 webkit_blob::ScopedFile snapshot, 395 const LocalFileSyncInfo& sync_file_info, 396 const FileChange& processed_change, 397 const FileChangeList& changes, 398 SyncStatusCode status) { 399 DVLOG(1) << "Processed one local change: " 400 << sync_file_info.url.DebugString() 401 << " change:" << processed_change.DebugString() 402 << " status:" << status; 403 404 if (status == SYNC_STATUS_RETRY) { 405 local_change_processor_->ApplyLocalChange( 406 processed_change, 407 sync_file_info.local_file_path, 408 sync_file_info.metadata, 409 sync_file_info.url, 410 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 411 AsWeakPtr(), base::Passed(&snapshot), 412 sync_file_info, processed_change, changes)); 413 return; 414 } 415 416 if (status == SYNC_FILE_ERROR_NOT_FOUND && 417 processed_change.change() == FileChange::FILE_CHANGE_DELETE) { 418 // This must be ok (and could happen). 419 status = SYNC_STATUS_OK; 420 } 421 422 const FileSystemURL& url = sync_file_info.url; 423 if (status != SYNC_STATUS_OK || changes.empty()) { 424 DCHECK(ContainsKey(origin_to_contexts_, url.origin())); 425 sync_context_->FinalizeSnapshotSync( 426 origin_to_contexts_[url.origin()], url, status, 427 base::Bind(&LocalFileSyncService::RunLocalSyncCallback, 428 AsWeakPtr(), status, url)); 429 return; 430 } 431 432 FileChange next_change = changes.front(); 433 local_change_processor_->ApplyLocalChange( 434 changes.front(), 435 sync_file_info.local_file_path, 436 sync_file_info.metadata, 437 url, 438 base::Bind(&LocalFileSyncService::ProcessNextChangeForURL, 439 AsWeakPtr(), base::Passed(&snapshot), sync_file_info, 440 next_change, changes.PopAndGetNewList())); 441} 442 443} // namespace sync_file_system 444