mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-16 04:07:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
31
dom/push/test/xpcshell/PushServiceHandler.js
Normal file
31
dom/push/test/xpcshell/PushServiceHandler.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// An XPCOM service that's registered with the category manager in the parent
|
||||
// process for handling push notifications with scope "chrome://test-scope"
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
||||
|
||||
function PushServiceHandler() {
|
||||
// So JS code can reach into us.
|
||||
this.wrappedJSObject = this;
|
||||
// Register a push observer.
|
||||
this.observed = [];
|
||||
Services.obs.addObserver(this, pushService.pushTopic, false);
|
||||
Services.obs.addObserver(this, pushService.subscriptionChangeTopic, false);
|
||||
Services.obs.addObserver(this, pushService.subscriptionModifiedTopic, false);
|
||||
}
|
||||
|
||||
PushServiceHandler.prototype = {
|
||||
classID: Components.ID("{bb7c5199-c0f7-4976-9f6d-1306e32c5591}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([]),
|
||||
|
||||
observe(subject, topic, data) {
|
||||
this.observed.push({ subject, topic, data });
|
||||
},
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushServiceHandler]);
|
||||
4
dom/push/test/xpcshell/PushServiceHandler.manifest
Normal file
4
dom/push/test/xpcshell/PushServiceHandler.manifest
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
component {bb7c5199-c0f7-4976-9f6d-1306e32c5591} PushServiceHandler.js
|
||||
contract @mozilla.org/dom/push/test/PushServiceHandler;1 {bb7c5199-c0f7-4976-9f6d-1306e32c5591}
|
||||
|
||||
category push chrome://test-scope @mozilla.org/dom/push/test/PushServiceHandler;1
|
||||
62
dom/push/test/xpcshell/head-http2.js
Normal file
62
dom/push/test/xpcshell/head-http2.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Returns the test H/2 server port, throwing if it's missing or invalid.
|
||||
function getTestServerPort() {
|
||||
let portEnv = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment).get("MOZHTTP2_PORT");
|
||||
let port = parseInt(portEnv, 10);
|
||||
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
||||
throw new Error(`Invalid port in MOZHTTP2_PORT env var: ${portEnv}`);
|
||||
}
|
||||
do_print(`Using HTTP/2 server on port ${port}`);
|
||||
return port;
|
||||
}
|
||||
|
||||
// Support for making sure we can talk to the invalid cert the server presents
|
||||
var CertOverrideListener = function(host, port, bits) {
|
||||
this.host = host;
|
||||
this.port = port || 443;
|
||||
this.bits = bits;
|
||||
};
|
||||
|
||||
CertOverrideListener.prototype = {
|
||||
host: null,
|
||||
bits: null,
|
||||
|
||||
getInterface: function(aIID) {
|
||||
return this.QueryInterface(aIID);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Ci.nsIBadCertListener2) ||
|
||||
aIID.equals(Ci.nsIInterfaceRequestor) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
notifyCertProblem: function(socketInfo, sslStatus, targetHost) {
|
||||
var cert = sslStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
|
||||
var cos = Cc["@mozilla.org/security/certoverride;1"].
|
||||
getService(Ci.nsICertOverrideService);
|
||||
cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false);
|
||||
dump("Certificate Override in place\n");
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
function addCertOverride(host, port, bits) {
|
||||
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
try {
|
||||
var url;
|
||||
if (port && (port > 0) && (port !== 443)) {
|
||||
url = "https://" + host + ":" + port + "/";
|
||||
} else {
|
||||
url = "https://" + host + "/";
|
||||
}
|
||||
req.open("GET", url, false);
|
||||
req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits);
|
||||
req.send(null);
|
||||
} catch (e) {
|
||||
// This will fail since the server is not trusted yet
|
||||
}
|
||||
}
|
||||
463
dom/push/test/xpcshell/head.js
Normal file
463
dom/push/test/xpcshell/head.js
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
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');
|
||||
Cu.import('resource://gre/modules/PlacesUtils.jsm');
|
||||
Cu.import('resource://gre/modules/ObjectUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'PlacesTestUtils',
|
||||
'resource://testing-common/PlacesTestUtils.jsm');
|
||||
XPCOMUtils.defineLazyServiceGetter(this, 'PushServiceComponent',
|
||||
'@mozilla.org/push/Service;1', 'nsIPushService');
|
||||
|
||||
const serviceExports = Cu.import('resource://gre/modules/PushService.jsm', {});
|
||||
const servicePrefs = new Preferences('dom.push.');
|
||||
|
||||
const WEBSOCKET_CLOSE_GOING_AWAY = 1001;
|
||||
|
||||
const MS_IN_ONE_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
var isParent = Cc['@mozilla.org/xre/runtime;1']
|
||||
.getService(Ci.nsIXULRuntime).processType ==
|
||||
Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
|
||||
// Stop and clean up after the PushService.
|
||||
Services.obs.addObserver(function observe(subject, topic, data) {
|
||||
Services.obs.removeObserver(observe, topic, false);
|
||||
serviceExports.PushService.uninit();
|
||||
// Occasionally, `profile-change-teardown` and `xpcom-shutdown` will fire
|
||||
// before the PushService and AlarmService finish writing to IndexedDB. This
|
||||
// causes spurious errors and crashes, so we spin the event loop to let the
|
||||
// writes finish.
|
||||
let done = false;
|
||||
setTimeout(() => done = true, 1000);
|
||||
let thread = Services.tm.mainThread;
|
||||
while (!done) {
|
||||
try {
|
||||
thread.processNextEvent(true);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
}, 'profile-change-net-teardown', false);
|
||||
|
||||
/**
|
||||
* Gates a function so that it is called only after the wrapper is called a
|
||||
* given number of times.
|
||||
*
|
||||
* @param {Number} times The number of wrapper calls before |func| is called.
|
||||
* @param {Function} func The function to gate.
|
||||
* @returns {Function} The gated function wrapper.
|
||||
*/
|
||||
function after(times, func) {
|
||||
return function afterFunc() {
|
||||
if (--times <= 0) {
|
||||
return func.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers one or more callbacks until the next turn of the event loop. Multiple
|
||||
* callbacks are executed in order.
|
||||
*
|
||||
* @param {Function[]} callbacks The callbacks to execute. One callback will be
|
||||
* executed per tick.
|
||||
*/
|
||||
function waterfall(...callbacks) {
|
||||
callbacks.reduce((promise, callback) => promise.then(() => {
|
||||
callback();
|
||||
}), Promise.resolve()).catch(Cu.reportError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an observer notification to fire.
|
||||
*
|
||||
* @param {String} topic The notification topic.
|
||||
* @returns {Promise} A promise that fulfills when the notification is fired.
|
||||
*/
|
||||
function promiseObserverNotification(topic, matchFunc) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.obs.addObserver(function observe(subject, topic, data) {
|
||||
let matches = typeof matchFunc != 'function' || matchFunc(subject, data);
|
||||
if (!matches) {
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(observe, topic, false);
|
||||
resolve({subject, data});
|
||||
}, topic, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an object in a proxy that traps property gets and returns stubs. If
|
||||
* the stub is a function, the original value will be passed as the first
|
||||
* argument. If the original value is a function, the proxy returns a wrapper
|
||||
* that calls the stub; otherwise, the stub is called as a getter.
|
||||
*
|
||||
* @param {Object} target The object to wrap.
|
||||
* @param {Object} stubs An object containing stubbed values and functions.
|
||||
* @returns {Proxy} A proxy that returns stubs for property gets.
|
||||
*/
|
||||
function makeStub(target, stubs) {
|
||||
return new Proxy(target, {
|
||||
get(target, property) {
|
||||
if (!stubs || typeof stubs != 'object' || !(property in stubs)) {
|
||||
return target[property];
|
||||
}
|
||||
let stub = stubs[property];
|
||||
if (typeof stub != 'function') {
|
||||
return stub;
|
||||
}
|
||||
let original = target[property];
|
||||
if (typeof original != 'function') {
|
||||
return stub.call(this, original);
|
||||
}
|
||||
return function callStub(...params) {
|
||||
return stub.call(this, original, ...params);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default PushService preferences. All pref names are prefixed with
|
||||
* `dom.push.`; any additional preferences will override the defaults.
|
||||
*
|
||||
* @param {Object} [prefs] Additional preferences to set.
|
||||
*/
|
||||
function setPrefs(prefs = {}) {
|
||||
let defaultPrefs = Object.assign({
|
||||
loglevel: 'all',
|
||||
serverURL: 'wss://push.example.org',
|
||||
'connection.enabled': true,
|
||||
userAgentID: '',
|
||||
enabled: true,
|
||||
// Defaults taken from /modules/libpref/init/all.js.
|
||||
requestTimeout: 10000,
|
||||
retryBaseInterval: 5000,
|
||||
pingInterval: 30 * 60 * 1000,
|
||||
// Misc. defaults.
|
||||
'http2.maxRetries': 2,
|
||||
'http2.retryInterval': 500,
|
||||
'http2.reset_retry_count_after_ms': 60000,
|
||||
maxQuotaPerSubscription: 16,
|
||||
quotaUpdateDelay: 3000,
|
||||
'testing.notifyWorkers': false,
|
||||
}, prefs);
|
||||
for (let pref in defaultPrefs) {
|
||||
servicePrefs.set(pref, defaultPrefs[pref]);
|
||||
}
|
||||
}
|
||||
|
||||
function compareAscending(a, b) {
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock WebSocket object that implements a subset of the
|
||||
* nsIWebSocketChannel interface used by the PushService.
|
||||
*
|
||||
* The given protocol handlers are invoked for each Simple Push command sent
|
||||
* by the PushService. The ping handler is optional; all others will throw if
|
||||
* the PushService sends a command for which no handler is registered.
|
||||
*
|
||||
* All nsIWebSocketListener methods will be called asynchronously.
|
||||
* serverSendMsg() and serverClose() can be used to respond to client messages
|
||||
* and close the "server" end of the connection, respectively.
|
||||
*
|
||||
* @param {nsIURI} originalURI The original WebSocket URL.
|
||||
* @param {Function} options.onHello The "hello" handshake command handler.
|
||||
* @param {Function} options.onRegister The "register" command handler.
|
||||
* @param {Function} options.onUnregister The "unregister" command handler.
|
||||
* @param {Function} options.onACK The "ack" command handler.
|
||||
* @param {Function} [options.onPing] An optional ping handler.
|
||||
*/
|
||||
function MockWebSocket(originalURI, handlers = {}) {
|
||||
this._originalURI = originalURI;
|
||||
this._onHello = handlers.onHello;
|
||||
this._onRegister = handlers.onRegister;
|
||||
this._onUnregister = handlers.onUnregister;
|
||||
this._onACK = handlers.onACK;
|
||||
this._onPing = handlers.onPing;
|
||||
}
|
||||
|
||||
MockWebSocket.prototype = {
|
||||
_originalURI: null,
|
||||
_onHello: null,
|
||||
_onRegister: null,
|
||||
_onUnregister: null,
|
||||
_onACK: null,
|
||||
_onPing: null,
|
||||
|
||||
_listener: null,
|
||||
_context: null,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsISupports,
|
||||
Ci.nsIWebSocketChannel
|
||||
]),
|
||||
|
||||
get originalURI() {
|
||||
return this._originalURI;
|
||||
},
|
||||
|
||||
asyncOpen(uri, origin, windowId, listener, context) {
|
||||
this._listener = listener;
|
||||
this._context = context;
|
||||
waterfall(() => this._listener.onStart(this._context));
|
||||
},
|
||||
|
||||
_handleMessage(msg) {
|
||||
let messageType, request;
|
||||
if (msg == '{}') {
|
||||
request = {};
|
||||
messageType = 'ping';
|
||||
} else {
|
||||
request = JSON.parse(msg);
|
||||
messageType = request.messageType;
|
||||
}
|
||||
switch (messageType) {
|
||||
case 'hello':
|
||||
if (typeof this._onHello != 'function') {
|
||||
throw new Error('Unexpected handshake request');
|
||||
}
|
||||
this._onHello(request);
|
||||
break;
|
||||
|
||||
case 'register':
|
||||
if (typeof this._onRegister != 'function') {
|
||||
throw new Error('Unexpected register request');
|
||||
}
|
||||
this._onRegister(request);
|
||||
break;
|
||||
|
||||
case 'unregister':
|
||||
if (typeof this._onUnregister != 'function') {
|
||||
throw new Error('Unexpected unregister request');
|
||||
}
|
||||
this._onUnregister(request);
|
||||
break;
|
||||
|
||||
case 'ack':
|
||||
if (typeof this._onACK != 'function') {
|
||||
throw new Error('Unexpected acknowledgement');
|
||||
}
|
||||
this._onACK(request);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
if (typeof this._onPing == 'function') {
|
||||
this._onPing(request);
|
||||
} else {
|
||||
// Echo ping packets.
|
||||
this.serverSendMsg('{}');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Unexpected message: ' + messageType);
|
||||
}
|
||||
},
|
||||
|
||||
sendMsg(msg) {
|
||||
this._handleMessage(msg);
|
||||
},
|
||||
|
||||
close(code, reason) {
|
||||
waterfall(() => this._listener.onStop(this._context, Cr.NS_OK));
|
||||
},
|
||||
|
||||
/**
|
||||
* Responds with the given message, calling onMessageAvailable() and
|
||||
* onAcknowledge() synchronously. Throws if the message is not a string.
|
||||
* Used by the tests to respond to client commands.
|
||||
*
|
||||
* @param {String} msg The message to send to the client.
|
||||
*/
|
||||
serverSendMsg(msg) {
|
||||
if (typeof msg != 'string') {
|
||||
throw new Error('Invalid response message');
|
||||
}
|
||||
waterfall(
|
||||
() => this._listener.onMessageAvailable(this._context, msg),
|
||||
() => this._listener.onAcknowledge(this._context, 0)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes the server end of the connection, calling onServerClose()
|
||||
* followed by onStop(). Used to test abrupt connection termination.
|
||||
*
|
||||
* @param {Number} [statusCode] The WebSocket connection close code.
|
||||
* @param {String} [reason] The connection close reason.
|
||||
*/
|
||||
serverClose(statusCode, reason = '') {
|
||||
if (!isFinite(statusCode)) {
|
||||
statusCode = WEBSOCKET_CLOSE_GOING_AWAY;
|
||||
}
|
||||
waterfall(
|
||||
() => this._listener.onServerClose(this._context, statusCode, reason),
|
||||
() => this._listener.onStop(this._context, Cr.NS_BASE_STREAM_CLOSED)
|
||||
);
|
||||
},
|
||||
|
||||
serverInterrupt(result = Cr.NS_ERROR_NET_RESET) {
|
||||
waterfall(() => this._listener.onStop(this._context, result));
|
||||
},
|
||||
};
|
||||
|
||||
var setUpServiceInParent = Task.async(function* (service, db) {
|
||||
if (!isParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userAgentID = 'ce704e41-cb77-4206-b07b-5bf47114791b';
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
|
||||
yield db.put({
|
||||
channelID: '6e2814e1-5f84-489e-b542-855cc1311f09',
|
||||
pushEndpoint: 'https://example.org/push/get',
|
||||
scope: 'https://example.com/get/ok',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
pushCount: 10,
|
||||
lastPush: 1438360548322,
|
||||
quota: 16,
|
||||
});
|
||||
yield db.put({
|
||||
channelID: '3a414737-2fd0-44c0-af05-7efc172475fc',
|
||||
pushEndpoint: 'https://example.org/push/unsub',
|
||||
scope: 'https://example.com/unsub/ok',
|
||||
originAttributes: '',
|
||||
version: 2,
|
||||
pushCount: 10,
|
||||
lastPush: 1438360848322,
|
||||
quota: 4,
|
||||
});
|
||||
yield db.put({
|
||||
channelID: 'ca3054e8-b59b-4ea0-9c23-4a3c518f3161',
|
||||
pushEndpoint: 'https://example.org/push/stale',
|
||||
scope: 'https://example.com/unsub/fail',
|
||||
originAttributes: '',
|
||||
version: 3,
|
||||
pushCount: 10,
|
||||
lastPush: 1438362348322,
|
||||
quota: 1,
|
||||
});
|
||||
|
||||
service.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db: makeStub(db, {
|
||||
put(prev, record) {
|
||||
if (record.scope == 'https://example.com/sub/fail') {
|
||||
return Promise.reject('synergies not aligned');
|
||||
}
|
||||
return prev.call(this, record);
|
||||
},
|
||||
delete: function(prev, channelID) {
|
||||
if (channelID == 'ca3054e8-b59b-4ea0-9c23-4a3c518f3161') {
|
||||
return Promise.reject('splines not reticulated');
|
||||
}
|
||||
return prev.call(this, channelID);
|
||||
},
|
||||
getByIdentifiers(prev, identifiers) {
|
||||
if (identifiers.scope == 'https://example.com/get/fail') {
|
||||
return Promise.reject('qualia unsynchronized');
|
||||
}
|
||||
return prev.call(this, identifiers);
|
||||
},
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200,
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
if (request.key) {
|
||||
let appServerKey = new Uint8Array(
|
||||
ChromeUtils.base64URLDecode(request.key, {
|
||||
padding: "require",
|
||||
})
|
||||
);
|
||||
equal(appServerKey.length, 65, 'Wrong app server key length');
|
||||
equal(appServerKey[0], 4, 'Wrong app server key format');
|
||||
}
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
uaid: userAgentID,
|
||||
channelID: request.channelID,
|
||||
status: 200,
|
||||
pushEndpoint: 'https://example.org/push/' + request.channelID,
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
channelID: request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var tearDownServiceInParent = Task.async(function* (db) {
|
||||
if (!isParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
let record = yield db.getByIdentifiers({
|
||||
scope: 'https://example.com/sub/ok',
|
||||
originAttributes: '',
|
||||
});
|
||||
ok(record.pushEndpoint.startsWith('https://example.org/push'),
|
||||
'Wrong push endpoint in subscription record');
|
||||
|
||||
record = yield db.getByIdentifiers({
|
||||
scope: 'https://example.net/scope/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: 1, inIsolatedMozBrowser: true }),
|
||||
});
|
||||
ok(record.pushEndpoint.startsWith('https://example.org/push'),
|
||||
'Wrong push endpoint in app record');
|
||||
|
||||
record = yield db.getByKeyID('3a414737-2fd0-44c0-af05-7efc172475fc');
|
||||
ok(!record, 'Unsubscribed record should not exist');
|
||||
});
|
||||
|
||||
function putTestRecord(db, keyID, scope, quota) {
|
||||
return db.put({
|
||||
channelID: keyID,
|
||||
pushEndpoint: 'https://example.org/push/' + keyID,
|
||||
scope: scope,
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: quota,
|
||||
systemRecord: quota == Infinity,
|
||||
});
|
||||
}
|
||||
|
||||
function getAllKeyIDs(db) {
|
||||
return db.getAllKeyIDs().then(records =>
|
||||
records.map(record => record.keyID).sort(compareAscending)
|
||||
);
|
||||
}
|
||||
4
dom/push/test/xpcshell/moz.build
Normal file
4
dom/push/test/xpcshell/moz.build
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
EXTRA_COMPONENTS += [
|
||||
'PushServiceHandler.js',
|
||||
'PushServiceHandler.manifest',
|
||||
]
|
||||
115
dom/push/test/xpcshell/test_clearAll_successful.js
Normal file
115
dom/push/test/xpcshell/test_clearAll_successful.js
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var db;
|
||||
var unregisterDefers = {};
|
||||
var userAgentID = '4ce480ef-55b2-4f83-924c-dcd35ab978b4';
|
||||
|
||||
function promiseUnregister(keyID, code) {
|
||||
return new Promise(r => unregisterDefers[keyID] = r);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(_ => db.drop().then(_ => db.close()));
|
||||
|
||||
// Active subscriptions; should be expired then dropped.
|
||||
yield putTestRecord(db, 'active-1', 'https://example.info/some-page', 8);
|
||||
yield putTestRecord(db, 'active-2', 'https://example.com/another-page', 16);
|
||||
|
||||
// Expired subscription; should be dropped.
|
||||
yield putTestRecord(db, 'expired', 'https://example.net/yet-another-page', 0);
|
||||
|
||||
// A privileged subscription that should not be affected by sanitizing data
|
||||
// because its quota is set to `Infinity`.
|
||||
yield putTestRecord(db, 'privileged', 'app://chrome/only', Infinity);
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(r => handshakeDone = r);
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db: db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200,
|
||||
use_webpush: true,
|
||||
}));
|
||||
handshakeDone();
|
||||
},
|
||||
onUnregister(request) {
|
||||
let resolve = unregisterDefers[request.channelID];
|
||||
equal(typeof resolve, 'function',
|
||||
'Dropped unexpected channel ID ' + request.channelID);
|
||||
delete unregisterDefers[request.channelID];
|
||||
equal(request.code, 200,
|
||||
'Expected manual unregister reason');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
channelID: request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
yield handshakePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_sanitize() {
|
||||
let modifiedScopes = [];
|
||||
let changeScopes = [];
|
||||
|
||||
let promiseCleared = Promise.all([
|
||||
// Active subscriptions should be unregistered.
|
||||
promiseUnregister('active-1'),
|
||||
promiseUnregister('active-2'),
|
||||
promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic, (subject, data) => {
|
||||
modifiedScopes.push(data);
|
||||
return modifiedScopes.length == 3;
|
||||
}),
|
||||
|
||||
// Privileged should be recreated.
|
||||
promiseUnregister('privileged'),
|
||||
promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionChangeTopic, (subject, data) => {
|
||||
changeScopes.push(data);
|
||||
return changeScopes.length == 1;
|
||||
}),
|
||||
]);
|
||||
|
||||
yield PushService.clear({
|
||||
domain: '*',
|
||||
});
|
||||
|
||||
yield promiseCleared;
|
||||
|
||||
deepEqual(modifiedScopes.sort(compareAscending), [
|
||||
'app://chrome/only',
|
||||
'https://example.com/another-page',
|
||||
'https://example.info/some-page',
|
||||
], 'Should modify active subscription scopes');
|
||||
|
||||
deepEqual(changeScopes, ['app://chrome/only'],
|
||||
'Should fire change notification for privileged scope');
|
||||
|
||||
let remainingIDs = yield getAllKeyIDs(db);
|
||||
deepEqual(remainingIDs, [], 'Should drop all subscriptions');
|
||||
});
|
||||
128
dom/push/test/xpcshell/test_clear_forgetAboutSite.js
Normal file
128
dom/push/test/xpcshell/test_clear_forgetAboutSite.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
'use strict';
|
||||
|
||||
const {PushService, PushServiceWebSocket} = serviceExports;
|
||||
const {ForgetAboutSite} = Cu.import(
|
||||
'resource://gre/modules/ForgetAboutSite.jsm', {});
|
||||
|
||||
var db;
|
||||
var unregisterDefers = {};
|
||||
var userAgentID = '4fe01c2d-72ac-4c13-93d2-bb072caf461d';
|
||||
|
||||
function promiseUnregister(keyID) {
|
||||
return new Promise(r => unregisterDefers[keyID] = r);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(_ => db.drop().then(_ => db.close()));
|
||||
|
||||
// Active and expired subscriptions for a subdomain. The active subscription
|
||||
// should be expired, then removed; the expired subscription should be
|
||||
// removed immediately.
|
||||
yield putTestRecord(db, 'active-sub', 'https://sub.example.com/sub-page', 4);
|
||||
yield putTestRecord(db, 'expired-sub', 'https://sub.example.com/yet-another-page', 0);
|
||||
|
||||
// Active subscriptions for another subdomain. Should be unsubscribed and
|
||||
// dropped.
|
||||
yield putTestRecord(db, 'active-1', 'https://sub2.example.com/some-page', 8);
|
||||
yield putTestRecord(db, 'active-2', 'https://sub3.example.com/another-page', 16);
|
||||
|
||||
// A privileged subscription with a real URL that should not be affected
|
||||
// because its quota is set to `Infinity`.
|
||||
yield putTestRecord(db, 'privileged', 'https://sub.example.com/real-url', Infinity);
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(r => handshakeDone = r);
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db: db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200,
|
||||
use_webpush: true,
|
||||
}));
|
||||
handshakeDone();
|
||||
},
|
||||
onUnregister(request) {
|
||||
let resolve = unregisterDefers[request.channelID];
|
||||
equal(typeof resolve, 'function',
|
||||
'Dropped unexpected channel ID ' + request.channelID);
|
||||
delete unregisterDefers[request.channelID];
|
||||
equal(request.code, 200,
|
||||
'Expected manual unregister reason');
|
||||
resolve();
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
status: 200,
|
||||
channelID: request.channelID,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
// For cleared subscriptions, we only send unregister requests in the
|
||||
// background and if we're connected.
|
||||
yield handshakePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_forgetAboutSubdomain() {
|
||||
let modifiedScopes = [];
|
||||
let promiseForgetSubs = Promise.all([
|
||||
// Active subscriptions should be dropped.
|
||||
promiseUnregister('active-sub'),
|
||||
promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic, (subject, data) => {
|
||||
modifiedScopes.push(data);
|
||||
return modifiedScopes.length == 1;
|
||||
}
|
||||
),
|
||||
]);
|
||||
yield ForgetAboutSite.removeDataFromDomain('sub.example.com');
|
||||
yield promiseForgetSubs;
|
||||
|
||||
deepEqual(modifiedScopes.sort(compareAscending), [
|
||||
'https://sub.example.com/sub-page',
|
||||
], 'Should fire modified notifications for active subscriptions');
|
||||
|
||||
let remainingIDs = yield getAllKeyIDs(db);
|
||||
deepEqual(remainingIDs, ['active-1', 'active-2', 'privileged'],
|
||||
'Should only forget subscriptions for subdomain');
|
||||
});
|
||||
|
||||
add_task(function* test_forgetAboutRootDomain() {
|
||||
let modifiedScopes = [];
|
||||
let promiseForgetSubs = Promise.all([
|
||||
promiseUnregister('active-1'),
|
||||
promiseUnregister('active-2'),
|
||||
promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic, (subject, data) => {
|
||||
modifiedScopes.push(data);
|
||||
return modifiedScopes.length == 2;
|
||||
}
|
||||
),
|
||||
]);
|
||||
|
||||
yield ForgetAboutSite.removeDataFromDomain('example.com');
|
||||
yield promiseForgetSubs;
|
||||
|
||||
deepEqual(modifiedScopes.sort(compareAscending), [
|
||||
'https://sub2.example.com/some-page',
|
||||
'https://sub3.example.com/another-page',
|
||||
], 'Should fire modified notifications for entire domain');
|
||||
|
||||
let remainingIDs = yield getAllKeyIDs(db);
|
||||
deepEqual(remainingIDs, ['privileged'],
|
||||
'Should ignore privileged records with a real URL');
|
||||
});
|
||||
141
dom/push/test/xpcshell/test_clear_origin_data.js
Normal file
141
dom/push/test/xpcshell/test_clear_origin_data.js
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
|
||||
|
||||
let clearForPattern = Task.async(function* (testRecords, pattern) {
|
||||
let patternString = JSON.stringify(pattern);
|
||||
yield PushService._clearOriginData(patternString);
|
||||
|
||||
for (let length = testRecords.length; length--;) {
|
||||
let test = testRecords[length];
|
||||
let originSuffix = ChromeUtils.originAttributesToSuffix(
|
||||
test.originAttributes);
|
||||
|
||||
let registration = yield PushService.registration({
|
||||
scope: test.scope,
|
||||
originAttributes: originSuffix,
|
||||
});
|
||||
|
||||
let url = test.scope + originSuffix;
|
||||
|
||||
if (ObjectUtils.deepEqual(test.clearIf, pattern)) {
|
||||
ok(!registration, 'Should clear registration ' + url +
|
||||
' for pattern ' + patternString);
|
||||
testRecords.splice(length, 1);
|
||||
} else {
|
||||
ok(registration, 'Should not clear registration ' + url +
|
||||
' for pattern ' + patternString);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_webapps_cleardata() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let testRecords = [{
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 1 },
|
||||
clearIf: { appId: 1, inIsolatedMozBrowser: false },
|
||||
}, {
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 1, inIsolatedMozBrowser: true },
|
||||
clearIf: { appId: 1 },
|
||||
}, {
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: { appId: 2, inIsolatedMozBrowser: true },
|
||||
clearIf: { appId: 2, inIsolatedMozBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/2',
|
||||
originAttributes: { appId: 1 },
|
||||
clearIf: { appId: 1, inIsolatedMozBrowser: false },
|
||||
}, {
|
||||
scope: 'https://example.org/2',
|
||||
originAttributes: { appId: 2, inIsolatedMozBrowser: true },
|
||||
clearIf: { appId: 2, inIsolatedMozBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/3',
|
||||
originAttributes: { appId: 3, inIsolatedMozBrowser: true },
|
||||
clearIf: { inIsolatedMozBrowser: true },
|
||||
}, {
|
||||
scope: 'https://example.org/3',
|
||||
originAttributes: { appId: 4, inIsolatedMozBrowser: true },
|
||||
clearIf: { inIsolatedMozBrowser: true },
|
||||
}];
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve =>
|
||||
unregisterDone = after(testRecords.length, resolve));
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(data) {
|
||||
equal(data.messageType, 'hello', 'Handshake: wrong message type');
|
||||
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(data) {
|
||||
equal(data.messageType, 'register', 'Register: wrong message type');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: data.channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/' + Math.random(),
|
||||
}));
|
||||
},
|
||||
onUnregister(data) {
|
||||
equal(data.code, 200, 'Expected manual unregister reason');
|
||||
unregisterDone();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield Promise.all(testRecords.map(test =>
|
||||
PushService.register({
|
||||
scope: test.scope,
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
test.originAttributes),
|
||||
})
|
||||
));
|
||||
|
||||
// Removes records for all scopes with the same app ID. Excludes records
|
||||
// where `inIsolatedMozBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 1, inIsolatedMozBrowser: false });
|
||||
|
||||
// Removes the remaining record for app ID 1, where `inIsolatedMozBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 1 });
|
||||
|
||||
// Removes all records for all scopes with the same app ID, where
|
||||
// `inIsolatedMozBrowser` is true.
|
||||
yield clearForPattern(testRecords, { appId: 2, inIsolatedMozBrowser: true });
|
||||
|
||||
// Removes all records where `inIsolatedMozBrowser` is true.
|
||||
yield clearForPattern(testRecords, { inIsolatedMozBrowser: true });
|
||||
|
||||
equal(testRecords.length, 0, 'Should remove all test records');
|
||||
yield unregisterPromise;
|
||||
});
|
||||
249
dom/push/test/xpcshell/test_crypto.js
Normal file
249
dom/push/test/xpcshell/test_crypto.js
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
getCryptoParams,
|
||||
PushCrypto,
|
||||
} = Cu.import('resource://gre/modules/PushCrypto.jsm', {});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_crypto_getCryptoParams() {
|
||||
// These headers should parse correctly.
|
||||
let shouldParse = [{
|
||||
desc: 'aesgcm with multiple keys',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
crypto_key: 'keyid=p256dh;dh=Iy1Je2Kv11A,p256ecdsa=o2M8QfiEKuI',
|
||||
encryption: 'keyid=p256dh;salt=upk1yFkp1xI',
|
||||
},
|
||||
params: {
|
||||
dh: 'Iy1Je2Kv11A',
|
||||
salt: 'upk1yFkp1xI',
|
||||
rs: 4096,
|
||||
padSize: 2,
|
||||
},
|
||||
}, {
|
||||
desc: 'aesgcm with quoted key param',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
crypto_key: 'dh="byfHbUffc-k"',
|
||||
encryption: 'salt=C11AvAsp6Gc',
|
||||
},
|
||||
params: {
|
||||
dh: 'byfHbUffc-k',
|
||||
salt: 'C11AvAsp6Gc',
|
||||
rs: 4096,
|
||||
padSize: 2,
|
||||
},
|
||||
}, {
|
||||
desc: 'aesgcm with Crypto-Key and rs = 24',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
crypto_key: 'dh="ybuT4VDz-Bg"',
|
||||
encryption: 'salt=H7U7wcIoIKs; rs=24',
|
||||
},
|
||||
params: {
|
||||
dh: 'ybuT4VDz-Bg',
|
||||
salt: 'H7U7wcIoIKs',
|
||||
rs: 24,
|
||||
padSize: 2,
|
||||
},
|
||||
}, {
|
||||
desc: 'aesgcm128 with Encryption-Key and rs = 2',
|
||||
headers: {
|
||||
encoding: 'aesgcm128',
|
||||
encryption_key: 'keyid=legacy; dh=LqrDQuVl9lY',
|
||||
encryption: 'keyid=legacy; salt=YngI8B7YapM; rs=2',
|
||||
},
|
||||
params: {
|
||||
dh: 'LqrDQuVl9lY',
|
||||
salt: 'YngI8B7YapM',
|
||||
rs: 2,
|
||||
padSize: 1,
|
||||
},
|
||||
}, {
|
||||
desc: 'aesgcm128 with Encryption-Key',
|
||||
headers: {
|
||||
encoding: 'aesgcm128',
|
||||
encryption_key: 'keyid=v2; dh=VA6wmY1IpiE',
|
||||
encryption: 'keyid=v2; salt=khtpyXhpDKM',
|
||||
},
|
||||
params: {
|
||||
dh: 'VA6wmY1IpiE',
|
||||
salt: 'khtpyXhpDKM',
|
||||
rs: 4096,
|
||||
padSize: 1,
|
||||
}
|
||||
}];
|
||||
for (let test of shouldParse) {
|
||||
let params = getCryptoParams(test.headers);
|
||||
deepEqual(params, test.params, test.desc);
|
||||
}
|
||||
|
||||
// These headers should be rejected.
|
||||
let shouldThrow = [{
|
||||
desc: 'aesgcm128 with Crypto-Key',
|
||||
headers: {
|
||||
encoding: 'aesgcm128',
|
||||
crypto_key: 'keyid=v2; dh=VA6wmY1IpiE',
|
||||
encryption: 'keyid=v2; salt=F0Im7RtGgNY',
|
||||
},
|
||||
}, {
|
||||
desc: 'Invalid encoding',
|
||||
headers: {
|
||||
encoding: 'nonexistent',
|
||||
},
|
||||
}, {
|
||||
desc: 'Invalid record size',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
crypto_key: 'dh=pbmv1QkcEDY',
|
||||
encryption: 'dh=Esao8aTBfIk;rs=bad',
|
||||
},
|
||||
}, {
|
||||
desc: 'Insufficiently large record size',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
crypto_key: 'dh=fK0EXaw5IU8',
|
||||
encryption: 'salt=orbLLmlbJfM;rs=1',
|
||||
},
|
||||
}, {
|
||||
desc: 'aesgcm with Encryption-Key',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
encryption_key: 'dh=FplK5KkvUF0',
|
||||
encryption: 'salt=p6YHhFF3BQY',
|
||||
},
|
||||
}];
|
||||
for (let test of shouldThrow) {
|
||||
throws(() => getCryptoParams(test.headers), test.desc);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_crypto_decodeMsg() {
|
||||
let privateKey = {
|
||||
crv: 'P-256',
|
||||
d: '4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg',
|
||||
ext: true,
|
||||
key_ops: ['deriveBits'],
|
||||
kty: 'EC',
|
||||
x: 'sd85ZCbEG6dEkGMCmDyGBIt454Qy-Yo-1xhbaT2Jlk4',
|
||||
y: 'vr3cKpQ-Sp1kpZ9HipNjUCwSA55yy0uM8N9byE8dmLs',
|
||||
};
|
||||
let publicKey = ChromeUtils.base64URLDecode('BLHfOWQmxBunRJBjApg8hgSLeOeEMvmKPtcYW2k9iZZOvr3cKpQ-Sp1kpZ9HipNjUCwSA55yy0uM8N9byE8dmLs', {
|
||||
padding: "reject",
|
||||
});
|
||||
|
||||
let expectedSuccesses = [{
|
||||
desc: 'padSize = 2, rs = 24, pad = 0',
|
||||
result: 'Some message',
|
||||
data: 'Oo34w2F9VVnTMFfKtdx48AZWQ9Li9M6DauWJVgXU',
|
||||
authSecret: 'aTDc6JebzR6eScy2oLo4RQ',
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo',
|
||||
encryption: 'salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 2, rs = 8, pad = 16',
|
||||
result: 'Yet another message',
|
||||
data: 'uEC5B_tR-fuQ3delQcrzrDCp40W6ipMZjGZ78USDJ5sMj-6bAOVG3AK6JqFl9E6AoWiBYYvMZfwThVxmDnw6RHtVeLKFM5DWgl1EwkOohwH2EhiDD0gM3io-d79WKzOPZE9rDWUSv64JstImSfX_ADQfABrvbZkeaWxh53EG59QMOElFJqHue4dMURpsMXg',
|
||||
authSecret: '6plwZnSpVUbF7APDXus3UQ',
|
||||
headers: {
|
||||
crypto_key: 'dh=BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ',
|
||||
encryption: 'salt=ZFhzj0S-n29g9P2p4-I7tA; rs=8',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 1, rs = 4096, pad = 2',
|
||||
result: 'aesgcm128 encrypted message',
|
||||
data: 'ljBJ44NPzJFH9EuyT5xWMU4vpZ90MdAqaq1TC1kOLRoPNHtNFXeJ0GtuSaE',
|
||||
headers: {
|
||||
encryption_key: 'dh=BOmnfg02vNd6RZ7kXWWrCGFF92bI-rQ-bV0Pku3-KmlHwbGv4ejWqgasEdLGle5Rhmp6SKJunZw2l2HxKvrIjfI',
|
||||
encryption: 'salt=btxxUtclbmgcc30b9rT3Bg; rs=4096',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 2, rs = 3, pad = 0',
|
||||
result: 'Small record size',
|
||||
data: 'oY4e5eDatDVt2fpQylxbPJM-3vrfhDasfPc8Q1PWt4tPfMVbz_sDNL_cvr0DXXkdFzS1lxsJsj550USx4MMl01ihjImXCjrw9R5xFgFrCAqJD3GwXA1vzS4T5yvGVbUp3SndMDdT1OCcEofTn7VC6xZ-zP8rzSQfDCBBxmPU7OISzr8Z4HyzFCGJeBfqiZ7yUfNlKF1x5UaZ4X6iU_TXx5KlQy_toV1dXZ2eEAMHJUcSdArvB6zRpFdEIxdcHcJyo1BIYgAYTDdAIy__IJVCPY_b2CE5W_6ohlYKB7xDyH8giNuWWXAgBozUfScLUVjPC38yJTpAUi6w6pXgXUWffende5FreQpnMFL1L4G-38wsI_-ISIOzdO8QIrXHxmtc1S5xzYu8bMqSgCinvCEwdeGFCmighRjj8t1zRWo0D14rHbQLPR_b1P5SvEeJTtS9Nm3iibM',
|
||||
authSecret: 'g2rWVHUCpUxgcL9Tz7vyeQ',
|
||||
headers: {
|
||||
crypto_key: 'dh=BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_fYHBP4ObxwJNl56bk',
|
||||
encryption: 'salt=5LIDBXbvkBvvb7ZdD-T4PQ; rs=3',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}];
|
||||
for (let test of expectedSuccesses) {
|
||||
let authSecret = test.authSecret ? ChromeUtils.base64URLDecode(test.authSecret, {
|
||||
padding: "reject",
|
||||
}) : null;
|
||||
let data = ChromeUtils.base64URLDecode(test.data, {
|
||||
padding: "reject",
|
||||
});
|
||||
let result = yield PushCrypto.decrypt(privateKey, publicKey, authSecret,
|
||||
test.headers, data);
|
||||
let decoder = new TextDecoder('utf-8');
|
||||
equal(decoder.decode(new Uint8Array(result)), test.result, test.desc);
|
||||
}
|
||||
|
||||
let expectedFailures = [{
|
||||
desc: 'padSize = 1, rs = 4096, auth secret, pad = 8',
|
||||
data: 'h0FmyldY8aT5EQ6CJrbfRn_IdDvytoLeHb9_q5CjtdFRfgDRknxLmOzavLaVG4oOiS0r',
|
||||
senderPublicKey: '',
|
||||
authSecret: 'Sxb6u0gJIhGEogyLawjmCw',
|
||||
headers: {
|
||||
crypto_key: 'dh=BCXHk7O8CE-9AOp6xx7g7c-NCaNpns1PyyHpdcmDaijLbT6IdGq0ezGatBwtFc34BBfscFxdk4Tjksa2Mx5rRCM',
|
||||
encryption: 'salt=aGBpoKklLtrLcAUCcCr7JQ',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'Missing padding',
|
||||
data: 'anvsHj7oBQTPMhv7XSJEsvyMS4-8EtbC7HgFZsKaTg',
|
||||
headers: {
|
||||
crypto_key: 'dh=BMSqfc3ohqw2DDgu3nsMESagYGWubswQPGxrW1bAbYKD18dIHQBUmD3ul_lu7MyQiT5gNdzn5JTXQvCcpf-oZE4',
|
||||
encryption: 'salt=Czx2i18rar8XWOXAVDnUuw',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize > rs',
|
||||
data: 'Ct_h1g7O55e6GvuhmpjLsGnv8Rmwvxgw8iDESNKGxk_8E99iHKDzdV8wJPyHA-6b2E6kzuVa5UWiQ7s4Zms1xzJ4FKgoxvBObXkc_r_d4mnb-j245z3AcvRmcYGk5_HZ0ci26SfhAN3lCgxGzTHS4nuHBRkGwOb4Tj4SFyBRlLoTh2jyVK2jYugNjH9tTrGOBg7lP5lajLTQlxOi91-RYZSfFhsLX3LrAkXuRoN7G1CdiI7Y3_eTgbPIPabDcLCnGzmFBTvoJSaQF17huMl_UnWoCj2WovA4BwK_TvWSbdgElNnQ4CbArJ1h9OqhDOphVu5GUGr94iitXRQR-fqKPMad0ULLjKQWZOnjuIdV1RYEZ873r62Yyd31HoveJcSDb1T8l_QK2zVF8V4k0xmK9hGuC0rF5YJPYPHgl5__usknzxMBnRrfV5_MOL5uPZwUEFsu',
|
||||
headers: {
|
||||
crypto_key: 'dh=BAcMdWLJRGx-kPpeFtwqR3GE1LWzd1TYh2rg6CEFu53O-y3DNLkNe_BtGtKRR4f7ZqpBMVS6NgfE2NwNPm3Ndls',
|
||||
encryption: 'salt=NQVTKhB0rpL7ZzKkotTGlA; rs=1',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'Encrypted with padSize = 1, decrypted with padSize = 2 and auth secret',
|
||||
data: 'fwkuwTTChcLnrzsbDI78Y2EoQzfnbMI8Ax9Z27_rwX8',
|
||||
authSecret: 'BhbpNTWyO5wVJmVKTV6XaA',
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0',
|
||||
encryption: 'salt=c6JQl9eJ0VvwrUVCQDxY7Q',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'Truncated input',
|
||||
data: 'AlDjj6NvT5HGyrHbT8M5D6XBFSra6xrWS9B2ROaCIjwSu3RyZ1iyuv0',
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0',
|
||||
encryption: 'salt=c6JQl9eJ0VvwrUVCQDxY7Q; rs=25',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}];
|
||||
for (let test of expectedFailures) {
|
||||
let authSecret = test.authSecret ? ChromeUtils.base64URLDecode(test.authSecret, {
|
||||
padding: "reject",
|
||||
}) : null;
|
||||
let data = ChromeUtils.base64URLDecode(test.data, {
|
||||
padding: "reject",
|
||||
});
|
||||
yield rejects(
|
||||
PushCrypto.decrypt(privateKey, publicKey, authSecret,
|
||||
test.headers, data),
|
||||
test.desc
|
||||
);
|
||||
}
|
||||
});
|
||||
154
dom/push/test/xpcshell/test_drop_expired.js
Normal file
154
dom/push/test/xpcshell/test_drop_expired.js
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* 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 PlacesTestUtils.addVisits({
|
||||
uri: uri,
|
||||
title: uri.spec,
|
||||
visitDate: timestamp * 1000,
|
||||
transition: 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(
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.com/expired-quota-restored'
|
||||
);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
channelID: request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield subChangePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_site_visited() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.xyz/expired-quota-exceeded'
|
||||
);
|
||||
|
||||
yield visitURI(quotaURI, Date.now());
|
||||
PushService.observe(null, 'idle-daily', '');
|
||||
|
||||
yield subChangePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_perm_restored() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.info/expired-perm-revoked'
|
||||
);
|
||||
|
||||
Services.perms.add(permURI, 'desktop-notification',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
yield subChangePromise;
|
||||
});
|
||||
47
dom/push/test/xpcshell/test_handler_service.js
Normal file
47
dom/push/test/xpcshell/test_handler_service.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"use strict";
|
||||
|
||||
// Here we test that if an xpcom component is registered with the category
|
||||
// manager for push notifications against a specific scope, that service is
|
||||
// instantiated before the message is delivered.
|
||||
|
||||
// This component is registered for "chrome://test-scope"
|
||||
const kServiceContractID = "@mozilla.org/dom/push/test/PushServiceHandler;1";
|
||||
|
||||
let pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
||||
|
||||
add_test(function test_service_instantiation() {
|
||||
do_load_manifest("PushServiceHandler.manifest");
|
||||
|
||||
let scope = "chrome://test-scope";
|
||||
let pushNotifier = Cc["@mozilla.org/push/Notifier;1"].getService(Ci.nsIPushNotifier);
|
||||
let principal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
pushNotifier.notifyPush(scope, principal, "");
|
||||
|
||||
// Now get a handle to our service and check it received the notification.
|
||||
let handlerService = Cc[kServiceContractID]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
|
||||
equal(handlerService.observed.length, 1);
|
||||
equal(handlerService.observed[0].topic, pushService.pushTopic);
|
||||
let message = handlerService.observed[0].subject.QueryInterface(Ci.nsIPushMessage);
|
||||
equal(message.principal, principal);
|
||||
strictEqual(message.data, null);
|
||||
equal(handlerService.observed[0].data, scope);
|
||||
|
||||
// and a subscription change.
|
||||
pushNotifier.notifySubscriptionChange(scope, principal);
|
||||
equal(handlerService.observed.length, 2);
|
||||
equal(handlerService.observed[1].topic, pushService.subscriptionChangeTopic);
|
||||
equal(handlerService.observed[1].subject, principal);
|
||||
equal(handlerService.observed[1].data, scope);
|
||||
|
||||
// and a subscription modified event.
|
||||
pushNotifier.notifySubscriptionModified(scope, principal);
|
||||
equal(handlerService.observed.length, 3);
|
||||
equal(handlerService.observed[2].topic, pushService.subscriptionModifiedTopic);
|
||||
equal(handlerService.observed[2].subject, principal);
|
||||
equal(handlerService.observed[2].data, scope);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
125
dom/push/test/xpcshell/test_notification_ack.js
Normal file
125
dom/push/test/xpcshell/test_notification_ack.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var userAgentID = '5ab1d1df-7a3d-4024-a469-b9e1bb399fad';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({userAgentID});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_notification_ack() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let records = [{
|
||||
channelID: '21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
|
||||
pushEndpoint: 'https://example.com/update/1',
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
|
||||
pushEndpoint: 'https://example.com/update/2',
|
||||
scope: 'https://example.org/2',
|
||||
originAttributes: '',
|
||||
version: 2,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: '5477bfda-22db-45d4-9614-fee369630260',
|
||||
pushEndpoint: 'https://example.com/update/3',
|
||||
scope: 'https://example.org/3',
|
||||
originAttributes: '',
|
||||
version: 3,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let notifyCount = 0;
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, () =>
|
||||
++notifyCount == 3);
|
||||
|
||||
let acks = 0;
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should send matching device IDs in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: '21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
|
||||
version: 2
|
||||
}]
|
||||
}));
|
||||
},
|
||||
onACK(request) {
|
||||
equal(request.messageType, 'ack', 'Should send acknowledgements');
|
||||
let updates = request.updates;
|
||||
switch (++acks) {
|
||||
case 1:
|
||||
deepEqual([{
|
||||
channelID: '21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
|
||||
version: 2,
|
||||
code: 100,
|
||||
}], updates, 'Wrong updates for acknowledgement 1');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
|
||||
version: 4
|
||||
}, {
|
||||
channelID: '5477bfda-22db-45d4-9614-fee369630260',
|
||||
version: 6
|
||||
}]
|
||||
}));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
deepEqual([{
|
||||
channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
|
||||
version: 4,
|
||||
code: 100,
|
||||
}], updates, 'Wrong updates for acknowledgement 2');
|
||||
break;
|
||||
|
||||
case 3:
|
||||
deepEqual([{
|
||||
channelID: '5477bfda-22db-45d4-9614-fee369630260',
|
||||
version: 6,
|
||||
code: 100,
|
||||
}], updates, 'Wrong updates for acknowledgement 3');
|
||||
ackDone();
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, 'Unexpected acknowledgement ' + acks);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
yield ackPromise;
|
||||
});
|
||||
280
dom/push/test/xpcshell/test_notification_data.js
Normal file
280
dom/push/test/xpcshell/test_notification_data.js
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
let db;
|
||||
let userAgentID = 'f5b47f8d-771f-4ea3-b999-91c135f8766d';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function putRecord(channelID, scope, publicKey, privateKey, authSecret) {
|
||||
return db.put({
|
||||
channelID: channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + channelID,
|
||||
scope: scope,
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
p256dhPublicKey: ChromeUtils.base64URLDecode(publicKey, {
|
||||
padding: "reject",
|
||||
}),
|
||||
p256dhPrivateKey: privateKey,
|
||||
authenticationSecret: ChromeUtils.base64URLDecode(authSecret, {
|
||||
padding: "reject",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
let ackDone;
|
||||
let server;
|
||||
add_task(function* test_notification_ack_data_setup() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
yield putRecord(
|
||||
'subscription1',
|
||||
'https://example.com/page/1',
|
||||
'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
},
|
||||
'c_sGN6uCv9Hu7JOQT34jAQ'
|
||||
);
|
||||
yield putRecord(
|
||||
'subscription2',
|
||||
'https://example.com/page/2',
|
||||
'BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: 'lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: '-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE',
|
||||
y: '5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E'
|
||||
},
|
||||
't3P246Gj9vjKDHHRYaY6hw'
|
||||
);
|
||||
yield putRecord(
|
||||
'subscription3',
|
||||
'https://example.com/page/3',
|
||||
'BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI',
|
||||
{
|
||||
crv: 'P-256',
|
||||
d: 'Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: 'OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po',
|
||||
y: 'Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI'
|
||||
},
|
||||
'E0qiXGWvFSR0PS352ES1_Q'
|
||||
);
|
||||
|
||||
let setupDone;
|
||||
let setupDonePromise = new Promise(r => setupDone = r);
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should send matching device IDs in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200,
|
||||
use_webpush: true,
|
||||
}));
|
||||
server = this;
|
||||
setupDone();
|
||||
},
|
||||
onACK(request) {
|
||||
if (ackDone) {
|
||||
ackDone(request);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
yield setupDonePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_notification_ack_data() {
|
||||
let allTestData = [
|
||||
{
|
||||
channelID: 'subscription1',
|
||||
version: 'v1',
|
||||
send: {
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification1"; dh="BO_tgGm-yvYAGLeRe16AvhzaUcpYRiqgsGOlXpt0DRWDRGGdzVLGlEVJMygqAUECarLnxCiAOHTP_znkedrlWoU"',
|
||||
encryption: 'keyid="notification1";salt="uAZaiXpOSfOLJxtOCZ09dA"',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
data: 'NwrrOWPxLE8Sv5Rr0Kep7n0-r_j3rsYrUw_CXPo',
|
||||
version: 'v1',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/1',
|
||||
data: 'Some message'
|
||||
}
|
||||
},
|
||||
{
|
||||
channelID: 'subscription2',
|
||||
version: 'v2',
|
||||
send: {
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification2"; dh="BKVdQcgfncpNyNWsGrbecX0zq3eHIlHu5XbCGmVcxPnRSbhjrA6GyBIeGdqsUL69j5Z2CvbZd-9z1UBH0akUnGQ"',
|
||||
encryption: 'keyid="notification2";salt="vFn3t3M_k42zHBdpch3VRw"',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
data: 'Zt9dEdqgHlyAL_l83385aEtb98ZBilz5tgnGgmwEsl5AOCNgesUUJ4p9qUU',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/2',
|
||||
data: 'Some message'
|
||||
}
|
||||
},
|
||||
{
|
||||
channelID: 'subscription3',
|
||||
version: 'v3',
|
||||
send: {
|
||||
headers: {
|
||||
encryption_key: 'keyid="notification3";dh="BD3xV_ACT8r6hdIYES3BJj1qhz9wyv7MBrG9vM2UCnjPzwE_YFVpkD-SGqE-BR2--0M-Yf31wctwNsO1qjBUeMg"',
|
||||
encryption: 'keyid="notification3"; salt="DFq188piWU7osPBgqn4Nlg"; rs=24',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
data: 'LKru3ZzxBZuAxYtsaCfaj_fehkrIvqbVd1iSwnwAUgnL-cTeDD-83blxHXTq7r0z9ydTdMtC3UjAcWi8LMnfY-BFzi0qJAjGYIikDA',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/3',
|
||||
data: 'Some message'
|
||||
}
|
||||
},
|
||||
// A message encoded with `aesgcm` (2 bytes of padding, authenticated).
|
||||
{
|
||||
channelID: 'subscription1',
|
||||
version: 'v5',
|
||||
send: {
|
||||
headers: {
|
||||
crypto_key: 'keyid=v4;dh="BMh_vsnqu79ZZkMTYkxl4gWDLdPSGE72Lr4w2hksSFW398xCMJszjzdblAWXyhSwakRNEU_GopAm4UGzyMVR83w"',
|
||||
encryption: 'keyid="v4";salt="C14Wb7rQTlXzrgcPHtaUzw"',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
data: 'pus4kUaBWzraH34M-d_oN8e0LPpF_X6acx695AMXovDe',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/1',
|
||||
data: 'Another message'
|
||||
}
|
||||
},
|
||||
// A message with 17 bytes of padding and rs of 24
|
||||
{
|
||||
channelID: 'subscription2',
|
||||
version: 'v5',
|
||||
send: {
|
||||
headers: {
|
||||
crypto_key: 'keyid="v5"; dh="BOp-DpyR9eLY5Ci11_loIFqeHzWfc_0evJmq7N8NKzgp60UAMMM06XIi2VZp2_TSdw1omk7E19SyeCCwRp76E-U"',
|
||||
encryption: 'keyid=v5;salt="TvjOou1TqJOQY_ZsOYV3Ww";rs=24',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
data: 'rG9WYQ2ZwUgfj_tMlZ0vcIaNpBN05FW-9RUBZAM-UUZf0_9eGpuENBpUDAw3mFmd2XJpmvPvAtLVs54l3rGwg1o',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/2',
|
||||
data: 'Some message'
|
||||
}
|
||||
},
|
||||
// A message without key identifiers.
|
||||
{
|
||||
channelID: 'subscription3',
|
||||
version: 'v6',
|
||||
send: {
|
||||
headers: {
|
||||
crypto_key: 'dh="BEEjwWbF5jZKCgW0kmUWgG-wNcRvaa9_3zZElHAF8przHwd4cp5_kQsc-IMNZcVA0iUix31jxuMOytU-5DwWtyQ"',
|
||||
encryption: 'salt=aAQcr2khAksgNspPiFEqiQ',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
data: 'pEYgefdI-7L46CYn5dR9TIy2AXGxe07zxclbhstY',
|
||||
},
|
||||
ackCode: 100,
|
||||
receive: {
|
||||
scope: 'https://example.com/page/3',
|
||||
data: 'Some message'
|
||||
}
|
||||
},
|
||||
// A malformed encrypted message.
|
||||
{
|
||||
channelID: 'subscription3',
|
||||
version: 'v7',
|
||||
send: {
|
||||
headers: {
|
||||
crypto_key: 'dh=AAAAAAAA',
|
||||
encryption: 'salt=AAAAAAAA',
|
||||
},
|
||||
data: 'AAAAAAAA',
|
||||
},
|
||||
ackCode: 101,
|
||||
receive: null,
|
||||
},
|
||||
];
|
||||
|
||||
let sendAndReceive = testData => {
|
||||
let messageReceived = testData.receive ? promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
let notification = subject.QueryInterface(Ci.nsIPushMessage).data;
|
||||
equal(notification.text(), testData.receive.data,
|
||||
'Check data for notification ' + testData.version);
|
||||
equal(data, testData.receive.scope,
|
||||
'Check scope for notification ' + testData.version);
|
||||
return true;
|
||||
}) : Promise.resolve();
|
||||
|
||||
let ackReceived = new Promise(resolve => ackDone = resolve)
|
||||
.then(ackData => {
|
||||
deepEqual({
|
||||
messageType: 'ack',
|
||||
updates: [{
|
||||
channelID: testData.channelID,
|
||||
version: testData.version,
|
||||
code: testData.ackCode,
|
||||
}],
|
||||
}, ackData, 'Check updates for acknowledgment ' + testData.version);
|
||||
});
|
||||
|
||||
let msg = JSON.parse(JSON.stringify(testData.send));
|
||||
msg.messageType = 'notification';
|
||||
msg.channelID = testData.channelID;
|
||||
msg.version = testData.version;
|
||||
server.serverSendMsg(JSON.stringify(msg));
|
||||
|
||||
return Promise.all([messageReceived, ackReceived]);
|
||||
};
|
||||
|
||||
yield allTestData.reduce((p, testData) => {
|
||||
return p.then(_ => sendAndReceive(testData));
|
||||
}, Promise.resolve());
|
||||
});
|
||||
140
dom/push/test/xpcshell/test_notification_duplicate.js
Normal file
140
dom/push/test/xpcshell/test_notification_duplicate.js
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1500e7d9-8cbe-4ee6-98da-7fa5d6a39852';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
maxRecentMessageIDsPerSubscription: 4,
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Should acknowledge duplicate notifications, but not notify apps.
|
||||
add_task(function* test_notification_duplicate() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let records = [{
|
||||
channelID: 'has-recents',
|
||||
pushEndpoint: 'https://example.org/update/1',
|
||||
scope: 'https://example.com/1',
|
||||
originAttributes: "",
|
||||
recentMessageIDs: ['dupe'],
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: 'no-recents',
|
||||
pushEndpoint: 'https://example.org/update/2',
|
||||
scope: 'https://example.com/2',
|
||||
originAttributes: "",
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: 'dropped-recents',
|
||||
pushEndpoint: 'https://example.org/update/3',
|
||||
scope: 'https://example.com/3',
|
||||
originAttributes: '',
|
||||
recentMessageIDs: ['newest', 'newer', 'older', 'oldest'],
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let testData = [{
|
||||
channelID: 'has-recents',
|
||||
updates: 1,
|
||||
acks: [{
|
||||
version: 'dupe',
|
||||
code: 102,
|
||||
}, {
|
||||
version: 'not-dupe',
|
||||
code: 100,
|
||||
}],
|
||||
recents: ['not-dupe', 'dupe'],
|
||||
}, {
|
||||
channelID: 'no-recents',
|
||||
updates: 1,
|
||||
acks: [{
|
||||
version: 'not-dupe',
|
||||
code: 100,
|
||||
}],
|
||||
recents: ['not-dupe'],
|
||||
}, {
|
||||
channelID: 'dropped-recents',
|
||||
acks: [{
|
||||
version: 'overflow',
|
||||
code: 100,
|
||||
}, {
|
||||
version: 'oldest',
|
||||
code: 100,
|
||||
}],
|
||||
updates: 2,
|
||||
recents: ['oldest', 'overflow', 'newest', 'newer'],
|
||||
}];
|
||||
|
||||
let expectedUpdates = testData.reduce((sum, {updates}) => sum + updates, 0);
|
||||
let notifiedScopes = [];
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == expectedUpdates;
|
||||
});
|
||||
|
||||
let expectedAcks = testData.reduce((sum, {acks}) => sum + acks.length, 0);
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(expectedAcks, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
use_webpush: true,
|
||||
}));
|
||||
for (let {channelID, acks} of testData) {
|
||||
for (let {version} of acks) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
channelID: channelID,
|
||||
version: version,
|
||||
}))
|
||||
}
|
||||
}
|
||||
},
|
||||
onACK(request) {
|
||||
let [ack] = request.updates;
|
||||
let expectedData = testData.find(test =>
|
||||
test.channelID == ack.channelID);
|
||||
ok(expectedData, `Unexpected channel ${ack.channelID}`);
|
||||
let expectedAck = expectedData.acks.find(expectedAck =>
|
||||
expectedAck.version == ack.version);
|
||||
ok(expectedAck, `Unexpected ack for message ${
|
||||
ack.version} on ${ack.channelID}`);
|
||||
equal(expectedAck.code, ack.code, `Wrong ack status for message ${
|
||||
ack.version} on ${ack.channelID}`);
|
||||
ackDone();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
yield ackPromise;
|
||||
|
||||
for (let {channelID, recents} of testData) {
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
deepEqual(record.recentMessageIDs, recents,
|
||||
`Wrong recent message IDs for ${channelID}`);
|
||||
}
|
||||
});
|
||||
117
dom/push/test/xpcshell/test_notification_error.js
Normal file
117
dom/push/test/xpcshell/test_notification_error.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '3c7462fc-270f-45be-a459-b9d631b0d093';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_notification_error() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let originAttributes = '';
|
||||
let records = [{
|
||||
channelID: 'f04f1e46-9139-4826-b2d1-9411b0821283',
|
||||
pushEndpoint: 'https://example.org/update/success-1',
|
||||
scope: 'https://example.com/a',
|
||||
originAttributes: originAttributes,
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
|
||||
pushEndpoint: 'https://example.org/update/error',
|
||||
scope: 'https://example.com/b',
|
||||
originAttributes: originAttributes,
|
||||
version: 2,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
channelID: 'b63f7bef-0a0d-4236-b41e-086a69dfd316',
|
||||
pushEndpoint: 'https://example.org/update/success-2',
|
||||
scope: 'https://example.com/c',
|
||||
originAttributes: originAttributes,
|
||||
version: 3,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let scopes = [];
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) =>
|
||||
scopes.push(data) == 2);
|
||||
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(records.length, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db: makeStub(db, {
|
||||
getByKeyID(prev, channelID) {
|
||||
if (channelID == '3c3930ba-44de-40dc-a7ca-8a133ec1a866') {
|
||||
return Promise.reject('splines not reticulated');
|
||||
}
|
||||
return prev.call(this, channelID);
|
||||
}
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: records.map(({channelID, version}) =>
|
||||
({channelID, version: ++version}))
|
||||
}));
|
||||
},
|
||||
// Should acknowledge all received updates, even if updating
|
||||
// IndexedDB fails.
|
||||
onACK: ackDone
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
ok(scopes.includes('https://example.com/a'),
|
||||
'Missing scope for notification A');
|
||||
ok(scopes.includes('https://example.com/c'),
|
||||
'Missing scope for notification C');
|
||||
|
||||
yield ackPromise;
|
||||
|
||||
let aRecord = yield db.getByIdentifiers({scope: 'https://example.com/a',
|
||||
originAttributes: originAttributes });
|
||||
equal(aRecord.channelID, 'f04f1e46-9139-4826-b2d1-9411b0821283',
|
||||
'Wrong channel ID for record A');
|
||||
strictEqual(aRecord.version, 2,
|
||||
'Should return the new version for record A');
|
||||
|
||||
let bRecord = yield db.getByIdentifiers({scope: 'https://example.com/b',
|
||||
originAttributes: originAttributes });
|
||||
equal(bRecord.channelID, '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
|
||||
'Wrong channel ID for record B');
|
||||
strictEqual(bRecord.version, 2,
|
||||
'Should return the previous version for record B');
|
||||
|
||||
let cRecord = yield db.getByIdentifiers({scope: 'https://example.com/c',
|
||||
originAttributes: originAttributes });
|
||||
equal(cRecord.channelID, 'b63f7bef-0a0d-4236-b41e-086a69dfd316',
|
||||
'Wrong channel ID for record C');
|
||||
strictEqual(cRecord.version, 4,
|
||||
'Should return the new version for record C');
|
||||
});
|
||||
189
dom/push/test/xpcshell/test_notification_http2.js
Normal file
189
dom/push/test/xpcshell/test_notification_http2.js
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var prefs;
|
||||
var tlsProfile;
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
});
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
|
||||
|
||||
// Set to allow the cert presented by our H2 server
|
||||
var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
|
||||
prefs.setBoolPref("dom.push.enabled", true);
|
||||
prefs.setBoolPref("dom.push.connection.enabled", true);
|
||||
|
||||
addCertOverride("localhost", serverPort,
|
||||
Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH |
|
||||
Ci.nsICertOverrideService.ERROR_TIME);
|
||||
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_pushNotifications() {
|
||||
|
||||
// /pushNotifications/subscription1 will send a message with no rs and padding
|
||||
// length 1.
|
||||
// /pushNotifications/subscription2 will send a message with no rs and padding
|
||||
// length 16.
|
||||
// /pushNotifications/subscription3 will send a message with rs equal 24 and
|
||||
// padding length 16.
|
||||
// /pushNotifications/subscription4 will send a message with no rs and padding
|
||||
// length 256.
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
let records = [{
|
||||
subscriptionUri: serverURL + '/pushNotifications/subscription1',
|
||||
pushEndpoint: serverURL + '/pushEndpoint1',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint1',
|
||||
scope: 'https://example.com/page/1',
|
||||
p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
subscriptionUri: serverURL + '/pushNotifications/subscription2',
|
||||
pushEndpoint: serverURL + '/pushEndpoint2',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint2',
|
||||
scope: 'https://example.com/page/2',
|
||||
p256dhPublicKey: 'BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: 'lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: '-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE',
|
||||
y: '5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
subscriptionUri: serverURL + '/pushNotifications/subscription3',
|
||||
pushEndpoint: serverURL + '/pushEndpoint3',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint3',
|
||||
scope: 'https://example.com/page/3',
|
||||
p256dhPublicKey: 'BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: 'Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: 'OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po',
|
||||
y: 'Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}, {
|
||||
subscriptionUri: serverURL + '/pushNotifications/subscription4',
|
||||
pushEndpoint: serverURL + '/pushEndpoint4',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint4',
|
||||
scope: 'https://example.com/page/4',
|
||||
p256dhPublicKey: ChromeUtils.base64URLDecode('BEcvDzkWCrUtjU_wygL98sbQCQrW1lY9irtgGnlCc4B0JJXLCHB9MTM73qD6GZYfL0YOvKo8XLOflh-J4dMGklU', {
|
||||
padding: "reject",
|
||||
}),
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: 'fWi7tZaX0Pk6WnLrjQ3kiRq_g5XStL5pdH4pllNCqXw',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: 'Ry8PORYKtS2NT_DKAv3yxtAJCtbWVj2Ku2AaeUJzgHQ',
|
||||
y: 'JJXLCHB9MTM73qD6GZYfL0YOvKo8XLOflh-J4dMGklU'
|
||||
},
|
||||
authenticationSecret: ChromeUtils.base64URLDecode('cwDVC1iwAn8E37mkR3tMSg', {
|
||||
padding: "reject",
|
||||
}),
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
}];
|
||||
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let notifyPromise = Promise.all([
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage).data;
|
||||
if (message && (data == "https://example.com/page/1")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage).data;
|
||||
if (message && (data == "https://example.com/page/2")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage).data;
|
||||
if (message && (data == "https://example.com/page/3")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage).data;
|
||||
if (message && (data == "https://example.com/page/4")){
|
||||
equal(message.text(), "Yet another message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL,
|
||||
db
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
});
|
||||
|
||||
add_task(function* test_complete() {
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
|
||||
});
|
||||
130
dom/push/test/xpcshell/test_notification_incomplete.js
Normal file
130
dom/push/test/xpcshell/test_notification_incomplete.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_notification_incomplete() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let records = [{
|
||||
channelID: '123',
|
||||
pushEndpoint: 'https://example.org/update/1',
|
||||
scope: 'https://example.com/page/1',
|
||||
version: 1,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: '3ad1ed95-d37a-4d88-950f-22cbe2e240d7',
|
||||
pushEndpoint: 'https://example.org/update/2',
|
||||
scope: 'https://example.com/page/2',
|
||||
version: 1,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: 'd239498b-1c85-4486-b99b-205866e82d1f',
|
||||
pushEndpoint: 'https://example.org/update/3',
|
||||
scope: 'https://example.com/page/3',
|
||||
version: 3,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: 'a50de97d-b496-43ce-8b53-05522feb78db',
|
||||
pushEndpoint: 'https://example.org/update/4',
|
||||
scope: 'https://example.com/page/4',
|
||||
version: 10,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
function observeMessage(subject, topic, data) {
|
||||
ok(false, 'Should not deliver malformed updates');
|
||||
}
|
||||
do_register_cleanup(() =>
|
||||
Services.obs.removeObserver(observeMessage, PushServiceComponent.pushTopic));
|
||||
Services.obs.addObserver(observeMessage, PushServiceComponent.pushTopic, false);
|
||||
|
||||
let notificationDone;
|
||||
let notificationPromise = new Promise(resolve => notificationDone = after(2, resolve));
|
||||
let prevHandler = PushServiceWebSocket._handleNotificationReply;
|
||||
PushServiceWebSocket._handleNotificationReply = function _handleNotificationReply() {
|
||||
notificationDone();
|
||||
return prevHandler.apply(this, arguments);
|
||||
};
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
// Missing "updates" field; should ignore message.
|
||||
messageType: 'notification'
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
// Wrong channel ID field type.
|
||||
channelID: 123,
|
||||
version: 3
|
||||
}, {
|
||||
// Missing version field.
|
||||
channelID: '3ad1ed95-d37a-4d88-950f-22cbe2e240d7'
|
||||
}, {
|
||||
// Wrong version field type.
|
||||
channelID: 'd239498b-1c85-4486-b99b-205866e82d1f',
|
||||
version: true
|
||||
}, {
|
||||
// Negative versions should be ignored.
|
||||
channelID: 'a50de97d-b496-43ce-8b53-05522feb78db',
|
||||
version: -5
|
||||
}]
|
||||
}));
|
||||
},
|
||||
onACK() {
|
||||
ok(false, 'Should not acknowledge malformed updates');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield notificationPromise;
|
||||
|
||||
let storeRecords = yield db.getAllKeyIDs();
|
||||
storeRecords.sort(({pushEndpoint: a}, {pushEndpoint: b}) =>
|
||||
compareAscending(a, b));
|
||||
recordsAreEqual(records, storeRecords);
|
||||
});
|
||||
|
||||
function recordIsEqual(a, b) {
|
||||
strictEqual(a.channelID, b.channelID, 'Wrong channel ID in record');
|
||||
strictEqual(a.pushEndpoint, b.pushEndpoint, 'Wrong push endpoint in record');
|
||||
strictEqual(a.scope, b.scope, 'Wrong scope in record');
|
||||
strictEqual(a.version, b.version, 'Wrong version in record');
|
||||
}
|
||||
|
||||
function recordsAreEqual(a, b) {
|
||||
equal(a.length, b.length, 'Mismatched record count');
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
recordIsEqual(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
69
dom/push/test/xpcshell/test_notification_version_string.js
Normal file
69
dom/push/test/xpcshell/test_notification_version_string.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'ba31ac13-88d4-4984-8e6b-8731315a7cf8';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_notification_version_string() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
yield db.put({
|
||||
channelID: '6ff97d56-d0c0-43bc-8f5b-61b855e1d93b',
|
||||
pushEndpoint: 'https://example.org/updates/1',
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: '',
|
||||
version: 2,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
});
|
||||
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: '6ff97d56-d0c0-43bc-8f5b-61b855e1d93b',
|
||||
version: '4'
|
||||
}]
|
||||
}));
|
||||
},
|
||||
onACK: ackDone
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let {subject: message, data: scope} = yield notifyPromise;
|
||||
equal(message.QueryInterface(Ci.nsIPushMessage).data, null,
|
||||
'Unexpected data for Simple Push message');
|
||||
|
||||
yield ackPromise;
|
||||
|
||||
let storeRecord = yield db.getByKeyID(
|
||||
'6ff97d56-d0c0-43bc-8f5b-61b855e1d93b');
|
||||
strictEqual(storeRecord.version, 4, 'Wrong record version');
|
||||
equal(storeRecord.quota, Infinity, 'Wrong quota');
|
||||
});
|
||||
42
dom/push/test/xpcshell/test_observer_data.js
Normal file
42
dom/push/test/xpcshell/test_observer_data.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
var pushNotifier = Cc['@mozilla.org/push/Notifier;1']
|
||||
.getService(Ci.nsIPushNotifier);
|
||||
var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_notifyWithData() {
|
||||
let textData = '{"hello":"world"}';
|
||||
let payload = new TextEncoder('utf-8').encode(textData);
|
||||
|
||||
let notifyPromise =
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
pushNotifier.notifyPushWithData('chrome://notify-test', systemPrincipal,
|
||||
'' /* messageId */, payload.length, payload);
|
||||
|
||||
let data = (yield notifyPromise).subject.QueryInterface(
|
||||
Ci.nsIPushMessage).data;
|
||||
deepEqual(data.json(), {
|
||||
hello: 'world',
|
||||
}, 'Should extract JSON values');
|
||||
deepEqual(data.binary(), Array.from(payload),
|
||||
'Should extract raw binary data');
|
||||
equal(data.text(), textData, 'Should extract text data');
|
||||
});
|
||||
|
||||
add_task(function* test_empty_notifyWithData() {
|
||||
let notifyPromise =
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
pushNotifier.notifyPushWithData('chrome://notify-test', systemPrincipal,
|
||||
'' /* messageId */, 0, null);
|
||||
|
||||
let data = (yield notifyPromise).subject.QueryInterface(
|
||||
Ci.nsIPushMessage).data;
|
||||
throws(_ => data.json(),
|
||||
'Should throw an error when parsing an empty string as JSON');
|
||||
strictEqual(data.text(), '', 'Should return an empty string');
|
||||
deepEqual(data.binary(), [], 'Should return an empty array');
|
||||
});
|
||||
111
dom/push/test/xpcshell/test_observer_remoting.js
Normal file
111
dom/push/test/xpcshell/test_observer_remoting.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
'use strict';
|
||||
|
||||
const pushNotifier = Cc['@mozilla.org/push/Notifier;1']
|
||||
.getService(Ci.nsIPushNotifier);
|
||||
|
||||
add_task(function* test_observer_remoting() {
|
||||
if (isParent) {
|
||||
yield testInParent();
|
||||
} else {
|
||||
yield testInChild();
|
||||
}
|
||||
});
|
||||
|
||||
const childTests = [{
|
||||
text: 'Hello from child!',
|
||||
principal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
}];
|
||||
|
||||
const parentTests = [{
|
||||
text: 'Hello from parent!',
|
||||
principal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
}];
|
||||
|
||||
function* testInParent() {
|
||||
// Register observers for notifications from the child, then run the test in
|
||||
// the child and wait for the notifications.
|
||||
let promiseNotifications = childTests.reduce(
|
||||
(p, test) => p.then(_ => waitForNotifierObservers(test, /* shouldNotify = */ false)),
|
||||
Promise.resolve()
|
||||
);
|
||||
let promiseFinished = run_test_in_child('./test_observer_remoting.js');
|
||||
yield promiseNotifications;
|
||||
|
||||
// Wait until the child is listening for notifications from the parent.
|
||||
yield do_await_remote_message('push_test_observer_remoting_child_ready');
|
||||
|
||||
// Fire an observer notification in the parent that should be forwarded to
|
||||
// the child.
|
||||
yield parentTests.reduce(
|
||||
(p, test) => p.then(_ => waitForNotifierObservers(test, /* shouldNotify = */ true)),
|
||||
Promise.resolve()
|
||||
);
|
||||
|
||||
// Wait for the child to exit.
|
||||
yield promiseFinished;
|
||||
}
|
||||
|
||||
function* testInChild() {
|
||||
// Fire an observer notification in the child that should be forwarded to
|
||||
// the parent.
|
||||
yield childTests.reduce(
|
||||
(p, test) => p.then(_ => waitForNotifierObservers(test, /* shouldNotify = */ true)),
|
||||
Promise.resolve()
|
||||
);
|
||||
|
||||
// Register observers for notifications from the parent, let the parent know
|
||||
// we're ready, and wait for the notifications.
|
||||
let promiseNotifierObservers = parentTests.reduce(
|
||||
(p, test) => p.then(_ => waitForNotifierObservers(test, /* shouldNotify = */ false)),
|
||||
Promise.resolve()
|
||||
);
|
||||
do_send_remote_message('push_test_observer_remoting_child_ready');
|
||||
yield promiseNotifierObservers;
|
||||
}
|
||||
|
||||
var waitForNotifierObservers = Task.async(function* ({ text, principal }, shouldNotify = false) {
|
||||
let notifyPromise = promiseObserverNotification(
|
||||
PushServiceComponent.pushTopic);
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionChangeTopic);
|
||||
let subModifiedPromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic);
|
||||
|
||||
let scope = 'chrome://test-scope';
|
||||
let data = new TextEncoder('utf-8').encode(text);
|
||||
|
||||
if (shouldNotify) {
|
||||
pushNotifier.notifyPushWithData(scope, principal, '', data.length, data);
|
||||
pushNotifier.notifySubscriptionChange(scope, principal);
|
||||
pushNotifier.notifySubscriptionModified(scope, principal);
|
||||
}
|
||||
|
||||
let {
|
||||
data: notifyScope,
|
||||
subject: notifySubject,
|
||||
} = yield notifyPromise;
|
||||
equal(notifyScope, scope,
|
||||
'Should fire push notifications with the correct scope');
|
||||
let message = notifySubject.QueryInterface(Ci.nsIPushMessage);
|
||||
equal(message.principal, principal,
|
||||
'Should include the principal in the push message');
|
||||
strictEqual(message.data.text(), text, 'Should include data');
|
||||
|
||||
let {
|
||||
data: subChangeScope,
|
||||
subject: subChangePrincipal,
|
||||
} = yield subChangePromise;
|
||||
equal(subChangeScope, scope,
|
||||
'Should fire subscription change notifications with the correct scope');
|
||||
equal(subChangePrincipal, principal,
|
||||
'Should pass the principal as the subject of a change notification');
|
||||
|
||||
let {
|
||||
data: subModifiedScope,
|
||||
subject: subModifiedPrincipal,
|
||||
} = yield subModifiedPromise;
|
||||
equal(subModifiedScope, scope,
|
||||
'Should fire subscription modified notifications with the correct scope');
|
||||
equal(subModifiedPrincipal, principal,
|
||||
'Should pass the principal as the subject of a modified notification');
|
||||
});
|
||||
296
dom/push/test/xpcshell/test_permissions.js
Normal file
296
dom/push/test/xpcshell/test_permissions.js
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
/* 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';
|
||||
|
||||
let db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
});
|
||||
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let unregisterDefers = {};
|
||||
|
||||
function promiseUnregister(keyID) {
|
||||
return new Promise(r => unregisterDefers[keyID] = r);
|
||||
}
|
||||
|
||||
function makePushPermission(url, capability) {
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPermission]),
|
||||
capability: Ci.nsIPermissionManager[capability],
|
||||
expireTime: 0,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
|
||||
principal: Services.scriptSecurityManager.getCodebasePrincipal(
|
||||
Services.io.newURI(url, null, null)
|
||||
),
|
||||
type: 'desktop-notification',
|
||||
};
|
||||
}
|
||||
|
||||
function promiseObserverNotifications(topic, count) {
|
||||
let notifiedScopes = [];
|
||||
let subChangePromise = promiseObserverNotification(topic, (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == count;
|
||||
});
|
||||
return subChangePromise.then(_ => notifiedScopes.sort());
|
||||
}
|
||||
|
||||
function promiseSubscriptionChanges(count) {
|
||||
return promiseObserverNotifications(
|
||||
PushServiceComponent.subscriptionChangeTopic, count);
|
||||
}
|
||||
|
||||
function promiseSubscriptionModifications(count) {
|
||||
return promiseObserverNotifications(
|
||||
PushServiceComponent.subscriptionModifiedTopic, count);
|
||||
}
|
||||
|
||||
function allExpired(...keyIDs) {
|
||||
return Promise.all(keyIDs.map(
|
||||
keyID => db.getByKeyID(keyID)
|
||||
)).then(records =>
|
||||
records.every(record => record.isExpired())
|
||||
);
|
||||
}
|
||||
|
||||
add_task(function* setUp() {
|
||||
// Active registration; quota should be reset to 16. Since the quota isn't
|
||||
// exposed to content, we shouldn't receive a subscription change event.
|
||||
yield putTestRecord(db, 'active-allow', 'https://example.info/page/1', 8);
|
||||
|
||||
// Expired registration; should be dropped.
|
||||
yield putTestRecord(db, 'expired-allow', 'https://example.info/page/2', 0);
|
||||
|
||||
// Active registration; should be expired when we change the permission
|
||||
// to "deny".
|
||||
yield putTestRecord(db, 'active-deny-changed', 'https://example.xyz/page/1', 16);
|
||||
|
||||
// Two active registrations for a visited site. These will expire when we
|
||||
// add a "deny" permission.
|
||||
yield putTestRecord(db, 'active-deny-added-1', 'https://example.net/ham', 16);
|
||||
yield putTestRecord(db, 'active-deny-added-2', 'https://example.net/green', 8);
|
||||
|
||||
// An already-expired registration for a visited site. We shouldn't send an
|
||||
// `unregister` request for this one, but still receive an observer
|
||||
// notification when we restore permissions.
|
||||
yield putTestRecord(db, 'expired-deny-added', 'https://example.net/eggs', 0);
|
||||
|
||||
// A registration that should not be affected by permission list changes
|
||||
// because its quota is set to `Infinity`.
|
||||
yield putTestRecord(db, 'never-expires', 'app://chrome/only', Infinity);
|
||||
|
||||
// A registration that should be dropped when we clear the permission
|
||||
// list.
|
||||
yield putTestRecord(db, 'drop-on-clear', 'https://example.edu/lonely', 16);
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
handshakeDone();
|
||||
},
|
||||
onUnregister(request) {
|
||||
let resolve = unregisterDefers[request.channelID];
|
||||
equal(typeof resolve, 'function',
|
||||
'Dropped unexpected channel ID ' + request.channelID);
|
||||
delete unregisterDefers[request.channelID];
|
||||
equal(request.code, 202,
|
||||
'Expected permission revoked unregister reason');
|
||||
resolve();
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
status: 200,
|
||||
channelID: request.channelID,
|
||||
}));
|
||||
},
|
||||
onACK(request) {},
|
||||
});
|
||||
}
|
||||
});
|
||||
yield handshakePromise;
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_added() {
|
||||
let subChangePromise = promiseSubscriptionChanges(1);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.info', 'ALLOW_ACTION'),
|
||||
'added'
|
||||
);
|
||||
let notifiedScopes = yield subChangePromise;
|
||||
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.info/page/2',
|
||||
], 'Wrong scopes after adding allow');
|
||||
|
||||
let record = yield db.getByKeyID('active-allow');
|
||||
equal(record.quota, 16,
|
||||
'Should reset quota for active records after adding allow');
|
||||
|
||||
record = yield db.getByKeyID('expired-allow');
|
||||
ok(!record, 'Should drop expired records after adding allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_deleted() {
|
||||
let subModifiedPromise = promiseSubscriptionModifications(1);
|
||||
|
||||
let unregisterPromise = promiseUnregister('active-allow');
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.info', 'ALLOW_ACTION'),
|
||||
'deleted'
|
||||
);
|
||||
|
||||
yield unregisterPromise;
|
||||
|
||||
let notifiedScopes = yield subModifiedPromise;
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.info/page/1',
|
||||
], 'Wrong scopes modified after deleting allow');
|
||||
|
||||
let record = yield db.getByKeyID('active-allow');
|
||||
ok(record.isExpired(),
|
||||
'Should expire active record after deleting allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_added() {
|
||||
let subModifiedPromise = promiseSubscriptionModifications(2);
|
||||
|
||||
let unregisterPromise = Promise.all([
|
||||
promiseUnregister('active-deny-added-1'),
|
||||
promiseUnregister('active-deny-added-2'),
|
||||
]);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'DENY_ACTION'),
|
||||
'added'
|
||||
);
|
||||
yield unregisterPromise;
|
||||
|
||||
let notifiedScopes = yield subModifiedPromise;
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.net/green',
|
||||
'https://example.net/ham',
|
||||
], 'Wrong scopes modified after adding deny');
|
||||
|
||||
let isExpired = yield allExpired(
|
||||
'active-deny-added-1',
|
||||
'expired-deny-added'
|
||||
);
|
||||
ok(isExpired, 'Should expire all registrations after adding deny');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_deleted() {
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'DENY_ACTION'),
|
||||
'deleted'
|
||||
);
|
||||
|
||||
let isExpired = yield allExpired(
|
||||
'active-deny-added-1',
|
||||
'expired-deny-added'
|
||||
);
|
||||
ok(isExpired, 'Should retain expired registrations after deleting deny');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_allow_changed() {
|
||||
let subChangePromise = promiseSubscriptionChanges(3);
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.net', 'ALLOW_ACTION'),
|
||||
'changed'
|
||||
);
|
||||
|
||||
let notifiedScopes = yield subChangePromise;
|
||||
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.net/eggs',
|
||||
'https://example.net/green',
|
||||
'https://example.net/ham'
|
||||
], 'Wrong scopes after changing to allow');
|
||||
|
||||
let droppedRecords = yield Promise.all([
|
||||
db.getByKeyID('active-deny-added-1'),
|
||||
db.getByKeyID('active-deny-added-2'),
|
||||
db.getByKeyID('expired-deny-added'),
|
||||
]);
|
||||
ok(!droppedRecords.some(Boolean),
|
||||
'Should drop all expired registrations after changing to allow');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_deny_changed() {
|
||||
let subModifiedPromise = promiseSubscriptionModifications(1);
|
||||
|
||||
let unregisterPromise = promiseUnregister('active-deny-changed');
|
||||
|
||||
yield PushService._onPermissionChange(
|
||||
makePushPermission('https://example.xyz', 'DENY_ACTION'),
|
||||
'changed'
|
||||
);
|
||||
|
||||
yield unregisterPromise;
|
||||
|
||||
let notifiedScopes = yield subModifiedPromise;
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.xyz/page/1',
|
||||
], 'Wrong scopes modified after changing to deny');
|
||||
|
||||
let record = yield db.getByKeyID('active-deny-changed');
|
||||
ok(record.isExpired(),
|
||||
'Should expire active record after changing to deny');
|
||||
});
|
||||
|
||||
add_task(function* test_permissions_clear() {
|
||||
let subModifiedPromise = promiseSubscriptionModifications(3);
|
||||
|
||||
deepEqual(yield getAllKeyIDs(db), [
|
||||
'active-allow',
|
||||
'active-deny-changed',
|
||||
'drop-on-clear',
|
||||
'never-expires',
|
||||
], 'Wrong records in database before clearing');
|
||||
|
||||
let unregisterPromise = Promise.all([
|
||||
promiseUnregister('active-allow'),
|
||||
promiseUnregister('active-deny-changed'),
|
||||
promiseUnregister('drop-on-clear'),
|
||||
]);
|
||||
|
||||
yield PushService._onPermissionChange(null, 'cleared');
|
||||
|
||||
yield unregisterPromise;
|
||||
|
||||
let notifiedScopes = yield subModifiedPromise;
|
||||
deepEqual(notifiedScopes, [
|
||||
'https://example.edu/lonely',
|
||||
'https://example.info/page/1',
|
||||
'https://example.xyz/page/1',
|
||||
], 'Wrong scopes modified after clearing registrations');
|
||||
|
||||
deepEqual(yield getAllKeyIDs(db), [
|
||||
'never-expires',
|
||||
], 'Unrestricted registrations should not be dropped');
|
||||
});
|
||||
141
dom/push/test/xpcshell/test_quota_exceeded.js
Normal file
141
dom/push/test/xpcshell/test_quota_exceeded.js
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
const userAgentID = '7eb873f9-8d47-4218-804b-fff78dc04e88';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
'testing.ignorePermission': true,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_expiration_origin_threshold() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => db.drop().then(_ => db.close()));
|
||||
|
||||
yield db.put({
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/auctions',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 16,
|
||||
});
|
||||
yield db.put({
|
||||
channelID: '46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/deals',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 16,
|
||||
});
|
||||
|
||||
// The notification threshold is per-origin, even with multiple service
|
||||
// workers for different scopes.
|
||||
yield PlacesTestUtils.addVisits([
|
||||
{
|
||||
uri: 'https://example.com/login',
|
||||
title: 'Sign in to see your auctions',
|
||||
visitDate: (Date.now() - 7 * 24 * 60 * 60 * 1000) * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
},
|
||||
// We'll always use your most recent visit to an origin.
|
||||
{
|
||||
uri: 'https://example.com/auctions',
|
||||
title: 'Your auctions',
|
||||
visitDate: (Date.now() - 2 * 24 * 60 * 60 * 1000) * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
},
|
||||
// ...But we won't count downloads or embeds.
|
||||
{
|
||||
uri: 'https://example.com/invoices/invoice.pdf',
|
||||
title: 'Invoice #123',
|
||||
visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_EMBED
|
||||
},
|
||||
{
|
||||
uri: 'https://example.com/invoices/invoice.pdf',
|
||||
title: 'Invoice #123',
|
||||
visitDate: Date.now() * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
}
|
||||
]);
|
||||
|
||||
// We expect to receive 6 notifications: 5 on the `auctions` channel,
|
||||
// and 1 on the `deals` channel. They're from the same origin, but
|
||||
// different scopes, so each can send 5 notifications before we remove
|
||||
// their subscription.
|
||||
let updates = 0;
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
updates++;
|
||||
return updates == 6;
|
||||
});
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
// We last visited the site 2 days ago, so we can send 5
|
||||
// notifications without throttling. Sending a 6th should
|
||||
// drop the registration.
|
||||
for (let version = 1; version <= 6; version++) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
version,
|
||||
}],
|
||||
}));
|
||||
}
|
||||
// But the limits are per-channel, so we can send 5 more
|
||||
// notifications on a different channel.
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: '46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
|
||||
version: 1,
|
||||
}],
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, 'eb33fc90-c883-4267-b5cb-613969e8e349', 'Unregistered wrong channel ID');
|
||||
equal(request.code, 201, 'Expected quota exceeded unregister reason');
|
||||
unregisterDone();
|
||||
},
|
||||
// We expect to receive acks, but don't care about their
|
||||
// contents.
|
||||
onACK(request) {},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield unregisterPromise;
|
||||
|
||||
yield notifyPromise;
|
||||
|
||||
let expiredRecord = yield db.getByKeyID('eb33fc90-c883-4267-b5cb-613969e8e349');
|
||||
strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
|
||||
});
|
||||
183
dom/push/test/xpcshell/test_quota_observer.js
Normal file
183
dom/push/test/xpcshell/test_quota_observer.js
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '28cd09e2-7506-42d8-9e50-b02785adc7ef';
|
||||
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
});
|
||||
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() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => db.drop().then(_ => db.close()));
|
||||
|
||||
// A registration that we'll expire...
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/deals',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 16,
|
||||
});
|
||||
|
||||
// ...And a registration that we'll evict on startup.
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '4cb6e454-37cf-41c4-a013-4e3a7fdd0bf1',
|
||||
pushEndpoint: 'https://example.org/push/3',
|
||||
scope: 'https://example.com/stuff',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 0,
|
||||
});
|
||||
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: 'https://example.com/infrequent',
|
||||
title: 'Infrequently-visited page',
|
||||
visitDate: (Date.now() - 14 * 24 * 60 * 60 * 1000) * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
});
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
let subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) =>
|
||||
data == 'https://example.com/stuff');
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
version: 2,
|
||||
}],
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, '379c0668-8323-44d2-a315-4ee83f1a9ee9', 'Dropped wrong channel ID');
|
||||
equal(request.code, 201, 'Expected quota exceeded unregister reason');
|
||||
unregisterDone();
|
||||
},
|
||||
onACK(request) {},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield subChangePromise;
|
||||
yield unregisterPromise;
|
||||
|
||||
let expiredRecord = yield db.getByKeyID('379c0668-8323-44d2-a315-4ee83f1a9ee9');
|
||||
strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
|
||||
|
||||
let notifiedScopes = [];
|
||||
subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == 2;
|
||||
});
|
||||
|
||||
// Add an expired registration that we'll revive later using the idle
|
||||
// observer.
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/auctions',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 0,
|
||||
});
|
||||
// ...And an expired registration that we'll revive on fetch.
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '6b2d13fe-d848-4c5f-bdda-e9fc89727dca',
|
||||
pushEndpoint: 'https://example.org/push/4',
|
||||
scope: 'https://example.net/sales',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 0,
|
||||
});
|
||||
|
||||
// Now visit the site...
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: 'https://example.com/another-page',
|
||||
title: 'Infrequently-visited page',
|
||||
visitDate: Date.now() * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
});
|
||||
Services.obs.notifyObservers(null, 'idle-daily', '');
|
||||
|
||||
// And we should receive notifications for both scopes.
|
||||
yield subChangePromise;
|
||||
deepEqual(notifiedScopes.sort(), [
|
||||
'https://example.com/auctions',
|
||||
'https://example.com/deals'
|
||||
], 'Wrong scopes for subscription changes');
|
||||
|
||||
let aRecord = yield db.getByKeyID('379c0668-8323-44d2-a315-4ee83f1a9ee9');
|
||||
ok(!aRecord, 'Should drop expired record');
|
||||
|
||||
let bRecord = yield db.getByKeyID('eb33fc90-c883-4267-b5cb-613969e8e349');
|
||||
ok(!bRecord, 'Should drop evicted record');
|
||||
|
||||
// Simulate a visit to a site with an expired registration, then fetch the
|
||||
// record. This should drop the expired record and fire an observer
|
||||
// notification.
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: 'https://example.net/sales',
|
||||
title: 'Firefox plushies, 99% off',
|
||||
visitDate: Date.now() * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
});
|
||||
subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) => {
|
||||
if (data == 'https://example.net/sales') {
|
||||
ok(subject.isCodebasePrincipal,
|
||||
'Should pass subscription principal as the subject');
|
||||
return true;
|
||||
}
|
||||
});
|
||||
let record = yield PushService.registration({
|
||||
scope: 'https://example.net/sales',
|
||||
originAttributes: '',
|
||||
});
|
||||
ok(!record, 'Should not return evicted record');
|
||||
ok(!(yield db.getByKeyID('6b2d13fe-d848-4c5f-bdda-e9fc89727dca')),
|
||||
'Should drop evicted record on fetch');
|
||||
yield subChangePromise;
|
||||
});
|
||||
120
dom/push/test/xpcshell/test_quota_with_notification.js
Normal file
120
dom/push/test/xpcshell/test_quota_with_notification.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
const userAgentID = 'aaabf1f8-2f68-44f1-a920-b88e9e7d7559';
|
||||
const nsIPushQuotaManager = Components.interfaces.nsIPushQuotaManager;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
'testing.ignorePermission': true,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_expiration_origin_threshold() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
PushService.notificationForOriginClosed("https://example.com");
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
// Simulate a notification being shown for the origin,
|
||||
// this should relax the quota and allow as many push messages
|
||||
// as we want.
|
||||
PushService.notificationForOriginShown("https://example.com");
|
||||
|
||||
yield db.put({
|
||||
channelID: 'f56645a9-1f32-4655-92ad-ddc37f6d54fb',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/quota',
|
||||
pushCount: 0,
|
||||
lastPush: 0,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: 16,
|
||||
});
|
||||
|
||||
// A visit one day ago should provide a quota of 8 messages.
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: 'https://example.com/login',
|
||||
title: 'Sign in to see your auctions',
|
||||
visitDate: (Date.now() - MS_IN_ONE_DAY) * 1000,
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
});
|
||||
|
||||
let numMessages = 10;
|
||||
|
||||
let updates = 0;
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
updates++;
|
||||
return updates == numMessages;
|
||||
});
|
||||
|
||||
let modifications = 0;
|
||||
let modifiedPromise = promiseObserverNotification(PushServiceComponent.subscriptionModifiedTopic, (subject, data) => {
|
||||
// Each subscription should be modified twice: once to update the message
|
||||
// count and last push time, and the second time to update the quota.
|
||||
modifications++;
|
||||
return modifications == numMessages * 2;
|
||||
});
|
||||
|
||||
let updateQuotaPromise = new Promise((resolve, reject) => {
|
||||
let quotaUpdateCount = 0;
|
||||
PushService._updateQuotaTestCallback = function() {
|
||||
quotaUpdateCount++;
|
||||
if (quotaUpdateCount == numMessages) {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
|
||||
// If the origin has visible notifications, the
|
||||
// message should not affect quota.
|
||||
for (let version = 1; version <= 10; version++) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: 'f56645a9-1f32-4655-92ad-ddc37f6d54fb',
|
||||
version,
|
||||
}],
|
||||
}));
|
||||
}
|
||||
},
|
||||
onUnregister(request) {
|
||||
ok(false, "Channel should not be unregistered.");
|
||||
},
|
||||
// We expect to receive acks, but don't care about their
|
||||
// contents.
|
||||
onACK(request) {},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
|
||||
yield updateQuotaPromise;
|
||||
yield modifiedPromise;
|
||||
|
||||
let expiredRecord = yield db.getByKeyID('f56645a9-1f32-4655-92ad-ddc37f6d54fb');
|
||||
notStrictEqual(expiredRecord.quota, 0, 'Expired record not updated');
|
||||
});
|
||||
73
dom/push/test/xpcshell/test_reconnect_retry.js
Normal file
73
dom/push/test/xpcshell/test_reconnect_retry.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 10000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_reconnect_retry() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let registers = 0;
|
||||
let channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '083e6c17-1063-4677-8638-ab705aebebc2'
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
registers++;
|
||||
if (registers == 1) {
|
||||
channelID = request.channelID;
|
||||
this.serverClose();
|
||||
return;
|
||||
}
|
||||
if (registers == 2) {
|
||||
equal(request.channelID, channelID,
|
||||
'Should retry registers after reconnect');
|
||||
}
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
channelID: request.channelID,
|
||||
pushEndpoint: 'https://example.org/push/' + request.channelID,
|
||||
status: 200,
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let registration = yield PushService.register({
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
let retryEndpoint = 'https://example.org/push/' + channelID;
|
||||
equal(registration.endpoint, retryEndpoint, 'Wrong endpoint for retried request');
|
||||
|
||||
registration = yield PushService.register({
|
||||
scope: 'https://example.com/page/2',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
notEqual(registration.endpoint, retryEndpoint, 'Wrong endpoint for new request');
|
||||
|
||||
equal(registers, 3, 'Wrong registration count');
|
||||
});
|
||||
93
dom/push/test/xpcshell/test_record.js
Normal file
93
dom/push/test/xpcshell/test_record.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
const {PushRecord} = Cu.import('resource://gre/modules/PushRecord.jsm', {});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_updateQuota() {
|
||||
let record = new PushRecord({
|
||||
quota: 8,
|
||||
lastPush: Date.now() - 1 * MS_IN_ONE_DAY,
|
||||
});
|
||||
|
||||
record.updateQuota(Date.now() - 2 * MS_IN_ONE_DAY);
|
||||
equal(record.quota, 8,
|
||||
'Should not update quota if last visit is older than last push');
|
||||
|
||||
record.updateQuota(Date.now());
|
||||
equal(record.quota, 16,
|
||||
'Should reset quota if last visit is newer than last push');
|
||||
|
||||
record.reduceQuota();
|
||||
equal(record.quota, 15, 'Should reduce quota');
|
||||
|
||||
// Make sure we calculate the quota correctly for visit dates in the
|
||||
// future (bug 1206424).
|
||||
record.updateQuota(Date.now() + 1 * MS_IN_ONE_DAY);
|
||||
equal(record.quota, 16,
|
||||
'Should reset quota to maximum if last visit is in the future');
|
||||
|
||||
record.updateQuota(-1);
|
||||
strictEqual(record.quota, 0, 'Should set quota to 0 if history was cleared');
|
||||
ok(record.isExpired(), 'Should expire records once the quota reaches 0');
|
||||
record.reduceQuota();
|
||||
strictEqual(record.quota, 0, 'Quota should never be negative');
|
||||
});
|
||||
|
||||
add_task(function* test_systemRecord_updateQuota() {
|
||||
let systemRecord = new PushRecord({
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
});
|
||||
systemRecord.updateQuota(Date.now() - 3 * MS_IN_ONE_DAY);
|
||||
equal(systemRecord.quota, Infinity,
|
||||
'System subscriptions should ignore quota updates');
|
||||
systemRecord.updateQuota(-1);
|
||||
equal(systemRecord.quota, Infinity,
|
||||
'System subscriptions should ignore the last visit time');
|
||||
systemRecord.reduceQuota();
|
||||
equal(systemRecord.quota, Infinity,
|
||||
'System subscriptions should ignore quota reductions');
|
||||
});
|
||||
|
||||
function testPermissionCheck(props) {
|
||||
let record = new PushRecord(props);
|
||||
equal(record.uri.spec, props.scope,
|
||||
`Record URI should match scope URL for ${JSON.stringify(props)}`);
|
||||
if (props.originAttributes) {
|
||||
let originSuffix = ChromeUtils.originAttributesToSuffix(
|
||||
record.principal.originAttributes);
|
||||
equal(originSuffix, props.originAttributes,
|
||||
`Origin suffixes should match for ${JSON.stringify(props)}`);
|
||||
}
|
||||
ok(!record.hasPermission(), `Record ${
|
||||
JSON.stringify(props)} should not have permission yet`);
|
||||
let permURI = Services.io.newURI(props.scope, null, null);
|
||||
Services.perms.add(permURI, 'desktop-notification',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
try {
|
||||
ok(record.hasPermission(), `Record ${
|
||||
JSON.stringify(props)} should have permission`);
|
||||
} finally {
|
||||
Services.perms.remove(permURI, 'desktop-notification');
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function* test_principal_permissions() {
|
||||
let testProps = [{
|
||||
scope: 'https://example.com/',
|
||||
}, {
|
||||
scope: 'https://example.com/',
|
||||
originAttributes: '^userContextId=1',
|
||||
}, {
|
||||
scope: 'https://блог.фанфрог.рф/',
|
||||
}, {
|
||||
scope: 'https://блог.фанфрог.рф/',
|
||||
originAttributes: '^userContextId=1',
|
||||
}];
|
||||
for (let props of testProps) {
|
||||
testPermissionCheck(props);
|
||||
}
|
||||
});
|
||||
112
dom/push/test/xpcshell/test_register_5xxCode_http2.js
Normal file
112
dom/push/test/xpcshell/test_register_5xxCode_http2.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var retries = 0
|
||||
|
||||
function subscribe5xxCodeHandler(metadata, response) {
|
||||
if (retries == 0) {
|
||||
ok(true, "Subscribe 5xx code");
|
||||
do_test_finished();
|
||||
response.setHeader("Retry-After", '1');
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
} else {
|
||||
ok(true, "Subscribed");
|
||||
do_test_finished();
|
||||
response.setHeader("Location",
|
||||
'http://localhost:' + serverPort + '/subscription')
|
||||
response.setHeader("Link",
|
||||
'</pushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</receiptPushEndpoint>; rel="urn:ietf:params:push:receipt"');
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
do_check_true(true, "New listener point");
|
||||
ok(retries == 2, "Should try 2 times.");
|
||||
do_test_finished();
|
||||
response.setHeader("Retry-After", '10');
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscribe5xxCode", subscribe5xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
'http2.retryInterval': 1000,
|
||||
'http2.maxRetries': 2
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe5xxCode",
|
||||
db
|
||||
});
|
||||
|
||||
let originAttributes = ChromeUtils.originAttributesToSuffix({
|
||||
appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
|
||||
inIsolatedMozBrowser: false,
|
||||
});
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.com/retry5xxCode',
|
||||
originAttributes: originAttributes,
|
||||
});
|
||||
|
||||
var subscriptionUri = serverURL + '/subscription';
|
||||
var pushEndpoint = serverURL + '/pushEndpoint';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpoint';
|
||||
equal(newRecord.endpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in database record');
|
||||
equal(record.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in database record');
|
||||
equal(record.scope, 'https://example.com/retry5xxCode',
|
||||
'Wrong scope in database record');
|
||||
|
||||
httpServer.stop(do_test_finished);
|
||||
});
|
||||
56
dom/push/test/xpcshell/test_register_case.js
Normal file
56
dom/push/test/xpcshell/test_register_case.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1760b1f5-c3ba-40e3-9344-adef7c18ab12';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_case() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'HELLO',
|
||||
uaid: userAgentID,
|
||||
status: 200
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'ReGiStEr',
|
||||
uaid: userAgentID,
|
||||
channelID: request.channelID,
|
||||
status: 200,
|
||||
pushEndpoint: 'https://example.com/update/case'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.net/case',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
equal(newRecord.endpoint, 'https://example.com/update/case',
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
let record = yield db.getByPushEndpoint('https://example.com/update/case');
|
||||
equal(record.scope, 'https://example.net/case',
|
||||
'Wrong scope in database record');
|
||||
});
|
||||
201
dom/push/test/xpcshell/test_register_error_http2.js
Normal file
201
dom/push/test/xpcshell/test_register_error_http2.js
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var prefs;
|
||||
var tlsProfile;
|
||||
var serverURL;
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
|
||||
|
||||
serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Connection will fail because of the certificates.
|
||||
add_task(function* test_pushSubscriptionNoConnection() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionNoConnection/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for not being able to establish connecion.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, "Should not store records when connection couldn't be established.");
|
||||
PushService.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_TLS() {
|
||||
// Set to allow the cert presented by our H2 server
|
||||
var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
|
||||
|
||||
addCertOverride("localhost", serverPort,
|
||||
Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH |
|
||||
Ci.nsICertOverrideService.ERROR_TIME);
|
||||
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionMissingLocation() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionMissingLocation/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for the missing location header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, 'Should not store records when the location header is missing.');
|
||||
PushService.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionMissingLink() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionMissingLink/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for the missing link header.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, 'Should not store records when a link header is missing.');
|
||||
PushService.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionMissingLink1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionMissingLink1/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for the missing push endpoint.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, 'Should not store records when the push endpoint is missing.');
|
||||
PushService.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionLocationBogus() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionLocationBogus/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for the bogus location'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, 'Should not store records when location header is bogus.');
|
||||
PushService.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionNot2xxCode() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionNot201Code/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-response',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for not 201 responce code.'
|
||||
);
|
||||
|
||||
let record = yield db.getAllKeyIDs();
|
||||
ok(record.length === 0, 'Should not store records when respons code is not 201.');
|
||||
});
|
||||
|
||||
add_task(function* test_complete() {
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
|
||||
});
|
||||
96
dom/push/test/xpcshell/test_register_flush.js
Normal file
96
dom/push/test/xpcshell/test_register_flush.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '9ce1e6d3-7bdb-4fe9-90a5-def1d64716f1';
|
||||
const channelID = 'c26892c5-6e08-4c16-9f0c-0044697b4d85';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_flush() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let record = {
|
||||
channelID: '9bcc7efb-86c7-4457-93ea-e24e6eb59b74',
|
||||
pushEndpoint: 'https://example.org/update/1',
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: '',
|
||||
version: 2,
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
};
|
||||
yield db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(2, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'notification',
|
||||
updates: [{
|
||||
channelID: request.channelID,
|
||||
version: 2
|
||||
}, {
|
||||
channelID: '9bcc7efb-86c7-4457-93ea-e24e6eb59b74',
|
||||
version: 3
|
||||
}]
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: request.channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.org/update/2'
|
||||
}));
|
||||
},
|
||||
onACK: ackDone
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.com/page/2',
|
||||
originAttributes: '',
|
||||
});
|
||||
equal(newRecord.endpoint, 'https://example.org/update/2',
|
||||
'Wrong push endpoint in record');
|
||||
|
||||
let {data: scope} = yield notifyPromise;
|
||||
equal(scope, 'https://example.com/page/1', 'Wrong notification scope');
|
||||
|
||||
yield ackPromise;
|
||||
|
||||
let prevRecord = yield db.getByKeyID(
|
||||
'9bcc7efb-86c7-4457-93ea-e24e6eb59b74');
|
||||
equal(prevRecord.pushEndpoint, 'https://example.org/update/1',
|
||||
'Wrong existing push endpoint');
|
||||
strictEqual(prevRecord.version, 3,
|
||||
'Should record version updates sent before register responses');
|
||||
|
||||
let registeredRecord = yield db.getByPushEndpoint('https://example.org/update/2');
|
||||
ok(!registeredRecord.version, 'Should not record premature updates');
|
||||
});
|
||||
57
dom/push/test/xpcshell/test_register_invalid_channel.js
Normal file
57
dom/push/test/xpcshell/test_register_invalid_channel.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '52b2b04c-b6cc-42c6-abdf-bef9cbdbea00';
|
||||
const channelID = 'cafed00d';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_invalid_channel() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
uaid: userAgentID,
|
||||
status: 200
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 403,
|
||||
channelID,
|
||||
error: 'Invalid channel ID'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.com/invalid-channel',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for invalid channel ID'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Should not store records for error responses');
|
||||
});
|
||||
58
dom/push/test/xpcshell/test_register_invalid_endpoint.js
Normal file
58
dom/push/test/xpcshell/test_register_invalid_endpoint.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'c9a12e81-ea5e-40f9-8bf4-acee34621671';
|
||||
const channelID = 'c0660af8-b532-4931-81f0-9fd27a12d6ab';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_invalid_endpoint() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: '!@#$%^&*'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-endpoint',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for invalid endpoint'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Should not store records with invalid endpoints');
|
||||
});
|
||||
58
dom/push/test/xpcshell/test_register_invalid_json.js
Normal file
58
dom/push/test/xpcshell/test_register_invalid_json.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '8271186b-8073-43a3-adf6-225bd44a8b0a';
|
||||
const channelID = '2d08571e-feab-48a0-9f05-8254c3c7e61f';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_invalid_json() {
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
let registers = 0;
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
helloDone();
|
||||
},
|
||||
onRegister(request) {
|
||||
equal(request.channelID, channelID, 'Register: wrong channel ID');
|
||||
this.serverSendMsg(');alert(1);(');
|
||||
registers++;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/invalid-json',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for invalid JSON response'
|
||||
);
|
||||
|
||||
yield helloPromise;
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
62
dom/push/test/xpcshell/test_register_no_id.js
Normal file
62
dom/push/test/xpcshell/test_register_no_id.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var userAgentID = '9a2f9efe-2ebb-4bcb-a5d9-9e2b73d30afe';
|
||||
var channelID = '264c2ba0-f6db-4e84-acdb-bd225b62d9e3';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_no_id() {
|
||||
let registers = 0;
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
helloDone();
|
||||
},
|
||||
onRegister(request) {
|
||||
registers++;
|
||||
equal(request.channelID, channelID, 'Register: wrong channel ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.com/incomplete',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for incomplete register response'
|
||||
);
|
||||
|
||||
yield helloPromise;
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
61
dom/push/test/xpcshell/test_register_request_queue.js
Normal file
61
dom/push/test/xpcshell/test_register_request_queue.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_request_queue() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let onHello;
|
||||
let helloPromise = new Promise(resolve => onHello = after(2, function onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '54b08a9e-59c6-4ed7-bb54-f4fd60d6f606'
|
||||
}));
|
||||
resolve();
|
||||
}));
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello,
|
||||
onRegister() {
|
||||
ok(false, 'Should cancel timed-out requests');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let firstRegister = PushService.register({
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
let secondRegister = PushService.register({
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
|
||||
yield Promise.all([
|
||||
rejects(firstRegister, 'Should time out the first request'),
|
||||
rejects(secondRegister, 'Should time out the second request')
|
||||
]);
|
||||
|
||||
yield helloPromise;
|
||||
});
|
||||
87
dom/push/test/xpcshell/test_register_rollback.js
Normal file
87
dom/push/test/xpcshell/test_register_rollback.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'b2546987-4f63-49b1-99f7-739cd3c40e44';
|
||||
const channelID = '35a820f7-d7dd-43b3-af21-d65352212ae3';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_rollback() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let handshakes = 0;
|
||||
let registers = 0;
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db: makeStub(db, {
|
||||
put(prev, record) {
|
||||
return Promise.reject('universe has imploded');
|
||||
}
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
handshakes++;
|
||||
equal(request.uaid, userAgentID, 'Handshake: wrong device ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
equal(request.channelID, channelID, 'Register: wrong channel ID');
|
||||
registers++;
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
channelID,
|
||||
pushEndpoint: 'https://example.com/update/rollback'
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, channelID, 'Unregister: wrong channel ID');
|
||||
equal(request.code, 200, 'Expected manual unregister reason');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
status: 200,
|
||||
channelID
|
||||
}));
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Should return a rejected promise if storage fails.
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.com/storage-error',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for unregister database failure'
|
||||
);
|
||||
|
||||
// Should send an out-of-band unregister request.
|
||||
yield unregisterPromise;
|
||||
equal(handshakes, 1, 'Wrong handshake count');
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
77
dom/push/test/xpcshell/test_register_success.js
Normal file
77
dom/push/test/xpcshell/test_register_success.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
|
||||
const channelID = '0ef2ad4a-6c49-41ad-af6e-95d2425276bf';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_success() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(data) {
|
||||
equal(data.messageType, 'hello', 'Handshake: wrong message type');
|
||||
equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
},
|
||||
onRegister(data) {
|
||||
equal(data.messageType, 'register', 'Register: wrong message type');
|
||||
equal(data.channelID, channelID, 'Register: wrong channel ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/1',
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let subModifiedPromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic);
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
equal(newRecord.endpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
let {data: subModifiedScope} = yield subModifiedPromise;
|
||||
equal(subModifiedScope, 'https://example.org/1',
|
||||
'Should fire a subscription modified event after subscribing');
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
equal(record.channelID, channelID,
|
||||
'Wrong channel ID in database record');
|
||||
equal(record.pushEndpoint, 'https://example.com/update/1',
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.quota, 16,
|
||||
'Wrong quota in database record');
|
||||
});
|
||||
128
dom/push/test/xpcshell/test_register_success_http2.js
Normal file
128
dom/push/test/xpcshell/test_register_success_http2.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var prefs;
|
||||
var tlsProfile;
|
||||
var serverURL;
|
||||
var serverPort = -1;
|
||||
var pushEnabled;
|
||||
var pushConnectionEnabled;
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
|
||||
pushEnabled = prefs.getBoolPref("dom.push.enabled");
|
||||
pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
|
||||
|
||||
// Set to allow the cert presented by our H2 server
|
||||
var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
|
||||
prefs.setBoolPref("dom.push.enabled", true);
|
||||
prefs.setBoolPref("dom.push.connection.enabled", true);
|
||||
|
||||
addCertOverride("localhost", serverPort,
|
||||
Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH |
|
||||
Ci.nsICertOverrideService.ERROR_TIME);
|
||||
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
|
||||
|
||||
serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
|
||||
db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionSuccess() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionSuccess/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.org/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
|
||||
var subscriptionUri = serverURL + '/pushSubscriptionSuccesss';
|
||||
var pushEndpoint = serverURL + '/pushEndpointSuccess';
|
||||
var pushReceiptEndpoint = serverURL + '/receiptPushEndpointSuccess';
|
||||
equal(newRecord.endpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in database record');
|
||||
equal(record.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in database record');
|
||||
equal(record.scope, 'https://example.org/1',
|
||||
'Wrong scope in database record');
|
||||
|
||||
PushService.uninit()
|
||||
});
|
||||
|
||||
add_task(function* test_pushSubscriptionMissingLink2() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/pushSubscriptionMissingLink2/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.org/no_receiptEndpoint',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
|
||||
var subscriptionUri = serverURL + '/subscriptionMissingLink2';
|
||||
var pushEndpoint = serverURL + '/pushEndpointMissingLink2';
|
||||
var pushReceiptEndpoint = '';
|
||||
equal(newRecord.endpoint, pushEndpoint,
|
||||
'Wrong push endpoint in registration record');
|
||||
|
||||
equal(newRecord.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in registration record');
|
||||
|
||||
let record = yield db.getByKeyID(subscriptionUri);
|
||||
equal(record.subscriptionUri, subscriptionUri,
|
||||
'Wrong subscription ID in database record');
|
||||
equal(record.pushEndpoint, pushEndpoint,
|
||||
'Wrong push endpoint in database record');
|
||||
equal(record.pushReceiptEndpoint, pushReceiptEndpoint,
|
||||
'Wrong push endpoint receipt in database record');
|
||||
equal(record.scope, 'https://example.org/no_receiptEndpoint',
|
||||
'Wrong scope in database record');
|
||||
});
|
||||
|
||||
add_task(function* test_complete() {
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
|
||||
prefs.setBoolPref("dom.push.enabled", pushEnabled);
|
||||
prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled);
|
||||
});
|
||||
87
dom/push/test/xpcshell/test_register_timeout.js
Normal file
87
dom/push/test/xpcshell/test_register_timeout.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'a4be0df9-b16d-4b5f-8f58-0f93b6f1e23d';
|
||||
const channelID = 'e1944e0b-48df-45e7-bdc0-d1fbaa7986d3';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_timeout() {
|
||||
let handshakes = 0;
|
||||
let timeoutDone;
|
||||
let timeoutPromise = new Promise(resolve => timeoutDone = resolve);
|
||||
let registers = 0;
|
||||
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushServiceWebSocket._generateID = () => channelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
if (handshakes === 0) {
|
||||
equal(request.uaid, null, 'Should not include device ID');
|
||||
} else if (handshakes === 1) {
|
||||
// Should use the previously-issued device ID when reconnecting,
|
||||
// but should not include the timed-out channel ID.
|
||||
equal(request.uaid, userAgentID,
|
||||
'Should include device ID on reconnect');
|
||||
} else {
|
||||
ok(false, 'Unexpected reconnect attempt ' + handshakes);
|
||||
}
|
||||
handshakes++;
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
equal(request.channelID, channelID,
|
||||
'Wrong channel ID in register request');
|
||||
setTimeout(() => {
|
||||
// Should ignore replies for timed-out requests.
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: channelID,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.com/update/timeout',
|
||||
}));
|
||||
timeoutDone();
|
||||
}, 2000);
|
||||
registers++;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.net/page/timeout',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for request timeout'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Should not store records for timed-out responses');
|
||||
|
||||
yield timeoutPromise;
|
||||
equal(registers, 1, 'Should not handle timed-out register requests');
|
||||
});
|
||||
68
dom/push/test/xpcshell/test_register_wrong_id.js
Normal file
68
dom/push/test/xpcshell/test_register_wrong_id.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '84afc774-6995-40d1-9c90-8c34ddcd0cb4';
|
||||
const clientChannelID = '4b42a681c99e4dfbbb166a7e01a09b8b';
|
||||
const serverChannelID = '3f5aeb89c6e8405a9569619522783436';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_wrong_id() {
|
||||
// Should reconnect after the register request times out.
|
||||
let registers = 0;
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushServiceWebSocket._generateID = () => clientChannelID;
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
helloDone();
|
||||
},
|
||||
onRegister(request) {
|
||||
equal(request.channelID, clientChannelID,
|
||||
'Register: wrong channel ID');
|
||||
registers++;
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
// Reply with a different channel ID. Since the ID is used as a
|
||||
// nonce, the registration request will time out.
|
||||
channelID: serverChannelID
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.com/mismatched',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for mismatched register reply'
|
||||
);
|
||||
|
||||
yield helloPromise;
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
62
dom/push/test/xpcshell/test_register_wrong_type.js
Normal file
62
dom/push/test/xpcshell/test_register_wrong_type.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService} = serviceExports;
|
||||
|
||||
const userAgentID = 'c293fdc5-a75e-4eb1-af88-a203991c0787';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
requestTimeout: 1000,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_register_wrong_type() {
|
||||
let registers = 0;
|
||||
let helloDone;
|
||||
let helloPromise = new Promise(resolve => helloDone = after(2, resolve));
|
||||
|
||||
PushService._generateID = () => '1234';
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
helloDone();
|
||||
},
|
||||
onRegister(request) {
|
||||
registers++;
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
channelID: 1234,
|
||||
uaid: userAgentID,
|
||||
pushEndpoint: 'https://example.org/update/wrong-type'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: 'https://example.com/mistyped',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for non-string channel ID'
|
||||
);
|
||||
|
||||
yield helloPromise;
|
||||
equal(registers, 1, 'Wrong register count');
|
||||
});
|
||||
43
dom/push/test/xpcshell/test_registration_error.js
Normal file
43
dom/push/test/xpcshell/test_registration_error.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: '6faed1f0-1439-4aac-a978-db21c81cd5eb'
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_registrations_error() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db: makeStub(db, {
|
||||
getByIdentifiers(prev, scope) {
|
||||
return Promise.reject('Database error');
|
||||
}
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri);
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.registration({
|
||||
scope: 'https://example.net/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
function(error) {
|
||||
return error == 'Database error';
|
||||
},
|
||||
'Wrong message'
|
||||
);
|
||||
});
|
||||
37
dom/push/test/xpcshell/test_registration_error_http2.js
Normal file
37
dom/push/test/xpcshell/test_registration_error_http2.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_registrations_error() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushService.init({
|
||||
serverURI: "https://push.example.org/",
|
||||
db: makeStub(db, {
|
||||
getByIdentifiers() {
|
||||
return Promise.reject('Database error');
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.registration({
|
||||
scope: 'https://example.net/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
function(error) {
|
||||
return error == 'Database error';
|
||||
},
|
||||
'Wrong message'
|
||||
);
|
||||
});
|
||||
25
dom/push/test/xpcshell/test_registration_missing_scope.js
Normal file
25
dom/push/test/xpcshell/test_registration_missing_scope.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_registration_missing_scope() {
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri);
|
||||
}
|
||||
});
|
||||
yield rejects(
|
||||
PushService.registration({ scope: '', originAttributes: '' }),
|
||||
'Record missing page and manifest URLs'
|
||||
);
|
||||
});
|
||||
31
dom/push/test/xpcshell/test_registration_none.js
Normal file
31
dom/push/test/xpcshell/test_registration_none.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService} = serviceExports;
|
||||
|
||||
const userAgentID = 'a722e448-c481-4c48-aea0-fc411cb7c9ed';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({userAgentID});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Should not open a connection if the client has no registrations.
|
||||
add_task(function* test_registration_none() {
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri);
|
||||
}
|
||||
});
|
||||
|
||||
let registration = yield PushService.registration({
|
||||
scope: 'https://example.net/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
ok(!registration, 'Should not open a connection without registration');
|
||||
});
|
||||
78
dom/push/test/xpcshell/test_registration_success.js
Normal file
78
dom/push/test/xpcshell/test_registration_success.js
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '997ee7ba-36b1-4526-ae9e-2d3f38d6efe8';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({userAgentID});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_registration_success() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let records = [{
|
||||
channelID: 'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
|
||||
pushEndpoint: 'https://example.com/update/same-manifest/1',
|
||||
scope: 'https://example.net/a',
|
||||
originAttributes: '',
|
||||
version: 5,
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: 'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
|
||||
pushEndpoint: 'https://example.com/update/same-manifest/2',
|
||||
scope: 'https://example.net/b',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: 42 }),
|
||||
version: 10,
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: 'b1cf38c9-6836-4d29-8a30-a3e98d59b728',
|
||||
pushEndpoint: 'https://example.org/update/different-manifest',
|
||||
scope: 'https://example.org/c',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: 42, inIsolatedMozBrowser: true }),
|
||||
version: 15,
|
||||
quota: Infinity,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
equal(request.uaid, userAgentID, 'Wrong device ID in handshake');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID
|
||||
}));
|
||||
handshakeDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield handshakePromise;
|
||||
|
||||
let registration = yield PushService.registration({
|
||||
scope: 'https://example.net/a',
|
||||
originAttributes: '',
|
||||
});
|
||||
equal(
|
||||
registration.endpoint,
|
||||
'https://example.com/update/same-manifest/1',
|
||||
'Wrong push endpoint for scope'
|
||||
);
|
||||
equal(registration.version, 5, 'Wrong version for scope');
|
||||
});
|
||||
77
dom/push/test/xpcshell/test_registration_success_http2.js
Normal file
77
dom/push/test/xpcshell/test_registration_success_http2.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var prefs;
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_pushNotifications() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
let records = [{
|
||||
subscriptionUri: serverURL + '/subscriptionA',
|
||||
pushEndpoint: serverURL + '/pushEndpointA',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpointA',
|
||||
scope: 'https://example.net/a',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
}, {
|
||||
subscriptionUri: serverURL + '/subscriptionB',
|
||||
pushEndpoint: serverURL + '/pushEndpointB',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpointB',
|
||||
scope: 'https://example.net/b',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
}, {
|
||||
subscriptionUri: serverURL + '/subscriptionC',
|
||||
pushEndpoint: serverURL + '/pushEndpointC',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpointC',
|
||||
scope: 'https://example.net/c',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL,
|
||||
db
|
||||
});
|
||||
|
||||
let registration = yield PushService.registration({
|
||||
scope: 'https://example.net/a',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
equal(
|
||||
registration.endpoint,
|
||||
serverURL + '/pushEndpointA',
|
||||
'Wrong push endpoint for scope'
|
||||
);
|
||||
});
|
||||
103
dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
Normal file
103
dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => handlerDone = after(3, r));
|
||||
|
||||
function listen4xxCodeHandler(metadata, response) {
|
||||
ok(true, "Listener point error")
|
||||
handlerDone();
|
||||
response.setStatusLine(metadata.httpVersion, 410, "GONE");
|
||||
}
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
handlerDone();
|
||||
response.setHeader("Location",
|
||||
'http://localhost:' + serverPort + '/newSubscription')
|
||||
response.setHeader("Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"');
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
do_check_true(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscription4xxCode", listen4xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
'testing.notifyWorkers': false,
|
||||
'testing.notifyAllObservers': true,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [{
|
||||
subscriptionUri: serverURL + '/subscription4xxCode',
|
||||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield handlerPromise;
|
||||
|
||||
let record = yield db.getByIdentifiers({
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
});
|
||||
equal(record.keyID, serverURL + '/newSubscription',
|
||||
'Should update subscription URL');
|
||||
equal(record.pushEndpoint, serverURL + '/newPushEndpoint',
|
||||
'Should update push endpoint');
|
||||
equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint',
|
||||
'Should update push receipt endpoint');
|
||||
|
||||
});
|
||||
106
dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
Normal file
106
dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var retries = 0;
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => handlerDone = after(5, r));
|
||||
|
||||
function listen5xxCodeHandler(metadata, response) {
|
||||
ok(true, "Listener 5xx code");
|
||||
handlerDone();
|
||||
retries++;
|
||||
response.setHeader("Retry-After", '1');
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
ok(retries == 3, "Should retry 2 times.");
|
||||
handlerDone();
|
||||
response.setHeader("Location",
|
||||
'http://localhost:' + serverPort + '/newSubscription')
|
||||
response.setHeader("Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"');
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
do_check_true(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscription5xxCode", listen5xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
'http2.retryInterval': 1000,
|
||||
'http2.maxRetries': 2
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [{
|
||||
subscriptionUri: serverURL + '/subscription5xxCode',
|
||||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield handlerPromise;
|
||||
|
||||
let record = yield db.getByIdentifiers({
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
});
|
||||
equal(record.keyID, serverURL + '/newSubscription',
|
||||
'Should update subscription URL');
|
||||
equal(record.pushEndpoint, serverURL + '/newPushEndpoint',
|
||||
'Should update push endpoint');
|
||||
equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint',
|
||||
'Should update push receipt endpoint');
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => handlerDone = after(2, r));
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
handlerDone();
|
||||
response.setHeader("Location",
|
||||
'http://localhost:' + serverPort + '/newSubscription')
|
||||
response.setHeader("Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"');
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
do_check_true(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
'http2.retryInterval': 1000,
|
||||
'http2.maxRetries': 2
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [{
|
||||
subscriptionUri: 'http://localhost/subscriptionNotExist',
|
||||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
},
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield handlerPromise;
|
||||
|
||||
let record = yield db.getByIdentifiers({
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
});
|
||||
equal(record.keyID, serverURL + '/newSubscription',
|
||||
'Should update subscription URL');
|
||||
equal(record.pushEndpoint, serverURL + '/newPushEndpoint',
|
||||
'Should update push endpoint');
|
||||
equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint',
|
||||
'Should update push receipt endpoint');
|
||||
|
||||
});
|
||||
69
dom/push/test/xpcshell/test_retry_ws.js
Normal file
69
dom/push/test/xpcshell/test_retry_ws.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '05f7b940-51b6-4b6f-8032-b83ebb577ded';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
pingInterval: 2000,
|
||||
retryBaseInterval: 25,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_ws_retry() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
yield db.put({
|
||||
channelID: '61770ba9-2d57-4134-b949-d40404630d5b',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.net/push/1',
|
||||
version: 1,
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
// Use a mock timer to avoid waiting for the backoff interval.
|
||||
let reconnects = 0;
|
||||
PushServiceWebSocket._backoffTimer = {
|
||||
init(observer, delay, type) {
|
||||
reconnects++;
|
||||
ok(delay >= 5 && delay <= 2000, `Backoff delay ${
|
||||
delay} out of range for attempt ${reconnects}`);
|
||||
observer.observe(this, "timer-callback", null);
|
||||
},
|
||||
|
||||
cancel() {},
|
||||
};
|
||||
|
||||
let handshakeDone;
|
||||
let handshakePromise = new Promise(resolve => handshakeDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
if (reconnects == 10) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
handshakeDone();
|
||||
return;
|
||||
}
|
||||
this.serverInterrupt();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield handshakePromise;
|
||||
});
|
||||
307
dom/push/test/xpcshell/test_service_child.js
Normal file
307
dom/push/test/xpcshell/test_service_child.js
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.importGlobalProperties(["crypto"]);
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var db;
|
||||
|
||||
function done() {
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function generateKey() {
|
||||
return crypto.subtle.generateKey({
|
||||
name: "ECDSA",
|
||||
namedCurve: "P-256",
|
||||
}, true, ["sign", "verify"]).then(cryptoKey =>
|
||||
crypto.subtle.exportKey("raw", cryptoKey.publicKey)
|
||||
).then(publicKey => new Uint8Array(publicKey));
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
if (isParent) {
|
||||
do_get_profile();
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
if (isParent) {
|
||||
add_test(function setUp() {
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
setUpServiceInParent(PushService, db).then(run_next_test, run_next_test);
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_error() {
|
||||
do_test_pending();
|
||||
|
||||
let invalidKey = [0, 1];
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/invalid',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
invalidKey.length,
|
||||
invalidKey,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error creating subscription with invalid key');
|
||||
equal(result, Cr.NS_ERROR_DOM_PUSH_INVALID_KEY_ERR, 'Wrong error code for invalid key');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_success() {
|
||||
do_test_pending();
|
||||
|
||||
generateKey().then(key => {
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
key.length,
|
||||
key,
|
||||
(result, subscription) => {
|
||||
ok(Components.isSuccessCode(result), 'Error creating subscription with key');
|
||||
notStrictEqual(subscription, null, 'Expected subscription');
|
||||
done();
|
||||
}
|
||||
);
|
||||
}, error => {
|
||||
ok(false, "Error generating app server key");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_subscribeWithKey_conflict() {
|
||||
do_test_pending();
|
||||
|
||||
generateKey().then(differentKey => {
|
||||
PushServiceComponent.subscribeWithKey(
|
||||
'https://example.com/sub-key/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
differentKey.length,
|
||||
differentKey,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error creating subscription with conflicting key');
|
||||
equal(result, Cr.NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR, 'Wrong error code for mismatched key');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
done();
|
||||
}
|
||||
);
|
||||
}, error => {
|
||||
ok(false, "Error generating different app server key");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_subscribe_error() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.subscribe(
|
||||
'https://example.com/sub/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error creating subscription');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_getSubscription_exists() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
ok(Components.isSuccessCode(result), 'Error getting subscription');
|
||||
|
||||
equal(subscription.endpoint, 'https://example.org/push/get', 'Wrong endpoint');
|
||||
equal(subscription.pushCount, 10, 'Wrong push count');
|
||||
equal(subscription.lastPush, 1438360548322, 'Wrong last push');
|
||||
equal(subscription.quota, 16, 'Wrong quota for subscription');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_getSubscription_missing() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/missing',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
ok(Components.isSuccessCode(result), 'Error getting nonexistent subscription');
|
||||
strictEqual(subscription, null, 'Nonexistent subscriptions should return null');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_getSubscription_error() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error getting subscription');
|
||||
strictEqual(subscription, null, 'Unexpected subscription');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_unsubscribe_success() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
ok(Components.isSuccessCode(result), 'Error unsubscribing');
|
||||
strictEqual(success, true, 'Expected successful unsubscribe');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_unsubscribe_nonexistent() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
ok(Components.isSuccessCode(result), 'Error removing nonexistent subscription');
|
||||
strictEqual(success, false, 'Nonexistent subscriptions should return false');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_unsubscribe_error() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
ok(!Components.isSuccessCode(result), 'Expected error unsubscribing');
|
||||
strictEqual(success, false, 'Unexpected successful unsubscribe');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribe_app_principal() {
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
Services.io.newURI('https://example.net/app/1', null, null),
|
||||
1, /* appId */
|
||||
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 null principal');
|
||||
strictEqual(subscription, null,
|
||||
'Unexpected subscription with null principal');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function test_subscribe_missing_principal() {
|
||||
do_test_pending();
|
||||
PushServiceComponent.subscribe('chrome://push/missing-principal', null,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result),
|
||||
'Expected error creating subscription without principal');
|
||||
strictEqual(subscription, null,
|
||||
'Unexpected subscription without principal');
|
||||
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (isParent) {
|
||||
add_test(function tearDown() {
|
||||
tearDownServiceInParent(db).then(run_next_test, run_next_test);
|
||||
});
|
||||
}
|
||||
28
dom/push/test/xpcshell/test_service_parent.js
Normal file
28
dom/push/test/xpcshell/test_service_parent.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_service_parent() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
yield setUpServiceInParent(PushService, db);
|
||||
|
||||
// Accessing the lazy service getter will start the service in the main
|
||||
// process.
|
||||
equal(PushServiceComponent.pushTopic, "push-message",
|
||||
"Wrong push message observer topic");
|
||||
equal(PushServiceComponent.subscriptionChangeTopic,
|
||||
"push-subscription-change", "Wrong subscription change observer topic");
|
||||
|
||||
yield run_test_in_child('./test_service_child.js');
|
||||
|
||||
yield tearDownServiceInParent(db);
|
||||
});
|
||||
71
dom/push/test/xpcshell/test_startup_error.js
Normal file
71
dom/push/test/xpcshell/test_startup_error.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
const {PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
setPrefs();
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_startup_error() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db: makeStub(db, {
|
||||
getAllExpired(prev) {
|
||||
return Promise.reject('database corruption on startup');
|
||||
},
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
ok(false, 'Unexpected handshake');
|
||||
},
|
||||
onRegister(request) {
|
||||
ok(false, 'Unexpected register request');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.register({
|
||||
scope: `https://example.net/1`,
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Should not register if startup failed'
|
||||
);
|
||||
|
||||
PushService.uninit();
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
db: makeStub(db, {
|
||||
getAllUnexpired(prev) {
|
||||
return Promise.reject('database corruption on connect');
|
||||
},
|
||||
}),
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
ok(false, 'Unexpected handshake');
|
||||
},
|
||||
onRegister(request) {
|
||||
ok(false, 'Unexpected register request');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
yield rejects(
|
||||
PushService.registration({
|
||||
scope: `https://example.net/1`,
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Should not return registration if connection failed'
|
||||
);
|
||||
});
|
||||
38
dom/push/test/xpcshell/test_unregister_empty_scope.js
Normal file
38
dom/push/test/xpcshell/test_unregister_empty_scope.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_unregister_empty_scope() {
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '5619557c-86fe-4711-8078-d1fd6987aef7'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.unregister({
|
||||
scope: '',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for empty endpoint'
|
||||
);
|
||||
});
|
||||
68
dom/push/test/xpcshell/test_unregister_error.js
Normal file
68
dom/push/test/xpcshell/test_unregister_error.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const channelID = '00c7fa13-7b71-447d-bd27-a91abc09d1b2';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_unregister_error() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
yield db.put({
|
||||
channelID: channelID,
|
||||
pushEndpoint: 'https://example.org/update/failure',
|
||||
scope: 'https://example.net/page/failure',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '083e6c17-1063-4677-8638-ab705aebebc2'
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
// The server is notified out-of-band. Since channels may be pruned,
|
||||
// any failures are swallowed.
|
||||
equal(request.channelID, channelID, 'Unregister: wrong channel ID');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
status: 500,
|
||||
error: 'omg, everything is exploding',
|
||||
channelID
|
||||
}));
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield PushService.unregister({
|
||||
scope: 'https://example.net/page/failure',
|
||||
originAttributes: '',
|
||||
});
|
||||
|
||||
let result = yield db.getByKeyID(channelID);
|
||||
ok(!result, 'Deleted push record exists');
|
||||
|
||||
// Make sure we send a request to the server.
|
||||
yield unregisterPromise;
|
||||
});
|
||||
92
dom/push/test/xpcshell/test_unregister_invalid_json.js
Normal file
92
dom/push/test/xpcshell/test_unregister_invalid_json.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '7f0af1bb-7e1f-4fb8-8e4a-e8de434abde3';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
requestTimeout: 150,
|
||||
retryBaseInterval: 150
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_unregister_invalid_json() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
let records = [{
|
||||
channelID: '87902e90-c57e-4d18-8354-013f4a556559',
|
||||
pushEndpoint: 'https://example.org/update/1',
|
||||
scope: 'https://example.edu/page/1',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: '057caa8f-9b99-47ff-891c-adad18ce603e',
|
||||
pushEndpoint: 'https://example.com/update/2',
|
||||
scope: 'https://example.net/page/1',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = after(2, resolve));
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
use_webpush: true,
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
this.serverSendMsg(');alert(1);(');
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
yield rejects(
|
||||
PushService.unregister({
|
||||
scope: 'https://example.edu/page/1',
|
||||
originAttributes: '',
|
||||
}),
|
||||
'Expected error for first invalid JSON response'
|
||||
);
|
||||
|
||||
let record = yield db.getByKeyID(
|
||||
'87902e90-c57e-4d18-8354-013f4a556559');
|
||||
ok(!record, 'Failed to delete unregistered record');
|
||||
|
||||
yield rejects(
|
||||
PushService.unregister({
|
||||
scope: 'https://example.net/page/1',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
}),
|
||||
'Expected error for second invalid JSON response'
|
||||
);
|
||||
|
||||
record = yield db.getByKeyID(
|
||||
'057caa8f-9b99-47ff-891c-adad18ce603e');
|
||||
ok(!record,
|
||||
'Failed to delete unregistered record after receiving invalid JSON');
|
||||
|
||||
yield unregisterPromise;
|
||||
});
|
||||
36
dom/push/test/xpcshell/test_unregister_not_found.js
Normal file
36
dom/push/test/xpcshell/test_unregister_not_found.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService} = serviceExports;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_unregister_not_found() {
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: 'f074ed80-d479-44fa-ba65-792104a79ea9'
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let result = yield PushService.unregister({
|
||||
scope: 'https://example.net/nonexistent',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
ok(result === false, "unregister should resolve with false for nonexistent scope");
|
||||
});
|
||||
76
dom/push/test/xpcshell/test_unregister_success.js
Normal file
76
dom/push/test/xpcshell/test_unregister_success.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = 'fbe865a6-aeb8-446f-873c-aeebdb8d493c';
|
||||
const channelID = 'db0a7021-ec2d-4bd3-8802-7a6966f10ed8';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_unregister_success() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
yield db.put({
|
||||
channelID,
|
||||
pushEndpoint: 'https://example.org/update/unregister-success',
|
||||
scope: 'https://example.com/page/unregister-success',
|
||||
originAttributes: '',
|
||||
version: 1,
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
use_webpush: true,
|
||||
}));
|
||||
},
|
||||
onUnregister(request) {
|
||||
equal(request.channelID, channelID, 'Should include the channel ID');
|
||||
equal(request.code, 200, 'Expected manual unregister reason');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'unregister',
|
||||
status: 200,
|
||||
channelID
|
||||
}));
|
||||
unregisterDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let subModifiedPromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionModifiedTopic);
|
||||
|
||||
yield PushService.unregister({
|
||||
scope: 'https://example.com/page/unregister-success',
|
||||
originAttributes: '',
|
||||
});
|
||||
|
||||
let {data: subModifiedScope} = yield subModifiedPromise;
|
||||
equal(subModifiedScope, 'https://example.com/page/unregister-success',
|
||||
'Should fire a subscription modified event after unsubscribing');
|
||||
|
||||
let record = yield db.getByKeyID(channelID);
|
||||
ok(!record, 'Unregister did not remove record');
|
||||
|
||||
yield unregisterPromise;
|
||||
});
|
||||
81
dom/push/test/xpcshell/test_unregister_success_http2.js
Normal file
81
dom/push/test/xpcshell/test_unregister_success_http2.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var prefs;
|
||||
var tlsProfile;
|
||||
var pushEnabled;
|
||||
var pushConnectionEnabled;
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
||||
tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
|
||||
pushEnabled = prefs.getBoolPref("dom.push.enabled");
|
||||
pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
|
||||
|
||||
// Set to allow the cert presented by our H2 server
|
||||
var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
|
||||
prefs.setBoolPref("dom.push.enabled", true);
|
||||
prefs.setBoolPref("dom.push.connection.enabled", true);
|
||||
|
||||
addCertOverride("localhost", serverPort,
|
||||
Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH |
|
||||
Ci.nsICertOverrideService.ERROR_TIME);
|
||||
|
||||
prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_pushUnsubscriptionSuccess() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
yield db.put({
|
||||
subscriptionUri: serverURL + '/subscriptionUnsubscriptionSuccess',
|
||||
pushEndpoint: serverURL + '/pushEndpointUnsubscriptionSuccess',
|
||||
pushReceiptEndpoint: serverURL + '/receiptPushEndpointUnsubscriptionSuccess',
|
||||
scope: 'https://example.com/page/unregister-success',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL,
|
||||
db
|
||||
});
|
||||
|
||||
yield PushService.unregister({
|
||||
scope: 'https://example.com/page/unregister-success',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
let record = yield db.getByKeyID(serverURL + '/subscriptionUnsubscriptionSuccess');
|
||||
ok(!record, 'Unregister did not remove record');
|
||||
|
||||
});
|
||||
|
||||
add_task(function* test_complete() {
|
||||
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
|
||||
prefs.setBoolPref("dom.push.enabled", pushEnabled);
|
||||
prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled);
|
||||
});
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
function listenHandler(metadata, response) {
|
||||
do_check_true(true, "Start listening");
|
||||
httpServer.stop(do_test_finished);
|
||||
response.setHeader("Retry-After", "10");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscriptionNoKey", listenHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'testing.allowInsecureServerURL': true,
|
||||
'http2.retryInterval': 1000,
|
||||
'http2.maxRetries': 2
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(_ => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let record = {
|
||||
subscriptionUri: serverURL + '/subscriptionNoKey',
|
||||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
};
|
||||
|
||||
yield db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic,
|
||||
_ => true);
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db
|
||||
});
|
||||
|
||||
yield notifyPromise;
|
||||
|
||||
let aRecord = yield db.getByKeyID(serverURL + '/subscriptionNoKey');
|
||||
ok(aRecord, 'The record should still be there');
|
||||
ok(aRecord.p256dhPublicKey, 'There should be a public key');
|
||||
ok(aRecord.p256dhPrivateKey, 'There should be a private key');
|
||||
});
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket, PushCrypto} = serviceExports;
|
||||
|
||||
const userAgentID = '4dffd396-6582-471d-8c0c-84f394e9f7db';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID,
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_with_data_enabled() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
let [publicKey, privateKey] = yield PushCrypto.generateKeys();
|
||||
let records = [{
|
||||
channelID: 'eb18f12a-cc42-4f14-accb-3bfc1227f1aa',
|
||||
pushEndpoint: 'https://example.org/push/no-key/1',
|
||||
scope: 'https://example.com/page/1',
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}, {
|
||||
channelID: '0d8886b9-8da1-4778-8f5d-1cf93a877ed6',
|
||||
pushEndpoint: 'https://example.org/push/key',
|
||||
scope: 'https://example.com/page/2',
|
||||
originAttributes: '',
|
||||
p256dhPublicKey: publicKey,
|
||||
p256dhPrivateKey: privateKey,
|
||||
quota: Infinity,
|
||||
}];
|
||||
for (let record of records) {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: "wss://push.example.org/",
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
ok(request.use_webpush,
|
||||
'Should use Web Push if data delivery is enabled');
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: request.uaid,
|
||||
use_webpush: true,
|
||||
}));
|
||||
},
|
||||
onRegister(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'register',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
channelID: request.channelID,
|
||||
pushEndpoint: 'https://example.org/push/new',
|
||||
}));
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let newRecord = yield PushService.register({
|
||||
scope: 'https://example.com/page/3',
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix(
|
||||
{ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inIsolatedMozBrowser: false }),
|
||||
});
|
||||
ok(newRecord.p256dhKey, 'Should generate public keys for new records');
|
||||
|
||||
let record = yield db.getByKeyID('eb18f12a-cc42-4f14-accb-3bfc1227f1aa');
|
||||
ok(record.p256dhPublicKey, 'Should add public key to partial record');
|
||||
ok(record.p256dhPrivateKey, 'Should add private key to partial record');
|
||||
|
||||
record = yield db.getByKeyID('0d8886b9-8da1-4778-8f5d-1cf93a877ed6');
|
||||
deepEqual(record.p256dhPublicKey, publicKey,
|
||||
'Should leave existing public key');
|
||||
deepEqual(record.p256dhPrivateKey, privateKey,
|
||||
'Should leave existing private key');
|
||||
});
|
||||
83
dom/push/test/xpcshell/xpcshell.ini
Normal file
83
dom/push/test/xpcshell/xpcshell.ini
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
[DEFAULT]
|
||||
head = head.js head-http2.js
|
||||
tail =
|
||||
# Push notifications and alarms are currently disabled on Android.
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_clear_forgetAboutSite.js]
|
||||
[test_clear_origin_data.js]
|
||||
[test_crypto.js]
|
||||
[test_drop_expired.js]
|
||||
[test_handler_service.js]
|
||||
support-files = PushServiceHandler.js PushServiceHandler.manifest
|
||||
[test_notification_ack.js]
|
||||
[test_notification_data.js]
|
||||
[test_notification_duplicate.js]
|
||||
[test_notification_error.js]
|
||||
[test_notification_incomplete.js]
|
||||
[test_notification_version_string.js]
|
||||
[test_observer_data.js]
|
||||
[test_observer_remoting.js]
|
||||
|
||||
[test_permissions.js]
|
||||
run-sequentially = This will delete all existing push subscriptions.
|
||||
|
||||
[test_quota_exceeded.js]
|
||||
[test_quota_observer.js]
|
||||
[test_quota_with_notification.js]
|
||||
[test_record.js]
|
||||
[test_register_case.js]
|
||||
[test_register_flush.js]
|
||||
[test_register_invalid_channel.js]
|
||||
[test_register_invalid_endpoint.js]
|
||||
[test_register_invalid_json.js]
|
||||
[test_register_no_id.js]
|
||||
[test_register_request_queue.js]
|
||||
[test_register_rollback.js]
|
||||
[test_register_success.js]
|
||||
[test_register_timeout.js]
|
||||
[test_register_wrong_id.js]
|
||||
[test_register_wrong_type.js]
|
||||
[test_registration_error.js]
|
||||
[test_registration_missing_scope.js]
|
||||
[test_registration_none.js]
|
||||
[test_registration_success.js]
|
||||
[test_unregister_empty_scope.js]
|
||||
[test_unregister_error.js]
|
||||
[test_unregister_invalid_json.js]
|
||||
[test_unregister_not_found.js]
|
||||
[test_unregister_success.js]
|
||||
[test_updateRecordNoEncryptionKeys_ws.js]
|
||||
[test_reconnect_retry.js]
|
||||
[test_retry_ws.js]
|
||||
[test_service_parent.js]
|
||||
[test_service_child.js]
|
||||
[test_startup_error.js]
|
||||
|
||||
#http2 test
|
||||
[test_resubscribe_4xxCode_http2.js]
|
||||
[test_resubscribe_5xxCode_http2.js]
|
||||
[test_resubscribe_listening_for_msg_error_http2.js]
|
||||
[test_register_5xxCode_http2.js]
|
||||
[test_updateRecordNoEncryptionKeys_http2.js]
|
||||
[test_register_success_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_register_error_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_unregister_success_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_notification_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_registration_success_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_registration_error_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_clearAll_successful.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = This will delete all existing push subscriptions.
|
||||
Loading…
Add table
Add a link
Reference in a new issue