mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1223202 - Only send subscription change events if the Push permission is granted. r=mt
--HG-- extra : commitid : 4m6UEhlrFtl extra : rebase_source : 512c9e2ea4be850994e891fc8193df17a2788260
This commit is contained in:
parent
9614a171ba
commit
53cdf28e5a
@ -127,7 +127,10 @@ this.PushDB.prototype = {
|
||||
this._dbStoreName,
|
||||
function txnCb(aTxn, aStore) {
|
||||
console.debug("delete: Removing record", aKeyID);
|
||||
aStore.delete(aKeyID);
|
||||
aStore.get(aKeyID).onsuccess = event => {
|
||||
aTxn.result = event.target.result;
|
||||
aStore.delete(aKeyID);
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
@ -213,28 +216,26 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates all push registrations for an origin.
|
||||
* Reduces all records associated with an origin to a single value.
|
||||
*
|
||||
* @param {String} origin The origin, matched as a prefix against the scope.
|
||||
* @param {String} originAttributes Additional origin attributes. Requires
|
||||
* an exact match.
|
||||
* @param {Function} updateFunc A function that receives the existing
|
||||
* registration record as its argument, and returns a new record. The record
|
||||
* will not be updated if the function returns `null`, `undefined`, or an
|
||||
* invalid record. If the function returns `false`, the record will be
|
||||
* dropped.
|
||||
* @returns {Promise} A promise that resolves once all records have been
|
||||
* updated.
|
||||
* @param {Function} callback A function with the signature `(result,
|
||||
* record, cursor)`, where `result` is the value returned by the previous
|
||||
* invocation, `record` is the registration, and `cursor` is an `IDBCursor`.
|
||||
* @param {Object} [initialValue] The value to use for the first invocation.
|
||||
* @returns {Promise} Resolves with the value of the last invocation.
|
||||
*/
|
||||
updateByOrigin: function(origin, originAttributes, updateFunc) {
|
||||
console.debug("updateByOrigin()");
|
||||
reduceByOrigin: function(origin, originAttributes, callback, initialValue) {
|
||||
console.debug("forEachOrigin()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
(aTxn, aStore) => {
|
||||
aTxn.result = [];
|
||||
aTxn.result = initialValue;
|
||||
|
||||
let index = aStore.index("identifiers");
|
||||
let range = IDBKeyRange.bound(
|
||||
@ -247,19 +248,7 @@ this.PushDB.prototype = {
|
||||
return;
|
||||
}
|
||||
let record = this.toPushRecord(cursor.value);
|
||||
let newRecord = updateFunc(record);
|
||||
if (newRecord === false) {
|
||||
console.debug("updateByOrigin: Removing record for key ID",
|
||||
record.keyID);
|
||||
cursor.delete();
|
||||
} else if (this.isValidRecord(newRecord)) {
|
||||
console.debug("updateByOrigin: Updating record for key ID",
|
||||
record.keyID, newRecord);
|
||||
cursor.update(newRecord);
|
||||
} else {
|
||||
console.error("updateByOrigin: Ignoring invalid update for record",
|
||||
record.keyID, newRecord);
|
||||
}
|
||||
aTxn.result = callback(aTxn.result, record, cursor);
|
||||
cursor.continue();
|
||||
};
|
||||
},
|
||||
|
@ -195,6 +195,9 @@ PushRecord.prototype = {
|
||||
},
|
||||
|
||||
quotaChanged() {
|
||||
if (!this.hasPermission()) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return this.getLastVisit()
|
||||
.then(lastVisit => lastVisit > this.lastPush);
|
||||
},
|
||||
@ -207,7 +210,7 @@ PushRecord.prototype = {
|
||||
return this.quota === 0;
|
||||
},
|
||||
|
||||
toRegistration() {
|
||||
toSubscription() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
lastPush: this.lastPush,
|
||||
@ -215,13 +218,6 @@ PushRecord.prototype = {
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
|
||||
toRegister() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Define lazy getters for the principal and scope URI. IndexedDB can't store
|
||||
|
@ -282,14 +282,14 @@ this.PushService = {
|
||||
inBrowser: data.browserOnly });
|
||||
this._db.getAllByOriginAttributes(originAttributes)
|
||||
.then(records => Promise.all(records.map(record =>
|
||||
this._db.delete(record.keyID).then(
|
||||
_ => this._backgroundUnregister(record),
|
||||
err => {
|
||||
this._db.delete(record.keyID)
|
||||
.catch(err => {
|
||||
console.error("webapps-clear-data: Error removing record",
|
||||
record, err);
|
||||
|
||||
this._backgroundUnregister(record);
|
||||
// This is the record we were unable to delete.
|
||||
return record;
|
||||
})
|
||||
.then(maybeDeleted => this._backgroundUnregister(maybeDeleted))
|
||||
)
|
||||
));
|
||||
|
||||
@ -297,16 +297,23 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends an unregister request to the server in the background. If the
|
||||
* service is not connected, this function is a no-op.
|
||||
*
|
||||
* @param {PushRecord} record The record to unregister.
|
||||
*/
|
||||
_backgroundUnregister: function(record) {
|
||||
if (this._service.isConnected()) {
|
||||
// courtesy, but don't establish a connection
|
||||
// just for it
|
||||
console.debug("backgroundUnregister: Notifying server", record);
|
||||
return this._sendUnregister(record)
|
||||
.catch(function(e) {
|
||||
console.error("backgroundUnregister: Error notifying server", e);
|
||||
});
|
||||
console.debug("backgroundUnregister()");
|
||||
|
||||
if (!this._service.isConnected() || !record) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug("backgroundUnregister: Notifying server", record);
|
||||
this._sendUnregister(record).catch(e => {
|
||||
console.error("backgroundUnregister: Error notifying server", e);
|
||||
});
|
||||
},
|
||||
|
||||
// utility function used to add/remove observers in startObservers() and
|
||||
@ -573,7 +580,7 @@ this.PushService = {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.dropRegistrations()
|
||||
return this.dropUnexpiredRegistrations()
|
||||
.then(_ => {
|
||||
this._db.close();
|
||||
this._db = null;
|
||||
@ -664,12 +671,35 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
dropRegistrations: function() {
|
||||
return this._notifyAllAppsRegister()
|
||||
.then(_ => this._db.drop());
|
||||
/**
|
||||
* Drops all active registrations and notifies the associated service
|
||||
* workers. This function is called when the user switches Push servers,
|
||||
* or when the server invalidates all existing registrations.
|
||||
*
|
||||
* We ignore expired registrations because they're already handled in other
|
||||
* code paths. Registrations that expired after exceeding their quotas are
|
||||
* evicted at startup, or on the next `idle-daily` event. Registrations that
|
||||
* expired because the user revoked the notification permission are evicted
|
||||
* once the permission is reinstated.
|
||||
*/
|
||||
dropUnexpiredRegistrations: function() {
|
||||
let subscriptionChanges = [];
|
||||
return this._db.clearIf(record => {
|
||||
if (record.isExpired()) {
|
||||
return false;
|
||||
}
|
||||
subscriptionChanges.push(record);
|
||||
return true;
|
||||
}).then(() => {
|
||||
this.notifySubscriptionChanges(subscriptionChanges);
|
||||
});
|
||||
},
|
||||
|
||||
_notifySubscriptionChangeObservers: function(record) {
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify XPCOM observers.
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
@ -704,31 +734,51 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
// Fires a push-register system message to all applications that have
|
||||
// registration.
|
||||
_notifyAllAppsRegister: function() {
|
||||
console.debug("notifyAllAppsRegister()");
|
||||
// records are objects describing the registration as stored in IndexedDB.
|
||||
return this._db.getAllUnexpired().then(records => {
|
||||
records.forEach(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
dropRegistrationAndNotifyApp: function(aKeyId) {
|
||||
return this._db.getByKeyID(aKeyId).then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return this._db.delete(aKeyId);
|
||||
});
|
||||
},
|
||||
|
||||
updateRegistrationAndNotifyApp: function(aOldKey, aRecord) {
|
||||
return this._db.delete(aOldKey)
|
||||
.then(_ => this._db.put(aRecord))
|
||||
/**
|
||||
* Drops a registration and notifies the associated service worker. If the
|
||||
* registration does not exist, this function is a no-op.
|
||||
*
|
||||
* @param {String} keyID The registration ID to remove.
|
||||
* @returns {Promise} Resolves once the worker has been notified.
|
||||
*/
|
||||
dropRegistrationAndNotifyApp: function(aKeyID) {
|
||||
return this._db.delete(aKeyID)
|
||||
.then(record => this._notifySubscriptionChangeObservers(record));
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces an existing registration and notifies the associated service
|
||||
* worker.
|
||||
*
|
||||
* @param {String} aOldKey The registration ID to replace.
|
||||
* @param {PushRecord} aNewRecord The new record.
|
||||
* @returns {Promise} Resolves once the worker has been notified.
|
||||
*/
|
||||
updateRegistrationAndNotifyApp: function(aOldKey, aNewRecord) {
|
||||
return this.updateRecordAndNotifyApp(aOldKey, _ => aNewRecord);
|
||||
},
|
||||
/**
|
||||
* Updates a registration and notifies the associated service worker.
|
||||
*
|
||||
* @param {String} keyID The registration ID to update.
|
||||
* @param {Function} updateFunc Returns the updated record.
|
||||
* @returns {Promise} Resolves with the updated record once the worker
|
||||
* has been notified.
|
||||
*/
|
||||
updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
|
||||
return this._db.update(aKeyID, aUpdateFunc)
|
||||
.then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
notifySubscriptionChanges: function(records) {
|
||||
records.forEach(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
});
|
||||
},
|
||||
|
||||
ensureP256dhKey: function(record) {
|
||||
if (record.p256dhPublicKey && record.p256dhPrivateKey) {
|
||||
return Promise.resolve(record);
|
||||
@ -748,19 +798,6 @@ this.PushService = {
|
||||
});
|
||||
},
|
||||
|
||||
updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
|
||||
return this._db.update(aKeyID, aUpdateFunc)
|
||||
.then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
dropRecordAndNotifyApp: function(aRecord) {
|
||||
return this._db.delete(aRecord.keyID)
|
||||
.then(_ => this._notifySubscriptionChangeObservers(aRecord));
|
||||
},
|
||||
|
||||
_recordDidNotNotify: function(reason) {
|
||||
Services.telemetry.
|
||||
getHistogramById("PUSH_API_NOTIFICATION_RECEIVED_BUT_DID_NOT_NOTIFY").
|
||||
@ -935,7 +972,7 @@ this.PushService = {
|
||||
err => this._onRegisterError(err))
|
||||
.then(record => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
return record.toRegister();
|
||||
return record.toSubscription();
|
||||
}, err => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
throw err;
|
||||
@ -1076,12 +1113,12 @@ this.PushService = {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and re-register.
|
||||
return this._db.delete(record.keyID);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID);
|
||||
}
|
||||
throw new Error("Push subscription expired");
|
||||
}).then(_ => this._lookupOrPutPendingRequest(aPageRecord));
|
||||
}
|
||||
return record.toRegister();
|
||||
return record.toSubscription();
|
||||
});
|
||||
},
|
||||
|
||||
@ -1194,12 +1231,12 @@ this.PushService = {
|
||||
if (record.isExpired()) {
|
||||
return record.quotaChanged().then(isChanged => {
|
||||
if (isChanged) {
|
||||
return this._db.delete(record.keyID).then(_ => null);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID).then(_ => null);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return record.toRegistration();
|
||||
return record.toSubscription();
|
||||
});
|
||||
},
|
||||
|
||||
@ -1212,7 +1249,7 @@ this.PushService = {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and notify the associated service worker.
|
||||
return this.dropRecordAndNotifyApp(record);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID);
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("dropExpiredRegistrations: Error dropping registration",
|
||||
@ -1259,69 +1296,84 @@ this.PushService = {
|
||||
// Permission set to "allow". Drop all expired registrations for this
|
||||
// site, notify the associated service workers, and reset the quota
|
||||
// for active registrations.
|
||||
return this._updateByPrincipal(
|
||||
return this._reduceByPrincipal(
|
||||
permission.principal,
|
||||
record => this._permissionAllowed(record)
|
||||
);
|
||||
(subscriptionChanges, record, cursor) => {
|
||||
this._permissionAllowed(subscriptionChanges, record, cursor);
|
||||
return subscriptionChanges;
|
||||
},
|
||||
[]
|
||||
).then(subscriptionChanges => {
|
||||
this.notifySubscriptionChanges(subscriptionChanges);
|
||||
});
|
||||
} else if (isChange || (isAllow && type == "deleted")) {
|
||||
// Permission set to "block" or "always ask," or "allow" permission
|
||||
// removed. Expire all registrations for this site.
|
||||
return this._updateByPrincipal(
|
||||
return this._reduceByPrincipal(
|
||||
permission.principal,
|
||||
record => this._permissionDenied(record)
|
||||
(memo, record, cursor) => this._permissionDenied(record, cursor)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
_updateByPrincipal: function(principal, updateFunc) {
|
||||
return this._db.updateByOrigin(
|
||||
_reduceByPrincipal: function(principal, callback, initialValue) {
|
||||
return this._db.reduceByOrigin(
|
||||
principal.URI.prePath,
|
||||
ChromeUtils.originAttributesToSuffix(principal.originAttributes),
|
||||
updateFunc
|
||||
callback,
|
||||
initialValue
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Expires all registrations if the push permission is revoked. We only
|
||||
* expire the registration so we can notify the service worker as soon as
|
||||
* the permission is reinstated. If we just deleted the registration, the
|
||||
* worker wouldn't be notified until the next visit to the site.
|
||||
* The update function called for each registration record if the push
|
||||
* permission is revoked. We only expire the record so we can notify the
|
||||
* service worker as soon as the permission is reinstated. If we just
|
||||
* deleted the record, the worker wouldn't be notified until the next visit
|
||||
* to the site.
|
||||
*
|
||||
* @param {Array} A list of records to expire.
|
||||
* @returns {Promise} A promise resolved with the expired records.
|
||||
* @param {PushRecord} record The record to expire.
|
||||
* @param {IDBCursor} cursor The IndexedDB cursor.
|
||||
*/
|
||||
_permissionDenied: function(record) {
|
||||
_permissionDenied: function(record, cursor) {
|
||||
console.debug("permissionDenied()");
|
||||
|
||||
if (!record.quotaApplies() || record.isExpired()) {
|
||||
// Ignore already-expired records.
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
// Drop the registration in the background.
|
||||
this._backgroundUnregister(record);
|
||||
record.setQuota(0);
|
||||
return record;
|
||||
cursor.update(record);
|
||||
},
|
||||
|
||||
/**
|
||||
* Drops all expired registrations, notifies the associated service
|
||||
* workers, and resets the quota for active registrations if the push
|
||||
* permission is granted.
|
||||
* The update function called for each registration record if the push
|
||||
* permission is granted. If the record has expired, it will be dropped;
|
||||
* otherwise, its quota will be reset to the default value.
|
||||
*
|
||||
* @param {Array} A list of records to refresh.
|
||||
* @returns {Promise} A promise resolved with the refreshed records.
|
||||
* @param {Array} subscriptionChanges A list of records whose associated
|
||||
* service workers should be notified once the transaction has committed.
|
||||
* @param {PushRecord} record The record to update.
|
||||
* @param {IDBCursor} cursor The IndexedDB cursor.
|
||||
*/
|
||||
_permissionAllowed: function(record) {
|
||||
_permissionAllowed: function(subscriptionChanges, record, cursor) {
|
||||
console.debug("permissionAllowed()");
|
||||
|
||||
if (!record.quotaApplies()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
// If the registration has expired, drop and notify the worker
|
||||
// unconditionally.
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return false;
|
||||
subscriptionChanges.push(record);
|
||||
cursor.delete();
|
||||
return;
|
||||
}
|
||||
record.resetQuota();
|
||||
return record;
|
||||
cursor.update(record);
|
||||
},
|
||||
};
|
||||
|
@ -858,14 +858,8 @@ PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordHttp2.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return registration;
|
||||
};
|
||||
|
||||
PushRecordHttp2.prototype.toRegister = function() {
|
||||
let register = PushRecord.prototype.toRegister.call(this);
|
||||
register.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return register;
|
||||
PushRecordHttp2.prototype.toSubscription = function() {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return subscription;
|
||||
};
|
||||
|
@ -840,7 +840,7 @@ this.PushServiceWebSocket = {
|
||||
if (this._UAID != reply.uaid) {
|
||||
console.debug("handleHelloReply: Received new UAID");
|
||||
|
||||
this._mainPushService.dropRegistrations()
|
||||
this._mainPushService.dropUnexpiredRegistrations()
|
||||
.then(finishHandshake.bind(this));
|
||||
|
||||
return;
|
||||
@ -1472,8 +1472,8 @@ PushRecordWebSocket.prototype = Object.create(PushRecord.prototype, {
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordWebSocket.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.version = this.version;
|
||||
return registration;
|
||||
PushRecordWebSocket.prototype.toSubscription = function() {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.version = this.version;
|
||||
return subscription;
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/Task.jsm');
|
||||
Cu.import('resource://gre/modules/Timer.jsm');
|
||||
Cu.import('resource://gre/modules/Promise.jsm');
|
||||
Cu.import('resource://gre/modules/Preferences.jsm');
|
||||
|
153
dom/push/test/xpcshell/test_drop_expired.js
Normal file
153
dom/push/test/xpcshell/test_drop_expired.js
Normal file
@ -0,0 +1,153 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '2c43af06-ab6e-476a-adc4-16cbda54fb89';
|
||||
|
||||
var db;
|
||||
var quotaURI;
|
||||
var permURI;
|
||||
|
||||
function visitURI(uri, timestamp) {
|
||||
return addVisit({
|
||||
uri: uri,
|
||||
title: uri.spec,
|
||||
visits: [{
|
||||
visitDate: timestamp * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
var putRecord = Task.async(function* ({scope, perm, quota, lastPush, lastVisit}) {
|
||||
let uri = Services.io.newURI(scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield visitURI(uri, lastVisit);
|
||||
|
||||
yield db.put({
|
||||
channelID: uri.path,
|
||||
pushEndpoint: 'https://example.org/push' + uri.path,
|
||||
scope: uri.spec,
|
||||
pushCount: 0,
|
||||
lastPush: lastPush,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: quota,
|
||||
});
|
||||
|
||||
return uri;
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* setUp() {
|
||||
// An expired registration that should be evicted on startup. Permission is
|
||||
// granted for this origin, and the last visit is more recent than the last
|
||||
// push message.
|
||||
yield putRecord({
|
||||
scope: 'https://example.com/expired-quota-restored',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when the origin is visited
|
||||
// again.
|
||||
quotaURI = yield putRecord({
|
||||
scope: 'https://example.xyz/expired-quota-exceeded',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when permission is granted
|
||||
// again.
|
||||
permURI = yield putRecord({
|
||||
scope: 'https://example.info/expired-perm-revoked',
|
||||
perm: 'DENY_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An active registration that we should leave alone.
|
||||
yield putRecord({
|
||||
scope: 'https://example.ninja/active',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 16,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.com/expired-quota-restored'
|
||||
);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event on startup');
|
||||
});
|
||||
|
||||
add_task(function* test_site_visited() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.xyz/expired-quota-exceeded'
|
||||
);
|
||||
|
||||
yield visitURI(quotaURI, Date.now());
|
||||
PushService.observe(null, 'idle-daily', '');
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after visit');
|
||||
});
|
||||
|
||||
add_task(function* test_perm_restored() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.info/expired-perm-revoked'
|
||||
);
|
||||
|
||||
Services.perms.add(permURI, 'desktop-notification',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after permission');
|
||||
});
|
@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/page/1',
|
||||
'https://example.com/page/2',
|
||||
@ -74,7 +78,7 @@ add_task(function* test_notification_incomplete() {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
// Missing "updates" field; should ignore message.
|
||||
|
@ -7,6 +7,8 @@ const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '28cd09e2-7506-42d8-9e50-b02785adc7ef';
|
||||
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
@ -15,12 +17,24 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let putRecord = Task.async(function* (perm, record) {
|
||||
let uri = Services.io.newURI(record.scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield db.put(record);
|
||||
});
|
||||
|
||||
add_task(function* test_expiration_history_observer() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => db.drop().then(_ => db.close()));
|
||||
|
||||
// A registration that we'll expire...
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/deals',
|
||||
@ -32,7 +46,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
});
|
||||
|
||||
// ...And a registration that we'll evict on startup.
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '4cb6e454-37cf-41c4-a013-4e3a7fdd0bf1',
|
||||
pushEndpoint: 'https://example.org/push/3',
|
||||
scope: 'https://example.com/stuff',
|
||||
@ -101,7 +115,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
});
|
||||
|
||||
// Add an expired registration that we'll revive later.
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/auctions',
|
||||
|
@ -4,6 +4,7 @@ tail =
|
||||
# Push notifications and alarms are currently disabled on Android.
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_drop_expired.js]
|
||||
[test_notification_ack.js]
|
||||
[test_notification_data.js]
|
||||
[test_notification_duplicate.js]
|
||||
|
Loading…
Reference in New Issue
Block a user