Home | History | Annotate | Download | only in sync_file_system
      1 // Copyright (c) 2012 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/sync_file_system_service.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/format_macros.h"
     11 #include "base/logging.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/stl_util.h"
     15 #include "chrome/browser/chrome_notification_types.h"
     16 #include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h"
     17 #include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/sync/profile_sync_service.h"
     20 #include "chrome/browser/sync/profile_sync_service_factory.h"
     21 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
     22 #include "chrome/browser/sync_file_system/logger.h"
     23 #include "chrome/browser/sync_file_system/sync_direction.h"
     24 #include "chrome/browser/sync_file_system/sync_event_observer.h"
     25 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
     26 #include "chrome/browser/sync_file_system/sync_process_runner.h"
     27 #include "chrome/browser/sync_file_system/sync_status_code.h"
     28 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     29 #include "components/keyed_service/content/browser_context_dependency_manager.h"
     30 #include "content/public/browser/browser_thread.h"
     31 #include "content/public/browser/notification_details.h"
     32 #include "content/public/browser/notification_service.h"
     33 #include "content/public/browser/storage_partition.h"
     34 #include "extensions/browser/extension_prefs.h"
     35 #include "extensions/common/extension.h"
     36 #include "extensions/common/manifest_constants.h"
     37 #include "url/gurl.h"
     38 #include "webkit/browser/fileapi/file_system_context.h"
     39 
     40 using content::BrowserThread;
     41 using extensions::Extension;
     42 using extensions::ExtensionPrefs;
     43 using fileapi::FileSystemURL;
     44 using fileapi::FileSystemURLSet;
     45 
     46 namespace sync_file_system {
     47 
     48 namespace {
     49 
     50 const char kLocalSyncName[] = "Local sync";
     51 const char kRemoteSyncName[] = "Remote sync";
     52 const char kRemoteSyncNameV2[] = "Remote sync (v2)";
     53 
     54 SyncServiceState RemoteStateToSyncServiceState(
     55     RemoteServiceState state) {
     56   switch (state) {
     57     case REMOTE_SERVICE_OK:
     58       return SYNC_SERVICE_RUNNING;
     59     case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
     60       return SYNC_SERVICE_TEMPORARY_UNAVAILABLE;
     61     case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
     62       return SYNC_SERVICE_AUTHENTICATION_REQUIRED;
     63     case REMOTE_SERVICE_DISABLED:
     64       return SYNC_SERVICE_DISABLED;
     65     case REMOTE_SERVICE_STATE_MAX:
     66       NOTREACHED();
     67   }
     68   NOTREACHED() << "Unknown remote service state: " << state;
     69   return SYNC_SERVICE_DISABLED;
     70 }
     71 
     72 void DidHandleOriginForExtensionUnloadedEvent(
     73     int type,
     74     const GURL& origin,
     75     SyncStatusCode code) {
     76   DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED == type ||
     77          chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED == type);
     78   if (code != SYNC_STATUS_OK &&
     79       code != SYNC_STATUS_UNKNOWN_ORIGIN) {
     80     switch (type) {
     81       case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED:
     82         util::Log(logging::LOG_WARNING,
     83                   FROM_HERE,
     84                   "Disabling origin for UNLOADED(DISABLE) failed: %s",
     85                   origin.spec().c_str());
     86         break;
     87       case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED:
     88         util::Log(logging::LOG_WARNING,
     89                   FROM_HERE,
     90                   "Uninstall origin for UNINSTALLED failed: %s",
     91                   origin.spec().c_str());
     92         break;
     93       default:
     94         break;
     95     }
     96   }
     97 }
     98 
     99 void DidHandleOriginForExtensionEnabledEvent(
    100     int type,
    101     const GURL& origin,
    102     SyncStatusCode code) {
    103   DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type);
    104   if (code != SYNC_STATUS_OK)
    105     util::Log(logging::LOG_WARNING,
    106               FROM_HERE,
    107               "Enabling origin for ENABLED failed: %s",
    108               origin.spec().c_str());
    109 }
    110 
    111 std::string SyncFileStatusToString(SyncFileStatus sync_file_status) {
    112   return extensions::api::sync_file_system::ToString(
    113       extensions::SyncFileStatusToExtensionEnum(sync_file_status));
    114 }
    115 
    116 // Gets called repeatedly until every SyncFileStatus has been mapped.
    117 void DidGetFileSyncStatusForDump(
    118     base::ListValue* files,
    119     size_t* num_results,
    120     const SyncFileSystemService::DumpFilesCallback& callback,
    121     base::DictionaryValue* file,
    122     SyncStatusCode sync_status_code,
    123     SyncFileStatus sync_file_status) {
    124   DCHECK(files);
    125   DCHECK(num_results);
    126 
    127   if (file)
    128     file->SetString("status", SyncFileStatusToString(sync_file_status));
    129 
    130   // Once all results have been received, run the callback to signal end.
    131   DCHECK_LE(*num_results, files->GetSize());
    132   if (++*num_results < files->GetSize())
    133     return;
    134 
    135   callback.Run(*files);
    136 }
    137 
    138 // We need this indirection because WeakPtr can only be bound to methods
    139 // without a return value.
    140 LocalChangeProcessor* GetLocalChangeProcessorAdapter(
    141     base::WeakPtr<SyncFileSystemService> service,
    142     const GURL& origin) {
    143   if (!service)
    144     return NULL;
    145   return service->GetLocalChangeProcessor(origin);
    146 }
    147 
    148 }  // namespace
    149 
    150 //---------------------------------------------------------------------------
    151 // SyncProcessRunner's.
    152 
    153 // SyncProcessRunner implementation for LocalSync.
    154 class LocalSyncRunner : public SyncProcessRunner,
    155                         public LocalFileSyncService::Observer {
    156  public:
    157   LocalSyncRunner(const std::string& name,
    158                   SyncFileSystemService* sync_service)
    159       : SyncProcessRunner(name, sync_service),
    160         factory_(this) {}
    161 
    162   virtual void StartSync(const SyncStatusCallback& callback) OVERRIDE {
    163     sync_service()->local_service_->ProcessLocalChange(
    164         base::Bind(&LocalSyncRunner::DidProcessLocalChange,
    165                    factory_.GetWeakPtr(), callback));
    166   }
    167 
    168   // LocalFileSyncService::Observer overrides.
    169   virtual void OnLocalChangeAvailable(int64 pending_changes) OVERRIDE {
    170     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    171 
    172     OnChangesUpdated(pending_changes);
    173 
    174     // Kick other sync runners just in case they're not running.
    175     sync_service()->RunForEachSyncRunners(
    176         &SyncProcessRunner::ScheduleIfNotRunning);
    177   }
    178 
    179  private:
    180   void DidProcessLocalChange(
    181       const SyncStatusCallback& callback,
    182       SyncStatusCode status,
    183       const FileSystemURL& url) {
    184     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    185               "ProcessLocalChange finished with status=%d (%s) for url=%s",
    186               status, SyncStatusCodeToString(status),
    187               url.DebugString().c_str());
    188     callback.Run(status);
    189   }
    190 
    191   base::WeakPtrFactory<LocalSyncRunner> factory_;
    192   DISALLOW_COPY_AND_ASSIGN(LocalSyncRunner);
    193 };
    194 
    195 // SyncProcessRunner implementation for RemoteSync.
    196 class RemoteSyncRunner : public SyncProcessRunner,
    197                          public RemoteFileSyncService::Observer {
    198  public:
    199   RemoteSyncRunner(const std::string& name,
    200                    SyncFileSystemService* sync_service,
    201                    RemoteFileSyncService* remote_service)
    202       : SyncProcessRunner(name, sync_service),
    203         remote_service_(remote_service),
    204         last_state_(REMOTE_SERVICE_OK),
    205         factory_(this) {}
    206 
    207   virtual void StartSync(const SyncStatusCallback& callback) OVERRIDE {
    208     remote_service_->ProcessRemoteChange(
    209         base::Bind(&RemoteSyncRunner::DidProcessRemoteChange,
    210                    factory_.GetWeakPtr(), callback));
    211   }
    212 
    213   virtual SyncServiceState GetServiceState() OVERRIDE {
    214     return RemoteStateToSyncServiceState(last_state_);
    215   }
    216 
    217   // RemoteFileSyncService::Observer overrides.
    218   virtual void OnRemoteChangeQueueUpdated(int64 pending_changes) OVERRIDE {
    219     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    220 
    221     OnChangesUpdated(pending_changes);
    222 
    223     // Kick other sync runners just in case they're not running.
    224     sync_service()->RunForEachSyncRunners(
    225         &SyncProcessRunner::ScheduleIfNotRunning);
    226   }
    227 
    228   virtual void OnRemoteServiceStateUpdated(
    229       RemoteServiceState state,
    230       const std::string& description) OVERRIDE {
    231     // Just forward to SyncFileSystemService.
    232     sync_service()->OnRemoteServiceStateUpdated(state, description);
    233     last_state_ = state;
    234   }
    235 
    236  private:
    237   void DidProcessRemoteChange(
    238       const SyncStatusCallback& callback,
    239       SyncStatusCode status,
    240       const FileSystemURL& url) {
    241     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    242               "ProcessRemoteChange finished with status=%d (%s) for url=%s",
    243               status, SyncStatusCodeToString(status),
    244               url.DebugString().c_str());
    245 
    246     if (status == SYNC_STATUS_FILE_BUSY) {
    247       sync_service()->local_service_->RegisterURLForWaitingSync(
    248           url, base::Bind(&RemoteSyncRunner::Schedule,
    249                           factory_.GetWeakPtr()));
    250     }
    251     callback.Run(status);
    252   }
    253 
    254   RemoteFileSyncService* remote_service_;
    255   RemoteServiceState last_state_;
    256   base::WeakPtrFactory<RemoteSyncRunner> factory_;
    257   DISALLOW_COPY_AND_ASSIGN(RemoteSyncRunner);
    258 };
    259 
    260 //-----------------------------------------------------------------------------
    261 // SyncFileSystemService
    262 
    263 void SyncFileSystemService::Shutdown() {
    264   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    265 
    266   local_service_->Shutdown();
    267   local_service_.reset();
    268 
    269   remote_service_.reset();
    270   v2_remote_service_.reset();
    271 
    272   ProfileSyncServiceBase* profile_sync_service =
    273       ProfileSyncServiceFactory::GetForProfile(profile_);
    274   if (profile_sync_service)
    275     profile_sync_service->RemoveObserver(this);
    276 
    277   profile_ = NULL;
    278 }
    279 
    280 SyncFileSystemService::~SyncFileSystemService() {
    281   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    282   DCHECK(!profile_);
    283 }
    284 
    285 void SyncFileSystemService::InitializeForApp(
    286     fileapi::FileSystemContext* file_system_context,
    287     const GURL& app_origin,
    288     const SyncStatusCallback& callback) {
    289   DCHECK(local_service_);
    290   DCHECK(remote_service_);
    291   DCHECK(app_origin == app_origin.GetOrigin());
    292 
    293   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    294             "Initializing for App: %s", app_origin.spec().c_str());
    295 
    296   local_service_->MaybeInitializeFileSystemContext(
    297       app_origin, file_system_context,
    298       base::Bind(&SyncFileSystemService::DidInitializeFileSystem,
    299                  AsWeakPtr(), app_origin, callback));
    300 }
    301 
    302 SyncServiceState SyncFileSystemService::GetSyncServiceState() {
    303   // For now we always query the state from the main RemoteFileSyncService.
    304   return RemoteStateToSyncServiceState(remote_service_->GetCurrentState());
    305 }
    306 
    307 void SyncFileSystemService::GetExtensionStatusMap(
    308     const ExtensionStatusMapCallback& callback) {
    309   remote_service_->GetOriginStatusMap(
    310       base::Bind(&SyncFileSystemService::DidGetExtensionStatusMap,
    311                  AsWeakPtr(), callback));
    312 }
    313 
    314 void SyncFileSystemService::DumpFiles(const GURL& origin,
    315                                       const DumpFilesCallback& callback) {
    316   DCHECK(!origin.is_empty());
    317 
    318   content::StoragePartition* storage_partition =
    319       content::BrowserContext::GetStoragePartitionForSite(profile_, origin);
    320   fileapi::FileSystemContext* file_system_context =
    321       storage_partition->GetFileSystemContext();
    322   local_service_->MaybeInitializeFileSystemContext(
    323       origin, file_system_context,
    324       base::Bind(&SyncFileSystemService::DidInitializeFileSystemForDump,
    325                  AsWeakPtr(), origin, callback));
    326 }
    327 
    328 void SyncFileSystemService::DumpDatabase(const DumpFilesCallback& callback) {
    329   remote_service_->DumpDatabase(
    330       base::Bind(&SyncFileSystemService::DidDumpDatabase,
    331                  AsWeakPtr(), callback));
    332 }
    333 
    334 void SyncFileSystemService::GetFileSyncStatus(
    335     const FileSystemURL& url, const SyncFileStatusCallback& callback) {
    336   DCHECK(local_service_);
    337   DCHECK(GetRemoteService(url.origin()));
    338 
    339   // It's possible to get an invalid FileEntry.
    340   if (!url.is_valid()) {
    341     base::MessageLoopProxy::current()->PostTask(
    342         FROM_HERE,
    343         base::Bind(callback,
    344                    SYNC_FILE_ERROR_INVALID_URL,
    345                    SYNC_FILE_STATUS_UNKNOWN));
    346     return;
    347   }
    348 
    349   local_service_->HasPendingLocalChanges(
    350       url,
    351       base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus,
    352                  AsWeakPtr(), callback));
    353 }
    354 
    355 void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) {
    356   observers_.AddObserver(observer);
    357 }
    358 
    359 void SyncFileSystemService::RemoveSyncEventObserver(
    360     SyncEventObserver* observer) {
    361   observers_.RemoveObserver(observer);
    362 }
    363 
    364 LocalChangeProcessor* SyncFileSystemService::GetLocalChangeProcessor(
    365     const GURL& origin) {
    366   return GetRemoteService(origin)->GetLocalChangeProcessor();
    367 }
    368 
    369 SyncFileSystemService::SyncFileSystemService(Profile* profile)
    370     : profile_(profile),
    371       sync_enabled_(true) {
    372 }
    373 
    374 void SyncFileSystemService::Initialize(
    375     scoped_ptr<LocalFileSyncService> local_service,
    376     scoped_ptr<RemoteFileSyncService> remote_service) {
    377   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    378   DCHECK(local_service);
    379   DCHECK(remote_service);
    380   DCHECK(profile_);
    381 
    382   local_service_ = local_service.Pass();
    383   remote_service_ = remote_service.Pass();
    384 
    385   scoped_ptr<LocalSyncRunner> local_syncer(
    386       new LocalSyncRunner(kLocalSyncName, this));
    387   scoped_ptr<RemoteSyncRunner> remote_syncer(
    388       new RemoteSyncRunner(kRemoteSyncName, this, remote_service_.get()));
    389 
    390   local_service_->AddChangeObserver(local_syncer.get());
    391   local_service_->SetLocalChangeProcessorCallback(
    392       base::Bind(&GetLocalChangeProcessorAdapter, AsWeakPtr()));
    393 
    394   remote_service_->AddServiceObserver(remote_syncer.get());
    395   remote_service_->AddFileStatusObserver(this);
    396   remote_service_->SetRemoteChangeProcessor(local_service_.get());
    397 
    398   local_sync_runners_.push_back(local_syncer.release());
    399   remote_sync_runners_.push_back(remote_syncer.release());
    400 
    401   ProfileSyncServiceBase* profile_sync_service =
    402       ProfileSyncServiceFactory::GetForProfile(profile_);
    403   if (profile_sync_service) {
    404     UpdateSyncEnabledStatus(profile_sync_service);
    405     profile_sync_service->AddObserver(this);
    406   }
    407 
    408   registrar_.Add(this,
    409                  chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
    410                  content::Source<Profile>(profile_));
    411   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
    412                  content::Source<Profile>(profile_));
    413   registrar_.Add(this,
    414                  chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
    415                  content::Source<Profile>(profile_));
    416   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
    417                  content::Source<Profile>(profile_));
    418 }
    419 
    420 void SyncFileSystemService::DidInitializeFileSystem(
    421     const GURL& app_origin,
    422     const SyncStatusCallback& callback,
    423     SyncStatusCode status) {
    424   DVLOG(1) << "DidInitializeFileSystem: "
    425            << app_origin.spec() << " " << status;
    426 
    427   if (status != SYNC_STATUS_OK) {
    428     callback.Run(status);
    429     return;
    430   }
    431 
    432   // Local side of initialization for the app is done.
    433   // Continue on initializing the remote side.
    434   GetRemoteService(app_origin)->RegisterOrigin(
    435       app_origin,
    436       base::Bind(&SyncFileSystemService::DidRegisterOrigin,
    437                  AsWeakPtr(), app_origin, callback));
    438 }
    439 
    440 void SyncFileSystemService::DidRegisterOrigin(
    441     const GURL& app_origin,
    442     const SyncStatusCallback& callback,
    443     SyncStatusCode status) {
    444   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    445             "DidInitializeForApp (registered the origin): %s: %s",
    446             app_origin.spec().c_str(),
    447             SyncStatusCodeToString(status));
    448 
    449   UMA_HISTOGRAM_ENUMERATION("SyncFileSystem.RegisterOriginResult",
    450                             GetRemoteService(app_origin)->GetCurrentState(),
    451                             REMOTE_SERVICE_STATE_MAX);
    452 
    453   if (status == SYNC_STATUS_FAILED) {
    454     // If we got generic error return the service status information.
    455     switch (GetRemoteService(app_origin)->GetCurrentState()) {
    456       case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
    457         callback.Run(SYNC_STATUS_AUTHENTICATION_FAILED);
    458         return;
    459       case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
    460         callback.Run(SYNC_STATUS_SERVICE_TEMPORARILY_UNAVAILABLE);
    461         return;
    462       default:
    463         break;
    464     }
    465   }
    466 
    467   callback.Run(status);
    468 }
    469 
    470 void SyncFileSystemService::DidInitializeFileSystemForDump(
    471     const GURL& origin,
    472     const DumpFilesCallback& callback,
    473     SyncStatusCode status) {
    474   DCHECK(!origin.is_empty());
    475 
    476   if (status != SYNC_STATUS_OK) {
    477     callback.Run(base::ListValue());
    478     return;
    479   }
    480 
    481   GetRemoteService(origin)->DumpFiles(
    482       origin,
    483       base::Bind(
    484           &SyncFileSystemService::DidDumpFiles,
    485           AsWeakPtr(),
    486           origin,
    487           callback));
    488 }
    489 
    490 void SyncFileSystemService::DidDumpFiles(
    491     const GURL& origin,
    492     const DumpFilesCallback& callback,
    493     scoped_ptr<base::ListValue> dump_files) {
    494   if (!dump_files || !dump_files->GetSize()) {
    495     callback.Run(base::ListValue());
    496     return;
    497   }
    498 
    499   base::ListValue* files = dump_files.get();
    500   base::Callback<void(base::DictionaryValue*,
    501                       SyncStatusCode,
    502                       SyncFileStatus)> completion_callback =
    503       base::Bind(&DidGetFileSyncStatusForDump,
    504                  base::Owned(dump_files.release()),
    505                  base::Owned(new size_t(0)),
    506                  callback);
    507 
    508   // After all metadata loaded, sync status can be added to each entry.
    509   for (size_t i = 0; i < files->GetSize(); ++i) {
    510     base::DictionaryValue* file = NULL;
    511     std::string path_string;
    512     if (!files->GetDictionary(i, &file) ||
    513         !file->GetString("path", &path_string)) {
    514       NOTREACHED();
    515       completion_callback.Run(
    516           NULL, SYNC_FILE_ERROR_FAILED, SYNC_FILE_STATUS_UNKNOWN);
    517       continue;
    518     }
    519 
    520     base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_string);
    521     FileSystemURL url = CreateSyncableFileSystemURL(origin, file_path);
    522     GetFileSyncStatus(url, base::Bind(completion_callback, file));
    523   }
    524 }
    525 
    526 void SyncFileSystemService::DidDumpDatabase(
    527     const DumpFilesCallback& callback, scoped_ptr<base::ListValue> list) {
    528   if (!list)
    529     list = make_scoped_ptr(new base::ListValue);
    530 
    531   if (!v2_remote_service_) {
    532     callback.Run(*list);
    533     return;
    534   }
    535 
    536   v2_remote_service_->DumpDatabase(
    537       base::Bind(&SyncFileSystemService::DidDumpV2Database,
    538                  AsWeakPtr(), callback, base::Passed(&list)));
    539 }
    540 
    541 void SyncFileSystemService::DidDumpV2Database(
    542     const DumpFilesCallback& callback,
    543     scoped_ptr<base::ListValue> v1list,
    544     scoped_ptr<base::ListValue> v2list) {
    545   DCHECK(v1list);
    546 
    547   if (v2list) {
    548     for (base::ListValue::iterator itr = v2list->begin();
    549          itr != v2list->end();) {
    550       scoped_ptr<base::Value> item;
    551       itr = v2list->Erase(itr, &item);
    552       v1list->Append(item.release());
    553     }
    554   }
    555 
    556   callback.Run(*v1list);
    557 }
    558 
    559 void SyncFileSystemService::DidGetExtensionStatusMap(
    560     const ExtensionStatusMapCallback& callback,
    561     scoped_ptr<RemoteFileSyncService::OriginStatusMap> status_map) {
    562   if (!v2_remote_service_) {
    563     callback.Run(*status_map);
    564     return;
    565   }
    566 
    567   v2_remote_service_->GetOriginStatusMap(
    568       base::Bind(&SyncFileSystemService::DidGetV2ExtensionStatusMap,
    569                  AsWeakPtr(),
    570                  callback,
    571                  base::Passed(&status_map)));
    572 }
    573 
    574 void SyncFileSystemService::DidGetV2ExtensionStatusMap(
    575     const ExtensionStatusMapCallback& callback,
    576     scoped_ptr<RemoteFileSyncService::OriginStatusMap> status_map_v1,
    577     scoped_ptr<RemoteFileSyncService::OriginStatusMap> status_map_v2) {
    578   // Merge |status_map_v2| into |status_map_v1|.
    579   status_map_v1->insert(status_map_v2->begin(), status_map_v2->end());
    580 
    581   callback.Run(*status_map_v1);
    582 }
    583 
    584 void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) {
    585   sync_enabled_ = enabled;
    586   remote_service_->SetSyncEnabled(sync_enabled_);
    587   if (v2_remote_service_)
    588     v2_remote_service_->SetSyncEnabled(sync_enabled_);
    589 }
    590 
    591 void SyncFileSystemService::DidGetLocalChangeStatus(
    592     const SyncFileStatusCallback& callback,
    593     SyncStatusCode status,
    594     bool has_pending_local_changes) {
    595   callback.Run(
    596       status,
    597       has_pending_local_changes ?
    598           SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED);
    599 }
    600 
    601 void SyncFileSystemService::OnSyncIdle() {
    602   int64 remote_changes = 0;
    603   for (ScopedVector<SyncProcessRunner>::iterator iter =
    604            remote_sync_runners_.begin();
    605        iter != remote_sync_runners_.end(); ++iter)
    606     remote_changes += (*iter)->pending_changes();
    607   if (remote_changes == 0)
    608     local_service_->PromoteDemotedChanges();
    609 
    610   int64 local_changes = 0;
    611   for (ScopedVector<SyncProcessRunner>::iterator iter =
    612            local_sync_runners_.begin();
    613        iter != local_sync_runners_.end(); ++iter)
    614     local_changes += (*iter)->pending_changes();
    615   if (local_changes == 0 && v2_remote_service_)
    616     v2_remote_service_->PromoteDemotedChanges();
    617 }
    618 
    619 void SyncFileSystemService::OnRemoteServiceStateUpdated(
    620     RemoteServiceState state,
    621     const std::string& description) {
    622   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    623   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    624             "OnRemoteServiceStateChanged: %d %s", state, description.c_str());
    625 
    626   FOR_EACH_OBSERVER(
    627       SyncEventObserver, observers_,
    628       OnSyncStateUpdated(GURL(),
    629                          RemoteStateToSyncServiceState(state),
    630                          description));
    631 
    632   RunForEachSyncRunners(&SyncProcessRunner::Schedule);
    633 }
    634 
    635 void SyncFileSystemService::Observe(
    636     int type,
    637     const content::NotificationSource& source,
    638     const content::NotificationDetails& details) {
    639   // Event notification sequence.
    640   //
    641   // (User action)    (Notification type)
    642   // Install:         INSTALLED.
    643   // Update:          INSTALLED.
    644   // Uninstall:       UNINSTALLED.
    645   // Launch, Close:   No notification.
    646   // Enable:          ENABLED.
    647   // Disable:         UNLOADED(DISABLE).
    648   // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED.
    649   //
    650   switch (type) {
    651     case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED:
    652       HandleExtensionInstalled(details);
    653       break;
    654     case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED:
    655       HandleExtensionUnloaded(type, details);
    656       break;
    657     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED:
    658       HandleExtensionUninstalled(type, details);
    659       break;
    660     case chrome::NOTIFICATION_EXTENSION_ENABLED:
    661       HandleExtensionEnabled(type, details);
    662       break;
    663     default:
    664       NOTREACHED() << "Unknown notification.";
    665       break;
    666   }
    667 }
    668 
    669 void SyncFileSystemService::HandleExtensionInstalled(
    670     const content::NotificationDetails& details) {
    671   const Extension* extension =
    672       content::Details<const extensions::InstalledExtensionInfo>(details)->
    673           extension;
    674   GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id());
    675   DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin;
    676   // NOTE: When an app is uninstalled and re-installed in a sequence,
    677   // |local_service_| may still keeps |app_origin| as disabled origin.
    678   local_service_->SetOriginEnabled(app_origin, true);
    679 }
    680 
    681 void SyncFileSystemService::HandleExtensionUnloaded(
    682     int type,
    683     const content::NotificationDetails& details) {
    684   content::Details<const extensions::UnloadedExtensionInfo> info(details);
    685   if (info->reason != extensions::UnloadedExtensionInfo::REASON_DISABLE)
    686     return;
    687 
    688   std::string extension_id = info->extension->id();
    689   GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
    690 
    691   int reasons = ExtensionPrefs::Get(profile_)->GetDisableReasons(extension_id);
    692   if (reasons & Extension::DISABLE_RELOAD) {
    693     // Bypass disabling the origin since the app will be re-enabled soon.
    694     // NOTE: If re-enabling the app fails, the app is disabled while it is
    695     // handled as enabled origin in the SyncFS. This should be safe and will be
    696     // recovered when the user re-enables the app manually or the sync service
    697     // restarts.
    698     DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE_RELOAD): "
    699              << app_origin;
    700     return;
    701   }
    702 
    703   DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
    704            << app_origin;
    705   GetRemoteService(app_origin)->DisableOrigin(
    706       app_origin,
    707       base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
    708                  type, app_origin));
    709   local_service_->SetOriginEnabled(app_origin, false);
    710 }
    711 
    712 void SyncFileSystemService::HandleExtensionUninstalled(
    713     int type,
    714     const content::NotificationDetails& details) {
    715   const Extension* extension = content::Details<const Extension>(details).ptr();
    716   DCHECK(extension);
    717 
    718   RemoteFileSyncService::UninstallFlag flag =
    719       RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE;
    720   // If it's loaded from an unpacked package and with key: field,
    721   // the uninstall will not be sync'ed and the user might be using the
    722   // same app key in other installs, so avoid purging the remote folder.
    723   if (extensions::Manifest::IsUnpackedLocation(extension->location()) &&
    724       extension->manifest()->HasKey(extensions::manifest_keys::kKey)) {
    725     flag = RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE;
    726   }
    727 
    728   GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id());
    729   DVLOG(1) << "Handle extension notification for UNINSTALLED: "
    730            << app_origin;
    731   GetRemoteService(app_origin)->UninstallOrigin(
    732       app_origin, flag,
    733       base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
    734                  type, app_origin));
    735   local_service_->SetOriginEnabled(app_origin, false);
    736 }
    737 
    738 void SyncFileSystemService::HandleExtensionEnabled(
    739     int type,
    740     const content::NotificationDetails& details) {
    741   std::string extension_id = content::Details<const Extension>(details)->id();
    742   GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
    743   DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin;
    744   GetRemoteService(app_origin)->EnableOrigin(
    745       app_origin,
    746       base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin));
    747   local_service_->SetOriginEnabled(app_origin, true);
    748 }
    749 
    750 void SyncFileSystemService::OnStateChanged() {
    751   ProfileSyncServiceBase* profile_sync_service =
    752       ProfileSyncServiceFactory::GetForProfile(profile_);
    753   if (profile_sync_service)
    754     UpdateSyncEnabledStatus(profile_sync_service);
    755 }
    756 
    757 void SyncFileSystemService::OnFileStatusChanged(
    758     const FileSystemURL& url,
    759     SyncFileStatus sync_status,
    760     SyncAction action_taken,
    761     SyncDirection direction) {
    762   FOR_EACH_OBSERVER(
    763       SyncEventObserver, observers_,
    764       OnFileSynced(url, sync_status, action_taken, direction));
    765 }
    766 
    767 void SyncFileSystemService::UpdateSyncEnabledStatus(
    768     ProfileSyncServiceBase* profile_sync_service) {
    769   if (!profile_sync_service->HasSyncSetupCompleted())
    770     return;
    771   bool old_sync_enabled = sync_enabled_;
    772   sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has(
    773       syncer::APPS);
    774   remote_service_->SetSyncEnabled(sync_enabled_);
    775   if (v2_remote_service_)
    776     v2_remote_service_->SetSyncEnabled(sync_enabled_);
    777   if (!old_sync_enabled && sync_enabled_)
    778     RunForEachSyncRunners(&SyncProcessRunner::Schedule);
    779 }
    780 
    781 void SyncFileSystemService::RunForEachSyncRunners(
    782     void(SyncProcessRunner::*method)()) {
    783   for (ScopedVector<SyncProcessRunner>::iterator iter =
    784            local_sync_runners_.begin();
    785        iter != local_sync_runners_.end(); ++iter)
    786     ((*iter)->*method)();
    787   for (ScopedVector<SyncProcessRunner>::iterator iter =
    788            remote_sync_runners_.begin();
    789        iter != remote_sync_runners_.end(); ++iter)
    790     ((*iter)->*method)();
    791 }
    792 
    793 RemoteFileSyncService* SyncFileSystemService::GetRemoteService(
    794     const GURL& origin) {
    795   if (IsV2Enabled())
    796     return remote_service_.get();
    797   if (!IsV2EnabledForOrigin(origin))
    798     return remote_service_.get();
    799 
    800   if (!v2_remote_service_) {
    801     v2_remote_service_ = RemoteFileSyncService::CreateForBrowserContext(
    802         RemoteFileSyncService::V2, profile_, &task_logger_);
    803     scoped_ptr<RemoteSyncRunner> v2_remote_syncer(
    804         new RemoteSyncRunner(kRemoteSyncNameV2, this,
    805                              v2_remote_service_.get()));
    806     v2_remote_service_->AddServiceObserver(v2_remote_syncer.get());
    807     v2_remote_service_->AddFileStatusObserver(this);
    808     v2_remote_service_->SetRemoteChangeProcessor(local_service_.get());
    809     v2_remote_service_->SetSyncEnabled(sync_enabled_);
    810     remote_sync_runners_.push_back(v2_remote_syncer.release());
    811   }
    812   return v2_remote_service_.get();
    813 }
    814 
    815 }  // namespace sync_file_system
    816