Bug 1926591 - PreferencesCleaner support for OriginAttributesPattern. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D221783
This commit is contained in:
Paul Zuehlcke 2024-11-18 11:40:02 +00:00
parent 09e9104b76
commit 617efd9bd8
11 changed files with 1128 additions and 256 deletions

View File

@ -87,6 +87,11 @@ interface nsIContentPrefObserver : nsISupports
* window or channel whose content pertains to the preferences being modified or
* retrieved.
*
* The remove methods, used to clear prefs, also accept an optional
* nsILoadContext. If you pass null, both private and normal browsing data will
* be removed. Passing a normal browsing context will remove only normal
* browsing data, and passing a private browsing context will remove only
* private browsing data.
*
* Callbacks
*
@ -255,7 +260,9 @@ interface nsIContentPrefService2 : nsISupports
*
* @param domain The preference's domain.
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByDomainAndName(in AString domain,
@ -269,7 +276,9 @@ interface nsIContentPrefService2 : nsISupports
*
* @param domain The preferences' domain.
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeBySubdomainAndName(in AString domain,
@ -281,7 +290,9 @@ interface nsIContentPrefService2 : nsISupports
* Removes the preference with no domain and the given name.
*
* @param name The preference's name.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeGlobal(in AString name,
@ -292,7 +303,9 @@ interface nsIContentPrefService2 : nsISupports
* Removes all preferences with the given domain.
*
* @param domain The preferences' domain.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByDomain(in AString domain,
@ -304,7 +317,9 @@ interface nsIContentPrefService2 : nsISupports
* of the given domain.
*
* @param domain The preferences' domain.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeBySubdomain(in AString domain,
@ -316,7 +331,9 @@ interface nsIContentPrefService2 : nsISupports
* global preferences with the given name.
*
* @param name The preferences' name.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeByName(in AString name,
@ -327,7 +344,9 @@ interface nsIContentPrefService2 : nsISupports
* Removes all non-global preferences -- in other words, all preferences that
* have a domain.
*
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllDomains(in nsILoadContext context,
@ -337,7 +356,9 @@ interface nsIContentPrefService2 : nsISupports
* Removes all non-global preferences created after and including |since|.
*
* @param since Timestamp in milliseconds.
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllDomainsSince(in unsigned long long since,
@ -348,7 +369,9 @@ interface nsIContentPrefService2 : nsISupports
* Removes all global preferences -- in other words, all preferences that have
* no domain.
*
* @param context The private-browsing context, if any.
* @param [context] Optional context to pass to indicate whether normal or
* private-browsing data should be removed. Passing null
* removes both private and normal browsing data.
* @param callback handleCompletion is called when the operation completes.
*/
void removeAllGlobals(in nsILoadContext context,

View File

@ -1546,13 +1546,26 @@ const PermissionsCleaner = {
};
const PreferencesCleaner = {
deleteByHost(aHost) {
deleteByHost(aHost, aOriginAttributes = {}) {
aOriginAttributes =
ChromeUtils.fillNonDefaultOriginAttributes(aOriginAttributes);
let loadContext;
if (
aOriginAttributes.privateBrowsingId ==
Services.scriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID
) {
loadContext = Cu.createLoadContext();
} else {
loadContext = Cu.createPrivateLoadContext();
}
// Also clears subdomains of aHost.
return new Promise((aResolve, aReject) => {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
cps2.removeBySubdomain(aHost, null, {
cps2.removeBySubdomain(aHost, loadContext, {
handleCompletion: aReason => {
if (aReason === cps2.COMPLETE_ERROR) {
aReject();
@ -1560,7 +1573,6 @@ const PreferencesCleaner = {
aResolve();
}
},
handleError() {},
});
});
},
@ -1569,23 +1581,74 @@ const PreferencesCleaner = {
return this.deleteByHost(aPrincipal.host, aPrincipal.originAttributes);
},
deleteBySite(aSchemelessSite, _aOriginAttributesPattern) {
// TODO: aOriginAttributesPattern
return this.deleteByHost(aSchemelessSite, {});
async deleteBySite(aSchemelessSite, aOriginAttributesPattern) {
// If aOriginAttributesPattern does not specify private or normal browsing
// clear both.
let loadContext = null;
// If the pattern filters by normal or private browsing mode only clear that mode.
if (aOriginAttributesPattern.privateBrowsingId != null) {
// The default private browsing ID is 0 which is non private browsing mode
// / normal mode.
let isPrivateBrowsing =
aOriginAttributesPattern.privateBrowsingId !=
Ci.nsIScriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID;
loadContext = isPrivateBrowsing
? Cu.createPrivateLoadContext()
: Cu.createLoadContext();
}
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
await new Promise((aResolve, aReject) => {
cps2.removeBySubdomain(aSchemelessSite, loadContext, {
handleCompletion: aReason => {
if (aReason === cps2.COMPLETE_ERROR) {
aReject();
} else {
aResolve();
}
},
});
});
},
async deleteByRange(aFrom) {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
cps2.removeAllDomainsSince(aFrom / 1000, null);
await new Promise((aResolve, aReject) => {
cps2.removeAllDomainsSince(aFrom / 1000, null, {
handleCompletion: aReason => {
if (aReason === cps2.COMPLETE_ERROR) {
aReject();
} else {
aResolve();
}
},
});
});
},
async deleteAll() {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
cps2.removeAllDomains(null);
await new Promise((aResolve, aReject) => {
cps2.removeAllDomains(null, {
handleCompletion: aReason => {
if (aReason === cps2.COMPLETE_ERROR) {
aReject();
} else {
aResolve();
}
},
});
});
},
};

View File

@ -0,0 +1,371 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
// Async wrappers around content pref service setter / getter. Would be nice if
// the service provided that itself.
async function setContentPref(domain, name, value, isPBM) {
return new Promise((resolve, reject) => {
let loadContext = isPBM
? Cu.createPrivateLoadContext()
: Cu.createLoadContext();
cps2.set(domain, name, value, loadContext, {
handleCompletion: aReason => {
if (aReason === cps2.COMPLETE_ERROR) {
reject(new Error("Failed to set content pref"));
} else {
resolve();
}
},
});
});
}
async function getContentPref(domain, name, isPBM) {
return new Promise((resolve, reject) => {
let loadContext = isPBM
? Cu.createPrivateLoadContext()
: Cu.createLoadContext();
let result;
cps2.getByDomainAndName(domain, name, loadContext, {
handleResult({ value }) {
result = value;
},
handleCompletion(aReason) {
if (aReason === cps2.COMPLETE_ERROR) {
reject(new Error("Failed to get content pref"));
} else {
resolve(result);
}
},
});
});
}
add_task(async function test_deleteByHost() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "bar", "bar", true);
await setContentPref("foo.example.org", "bar", "bar", false);
await setContentPref("foo.example.org", "bar", "bar", true);
await setContentPref("bar.foo.example.org", "subsub", "subsub", false);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "bar", true), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", true), "bar");
Assert.equal(
await getContentPref("bar.foo.example.org", "subsub", false),
"subsub"
);
await new Promise(aResolve => {
Services.clearData.deleteDataFromHost(
"foo.example.org",
true,
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info(
"Verify content prefs matching host 'foo.example.org' have been cleared"
);
Assert.equal(
await getContentPref("example.com", "foo", false),
"foo",
"Unrelated domain entry should still exist."
);
Assert.equal(
await getContentPref("example.org", "bar", false),
"bar",
"Base domain entry should still exist."
);
Assert.equal(
await getContentPref("example.org", "bar", true),
"bar",
"Base domain PBM entry should still exist."
);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
undefined,
"Exact domain match should have been cleared in normal browsing."
);
Assert.equal(
await getContentPref("foo.example.org", "bar", true),
"bar",
"Exact domain match should not have been cleared in private browsing."
);
Assert.equal(
await getContentPref("bar.foo.example.org", "subsub", false),
undefined,
"Subdomain should have been cleared"
);
await SiteDataTestUtils.clear();
});
add_task(async function test_deleteByPrincipal() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "bar", "bar", true);
await setContentPref("foo.example.org", "bar", "bar", false);
await setContentPref("foo.example.org", "bar", "bar", true);
await setContentPref("bar.foo.example.org", "subsub", "subsub", false);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "bar", true), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", true), "bar");
Assert.equal(
await getContentPref("bar.foo.example.org", "subsub", false),
"subsub"
);
await new Promise(aResolve => {
Services.clearData.deleteDataFromPrincipal(
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
"https://foo.example.org"
),
true,
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info(
"Verify content prefs matching principal 'https://foo.example.org' have been cleared"
);
Assert.equal(
await getContentPref("example.com", "foo", false),
"foo",
"Unrelated domain entry should still exist."
);
Assert.equal(
await getContentPref("example.org", "bar", false),
"bar",
"Base domain entry should still exist."
);
Assert.equal(
await getContentPref("example.org", "bar", true),
"bar",
"Base domain PBM entry should still exist."
);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
undefined,
"Exact domain match should have been cleared in normal browsing."
);
Assert.equal(
await getContentPref("foo.example.org", "bar", true),
"bar",
"Exact domain match should NOT have been cleared in private browsing."
);
// TODO: PreferencesCleaner does not clear by exact principal but includes
// subdomains of the given principal.
Assert.equal(
await getContentPref("bar.foo.example.org", "subsub", false),
undefined,
"TODO: Subdomain entry should still exist."
);
await SiteDataTestUtils.clear();
});
add_task(async function test_deleteBySite() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "bar", "bar", true);
await setContentPref("foo.example.org", "bar", "bar", false);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "bar", true), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
await new Promise(aResolve => {
Services.clearData.deleteDataFromSite(
"example.org",
{},
true,
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info(
"Verify content prefs for 'example.org' have been cleared, including PBM."
);
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), undefined);
Assert.equal(await getContentPref("example.org", "bar", true), undefined);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
undefined
);
await SiteDataTestUtils.clear();
});
add_task(async function test_deleteBySite_pattern() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "barPBM", "barPBM", true);
await setContentPref("foo.example.org", "bar", "bar", false);
await setContentPref("foo.example.org", "subPBM", "subPBM", true);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "barPBM", true), "barPBM");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
Assert.equal(
await getContentPref("foo.example.org", "subPBM", true),
"subPBM"
);
await new Promise(aResolve => {
Services.clearData.deleteDataFromSite(
"example.org",
{ privateBrowsingId: 1 },
true,
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info(
"Verify content prefs for 'example.org' have been cleared, but only for PBM."
);
Assert.equal(
await getContentPref("example.com", "foo", false),
"foo",
"Unrelated domain should have not been cleared."
);
Assert.equal(
await getContentPref("example.org", "bar", false),
"bar",
"Base domain entry should NOT have been cleared for normal browsing."
);
Assert.equal(
await getContentPref("example.org", "barPBM", true),
undefined,
"Base domain entry should have been cleared for private browsing."
);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
"bar",
"Subdomain entry should not have been cleared for normal browsing."
);
Assert.equal(
await getContentPref("foo.example.org", "subPBM", true),
undefined,
"Subdomain entry should have been cleared for private browsing."
);
await SiteDataTestUtils.clear();
});
// TODO: implement a proper range clearing test. We're currently lacking the
// capability to set content prefs with a specific creation timestamp. This
// tests only clearing everything if the entire time range is passed.
add_task(async function test_deleteByRange() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "bar", "bar", true);
await setContentPref("foo.example.org", "bar", "bar", false);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "bar", true), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
info("Delete entire time range.");
await new Promise(aResolve => {
Services.clearData.deleteDataInTimeRange(
0,
Date.now() * 1000,
true,
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info("Verify all content prefs have been cleared");
Assert.equal(await getContentPref("example.com", "foo", false), undefined);
Assert.equal(await getContentPref("example.org", "bar", false), undefined);
Assert.equal(await getContentPref("example.org", "bar", true), undefined);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
undefined
);
await SiteDataTestUtils.clear();
});
add_task(async function test_deleteAll() {
info("Set content prefs");
await setContentPref("example.com", "foo", "foo", false);
await setContentPref("example.org", "bar", "bar", false);
await setContentPref("example.org", "bar", "bar", true);
await setContentPref("foo.example.org", "bar", "bar", false);
info("Verify content prefs have been set");
Assert.equal(await getContentPref("example.com", "foo", false), "foo");
Assert.equal(await getContentPref("example.org", "bar", false), "bar");
Assert.equal(await getContentPref("example.org", "bar", true), "bar");
Assert.equal(await getContentPref("foo.example.org", "bar", false), "bar");
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_CONTENT_PREFERENCES,
value => {
Assert.equal(value, 0);
aResolve();
}
);
});
info("Verify all content prefs have been cleared");
Assert.equal(await getContentPref("example.com", "foo", false), undefined);
Assert.equal(await getContentPref("example.org", "bar", false), undefined);
Assert.equal(await getContentPref("example.org", "bar", true), undefined);
Assert.equal(
await getContentPref("foo.example.org", "bar", false),
undefined
);
await SiteDataTestUtils.clear();
});

View File

@ -19,6 +19,8 @@ skip-if = ["condprof"] # Bug 1893058
["test_clear_data_service_flags.js"]
["test_content_preferences.js"]
["test_cookie_banner_handling.js"]
["test_cookies.js"]

View File

@ -529,16 +529,18 @@ ContentPrefService2.prototype = {
this._cache.remove(sgroup, name);
}
let isPrivate = context && context.usePrivateBrowsing;
let stmts = [];
// First get the matching prefs.
stmts.push(this._commonGetStmt(group, name, includeSubdomains));
if (context == null || !isPrivate) {
// First get the matching prefs.
stmts.push(this._commonGetStmt(group, name, includeSubdomains));
// Delete the matching prefs.
let stmt = this._stmtWithGroupClause(
group,
includeSubdomains,
`
// Delete the matching prefs.
let stmt = this._stmtWithGroupClause(
group,
includeSubdomains,
`
DELETE FROM prefs
WHERE settingID = (SELECT id FROM settings WHERE name = :name) AND
CASE typeof(:group)
@ -546,45 +548,69 @@ ContentPrefService2.prototype = {
ELSE prefs.groupID IN (${GROUP_CLAUSE})
END
`
);
stmt.params.name = name;
stmts.push(stmt);
);
stmt.params.name = name;
stmts.push(stmt);
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
}
let prefs = new ContentPrefStore();
let prefsNonPrivateBrowsing = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
if (ok) {
this._cache.set(group, name, undefined);
if (isPrivate) {
for (let [sgroup] of this._pbStore.match(
group,
name,
includeSubdomains
)) {
prefs.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
// Only execute if we have statements to run. stmts is empty for clearing
// only private browsing data.
let queryPromise = Promise.resolve([
Ci.nsIContentPrefService2.COMPLETE_OK,
false,
]);
if (stmts.length) {
queryPromise = new Promise(resolve => {
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
prefsNonPrivateBrowsing.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
this._cache.set(group, name, undefined);
resolve([reason, ok]);
},
onError: nsresult => {
cbHandleError(callback, nsresult);
},
});
});
}
let prefsPrivateBrowsing = new ContentPrefStore();
if (isPrivate || context == null) {
for (let [sgroup] of this._pbStore.match(
group,
name,
includeSubdomains
)) {
prefsPrivateBrowsing.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
queryPromise.then(([reason, hasNonPrivateEntries]) => {
// Notify caller of completion.
cbHandleCompletion(callback, reason);
// Notify for non PBM changes.
if (hasNonPrivateEntries) {
for (let [sgroup] of prefsNonPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, name, false);
}
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, ,] of prefs) {
this._notifyPrefRemoved(sgroup, name, isPrivate);
}
}
},
onError: nsresult => {
cbHandleError(callback, nsresult);
},
}
// Notify for PBM changes.
for (let [sgroup] of prefsPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, name, true);
}
});
},
@ -634,93 +660,126 @@ ContentPrefService2.prototype = {
this._cache.removeGroup(sgroup);
}
let isPrivate = context?.usePrivateBrowsing;
let stmts = [];
// First get the matching prefs, then delete groups and prefs that reference
// deleted groups.
if (group) {
stmts.push(
this._stmtWithGroupClause(
group,
includeSubdomains,
`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE prefs.groupID IN (${GROUP_CLAUSE})
`
)
);
stmts.push(
this._stmtWithGroupClause(
group,
includeSubdomains,
`DELETE FROM groups WHERE id IN (${GROUP_CLAUSE})`
)
);
// For null we clear normal and private browsing data. false only clears
// normal browsing data. true only clears private browsing data.
if (context == null || !isPrivate) {
// First get the matching prefs, then delete groups and prefs that reference
// deleted groups.
if (group) {
stmts.push(
this._stmtWithGroupClause(
group,
includeSubdomains,
`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE prefs.groupID IN (${GROUP_CLAUSE})
`
)
);
stmts.push(
this._stmtWithGroupClause(
group,
includeSubdomains,
`DELETE FROM groups WHERE id IN (${GROUP_CLAUSE})`
)
);
stmts.push(
this._stmt(`
DELETE FROM prefs
WHERE groupID NOTNULL AND groupID NOT IN (SELECT id FROM groups)
`)
);
} else {
stmts.push(
this._stmt(`
SELECT NULL AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE prefs.groupID IS NULL
`)
);
stmts.push(this._stmt("DELETE FROM prefs WHERE groupID IS NULL"));
}
// Finally delete settings that are no longer referenced.
stmts.push(
this._stmt(`
DELETE FROM prefs
WHERE groupID NOTNULL AND groupID NOT IN (SELECT id FROM groups)
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`)
);
} else {
stmts.push(
this._stmt(`
SELECT NULL AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE prefs.groupID IS NULL
`)
);
stmts.push(this._stmt("DELETE FROM prefs WHERE groupID IS NULL"));
}
// Finally delete settings that are no longer referenced.
stmts.push(
this._stmt(`
DELETE FROM settings
WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)
`)
);
let prefsNonPrivateBrowsing = new ContentPrefStore();
let prefs = new ContentPrefStore();
// Only execute if we have statements to run. stmts is empty for clearing
// only private browsing data.
let queryPromise = Promise.resolve([
Ci.nsIContentPrefService2.COMPLETE_OK,
false,
]);
if (stmts.length) {
queryPromise = new Promise(resolve => {
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefsNonPrivateBrowsing.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
resolve([reason, ok]);
},
onError: nsresult => {
cbHandleError(callback, nsresult);
},
});
});
}
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
if (ok && isPrivate) {
for (let [sgroup, sname] of this._pbStore) {
if (
!group ||
(!includeSubdomains && group == sgroup) ||
(includeSubdomains &&
sgroup &&
this._pbStore.groupsMatchIncludingSubdomains(group, sgroup))
) {
prefs.set(sgroup, sname, undefined);
this._pbStore.remove(sgroup, sname);
}
}
let prefsPrivateBrowsing = new ContentPrefStore();
if (isPrivate || context == null) {
for (let [sgroup, sname] of this._pbStore) {
if (
!group ||
(!includeSubdomains && group == sgroup) ||
(includeSubdomains &&
sgroup &&
this._pbStore.groupsMatchIncludingSubdomains(group, sgroup))
) {
prefsPrivateBrowsing.set(sgroup, sname, undefined);
this._pbStore.remove(sgroup, sname);
}
}
}
queryPromise
.then(([reason, hasNonPrivateEntries]) => {
// Notify caller of completion.
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, sname] of prefs) {
this._notifyPrefRemoved(sgroup, sname, isPrivate);
// Notify for non PBM changes.
if (hasNonPrivateEntries) {
for (let [sgroup, sname] of prefsNonPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, sname, false);
}
}
},
onError: nsresult => {
// Notify for PBM changes.
for (let [sgroup, sname] of prefsPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, sname, true);
}
})
.catch(nsresult => {
cbHandleError(callback, nsresult);
},
});
});
},
_removeAllDomainsSince: function CPS2__removeAllDomainsSince(
@ -737,60 +796,95 @@ ContentPrefService2.prototype = {
// Invalidate all the group cache because we don't know which groups will be removed.
this._cache.removeAllGroups();
let isPrivate = context?.usePrivateBrowsing;
let stmts = [];
// Get prefs that are about to be removed to notify about their removal.
let stmt = this._stmt(`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// For null we clear normal and private browsing data. false only clears
// normal browsing data. true only clears private browsing data.
if (context == null || !isPrivate) {
// Get prefs that are about to be removed to notify about their removal.
let stmt = this._stmt(`
SELECT groups.name AS grp, settings.name AS name
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Do the actual remove.
stmt = this._stmt(`
DELETE FROM prefs WHERE groupID NOTNULL AND timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Do the actual remove.
stmt = this._stmt(`
DELETE FROM prefs WHERE groupID NOTNULL AND timestamp >= :since
`);
stmt.params.since = since;
stmts.push(stmt);
// Cleanup no longer used values.
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
// Cleanup no longer used values.
stmts = stmts.concat(this._settingsAndGroupsCleanupStmts());
}
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
// This nukes all the groups in _pbStore since we don't have their timestamp
// information.
if (ok && isPrivate) {
for (let [sgroup, sname] of this._pbStore) {
if (sgroup) {
prefs.set(sgroup, sname, undefined);
}
}
this._pbStore.removeAllGroups();
let prefsNonPrivateBrowsing = new ContentPrefStore();
// Only execute if we have statements to run. stmts is empty for clearing
// only private browsing data.
let queryPromise = Promise.resolve([
Ci.nsIContentPrefService2.COMPLETE_OK,
false,
]);
if (stmts.length) {
queryPromise = new Promise(resolve => {
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
let name = row.getResultByName("name");
prefsNonPrivateBrowsing.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
resolve([reason, ok]);
},
onError: nsresult => {
cbHandleError(callback, nsresult);
},
});
});
}
let prefsPrivateBrowsing = new ContentPrefStore();
// This nukes all the groups in _pbStore since we don't have their timestamp
// information.
if (isPrivate || context == null) {
for (let [sgroup, sname] of this._pbStore) {
if (sgroup) {
prefsPrivateBrowsing.set(sgroup, sname, undefined);
}
}
this._pbStore.removeAllGroups();
}
queryPromise
.then(([reason, hasNonPrivateEntries]) => {
// Notify caller of completion.
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, sname] of prefs) {
this._notifyPrefRemoved(sgroup, sname, isPrivate);
// Notify for non PBM changes.
if (hasNonPrivateEntries) {
for (let [sgroup, sname] of prefsNonPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, sname, false);
}
}
},
onError: nsresult => {
// Notify for PBM changes.
for (let [sgroup, sname] of prefsPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, sname, true);
}
})
.catch(nsresult => {
cbHandleError(callback, nsresult);
},
});
});
},
removeAllDomainsSince: function CPS2_removeAllDomainsSince(
@ -817,76 +911,110 @@ ContentPrefService2.prototype = {
}
}
let isPrivate = context?.usePrivateBrowsing;
let stmts = [];
// First get the matching prefs. Include null if any of those prefs are
// global.
let stmt = this._stmt(`
SELECT groups.name AS grp
FROM prefs
JOIN settings ON settings.id = prefs.settingID
JOIN groups ON groups.id = prefs.groupID
WHERE settings.name = :name
UNION
SELECT NULL AS grp
WHERE EXISTS (
SELECT prefs.id
// For null we clear normal and private browsing data. false only clears
// normal browsing data. true only clears private browsing data.
if (context == null || !isPrivate) {
// First get the matching prefs. Include null if any of those prefs are
// global.
let stmt = this._stmt(`
SELECT groups.name AS grp
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE settings.name = :name AND prefs.groupID IS NULL
)
`);
stmt.params.name = name;
stmts.push(stmt);
JOIN groups ON groups.id = prefs.groupID
WHERE settings.name = :name
UNION
SELECT NULL AS grp
WHERE EXISTS (
SELECT prefs.id
FROM prefs
JOIN settings ON settings.id = prefs.settingID
WHERE settings.name = :name AND prefs.groupID IS NULL
)
`);
stmt.params.name = name;
stmts.push(stmt);
// Delete the target settings.
stmt = this._stmt("DELETE FROM settings WHERE name = :name");
stmt.params.name = name;
stmts.push(stmt);
// Delete the target settings.
stmt = this._stmt("DELETE FROM settings WHERE name = :name");
stmt.params.name = name;
stmts.push(stmt);
// Delete prefs and groups that are no longer used.
stmts.push(
this._stmt(
"DELETE FROM prefs WHERE settingID NOT IN (SELECT id FROM settings)"
)
);
stmts.push(
this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`)
);
// Delete prefs and groups that are no longer used.
stmts.push(
this._stmt(
"DELETE FROM prefs WHERE settingID NOT IN (SELECT id FROM settings)"
)
);
stmts.push(
this._stmt(`
DELETE FROM groups WHERE id NOT IN (
SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL
)
`)
);
}
let prefs = new ContentPrefStore();
let isPrivate = context && context.usePrivateBrowsing;
let prefsNonPrivateBrowsing = new ContentPrefStore();
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
prefs.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
if (ok && isPrivate) {
for (let [sgroup, sname] of this._pbStore) {
if (sname === name) {
prefs.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
// Only execute if we have statements to run. stmts is empty for clearing
// only private browsing data.
let queryPromise = Promise.resolve([
Ci.nsIContentPrefService2.COMPLETE_OK,
false,
]);
if (stmts.length) {
queryPromise = new Promise(resolve => {
this._execStmts(stmts, {
onRow: row => {
let grp = row.getResultByName("grp");
prefsNonPrivateBrowsing.set(grp, name, undefined);
this._cache.set(grp, name, undefined);
},
onDone: (reason, ok) => {
resolve([reason, ok]);
},
onError: nsresult => {
cbHandleError(callback, nsresult);
},
});
});
}
let prefsPrivateBrowsing = new ContentPrefStore();
if (isPrivate || context == null) {
for (let [sgroup, sname] of this._pbStore) {
if (sname === name) {
prefsPrivateBrowsing.set(sgroup, name, undefined);
this._pbStore.remove(sgroup, name);
}
}
}
queryPromise
.then(([reason, hasNonPrivateEntries]) => {
// Notify caller of completion.
cbHandleCompletion(callback, reason);
if (ok) {
for (let [sgroup, ,] of prefs) {
this._notifyPrefRemoved(sgroup, name, isPrivate);
// Notify for non PBM changes.
if (hasNonPrivateEntries) {
for (let [sgroup, ,] of prefsNonPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, name, false);
}
}
},
onError: nsresult => {
// Notify for PBM changes.
for (let [sgroup, ,] of prefsPrivateBrowsing) {
this._notifyPrefRemoved(sgroup, name, true);
}
})
.catch(nsresult => {
cbHandleError(callback, nsresult);
},
});
});
},
/**

View File

@ -175,16 +175,18 @@ add_task(async function privateBrowsing() {
await set("a.com", "foo", 8, context);
await setGlobal("foo", 9, context);
await new Promise(resolve =>
cps.removeByDomainAndName("a.com", "foo", context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeByDomainAndName("a.com", "foo", null, makeCallback(resolve))
);
await new Promise(resolve =>
cps.removeGlobal("foo", context, makeCallback(resolve))
cps.removeGlobal("foo", null, makeCallback(resolve))
);
await new Promise(resolve =>
cps.removeGlobal("qux", context, makeCallback(resolve))
cps.removeGlobal("qux", null, makeCallback(resolve))
);
await new Promise(resolve =>
cps.removeByDomainAndName("b.com", "foo", context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeByDomainAndName("b.com", "foo", null, makeCallback(resolve))
);
await dbOK([
["a.com", "bar", 2],
@ -209,6 +211,74 @@ add_task(async function privateBrowsing() {
await reset();
});
/**
* Tests that when clearing data for normal browsing, private browsing is not
* affected and vice versa.
*/
add_task(async function privateBrowsingIsolatedRemoval() {
await set("a.com", "foo", 1);
await set("a.com", "bar", 2);
await setGlobal("foo", 3);
await setGlobal("bar", 4);
await setGlobal("qux", 5);
await set("b.com", "foo", 6);
await set("b.com", "bar", 7);
await set("a.com", "foo", 8, privateLoadContext);
await set("b.com", "foo", 9, privateLoadContext);
await setGlobal("foo", 10, privateLoadContext);
info("For a.com only clear the normal browsing entry.");
await new Promise(resolve =>
cps.removeByDomainAndName(
"a.com",
"foo",
loadContext,
makeCallback(resolve)
)
);
info("For a.com, 'foo' only the PBM entry should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], 6);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
await getOK(["b.com", "foo", privateLoadContext], 9);
await getGlobalOK(["foo", privateLoadContext], 10);
info("For b.com only clear PBM entry.");
await new Promise(resolve =>
cps.removeByDomainAndName(
"b.com",
"foo",
privateLoadContext,
makeCallback(resolve)
)
);
info("For b.com, 'foo' only the non PBM entry should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], 6);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
// This still returns an entry because even if a PBM load context is passed we
// will fall back to non PBM entries with the same key.
await getOK(["b.com", "foo", privateLoadContext], 6);
await getGlobalOK(["foo", privateLoadContext], 10);
await reset();
});
add_task(async function erroneous() {
do_check_throws(() => cps.removeByDomainAndName(null, "foo", null));
do_check_throws(() => cps.removeByDomainAndName("", "foo", null));

View File

@ -51,7 +51,8 @@ add_task(async function privateBrowsing() {
await set("a.com", "foo", 6, context);
await setGlobal("foo", 7, context);
await new Promise(resolve =>
cps.removeAllDomains(context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeAllDomains(null, makeCallback(resolve))
);
await dbOK([
[null, "foo", 3],

View File

@ -73,7 +73,8 @@ add_task(async function privateBrowsing() {
await set("a.com", "foo", 6, context);
await setGlobal("foo", 7, context);
await new Promise(resolve =>
cps.removeAllDomainsSince(0, context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeAllDomainsSince(0, null, makeCallback(resolve))
);
await dbOK([
[null, "foo", 3],
@ -93,6 +94,63 @@ add_task(async function privateBrowsing() {
await reset();
});
/**
* Tests that when clearing data for normal browsing, private browsing is not
* affected and vice versa.
*/
add_task(async function privateBrowsingIsolatedRemoval() {
await set("a.com", "foo", 1);
await set("a.com", "bar", 2);
await setGlobal("foo", 3);
await setGlobal("bar", 4);
await setGlobal("qux", 5);
await set("b.com", "foo", 6);
await set("b.com", "bar", 7);
await set("a.com", "foo", 8, privateLoadContext);
await set("b.com", "foo", 9, privateLoadContext);
await setGlobal("foo", 10, privateLoadContext);
info("Clear all PBM data.");
await new Promise(resolve =>
cps.removeAllDomainsSince(0, privateLoadContext, makeCallback(resolve))
);
await getOK(["a.com", "foo", loadContext], 1);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], 6);
await getOK(["b.com", "bar", loadContext], 7);
// This still returns an entry because even if a PBM load context is passed we
// will fall back to non PBM entries with the same key.
await getOK(["a.com", "foo", privateLoadContext], 1);
await getOK(["b.com", "foo", privateLoadContext], 6);
await getGlobalOK(["foo", privateLoadContext], 10);
info("Clear all non PBM data.");
await new Promise(resolve =>
cps.removeAllDomainsSince(0, loadContext, makeCallback(resolve))
);
info("Should have cleared all domain keyed entries");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], undefined);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], undefined);
await getOK(["b.com", "bar", loadContext], undefined);
await getOK(["a.com", "foo", privateLoadContext], undefined);
await getOK(["b.com", "foo", privateLoadContext], undefined);
await getGlobalOK(["foo", privateLoadContext], 10);
await reset();
});
add_task(async function erroneous() {
do_check_throws(() => cps.removeAllDomainsSince(null, "bogus"));
await reset();

View File

@ -149,12 +149,14 @@ add_task(async function privateBrowsing() {
await set("b.com", "foo", 7, context);
await setGlobal("foo", 8, context);
await new Promise(resolve =>
cps.removeByDomain("a.com", context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeByDomain("a.com", null, makeCallback(resolve))
);
await getOK(["b.com", "foo", context], 7);
await getGlobalOK(["foo", context], 8);
await new Promise(resolve =>
cps.removeAllGlobals(context, makeCallback(resolve))
// Passing context=null clears both normal and private browsing data.
cps.removeAllGlobals(null, makeCallback(resolve))
);
await dbOK([["b.com", "foo", 5]]);
await getOK(["a.com", "foo", context], undefined);
@ -171,6 +173,65 @@ add_task(async function privateBrowsing() {
await reset();
});
/**
* Tests that when clearing data for normal browsing, private browsing is not
* affected and vice versa.
*/
add_task(async function privateBrowsingIsolatedRemoval() {
await set("a.com", "foo", 1);
await set("a.com", "bar", 2);
await setGlobal("foo", 3);
await setGlobal("bar", 4);
await setGlobal("qux", 5);
await set("b.com", "foo", 6);
await set("b.com", "bar", 7);
await set("a.com", "foo", 8, privateLoadContext);
await set("b.com", "foo", 9, privateLoadContext);
await setGlobal("foo", 10, privateLoadContext);
info("For a.com only clear the normal browsing entries.");
await new Promise(resolve =>
cps.removeByDomain("a.com", loadContext, makeCallback(resolve))
);
info("For a.com only the PBM entries should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], undefined);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], 6);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
await getOK(["b.com", "foo", privateLoadContext], 9);
await getGlobalOK(["foo", privateLoadContext], 10);
info("For b.com only clear PBM entries.");
await new Promise(resolve =>
cps.removeByDomain("b.com", privateLoadContext, makeCallback(resolve))
);
info("For b.com only the non PBM entries should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], undefined);
await getGlobalOK(["foo", loadContext], 3);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], 6);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
info("b.com, foo, pbm should be cleared");
// This still returns an entry because even if a PBM load context is passed we
// will fall back to non PBM entries with the same key.
await getOK(["b.com", "foo", privateLoadContext], 6);
await getGlobalOK(["foo", privateLoadContext], 10);
await reset();
});
add_task(async function erroneous() {
do_check_throws(() => cps.removeByDomain(null, null));
do_check_throws(() => cps.removeByDomain("", null));

View File

@ -60,7 +60,7 @@ add_task(async function privateBrowsing() {
await setGlobal("foo", 8, context);
await set("b.com", "bar", 9, context);
await new Promise(resolve =>
cps.removeByName("bar", context, makeCallback(resolve))
cps.removeByName("bar", null, makeCallback(resolve))
);
await dbOK([
["a.com", "foo", 1],
@ -83,6 +83,107 @@ add_task(async function privateBrowsing() {
await reset();
});
/**
* Tests that when clearing data for normal browsing, private browsing is not
* affected and vice versa.
*/
add_task(async function privateBrowsingIsolatedRemoval() {
await set("a.com", "foo", 1);
await set("a.com", "bar", 2);
await setGlobal("foo", 3);
await setGlobal("bar", 4);
await setGlobal("qux", 5);
await set("b.com", "foo", 6);
await set("b.com", "bar", 7);
await set("a.com", "foo", 8, privateLoadContext);
await set("b.com", "foo", 9, privateLoadContext);
await set("b.com", "bar", 10, privateLoadContext);
await setGlobal("foo", 11, privateLoadContext);
info("Clear 'foo' for non PBM.");
await new Promise(resolve =>
cps.removeByName("foo", loadContext, makeCallback(resolve))
);
info("For 'foo' only the PBM entries should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], undefined);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], undefined);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
await getOK(["b.com", "foo", privateLoadContext], 9);
await getOK(["b.com", "bar", privateLoadContext], 10);
await getGlobalOK(["foo", privateLoadContext], 11);
info("Clear 'bar' for PBM.");
await new Promise(resolve =>
cps.removeByName("bar", privateLoadContext, makeCallback(resolve))
);
info("For 'bar' only the non PBM entries should remain.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], undefined);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], undefined);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], 8);
await getOK(["b.com", "foo", privateLoadContext], 9);
// This still returns an entry because even if a PBM load context is passed we
// will fall back to non PBM entry with the same key.
await getOK(["b.com", "bar", privateLoadContext], 7);
await getGlobalOK(["foo", privateLoadContext], 11);
info("Clear 'foo' for PBM.");
await new Promise(resolve =>
cps.removeByName("foo", privateLoadContext, makeCallback(resolve))
);
info("All 'foo' entries should have been cleared.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], 2);
await getGlobalOK(["foo", loadContext], undefined);
await getGlobalOK(["bar", loadContext], 4);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], undefined);
await getOK(["b.com", "bar", loadContext], 7);
await getOK(["a.com", "foo", privateLoadContext], undefined);
await getOK(["b.com", "foo", privateLoadContext], undefined);
// This still returns an entry because even if a PBM load context is passed we
// will fall back to non PBM entry with the same key.
await getOK(["b.com", "bar", privateLoadContext], 7);
await getGlobalOK(["foo", privateLoadContext], undefined);
info("Clear 'bar' for non PBM.");
await new Promise(resolve =>
cps.removeByName("bar", loadContext, makeCallback(resolve))
);
info("All 'bar' and 'foo' entries should have been cleared.");
await getOK(["a.com", "foo", loadContext], undefined);
await getOK(["a.com", "bar", loadContext], undefined);
await getGlobalOK(["foo", loadContext], undefined);
await getGlobalOK(["bar", loadContext], undefined);
await getGlobalOK(["qux", loadContext], 5);
await getOK(["b.com", "foo", loadContext], undefined);
await getOK(["b.com", "bar", loadContext], undefined);
await getOK(["a.com", "foo", privateLoadContext], undefined);
await getOK(["b.com", "foo", privateLoadContext], undefined);
await getOK(["b.com", "bar", privateLoadContext], undefined);
await getGlobalOK(["foo", privateLoadContext], undefined);
await reset();
});
add_task(async function erroneous() {
do_check_throws(() => cps.removeByName("", null));
do_check_throws(() => cps.removeByName(null, null));

View File

@ -58,23 +58,17 @@ var observer = {
if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) {
Services.prefs.clearUserPref(LAST_DIR_PREF);
}
// Ensure that purging session history causes both the session-only PB cache
// and persistent prefs to be cleared.
let promises = [
new Promise(resolve =>
lazy.cps2.removeByName(LAST_DIR_PREF, nonPrivateLoadContext, {
handleCompletion: resolve,
})
),
new Promise(resolve =>
lazy.cps2.removeByName(LAST_DIR_PREF, privateLoadContext, {
handleCompletion: resolve,
})
),
];
// Ensure that purging session history causes both the session-only PB
// cache and persistent prefs to be cleared. Passing loadContext=null to
// cps will clear both.
let promise = new Promise(resolve =>
lazy.cps2.removeByName(LAST_DIR_PREF, null, {
handleCompletion: resolve,
})
);
// This is for testing purposes.
if (aSubject && typeof subject == "object") {
aSubject.promise = Promise.all(promises);
aSubject.promise = promise;
}
break;
}