Bug 1468501 - Implement a way to delete network cache by nsIPrincipal, r=mayhemer, r=michal

This commit is contained in:
Andrea Marchesini 2018-07-20 13:57:18 +02:00
parent d55ae44eff
commit 28962b5168
10 changed files with 297 additions and 55 deletions

View File

@ -16,6 +16,7 @@
#include "nsIDirectoryEnumerator.h"
#include "mozilla/Base64.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/net/MozURL.h"
namespace mozilla {
@ -93,7 +94,8 @@ CacheFileContextEvictor::ContextsCount()
nsresult
CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
bool aPinned)
bool aPinned,
const nsAString& aOrigin)
{
LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p, pinned=%d]",
this, aLoadContextInfo, aPinned));
@ -107,7 +109,8 @@ CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
if (mEntries[i]->mInfo &&
mEntries[i]->mInfo->Equals(aLoadContextInfo) &&
mEntries[i]->mPinned == aPinned) {
mEntries[i]->mPinned == aPinned &&
mEntries[i]->mOrigin.Equals(aOrigin)) {
entry = mEntries[i];
break;
}
@ -119,7 +122,8 @@ CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
for (uint32_t i = mEntries.Length(); i > 0;) {
--i;
if (mEntries[i]->mInfo && mEntries[i]->mPinned == aPinned) {
RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned);
RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned,
mEntries[i]->mOrigin);
mEntries.RemoveElementAt(i);
}
}
@ -129,12 +133,13 @@ CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
entry = new CacheFileContextEvictorEntry();
entry->mInfo = aLoadContextInfo;
entry->mPinned = aPinned;
entry->mOrigin = aOrigin;
mEntries.AppendElement(entry);
}
entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
PersistEvictionInfoToDisk(aLoadContextInfo, aPinned);
PersistEvictionInfoToDisk(aLoadContextInfo, aPinned, aOrigin);
if (mIndexIsUpToDate) {
// Already existing context could be added again, in this case the iterator
@ -263,7 +268,8 @@ CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile,
nsresult
CacheFileContextEvictor::PersistEvictionInfoToDisk(
nsILoadContextInfo *aLoadContextInfo, bool aPinned)
nsILoadContextInfo *aLoadContextInfo, bool aPinned,
const nsAString& aOrigin)
{
LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
"loadContextInfo=%p]", this, aLoadContextInfo));
@ -273,7 +279,7 @@ CacheFileContextEvictor::PersistEvictionInfoToDisk(
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
nsCOMPtr<nsIFile> file;
rv = GetContextFile(aLoadContextInfo, aPinned, getter_AddRefs(file));
rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -299,7 +305,7 @@ CacheFileContextEvictor::PersistEvictionInfoToDisk(
nsresult
CacheFileContextEvictor::RemoveEvictInfoFromDisk(
nsILoadContextInfo *aLoadContextInfo, bool aPinned)
nsILoadContextInfo *aLoadContextInfo, bool aPinned, const nsAString& aOrigin)
{
LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
"loadContextInfo=%p]", this, aLoadContextInfo));
@ -309,7 +315,7 @@ CacheFileContextEvictor::RemoveEvictInfoFromDisk(
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
nsCOMPtr<nsIFile> file;
rv = GetContextFile(aLoadContextInfo, aPinned, getter_AddRefs(file));
rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -393,6 +399,16 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
decoded = Substring(decoded, 1);
}
// Let's see if we have an origin.
nsAutoCString origin;
if (decoded.Contains('\t')) {
auto split = decoded.Split('\t');
MOZ_ASSERT(decoded.CountChar('\t') == 2);
origin = split.Get(0);
decoded = split.Get(1);
}
nsCOMPtr<nsILoadContextInfo> info;
if (!NS_LITERAL_CSTRING("*").Equals(decoded)) {
// "*" is indication of 'delete all', info left null will pass
@ -417,6 +433,7 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry();
entry->mInfo = info;
entry->mPinned = pinned;
CopyUTF8toUTF16(origin, entry->mOrigin);
entry->mTimeStamp = lastModifiedTime;
mEntries.AppendElement(entry);
}
@ -427,6 +444,7 @@ CacheFileContextEvictor::LoadEvictInfoFromDisk()
nsresult
CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
bool aPinned,
const nsAString& aOrigin,
nsIFile **_retval)
{
nsresult rv;
@ -445,6 +463,10 @@ CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
} else {
keyPrefix.Append('*');
}
if (!aOrigin.IsEmpty()) {
keyPrefix.Append('\t');
keyPrefix.Append(NS_ConvertUTF16toUTF8(aOrigin));
}
nsAutoCString data64;
rv = Base64Encode(keyPrefix, data64);
@ -516,7 +538,7 @@ CacheFileContextEvictor::StartEvicting()
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
if (mEvicting) {
LOG(("CacheFileContextEvictor::StartEvicting() - already evicintg."));
LOG(("CacheFileContextEvictor::StartEvicting() - already evicting."));
return;
}
@ -590,7 +612,8 @@ CacheFileContextEvictor::EvictEntries()
LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
"iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
mEntries[0]->mInfo.get()));
RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned);
RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned,
mEntries[0]->mOrigin);
mEntries.RemoveElementAt(0);
continue;
} else if (NS_FAILED(rv)) {
@ -634,6 +657,47 @@ CacheFileContextEvictor::EvictEntries()
continue;
}
if (!mEntries[0]->mOrigin.IsEmpty()) {
nsCOMPtr<nsIFile> file;
CacheFileIOManager::gInstance->GetFile(&hash, getter_AddRefs(file));
// Read metadata from the file synchronously
RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata();
rv = metadata->SyncReadMetadata(file);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
// Now get the context + enhance id + URL from the key.
nsAutoCString key;
metadata->GetKey(key);
nsAutoCString uriSpec;
RefPtr<nsILoadContextInfo> info =
CacheFileUtils::ParseKey(key, nullptr, &uriSpec);
MOZ_ASSERT(info);
if (!info) {
continue;
}
RefPtr<MozURL> url;
rv = MozURL::Init(getter_AddRefs(url), uriSpec);
if (NS_FAILED(rv)) {
LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since MozURL "
"fails in the parsing of the uriSpec"));
continue;
}
nsAutoCString urlOrigin;
url->Origin(urlOrigin);
if (urlOrigin.Equals(NS_ConvertUTF16toUTF8(mEntries[0]->mOrigin))) {
LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since origin "
"doesn't match"));
continue;
}
}
nsAutoCString leafName;
CacheFileIOManager::HashToStr(&hash, leafName);

View File

@ -21,6 +21,7 @@ struct CacheFileContextEvictorEntry
{
nsCOMPtr<nsILoadContextInfo> mInfo;
bool mPinned;
nsString mOrigin; // it can be empty
PRTime mTimeStamp; // in milliseconds
RefPtr<CacheIndexIterator> mIterator;
};
@ -41,8 +42,9 @@ public:
// Returns number of contexts that are being evicted.
uint32_t ContextsCount();
// Start evicting given context.
nsresult AddContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
// Start evicting given context and an origin, if not empty.
nsresult AddContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned,
const nsAString& aOrigin);
// CacheFileIOManager calls this method when CacheIndex's state changes. We
// check whether the index is up to date and start or stop evicting according
// to index's state.
@ -60,15 +62,17 @@ private:
// done for every context added to the evictor to be able to recover eviction
// after a shutdown or crash. When the context file is found after startup, we
// restore mTimeStamp from the last modified time of the file.
nsresult PersistEvictionInfoToDisk(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
nsresult PersistEvictionInfoToDisk(nsILoadContextInfo *aLoadContextInfo,
bool aPinned, const nsAString& aOrigin);
// Once we are done with eviction for the given context, the eviction info is
// removed from the disk.
nsresult RemoveEvictInfoFromDisk(nsILoadContextInfo *aLoadContextInfo, bool aPinned);
nsresult RemoveEvictInfoFromDisk(nsILoadContextInfo *aLoadContextInfo,
bool aPinned, const nsAString& aOrigin);
// Tries to load all contexts from the disk. This method is called just once
// after startup.
nsresult LoadEvictInfoFromDisk();
nsresult GetContextFile(nsILoadContextInfo *aLoadContextInfo, bool aPinned,
nsIFile **_retval);
const nsAString& aOrigin, nsIFile **_retval);
void CreateIterators();
void CloseIterators();

View File

@ -21,6 +21,7 @@
#include "nsIObserverService.h"
#include "nsICacheStorageVisitor.h"
#include "nsISizeOf.h"
#include "mozilla/net/MozURL.h"
#include "mozilla/Telemetry.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Services.h"
@ -3134,7 +3135,9 @@ CacheFileIOManager::EvictAllInternal()
// static
nsresult
CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo,
bool aPinned,
const nsAString& aOrigin)
{
LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
aLoadContextInfo));
@ -3147,12 +3150,13 @@ CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aP
}
nsCOMPtr<nsIRunnable> ev;
ev = NewRunnableMethod<nsCOMPtr<nsILoadContextInfo>, bool>(
ev = NewRunnableMethod<nsCOMPtr<nsILoadContextInfo>, bool, nsString>(
"net::CacheFileIOManager::EvictByContextInternal",
ioMan,
&CacheFileIOManager::EvictByContextInternal,
aLoadContextInfo,
aPinned);
aPinned,
aOrigin);
rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -3163,7 +3167,9 @@ CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aP
}
nsresult
CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
bool aPinned,
const nsAString& aOrigin)
{
LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, pinned=%d]",
aLoadContextInfo, aPinned));
@ -3206,6 +3212,8 @@ CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
}
}
NS_ConvertUTF16toUTF8 origin(aOrigin);
// Doom all active handles that matches the load context
nsTArray<RefPtr<CacheFileHandle> > handles;
mHandles.GetActiveHandles(&handles);
@ -3213,18 +3221,30 @@ CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
for (uint32_t i = 0; i < handles.Length(); ++i) {
CacheFileHandle* handle = handles[i];
if (aLoadContextInfo) {
bool equals;
rv = CacheFileUtils::KeyMatchesLoadContextInfo(handle->Key(),
aLoadContextInfo,
&equals);
nsAutoCString uriSpec;
RefPtr<nsILoadContextInfo> info =
CacheFileUtils::ParseKey(handle->Key(), nullptr, &uriSpec);
if (!info) {
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
"handle! [handle=%p, key=%s]", handle, handle->Key().get()));
MOZ_CRASH("Unexpected error!");
}
if (aLoadContextInfo && !info->Equals(aLoadContextInfo)) {
continue;
}
if (!origin.IsEmpty()) {
RefPtr<MozURL> url;
rv = MozURL::Init(getter_AddRefs(url), uriSpec);
if (NS_FAILED(rv)) {
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
"handle! [handle=%p, key=%s]", handle, handle->Key().get()));
MOZ_CRASH("Unexpected error!");
continue;
}
if (!equals) {
nsAutoCString urlOrigin;
url->Origin(urlOrigin);
if (!urlOrigin.Equals(origin)) {
continue;
}
}
@ -3250,7 +3270,7 @@ CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
mContextEvictor->Init(mCacheDirectory);
}
mContextEvictor->AddContext(aLoadContextInfo, aPinned);
mContextEvictor->AddContext(aLoadContextInfo, aPinned, aOrigin);
return NS_OK;
}

View File

@ -332,7 +332,8 @@ public:
static nsresult EvictIfOverLimit();
static nsresult EvictAll();
static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo,
bool aPinning);
bool aPinning,
const nsAString& aOrigin);
static nsresult InitIndexEntry(CacheFileHandle *aHandle,
OriginAttrsHash aOriginAttrsHash,
@ -417,7 +418,7 @@ private:
nsresult OverLimitEvictionInternal();
nsresult EvictAllInternal();
nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo,
bool aPinning);
bool aPinning, const nsAString& aOrigin);
nsresult TrashDirectory(nsIFile *aFile);
static void OnTrashTimer(nsITimer *aTimer, void *aClosure);

View File

@ -24,6 +24,7 @@
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsAutoPtr.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@ -795,7 +796,117 @@ NS_IMETHODIMP CacheStorageService::Clear()
// Passing null as a load info means to evict all contexts.
// EvictByContext() respects the entry pinning. EvictAll() does not.
rv = CacheFileIOManager::EvictByContext(nullptr, false);
rv = CacheFileIOManager::EvictByContext(nullptr, false, EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP CacheStorageService::ClearOrigin(nsIPrincipal* aPrincipal)
{
nsresult rv;
if (NS_WARN_IF(!aPrincipal)) {
return NS_ERROR_FAILURE;
}
nsAutoString origin;
rv = nsContentUtils::GetUTFOrigin(aPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);
rv = ClearOriginInternal(origin, aPrincipal->OriginAttributesRef(), true);
NS_ENSURE_SUCCESS(rv, rv);
rv = ClearOriginInternal(origin, aPrincipal->OriginAttributesRef(), false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
static bool
RemoveExactEntry(CacheEntryTable* aEntries,
nsACString const& aKey,
CacheEntry* aEntry,
bool aOverwrite)
{
RefPtr<CacheEntry> existingEntry;
if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
return false; // Already removed...
}
if (!aOverwrite && existingEntry != aEntry) {
LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
return false; // Already replaced...
}
LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
aEntries->Remove(aKey);
return true;
}
nsresult
CacheStorageService::ClearOriginInternal(const nsAString& aOrigin,
const OriginAttributes& aOriginAttributes,
bool aAnonymous)
{
nsresult rv;
RefPtr<LoadContextInfo> info =
GetLoadContextInfo(aAnonymous, aOriginAttributes);
if (NS_WARN_IF(!info)) {
return NS_ERROR_FAILURE;
}
mozilla::MutexAutoLock lock(mLock);
if (sGlobalEntryTables) {
for (auto iter = sGlobalEntryTables->Iter(); !iter.Done(); iter.Next()) {
bool matches = false;
rv = CacheFileUtils::KeyMatchesLoadContextInfo(iter.Key(), info,
&matches);
NS_ENSURE_SUCCESS(rv, rv);
if (!matches) {
continue;
}
CacheEntryTable* table = iter.UserData();
MOZ_ASSERT(table);
nsTArray<RefPtr<CacheEntry>> entriesToDelete;
for (auto entryIter = table->Iter(); !entryIter.Done(); entryIter.Next()) {
CacheEntry* entry = entryIter.UserData();
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), entry->GetURI());
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString origin;
rv = nsContentUtils::GetUTFOrigin(uri, origin);
NS_ENSURE_SUCCESS(rv, rv);
if (origin != aOrigin) {
continue;
}
entriesToDelete.AppendElement(entry);
}
for (RefPtr<CacheEntry>& entry : entriesToDelete) {
nsAutoCString entryKey;
rv = entry->HashingKey(entryKey);
if (NS_FAILED(rv)) {
NS_ERROR("aEntry->HashingKey() failed?");
return rv;
}
RemoveExactEntry(table, entryKey, entry, false /* don't overwrite */);
}
}
}
rv = CacheFileIOManager::EvictByContext(info, false /* pinned */, aOrigin);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -976,28 +1087,6 @@ AddExactEntry(CacheEntryTable* aEntries,
return true;
}
static bool
RemoveExactEntry(CacheEntryTable* aEntries,
nsACString const& aKey,
CacheEntry* aEntry,
bool aOverwrite)
{
RefPtr<CacheEntry> existingEntry;
if (!aEntries->Get(aKey, getter_AddRefs(existingEntry))) {
LOG(("RemoveExactEntry [entry=%p already gone]", aEntry));
return false; // Already removed...
}
if (!aOverwrite && existingEntry != aEntry) {
LOG(("RemoveExactEntry [entry=%p already replaced]", aEntry));
return false; // Already replaced...
}
LOG(("RemoveExactEntry [entry=%p removed]", aEntry));
aEntries->Remove(aKey);
return true;
}
bool
CacheStorageService::RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced)
{
@ -1844,7 +1933,7 @@ CacheStorageService::DoomStorageEntries(const nsACString& aContextKey,
if (aContext && !aContext->IsPrivate()) {
LOG((" dooming disk entries"));
CacheFileIOManager::EvictByContext(aContext, aPinned);
CacheFileIOManager::EvictByContext(aContext, aPinned, EmptyString());
}
} else {
LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));

View File

@ -30,6 +30,9 @@ class nsIThread;
class nsIEventTarget;
namespace mozilla {
class OriginAttributes;
namespace net {
class CacheStorageService;
@ -311,6 +314,10 @@ private:
bool aReplace,
CacheEntryHandle** aResult);
nsresult ClearOriginInternal(const nsAString& aOrigin,
const mozilla::OriginAttributes& aOriginAttributes,
bool aAnonymous);
static CacheStorageService* sSelf;
mozilla::Mutex mLock;

View File

@ -10,6 +10,7 @@ interface nsIApplicationCache;
interface nsIEventTarget;
interface nsICacheStorageConsumptionObserver;
interface nsICacheStorageVisitor;
interface nsIPrincipal;
/**
* Provides access to particual cache storages of the network URI cache.
@ -70,6 +71,14 @@ interface nsICacheStorageService : nsISupports
*/
nsICacheStorage synthesizedCacheStorage(in nsILoadContextInfo aLoadContextInfo);
/**
* Evict any cache entry having the same origin of aPrincipal.
*
* @param aPrincipal
* The principal to compare the entries with.
*/
void clearOrigin(in nsIPrincipal aPrincipal);
/**
* Evict the whole cache.
*/

View File

@ -0,0 +1,40 @@
const URL = "http://example.net";
const URL2 = "http://foo.bar";
function run_test()
{
do_get_profile();
asyncOpenCacheEntry(URL + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NEW, "e1m", "e1d", function(entry) {
asyncOpenCacheEntry(URL + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NORMAL, "e1m", "e1d", function(entry) {
asyncOpenCacheEntry(URL2 + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NEW, "f1m", "f1d", function(entry) {
asyncOpenCacheEntry(URL2 + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NORMAL, "f1m", "f1d", function(entry) {
var url = Services.io.newURI(URL);
var principal = Services.scriptSecurityManager.createCodebasePrincipal(url, {});
get_cache_service().clearOrigin(principal);
asyncOpenCacheEntry(URL + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NEW, "e1m", "e1d", function(entry) {
asyncOpenCacheEntry(URL2 + "/a", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
new OpenCallback(NORMAL, "f1m", "f1d", function(entry) {
finish_cache2_test();
})
);
})
);
})
);
})
);
})
);
})
);
do_test_pending();
}

View File

@ -78,6 +78,7 @@ skip-if = true
[test_cache2-30c-pinning-deferred-doom.js]
[test_cache2-30d-pinning-WasEvicted-API.js]
[test_cache2-31-visit-all.js]
[test_cache2-32-clear-origin.js]
[test_partial_response_entry_size_smart_shrink.js]
[test_304_responses.js]
[test_421.js]

View File

@ -86,6 +86,13 @@ const CookieCleaner = {
};
const NetworkCacheCleaner = {
deleteByPrincipal(aPrincipal) {
return new Promise(aResolve => {
Services.cache2.asyncClearOrigin(aPrincipal);
aResolve();
});
},
deleteAll() {
return new Promise(aResolve => {
Services.cache2.clear();