dom/src/notification/DesktopNotification.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/. */
     4 #include "mozilla/dom/DesktopNotification.h"
     5 #include "mozilla/dom/DesktopNotificationBinding.h"
     6 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
     7 #include "nsContentPermissionHelper.h"
     8 #include "nsXULAppAPI.h"
     9 #include "mozilla/dom/PBrowserChild.h"
    10 #include "nsIDOMDesktopNotification.h"
    11 #include "TabChild.h"
    12 #include "mozilla/Preferences.h"
    13 #include "nsGlobalWindow.h"
    14 #include "nsIAppsService.h"
    15 #include "PCOMContentPermissionRequestChild.h"
    16 #include "nsIScriptSecurityManager.h"
    17 #include "nsServiceManagerUtils.h"
    18 #include "PermissionMessageUtils.h"
    20 namespace mozilla {
    21 namespace dom {
    23 /*
    24  * Simple Request
    25  */
    26 class DesktopNotificationRequest : public nsIContentPermissionRequest,
    27                                    public nsRunnable,
    28                                    public PCOMContentPermissionRequestChild
    30 {
    31 public:
    32   NS_DECL_ISUPPORTS
    33   NS_DECL_NSICONTENTPERMISSIONREQUEST
    35   DesktopNotificationRequest(DesktopNotification* notification)
    36     : mDesktopNotification(notification) {}
    38   NS_IMETHOD Run() MOZ_OVERRIDE
    39   {
    40     nsCOMPtr<nsIContentPermissionPrompt> prompt =
    41       do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
    42     if (prompt) {
    43       prompt->Prompt(this);
    44     }
    45     return NS_OK;
    46   }
    48   ~DesktopNotificationRequest()
    49   {
    50   }
    52   virtual bool Recv__delete__(const bool& aAllow,
    53                               const InfallibleTArray<PermissionChoice>& choices) MOZ_OVERRIDE
    54   {
    55     MOZ_ASSERT(choices.IsEmpty(), "DesktopNotification doesn't support permission choice");
    56     if (aAllow) {
    57       (void) Allow(JS::UndefinedHandleValue);
    58     } else {
    59      (void) Cancel();
    60     }
    61    return true;
    62   }
    63   virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
    65   nsRefPtr<DesktopNotification> mDesktopNotification;
    66 };
    68 /* ------------------------------------------------------------------------ */
    69 /* AlertServiceObserver                                                     */
    70 /* ------------------------------------------------------------------------ */
    72 NS_IMPL_ISUPPORTS(AlertServiceObserver, nsIObserver)
    74 /* ------------------------------------------------------------------------ */
    75 /* DesktopNotification                                                      */
    76 /* ------------------------------------------------------------------------ */
    78 uint32_t DesktopNotification::sCount = 0;
    80 nsresult
    81 DesktopNotification::PostDesktopNotification()
    82 {
    83   if (!mObserver) {
    84     mObserver = new AlertServiceObserver(this);
    85   }
    87 #ifdef MOZ_B2G
    88   nsCOMPtr<nsIAppNotificationService> appNotifier =
    89     do_GetService("@mozilla.org/system-alerts-service;1");
    90   if (appNotifier) {
    91     nsCOMPtr<nsPIDOMWindow> window = GetOwner();
    92     uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
    94     if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
    95       nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
    96       nsString manifestUrl = EmptyString();
    97       appsService->GetManifestURLByLocalId(appId, manifestUrl);
    98       mozilla::AutoSafeJSContext cx;
    99       JS::Rooted<JS::Value> val(cx);
   100       AppNotificationServiceOptions ops;
   101       ops.mTextClickable = true;
   102       ops.mManifestURL = manifestUrl;
   104       if (!ops.ToObject(cx, &val)) {
   105         return NS_ERROR_FAILURE;
   106       }
   108       return appNotifier->ShowAppNotification(mIconURL, mTitle, mDescription,
   109                                               mObserver, val);
   110     }
   111   }
   112 #endif
   114   nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
   115   if (!alerts) {
   116     return NS_ERROR_NOT_IMPLEMENTED;
   117   }
   119   // Generate a unique name (which will also be used as a cookie) because
   120   // the nsIAlertsService will coalesce notifications with the same name.
   121   // In the case of IPC, the parent process will use the cookie to map
   122   // to nsIObservers, thus cookies must be unique to differentiate observers.
   123   nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
   124   uniqueName.AppendInt(sCount++);
   125   nsIPrincipal* principal = GetOwner()->GetDoc()->NodePrincipal();
   126   return alerts->ShowAlertNotification(mIconURL, mTitle, mDescription,
   127                                        true,
   128                                        uniqueName,
   129                                        mObserver,
   130                                        uniqueName,
   131                                        NS_LITERAL_STRING("auto"),
   132                                        EmptyString(),
   133                                        principal);
   134 }
   136 DesktopNotification::DesktopNotification(const nsAString & title,
   137                                          const nsAString & description,
   138                                          const nsAString & iconURL,
   139                                          nsPIDOMWindow *aWindow,
   140                                          nsIPrincipal* principal)
   141   : DOMEventTargetHelper(aWindow)
   142   , mTitle(title)
   143   , mDescription(description)
   144   , mIconURL(iconURL)
   145   , mPrincipal(principal)
   146   , mAllow(false)
   147   , mShowHasBeenCalled(false)
   148 {
   149   if (Preferences::GetBool("notification.disabled", false)) {
   150     return;
   151   }
   153   // If we are in testing mode (running mochitests, for example)
   154   // and we are suppose to allow requests, then just post an allow event.
   155   if (Preferences::GetBool("notification.prompt.testing", false) &&
   156       Preferences::GetBool("notification.prompt.testing.allow", true)) {
   157     mAllow = true;
   158   }
   159 }
   161 void
   162 DesktopNotification::Init()
   163 {
   164   nsRefPtr<DesktopNotificationRequest> request = new DesktopNotificationRequest(this);
   166   // if we are in the content process, then remote it to the parent.
   167   if (XRE_GetProcessType() == GeckoProcessType_Content) {
   169     // if for some reason mOwner is null, just silently
   170     // bail.  The user will not see a notification, and that
   171     // is fine.
   172     if (!GetOwner())
   173       return;
   175     // because owner implements nsITabChild, we can assume that it is
   176     // the one and only TabChild for this docshell.
   177     TabChild* child = TabChild::GetFrom(GetOwner()->GetDocShell());
   179     // Retain a reference so the object isn't deleted without IPDL's knowledge.
   180     // Corresponding release occurs in DeallocPContentPermissionRequest.
   181     nsRefPtr<DesktopNotificationRequest> copy = request;
   183     nsTArray<PermissionRequest> permArray;
   184     nsTArray<nsString> emptyOptions;
   185     permArray.AppendElement(PermissionRequest(
   186                             NS_LITERAL_CSTRING("desktop-notification"),
   187                             NS_LITERAL_CSTRING("unused"),
   188                             emptyOptions));
   189     child->SendPContentPermissionRequestConstructor(copy.forget().take(),
   190                                                     permArray,
   191                                                     IPC::Principal(mPrincipal));
   193     request->Sendprompt();
   194     return;
   195   }
   197   // otherwise, dispatch it
   198   NS_DispatchToMainThread(request);
   199 }
   201 DesktopNotification::~DesktopNotification()
   202 {
   203   if (mObserver) {
   204     mObserver->Disconnect();
   205   }
   206 }
   208 void
   209 DesktopNotification::DispatchNotificationEvent(const nsString& aName)
   210 {
   211   if (NS_FAILED(CheckInnerWindowCorrectness())) {
   212     return;
   213   }
   215   nsCOMPtr<nsIDOMEvent> event;
   216   nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
   217   if (NS_SUCCEEDED(rv)) {
   218     // it doesn't bubble, and it isn't cancelable
   219     rv = event->InitEvent(aName, false, false);
   220     if (NS_SUCCEEDED(rv)) {
   221       event->SetTrusted(true);
   222       DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   223     }
   224   }
   225 }
   227 nsresult
   228 DesktopNotification::SetAllow(bool aAllow)
   229 {
   230   mAllow = aAllow;
   232   // if we have called Show() already, lets go ahead and post a notification
   233   if (mShowHasBeenCalled && aAllow) {
   234     return PostDesktopNotification();
   235   }
   237   return NS_OK;
   238 }
   240 void
   241 DesktopNotification::HandleAlertServiceNotification(const char *aTopic)
   242 {
   243   if (NS_FAILED(CheckInnerWindowCorrectness())) {
   244     return;
   245   }
   247   if (!strcmp("alertclickcallback", aTopic)) {
   248     DispatchNotificationEvent(NS_LITERAL_STRING("click"));
   249   } else if (!strcmp("alertfinished", aTopic)) {
   250     DispatchNotificationEvent(NS_LITERAL_STRING("close"));
   251   }
   252 }
   254 void
   255 DesktopNotification::Show(ErrorResult& aRv)
   256 {
   257   mShowHasBeenCalled = true;
   259   if (!mAllow) {
   260     return;
   261   }
   263   aRv = PostDesktopNotification();
   264 }
   266 JSObject*
   267 DesktopNotification::WrapObject(JSContext* aCx)
   268 {
   269   return DesktopNotificationBinding::Wrap(aCx, this);
   270 }
   272 /* ------------------------------------------------------------------------ */
   273 /* DesktopNotificationCenter                                                */
   274 /* ------------------------------------------------------------------------ */
   276 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(DesktopNotificationCenter)
   277 NS_IMPL_CYCLE_COLLECTING_ADDREF(DesktopNotificationCenter)
   278 NS_IMPL_CYCLE_COLLECTING_RELEASE(DesktopNotificationCenter)
   279 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DesktopNotificationCenter)
   280   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   281   NS_INTERFACE_MAP_ENTRY(nsISupports)
   282 NS_INTERFACE_MAP_END
   284 already_AddRefed<DesktopNotification>
   285 DesktopNotificationCenter::CreateNotification(const nsAString& aTitle,
   286                                               const nsAString& aDescription,
   287                                               const nsAString& aIconURL)
   288 {
   289   MOZ_ASSERT(mOwner);
   291   nsRefPtr<DesktopNotification> notification =
   292     new DesktopNotification(aTitle,
   293                             aDescription,
   294                             aIconURL,
   295                             mOwner,
   296                             mPrincipal);
   297   notification->Init();
   298   return notification.forget();
   299 }
   301 JSObject*
   302 DesktopNotificationCenter::WrapObject(JSContext* aCx)
   303 {
   304   return DesktopNotificationCenterBinding::Wrap(aCx, this);
   305 }
   307 /* ------------------------------------------------------------------------ */
   308 /* DesktopNotificationRequest                                               */
   309 /* ------------------------------------------------------------------------ */
   311 NS_IMPL_ISUPPORTS(DesktopNotificationRequest,
   312                   nsIContentPermissionRequest,
   313                   nsIRunnable)
   315 NS_IMETHODIMP
   316 DesktopNotificationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
   317 {
   318   if (!mDesktopNotification) {
   319     return NS_ERROR_NOT_INITIALIZED;
   320   }
   322   NS_IF_ADDREF(*aRequestingPrincipal = mDesktopNotification->mPrincipal);
   323   return NS_OK;
   324 }
   326 NS_IMETHODIMP
   327 DesktopNotificationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
   328 {
   329   if (!mDesktopNotification) {
   330     return NS_ERROR_NOT_INITIALIZED;
   331   }
   333   NS_IF_ADDREF(*aRequestingWindow = mDesktopNotification->GetOwner());
   334   return NS_OK;
   335 }
   337 NS_IMETHODIMP
   338 DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement)
   339 {
   340   NS_ENSURE_ARG_POINTER(aElement);
   341   *aElement = nullptr;
   342   return NS_OK;
   343 }
   345 NS_IMETHODIMP
   346 DesktopNotificationRequest::Cancel()
   347 {
   348   nsresult rv = mDesktopNotification->SetAllow(false);
   349   mDesktopNotification = nullptr;
   350   return rv;
   351 }
   353 NS_IMETHODIMP
   354 DesktopNotificationRequest::Allow(JS::HandleValue aChoices)
   355 {
   356   MOZ_ASSERT(aChoices.isUndefined());
   357   nsresult rv = mDesktopNotification->SetAllow(true);
   358   mDesktopNotification = nullptr;
   359   return rv;
   360 }
   362 NS_IMETHODIMP
   363 DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
   364 {
   365   nsTArray<nsString> emptyOptions;
   366   return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
   367                                NS_LITERAL_CSTRING("unused"),
   368                                emptyOptions,
   369                                aTypes);
   370 }
   372 } // namespace dom
   373 } // namespace mozilla

mercurial