Bug 1265841 - Implement the `notificationclose` service worker event. r=wchen,baku
authorKit Cambridge <kcambridge@mozilla.com>
Tue, 19 Apr 2016 22:04:09 -0700
changeset 338865 253aa0a9809b9c3f7a4e02a3beb5baca3fc5fe13
parent 338864 ca252a2e21711cf5dd7843de01e04b6143d8900e
child 338866 38f8d1092552919b3797d2975337e56602614b9b
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen, baku
bugs1265841
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1265841 - Implement the `notificationclose` service worker event. r=wchen,baku MozReview-Commit-ID: EQfCbQKqn9H
dom/base/nsGkAtomList.h
dom/interfaces/base/nsIServiceWorkerManager.idl
dom/notification/Notification.cpp
dom/webidl/NotificationEvent.webidl
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/ServiceWorkerPrivate.h
dom/workers/WorkerScope.h
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/notificationclose.html
dom/workers/test/serviceworkers/notificationclose.js
dom/workers/test/serviceworkers/test_notificationclose.html
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -851,16 +851,17 @@ GK_ATOM(onmoznetworkdownload, "onmoznetw
 GK_ATOM(onmapfolderlistingreq, "onmapfolderlistingreq")
 GK_ATOM(onmapmessageslistingreq, "onmapmessageslistingreq")
 GK_ATOM(onmapgetmessagereq, "onmapgetmessagereq")
 GK_ATOM(onmapsetmessagestatusreq, "onmapsetmessagestatusreq")
 GK_ATOM(onmapsendmessagereq, "onmapsendmessagereq")
 GK_ATOM(onmapmessageupdatereq, "onmapmessageupdatereq")
 GK_ATOM(onnewrdsgroup, "onnewrdsgroup")
 GK_ATOM(onnotificationclick, "onnotificationclick")
+GK_ATOM(onnotificationclose, "onnotificationclose")
 GK_ATOM(onnoupdate, "onnoupdate")
 GK_ATOM(onobexpasswordreq, "onobexpasswordreq")
 GK_ATOM(onobsolete, "onobsolete")
 GK_ATOM(ononline, "ononline")
 GK_ATOM(onoffline, "onoffline")
 GK_ATOM(onopen, "onopen")
 GK_ATOM(onorientationchange, "onorientationchange")
 GK_ATOM(onotastatuschange, "onotastatuschange")
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -180,16 +180,29 @@ interface nsIServiceWorkerManager : nsIS
                                   in AString aTitle,
                                   in AString aDir,
                                   in AString aLang,
                                   in AString aBody,
                                   in AString aTag,
                                   in AString aIcon,
                                   in AString aData,
                                   in AString aBehavior);
+
+  void sendNotificationCloseEvent(in ACString aOriginSuffix,
+                                  in ACString scope,
+                                  in AString aID,
+                                  in AString aTitle,
+                                  in AString aDir,
+                                  in AString aLang,
+                                  in AString aBody,
+                                  in AString aTag,
+                                  in AString aIcon,
+                                  in AString aData,
+                                  in AString aBehavior);
+
   [optional_argc] void sendPushEvent(in ACString aOriginAttributes,
                                      in ACString aScope,
                                      [optional] in uint32_t aDataLength,
                                      [optional, array, size_is(aDataLength)] in uint8_t aDataBytes);
   void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
                                        in ACString scope);
 
   void addListener(in nsIServiceWorkerManagerListener aListener);
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -1585,55 +1585,71 @@ WorkerNotificationObserver::Observe(nsIS
 
 NS_IMETHODIMP
 ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
                                            const char* aTopic,
                                            const char16_t* aData)
 {
   AssertIsOnMainThread();
 
+  nsAutoCString originSuffix;
+  nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIServiceWorkerManager> swm =
+    mozilla::services::GetServiceWorkerManager();
+  if (NS_WARN_IF(!swm)) {
+    return NS_ERROR_FAILURE;
+  }
+
   if (!strcmp("alertclickcallback", aTopic)) {
-    nsAutoCString originSuffix;
-    nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    nsCOMPtr<nsIServiceWorkerManager> swm =
-      mozilla::services::GetServiceWorkerManager();
-
-    if (swm) {
-      swm->SendNotificationClickEvent(originSuffix,
-                                      NS_ConvertUTF16toUTF8(mScope),
-                                      mID,
-                                      mTitle,
-                                      mDir,
-                                      mLang,
-                                      mBody,
-                                      mTag,
-                                      mIcon,
-                                      mData,
-                                      mBehavior);
-    }
+    rv = swm->SendNotificationClickEvent(originSuffix,
+                                         NS_ConvertUTF16toUTF8(mScope),
+                                         mID,
+                                         mTitle,
+                                         mDir,
+                                         mLang,
+                                         mBody,
+                                         mTag,
+                                         mIcon,
+                                         mData,
+                                         mBehavior);
+    Unused << NS_WARN_IF(NS_FAILED(rv));
     return NS_OK;
   }
 
   if (!strcmp("alertfinished", aTopic)) {
     nsString origin;
     nsresult rv = Notification::GetOrigin(mPrincipal, origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Remove closed or dismissed persistent notifications.
     nsCOMPtr<nsINotificationStorage> notificationStorage =
       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
     if (notificationStorage) {
       notificationStorage->Delete(origin, mID);
     }
+
+    rv = swm->SendNotificationCloseEvent(originSuffix,
+                                         NS_ConvertUTF16toUTF8(mScope),
+                                         mID,
+                                         mTitle,
+                                         mDir,
+                                         mLang,
+                                         mBody,
+                                         mTag,
+                                         mIcon,
+                                         mData,
+                                         mBehavior);
+    Unused << NS_WARN_IF(NS_FAILED(rv));
+    return NS_OK;
   }
 
   return NS_OK;
 }
 
 bool
 Notification::IsInPrivateBrowsing()
 {
--- a/dom/webidl/NotificationEvent.webidl
+++ b/dom/webidl/NotificationEvent.webidl
@@ -18,9 +18,10 @@ interface NotificationEvent : Extendable
 };
 
 dictionary NotificationEventInit : ExtendableEventInit {
   required Notification notification;
 };
 
 partial interface ServiceWorkerGlobalScope {
   attribute EventHandler onnotificationclick;
+  attribute EventHandler onnotificationclose;
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -954,44 +954,81 @@ ServiceWorkerManager::SendPushSubscripti
   ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
   if (!info) {
     return NS_ERROR_FAILURE;
   }
   return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
 #endif
 }
 
+nsresult
+ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
+                                            const nsACString& aOriginSuffix,
+                                            const nsACString& aScope,
+                                            const nsAString& aID,
+                                            const nsAString& aTitle,
+                                            const nsAString& aDir,
+                                            const nsAString& aLang,
+                                            const nsAString& aBody,
+                                            const nsAString& aTag,
+                                            const nsAString& aIcon,
+                                            const nsAString& aData,
+                                            const nsAString& aBehavior)
+{
+  PrincipalOriginAttributes attrs;
+  if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
+  if (!info) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
+  return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
+                                              aLang, aBody, aTag,
+                                              aIcon, aData, aBehavior,
+                                              NS_ConvertUTF8toUTF16(aScope));
+}
+
 NS_IMETHODIMP
 ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
                                                  const nsACString& aScope,
                                                  const nsAString& aID,
                                                  const nsAString& aTitle,
                                                  const nsAString& aDir,
                                                  const nsAString& aLang,
                                                  const nsAString& aBody,
                                                  const nsAString& aTag,
                                                  const nsAString& aIcon,
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
-  PrincipalOriginAttributes attrs;
-  if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
-  if (!info) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
-  return workerPrivate->SendNotificationClickEvent(aID, aTitle, aDir,
-                                                   aLang, aBody, aTag,
-                                                   aIcon, aData, aBehavior,
-                                                   NS_ConvertUTF8toUTF16(aScope));
+  return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
+                               aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
+                               aBody, aTag, aIcon, aData, aBehavior);
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
+                                                 const nsACString& aScope,
+                                                 const nsAString& aID,
+                                                 const nsAString& aTitle,
+                                                 const nsAString& aDir,
+                                                 const nsAString& aLang,
+                                                 const nsAString& aBody,
+                                                 const nsAString& aTag,
+                                                 const nsAString& aIcon,
+                                                 const nsAString& aData,
+                                                 const nsAString& aBehavior)
+{
+  return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
+                               aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
+                               aBody, aTag, aIcon, aData, aBehavior);
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetReadyPromise(mozIDOMWindow* aWindow,
                                       nsISupports** aPromise)
 {
   AssertIsOnMainThread();
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -426,15 +426,29 @@ private:
   void
   ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
   void
   UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
   void
   MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+  nsresult
+  SendNotificationEvent(const nsAString& aEventName,
+                        const nsACString& aOriginSuffix,
+                        const nsACString& aScope,
+                        const nsAString& aID,
+                        const nsAString& aTitle,
+                        const nsAString& aDir,
+                        const nsAString& aLang,
+                        const nsAString& aBody,
+                        const nsAString& aTag,
+                        const nsAString& aIcon,
+                        const nsAString& aData,
+                        const nsAString& aBehavior);
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkermanager_h
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1074,43 +1074,46 @@ NS_IMPL_ISUPPORTS0(AllowWindowInteractio
 bool
 ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 {
   mHandler->ClearWindowAllowed(aWorkerPrivate);
   mHandler = nullptr;
   return true;
 }
 
-class SendNotificationClickEventRunnable final : public ExtendableEventWorkerRunnable
+class SendNotificationEventRunnable final : public ExtendableEventWorkerRunnable
 {
+  const nsString mEventName;
   const nsString mID;
   const nsString mTitle;
   const nsString mDir;
   const nsString mLang;
   const nsString mBody;
   const nsString mTag;
   const nsString mIcon;
   const nsString mData;
   const nsString mBehavior;
   const nsString mScope;
 
 public:
-  SendNotificationClickEventRunnable(WorkerPrivate* aWorkerPrivate,
-                                     KeepAliveToken* aKeepAliveToken,
-                                     const nsAString& aID,
-                                     const nsAString& aTitle,
-                                     const nsAString& aDir,
-                                     const nsAString& aLang,
-                                     const nsAString& aBody,
-                                     const nsAString& aTag,
-                                     const nsAString& aIcon,
-                                     const nsAString& aData,
-                                     const nsAString& aBehavior,
-                                     const nsAString& aScope)
+  SendNotificationEventRunnable(WorkerPrivate* aWorkerPrivate,
+                                KeepAliveToken* aKeepAliveToken,
+                                const nsAString& aEventName,
+                                const nsAString& aID,
+                                const nsAString& aTitle,
+                                const nsAString& aDir,
+                                const nsAString& aLang,
+                                const nsAString& aBody,
+                                const nsAString& aTag,
+                                const nsAString& aIcon,
+                                const nsAString& aData,
+                                const nsAString& aBehavior,
+                                const nsAString& aScope)
       : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
+      , mEventName(aEventName)
       , mID(aID)
       , mTitle(aTitle)
       , mDir(aDir)
       , mLang(aLang)
       , mBody(aBody)
       , mTag(aTag)
       , mIcon(aIcon)
       , mData(aData)
@@ -1139,18 +1142,17 @@ public:
     }
 
     NotificationEventInit nei;
     nei.mNotification = notification;
     nei.mBubbles = false;
     nei.mCancelable = false;
 
     RefPtr<NotificationEvent> event =
-      NotificationEvent::Constructor(target,
-                                     NS_LITERAL_STRING("notificationclick"),
+      NotificationEvent::Constructor(target, mEventName,
                                      nei, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
     event->SetTrusted(true);
     RefPtr<Promise> waitUntil;
     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
@@ -1165,37 +1167,47 @@ public:
 
     return true;
   }
 };
 
 } // namespace anonymous
 
 nsresult
-ServiceWorkerPrivate::SendNotificationClickEvent(const nsAString& aID,
-                                                 const nsAString& aTitle,
-                                                 const nsAString& aDir,
-                                                 const nsAString& aLang,
-                                                 const nsAString& aBody,
-                                                 const nsAString& aTag,
-                                                 const nsAString& aIcon,
-                                                 const nsAString& aData,
-                                                 const nsAString& aBehavior,
-                                                 const nsAString& aScope)
+ServiceWorkerPrivate::SendNotificationEvent(const nsAString& aEventName,
+                                            const nsAString& aID,
+                                            const nsAString& aTitle,
+                                            const nsAString& aDir,
+                                            const nsAString& aLang,
+                                            const nsAString& aBody,
+                                            const nsAString& aTag,
+                                            const nsAString& aIcon,
+                                            const nsAString& aData,
+                                            const nsAString& aBehavior,
+                                            const nsAString& aScope)
 {
-  nsresult rv = SpawnWorkerIfNeeded(NotificationClickEvent, nullptr);
+  WakeUpReason why;
+  if (aEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) {
+    why = NotificationClickEvent;
+    gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
+  } else if (aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) {
+    why = NotificationCloseEvent;
+  } else {
+    MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = SpawnWorkerIfNeeded(why, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
-
   RefPtr<WorkerRunnable> r =
-    new SendNotificationClickEventRunnable(mWorkerPrivate, mKeepAliveToken,
-                                           aID, aTitle, aDir, aLang,
-                                           aBody, aTag, aIcon, aData,
-                                           aBehavior, aScope);
+    new SendNotificationEventRunnable(mWorkerPrivate, mKeepAliveToken,
+                                      aEventName, aID, aTitle, aDir, aLang,
+                                      aBody, aTag, aIcon, aData, aBehavior,
+                                      aScope);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 namespace {
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -6,16 +6,19 @@
 
 #ifndef mozilla_dom_workers_serviceworkerprivate_h
 #define mozilla_dom_workers_serviceworkerprivate_h
 
 #include "nsCOMPtr.h"
 
 #include "WorkerPrivate.h"
 
+#define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
+#define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
+
 class nsIInterceptedChannel;
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 class ServiceWorkerInfo;
 class ServiceWorkerRegistrationInfo;
@@ -88,26 +91,27 @@ public:
   SendPushEvent(const nsAString& aMessageId,
                 const Maybe<nsTArray<uint8_t>>& aData,
                 ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   SendPushSubscriptionChangeEvent();
 
   nsresult
-  SendNotificationClickEvent(const nsAString& aID,
-                             const nsAString& aTitle,
-                             const nsAString& aDir,
-                             const nsAString& aLang,
-                             const nsAString& aBody,
-                             const nsAString& aTag,
-                             const nsAString& aIcon,
-                             const nsAString& aData,
-                             const nsAString& aBehavior,
-                             const nsAString& aScope);
+  SendNotificationEvent(const nsAString& aEventName,
+                        const nsAString& aID,
+                        const nsAString& aTitle,
+                        const nsAString& aDir,
+                        const nsAString& aLang,
+                        const nsAString& aBody,
+                        const nsAString& aTag,
+                        const nsAString& aIcon,
+                        const nsAString& aData,
+                        const nsAString& aBehavior,
+                        const nsAString& aScope);
 
   nsresult
   SendFetchEvent(nsIInterceptedChannel* aChannel,
                  nsILoadGroup* aLoadGroup,
                  const nsAString& aDocumentId,
                  bool aIsReload);
 
   void
@@ -144,16 +148,17 @@ public:
 
 private:
   enum WakeUpReason {
     FetchEvent = 0,
     PushEvent,
     PushSubscriptionChangeEvent,
     MessageEvent,
     NotificationClickEvent,
+    NotificationCloseEvent,
     LifeCycleEvent,
     AttachEvent
   };
 
   // Timer callbacks
   static void
   NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate);
 
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -238,16 +238,17 @@ class ServiceWorkerGlobalScope final : p
 
   ~ServiceWorkerGlobalScope();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
                                            WorkerGlobalScope)
   IMPL_EVENT_HANDLER(notificationclick)
+  IMPL_EVENT_HANDLER(notificationclose)
 
   ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
 
   virtual bool
   WrapGlobalObject(JSContext* aCx,
                    JS::MutableHandle<JSObject*> aReflector) override;
 
   static bool
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -128,16 +128,18 @@ support-files =
   bug1151916_worker.js
   bug1151916_driver.html
   bug1240436_worker.js
   notificationclick.html
   notificationclick-otherwindow.html
   notificationclick.js
   notificationclick_focus.html
   notificationclick_focus.js
+  notificationclose.html
+  notificationclose.js
   worker_updatefoundevent.js
   worker_updatefoundevent2.js
   updatefoundevent.html
   empty.js
   notification_constructor_error.js
   notification_get_sw.js
   notification/register.html
   notification/unregister.html
@@ -244,16 +246,17 @@ tags = mcb
 [test_match_all_client_properties.html]
 [test_navigator.html]
 [test_not_intercept_plugin.html]
 [test_notification_constructor_error.html]
 [test_notification_get.html]
 [test_notificationclick.html]
 [test_notificationclick_focus.html]
 [test_notificationclick-otherwindow.html]
+[test_notificationclose.html]
 [test_opaque_intercept.html]
 [test_openWindow.html]
 [test_origin_after_redirect.html]
 [test_origin_after_redirect_cached.html]
 [test_origin_after_redirect_to_https.html]
 [test_origin_after_redirect_to_https_cached.html]
 [test_post_message.html]
 [test_post_message_advanced.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.html
@@ -0,0 +1,37 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1265841 - controlled page</title>
+<script class="testbody" type="text/javascript">
+  var testWindow = parent;
+  if (opener) {
+    testWindow = opener;
+  }
+
+  navigator.serviceWorker.ready.then(function(swr) {
+    return swr.showNotification(
+      "Hi there. The ServiceWorker should receive a close event for this.",
+      { data: { complex: ["jsval", 5] }}).then(function() {
+        return swr;
+      });
+  }).then(function(swr) {
+    return swr.getNotifications();
+  }).then(function(notifications) {
+    notifications.forEach(function(notification) {
+      notification.close();
+    });
+  });
+
+  navigator.serviceWorker.onmessage = function(msg) {
+    testWindow.callback(msg.data.result);
+  };
+</script>
+
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.js
@@ -0,0 +1,19 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+onnotificationclose = function(e) {
+  self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return;
+    }
+
+    clients.forEach(function(client) {
+      client.postMessage({ result: e.notification.data &&
+                                   e.notification.data['complex'] &&
+                                   e.notification.data['complex'][0] == "jsval" &&
+                                   e.notification.data['complex'][1] == 5 });
+
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notificationclose.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265841
+-->
+<head>
+  <title>Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265841">Bug 1265841</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+  SimpleTest.requestFlakyTimeout("Mock alert service dispatches show, click, and close events.");
+
+  function testFrame(src) {
+    var iframe = document.createElement("iframe");
+    iframe.src = src;
+    window.callback = function(result) {
+      window.callback = null;
+      document.body.removeChild(iframe);
+      iframe = null;
+      ok(result, "Got notificationclose event with correct data.");
+      MockServices.unregister();
+      registration.unregister().then(function() {
+        SimpleTest.finish();
+      });
+    };
+    document.body.appendChild(iframe);
+  }
+
+  var registration;
+
+  function runTest() {
+    MockServices.register();
+    testFrame('notificationclose.html');
+    navigator.serviceWorker.register("notificationclose.js", { scope: "notificationclose.html" }).then(function(reg) {
+      registration = reg;
+    }, function(e) {
+      ok(false, "registration should have passed!");
+    });
+  };
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.webnotifications.workers.enabled", true],
+    ["dom.webnotifications.serviceworker.enabled", true],
+    ["notification.prompt.testing", true],
+  ]}, runTest);
+</script>
+</body>
+</html>