Bug 782211 - Part 8: Updated existing XUL alerts to implement the Notification API. r=enn
authorWilliam Chen <wchen@mozilla.com>
Mon, 18 Mar 2013 06:24:54 -0700
changeset 125221 d068e438438dc7320cb2f5a685140a2ae7d0763a
parent 125220 9a471f858d6b4b2bf730f637e02b0841ea7dabde
child 125222 c9732200552d37d7304845e4f3459af9604d6640
push idunknown
push userunknown
push dateunknown
reviewersenn
bugs782211
milestone22.0a1
Bug 782211 - Part 8: Updated existing XUL alerts to implement the Notification API. r=enn
toolkit/components/alerts/Makefile.in
toolkit/components/alerts/nsAlertsService.cpp
toolkit/components/alerts/nsAlertsService.h
toolkit/components/alerts/nsXULAlerts.cpp
toolkit/components/alerts/nsXULAlerts.h
toolkit/components/alerts/resources/content/alert.js
toolkit/components/alerts/resources/content/alert.xul
toolkit/locales/en-US/chrome/alerts/alert.dtd
toolkit/locales/jar.mn
toolkit/themes/linux/global/alerts/alert.css
toolkit/themes/linux/global/alerts/notification-48.png
toolkit/themes/linux/global/jar.mn
toolkit/themes/osx/global/alerts/alert.css
toolkit/themes/osx/global/alerts/notification-48.png
toolkit/themes/osx/global/jar.mn
toolkit/themes/windows/global/alerts/alert.css
toolkit/themes/windows/global/alerts/notification-48.png
toolkit/themes/windows/global/jar.mn
widget/cocoa/nsLookAndFeel.mm
widget/gtk2/nsLookAndFeel.cpp
--- a/toolkit/components/alerts/Makefile.in
+++ b/toolkit/components/alerts/Makefile.in
@@ -10,15 +10,16 @@ VPATH = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = alerts
 LIBRARY_NAME = alerts_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 CPPSRCS = \
   nsAlertsService.cpp \
+  nsXULAlerts.cpp \
   $(NULL)
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/build/
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -7,27 +7,21 @@
 #include "nsXULAppAPI.h"
 
 #include "nsAlertsService.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #else
 
-#include "nsISupportsArray.h"
 #include "nsXPCOM.h"
-#include "nsISupportsPrimitives.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMWindow.h"
-#include "nsIWindowWatcher.h"
 #include "nsPromiseFlatString.h"
 #include "nsToolkitCompsCID.h"
-#include "mozilla/LookAndFeel.h"
-
-#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
 
 #endif // !MOZ_WIDGET_ANDROID
 
 using namespace mozilla;
 
 using mozilla::dom::ContentChild;
 
 NS_IMPL_THREADSAFE_ISUPPORTS2(nsAlertsService, nsIAlertsService, nsIAlertsProgressListener)
@@ -113,84 +107,20 @@ NS_IMETHODIMP nsAlertsService::ShowAlert
       aAlertListener->Observe(NULL, "alertfinished", PromiseFlatString(aAlertCookie).get());
     return NS_OK;
   }
 
 #ifdef XP_MACOSX
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 
-  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-  nsCOMPtr<nsIDOMWindow> newWindow;
-
-  nsCOMPtr<nsISupportsArray> argsArray;
-  rv = NS_NewISupportsArray(getter_AddRefs(argsArray));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // create scriptable versions of our strings that we can store in our nsISupportsArray....
-  nsCOMPtr<nsISupportsString> scriptableImageUrl (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableImageUrl, NS_ERROR_FAILURE);
-
-  scriptableImageUrl->SetData(aImageUrl);
-  rv = argsArray->AppendElement(scriptableImageUrl);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsString> scriptableAlertTitle (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableAlertTitle, NS_ERROR_FAILURE);
-
-  scriptableAlertTitle->SetData(aAlertTitle);
-  rv = argsArray->AppendElement(scriptableAlertTitle);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsString> scriptableAlertText (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableAlertText, NS_ERROR_FAILURE);
-
-  scriptableAlertText->SetData(aAlertText);
-  rv = argsArray->AppendElement(scriptableAlertText);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsPRBool> scriptableIsClickable (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableIsClickable, NS_ERROR_FAILURE);
-
-  scriptableIsClickable->SetData(aAlertTextClickable);
-  rv = argsArray->AppendElement(scriptableIsClickable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsString> scriptableAlertCookie (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableAlertCookie, NS_ERROR_FAILURE);
-
-  scriptableAlertCookie->SetData(aAlertCookie);
-  rv = argsArray->AppendElement(scriptableAlertCookie);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISupportsPRInt32> scriptableOrigin (do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID));
-  NS_ENSURE_TRUE(scriptableOrigin, NS_ERROR_FAILURE);
-
-  int32_t origin =
-    LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin);
-  scriptableOrigin->SetData(origin);
-
-  rv = argsArray->AppendElement(scriptableOrigin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (aAlertListener)
-  {
-    nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsISupports> iSupports (do_QueryInterface(aAlertListener));
-    ifptr->SetData(iSupports);
-    ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
-    rv = argsArray->AppendElement(ifptr);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  rv = wwatch->OpenWindow(0, ALERT_CHROME_URL, "_blank",
-                 "chrome,dialog=yes,titlebar=no,popup=yes", argsArray,
-                 getter_AddRefs(newWindow));
+  // Use XUL notifications as a fallback if above methods have failed.
+  rv = mXULAlerts.ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
+                                        aAlertCookie, aAlertListener, aAlertName,
+                                        aBidi, aLang);
   return rv;
 #endif // !MOZ_WIDGET_ANDROID
 }
 
 NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
@@ -209,17 +139,17 @@ NS_IMETHODIMP nsAlertsService::CloseAler
 
   // 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;
+  return mXULAlerts.CloseAlert(aAlertName);
 #endif // !MOZ_WIDGET_ANDROID
 }
 
 
 NS_IMETHODIMP nsAlertsService::OnProgress(const nsAString & aAlertName,
                                           int64_t aProgress,
                                           int64_t aProgressMax,
                                           const nsAString & aAlertText)
--- a/toolkit/components/alerts/nsAlertsService.h
+++ b/toolkit/components/alerts/nsAlertsService.h
@@ -3,16 +3,17 @@
  * 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 nsAlertsService_h__
 #define nsAlertsService_h__
 
 #include "nsIAlertsService.h"
 #include "nsCOMPtr.h"
+#include "nsXULAlerts.h"
 
 #ifdef XP_WIN
 typedef enum tagMOZ_QUERY_USER_NOTIFICATION_STATE {
     QUNS_NOT_PRESENT = 1,
     QUNS_BUSY = 2,
     QUNS_RUNNING_D3D_FULL_SCREEN = 3,
     QUNS_PRESENTATION_MODE = 4,
     QUNS_ACCEPTS_NOTIFICATIONS = 5,
@@ -34,11 +35,12 @@ public:
   NS_DECL_NSIALERTSSERVICE
   NS_DECL_ISUPPORTS
 
   nsAlertsService();
   virtual ~nsAlertsService();
 
 protected:
   bool ShouldShowAlert();
+  nsXULAlerts mXULAlerts;
 };
 
 #endif /* nsAlertsService_h__ */
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/nsXULAlerts.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* 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 "nsXULAlerts.h"
+
+#include "nsAutoPtr.h"
+#include "mozilla/LookAndFeel.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsArray.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIWindowWatcher.h"
+
+#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
+
+NS_IMPL_ISUPPORTS1(nsXULAlertObserver, nsIObserver)
+
+NS_IMETHODIMP
+nsXULAlertObserver::Observe(nsISupports* aSubject, const char* aTopic,
+                            const PRUnichar* aData)
+{
+  if (!strcmp("alertfinished", aTopic)) {
+    nsIDOMWindow* 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);
+    }
+  }
+
+  nsresult rv = NS_OK;
+  if (mObserver) {
+    rv = mObserver->Observe(aSubject, aTopic, aData);
+  }
+  return rv;
+}
+
+nsresult
+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)
+{
+  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+  nsCOMPtr<nsIDOMWindow> newWindow;
+
+  nsCOMPtr<nsISupportsArray> argsArray;
+  nsresult rv = NS_NewISupportsArray(getter_AddRefs(argsArray));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // create scriptable versions of our strings that we can store in our nsISupportsArray....
+  nsCOMPtr<nsISupportsString> scriptableImageUrl (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableImageUrl, NS_ERROR_FAILURE);
+
+  scriptableImageUrl->SetData(aImageUrl);
+  rv = argsArray->AppendElement(scriptableImageUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsString> scriptableAlertTitle (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableAlertTitle, NS_ERROR_FAILURE);
+
+  scriptableAlertTitle->SetData(aAlertTitle);
+  rv = argsArray->AppendElement(scriptableAlertTitle);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsString> scriptableAlertText (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableAlertText, NS_ERROR_FAILURE);
+
+  scriptableAlertText->SetData(aAlertText);
+  rv = argsArray->AppendElement(scriptableAlertText);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsPRBool> scriptableIsClickable (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableIsClickable, NS_ERROR_FAILURE);
+
+  scriptableIsClickable->SetData(aAlertTextClickable);
+  rv = argsArray->AppendElement(scriptableIsClickable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsString> scriptableAlertCookie (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableAlertCookie, NS_ERROR_FAILURE);
+
+  scriptableAlertCookie->SetData(aAlertCookie);
+  rv = argsArray->AppendElement(scriptableAlertCookie);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsPRInt32> scriptableOrigin (do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableOrigin, NS_ERROR_FAILURE);
+
+  int32_t origin =
+    LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin);
+  scriptableOrigin->SetData(origin);
+
+  rv = argsArray->AppendElement(scriptableOrigin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsString> scriptableBidi (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableBidi, NS_ERROR_FAILURE);
+
+  scriptableBidi->SetData(aBidi);
+  rv = argsArray->AppendElement(scriptableBidi);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupportsString> scriptableLang (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableLang, NS_ERROR_FAILURE);
+
+  scriptableLang->SetData(aLang);
+  rv = argsArray->AppendElement(scriptableLang);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Alerts with the same name should replace the old alert in the same position.
+  // Provide the new alert window with a pointer to the replaced window so that
+  // it may take the same position.
+  nsCOMPtr<nsISupportsInterfacePointer> replacedWindow = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+  NS_ENSURE_TRUE(replacedWindow, NS_ERROR_FAILURE);
+  nsIDOMWindow* previousAlert = mNamedWindows.GetWeak(aAlertName);
+  replacedWindow->SetData(previousAlert);
+  replacedWindow->SetDataIID(&NS_GET_IID(nsIDOMWindow));
+  rv = argsArray->AppendElement(replacedWindow);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsXULAlertObserver> alertObserver;
+  if (aAlertListener)
+  {
+    nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    alertObserver = new nsXULAlertObserver(this, aAlertName, aAlertListener);
+    nsCOMPtr<nsISupports> iSupports(do_QueryInterface(alertObserver));
+    ifptr->SetData(iSupports);
+    ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
+    rv = argsArray->AppendElement(ifptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = wwatch->OpenWindow(0, ALERT_CHROME_URL, "_blank",
+                          "chrome,dialog=yes,titlebar=no,popup=yes", argsArray,
+                          getter_AddRefs(newWindow));
+
+  mNamedWindows.Put(aAlertName, newWindow);
+  if (alertObserver) {
+    alertObserver->SetAlertWindow(newWindow);
+  }
+
+  return rv;
+}
+
+nsresult
+nsXULAlerts::CloseAlert(const nsAString& aAlertName)
+{
+  nsCOMPtr<nsIDOMWindow> domWindow = mNamedWindows.Get(aAlertName);
+  if (domWindow) {
+    return domWindow->Close();
+  }
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/nsXULAlerts.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* 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 "nsHashKeys.h"
+#include "nsInterfaceHashtable.h"
+
+#include "nsIDOMWindow.h"
+#include "nsIObserver.h"
+
+using namespace mozilla;
+
+class nsXULAlerts {
+  friend class nsXULAlertObserver;
+public:
+  nsXULAlerts()
+  {
+    mNamedWindows.Init();
+  }
+
+  virtual ~nsXULAlerts() {}
+
+  nsresult 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);
+
+  nsresult CloseAlert(const nsAString& aAlertName);
+protected:
+  nsInterfaceHashtable<nsStringHashKey, nsIDOMWindow> mNamedWindows;
+};
+
+/**
+ * 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_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  nsXULAlertObserver(nsXULAlerts* aXULAlerts, const nsAString& aAlertName,
+                     nsIObserver* aObserver)
+    : mXULAlerts(aXULAlerts), mAlertName(aAlertName),
+      mObserver(aObserver) {}
+
+  void SetAlertWindow(nsIDOMWindow* aWindow) { mAlertWindow = aWindow; }
+
+  virtual ~nsXULAlertObserver() {}
+protected:
+  nsXULAlerts* mXULAlerts;
+  nsString mAlertName;
+  nsCOMPtr<nsIDOMWindow> mAlertWindow;
+  nsCOMPtr<nsIObserver> mObserver;
+};
+
+#endif /* nsXULAlerts_h__ */
+
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -1,100 +1,192 @@
 // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 
 /* 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/. */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Ci.nsIWindowMediator);
+
 // Copied from nsILookAndFeel.h, see comments on eMetric_AlertNotificationOrigin
 const NS_ALERT_HORIZONTAL = 1;
 const NS_ALERT_LEFT = 2;
 const NS_ALERT_TOP = 4;
 
+const WINDOW_MARGIN = 10;
+
 var gOrigin = 0; // Default value: alert from bottom right.
- 
+var gReplacedWindow = null;
 var gAlertListener = null;
 var gAlertTextClickable = false;
 var gAlertCookie = "";
+var gIsReplaced = false;
 
 function prefillAlertInfo() {
   // unwrap all the args....
   // arguments[0] --> the image src url
   // arguments[1] --> the alert title
   // arguments[2] --> the alert text
   // arguments[3] --> is the text clickable?
   // arguments[4] --> the alert cookie to be passed back to the listener
   // arguments[5] --> the alert origin reported by the look and feel
-  // arguments[6] --> an optional callback listener (nsIObserver)
+  // arguments[6] --> bidi
+  // arguments[7] --> lang
+  // arguments[8] --> replaced alert window (nsIDOMWindow)
+  // arguments[9] --> an optional callback listener (nsIObserver)
 
   switch (window.arguments.length) {
     default:
-    case 7:
-      gAlertListener = window.arguments[6];
+    case 10:
+      gAlertListener = window.arguments[9];
+    case 9:
+      gReplacedWindow = window.arguments[8];
     case 6:
       gOrigin = window.arguments[5];
     case 5:
       gAlertCookie = window.arguments[4];
     case 4:
       gAlertTextClickable = window.arguments[3];
       if (gAlertTextClickable) {
         document.getElementById('alertNotification').setAttribute('clickable', true);
         document.getElementById('alertTextLabel').setAttribute('clickable', true);
       }
     case 3:
       document.getElementById('alertTextLabel').textContent = window.arguments[2];
     case 2:
       document.getElementById('alertTitleLabel').setAttribute('value', window.arguments[1]);
     case 1:
-      document.getElementById('alertImage').setAttribute('src', window.arguments[0]);
+      if (window.arguments[0]) {
+        document.getElementById('alertImage').setAttribute('src', window.arguments[0]);
+      }
     case 0:
       break;
   }
 }
 
 function onAlertLoad() {
   const ALERT_DURATION_IMMEDIATE = 4000;
   let alertTextBox = document.getElementById("alertTextBox");
   let alertImageBox = document.getElementById("alertImageBox");
   alertImageBox.style.minHeight = alertTextBox.scrollHeight + "px";
 
   sizeToContent();
 
-  // Determine position
-  let x = gOrigin & NS_ALERT_LEFT ? screen.availLeft :
-          screen.availLeft + screen.availWidth - window.outerWidth;
-  let y = gOrigin & NS_ALERT_TOP ? screen.availTop :
-          screen.availTop + screen.availHeight - window.outerHeight;
-
-  // Offset the alert by 10 pixels from the edge of the screen
-  y += gOrigin & NS_ALERT_TOP ? 10 : -10;
-  x += gOrigin & NS_ALERT_LEFT ? 10 : -10;
-
-  window.moveTo(x, y);
+  if (gReplacedWindow) {
+    moveWindowToReplace(gReplacedWindow);
+    gReplacedWindow.gIsReplaced = true;
+    gReplacedWindow.close();
+  } else {
+    moveWindowToEnd();
+  }
 
   if (Services.prefs.getBoolPref("alerts.disableSlidingEffect")) {
-    setTimeout(closeAlert, ALERT_DURATION_IMMEDIATE);
+    setTimeout(function() { window.close(); }, ALERT_DURATION_IMMEDIATE);
     return;
   }
 
   let alertBox = document.getElementById("alertBox");
   alertBox.addEventListener("animationend", function hideAlert(event) {
     if (event.animationName == "alert-animation") {
       alertBox.removeEventListener("animationend", hideAlert, false);
-      closeAlert();
+      window.close();
     }
   }, false);
   alertBox.setAttribute("animate", true);
+
+  if (gAlertListener) {
+    gAlertListener.observe(null, "alertshow", gAlertCookie);
+  }
+}
+
+function moveWindowToReplace(aReplacedAlert) {
+  let heightDelta = window.outerHeight - aReplacedAlert.outerHeight;
+
+  // Move windows that come after the replaced alert if the height is different.
+  if (heightDelta != 0) {
+    let windows = windowMediator.getEnumerator('alert:alert');
+    while (windows.hasMoreElements()) {
+      let alertWindow = windows.getNext();
+      // boolean to determine if the alert window is after the replaced alert.
+      let alertIsAfter = gOrigin & NS_ALERT_TOP ?
+                         alertWindow.screenY > aReplacedAlert.screenY :
+                         aReplacedAlert.screenY > alertWindow.screenY;
+      if (alertIsAfter) {
+        // The new Y position of the window.
+        let adjustedY = gOrigin & NS_ALERT_TOP ?
+                        alertWindow.screenY + heightDelta :
+                        alertWindow.screenY - heightDelta;
+        alertWindow.moveTo(alertWindow.screenX, adjustedY);
+      }
+    }
+  }
+
+  let adjustedY = gOrigin & NS_ALERT_TOP ? aReplacedAlert.screenY :
+                  aReplacedAlert.screenY - heightDelta;
+  window.moveTo(aReplacedAlert.screenX, adjustedY);
 }
 
-function closeAlert() {
-  if (gAlertListener)
+function moveWindowToEnd() {
+  // Determine position
+  let x = gOrigin & NS_ALERT_LEFT ? screen.availLeft :
+          screen.availLeft + screen.availWidth - window.outerWidth;
+  let y = gOrigin & NS_ALERT_TOP ? screen.availTop :
+          screen.availTop + screen.availHeight - window.outerHeight;
+
+  // Position the window at the end of all alerts.
+  let windows = windowMediator.getEnumerator('alert:alert');
+  while (windows.hasMoreElements()) {
+    let alertWindow = windows.getNext();
+    if (alertWindow != window) {
+      if (gOrigin & NS_ALERT_TOP) {
+        y = Math.max(y, alertWindow.screenY + alertWindow.outerHeight);
+      } else {
+        y = Math.min(y, alertWindow.screenY - window.outerHeight);
+      }
+    }
+  }
+
+  // Offset the alert by WINDOW_MARGIN pixels from the edge of the screen
+  y += gOrigin & NS_ALERT_TOP ? WINDOW_MARGIN : -WINDOW_MARGIN;
+  x += gOrigin & NS_ALERT_LEFT ? WINDOW_MARGIN : -WINDOW_MARGIN;
+
+  window.moveTo(x, y);
+}
+
+function onAlertBeforeUnload() {
+  if (!gIsReplaced) {
+    // Move other alert windows to fill the gap left by closing alert.
+    let heightDelta = window.outerHeight + WINDOW_MARGIN;
+    let windows = windowMediator.getEnumerator('alert:alert');
+    while (windows.hasMoreElements()) {
+      let alertWindow = windows.getNext();
+      if (alertWindow != window) {
+        if (gOrigin & NS_ALERT_TOP) {
+          if (alertWindow.screenY > window.screenY) {
+            alertWindow.moveTo(alertWindow.screenX, alertWindow.screenY - heightDelta);
+          }
+        } else {
+          if (window.screenY > alertWindow.screenY) {
+            alertWindow.moveTo(alertWindow.screenX, alertWindow.screenY + heightDelta);
+          }
+        }
+      }
+    }
+  }
+
+  if (gAlertListener) {
     gAlertListener.observe(null, "alertfinished", gAlertCookie);
-  window.close();
+  }
 }
 
 function onAlertClick() {
-  if (gAlertListener && gAlertTextClickable)
+  if (gAlertListener && gAlertTextClickable) {
     gAlertListener.observe(null, "alertclickcallback", gAlertCookie);
-  closeAlert();
+  }
+
+  window.close();
 }
--- a/toolkit/components/alerts/resources/content/alert.xul
+++ b/toolkit/components/alerts/resources/content/alert.xul
@@ -1,37 +1,50 @@
 <?xml version="1.0"?>
 <!-- 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/. -->
 
+<!DOCTYPE window [
+<!ENTITY % alertDTD SYSTEM "chrome://alerts/locale/alert.dtd">
+%alertDTD;
+]>
+
 <?xml-stylesheet href="chrome://global/content/alerts/alert.css" type="text/css"?>
 <?xml-stylesheet href="chrome://global/skin/alerts/alert.css" type="text/css"?>
 
 <window id="alertNotification"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         windowtype="alert:alert"
         xmlns:xhtml="http://www.w3.org/1999/xhtml"
         xhtml:role="alert"
         pack="start"
-        onload="onAlertLoad()"
-        onclick="onAlertClick();">
+        onload="onAlertLoad();"
+        onclick="onAlertClick();"
+        onbeforeunload="onAlertBeforeUnload();">
 
   <script type="application/javascript" src="chrome://global/content/alerts/alert.js"/>
 
   <box id="alertBox" class="alertBox">
-    
-    <hbox id="alertImageBox" class="alertImageBox" align="center" pack="center">
-      <image id="alertImage"/>
-    </hbox>
+    <box>
+      <hbox id="alertImageBox" class="alertImageBox" align="center" pack="center">
+        <image id="alertImage"/>
+      </hbox>
 
-    <vbox id="alertTextBox" class="alertTextBox">
-      <label id="alertTitleLabel" class="alertTitle plain"/>
-      <label id="alertTextLabel" class="alertText plain"/>
+      <vbox id="alertTextBox" class="alertTextBox">
+        <label id="alertTitleLabel" class="alertTitle plain"/>
+        <label id="alertTextLabel" class="alertText plain"/>
+      </vbox>
+    </box>
+
+    <vbox class="alertCloseBox">
+      <toolbarbutton class="alertCloseButton"
+                     tooltiptext="&closeAlert.tooltip;"
+                     onclick="event.stopPropagation();"
+                     oncommand="close();"/>
     </vbox>
-
   </box>
 
   <!-- This method is called inline because we want to make sure we establish the width
        and height of the alert before we fire the onload handler. -->
   <script type="application/javascript">prefillAlertInfo();</script>
 </window>
 
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/alerts/alert.dtd
@@ -0,0 +1,5 @@
+<!-- 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/. -->
+
+<!ENTITY     closeAlert.tooltip          "Close this notification">
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -108,16 +108,17 @@
   locale/@AB_CD@/mozapps/profile/profileSelection.properties      (%chrome/mozapps/profile/profileSelection.properties)
   locale/@AB_CD@/mozapps/profile/profileSelection.dtd             (%chrome/mozapps/profile/profileSelection.dtd)
   locale/@AB_CD@/mozapps/update/updates.dtd                       (%chrome/mozapps/update/updates.dtd)
   locale/@AB_CD@/mozapps/update/updates.properties                (%chrome/mozapps/update/updates.properties)
   locale/@AB_CD@/mozapps/update/history.dtd                       (%chrome/mozapps/update/history.dtd)
   locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.dtd           (%chrome/mozapps/extensions/xpinstallConfirm.dtd)
   locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.properties    (%chrome/mozapps/extensions/xpinstallConfirm.properties)
 % locale alerts @AB_CD@ %locale/@AB_CD@/alerts/
+  locale/@AB_CD@/alerts/alert.dtd                                (%chrome/alerts/alert.dtd)
   locale/@AB_CD@/alerts/notificationNames.properties             (%chrome/alerts/notificationNames.properties)
 % locale cookie @AB_CD@ %locale/@AB_CD@/cookie/
   locale/@AB_CD@/cookie/cookieAcceptDialog.dtd           (%chrome/cookie/cookieAcceptDialog.dtd)
   locale/@AB_CD@/cookie/cookieAcceptDialog.properties    (%chrome/cookie/cookieAcceptDialog.properties)
 % locale passwordmgr @AB_CD@ %locale/@AB_CD@/passwordmgr/
   locale/@AB_CD@/passwordmgr/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
   locale/@AB_CD@/passwordmgr/passwordManager.dtd    (%chrome/passwordmgr/passwordManager.dtd)
 % locale autoconfig @AB_CD@ %locale/@AB_CD@/autoconfig/
--- a/toolkit/themes/linux/global/alerts/alert.css
+++ b/toolkit/themes/linux/global/alerts/alert.css
@@ -21,27 +21,32 @@
   background-image: linear-gradient(rgba(255,255,255,0.7), rgba(255,255,255,0.6));
   -moz-border-end: 1px solid rgba(0,0,0,.1);
 }
 
 .alertTextBox {
   padding: 8px;
   -moz-padding-start: 16px;
   width: 255px;
+}
+
+.alertTextBox,
+.alertCloseBox {
   background-image: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0.1));
 }
 
 .alertTitle {
   font-weight: bold;
   font-size: 110%;
 }
 
 #alertImage {
   max-width: 48px;
   max-height: 48px;
+  list-style-image: url(chrome://global/skin/alerts/notification-48.png);
 }
 
 #alertNotification[clickable="true"] {
   cursor: pointer;
 }
 
 label {
   cursor: inherit;
@@ -50,8 +55,20 @@ label {
 .alertText[clickable="true"] {
   color: -moz-nativehyperlinktext;
   text-decoration: underline;
 }
 
 .alertText[clickable="true"]:hover:active {
   color: -moz-activehyperlinktext;
 }
+
+.alertCloseButton {
+  list-style-image: url("moz-icon://stock/gtk-close?size=button");
+}
+
+.alertCloseButton > .toolbarbutton-icon {
+  margin: -4px;
+}
+
+.alertCloseButton > .toolbarbutton-text {
+  display: none;
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..23ab5a0eb8fa19d70eb43c6c0a53c5ea8f3c695f
GIT binary patch
literal 2517
zc$@*%2`cu9P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU*cS%G+RA}DKnG0-F=M~2tV5}`GYtaU*
z1GII5b*+TDLfefER2ZYvx-pfq4bin58%-e)AaBQs6DM&T@=k2Wc4Eg){EFX6Z1PT=
zI5AGJFRRv;wY&+?u5_bi8VCqN?0>u%d63w?X@OY!DY?GqoO{mw&Ue1^edcf;&}Uy7
zZytyTTHpo4zzGE4dCqkHz`@OFnK^@{l~Sfotz&deRz`2KF=|~CQ!T4wa`KB9$kc)*
z)5`I^X}ma)Tv$@cXvt}_eNyDA28PSe^Fn^@^l(0vHyiNEtL1px_8<ncW2C#!BRhKR
zWa7Ure)i^>(R1-$pI>BgxVq`S)@Wq}!V*99UU|UIXFidXo~5gl$b+dum~Ab6r_PLS
zzjf=@axf1(1m=S8-MDe%Ww*y$0l5IBS3yx3g2;UFK{>xJBU><_sMj(&I8GQX4lmyJ
zR&sbE9eh6nJmo+9P8Q_-lx{f%`v+ia;r(+wCG)Ew11Z30^kz&{+ig7F5dj>W6U_!>
z79phyNQZ1>WaZV|C+E3KkuuhDu`F0&!m&7<?bol5JsGPl1o7m7j&2R<N)`1CZ2#<@
zIld=_8wa*#@bi4NYHhI6+(}QrhX<$P*x3E}lM8E*b{^8tFDi5588h#N^WOrp!s1G%
z(O?NykUK#t92z})?kTpm5%BDO&%s=>QK!)}NoiRdzU;UDY8o$hu%@;ij#?(g*5b0m
znOReL&gY+BT82>=AUlP{6^1V@fW&0(x_nU?qt-SB-+Uw-t!)Kidb7YnFn1D`(BJUN
z)YaQBB0Dv88V2{i`vsiNm<cw5Sp`L<`bLA9i3OX(`TDs2T1-ZF3YU*`;7tfx4f;C>
z*;S$Eet7iwO4#v}Eq;E{o~b&&A~A()C==Hdq-W(Wme#2Q4KNrBwr1xl=zkx*&Y^m(
zaZ<UC9(T>Tk3Rm3tD{?<ohR}@XI^M7Sesook}0pQv9WVJDg9|6&de!bWEeuXXXrJ4
zp6E{vdJ_{XMw4wRFIQNFnmD<P#ompTm{#XGH*em27Av*|^J`zo4`*9vCdt8v0Q~uS
zM5lKrrOyCsfGj;*5bP8+P{u^^UcPeW8yE#KUB`^h`0c8iT3;j?duy9Gyv!aP@&B=;
zQrZ=|?sa?Cgk*WS1?whq`eCpNWFo@OY(XI-CdcvCLYQpLW_pNJp=t^LJ{khy{+0EO
zhKMryCQEX-KVxHK-&8f|2jI^ly1sPj-*Yhs4w7tXg(NxL@khW;^cxNgGgDG2L9ecz
z@F511t(<gf>l;060w}GNY>FnMP-^a)oK4o|CKQVe<MSZSWNo&REJjlj6xg*dZ4Rn}
zPE=CC$dwwDL}Ns>26H;;sTy>{(PYDs$d%io$yCc~^Y07*si&xtD&7oT>rK`wl9kq~
ztf3teAL$UYBT9`ff%K3LBWwbYE7kGQWGcn-yQ)jAszIq#YhMpt;{|g6XHV0^*|j%y
zVR41nPq`T@3RT07@c_JR^x(*L{Ehf5j8XtTEea1nBCm7ljOJg4t{Zese-7HK*Lpb_
zS%SAPNf<VcNad=epdo!zHyb@{Xt)Nm-4#iOd`ai!R)m~IFw10h>XG-}|NHIzF-Btx
z$zqNMI3*PlH7T({TvI0;e-joqdKJ~O`etWael)qVDk+n&H&p~SBA6Cyvr`-1Lm`0Q
zkQ`E(!SE=1*#=N0t*sB{qR}j7qgN?a3?2F4!xhM1Uo=@dvEb*60(gdg^z8ZntM?L_
zaudmr9ga+?8Ky|sP$iM~1*<`mHI<DX<_?pUD}1i2*Ig7vHehkI8jl`3k$^Y9idu;9
z0H`97Tz#Y7O|_h18vt^W*|{fW!GZR!^=x&~2W4EndTp82;WSL-I9hJ)iIb<_hSNU+
zk4@AeRET9MB(K&Pn^DX4K3cHY!p+Y6?Zu0q&BJ6GVxw17TGfplo`Dti$35<W67)4x
z2!`w}F3ssPXX%vUg~>Mip${6lDzb$lQpZti4INVnpkRNQnpQ=#y+z4J57URi!ub;E
zf~8;scm=EkkH*qiR!HPdvV{eW$7$4?G?YecL8S=nNKAeL`Mi~_E<dkOH`Toeo;k0&
zrZzx!P|<$PX|mXPQwe}3BsLw1NQL>022(p5UF0wWM^;ZIfK+bw=J9#!`^P627I3=z
z`qz>(Qz2he>Un#2($@yPz67?qnK}7R=sv_Y0D-XBO8O?NLqpq4>h7pLbxkDz+QehG
zlm;6M(4pGc=pqehF7VknkAPpG<4Mx*?sdO$`$kufJBhl`sen=N!|?KV-#@YmL+fLs
zE8JhsKzD15&d*NI610#WI)HXc-P<>>UAwjfdF-4@0JJ8ga`_z=d&^>v*T=(m0XF)4
zL7_id5V#+Qbte}qc~EE2JG|r0gQK{2-#fAeLo-Ycv1L3RjECR(+tZj+Mfffdt1hKs
zmO#kF@7gB>cOBm1yGMaYxW6nYkG@C+f9Fqh`ZagNe>fRI_}Dm)wDF=M^hxE)cFYIS
z=(+RHU{dE`$K=8q(~0{-vDhiZW@`7ov|%_~1pWZFfI__a0lG&rWbeX-Pgg}c!drm;
zV>=osE4>*40Sy_=c75xC4spL{NZ@Dwns)5?i5(X{{pTAvHP{55Ux8nMpM&SAYXnQc
zI<TJZ|L5}M-yiwlXo7oSDBtR6Rbut;3fT{bHdQ!vqkf6u;q*gTTprwy(>>^Po*6y+
zTGW%h@gEs}ile-(Cr_P@zdkm$5!Wwzh7N8hEG`%F`1$grbe<l4x)X269iOnbe;oSX
z1zJG^m>*`7f_fZ$2Rui?o;&~1Hl&*fyDJ+U3``WzI03Bo8vKGoemu<TK$VvEV6DZc
zTB=wsC@f|~_<gISLL6L-DEGsm?+8q2L9~bA{v-5j3|M{n@|E8m3LH+O(Lsys#9q4-
zMa9wLYL^^8aq2C|tN>s6LT8{zna7ZaJ{smoV5r-#UcI`E<>~WGun_zZyfmJAtHCqT
z8ap-*iBoI!HmO3%M53*;JDu(M|9$(QvXo#4$3&vl)&7%d`vbMXWZf1mfb6`&SEp?N
zT|Irz;)sKZ1UFmIzI|un<I^$#svJ0+@=$G_3_4Ljt8Ci7BXRMx3_{(g*GCF~pfZcr
zVh};hoQ4zGC=knPnP^bko3J<K2h%P9?BF(wv1p<9@zp3)UP4i)cH>mJmr!Z6ZNNo{
fip$sEYk&R=1GH@0$vYc300000NkvXXu0mjfAkD<?
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -29,16 +29,17 @@ toolkit.jar:
 +  skin/classic/global/scrollbox.css
 +  skin/classic/global/splitter.css
 +  skin/classic/global/tabbox.css
 +  skin/classic/global/textbox.css
 +  skin/classic/global/toolbar.css
 +  skin/classic/global/toolbarbutton.css
 +  skin/classic/global/tree.css
 +  skin/classic/global/alerts/alert.css                        (alerts/alert.css)
++  skin/classic/global/alerts/notification-48.png              (alerts/notification-48.png)
 +  skin/classic/global/console/console.css                     (console/console.css)
 +  skin/classic/global/console/console.png                     (console/console.png)
 +  skin/classic/global/console/console-toolbar.png             (console/console-toolbar.png)
 +  skin/classic/global/dirListing/remote.png                   (dirListing/remote.png)
 +  skin/classic/global/icons/Authentication.png                (icons/Authentication.png)
 +  skin/classic/global/icons/autoscroll.png                    (icons/autoscroll.png)
 +  skin/classic/global/icons/blacklist_favicon.png             (icons/blacklist_favicon.png)
 +  skin/classic/global/icons/blacklist_large.png               (icons/blacklist_large.png)
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/osx/global/alerts/alert.css
@@ -0,0 +1,100 @@
+/* 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/. */
+
+/* ===== alert.css =====================================================
+  == Styles specific to the alerts dialog.
+  ======================================================================= */
+
+@import url("chrome://global/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.alertBox {
+  border: 1px solid threedshadow;
+  border-radius: 3px;
+  background-color: -moz-Dialog;
+}
+
+.alertImageBox {
+  padding: 8px 0;
+  width: 64px;
+  background-image: linear-gradient(rgba(255,255,255,0.7), rgba(255,255,255,0.6));
+  -moz-border-end: 1px solid rgba(0,0,0,.1);
+}
+
+.alertTextBox {
+  padding: 8px;
+  -moz-padding-start: 16px;
+  width: 255px;
+}
+
+.alertTextBox,
+.alertCloseBox {
+  background-image: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0.1));
+}
+
+.alertTitle {
+  font-weight: bold;
+  font-size: 110%;
+}
+
+#alertImage {
+  max-width: 48px;
+  max-height: 48px;
+  list-style-image: url(chrome://global/skin/alerts/notification-48.png);
+}
+
+#alertNotification[clickable="true"] {
+  cursor: pointer;
+}
+
+label {
+  cursor: inherit;
+}
+
+.alertText[clickable="true"] {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+.alertText[clickable="true"]:hover:active {
+  color: -moz-activehyperlinktext;
+}
+
+@keyframes alert-animation {
+  from {
+    opacity: 0;
+  }
+  6.25% {
+    opacity: 1;
+  }
+  93.75% {
+    opacity: 1;
+  }
+  to {
+    opacity: 0;
+  }
+}
+
+.alertCloseButton {
+  -moz-appearance: none;
+  padding: 0;
+  margin: 0 2px;
+  list-style-image: url("chrome://global/skin/icons/close.png");
+  border: none;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.alertCloseButton > .toolbarbutton-text {
+  display: none;
+}
+
+.alertCloseButton:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+.alertCloseButton:hover:active {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..23ab5a0eb8fa19d70eb43c6c0a53c5ea8f3c695f
GIT binary patch
literal 2517
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
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -52,16 +52,18 @@ toolkit.jar:
   skin/classic/global/tabbox.css
   skin/classic/global/textbox.css
   skin/classic/global/datetimepicker.css
   skin/classic/global/toolbar.css
   skin/classic/global/toolbarbutton.css
 * skin/classic/global/tree.css
 * skin/classic/global/viewbuttons.css
   skin/classic/global/wizard.css
+  skin/classic/global/alerts/alert.css                               (alerts/alert.css)
+  skin/classic/global/alerts/notification-48.png                     (alerts/notification-48.png)
   skin/classic/global/arrow/arrow-dn-dis.gif                         (arrow/arrow-dn-dis.gif)
   skin/classic/global/arrow/arrow-dn-dis.png                         (arrow/arrow-dn-dis.png)
   skin/classic/global/arrow/arrow-dn-sharp.gif                       (arrow/arrow-dn-sharp.gif)
   skin/classic/global/arrow/arrow-dn.gif                             (arrow/arrow-dn.gif)
   skin/classic/global/arrow/arrow-dn.png                             (arrow/arrow-dn.png)
   skin/classic/global/arrow/arrow-lft-dis.gif                        (arrow/arrow-lft-dis.gif)
   skin/classic/global/arrow/arrow-lft-hov.gif                        (arrow/arrow-lft-hov.gif)
   skin/classic/global/arrow/arrow-lft-sharp-end.gif                  (arrow/arrow-lft-sharp-end.gif)
--- a/toolkit/themes/windows/global/alerts/alert.css
+++ b/toolkit/themes/windows/global/alerts/alert.css
@@ -22,27 +22,32 @@
   background-image: linear-gradient(rgba(255,255,255,0.7), rgba(255,255,255,0.6));
   -moz-border-end: 1px solid rgba(0,0,0,.1);
 }
 
 .alertTextBox {
   padding: 8px;
   -moz-padding-start: 16px;
   width: 255px;
+}
+
+.alertTextBox,
+.alertCloseBox {
   background-image: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0.1));
 }
 
 .alertTitle {
   font-weight: bold;
   font-size: 110%;
 }
 
 #alertImage {
   max-width: 48px;
   max-height: 48px;
+  list-style-image: url(chrome://global/skin/alerts/notification-48.png);
 }
 
 #alertNotification[clickable="true"] {
   cursor: pointer;
 }
 
 label {
   cursor: inherit;
@@ -66,8 +71,29 @@ label {
   }
   93.75% {
     opacity: 1;
   }
   to {
     opacity: 0;
   }
 }
+
+.alertCloseButton {
+  list-style-image: url("chrome://global/skin/icons/close.png");
+  -moz-appearance: none;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+  padding: 4px 2px;
+  border: none !important;
+}
+
+.alertCloseButton > .toolbarbutton-text {
+  display: none;
+}
+
+.alertCloseButton:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+.alertCloseButton:hover:active {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..23ab5a0eb8fa19d70eb43c6c0a53c5ea8f3c695f
GIT binary patch
literal 2517
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
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -56,16 +56,17 @@ toolkit.jar:
         skin/classic/global/tabbox.css
         skin/classic/global/tabprompts.css
         skin/classic/global/textbox.css
         skin/classic/global/toolbar.css
         skin/classic/global/toolbarbutton.css
         skin/classic/global/tree.css
         skin/classic/global/wizard.css
         skin/classic/global/alerts/alert.css                     (alerts/alert.css)
+        skin/classic/global/alerts/notification-48.png           (alerts/notification-48.png)
         skin/classic/global/arrow/arrow-dn.gif                   (arrow/arrow-dn.gif)
         skin/classic/global/arrow/arrow-dn-dis.gif               (arrow/arrow-dn-dis.gif)
         skin/classic/global/arrow/arrow-dn-hov.gif               (arrow/arrow-dn-hov.gif)
         skin/classic/global/arrow/arrow-dn-sharp.gif             (arrow/arrow-dn-sharp.gif)
         skin/classic/global/arrow/arrow-down.png                 (arrow/arrow-down.png)
         skin/classic/global/arrow/arrow-lft.gif                  (arrow/arrow-lft.gif)
         skin/classic/global/arrow/arrow-lft-dis.gif              (arrow/arrow-lft-dis.gif)
         skin/classic/global/arrow/arrow-lft-hov.gif              (arrow/arrow-lft.gif)
@@ -234,16 +235,17 @@ toolkit.jar:
         skin/classic/aero/global/tabbox.css
         skin/classic/aero/global/tabprompts.css
 *       skin/classic/aero/global/textbox.css                             (textbox-aero.css)
         skin/classic/aero/global/toolbar.css
 *       skin/classic/aero/global/toolbarbutton.css                       (toolbarbutton-aero.css)
 *       skin/classic/aero/global/tree.css                                (tree-aero.css)
         skin/classic/aero/global/wizard.css
         skin/classic/aero/global/alerts/alert.css                        (alerts/alert.css)
+        skin/classic/aero/global/alerts/notification-48.png              (alerts/notification-48.png)
         skin/classic/aero/global/arrow/arrow-dn.gif                      (arrow/arrow-dn.gif)
         skin/classic/aero/global/arrow/arrow-dn-dis.gif                  (arrow/arrow-dn-dis.gif)
         skin/classic/aero/global/arrow/arrow-dn-hov.gif                  (arrow/arrow-dn-hov.gif)
         skin/classic/aero/global/arrow/arrow-dn-sharp.gif                (arrow/arrow-dn-sharp.gif)
         skin/classic/aero/global/arrow/arrow-down.png                    (arrow/arrow-down.png)
         skin/classic/aero/global/arrow/arrow-lft.gif                     (arrow/arrow-lft.gif)
         skin/classic/aero/global/arrow/arrow-lft-dis.gif                 (arrow/arrow-lft-dis.gif)
         skin/classic/aero/global/arrow/arrow-lft-hov.gif                 (arrow/arrow-lft.gif)
--- a/widget/cocoa/nsLookAndFeel.mm
+++ b/widget/cocoa/nsLookAndFeel.mm
@@ -366,16 +366,19 @@ nsLookAndFeel::GetIntImpl(IntID aID, int
       res = NS_ERROR_NOT_IMPLEMENTED;
       break;
     case eIntID_MacGraphiteTheme:
       aResult = [NSColor currentControlTint] == NSGraphiteControlTint;
       break;
     case eIntID_MacLionTheme:
       aResult = nsCocoaFeatures::OnLionOrLater();
       break;
+    case eIntID_AlertNotificationOrigin:
+      aResult = NS_ALERT_TOP;
+      break;
     case eIntID_TabFocusModel:
     {
       // we should probably cache this
       CFPropertyListRef fullKeyboardAccessProperty;
       fullKeyboardAccessProperty = ::CFPreferencesCopyValue(CFSTR("AppleKeyboardUIMode"),
                                                             kCFPreferencesAnyApplication,
                                                             kCFPreferencesCurrentUser,
                                                             kCFPreferencesAnyHost);
--- a/widget/gtk2/nsLookAndFeel.cpp
+++ b/widget/gtk2/nsLookAndFeel.cpp
@@ -535,16 +535,19 @@ nsLookAndFeel::GetIntImpl(IntID aID, int
         res = NS_ERROR_NOT_IMPLEMENTED;
 #endif
         break;
     case eIntID_MacGraphiteTheme:
     case eIntID_MacLionTheme:
         aResult = 0;
         res = NS_ERROR_NOT_IMPLEMENTED;
         break;
+    case eIntID_AlertNotificationOrigin:
+        aResult = NS_ALERT_TOP;
+        break;
     case eIntID_IMERawInputUnderlineStyle:
     case eIntID_IMEConvertedTextUnderlineStyle:
         aResult = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
         break;
     case eIntID_IMESelectedRawTextUnderlineStyle:
     case eIntID_IMESelectedConvertedTextUnderline:
         aResult = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
         break;