Bug 1921207 - PushNotificationsCleaner support for OriginAttributesPattern. r=anti-tracking-reviewers,geckoview-reviewers,saschanaz,owlish,timhuang

Differential Revision: https://phabricator.services.mozilla.com/D221336
This commit is contained in:
Paul Zuehlcke 2024-09-30 12:33:06 +00:00
parent 08e141eb52
commit 4ec61c27cb
6 changed files with 144 additions and 20 deletions

View File

@ -123,8 +123,16 @@ interface nsIPushService : nsISupports
/**
* Drops every subscription for the given |domain|, or all domains if
* |domain| is "*".
* Optionally pass an OriginAttributesPattern to match against entries.
*/
void clearForDomain(in AString domain,
in jsval originAttributesPattern,
in nsIPushClearResultCallback callback);
/**
* Drops every subscription for the given |principal|.
*/
void clearForPrincipal(in nsIPrincipal principal,
in nsIPushClearResultCallback callback);
};

View File

@ -187,9 +187,25 @@ Object.assign(PushServiceParent.prototype, {
.catch(console.error);
},
clearForDomain(domain, callback) {
clearForDomain(domain, originAttributesPattern, callback) {
return this._handleRequest("Push:Clear", null, {
domain,
originAttributesPattern,
})
.then(
() => {
callback.onClear(Cr.NS_OK);
},
() => {
callback.onClear(Cr.NS_ERROR_FAILURE);
}
)
.catch(console.error);
},
clearForPrincipal(principal, callback) {
return this._handleRequest("Push:Clear", null, {
principal,
})
.then(
() => {

View File

@ -1268,20 +1268,79 @@ export var PushService = {
});
},
clear(info) {
/**
* Clear subscriptions matching either a principal or a domain and
* OriginAttributesPattern. If domain="*" is passed all records will be
* deleted.
* @param {*} options
* @param {nsIPrincipal} [options.principal] - The principal to clear
* subscriptions for. This does an exact origin match.
* @param {string} [options.domain] - Clear all records matching the domain,
* including subdomains.
* @param {OriginAttributesPattern} [options.originAttributesPattern] -
* Additional OriginAttributes filter for clearing by domain. Ignored for
* domain == "*".
* @returns {Promise} - A Promise which resolves once the operation has
* completed.
*/
clear({ principal, domain, originAttributesPattern }) {
return this._checkActivated()
.then(_ => {
return this._dropRegistrationsIf(
record =>
info.domain == "*" ||
(record.uri &&
Services.eTLD.hasRootDomain(record.uri.prePath, info.domain))
);
return this._dropRegistrationsIf(record => {
// Drop all
if (domain == "*") {
return true;
}
// Can't match against record if it doesn't have a URI.
if (record.uri == null) {
return false;
}
let originAttributes;
if (principal || originAttributesPattern) {
// Restore OriginAttributes from record.originAttributes which
// contains the OA suffix string. We need this to match against the
// principal or the OriginAttributesPattern.
try {
originAttributes =
ChromeUtils.CreateOriginAttributesFromOriginSuffix(
record.originAttributes
);
} catch (e) {
console.warn("Error while parsing record OA suffix.", e);
return false;
}
}
// Drop all matching principal.
if (principal) {
// Build a principal from the record metadata so we can compare it
// to the given principal.
let recordPrincipal =
Services.scriptSecurityManager.createContentPrincipal(
record.uri,
originAttributes
);
return recordPrincipal.equals(principal);
}
if (!record.uri.host) {
return false;
}
// Drop all matching domain + OA pattern.
return Services.clearData.hostMatchesSite(
record.uri.host,
originAttributes,
domain,
originAttributesPattern
);
});
})
.catch(e => {
lazy.console.warn(
"clear: Error dropping subscriptions for domain",
info.domain,
"clear: Error dropping subscriptions for domain or principal",
domain,
e
);
return Promise.resolve();

View File

@ -144,7 +144,11 @@ export class PushService {
}
}
clearForDomain(domain, callback) {
clearForDomain(domain, originAttributesPattern, callback) {
callback.onClear(Cr.NS_OK);
}
clearForPrincipal(principal, callback) {
callback.onClear(Cr.NS_OK);
}

View File

@ -1093,9 +1093,10 @@ const PushNotificationsCleaner = {
/**
* Clear entries for aDomain including subdomains of aDomain.
* @param {string} aDomain - Domain to clear data for.
* @param {Object} aOriginAttributesPattern - Optional pattern to filter OriginAttributes.
* @returns {Promise} a promise which resolves once data has been cleared.
*/
_deleteByRootDomain(aDomain) {
_deleteByRootDomain(aDomain, aOriginAttributesPattern = null) {
if (!Services.prefs.getBoolPref("dom.push.enabled", false)) {
return Promise.resolve();
}
@ -1105,7 +1106,7 @@ const PushNotificationsCleaner = {
Ci.nsIPushService
);
// ClearForDomain also clears subdomains.
push.clearForDomain(aDomain, aStatus => {
push.clearForDomain(aDomain, aOriginAttributesPattern, aStatus => {
if (!Components.isSuccessCode(aStatus)) {
aReject();
} else {
@ -1122,14 +1123,26 @@ const PushNotificationsCleaner = {
},
deleteByPrincipal(aPrincipal) {
// Will also clear entries for subdomains of the principal host. Data is
// cleared across all origin attributes.
return this._deleteByRootDomain(aPrincipal.host);
if (!Services.prefs.getBoolPref("dom.push.enabled", false)) {
return Promise.resolve();
}
return new Promise((aResolve, aReject) => {
let push = Cc["@mozilla.org/push/Service;1"].getService(
Ci.nsIPushService
);
push.clearForPrincipal(aPrincipal, aStatus => {
if (!Components.isSuccessCode(aStatus)) {
aReject();
} else {
aResolve();
}
});
});
},
deleteBySite(aSchemelessSite, _aOriginAttributesPattern) {
// TODO: aOriginAttributesPattern
return this._deleteByRootDomain(aSchemelessSite);
deleteBySite(aSchemelessSite, aOriginAttributesPattern) {
return this._deleteByRootDomain(aSchemelessSite, aOriginAttributesPattern);
},
deleteAll() {
@ -1141,7 +1154,7 @@ const PushNotificationsCleaner = {
let push = Cc["@mozilla.org/push/Service;1"].getService(
Ci.nsIPushService
);
push.clearForDomain("*", aStatus => {
push.clearForDomain("*", null, aStatus => {
if (!Components.isSuccessCode(aStatus)) {
aReject();
} else {
@ -2474,6 +2487,19 @@ ClearDataService.prototype = Object.freeze({
});
},
hostMatchesSite(
aHost,
aOriginAttributes,
aSchemelessSite,
aOriginAttributesPattern = {}
) {
return hasSite(
{ host: aHost, originAttributes: aOriginAttributes },
aSchemelessSite,
aOriginAttributesPattern
);
},
// This internal method uses aFlags against FLAGS_MAP in order to retrieve a
// list of 'Cleaners'. For each of them, the aHelper callback retrieves a
// promise object. All these promise objects are resolved before calling

View File

@ -173,6 +173,17 @@ interface nsIClearDataService : nsISupports
*/
void cleanupAfterDeletionAtShutdown(in uint32_t aFlags, in nsIClearDataCallback aCallback);
/**
* Match a host and OriginAttributes against a schemeless site and
* OriginAttributesPattern.
* Also considers partitioned state by inspecting OriginAttributes
* partitionKey.
* This is a helper method for external callers that need to do
* filtering for data clearing.
*/
boolean hostMatchesSite(in AUTF8String aHost, in jsval aOriginAttributes, in AUTF8String aSchemelessSite, [optional] in jsval aOriginAttributesPattern);
/**************************************************************************
* Listed below are the various flags which may be or'd together.
*/