dom/src/notification/Notification.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "PCOMContentPermissionRequestChild.h"
     6 #include "mozilla/dom/Notification.h"
     7 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
     8 #include "mozilla/dom/OwningNonNull.h"
     9 #include "mozilla/dom/Promise.h"
    10 #include "mozilla/Preferences.h"
    11 #include "TabChild.h"
    12 #include "nsContentUtils.h"
    13 #include "nsIAlertsService.h"
    14 #include "nsIAppsService.h"
    15 #include "nsIContentPermissionPrompt.h"
    16 #include "nsIDocument.h"
    17 #include "nsINotificationStorage.h"
    18 #include "nsIPermissionManager.h"
    19 #include "nsIUUIDGenerator.h"
    20 #include "nsServiceManagerUtils.h"
    21 #include "nsToolkitCompsCID.h"
    22 #include "nsGlobalWindow.h"
    23 #include "nsDOMJSUtils.h"
    24 #include "nsIScriptSecurityManager.h"
    25 #include "mozilla/dom/PermissionMessageUtils.h"
    26 #include "nsContentPermissionHelper.h"
    27 #ifdef MOZ_B2G
    28 #include "nsIDOMDesktopNotification.h"
    29 #endif
    31 namespace mozilla {
    32 namespace dom {
    34 class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback
    35 {
    36 public:
    37   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    38   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
    40   NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise)
    41     : mCount(0),
    42       mGlobal(aGlobal.Get()),
    43       mWindow(aWindow),
    44       mPromise(aPromise)
    45   {
    46     MOZ_ASSERT(aWindow);
    47     MOZ_ASSERT(aPromise);
    48     JSContext* cx = aGlobal.GetContext();
    49     JSAutoCompartment ac(cx, mGlobal);
    50     mNotifications = JS_NewArrayObject(cx, 0);
    51     HoldData();
    52   }
    54   NS_IMETHOD Handle(const nsAString& aID,
    55                     const nsAString& aTitle,
    56                     const nsAString& aDir,
    57                     const nsAString& aLang,
    58                     const nsAString& aBody,
    59                     const nsAString& aTag,
    60                     const nsAString& aIcon,
    61                     JSContext* aCx)
    62   {
    63     MOZ_ASSERT(!aID.IsEmpty());
    65     NotificationOptions options;
    66     options.mDir = Notification::StringToDirection(nsString(aDir));
    67     options.mLang = aLang;
    68     options.mBody = aBody;
    69     options.mTag = aTag;
    70     options.mIcon = aIcon;
    71     nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow,
    72                                                                        aID,
    73                                                                        aTitle,
    74                                                                        options);
    75     JSAutoCompartment ac(aCx, mGlobal);
    76     JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx));
    77     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
    79     if (!JS_DefineElement(aCx, mNotifications, mCount++,
    80                           JS::ObjectValue(*element), nullptr, nullptr, 0)) {
    81       return NS_ERROR_FAILURE;
    82     }
    83     return NS_OK;
    84   }
    86   NS_IMETHOD Done(JSContext* aCx)
    87   {
    88     JSAutoCompartment ac(aCx, mGlobal);
    89     JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications));
    90     mPromise->MaybeResolve(aCx, result);
    91     return NS_OK;
    92   }
    94 private:
    95   ~NotificationStorageCallback()
    96   {
    97     DropData();
    98   }
   100   void HoldData()
   101   {
   102     mozilla::HoldJSObjects(this);
   103   }
   105   void DropData()
   106   {
   107     mGlobal = nullptr;
   108     mNotifications = nullptr;
   109     mozilla::DropJSObjects(this);
   110   }
   112   uint32_t  mCount;
   113   JS::Heap<JSObject *> mGlobal;
   114   nsCOMPtr<nsPIDOMWindow> mWindow;
   115   nsRefPtr<Promise> mPromise;
   116   JS::Heap<JSObject *> mNotifications;
   117 };
   119 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
   120 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
   121 NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
   123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
   124   NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
   125   NS_INTERFACE_MAP_ENTRY(nsISupports)
   126 NS_INTERFACE_MAP_END
   128 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback)
   129   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
   130   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications)
   131 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback)
   134   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   135   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   136   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
   137 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
   140   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   141   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
   142   tmp->DropData();
   143 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   145 class NotificationPermissionRequest : public nsIContentPermissionRequest,
   146                                       public PCOMContentPermissionRequestChild,
   147                                       public nsIRunnable
   148 {
   149 public:
   150   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   151   NS_DECL_NSICONTENTPERMISSIONREQUEST
   152   NS_DECL_NSIRUNNABLE
   153   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
   154                                            nsIContentPermissionRequest)
   156   NotificationPermissionRequest(nsIPrincipal* aPrincipal, nsPIDOMWindow* aWindow,
   157                                 NotificationPermissionCallback* aCallback)
   158     : mPrincipal(aPrincipal), mWindow(aWindow),
   159       mPermission(NotificationPermission::Default),
   160       mCallback(aCallback) {}
   162   virtual ~NotificationPermissionRequest() {}
   164   bool Recv__delete__(const bool& aAllow,
   165                       const InfallibleTArray<PermissionChoice>& choices);
   166   void IPDLRelease() { Release(); }
   168 protected:
   169   nsresult CallCallback();
   170   nsresult DispatchCallback();
   171   nsCOMPtr<nsIPrincipal> mPrincipal;
   172   nsCOMPtr<nsPIDOMWindow> mWindow;
   173   NotificationPermission mPermission;
   174   nsRefPtr<NotificationPermissionCallback> mCallback;
   175 };
   177 class NotificationObserver : public nsIObserver
   178 {
   179 public:
   180   NS_DECL_ISUPPORTS
   181   NS_DECL_NSIOBSERVER
   183   NotificationObserver(Notification* aNotification)
   184     : mNotification(aNotification) {}
   186   virtual ~NotificationObserver() {}
   188 protected:
   189   nsRefPtr<Notification> mNotification;
   190 };
   192 class NotificationTask : public nsIRunnable
   193 {
   194 public:
   195   NS_DECL_ISUPPORTS
   196   NS_DECL_NSIRUNNABLE
   198   enum NotificationAction {
   199     eShow,
   200     eClose
   201   };
   203   NotificationTask(Notification* aNotification, NotificationAction aAction)
   204     : mNotification(aNotification), mAction(aAction) {}
   206   virtual ~NotificationTask() {}
   208 protected:
   209   nsRefPtr<Notification> mNotification;
   210   NotificationAction mAction;
   211 };
   213 uint32_t Notification::sCount = 0;
   215 NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow)
   217 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
   218   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   219   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   220   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   221 NS_INTERFACE_MAP_END
   223 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
   224 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
   226 NS_IMETHODIMP
   227 NotificationPermissionRequest::Run()
   228 {
   229   if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
   230     mPermission = NotificationPermission::Granted;
   231   } else {
   232     // File are automatically granted permission.
   233     nsCOMPtr<nsIURI> uri;
   234     mPrincipal->GetURI(getter_AddRefs(uri));
   236     if (uri) {
   237       bool isFile;
   238       uri->SchemeIs("file", &isFile);
   239       if (isFile) {
   240         mPermission = NotificationPermission::Granted;
   241       }
   242     }
   243   }
   245   // Grant permission if pref'ed on.
   246   if (Preferences::GetBool("notification.prompt.testing", false)) {
   247     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
   248       mPermission = NotificationPermission::Granted;
   249     } else {
   250       mPermission = NotificationPermission::Denied;
   251     }
   252   }
   254   if (mPermission != NotificationPermission::Default) {
   255     return DispatchCallback();
   256   }
   258   if (XRE_GetProcessType() == GeckoProcessType_Content) {
   259     // because owner implements nsITabChild, we can assume that it is
   260     // the one and only TabChild.
   261     TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
   262     if (!child) {
   263       return NS_ERROR_NOT_AVAILABLE;
   264     }
   266     // Retain a reference so the object isn't deleted without IPDL's knowledge.
   267     // Corresponding release occurs in DeallocPContentPermissionRequest.
   268     AddRef();
   270     nsTArray<PermissionRequest> permArray;
   271     nsTArray<nsString> emptyOptions;
   272     permArray.AppendElement(PermissionRequest(
   273                             NS_LITERAL_CSTRING("desktop-notification"),
   274                             NS_LITERAL_CSTRING("unused"),
   275                             emptyOptions));
   276     child->SendPContentPermissionRequestConstructor(this, permArray,
   277                                                     IPC::Principal(mPrincipal));
   279     Sendprompt();
   280     return NS_OK;
   281   }
   283   nsCOMPtr<nsIContentPermissionPrompt> prompt =
   284     do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   285   if (prompt) {
   286     prompt->Prompt(this);
   287   }
   289   return NS_OK;
   290 }
   292 NS_IMETHODIMP
   293 NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
   294 {
   295   NS_ADDREF(*aRequestingPrincipal = mPrincipal);
   296   return NS_OK;
   297 }
   299 NS_IMETHODIMP
   300 NotificationPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
   301 {
   302   NS_ADDREF(*aRequestingWindow = mWindow);
   303   return NS_OK;
   304 }
   306 NS_IMETHODIMP
   307 NotificationPermissionRequest::GetElement(nsIDOMElement** aElement)
   308 {
   309   NS_ENSURE_ARG_POINTER(aElement);
   310   *aElement = nullptr;
   311   return NS_OK;
   312 }
   314 NS_IMETHODIMP
   315 NotificationPermissionRequest::Cancel()
   316 {
   317   mPermission = NotificationPermission::Denied;
   318   return DispatchCallback();
   319 }
   321 NS_IMETHODIMP
   322 NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
   323 {
   324   MOZ_ASSERT(aChoices.isUndefined());
   326   mPermission = NotificationPermission::Granted;
   327   return DispatchCallback();
   328 }
   330 inline nsresult
   331 NotificationPermissionRequest::DispatchCallback()
   332 {
   333   if (!mCallback) {
   334     return NS_OK;
   335   }
   337   nsCOMPtr<nsIRunnable> callbackRunnable = NS_NewRunnableMethod(this,
   338     &NotificationPermissionRequest::CallCallback);
   339   return NS_DispatchToMainThread(callbackRunnable);
   340 }
   342 nsresult
   343 NotificationPermissionRequest::CallCallback()
   344 {
   345   ErrorResult rv;
   346   mCallback->Call(mPermission, rv);
   347   return rv.ErrorCode();
   348 }
   350 NS_IMETHODIMP
   351 NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
   352 {
   353   nsTArray<nsString> emptyOptions;
   354   return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
   355                                NS_LITERAL_CSTRING("unused"),
   356                                emptyOptions,
   357                                aTypes);
   358 }
   360 bool
   361 NotificationPermissionRequest::Recv__delete__(const bool& aAllow,
   362                                               const InfallibleTArray<PermissionChoice>& choices)
   363 {
   364   MOZ_ASSERT(choices.IsEmpty(), "Notification doesn't support permission choice");
   366   if (aAllow) {
   367     (void) Allow(JS::UndefinedHandleValue);
   368   } else {
   369     (void) Cancel();
   370   }
   371   return true;
   372 }
   374 NS_IMPL_ISUPPORTS(NotificationTask, nsIRunnable)
   376 NS_IMETHODIMP
   377 NotificationTask::Run()
   378 {
   379   switch (mAction) {
   380   case eShow:
   381     mNotification->ShowInternal();
   382     break;
   383   case eClose:
   384     mNotification->CloseInternal();
   385     break;
   386   default:
   387     MOZ_CRASH("Unexpected action for NotificationTask.");
   388   }
   389   return NS_OK;
   390 }
   392 NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
   394 NS_IMETHODIMP
   395 NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
   396                               const char16_t* aData)
   397 {
   398   if (!strcmp("alertclickcallback", aTopic)) {
   399     nsCOMPtr<nsPIDOMWindow> window = mNotification->GetOwner();
   400     nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
   401     if (doc) {
   402       nsContentUtils::DispatchChromeEvent(doc, window,
   403                                           NS_LITERAL_STRING("DOMWebNotificationClicked"),
   404                                           true, true);
   405     }
   406     mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
   407   } else if (!strcmp("alertfinished", aTopic)) {
   408     mNotification->mIsClosed = true;
   409     mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
   410   } else if (!strcmp("alertshow", aTopic)) {
   411     mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
   412   }
   414   return NS_OK;
   415 }
   417 Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
   418                            NotificationDirection aDir, const nsAString& aLang,
   419                            const nsAString& aTag, const nsAString& aIconUrl,
   420 			   nsPIDOMWindow* aWindow)
   421   : DOMEventTargetHelper(aWindow),
   422     mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
   423     mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
   424 {
   425 }
   427 // static
   428 already_AddRefed<Notification>
   429 Notification::Constructor(const GlobalObject& aGlobal,
   430                           const nsAString& aTitle,
   431                           const NotificationOptions& aOptions,
   432                           ErrorResult& aRv)
   433 {
   434   MOZ_ASSERT(NS_IsMainThread());
   435   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   436   MOZ_ASSERT(window, "Window should not be null.");
   437   nsRefPtr<Notification> notification = CreateInternal(window,
   438                                                        EmptyString(),
   439                                                        aTitle,
   440                                                        aOptions);
   442   // Queue a task to show the notification.
   443   nsCOMPtr<nsIRunnable> showNotificationTask =
   444     new NotificationTask(notification, NotificationTask::eShow);
   445   NS_DispatchToCurrentThread(showNotificationTask);
   447   // Persist the notification.
   448   nsresult rv;
   449   nsCOMPtr<nsINotificationStorage> notificationStorage =
   450     do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
   451   if (NS_FAILED(rv)) {
   452     aRv.Throw(rv);
   453     return nullptr;
   454   }
   456   nsString origin;
   457   aRv = GetOrigin(window, origin);
   458   if (aRv.Failed()) {
   459     return nullptr;
   460   }
   462   nsString id;
   463   notification->GetID(id);
   464   aRv = notificationStorage->Put(origin,
   465                                  id,
   466                                  aTitle,
   467                                  DirectionToString(aOptions.mDir),
   468                                  aOptions.mLang,
   469                                  aOptions.mBody,
   470                                  aOptions.mTag,
   471                                  aOptions.mIcon);
   472   if (aRv.Failed()) {
   473     return nullptr;
   474   }
   476   return notification.forget();
   477 }
   479 already_AddRefed<Notification>
   480 Notification::CreateInternal(nsPIDOMWindow* aWindow,
   481                              const nsAString& aID,
   482                              const nsAString& aTitle,
   483                              const NotificationOptions& aOptions)
   484 {
   485   nsString id;
   486   if (!aID.IsEmpty()) {
   487     id = aID;
   488   } else {
   489     nsCOMPtr<nsIUUIDGenerator> uuidgen =
   490       do_GetService("@mozilla.org/uuid-generator;1");
   491     NS_ENSURE_TRUE(uuidgen, nullptr);
   492     nsID uuid;
   493     nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
   494     NS_ENSURE_SUCCESS(rv, nullptr);
   496     char buffer[NSID_LENGTH];
   497     uuid.ToProvidedString(buffer);
   498     NS_ConvertASCIItoUTF16 convertedID(buffer);
   499     id = convertedID;
   500   }
   502   nsRefPtr<Notification> notification = new Notification(id,
   503                                                          aTitle,
   504                                                          aOptions.mBody,
   505                                                          aOptions.mDir,
   506                                                          aOptions.mLang,
   507                                                          aOptions.mTag,
   508                                                          aOptions.mIcon,
   509 							 aWindow);
   510   return notification.forget();
   511 }
   513 nsIPrincipal*
   514 Notification::GetPrincipal()
   515 {
   516   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
   517   NS_ENSURE_TRUE(sop, nullptr);
   518   return sop->GetPrincipal();
   519 }
   521 void
   522 Notification::ShowInternal()
   523 {
   524   nsCOMPtr<nsIAlertsService> alertService =
   525     do_GetService(NS_ALERTSERVICE_CONTRACTID);
   527   ErrorResult result;
   528   if (GetPermissionInternal(GetOwner(), result) !=
   529     NotificationPermission::Granted || !alertService) {
   530     // We do not have permission to show a notification or alert service
   531     // is not available.
   532     DispatchTrustedEvent(NS_LITERAL_STRING("error"));
   533     return;
   534   }
   536   nsresult rv;
   537   nsAutoString absoluteUrl;
   538   if (mIconUrl.Length() > 0) {
   539     // Resolve image URL against document base URI.
   540     nsIDocument* doc = GetOwner()->GetExtantDoc();
   541     if (doc) {
   542       nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
   543       if (baseUri) {
   544         nsCOMPtr<nsIURI> srcUri;
   545         rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
   546                                                        mIconUrl, doc, baseUri);
   547         if (NS_SUCCEEDED(rv)) {
   548           nsAutoCString src;
   549           srcUri->GetSpec(src);
   550           absoluteUrl = NS_ConvertUTF8toUTF16(src);
   551         }
   552       }
   554     }
   555   }
   557   nsCOMPtr<nsIObserver> observer = new NotificationObserver(this);
   559   nsString alertName;
   560   rv = GetAlertName(alertName);
   561   NS_ENSURE_SUCCESS_VOID(rv);
   563 #ifdef MOZ_B2G
   564   nsCOMPtr<nsIAppNotificationService> appNotifier =
   565     do_GetService("@mozilla.org/system-alerts-service;1");
   566   if (appNotifier) {
   567     nsCOMPtr<nsPIDOMWindow> window = GetOwner();
   568     uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
   570     if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
   571       nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
   572       nsString manifestUrl = EmptyString();
   573       rv = appsService->GetManifestURLByLocalId(appId, manifestUrl);
   574       if (NS_SUCCEEDED(rv)) {
   575         mozilla::AutoSafeJSContext cx;
   576         JS::Rooted<JS::Value> val(cx);
   577         AppNotificationServiceOptions ops;
   578         ops.mTextClickable = true;
   579         ops.mManifestURL = manifestUrl;
   580         ops.mId = alertName;
   581         ops.mDbId = mID;
   582         ops.mDir = DirectionToString(mDir);
   583         ops.mLang = mLang;
   584         ops.mTag = mTag;
   586         if (!ops.ToObject(cx, &val)) {
   587           NS_WARNING("Converting dict to object failed!");
   588           return;
   589         }
   591         appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
   592                                          observer, val);
   593         return;
   594       }
   595     }
   596   }
   597 #endif
   599   // In the case of IPC, the parent process uses the cookie to map to
   600   // nsIObserver. Thus the cookie must be unique to differentiate observers.
   601   nsString uniqueCookie = NS_LITERAL_STRING("notification:");
   602   uniqueCookie.AppendInt(sCount++);
   603   alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
   604                                       uniqueCookie, observer, alertName,
   605                                       DirectionToString(mDir), mLang,
   606                                              GetPrincipal());
   607 }
   609 void
   610 Notification::RequestPermission(const GlobalObject& aGlobal,
   611                                 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
   612                                 ErrorResult& aRv)
   613 {
   614   // Get principal from global to make permission request for notifications.
   615   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   616   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports());
   617   if (!sop) {
   618     aRv.Throw(NS_ERROR_UNEXPECTED);
   619     return;
   620   }
   621   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   623   NotificationPermissionCallback* permissionCallback = nullptr;
   624   if (aCallback.WasPassed()) {
   625     permissionCallback = &aCallback.Value();
   626   }
   627   nsCOMPtr<nsIRunnable> request =
   628     new NotificationPermissionRequest(principal, window, permissionCallback);
   630   NS_DispatchToMainThread(request);
   631 }
   633 NotificationPermission
   634 Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
   635 {
   636   return GetPermissionInternal(aGlobal.GetAsSupports(), aRv);
   637 }
   639 NotificationPermission
   640 Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
   641 {
   642   // Get principal from global to check permission for notifications.
   643   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
   644   if (!sop) {
   645     aRv.Throw(NS_ERROR_UNEXPECTED);
   646     return NotificationPermission::Denied;
   647   }
   649   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   650   if (nsContentUtils::IsSystemPrincipal(principal)) {
   651     return NotificationPermission::Granted;
   652   } else {
   653     // Allow files to show notifications by default.
   654     nsCOMPtr<nsIURI> uri;
   655     principal->GetURI(getter_AddRefs(uri));
   656     if (uri) {
   657       bool isFile;
   658       uri->SchemeIs("file", &isFile);
   659       if (isFile) {
   660         return NotificationPermission::Granted;
   661       }
   662     }
   663   }
   665   // We also allow notifications is they are pref'ed on.
   666   if (Preferences::GetBool("notification.prompt.testing", false)) {
   667     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
   668       return NotificationPermission::Granted;
   669     } else {
   670       return NotificationPermission::Denied;
   671     }
   672   }
   674   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
   676   nsCOMPtr<nsIPermissionManager> permissionManager =
   677     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   679   permissionManager->TestPermissionFromPrincipal(principal,
   680                                                  "desktop-notification",
   681                                                  &permission);
   683   // Convert the result to one of the enum types.
   684   switch (permission) {
   685   case nsIPermissionManager::ALLOW_ACTION:
   686     return NotificationPermission::Granted;
   687   case nsIPermissionManager::DENY_ACTION:
   688     return NotificationPermission::Denied;
   689   default:
   690     return NotificationPermission::Default;
   691   }
   692 }
   694 already_AddRefed<Promise>
   695 Notification::Get(const GlobalObject& aGlobal,
   696                   const GetNotificationOptions& aFilter,
   697                   ErrorResult& aRv)
   698 {
   699   nsCOMPtr<nsIGlobalObject> global =
   700     do_QueryInterface(aGlobal.GetAsSupports());
   701   MOZ_ASSERT(global);
   702   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
   703   MOZ_ASSERT(window);
   704   nsIDocument* doc = window->GetExtantDoc();
   705   if (!doc) {
   706     aRv.Throw(NS_ERROR_UNEXPECTED);
   707     return nullptr;
   708   }
   710   nsString origin;
   711   aRv = GetOrigin(window, origin);
   712   if (aRv.Failed()) {
   713     return nullptr;
   714   }
   716   nsresult rv;
   717   nsCOMPtr<nsINotificationStorage> notificationStorage =
   718     do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
   719   if (NS_FAILED(rv)) {
   720     aRv.Throw(rv);
   721     return nullptr;
   722   }
   724   nsRefPtr<Promise> promise = new Promise(global);
   725   nsCOMPtr<nsINotificationStorageCallback> callback =
   726     new NotificationStorageCallback(aGlobal, window, promise);
   727   nsString tag = aFilter.mTag.WasPassed() ?
   728                  aFilter.mTag.Value() :
   729                  EmptyString();
   730   aRv = notificationStorage->Get(origin, tag, callback);
   731   if (aRv.Failed()) {
   732     return nullptr;
   733   }
   735   return promise.forget();
   736 }
   738 JSObject*
   739 Notification::WrapObject(JSContext* aCx)
   740 {
   741   return mozilla::dom::NotificationBinding::Wrap(aCx, this);
   742 }
   744 void
   745 Notification::Close()
   746 {
   747   // Queue a task to close the notification.
   748   nsCOMPtr<nsIRunnable> closeNotificationTask =
   749     new NotificationTask(this, NotificationTask::eClose);
   750   NS_DispatchToMainThread(closeNotificationTask);
   751 }
   753 void
   754 Notification::CloseInternal()
   755 {
   756   if (!mIsClosed) {
   757     nsresult rv;
   758     // Don't bail out if notification storage fails, since we still
   759     // want to send the close event through the alert service.
   760     nsCOMPtr<nsINotificationStorage> notificationStorage =
   761       do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
   762     if (notificationStorage) {
   763       nsString origin;
   764       rv = GetOrigin(GetOwner(), origin);
   765       if (NS_SUCCEEDED(rv)) {
   766         notificationStorage->Delete(origin, mID);
   767       }
   768     }
   770     nsCOMPtr<nsIAlertsService> alertService =
   771       do_GetService(NS_ALERTSERVICE_CONTRACTID);
   772     if (alertService) {
   773       nsString alertName;
   774       rv = GetAlertName(alertName);
   775       if (NS_SUCCEEDED(rv)) {
   776         alertService->CloseAlert(alertName, GetPrincipal());
   777       }
   778     }
   779   }
   780 }
   782 nsresult
   783 Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
   784 {
   785   if (!aWindow) {
   786     return NS_ERROR_FAILURE;
   787   }
   788   nsresult rv;
   789   nsIDocument* doc = aWindow->GetExtantDoc();
   790   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
   791   nsIPrincipal* principal = doc->NodePrincipal();
   792   NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
   794   uint16_t appStatus = principal->GetAppStatus();
   795   uint32_t appId = principal->GetAppId();
   797   if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
   798       appId == nsIScriptSecurityManager::NO_APP_ID ||
   799       appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
   800     rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
   801     NS_ENSURE_SUCCESS(rv, rv);
   802   } else {
   803     // If we are in "app code", use manifest URL as unique origin since
   804     // multiple apps can share the same origin but not same notifications.
   805     nsCOMPtr<nsIAppsService> appsService =
   806       do_GetService("@mozilla.org/AppsService;1", &rv);
   807     NS_ENSURE_SUCCESS(rv, rv);
   808     appsService->GetManifestURLByLocalId(appId, aOrigin);
   809   }
   811   return NS_OK;
   812 }
   814 nsresult
   815 Notification::GetAlertName(nsString& aAlertName)
   816 {
   817   // Get the notification name that is unique per origin + tag/ID.
   818   // The name of the alert is of the form origin#tag/ID.
   819   nsresult rv = GetOrigin(GetOwner(), aAlertName);
   820   NS_ENSURE_SUCCESS(rv, rv);
   821   aAlertName.AppendLiteral("#");
   822   if (!mTag.IsEmpty()) {
   823     aAlertName.Append(NS_LITERAL_STRING("tag:"));
   824     aAlertName.Append(mTag);
   825   } else {
   826     aAlertName.Append(NS_LITERAL_STRING("notag:"));
   827     aAlertName.Append(mID);
   828   }
   829   return NS_OK;
   830 }
   832 } // namespace dom
   833 } // namespace mozilla

mercurial