summaryrefslogtreecommitdiffstats
path: root/dom/push/test/mockpushserviceparent.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/push/test/mockpushserviceparent.js')
-rw-r--r--dom/push/test/mockpushserviceparent.js168
1 files changed, 168 insertions, 0 deletions
diff --git a/dom/push/test/mockpushserviceparent.js b/dom/push/test/mockpushserviceparent.js
new file mode 100644
index 000000000..78cf246ee
--- /dev/null
+++ b/dom/push/test/mockpushserviceparent.js
@@ -0,0 +1,168 @@
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/**
+ * 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);
+}
+
+/**
+ * Minimal implementation of a mock WebSocket connect to be used with
+ * PushService. Forwards and receive messages from the implementation
+ * that lives in the content process.
+ */
+function MockWebSocketParent(originalURI) {
+ this._originalURI = originalURI;
+}
+
+MockWebSocketParent.prototype = {
+ _originalURI: 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));
+ },
+
+ sendMsg(msg) {
+ sendAsyncMessage("socket-client-msg", msg);
+ },
+
+ close() {
+ waterfall(() => this._listener.onStop(this._context, Cr.NS_OK));
+ },
+
+ serverSendMsg(msg) {
+ waterfall(() => this._listener.onMessageAvailable(this._context, msg),
+ () => this._listener.onAcknowledge(this._context, 0));
+ },
+};
+
+var pushService = Cc["@mozilla.org/push/Service;1"].
+ getService(Ci.nsIPushService).
+ wrappedJSObject;
+
+var mockSocket;
+var serverMsgs = [];
+
+addMessageListener("socket-setup", function () {
+ pushService.replaceServiceBackend({
+ serverURI: "wss://push.example.org/",
+ makeWebSocket(uri) {
+ mockSocket = new MockWebSocketParent(uri);
+ while (serverMsgs.length > 0) {
+ let msg = serverMsgs.shift();
+ mockSocket.serverSendMsg(msg);
+ }
+ return mockSocket;
+ }
+ });
+});
+
+addMessageListener("socket-teardown", function (msg) {
+ pushService.restoreServiceBackend().then(_ => {
+ serverMsgs.length = 0;
+ if (mockSocket) {
+ mockSocket.close();
+ mockSocket = null;
+ }
+ sendAsyncMessage("socket-server-teardown");
+ }).catch(error => {
+ Cu.reportError(`Error restoring service backend: ${error}`);
+ })
+});
+
+addMessageListener("socket-server-msg", function (msg) {
+ if (mockSocket) {
+ mockSocket.serverSendMsg(msg);
+ } else {
+ serverMsgs.push(msg);
+ }
+});
+
+var MockService = {
+ requestID: 1,
+ resolvers: new Map(),
+
+ sendRequest(name, params) {
+ return new Promise((resolve, reject) => {
+ let id = this.requestID++;
+ this.resolvers.set(id, { resolve, reject });
+ sendAsyncMessage("service-request", {
+ name: name,
+ id: id,
+ params: params,
+ });
+ });
+ },
+
+ handleResponse(response) {
+ if (!this.resolvers.has(response.id)) {
+ Cu.reportError(`Unexpected response for request ${response.id}`);
+ return;
+ }
+ let resolver = this.resolvers.get(response.id);
+ this.resolvers.delete(response.id);
+ if (response.error) {
+ resolver.reject(response.error);
+ } else {
+ resolver.resolve(response.result);
+ }
+ },
+
+ init() {},
+
+ register(pageRecord) {
+ return this.sendRequest("register", pageRecord);
+ },
+
+ registration(pageRecord) {
+ return this.sendRequest("registration", pageRecord);
+ },
+
+ unregister(pageRecord) {
+ return this.sendRequest("unregister", pageRecord);
+ },
+
+ reportDeliveryError(messageId, reason) {
+ sendAsyncMessage("service-delivery-error", {
+ messageId: messageId,
+ reason: reason,
+ });
+ },
+};
+
+addMessageListener("service-replace", function () {
+ pushService.service = MockService;
+});
+
+addMessageListener("service-restore", function () {
+ pushService.service = null;
+});
+
+addMessageListener("service-response", function (response) {
+ MockService.handleResponse(response);
+});