Bug 1266433 - Indicate push subscriptions created by privileged code. r=dragana
MozReview-Commit-ID: HYKndQiU98U
--- a/dom/interfaces/push/nsIPushService.idl
+++ b/dom/interfaces/push/nsIPushService.idl
@@ -13,16 +13,17 @@ interface nsIPrincipal;
*/
[scriptable, uuid(1de32d5c-ea88-4c9e-9626-b032bd87f415)]
interface nsIPushSubscription : nsISupports
{
readonly attribute DOMString endpoint;
readonly attribute long long pushCount;
readonly attribute long long lastPush;
readonly attribute long quota;
+ readonly attribute bool isSystemSubscription;
bool quotaApplies();
bool isExpired();
void getKey(in DOMString name,
[optional] out uint32_t keyLen,
[array, size_is(keyLen), retval] out uint8_t key);
};
--- a/dom/push/PushComponents.js
+++ b/dom/push/PushComponents.js
@@ -243,17 +243,17 @@ Object.assign(PushServiceParent.prototyp
throw new Error("Invalid page record: missing principal");
}
if (principal.isNullPrincipal || principal.isExpandedPrincipal) {
throw new Error("Invalid page record: unsupported principal");
}
// System subscriptions can only be created by chrome callers, and are
// exempt from the background message quota and permission checks. They
- // also use XPCOM observer notifications instead of service worker events.
+ // also do not fire service worker events.
data.systemRecord = principal.isSystemPrincipal;
data.originAttributes =
ChromeUtils.originAttributesToSuffix(principal.originAttributes);
return data;
},
@@ -485,16 +485,25 @@ PushSubscription.prototype = {
/** The number of remaining background messages that can be sent to this
* subscription, or -1 of the subscription is exempt from the quota.
*/
get quota() {
return this._props.quota;
},
/**
+ * Indicates whether this subscription was created with the system principal.
+ * System subscriptions are exempt from the background message quota and
+ * permission checks.
+ */
+ get isSystemSubscription() {
+ return !!this._props.systemRecord;
+ },
+
+ /**
* Indicates whether this subscription is subject to the background message
* quota.
*/
quotaApplies() {
return this.quota >= 0;
},
/**
--- a/dom/push/PushRecord.jsm
+++ b/dom/push/PushRecord.jsm
@@ -247,16 +247,17 @@ PushRecord.prototype = {
return {
endpoint: this.pushEndpoint,
lastPush: this.lastPush,
pushCount: this.pushCount,
p256dhKey: this.p256dhPublicKey,
authenticationSecret: this.authenticationSecret,
appServerKey: this.appServerKey,
quota: this.quotaApplies() ? this.quota : -1,
+ systemRecord: this.systemRecord,
};
},
};
// Define lazy getters for the principal and scope URI. IndexedDB can't store
// `nsIPrincipal` objects, so we keep them in a private weak map.
var principals = new WeakMap();
Object.defineProperties(PushRecord.prototype, {
--- a/dom/push/test/xpcshell/test_service_child.js
+++ b/dom/push/test/xpcshell/test_service_child.js
@@ -40,16 +40,17 @@ if (isParent) {
add_test(function test_subscribe_success() {
do_test_pending();
PushServiceComponent.subscribe(
'https://example.com/sub/ok',
Services.scriptSecurityManager.getSystemPrincipal(),
(result, subscription) => {
ok(Components.isSuccessCode(result), 'Error creating subscription');
+ ok(subscription.isSystemSubscription, 'Expected system subscription');
ok(subscription.endpoint.startsWith('https://example.org/push'), 'Wrong endpoint prefix');
equal(subscription.pushCount, 0, 'Wrong push count');
equal(subscription.lastPush, 0, 'Wrong last push time');
equal(subscription.quota, -1, 'Wrong quota for system subscription');
do_test_finished();
run_next_test();
}
@@ -235,49 +236,53 @@ add_test(function test_subscribe_app_pri
true /* browserOnly */
);
do_test_pending();
PushServiceComponent.subscribe('https://example.net/scope/1', principal, (result, subscription) => {
ok(Components.isSuccessCode(result), 'Error creating subscription');
ok(subscription.endpoint.startsWith('https://example.org/push'),
'Wrong push endpoint in app subscription');
+ ok(!subscription.isSystemSubscription,
+ 'Unexpected system subscription for app principal');
equal(subscription.quota, 16, 'Wrong quota for app subscription');
do_test_finished();
run_next_test();
});
});
add_test(function test_subscribe_origin_principal() {
let scope = 'https://example.net/origin-principal';
let principal =
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(scope);
do_test_pending();
PushServiceComponent.subscribe(scope, principal, (result, subscription) => {
ok(Components.isSuccessCode(result),
'Expected error creating subscription with origin principal');
+ ok(!subscription.isSystemSubscription,
+ 'Unexpected system subscription for origin principal');
equal(subscription.quota, 16, 'Wrong quota for origin subscription');
do_test_finished();
run_next_test();
});
});
add_test(function test_subscribe_null_principal() {
do_test_pending();
PushServiceComponent.subscribe(
'chrome://push/null-principal',
Services.scriptSecurityManager.createNullPrincipal({}),
(result, subscription) => {
ok(!Components.isSuccessCode(result),
- 'Expected error creating subscription with expanded principal');
+ 'Expected error creating subscription with null principal');
strictEqual(subscription, null,
- 'Unexpected subscription with expanded principal');
+ 'Unexpected subscription with null principal');
do_test_finished();
run_next_test();
}
);
});
add_test(function test_subscribe_missing_principal() {