LCOV - code coverage report
Current view: top level - dom/notification - Notification.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 48 1241 3.9 %
Date: 2017-07-14 16:53:18 Functions: 11 218 5.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/Notification.h"
       8             : 
       9             : #include "mozilla/Encoding.h"
      10             : #include "mozilla/JSONWriter.h"
      11             : #include "mozilla/Move.h"
      12             : #include "mozilla/OwningNonNull.h"
      13             : #include "mozilla/Preferences.h"
      14             : #include "mozilla/Services.h"
      15             : #include "mozilla/Telemetry.h"
      16             : #include "mozilla/Unused.h"
      17             : 
      18             : #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
      19             : #include "mozilla/dom/BindingUtils.h"
      20             : #include "mozilla/dom/ContentChild.h"
      21             : #include "mozilla/dom/NotificationEvent.h"
      22             : #include "mozilla/dom/PermissionMessageUtils.h"
      23             : #include "mozilla/dom/Promise.h"
      24             : #include "mozilla/dom/PromiseWorkerProxy.h"
      25             : #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
      26             : 
      27             : #include "nsAlertsUtils.h"
      28             : #include "nsComponentManagerUtils.h"
      29             : #include "nsContentPermissionHelper.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsCRTGlue.h"
      32             : #include "nsDOMJSUtils.h"
      33             : #include "nsGlobalWindow.h"
      34             : #include "nsIAlertsService.h"
      35             : #include "nsIContentPermissionPrompt.h"
      36             : #include "nsIDocument.h"
      37             : #include "nsILoadContext.h"
      38             : #include "nsINotificationStorage.h"
      39             : #include "nsIPermissionManager.h"
      40             : #include "nsIPermission.h"
      41             : #include "nsIPushService.h"
      42             : #include "nsIScriptSecurityManager.h"
      43             : #include "nsIServiceWorkerManager.h"
      44             : #include "nsISimpleEnumerator.h"
      45             : #include "nsIUUIDGenerator.h"
      46             : #include "nsIXPConnect.h"
      47             : #include "nsNetUtil.h"
      48             : #include "nsProxyRelease.h"
      49             : #include "nsServiceManagerUtils.h"
      50             : #include "nsStructuredCloneContainer.h"
      51             : #include "nsThreadUtils.h"
      52             : #include "nsToolkitCompsCID.h"
      53             : #include "nsXULAppAPI.h"
      54             : #include "ServiceWorkerManager.h"
      55             : #include "WorkerPrivate.h"
      56             : #include "WorkerRunnable.h"
      57             : #include "WorkerScope.h"
      58             : 
      59             : namespace mozilla {
      60             : namespace dom {
      61             : 
      62             : using namespace workers;
      63             : 
      64           0 : struct NotificationStrings
      65             : {
      66             :   const nsString mID;
      67             :   const nsString mTitle;
      68             :   const nsString mDir;
      69             :   const nsString mLang;
      70             :   const nsString mBody;
      71             :   const nsString mTag;
      72             :   const nsString mIcon;
      73             :   const nsString mData;
      74             :   const nsString mBehavior;
      75             :   const nsString mServiceWorkerRegistrationScope;
      76             : };
      77             : 
      78             : class ScopeCheckingGetCallback : public nsINotificationStorageCallback
      79             : {
      80             :   const nsString mScope;
      81             : public:
      82           0 :   explicit ScopeCheckingGetCallback(const nsAString& aScope)
      83           0 :     : mScope(aScope)
      84           0 :   {}
      85             : 
      86           0 :   NS_IMETHOD Handle(const nsAString& aID,
      87             :                     const nsAString& aTitle,
      88             :                     const nsAString& aDir,
      89             :                     const nsAString& aLang,
      90             :                     const nsAString& aBody,
      91             :                     const nsAString& aTag,
      92             :                     const nsAString& aIcon,
      93             :                     const nsAString& aData,
      94             :                     const nsAString& aBehavior,
      95             :                     const nsAString& aServiceWorkerRegistrationScope) final
      96             :   {
      97           0 :     AssertIsOnMainThread();
      98           0 :     MOZ_ASSERT(!aID.IsEmpty());
      99             : 
     100             :     // Skip scopes that don't match when called from getNotifications().
     101           0 :     if (!mScope.IsEmpty() && !mScope.Equals(aServiceWorkerRegistrationScope)) {
     102           0 :       return NS_OK;
     103             :     }
     104             : 
     105             :     NotificationStrings strings = {
     106             :       nsString(aID),
     107             :       nsString(aTitle),
     108             :       nsString(aDir),
     109             :       nsString(aLang),
     110             :       nsString(aBody),
     111             :       nsString(aTag),
     112             :       nsString(aIcon),
     113             :       nsString(aData),
     114             :       nsString(aBehavior),
     115             :       nsString(aServiceWorkerRegistrationScope),
     116           0 :     };
     117             : 
     118           0 :     mStrings.AppendElement(Move(strings));
     119           0 :     return NS_OK;
     120             :   }
     121             : 
     122             :   NS_IMETHOD Done() override = 0;
     123             : 
     124             : protected:
     125           0 :   virtual ~ScopeCheckingGetCallback()
     126           0 :   {}
     127             : 
     128             :   nsTArray<NotificationStrings> mStrings;
     129             : };
     130             : 
     131             : class NotificationStorageCallback final : public ScopeCheckingGetCallback
     132             : {
     133             : public:
     134             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     135           0 :   NS_DECL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
     136             : 
     137           0 :   NotificationStorageCallback(nsIGlobalObject* aWindow, const nsAString& aScope,
     138             :                               Promise* aPromise)
     139           0 :     : ScopeCheckingGetCallback(aScope),
     140             :       mWindow(aWindow),
     141           0 :       mPromise(aPromise)
     142             :   {
     143           0 :     AssertIsOnMainThread();
     144           0 :     MOZ_ASSERT(aWindow);
     145           0 :     MOZ_ASSERT(aPromise);
     146           0 :   }
     147             : 
     148           0 :   NS_IMETHOD Done() final
     149             :   {
     150           0 :     ErrorResult result;
     151           0 :     AutoTArray<RefPtr<Notification>, 5> notifications;
     152             : 
     153           0 :     for (uint32_t i = 0; i < mStrings.Length(); ++i) {
     154             :       RefPtr<Notification> n =
     155           0 :         Notification::ConstructFromFields(mWindow,
     156           0 :                                           mStrings[i].mID,
     157           0 :                                           mStrings[i].mTitle,
     158           0 :                                           mStrings[i].mDir,
     159           0 :                                           mStrings[i].mLang,
     160           0 :                                           mStrings[i].mBody,
     161           0 :                                           mStrings[i].mTag,
     162           0 :                                           mStrings[i].mIcon,
     163           0 :                                           mStrings[i].mData,
     164             :                                           /* mStrings[i].mBehavior, not
     165             :                                            * supported */
     166           0 :                                           mStrings[i].mServiceWorkerRegistrationScope,
     167           0 :                                           result);
     168             : 
     169           0 :       n->SetStoredState(true);
     170           0 :       Unused << NS_WARN_IF(result.Failed());
     171           0 :       if (!result.Failed()) {
     172           0 :         notifications.AppendElement(n.forget());
     173             :       }
     174             :     }
     175             : 
     176           0 :     mPromise->MaybeResolve(notifications);
     177           0 :     return NS_OK;
     178             :   }
     179             : 
     180             : private:
     181           0 :   virtual ~NotificationStorageCallback()
     182           0 :   {}
     183             : 
     184             :   nsCOMPtr<nsIGlobalObject> mWindow;
     185             :   RefPtr<Promise> mPromise;
     186             :   const nsString mScope;
     187             : };
     188             : 
     189           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
     190           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
     191           0 : NS_IMPL_CYCLE_COLLECTION(NotificationStorageCallback, mWindow, mPromise);
     192             : 
     193           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
     194           0 :   NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
     195           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     196           0 : NS_INTERFACE_MAP_END
     197             : 
     198           0 : class NotificationGetRunnable final : public Runnable
     199             : {
     200             :   const nsString mOrigin;
     201             :   const nsString mTag;
     202             :   nsCOMPtr<nsINotificationStorageCallback> mCallback;
     203             : public:
     204           0 :   NotificationGetRunnable(const nsAString& aOrigin,
     205             :                           const nsAString& aTag,
     206             :                           nsINotificationStorageCallback* aCallback)
     207           0 :     : Runnable("NotificationGetRunnable")
     208           0 :     , mOrigin(aOrigin), mTag(aTag), mCallback(aCallback)
     209           0 :   {}
     210             : 
     211             :   NS_IMETHOD
     212           0 :   Run() override
     213             :   {
     214             :     nsresult rv;
     215             :     nsCOMPtr<nsINotificationStorage> notificationStorage =
     216           0 :       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
     217           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     218           0 :       return rv;
     219             :     }
     220             : 
     221           0 :     rv = notificationStorage->Get(mOrigin, mTag, mCallback);
     222             :     //XXXnsm Is it guaranteed mCallback will be called in case of failure?
     223           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
     224           0 :     return rv;
     225             :   }
     226             : };
     227             : 
     228             : class NotificationPermissionRequest : public nsIContentPermissionRequest,
     229             :                                       public nsIRunnable
     230             : {
     231             : public:
     232             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     233             :   NS_DECL_NSICONTENTPERMISSIONREQUEST
     234             :   NS_DECL_NSIRUNNABLE
     235           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
     236             :                                            nsIContentPermissionRequest)
     237             : 
     238           0 :   NotificationPermissionRequest(nsIPrincipal* aPrincipal,
     239             :                                 nsPIDOMWindowInner* aWindow, Promise* aPromise,
     240             :                                 NotificationPermissionCallback* aCallback)
     241           0 :     : mPrincipal(aPrincipal), mWindow(aWindow),
     242             :       mPermission(NotificationPermission::Default),
     243             :       mPromise(aPromise),
     244           0 :       mCallback(aCallback)
     245             :   {
     246           0 :     MOZ_ASSERT(aPromise);
     247           0 :     mRequester = new nsContentPermissionRequester(mWindow);
     248           0 :   }
     249             : 
     250             : protected:
     251           0 :   virtual ~NotificationPermissionRequest() {}
     252             : 
     253             :   nsresult ResolvePromise();
     254             :   nsresult DispatchResolvePromise();
     255             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     256             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
     257             :   NotificationPermission mPermission;
     258             :   RefPtr<Promise> mPromise;
     259             :   RefPtr<NotificationPermissionCallback> mCallback;
     260             :   nsCOMPtr<nsIContentPermissionRequester> mRequester;
     261             : };
     262             : 
     263             : namespace {
     264           0 : class ReleaseNotificationControlRunnable final : public MainThreadWorkerControlRunnable
     265             : {
     266             :   Notification* mNotification;
     267             : 
     268             : public:
     269           0 :   explicit ReleaseNotificationControlRunnable(Notification* aNotification)
     270           0 :     : MainThreadWorkerControlRunnable(aNotification->mWorkerPrivate)
     271           0 :     , mNotification(aNotification)
     272           0 :   { }
     273             : 
     274             :   bool
     275           0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
     276             :   {
     277           0 :     mNotification->ReleaseObject();
     278           0 :     return true;
     279             :   }
     280             : };
     281             : 
     282           0 : class GetPermissionRunnable final : public WorkerMainThreadRunnable
     283             : {
     284             :   NotificationPermission mPermission;
     285             : 
     286             : public:
     287           0 :   explicit GetPermissionRunnable(WorkerPrivate* aWorker)
     288           0 :     : WorkerMainThreadRunnable(aWorker,
     289           0 :                                NS_LITERAL_CSTRING("Notification :: Get Permission"))
     290           0 :     , mPermission(NotificationPermission::Denied)
     291           0 :   { }
     292             : 
     293             :   bool
     294           0 :   MainThreadRun() override
     295             :   {
     296           0 :     ErrorResult result;
     297           0 :     mPermission =
     298           0 :       Notification::GetPermissionInternal(mWorkerPrivate->GetPrincipal(),
     299             :                                           result);
     300           0 :     return true;
     301             :   }
     302             : 
     303             :   NotificationPermission
     304           0 :   GetPermission()
     305             :   {
     306           0 :     return mPermission;
     307             :   }
     308             : };
     309             : 
     310           0 : class FocusWindowRunnable final : public Runnable
     311             : {
     312             :   nsMainThreadPtrHandle<nsPIDOMWindowInner> mWindow;
     313             : public:
     314           0 :   explicit FocusWindowRunnable(const nsMainThreadPtrHandle<nsPIDOMWindowInner>& aWindow)
     315           0 :     : Runnable("FocusWindowRunnable")
     316           0 :     , mWindow(aWindow)
     317           0 :   { }
     318             : 
     319             :   NS_IMETHOD
     320           0 :   Run() override
     321             :   {
     322           0 :     AssertIsOnMainThread();
     323           0 :     if (!mWindow->IsCurrentInnerWindow()) {
     324             :       // Window has been closed, this observer is not valid anymore
     325           0 :       return NS_OK;
     326             :     }
     327             : 
     328             :     // Browser UI may use DOMWindowFocus to focus the tab
     329             :     // from which the event was dispatched.
     330           0 :     nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
     331             : 
     332           0 :     return NS_OK;
     333             :   }
     334             : };
     335             : 
     336             : nsresult
     337           0 : CheckScope(nsIPrincipal* aPrincipal, const nsACString& aScope)
     338             : {
     339           0 :   AssertIsOnMainThread();
     340           0 :   MOZ_ASSERT(aPrincipal);
     341             : 
     342           0 :   nsCOMPtr<nsIURI> scopeURI;
     343           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
     344           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     345           0 :     return rv;
     346             :   }
     347             : 
     348           0 :   return aPrincipal->CheckMayLoad(scopeURI, /* report = */ true,
     349           0 :                                   /* allowIfInheritsPrincipal = */ false);
     350             : }
     351             : } // anonymous namespace
     352             : 
     353             : // Subclass that can be directly dispatched to child workers from the main
     354             : // thread.
     355           0 : class NotificationWorkerRunnable : public MainThreadWorkerRunnable
     356             : {
     357             : protected:
     358           0 :   explicit NotificationWorkerRunnable(WorkerPrivate* aWorkerPrivate)
     359           0 :     : MainThreadWorkerRunnable(aWorkerPrivate)
     360             :   {
     361           0 :   }
     362             : 
     363             :   bool
     364           0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
     365             :   {
     366           0 :     aWorkerPrivate->AssertIsOnWorkerThread();
     367           0 :     aWorkerPrivate->ModifyBusyCountFromWorker(true);
     368           0 :     WorkerRunInternal(aWorkerPrivate);
     369           0 :     return true;
     370             :   }
     371             : 
     372             :   void
     373           0 :   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     374             :           bool aRunResult) override
     375             :   {
     376           0 :     aWorkerPrivate->ModifyBusyCountFromWorker(false);
     377           0 :   }
     378             : 
     379             :   virtual void
     380             :   WorkerRunInternal(WorkerPrivate* aWorkerPrivate) = 0;
     381             : };
     382             : 
     383             : // Overrides dispatch and run handlers so we can directly dispatch from main
     384             : // thread to child workers.
     385           0 : class NotificationEventWorkerRunnable final : public NotificationWorkerRunnable
     386             : {
     387             :   Notification* mNotification;
     388             :   const nsString mEventName;
     389             : public:
     390           0 :   NotificationEventWorkerRunnable(Notification* aNotification,
     391             :                                   const nsString& aEventName)
     392           0 :     : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
     393             :     , mNotification(aNotification)
     394           0 :     , mEventName(aEventName)
     395           0 :   {}
     396             : 
     397             :   void
     398           0 :   WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
     399             :   {
     400           0 :     mNotification->DispatchTrustedEvent(mEventName);
     401           0 :   }
     402             : };
     403             : 
     404           0 : class ReleaseNotificationRunnable final : public NotificationWorkerRunnable
     405             : {
     406             :   Notification* mNotification;
     407             : public:
     408           0 :   explicit ReleaseNotificationRunnable(Notification* aNotification)
     409           0 :     : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
     410           0 :     , mNotification(aNotification)
     411           0 :   {}
     412             : 
     413             :   void
     414           0 :   WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
     415             :   {
     416           0 :     mNotification->ReleaseObject();
     417           0 :   }
     418             : };
     419             : 
     420             : // Create one whenever you require ownership of the notification. Use with
     421             : // UniquePtr<>. See Notification.h for details.
     422             : class NotificationRef final {
     423             :   friend class WorkerNotificationObserver;
     424             : 
     425             : private:
     426             :   Notification* mNotification;
     427             :   bool mInited;
     428             : 
     429             :   // Only useful for workers.
     430             :   void
     431           0 :   Forget()
     432             :   {
     433           0 :     mNotification = nullptr;
     434           0 :   }
     435             : 
     436             : public:
     437           0 :   explicit NotificationRef(Notification* aNotification)
     438           0 :     : mNotification(aNotification)
     439             :   {
     440           0 :     MOZ_ASSERT(mNotification);
     441           0 :     if (mNotification->mWorkerPrivate) {
     442           0 :       mNotification->mWorkerPrivate->AssertIsOnWorkerThread();
     443             :     } else {
     444           0 :       AssertIsOnMainThread();
     445             :     }
     446             : 
     447           0 :     mInited = mNotification->AddRefObject();
     448           0 :   }
     449             : 
     450             :   // This is only required because Gecko runs script in a worker's onclose
     451             :   // handler (non-standard, Bug 790919) where calls to HoldWorker() will
     452             :   // fail. Due to non-standardness and added complications if we decide to
     453             :   // support this, attempts to create a Notification in onclose just throw
     454             :   // exceptions.
     455             :   bool
     456           0 :   Initialized()
     457             :   {
     458           0 :     return mInited;
     459             :   }
     460             : 
     461           0 :   ~NotificationRef()
     462           0 :   {
     463           0 :     if (Initialized() && mNotification) {
     464           0 :       Notification* notification = mNotification;
     465           0 :       mNotification = nullptr;
     466           0 :       if (notification->mWorkerPrivate && NS_IsMainThread()) {
     467             :         // Try to pass ownership back to the worker. If the dispatch succeeds we
     468             :         // are guaranteed this runnable will run, and that it will run after queued
     469             :         // event runnables, so event runnables will have a safe pointer to the
     470             :         // Notification.
     471             :         //
     472             :         // If the dispatch fails, the worker isn't running anymore and the event
     473             :         // runnables have already run or been canceled. We can use a control
     474             :         // runnable to release the reference.
     475             :         RefPtr<ReleaseNotificationRunnable> r =
     476           0 :           new ReleaseNotificationRunnable(notification);
     477             : 
     478           0 :         if (!r->Dispatch()) {
     479             :           RefPtr<ReleaseNotificationControlRunnable> r =
     480           0 :             new ReleaseNotificationControlRunnable(notification);
     481           0 :           MOZ_ALWAYS_TRUE(r->Dispatch());
     482             :         }
     483             :       } else {
     484           0 :         notification->AssertIsOnTargetThread();
     485           0 :         notification->ReleaseObject();
     486             :       }
     487             :     }
     488           0 :   }
     489             : 
     490             :   // XXXnsm, is it worth having some sort of WeakPtr like wrapper instead of
     491             :   // a rawptr that the NotificationRef can invalidate?
     492             :   Notification*
     493           0 :   GetNotification()
     494             :   {
     495           0 :     MOZ_ASSERT(Initialized());
     496           0 :     return mNotification;
     497             :   }
     498             : };
     499             : 
     500             : class NotificationTask : public Runnable
     501             : {
     502             : public:
     503             :   enum NotificationAction {
     504             :     eShow,
     505             :     eClose
     506             :   };
     507             : 
     508           0 :   NotificationTask(const char* aName, UniquePtr<NotificationRef> aRef,
     509             :                    NotificationAction aAction)
     510           0 :     : Runnable(aName)
     511           0 :     , mNotificationRef(Move(aRef)), mAction(aAction)
     512           0 :   {}
     513             : 
     514             :   NS_IMETHOD
     515             :   Run() override;
     516             : protected:
     517           0 :   virtual ~NotificationTask() {}
     518             : 
     519             :   UniquePtr<NotificationRef> mNotificationRef;
     520             :   NotificationAction mAction;
     521             : };
     522             : 
     523             : uint32_t Notification::sCount = 0;
     524             : 
     525           0 : NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise,
     526             :                                                         mCallback)
     527             : 
     528           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
     529           0 :   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
     530           0 :   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
     531           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
     532           0 : NS_INTERFACE_MAP_END
     533             : 
     534           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
     535           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
     536             : 
     537             : NS_IMETHODIMP
     538           0 : NotificationPermissionRequest::Run()
     539             : {
     540           0 :   if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
     541           0 :     mPermission = NotificationPermission::Granted;
     542             :   } else {
     543             :     // File are automatically granted permission.
     544           0 :     nsCOMPtr<nsIURI> uri;
     545           0 :     mPrincipal->GetURI(getter_AddRefs(uri));
     546             : 
     547           0 :     if (uri) {
     548             :       bool isFile;
     549           0 :       uri->SchemeIs("file", &isFile);
     550           0 :       if (isFile) {
     551           0 :         mPermission = NotificationPermission::Granted;
     552             :       }
     553             :     }
     554             :   }
     555             : 
     556             :   // Grant permission if pref'ed on.
     557           0 :   if (Preferences::GetBool("notification.prompt.testing", false)) {
     558           0 :     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
     559           0 :       mPermission = NotificationPermission::Granted;
     560             :     } else {
     561           0 :       mPermission = NotificationPermission::Denied;
     562             :     }
     563             :   }
     564             : 
     565           0 :   if (mPermission != NotificationPermission::Default) {
     566           0 :     return DispatchResolvePromise();
     567             :   }
     568             : 
     569           0 :   return nsContentPermissionUtils::AskPermission(this, mWindow);
     570             : }
     571             : 
     572             : NS_IMETHODIMP
     573           0 : NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
     574             : {
     575           0 :   NS_ADDREF(*aRequestingPrincipal = mPrincipal);
     576           0 :   return NS_OK;
     577             : }
     578             : 
     579             : NS_IMETHODIMP
     580           0 : NotificationPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
     581             : {
     582           0 :   NS_ADDREF(*aRequestingWindow = mWindow);
     583           0 :   return NS_OK;
     584             : }
     585             : 
     586             : NS_IMETHODIMP
     587           0 : NotificationPermissionRequest::GetElement(nsIDOMElement** aElement)
     588             : {
     589           0 :   NS_ENSURE_ARG_POINTER(aElement);
     590           0 :   *aElement = nullptr;
     591           0 :   return NS_OK;
     592             : }
     593             : 
     594             : NS_IMETHODIMP
     595           0 : NotificationPermissionRequest::Cancel()
     596             : {
     597             :   // `Cancel` is called if the user denied permission or dismissed the
     598             :   // permission request. To distinguish between the two, we set the
     599             :   // permission to "default" and query the permission manager in
     600             :   // `ResolvePromise`.
     601           0 :   mPermission = NotificationPermission::Default;
     602           0 :   return DispatchResolvePromise();
     603             : }
     604             : 
     605             : NS_IMETHODIMP
     606           0 : NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
     607             : {
     608           0 :   MOZ_ASSERT(aChoices.isUndefined());
     609             : 
     610           0 :   mPermission = NotificationPermission::Granted;
     611           0 :   return DispatchResolvePromise();
     612             : }
     613             : 
     614             : NS_IMETHODIMP
     615           0 : NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
     616             : {
     617           0 :   NS_ENSURE_ARG_POINTER(aRequester);
     618             : 
     619           0 :   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
     620           0 :   requester.forget(aRequester);
     621           0 :   return NS_OK;
     622             : }
     623             : 
     624             : inline nsresult
     625           0 : NotificationPermissionRequest::DispatchResolvePromise()
     626             : {
     627             :   nsCOMPtr<nsIRunnable> resolver =
     628           0 :     NewRunnableMethod("NotificationPermissionRequest::DispatchResolvePromise",
     629           0 :                       this, &NotificationPermissionRequest::ResolvePromise);
     630           0 :   if (nsIEventTarget* target = mWindow->EventTargetFor(TaskCategory::Other)) {
     631           0 :     return target->Dispatch(resolver.forget(), nsIEventTarget::DISPATCH_NORMAL);
     632             :   }
     633           0 :   return NS_ERROR_FAILURE;
     634             : }
     635             : 
     636             : nsresult
     637           0 : NotificationPermissionRequest::ResolvePromise()
     638             : {
     639           0 :   nsresult rv = NS_OK;
     640           0 :   if (mPermission == NotificationPermission::Default) {
     641             :     // This will still be "default" if the user dismissed the doorhanger,
     642             :     // or "denied" otherwise.
     643           0 :     mPermission = Notification::TestPermission(mPrincipal);
     644             :   }
     645           0 :   if (mCallback) {
     646           0 :     ErrorResult error;
     647           0 :     mCallback->Call(mPermission, error);
     648           0 :     rv = error.StealNSResult();
     649             :   }
     650           0 :   mPromise->MaybeResolve(mPermission);
     651           0 :   return rv;
     652             : }
     653             : 
     654             : NS_IMETHODIMP
     655           0 : NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
     656             : {
     657           0 :   nsTArray<nsString> emptyOptions;
     658           0 :   return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
     659           0 :                                                          NS_LITERAL_CSTRING("unused"),
     660             :                                                          emptyOptions,
     661           0 :                                                          aTypes);
     662             : }
     663             : 
     664          13 : NS_IMPL_ISUPPORTS(NotificationTelemetryService, nsISupports)
     665             : 
     666           1 : NotificationTelemetryService::NotificationTelemetryService()
     667           1 :   : mDNDRecorded(false)
     668           1 : {}
     669             : 
     670           0 : NotificationTelemetryService::~NotificationTelemetryService()
     671           0 : {}
     672             : 
     673             : /* static */ already_AddRefed<NotificationTelemetryService>
     674           0 : NotificationTelemetryService::GetInstance()
     675             : {
     676             :   nsCOMPtr<nsISupports> telemetrySupports =
     677           0 :     do_GetService(NOTIFICATIONTELEMETRYSERVICE_CONTRACTID);
     678           0 :   if (!telemetrySupports) {
     679           0 :     return nullptr;
     680             :   }
     681             :   RefPtr<NotificationTelemetryService> telemetry =
     682           0 :     static_cast<NotificationTelemetryService*>(telemetrySupports.get());
     683           0 :   return telemetry.forget();
     684             : }
     685             : 
     686             : nsresult
     687           1 : NotificationTelemetryService::Init()
     688             : {
     689             :   // Only perform permissions telemetry collection in the parent process.
     690           1 :   if (!XRE_IsParentProcess()) {
     691           0 :     return NS_OK;
     692             :   }
     693             : 
     694           1 :   RecordPermissions();
     695             : 
     696           1 :   return NS_OK;
     697             : }
     698             : 
     699             : void
     700           1 : NotificationTelemetryService::RecordPermissions()
     701             : {
     702           1 :   MOZ_ASSERT(XRE_IsParentProcess(),
     703             :              "RecordPermissions may only be called in the parent process");
     704             : 
     705           1 :   if (!Telemetry::CanRecordBase() || !Telemetry::CanRecordExtended()) {
     706           0 :     return;
     707             :   }
     708             : 
     709             :   nsCOMPtr<nsIPermissionManager> permissionManager =
     710           2 :     services::GetPermissionManager();
     711           1 :   if (!permissionManager) {
     712           0 :     return;
     713             :   }
     714             : 
     715           2 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
     716           1 :   nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
     717           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     718           0 :     return;
     719             :   }
     720             : 
     721             :   for (;;) {
     722             :     bool hasMoreElements;
     723          11 :     nsresult rv = enumerator->HasMoreElements(&hasMoreElements);
     724          11 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     725           0 :       return;
     726             :     }
     727          11 :     if (!hasMoreElements) {
     728           1 :       break;
     729             :     }
     730          10 :     nsCOMPtr<nsISupports> supportsPermission;
     731          10 :     rv = enumerator->GetNext(getter_AddRefs(supportsPermission));
     732          10 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     733           0 :       return;
     734             :     }
     735             :     uint32_t capability;
     736          10 :     if (!GetNotificationPermission(supportsPermission, &capability)) {
     737          10 :       continue;
     738             :     }
     739           0 :     if (capability == nsIPermissionManager::DENY_ACTION) {
     740           0 :       Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_PERMISSIONS, 0);
     741           0 :     } else if (capability == nsIPermissionManager::ALLOW_ACTION) {
     742           0 :       Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_PERMISSIONS, 1);
     743             :     }
     744          10 :   }
     745             : }
     746             : 
     747             : bool
     748          10 : NotificationTelemetryService::GetNotificationPermission(nsISupports* aSupports,
     749             :                                                         uint32_t* aCapability)
     750             : {
     751          20 :   nsCOMPtr<nsIPermission> permission = do_QueryInterface(aSupports);
     752          10 :   if (!permission) {
     753           0 :     return false;
     754             :   }
     755          20 :   nsAutoCString type;
     756          10 :   permission->GetType(type);
     757          10 :   if (!type.Equals("desktop-notification")) {
     758          10 :     return false;
     759             :   }
     760           0 :   permission->GetCapability(aCapability);
     761           0 :   return true;
     762             : }
     763             : 
     764             : void
     765           0 : NotificationTelemetryService::RecordDNDSupported()
     766             : {
     767           0 :   if (mDNDRecorded) {
     768           0 :     return;
     769             :   }
     770             : 
     771             :   nsCOMPtr<nsIAlertsService> alertService =
     772           0 :     do_GetService(NS_ALERTSERVICE_CONTRACTID);
     773           0 :   if (!alertService) {
     774           0 :     return;
     775             :   }
     776             : 
     777             :   nsCOMPtr<nsIAlertsDoNotDisturb> alertServiceDND =
     778           0 :     do_QueryInterface(alertService);
     779           0 :   if (!alertServiceDND) {
     780           0 :     return;
     781             :   }
     782             : 
     783           0 :   mDNDRecorded = true;
     784             :   bool isEnabled;
     785           0 :   nsresult rv = alertServiceDND->GetManualDoNotDisturb(&isEnabled);
     786           0 :   if (NS_FAILED(rv)) {
     787           0 :     return;
     788             :   }
     789             : 
     790             :   Telemetry::Accumulate(
     791           0 :     Telemetry::ALERTS_SERVICE_DND_SUPPORTED_FLAG, true);
     792             : }
     793             : 
     794             : // Observer that the alert service calls to do common tasks and/or dispatch to the
     795             : // specific observer for the context e.g. main thread, worker, or service worker.
     796             : class NotificationObserver final : public nsIObserver
     797             : {
     798             : public:
     799             :   nsCOMPtr<nsIObserver> mObserver;
     800             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     801             :   bool mInPrivateBrowsing;
     802             :   NS_DECL_ISUPPORTS
     803             :   NS_DECL_NSIOBSERVER
     804             : 
     805           0 :   NotificationObserver(nsIObserver* aObserver, nsIPrincipal* aPrincipal,
     806             :                        bool aInPrivateBrowsing)
     807           0 :     : mObserver(aObserver), mPrincipal(aPrincipal),
     808           0 :       mInPrivateBrowsing(aInPrivateBrowsing)
     809             :   {
     810           0 :     AssertIsOnMainThread();
     811           0 :     MOZ_ASSERT(mObserver);
     812           0 :     MOZ_ASSERT(mPrincipal);
     813           0 :   }
     814             : 
     815             : protected:
     816           0 :   virtual ~NotificationObserver()
     817           0 :   {
     818           0 :     AssertIsOnMainThread();
     819           0 :   }
     820             : 
     821             :   nsresult AdjustPushQuota(const char* aTopic);
     822             : };
     823             : 
     824           0 : NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
     825             : 
     826             : class MainThreadNotificationObserver : public nsIObserver
     827             : {
     828             : public:
     829             :   UniquePtr<NotificationRef> mNotificationRef;
     830             :   NS_DECL_ISUPPORTS
     831             :   NS_DECL_NSIOBSERVER
     832             : 
     833           0 :   explicit MainThreadNotificationObserver(UniquePtr<NotificationRef> aRef)
     834           0 :     : mNotificationRef(Move(aRef))
     835             :   {
     836           0 :     AssertIsOnMainThread();
     837           0 :   }
     838             : 
     839             : protected:
     840           0 :   virtual ~MainThreadNotificationObserver()
     841           0 :   {
     842           0 :     AssertIsOnMainThread();
     843           0 :   }
     844             : };
     845             : 
     846           0 : NS_IMPL_ISUPPORTS(MainThreadNotificationObserver, nsIObserver)
     847             : 
     848             : NS_IMETHODIMP
     849           0 : NotificationTask::Run()
     850             : {
     851           0 :   AssertIsOnMainThread();
     852             : 
     853             :   // Get a pointer to notification before the notification takes ownership of
     854             :   // the ref (it owns itself temporarily, with ShowInternal() and
     855             :   // CloseInternal() passing on the ownership appropriately.)
     856           0 :   Notification* notif = mNotificationRef->GetNotification();
     857           0 :   notif->mTempRef.swap(mNotificationRef);
     858           0 :   if (mAction == eShow) {
     859           0 :     notif->ShowInternal();
     860           0 :   } else if (mAction == eClose) {
     861           0 :     notif->CloseInternal();
     862             :   } else {
     863           0 :     MOZ_CRASH("Invalid action");
     864             :   }
     865             : 
     866           0 :   MOZ_ASSERT(!mNotificationRef);
     867           0 :   return NS_OK;
     868             : }
     869             : 
     870             : bool
     871           1 : Notification::RequireInteractionEnabled(JSContext* aCx, JSObject* aOjb)
     872             : {
     873           1 :   if (NS_IsMainThread()) {
     874           0 :     return Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false);
     875             :   }
     876             : 
     877           1 :   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
     878           1 :   if (!workerPrivate) {
     879           0 :     return false;
     880             :   }
     881             : 
     882           1 :   return workerPrivate->DOMWorkerNotificationRIEnabled();
     883             : }
     884             : 
     885             : // static
     886             : bool
     887           1 : Notification::PrefEnabled(JSContext* aCx, JSObject* aObj)
     888             : {
     889           1 :   if (NS_IsMainThread()) {
     890           0 :     return Preferences::GetBool("dom.webnotifications.enabled", false);
     891             :   }
     892             : 
     893           1 :   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
     894           1 :   if (!workerPrivate) {
     895           0 :     return false;
     896             :   }
     897             : 
     898           1 :   if (workerPrivate->IsServiceWorker()) {
     899           0 :     return workerPrivate->DOMServiceWorkerNotificationEnabled();
     900             :   }
     901             : 
     902           1 :   return workerPrivate->DOMWorkerNotificationEnabled();
     903             : }
     904             : 
     905             : // static
     906             : bool
     907           1 : Notification::IsGetEnabled(JSContext* aCx, JSObject* aObj)
     908             : {
     909           1 :   return NS_IsMainThread();
     910             : }
     911             : 
     912           0 : Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
     913             :                            const nsAString& aTitle, const nsAString& aBody,
     914             :                            NotificationDirection aDir, const nsAString& aLang,
     915             :                            const nsAString& aTag, const nsAString& aIconUrl,
     916             :                            bool aRequireInteraction,
     917           0 :                            const NotificationBehavior& aBehavior)
     918             :   : DOMEventTargetHelper(),
     919             :     mWorkerPrivate(nullptr), mObserver(nullptr),
     920             :     mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
     921             :     mTag(aTag), mIconUrl(aIconUrl), mRequireInteraction(aRequireInteraction),
     922           0 :     mBehavior(aBehavior), mData(JS::NullValue()),
     923           0 :     mIsClosed(false), mIsStored(false), mTaskCount(0)
     924             : {
     925           0 :   if (NS_IsMainThread()) {
     926             :     // We can only call this on the main thread because
     927             :     // Event::SetEventType() called down the call chain when dispatching events
     928             :     // using DOMEventTargetHelper::DispatchTrustedEvent() will assume the event
     929             :     // is a main thread event if it has a valid owner. It will then attempt to
     930             :     // fetch the atom for the event name which asserts main thread only.
     931           0 :     BindToOwner(aGlobal);
     932             :   } else {
     933           0 :     mWorkerPrivate = GetCurrentThreadWorkerPrivate();
     934           0 :     MOZ_ASSERT(mWorkerPrivate);
     935             :   }
     936           0 : }
     937             : 
     938             : nsresult
     939           0 : Notification::Init()
     940             : {
     941           0 :   if (!mWorkerPrivate) {
     942           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     943           0 :     NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
     944             : 
     945           0 :     nsresult rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
     946           0 :     NS_ENSURE_SUCCESS(rv, rv);
     947             : 
     948           0 :     rv = obs->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
     949           0 :     NS_ENSURE_SUCCESS(rv, rv);
     950             :   }
     951             : 
     952           0 :   return NS_OK;
     953             : }
     954             : 
     955             : void
     956           0 : Notification::SetAlertName()
     957             : {
     958           0 :   AssertIsOnMainThread();
     959           0 :   if (!mAlertName.IsEmpty()) {
     960           0 :     return;
     961             :   }
     962             : 
     963           0 :   nsAutoString alertName;
     964           0 :   nsresult rv = GetOrigin(GetPrincipal(), alertName);
     965           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     966           0 :     return;
     967             :   }
     968             : 
     969             :   // Get the notification name that is unique per origin + tag/ID.
     970             :   // The name of the alert is of the form origin#tag/ID.
     971           0 :   alertName.Append('#');
     972           0 :   if (!mTag.IsEmpty()) {
     973           0 :     alertName.AppendLiteral("tag:");
     974           0 :     alertName.Append(mTag);
     975             :   } else {
     976           0 :     alertName.AppendLiteral("notag:");
     977           0 :     alertName.Append(mID);
     978             :   }
     979             : 
     980           0 :   mAlertName = alertName;
     981             : }
     982             : 
     983             : // May be called on any thread.
     984             : // static
     985             : already_AddRefed<Notification>
     986           0 : Notification::Constructor(const GlobalObject& aGlobal,
     987             :                           const nsAString& aTitle,
     988             :                           const NotificationOptions& aOptions,
     989             :                           ErrorResult& aRv)
     990             : {
     991             :   // FIXME(nsm): If the sticky flag is set, throw an error.
     992           0 :   RefPtr<ServiceWorkerGlobalScope> scope;
     993           0 :   UNWRAP_OBJECT(ServiceWorkerGlobalScope, aGlobal.Get(), scope);
     994           0 :   if (scope) {
     995           0 :     aRv.ThrowTypeError<MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER>();
     996           0 :     return nullptr;
     997             :   }
     998             : 
     999           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    1000             :   RefPtr<Notification> notification =
    1001           0 :     CreateAndShow(aGlobal.Context(), global, aTitle, aOptions,
    1002           0 :                   EmptyString(), aRv);
    1003           0 :   if (NS_WARN_IF(aRv.Failed())) {
    1004           0 :     return nullptr;
    1005             :   }
    1006             : 
    1007             :   // This is be ok since we are on the worker thread where this function will
    1008             :   // run to completion before the Notification has a chance to go away.
    1009           0 :   return notification.forget();
    1010             : }
    1011             : 
    1012             : // static
    1013             : already_AddRefed<Notification>
    1014           0 : Notification::ConstructFromFields(
    1015             :     nsIGlobalObject* aGlobal,
    1016             :     const nsAString& aID,
    1017             :     const nsAString& aTitle,
    1018             :     const nsAString& aDir,
    1019             :     const nsAString& aLang,
    1020             :     const nsAString& aBody,
    1021             :     const nsAString& aTag,
    1022             :     const nsAString& aIcon,
    1023             :     const nsAString& aData,
    1024             :     const nsAString& aServiceWorkerRegistrationScope,
    1025             :     ErrorResult& aRv)
    1026             : {
    1027           0 :   MOZ_ASSERT(aGlobal);
    1028             : 
    1029           0 :   RootedDictionary<NotificationOptions> options(RootingCx());
    1030           0 :   options.mDir = Notification::StringToDirection(nsString(aDir));
    1031           0 :   options.mLang = aLang;
    1032           0 :   options.mBody = aBody;
    1033           0 :   options.mTag = aTag;
    1034           0 :   options.mIcon = aIcon;
    1035           0 :   RefPtr<Notification> notification = CreateInternal(aGlobal, aID, aTitle,
    1036           0 :                                                      options);
    1037             : 
    1038           0 :   notification->InitFromBase64(aData, aRv);
    1039           0 :   if (NS_WARN_IF(aRv.Failed())) {
    1040           0 :     return nullptr;
    1041             :   }
    1042             : 
    1043           0 :   notification->SetScope(aServiceWorkerRegistrationScope);
    1044             : 
    1045           0 :   return notification.forget();
    1046             : }
    1047             : 
    1048             : nsresult
    1049           0 : Notification::PersistNotification()
    1050             : {
    1051           0 :   AssertIsOnMainThread();
    1052             :   nsresult rv;
    1053             :   nsCOMPtr<nsINotificationStorage> notificationStorage =
    1054           0 :     do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
    1055           0 :   if (NS_FAILED(rv)) {
    1056           0 :     return rv;
    1057             :   }
    1058             : 
    1059           0 :   nsString origin;
    1060           0 :   rv = GetOrigin(GetPrincipal(), origin);
    1061           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1062           0 :     return rv;
    1063             :   }
    1064             : 
    1065           0 :   nsString id;
    1066           0 :   GetID(id);
    1067             : 
    1068           0 :   nsString alertName;
    1069           0 :   GetAlertName(alertName);
    1070             : 
    1071           0 :   nsAutoString behavior;
    1072           0 :   if (!mBehavior.ToJSON(behavior)) {
    1073           0 :     return NS_ERROR_FAILURE;
    1074             :   }
    1075             : 
    1076           0 :   rv = notificationStorage->Put(origin,
    1077             :                                 id,
    1078             :                                 mTitle,
    1079           0 :                                 DirectionToString(mDir),
    1080             :                                 mLang,
    1081             :                                 mBody,
    1082             :                                 mTag,
    1083             :                                 mIconUrl,
    1084             :                                 alertName,
    1085             :                                 mDataAsBase64,
    1086             :                                 behavior,
    1087           0 :                                 mScope);
    1088             : 
    1089           0 :   if (NS_FAILED(rv)) {
    1090           0 :     return rv;
    1091             :   }
    1092             : 
    1093           0 :   SetStoredState(true);
    1094           0 :   return NS_OK;
    1095             : }
    1096             : 
    1097             : void
    1098           0 : Notification::UnpersistNotification()
    1099             : {
    1100           0 :   AssertIsOnMainThread();
    1101           0 :   if (IsStored()) {
    1102             :     nsCOMPtr<nsINotificationStorage> notificationStorage =
    1103           0 :       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
    1104           0 :     if (notificationStorage) {
    1105           0 :       nsString origin;
    1106           0 :       nsresult rv = GetOrigin(GetPrincipal(), origin);
    1107           0 :       if (NS_SUCCEEDED(rv)) {
    1108           0 :         notificationStorage->Delete(origin, mID);
    1109             :       }
    1110             :     }
    1111           0 :     SetStoredState(false);
    1112             :   }
    1113           0 : }
    1114             : 
    1115             : already_AddRefed<Notification>
    1116           0 : Notification::CreateInternal(nsIGlobalObject* aGlobal,
    1117             :                              const nsAString& aID,
    1118             :                              const nsAString& aTitle,
    1119             :                              const NotificationOptions& aOptions)
    1120             : {
    1121             :   nsresult rv;
    1122           0 :   nsString id;
    1123           0 :   if (!aID.IsEmpty()) {
    1124           0 :     id = aID;
    1125             :   } else {
    1126             :     nsCOMPtr<nsIUUIDGenerator> uuidgen =
    1127           0 :       do_GetService("@mozilla.org/uuid-generator;1");
    1128           0 :     NS_ENSURE_TRUE(uuidgen, nullptr);
    1129             :     nsID uuid;
    1130           0 :     rv = uuidgen->GenerateUUIDInPlace(&uuid);
    1131           0 :     NS_ENSURE_SUCCESS(rv, nullptr);
    1132             : 
    1133             :     char buffer[NSID_LENGTH];
    1134           0 :     uuid.ToProvidedString(buffer);
    1135           0 :     NS_ConvertASCIItoUTF16 convertedID(buffer);
    1136           0 :     id = convertedID;
    1137             :   }
    1138             : 
    1139             :   RefPtr<Notification> notification = new Notification(aGlobal, id, aTitle,
    1140             :                                                          aOptions.mBody,
    1141           0 :                                                          aOptions.mDir,
    1142             :                                                          aOptions.mLang,
    1143             :                                                          aOptions.mTag,
    1144             :                                                          aOptions.mIcon,
    1145           0 :                                                          aOptions.mRequireInteraction,
    1146           0 :                                                          aOptions.mMozbehavior);
    1147           0 :   rv = notification->Init();
    1148           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1149           0 :   return notification.forget();
    1150             : }
    1151             : 
    1152           0 : Notification::~Notification()
    1153             : {
    1154           0 :   mData.setUndefined();
    1155           0 :   mozilla::DropJSObjects(this);
    1156           0 :   AssertIsOnTargetThread();
    1157           0 :   MOZ_ASSERT(!mWorkerHolder);
    1158           0 :   MOZ_ASSERT(!mTempRef);
    1159           0 : }
    1160             : 
    1161             : NS_IMPL_CYCLE_COLLECTION_CLASS(Notification)
    1162           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
    1163           0 :   tmp->mData.setUndefined();
    1164           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1165             : 
    1166           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
    1167           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1168             : 
    1169           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
    1170           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
    1171           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
    1172             : 
    1173           0 : NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
    1174           0 : NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
    1175             : 
    1176           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
    1177           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
    1178           0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1179           0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    1180             : 
    1181             : nsIPrincipal*
    1182           0 : Notification::GetPrincipal()
    1183             : {
    1184           0 :   AssertIsOnMainThread();
    1185           0 :   if (mWorkerPrivate) {
    1186           0 :     return mWorkerPrivate->GetPrincipal();
    1187             :   } else {
    1188           0 :     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
    1189           0 :     NS_ENSURE_TRUE(sop, nullptr);
    1190           0 :     return sop->GetPrincipal();
    1191             :   }
    1192             : }
    1193             : 
    1194             : class WorkerNotificationObserver final : public MainThreadNotificationObserver
    1195             : {
    1196             : public:
    1197             :   NS_DECL_ISUPPORTS_INHERITED
    1198             :   NS_DECL_NSIOBSERVER
    1199             : 
    1200           0 :   explicit WorkerNotificationObserver(UniquePtr<NotificationRef> aRef)
    1201           0 :     : MainThreadNotificationObserver(Move(aRef))
    1202             :   {
    1203           0 :     AssertIsOnMainThread();
    1204           0 :     MOZ_ASSERT(mNotificationRef->GetNotification()->mWorkerPrivate);
    1205           0 :   }
    1206             : 
    1207             :   void
    1208           0 :   ForgetNotification()
    1209             :   {
    1210           0 :     AssertIsOnMainThread();
    1211           0 :     mNotificationRef->Forget();
    1212           0 :   }
    1213             : 
    1214             : protected:
    1215           0 :   virtual ~WorkerNotificationObserver()
    1216           0 :   {
    1217           0 :     AssertIsOnMainThread();
    1218             : 
    1219           0 :     MOZ_ASSERT(mNotificationRef);
    1220           0 :     Notification* notification = mNotificationRef->GetNotification();
    1221           0 :     if (notification) {
    1222           0 :       notification->mObserver = nullptr;
    1223             :     }
    1224           0 :   }
    1225             : };
    1226             : 
    1227           0 : NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, MainThreadNotificationObserver)
    1228             : 
    1229             : class ServiceWorkerNotificationObserver final : public nsIObserver
    1230             : {
    1231             : public:
    1232             :   NS_DECL_ISUPPORTS
    1233             :   NS_DECL_NSIOBSERVER
    1234             : 
    1235           0 :   ServiceWorkerNotificationObserver(const nsAString& aScope,
    1236             :                                     nsIPrincipal* aPrincipal,
    1237             :                                     const nsAString& aID,
    1238             :                                     const nsAString& aTitle,
    1239             :                                     const nsAString& aDir,
    1240             :                                     const nsAString& aLang,
    1241             :                                     const nsAString& aBody,
    1242             :                                     const nsAString& aTag,
    1243             :                                     const nsAString& aIcon,
    1244             :                                     const nsAString& aData,
    1245             :                                     const nsAString& aBehavior)
    1246           0 :     : mScope(aScope), mID(aID), mPrincipal(aPrincipal), mTitle(aTitle)
    1247             :     , mDir(aDir), mLang(aLang), mBody(aBody), mTag(aTag), mIcon(aIcon)
    1248           0 :     , mData(aData), mBehavior(aBehavior)
    1249             :   {
    1250           0 :     AssertIsOnMainThread();
    1251           0 :     MOZ_ASSERT(aPrincipal);
    1252           0 :   }
    1253             : 
    1254             : private:
    1255           0 :   ~ServiceWorkerNotificationObserver()
    1256           0 :   {}
    1257             : 
    1258             :   const nsString mScope;
    1259             :   const nsString mID;
    1260             :   nsCOMPtr<nsIPrincipal> mPrincipal;
    1261             :   const nsString mTitle;
    1262             :   const nsString mDir;
    1263             :   const nsString mLang;
    1264             :   const nsString mBody;
    1265             :   const nsString mTag;
    1266             :   const nsString mIcon;
    1267             :   const nsString mData;
    1268             :   const nsString mBehavior;
    1269             : };
    1270             : 
    1271           0 : NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver)
    1272             : 
    1273             : // For ServiceWorkers.
    1274             : bool
    1275           0 : Notification::DispatchNotificationClickEvent()
    1276             : {
    1277           0 :   MOZ_ASSERT(mWorkerPrivate);
    1278           0 :   MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
    1279           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
    1280             : 
    1281           0 :   NotificationEventInit options;
    1282           0 :   options.mNotification = this;
    1283             : 
    1284           0 :   ErrorResult result;
    1285           0 :   RefPtr<EventTarget> target = mWorkerPrivate->GlobalScope();
    1286             :   RefPtr<NotificationEvent> event =
    1287           0 :     NotificationEvent::Constructor(target,
    1288           0 :                                    NS_LITERAL_STRING("notificationclick"),
    1289             :                                    options,
    1290           0 :                                    result);
    1291           0 :   if (NS_WARN_IF(result.Failed())) {
    1292           0 :     return false;
    1293             :   }
    1294             : 
    1295           0 :   event->SetTrusted(true);
    1296           0 :   WantsPopupControlCheck popupControlCheck(event);
    1297           0 :   target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
    1298             :   // We always return false since in case of dispatching on the serviceworker,
    1299             :   // there is no well defined window to focus. The script may use the
    1300             :   // Client.focus() API if it wishes.
    1301           0 :   return false;
    1302             : }
    1303             : 
    1304             : bool
    1305           0 : Notification::DispatchClickEvent()
    1306             : {
    1307           0 :   AssertIsOnTargetThread();
    1308           0 :   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
    1309           0 :   event->InitEvent(NS_LITERAL_STRING("click"), false, true);
    1310           0 :   event->SetTrusted(true);
    1311           0 :   WantsPopupControlCheck popupControlCheck(event);
    1312           0 :   bool doDefaultAction = true;
    1313           0 :   DispatchEvent(event, &doDefaultAction);
    1314           0 :   return doDefaultAction;
    1315             : }
    1316             : 
    1317             : // Overrides dispatch and run handlers so we can directly dispatch from main
    1318             : // thread to child workers.
    1319           0 : class NotificationClickWorkerRunnable final : public NotificationWorkerRunnable
    1320             : {
    1321             :   Notification* mNotification;
    1322             :   // Optional window that gets focused if click event is not
    1323             :   // preventDefault()ed.
    1324             :   nsMainThreadPtrHandle<nsPIDOMWindowInner> mWindow;
    1325             : public:
    1326           0 :   NotificationClickWorkerRunnable(Notification* aNotification,
    1327             :                                   const nsMainThreadPtrHandle<nsPIDOMWindowInner>& aWindow)
    1328           0 :     : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
    1329             :     , mNotification(aNotification)
    1330           0 :     , mWindow(aWindow)
    1331             :   {
    1332           0 :     MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !mWindow);
    1333           0 :   }
    1334             : 
    1335             :   void
    1336           0 :   WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
    1337             :   {
    1338           0 :     bool doDefaultAction = mNotification->DispatchClickEvent();
    1339           0 :     MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction);
    1340           0 :     if (doDefaultAction) {
    1341           0 :       RefPtr<FocusWindowRunnable> r = new FocusWindowRunnable(mWindow);
    1342           0 :       mWorkerPrivate->DispatchToMainThread(r.forget());
    1343             :     }
    1344           0 :   }
    1345             : };
    1346             : 
    1347             : NS_IMETHODIMP
    1348           0 : NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
    1349             :                               const char16_t* aData)
    1350             : {
    1351           0 :   AssertIsOnMainThread();
    1352             : 
    1353           0 :   if (!strcmp("alertdisablecallback", aTopic)) {
    1354           0 :     Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_MENU, 1);
    1355           0 :     if (XRE_IsParentProcess()) {
    1356           0 :       return Notification::RemovePermission(mPrincipal);
    1357             :     }
    1358             :     // Permissions can't be removed from the content process. Send a message
    1359             :     // to the parent; `ContentParent::RecvDisableNotifications` will call
    1360             :     // `RemovePermission`.
    1361           0 :     ContentChild::GetSingleton()->SendDisableNotifications(
    1362           0 :       IPC::Principal(mPrincipal));
    1363           0 :     return NS_OK;
    1364           0 :   } else if (!strcmp("alertclickcallback", aTopic)) {
    1365           0 :     Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_CLICKED, 1);
    1366           0 :   } else if (!strcmp("alertsettingscallback", aTopic)) {
    1367           0 :     Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_MENU, 2);
    1368           0 :     if (XRE_IsParentProcess()) {
    1369           0 :       return Notification::OpenSettings(mPrincipal);
    1370             :     }
    1371             :     // `ContentParent::RecvOpenNotificationSettings` notifies observers in the
    1372             :     // parent process.
    1373           0 :     ContentChild::GetSingleton()->SendOpenNotificationSettings(
    1374           0 :       IPC::Principal(mPrincipal));
    1375           0 :     return NS_OK;
    1376           0 :   } else if (!strcmp("alertshow", aTopic) ||
    1377           0 :              !strcmp("alertfinished", aTopic)) {
    1378             :     RefPtr<NotificationTelemetryService> telemetry =
    1379           0 :       NotificationTelemetryService::GetInstance();
    1380           0 :     if (telemetry) {
    1381             :       // Record whether "do not disturb" is supported after the first
    1382             :       // notification, to account for falling back to XUL alerts.
    1383           0 :       telemetry->RecordDNDSupported();
    1384             :     }
    1385           0 :     Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
    1386             : 
    1387           0 :     if (!strcmp("alertshow", aTopic)) {
    1388             :       // Record notifications actually shown (e.g. don't count if DND is on).
    1389           0 :       Telemetry::Accumulate(Telemetry::WEB_NOTIFICATION_SHOWN, 1);
    1390             :     }
    1391             :   }
    1392             : 
    1393           0 :   return mObserver->Observe(aSubject, aTopic, aData);
    1394             : }
    1395             : 
    1396             : nsresult
    1397           0 : NotificationObserver::AdjustPushQuota(const char* aTopic)
    1398             : {
    1399             :   nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
    1400           0 :     do_GetService("@mozilla.org/push/Service;1");
    1401           0 :   if (!pushQuotaManager) {
    1402           0 :     return NS_ERROR_FAILURE;
    1403             :   }
    1404             : 
    1405           0 :   nsAutoCString origin;
    1406           0 :   nsresult rv = mPrincipal->GetOrigin(origin);
    1407           0 :   if (NS_FAILED(rv)) {
    1408           0 :     return rv;
    1409             :   }
    1410             : 
    1411           0 :   if (!strcmp("alertshow", aTopic)) {
    1412           0 :     return pushQuotaManager->NotificationForOriginShown(origin.get());
    1413             :   }
    1414           0 :   return pushQuotaManager->NotificationForOriginClosed(origin.get());
    1415             : }
    1416             : 
    1417             : NS_IMETHODIMP
    1418           0 : MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
    1419             :                                         const char16_t* aData)
    1420             : {
    1421           0 :   AssertIsOnMainThread();
    1422           0 :   MOZ_ASSERT(mNotificationRef);
    1423           0 :   Notification* notification = mNotificationRef->GetNotification();
    1424           0 :   MOZ_ASSERT(notification);
    1425           0 :   if (!strcmp("alertclickcallback", aTopic)) {
    1426           0 :     nsCOMPtr<nsPIDOMWindowInner> window = notification->GetOwner();
    1427           0 :     if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
    1428             :       // Window has been closed, this observer is not valid anymore
    1429           0 :       return NS_ERROR_FAILURE;
    1430             :     }
    1431             : 
    1432           0 :     bool doDefaultAction = notification->DispatchClickEvent();
    1433           0 :     if (doDefaultAction) {
    1434             :       // Browser UI may use DOMWindowFocus to focus the tab
    1435             :       // from which the event was dispatched.
    1436           0 :       nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
    1437             :     }
    1438           0 :   } else if (!strcmp("alertfinished", aTopic)) {
    1439           0 :     notification->UnpersistNotification();
    1440           0 :     notification->mIsClosed = true;
    1441           0 :     notification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
    1442           0 :   } else if (!strcmp("alertshow", aTopic)) {
    1443           0 :     notification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
    1444             :   }
    1445           0 :   return NS_OK;
    1446             : }
    1447             : 
    1448             : NS_IMETHODIMP
    1449           0 : WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
    1450             :                                     const char16_t* aData)
    1451             : {
    1452           0 :   AssertIsOnMainThread();
    1453           0 :   MOZ_ASSERT(mNotificationRef);
    1454             :   // For an explanation of why it is OK to pass this rawptr to the event
    1455             :   // runnables, see the Notification class comment.
    1456           0 :   Notification* notification = mNotificationRef->GetNotification();
    1457             :   // We can't assert notification here since the feature could've unset it.
    1458           0 :   if (NS_WARN_IF(!notification)) {
    1459           0 :     return NS_ERROR_FAILURE;
    1460             :   }
    1461             : 
    1462           0 :   MOZ_ASSERT(notification->mWorkerPrivate);
    1463             : 
    1464           0 :   RefPtr<WorkerRunnable> r;
    1465           0 :   if (!strcmp("alertclickcallback", aTopic)) {
    1466           0 :     nsPIDOMWindowInner* window = nullptr;
    1467           0 :     if (!notification->mWorkerPrivate->IsServiceWorker()) {
    1468           0 :       WorkerPrivate* top = notification->mWorkerPrivate;
    1469           0 :       while (top->GetParent()) {
    1470           0 :         top = top->GetParent();
    1471             :       }
    1472             : 
    1473           0 :       window = top->GetWindow();
    1474           0 :       if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
    1475             :         // Window has been closed, this observer is not valid anymore
    1476           0 :         return NS_ERROR_FAILURE;
    1477             :       }
    1478             :     }
    1479             : 
    1480             :     // Instead of bothering with adding features and other worker lifecycle
    1481             :     // management, we simply hold strongrefs to the window and document.
    1482             :     nsMainThreadPtrHandle<nsPIDOMWindowInner> windowHandle(
    1483             :       new nsMainThreadPtrHolder<nsPIDOMWindowInner>(
    1484           0 :         "WorkerNotificationObserver::Observe::nsPIDOMWindowInner", window));
    1485             : 
    1486           0 :     r = new NotificationClickWorkerRunnable(notification, windowHandle);
    1487           0 :   } else if (!strcmp("alertfinished", aTopic)) {
    1488           0 :     notification->UnpersistNotification();
    1489           0 :     notification->mIsClosed = true;
    1490             :     r = new NotificationEventWorkerRunnable(notification,
    1491           0 :                                             NS_LITERAL_STRING("close"));
    1492           0 :   } else if (!strcmp("alertshow", aTopic)) {
    1493             :     r = new NotificationEventWorkerRunnable(notification,
    1494           0 :                                             NS_LITERAL_STRING("show"));
    1495             :   }
    1496             : 
    1497           0 :   MOZ_ASSERT(r);
    1498           0 :   if (!r->Dispatch()) {
    1499           0 :     NS_WARNING("Could not dispatch event to worker notification");
    1500             :   }
    1501           0 :   return NS_OK;
    1502             : }
    1503             : 
    1504             : NS_IMETHODIMP
    1505           0 : ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
    1506             :                                            const char* aTopic,
    1507             :                                            const char16_t* aData)
    1508             : {
    1509           0 :   AssertIsOnMainThread();
    1510             : 
    1511           0 :   nsAutoCString originSuffix;
    1512           0 :   nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
    1513           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1514           0 :     return rv;
    1515             :   }
    1516             : 
    1517             :   nsCOMPtr<nsIServiceWorkerManager> swm =
    1518           0 :     mozilla::services::GetServiceWorkerManager();
    1519           0 :   if (NS_WARN_IF(!swm)) {
    1520           0 :     return NS_ERROR_FAILURE;
    1521             :   }
    1522             : 
    1523           0 :   if (!strcmp("alertclickcallback", aTopic)) {
    1524           0 :     rv = swm->SendNotificationClickEvent(originSuffix,
    1525           0 :                                          NS_ConvertUTF16toUTF8(mScope),
    1526             :                                          mID,
    1527             :                                          mTitle,
    1528             :                                          mDir,
    1529             :                                          mLang,
    1530             :                                          mBody,
    1531             :                                          mTag,
    1532             :                                          mIcon,
    1533             :                                          mData,
    1534           0 :                                          mBehavior);
    1535           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
    1536           0 :     return NS_OK;
    1537             :   }
    1538             : 
    1539           0 :   if (!strcmp("alertfinished", aTopic)) {
    1540           0 :     nsString origin;
    1541           0 :     nsresult rv = Notification::GetOrigin(mPrincipal, origin);
    1542           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1543           0 :       return rv;
    1544             :     }
    1545             : 
    1546             :     // Remove closed or dismissed persistent notifications.
    1547             :     nsCOMPtr<nsINotificationStorage> notificationStorage =
    1548           0 :       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
    1549           0 :     if (notificationStorage) {
    1550           0 :       notificationStorage->Delete(origin, mID);
    1551             :     }
    1552             : 
    1553           0 :     rv = swm->SendNotificationCloseEvent(originSuffix,
    1554           0 :                                          NS_ConvertUTF16toUTF8(mScope),
    1555             :                                          mID,
    1556             :                                          mTitle,
    1557             :                                          mDir,
    1558             :                                          mLang,
    1559             :                                          mBody,
    1560             :                                          mTag,
    1561             :                                          mIcon,
    1562             :                                          mData,
    1563           0 :                                          mBehavior);
    1564           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
    1565           0 :     return NS_OK;
    1566             :   }
    1567             : 
    1568           0 :   return NS_OK;
    1569             : }
    1570             : 
    1571             : bool
    1572           0 : Notification::IsInPrivateBrowsing()
    1573             : {
    1574           0 :   AssertIsOnMainThread();
    1575             : 
    1576           0 :   nsIDocument* doc = nullptr;
    1577             : 
    1578           0 :   if (mWorkerPrivate) {
    1579           0 :     doc = mWorkerPrivate->GetDocument();
    1580           0 :   } else if (GetOwner()) {
    1581           0 :     doc = GetOwner()->GetExtantDoc();
    1582             :   }
    1583             : 
    1584           0 :   if (doc) {
    1585           0 :     nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
    1586           0 :     return loadContext && loadContext->UsePrivateBrowsing();
    1587             :   }
    1588             : 
    1589           0 :   if (mWorkerPrivate) {
    1590             :     // Not all workers may have a document, but with Bug 1107516 fixed, they
    1591             :     // should all have a loadcontext.
    1592           0 :     nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
    1593           0 :     nsCOMPtr<nsILoadContext> loadContext;
    1594           0 :     NS_QueryNotificationCallbacks(nullptr, loadGroup, NS_GET_IID(nsILoadContext),
    1595           0 :                                   getter_AddRefs(loadContext));
    1596           0 :     return loadContext && loadContext->UsePrivateBrowsing();
    1597             :   }
    1598             : 
    1599             :   //XXXnsm Should this default to true?
    1600           0 :   return false;
    1601             : }
    1602             : 
    1603             : namespace {
    1604           0 :   struct StringWriteFunc : public JSONWriteFunc
    1605             :   {
    1606             :     nsAString& mBuffer; // This struct must not outlive this buffer
    1607           0 :     explicit StringWriteFunc(nsAString& buffer) : mBuffer(buffer) {}
    1608             : 
    1609           0 :     void Write(const char* aStr)
    1610             :     {
    1611           0 :       mBuffer.Append(NS_ConvertUTF8toUTF16(aStr));
    1612           0 :     }
    1613             :   };
    1614             : }
    1615             : 
    1616             : void
    1617           0 : Notification::ShowInternal()
    1618             : {
    1619           0 :   AssertIsOnMainThread();
    1620           0 :   MOZ_ASSERT(mTempRef, "Notification should take ownership of itself before"
    1621             :                        "calling ShowInternal!");
    1622             :   // A notification can only have one observer and one call to ShowInternal.
    1623           0 :   MOZ_ASSERT(!mObserver);
    1624             : 
    1625             :   // Transfer ownership to local scope so we can either release it at the end
    1626             :   // of this function or transfer it to the observer.
    1627           0 :   UniquePtr<NotificationRef> ownership;
    1628           0 :   mozilla::Swap(ownership, mTempRef);
    1629           0 :   MOZ_ASSERT(ownership->GetNotification() == this);
    1630             : 
    1631           0 :   nsresult rv = PersistNotification();
    1632           0 :   if (NS_FAILED(rv)) {
    1633           0 :     NS_WARNING("Could not persist Notification");
    1634             :   }
    1635             : 
    1636             :   nsCOMPtr<nsIAlertsService> alertService =
    1637           0 :     do_GetService(NS_ALERTSERVICE_CONTRACTID);
    1638             : 
    1639           0 :   ErrorResult result;
    1640           0 :   NotificationPermission permission = NotificationPermission::Denied;
    1641           0 :   if (mWorkerPrivate) {
    1642           0 :     permission = GetPermissionInternal(mWorkerPrivate->GetPrincipal(), result);
    1643             :   } else {
    1644           0 :     permission = GetPermissionInternal(GetOwner(), result);
    1645             :   }
    1646             :   // We rely on GetPermissionInternal returning Denied on all failure codepaths.
    1647           0 :   MOZ_ASSERT_IF(result.Failed(), permission == NotificationPermission::Denied);
    1648           0 :   result.SuppressException();
    1649           0 :   if (permission != NotificationPermission::Granted || !alertService) {
    1650           0 :     if (mWorkerPrivate) {
    1651             :       RefPtr<NotificationEventWorkerRunnable> r =
    1652             :         new NotificationEventWorkerRunnable(this,
    1653           0 :                                             NS_LITERAL_STRING("error"));
    1654           0 :       if (!r->Dispatch()) {
    1655           0 :         NS_WARNING("Could not dispatch event to worker notification");
    1656             :       }
    1657             :     } else {
    1658           0 :       DispatchTrustedEvent(NS_LITERAL_STRING("error"));
    1659             :     }
    1660           0 :     return;
    1661             :   }
    1662             : 
    1663           0 :   nsAutoString iconUrl;
    1664           0 :   nsAutoString soundUrl;
    1665           0 :   ResolveIconAndSoundURL(iconUrl, soundUrl);
    1666             : 
    1667           0 :   bool isPersistent = false;
    1668           0 :   nsCOMPtr<nsIObserver> observer;
    1669           0 :   if (mScope.IsEmpty()) {
    1670             :     // Ownership passed to observer.
    1671           0 :     if (mWorkerPrivate) {
    1672             :       // Scope better be set on ServiceWorker initiated requests.
    1673           0 :       MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
    1674             :       // Keep a pointer so that the feature can tell the observer not to release
    1675             :       // the notification.
    1676           0 :       mObserver = new WorkerNotificationObserver(Move(ownership));
    1677           0 :       observer = mObserver;
    1678             :     } else {
    1679           0 :       observer = new MainThreadNotificationObserver(Move(ownership));
    1680             :     }
    1681             :   } else {
    1682           0 :     isPersistent = true;
    1683             :     // This observer does not care about the Notification. It will be released
    1684             :     // at the end of this function.
    1685             :     //
    1686             :     // The observer is wholly owned by the NotificationObserver passed to the alert service.
    1687           0 :     nsAutoString behavior;
    1688           0 :     if (NS_WARN_IF(!mBehavior.ToJSON(behavior))) {
    1689           0 :       behavior.Truncate();
    1690             :     }
    1691             :     observer = new ServiceWorkerNotificationObserver(mScope,
    1692           0 :                                                      GetPrincipal(),
    1693             :                                                      mID,
    1694             :                                                      mTitle,
    1695           0 :                                                      DirectionToString(mDir),
    1696             :                                                      mLang,
    1697             :                                                      mBody,
    1698             :                                                      mTag,
    1699             :                                                      iconUrl,
    1700             :                                                      mDataAsBase64,
    1701           0 :                                                      behavior);
    1702             :   }
    1703           0 :   MOZ_ASSERT(observer);
    1704             :   nsCOMPtr<nsIObserver> alertObserver = new NotificationObserver(observer,
    1705           0 :                                                                  GetPrincipal(),
    1706           0 :                                                                  IsInPrivateBrowsing());
    1707             : 
    1708             : 
    1709             :   // In the case of IPC, the parent process uses the cookie to map to
    1710             :   // nsIObserver. Thus the cookie must be unique to differentiate observers.
    1711           0 :   nsString uniqueCookie = NS_LITERAL_STRING("notification:");
    1712           0 :   uniqueCookie.AppendInt(sCount++);
    1713           0 :   bool inPrivateBrowsing = IsInPrivateBrowsing();
    1714             : 
    1715           0 :   bool requireInteraction = mRequireInteraction;
    1716           0 :   if (!Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false)) {
    1717           0 :     requireInteraction = false;
    1718             :   }
    1719             : 
    1720           0 :   nsAutoString alertName;
    1721           0 :   GetAlertName(alertName);
    1722             :   nsCOMPtr<nsIAlertNotification> alert =
    1723           0 :     do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
    1724           0 :   NS_ENSURE_TRUE_VOID(alert);
    1725           0 :   nsIPrincipal* principal = GetPrincipal();
    1726           0 :   rv = alert->Init(alertName, iconUrl, mTitle, mBody,
    1727             :                    true,
    1728             :                    uniqueCookie,
    1729           0 :                    DirectionToString(mDir),
    1730             :                    mLang,
    1731             :                    mDataAsBase64,
    1732             :                    GetPrincipal(),
    1733             :                    inPrivateBrowsing,
    1734           0 :                    requireInteraction);
    1735           0 :   NS_ENSURE_SUCCESS_VOID(rv);
    1736             : 
    1737           0 :   if (isPersistent) {
    1738           0 :     nsAutoString persistentData;
    1739             : 
    1740           0 :     JSONWriter w(MakeUnique<StringWriteFunc>(persistentData));
    1741           0 :     w.Start();
    1742             : 
    1743           0 :     nsAutoString origin;
    1744           0 :     Notification::GetOrigin(principal, origin);
    1745           0 :     w.StringProperty("origin", NS_ConvertUTF16toUTF8(origin).get());
    1746             : 
    1747           0 :     w.StringProperty("id", NS_ConvertUTF16toUTF8(mID).get());
    1748             : 
    1749           0 :     nsAutoCString originSuffix;
    1750           0 :     principal->GetOriginSuffix(originSuffix);
    1751           0 :     w.StringProperty("originSuffix", originSuffix.get());
    1752             : 
    1753           0 :     w.End();
    1754             : 
    1755           0 :     alertService->ShowPersistentNotification(persistentData, alert, alertObserver);
    1756             :   } else {
    1757           0 :     alertService->ShowAlert(alert, alertObserver);
    1758             :   }
    1759             : }
    1760             : 
    1761             : /* static */ bool
    1762           1 : Notification::RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */)
    1763             : {
    1764             :   // requestPermission() is not allowed on workers. The calling page should ask
    1765             :   // for permission on the worker's behalf. This is to prevent 'which window
    1766             :   // should show the browser pop-up'. See discussion:
    1767             :   // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-October/041272.html
    1768           1 :   return NS_IsMainThread();
    1769             : }
    1770             : 
    1771             : // static
    1772             : already_AddRefed<Promise>
    1773           0 : Notification::RequestPermission(const GlobalObject& aGlobal,
    1774             :                                 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
    1775             :                                 ErrorResult& aRv)
    1776             : {
    1777           0 :   AssertIsOnMainThread();
    1778             : 
    1779             :   // Get principal from global to make permission request for notifications.
    1780           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
    1781           0 :   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports());
    1782           0 :   if (!sop) {
    1783           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    1784           0 :     return nullptr;
    1785             :   }
    1786           0 :   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
    1787             : 
    1788           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
    1789           0 :   RefPtr<Promise> promise = Promise::Create(global, aRv);
    1790           0 :   if (aRv.Failed()) {
    1791           0 :     return nullptr;
    1792             :   }
    1793           0 :   NotificationPermissionCallback* permissionCallback = nullptr;
    1794           0 :   if (aCallback.WasPassed()) {
    1795           0 :     permissionCallback = &aCallback.Value();
    1796             :   }
    1797             :   nsCOMPtr<nsIRunnable> request =
    1798           0 :     new NotificationPermissionRequest(principal, window, promise, permissionCallback);
    1799             : 
    1800           0 :   global->Dispatch("Notification::RequestPermission", TaskCategory::Other,
    1801           0 :                    request.forget());
    1802             : 
    1803           0 :   return promise.forget();
    1804             : }
    1805             : 
    1806             : // static
    1807             : NotificationPermission
    1808           0 : Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
    1809             : {
    1810           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    1811           0 :   return GetPermission(global, aRv);
    1812             : }
    1813             : 
    1814             : // static
    1815             : NotificationPermission
    1816           0 : Notification::GetPermission(nsIGlobalObject* aGlobal, ErrorResult& aRv)
    1817             : {
    1818           0 :   if (NS_IsMainThread()) {
    1819           0 :     return GetPermissionInternal(aGlobal, aRv);
    1820             :   } else {
    1821           0 :     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
    1822           0 :     MOZ_ASSERT(worker);
    1823             :     RefPtr<GetPermissionRunnable> r =
    1824           0 :       new GetPermissionRunnable(worker);
    1825           0 :     r->Dispatch(Terminating, aRv);
    1826           0 :     if (aRv.Failed()) {
    1827           0 :       return NotificationPermission::Denied;
    1828             :     }
    1829             : 
    1830           0 :     return r->GetPermission();
    1831             :   }
    1832             : }
    1833             : 
    1834             : /* static */ NotificationPermission
    1835           0 : Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
    1836             : {
    1837             :   // Get principal from global to check permission for notifications.
    1838           0 :   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
    1839           0 :   if (!sop) {
    1840           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    1841           0 :     return NotificationPermission::Denied;
    1842             :   }
    1843             : 
    1844           0 :   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
    1845           0 :   return GetPermissionInternal(principal, aRv);
    1846             : }
    1847             : 
    1848             : /* static */ NotificationPermission
    1849           0 : Notification::GetPermissionInternal(nsIPrincipal* aPrincipal,
    1850             :                                     ErrorResult& aRv)
    1851             : {
    1852           0 :   AssertIsOnMainThread();
    1853           0 :   MOZ_ASSERT(aPrincipal);
    1854             : 
    1855           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    1856           0 :     return NotificationPermission::Granted;
    1857             :   } else {
    1858             :     // Allow files to show notifications by default.
    1859           0 :     nsCOMPtr<nsIURI> uri;
    1860           0 :     aPrincipal->GetURI(getter_AddRefs(uri));
    1861           0 :     if (uri) {
    1862             :       bool isFile;
    1863           0 :       uri->SchemeIs("file", &isFile);
    1864           0 :       if (isFile) {
    1865           0 :         return NotificationPermission::Granted;
    1866             :       }
    1867             :     }
    1868             :   }
    1869             : 
    1870             :   // We also allow notifications is they are pref'ed on.
    1871           0 :   if (Preferences::GetBool("notification.prompt.testing", false)) {
    1872           0 :     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
    1873           0 :       return NotificationPermission::Granted;
    1874             :     } else {
    1875           0 :       return NotificationPermission::Denied;
    1876             :     }
    1877             :   }
    1878             : 
    1879           0 :   return TestPermission(aPrincipal);
    1880             : }
    1881             : 
    1882             : /* static */ NotificationPermission
    1883           0 : Notification::TestPermission(nsIPrincipal* aPrincipal)
    1884             : {
    1885           0 :   AssertIsOnMainThread();
    1886             : 
    1887           0 :   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
    1888             : 
    1889             :   nsCOMPtr<nsIPermissionManager> permissionManager =
    1890           0 :     services::GetPermissionManager();
    1891           0 :   if (!permissionManager) {
    1892           0 :     return NotificationPermission::Default;
    1893             :   }
    1894             : 
    1895           0 :   permissionManager->TestExactPermissionFromPrincipal(aPrincipal,
    1896             :                                                       "desktop-notification",
    1897           0 :                                                       &permission);
    1898             : 
    1899             :   // Convert the result to one of the enum types.
    1900           0 :   switch (permission) {
    1901             :   case nsIPermissionManager::ALLOW_ACTION:
    1902           0 :     return NotificationPermission::Granted;
    1903             :   case nsIPermissionManager::DENY_ACTION:
    1904           0 :     return NotificationPermission::Denied;
    1905             :   default:
    1906           0 :     return NotificationPermission::Default;
    1907             :   }
    1908             : }
    1909             : 
    1910             : nsresult
    1911           0 : Notification::ResolveIconAndSoundURL(nsString& iconUrl, nsString& soundUrl)
    1912             : {
    1913           0 :   AssertIsOnMainThread();
    1914           0 :   nsresult rv = NS_OK;
    1915             : 
    1916           0 :   nsCOMPtr<nsIURI> baseUri;
    1917             : 
    1918             :   // XXXnsm If I understand correctly, the character encoding for resolving
    1919             :   // URIs in new specs is dictated by the URL spec, which states that unless
    1920             :   // the URL parser is passed an override encoding, the charset to be used is
    1921             :   // UTF-8. The new Notification icon/sound specification just says to use the
    1922             :   // Fetch API, where the Request constructor defers to URL parsing specifying
    1923             :   // the API base URL and no override encoding. So we've to use UTF-8 on
    1924             :   // workers, but for backwards compat keeping it document charset on main
    1925             :   // thread.
    1926           0 :   auto encoding = UTF_8_ENCODING;
    1927             : 
    1928           0 :   if (mWorkerPrivate) {
    1929           0 :     baseUri = mWorkerPrivate->GetBaseURI();
    1930             :   } else {
    1931           0 :     nsIDocument* doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
    1932           0 :     if (doc) {
    1933           0 :       baseUri = doc->GetBaseURI();
    1934           0 :       encoding = doc->GetDocumentCharacterSet();
    1935             :     } else {
    1936           0 :       NS_WARNING("No document found for main thread notification!");
    1937           0 :       return NS_ERROR_FAILURE;
    1938             :     }
    1939             :   }
    1940             : 
    1941           0 :   if (baseUri) {
    1942           0 :     if (mIconUrl.Length() > 0) {
    1943           0 :       nsCOMPtr<nsIURI> srcUri;
    1944           0 :       rv = NS_NewURI(getter_AddRefs(srcUri), mIconUrl, encoding, baseUri);
    1945           0 :       if (NS_SUCCEEDED(rv)) {
    1946           0 :         nsAutoCString src;
    1947           0 :         srcUri->GetSpec(src);
    1948           0 :         iconUrl = NS_ConvertUTF8toUTF16(src);
    1949             :       }
    1950             :     }
    1951           0 :     if (mBehavior.mSoundFile.Length() > 0) {
    1952           0 :       nsCOMPtr<nsIURI> srcUri;
    1953           0 :       rv = NS_NewURI(getter_AddRefs(srcUri), mBehavior.mSoundFile, encoding, baseUri);
    1954           0 :       if (NS_SUCCEEDED(rv)) {
    1955           0 :         nsAutoCString src;
    1956           0 :         srcUri->GetSpec(src);
    1957           0 :         soundUrl = NS_ConvertUTF8toUTF16(src);
    1958             :       }
    1959             :     }
    1960             :   }
    1961             : 
    1962           0 :   return rv;
    1963             : }
    1964             : 
    1965             : already_AddRefed<Promise>
    1966           0 : Notification::Get(nsPIDOMWindowInner* aWindow,
    1967             :                   const GetNotificationOptions& aFilter,
    1968             :                   const nsAString& aScope,
    1969             :                   ErrorResult& aRv)
    1970             : {
    1971           0 :   AssertIsOnMainThread();
    1972           0 :   MOZ_ASSERT(aWindow);
    1973             : 
    1974           0 :   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
    1975           0 :   if (!doc) {
    1976           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    1977           0 :     return nullptr;
    1978             :   }
    1979             : 
    1980           0 :   nsString origin;
    1981           0 :   aRv = GetOrigin(doc->NodePrincipal(), origin);
    1982           0 :   if (aRv.Failed()) {
    1983           0 :     return nullptr;
    1984             :   }
    1985             : 
    1986           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
    1987           0 :   RefPtr<Promise> promise = Promise::Create(global, aRv);
    1988           0 :   if (aRv.Failed()) {
    1989           0 :     return nullptr;
    1990             :   }
    1991             : 
    1992             :   nsCOMPtr<nsINotificationStorageCallback> callback =
    1993           0 :     new NotificationStorageCallback(global, aScope, promise);
    1994             : 
    1995             :   RefPtr<NotificationGetRunnable> r =
    1996           0 :     new NotificationGetRunnable(origin, aFilter.mTag, callback);
    1997             : 
    1998           0 :   aRv = global->Dispatch("Notification::Get", TaskCategory::Other,
    1999           0 :                          r.forget());
    2000           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2001           0 :     return nullptr;
    2002             :   }
    2003             : 
    2004           0 :   return promise.forget();
    2005             : }
    2006             : 
    2007             : already_AddRefed<Promise>
    2008           0 : Notification::Get(const GlobalObject& aGlobal,
    2009             :                   const GetNotificationOptions& aFilter,
    2010             :                   ErrorResult& aRv)
    2011             : {
    2012           0 :   AssertIsOnMainThread();
    2013             :   nsCOMPtr<nsIGlobalObject> global =
    2014           0 :     do_QueryInterface(aGlobal.GetAsSupports());
    2015           0 :   MOZ_ASSERT(global);
    2016           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
    2017             : 
    2018           0 :   return Get(window, aFilter, EmptyString(), aRv);
    2019             : }
    2020             : 
    2021           0 : class WorkerGetResultRunnable final : public NotificationWorkerRunnable
    2022             : {
    2023             :   RefPtr<PromiseWorkerProxy> mPromiseProxy;
    2024             :   const nsTArray<NotificationStrings> mStrings;
    2025             : public:
    2026           0 :   WorkerGetResultRunnable(WorkerPrivate* aWorkerPrivate,
    2027             :                           PromiseWorkerProxy* aPromiseProxy,
    2028             :                           const nsTArray<NotificationStrings>&& aStrings)
    2029           0 :     : NotificationWorkerRunnable(aWorkerPrivate)
    2030             :     , mPromiseProxy(aPromiseProxy)
    2031           0 :     , mStrings(Move(aStrings))
    2032             :   {
    2033           0 :   }
    2034             : 
    2035             :   void
    2036           0 :   WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
    2037             :   {
    2038           0 :     RefPtr<Promise> workerPromise = mPromiseProxy->WorkerPromise();
    2039             : 
    2040           0 :     ErrorResult result;
    2041           0 :     AutoTArray<RefPtr<Notification>, 5> notifications;
    2042           0 :     for (uint32_t i = 0; i < mStrings.Length(); ++i) {
    2043             :       RefPtr<Notification> n =
    2044           0 :         Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(),
    2045           0 :                                           mStrings[i].mID,
    2046           0 :                                           mStrings[i].mTitle,
    2047           0 :                                           mStrings[i].mDir,
    2048           0 :                                           mStrings[i].mLang,
    2049           0 :                                           mStrings[i].mBody,
    2050           0 :                                           mStrings[i].mTag,
    2051           0 :                                           mStrings[i].mIcon,
    2052           0 :                                           mStrings[i].mData,
    2053             :                                           /* mStrings[i].mBehavior, not
    2054             :                                            * supported */
    2055           0 :                                           mStrings[i].mServiceWorkerRegistrationScope,
    2056           0 :                                           result);
    2057             : 
    2058           0 :       n->SetStoredState(true);
    2059           0 :       Unused << NS_WARN_IF(result.Failed());
    2060           0 :       if (!result.Failed()) {
    2061           0 :         notifications.AppendElement(n.forget());
    2062             :       }
    2063             :     }
    2064             : 
    2065           0 :     workerPromise->MaybeResolve(notifications);
    2066           0 :     mPromiseProxy->CleanUp();
    2067           0 :   }
    2068             : };
    2069             : 
    2070             : class WorkerGetCallback final : public ScopeCheckingGetCallback
    2071             : {
    2072             :   RefPtr<PromiseWorkerProxy> mPromiseProxy;
    2073             : public:
    2074             :   NS_DECL_ISUPPORTS
    2075             : 
    2076           0 :   WorkerGetCallback(PromiseWorkerProxy* aProxy, const nsAString& aScope)
    2077           0 :     : ScopeCheckingGetCallback(aScope), mPromiseProxy(aProxy)
    2078             :   {
    2079           0 :     AssertIsOnMainThread();
    2080           0 :     MOZ_ASSERT(aProxy);
    2081           0 :   }
    2082             : 
    2083           0 :   NS_IMETHOD Done() final
    2084             :   {
    2085           0 :     AssertIsOnMainThread();
    2086           0 :     MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
    2087             : 
    2088           0 :     RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
    2089           0 :     MutexAutoLock lock(proxy->Lock());
    2090           0 :     if (proxy->CleanedUp()) {
    2091           0 :       return NS_OK;
    2092             :     }
    2093             : 
    2094             :     RefPtr<WorkerGetResultRunnable> r =
    2095           0 :       new WorkerGetResultRunnable(proxy->GetWorkerPrivate(),
    2096             :                                   proxy,
    2097           0 :                                   Move(mStrings));
    2098             : 
    2099           0 :     r->Dispatch();
    2100           0 :     return NS_OK;
    2101             :   }
    2102             : 
    2103             : private:
    2104           0 :   ~WorkerGetCallback()
    2105           0 :   {}
    2106             : };
    2107             : 
    2108           0 : NS_IMPL_ISUPPORTS(WorkerGetCallback, nsINotificationStorageCallback)
    2109             : 
    2110             : class WorkerGetRunnable final : public Runnable
    2111             : {
    2112             :   RefPtr<PromiseWorkerProxy> mPromiseProxy;
    2113             :   const nsString mTag;
    2114             :   const nsString mScope;
    2115             : public:
    2116           0 :   WorkerGetRunnable(PromiseWorkerProxy* aProxy,
    2117             :                     const nsAString& aTag,
    2118             :                     const nsAString& aScope)
    2119           0 :     : Runnable("WorkerGetRunnable")
    2120           0 :     , mPromiseProxy(aProxy), mTag(aTag), mScope(aScope)
    2121             :   {
    2122           0 :     MOZ_ASSERT(mPromiseProxy);
    2123           0 :   }
    2124             : 
    2125             :   NS_IMETHOD
    2126           0 :   Run() override
    2127             :   {
    2128           0 :     AssertIsOnMainThread();
    2129             :     nsCOMPtr<nsINotificationStorageCallback> callback =
    2130           0 :       new WorkerGetCallback(mPromiseProxy, mScope);
    2131             : 
    2132             :     nsresult rv;
    2133             :     nsCOMPtr<nsINotificationStorage> notificationStorage =
    2134           0 :       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
    2135           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2136           0 :       callback->Done();
    2137           0 :       return rv;
    2138             :     }
    2139             : 
    2140           0 :     MutexAutoLock lock(mPromiseProxy->Lock());
    2141           0 :     if (mPromiseProxy->CleanedUp()) {
    2142           0 :       return NS_OK;
    2143             :     }
    2144             : 
    2145           0 :     nsString origin;
    2146           0 :     rv =
    2147           0 :       Notification::GetOrigin(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
    2148             :                               origin);
    2149           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2150           0 :       callback->Done();
    2151           0 :       return rv;
    2152             :     }
    2153             : 
    2154           0 :     rv = notificationStorage->Get(origin, mTag, callback);
    2155           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2156           0 :       callback->Done();
    2157           0 :       return rv;
    2158             :     }
    2159             : 
    2160           0 :     return NS_OK;
    2161             :   }
    2162             : private:
    2163           0 :   ~WorkerGetRunnable()
    2164           0 :   {}
    2165             : };
    2166             : 
    2167             : // static
    2168             : already_AddRefed<Promise>
    2169           0 : Notification::WorkerGet(WorkerPrivate* aWorkerPrivate,
    2170             :                         const GetNotificationOptions& aFilter,
    2171             :                         const nsAString& aScope,
    2172             :                         ErrorResult& aRv)
    2173             : {
    2174           0 :   MOZ_ASSERT(aWorkerPrivate);
    2175           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
    2176           0 :   RefPtr<Promise> p = Promise::Create(aWorkerPrivate->GlobalScope(), aRv);
    2177           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2178           0 :     return nullptr;
    2179             :   }
    2180             : 
    2181             :   RefPtr<PromiseWorkerProxy> proxy =
    2182           0 :     PromiseWorkerProxy::Create(aWorkerPrivate, p);
    2183           0 :   if (!proxy) {
    2184           0 :     aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
    2185           0 :     return nullptr;
    2186             :   }
    2187             : 
    2188             :   RefPtr<WorkerGetRunnable> r =
    2189           0 :     new WorkerGetRunnable(proxy, aFilter.mTag, aScope);
    2190             :   // Since this is called from script via
    2191             :   // ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
    2192           0 :   MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
    2193           0 :   return p.forget();
    2194             : }
    2195             : 
    2196             : JSObject*
    2197           0 : Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    2198             : {
    2199           0 :   return mozilla::dom::NotificationBinding::Wrap(aCx, this, aGivenProto);
    2200             : }
    2201             : 
    2202             : void
    2203           0 : Notification::Close()
    2204             : {
    2205           0 :   AssertIsOnTargetThread();
    2206           0 :   auto ref = MakeUnique<NotificationRef>(this);
    2207           0 :   if (!ref->Initialized()) {
    2208           0 :     return;
    2209             :   }
    2210             : 
    2211             :   nsCOMPtr<nsIRunnable> closeNotificationTask =
    2212           0 :     new NotificationTask("Notification::Close", Move(ref),
    2213           0 :                          NotificationTask::eClose);
    2214           0 :   nsresult rv = DispatchToMainThread(closeNotificationTask.forget());
    2215             : 
    2216           0 :   if (NS_FAILED(rv)) {
    2217           0 :     DispatchTrustedEvent(NS_LITERAL_STRING("error"));
    2218             :     // If dispatch fails, NotificationTask will release the ref when it goes
    2219             :     // out of scope at the end of this function.
    2220             :   }
    2221             : }
    2222             : 
    2223             : void
    2224           0 : Notification::CloseInternal()
    2225             : {
    2226           0 :   AssertIsOnMainThread();
    2227             :   // Transfer ownership (if any) to local scope so we can release it at the end
    2228             :   // of this function. This is relevant when the call is from
    2229             :   // NotificationTask::Run().
    2230           0 :   UniquePtr<NotificationRef> ownership;
    2231           0 :   mozilla::Swap(ownership, mTempRef);
    2232             : 
    2233           0 :   SetAlertName();
    2234           0 :   UnpersistNotification();
    2235           0 :   if (!mIsClosed) {
    2236             :     nsCOMPtr<nsIAlertsService> alertService =
    2237           0 :       do_GetService(NS_ALERTSERVICE_CONTRACTID);
    2238           0 :     if (alertService) {
    2239           0 :       nsAutoString alertName;
    2240           0 :       GetAlertName(alertName);
    2241           0 :       alertService->CloseAlert(alertName, GetPrincipal());
    2242             :     }
    2243             :   }
    2244           0 : }
    2245             : 
    2246             : nsresult
    2247           0 : Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
    2248             : {
    2249           0 :   if (!aPrincipal) {
    2250           0 :     return NS_ERROR_FAILURE;
    2251             :   }
    2252             : 
    2253           0 :   nsresult rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin);
    2254           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2255             : 
    2256           0 :   return NS_OK;
    2257             : }
    2258             : 
    2259             : bool
    2260           0 : Notification::RequireInteraction() const
    2261             : {
    2262           0 :   return mRequireInteraction;
    2263             : }
    2264             : 
    2265             : void
    2266           0 : Notification::GetData(JSContext* aCx,
    2267             :                       JS::MutableHandle<JS::Value> aRetval)
    2268             : {
    2269           0 :   if (mData.isNull() && !mDataAsBase64.IsEmpty()) {
    2270             :     nsresult rv;
    2271             :     RefPtr<nsStructuredCloneContainer> container =
    2272           0 :       new nsStructuredCloneContainer();
    2273           0 :     rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION);
    2274           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2275           0 :       aRetval.setNull();
    2276           0 :       return;
    2277             :     }
    2278             : 
    2279           0 :     JS::Rooted<JS::Value> data(aCx);
    2280           0 :     rv = container->DeserializeToJsval(aCx, &data);
    2281           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2282           0 :       aRetval.setNull();
    2283           0 :       return;
    2284             :     }
    2285             : 
    2286           0 :     if (data.isGCThing()) {
    2287           0 :       mozilla::HoldJSObjects(this);
    2288             :     }
    2289           0 :     mData = data;
    2290             :   }
    2291           0 :   if (mData.isNull()) {
    2292           0 :     aRetval.setNull();
    2293           0 :     return;
    2294             :   }
    2295             : 
    2296           0 :   aRetval.set(mData);
    2297             : }
    2298             : 
    2299             : void
    2300           0 : Notification::InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
    2301             :                             ErrorResult& aRv)
    2302             : {
    2303           0 :   if (!mDataAsBase64.IsEmpty() || aData.isNull()) {
    2304           0 :     return;
    2305             :   }
    2306             :   RefPtr<nsStructuredCloneContainer> dataObjectContainer =
    2307           0 :     new nsStructuredCloneContainer();
    2308           0 :   aRv = dataObjectContainer->InitFromJSVal(aData, aCx);
    2309           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2310           0 :     return;
    2311             :   }
    2312             : 
    2313           0 :   dataObjectContainer->GetDataAsBase64(mDataAsBase64);
    2314             : }
    2315             : 
    2316           0 : void Notification::InitFromBase64(const nsAString& aData, ErrorResult& aRv)
    2317             : {
    2318           0 :   if (!mDataAsBase64.IsEmpty() || aData.IsEmpty()) {
    2319           0 :     return;
    2320             :   }
    2321             : 
    2322             :   // To and fro to ensure it is valid base64.
    2323             :   RefPtr<nsStructuredCloneContainer> container =
    2324           0 :     new nsStructuredCloneContainer();
    2325           0 :   aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION);
    2326           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2327           0 :     return;
    2328             :   }
    2329             : 
    2330           0 :   container->GetDataAsBase64(mDataAsBase64);
    2331             : }
    2332             : 
    2333             : bool
    2334           0 : Notification::AddRefObject()
    2335             : {
    2336           0 :   AssertIsOnTargetThread();
    2337           0 :   MOZ_ASSERT_IF(mWorkerPrivate && !mWorkerHolder, mTaskCount == 0);
    2338           0 :   MOZ_ASSERT_IF(mWorkerPrivate && mWorkerHolder, mTaskCount > 0);
    2339           0 :   if (mWorkerPrivate && !mWorkerHolder) {
    2340           0 :     if (!RegisterWorkerHolder()) {
    2341           0 :       return false;
    2342             :     }
    2343             :   }
    2344           0 :   AddRef();
    2345           0 :   ++mTaskCount;
    2346           0 :   return true;
    2347             : }
    2348             : 
    2349             : void
    2350           0 : Notification::ReleaseObject()
    2351             : {
    2352           0 :   AssertIsOnTargetThread();
    2353           0 :   MOZ_ASSERT(mTaskCount > 0);
    2354           0 :   MOZ_ASSERT_IF(mWorkerPrivate, mWorkerHolder);
    2355             : 
    2356           0 :   --mTaskCount;
    2357           0 :   if (mWorkerPrivate && mTaskCount == 0) {
    2358           0 :     UnregisterWorkerHolder();
    2359             :   }
    2360           0 :   Release();
    2361           0 : }
    2362             : 
    2363           0 : NotificationWorkerHolder::NotificationWorkerHolder(Notification* aNotification)
    2364           0 :   : mNotification(aNotification)
    2365             : {
    2366           0 :   MOZ_ASSERT(mNotification->mWorkerPrivate);
    2367           0 :   mNotification->mWorkerPrivate->AssertIsOnWorkerThread();
    2368           0 : }
    2369             : 
    2370             : /*
    2371             :  * Called from the worker, runs on main thread, blocks worker.
    2372             :  *
    2373             :  * We can freely access mNotification here because the feature supplied it and
    2374             :  * the Notification owns the feature.
    2375             :  */
    2376           0 : class CloseNotificationRunnable final
    2377             :   : public WorkerMainThreadRunnable
    2378             : {
    2379             :   Notification* mNotification;
    2380             :   bool mHadObserver;
    2381             : 
    2382             :   public:
    2383           0 :   explicit CloseNotificationRunnable(Notification* aNotification)
    2384           0 :     : WorkerMainThreadRunnable(aNotification->mWorkerPrivate,
    2385           0 :                                NS_LITERAL_CSTRING("Notification :: Close Notification"))
    2386             :     , mNotification(aNotification)
    2387           0 :     , mHadObserver(false)
    2388           0 :   {}
    2389             : 
    2390             :   bool
    2391           0 :   MainThreadRun() override
    2392             :   {
    2393           0 :     if (mNotification->mObserver) {
    2394             :       // The Notify() take's responsibility of releasing the Notification.
    2395           0 :       mNotification->mObserver->ForgetNotification();
    2396           0 :       mNotification->mObserver = nullptr;
    2397           0 :       mHadObserver = true;
    2398             :     }
    2399           0 :     mNotification->CloseInternal();
    2400           0 :     return true;
    2401             :   }
    2402             : 
    2403             :   bool
    2404           0 :   HadObserver()
    2405             :   {
    2406           0 :     return mHadObserver;
    2407             :   }
    2408             : };
    2409             : 
    2410             : bool
    2411           0 : NotificationWorkerHolder::Notify(Status aStatus)
    2412             : {
    2413           0 :   if (aStatus >= Canceling) {
    2414             :     // CloseNotificationRunnable blocks the worker by pushing a sync event loop
    2415             :     // on the stack. Meanwhile, WorkerControlRunnables dispatched to the worker
    2416             :     // can still continue running. One of these is
    2417             :     // ReleaseNotificationControlRunnable that releases the notification,
    2418             :     // invalidating the notification and this feature. We hold this reference to
    2419             :     // keep the notification valid until we are done with it.
    2420             :     //
    2421             :     // An example of when the control runnable could get dispatched to the
    2422             :     // worker is if a Notification is created and the worker is immediately
    2423             :     // closed, but there is no permission to show it so that the main thread
    2424             :     // immediately drops the NotificationRef. In this case, this function blocks
    2425             :     // on the main thread, but the main thread dispatches the control runnable,
    2426             :     // invalidating mNotification.
    2427           0 :     RefPtr<Notification> kungFuDeathGrip = mNotification;
    2428             : 
    2429             :     // Dispatched to main thread, blocks on closing the Notification.
    2430             :     RefPtr<CloseNotificationRunnable> r =
    2431           0 :       new CloseNotificationRunnable(kungFuDeathGrip);
    2432           0 :     ErrorResult rv;
    2433           0 :     r->Dispatch(Killing, rv);
    2434             :     // XXXbz I'm told throwing and returning false from here is pointless (and
    2435             :     // also that doing sync stuff from here is really weird), so I guess we just
    2436             :     // suppress the exception on rv, if any.
    2437           0 :     rv.SuppressException();
    2438             : 
    2439             :     // Only call ReleaseObject() to match the observer's NotificationRef
    2440             :     // ownership (since CloseNotificationRunnable asked the observer to drop the
    2441             :     // reference to the notification).
    2442           0 :     if (r->HadObserver()) {
    2443           0 :       kungFuDeathGrip->ReleaseObject();
    2444             :     }
    2445             : 
    2446             :     // From this point we cannot touch properties of this feature because
    2447             :     // ReleaseObject() may have led to the notification going away and the
    2448             :     // notification owns this feature!
    2449             :   }
    2450           0 :   return true;
    2451             : }
    2452             : 
    2453             : bool
    2454           0 : Notification::RegisterWorkerHolder()
    2455             : {
    2456           0 :   MOZ_ASSERT(mWorkerPrivate);
    2457           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
    2458           0 :   MOZ_ASSERT(!mWorkerHolder);
    2459           0 :   mWorkerHolder = MakeUnique<NotificationWorkerHolder>(this);
    2460           0 :   if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
    2461           0 :     return false;
    2462             :   }
    2463             : 
    2464           0 :   return true;
    2465             : }
    2466             : 
    2467             : void
    2468           0 : Notification::UnregisterWorkerHolder()
    2469             : {
    2470           0 :   MOZ_ASSERT(mWorkerPrivate);
    2471           0 :   mWorkerPrivate->AssertIsOnWorkerThread();
    2472           0 :   MOZ_ASSERT(mWorkerHolder);
    2473           0 :   mWorkerHolder = nullptr;
    2474           0 : }
    2475             : 
    2476             : /*
    2477             :  * Checks:
    2478             :  * 1) Is aWorker allowed to show a notification for scope?
    2479             :  * 2) Is aWorker an active worker?
    2480             :  *
    2481             :  * If it is not an active worker, Result() will be NS_ERROR_NOT_AVAILABLE.
    2482             :  */
    2483           0 : class CheckLoadRunnable final : public WorkerMainThreadRunnable
    2484             : {
    2485             :   nsresult mRv;
    2486             :   nsCString mScope;
    2487             : 
    2488             : public:
    2489           0 :   explicit CheckLoadRunnable(WorkerPrivate* aWorker, const nsACString& aScope)
    2490           0 :     : WorkerMainThreadRunnable(aWorker,
    2491           0 :                                NS_LITERAL_CSTRING("Notification :: Check Load"))
    2492             :     , mRv(NS_ERROR_DOM_SECURITY_ERR)
    2493           0 :     , mScope(aScope)
    2494           0 :   { }
    2495             : 
    2496             :   bool
    2497           0 :   MainThreadRun() override
    2498             :   {
    2499           0 :     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
    2500           0 :     mRv = CheckScope(principal, mScope);
    2501             : 
    2502           0 :     if (NS_FAILED(mRv)) {
    2503           0 :       return true;
    2504             :     }
    2505             : 
    2506           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    2507           0 :     if (!swm) {
    2508             :       // browser shutdown began
    2509           0 :       mRv = NS_ERROR_FAILURE;
    2510           0 :       return true;
    2511             :     }
    2512             : 
    2513             :     RefPtr<ServiceWorkerRegistrationInfo> registration =
    2514           0 :       swm->GetRegistration(principal, mScope);
    2515             : 
    2516             :     // This is coming from a ServiceWorkerRegistration.
    2517           0 :     MOZ_ASSERT(registration);
    2518             : 
    2519           0 :     if (!registration->GetActive() ||
    2520           0 :         registration->GetActive()->ID() != mWorkerPrivate->ServiceWorkerID()) {
    2521           0 :       mRv = NS_ERROR_NOT_AVAILABLE;
    2522             :     }
    2523             : 
    2524           0 :     return true;
    2525             :   }
    2526             : 
    2527             :   nsresult
    2528           0 :   Result()
    2529             :   {
    2530           0 :     return mRv;
    2531             :   }
    2532             : 
    2533             : };
    2534             : 
    2535             : /* static */
    2536             : already_AddRefed<Promise>
    2537           0 : Notification::ShowPersistentNotification(JSContext* aCx,
    2538             :                                          nsIGlobalObject *aGlobal,
    2539             :                                          const nsAString& aScope,
    2540             :                                          const nsAString& aTitle,
    2541             :                                          const NotificationOptions& aOptions,
    2542             :                                          ErrorResult& aRv)
    2543             : {
    2544           0 :   MOZ_ASSERT(aGlobal);
    2545             : 
    2546             :   // Validate scope.
    2547             :   // XXXnsm: This may be slow due to blocking the worker and waiting on the main
    2548             :   // thread. On calls from content, we can be sure the scope is valid since
    2549             :   // ServiceWorkerRegistrations have their scope set correctly. Can this be made
    2550             :   // debug only? The problem is that there would be different semantics in
    2551             :   // debug and non-debug builds in such a case.
    2552           0 :   if (NS_IsMainThread()) {
    2553           0 :     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
    2554           0 :     if (NS_WARN_IF(!sop)) {
    2555           0 :       aRv.Throw(NS_ERROR_UNEXPECTED);
    2556           0 :       return nullptr;
    2557             :     }
    2558             : 
    2559           0 :     nsIPrincipal* principal = sop->GetPrincipal();
    2560           0 :     if (NS_WARN_IF(!principal)) {
    2561           0 :       aRv.Throw(NS_ERROR_UNEXPECTED);
    2562           0 :       return nullptr;
    2563             :     }
    2564             : 
    2565           0 :     aRv = CheckScope(principal, NS_ConvertUTF16toUTF8(aScope));
    2566           0 :     if (NS_WARN_IF(aRv.Failed())) {
    2567           0 :       aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    2568           0 :       return nullptr;
    2569             :     }
    2570             :   } else {
    2571           0 :     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
    2572           0 :     MOZ_ASSERT(worker);
    2573           0 :     worker->AssertIsOnWorkerThread();
    2574             :     RefPtr<CheckLoadRunnable> loadChecker =
    2575           0 :       new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope));
    2576           0 :     loadChecker->Dispatch(Terminating, aRv);
    2577           0 :     if (aRv.Failed()) {
    2578           0 :       return nullptr;
    2579             :     }
    2580             : 
    2581           0 :     if (NS_WARN_IF(NS_FAILED(loadChecker->Result()))) {
    2582           0 :       if (loadChecker->Result() == NS_ERROR_NOT_AVAILABLE) {
    2583           0 :         aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(aScope);
    2584             :       } else {
    2585           0 :         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    2586             :       }
    2587           0 :       return nullptr;
    2588             :     }
    2589             :   }
    2590             : 
    2591             : 
    2592           0 :   RefPtr<Promise> p = Promise::Create(aGlobal, aRv);
    2593           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2594           0 :     return nullptr;
    2595             :   }
    2596             : 
    2597             :   // We check permission here rather than pass the Promise to NotificationTask
    2598             :   // which leads to uglier code.
    2599           0 :   NotificationPermission permission = GetPermission(aGlobal, aRv);
    2600             : 
    2601             :   // "If permission for notification's origin is not "granted", reject promise with a TypeError exception, and terminate these substeps."
    2602           0 :   if (NS_WARN_IF(aRv.Failed()) || permission == NotificationPermission::Denied) {
    2603           0 :     ErrorResult result;
    2604           0 :     result.ThrowTypeError<MSG_NOTIFICATION_PERMISSION_DENIED>();
    2605           0 :     p->MaybeReject(result);
    2606           0 :     return p.forget();
    2607             :   }
    2608             : 
    2609             :   // "Otherwise, resolve promise with undefined."
    2610             :   // The Notification may still not be shown due to other errors, but the spec
    2611             :   // is not concerned with those.
    2612           0 :   p->MaybeResolveWithUndefined();
    2613             : 
    2614             :   RefPtr<Notification> notification =
    2615           0 :     CreateAndShow(aCx, aGlobal, aTitle, aOptions, aScope, aRv);
    2616           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2617           0 :     return nullptr;
    2618             :   }
    2619             : 
    2620           0 :   return p.forget();
    2621             : }
    2622             : 
    2623             : /* static */ already_AddRefed<Notification>
    2624           0 : Notification::CreateAndShow(JSContext* aCx,
    2625             :                             nsIGlobalObject* aGlobal,
    2626             :                             const nsAString& aTitle,
    2627             :                             const NotificationOptions& aOptions,
    2628             :                             const nsAString& aScope,
    2629             :                             ErrorResult& aRv)
    2630             : {
    2631           0 :   MOZ_ASSERT(aGlobal);
    2632             : 
    2633           0 :   RefPtr<Notification> notification = CreateInternal(aGlobal, EmptyString(),
    2634           0 :                                                      aTitle, aOptions);
    2635             : 
    2636             :   // Make a structured clone of the aOptions.mData object
    2637           0 :   JS::Rooted<JS::Value> data(aCx, aOptions.mData);
    2638           0 :   notification->InitFromJSVal(aCx, data, aRv);
    2639           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2640           0 :     return nullptr;
    2641             :   }
    2642             : 
    2643           0 :   notification->SetScope(aScope);
    2644             : 
    2645           0 :   auto ref = MakeUnique<NotificationRef>(notification);
    2646           0 :   if (NS_WARN_IF(!ref->Initialized())) {
    2647           0 :     aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
    2648           0 :     return nullptr;
    2649             :   }
    2650             : 
    2651             :   // Queue a task to show the notification.
    2652             :   nsCOMPtr<nsIRunnable> showNotificationTask =
    2653           0 :     new NotificationTask("Notification::CreateAndShow", Move(ref),
    2654           0 :                          NotificationTask::eShow);
    2655             : 
    2656             :   nsresult rv =
    2657           0 :     notification->DispatchToMainThread(showNotificationTask.forget());
    2658             : 
    2659           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2660           0 :     notification->DispatchTrustedEvent(NS_LITERAL_STRING("error"));
    2661             :   }
    2662             : 
    2663           0 :   return notification.forget();
    2664             : }
    2665             : 
    2666             : /* static */ nsresult
    2667           0 : Notification::RemovePermission(nsIPrincipal* aPrincipal)
    2668             : {
    2669           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    2670             :   nsCOMPtr<nsIPermissionManager> permissionManager =
    2671           0 :     mozilla::services::GetPermissionManager();
    2672           0 :   if (!permissionManager) {
    2673           0 :     return NS_ERROR_FAILURE;
    2674             :   }
    2675           0 :   permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification");
    2676           0 :   return NS_OK;
    2677             : }
    2678             : 
    2679             : /* static */ nsresult
    2680           0 : Notification::OpenSettings(nsIPrincipal* aPrincipal)
    2681             : {
    2682           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    2683           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    2684           0 :   if (!obs) {
    2685           0 :     return NS_ERROR_FAILURE;
    2686             :   }
    2687             :   // Notify other observers so they can show settings UI.
    2688           0 :   obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
    2689           0 :   return NS_OK;
    2690             : }
    2691             : 
    2692             : NS_IMETHODIMP
    2693           0 : Notification::Observe(nsISupports* aSubject, const char* aTopic,
    2694             :                       const char16_t* aData)
    2695             : {
    2696           0 :   AssertIsOnMainThread();
    2697             : 
    2698           0 :   if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) ||
    2699           0 :       !strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC)) {
    2700             : 
    2701           0 :     nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
    2702           0 :     if (SameCOMIdentity(aSubject, window)) {
    2703             :       nsCOMPtr<nsIObserverService> obs =
    2704           0 :         mozilla::services::GetObserverService();
    2705           0 :       if (obs) {
    2706           0 :         obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
    2707           0 :         obs->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
    2708             :       }
    2709             : 
    2710           0 :       CloseInternal();
    2711             :     }
    2712             :   }
    2713             : 
    2714           0 :   return NS_OK;
    2715             : }
    2716             : 
    2717             : nsresult
    2718           0 : Notification::DispatchToMainThread(already_AddRefed<nsIRunnable>&& aRunnable)
    2719             : {
    2720           0 :   if (mWorkerPrivate) {
    2721           0 :     return mWorkerPrivate->DispatchToMainThread(Move(aRunnable));
    2722             :   }
    2723           0 :   AssertIsOnMainThread();
    2724           0 :   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
    2725           0 :     if (nsIEventTarget* target = global->EventTargetFor(TaskCategory::Other)) {
    2726           0 :       return target->Dispatch(Move(aRunnable), nsIEventTarget::DISPATCH_NORMAL);
    2727             :     }
    2728             :   }
    2729           0 :   nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
    2730           0 :   MOZ_ASSERT(mainTarget);
    2731           0 :   return mainTarget->Dispatch(Move(aRunnable), nsIEventTarget::DISPATCH_NORMAL);
    2732             : }
    2733             : 
    2734             : } // namespace dom
    2735             : } // namespace mozilla

Generated by: LCOV version 1.13