Commit 9a87b6cd authored by tmdiep@chromium.org's avatar tmdiep@chromium.org

Remove the ability to retain local data of evicted ephemeral apps

This is effectively a revert of http://crrev.com/202763005

BUG=339004

Review URL: https://codereview.chromium.org/337323003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277972 0039d316-1c4b-4281-b951-d872f2087c98
parent 170e6d1b
......@@ -7,7 +7,6 @@
#include "base/command_line.h"
#include "chrome/browser/apps/ephemeral_app_service_factory.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/data_deleter.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
......@@ -23,11 +22,9 @@
#include "extensions/common/extension_set.h"
using extensions::Extension;
using extensions::ExtensionInfo;
using extensions::ExtensionPrefs;
using extensions::ExtensionSet;
using extensions::ExtensionSystem;
using extensions::InstalledExtensionInfo;
namespace {
......@@ -44,16 +41,11 @@ const int kGarbageCollectAppsInstallDelay = 15;
// kMaxEphemeralAppsCount.
const int kGarbageCollectAppsTriggerCount = 35;
// The number of seconds after startup before performing garbage collection
// of the data of evicted ephemeral apps.
const int kGarbageCollectDataStartupDelay = 120;
} // namespace
const int EphemeralAppService::kAppInactiveThreshold = 10;
const int EphemeralAppService::kAppKeepThreshold = 1;
const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
const int EphemeralAppService::kDataInactiveThreshold = 90;
// static
EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
......@@ -131,12 +123,6 @@ void EphemeralAppService::Init() {
InitEphemeralAppCount();
TriggerGarbageCollect(
base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay));
garbage_collect_data_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(kGarbageCollectDataStartupDelay),
this,
&EphemeralAppService::GarbageCollectData);
}
void EphemeralAppService::InitEphemeralAppCount() {
......@@ -245,52 +231,3 @@ void EphemeralAppService::GetAppsToRemove(
}
}
}
void EphemeralAppService::GarbageCollectData() {
ExtensionService* service =
ExtensionSystem::Get(profile_)->extension_service();
DCHECK(service);
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
DCHECK(prefs);
scoped_ptr<ExtensionPrefs::ExtensionsInfo> evicted_apps_info(
prefs->GetEvictedEphemeralAppsInfo());
base::Time time_now = base::Time::Now();
const base::Time inactive_threshold =
time_now - base::TimeDelta::FromDays(kDataInactiveThreshold);
for (size_t i = 0; i < evicted_apps_info->size(); ++i) {
ExtensionInfo* info = evicted_apps_info->at(i).get();
base::Time last_launch_time = prefs->GetLastLaunchTime(info->extension_id);
if (last_launch_time > inactive_threshold)
continue;
// Sanity check to ensure the app is not currently installed.
if (service->GetInstalledExtension(info->extension_id)) {
NOTREACHED();
continue;
}
// Ensure the app is not waiting to be installed.
scoped_ptr<ExtensionInfo> delayed_install(
prefs->GetDelayedInstallInfo(info->extension_id));
if (delayed_install.get())
continue;
if (info->extension_manifest.get()) {
std::string error;
scoped_refptr<const Extension> extension(Extension::Create(
info->extension_path,
info->extension_location,
*info->extension_manifest,
prefs->GetCreationFlags(info->extension_id),
info->extension_id,
&error));
if (extension.get())
extensions::DataDeleter::StartDeleting(profile_, extension.get());
}
prefs->RemoveEvictedEphemeralApp(info->extension_id);
}
}
......@@ -45,9 +45,6 @@ class EphemeralAppService : public KeyedService,
static const int kAppKeepThreshold;
// The maximum number of ephemeral apps to keep cached. Excess may be removed.
static const int kMaxEphemeralAppsCount;
// The number of days of inactivity before the data of an already evicted
// ephemeral app will be removed.
static const int kDataInactiveThreshold;
private:
// A map used to order the ephemeral apps by their last launch time.
......@@ -79,25 +76,18 @@ class EphemeralAppService : public KeyedService,
const LaunchTimeAppMap& app_launch_times,
std::set<std::string>* remove_app_ids);
// Garbage collect the data of ephemeral apps that have been evicted and
// inactive for a long period of time.
void GarbageCollectData();
Profile* profile_;
content::NotificationRegistrar registrar_;
ScopedObserver<extensions::ExtensionRegistry,
extensions::ExtensionRegistryObserver>
extension_registry_observer_;
base::OneShotTimer<EphemeralAppService> garbage_collect_apps_timer_;
base::OneShotTimer<EphemeralAppService> garbage_collect_data_timer_;
// The count of cached ephemeral apps.
int ephemeral_app_count_;
friend class EphemeralAppBrowserTest;
friend class EphemeralAppServiceTest;
friend class EphemeralAppServiceBrowserTest;
......
......@@ -227,20 +227,6 @@ void ExtensionGarbageCollector::GarbageCollectIsolatedStorageIfNeeded() {
}
}
// The data of ephemeral apps can outlive their cache lifetime. Ensure
// they are not garbage collected.
scoped_ptr<ExtensionPrefs::ExtensionsInfo> evicted_apps_info(
extension_prefs->GetEvictedEphemeralAppsInfo());
for (size_t i = 0; i < evicted_apps_info->size(); ++i) {
ExtensionInfo* info = evicted_apps_info->at(i).get();
if (util::HasIsolatedStorage(*info)) {
active_paths->insert(content::BrowserContext::GetStoragePartitionForSite(
context_,
util::GetSiteForExtensionId(
info->extension_id, context_))->GetPath());
}
}
ExtensionService* service =
ExtensionSystem::Get(context_)->extension_service();
service->OnGarbageCollectIsolatedStorageStart();
......
......@@ -768,10 +768,7 @@ bool ExtensionService::UninstallExtension(
NOTREACHED();
}
// Do not remove the data of ephemeral apps. They will be garbage collected by
// EphemeralAppService.
if (!extension_prefs_->IsEphemeralApp(extension->id()))
extensions::DataDeleter::StartDeleting(profile_, extension.get());
extensions::DataDeleter::StartDeleting(profile_, extension.get());
UntrackTerminatedExtension(extension->id());
......
......@@ -256,22 +256,10 @@ bool SiteHasIsolatedStorage(const GURL& extension_site_url,
content::BrowserContext* context) {
const Extension* extension = ExtensionRegistry::Get(context)->
enabled_extensions().GetExtensionOrAppByURL(extension_site_url);
if (extension)
return AppIsolationInfo::HasIsolatedStorage(extension);
if (extension_site_url.SchemeIs(kExtensionScheme)) {
// The site URL may also be from an evicted ephemeral app. We do not
// immediately delete their data when they are removed from extension
// system.
ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
DCHECK(prefs);
scoped_ptr<ExtensionInfo> info = prefs->GetEvictedEphemeralAppInfo(
extension_site_url.host());
if (info.get())
return HasIsolatedStorage(*info);
}
if (!extension)
return false;
return false;
return AppIsolationInfo::HasIsolatedStorage(extension);
}
const gfx::ImageSkia& GetDefaultAppIcon() {
......
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANPxk3C4VXX4xnwrj
QcPozskZDd0lHEp+Pjl62niY5XTiWrZkYX2At521OeDV1Ylwfweg7uwxpatoylJP8
6o8X2qJWii9mp2j+LMnkckbt/dkI60xII58EazzPfZhRhzKZxsoE4Ytt2D7zhyRbE
a7qXyI8S4nD0Qj7w/9ac6lDh/AgMBAAECgYB/v2aNVK4+U1rf0ShKD0TmCwNU4bHv
m8rzyzHgOpKn5j835jfutN/500p02Re1V0DbhFEGuoCYpcRoyDvrhq03ZWgpidE2e
8NQ3A0EXYLAUwuOlsdn/jcMEwRtrHpgiXrPMxpn2I0kSRklHYI66KnEvAt6UBvmYG
5g9NJGQ1Zs0QJBAP+nkuYwh28Av0eglgIs0SLev+D7PemKNHZUoYHcLJM3XAOKbaL
sb/xm16sdRKvN7qrMIv0jJYiBnLO6qKMCdysCQQDUOuIg664kDK20atzjUsddhBvG
2czxOsdFaYFHDlZnu9x96rw2xCLbgArg9IypyVw2m5lPYwP7y5iz76Sd49n9AkBvr
u8XrF+d+H+XdOnTbWy3hQPh7x/u5Ddi8jnUFzFJ5sdFrLWUlSGe6/aPhCu5ui7nYm
Jun2oIJkckpQiCndMdAkBMd27UR7Z1vK+1iq9NpJy6gAf2DLF/1RrJUqtGq87MD27
xW2s3HFIm3iqNRO+NmUSFVGjXpqhfx8qcQmMAlIENAkBtde4Pk8q6AZMMPVN+dFEn
Tr2oJdvvASJ6HSLBWDaGTCOtPIuoCf6TXnfpTkNPE3BBZPspwDbwPyIB+f4D26Ny
-----END PRIVATE KEY-----
<!--
* Copyright 2014 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.
-->
<!DOCTYPE html>
<html>
<body>
<script src="index.js"></script>
</body>
</html>
// Copyright 2014 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.
var callbackPass = chrome.test.callbackPass;
var callbackFail = chrome.test.callbackFail;
var kSavedKey = 'ephemeral';
var kSavedValue = 'app';
var kTestFileName = 'ephemeral.txt';
var kTestDBName = 'ephemeral_db';
function FileSystemWriteError() {
chrome.test.fail('Filesystem write error');
}
function FileSystemReadError() {
chrome.test.fail('Filesystem read error');
}
function IndexDBError() {
chrome.test.fail('IndexDB error');
}
function WriteLocalStorage() {
var data = {};
data[kSavedKey] = kSavedValue;
chrome.storage.local.set(data, callbackPass(function() {}));
}
function WriteFileSystem() {
// Testing the existence of a file is sufficient.
window.webkitRequestFileSystem(
PERSISTENT,
512,
callbackPass(function(fs) {
fs.root.getFile(
kTestFileName,
{create: true, exclusive: true},
callbackPass(function(fileEntry) {
// Succeeded
}),
FileSystemWriteError);
}), FileSystemWriteError);
}
function WriteIndexedDB() {
var openDB = indexedDB.open(kTestDBName, 1);
openDB.onerror = IndexDBError;
openDB.onsuccess = callbackPass(function(e) {
var db = e.target.result;
var transaction = db.transaction([kTestDBName], 'readwrite');
var store = transaction.objectStore(kTestDBName);
var request = store.add(kSavedValue, kSavedKey);
request.onerror = IndexDBError;
request.onsuccess = callbackPass(function(e) {
// Succeeded
});
});
openDB.onupgradeneeded = function(e) {
var db = e.target.result;
db.createObjectStore(kTestDBName);
};
}
function ReadLocalStorage() {
chrome.storage.local.get(kSavedKey, callbackPass(function(items) {
chrome.test.assertTrue(typeof(items[kSavedKey]) !== 'undefined');
chrome.test.assertEq(kSavedValue, items[kSavedKey]);
}));
}
function ReadFileSystem() {
window.webkitRequestFileSystem(
PERSISTENT,
512,
callbackPass(function(fs) {
fs.root.getFile(
kTestFileName,
{},
callbackPass(function(fileEntry) {
// Succeeded
}),
FileSystemReadError);
}),
FileSystemReadError);
}
function ReadIndexedDB() {
var openDB = indexedDB.open(kTestDBName, 1);
openDB.onerror = IndexDBError;
openDB.onsuccess = callbackPass(function(e) {
var db = e.target.result;
var transaction = db.transaction([kTestDBName], 'readonly');
var store = transaction.objectStore(kTestDBName);
var request = store.get(kSavedKey);
request.onerror = IndexDBError;
request.onsuccess = callbackPass(function(e) {
chrome.test.assertEq(kSavedValue, e.target.result);
});
});
openDB.onupgradeneeded = function(e) {
chrome.test.fail('Indexed DB not initialized');
};
}
function CheckLocalStorageReset() {
chrome.storage.local.get(kSavedKey, callbackPass(function(items) {
chrome.test.assertEq('undefined', typeof(items[kSavedKey]));
}));
}
function CheckFileSystemReset() {
window.webkitRequestFileSystem(
PERSISTENT,
512,
callbackPass(function(fs) {
fs.root.getFile(
kTestFileName,
{},
function(fileEntry) {
chrome.test.fail('File ' + kTestFileName + ' should not exist');
},
callbackPass(function(e) {
// Expected failure
}));
}),
FileSystemReadError);
}
function CheckIndexedDBReset() {
var openDB = indexedDB.open(kTestDBName, 1);
openDB.onerror = IndexDBError;
openDB.onsuccess = callbackPass(function(e) {
var db = e.target.result;
chrome.test.assertFalse(db.objectStoreNames.contains(kTestDBName));
});
}
// Phase 1 - Write data to various storage types.
function WriteData() {
chrome.test.runTests([
WriteLocalStorage,
WriteFileSystem,
WriteIndexedDB
]);
}
// Phase 2 - Read data back from the various storage types.
function ReadData() {
chrome.test.runTests([
ReadLocalStorage,
ReadFileSystem,
ReadIndexedDB
]);
}
// Verify that all data has been reset.
function DataReset() {
chrome.test.runTests([
CheckLocalStorageReset,
CheckFileSystemReset,
CheckIndexedDBReset
]);
}
onload = function() {
chrome.test.sendMessage('launched', function(reply) {
window[reply]();
});
};
// Copyright 2014 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.
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {});
});
{
"name": "Ephemeral Apps Retain Data",
"version": "1.0",
"manifest_version": 2,
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT8ZNwuFV1+MZ8K40HD6M7JGQ3dJRxKfj45etp4mOV04lq2ZGF9gLedtTng1dWJcH8HoO7sMaWraMpST/OqPF9qiVoovZqdo/izJ5HJG7f3ZCOtMSCOfBGs8z32YUYcymcbKBOGLbdg+84ckWxGu6l8iPEuJw9EI+8P/WnOpQ4fwIDAQAB",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": ["storage", "unlimitedStorage"]
}
......@@ -182,11 +182,6 @@ const char kPrefGeometryCache[] = "geometry_cache";
// A preference that indicates when an extension is last launched.
const char kPrefLastLaunchTime[] = "last_launch_time";
// A preference that marks an ephemeral app that was evicted from the cache.
// Their data is retained and garbage collected when inactive for a long period
// of time.
const char kPrefEvictedEphemeralApp[] = "evicted_ephemeral_app";
// A preference indicating whether the extension is an ephemeral app.
const char kPrefEphemeralApp[] = "ephemeral_app";
......@@ -241,11 +236,6 @@ bool IsBlacklistBitSet(const base::DictionaryValue* ext) {
return ext->GetBoolean(kPrefBlacklist, &bool_value) && bool_value;
}
bool IsEvictedEphemeralApp(const base::DictionaryValue* ext) {
bool bool_value;
return ext->GetBoolean(kPrefEvictedEphemeralApp, &bool_value) && bool_value;
}
void LoadExtensionControlledPrefs(ExtensionPrefs* prefs,
ExtensionPrefValueMap* value_map,
const std::string& extension_id,
......@@ -1279,13 +1269,7 @@ void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id,
observer_list_,
OnExtensionStateChanged(extension_id, false));
} else {
if (IsEphemeralApp(extension_id)) {
// Keep ephemeral apps around, but mark them as evicted.
UpdateExtensionPref(extension_id, kPrefEvictedEphemeralApp,
new base::FundamentalValue(true));
} else {
DeleteExtensionPrefs(extension_id);
}
DeleteExtensionPrefs(extension_id);
}
}
......@@ -1413,11 +1397,6 @@ scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledExtensionInfo(
return scoped_ptr<ExtensionInfo>();
}
if (IsEvictedEphemeralApp(ext)) {
// Hide evicted ephemeral apps.
return scoped_ptr<ExtensionInfo>();
}
return GetInstalledInfoHelper(extension_id, ext);
}
......@@ -1594,55 +1573,7 @@ scoped_ptr<ExtensionPrefs::ExtensionsInfo> ExtensionPrefs::
return extensions_info.Pass();
}
scoped_ptr<ExtensionPrefs::ExtensionsInfo>
ExtensionPrefs::GetEvictedEphemeralAppsInfo() const {
scoped_ptr<ExtensionsInfo> extensions_info(new ExtensionsInfo);
const base::DictionaryValue* extensions =
prefs_->GetDictionary(pref_names::kExtensions);
for (base::DictionaryValue::Iterator extension_id(*extensions);
!extension_id.IsAtEnd(); extension_id.Advance()) {
const base::DictionaryValue* ext = NULL;
if (!Extension::IdIsValid(extension_id.key()) ||
!extension_id.value().GetAsDictionary(&ext)) {
continue;
}
if (!IsEvictedEphemeralApp(ext))
continue;
scoped_ptr<ExtensionInfo> info =
GetInstalledInfoHelper(extension_id.key(), ext);
if (info)
extensions_info->push_back(linked_ptr<ExtensionInfo>(info.release()));
}
return extensions_info.Pass();
}
scoped_ptr<ExtensionInfo> ExtensionPrefs::GetEvictedEphemeralAppInfo(
const std::string& extension_id) const {
const base::DictionaryValue* extension_prefs = GetExtensionPref(extension_id);
if (!extension_prefs)
return scoped_ptr<ExtensionInfo>();
if (!IsEvictedEphemeralApp(extension_prefs))
return scoped_ptr<ExtensionInfo>();
return GetInstalledInfoHelper(extension_id, extension_prefs);
}
void ExtensionPrefs::RemoveEvictedEphemeralApp(
const std::string& extension_id) {
if (ReadPrefAsBooleanAndReturn(extension_id, kPrefEvictedEphemeralApp))
DeleteExtensionPrefs(extension_id);
}
bool ExtensionPrefs::IsEphemeralApp(const std::string& extension_id) const {
// Hide the data of evicted ephemeral apps.
if (ReadPrefAsBooleanAndReturn(extension_id, kPrefEvictedEphemeralApp))
return false;
if (ReadPrefAsBooleanAndReturn(extension_id, kPrefEphemeralApp))
return true;
......@@ -1654,11 +1585,8 @@ bool ExtensionPrefs::IsEphemeralApp(const std::string& extension_id) const {
void ExtensionPrefs::OnEphemeralAppPromoted(const std::string& extension_id) {
DCHECK(IsEphemeralApp(extension_id));
ScopedExtensionPrefUpdate update(prefs_, extension_id);
update->Set(kPrefEphemeralApp, new base::FundamentalValue(false));
DCHECK(!IsEvictedEphemeralApp(update.Get()));
update->Remove(kPrefEvictedEphemeralApp, NULL);
UpdateExtensionPref(
extension_id, kPrefEphemeralApp, new base::FundamentalValue(false));
}
bool ExtensionPrefs::WasAppDraggedByUser(const std::string& extension_id) {
......@@ -2224,9 +2152,6 @@ void ExtensionPrefs::FinishExtensionInfoPrefs(
// Clear state that may be registered from a previous install.
extension_dict->Remove(EventRouter::kRegisteredEvents, NULL);
// When evicted ephemeral apps are re-installed, this flag must be reset.
extension_dict->Remove(kPrefEvictedEphemeralApp, NULL);
// FYI, all code below here races on sudden shutdown because |extension_dict|,
// |app_sorting_|, |extension_pref_value_map_|, and (potentially) observers
// are updated non-transactionally. This is probably not fixable without
......
......@@ -481,17 +481,6 @@ class ExtensionPrefs : public ExtensionScopedPrefs, public KeyedService {
// information.
scoped_ptr<ExtensionsInfo> GetAllDelayedInstallInfo() const;
// Returns information about evicted ephemeral apps.
scoped_ptr<ExtensionsInfo> GetEvictedEphemeralAppsInfo() const;
// Return information about a specific evicted ephemeral app. Can return NULL
// if no such evicted app exists or is currently installed.
scoped_ptr<ExtensionInfo> GetEvictedEphemeralAppInfo(
const std::string& extension_id) const;
// Permanently remove the preferences for an evicted ephemeral app.
void RemoveEvictedEphemeralApp(const std::string& extension_id);
// Returns true if the extension is an ephemeral app.
bool IsEphemeralApp(const std::string& extension_id) const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment