| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/sync_file_system/sync_file_system_service.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/format_macros.h" |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h" |
| #include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h" |
| #include "chrome/browser/extensions/extension_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h" |
| #include "chrome/browser/sync_file_system/local/local_file_sync_service.h" |
| #include "chrome/browser/sync_file_system/logger.h" |
| #include "chrome/browser/sync_file_system/sync_direction.h" |
| #include "chrome/browser/sync_file_system/sync_event_observer.h" |
| #include "chrome/browser/sync_file_system/sync_file_metadata.h" |
| #include "chrome/browser/sync_file_system/sync_status_code.h" |
| #include "chrome/browser/sync_file_system/syncable_file_system_util.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "url/gurl.h" |
| #include "webkit/browser/fileapi/file_system_context.h" |
| |
| using content::BrowserThread; |
| using extensions::Extension; |
| using extensions::ExtensionPrefs; |
| using fileapi::FileSystemURL; |
| using fileapi::FileSystemURLSet; |
| |
| namespace sync_file_system { |
| |
| namespace { |
| |
| // Default delay when more changes are available. |
| const int64 kSyncDelayInMilliseconds = 1 * base::Time::kMillisecondsPerSecond; |
| |
| // Default delay when there're more than 10 pending changes. |
| const int64 kSyncDelayFastInMilliseconds = 100; |
| const int kPendingChangeThresholdForFastSync = 10; |
| |
| // Default delay when remote service is temporarily unavailable. |
| const int64 kSyncDelaySlowInMilliseconds = |
| 30 * base::Time::kMillisecondsPerSecond; // Start with 30 sec + exp backoff |
| |
| // Default delay when there're no changes. |
| const int64 kSyncDelayMaxInMilliseconds = |
| 30 * 60 * base::Time::kMillisecondsPerSecond; // 30 min |
| |
| SyncServiceState RemoteStateToSyncServiceState( |
| RemoteServiceState state) { |
| switch (state) { |
| case REMOTE_SERVICE_OK: |
| return SYNC_SERVICE_RUNNING; |
| case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE: |
| return SYNC_SERVICE_TEMPORARY_UNAVAILABLE; |
| case REMOTE_SERVICE_AUTHENTICATION_REQUIRED: |
| return SYNC_SERVICE_AUTHENTICATION_REQUIRED; |
| case REMOTE_SERVICE_DISABLED: |
| return SYNC_SERVICE_DISABLED; |
| } |
| NOTREACHED() << "Unknown remote service state: " << state; |
| return SYNC_SERVICE_DISABLED; |
| } |
| |
| void DidHandleOriginForExtensionUnloadedEvent( |
| int type, |
| const GURL& origin, |
| SyncStatusCode code) { |
| DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type || |
| chrome::NOTIFICATION_EXTENSION_UNINSTALLED == type); |
| if (code != SYNC_STATUS_OK && |
| code != SYNC_STATUS_UNKNOWN_ORIGIN) { |
| switch (type) { |
| case chrome::NOTIFICATION_EXTENSION_UNLOADED: |
| util::Log(logging::LOG_WARNING, |
| FROM_HERE, |
| "Disabling origin for UNLOADED(DISABLE) failed: %s", |
| origin.spec().c_str()); |
| break; |
| case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: |
| util::Log(logging::LOG_WARNING, |
| FROM_HERE, |
| "Uninstall origin for UNINSTALLED failed: %s", |
| origin.spec().c_str()); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| void DidHandleOriginForExtensionEnabledEvent( |
| int type, |
| const GURL& origin, |
| SyncStatusCode code) { |
| DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type); |
| if (code != SYNC_STATUS_OK) |
| util::Log(logging::LOG_WARNING, |
| FROM_HERE, |
| "Enabling origin for ENABLED failed: %s", |
| origin.spec().c_str()); |
| } |
| |
| std::string SyncFileStatusToString(SyncFileStatus sync_file_status) { |
| return extensions::api::sync_file_system::ToString( |
| extensions::SyncFileStatusToExtensionEnum(sync_file_status)); |
| } |
| |
| // Gets called repeatedly until every SyncFileStatus has been mapped. |
| void DidGetFileSyncStatusForDump( |
| base::ListValue* files, |
| size_t* num_results, |
| const SyncFileSystemService::DumpFilesCallback& callback, |
| base::DictionaryValue* file, |
| SyncStatusCode sync_status_code, |
| SyncFileStatus sync_file_status) { |
| DCHECK(files); |
| DCHECK(num_results); |
| |
| if (file) |
| file->SetString("status", SyncFileStatusToString(sync_file_status)); |
| |
| // Once all results have been received, run the callback to signal end. |
| DCHECK_LE(*num_results, files->GetSize()); |
| if (++*num_results < files->GetSize()) |
| return; |
| |
| callback.Run(files); |
| } |
| |
| } // namespace |
| |
| class SyncFileSystemService::SyncRunner { |
| public: |
| typedef base::Callback<void(const SyncStatusCallback&)> Task; |
| SyncRunner(const std::string& task_name, |
| const Task& sync_task, |
| RemoteFileSyncService* remote_service) |
| : task_name_(task_name), |
| sync_task_(sync_task), |
| remote_service_(remote_service), |
| current_delay_(0), |
| last_delay_(0), |
| pending_changes_(0), |
| running_(false), |
| factory_(this) {} |
| ~SyncRunner() {} |
| |
| void Schedule() { |
| int64 delay = kSyncDelayInMilliseconds; |
| if (pending_changes_ == 0) { |
| ScheduleInternal(kSyncDelayMaxInMilliseconds); |
| return; |
| } |
| switch (remote_service_->GetCurrentState()) { |
| case REMOTE_SERVICE_OK: |
| if (pending_changes_ > kPendingChangeThresholdForFastSync) |
| delay = kSyncDelayFastInMilliseconds; |
| else |
| delay = kSyncDelayInMilliseconds; |
| break; |
| |
| case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE: |
| delay = kSyncDelaySlowInMilliseconds; |
| if (last_delay_ >= kSyncDelaySlowInMilliseconds && |
| last_delay_ < kSyncDelayMaxInMilliseconds) |
| delay = last_delay_ * 2; |
| break; |
| |
| case REMOTE_SERVICE_AUTHENTICATION_REQUIRED: |
| case REMOTE_SERVICE_DISABLED: |
| delay = kSyncDelayMaxInMilliseconds; |
| break; |
| } |
| ScheduleInternal(delay); |
| } |
| |
| void ScheduleIfNotRunning() { |
| if (!timer_.IsRunning()) |
| Schedule(); |
| } |
| |
| void OnChangesUpdated(int64 pending_changes) { |
| DCHECK_GE(pending_changes, 0); |
| if (pending_changes_ != pending_changes) { |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "[%s] pending_changes updated: %" PRId64, |
| task_name_.c_str(), pending_changes); |
| } |
| pending_changes_ = pending_changes; |
| Schedule(); |
| } |
| |
| int64 pending_changes() const { return pending_changes_; } |
| |
| private: |
| void Finished(SyncStatusCode status) { |
| DCHECK(running_); |
| running_ = false; |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "[%s] * Finished (elapsed: %" PRId64 " sec)", |
| task_name_.c_str(), |
| (base::Time::Now() - last_scheduled_).InSeconds()); |
| if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC || |
| status == SYNC_STATUS_FILE_BUSY) |
| ScheduleInternal(kSyncDelayMaxInMilliseconds); |
| else |
| Schedule(); |
| } |
| |
| void Run() { |
| if (running_) |
| return; |
| running_ = true; |
| last_scheduled_ = base::Time::Now(); |
| last_delay_ = current_delay_; |
| |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "[%s] * Started", task_name_.c_str()); |
| |
| sync_task_.Run(base::Bind(&SyncRunner::Finished, factory_.GetWeakPtr())); |
| } |
| |
| void ScheduleInternal(int64 delay) { |
| base::TimeDelta time_to_next = base::TimeDelta::FromMilliseconds(delay); |
| |
| if (timer_.IsRunning()) { |
| if (current_delay_ == delay) |
| return; |
| |
| base::TimeDelta elapsed = base::Time::Now() - last_scheduled_; |
| if (elapsed < time_to_next) { |
| time_to_next = time_to_next - elapsed; |
| } else { |
| time_to_next = base::TimeDelta::FromMilliseconds( |
| kSyncDelayFastInMilliseconds); |
| } |
| timer_.Stop(); |
| } |
| |
| if (current_delay_ != delay) { |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "[%s] Scheduling task in %" PRId64 " secs", |
| task_name_.c_str(), time_to_next.InSeconds()); |
| } |
| current_delay_ = delay; |
| |
| timer_.Start(FROM_HERE, time_to_next, this, &SyncRunner::Run); |
| } |
| |
| std::string task_name_; |
| Task sync_task_; |
| RemoteFileSyncService* remote_service_; |
| base::OneShotTimer<SyncRunner> timer_; |
| base::Time last_scheduled_; |
| int64 current_delay_; |
| int64 last_delay_; |
| int64 pending_changes_; |
| bool running_; |
| base::WeakPtrFactory<SyncRunner> factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SyncRunner); |
| }; |
| |
| //----------------------------------------------------------------------------- |
| |
| void SyncFileSystemService::Shutdown() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| local_sync_.reset(); |
| remote_sync_.reset(); |
| |
| local_file_service_->Shutdown(); |
| local_file_service_.reset(); |
| |
| remote_file_service_.reset(); |
| |
| ProfileSyncServiceBase* profile_sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (profile_sync_service) |
| profile_sync_service->RemoveObserver(this); |
| |
| profile_ = NULL; |
| } |
| |
| SyncFileSystemService::~SyncFileSystemService() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(!profile_); |
| } |
| |
| void SyncFileSystemService::InitializeForApp( |
| fileapi::FileSystemContext* file_system_context, |
| const GURL& app_origin, |
| const SyncStatusCallback& callback) { |
| DCHECK(local_file_service_); |
| DCHECK(remote_file_service_); |
| DCHECK(app_origin == app_origin.GetOrigin()); |
| |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "Initializing for App: %s", app_origin.spec().c_str()); |
| |
| local_file_service_->MaybeInitializeFileSystemContext( |
| app_origin, file_system_context, |
| base::Bind(&SyncFileSystemService::DidInitializeFileSystem, |
| AsWeakPtr(), app_origin, callback)); |
| } |
| |
| SyncServiceState SyncFileSystemService::GetSyncServiceState() { |
| return RemoteStateToSyncServiceState(remote_file_service_->GetCurrentState()); |
| } |
| |
| void SyncFileSystemService::GetExtensionStatusMap( |
| std::map<GURL, std::string>* status_map) { |
| DCHECK(status_map); |
| remote_file_service_->GetOriginStatusMap(status_map); |
| } |
| |
| void SyncFileSystemService::DumpFiles(const GURL& origin, |
| const DumpFilesCallback& callback) { |
| DCHECK(!origin.is_empty()); |
| |
| content::StoragePartition* storage_partition = |
| content::BrowserContext::GetStoragePartitionForSite(profile_, origin); |
| fileapi::FileSystemContext* file_system_context = |
| storage_partition->GetFileSystemContext(); |
| local_file_service_->MaybeInitializeFileSystemContext( |
| origin, file_system_context, |
| base::Bind(&SyncFileSystemService::DidInitializeFileSystemForDump, |
| AsWeakPtr(), origin, callback)); |
| } |
| |
| void SyncFileSystemService::GetFileSyncStatus( |
| const FileSystemURL& url, const SyncFileStatusCallback& callback) { |
| DCHECK(local_file_service_); |
| DCHECK(remote_file_service_); |
| |
| // It's possible to get an invalid FileEntry. |
| if (!url.is_valid()) { |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| base::Bind(callback, |
| SYNC_FILE_ERROR_INVALID_URL, |
| SYNC_FILE_STATUS_UNKNOWN)); |
| return; |
| } |
| |
| if (remote_file_service_->IsConflicting(url)) { |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| base::Bind(callback, |
| SYNC_STATUS_OK, |
| SYNC_FILE_STATUS_CONFLICTING)); |
| return; |
| } |
| |
| local_file_service_->HasPendingLocalChanges( |
| url, |
| base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus, |
| AsWeakPtr(), callback)); |
| } |
| |
| void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void SyncFileSystemService::RemoveSyncEventObserver( |
| SyncEventObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| ConflictResolutionPolicy |
| SyncFileSystemService::GetConflictResolutionPolicy() const { |
| return remote_file_service_->GetConflictResolutionPolicy(); |
| } |
| |
| SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy( |
| ConflictResolutionPolicy policy) { |
| return remote_file_service_->SetConflictResolutionPolicy(policy); |
| } |
| |
| SyncFileSystemService::SyncFileSystemService(Profile* profile) |
| : profile_(profile), |
| sync_enabled_(true) { |
| } |
| |
| void SyncFileSystemService::Initialize( |
| scoped_ptr<LocalFileSyncService> local_file_service, |
| scoped_ptr<RemoteFileSyncService> remote_file_service) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(local_file_service); |
| DCHECK(remote_file_service); |
| DCHECK(profile_); |
| |
| local_file_service_ = local_file_service.Pass(); |
| remote_file_service_ = remote_file_service.Pass(); |
| |
| local_file_service_->AddChangeObserver(this); |
| local_file_service_->SetLocalChangeProcessor( |
| remote_file_service_->GetLocalChangeProcessor()); |
| |
| remote_file_service_->AddServiceObserver(this); |
| remote_file_service_->AddFileStatusObserver(this); |
| remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get()); |
| |
| local_sync_.reset(new SyncRunner( |
| "Local sync", |
| base::Bind(&SyncFileSystemService::StartLocalSync, AsWeakPtr()), |
| remote_file_service_.get())); |
| remote_sync_.reset(new SyncRunner( |
| "Remote sync", |
| base::Bind(&SyncFileSystemService::StartRemoteSync, AsWeakPtr()), |
| remote_file_service_.get())); |
| |
| ProfileSyncServiceBase* profile_sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (profile_sync_service) { |
| UpdateSyncEnabledStatus(profile_sync_service); |
| profile_sync_service->AddObserver(this); |
| } |
| |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| content::Source<Profile>(profile_)); |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| content::Source<Profile>(profile_)); |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, |
| content::Source<Profile>(profile_)); |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED, |
| content::Source<Profile>(profile_)); |
| } |
| |
| void SyncFileSystemService::DidInitializeFileSystem( |
| const GURL& app_origin, |
| const SyncStatusCallback& callback, |
| SyncStatusCode status) { |
| DVLOG(1) << "DidInitializeFileSystem: " |
| << app_origin.spec() << " " << status; |
| |
| if (status != SYNC_STATUS_OK) { |
| callback.Run(status); |
| return; |
| } |
| |
| // Local side of initialization for the app is done. |
| // Continue on initializing the remote side. |
| remote_file_service_->RegisterOrigin( |
| app_origin, |
| base::Bind(&SyncFileSystemService::DidRegisterOrigin, |
| AsWeakPtr(), app_origin, callback)); |
| } |
| |
| void SyncFileSystemService::DidRegisterOrigin( |
| const GURL& app_origin, |
| const SyncStatusCallback& callback, |
| SyncStatusCode status) { |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "DidInitializeForApp (registered the origin): %s: %s", |
| app_origin.spec().c_str(), |
| SyncStatusCodeToString(status)); |
| |
| callback.Run(status); |
| } |
| |
| void SyncFileSystemService::DidInitializeFileSystemForDump( |
| const GURL& origin, |
| const DumpFilesCallback& callback, |
| SyncStatusCode status) { |
| DCHECK(!origin.is_empty()); |
| |
| if (status != SYNC_STATUS_OK) { |
| base::ListValue empty_result; |
| callback.Run(&empty_result); |
| return; |
| } |
| |
| base::ListValue* files = remote_file_service_->DumpFiles(origin).release(); |
| if (!files->GetSize()) { |
| callback.Run(files); |
| return; |
| } |
| |
| base::Callback<void(base::DictionaryValue* file, |
| SyncStatusCode sync_status, |
| SyncFileStatus sync_file_status)> completion_callback = |
| base::Bind(&DidGetFileSyncStatusForDump, base::Owned(files), |
| base::Owned(new size_t(0)), callback); |
| |
| // After all metadata loaded, sync status can be added to each entry. |
| for (size_t i = 0; i < files->GetSize(); ++i) { |
| base::DictionaryValue* file = NULL; |
| std::string path_string; |
| if (!files->GetDictionary(i, &file) || |
| !file->GetString("path", &path_string)) { |
| NOTREACHED(); |
| completion_callback.Run( |
| NULL, SYNC_FILE_ERROR_FAILED, SYNC_FILE_STATUS_UNKNOWN); |
| continue; |
| } |
| |
| base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_string); |
| FileSystemURL url = CreateSyncableFileSystemURL(origin, file_path); |
| GetFileSyncStatus(url, base::Bind(completion_callback, file)); |
| } |
| } |
| |
| void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) { |
| sync_enabled_ = enabled; |
| remote_file_service_->SetSyncEnabled(sync_enabled_); |
| } |
| |
| void SyncFileSystemService::StartLocalSync( |
| const SyncStatusCallback& callback) { |
| local_file_service_->ProcessLocalChange( |
| base::Bind(&SyncFileSystemService::DidProcessLocalChange, AsWeakPtr(), |
| callback)); |
| } |
| |
| void SyncFileSystemService::StartRemoteSync( |
| const SyncStatusCallback& callback) { |
| remote_file_service_->ProcessRemoteChange( |
| base::Bind(&SyncFileSystemService::DidProcessRemoteChange, AsWeakPtr(), |
| callback)); |
| } |
| |
| void SyncFileSystemService::DidProcessRemoteChange( |
| const SyncStatusCallback& callback, |
| SyncStatusCode status, |
| const FileSystemURL& url) { |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "ProcessRemoteChange finished with status=%d (%s) for url=%s", |
| status, SyncStatusCodeToString(status), url.DebugString().c_str()); |
| |
| if (status == SYNC_STATUS_FILE_BUSY) { |
| local_file_service_->RegisterURLForWaitingSync( |
| url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync, |
| AsWeakPtr())); |
| } |
| |
| callback.Run(status); |
| } |
| |
| void SyncFileSystemService::DidProcessLocalChange( |
| const SyncStatusCallback& callback, |
| SyncStatusCode status, |
| const FileSystemURL& url) { |
| util::Log(logging::LOG_VERBOSE, FROM_HERE, |
| "ProcessLocalChange finished with status=%d (%s) for url=%s", |
| status, SyncStatusCodeToString(status), url.DebugString().c_str()); |
| |
| callback.Run(status); |
| } |
| |
| void SyncFileSystemService::DidGetLocalChangeStatus( |
| const SyncFileStatusCallback& callback, |
| SyncStatusCode status, |
| bool has_pending_local_changes) { |
| callback.Run( |
| status, |
| has_pending_local_changes ? |
| SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED); |
| } |
| |
| void SyncFileSystemService::OnSyncEnabledForRemoteSync() { |
| remote_sync_->Schedule(); |
| } |
| |
| void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| local_sync_->OnChangesUpdated(pending_changes); |
| remote_sync_->ScheduleIfNotRunning(); |
| } |
| |
| void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| remote_sync_->OnChangesUpdated(pending_changes); |
| local_sync_->ScheduleIfNotRunning(); |
| } |
| |
| void SyncFileSystemService::OnRemoteServiceStateUpdated( |
| RemoteServiceState state, |
| const std::string& description) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| util::Log(logging::LOG_INFO, FROM_HERE, |
| "OnRemoteServiceStateChanged: %d %s", state, description.c_str()); |
| |
| remote_sync_->Schedule(); |
| local_sync_->Schedule(); |
| |
| FOR_EACH_OBSERVER( |
| SyncEventObserver, observers_, |
| OnSyncStateUpdated(GURL(), |
| RemoteStateToSyncServiceState(state), |
| description)); |
| } |
| |
| void SyncFileSystemService::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| // Event notification sequence. |
| // |
| // (User action) (Notification type) |
| // Install: INSTALLED. |
| // Update: INSTALLED. |
| // Uninstall: UNINSTALLED. |
| // Launch, Close: No notification. |
| // Enable: ENABLED. |
| // Disable: UNLOADED(DISABLE). |
| // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED. |
| // |
| switch (type) { |
| case chrome::NOTIFICATION_EXTENSION_INSTALLED: |
| HandleExtensionInstalled(details); |
| break; |
| case chrome::NOTIFICATION_EXTENSION_UNLOADED: |
| HandleExtensionUnloaded(type, details); |
| break; |
| case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: |
| HandleExtensionUninstalled(type, details); |
| break; |
| case chrome::NOTIFICATION_EXTENSION_ENABLED: |
| HandleExtensionEnabled(type, details); |
| break; |
| default: |
| NOTREACHED() << "Unknown notification."; |
| break; |
| } |
| } |
| |
| void SyncFileSystemService::HandleExtensionInstalled( |
| const content::NotificationDetails& details) { |
| const Extension* extension = |
| content::Details<const extensions::InstalledExtensionInfo>(details)-> |
| extension; |
| GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id()); |
| DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin; |
| // NOTE: When an app is uninstalled and re-installed in a sequence, |
| // |local_file_service_| may still keeps |app_origin| as disabled origin. |
| local_file_service_->SetOriginEnabled(app_origin, true); |
| } |
| |
| void SyncFileSystemService::HandleExtensionUnloaded( |
| int type, |
| const content::NotificationDetails& details) { |
| content::Details<const extensions::UnloadedExtensionInfo> info(details); |
| if (info->reason != extensions::UnloadedExtensionInfo::REASON_DISABLE) |
| return; |
| |
| std::string extension_id = info->extension->id(); |
| GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id); |
| |
| int reasons = ExtensionPrefs::Get(profile_)->GetDisableReasons(extension_id); |
| if (reasons & Extension::DISABLE_RELOAD) { |
| // Bypass disabling the origin since the app will be re-enabled soon. |
| // NOTE: If re-enabling the app fails, the app is disabled while it is |
| // handled as enabled origin in the SyncFS. This should be safe and will be |
| // recovered when the user re-enables the app manually or the sync service |
| // restarts. |
| DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE_RELOAD): " |
| << app_origin; |
| return; |
| } |
| |
| DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): " |
| << app_origin; |
| remote_file_service_->DisableOrigin( |
| app_origin, |
| base::Bind(&DidHandleOriginForExtensionUnloadedEvent, |
| type, app_origin)); |
| local_file_service_->SetOriginEnabled(app_origin, false); |
| } |
| |
| void SyncFileSystemService::HandleExtensionUninstalled( |
| int type, |
| const content::NotificationDetails& details) { |
| const Extension* extension = content::Details<const Extension>(details).ptr(); |
| DCHECK(extension); |
| |
| RemoteFileSyncService::UninstallFlag flag = |
| RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE; |
| // If it's loaded from an unpacked package and with key: field, |
| // the uninstall will not be sync'ed and the user might be using the |
| // same app key in other installs, so avoid purging the remote folder. |
| if (extensions::Manifest::IsUnpackedLocation(extension->location()) && |
| extension->manifest()->HasKey(extensions::manifest_keys::kKey)) { |
| flag = RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE; |
| } |
| |
| GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id()); |
| DVLOG(1) << "Handle extension notification for UNINSTALLED: " |
| << app_origin; |
| remote_file_service_->UninstallOrigin( |
| app_origin, flag, |
| base::Bind(&DidHandleOriginForExtensionUnloadedEvent, |
| type, app_origin)); |
| local_file_service_->SetOriginEnabled(app_origin, false); |
| } |
| |
| void SyncFileSystemService::HandleExtensionEnabled( |
| int type, |
| const content::NotificationDetails& details) { |
| std::string extension_id = content::Details<const Extension>(details)->id(); |
| GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id); |
| DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin; |
| remote_file_service_->EnableOrigin( |
| app_origin, |
| base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin)); |
| local_file_service_->SetOriginEnabled(app_origin, true); |
| } |
| |
| void SyncFileSystemService::OnStateChanged() { |
| ProfileSyncServiceBase* profile_sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (profile_sync_service) |
| UpdateSyncEnabledStatus(profile_sync_service); |
| } |
| |
| void SyncFileSystemService::OnFileStatusChanged( |
| const FileSystemURL& url, |
| SyncFileStatus sync_status, |
| SyncAction action_taken, |
| SyncDirection direction) { |
| FOR_EACH_OBSERVER( |
| SyncEventObserver, observers_, |
| OnFileSynced(url, sync_status, action_taken, direction)); |
| } |
| |
| void SyncFileSystemService::UpdateSyncEnabledStatus( |
| ProfileSyncServiceBase* profile_sync_service) { |
| if (!profile_sync_service->HasSyncSetupCompleted()) |
| return; |
| bool old_sync_enabled = sync_enabled_; |
| sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has( |
| syncer::APPS); |
| remote_file_service_->SetSyncEnabled(sync_enabled_); |
| if (!old_sync_enabled && sync_enabled_) { |
| local_sync_->Schedule(); |
| remote_sync_->Schedule(); |
| } |
| } |
| |
| } // namespace sync_file_system |