Bug 782211 - Part 2: Updated the nsIAlertsService API and its implementation to include support for bidi overrides and a method to close notifications. r=dougt
authorWilliam Chen <wchen@mozilla.com>
Mon, 18 Mar 2013 06:24:53 -0700
changeset 125215 c8b3e4b6a8b51a1766d60b97868a6aac8b7c5017
parent 125214 e96b9c546f07c615d20543c8bd00198e654f005a
child 125216 a4508a65ccbe30c23d15b49f44f569470fe79a19
push id24449
push useremorley@mozilla.com
push dateMon, 18 Mar 2013 20:06:48 +0000
treeherdermozilla-central@e23e43a2c14e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt
bugs782211
milestone22.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 782211 - Part 2: Updated the nsIAlertsService API and its implementation to include support for bidi overrides and a method to close notifications. r=dougt
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/src/notification/nsDesktopNotification.cpp
toolkit/components/alerts/mac/nsMacAlertsService.mm
toolkit/components/alerts/nsAlertsService.cpp
toolkit/components/alerts/nsIAlertsService.idl
toolkit/components/alerts/test/test_alerts.html
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/system/gnome/nsSystemAlertsService.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1503,17 +1503,18 @@ ContentParent::Observe(nsISupports* aSub
     else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
       NS_ConvertUTF16toUTF8 dataStr(aData);
       const char *offline = dataStr.get();
       if (!SendSetOffline(!strcmp(offline, "true") ? true : false))
           return NS_ERROR_NOT_AVAILABLE;
     }
     // listening for alert notifications
     else if (!strcmp(aTopic, "alertfinished") ||
-             !strcmp(aTopic, "alertclickcallback") ) {
+             !strcmp(aTopic, "alertclickcallback") ||
+             !strcmp(aTopic, "alertshow") ) {
         if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
                                       nsDependentString(aData)))
             return NS_ERROR_NOT_AVAILABLE;
     }
     else if (!strcmp(aTopic, "child-memory-reporter-request")) {
         unused << SendPMemoryReportRequestConstructor();
     }
     else if (!strcmp(aTopic, "child-gc-request")){
@@ -2237,25 +2238,40 @@ ContentParent::AfterProcessNextEvent(nsI
     }
 
     return NS_OK;
 }
 
 bool
 ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                          const nsString& aText, const bool& aTextClickable,
-                                         const nsString& aCookie, const nsString& aName)
+                                         const nsString& aCookie, const nsString& aName,
+                                         const nsString& aBidi, const nsString& aLang)
 {
     if (!AssertAppProcessPermission(this, "desktop-notification")) {
         return false;
     }
     nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
     if (sysAlerts) {
         sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable,
-                                         aCookie, this, aName);
+                                         aCookie, this, aName, aBidi, aLang);
+    }
+
+    return true;
+}
+
+bool
+ContentParent::RecvCloseAlert(const nsString& aName)
+{
+    if (!AssertAppProcessPermission(this, "desktop-notification")) {
+        return false;
+    }
+    nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
+    if (sysAlerts) {
+        sysAlerts->CloseAlert(aName);
     }
 
     return true;
 }
 
 bool
 ContentParent::RecvSyncMessage(const nsString& aMsg,
                                const ClonedMessageData& aData,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -323,17 +323,20 @@ private:
                                     const InfallibleTArray<nsString>& filters,
                                     const InfallibleTArray<nsString>& filterNames,
                                     InfallibleTArray<nsString>* files,
                                     int16_t* retValue,
                                     nsresult* result);
 
     virtual bool RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                            const nsString& aText, const bool& aTextClickable,
-                                           const nsString& aCookie, const nsString& aName);
+                                           const nsString& aCookie, const nsString& aName,
+                                           const nsString& aBidi, const nsString& aLang);
+
+    virtual bool RecvCloseAlert(const nsString& aName);
 
     virtual bool RecvLoadURIExternal(const URIParams& uri);
 
     virtual bool RecvSyncMessage(const nsString& aMsg,
                                  const ClonedMessageData& aData,
                                  InfallibleTArray<nsString>* aRetvals);
     virtual bool RecvAsyncMessage(const nsString& aMsg,
                                   const ClonedMessageData& aData);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -417,22 +417,26 @@ parent:
     // PrefService message
     sync ReadPrefsArray() returns (PrefSetting[] prefs);
 
     sync ReadFontList() returns (FontListEntry[] retValue);
 
     sync SyncMessage(nsString aMessage, ClonedMessageData aData)
       returns (nsString[] retval);
 
-    ShowAlertNotification(nsString imageUrl, 
-                          nsString title, 
-                          nsString text, 
+    ShowAlertNotification(nsString imageUrl,
+                          nsString title,
+                          nsString text,
                           bool textClickable,
                           nsString cookie,
-                          nsString name);
+                          nsString name,
+                          nsString bidi,
+                          nsString lang);
+
+    CloseAlert(nsString name);
 
     PExternalHelperApp(OptionalURIParams uri, nsCString aMimeContentType,
                        nsCString aContentDisposition, bool aForceSave,
                        int64_t aContentLength, OptionalURIParams aReferrer);
 
     AddGeolocationListener(Principal principal, bool highAccuracy);
     RemoveGeolocationListener();
     SetGeolocationHigherAccuracy(bool enable);
--- a/dom/src/notification/nsDesktopNotification.cpp
+++ b/dom/src/notification/nsDesktopNotification.cpp
@@ -55,16 +55,18 @@ nsDOMDesktopNotification::PostDesktopNot
   nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
   if (!alerts)
     return NS_ERROR_NOT_IMPLEMENTED;
 
   return alerts->ShowAlertNotification(mIconURL, mTitle, mDescription,
                                        true,
                                        EmptyString(),
                                        mObserver,
+                                       EmptyString(),
+                                       NS_LITERAL_STRING("auto"),
                                        EmptyString());
 }
 
 DOMCI_DATA(DesktopNotification, nsDOMDesktopNotification)
 
 NS_INTERFACE_MAP_BEGIN(nsDOMDesktopNotification)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDesktopNotification)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DesktopNotification)
--- a/toolkit/components/alerts/mac/nsMacAlertsService.mm
+++ b/toolkit/components/alerts/mac/nsMacAlertsService.mm
@@ -271,17 +271,19 @@ nsMacAlertsService::~nsMacAlertsService(
 
 NS_IMETHODIMP
 nsMacAlertsService::ShowAlertNotification(const nsAString& aImageUrl,
                                             const nsAString& aAlertTitle,
                                             const nsAString& aAlertText,
                                             bool aAlertClickable,
                                             const nsAString& aAlertCookie,
                                             nsIObserver* aAlertListener,
-                                            const nsAString& aAlertName)
+                                            const nsAString& aAlertName,
+                                            const nsAString& aDir,
+                                            const nsAString& aLang)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   // Growl requires a few extra steps, like checking that it's registered and
   // the alert has a name.
   if (mGrowlDelegate) {
     NS_ASSERTION(mGrowlDelegate->mDelegate == [GrowlApplicationBridge growlDelegate],
                  "Growl Delegate was not registered properly.");
@@ -316,16 +318,22 @@ nsMacAlertsService::ShowAlertNotificatio
     // Notification Center is easy: no image, no name.
     return DispatchNCNotification(mNCDelegate, aAlertTitle, aAlertText,
                                   aAlertCookie, aAlertListener);
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+NS_IMETHODIMP
+nsMacAlertsService::CloseAlert(const nsAString& aName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 nsMacAlertsService::Observe(nsISupports* aSubject, const char* aTopic,
                               const PRUnichar* aData)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -65,47 +65,51 @@ bool nsAlertsService::ShouldShowAlert()
 
   return result;
 }
 
 NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, 
                                                      const nsAString & aAlertText, bool aAlertTextClickable,
                                                      const nsAString & aAlertCookie,
                                                      nsIObserver * aAlertListener,
-                                                     const nsAString & aAlertName)
+                                                     const nsAString & aAlertName,
+                                                     const nsAString & aBidi,
+                                                     const nsAString & aLang)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
 
     if (aAlertListener)
       cpc->AddRemoteAlertObserver(PromiseFlatString(aAlertCookie), aAlertListener);
 
     cpc->SendShowAlertNotification(PromiseFlatString(aImageUrl),
                                    PromiseFlatString(aAlertTitle),
                                    PromiseFlatString(aAlertText),
                                    aAlertTextClickable,
                                    PromiseFlatString(aAlertCookie),
-                                   PromiseFlatString(aAlertName));
+                                   PromiseFlatString(aAlertName),
+                                   PromiseFlatString(aBidi),
+                                   PromiseFlatString(aLang));
     return NS_OK;
   }
 
 #ifdef MOZ_WIDGET_ANDROID
   mozilla::AndroidBridge::Bridge()->CloseNotification(aAlertName);
   mozilla::AndroidBridge::Bridge()->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertCookie,
                                                           aAlertListener, aAlertName);
   return NS_OK;
 #else
   // Check if there is an optional service that handles system-level notifications
   nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID));
   nsresult rv;
   if (sysAlerts) {
     rv = sysAlerts->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
-                                          aAlertCookie, aAlertListener, aAlertName);
-    if (NS_SUCCEEDED(rv))
-      return rv;
+                                          aAlertCookie, aAlertListener, aAlertName,
+                                          aBidi, aLang);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!ShouldShowAlert()) {
     // Do not display the alert. Instead call alertfinished and get out.
     if (aAlertListener)
       aAlertListener->Observe(NULL, "alertfinished", PromiseFlatString(aAlertCookie).get());
     return NS_OK;
   }
@@ -181,16 +185,45 @@ NS_IMETHODIMP nsAlertsService::ShowAlert
 
   rv = wwatch->OpenWindow(0, ALERT_CHROME_URL, "_blank",
                  "chrome,dialog=yes,titlebar=no,popup=yes", argsArray,
                  getter_AddRefs(newWindow));
   return rv;
 #endif // !MOZ_WIDGET_ANDROID
 }
 
+NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName)
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    ContentChild* cpc = ContentChild::GetSingleton();
+    cpc->SendCloseAlert(nsAutoString(aAlertName));
+    return NS_OK;
+  }
+
+#ifdef MOZ_WIDGET_ANDROID
+  mozilla::AndroidBridge::Bridge()->CloseNotification(aAlertName);
+  return NS_OK;
+#else
+
+#ifdef XP_MACOSX
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+
+  // Try the system notification service.
+  nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID));
+  if (sysAlerts) {
+    nsresult rv = sysAlerts->CloseAlert(aAlertName);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif // !MOZ_WIDGET_ANDROID
+}
+
+
 NS_IMETHODIMP nsAlertsService::OnProgress(const nsAString & aAlertName,
                                           int64_t aProgress,
                                           int64_t aProgressMax,
                                           const nsAString & aAlertText)
 {
 #ifdef MOZ_WIDGET_ANDROID
   mozilla::AndroidBridge::Bridge()->AlertsProgressListener_OnProgress(aAlertName, aProgress, aProgressMax, aAlertText);
   return NS_OK;
--- a/toolkit/components/alerts/nsIAlertsService.idl
+++ b/toolkit/components/alerts/nsIAlertsService.idl
@@ -2,60 +2,76 @@
 /* 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/. */
 
 
 #include "nsISupports.idl"
 #include "nsIObserver.idl"
 
-[scriptable, uuid(e177399e-2e31-4019-aed3-cba63ce9fa99)]
+[scriptable, uuid(160e87e1-d57d-456b-b6ea-17826f6ea7a8)]
 interface nsIAlertsService : nsISupports
 {
    /**
     * Displays a sliding notification window.
-    *    
+    *
     * @param imageUrl       A URL identifying the image to put in the alert.
     * @param title          The title for the alert.
     * @param text           The contents of the alert.
     * @param textClickable  If true, causes the alert text to look like a link
     *                       and notifies the listener when user attempts to 
     *                       click the alert text.
     * @param cookie         A blind cookie the alert will pass back to the 
     *                       consumer during the alert listener callbacks.
     * @param alertListener  Used for callbacks. May be null if the caller 
     *                       doesn't care about callbacks.
     * @param name           The name of the notification. This is currently
     *                       only used on OS X with Growl and Android.
     *                       On OS X with Growl, users can disable notifications
     *                       with a given name. On Android the name is hashed
-    *                       and used as a notification ID.
-    *
+    *                       and used as a notification ID. Notifications will
+    *                       replace previous notifications with the same name.
+    * @param dir            Bidi override for the title. Valid values are
+    *                       "auto", "ltr" or "rtl". Only available on supported
+    *                       platforms.
+    * @param lang           Language of title and text of the alert. Only available
+    *                       on supported platforms.
     * @throws NS_ERROR_NOT_AVAILABLE If the notification cannot be displayed.
     *
     * The following arguments will be passed to the alertListener's observe() 
     * method:
     *   subject - null
     *   topic   - "alertfinished" when the alert goes away
     *             "alertclickcallback" when the text is clicked
+    *             "alertshow" when the alert is shown
     *   data    - the value of the cookie parameter passed to showAlertNotification.
     *
     * @note Depending on current circumstances (if the user's in a fullscreen
     *       application, for instance), the alert might not be displayed at all.
     *       In that case, if an alert listener is passed in it will receive the
     *       "alertfinished" notification immediately.
     */
-   void showAlertNotification(in AString  imageUrl, 
-                              in AString  title, 
-                              in AString  text, 
+   void showAlertNotification(in AString  imageUrl,
+                              in AString  title,
+                              in AString  text,
                               [optional] in boolean textClickable,
                               [optional] in AString cookie,
                               [optional] in nsIObserver alertListener,
-                              [optional] in AString name);
+                              [optional] in AString name,
+                              [optional] in AString dir,
+                              [optional] in AString lang);
 
+   /**
+    * Close alerts created by the service.
+    *
+    * @param name           The name of the notification to close. If no name
+    *                       is provided then only a notification created with
+    *                       no name (if any) will be closed.
+    */
+   void closeAlert([optional] in AString name);
 };
 
 [scriptable, uuid(df1bd4b0-3a8c-40e6-806a-203f38b0bd9f)]
 interface nsIAlertsProgressListener : nsISupports
 {
     /**
      * Called to notify the alert service that progress has occurred for the
      * given notification previously displayed with showAlertNotification().
@@ -77,8 +93,9 @@ interface nsIAlertsProgressListener : ns
     /**
      * Called to cancel and hide the given notification previously displayed
      * with showAlertNotification().
      *
      * @param name         The name of the notification.
      */
     void onCancel(in AString name);
 };
+
--- a/toolkit/components/alerts/test/test_alerts.html
+++ b/toolkit/components/alerts/test/test_alerts.html
@@ -16,24 +16,28 @@
 <br>Did a notification appear anywhere?
 <br>If so, the test will finish once the notification disappears.
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
 var observer = {
+  alertShow: false,
   observe: function (aSubject, aTopic, aData) {
     if (aTopic == "alertclickcallback") { 
       todo(false, "Did someone click the notification while running mochitests? (Please don't.)");
+    } else if (aTopic == "alertshow") {
+      ok(!this.alertShow, "Alert should not be shown more than once");
+      this.alertShow = true;
     } else {
       is(aTopic, "alertfinished", "Checking the topic for a finished notification");
+      SimpleTest.finish();
     }
     is(aData, "foobarcookie", "Checking whether the alert cookie was passed correctly");
-    SimpleTest.finish();
   }
 };
 
 function runTest() {
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
   if (!("@mozilla.org/alerts-service;1" in Cc)) {
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -2623,17 +2623,17 @@ nsDownload::SetState(DownloadState aStat
               // If downloads are automatically removed per the user's
               // retention policy, there's no reason to make the text clickable
               // because if it is, they'll click open the download manager and
               // the items they downloaded will have been removed.
               alerts->ShowAlertNotification(
                   NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
                   message, !removeWhenDone,
                   mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"),
-                  mDownloadManager, EmptyString());
+                  mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"), EmptyString());
             }
         }
       }
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK2)
       nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
       nsCOMPtr<nsIFile> file;
       nsAutoString path;
--- a/toolkit/system/gnome/nsSystemAlertsService.cpp
+++ b/toolkit/system/gnome/nsSystemAlertsService.cpp
@@ -29,17 +29,26 @@ nsSystemAlertsService::Init()
 
 NS_IMETHODIMP
 nsSystemAlertsService::ShowAlertNotification(const nsAString & aImageUrl,
                                              const nsAString & aAlertTitle, 
                                              const nsAString & aAlertText,
                                              bool aAlertTextClickable,
                                              const nsAString & aAlertCookie,
                                              nsIObserver * aAlertListener,
-                                             const nsAString & aAlertName)
+                                             const nsAString & aAlertName,
+                                             const nsAString & aBidi,
+                                             const nsAString & aLang)
 {
   nsRefPtr<nsAlertsIconListener> alertListener = new nsAlertsIconListener();
   if (!alertListener)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return alertListener->InitAlertAsync(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
                                        aAlertCookie, aAlertListener);
 }
+
+NS_IMETHODIMP
+nsSystemAlertsService::CloseAlert(const nsAString & aAlertName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+