Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / apps / ephemeral_app_service.cc
blob612b21437f958d44cfff3ed4f257108a593a2a11
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/apps/ephemeral_app_service.h"
7 #include "base/command_line.h"
8 #include "chrome/browser/apps/ephemeral_app_service_factory.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/extension_prefs.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_system.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_set.h"
22 using extensions::Extension;
23 using extensions::ExtensionPrefs;
24 using extensions::ExtensionSet;
25 using extensions::ExtensionSystem;
26 using extensions::InstalledExtensionInfo;
28 namespace {
30 // The number of seconds after startup before performing garbage collection
31 // of ephemeral apps.
32 const int kGarbageCollectStartupDelay = 60;
34 // The number of seconds after an ephemeral app has been installed before
35 // performing garbage collection.
36 const int kGarbageCollectInstallDelay = 15;
38 // When the number of ephemeral apps reaches this count, trigger garbage
39 // collection to trim off the least-recently used apps in excess of
40 // kMaxEphemeralAppsCount.
41 const int kGarbageCollectTriggerCount = 35;
43 } // namespace
45 // The number of days of inactivity before an ephemeral app will be removed.
46 const int EphemeralAppService::kAppInactiveThreshold = 10;
48 // If the ephemeral app has been launched within this number of days, it will
49 // definitely not be garbage collected.
50 const int EphemeralAppService::kAppKeepThreshold = 1;
52 // The maximum number of ephemeral apps to keep cached. Excess may be removed.
53 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
55 // static
56 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
57 return EphemeralAppServiceFactory::GetForProfile(profile);
60 EphemeralAppService::EphemeralAppService(Profile* profile)
61 : profile_(profile),
62 ephemeral_app_count_(-1) {
63 if (!CommandLine::ForCurrentProcess()->HasSwitch(
64 switches::kEnableEphemeralApps))
65 return;
67 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
68 content::Source<Profile>(profile_));
69 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
70 content::Source<Profile>(profile_));
71 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
72 content::Source<Profile>(profile_));
73 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
74 content::Source<Profile>(profile_));
77 EphemeralAppService::~EphemeralAppService() {
80 void EphemeralAppService::Observe(
81 int type,
82 const content::NotificationSource& source,
83 const content::NotificationDetails& details) {
84 switch (type) {
85 case chrome::NOTIFICATION_EXTENSIONS_READY: {
86 Init();
87 break;
89 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
90 const Extension* extension =
91 content::Details<const InstalledExtensionInfo>(details)->extension;
92 DCHECK(extension);
93 if (extension->is_ephemeral()) {
94 ++ephemeral_app_count_;
95 if (ephemeral_app_count_ >= kGarbageCollectTriggerCount)
96 TriggerGarbageCollect(
97 base::TimeDelta::FromSeconds(kGarbageCollectInstallDelay));
99 break;
101 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
102 const Extension* extension =
103 content::Details<const Extension>(details).ptr();
104 DCHECK(extension);
105 if (extension->is_ephemeral())
106 --ephemeral_app_count_;
107 break;
109 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
110 // Ideally we need to know when the extension system is shutting down.
111 garbage_collect_timer_.Stop();
112 break;
114 default:
115 NOTREACHED();
119 void EphemeralAppService::Init() {
120 InitEphemeralAppCount();
121 TriggerGarbageCollect(
122 base::TimeDelta::FromSeconds(kGarbageCollectStartupDelay));
125 void EphemeralAppService::InitEphemeralAppCount() {
126 ExtensionService* service =
127 ExtensionSystem::Get(profile_)->extension_service();
128 DCHECK(service);
129 scoped_ptr<ExtensionSet> extensions =
130 service->GenerateInstalledExtensionsSet();
132 ephemeral_app_count_ = 0;
133 for (ExtensionSet::const_iterator it = extensions->begin();
134 it != extensions->end(); ++it) {
135 const Extension* extension = *it;
136 if (extension->is_ephemeral())
137 ++ephemeral_app_count_;
141 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
142 if (!garbage_collect_timer_.IsRunning())
143 garbage_collect_timer_.Start(
144 FROM_HERE,
145 delay,
146 this,
147 &EphemeralAppService::GarbageCollectApps);
150 void EphemeralAppService::GarbageCollectApps() {
151 ExtensionSystem* system = ExtensionSystem::Get(profile_);
152 DCHECK(system);
153 ExtensionService* service = system->extension_service();
154 DCHECK(service);
155 ExtensionPrefs* prefs = service->extension_prefs();
156 scoped_ptr<ExtensionSet> extensions =
157 service->GenerateInstalledExtensionsSet();
159 int app_count = 0;
160 LaunchTimeAppMap app_launch_times;
161 std::set<std::string> remove_app_ids;
163 // Populate a list of ephemeral apps, ordered by their last launch time.
164 for (ExtensionSet::const_iterator it = extensions->begin();
165 it != extensions->end(); ++it) {
166 const Extension* extension = *it;
167 if (!extension->is_ephemeral())
168 continue;
170 ++app_count;
172 // Do not remove ephemeral apps that are running.
173 if (!extension_util::IsExtensionIdle(extension->id(), system))
174 continue;
176 base::Time last_launch_time = prefs->GetLastLaunchTime(extension->id());
178 // If the last launch time is invalid, this may be because it was just
179 // installed. So use the install time. If this is also null for some reason,
180 // the app will be removed.
181 if (last_launch_time.is_null())
182 last_launch_time = prefs->GetInstallTime(extension->id());
184 app_launch_times.insert(std::make_pair(last_launch_time, extension->id()));
187 // Execute the replacement policies and remove apps marked for deletion.
188 if (!app_launch_times.empty()) {
189 GetAppsToRemove(app_count, app_launch_times, &remove_app_ids);
190 for (std::set<std::string>::const_iterator id = remove_app_ids.begin();
191 id != remove_app_ids.end(); ++id) {
192 if (service->UninstallExtension(*id, false, NULL))
193 --app_count;
197 ephemeral_app_count_ = app_count;
200 // static
201 void EphemeralAppService::GetAppsToRemove(
202 int app_count,
203 const LaunchTimeAppMap& app_launch_times,
204 std::set<std::string>* remove_app_ids) {
205 base::Time time_now = base::Time::Now();
206 const base::Time inactive_threshold =
207 time_now - base::TimeDelta::FromDays(kAppInactiveThreshold);
208 const base::Time keep_threshold =
209 time_now - base::TimeDelta::FromDays(kAppKeepThreshold);
211 // Visit the apps in order of least recently to most recently launched.
212 for (LaunchTimeAppMap::const_iterator it = app_launch_times.begin();
213 it != app_launch_times.end(); ++it) {
214 // Cannot remove apps that have been launched recently. So break when we
215 // reach the new apps.
216 if (it->first > keep_threshold)
217 break;
219 // Remove ephemeral apps that have been inactive for a while or if the cache
220 // is larger than the desired size.
221 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
222 remove_app_ids->insert(it->second);
223 --app_count;
224 } else {
225 break;