Bug 778668 - Bubble the application name/origin/manifest in the desktop-notification mozChromeEvent [r=wchen]
authorFabrice Desré <fabrice@mozilla.com>
Wed, 28 Nov 2012 22:36:15 -0800
changeset 114466 60378fae4e94b39e96841cc6c44fcd2a7cc6b591
parent 114465 602add2d02e6fcf9913acfac62eb0d24970c37c6
child 114467 14e5a7d93bc07794d0d2db2e6fe468424e394a36
push id23917
push useremorley@mozilla.com
push dateThu, 29 Nov 2012 14:20:29 +0000
treeherdermozilla-central@c72d38e7a212 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen
bugs778668
milestone20.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 778668 - Bubble the application name/origin/manifest in the desktop-notification mozChromeEvent [r=wchen]
b2g/chrome/content/shell.js
b2g/components/AlertsService.js
dom/base/nsGlobalWindow.h
dom/interfaces/notification/nsIDOMDesktopNotification.idl
dom/src/notification/nsDesktopNotification.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -258,16 +258,17 @@ var shell = {
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
 
     ppmm.addMessageListener("content-handler", this);
     ppmm.addMessageListener("dial-handler", this);
     ppmm.addMessageListener("sms-handler", this);
     ppmm.addMessageListener("mail-handler", this);
+    ppmm.addMessageListener("app-notification-send", AlertsHelper);
   },
 
   stop: function shell_stop() {
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
@@ -604,47 +605,106 @@ var AlertsHelper = {
   _listeners: {},
   _count: 0,
 
   handleEvent: function alert_handleEvent(detail) {
     if (!detail || !detail.id)
       return;
 
     let listener = this._listeners[detail.id];
-    let topic = detail.type == "desktop-notification-click" ? "alertclickcallback" : "alertfinished";
-    listener.observer.observe(null, topic, listener.cookie);
+    if (!listener)
+     return;
+
+    let topic = detail.type == "desktop-notification-click" ? "alertclickcallback"
+                                                            : "alertfinished";
+
+    if (detail.id.startsWith("alert")) {
+      listener.observer.observe(null, topic, listener.cookie);
+    } else {
+      listener.mm.sendAsyncMessage("app-notification-return",
+                                   { id: detail.id,
+                                     type: detail.type });
+    }
 
     // we're done with this notification
     if (topic === "alertfinished")
       delete this._listeners[detail.id];
   },
 
   registerListener: function alert_registerListener(cookie, alertListener) {
     let id = "alert" + this._count++;
     this._listeners[id] = { observer: alertListener, cookie: cookie };
     return id;
   },
 
+  registerAppListener: function alertRegisterAppListener(id, mm, title, text,
+                                                         manifestURL, imageURL) {
+    this._listeners[id] = {
+      mm: mm,
+      title: title,
+      text: text,
+      manifestURL: manifestURL,
+      imageURL: imageURL
+    };
+  },
+
+  showNotification: function alert_showNotification(imageUrl,
+                                                    title,
+                                                    text,
+                                                    textClickable,
+                                                    cookie,
+                                                    id,
+                                                    name,
+                                                    manifestUrl) {
+    function send(appName, appIcon) {
+      shell.sendChromeEvent({
+        type: "desktop-notification",
+        id: id,
+        icon: imageUrl,
+        title: title,
+        text: text,
+        appName: appName,
+        appIcon: appIcon
+      });
+    }
+
+    // If we have a manifest URL, get the icon and title from the manifest
+    // to prevent spoofing.
+    if (manifestUrl && manifestUrl.length) {
+      let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl);
+      DOMApplicationRegistry.getManifestFor(app.origin, function(aManifest) {
+        let helper = new ManifestHelper(aManifest, app.origin);
+        send(helper.name, helper.iconURLForSize(128));
+      });
+    } else {
+      send(null, null);
+    }
+  },
+
   showAlertNotification: function alert_showAlertNotification(imageUrl,
                                                               title,
                                                               text,
                                                               textClickable,
                                                               cookie,
                                                               alertListener,
-                                                              name)
-  {
-    let id = this.registerListener(cookie, alertListener);
-    shell.sendChromeEvent({
-      type: "desktop-notification",
-      id: id,
-      icon: imageUrl,
-      title: title,
-      text: text
-    });
-  }
+                                                              name) {
+    let id = this.registerListener(null, alertListener);
+    this.showNotification(imageUrl, title, text, textClickable, cookie,
+                          id, name, null);
+  },
+
+  receiveMessage: function alert_receiveMessage(message) {
+    let data = message.data;
+
+    this.registerAppListener(data.id, message.target, data.title, data.text,
+                             data.manifestURL, data.imageURL);
+    this.showNotification(data.imageURL, data.title, data.text,
+                          data.textClickable, null,
+                          data.id, null, data.manifestURL);
+  },
 }
 
 var WebappsHelper = {
   _installers: {},
   _count: 0,
 
   init: function webapps_init() {
     Services.obs.addObserver(this, "webapps-launch", false);
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -1,27 +1,86 @@
 /* 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/. */
- 
+
 const Ci = Components.interfaces;
 const Cu = Components.utils;
+const Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"]
+           .getService(Ci.nsIMessageSender);
+});
+
 // -----------------------------------------------------------------------
 // Alerts Service
 // -----------------------------------------------------------------------
 
-function AlertsService() { }
+function AlertsService() {
+  cpmm.addMessageListener("app-notification-return", this);
+  this._id = 0;
+}
 
 AlertsService.prototype = {
   classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService,
+                                         Ci.nsIAppNotificationService]),
+
+  // nsIAlertsService
+  showAlertNotification: function showAlertNotification(aImageUrl,
+                                                        aTitle,
+                                                        aText,
+                                                        aTextClickable,
+                                                        aCookie,
+                                                        aAlertListener,
+                                                        aName) {
+    let browser = Services.wm.getMostRecentWindow("navigator:browser");
+    browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText,
+                                               aTextClickable, aCookie,
+                                               aAlertListener, aName);
+  },
+
+  // nsIAppNotificationService
+  _listeners: [],
+
+  receiveMessage: function receiveMessage(aMessage) {
+    let data = aMessage.data;
+    if (aMessage.name !== "app-notification-return" ||
+        !this._listeners[data.id]) {
+      return;
+    }
 
-  showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName) {
-    let browser = Services.wm.getMostRecentWindow("navigator:browser");
-    browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName);
+    let obs = this._listeners[data.id];
+    let topic = data.type == "desktop-notification-click" ? "alertclickcallback"
+                                                          : "alertfinished";
+    obs.observe(null, topic, null);
+
+    // we're done with this notification
+    if (topic === "alertfinished")
+      delete this._listeners[data.id];
+  },
+
+  // This method is called in the content process, so we remote the call
+  // to shell.js
+  showAppNotification: function showAppNotification(aImageURL,
+                                                    aTitle,
+                                                    aText,
+                                                    aTextClickable,
+                                                    aManifestURL,
+                                                    aAlertListener) {
+    let id = "app-notif" + this._id++;
+    this._listeners[id] = aAlertListener;
+    cpmm.sendAsyncMessage("app-notification-send", {
+      imageURL: aImageURL,
+      title: aTitle,
+      text: aText,
+      textClickable: aTextClickable,
+      manifestURL: aManifestURL,
+      id: id
+    });
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -59,17 +59,16 @@
 #include "nsIContent.h"
 #include "nsIIDBFactory.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsIInlineEventHandlers.h"
 #include "nsWrapperCacheInlines.h"
-#include "nsIDOMApplicationRegistry.h"
 #include "nsIIdleObserver.h"
 #include "nsIDOMWakeLock.h"
 
 // JS includes
 #include "jsapi.h"
 
 #ifdef MOZ_B2G
 #include "nsIDOMWindowB2G.h"
@@ -1106,16 +1105,17 @@ protected:
 
   nsTHashtable<nsPtrHashKey<nsDOMEventTargetHelper> > mEventTargetObjects;
 
   nsTArray<uint32_t> mEnabledSensors;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class PostMessageEvent;
+  friend class nsDOMDesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
 
 /*
  * nsGlobalChromeWindow inherits from nsGlobalWindow. It is the global
  * object created for a Chrome Window only.
--- a/dom/interfaces/notification/nsIDOMDesktopNotification.idl
+++ b/dom/interfaces/notification/nsIDOMDesktopNotification.idl
@@ -1,17 +1,17 @@
 /* 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 "domstubs.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMDesktopNotification;
-
+interface nsIObserver;
 
 [scriptable, uuid(CCEA6185-0A3D-45AB-9058-1004DD4B8C50)]
 interface nsIDOMDesktopNotificationCenter : nsISupports
 {
   nsIDOMDesktopNotification createNotification(in DOMString title,
                                                in DOMString description,
                                                [optional] in DOMString iconURL);
 };
@@ -20,8 +20,20 @@ interface nsIDOMDesktopNotificationCente
 [scriptable, uuid(77bc6adc-77d6-4b29-9844-7eaac25e995d)]
 interface nsIDOMDesktopNotification : nsISupports
 {
   void show();
 
   [implicit_jscontext] attribute jsval onclick;
   [implicit_jscontext] attribute jsval onclose;
 };
+
+// Notification service that also provides the manifest URL
+[scriptable, uuid(7fb4f0f9-ff5b-4620-8e1b-d82d723605af)]
+interface nsIAppNotificationService : nsISupports
+{
+    void showAppNotification(in AString  imageUrl,
+                              in AString  title,
+                              in AString  text,
+                              [optional] in boolean textClickable,
+                              [optional] in AString manifestURL,
+                              [optional] in nsIObserver alertListener);
+};
--- a/dom/src/notification/nsDesktopNotification.cpp
+++ b/dom/src/notification/nsDesktopNotification.cpp
@@ -5,16 +5,19 @@
 #include "nsDesktopNotification.h"
 
 #include "nsContentPermissionHelper.h"
 #include "nsXULAppAPI.h"
 
 #include "mozilla/dom/PBrowserChild.h"
 #include "TabChild.h"
 #include "mozilla/Preferences.h"
+#include "nsGlobalWindow.h"
+#include "nsIAppsService.h"
+#include "nsIDOMDesktopNotification.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /* ------------------------------------------------------------------------ */
 /* AlertServiceObserver                                                     */
 /* ------------------------------------------------------------------------ */
 
@@ -22,23 +25,42 @@ NS_IMPL_ISUPPORTS1(AlertServiceObserver,
 
 /* ------------------------------------------------------------------------ */
 /* nsDesktopNotification                                                    */
 /* ------------------------------------------------------------------------ */
 
 nsresult
 nsDOMDesktopNotification::PostDesktopNotification()
 {
+  if (!mObserver)
+    mObserver = new AlertServiceObserver(this);
+
+#ifdef MOZ_B2G
+  nsCOMPtr<nsIAppNotificationService> appNotifier =
+    do_GetService("@mozilla.org/system-alerts-service;1");
+  if (appNotifier) {
+    nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+    uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
+
+    if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+      nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
+      nsString manifestUrl = EmptyString();
+      appsService->GetManifestURLByLocalId(appId, manifestUrl);
+      return appNotifier->ShowAppNotification(mIconURL, mTitle, mDescription,
+                                              true,
+                                              manifestUrl,
+                                              mObserver);
+    }
+  }
+#endif
+
   nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
   if (!alerts)
     return NS_ERROR_NOT_IMPLEMENTED;
 
-  if (!mObserver)
-    mObserver = new AlertServiceObserver(this);
-
   return alerts->ShowAlertNotification(mIconURL, mTitle, mDescription,
                                        true,
                                        EmptyString(),
                                        mObserver,
                                        EmptyString());
 }
 
 DOMCI_DATA(DesktopNotification, nsDOMDesktopNotification)