Bug 1259148 - Notify content when the notification permission pop-up is dismissed by the user. f?past,MattN draft
authorKit Cambridge <kcambridge@mozilla.com>
Wed, 30 Mar 2016 16:54:37 -0700
changeset 346135 bdebc0450a70362286299a3b81f842e628e2eec3
parent 345720 494289c72ba3997183e7b5beaca3e0447ecaf96d
child 517332 f479463a50371ccfbfe3a5d4410cd1493a234c12
push id14248
push userkcambridge@mozilla.com
push dateThu, 31 Mar 2016 00:14:07 +0000
bugs1259148
milestone48.0a1
Bug 1259148 - Notify content when the notification permission pop-up is dismissed by the user. f?past,MattN MozReview-Commit-ID: 8CcgQcJDeie
browser/components/nsBrowserGlue.js
dom/notification/Notification.cpp
dom/notification/Notification.h
dom/push/Push.js
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/histogram-whitelists.json
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2630,16 +2630,26 @@ ContentPermissionPrompt.prototype = {
           callback: function() {},
         },
       ];
     }
 
     var options = {
       learnMoreURL:
         Services.urlFormatter.formatURLPref("app.support.baseURL") + "push",
+      eventCallback(type) {
+        if (type == "dismissed") {
+          // Hide the doorhanger. Unlike other permission doorhangers, the
+          // user can't click the icon in the address bar to restore the
+          // doorhanger. Instead, the site must request permission again.
+          // See bug 1259148 for context.
+          this.remove();
+          aRequest.cancel();
+        }
+      },
     };
 
     this._showPrompt(aRequest, message, "desktop-notification", actions,
                      "web-notifications",
                      "web-notifications-notification-icon", options);
   },
 
   _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) {
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -609,17 +609,21 @@ NotificationPermissionRequest::GetElemen
   NS_ENSURE_ARG_POINTER(aElement);
   *aElement = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NotificationPermissionRequest::Cancel()
 {
-  mPermission = NotificationPermission::Denied;
+  // `Cancel` is called if the user denied permission or dismissed the
+  // permission request. To distinguish between the two, we set the
+  // permission to "default" and query the permission manager on the
+  // main thread.
+  mPermission = NotificationPermission::Default;
   return DispatchResolvePromise();
 }
 
 NS_IMETHODIMP
 NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
 {
   MOZ_ASSERT(aChoices.isUndefined());
 
@@ -644,16 +648,21 @@ NotificationPermissionRequest::DispatchR
     &NotificationPermissionRequest::ResolvePromise);
   return NS_DispatchToMainThread(resolveRunnable);
 }
 
 nsresult
 NotificationPermissionRequest::ResolvePromise()
 {
   nsresult rv = NS_OK;
+  if (mPermission == NotificationPermission::Default) {
+    // This will still be "default" if the user dismissed the doorhanger,
+    // or "denied" otherwise.
+    mPermission = Notification::TestPermission(mPrincipal);
+  }
   if (mCallback) {
     ErrorResult error;
     mCallback->Call(mPermission, error);
     rv = error.StealNSResult();
   }
   Telemetry::Accumulate(
     Telemetry::WEB_NOTIFICATION_REQUEST_PERMISSION_CALLBACK, !!mCallback);
   mPromise->MaybeResolve(mPermission);
@@ -1942,20 +1951,31 @@ Notification::GetPermissionInternal(nsIP
   if (Preferences::GetBool("notification.prompt.testing", false)) {
     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
       return NotificationPermission::Granted;
     } else {
       return NotificationPermission::Denied;
     }
   }
 
+  return TestPermission(aPrincipal);
+}
+
+/* static */ NotificationPermission
+Notification::TestPermission(nsIPrincipal* aPrincipal)
+{
+  AssertIsOnMainThread();
+
   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
 
   nsCOMPtr<nsIPermissionManager> permissionManager =
     services::GetPermissionManager();
+  if (!permissionManager) {
+    return NotificationPermission::Default;
+  }
 
   permissionManager->TestExactPermissionFromPrincipal(aPrincipal,
                                                       "desktop-notification",
                                                       &permission);
 
   // Convert the result to one of the enum types.
   switch (permission) {
   case nsIPermissionManager::ALLOW_ACTION:
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -312,16 +312,18 @@ public:
   void ReleaseObject();
 
   static NotificationPermission GetPermission(nsIGlobalObject* aGlobal,
                                               ErrorResult& aRv);
 
   static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
                                                       ErrorResult& rv);
 
+  static NotificationPermission TestPermission(nsIPrincipal* aPrincipal);
+
   bool DispatchClickEvent();
   bool DispatchNotificationClickEvent();
 
   static nsresult RemovePermission(nsIPrincipal* aPrincipal);
   static nsresult OpenSettings(nsIPrincipal* aPrincipal);
 protected:
   Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
                const nsAString& aTitle, const nsAString& aBody,
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -156,18 +156,21 @@ Push.prototype = {
       types: typeArray,
       principal: this._principal,
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
       allow: function() {
         let histogram = Services.telemetry.getHistogramById("PUSH_API_PERMISSION_GRANTED");
         histogram.add();
         allowCallback();
       },
-      cancel: function() {
-        let histogram = Services.telemetry.getHistogramById("PUSH_API_PERMISSION_DENIED");
+      cancel: () => {
+        let histogramName =
+          this._testPermission() == Ci.nsIPermissionManager.DENY_ACTION ?
+          "PUSH_API_PERMISSION_DENIED" : "PUSH_API_PERMISSION_DISMISSED";
+        let histogram = Services.telemetry.getHistogramById(histogramName);
         histogram.add();
         cancelCallback();
       },
       window: this._window,
     };
 
     let histogram = Services.telemetry.getHistogramById("PUSH_API_PERMISSION_REQUESTED");
     histogram.add(1);
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -10088,16 +10088,22 @@
     "description": "Count of number of times the PermissionManager explicitly prompted user for Push Notifications permission"
   },
   "PUSH_API_PERMISSION_DENIED": {
     "alert_emails": ["push@mozilla.com"],
     "expires_in_version": "55",
     "kind": "count",
     "description": "User explicitly denied Push Notifications permission"
   },
+  "PUSH_API_PERMISSION_DISMISSED": {
+    "alert_emails": ["push@mozilla.com"],
+    "expires_in_version": "55",
+    "kind": "count",
+    "description": "User dismissed request for Push Notifications permission"
+  },
   "PUSH_API_PERMISSION_GRANTED": {
     "alert_emails": ["push@mozilla.com"],
     "expires_in_version": "55",
     "kind": "count",
     "description": "User explicitly granted Push Notifications permission"
   },
   "PUSH_API_SUBSCRIBE_ATTEMPT": {
     "alert_emails": ["push@mozilla.com"],
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -2163,16 +2163,17 @@
     "PREDICTOR_WAIT_TIME",
     "PROCESS_CRASH_SUBMIT_ATTEMPT",
     "PROCESS_CRASH_SUBMIT_SUCCESS",
     "PUSH_API_NOTIFICATION_RECEIVED",
     "PUSH_API_NOTIFICATION_RECEIVED_BUT_DID_NOT_NOTIFY",
     "PUSH_API_NOTIFY",
     "PUSH_API_NOTIFY_REGISTRATION_LOST",
     "PUSH_API_PERMISSION_DENIED",
+    "PUSH_API_PERMISSION_DISMISSED",
     "PUSH_API_PERMISSION_GRANTED",
     "PUSH_API_PERMISSION_REQUESTED",
     "PUSH_API_QUOTA_EXPIRATION_TIME",
     "PUSH_API_QUOTA_RESET_TO",
     "PUSH_API_SUBSCRIBE_ATTEMPT",
     "PUSH_API_SUBSCRIBE_FAILED",
     "PUSH_API_SUBSCRIBE_HTTP2_TIME",
     "PUSH_API_SUBSCRIBE_SUCCEEDED",