Home | History | Annotate | Download | only in local
      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_util.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_registry.h"
     22 #include "extensions/common/extension_set.h"
     23 #include "url/gurl.h"
     24 #include "webkit/browser/fileapi/file_system_context.h"
     25 #include "webkit/browser/fileapi/file_system_url.h"
     26 #include "webkit/common/blob/scoped_file.h"
     27 
     28 using content::BrowserThread;
     29 using fileapi::FileSystemURL;
     30 
     31 namespace sync_file_system {
     32 
     33 namespace {
     34 
     35 void PrepareForProcessRemoteChangeCallbackAdapter(
     36     const RemoteChangeProcessor::PrepareChangeCallback& callback,
     37     SyncStatusCode status,
     38     const LocalFileSyncInfo& sync_file_info,
     39     webkit_blob::ScopedFile snapshot) {
     40   callback.Run(status, sync_file_info.metadata, sync_file_info.changes);
     41 }
     42 
     43 }  // namespace
     44 
     45 LocalFileSyncService::OriginChangeMap::OriginChangeMap()
     46     : next_(change_count_map_.end()) {}
     47 LocalFileSyncService::OriginChangeMap::~OriginChangeMap() {}
     48 
     49 bool LocalFileSyncService::OriginChangeMap::NextOriginToProcess(GURL* origin) {
     50   DCHECK(origin);
     51   if (change_count_map_.empty())
     52     return false;
     53   Map::iterator begin = next_;
     54   do {
     55     if (next_ == change_count_map_.end())
     56       next_ = change_count_map_.begin();
     57     DCHECK_NE(0, next_->second);
     58     *origin = next_++->first;
     59     if (!ContainsKey(disabled_origins_, *origin))
     60       return true;
     61   } while (next_ != begin);
     62   return false;
     63 }
     64 
     65 int64 LocalFileSyncService::OriginChangeMap::GetTotalChangeCount() const {
     66   int64 num_changes = 0;
     67   for (Map::const_iterator iter = change_count_map_.begin();
     68        iter != change_count_map_.end(); ++iter) {
     69     if (ContainsKey(disabled_origins_, iter->first))
     70       continue;
     71     num_changes += iter->second;
     72   }
     73   return num_changes;
     74 }
     75 
     76 void LocalFileSyncService::OriginChangeMap::SetOriginChangeCount(
     77     const GURL& origin, int64 changes) {
     78   if (changes != 0) {
     79     change_count_map_[origin] = changes;
     80     return;
     81   }
     82   Map::iterator found = change_count_map_.find(origin);
     83   if (found != change_count_map_.end()) {
     84     if (next_ == found)
     85       ++next_;
     86     change_count_map_.erase(found);
     87   }
     88 }
     89 
     90 void LocalFileSyncService::OriginChangeMap::SetOriginEnabled(
     91     const GURL& origin, bool enabled) {
     92   if (enabled)
     93     disabled_origins_.erase(origin);
     94   else
     95     disabled_origins_.insert(origin);
     96 }
     97 
     98 // LocalFileSyncService -------------------------------------------------------
     99 
    100 scoped_ptr<LocalFileSyncService> LocalFileSyncService::Create(
    101     Profile* profile) {
    102   return make_scoped_ptr(new LocalFileSyncService(profile, NULL));
    103 }
    104 
    105 scoped_ptr<LocalFileSyncService> LocalFileSyncService::CreateForTesting(
    106     Profile* profile,
    107     leveldb::Env* env) {
    108   return make_scoped_ptr(new LocalFileSyncService(profile, env));
    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::PromoteDemotedChanges() {
    188   for (OriginToContext::iterator iter = origin_to_contexts_.begin();
    189        iter != origin_to_contexts_.end(); ++iter)
    190     sync_context_->PromoteDemotedChanges(iter->first, iter->second);
    191 }
    192 
    193 void LocalFileSyncService::GetLocalFileMetadata(
    194     const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
    195   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    196   sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
    197                                  url, callback);
    198 }
    199 
    200 void LocalFileSyncService::PrepareForProcessRemoteChange(
    201     const FileSystemURL& url,
    202     const PrepareChangeCallback& callback) {
    203   DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
    204 
    205   if (!ContainsKey(origin_to_contexts_, url.origin())) {
    206     // This could happen if a remote sync is triggered for the app that hasn't
    207     // been initialized in this service.
    208     DCHECK(profile_);
    209     // The given url.origin() must be for valid installed app.
    210     const extensions::Extension* extension =
    211         extensions::ExtensionRegistry::Get(profile_)
    212             ->enabled_extensions().GetAppByURL(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 =
    227         extensions::util::GetSiteForExtensionId(extension->id(), profile_);
    228     DCHECK(!site_url.is_empty());
    229     scoped_refptr<fileapi::FileSystemContext> file_system_context =
    230         content::BrowserContext::GetStoragePartitionForSite(
    231             profile_, site_url)->GetFileSystemContext();
    232     MaybeInitializeFileSystemContext(
    233         url.origin(),
    234         file_system_context.get(),
    235         base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
    236                    AsWeakPtr(),
    237                    url,
    238                    file_system_context,
    239                    callback));
    240     return;
    241   }
    242 
    243   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    244   sync_context_->PrepareForSync(
    245       origin_to_contexts_[url.origin()], url,
    246       LocalFileSyncContext::SYNC_EXCLUSIVE,
    247       base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
    248 }
    249 
    250 void LocalFileSyncService::ApplyRemoteChange(
    251     const FileChange& change,
    252     const base::FilePath& local_path,
    253     const FileSystemURL& url,
    254     const SyncStatusCallback& callback) {
    255   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    256   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    257             "[Remote -> Local] ApplyRemoteChange: %s on %s",
    258             change.DebugString().c_str(),
    259             url.DebugString().c_str());
    260 
    261   sync_context_->ApplyRemoteChange(
    262       origin_to_contexts_[url.origin()],
    263       change, local_path, url,
    264       base::Bind(&LocalFileSyncService::DidApplyRemoteChange, AsWeakPtr(),
    265                  callback));
    266 }
    267 
    268 void LocalFileSyncService::FinalizeRemoteSync(
    269     const FileSystemURL& url,
    270     bool clear_local_changes,
    271     const base::Closure& completion_callback) {
    272   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    273   sync_context_->FinalizeExclusiveSync(
    274       origin_to_contexts_[url.origin()],
    275       url, clear_local_changes, completion_callback);
    276 }
    277 
    278 void LocalFileSyncService::RecordFakeLocalChange(
    279     const FileSystemURL& url,
    280     const FileChange& change,
    281     const SyncStatusCallback& callback) {
    282   DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    283   sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
    284                                        url, change, callback);
    285 }
    286 
    287 void LocalFileSyncService::OnChangesAvailableInOrigins(
    288     const std::set<GURL>& origins) {
    289   bool need_notification = false;
    290   for (std::set<GURL>::const_iterator iter = origins.begin();
    291        iter != origins.end(); ++iter) {
    292     const GURL& origin = *iter;
    293     if (!ContainsKey(origin_to_contexts_, origin)) {
    294       // This could happen if this is called for apps/origins that haven't
    295       // been initialized yet, or for apps/origins that are disabled.
    296       // (Local change tracker could call this for uninitialized origins
    297       // while it's reading dirty files from the database in the
    298       // initialization phase.)
    299       pending_origins_with_changes_.insert(origin);
    300       continue;
    301     }
    302     need_notification = true;
    303     SyncFileSystemBackend* backend =
    304         SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
    305     DCHECK(backend);
    306     DCHECK(backend->change_tracker());
    307     origin_change_map_.SetOriginChangeCount(
    308         origin, backend->change_tracker()->num_changes());
    309   }
    310   if (!need_notification)
    311     return;
    312   int64 num_changes = origin_change_map_.GetTotalChangeCount();
    313   FOR_EACH_OBSERVER(Observer, change_observers_,
    314                     OnLocalChangeAvailable(num_changes));
    315 }
    316 
    317 void LocalFileSyncService::SetOriginEnabled(const GURL& origin, bool enabled) {
    318   if (!ContainsKey(origin_to_contexts_, origin))
    319     return;
    320   origin_change_map_.SetOriginEnabled(origin, enabled);
    321 }
    322 
    323 LocalFileSyncService::LocalFileSyncService(Profile* profile,
    324                                            leveldb::Env* env_override)
    325     : profile_(profile),
    326       sync_context_(new LocalFileSyncContext(
    327           profile_->GetPath(),
    328           env_override,
    329           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI).get(),
    330           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)
    331               .get())),
    332       local_change_processor_(NULL) {
    333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    334   sync_context_->AddOriginChangeObserver(this);
    335 }
    336 
    337 void LocalFileSyncService::DidInitializeFileSystemContext(
    338     const GURL& app_origin,
    339     fileapi::FileSystemContext* file_system_context,
    340     const SyncStatusCallback& callback,
    341     SyncStatusCode status) {
    342   if (status != SYNC_STATUS_OK) {
    343     callback.Run(status);
    344     return;
    345   }
    346   DCHECK(file_system_context);
    347   origin_to_contexts_[app_origin] = file_system_context;
    348 
    349   if (pending_origins_with_changes_.find(app_origin) !=
    350       pending_origins_with_changes_.end()) {
    351     // We have remaining changes for the origin.
    352     pending_origins_with_changes_.erase(app_origin);
    353     SyncFileSystemBackend* backend =
    354         SyncFileSystemBackend::GetBackend(file_system_context);
    355     DCHECK(backend);
    356     DCHECK(backend->change_tracker());
    357     origin_change_map_.SetOriginChangeCount(
    358         app_origin, backend->change_tracker()->num_changes());
    359     int64 num_changes = origin_change_map_.GetTotalChangeCount();
    360     FOR_EACH_OBSERVER(Observer, change_observers_,
    361                       OnLocalChangeAvailable(num_changes));
    362   }
    363   callback.Run(status);
    364 }
    365 
    366 void LocalFileSyncService::DidInitializeForRemoteSync(
    367     const FileSystemURL& url,
    368     fileapi::FileSystemContext* file_system_context,
    369     const PrepareChangeCallback& callback,
    370     SyncStatusCode status) {
    371   if (status != SYNC_STATUS_OK) {
    372     DVLOG(1) << "FileSystemContext initialization failed for remote sync:"
    373              << url.DebugString() << " status=" << status
    374              << " (" << SyncStatusCodeToString(status) << ")";
    375     callback.Run(status, SyncFileMetadata(), FileChangeList());
    376     return;
    377   }
    378   origin_to_contexts_[url.origin()] = file_system_context;
    379   PrepareForProcessRemoteChange(url, callback);
    380 }
    381 
    382 void LocalFileSyncService::RunLocalSyncCallback(
    383     SyncStatusCode status,
    384     const FileSystemURL& url) {
    385   DVLOG(1) << "Local sync is finished with: " << status
    386            << " on " << url.DebugString();
    387   DCHECK(!local_sync_callback_.is_null());
    388   SyncFileCallback callback = local_sync_callback_;
    389   local_sync_callback_.Reset();
    390   callback.Run(status, url);
    391 }
    392 
    393 void LocalFileSyncService::DidApplyRemoteChange(
    394     const SyncStatusCallback& callback,
    395     SyncStatusCode status) {
    396   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    397             "[Remote -> Local] ApplyRemoteChange finished --> %s",
    398             SyncStatusCodeToString(status));
    399   callback.Run(status);
    400 }
    401 
    402 void LocalFileSyncService::DidGetFileForLocalSync(
    403     SyncStatusCode status,
    404     const LocalFileSyncInfo& sync_file_info,
    405     webkit_blob::ScopedFile snapshot) {
    406   DCHECK(!local_sync_callback_.is_null());
    407   if (status != SYNC_STATUS_OK) {
    408     RunLocalSyncCallback(status, sync_file_info.url);
    409     return;
    410   }
    411   if (sync_file_info.changes.empty()) {
    412     // There's a slight chance this could happen.
    413     SyncFileCallback callback = local_sync_callback_;
    414     local_sync_callback_.Reset();
    415     ProcessLocalChange(callback);
    416     return;
    417   }
    418 
    419   FileChange next_change = sync_file_info.changes.front();
    420   DVLOG(1) << "ProcessLocalChange: " << sync_file_info.url.DebugString()
    421            << " change:" << next_change.DebugString();
    422 
    423   GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
    424       next_change,
    425       sync_file_info.local_file_path,
    426       sync_file_info.metadata,
    427       sync_file_info.url,
    428       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
    429                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
    430                  next_change, sync_file_info.changes.PopAndGetNewList()));
    431 }
    432 
    433 void LocalFileSyncService::ProcessNextChangeForURL(
    434     webkit_blob::ScopedFile snapshot,
    435     const LocalFileSyncInfo& sync_file_info,
    436     const FileChange& processed_change,
    437     const FileChangeList& changes,
    438     SyncStatusCode status) {
    439   DVLOG(1) << "Processed one local change: "
    440            << sync_file_info.url.DebugString()
    441            << " change:" << processed_change.DebugString()
    442            << " status:" << status;
    443 
    444   if (status == SYNC_STATUS_RETRY) {
    445     GetLocalChangeProcessor(sync_file_info.url)->ApplyLocalChange(
    446         processed_change,
    447         sync_file_info.local_file_path,
    448         sync_file_info.metadata,
    449         sync_file_info.url,
    450         base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
    451                    AsWeakPtr(), base::Passed(&snapshot),
    452                    sync_file_info, processed_change, changes));
    453     return;
    454   }
    455 
    456   if (status == SYNC_FILE_ERROR_NOT_FOUND &&
    457       processed_change.change() == FileChange::FILE_CHANGE_DELETE) {
    458     // This must be ok (and could happen).
    459     status = SYNC_STATUS_OK;
    460   }
    461 
    462   const FileSystemURL& url = sync_file_info.url;
    463   if (status != SYNC_STATUS_OK || changes.empty()) {
    464     DCHECK(ContainsKey(origin_to_contexts_, url.origin()));
    465     sync_context_->FinalizeSnapshotSync(
    466         origin_to_contexts_[url.origin()], url, status,
    467         base::Bind(&LocalFileSyncService::RunLocalSyncCallback,
    468                    AsWeakPtr(), status, url));
    469     return;
    470   }
    471 
    472   FileChange next_change = changes.front();
    473   GetLocalChangeProcessor(url)->ApplyLocalChange(
    474       changes.front(),
    475       sync_file_info.local_file_path,
    476       sync_file_info.metadata,
    477       url,
    478       base::Bind(&LocalFileSyncService::ProcessNextChangeForURL,
    479                  AsWeakPtr(), base::Passed(&snapshot), sync_file_info,
    480                  next_change, changes.PopAndGetNewList()));
    481 }
    482 
    483 LocalChangeProcessor* LocalFileSyncService::GetLocalChangeProcessor(
    484     const FileSystemURL& url) {
    485   if (!get_local_change_processor_.is_null())
    486     return get_local_change_processor_.Run(url.origin());
    487   DCHECK(local_change_processor_);
    488   return local_change_processor_;
    489 }
    490 
    491 }  // namespace sync_file_system
    492