Bug 1705032 - Added CSSCacheCleaner deleteByBaseDomain. r=emilio,timhuang

Differential Revision: https://phabricator.services.mozilla.com/D114518
This commit is contained in:
Paul Zuehlcke 2021-05-27 12:37:03 +00:00
parent aeca8bec83
commit 8e5f5e55fc
14 changed files with 229 additions and 22 deletions

View File

@ -753,11 +753,20 @@ void ChromeUtils::ClearRecentJSDevError(GlobalObject&) {
}
#endif // NIGHTLY_BUILD
void ChromeUtils::ClearStyleSheetCache(GlobalObject&,
nsIPrincipal* aForPrincipal) {
void ChromeUtils::ClearStyleSheetCacheByPrincipal(GlobalObject&,
nsIPrincipal* aForPrincipal) {
SharedStyleSheetCache::Clear(aForPrincipal);
}
void ChromeUtils::ClearStyleSheetCacheByBaseDomain(
GlobalObject&, const nsACString& aBaseDomain) {
SharedStyleSheetCache::Clear(nullptr, &aBaseDomain);
}
void ChromeUtils::ClearStyleSheetCache(GlobalObject&) {
SharedStyleSheetCache::Clear();
}
#define PROCTYPE_TO_WEBIDL_CASE(_procType, _webidl) \
case mozilla::ProcType::_procType: \
return WebIDLProcType::_webidl

View File

@ -151,7 +151,13 @@ class ChromeUtils {
static void ClearRecentJSDevError(GlobalObject& aGlobal);
static void ClearStyleSheetCache(GlobalObject&, nsIPrincipal* aForPrincipal);
static void ClearStyleSheetCacheByPrincipal(GlobalObject&,
nsIPrincipal* aForPrincipal);
static void ClearStyleSheetCacheByBaseDomain(GlobalObject& aGlobal,
const nsACString& aBaseDomain);
static void ClearStyleSheetCache(GlobalObject& aGlobal);
static already_AddRefed<Promise> RequestPerformanceMetrics(
GlobalObject& aGlobal, ErrorResult& aRv);

View File

@ -193,9 +193,20 @@ namespace ChromeUtils {
#endif // NIGHTLY_BUILD
/**
* Clears the stylesheet cache.
* Clears the stylesheet cache by baseDomain. This includes associated
* state-partitioned cache.
*/
void clearStyleSheetCache(optional Principal? principal = null);
void clearStyleSheetCacheByBaseDomain(UTF8String baseDomain);
/**
* Clears the stylesheet cache by principal.
*/
void clearStyleSheetCacheByPrincipal(Principal principal);
/**
* Clears the entire stylesheet cache.
*/
void clearStyleSheetCache();
/**
* If the profiler is currently running and recording the current thread,

View File

@ -2009,11 +2009,13 @@ mozilla::ipc::IPCResult ContentChild::RecvRegisterChromeItem(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvClearStyleSheetCache(
const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal) {
nsIPrincipal* prin = aForPrincipal ? aForPrincipal.value().get() : nullptr;
SharedStyleSheetCache::Clear(prin);
const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal,
const Maybe<nsCString>& aBaseDomain) {
nsIPrincipal* principal =
aForPrincipal ? aForPrincipal.value().get() : nullptr;
const nsCString* baseDomain = aBaseDomain ? aBaseDomain.ptr() : nullptr;
SharedStyleSheetCache::Clear(principal, baseDomain);
return IPC_OK();
}

View File

@ -277,7 +277,8 @@ class ContentChild final : public PContentChild,
const ChromeRegistryItem& item);
mozilla::ipc::IPCResult RecvClearStyleSheetCache(
const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal);
const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal,
const Maybe<nsCString>& aBaseDomain);
mozilla::ipc::IPCResult RecvClearImageCache(const bool& privateLoader,
const bool& chrome);

View File

@ -584,7 +584,8 @@ child:
async ClearImageCache(bool privateLoader, bool chrome);
async ClearStyleSheetCache(nsIPrincipal? aForPrincipal);
async ClearStyleSheetCache(nsIPrincipal? aForPrincipal,
nsCString? aBaseDomain);
async SetOffline(bool offline);
async SetConnectivity(bool connectivity);

View File

@ -130,6 +130,8 @@ class SheetLoadDataHashKey : public PLDHashEntryHdr {
nsIPrincipal* LoaderPrincipal() const { return mLoaderPrincipal; }
nsIPrincipal* PartitionPrincipal() const { return mPartitionPrincipal; }
css::SheetParsingMode ParsingMode() const { return mParsingMode; }
enum { ALLOW_MEMMOVE = true };

View File

@ -7,6 +7,7 @@
#include "SharedStyleSheetCache.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/css/SheetLoadData.h"
#include "mozilla/dom/ContentParent.h"
@ -28,13 +29,16 @@ using IsAlternate = css::Loader::IsAlternate;
SharedStyleSheetCache* SharedStyleSheetCache::sInstance;
void SharedStyleSheetCache::Clear(nsIPrincipal* aForPrincipal) {
void SharedStyleSheetCache::Clear(nsIPrincipal* aForPrincipal,
const nsACString* aBaseDomain) {
using ContentParent = dom::ContentParent;
if (XRE_IsParentProcess()) {
auto forPrincipal = aForPrincipal ? Some(RefPtr(aForPrincipal)) : Nothing();
auto baseDomain = aBaseDomain ? Some(nsCString(*aBaseDomain)) : Nothing();
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
Unused << cp->SendClearStyleSheetCache(forPrincipal);
Unused << cp->SendClearStyleSheetCache(forPrincipal, baseDomain);
}
}
@ -42,14 +46,43 @@ void SharedStyleSheetCache::Clear(nsIPrincipal* aForPrincipal) {
return;
}
if (!aForPrincipal) {
// No filter, clear all.
if (!aForPrincipal && !aBaseDomain) {
sInstance->mCompleteSheets.Clear();
return;
}
for (auto iter = sInstance->mCompleteSheets.Iter(); !iter.Done();
iter.Next()) {
if (iter.Key().Principal()->Equals(aForPrincipal)) {
const bool shouldRemove = [&] {
if (aForPrincipal && iter.Key().Principal()->Equals(aForPrincipal)) {
return true;
}
if (!aBaseDomain) {
return false;
}
// Clear by baseDomain.
nsIPrincipal* partitionPrincipal = iter.Key().PartitionPrincipal();
// Clear entries with matching base domain. This includes entries
// which are partitioned under other top level sites (= have a
// partitionKey set).
nsAutoCString principalBaseDomain;
nsresult rv = partitionPrincipal->GetBaseDomain(principalBaseDomain);
if (NS_SUCCEEDED(rv) && principalBaseDomain.Equals(*aBaseDomain)) {
return true;
}
// Clear entries partitioned under aBaseDomain.
nsAutoString partitionKeyBaseDomain;
bool ok = StoragePrincipalHelper::GetBaseDomainFromPartitionKey(
partitionPrincipal->OriginAttributesRef().mPartitionKey,
partitionKeyBaseDomain);
return ok &&
NS_ConvertUTF16toUTF8(partitionKeyBaseDomain).Equals(*aBaseDomain);
}();
if (shouldRemove) {
iter.Remove();
}
}

View File

@ -99,7 +99,8 @@ class SharedStyleSheetCache final : public nsIMemoryReporter {
// be called when the document goes away, or when its principal changes.
void UnregisterLoader(css::Loader&);
static void Clear(nsIPrincipal* aForPrincipal = nullptr);
static void Clear(nsIPrincipal* aForPrincipal = nullptr,
const nsACString* aBaseDomain = nullptr);
private:
static already_AddRefed<SharedStyleSheetCache> Create();

View File

@ -251,17 +251,16 @@ const CSSCacheCleaner = {
aOriginAttributes
);
ChromeUtils.clearStyleSheetCache(httpPrincipal);
ChromeUtils.clearStyleSheetCache(httpsPrincipal);
ChromeUtils.clearStyleSheetCacheByPrincipal(httpPrincipal);
ChromeUtils.clearStyleSheetCacheByPrincipal(httpsPrincipal);
},
async deleteByPrincipal(aPrincipal) {
ChromeUtils.clearStyleSheetCache(aPrincipal);
ChromeUtils.clearStyleSheetCacheByPrincipal(aPrincipal);
},
deleteByBaseDomain(aBaseDomain) {
// TODO: Bug 1705032
return this.deleteByHost(aBaseDomain, {});
async deleteByBaseDomain(aBaseDomain) {
ChromeUtils.clearStyleSheetCacheByBaseDomain(aBaseDomain);
},
async deleteAll() {

View File

@ -1,3 +1,7 @@
[browser_css_cache.js]
support-files =
file_css_cache.css
file_css_cache.html
[browser_serviceworkers.js]
[browser_quota.js]
support-files = worker.js

View File

@ -0,0 +1,129 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const BASE_DOMAIN_A = "example.com";
const ORIGIN_A = `https://${BASE_DOMAIN_A}`;
const ORIGIN_A_HTTP = `http://${BASE_DOMAIN_A}`;
const ORIGIN_A_SUB = `https://test1.${BASE_DOMAIN_A}`;
const BASE_DOMAIN_B = "example.org";
const ORIGIN_B = `https://${BASE_DOMAIN_B}`;
const ORIGIN_B_HTTP = `http://${BASE_DOMAIN_B}`;
const ORIGIN_B_SUB = `https://test1.${BASE_DOMAIN_B}`;
const TEST_ROOT_DIR = getRootDirectory(gTestPath);
// Stylesheets are cached per process, so we need to keep tabs open for the
// duration of a test.
let tabs = {};
function getTestURLForOrigin(origin) {
return (
TEST_ROOT_DIR.replace("chrome://mochitests/content", origin) +
"file_css_cache.html"
);
}
async function testCached(origin, isCached) {
let url = getTestURLForOrigin(origin);
let numParsed;
let tab = tabs[origin];
let loadedPromise;
if (!tab) {
info("Creating new tab for " + url);
tab = BrowserTestUtils.addTab(gBrowser, url);
loadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
tabs[origin] = tab;
} else {
loadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
tab.linkedBrowser.reload();
}
await loadedPromise;
numParsed = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
return SpecialPowers.getDOMWindowUtils(content).parsedStyleSheets;
});
// Stylesheets is cached if numParsed is 0.
is(!numParsed, isCached, `${origin} is${isCached ? " " : " not "}cached`);
}
async function addTestTabs() {
await testCached(ORIGIN_A, false);
await testCached(ORIGIN_A_SUB, false);
await testCached(ORIGIN_A_HTTP, false);
await testCached(ORIGIN_B, false);
await testCached(ORIGIN_B_SUB, false);
await testCached(ORIGIN_B_HTTP, false);
// Test that the cache has been populated.
await testCached(ORIGIN_A, true);
await testCached(ORIGIN_A_SUB, true);
await testCached(ORIGIN_A_HTTP, true);
await testCached(ORIGIN_B, true);
await testCached(ORIGIN_B_SUB, true);
await testCached(ORIGIN_B_HTTP, true);
}
async function cleanupTestTabs() {
Object.values(tabs).forEach(BrowserTestUtils.removeTab);
tabs = {};
}
add_task(async function test_deleteByPrincipal() {
await addTestTabs();
// Clear data for content principal of A
info("Clearing cache for principal " + ORIGIN_A);
await new Promise(resolve => {
Services.clearData.deleteDataFromPrincipal(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(ORIGIN_A),
false,
Ci.nsIClearDataService.CLEAR_CSS_CACHE,
resolve
);
});
// Only the cache entry for ORIGIN_A should have been cleared.
await testCached(ORIGIN_A, false);
await testCached(ORIGIN_A_SUB, true);
await testCached(ORIGIN_A_HTTP, true);
await testCached(ORIGIN_B, true);
await testCached(ORIGIN_B_SUB, true);
await testCached(ORIGIN_B_HTTP, true);
// Cleanup
cleanupTestTabs();
ChromeUtils.clearStyleSheetCache();
});
add_task(async function test_deleteByBaseDomain() {
await addTestTabs();
// Clear data for base domain of A.
info("Clearing cache for base domain " + BASE_DOMAIN_A);
await new Promise(resolve => {
Services.clearData.deleteDataFromBaseDomain(
BASE_DOMAIN_A,
false,
Ci.nsIClearDataService.CLEAR_CSS_CACHE,
resolve
);
});
// All entries for A should have been cleared.
await testCached(ORIGIN_A, false);
await testCached(ORIGIN_A_SUB, false);
await testCached(ORIGIN_A_HTTP, false);
// Entries for B should still exist.
await testCached(ORIGIN_B, true);
await testCached(ORIGIN_B_SUB, true);
await testCached(ORIGIN_B_HTTP, true);
// Cleanup
cleanupTestTabs();
ChromeUtils.clearStyleSheetCache();
});

View File

@ -0,0 +1,3 @@
:root {
background-color: lime;
}

View File

@ -0,0 +1,6 @@
<!doctype html>
<meta charset="utf-8">
<head>
<link rel="stylesheet" href="file_css_cache.css">
</head>
<body></body>