Bug 1246341 - Add a test for push event error reporting. r=dragana
authorKit Cambridge <kcambridge@mozilla.com>
Mon, 28 Mar 2016 11:40:58 -0700
changeset 290952 210366388d9047c28ee2180a50c1bb193f40f069
parent 290951 7aefa268e50cecfece06621123e5f6f2bd5e57ff
child 290953 ca8b229e4f20c34f05b41955c2128a4388f87e9b
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1246341
milestone48.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/mockpushserviceparent.js
dom/push/test/test_error_reporting.html
dom/push/test/test_utils.js
--- a/dom/push/PushNotifier.cpp
+++ b/dom/push/PushNotifier.cpp
@@ -60,19 +60,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)) {
@@ -84,19 +81,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
@@ -24,19 +24,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,20 +2,22 @@
 skip-if = os == "android" || toolkit == "gonk"
 support-files =
   worker.js
   frame.html
   webpush.js
   lifetime_worker.js
   test_utils.js
   mockpushserviceparent.js
+  error_worker.js
 
 [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]
 [test_try_registering_offline_disabled.html]
 [test_serviceworker_lifetime.html]
+[test_error_reporting.html]
--- a/dom/push/test/mockpushserviceparent.js
+++ b/dom/push/test/mockpushserviceparent.js
@@ -152,16 +152,23 @@ var MockService = {
 
   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;
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_error_reporting.html
@@ -0,0 +1,89 @@
+<!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 reporters = new Map();
+
+  var registration;
+  add_task(function* start() {
+    yield setupPrefsAndReplaceService({
+      reportDeliveryError(messageId, reason) {
+        ok(reporters.has(messageId),
+          'Unexpected error reported for message ' + messageId);
+        var resolve = reporters.get(messageId);
+        reporters.delete(messageId);
+        resolve(reason);
+      },
+    });
+    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 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();
+  });
+
+  add_task(function* unregister() {
+    yield registration.unregister();
+  });
+
+</script>
+</body>
+</html>
+
--- a/dom/push/test/test_utils.js
+++ b/dom/push/test/test_utils.js
@@ -6,16 +6,19 @@
 
   /**
    * Replaces `PushService.jsm` with a mock implementation that handles requests
    * from the DOM API. This allows tests to simulate local errors and error
    * reporting, bypassing the `PushService.jsm` machinery.
    */
   function replacePushService(mockService) {
     chromeScript.sendSyncMessage("service-replace");
+    chromeScript.addMessageListener("service-delivery-error", function(msg) {
+      mockService.reportDeliveryError(msg.messageId, msg.reason);
+    });
     chromeScript.addMessageListener("service-request", function(msg) {
       let promise;
       try {
         let handler = mockService[msg.name];
         promise = Promise.resolve(handler(msg.params));
       } catch (error) {
         promise = Promise.reject(error);
       }