summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-02 02:09:43 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-02 02:09:43 +0000
commit0d6ec3a7c816498626f48b4f3f8491f9658a8e2e (patch)
tree043e946873a431fb1d8ec488a820aa37a3a133a4 /chrome
parent1859248461da934dbc0e70dcedcb65442ec0d9ff (diff)
downloadchromium_src-0d6ec3a7c816498626f48b4f3f8491f9658a8e2e.zip
chromium_src-0d6ec3a7c816498626f48b4f3f8491f9658a8e2e.tar.gz
chromium_src-0d6ec3a7c816498626f48b4f3f8491f9658a8e2e.tar.bz2
When uninstalling a hosted app, delete data for its launch_url's origin, as
long as no other apps are installed on that origin. (cloned from matt's OCL http://codereview.chromium.org/7747025/) TBR=mpcomplete BUG=87258 Review URL: http://codereview.chromium.org/7807002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99288 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/extension_data_deleter.cc50
-rw-r--r--chrome/browser/extensions/extension_data_deleter.h29
-rw-r--r--chrome/browser/extensions/extension_service.cc37
-rw-r--r--chrome/browser/extensions/extension_service.h4
-rw-r--r--chrome/browser/extensions/extension_service_unittest.cc138
-rw-r--r--chrome/browser/extensions/extension_service_unittest.h2
6 files changed, 230 insertions, 30 deletions
diff --git a/chrome/browser/extensions/extension_data_deleter.cc b/chrome/browser/extensions/extension_data_deleter.cc
index e8e5ab4..3812d70 100644
--- a/chrome/browser/extensions/extension_data_deleter.cc
+++ b/chrome/browser/extensions/extension_data_deleter.cc
@@ -4,9 +4,13 @@
#include "chrome/browser/extensions/extension_data_deleter.h"
+#include "base/file_util.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_constants.h"
#include "chrome/common/extensions/extension.h"
+#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/in_process_webkit/webkit_context.h"
+#include "content/common/url_constants.h"
#include "net/base/cookie_monster.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_context.h"
@@ -16,15 +20,29 @@
#include "webkit/fileapi/file_system_context.h"
ExtensionDataDeleter::ExtensionDataDeleter(Profile* profile,
- const GURL& extension_url) {
+ const std::string& extension_id,
+ const GURL& storage_origin,
+ bool is_storage_isolated) {
DCHECK(profile);
+ appcache_service_ = profile->GetAppCacheService();
webkit_context_ = profile->GetWebKitContext();
database_tracker_ = profile->GetDatabaseTracker();
- extension_request_context_ = profile->GetRequestContextForExtensions();
+ // Pick the right request context depending on whether it's an extension,
+ // isolated app, or regular app.
+ if (storage_origin.SchemeIs(chrome::kExtensionScheme)) {
+ extension_request_context_ = profile->GetRequestContextForExtensions();
+ } else if (is_storage_isolated) {
+ extension_request_context_ =
+ profile->GetRequestContextForIsolatedApp(extension_id);
+ isolated_app_path_ = profile->GetPath().
+ Append(chrome::kIsolatedAppStateDirname).AppendASCII(extension_id);
+ } else {
+ extension_request_context_ = profile->GetRequestContext();
+ }
file_system_context_ = profile->GetFileSystemContext();
- extension_url_ = extension_url;
+ storage_origin_ = storage_origin;
origin_id_ =
- webkit_database::DatabaseUtil::GetOriginIdentifier(extension_url_);
+ webkit_database::DatabaseUtil::GetOriginIdentifier(storage_origin_);
}
ExtensionDataDeleter::~ExtensionDataDeleter() {
@@ -56,16 +74,21 @@ void ExtensionDataDeleter::StartDeleting() {
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(
this, &ExtensionDataDeleter::DeleteFileSystemOnFileThread));
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &ExtensionDataDeleter::DeleteAppcachesOnIOThread));
}
void ExtensionDataDeleter::DeleteCookiesOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
net::CookieMonster* cookie_monster =
extension_request_context_->GetURLRequestContext()->cookie_store()->
- GetCookieMonster();
+ GetCookieMonster();
if (cookie_monster)
cookie_monster->DeleteAllForHostAsync(
- extension_url_, net::CookieMonster::DeleteCallback());
+ storage_origin_, net::CookieMonster::DeleteCallback());
}
void ExtensionDataDeleter::DeleteDatabaseOnFileThread() {
@@ -83,10 +106,21 @@ void ExtensionDataDeleter::DeleteLocalStorageOnWebkitThread() {
void ExtensionDataDeleter::DeleteIndexedDBOnWebkitThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
webkit_context_->indexed_db_context()->DeleteIndexedDBForOrigin(
- extension_url_);
+ storage_origin_);
}
void ExtensionDataDeleter::DeleteFileSystemOnFileThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- file_system_context_->DeleteDataForOriginOnFileThread(extension_url_);
+ file_system_context_->DeleteDataForOriginOnFileThread(storage_origin_);
+
+ // TODO(creis): The following call fails because the request context is still
+ // around, and holding open file handles in this directory.
+ // See http://crbug.com/85127
+ if (!isolated_app_path_.empty())
+ file_util::Delete(isolated_app_path_, true);
+}
+
+void ExtensionDataDeleter::DeleteAppcachesOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ appcache_service_->DeleteAppCachesForOrigin(storage_origin_, NULL);
}
diff --git a/chrome/browser/extensions/extension_data_deleter.h b/chrome/browser/extensions/extension_data_deleter.h
index 8085138..e4ee218 100644
--- a/chrome/browser/extensions/extension_data_deleter.h
+++ b/chrome/browser/extensions/extension_data_deleter.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DATA_DELETER_H_
#pragma once
+#include "base/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/string16.h"
#include "content/browser/browser_thread.h"
@@ -19,13 +20,14 @@ namespace fileapi {
class FileSystemContext;
}
-class Profile;
-class WebKitContext;
-
namespace net {
class URLRequestContextGetter;
}
+class ChromeAppCacheService;
+class Profile;
+class WebKitContext;
+
// A helper class that takes care of removing local storage, databases and
// cookies for a given extension. This is used by
// ExtensionService::ClearExtensionData() upon uninstalling an extension.
@@ -33,7 +35,10 @@ class ExtensionDataDeleter
: public base::RefCountedThreadSafe<ExtensionDataDeleter,
BrowserThread::DeleteOnUIThread> {
public:
- ExtensionDataDeleter(Profile* profile, const GURL& extension_url);
+ ExtensionDataDeleter(Profile* profile,
+ const std::string& extension_id,
+ const GURL& storage_origin,
+ bool is_storage_isolated);
// Start removing data. The extension should not be running when this is
// called. Cookies are deleted on the current thread, local storage and
@@ -67,14 +72,18 @@ class ExtensionDataDeleter
// file thread.
void DeleteFileSystemOnFileThread();
+ // Deletes appcache files for the extension. May only be called on the IO
+ // thread.
+ void DeleteAppcachesOnIOThread();
+
// The database context for deleting the database.
scoped_refptr<webkit_database::DatabaseTracker> database_tracker_;
- // Provides access to the extension request context.
+ // Provides access to the request context.
scoped_refptr<net::URLRequestContextGetter> extension_request_context_;
- // The URL of the extension we're removing data for.
- GURL extension_url_;
+ // The origin of the extension/app for which we're going to clear data.
+ GURL storage_origin_;
// The security origin identifier for which we're deleting stuff.
string16 origin_id_;
@@ -84,6 +93,12 @@ class ExtensionDataDeleter
scoped_refptr<fileapi::FileSystemContext> file_system_context_;
+ scoped_refptr<ChromeAppCacheService> appcache_service_;
+
+ // If non-empty, the extension we're deleting is an isolated app, and this
+ // is its directory which we should delete.
+ FilePath isolated_app_path_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionDataDeleter);
};
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 27128ef..e9143d0 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -865,20 +865,16 @@ bool ExtensionService::UninstallExtension(
// this function.
std::string extension_id(extension_id_unsafe);
- const Extension* extension = GetInstalledExtension(extension_id);
+ scoped_refptr<const Extension> extension(GetInstalledExtension(extension_id));
// Callers should not send us nonexistent extensions.
CHECK(extension);
- // Get hold of information we need after unloading, since the extension
- // pointer will be invalid then.
- GURL extension_url(extension->url());
- Extension::Location location(extension->location());
-
// Policy change which triggers an uninstall will always set
// |external_uninstall| to true so this is the only way to uninstall
// managed extensions.
- if (!Extension::UserMayDisable(location) && !external_uninstall) {
+ if (!Extension::UserMayDisable(extension->location()) &&
+ !external_uninstall) {
NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED,
Source<Profile>(profile_),
@@ -917,11 +913,11 @@ bool ExtensionService::UninstallExtension(
// any of these resources.
UnloadExtension(extension_id, extension_misc::UNLOAD_REASON_UNINSTALL);
- extension_prefs_->OnExtensionUninstalled(extension_id, location,
+ extension_prefs_->OnExtensionUninstalled(extension_id, extension->location(),
external_uninstall);
// Tell the backend to start deleting installed extensions on the file thread.
- if (Extension::LOAD != location) {
+ if (Extension::LOAD != extension->location()) {
if (!BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableFunction(
@@ -931,7 +927,20 @@ bool ExtensionService::UninstallExtension(
NOTREACHED();
}
- ClearExtensionData(extension_url);
+ GURL launch_web_url_origin(extension->launch_web_url());
+ launch_web_url_origin = launch_web_url_origin.GetOrigin();
+ bool is_storage_isolated =
+ (extension->is_storage_isolated() &&
+ extension->HasAPIPermission(ExtensionAPIPermission::kExperimental));
+
+ if (extension->is_hosted_app() &&
+ !profile_->GetExtensionSpecialStoragePolicy()->
+ IsStorageProtected(launch_web_url_origin)) {
+ ClearExtensionData(extension_id, launch_web_url_origin,
+ is_storage_isolated);
+ }
+ ClearExtensionData(extension_id, extension->url(), is_storage_isolated);
+
UntrackTerminatedExtension(extension_id);
// Notify interested parties that we've uninstalled this extension.
@@ -949,9 +958,11 @@ bool ExtensionService::UninstallExtension(
return true;
}
-void ExtensionService::ClearExtensionData(const GURL& extension_url) {
- scoped_refptr<ExtensionDataDeleter> deleter(
- new ExtensionDataDeleter(profile_, extension_url));
+void ExtensionService::ClearExtensionData(const std::string& extension_id,
+ const GURL& storage_url,
+ bool is_storage_isolated) {
+ scoped_refptr<ExtensionDataDeleter> deleter(new ExtensionDataDeleter(
+ profile_, extension_id, storage_url, is_storage_isolated));
deleter->StartDeleting();
}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 4e1f290..276406c 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -609,7 +609,9 @@ class ExtensionService
SyncBundle& bundle);
// Clear all persistent data that may have been stored by the extension.
- void ClearExtensionData(const GURL& extension_url);
+ void ClearExtensionData(const std::string& extension_id,
+ const GURL& storage_url,
+ bool is_storage_isolated);
// Look up an extension by ID, optionally including either or both of enabled
// and disabled extensions.
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index a374f1a..3ded8b68 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -464,6 +464,13 @@ void ExtensionServiceTestBase::InitializeExtensionServiceHelper(
autoupdate_enabled);
}
+void ExtensionServiceTestBase::InitializeRequestContext() {
+ ASSERT_TRUE(profile_.get());
+ ExtensionTestingProfile* profile =
+ static_cast<ExtensionTestingProfile*>(profile_.get());
+ profile->CreateRequestContext();
+}
+
// static
void ExtensionServiceTestBase::SetUpTestCase() {
ExtensionErrorReporter::Init(false); // no noisy errors
@@ -1792,6 +1799,7 @@ TEST_F(ExtensionServiceTest, UpdateApps) {
TEST_F(ExtensionServiceTest, InstallAppsWithUnlimtedStorage) {
InitializeEmptyExtensionService();
+ InitializeRequestContext();
EXPECT_TRUE(service_->extensions()->empty());
int pref_count = 0;
@@ -1843,6 +1851,7 @@ TEST_F(ExtensionServiceTest, InstallAppsWithUnlimtedStorage) {
TEST_F(ExtensionServiceTest, InstallAppsAndCheckStorageProtection) {
InitializeEmptyExtensionService();
+ InitializeRequestContext();
EXPECT_TRUE(service_->extensions()->empty());
int pref_count = 0;
@@ -2702,7 +2711,7 @@ class ExtensionCookieCallback {
ScopedRunnableMethodFactory<MessageLoop> message_loop_factory_;
};
-// Verifies extension state is removed upon uninstall
+// Verifies extension state is removed upon uninstall.
TEST_F(ExtensionServiceTest, ClearExtensionData) {
InitializeEmptyExtensionService();
ExtensionCookieCallback callback;
@@ -2790,6 +2799,133 @@ TEST_F(ExtensionServiceTest, ClearExtensionData) {
EXPECT_FALSE(file_util::DirectoryExists(idb_path));
}
+// Verifies app state is removed upon uninstall.
+TEST_F(ExtensionServiceTest, ClearAppData) {
+ InitializeEmptyExtensionService();
+ InitializeRequestContext();
+ ExtensionCookieCallback callback;
+
+ int pref_count = 0;
+
+ // Install app1 with unlimited storage.
+ PackAndInstallCrx(data_dir_.AppendASCII("app1"), true);
+ ValidatePrefKeyCount(++pref_count);
+ ASSERT_EQ(1u, service_->extensions()->size());
+ const Extension* extension = service_->extensions()->at(0);
+ const std::string id1 = extension->id();
+ EXPECT_TRUE(extension->HasAPIPermission(
+ ExtensionAPIPermission::kUnlimitedStorage));
+ const GURL origin1(extension->GetFullLaunchURL().GetOrigin());
+ EXPECT_TRUE(profile_->GetExtensionSpecialStoragePolicy()->
+ IsStorageUnlimited(origin1));
+ string16 origin_id =
+ webkit_database::DatabaseUtil::GetOriginIdentifier(origin1);
+
+ // Install app2 from the same origin with unlimited storage.
+ PackAndInstallCrx(data_dir_.AppendASCII("app2"), true);
+ ValidatePrefKeyCount(++pref_count);
+ ASSERT_EQ(2u, service_->extensions()->size());
+ extension = service_->extensions()->at(1);
+ const std::string id2 = extension->id();
+ EXPECT_TRUE(extension->HasAPIPermission(
+ ExtensionAPIPermission::kUnlimitedStorage));
+ EXPECT_TRUE(extension->web_extent().MatchesURL(
+ extension->GetFullLaunchURL()));
+ const GURL origin2(extension->GetFullLaunchURL().GetOrigin());
+ EXPECT_EQ(origin1, origin2);
+ EXPECT_TRUE(profile_->GetExtensionSpecialStoragePolicy()->
+ IsStorageUnlimited(origin2));
+
+ // Set a cookie for the extension.
+ net::CookieMonster* cookie_monster =
+ profile_->GetRequestContext()->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ ASSERT_TRUE(cookie_monster);
+ net::CookieOptions options;
+ cookie_monster->SetCookieWithOptionsAsync(
+ origin1, "dummy=value", options,
+ base::Bind(&ExtensionCookieCallback::SetCookieCallback,
+ base::Unretained(&callback)));
+ loop_.RunAllPending();
+ EXPECT_TRUE(callback.result_);
+
+ cookie_monster->GetAllCookiesForURLAsync(
+ origin1,
+ base::Bind(&ExtensionCookieCallback::GetAllCookiesCallback,
+ base::Unretained(&callback)));
+ loop_.RunAllPending();
+ EXPECT_EQ(1U, callback.list_.size());
+
+ // Open a database.
+ webkit_database::DatabaseTracker* db_tracker = profile_->GetDatabaseTracker();
+ string16 db_name = UTF8ToUTF16("db");
+ string16 description = UTF8ToUTF16("db_description");
+ int64 size;
+ db_tracker->DatabaseOpened(origin_id, db_name, description, 1, &size);
+ db_tracker->DatabaseClosed(origin_id, db_name);
+ std::vector<webkit_database::OriginInfo> origins;
+ db_tracker->GetAllOriginsInfo(&origins);
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(origin_id, origins[0].GetOrigin());
+
+ // Create local storage. We only simulate this by creating the backing file
+ // since webkit is not initialized.
+ DOMStorageContext* context =
+ profile_->GetWebKitContext()->dom_storage_context();
+ FilePath lso_path = context->GetLocalStorageFilePath(origin_id);
+ EXPECT_TRUE(file_util::CreateDirectory(lso_path.DirName()));
+ EXPECT_EQ(0, file_util::WriteFile(lso_path, NULL, 0));
+ EXPECT_TRUE(file_util::PathExists(lso_path));
+
+ // Create indexed db. Similarly, it is enough to only simulate this by
+ // creating the directory on the disk.
+ IndexedDBContext* idb_context =
+ profile_->GetWebKitContext()->indexed_db_context();
+ FilePath idb_path = idb_context->GetIndexedDBFilePath(origin_id);
+ EXPECT_TRUE(file_util::CreateDirectory(idb_path));
+ EXPECT_TRUE(file_util::DirectoryExists(idb_path));
+
+ // Uninstall one of them, unlimited storage should still be granted
+ // to the origin.
+ UninstallExtension(id1, false);
+ EXPECT_EQ(1u, service_->extensions()->size());
+ EXPECT_TRUE(profile_->GetExtensionSpecialStoragePolicy()->
+ IsStorageUnlimited(origin1));
+
+ // Check that the cookie is still there.
+ cookie_monster->GetAllCookiesForURLAsync(
+ origin1,
+ base::Bind(&ExtensionCookieCallback::GetAllCookiesCallback,
+ base::Unretained(&callback)));
+ loop_.RunAllPending();
+ EXPECT_EQ(1U, callback.list_.size());
+
+ // Now uninstall the other. Storage should be cleared for the apps.
+ UninstallExtension(id2, false);
+ EXPECT_EQ(0u, service_->extensions()->size());
+ EXPECT_FALSE(profile_->GetExtensionSpecialStoragePolicy()->
+ IsStorageUnlimited(origin1));
+
+ // Check that the cookie is gone.
+ cookie_monster->GetAllCookiesForURLAsync(
+ origin1,
+ base::Bind(&ExtensionCookieCallback::GetAllCookiesCallback,
+ base::Unretained(&callback)));
+ loop_.RunAllPending();
+ EXPECT_EQ(0U, callback.list_.size());
+
+ // The database should have vanished as well.
+ origins.clear();
+ db_tracker->GetAllOriginsInfo(&origins);
+ EXPECT_EQ(0U, origins.size());
+
+ // Check that the LSO file has been removed.
+ EXPECT_FALSE(file_util::PathExists(lso_path));
+
+ // Check if the indexed db has disappeared too.
+ EXPECT_FALSE(file_util::DirectoryExists(idb_path));
+}
+
// Tests loading single extensions (like --load-extension)
TEST_F(ExtensionServiceTest, LoadExtension) {
InitializeEmptyExtensionService();
diff --git a/chrome/browser/extensions/extension_service_unittest.h b/chrome/browser/extensions/extension_service_unittest.h
index 847fda5..daaf6f1 100644
--- a/chrome/browser/extensions/extension_service_unittest.h
+++ b/chrome/browser/extensions/extension_service_unittest.h
@@ -31,6 +31,8 @@ class ExtensionServiceTestBase : public testing::Test {
void InitializeExtensionServiceWithUpdater();
+ void InitializeRequestContext();
+
static void SetUpTestCase();
virtual void SetUp();