Bug 1150812 - Adapt xpcshell test and add mochi tests for WebSocket version. r=nsm, r=mt
authorDragana Damjanovic <dd.mozilla@gmail.com>
Tue, 02 Jun 2015 07:14:00 -0400
changeset 269682 036d9abeb00c1de853fc4b04fc43c9c793038cb3
parent 269681 4d17715232f56f8c44afc560c06f98883f25918c
child 269683 db9050215b8a3ee9c044b9ffce055a399b7d7b34
push id2540
push userwcosta@mozilla.com
push dateWed, 03 Jun 2015 20:55:41 +0000
reviewersnsm, mt
bugs1150812
milestone41.0a1
Bug 1150812 - Adapt xpcshell test and add mochi tests for WebSocket version. r=nsm, r=mt
dom/push/test/mochitest.ini
dom/push/test/test_multiple_register_different_scope.html
dom/push/test/test_multiple_register_during_service_activation.html
dom/push/test/test_try_registering_offline_disabled.html
dom/push/test/xpcshell/test_clearAll_successful.js
dom/push/test/xpcshell/test_notification_ack.js
dom/push/test/xpcshell/test_notification_duplicate.js
dom/push/test/xpcshell/test_notification_error.js
dom/push/test/xpcshell/test_notification_incomplete.js
dom/push/test/xpcshell/test_notification_version_string.js
dom/push/test/xpcshell/test_register_case.js
dom/push/test/xpcshell/test_register_flush.js
dom/push/test/xpcshell/test_register_invalid_channel.js
dom/push/test/xpcshell/test_register_invalid_endpoint.js
dom/push/test/xpcshell/test_register_invalid_json.js
dom/push/test/xpcshell/test_register_no_id.js
dom/push/test/xpcshell/test_register_request_queue.js
dom/push/test/xpcshell/test_register_rollback.js
dom/push/test/xpcshell/test_register_success.js
dom/push/test/xpcshell/test_register_timeout.js
dom/push/test/xpcshell/test_register_wrong_id.js
dom/push/test/xpcshell/test_register_wrong_type.js
dom/push/test/xpcshell/test_registration_error.js
dom/push/test/xpcshell/test_registration_missing_scope.js
dom/push/test/xpcshell/test_registration_none.js
dom/push/test/xpcshell/test_registration_success.js
dom/push/test/xpcshell/test_unregister_empty_scope.js
dom/push/test/xpcshell/test_unregister_error.js
dom/push/test/xpcshell/test_unregister_invalid_json.js
dom/push/test/xpcshell/test_unregister_not_found.js
dom/push/test/xpcshell/test_unregister_success.js
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -7,9 +7,15 @@ support-files =
 
 [test_has_permissions.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_permissions.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_register.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register.html]
-skip-if = os == "android" || toolkit == "gonk"
\ No newline at end of file
+skip-if = os == "android" || toolkit == "gonk"
+[test_multiple_register_during_service_activation.html]
+skip-if = os == "android" || toolkit == "gonk"
+[test_multiple_register_different_scope.html]
+skip-if = os == "android" || toolkit == "gonk"
+[test_try_registering_offline_disabled.html]
+skip-if = os == "android" || toolkit == "gonk"
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_multiple_register_different_scope.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1150812: Test registering for two different scopes.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+-->
+<head>
+  <title>Test for Bug 1150812</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150812">Mozilla Bug 1150812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+  var scopeA = "./a/";
+  var scopeB = "./b/";
+
+  function debug(str) {
+  //  console.log(str + "\n");
+  }
+
+  function registerServiceWorker(scope) {
+    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: scope});
+  }
+
+  function unregister(swr) {
+    return swr.unregister()
+      .then(result => {
+        ok(result, "Unregister should return true.");
+      }, err => {
+        ok(false,"Unregistering the SW failed with " + err + "\n");
+        throw err;
+      });
+  }
+
+  function subscribe(swr) {
+    return swr.pushManager.subscribe()
+      .then(sub => {
+        ok(true, "successful registered for push notification");
+        return sub;
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+
+  function setupMultipleSubscriptions(swr1, swr2) {
+    return Promise.all([
+      subscribe(swr1),
+      subscribe(swr2)
+    ]).then(a => {
+      ok(a[0].endpoint != a[1].endpoint, "setupMultipleSubscriptions - Got different endpoints.");
+      return a;
+    });
+  }
+
+  function getEndpointExpectNull(swr) {
+    return swr.pushManager.getSubscription()
+      .then(pushSubscription => {
+        ok(pushSubscription == null, "getEndpoint should return null when app not subscribed.");
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+  function getEndpoint(swr, results) {
+    return swr.pushManager.getSubscription()
+      .then(sub => {
+        ok((results[0].endpoint == sub.endpoint) ||
+           (results[1].endpoint == sub.endpoint), "getEndpoint - Got the same endpoint back.");
+        return results;
+      }, err => {
+          ok(false, "could not register for push notification");
+          throw err;
+      });
+  }
+
+  function unsubscribe(result) {
+    return result[0].unsubscribe()
+      .then(_ => result[1].unsubscribe());
+  }
+
+  function runTest() {
+    registerServiceWorker(scopeA)
+    .then(swrA =>
+      registerServiceWorker(scopeB)
+        .then(swrB =>
+          getEndpointExpectNull(swrA)
+            .then(_ => getEndpointExpectNull(swrB))
+            .then(_ => setupMultipleSubscriptions(swrA, swrB))
+            .then(results => getEndpoint(swrA, results))
+            .then(results => getEndpoint(swrB, results))
+            .then(results => unsubscribe(results))
+            .then(_ => unregister(swrA))
+            .then(_ => unregister(swrB))
+        )
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    }).then(SimpleTest.finish);
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.push.enabled", true],
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+    ]}, runTest);
+  SpecialPowers.addPermission('push', true, document);
+  SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_multiple_register_during_service_activation.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1150812: If service is in activating or no connection state it can not send
+request immediately, but the requests are queued. This test test the case of
+multiple subscription for the same scope during activation.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+-->
+<head>
+  <title>Test for Bug 1150812</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150812">Mozilla Bug 1150812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+  function debug(str) {
+  //  console.log(str + "\n");
+  }
+
+  function registerServiceWorker() {
+    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."});
+  }
+
+  function unregister(swr) {
+    return swr.unregister()
+      .then(result => {
+        ok(result, "Unregister should return true.");
+      }, err => {
+        dump("Unregistering the SW failed with " + err + "\n");
+        throw err;
+      });
+  }
+
+  function subscribe(swr) {
+    return swr.pushManager.subscribe()
+      .then(sub => {
+        ok(true, "successful registered for push notification");
+        return sub;
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+var defaultServerURL = SpecialPowers.getCharPref("dom.push.serverURL");
+
+  function setupMultipleSubscriptions(swr) {
+
+    // We need to do this to restart service so that a queue will be formed.
+    SpecialPowers.pushPrefEnv({"set": [["dom.push.serverURL", defaultServerURL]]},
+                              null);
+    return Promise.all([
+      subscribe(swr),
+      subscribe(swr)
+    ]).then(a => {
+      ok(a[0].endpoint == a[1].endpoint, "setupMultipleSubscriptions - Got the same endpoint back.");
+      return a[0];
+    }, err => {
+      ok(false, "could not register for push notification");
+      throw err;
+    });
+  }
+
+  function getEndpointExpectNull(swr) {
+    return swr.pushManager.getSubscription()
+      .then(pushSubscription => {
+        ok(pushSubscription == null, "getEndpoint should return null when app not subscribed.");
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+  function unsubscribe(sub) {
+    return sub.unsubscribe();
+  }
+
+  function runTest() {
+    registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => setupMultipleSubscriptions(swr))
+        .then(sub => unsubscribe(sub))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    }).then(SimpleTest.finish);
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.push.enabled", true],
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.push.serverURL", "wss://something.org"]
+    ]}, runTest);
+  SpecialPowers.addPermission('push', true, document);
+  SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_try_registering_offline_disabled.html
@@ -0,0 +1,300 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1150812: Try to register when serviced if offline or connection is disabled.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+-->
+<head>
+  <title>Test for Bug 1150812</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150812">Mozilla Bug 1150812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+  function debug(str) {
+  //  console.log(str + "\n");
+  }
+
+  function registerServiceWorker() {
+    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."});
+  }
+
+  function unregister(swr) {
+    return swr.unregister()
+      .then(result => {
+        ok(result, "Unregister should return true.");
+      }, err => {
+        dump("Unregistering the SW failed with " + err + "\n");
+        throw err;
+      });
+  }
+
+  function subscribe(swr) {
+    return swr.pushManager.subscribe()
+      .then(sub => {
+        ok(true, "successful registered for push notification");
+        return sub;
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+  function subscribeFail(swr) {
+    return new Promise((res, rej) => {
+      swr.pushManager.subscribe()
+        .then(sub => {
+          ok(false, "successful registered for push notification");
+          throw "Should fail";
+        }, err => {
+          ok(true, "could not register for push notification");
+          res(swr);
+        });
+    });
+  }
+
+  function getEndpointExpectNull(swr) {
+    return swr.pushManager.getSubscription()
+      .then(pushSubscription => {
+        ok(pushSubscription == null, "getEndpoint should return null when app not subscribed.");
+      }, err => {
+        ok(false, "could not register for push notification");
+        throw err;
+      });
+  }
+
+  function getEndpoint(swr, subOld) {
+    return swr.pushManager.getSubscription()
+      .then(sub => {
+        ok(subOld.endpoint == sub.endpoint, "getEndpoint - Got the same endpoint back.");
+        return sub;
+      }, err => {
+          ok(false, "could not register for push notification");
+          throw err;
+      });
+  }
+
+  function offlineObserver(res) {
+    this._res = res;
+  }
+  offlineObserver.prototype = {
+    _res: null,
+
+    observe: function(subject, topic, data) {
+      debug("observe: " + subject + " " + topic + " " + data);
+      if (topic === "network:offline-status-changed") {
+        var obsService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                           .getService(SpecialPowers.Ci.nsIObserverService);
+        obsService.removeObserver(this, topic);
+        this._res(null);
+      }
+    }
+  }
+
+  function changeOfflineState(offline) {
+    return new Promise(function(res, rej) {
+      var obsService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                         .getService(SpecialPowers.Ci.nsIObserverService);
+      obsService.addObserver(SpecialPowers.wrapCallbackObject(new offlineObserver(res)),
+                             "network:offline-status-changed",
+                             false);
+      var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                        .getService(SpecialPowers.Ci.nsIIOService);
+      ioService.offline = offline;
+    });
+  }
+
+  function changePushServerConnectionEnabled(enable) {
+    debug("changePushServerConnectionEnabled");
+    SpecialPowers.pushPrefEnv({"set": [["dom.push.connection.enabled", enable]]},
+                              null);
+  }
+
+  function unsubscribe(sub) {
+    return sub.unsubscribe()
+      .then(_ => {ok(true, "Unsubscribed!");});
+  }
+
+  // go offline then go online
+  function runTest1() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changeOfflineState(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(false))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  // disable - enable push connection.
+  function runTest2() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changePushServerConnectionEnabled(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(true))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  // go offline - disable - enable - go online
+  function runTest3() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changeOfflineState(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(false))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  // disable - offline - online - enable.
+  function runTest4() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changePushServerConnectionEnabled(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(true))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  // go offline - disable - go online - enable
+  function runTest5() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changeOfflineState(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(true))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  // disable - go offline - enable - go online.
+  function runTest6() {
+    return registerServiceWorker()
+    .then(swr =>
+      getEndpointExpectNull(swr)
+        .then(_ => changePushServerConnectionEnabled(false))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changePushServerConnectionEnabled(true))
+        .then(_ => subscribeFail(swr))
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => changeOfflineState(false))
+        .then(_ => subscribe(swr))
+        .then(sub => getEndpoint(swr, sub)
+          .then(sub => unsubscribe(sub))
+        )
+        .then(_ => getEndpointExpectNull(swr))
+        .then(_ => unregister(swr))
+    )
+    .catch(err => {
+      ok(false, "Some test failed with error " + err);
+    })
+  }
+
+  function runTest() {
+    runTest1()
+    .then(_ => runTest2())
+    .then(_ => runTest3())
+    .then(_ => runTest4())
+    .then(_ => runTest5())
+    .then(_ => runTest6())
+    .then(SimpleTest.finish);
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.push.enabled", true],
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+    ]}, runTest);
+  SpecialPowers.addPermission('push', true, document);
+  SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
--- a/dom/push/test/xpcshell/test_clearAll_successful.js
+++ b/dom/push/test/xpcshell/test_clearAll_successful.js
@@ -1,35 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 const channelID = 'db0a7021-ec2d-4bd3-8802-7a6966f10ed8';
 
 function run_test() {
   do_get_profile();
   setPrefs();
   run_next_test();
 }
 
 add_task(function* test_unregister_success() {
-  let db = new PushDB();
+  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',
     version: 1
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_notification_ack.js
+++ b/dom/push/test/xpcshell/test_notification_ack.js
@@ -1,30 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 let userAgentID = '5ab1d1df-7a3d-4024-a469-b9e1bb399fad';
 
 function run_test() {
   do_get_profile();
   setPrefs({userAgentID});
   disableServiceWorkerEvents(
     'https://example.org/1',
     'https://example.org/2',
     'https://example.org/3'
   );
   run_next_test();
 }
 
 add_task(function* test_notification_ack() {
-  let db = new PushDB();
+  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',
     version: 1
   }, {
     channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
@@ -45,16 +45,17 @@ add_task(function* test_notification_ack
     promiseObserverNotification('push-notification'),
     promiseObserverNotification('push-notification'),
     promiseObserverNotification('push-notification')
   ]);
 
   let acks = 0;
   let ackDefer = Promise.defer();
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           equal(request.uaid, userAgentID,
             'Should send matching device IDs in handshake');
           deepEqual(request.channelIDs.sort(), [
--- a/dom/push/test/xpcshell/test_notification_duplicate.js
+++ b/dom/push/test/xpcshell/test_notification_duplicate.js
@@ -1,28 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.com/1',
     'https://example.com/2'
   );
   run_next_test();
 }
 
 // Should acknowledge duplicate notifications, but not notify apps.
 add_task(function* test_notification_duplicate() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '8d2d9400-3597-4c5a-8a38-c546b0043bcc',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/1',
     version: 2
   }, {
     channelID: '27d1e393-03ef-4c72-a5e6-9e890dfccad0',
@@ -35,16 +35,17 @@ add_task(function* test_notification_dup
   }
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let acks = 0;
   let ackDefer = Promise.defer();
   let ackDone = after(2, ackDefer.resolve);
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_notification_error.js
+++ b/dom/push/test/xpcshell/test_notification_error.js
@@ -1,28 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.com/a',
     'https://example.com/b',
     'https://example.com/c'
   );
   run_next_test();
 }
 
 add_task(function* test_notification_error() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: 'f04f1e46-9139-4826-b2d1-9411b0821283',
     pushEndpoint: 'https://example.org/update/success-1',
     scope: 'https://example.com/a',
     version: 1
   }, {
     channelID: '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
@@ -48,16 +48,17 @@ add_task(function* test_notification_err
       'push-notification',
       (subject, data) => data == 'https://example.com/c'
     )
   ]);
 
   let ackDefer = Promise.defer();
   let ackDone = after(records.length, ackDefer.resolve);
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db: makeStub(db, {
       getByChannelID(prev, channelID) {
         if (channelID == '3c3930ba-44de-40dc-a7ca-8a133ec1a866') {
           return Promise.reject('splines not reticulated');
         }
         return prev.call(this, channelID);
       }
--- a/dom/push/test/xpcshell/test_notification_incomplete.js
+++ b/dom/push/test/xpcshell/test_notification_incomplete.js
@@ -1,29 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.com/page/1',
     'https://example.com/page/2',
     'https://example.com/page/3',
     'https://example.com/page/4'
   );
   run_next_test();
 }
 
 add_task(function* test_notification_incomplete() {
-  let db = new PushDB();
+  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
   }, {
     channelID: '3ad1ed95-d37a-4d88-950f-22cbe2e240d7',
@@ -47,21 +47,22 @@ add_task(function* test_notification_inc
 
   Services.obs.addObserver(function observe(subject, topic, data) {
     ok(false, 'Should not deliver malformed updates');
   }, 'push-notification', false);
 
   let notificationDefer = Promise.defer();
   let notificationDone = after(2, notificationDefer.resolve);
   let prevHandler = PushService._handleNotificationReply;
-  PushService._handleNotificationReply = function _handleNotificationReply() {
+  PushServiceWebSocket._handleNotificationReply = function _handleNotificationReply() {
     notificationDone();
     return prevHandler.apply(this, arguments);
   };
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_notification_version_string.js
+++ b/dom/push/test/xpcshell/test_notification_version_string.js
@@ -1,38 +1,39 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.net/case'
   );
   run_next_test();
 }
 
 add_task(function* test_notification_version_string() {
-  let db = new PushDB();
+  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',
     version: 2
   });
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_register_case.js
+++ b/dom/push/test/xpcshell/test_register_case.js
@@ -1,31 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 const userAgentID = '1760b1f5-c3ba-40e3-9344-adef7c18ab12';
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.net/case'
   );
   run_next_test();
 }
 
 add_task(function* test_register_case() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'HELLO',
             uaid: userAgentID,
--- a/dom/push/test/xpcshell/test_register_flush.js
+++ b/dom/push/test/xpcshell/test_register_flush.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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,
@@ -18,31 +18,32 @@ function run_test() {
   disableServiceWorkerEvents(
     'https://example.com/page/1',
     'https://example.com/page/2'
   );
   run_next_test();
 }
 
 add_task(function* test_register_flush() {
-  let db = new PushDB();
+  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',
     version: 2
   };
   yield db.put(record);
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   let ackDone = after(2, ackDefer.resolve);
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_register_invalid_channel.js
+++ b/dom/push/test/xpcshell/test_register_invalid_channel.js
@@ -1,33 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 const userAgentID = '52b2b04c-b6cc-42c6-abdf-bef9cbdbea00';
 const channelID = 'cafed00d';
 
 function run_test() {
   do_get_profile();
   setPrefs();
   disableServiceWorkerEvents(
     'https://example.com/invalid-channel'
   );
   run_next_test();
 }
 
 add_task(function* test_register_invalid_channel() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             uaid: userAgentID,
--- a/dom/push/test/xpcshell/test_register_invalid_endpoint.js
+++ b/dom/push/test/xpcshell/test_register_invalid_endpoint.js
@@ -1,33 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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();
   disableServiceWorkerEvents(
     'https://example.net/page/invalid-endpoint'
   );
   run_next_test();
 }
 
 add_task(function* test_register_invalid_endpoint() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
@@ -46,16 +47,16 @@ add_task(function* test_register_invalid
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register(
       'https://example.net/page/invalid-endpoint'),
     function(error) {
-      return error && error.contains('Invalid pushEndpoint');
+      return error && error.includes('Invalid pushEndpoint');
     },
     'Wrong error for invalid endpoint'
   );
 
   let record = yield db.getByChannelID(channelID);
   ok(!record, 'Should not store records with invalid endpoints');
 });
--- a/dom/push/test/xpcshell/test_register_invalid_json.js
+++ b/dom/push/test/xpcshell/test_register_invalid_json.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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,
@@ -20,18 +20,19 @@ function run_test() {
   run_next_test();
 }
 
 add_task(function* test_register_invalid_json() {
   let helloDefer = Promise.defer();
   let helloDone = after(2, helloDefer.resolve);
   let registers = 0;
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: userAgentID
--- a/dom/push/test/xpcshell/test_register_no_id.js
+++ b/dom/push/test/xpcshell/test_register_no_id.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 let userAgentID = '9a2f9efe-2ebb-4bcb-a5d9-9e2b73d30afe';
 let channelID = '264c2ba0-f6db-4e84-acdb-bd225b62d9e3';
 
 function run_test() {
   do_get_profile();
   setPrefs({
     userAgentID,
@@ -21,18 +21,19 @@ function run_test() {
   run_next_test();
 }
 
 add_task(function* test_register_no_id() {
   let registers = 0;
   let helloDefer = Promise.defer();
   let helloDone = after(2, helloDefer.resolve);
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: userAgentID
--- a/dom/push/test/xpcshell/test_register_request_queue.js
+++ b/dom/push/test/xpcshell/test_register_request_queue.js
@@ -1,41 +1,42 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 function run_test() {
   do_get_profile();
   setPrefs({
     requestTimeout: 1000,
     retryBaseInterval: 150
   });
   disableServiceWorkerEvents(
     'https://example.com/page/1'
   );
   run_next_test();
 }
 
 add_task(function* test_register_request_queue() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   let helloDefer = Promise.defer();
   let onHello = after(2, function onHello(request) {
     this.serverSendMsg(JSON.stringify({
       messageType: 'hello',
       status: 200,
       uaid: '54b08a9e-59c6-4ed7-bb54-f4fd60d6f606'
     }));
     helloDefer.resolve();
   });
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello,
         onRegister() {
           ok(false, 'Should cancel timed-out requests');
         }
--- a/dom/push/test/xpcshell/test_register_rollback.js
+++ b/dom/push/test/xpcshell/test_register_rollback.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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,
@@ -17,24 +17,25 @@ function run_test() {
   });
   disableServiceWorkerEvents(
     'https://example.com/storage-error'
   );
   run_next_test();
 }
 
 add_task(function* test_register_rollback() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   let handshakes = 0;
   let registers = 0;
   let unregisterDefer = Promise.defer();
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db: makeStub(db, {
       put(prev, record) {
         return Promise.reject('universe has imploded');
       }
     }),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
--- a/dom/push/test/xpcshell/test_register_success.js
+++ b/dom/push/test/xpcshell/test_register_success.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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,
@@ -17,21 +17,22 @@ function run_test() {
   });
   disableServiceWorkerEvents(
     'https://example.org/1'
   );
   run_next_test();
 }
 
 add_task(function* test_register_success() {
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     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({
--- a/dom/push/test/xpcshell/test_register_timeout.js
+++ b/dom/push/test/xpcshell/test_register_timeout.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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,
@@ -20,21 +20,22 @@ function run_test() {
   run_next_test();
 }
 
 add_task(function* test_register_timeout() {
   let handshakes = 0;
   let timeoutDefer = Promise.defer();
   let registers = 0;
 
-  let db = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
-  PushService._generateID = () => channelID;
+  PushServiceWebSocket._generateID = () => channelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           switch (handshakes) {
           case 0:
             equal(request.uaid, null, 'Should not include device ID');
--- a/dom/push/test/xpcshell/test_register_wrong_id.js
+++ b/dom/push/test/xpcshell/test_register_wrong_id.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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({
@@ -23,18 +23,19 @@ function run_test() {
 }
 
 add_task(function* test_register_wrong_id() {
   // Should reconnect after the register request times out.
   let registers = 0;
   let helloDefer = Promise.defer();
   let helloDone = after(2, helloDefer.resolve);
 
-  PushService._generateID = () => clientChannelID;
+  PushServiceWebSocket._generateID = () => clientChannelID;
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: userAgentID
--- a/dom/push/test/xpcshell/test_register_wrong_type.js
+++ b/dom/push/test/xpcshell/test_register_wrong_type.js
@@ -21,16 +21,17 @@ function run_test() {
 
 add_task(function* test_register_wrong_type() {
   let registers = 0;
   let helloDefer = Promise.defer();
   let helloDone = after(2, helloDefer.resolve);
 
   PushService._generateID = () => '1234';
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: userAgentID
--- a/dom/push/test/xpcshell/test_registration_error.js
+++ b/dom/push/test/xpcshell/test_registration_error.js
@@ -1,32 +1,33 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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 = new PushDB();
+  let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db: makeStub(db, {
       getByScope(prev, scope) {
-        return Promise.reject('oops');
+        return Promise.reject('Database error');
       }
     }),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   yield rejects(
--- a/dom/push/test/xpcshell/test_registration_missing_scope.js
+++ b/dom/push/test/xpcshell/test_registration_missing_scope.js
@@ -8,21 +8,22 @@ const {PushDB, PushService} = serviceExp
 function run_test() {
   do_get_profile();
   setPrefs();
   run_next_test();
 }
 
 add_task(function* test_registration_missing_scope() {
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
   yield rejects(
     PushNotificationService.registration(''),
     function(error) {
-      return error == 'Database error';
+      return error.error == 'Database error';
     },
     'Record missing page and manifest URLs'
   );
 });
--- a/dom/push/test/xpcshell/test_registration_none.js
+++ b/dom/push/test/xpcshell/test_registration_none.js
@@ -11,16 +11,17 @@ 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/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   let registration = yield PushNotificationService.registration(
     'https://example.net/1');
--- a/dom/push/test/xpcshell/test_registration_success.js
+++ b/dom/push/test/xpcshell/test_registration_success.js
@@ -1,25 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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 = new PushDB();
+  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',
     version: 5
   }, {
     channelID: 'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
@@ -32,16 +32,17 @@ add_task(function* test_registration_suc
     scope: 'https://example.org/c',
     version: 15
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           equal(request.uaid, userAgentID, 'Wrong device ID in handshake');
           deepEqual(request.channelIDs.sort(), [
             'b1cf38c9-6836-4d29-8a30-a3e98d59b728',
             'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
--- a/dom/push/test/xpcshell/test_unregister_empty_scope.js
+++ b/dom/push/test/xpcshell/test_unregister_empty_scope.js
@@ -8,16 +8,17 @@ const {PushDB, PushService} = serviceExp
 function run_test() {
   do_get_profile();
   setPrefs();
   run_next_test();
 }
 
 add_task(function* test_unregister_empty_scope() {
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: '5619557c-86fe-4711-8078-d1fd6987aef7'
@@ -25,13 +26,13 @@ add_task(function* test_unregister_empty
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.unregister(''),
     function(error) {
-      return error == 'NotFoundError';
+      return error.error == 'NotFoundError';
     },
     'Wrong error for empty endpoint'
   );
 });
--- a/dom/push/test/xpcshell/test_unregister_error.js
+++ b/dom/push/test/xpcshell/test_unregister_error.js
@@ -1,35 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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 = new PushDB();
+  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',
     version: 1
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_unregister_invalid_json.js
+++ b/dom/push/test/xpcshell/test_unregister_invalid_json.js
@@ -1,29 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+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 = new PushDB();
+  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',
     version: 1
   }, {
     channelID: '057caa8f-9b99-47ff-891c-adad18ce603e',
@@ -33,16 +33,17 @@ add_task(function* test_unregister_inval
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let unregisterDefer = Promise.defer();
   let unregisterDone = after(2, unregisterDefer.resolve);
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
--- a/dom/push/test/xpcshell/test_unregister_not_found.js
+++ b/dom/push/test/xpcshell/test_unregister_not_found.js
@@ -8,16 +8,17 @@ const {PushDB, PushService} = serviceExp
 function run_test() {
   do_get_profile();
   setPrefs();
   run_next_test();
 }
 
 add_task(function* test_unregister_not_found() {
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: 'f074ed80-d479-44fa-ba65-792104a79ea9'
--- a/dom/push/test/xpcshell/test_unregister_success.js
+++ b/dom/push/test/xpcshell/test_unregister_success.js
@@ -1,35 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 'use strict';
 
-const {PushDB, PushService} = serviceExports;
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
 
 const channelID = 'db0a7021-ec2d-4bd3-8802-7a6966f10ed8';
 
 function run_test() {
   do_get_profile();
   setPrefs();
   run_next_test();
 }
 
 add_task(function* test_unregister_success() {
-  let db = new PushDB();
+  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',
     version: 1
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
+    serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,