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