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 @@
+
+
+
+
+
+Bug 967475
+
+
+
+
+
+