Bug 862395 - Part 4: Add preference for number of XUL alerts to show with requireInteraction set to true. r=baku
authorWilliam Chen <wchen@mozilla.com>
Tue, 11 Oct 2016 01:46:38 -0700
changeset 318173 9a67414d03126bf936cbfd64511b1a6d45080f71
parent 318172 f1919859fd56d21724c00f8f0374223f5f80b26f
child 318174 a24574db096ba4d45659e496baf0d09ac9b661a1
push id33211
push usercbook@mozilla.com
push dateMon, 17 Oct 2016 09:38:38 +0000
treeherderautoland@e4ef6fa03aa8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs862395
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 862395 - Part 4: Add preference for number of XUL alerts to show with requireInteraction set to true. r=baku
modules/libpref/init/all.js
toolkit/components/alerts/nsXULAlerts.cpp
toolkit/components/alerts/nsXULAlerts.h
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4685,16 +4685,17 @@ pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  32768);
 
 // Desktop Notification
 pref("notification.feature.enabled", false);
 
 // Web Notification
 pref("dom.webnotifications.enabled", true);
 pref("dom.webnotifications.serviceworker.enabled", true);
+pref("dom.webnotifications.requireinteraction.count", 3);
 #ifdef NIGHTLY_BUILD
 pref("dom.webnotifications.requireinteraction.enabled", true);
 #else
 pref("dom.webnotifications.requireinteraction.enabled", false);
 #endif
 
 // Alert animation effect, name is disableSlidingEffect for backwards-compat.
 pref("alerts.disableSlidingEffect", false);
--- a/toolkit/components/alerts/nsXULAlerts.cpp
+++ b/toolkit/components/alerts/nsXULAlerts.cpp
@@ -41,16 +41,20 @@ nsXULAlertObserver::Observe(nsISupports*
                             const char16_t* aData)
 {
   if (!strcmp("alertfinished", aTopic)) {
     mozIDOMWindowProxy* currentAlert = mXULAlerts->mNamedWindows.GetWeak(mAlertName);
     // The window in mNamedWindows might be a replacement, thus it should only
     // be removed if it is the same window that is associated with this listener.
     if (currentAlert == mAlertWindow) {
       mXULAlerts->mNamedWindows.Remove(mAlertName);
+
+      if (mIsPersistent) {
+        mXULAlerts->PersistentAlertFinished();
+      }
     }
   }
 
   nsresult rv = NS_OK;
   if (mObserver) {
     rv = mObserver->Observe(aSubject, aTopic, aData);
   }
   return rv;
@@ -69,16 +73,31 @@ nsXULAlerts::GetInstance()
     gXULAlerts = new nsXULAlerts();
     ClearOnShutdown(&gXULAlerts);
   }
 #endif // MOZ_WIDGET_ANDROID
   RefPtr<nsXULAlerts> instance = gXULAlerts.get();
   return instance.forget();
 }
 
+void
+nsXULAlerts::PersistentAlertFinished()
+{
+  MOZ_ASSERT(mPersistentAlertCount);
+  mPersistentAlertCount--;
+
+  // Show next pending persistent alert if any.
+  if (!mPendingPersistentAlerts.IsEmpty()) {
+    ShowAlertWithIconURI(mPendingPersistentAlerts[0].mAlert,
+                         mPendingPersistentAlerts[0].mListener,
+                         nullptr);
+    mPendingPersistentAlerts.RemoveElementAt(0);
+  }
+}
+
 NS_IMETHODIMP
 nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString& aAlertTitle,
                                    const nsAString& aAlertText, bool aAlertTextClickable,
                                    const nsAString& aAlertCookie, nsIObserver* aAlertListener,
                                    const nsAString& aAlertName, const nsAString& aBidi,
                                    const nsAString& aLang, const nsAString& aData,
                                    nsIPrincipal* aPrincipal, bool aInPrivateBrowsing,
                                    bool aRequireInteraction)
@@ -102,17 +121,59 @@ nsXULAlerts::ShowPersistentNotification(
 {
   return ShowAlert(aAlert, aAlertListener);
 }
 
 NS_IMETHODIMP
 nsXULAlerts::ShowAlert(nsIAlertNotification* aAlert,
                        nsIObserver* aAlertListener)
 {
-  return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
+  nsAutoString name;
+  nsresult rv = aAlert->GetName(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If there is a pending alert with the same name in the list of
+  // pending alerts, replace it.
+  if (!mPendingPersistentAlerts.IsEmpty()) {
+    for (uint32_t i = 0; i < mPendingPersistentAlerts.Length(); i++) {
+      nsAutoString pendingAlertName;
+      nsCOMPtr<nsIAlertNotification> pendingAlert = mPendingPersistentAlerts[i].mAlert;
+      rv = pendingAlert->GetName(pendingAlertName);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (pendingAlertName.Equals(name)) {
+        nsAutoString cookie;
+        rv = pendingAlert->GetCookie(cookie);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (mPendingPersistentAlerts[i].mListener) {
+          rv = mPendingPersistentAlerts[i].mListener->Observe(nullptr, "alertfinished", cookie.get());
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        mPendingPersistentAlerts[i].Init(aAlert, aAlertListener);
+        return NS_OK;
+      }
+    }
+  }
+
+  bool requireInteraction;
+  rv = aAlert->GetRequireInteraction(&requireInteraction);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (requireInteraction &&
+      !mNamedWindows.Contains(name) &&
+      static_cast<int32_t>(mPersistentAlertCount) >=
+        Preferences::GetInt("dom.webnotifications.requireinteraction.count", 0)) {
+    PendingAlert* pa = mPendingPersistentAlerts.AppendElement();
+    pa->Init(aAlert, aAlertListener);
+    return NS_OK;
+  } else {
+    return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
+  }
 }
 
 NS_IMETHODIMP
 nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert,
                                   nsIObserver* aAlertListener,
                                   nsIURI* aIconURI)
 {
   bool inPrivateBrowsing;
@@ -258,21 +319,25 @@ nsXULAlerts::ShowAlertWithIconURI(nsIAle
   nsCOMPtr<nsISupportsInterfacePointer> replacedWindow = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
   NS_ENSURE_TRUE(replacedWindow, NS_ERROR_FAILURE);
   mozIDOMWindowProxy* previousAlert = mNamedWindows.GetWeak(name);
   replacedWindow->SetData(previousAlert);
   replacedWindow->SetDataIID(&NS_GET_IID(mozIDOMWindowProxy));
   rv = argsArray->AppendElement(replacedWindow);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (requireInteraction) {
+    mPersistentAlertCount++;
+  }
+
   // Add an observer (that wraps aAlertListener) to remove the window from
   // mNamedWindows when it is closed.
   nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  RefPtr<nsXULAlertObserver> alertObserver = new nsXULAlertObserver(this, name, aAlertListener);
+  RefPtr<nsXULAlertObserver> alertObserver = new nsXULAlertObserver(this, name, aAlertListener, requireInteraction);
   nsCOMPtr<nsISupports> iSupports(do_QueryInterface(alertObserver));
   ifptr->SetData(iSupports);
   ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
   rv = argsArray->AppendElement(ifptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The source contains the host and port of the site that sent the
   // notification. It is empty for system alerts.
--- a/toolkit/components/alerts/nsXULAlerts.h
+++ b/toolkit/components/alerts/nsXULAlerts.h
@@ -2,22 +2,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXULAlerts_h__
 #define nsXULAlerts_h__
 
 #include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 
 #include "mozIDOMWindow.h"
 #include "nsIObserver.h"
 
+struct PendingAlert
+{
+  void Init(nsIAlertNotification* aAlert, nsIObserver* aListener)
+  {
+    mAlert = aAlert;
+    mListener = aListener;
+  }
+  nsCOMPtr<nsIAlertNotification> mAlert;
+  nsCOMPtr<nsIObserver> mListener;
+};
+
 class nsXULAlerts : public nsIAlertsService,
                     public nsIAlertsDoNotDisturb,
                     public nsIAlertsIconURI
 {
   friend class nsXULAlertObserver;
 public:
   NS_DECL_NSIALERTSICONURI
   NS_DECL_NSIALERTSDONOTDISTURB
@@ -27,42 +39,46 @@ public:
   nsXULAlerts()
   {
   }
 
   static already_AddRefed<nsXULAlerts> GetInstance();
 
 protected:
   virtual ~nsXULAlerts() {}
+  void PersistentAlertFinished();
 
   nsInterfaceHashtable<nsStringHashKey, mozIDOMWindowProxy> mNamedWindows;
+  uint32_t mPersistentAlertCount = 0;
+  nsTArray<PendingAlert> mPendingPersistentAlerts;
   bool mDoNotDisturb = false;
 };
 
 /**
  * This class wraps observers for alerts and watches
  * for the "alertfinished" event in order to release
  * the reference on the nsIDOMWindow of the XUL alert.
  */
 class nsXULAlertObserver : public nsIObserver {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTION_CLASS(nsXULAlertObserver)
 
   nsXULAlertObserver(nsXULAlerts* aXULAlerts, const nsAString& aAlertName,
-                     nsIObserver* aObserver)
+                     nsIObserver* aObserver, bool aIsPersistent)
     : mXULAlerts(aXULAlerts), mAlertName(aAlertName),
-      mObserver(aObserver) {}
+      mObserver(aObserver), mIsPersistent(aIsPersistent) {}
 
   void SetAlertWindow(mozIDOMWindowProxy* aWindow) { mAlertWindow = aWindow; }
 
 protected:
   virtual ~nsXULAlertObserver() {}
 
   RefPtr<nsXULAlerts> mXULAlerts;
   nsString mAlertName;
   nsCOMPtr<mozIDOMWindowProxy> mAlertWindow;
   nsCOMPtr<nsIObserver> mObserver;
+  bool mIsPersistent;
 };
 
 #endif /* nsXULAlerts_h__ */