Chromium Code Reviews| Index: chrome/browser/apps/ephemeral_app_service.cc |
| diff --git a/chrome/browser/apps/ephemeral_app_service.cc b/chrome/browser/apps/ephemeral_app_service.cc |
| index 0fbc007979c111fb2cb324f58864676ce208b571..f0f3703ea2e5dc03b2d65141cc92dcc02012262b 100644 |
| --- a/chrome/browser/apps/ephemeral_app_service.cc |
| +++ b/chrome/browser/apps/ephemeral_app_service.cc |
| @@ -4,7 +4,9 @@ |
| #include "chrome/browser/apps/ephemeral_app_service.h" |
| +#include "apps/app_lifetime_monitor_factory.h" |
| #include "base/command_line.h" |
| +#include "base/message_loop/message_loop.h" |
| #include "chrome/browser/apps/ephemeral_app_service_factory.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| @@ -43,6 +45,13 @@ const int kGarbageCollectAppsInstallDelay = 15; |
| // kMaxEphemeralAppsCount. |
| const int kGarbageCollectAppsTriggerCount = 35; |
| +// The number of seconds after an app has stopped running before it will be |
| +// unloaded and disabled. |
| +const int kDefaultUnloadAppDelay = 1; |
| + |
| +// The number of seconds after startup before unloading inactive ephemeral apps. |
| +const int kUnloadAppsOnStartupDelay = 5; |
| + |
| } // namespace |
| const int EphemeralAppService::kAppInactiveThreshold = 10; |
| @@ -57,16 +66,11 @@ EphemeralAppService* EphemeralAppService::Get(Profile* profile) { |
| EphemeralAppService::EphemeralAppService(Profile* profile) |
| : profile_(profile), |
| extension_registry_observer_(this), |
| - ephemeral_app_count_(-1) { |
| - if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kEnableEphemeralApps)) |
| - return; |
| - |
| - extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); |
| + ephemeral_app_count_(-1), |
| + unload_idle_app_delay_(kDefaultUnloadAppDelay), |
| + weak_ptr_factory_(this) { |
| registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, |
| content::Source<Profile>(profile_)); |
| - registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, |
| - content::Source<Profile>(profile_)); |
| } |
| EphemeralAppService::~EphemeralAppService() { |
| @@ -116,11 +120,6 @@ void EphemeralAppService::Observe( |
| Init(); |
| break; |
| } |
| - case chrome::NOTIFICATION_PROFILE_DESTROYED: { |
| - // Ideally we need to know when the extension system is shutting down. |
| - garbage_collect_apps_timer_.Stop(); |
| - break; |
| - } |
| default: |
| NOTREACHED(); |
| } |
| @@ -136,8 +135,10 @@ void EphemeralAppService::OnExtensionWillBeInstalled( |
| // An ephemeral app was just promoted to a regular installed app. |
| --ephemeral_app_count_; |
| DCHECK_GE(ephemeral_app_count_, 0); |
| + HandleEphemeralAppPromoted(extension); |
| } else if (!is_update && |
| extensions::util::IsEphemeralApp(extension->id(), profile_)) { |
| + // A new ephemeral app was launched. |
| ++ephemeral_app_count_; |
| if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) { |
| TriggerGarbageCollect( |
| @@ -156,10 +157,53 @@ void EphemeralAppService::OnExtensionUninstalled( |
| } |
| } |
| +void EphemeralAppService::OnAppStop(Profile* profile, |
| + const std::string& app_id) { |
| + if (!extensions::util::IsEphemeralApp(app_id, profile_)) |
| + return; |
| + |
| + base::MessageLoop::current()->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&EphemeralAppService::UnloadEphemeralApp, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + app_id), |
| + base::TimeDelta::FromSeconds(unload_idle_app_delay_)); |
| +} |
| + |
| +void EphemeralAppService::OnChromeTerminating() { |
| + garbage_collect_apps_timer_.Stop(); |
| + |
| + extension_registry_observer_.RemoveAll(); |
| + |
| + apps::AppLifetimeMonitor* lifetime_monitor = |
| + apps::AppLifetimeMonitorFactory::GetForProfile(profile_); |
| + DCHECK(lifetime_monitor); |
| + lifetime_monitor->RemoveObserver(this); |
| +} |
| + |
| void EphemeralAppService::Init() { |
| InitEphemeralAppCount(); |
| + |
| + // Start observing. |
| + extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); |
| + |
| + apps::AppLifetimeMonitor* lifetime_monitor = |
| + apps::AppLifetimeMonitorFactory::GetForProfile(profile_); |
| + DCHECK(lifetime_monitor); |
| + lifetime_monitor->AddObserver(this); |
| + |
| + // Execute startup clean up tasks (except during tests). |
| + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) |
| + return; |
| + |
| TriggerGarbageCollect( |
| base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay)); |
| + |
| + base::MessageLoop::current()->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&EphemeralAppService::UnloadEphemeralAppsOnStartup, |
| + weak_ptr_factory_.GetWeakPtr()), |
| + base::TimeDelta::FromSeconds(kUnloadAppsOnStartupDelay)); |
| } |
| void EphemeralAppService::InitEphemeralAppCount() { |
| @@ -177,6 +221,73 @@ void EphemeralAppService::InitEphemeralAppCount() { |
| } |
| } |
| +void EphemeralAppService::UnloadEphemeralApp(const std::string& app_id) { |
| + if (!extensions::util::IsEphemeralApp(app_id, profile_) || |
| + !extensions::util::IsExtensionIdle(app_id, profile_)) { |
| + return; |
| + } |
| + |
| + // After an ephemeral app has stopped running, unload it from extension |
| + // system to prevent all background activity. |
| + ExtensionService* service = |
| + ExtensionSystem::Get(profile_)->extension_service(); |
| + DCHECK(service); |
| + service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP); |
| +} |
| + |
| +void EphemeralAppService::UnloadEphemeralAppsOnStartup() { |
| + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); |
| + DCHECK(prefs); |
| + ExtensionService* service = |
| + ExtensionSystem::Get(profile_)->extension_service(); |
| + DCHECK(service); |
| + |
| + // Ensure that all inactive ephemeral apps are unloaded from extension system |
| + // to prevent all background activity. This is done on startup to catch any |
| + // apps that escaped unloading on shutdown. |
| + scoped_ptr<ExtensionSet> extensions = |
| + ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet(); |
| + for (ExtensionSet::const_iterator it = extensions->begin(); |
| + it != extensions->end(); |
| + ++it) { |
| + const Extension* extension = *it; |
| + if (!prefs->IsEphemeralApp(extension->id())) |
| + continue; |
| + |
| + // Only V2 apps are installed ephemerally. Remove other ephemeral app types |
| + // that were cached before this policy was introduced. |
| + if (!extension->is_platform_app()) { |
| + service->UninstallExtension( |
| + extension->id(), |
| + extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, |
| + NULL); |
| + } |
| + |
| + if (!prefs->IsExtensionDisabled(extension->id()) && |
|
benwells
2014/07/29 08:58:42
Does this mean if an ephemeral app is disabled for
tmdiep
2014/08/04 06:22:53
Good catch. If the app happened to be disabled for
|
| + !prefs->IsExtensionRunning(extension->id()) && |
| + extensions::util::IsExtensionIdle(extension->id(), profile_)) { |
| + service->DisableExtension(extension->id(), |
| + Extension::DISABLE_INACTIVE_EPHEMERAL_APP); |
| + } |
| + } |
| +} |
| + |
| +void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) { |
| + // When ephemeral apps are promoted to regular install apps, remove the |
| + // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no |
| + // other reasons. |
| + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); |
| + DCHECK(prefs); |
| + |
| + int disable_reasons = prefs->GetDisableReasons(app->id()); |
| + if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) { |
| + prefs->RemoveDisableReason(app->id(), |
| + Extension::DISABLE_INACTIVE_EPHEMERAL_APP); |
| + if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP) |
| + prefs->SetExtensionState(app->id(), Extension::ENABLED); |
| + } |
| +} |
| + |
| void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) { |
| if (!garbage_collect_apps_timer_.IsRunning()) { |
| garbage_collect_apps_timer_.Start( |