Bug 1206560 - Show the site favicon in XUL notifications. r=jaws draft
authorKit Cambridge <kcambridge@mozilla.com>
Wed, 24 Feb 2016 10:54:09 -0800
changeset 334227 493dd374633aeb8a54ee3fd4855448d9252991f7
parent 332881 789a12291942763bc1e3a89f97e0b82dc1c9d00b
child 514855 1620bb4f6b2a427eba53ea186d0e8dccf51692d4
push id11485
push userkcambridge@mozilla.com
push dateWed, 24 Feb 2016 20:10:42 +0000
reviewersjaws
bugs1206560
milestone47.0a1
Bug 1206560 - Show the site favicon in XUL notifications. r=jaws MozReview-Commit-ID: FMVYO1DCFDm
browser/base/content/test/alerts/browser_notification_close.js
toolkit/components/alerts/nsXULAlerts.cpp
toolkit/components/alerts/nsXULAlerts.h
toolkit/components/alerts/resources/content/alert.css
toolkit/components/alerts/resources/content/alert.js
toolkit/components/alerts/resources/content/alert.xul
toolkit/themes/shared/alert-common.css
--- a/browser/base/content/test/alerts/browser_notification_close.js
+++ b/browser/base/content/test/alerts/browser_notification_close.js
@@ -1,17 +1,30 @@
 "use strict";
 
+const {PlacesTestUtils} =
+  Cu.import("resource://testing-common/PlacesTestUtils.jsm", {});
+
 let tab;
 let notification;
 let notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
 
 add_task(function* test_notificationClose() {
   let pm = Services.perms;
-  pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+  let notificationURI = makeURI(notificationURL);
+  pm.add(notificationURI, "desktop-notification", pm.ALLOW_ACTION);
+
+  yield PlacesTestUtils.addVisits(notificationURI);
+  let faviconURI = yield new Promise(resolve => {
+    let faviconURI = makeURI("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC");
+    PlacesUtils.favicons.setAndFetchFaviconForPage(notificationURI, faviconURI,
+      true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+      (faviconURI, iconSize, iconData, mimeType) => resolve(faviconURI),
+      Services.scriptSecurityManager.getSystemPrincipal());
+  });
 
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: notificationURL
   }, function* dummyTabTask(aBrowser) {
     let win = aBrowser.contentWindow.wrappedJSObject;
     notification = win.showNotification2();
     yield BrowserTestUtils.waitForEvent(notification, "show");
@@ -24,16 +37,18 @@ add_task(function* test_notificationClos
       notification.close();
       return;
     }
 
     let alertTitleLabel = alertWindow.document.getElementById("alertTitleLabel");
     is(alertTitleLabel.value, "Test title", "Title text of notification should be present");
     let alertTextLabel = alertWindow.document.getElementById("alertTextLabel");
     is(alertTextLabel.textContent, "Test body", "Body text of notification should be present");
+    let alertIcon = alertWindow.document.getElementById("alertIcon");
+    is(alertIcon.src, faviconURI.spec, "Icon of notification should be present");
 
     let alertCloseButton = alertWindow.document.querySelector(".alertCloseButton");
     is(alertCloseButton.localName, "toolbarbutton", "close button found");
     let promiseBeforeUnloadEvent =
       BrowserTestUtils.waitForEvent(alertWindow, "beforeunload");
     let closedTime = alertWindow.Date.now();
     alertCloseButton.click();
     info("Clicked on close button");
--- a/toolkit/components/alerts/nsXULAlerts.cpp
+++ b/toolkit/components/alerts/nsXULAlerts.cpp
@@ -43,17 +43,17 @@ nsXULAlertObserver::Observe(nsISupports*
 
   nsresult rv = NS_OK;
   if (mObserver) {
     rv = mObserver->Observe(aSubject, aTopic, aData);
   }
   return rv;
 }
 
-NS_IMPL_ISUPPORTS(nsXULAlerts, nsIAlertsService, nsIAlertsDoNotDisturb)
+NS_IMPL_ISUPPORTS(nsXULAlerts, nsIAlertsService, nsIAlertsDoNotDisturb, nsIAlertsIconURI)
 
 /* static */ already_AddRefed<nsXULAlerts>
 nsXULAlerts::GetInstance()
 {
   if (!gXULAlerts) {
     gXULAlerts = new nsXULAlerts();
     ClearOnShutdown(&gXULAlerts);
   }
@@ -79,16 +79,24 @@ nsXULAlerts::ShowAlertNotification(const
   NS_ENSURE_SUCCESS(rv, rv);
   return ShowAlert(alert, aAlertListener);
 }
 
 NS_IMETHODIMP
 nsXULAlerts::ShowAlert(nsIAlertNotification* aAlert,
                        nsIObserver* aAlertListener)
 {
+  return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
+}
+
+NS_IMETHODIMP
+nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert,
+                                  nsIObserver* aAlertListener,
+                                  nsIURI* aIconURI)
+{
   bool inPrivateBrowsing;
   nsresult rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString cookie;
   rv = aAlert->GetCookie(cookie);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -235,16 +243,27 @@ nsXULAlerts::ShowAlert(nsIAlertNotificat
   // The source contains the host and port of the site that sent the
   // notification. It is empty for system alerts.
   nsCOMPtr<nsISupportsString> scriptableAlertSource (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
   NS_ENSURE_TRUE(scriptableAlertSource, NS_ERROR_FAILURE);
   scriptableAlertSource->SetData(source);
   rv = argsArray->AppendElement(scriptableAlertSource);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsCOMPtr<nsISupportsCString> scriptableIconURL (do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+  NS_ENSURE_TRUE(scriptableIconURL, NS_ERROR_FAILURE);
+  if (aIconURI) {
+    nsAutoCString iconURL;
+    rv = aIconURI->GetSpec(iconURL);
+    NS_ENSURE_SUCCESS(rv, rv);
+    scriptableIconURL->SetData(iconURL);
+  }
+  rv = argsArray->AppendElement(scriptableIconURL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<mozIDOMWindowProxy> newWindow;
   nsAutoCString features("chrome,dialog=yes,titlebar=no,popup=yes");
   if (inPrivateBrowsing) {
     features.AppendLiteral(",private");
   }
   rv = wwatch->OpenWindow(nullptr, ALERT_CHROME_URL, "_blank", features.get(),
                           argsArray, getter_AddRefs(newWindow));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/alerts/nsXULAlerts.h
+++ b/toolkit/components/alerts/nsXULAlerts.h
@@ -8,20 +8,22 @@
 
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 
 #include "mozIDOMWindow.h"
 #include "nsIObserver.h"
 
 class nsXULAlerts : public nsIAlertsService,
-                    public nsIAlertsDoNotDisturb
+                    public nsIAlertsDoNotDisturb,
+                    public nsIAlertsIconURI
 {
   friend class nsXULAlertObserver;
 public:
+  NS_DECL_NSIALERTSICONURI
   NS_DECL_NSIALERTSDONOTDISTURB
   NS_DECL_NSIALERTSSERVICE
   NS_DECL_ISUPPORTS
 
   nsXULAlerts()
   {
   }
 
--- a/toolkit/components/alerts/resources/content/alert.css
+++ b/toolkit/components/alerts/resources/content/alert.css
@@ -8,20 +8,26 @@
   animation-name: alert-animation;
 }
 
 #alertBox[animate]:not([clicked]):not([closing]):hover {
   animation-play-state: paused;
 }
 
 #alertBox:not([hasOrigin]) > box > #alertTextBox > #alertFooter,
+#alertBox:not([hasIcon]) > box > #alertIcon,
 #alertImage:not([src]) {
   display: none;
 }
 
+#alertTitleBox {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+}
+
 .alertText {
   white-space: pre-wrap;
 }
 
 @keyframes alert-animation {
   to {
     visibility: hidden;
   }
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -32,19 +32,29 @@ function prefillAlertInfo() {
   // 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] --> bidi
   // arguments[7] --> lang
   // arguments[8] --> replaced alert window (nsIDOMWindow)
   // arguments[9] --> an optional callback listener (nsIObserver)
   // arguments[10] -> the nsIURI.hostPort of the origin, optional
+  // arguments[11] -> the alert icon URL, optional
 
   switch (window.arguments.length) {
     default:
+    case 12: {
+      if (window.arguments[11]) {
+        let alertBox = document.getElementById("alertBox");
+        alertBox.setAttribute("hasIcon", true);
+
+        let icon = document.getElementById("alertIcon");
+        icon.src = window.arguments[11];
+      }
+    }
     case 11: {
       if (window.arguments[10]) {
         let alertBox = document.getElementById("alertBox");
         alertBox.setAttribute("hasOrigin", true);
 
         let hostPort = window.arguments[10];
         const ALERT_BUNDLE = Services.strings.createBundle(
           "chrome://alerts/locale/alert.properties");
--- a/toolkit/components/alerts/resources/content/alert.xul
+++ b/toolkit/components/alerts/resources/content/alert.xul
@@ -20,16 +20,17 @@
         onload="onAlertLoad();"
         onclick="onAlertClick();"
         onbeforeunload="onAlertBeforeUnload();">
 
   <script type="application/javascript" src="chrome://global/content/alerts/alert.js"/>
 
   <vbox id="alertBox" class="alertBox">
     <box id="alertTitleBox">
+      <image id="alertIcon"/>
       <label id="alertTitleLabel" class="alertTitle plain" crop="end"/>
       <vbox class="alertCloseBox">
         <toolbarbutton class="alertCloseButton close-icon"
                        tooltiptext="&closeAlert.tooltip;"
                        onclick="event.stopPropagation();"
                        oncommand="onAlertClose();"/>
       </vbox>
     </box>
--- a/toolkit/themes/shared/alert-common.css
+++ b/toolkit/themes/shared/alert-common.css
@@ -48,16 +48,32 @@
 }
 
 @keyframes alert-closing-animation {
   to {
     opacity: 0;
   }
 }
 
+#alertIcon {
+  margin-top: 6px;
+  margin-inline-start: 8px;
+  margin-inline-end: 0;
+  margin-bottom: 0;
+  width: 16px;
+  min-height: 16px;
+  max-height: 16px;
+}
+
+@media (resolution: 2dppx) {
+  #alertIcon {
+    image-rendering: -moz-crisp-edges;
+  }
+}
+
 #alertImage {
   width: 80px;
   height: 80px;
   max-width: 80px;
   max-height: 80px;
   object-fit: scale-down;
   margin: 0 7px 7px;
 }