Bug 398478: Clear globalStorage for offline apps separately from cookies, and the offline cache separately from the main cache. r=enndeakin, r=mconnor, sr=biesi
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -118,16 +118,38 @@ Sanitizer.prototype = {
},
get canClear()
{
return true;
}
},
+ offlineApps: {
+ clear: function ()
+ {
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+ var cacheService = Cc["@mozilla.org/network/cache-service;1"].
+ getService(Ci.nsICacheService);
+ try {
+ cacheService.evictEntries(Ci.nsICache.STORE_OFFLINE);
+ } catch(er) {}
+
+ var storageManagerService = Cc["@mozilla.org/dom/storagemanager;1"].
+ getService(Ci.nsIDOMStorageManager);
+ storageManagerService.clearOfflineApps();
+ },
+
+ get canClear()
+ {
+ return true;
+ }
+ },
+
history: {
clear: function ()
{
var globalHistory = Components.classes["@mozilla.org/browser/global-history;2"]
.getService(Components.interfaces.nsIBrowserHistory);
globalHistory.removeAllPages();
try {
--- a/browser/base/content/sanitize.xul
+++ b/browser/base/content/sanitize.xul
@@ -119,16 +119,17 @@
<preferences id="sanitizePreferences">
<preference id="privacy.item.history" name="privacy.item.history" type="bool" readonly="true"/>
<preference id="privacy.item.formdata" name="privacy.item.formdata" type="bool" readonly="true"/>
<preference id="privacy.item.passwords" name="privacy.item.passwords" type="bool" readonly="true"/>
<preference id="privacy.item.downloads" name="privacy.item.downloads" type="bool" readonly="true"/>
<preference id="privacy.item.cookies" name="privacy.item.cookies" type="bool" readonly="true"/>
<preference id="privacy.item.cache" name="privacy.item.cache" type="bool" readonly="true"/>
+ <preference id="privacy.item.offlineApps" name="privacy.item.offlineApps" type="bool"/>
<preference id="privacy.item.sessions" name="privacy.item.sessions" type="bool" readonly="true"/>
</preferences>
<description>&sanitizeItems.label;</description>
<checkbox label="&itemHistory.label;"
accesskey="&itemHistory.accesskey;"
preference="privacy.item.history"
@@ -144,16 +145,20 @@
<checkbox label="&itemCache.label;"
accesskey="&itemCache.accesskey;"
preference="privacy.item.cache"
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
<checkbox label="&itemCookies.label;"
accesskey="&itemCookies.accesskey;"
preference="privacy.item.cookies"
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineApps.accesskey;"
+ preference="privacy.item.offlineApps"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
<checkbox label="&itemPasswords.label;"
accesskey="&itemPasswords.accesskey;"
preference="privacy.item.passwords"
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
<checkbox label="&itemSessions.label;"
accesskey="&itemSessions.accesskey;"
preference="privacy.item.sessions"
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
--- a/browser/components/preferences/sanitize.xul
+++ b/browser/components/preferences/sanitize.xul
@@ -57,16 +57,17 @@
<preferences>
<preference id="privacy.item.history" name="privacy.item.history" type="bool"/>
<preference id="privacy.item.formdata" name="privacy.item.formdata" type="bool"/>
<preference id="privacy.item.passwords" name="privacy.item.passwords" type="bool"/>
<preference id="privacy.item.downloads" name="privacy.item.downloads" type="bool"/>
<preference id="privacy.item.cookies" name="privacy.item.cookies" type="bool"/>
<preference id="privacy.item.cache" name="privacy.item.cache" type="bool"/>
+ <preference id="privacy.item.offlineApps" name="privacy.item.offlineApps" type="bool"/>
<preference id="privacy.item.sessions" name="privacy.item.sessions" type="bool"/>
</preferences>
<description>&clearDataSettings.label;</description>
<checkbox label="&itemHistory.label;"
accesskey="&itemHistory.accesskey;"
preference="privacy.item.history"/>
@@ -77,16 +78,19 @@
accesskey="&itemFormSearchHistory.accesskey;"
preference="privacy.item.formdata"/>
<checkbox label="&itemCache.label;"
accesskey="&itemCache.accesskey;"
preference="privacy.item.cache"/>
<checkbox label="&itemCookies.label;"
accesskey="&itemCookies.accesskey;"
preference="privacy.item.cookies"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineApps.accesskey;"
+ preference="privacy.item.offlineApps"/>
<checkbox label="&itemPasswords.label;"
accesskey="&itemPasswords.accesskey;"
preference="privacy.item.passwords"/>
<checkbox label="&itemSessions.label;"
accesskey="&itemSessions.accesskey;"
preference="privacy.item.sessions"/>
</prefpane>
--- a/browser/locales/en-US/chrome/browser/sanitize.dtd
+++ b/browser/locales/en-US/chrome/browser/sanitize.dtd
@@ -10,13 +10,15 @@
<!ENTITY itemFormSearchHistory.label "Saved Form and Search History">
<!ENTITY itemFormSearchHistory.accesskey "F">
<!ENTITY itemPasswords.label "Saved Passwords">
<!ENTITY itemPasswords.accesskey "P">
<!ENTITY itemCookies.label "Cookies">
<!ENTITY itemCookies.accesskey "C">
<!ENTITY itemCache.label "Cache">
<!ENTITY itemCache.accesskey "a">
+<!ENTITY itemOfflineApps.label "Offline Website Data">
+<!ENTITY itemOfflineApps.accesskey "O">
<!ENTITY itemDownloads.label "Download History">
<!ENTITY itemDownloads.accesskey "D">
<!ENTITY itemSessions.label "Authenticated Sessions">
<!ENTITY itemSessions.accesskey "S">
<!ENTITY window.width "30em">
--- a/dom/public/idl/storage/nsIDOMStorageManager.idl
+++ b/dom/public/idl/storage/nsIDOMStorageManager.idl
@@ -32,21 +32,27 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
-[scriptable, uuid(74dc93d1-8cdf-43b1-a52c-776dcb873475)]
+[scriptable, uuid(6e4bc25e-f056-4c6c-b27e-89152ca91834)]
interface nsIDOMStorageManager : nsISupports
{
/**
* Return the amount of disk space used by a domain. Usage is checked
* against the domain of the page that set the key (the owner domain), not
* the domain of the storage object.
*
* @param aOwnerDomain The domain to check.
* @returns the space usage of the domain, in bytes.
*/
long getUsage(in AString aOwnerDomain);
+
+ /**
+ * Clear keys owned by offline applications. All data owned by a domain
+ * with the "offline-app" permission will be removed from the database.
+ */
+ void clearOfflineApps();
};
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -47,16 +47,17 @@
#include "nsIScriptSecurityManager.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "nsReadableUtils.h"
#include "nsIObserverService.h"
#include "nsNetUtil.h"
#include "nsIPrefBranch.h"
#include "nsICookiePermission.h"
+#include "nsIPermission.h"
#include "nsIPermissionManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIOfflineCacheUpdate.h"
#include "nsIJSContextStack.h"
static const PRUint32 ASK_BEFORE_ACCEPT = 1;
static const PRUint32 ACCEPT_SESSION = 2;
static const PRUint32 BEHAVIOR_REJECT = 2;
@@ -227,16 +228,54 @@ nsDOMStorageManager::Shutdown()
PR_STATIC_CALLBACK(PLDHashOperator)
ClearStorage(nsDOMStorageEntry* aEntry, void* userArg)
{
aEntry->mStorage->ClearAll();
return PL_DHASH_REMOVE;
}
+static nsresult
+GetOfflineDomains(nsStringArray& aDomains)
+{
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+ if (permissionManager) {
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRBool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsIPermission> perm;
+ rv = enumerator->GetNext(getter_AddRefs(perm));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 capability;
+ rv = perm->GetCapability(&capability);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (capability != nsIPermissionManager::DENY_ACTION) {
+ nsCAutoString type;
+ rv = perm->GetType(type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (type.EqualsLiteral("offline-app")) {
+ nsCAutoString host;
+ rv = perm->GetHost(host);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aDomains.AppendString(NS_ConvertUTF8toUTF16(host));
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
nsresult
nsDOMStorageManager::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
if (!strcmp(aTopic, "offline-app-removed")) {
#ifdef MOZ_STORAGE
nsresult rv = nsDOMStorage::InitDB();
@@ -245,33 +284,50 @@ nsDOMStorageManager::Observe(nsISupports
#endif
} else if (!strcmp(aTopic, "cookie-changed") &&
!nsCRT::strcmp(aData, NS_LITERAL_STRING("cleared").get())) {
mStorages.EnumerateEntries(ClearStorage, nsnull);
#ifdef MOZ_STORAGE
nsresult rv = nsDOMStorage::InitDB();
NS_ENSURE_SUCCESS(rv, rv);
- return nsDOMStorage::gStorageDB->RemoveAll();
+
+ // Remove global storage for domains that aren't marked for offline use.
+ nsStringArray domains;
+ rv = GetOfflineDomains(domains);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_FALSE);
#endif
}
return NS_OK;
}
NS_IMETHODIMP
nsDOMStorageManager::GetUsage(const nsAString& aDomain,
PRInt32 *aUsage)
{
nsresult rv = nsDOMStorage::InitDB();
NS_ENSURE_SUCCESS(rv, rv);
return nsDOMStorage::gStorageDB->GetUsage(aDomain, aUsage);
}
+NS_IMETHODIMP
+nsDOMStorageManager::ClearOfflineApps()
+{
+ nsresult rv = nsDOMStorage::InitDB();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsStringArray domains;
+ rv = GetOfflineDomains(domains);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_TRUE);
+}
+
void
nsDOMStorageManager::AddToStoragesHash(nsDOMStorage* aStorage)
{
nsDOMStorageEntry* entry = mStorages.PutEntry(aStorage);
if (entry)
entry->mStorage = aStorage;
}
--- a/dom/src/storage/nsDOMStorageDB.cpp
+++ b/dom/src/storage/nsDOMStorageDB.cpp
@@ -433,16 +433,57 @@ nsDOMStorageDB::RemoveOwner(const nsAStr
}
nsresult rv = mRemoveOwnerStatement->BindStringParameter(0, aOwner);
NS_ENSURE_SUCCESS(rv, rv);
return mRemoveOwnerStatement->Execute();
}
+
+nsresult
+nsDOMStorageDB::RemoveOwners(const nsStringArray &aOwners, PRBool aMatch)
+{
+ if (aOwners.Count() == 0) {
+ if (aMatch) {
+ return NS_OK;
+ }
+
+ return RemoveAll();
+ }
+
+ nsCAutoString expression;
+
+ if (aMatch) {
+ expression.Assign(NS_LITERAL_CSTRING("DELETE FROM webappsstore "
+ "WHERE owner IN (?"));
+ } else {
+ expression.Assign(NS_LITERAL_CSTRING("DELETE FROM webappsstore "
+ "WHERE owner NOT IN (?"));
+ }
+
+ for (PRInt32 i = 1; i < aOwners.Count(); i++) {
+ expression.Append(", ?");
+ }
+ expression.Append(")");
+
+ nsCOMPtr<mozIStorageStatement> statement;
+
+ nsresult rv = mConnection->CreateStatement(expression,
+ getter_AddRefs(statement));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (PRInt32 i = 0; i < aOwners.Count(); i++) {
+ rv = statement->BindStringParameter(i, *aOwners[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return statement->Execute();
+}
+
nsresult
nsDOMStorageDB::RemoveAll()
{
mozStorageStatementScoper scope(mRemoveAllStatement);
return mRemoveAllStatement->Execute();
}
nsresult
--- a/dom/src/storage/nsDOMStorageDB.h
+++ b/dom/src/storage/nsDOMStorageDB.h
@@ -108,16 +108,23 @@ public:
/**
* Removes all keys added by a given domain.
*/
nsresult
RemoveOwner(const nsAString& aOwner);
/**
+ * Removes keys owned by domains that either match or don't match the
+ * list.
+ */
+ nsresult
+ RemoveOwners(const nsStringArray& aOwners, PRBool aMatch);
+
+ /**
* Removes all keys from storage. Used when clearing storage.
*/
nsresult
RemoveAll();
nsresult GetUsage(const nsAString &aOwner, PRInt32 *aUsage);
protected:
--- a/netwerk/cache/src/nsCacheService.cpp
+++ b/netwerk/cache/src/nsCacheService.cpp
@@ -764,18 +764,18 @@ nsCacheService::EvictEntriesForClient(co
}
rv = mDiskDevice->EvictEntries(clientID);
if (NS_FAILED(rv)) return rv;
}
}
#endif // ! NECKO_DISK_CACHE
#ifdef NECKO_OFFLINE_CACHE
- if (storagePolicy == nsICache::STORE_ANYWHERE ||
- storagePolicy == nsICache::STORE_OFFLINE) {
+ // Only clear the offline cache if it has been specifically asked for.
+ if (storagePolicy == nsICache::STORE_OFFLINE) {
if (mEnableOfflineDevice) {
if (!mOfflineDevice) {
rv = CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
rv = mOfflineDevice->EvictEntries(clientID);
if (NS_FAILED(rv)) return rv;
}