Bug 1259148 - Notify content when a Push permissions pop-up is dismissed by the user draft
authorPanos Astithas <past@mozilla.com>
Thu, 24 Mar 2016 19:25:23 +0200
changeset 350106 a38bb913cb3f128d0fa0e065987d274a93a3af63
parent 349384 e847cfcb315f511f4928b03fd47dcf57aad05e1e
child 518255 b6e7842f44cae4fc47b12f2d38b86584eed785fd
push id15253
push userkcambridge@mozilla.com
push dateTue, 12 Apr 2016 22:13:41 +0000
bugs1259148
milestone48.0a1
Bug 1259148 - Notify content when a Push permissions pop-up is dismissed by the user MozReview-Commit-ID: 7HG7oOd8RWe
browser/components/nsBrowserGlue.js
dom/base/nsContentPermissionHelper.cpp
dom/base/nsContentPermissionHelper.h
dom/interfaces/base/nsIContentPermissionPrompt.idl
dom/ipc/PContentPermissionRequest.ipdl
dom/notification/Notification.cpp
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2625,16 +2625,29 @@ ContentPermissionPrompt.prototype = {
           callback: function() {},
         },
       ];
     }
 
     var options = {
       learnMoreURL:
         Services.urlFormatter.formatURLPref("app.support.baseURL") + "push",
+      eventCallback(type) {
+        if (type != "dismissed") {
+          return;
+        }
+        try {
+          aRequest.QueryInterface(Ci.nsIContentPermissionRequestDismissible);
+          aRequest.dismiss();
+        } catch (error) {
+          // Only the web notifications permission request can be dismissed.
+          // `PushManager.subscribe` uses the same permission, but returns a
+          // promise that can only be resolved with the final decision.
+        }
+      },
     };
 
     this._showPrompt(aRequest, message, "desktop-notification", actions,
                      "web-notifications",
                      "web-notifications-notification-icon", options);
   },
 
   _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) {
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -602,17 +602,19 @@ nsContentPermissionRequestProxy::Init(co
 
 void
 nsContentPermissionRequestProxy::OnParentDestroyed()
 {
   mRequester = nullptr;
   mParent = nullptr;
 }
 
-NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
+NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy,
+                  nsIContentPermissionRequest,
+                  nsIContentPermissionRequestDismissible)
 
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes)
 {
   nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
   if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray(mPermissionRequests, types)) {
     types.forget(aTypes);
     return NS_OK;
@@ -737,16 +739,33 @@ nsContentPermissionRequestProxy::Allow(J
     return NS_ERROR_FAILURE;
   }
 
   Unused << mParent->SendNotifyResult(true, choices);
   mParent = nullptr;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsContentPermissionRequestProxy::Dismiss()
+{
+  if (mParent == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Don't send out the delete message when the managing protocol (PBrowser) is
+  // being destroyed and PContentPermissionRequest will soon be.
+  if (mParent->IsBeingDestroyed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  Unused << mParent->SendNotifyDismiss();
+  return NS_OK;
+}
+
 void
 nsContentPermissionRequestProxy::NotifyVisibility(const bool& aIsVisible)
 {
   MOZ_ASSERT(mRequester);
 
   mRequester->NotifyVisibilityResult(aIsVisible);
 }
 
@@ -832,16 +851,29 @@ RemotePermissionRequest::RecvNotifyResul
     DoAllow(val);
   } else {
     DoCancel();
   }
   return true;
 }
 
 bool
+RemotePermissionRequest::RecvNotifyDismiss()
+{
+  // Don't destroy the request, since the user hasn't made a final decision.
+  NS_ASSERTION(mRequest, "We need a request");
+  nsCOMPtr<nsIContentPermissionRequestDismissible> request =
+    do_QueryInterface(mRequest);
+  if (request) {
+    request->Dismiss();
+  }
+  return true;
+}
+
+bool
 RemotePermissionRequest::RecvGetVisibility()
 {
   nsCOMPtr<nsIDocShell> docshell = mWindow->GetDocShell();
   if (!docshell) {
     return false;
   }
 
   bool isActive = false;
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -114,21 +114,23 @@ private:
   RefPtr<VisibilityChangeListener> mListener;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 using mozilla::dom::ContentPermissionRequestParent;
 
-class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
+class nsContentPermissionRequestProxy : public nsIContentPermissionRequest,
+                                        public nsIContentPermissionRequestDismissible
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_NSICONTENTPERMISSIONREQUESTDISMISSIBLE
 
   nsContentPermissionRequestProxy();
 
   nsresult Init(const nsTArray<mozilla::dom::PermissionRequest>& requests,
                 ContentPermissionRequestParent* parent);
 
   void OnParentDestroyed();
 
@@ -174,16 +176,17 @@ public:
   NS_DECL_NSICONTENTPERMISSIONREQUESTCALLBACK
 
   RemotePermissionRequest(nsIContentPermissionRequest* aRequest,
                           nsPIDOMWindowInner* aWindow);
 
   // It will be called when prompt dismissed.
   virtual bool RecvNotifyResult(const bool &aAllow,
                                 InfallibleTArray<PermissionChoice>&& aChoices) override;
+  virtual bool RecvNotifyDismiss() override;
 
   virtual bool RecvGetVisibility() override;
 
   void IPDLAddRef()
   {
     mIPCOpen = true;
     AddRef();
   }
--- a/dom/interfaces/base/nsIContentPermissionPrompt.idl
+++ b/dom/interfaces/base/nsIContentPermissionPrompt.idl
@@ -97,16 +97,29 @@ interface nsIContentPermissionRequest : 
    * allow or cancel the request
    */
 
   void cancel();
   void allow([optional] in jsval choices); // {"type1": "choice1", "type2": "choiceA"}
 };
 
 /**
+ * Interface allows access to a content to request
+ * permission to perform a privileged operation such as
+ * push notification that can be dismissed.
+ */
+[scriptable, uuid(bc65c84f-caa6-4b8e-8a30-c83432300036)]
+interface nsIContentPermissionRequestDismissible : nsISupports {
+  /**
+   * dismiss the request
+   */
+  void dismiss();
+};
+
+/**
  * Interface provides a way for the application to handle
  * the UI prompts associated with geo position.
  */
 [scriptable, function, uuid(F72DE90D-E954-4E69-9A61-917303029301)]
 interface nsIContentPermissionPrompt : nsISupports {
   /**
    * Called when a request has been made to access
    * privileged content apis
--- a/dom/ipc/PContentPermissionRequest.ipdl
+++ b/dom/ipc/PContentPermissionRequest.ipdl
@@ -15,14 +15,15 @@ protocol PContentPermissionRequest
 parent:
   async prompt();
   async NotifyVisibility(bool visibility);
   async Destroy();
 
 child:
   async GetVisibility();
   async NotifyResult(bool allow, PermissionChoice[] choices);
+  async NotifyDismiss();
   async __delete__();
 };
 
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -225,21 +225,23 @@ public:
     rv = notificationStorage->Get(mOrigin, mTag, mCallback);
     //XXXnsm Is it guaranteed mCallback will be called in case of failure?
     Unused << NS_WARN_IF(NS_FAILED(rv));
     return rv;
   }
 };
 
 class NotificationPermissionRequest : public nsIContentPermissionRequest,
+                                      public nsIContentPermissionRequestDismissible,
                                       public nsIRunnable
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_NSICONTENTPERMISSIONREQUESTDISMISSIBLE
   NS_DECL_NSIRUNNABLE
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
                                            nsIContentPermissionRequest)
 
   NotificationPermissionRequest(nsIPrincipal* aPrincipal,
                                 nsPIDOMWindowInner* aWindow, Promise* aPromise,
                                 NotificationPermissionCallback* aCallback)
     : mPrincipal(aPrincipal), mWindow(aWindow),
@@ -541,16 +543,17 @@ protected:
   NotificationAction mAction;
 };
 
 uint32_t Notification::sCount = 0;
 
 NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequestDismissible)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
 
@@ -577,20 +580,16 @@ NotificationPermissionRequest::Run()
   if (Preferences::GetBool("notification.prompt.testing", false)) {
     if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
       mPermission = NotificationPermission::Granted;
     } else {
       mPermission = NotificationPermission::Denied;
     }
   }
 
-  if (mPermission != NotificationPermission::Default) {
-    return DispatchResolvePromise();
-  }
-
   return nsContentPermissionUtils::AskPermission(this, mWindow);
 }
 
 NS_IMETHODIMP
 NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
 {
   NS_ADDREF(*aRequestingPrincipal = mPrincipal);
   return NS_OK;
@@ -623,16 +622,23 @@ NotificationPermissionRequest::Allow(JS:
 {
   MOZ_ASSERT(aChoices.isUndefined());
 
   mPermission = NotificationPermission::Granted;
   return DispatchResolvePromise();
 }
 
 NS_IMETHODIMP
+NotificationPermissionRequest::Dismiss()
+{
+  mPermission = NotificationPermission::Default;
+  return DispatchResolvePromise();
+}
+
+NS_IMETHODIMP
 NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
 {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
   return NS_OK;
 }
@@ -651,17 +657,21 @@ NotificationPermissionRequest::ResolvePr
   nsresult rv = NS_OK;
   if (mCallback) {
     ErrorResult error;
     mCallback->Call(mPermission, error);
     rv = error.StealNSResult();
   }
   Telemetry::Accumulate(
     Telemetry::WEB_NOTIFICATION_REQUEST_PERMISSION_CALLBACK, !!mCallback);
-  mPromise->MaybeResolve(mPermission);
+
+  if (mPermission != NotificationPermission::Default) {
+    mPromise->MaybeResolve(mPermission);
+  }
+
   return rv;
 }
 
 NS_IMETHODIMP
 NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
 {
   nsTArray<nsString> emptyOptions;
   return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
@@ -2781,9 +2791,8 @@ Notification::Observe(nsISupports* aSubj
     }
   }
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
-