Bug 1114554 - Patch 2 - ServiceWorkerRegistration.showNotification(). r=wchen,baku
☠☠ backed out by 531f100d2bd8 ☠ ☠
authorNikhil Marathe <nsm.nikhil@gmail.com>
Wed, 06 May 2015 13:52:41 -0700
changeset 268369 ce8a768782f234b8d70c30164c687024c7905dfb
parent 268368 0d860fd125343f67d71dafddd4e435cd624884f4
child 268370 75324b6862a8545eeeb1e1fddff80f93dc1c5754
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen, baku
bugs1114554
milestone41.0a1
Bug 1114554 - Patch 2 - ServiceWorkerRegistration.showNotification(). r=wchen,baku Refactor creation and show dispatch so Notification constructor and showNotification can use it. Move persistence to ShowInternal. NotificationStorage calls callback async even when fetching from cache, simply to have similar semantics. Calls to Notification::Get() are performed async since persistence is now async after being moved to ShowInternal(). Both are in accordance with the spec where the "append to list of notifications" operation is performed in the "show steps" which are performed in parallel from API invocations.
dom/bindings/Errors.msg
dom/notification/Notification.cpp
dom/notification/Notification.h
dom/notification/NotificationStorage.js
dom/webidl/Notification.webidl
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/ServiceWorkerRegistration.h
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/notification_constructor_error.js
dom/workers/test/serviceworkers/test_notification_constructor_error.html
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -68,8 +68,11 @@ MSG_DEF(MSG_DEFINE_NON_CONFIGURABLE_PROP
 MSG_DEF(MSG_INVALID_ZOOMANDPAN_VALUE_ERROR, 0, JSEXN_RANGEERR, "Invalid zoom and pan value.")
 MSG_DEF(MSG_INVALID_TRANSFORM_ANGLE_ERROR, 0, JSEXN_RANGEERR, "Invalid transform angle.")
 MSG_DEF(MSG_INVALID_RESPONSE_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid response status code.")
 MSG_DEF(MSG_INVALID_REDIRECT_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid redirect status code.")
 MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN_TYPEERR, "{0} URL {1} must be either http:// or https://.")
 MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.")
 MSG_DEF(MSG_RESPONSE_HAS_VARY_STAR, 0, JSEXN_TYPEERR, "Invalid Response object with a 'Vary: *' header.")
 MSG_DEF(MSG_BAD_FORMDATA, 0, JSEXN_TYPEERR, "Could not parse content as FormData.")
+MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}.")
+MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.")
+MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.")
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/unused.h"
 #include "nsContentUtils.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIDocument.h"
 #include "nsINotificationStorage.h"
 #include "nsIPermissionManager.h"
 #include "nsIUUIDGenerator.h"
@@ -24,24 +25,25 @@
 #include "nsToolkitCompsCID.h"
 #include "nsGlobalWindow.h"
 #include "nsDOMJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/Event.h"
+#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/Services.h"
 #include "nsContentPermissionHelper.h"
 #include "nsILoadContext.h"
 #ifdef MOZ_B2G
 #include "nsIDOMDesktopNotification.h"
 #endif
 
+#include "ServiceWorkerManager.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
@@ -82,20 +84,20 @@ public:
     options.mDir = Notification::StringToDirection(nsString(aDir));
     options.mLang = aLang;
     options.mBody = aBody;
     options.mTag = aTag;
     options.mIcon = aIcon;
     options.mMozbehavior.Init(aBehavior);
     nsRefPtr<Notification> notification;
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
-    notification = Notification::CreateInternal(global,
-                                                aID,
+    notification = Notification::CreateInternal(aID,
                                                 aTitle,
                                                 options);
+    notification->BindToOwner(mWindow);
     ErrorResult rv;
     notification->InitFromBase64(aCx, aData, rv);
     if (rv.Failed()) {
       return rv.StealNSResult();
     }
 
     notification->SetStoredState(true);
 
@@ -164,16 +166,45 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
   tmp->DropData();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
+class NotificationGetRunnable final : public nsRunnable
+{
+  const nsString mOrigin;
+  const nsString mTag;
+  nsCOMPtr<nsINotificationStorageCallback> mCallback;
+public:
+  NotificationGetRunnable(const nsAString& aOrigin,
+                          const nsAString& aTag,
+                          nsINotificationStorageCallback* aCallback)
+    : mOrigin(aOrigin), mTag(aTag), mCallback(aCallback)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    nsresult rv;
+    nsCOMPtr<nsINotificationStorage> notificationStorage =
+      do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = notificationStorage->Get(mOrigin, mTag, mCallback);
+    //XXXnsm Is it guaranteed mCallback will be called in case of failure?
+    unused << NS_WARN_IF(NS_FAILED(rv));
+    return rv;
+  }
+};
+
 class NotificationPermissionRequest : public nsIContentPermissionRequest,
                                       public nsIRunnable
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
@@ -270,16 +301,32 @@ public:
       nsContentUtils::DispatchChromeEvent(doc, mWindow->GetOuterWindow(),
                                           NS_LITERAL_STRING("DOMWebNotificationClicked"),
                                           true, true);
     }
 
     return NS_OK;
   }
 };
+
+nsresult
+CheckScope(nsIPrincipal* aPrincipal, const nsACString& aScope)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
+
+  nsCOMPtr<nsIURI> scopeURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return aPrincipal->CheckMayLoad(scopeURI, /* report = */ true,
+                                  /* allowIfInheritsPrincipal = */ false);
+}
 } // anonymous namespace
 
 // Subclass that can be directly dispatched to child workers from the main
 // thread.
 class NotificationWorkerRunnable : public WorkerRunnable
 {
 protected:
   explicit NotificationWorkerRunnable(WorkerPrivate* aWorkerPrivate)
@@ -412,17 +459,18 @@ class NotificationTask : public nsRunnab
 {
 public:
   enum NotificationAction {
     eShow,
     eClose
   };
 
   NotificationTask(UniquePtr<NotificationRef> aRef, NotificationAction aAction)
-    : mNotificationRef(Move(aRef)), mAction(aAction) {}
+    : mNotificationRef(Move(aRef)), mAction(aAction)
+  {}
 
   NS_IMETHOD
   Run() override;
 protected:
   virtual ~NotificationTask() {}
 
   UniquePtr<NotificationRef> mNotificationRef;
   NotificationAction mAction;
@@ -619,38 +667,36 @@ bool
 Notification::IsGetEnabled(JSContext* aCx, JSObject* aObj)
 {
   return NS_IsMainThread();
 }
 
 Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
                            NotificationDirection aDir, const nsAString& aLang,
                            const nsAString& aTag, const nsAString& aIconUrl,
-                           const NotificationBehavior& aBehavior, nsIGlobalObject* aGlobal)
+                           const NotificationBehavior& aBehavior)
   : DOMEventTargetHelper(),
     mWorkerPrivate(nullptr), mObserver(nullptr),
     mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
     mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false),
     mIsStored(false), mTaskCount(0)
 {
-  nsAutoString alertName;
-  if (NS_IsMainThread()) {
-    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
-    MOZ_ASSERT(window);
-    BindToOwner(window);
-
-    DebugOnly<nsresult> rv = GetOrigin(window, alertName);
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed");
-  } else {
+  if (!NS_IsMainThread()) {
     mWorkerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(mWorkerPrivate);
+  }
+}
 
-    DebugOnly<nsresult> rv = GetOriginWorker(alertName);
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed");
-  }
+void
+Notification::SetAlertName()
+{
+  AssertIsOnMainThread();
+  nsAutoString alertName;
+  DebugOnly<nsresult> rv = GetOrigin(GetPrincipal(), alertName);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed");
 
   // Get the notification name that is unique per origin + tag/ID.
   // The name of the alert is of the form origin#tag/ID.
   alertName.Append('#');
   if (!mTag.IsEmpty()) {
     alertName.AppendLiteral("tag:");
     alertName.Append(mTag);
   } else {
@@ -664,78 +710,51 @@ Notification::Notification(const nsAStri
 // May be called on any thread.
 // static
 already_AddRefed<Notification>
 Notification::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aTitle,
                           const NotificationOptions& aOptions,
                           ErrorResult& aRv)
 {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-  nsRefPtr<Notification> notification = CreateInternal(global,
-                                                       EmptyString(),
-                                                       aTitle,
-                                                       aOptions);
-  // Make a structured clone of the aOptions.mData object
-  JS::Rooted<JS::Value> data(aGlobal.Context(), aOptions.mData);
-  notification->InitFromJSVal(aGlobal.Context(), data, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  auto ref = MakeUnique<NotificationRef>(notification);
-  if (!ref->Initialized()) {
-    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+  // FIXME(nsm): If the sticky flag is set, throw an error.
+  ServiceWorkerGlobalScope* scope = nullptr;
+  UNWRAP_WORKER_OBJECT(ServiceWorkerGlobalScope, aGlobal.Get(), scope);
+  if (scope) {
+    aRv.ThrowTypeError(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER);
     return nullptr;
   }
 
-  // Queue a task to show the notification.
-  nsCOMPtr<nsIRunnable> showNotificationTask =
-    new NotificationTask(Move(ref), NotificationTask::eShow);
-  nsresult rv = NS_DispatchToMainThread(showNotificationTask);
-  if (NS_FAILED(rv)) {
-    notification->DispatchTrustedEvent(NS_LITERAL_STRING("error"));
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  nsRefPtr<Notification> notification =
+    CreateAndShow(global, aTitle, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
   }
 
-  //XXXnsm The persistence service seems like a prime candidate of
-  //PBackground.
-  // On workers, persistence is done in the NotificationTask.
-  if (NS_IsMainThread()) {
-    nsresult rv = notification->PersistNotification();
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Could not persist main thread Notification");
-    }
-  }
-
+  // This is be ok since we are on the worker thread where this function will
+  // run to completion before the Notification has a chance to go away.
   return notification.forget();
 }
 
+
 nsresult
 Notification::PersistNotification()
 {
   AssertIsOnMainThread();
   nsresult rv;
   nsCOMPtr<nsINotificationStorage> notificationStorage =
     do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsString origin;
-  if (mWorkerPrivate) {
-    rv = GetOriginWorker(origin);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-  } else {
-    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
-    MOZ_ASSERT(window);
-    rv = GetOrigin(window, origin);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
+  rv = GetOrigin(GetPrincipal(), origin);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   nsString id;
   GetID(id);
 
   nsString alertName;
   GetAlertName(alertName);
 
   nsString dataString;
@@ -769,33 +788,32 @@ Notification::PersistNotification()
   SetStoredState(true);
   return NS_OK;
 }
 
 void
 Notification::UnpersistNotification()
 {
   AssertIsOnMainThread();
-  if (mIsStored) {
+  if (IsStored()) {
     nsCOMPtr<nsINotificationStorage> notificationStorage =
       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
     if (notificationStorage) {
       nsString origin;
-      nsresult rv = GetOrigin(GetOwner(), origin);
+      nsresult rv = GetOrigin(GetPrincipal(), origin);
       if (NS_SUCCEEDED(rv)) {
         notificationStorage->Delete(origin, mID);
       }
     }
     SetStoredState(false);
   }
 }
 
 already_AddRefed<Notification>
-Notification::CreateInternal(nsIGlobalObject* aGlobal,
-                             const nsAString& aID,
+Notification::CreateInternal(const nsAString& aID,
                              const nsAString& aTitle,
                              const NotificationOptions& aOptions)
 {
   nsString id;
   if (!aID.IsEmpty()) {
     id = aID;
   } else {
     nsCOMPtr<nsIUUIDGenerator> uuidgen =
@@ -813,18 +831,17 @@ Notification::CreateInternal(nsIGlobalOb
 
   nsRefPtr<Notification> notification = new Notification(id,
                                                          aTitle,
                                                          aOptions.mBody,
                                                          aOptions.mDir,
                                                          aOptions.mLang,
                                                          aOptions.mTag,
                                                          aOptions.mIcon,
-                                                         aOptions.mMozbehavior,
-                                                         aGlobal);
+                                                         aOptions.mMozbehavior);
   return notification.forget();
 }
 
 Notification::~Notification()
 {
   AssertIsOnTargetThread();
   MOZ_ASSERT(!mFeature);
   MOZ_ASSERT(!mTempRef);
@@ -1070,38 +1087,32 @@ Notification::ShowInternal()
   MOZ_ASSERT(!mObserver);
 
   // Transfer ownership to local scope so we can either release it at the end
   // of this function or transfer it to the observer.
   UniquePtr<NotificationRef> ownership;
   mozilla::Swap(ownership, mTempRef);
   MOZ_ASSERT(ownership->GetNotification() == this);
 
-  if (mWorkerPrivate) {
-    // Persist the notification now since we couldn't do it on the worker
-    // thread.
-    nsresult rv = PersistNotification();
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Could not persist worker Notification");
-    }
+  nsresult rv = PersistNotification();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Could not persist Notification");
   }
 
   nsCOMPtr<nsIAlertsService> alertService =
     do_GetService(NS_ALERTSERVICE_CONTRACTID);
 
   ErrorResult result;
   NotificationPermission permission = NotificationPermission::Denied;
   if (mWorkerPrivate) {
     permission = GetPermissionInternal(mWorkerPrivate->GetPrincipal(), result);
   } else {
     permission = GetPermissionInternal(GetOwner(), result);
   }
   if (permission != NotificationPermission::Granted || !alertService) {
-    // We do not have permission to show a notification or alert service
-    // is not available.
     if (mWorkerPrivate) {
       nsRefPtr<NotificationEventWorkerRunnable> r =
         new NotificationEventWorkerRunnable(this,
                                             NS_LITERAL_STRING("error"));
       AutoSafeJSContext cx;
       if (!r->Dispatch(cx)) {
         NS_WARNING("Could not dispatch event to worker notification");
       }
@@ -1150,17 +1161,17 @@ Notification::ShowInternal()
       nsString manifestUrl = EmptyString();
       nsresult rv = appsService->GetManifestURLByLocalId(appId, manifestUrl);
       if (NS_SUCCEEDED(rv)) {
         mozilla::AutoSafeJSContext cx;
         JS::Rooted<JS::Value> val(cx);
         AppNotificationServiceOptions ops;
         ops.mTextClickable = true;
         ops.mManifestURL = manifestUrl;
-        ops.mId = mAlertName;
+        GetAlertName(ops.mId);
         ops.mDbId = mID;
         ops.mDir = DirectionToString(mDir);
         ops.mLang = mLang;
         ops.mTag = mTag;
         ops.mData = dataStr;
         ops.mMozbehavior = mBehavior;
         ops.mMozbehavior.mSoundFile = soundUrl;
 
@@ -1192,18 +1203,21 @@ Notification::ShowInternal()
     // Not all workers may have a document, but with Bug 1107516 fixed, they
     // should all have a loadcontext.
     nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
     nsCOMPtr<nsILoadContext> loadContext;
     NS_QueryNotificationCallbacks(nullptr, loadGroup, NS_GET_IID(nsILoadContext),
                                   getter_AddRefs(loadContext));
     inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
   }
+
+  nsAutoString alertName;
+  GetAlertName(alertName);
   alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true,
-                                      uniqueCookie, observer, mAlertName,
+                                      uniqueCookie, observer, alertName,
                                       DirectionToString(mDir), mLang,
                                       dataStr, GetPrincipal(),
                                       inPrivateBrowsing);
 }
 
 /* static */ bool
 Notification::RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */)
 {
@@ -1233,21 +1247,30 @@ Notification::RequestPermission(const Gl
     permissionCallback = &aCallback.Value();
   }
   nsCOMPtr<nsIRunnable> request =
     new NotificationPermissionRequest(principal, window, permissionCallback);
 
   NS_DispatchToMainThread(request);
 }
 
+// static
 NotificationPermission
 Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  return GetPermission(global, aRv);
+}
+
+// static
+NotificationPermission
+Notification::GetPermission(nsIGlobalObject* aGlobal, ErrorResult& aRv)
+{
   if (NS_IsMainThread()) {
-    return GetPermissionInternal(aGlobal.GetAsSupports(), aRv);
+    return GetPermissionInternal(aGlobal, aRv);
   } else {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
     nsRefPtr<GetPermissionRunnable> r =
       new GetPermissionRunnable(worker);
     if (!r->Dispatch(worker->GetJSContext())) {
       aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
       return NotificationPermission::Denied;
@@ -1383,44 +1406,42 @@ Notification::Get(const GlobalObject& aG
                   ErrorResult& aRv)
 {
   AssertIsOnMainThread();
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(global);
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
   MOZ_ASSERT(window);
-  nsIDocument* doc = window->GetExtantDoc();
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   if (!doc) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsString origin;
-  aRv = GetOrigin(window, origin);
+  aRv = GetOrigin(doc->NodePrincipal(), origin);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  nsresult rv;
-  nsCOMPtr<nsINotificationStorage> notificationStorage =
-    do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-    return nullptr;
-  }
-
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
+
   nsCOMPtr<nsINotificationStorageCallback> callback =
     new NotificationStorageCallback(aGlobal, window, promise);
-  aRv = notificationStorage->Get(origin, aFilter.mTag, callback);
-  if (aRv.Failed()) {
+
+  nsRefPtr<NotificationGetRunnable> r =
+    new NotificationGetRunnable(origin, aFilter.mTag, callback);
+
+  aRv = NS_DispatchToMainThread(r);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
 JSObject*
 Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@@ -1457,61 +1478,48 @@ Notification::CloseInternal()
   UniquePtr<NotificationRef> ownership;
   mozilla::Swap(ownership, mTempRef);
 
   UnpersistNotification();
   if (!mIsClosed) {
     nsCOMPtr<nsIAlertsService> alertService =
       do_GetService(NS_ALERTSERVICE_CONTRACTID);
     if (alertService) {
-      alertService->CloseAlert(mAlertName, GetPrincipal());
+      nsAutoString alertName;
+      GetAlertName(alertName);
+      alertService->CloseAlert(alertName, GetPrincipal());
     }
   }
 }
 
 nsresult
-Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
+Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
 {
-  if (!aWindow) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(aPrincipal);
+  uint16_t appStatus = aPrincipal->GetAppStatus();
+  uint32_t appId = aPrincipal->GetAppId();
+
   nsresult rv;
-  nsIDocument* doc = aWindow->GetExtantDoc();
-  NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
-  nsIPrincipal* principal = doc->NodePrincipal();
-  NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
-
-  uint16_t appStatus = principal->GetAppStatus();
-  uint32_t appId = principal->GetAppId();
-
   if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
       appId == nsIScriptSecurityManager::NO_APP_ID ||
       appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-    rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
+    rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     // If we are in "app code", use manifest URL as unique origin since
     // multiple apps can share the same origin but not same notifications.
     nsCOMPtr<nsIAppsService> appsService =
       do_GetService("@mozilla.org/AppsService;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     appsService->GetManifestURLByLocalId(appId, aOrigin);
   }
 
   return NS_OK;
 }
 
-nsresult
-Notification::GetOriginWorker(nsString& aOrigin)
-{
-  MOZ_ASSERT(mWorkerPrivate);
-  aOrigin = mWorkerPrivate->GetLocationInfo().mOrigin;
-  return NS_OK;
-}
-
 nsIStructuredCloneContainer* Notification::GetDataCloneContainer()
 {
   return mDataObjectContainer;
 }
 
 void
 Notification::GetData(JSContext* aCx,
                       JS::MutableHandle<JS::Value> aRetval)
@@ -1655,11 +1663,192 @@ Notification::UnregisterFeature()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mFeature);
   mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(),
                                 mFeature.get());
   mFeature = nullptr;
 }
+
+/*
+ * Checks:
+ * 1) Is aWorker allowed to show a notification for scope?
+ * 2) Is aWorker an active worker?
+ *
+ * If it is not an active worker, Result() will be NS_ERROR_NOT_AVAILABLE.
+ */
+class CheckLoadRunnable final : public WorkerMainThreadRunnable
+{
+  nsresult mRv;
+  nsCString mScope;
+
+public:
+  explicit CheckLoadRunnable(WorkerPrivate* aWorker, const nsACString& aScope)
+    : WorkerMainThreadRunnable(aWorker)
+    , mRv(NS_ERROR_DOM_SECURITY_ERR)
+    , mScope(aScope)
+  { }
+
+  bool
+  MainThreadRun() override
+  {
+    nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
+    mRv = CheckScope(principal, mScope);
+
+    if (NS_FAILED(mRv)) {
+      return true;
+    }
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    nsRefPtr<ServiceWorkerRegistrationInfo> registration =
+      swm->GetRegistration(principal, mScope);
+
+    // This is coming from a ServiceWorkerRegistrationWorkerThread.
+    MOZ_ASSERT(registration);
+
+    if (!registration->mActiveWorker ||
+        registration->mActiveWorker->ID() != mWorkerPrivate->ServiceWorkerID()) {
+      mRv = NS_ERROR_NOT_AVAILABLE;
+    }
+
+    return true;
+  }
+
+  nsresult
+  Result()
+  {
+    return mRv;
+  }
+
+};
+
+/* static */
+already_AddRefed<Promise>
+Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal,
+                                         const nsAString& aScope,
+                                         const nsAString& aTitle,
+                                         const NotificationOptions& aOptions,
+                                         ErrorResult& aRv)
+{
+  MOZ_ASSERT(aGlobal);
+
+  // Validate scope.
+  // XXXnsm: This may be slow due to blocking the worker and waiting on the main
+  // thread. On calls from content, we can be sure the scope is valid since
+  // ServiceWorkerRegistrations have their scope set correctly. Can this be made
+  // debug only? The problem is that there would be different semantics in
+  // debug and non-debug builds in such a case.
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
+    if (NS_WARN_IF(!sop)) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    nsIPrincipal* principal = sop->GetPrincipal();
+    if (NS_WARN_IF(!principal)) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    aRv = CheckScope(principal, NS_ConvertUTF16toUTF8(aScope));
+    if (NS_WARN_IF(aRv.Failed())) {
+      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      return nullptr;
+    }
+  } else {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+    nsRefPtr<CheckLoadRunnable> loadChecker =
+      new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope));
+    if (!loadChecker->Dispatch(worker->GetJSContext())) {
+      aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+      return nullptr;
+    }
+
+    if (NS_WARN_IF(NS_FAILED(loadChecker->Result()))) {
+      if (loadChecker->Result() == NS_ERROR_NOT_AVAILABLE) {
+        aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER);
+      } else {
+        aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      }
+      return nullptr;
+    }
+  }
+
+
+  nsRefPtr<Promise> p = Promise::Create(aGlobal, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  // We check permission here rather than pass the Promise to NotificationTask
+  // which leads to uglier code.
+  NotificationPermission permission = GetPermission(aGlobal, aRv);
+
+  // "If permission for notification’s origin is not "granted", reject promise with a TypeError exception, and terminate these substeps."
+  if (NS_WARN_IF(aRv.Failed()) || permission == NotificationPermission::Denied) {
+    ErrorResult result;
+    result.ThrowTypeError(MSG_NOTIFICATION_PERMISSION_DENIED);
+    p->MaybeReject(result);
+    return p.forget();
+  }
+
+  // "Otherwise, resolve promise with undefined."
+  // The Notification may still not be shown due to other errors, but the spec
+  // is not concerned with those.
+  p->MaybeResolve(JS::UndefinedHandleValue);
+
+  nsRefPtr<Notification> notification =
+    CreateAndShow(aGlobal, aTitle, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
+}
+
+/* static */ already_AddRefed<Notification>
+Notification::CreateAndShow(nsIGlobalObject* aGlobal,
+                            const nsAString& aTitle,
+                            const NotificationOptions& aOptions,
+                            ErrorResult& aRv)
+{
+  MOZ_ASSERT(aGlobal);
+
+  AutoJSAPI jsapi;
+  jsapi.Init(aGlobal);
+  JSContext* cx = jsapi.cx();
+
+  nsRefPtr<Notification> notification = CreateInternal(EmptyString(),
+                                                       aTitle,
+                                                       aOptions);
+
+  notification->BindToOwner(aGlobal);
+
+  // Make a structured clone of the aOptions.mData object
+  JS::Rooted<JS::Value> data(cx, aOptions.mData);
+  notification->InitFromJSVal(cx, data, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  auto ref = MakeUnique<NotificationRef>(notification);
+  if (!ref->Initialized()) {
+    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+    return nullptr;
+  }
+
+  // Queue a task to show the notification.
+  nsCOMPtr<nsIRunnable> showNotificationTask =
+    new NotificationTask(Move(ref), NotificationTask::eShow);
+  nsresult rv = NS_DispatchToMainThread(showNotificationTask);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    notification->DispatchTrustedEvent(NS_LITERAL_STRING("error"));
+  }
+
+  return notification.forget();
+}
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -188,16 +188,25 @@ public:
 
   static NotificationPermission GetPermission(const GlobalObject& aGlobal,
                                               ErrorResult& aRv);
 
   static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
                                        const GetNotificationOptions& aFilter,
                                        ErrorResult& aRv);
 
+  // Notification implementation of
+  // ServiceWorkerRegistration.showNotification.
+  static already_AddRefed<Promise>
+  ShowPersistentNotification(nsIGlobalObject* aGlobal,
+                             const nsAString& aScope,
+                             const nsAString& aTitle,
+                             const NotificationOptions& aOptions,
+                             ErrorResult& aRv);
+
   void Close();
 
   nsPIDOMWindow* GetParentObject()
   {
     return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -228,28 +237,32 @@ public:
   //
   // Main thread only.
   UniquePtr<NotificationRef> mTempRef;
 
   // Returns true if addref succeeded.
   bool AddRefObject();
   void ReleaseObject();
 
+  static NotificationPermission GetPermission(nsIGlobalObject* aGlobal,
+                                              ErrorResult& aRv);
+
   static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
                                                       ErrorResult& rv);
 
   bool DispatchClickEvent();
 protected:
+  // Callers MUST bind the Notification to the correct DOMEventTargetHelper parent using
+  // BindToOwner().
   Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
                NotificationDirection aDir, const nsAString& aLang,
                const nsAString& aTag, const nsAString& aIconUrl,
-               const NotificationBehavior& aBehavior, nsIGlobalObject* aGlobal);
+               const NotificationBehavior& aBehavior);
 
-  static already_AddRefed<Notification> CreateInternal(nsIGlobalObject* aGlobal,
-                                                       const nsAString& aID,
+  static already_AddRefed<Notification> CreateInternal(const nsAString& aID,
                                                        const nsAString& aTitle,
                                                        const NotificationOptions& aOptions);
 
   void ShowInternal();
   void CloseInternal();
 
   static NotificationPermission GetPermissionInternal(nsISupports* aGlobal,
                                                       ErrorResult& rv);
@@ -272,21 +285,24 @@ protected:
       return NotificationDirection::Ltr;
     }
     if (aDirection.EqualsLiteral("rtl")) {
       return NotificationDirection::Rtl;
     }
     return NotificationDirection::Auto;
   }
 
-  static nsresult GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin);
-  nsresult GetOriginWorker(nsString& aOrigin);
+  static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
 
   void GetAlertName(nsAString& aRetval)
   {
+    workers::AssertIsOnMainThread();
+    if (mAlertName.IsEmpty()) {
+      SetAlertName();
+    }
     aRetval = mAlertName;
   }
 
   const nsString mID;
   const nsString mTitle;
   const nsString mBody;
   const NotificationDirection mDir;
   const nsString mLang;
@@ -309,21 +325,34 @@ protected:
   // the notification.
   bool mIsStored;
 
   static uint32_t sCount;
 
 private:
   virtual ~Notification();
 
+  // Creates a Notification and shows it. Returns a reference to the
+  // Notification if result is NS_OK. The lifetime of this Notification is tied
+  // to an underlying NotificationRef. Do not hold a non-stack raw pointer to
+  // it. Be careful about thread safety if acquiring a strong reference.
+  static already_AddRefed<Notification>
+  CreateAndShow(nsIGlobalObject* aGlobal,
+                const nsAString& aTitle,
+                const NotificationOptions& aOptions,
+                ErrorResult& aRv);
+
   nsIPrincipal* GetPrincipal();
 
   nsresult PersistNotification();
   void UnpersistNotification();
 
+  void
+  SetAlertName();
+
   bool IsTargetThread() const
   {
     return NS_IsMainThread() == !mWorkerPrivate;
   }
 
   bool RegisterFeature();
   void UnregisterFeature();
 
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -206,33 +206,39 @@ NotificationStorage.prototype = {
       for (var id in this._notifications) {
         if (this._notifications[id].origin === origin) {
           notifications.push(this._notifications[id]);
         }
       }
     }
 
     // Pass each notification back separately.
+    // The callback is called asynchronously to match the behaviour when
+    // fetching from the database.
     notifications.forEach(function(notification) {
       try {
-        callback.handle(notification.id,
-                        notification.title,
-                        notification.dir,
-                        notification.lang,
-                        notification.body,
-                        notification.tag,
-                        notification.icon,
-                        notification.data,
-                        notification.mozbehavior);
+        Services.tm.currentThread.dispatch(
+          callback.handle.bind(callback,
+                               notification.id,
+                               notification.title,
+                               notification.dir,
+                               notification.lang,
+                               notification.body,
+                               notification.tag,
+                               notification.icon,
+                               notification.data,
+                               notification.mozbehavior),
+          Ci.nsIThread.DISPATCH_NORMAL);
       } catch (e) {
         if (DEBUG) { debug("Error calling callback handle: " + e); }
       }
     });
     try {
-      callback.done();
+      Services.tm.currentThread.dispatch(callback.done,
+                                         Ci.nsIThread.DISPATCH_NORMAL);
     } catch (e) {
       if (DEBUG) { debug("Error calling callback done: " + e); }
     }
   },
 
   _populateCache: function(notifications) {
     notifications.forEach(function(notification) {
       this._notifications[notification.id] = notification;
--- a/dom/webidl/Notification.webidl
+++ b/dom/webidl/Notification.webidl
@@ -89,11 +89,13 @@ callback NotificationPermissionCallback 
 
 enum NotificationDirection {
   "auto",
   "ltr",
   "rtl"
 };
 
 partial interface ServiceWorkerRegistration {
+  [Throws]
   Promise<void> showNotification(DOMString title, optional NotificationOptions options);
+  [Throws]
   Promise<sequence<Notification>> getNotifications(optional GetNotificationOptions filter);
 };
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerRegistration.h"
 
+#include "mozilla/dom/Notification.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
 #include "mozilla/Services.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "ServiceWorker.h"
@@ -587,24 +588,52 @@ ServiceWorkerRegistrationMainThread::Unr
 
   return promise.forget();
 }
 
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
                                                       const nsAString& aTitle,
-                                                      const NotificationOptions& aOptions)
+                                                      const NotificationOptions& aOptions,
+                                                      ErrorResult& aRv)
 {
-  MOZ_ASSERT(false);
-  return nullptr;
+  AssertIsOnMainThread();
+  MOZ_ASSERT(GetOwner());
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (NS_WARN_IF(!window)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<workers::ServiceWorker> worker = GetActive();
+  if (!worker) {
+    aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+  nsRefPtr<Promise> p =
+    Notification::ShowPersistentNotification(global,
+                                             mScope, aTitle, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
 }
 
 already_AddRefed<Promise>
-ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions)
+ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(false);
   return nullptr;
 }
 
 already_AddRefed<PushManager>
 ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv)
 {
@@ -1008,23 +1037,34 @@ WorkerListener::UpdateFound()
     }
   }
 }
 
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
                                                         const nsAString& aTitle,
-                                                        const NotificationOptions& aOptions)
+                                                        const NotificationOptions& aOptions,
+                                                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(false);
-  return nullptr;
+  // Until Bug 1131324 exposes ServiceWorkerContainer on workers,
+  // ShowPersistentNotification() checks for valid active worker while it is
+  // also verifying scope so that we block the worker on the main thread only
+  // once.
+  nsRefPtr<Promise> p =
+    Notification::ShowPersistentNotification(mWorkerPrivate->GlobalScope(),
+                                             mScope, aTitle, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
 }
 
 already_AddRefed<Promise>
-ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions)
+ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(false);
   return nullptr;
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -113,20 +113,21 @@ public:
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // Partial interface from Notification API.
   already_AddRefed<Promise>
   ShowNotification(JSContext* aCx,
                    const nsAString& aTitle,
-                   const NotificationOptions& aOptions);
+                   const NotificationOptions& aOptions,
+                   ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  GetNotifications(const GetNotificationOptions& aOptions);
+  GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv);
 
   already_AddRefed<workers::ServiceWorker>
   GetInstalling() override;
 
   already_AddRefed<workers::ServiceWorker>
   GetWaiting() override;
 
   already_AddRefed<workers::ServiceWorker>
@@ -201,20 +202,21 @@ public:
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // Partial interface from Notification API.
   already_AddRefed<Promise>
   ShowNotification(JSContext* aCx,
                    const nsAString& aTitle,
-                   const NotificationOptions& aOptions);
+                   const NotificationOptions& aOptions,
+                   ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  GetNotifications(const GetNotificationOptions& aOptions);
+  GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv);
 
   already_AddRefed<workers::ServiceWorker>
   GetInstalling() override;
 
   already_AddRefed<workers::ServiceWorker>
   GetWaiting() override;
 
   already_AddRefed<workers::ServiceWorker>
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -94,16 +94,17 @@ support-files =
   updatefoundevent.html
   empty.js
   periodic_update_test.js
   periodic.sjs
   periodic/frame.html
   periodic/register.html
   periodic/wait_for_update.html
   periodic/unregister.html
+  notification_constructor_error.js
   sanitize/frame.html
   sanitize/register.html
   sanitize/example_check_and_unregister.html
   sanitize_worker.js
   swa/worker_scope_different.js
   swa/worker_scope_different.js^headers^
   swa/worker_scope_different2.js
   swa/worker_scope_different2.js^headers^
@@ -180,16 +181,17 @@ support-files =
 [test_post_message_advanced.html]
 [test_post_message_source.html]
 [test_register_base.html]
 [test_register_https_in_http.html]
 [test_request_context.html]
 skip-if = toolkit == 'android' # Bug 1163410
 [test_scopes.html]
 [test_sandbox_intercept.html]
+[test_notification_constructor_error.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
 [test_service_worker_allowed.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_skip_waiting.html]
 [test_strict_mode_error.html]
 [test_third_party_iframes.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification_constructor_error.js
@@ -0,0 +1,1 @@
+new Notification("Hi there");
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notification_constructor_error.html
@@ -0,0 +1,52 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug XXXXXXX - Check that Notification constructor throws in ServiceWorkerGlobalScope</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>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function simpleRegister() {
+    return navigator.serviceWorker.register("notification_constructor_error.js", { scope: "notification_constructor_error/" }).then(function(swr) {
+      ok(false, "Registration should fail.");
+    }, function(e) {
+      ok(e.message.indexOf("Notification") != -1, "Registration should fail.");
+    });
+  }
+
+  function runTest() {
+    MockServices.register();
+    simpleRegister()
+      .then(function() {
+        MockServices.unregister();
+        SimpleTest.finish();
+      }).catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+        MockServices.unregister();
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["notification.prompt.testing", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+