diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index f097cbfa66e..b0f5768efe4 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -263,6 +263,7 @@ var shell = { 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() { @@ -609,8 +610,19 @@ var AlertsHelper = { 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") @@ -623,23 +635,71 @@ var AlertsHelper = { 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 = { diff --git a/b2g/components/AlertsService.js b/b2g/components/AlertsService.js index abe2838020c..ca95fe39e29 100644 --- a/b2g/components/AlertsService.js +++ b/b2g/components/AlertsService.js @@ -1,26 +1,85 @@ /* 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]), - showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName) { + // 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); + 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; + } + + 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 + }); } }; diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index ef5c34a75a6..0c92eec8e19 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -64,7 +64,6 @@ #include "nsIDOMTouchEvent.h" #include "nsIInlineEventHandlers.h" #include "nsWrapperCacheInlines.h" -#include "nsIDOMApplicationRegistry.h" #include "nsIIdleObserver.h" #include "nsIDOMWakeLock.h" @@ -1111,6 +1110,7 @@ protected: friend class nsDOMScriptableHelper; friend class nsDOMWindowUtils; friend class PostMessageEvent; + friend class nsDOMDesktopNotification; static WindowByIdTable* sWindowsById; static bool sWarnedAboutWindowInternal; diff --git a/dom/interfaces/notification/nsIDOMDesktopNotification.idl b/dom/interfaces/notification/nsIDOMDesktopNotification.idl index d3c9a69c764..71c40bf02ab 100644 --- a/dom/interfaces/notification/nsIDOMDesktopNotification.idl +++ b/dom/interfaces/notification/nsIDOMDesktopNotification.idl @@ -6,7 +6,7 @@ interface nsIDOMEventListener; interface nsIDOMDesktopNotification; - +interface nsIObserver; [scriptable, uuid(CCEA6185-0A3D-45AB-9058-1004DD4B8C50)] interface nsIDOMDesktopNotificationCenter : nsISupports @@ -25,3 +25,15 @@ interface nsIDOMDesktopNotification : nsISupports [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); +}; diff --git a/dom/src/notification/nsDesktopNotification.cpp b/dom/src/notification/nsDesktopNotification.cpp index 11ede304a4b..eca1c4c50a8 100644 --- a/dom/src/notification/nsDesktopNotification.cpp +++ b/dom/src/notification/nsDesktopNotification.cpp @@ -10,6 +10,9 @@ #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; @@ -27,13 +30,32 @@ NS_IMPL_ISUPPORTS1(AlertServiceObserver, nsIObserver) nsresult nsDOMDesktopNotification::PostDesktopNotification() { + if (!mObserver) + mObserver = new AlertServiceObserver(this); + +#ifdef MOZ_B2G + nsCOMPtr appNotifier = + do_GetService("@mozilla.org/system-alerts-service;1"); + if (appNotifier) { + nsCOMPtr window = GetOwner(); + uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId(); + + if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { + nsCOMPtr 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 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(),