diff --git a/b2g/components/AlertsHelper.jsm b/b2g/components/AlertsHelper.jsm index 9260c1066af..0089f381eca 100644 --- a/b2g/components/AlertsHelper.jsm +++ b/b2g/components/AlertsHelper.jsm @@ -144,7 +144,8 @@ let AlertsHelper = { lang: listener.lang, dir: listener.dir, id: listener.id, - tag: listener.tag + tag: listener.tag, + timestamp: listener.timestamp }, Services.io.newURI(listener.target, null, null), Services.io.newURI(listener.manifestURL, null, null) @@ -194,7 +195,7 @@ let AlertsHelper = { }, showNotification: function(imageURL, title, text, textClickable, cookie, - uid, bidi, lang, manifestURL) { + uid, bidi, lang, manifestURL, timestamp) { function send(appName, appIcon) { SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, { type: kDesktopNotification, @@ -206,7 +207,8 @@ let AlertsHelper = { lang: lang, appName: appName, appIcon: appIcon, - manifestURL: manifestURL + manifestURL: manifestURL, + timestamp: timestamp }); } @@ -248,12 +250,13 @@ let AlertsHelper = { lang: details.lang || undefined, id: details.id || undefined, dir: details.dir || undefined, - tag: details.tag || undefined + tag: details.tag || undefined, + timestamp: details.timestamp || undefined }; this.registerAppListener(data.uid, listener); this.showNotification(data.imageURL, data.title, data.text, details.textClickable, null, data.uid, details.dir, - details.lang, details.manifestURL); + details.lang, details.manifestURL, details.timestamp); }, closeAlert: function(name) { diff --git a/b2g/components/AlertsService.js b/b2g/components/AlertsService.js index 96be4593039..b7b2f1a8cd7 100644 --- a/b2g/components/AlertsService.js +++ b/b2g/components/AlertsService.js @@ -43,6 +43,7 @@ const kMessageAlertNotificationSend = "alert-notification-send"; const kMessageAlertNotificationClose = "alert-notification-close"; const kTopicAlertFinished = "alertfinished"; +const kTopicAlertClickCallback = "alertclickcallback"; function AlertsService() { Services.obs.addObserver(this, "xpcom-shutdown", false); @@ -103,7 +104,8 @@ AlertsService.prototype = { id: aDetails.id || undefined, dbId: aDetails.dbId || undefined, dir: aDetails.dir || undefined, - tag: aDetails.tag || undefined + tag: aDetails.tag || undefined, + timestamp: aDetails.timestamp || undefined }; cpmm.sendAsyncMessage(kMessageAppNotificationSend, { @@ -135,6 +137,7 @@ AlertsService.prototype = { // the notification so the app get a change to react. if (data.target) { gSystemMessenger.sendMessage(kNotificationSystemMessageName, { + clicked: (topic === kTopicAlertClickCallback), title: listener.title, body: listener.text, imageURL: listener.imageURL, @@ -142,7 +145,8 @@ AlertsService.prototype = { dir: listener.dir, id: listener.id, tag: listener.tag, - dbId: listener.dbId + dbId: listener.dbId, + timestamp: listener.timestamp }, Services.io.newURI(data.target, null, null), Services.io.newURI(listener.manifestURL, null, null) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 9796a7ed9c7..01d0110f12f 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 2b09417b078..97cde640c56 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + @@ -131,6 +131,6 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 10195a96db5..041d1245d90 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 9796a7ed9c7..01d0110f12f 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index b2eb5d047cf..bc99b81686b 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -18,7 +18,7 @@ - + @@ -118,30 +118,30 @@ - + - + - + - + - + - - + + - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 9b54edb2f7d..c3f039501f7 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "e9a13950c6656cbd21307ff82b8282ed1006c492", + "revision": "95766b5f4b8108d3524431422ccf744e11797497", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 3a163f82e65..f928989fcb1 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 10ee7ede4a8..4fc70fc1cfe 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index c0518cd6927..0de718faccb 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 9aa9f179441..fdbf1c47957 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 895d67edb86..a02db77de70 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -17,7 +17,7 @@ - + @@ -127,7 +127,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 1b9ae7a7f48..dc7674ce54b 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index cc6d99a78af..8a26fc7e293 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -336,6 +336,8 @@ @BINPATH@/components/zipwriter.xpt ; JavaScript components +@BINPATH@/components/ChromeNotifications.js +@BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest @BINPATH@/components/ConsoleAPIStorage.js @BINPATH@/components/BrowserElementParent.manifest diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 8b380fb04de..baa9f3b329e 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -344,6 +344,8 @@ @BINPATH@/components/telemetry.xpt ; JavaScript components +@BINPATH@/components/ChromeNotifications.js +@BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest @BINPATH@/components/ConsoleAPIStorage.js @BINPATH@/components/BrowserElementParent.manifest diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 572099a0d8b..bf9edb95e27 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -14,6 +14,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Assertions.h" +#include "mozilla/Preferences.h" #include "AccessCheck.h" #include "jsfriendapi.h" @@ -2163,7 +2164,8 @@ IsInPrivilegedApp(JSContext* aCx, JSObject* aObj) nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj); uint16_t appStatus = principal->GetAppStatus(); return (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED || - appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED); + appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) || + Preferences::GetBool("dom.ignore_webidl_scope_checks", false); } bool @@ -2175,7 +2177,8 @@ IsInCertifiedApp(JSContext* aCx, JSObject* aObj) } nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj); - return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED; + return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED || + Preferences::GetBool("dom.ignore_webidl_scope_checks", false); } void diff --git a/dom/interfaces/notification/nsINotificationStorage.idl b/dom/interfaces/notification/nsINotificationStorage.idl index a046989222d..da3b89212e1 100644 --- a/dom/interfaces/notification/nsINotificationStorage.idl +++ b/dom/interfaces/notification/nsINotificationStorage.idl @@ -39,7 +39,7 @@ interface nsINotificationStorageCallback : nsISupports /** * Interface for notification persistence layer. */ -[scriptable, uuid(b177b080-2a23-11e3-8224-0800200c9a66)] +[scriptable, uuid(cc4656d7-2a2a-47f1-8016-55891e833d64)] interface nsINotificationStorage : nsISupports { @@ -55,6 +55,10 @@ interface nsINotificationStorage : nsISupports * @param body: the notification body * @param tag: notification tag, will replace any existing * notifications with same origin/tag pair + * @param alertName: the alert identifier as used by system app. + * Stored in the database to avoid re-computing + * it. Built from origin and tag or id depending + * whether there is a tag defined. */ void put(in DOMString origin, in DOMString id, @@ -63,7 +67,8 @@ interface nsINotificationStorage : nsISupports in DOMString lang, in DOMString body, in DOMString tag, - in DOMString icon); + in DOMString icon, + in DOMString alertName); /** * Retrieve a list of notifications. diff --git a/dom/mobilemessage/src/gonk/MmsService.js b/dom/mobilemessage/src/gonk/MmsService.js index 9d0668af209..ca893d49cde 100644 --- a/dom/mobilemessage/src/gonk/MmsService.js +++ b/dom/mobilemessage/src/gonk/MmsService.js @@ -1120,8 +1120,11 @@ function SendTransaction(mmsConnection, cancellableId, msg, requestDeliveryRepor } msg.headers["x-mms-mms-version"] = MMS.MMS_VERSION; - // Let MMS Proxy Relay insert from address automatically for us - msg.headers["from"] = null; + // Insert Phone number if available. + // Otherwise, Let MMS Proxy Relay insert from address automatically for us. + let phoneNumber = mmsConnection.getPhoneNumber(); + msg.headers["from"] = (phoneNumber) ? + { address: phoneNumber, type: "PLMN" } : null; msg.headers["date"] = new Date(); msg.headers["x-mms-message-class"] = "personal"; @@ -1416,7 +1419,11 @@ function ReadRecTransaction(mmsConnection, messageID, toAddress) { let to = {address: toAddress, type: type} headers["to"] = to; - headers["from"] = null; + // Insert Phone number if available. + // Otherwise, Let MMS Proxy Relay insert from address automatically for us. + let phoneNumber = mmsConnection.getPhoneNumber(); + headers["from"] = (phoneNumber) ? + { address: phoneNumber, type: "PLMN" } : null; headers["x-mms-read-status"] = MMS.MMS_PDU_READ_STATUS_READ; this.istream = MMS.PduHelper.compose(null, {headers: headers}); diff --git a/dom/src/notification/ChromeNotifications.js b/dom/src/notification/ChromeNotifications.js new file mode 100644 index 00000000000..11e0af818e3 --- /dev/null +++ b/dom/src/notification/ChromeNotifications.js @@ -0,0 +1,113 @@ +/* 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/. */ + +"use strict"; + +const DEBUG = false; + +function debug(s) { + dump("-*- ChromeNotifications.js: " + s + "\n"); +} + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); + +XPCOMUtils.defineLazyServiceGetter(this, "appNotifier", + "@mozilla.org/system-alerts-service;1", + "nsIAppNotificationService"); + +const CHROMENOTIFICATIONS_CID = "{74f94093-8b37-497e-824f-c3b250a911da}"; +const CHROMENOTIFICATIONS_CONTRACTID = "@mozilla.org/mozChromeNotifications;1"; + +function ChromeNotifications() { + this.innerWindowID = null; + this.resendCallback = null; +} + +ChromeNotifications.prototype = { + + init: function(aWindow) { + let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + this.innerWindowID = util.currentInnerWindowID; + Services.obs.addObserver(this, "inner-window-destroyed", false); + cpmm.addMessageListener("Notification:GetAllCrossOrigin:Return:OK", this); + }, + + performResend: function(notifications) { + let resentNotifications = 0; + + notifications.forEach(function(notification) { + appNotifier.showAppNotification( + notification.icon, + notification.title, + notification.body, + null, + { + manifestURL: notification.origin, + id: notification.alertName, + dir: notification.dir, + lang: notification.lang, + tag: notification.tag, + dbId: notification.id, + timestamp: notification.timestamp + } + ); + resentNotifications++; + }); + + try { + this.resendCallback && this.resendCallback(resentNotifications); + } catch (ex) { + if (DEBUG) debug("Content sent exception: " + ex); + } + }, + + mozResendAllNotifications: function(resendCallback) { + this.resendCallback = resendCallback; + cpmm.sendAsyncMessage("Notification:GetAllCrossOrigin", {}); + }, + + receiveMessage: function(message) { + switch (message.name) { + case "Notification:GetAllCrossOrigin:Return:OK": + this.performResend(message.data.notifications); + break; + + default: + if (DEBUG) { debug("Unrecognized message: " + message.name); } + break; + } + }, + + observe: function(aSubject, aTopic, aData) { + if (DEBUG) debug("Topic: " + aTopic); + if (aTopic == "inner-window-destroyed") { + let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (wId != this.innerWindowID) { + return; + } + Services.obs.removeObserver(this, "inner-window-destroyed"); + cpmm.removeMessageListener("Notification:GetAllCrossOrigin:Return:OK", this); + } + }, + + classID : Components.ID(CHROMENOTIFICATIONS_CID), + contractID : CHROMENOTIFICATIONS_CONTRACTID, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIChromeNotifications, + Ci.nsIDOMGlobalPropertyInitializer, + Ci.nsIObserver, + Ci.nsIMessageListener]), +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeNotifications]); diff --git a/dom/src/notification/ChromeNotifications.manifest b/dom/src/notification/ChromeNotifications.manifest new file mode 100644 index 00000000000..74e93107ffd --- /dev/null +++ b/dom/src/notification/ChromeNotifications.manifest @@ -0,0 +1,3 @@ +# ChromeNotifications.js +component {74f94093-8b37-497e-824f-c3b250a911da} ChromeNotifications.js +contract @mozilla.org/mozChromeNotifications;1 {74f94093-8b37-497e-824f-c3b250a911da} diff --git a/dom/src/notification/Notification.cpp b/dom/src/notification/Notification.cpp index 047699c4884..157203bf870 100644 --- a/dom/src/notification/Notification.cpp +++ b/dom/src/notification/Notification.cpp @@ -423,6 +423,22 @@ Notification::Notification(const nsAString& aID, const nsAString& aTitle, const mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false) { + nsAutoString alertName; + DebugOnly rv = GetOrigin(GetOwner(), alertName); + MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed"); + + // Get the notification name that is unique per origin + tag/ID. + // The name of the alert is of the form origin#tag/ID. + alertName.AppendLiteral("#"); + if (!mTag.IsEmpty()) { + alertName.Append(NS_LITERAL_STRING("tag:")); + alertName.Append(mTag); + } else { + alertName.Append(NS_LITERAL_STRING("notag:")); + alertName.Append(mID); + } + + mAlertName = alertName; } // static @@ -462,6 +478,10 @@ Notification::Constructor(const GlobalObject& aGlobal, nsString id; notification->GetID(id); + + nsString alertName; + notification->GetAlertName(alertName); + aRv = notificationStorage->Put(origin, id, aTitle, @@ -469,7 +489,8 @@ Notification::Constructor(const GlobalObject& aGlobal, aOptions.mLang, aOptions.mBody, aOptions.mTag, - aOptions.mIcon); + aOptions.mIcon, + alertName); if (aRv.Failed()) { return nullptr; } @@ -557,10 +578,6 @@ Notification::ShowInternal() nsCOMPtr observer = new NotificationObserver(this); - nsString alertName; - rv = GetAlertName(alertName); - NS_ENSURE_SUCCESS_VOID(rv); - #ifdef MOZ_B2G nsCOMPtr appNotifier = do_GetService("@mozilla.org/system-alerts-service;1"); @@ -578,7 +595,7 @@ Notification::ShowInternal() AppNotificationServiceOptions ops; ops.mTextClickable = true; ops.mManifestURL = manifestUrl; - ops.mId = alertName; + ops.mId = mAlertName; ops.mDbId = mID; ops.mDir = DirectionToString(mDir); ops.mLang = mLang; @@ -602,7 +619,7 @@ Notification::ShowInternal() nsString uniqueCookie = NS_LITERAL_STRING("notification:"); uniqueCookie.AppendInt(sCount++); alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true, - uniqueCookie, observer, alertName, + uniqueCookie, observer, mAlertName, DirectionToString(mDir), mLang, GetPrincipal()); } @@ -771,10 +788,8 @@ Notification::CloseInternal() nsCOMPtr alertService = do_GetService(NS_ALERTSERVICE_CONTRACTID); if (alertService) { - nsString alertName; - rv = GetAlertName(alertName); if (NS_SUCCEEDED(rv)) { - alertService->CloseAlert(alertName, GetPrincipal()); + alertService->CloseAlert(mAlertName, GetPrincipal()); } } } @@ -812,24 +827,6 @@ Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin) return NS_OK; } -nsresult -Notification::GetAlertName(nsString& aAlertName) -{ - // Get the notification name that is unique per origin + tag/ID. - // The name of the alert is of the form origin#tag/ID. - nsresult rv = GetOrigin(GetOwner(), aAlertName); - NS_ENSURE_SUCCESS(rv, rv); - aAlertName.AppendLiteral("#"); - if (!mTag.IsEmpty()) { - aAlertName.Append(NS_LITERAL_STRING("tag:")); - aAlertName.Append(mTag); - } else { - aAlertName.Append(NS_LITERAL_STRING("notag:")); - aAlertName.Append(mID); - } - return NS_OK; -} - } // namespace dom } // namespace mozilla diff --git a/dom/src/notification/Notification.h b/dom/src/notification/Notification.h index cd5de4d69d9..ae83174b7d4 100644 --- a/dom/src/notification/Notification.h +++ b/dom/src/notification/Notification.h @@ -133,7 +133,10 @@ protected: static nsresult GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin); - nsresult GetAlertName(nsString& aAlertName); + void GetAlertName(nsAString& aRetval) + { + aRetval = mAlertName; + } nsString mID; nsString mTitle; @@ -143,6 +146,8 @@ protected: nsString mTag; nsString mIconUrl; + nsString mAlertName; + bool mIsClosed; static uint32_t sCount; diff --git a/dom/src/notification/NotificationDB.jsm b/dom/src/notification/NotificationDB.jsm index f082176f51d..5a53dbe6d28 100644 --- a/dom/src/notification/NotificationDB.jsm +++ b/dom/src/notification/NotificationDB.jsm @@ -16,6 +16,9 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm"); + XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageListenerManager"); @@ -33,8 +36,23 @@ const NOTIFICATION_STORE_DIR = OS.Constants.Path.profileDir; const NOTIFICATION_STORE_PATH = OS.Path.join(NOTIFICATION_STORE_DIR, "notificationstore.json"); +const kMessages = [ + "Notification:Save", + "Notification:Delete", + "Notification:GetAll", + "Notification:GetAllCrossOrigin" +]; + let NotificationDB = { + + // Ensure we won't call init() while xpcom-shutdown is performed + _shutdownInProgress: false, + init: function() { + if (this._shutdownInProgress) { + return; + } + this.notifications = {}; this.byTag = {}; this.loaded = false; @@ -42,9 +60,29 @@ let NotificationDB = { this.tasks = []; // read/write operation queue this.runningTask = false; - ppmm.addMessageListener("Notification:Save", this); - ppmm.addMessageListener("Notification:Delete", this); - ppmm.addMessageListener("Notification:GetAll", this); + Services.obs.addObserver(this, "xpcom-shutdown", false); + this.registerListeners(); + }, + + registerListeners: function() { + for (let message of kMessages) { + ppmm.addMessageListener(message, this); + } + }, + + unregisterListeners: function() { + for (let message of kMessages) { + ppmm.removeMessageListener(message, this); + } + }, + + observe: function(aSubject, aTopic, aData) { + if (DEBUG) debug("Topic: " + aTopic); + if (aTopic == "xpcom-shutdown") { + this._shutdownInProgress = true; + Services.obs.removeObserver(this, "xpcom-shutdown"); + this.unregisterListeners(); + } }, // Attempt to read notification file, if it's not there we will create it. @@ -160,6 +198,15 @@ let NotificationDB = { }); break; + case "Notification:GetAllCrossOrigin": + this.queueTask("getallaccrossorigin", message.data, + function(notifications) { + returnMessage("Notification:GetAllCrossOrigin:Return:OK", { + notifications: notifications + }); + }); + break; + case "Notification:Save": this.queueTask("save", message.data, function() { returnMessage("Notification:Save:Return:OK", { @@ -193,14 +240,14 @@ let NotificationDB = { // Only run immediately if we aren't currently running another task. if (!this.runningTask) { - if (DEBUG) { dump("Task queue was not running, starting now..."); } + if (DEBUG) { debug("Task queue was not running, starting now..."); } this.runNextTask(); } }, runNextTask: function() { if (this.tasks.length === 0) { - if (DEBUG) { dump("No more tasks to run, queue depleted"); } + if (DEBUG) { debug("No more tasks to run, queue depleted"); } this.runningTask = false; return; } @@ -223,6 +270,10 @@ let NotificationDB = { this.taskGetAll(task.data, wrappedCallback); break; + case "getallaccrossorigin": + this.taskGetAllCrossOrigin(wrappedCallback); + break; + case "save": this.taskSave(task.data, wrappedCallback); break; @@ -245,6 +296,27 @@ let NotificationDB = { callback(notifications); }, + taskGetAllCrossOrigin: function(callback) { + if (DEBUG) { debug("Task, getting all whatever origin"); } + var notifications = []; + for (var origin in this.notifications) { + for (var i in this.notifications[origin]) { + var notification = this.notifications[origin][i]; + + // Notifications without the alertName field cannot be resent by + // mozResendAllNotifications, so we just skip them. They will + // still be available to applications via Notification.get() + if (!('alertName' in notification)) { + continue; + } + + notification.origin = origin; + notifications.push(notification); + } + } + callback(notifications); + }, + taskSave: function(data, callback) { if (DEBUG) { debug("Task, saving"); } var origin = data.origin; diff --git a/dom/src/notification/NotificationStorage.js b/dom/src/notification/NotificationStorage.js index b8b86ea2811..a241a52158b 100644 --- a/dom/src/notification/NotificationStorage.js +++ b/dom/src/notification/NotificationStorage.js @@ -36,7 +36,7 @@ function NotificationStorage() { NotificationStorage.prototype = { - put: function(origin, id, title, dir, lang, body, tag, icon) { + put: function(origin, id, title, dir, lang, body, tag, icon, alertName) { if (DEBUG) { debug("PUT: " + id + ": " + title); } var notification = { id: id, @@ -45,7 +45,9 @@ NotificationStorage.prototype = { lang: lang, body: body, tag: tag, - icon: icon + icon: icon, + alertName: alertName, + timestamp: new Date().getTime() }; this._notifications[id] = notification; diff --git a/dom/src/notification/moz.build b/dom/src/notification/moz.build index 552eb3af0c9..45c29b89ee3 100644 --- a/dom/src/notification/moz.build +++ b/dom/src/notification/moz.build @@ -5,6 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXTRA_COMPONENTS += [ + 'ChromeNotifications.js', + 'ChromeNotifications.manifest', 'NotificationStorage.js', 'NotificationStorage.manifest', ] diff --git a/dom/src/notification/test/unit/test_notificationdb.js b/dom/src/notification/test/unit/test_notificationdb.js index b274b48606b..171324a174c 100644 --- a/dom/src/notification/test/unit/test_notificationdb.js +++ b/dom/src/notification/test/unit/test_notificationdb.js @@ -336,3 +336,116 @@ add_test(function test_send_two_get_two() { requestID: (requestID + 1) // 21 }); }); + +// Cleanup previous notification +add_test(function test_delete_previous() { + let requestID = 25; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function(message) { + do_check_eq(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: "{8ef9a628-f0f4-44b4-820d-c117573c33e3}", + requestID: requestID + }); +}); + +// Store two notifications, one without alertName and one with +add_test(function test_send_two_alertName() { + let requestID = 30; + let notifications = [ + { + origin: "app://system.gaiamobile.org/manifest.webapp", + id: "{27ead857-4f43-457f-a770-93b82fbfc223}", + title: "Notification title", + dir: "auto", + lang: "", + body: "Notification body", + tag: "", + icon: "icon.png", + timestamp: new Date().getTime() + }, { + origin: "app://system.gaiamobile.org/manifest.webapp", + id: "{40275e04-58d0-47be-8cc7-540578f793a4}", + title: "Notification title", + dir: "auto", + lang: "", + body: "Notification body", + tag: "", + icon: "icon.png", + alertName: "alertName", + timestamp: new Date().getTime() + } + ]; + let origin = notifications[0].origin; + + let msgGetCrossOriginReply = "Notification:GetAllCrossOrigin:Return:OK"; + let msgGetCrossOriginHandler = { + receiveMessage: function(message) { + if (message.name === msgGetCrossOriginReply) { + cpmm.removeMessageListener( + msgGetCrossOriginReply, msgGetCrossOriginHandler); + + let gotNotifications = message.data.notifications; + + // we expect to have one notification + do_check_eq(1, gotNotifications.length); + + // compare the only notification we should have got back + compareNotification(gotNotifications[0], notifications[1]); + + run_next_test(); + } + } + }; + cpmm.addMessageListener(msgGetCrossOriginReply, msgGetCrossOriginHandler); + + let msgGetReply = "Notification:GetAll:Return:OK"; + let msgGetHandler = { + receiveMessage: function(message) { + if (message.name === msgGetReply) { + cpmm.removeMessageListener(msgGetReply, msgGetHandler); + + let gotNotifications = message.data.notifications; + + // we expect to have two notifications + do_check_eq(2, gotNotifications.length); + + // compare each notification + for (let i = 0; i < gotNotifications.length; i++) { + compareNotification(gotNotifications[i], notifications[i]); + } + + run_next_test(); + } + } + }; + cpmm.addMessageListener(msgGetReply, msgGetHandler); + + let msgSaveReply = "Notification:Save:Return:OK"; + let msgSaveCalls = 0; + let msgSaveHandler = { + receiveMessage: function(message) { + if (message.name === msgSaveReply) { + msgSaveCalls++; + if (msgSaveCalls === 2) { + cpmm.removeMessageListener(msgSaveReply, msgSaveHandler); + // Trigger getall + cpmm.sendAsyncMessage("Notification:GetAll", { + origin: origin + }); + } + } + } + }; + cpmm.addMessageListener(msgSaveReply, msgSaveHandler); + + notifications.forEach(function(n) { + cpmm.sendAsyncMessage("Notification:Save", { + origin: origin, + notification: n + }); + }); +}); diff --git a/dom/tests/mochitest/notification/MockServices.js b/dom/tests/mochitest/notification/MockServices.js index 164cc7e9a91..28c5f7750ae 100644 --- a/dom/tests/mochitest/notification/MockServices.js +++ b/dom/tests/mochitest/notification/MockServices.js @@ -12,41 +12,66 @@ var MockServices = (function () { var registrar = SpecialPowers.wrap(SpecialPowers.Components).manager .QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar); - var activeNotifications = Object.create(null); + var activeAlertNotifications = Object.create(null); + + var activeAppNotifications = Object.create(null); var mockAlertsService = { showAlertNotification: function(imageUrl, title, text, textClickable, cookie, alertListener, name) { var listener = SpecialPowers.wrap(alertListener); - activeNotifications[name] = { + activeAlertNotifications[name] = { listener: listener, cookie: cookie }; // fake async alert show event - setTimeout(function () { - listener.observe(null, "alertshow", cookie); - }, 100); + if (listener) { + setTimeout(function () { + listener.observe(null, "alertshow", cookie); + }, 100); + } // ?? SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", cookie); }, - showAppNotification: function(imageUrl, title, text, textClickable, - manifestURL, alertListener, name) { - this.showAlertNotification(imageUrl, title, text, textClickable, "", alertListener, name); + showAppNotification: function(aImageUrl, aTitle, aText, aAlertListener, aDetails) { + var listener = aAlertListener || (activeAlertNotifications[aDetails.id] ? activeAlertNotifications[aDetails.id].listener : undefined); + activeAppNotifications[aDetails.id] = { + observer: listener, + title: aTitle, + text: aText, + manifestURL: aDetails.manifestURL, + imageURL: aImageUrl, + lang: aDetails.lang || undefined, + id: aDetails.id || undefined, + dbId: aDetails.dbId || undefined, + dir: aDetails.dir || undefined, + tag: aDetails.tag || undefined, + timestamp: aDetails.timestamp || undefined + }; + this.showAlertNotification(aImageUrl, aTitle, aText, true, "", listener, aDetails.id); }, closeAlert: function(name) { - var notification = activeNotifications[name]; - if (notification) { - notification.listener.observe(null, "alertfinished", notification.cookie); - delete activeNotifications[name]; + var alertNotification = activeAlertNotifications[name]; + if (alertNotification) { + if (alertNotification.listener) { + alertNotification.listener.observe(null, "alertfinished", alertNotification.cookie); + } + delete activeAlertNotifications[name]; + } + + var appNotification = activeAppNotifications[name]; + if (appNotification) { + delete activeAppNotifications[name]; } }, QueryInterface: function(aIID) { if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) || - SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)) { + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService) || + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAppNotificationService)) { return this; } throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE; @@ -77,5 +102,8 @@ var MockServices = (function () { registrar.unregisterFactory(MOCK_ALERTS_CID, mockAlertsService); registrar.unregisterFactory(MOCK_SYSTEM_ALERTS_CID, mockAlertsService); }, + + activeAlertNotifications: activeAlertNotifications, + activeAppNotifications: activeAppNotifications, }; })(); diff --git a/dom/tests/mochitest/notification/mochitest.ini b/dom/tests/mochitest/notification/mochitest.ini index ac54da83579..89d4e45169c 100644 --- a/dom/tests/mochitest/notification/mochitest.ini +++ b/dom/tests/mochitest/notification/mochitest.ini @@ -9,3 +9,4 @@ support-files = skip-if = (toolkit == 'gonk' && debug) #debug-only timeout [test_bug931307.html] skip-if = (toolkit == 'gonk' && debug) #debug-only timeout +[test_notification_resend.html] diff --git a/dom/tests/mochitest/notification/test_bug931307.html b/dom/tests/mochitest/notification/test_bug931307.html index 9477b1ea621..03030114fa7 100644 --- a/dom/tests/mochitest/notification/test_bug931307.html +++ b/dom/tests/mochitest/notification/test_bug931307.html @@ -13,15 +13,17 @@