Bug 1246341 - Add a test for push event error reporting. r=dragana draft
authorKit Cambridge <kcambridge@mozilla.com>
Thu, 25 Feb 2016 18:38:11 -0800
changeset 335539 658ee86e5af8d10781b89de96b5a22097c8d927d
parent 335538 faf193bf2da05e7dcb458ab2c61b67104e95258a
child 515150 6f3bf1afbe104c3ce9fba6d876891dd687ab09de
push id11804
push userkcambridge@mozilla.com
push dateMon, 29 Feb 2016 19:29:36 +0000
reviewersdragana
bugs1246341
milestone47.0a1
Bug 1246341 - Add a test for push event error reporting. r=dragana MozReview-Commit-ID: LABOJnYtpD5
dom/push/PushNotifier.cpp
dom/push/PushNotifier.h
dom/push/test/error_worker.js
dom/push/test/mochitest.ini
dom/push/test/test_error_reporting.html
--- a/dom/push/PushNotifier.cpp
+++ b/dom/push/PushNotifier.cpp
@@ -59,19 +59,16 @@ PushNotifier::NotifyPush(const nsACStrin
 {
   return NotifyPush(aScope, aPrincipal, aMessageId, Nothing());
 }
 
 NS_IMETHODIMP
 PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
                                        nsIPrincipal* aPrincipal)
 {
-  if (XRE_IsContentProcess()) {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
   nsresult rv;
   if (ShouldNotifyObservers(aPrincipal)) {
     rv = NotifySubscriptionChangeObservers(aScope);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   if (ShouldNotifyWorkers(aPrincipal)) {
@@ -83,19 +80,16 @@ PushNotifier::NotifySubscriptionChange(c
   return NS_OK;
 }
 
 nsresult
 PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
                          const nsAString& aMessageId,
                          const Maybe<nsTArray<uint8_t>>& aData)
 {
-  if (XRE_IsContentProcess()) {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
   nsresult rv;
   if (ShouldNotifyObservers(aPrincipal)) {
     rv = NotifyPushObservers(aScope, aData);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   if (ShouldNotifyWorkers(aPrincipal)) {
--- a/dom/push/PushNotifier.h
+++ b/dom/push/PushNotifier.h
@@ -20,19 +20,18 @@
 namespace mozilla {
 namespace dom {
 
 /**
  * `PushNotifier` implements the `nsIPushNotifier` interface. This service
  * forwards incoming push messages to service workers running in the content
  * process, and emits XPCOM observer notifications for system subscriptions.
  *
- * The XPCOM service can only be used from the main process. Callers running
- * in the content process should use
- * `ServiceWorkerManager::SendPush{SubscriptionChange}Event` directly.
+ * This service exists solely to support `PushService.jsm`. Other callers
+ * should use `ServiceWorkerManager` directly.
  */
 class PushNotifier final : public nsIPushNotifier
 {
 public:
   PushNotifier();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PushNotifier, nsIPushNotifier)
new file mode 100644
--- /dev/null
+++ b/dom/push/test/error_worker.js
@@ -0,0 +1,10 @@
+this.onpush = function(event) {
+  var request = event.data.json();
+  if (request.type == "exception") {
+    throw new Error("Uncaught exception");
+  }
+  if (request.type == "rejection") {
+    event.waitUntil(Promise.reject(
+      new Error("Unhandled rejection")));
+  }
+};
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -2,22 +2,24 @@
 subsuite = push
 support-files =
   worker.js
   push-server.sjs
   frame.html
   webpush.js
   lifetime_worker.js
   test_utils.js
+  error_worker.js
 skip-if = os == "android" || toolkit == "gonk"
 
 [test_has_permissions.html]
 [test_permissions.html]
 [test_register.html]
 [test_multiple_register.html]
 [test_multiple_register_during_service_activation.html]
 [test_unregister.html]
 [test_multiple_register_different_scope.html]
 [test_subscription_change.html]
 [test_data.html]
 # Disabled for too many intermittent failures (bug 1164432)
 #  [test_try_registering_offline_disabled.html]
 [test_serviceworker_lifetime.html]
+[test_error_reporting.html]
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_error_reporting.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1246341: Report message delivery failures to the Push server.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+-->
+<head>
+  <title>Test for Bug 1246341</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/dom/push/test/test_utils.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=1246341">Mozilla Bug 1246341</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+  var pushNotifier = SpecialPowers.Cc["@mozilla.org/push/Notifier;1"]
+                                  .getService(SpecialPowers.Ci.nsIPushNotifier);
+
+  var MOCK_PUSH_SERVICE_CID = SpecialPowers.wrap(
+    SpecialPowers.Components).ID("{ded2b14c-165d-4a9f-b312-a85971e289ee}");
+  var MOCK_PUSH_SERVICE_CONTRACT_ID = "@mozilla.org/push/Service;1";
+
+  var registrar = SpecialPowers.wrap(SpecialPowers.Components).manager.
+    QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar);
+
+  var reporters = new Map();
+  var mockPushService = SpecialPowers.wrapCallbackObject({
+
+    // nsISupports methods
+    QueryInterface(iid) {
+      if (SpecialPowers.wrap(iid).equals(SpecialPowers.Ci.nsISupports) ||
+          SpecialPowers.wrap(iid).equals(SpecialPowers.Ci.nsIPushService) ||
+          SpecialPowers.wrap(iid).equals(SpecialPowers.Ci.nsIPushErrorReporter)) {
+        return this;
+      }
+      throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
+    },
+    createInstance(outer, iid) {
+      if (outer != null) {
+        throw SpecialPowers.Components.results.NS_ERROR_NO_AGGREGATION;
+      }
+      return this.QueryInterface(iid);
+    },
+
+    // nsIPushService methods
+    subscribe(scope, principal, callback) {
+      callback.onPushSubscription(SpecialPowers.Cr.NS_OK, {
+        endpoint: "https://example.com/push",
+        pushCount: 0,
+        lastPush: 0,
+        quota: -1,
+        quotaApplies() {
+          return false;
+        },
+        isExpired() {
+          return false;
+        },
+        getKey(name) {
+          return null;
+        },
+      });
+    },
+    unsubscribe(scope, principal, callback) {
+      callback.onUnsubscribe(SpecialPowers.Cr.NS_OK, true);
+    },
+    getSubscription(scope, principal, callback) {
+      callback.onPushSubscription(SpecialPowers.Cr.NS_OK, null);
+    },
+    clearForDomain(domain, callback) {
+      callback.onClear(SpecialPowers.Cr.NS_OK);
+    },
+
+    // nsIPushErrorReporter methods
+    reportDeliveryError(messageId, reason) {
+      ok(reporters.has(messageId),
+        'Unexpected error reported for message ' + messageId);
+      var resolve = reporters.get(messageId);
+      reporters.delete(messageId);
+      resolve(reason);
+    },
+  });
+
+  SimpleTest.registerCleanupFunction(() => {
+    registrar.unregisterFactory(MOCK_PUSH_SERVICE_CID, mockPushService);
+  });
+
+  var registration;
+  add_task(function* start() {
+    registrar.registerFactory(MOCK_PUSH_SERVICE_CID, "Push Service",
+                              MOCK_PUSH_SERVICE_CONTRACT_ID,
+                              mockPushService);
+    yield setupPrefs();
+    yield setPushPermission(true);
+
+    var url = "error_worker.js" + "?" + (Math.random());
+    registration = yield navigator.serviceWorker.register(url, {scope: "."});
+  });
+
+  var controlledFrame;
+  add_task(function* createControlledIFrame() {
+    controlledFrame = yield injectControlledFrame();
+  });
+
+  var pushSubscription;
+  add_task(function* subscribe() {
+    pushSubscription = yield registration.pushManager.subscribe();
+  });
+
+  var idCounter = 1;
+  function waitForDeliveryError(request) {
+    return new Promise(resolve => {
+      var data = new TextEncoder("utf-8").encode(JSON.stringify(request));
+      var principal = SpecialPowers.wrap(document).nodePrincipal;
+
+      let messageId = "message-" + (idCounter++);
+      reporters.set(messageId, resolve);
+      pushNotifier.notifyPushWithData(registration.scope, principal, messageId,
+                                      data.length, data);
+    });
+  }
+
+  add_task(function* reportErrors() {
+    var reason = yield waitForDeliveryError({ type: "exception" });
+    is(reason, SpecialPowers.Ci.nsIPushErrorReporter.DELIVERY_UNCAUGHT_EXCEPTION,
+      "Should report uncaught exceptions");
+
+    reason = yield waitForDeliveryError({ type: "rejection" });
+    is(reason, SpecialPowers.Ci.nsIPushErrorReporter.DELIVERY_UNHANDLED_REJECTION,
+      "Should report unhandled rejections");
+  });
+
+  add_task(function* unsubscribe() {
+    controlledFrame.remove();
+    yield pushSubscription.unsubscribe();
+  });
+
+  add_task(function* unregister() {
+    yield registration.unregister();
+  });
+
+</script>
+</body>
+</html>
+