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_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