Bug 899585 - Add custom data support in the Notification constructor r=baku

This commit is contained in:
Robert Bindar 2014-08-20 17:56:12 -07:00
Родитель 10638101c5
Коммит 29ee85a120
26 изменённых файлов: 319 добавлений и 56 удалений

Просмотреть файл

@ -149,7 +149,8 @@ let AlertsHelper = {
dir: listener.dir, dir: listener.dir,
id: listener.id, id: listener.id,
tag: listener.tag, tag: listener.tag,
timestamp: listener.timestamp timestamp: listener.timestamp,
data: listener.dataObj
}, },
Services.io.newURI(listener.target, null, null), Services.io.newURI(listener.target, null, null),
Services.io.newURI(listener.manifestURL, null, null) Services.io.newURI(listener.manifestURL, null, null)
@ -199,8 +200,32 @@ let AlertsHelper = {
}); });
}, },
deserializeStructuredClone: function(dataString) {
if (!dataString) {
return null;
}
let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
// The maximum supported structured-clone serialization format version
// as defined in "js/public/StructuredClone.h"
let JS_STRUCTURED_CLONE_VERSION = 4;
scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
let dataObj = scContainer.deserializeToVariant();
// We have to check whether dataObj contains DOM objects (supported by
// nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
// After the structured clone callback systems will be unified, we'll not
// have to perform this check anymore.
try {
let data = Cu.cloneInto(dataObj, {});
} catch(e) { dataObj = null; }
return dataObj;
},
showNotification: function(imageURL, title, text, textClickable, cookie, showNotification: function(imageURL, title, text, textClickable, cookie,
uid, bidi, lang, manifestURL, timestamp) { uid, bidi, lang, dataObj, manifestURL, timestamp) {
function send(appName, appIcon) { function send(appName, appIcon) {
SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, { SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
type: kDesktopNotification, type: kDesktopNotification,
@ -213,7 +238,8 @@ let AlertsHelper = {
appName: appName, appName: appName,
appIcon: appIcon, appIcon: appIcon,
manifestURL: manifestURL, manifestURL: manifestURL,
timestamp: timestamp timestamp: timestamp,
data: dataObj
}); });
} }
@ -238,15 +264,17 @@ let AlertsHelper = {
currentListener.observer.observe(null, kTopicAlertFinished, currentListener.cookie); currentListener.observer.observe(null, kTopicAlertFinished, currentListener.cookie);
} }
let dataObj = this.deserializeStructuredClone(data.dataStr);
this.registerListener(data.name, data.cookie, data.alertListener); this.registerListener(data.name, data.cookie, data.alertListener);
this.showNotification(data.imageURL, data.title, data.text, this.showNotification(data.imageURL, data.title, data.text,
data.textClickable, data.cookie, data.name, data.bidi, data.textClickable, data.cookie, data.name, data.bidi,
data.lang, null); data.lang, dataObj, null);
}, },
showAppNotification: function(aMessage) { showAppNotification: function(aMessage) {
let data = aMessage.data; let data = aMessage.data;
let details = data.details; let details = data.details;
let dataObject = this.deserializeStructuredClone(details.data);
let listener = { let listener = {
mm: aMessage.target, mm: aMessage.target,
title: data.title, title: data.title,
@ -257,12 +285,14 @@ let AlertsHelper = {
id: details.id || undefined, id: details.id || undefined,
dir: details.dir || undefined, dir: details.dir || undefined,
tag: details.tag || undefined, tag: details.tag || undefined,
timestamp: details.timestamp || undefined timestamp: details.timestamp || undefined,
dataObj: dataObject || undefined
}; };
this.registerAppListener(data.uid, listener); this.registerAppListener(data.uid, listener);
this.showNotification(data.imageURL, data.title, data.text, this.showNotification(data.imageURL, data.title, data.text,
details.textClickable, null, data.uid, details.dir, details.textClickable, null, data.uid, details.dir,
details.lang, details.manifestURL, details.timestamp); details.lang, dataObject, details.manifestURL,
details.timestamp);
}, },
closeAlert: function(name) { closeAlert: function(name) {

Просмотреть файл

@ -69,7 +69,7 @@ AlertsService.prototype = {
// nsIAlertsService // nsIAlertsService
showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable, showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
aCookie, aAlertListener, aName, aBidi, aCookie, aAlertListener, aName, aBidi,
aLang) { aLang, aDataStr) {
cpmm.sendAsyncMessage(kMessageAlertNotificationSend, { cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
imageURL: aImageUrl, imageURL: aImageUrl,
title: aTitle, title: aTitle,
@ -79,7 +79,8 @@ AlertsService.prototype = {
listener: aAlertListener, listener: aAlertListener,
id: aName, id: aName,
dir: aBidi, dir: aBidi,
lang: aLang lang: aLang,
dataStr: aDataStr
}); });
}, },
@ -95,6 +96,7 @@ AlertsService.prototype = {
let uid = (aDetails.id == "") ? let uid = (aDetails.id == "") ?
"app-notif-" + uuidGenerator.generateUUID() : aDetails.id; "app-notif-" + uuidGenerator.generateUUID() : aDetails.id;
let dataObj = this.deserializeStructuredClone(aDetails.data);
this._listeners[uid] = { this._listeners[uid] = {
observer: aAlertListener, observer: aAlertListener,
title: aTitle, title: aTitle,
@ -106,7 +108,8 @@ AlertsService.prototype = {
dbId: aDetails.dbId || undefined, dbId: aDetails.dbId || undefined,
dir: aDetails.dir || undefined, dir: aDetails.dir || undefined,
tag: aDetails.tag || undefined, tag: aDetails.tag || undefined,
timestamp: aDetails.timestamp || undefined timestamp: aDetails.timestamp || undefined,
dataObj: dataObj || undefined
}; };
cpmm.sendAsyncMessage(kMessageAppNotificationSend, { cpmm.sendAsyncMessage(kMessageAppNotificationSend, {
@ -151,7 +154,8 @@ AlertsService.prototype = {
id: listener.id, id: listener.id,
tag: listener.tag, tag: listener.tag,
dbId: listener.dbId, dbId: listener.dbId,
timestamp: listener.timestamp timestamp: listener.timestamp,
data: listener.dataObj || undefined,
}, },
Services.io.newURI(data.target, null, null), Services.io.newURI(data.target, null, null),
Services.io.newURI(listener.manifestURL, null, null) Services.io.newURI(listener.manifestURL, null, null)
@ -167,6 +171,30 @@ AlertsService.prototype = {
} }
delete this._listeners[data.uid]; delete this._listeners[data.uid];
} }
},
deserializeStructuredClone: function(dataString) {
if (!dataString) {
return null;
}
let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
// The maximum supported structured-clone serialization format version
// as defined in "js/public/StructuredClone.h"
let JS_STRUCTURED_CLONE_VERSION = 4;
scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
let dataObj = scContainer.deserializeToVariant();
// We have to check whether dataObj contains DOM objects (supported by
// nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
// After the structured clone callback systems will be unified, we'll not
// have to perform this check anymore.
try {
let data = Cu.cloneInto(dataObj, {});
} catch(e) { dataObj = null; }
return dataObj;
} }
}; };

Просмотреть файл

@ -46,7 +46,7 @@ interface nsIStructuredCloneContainer : nsISupports
void initFromBase64(in AString aData,in unsigned long aFormatVersion); void initFromBase64(in AString aData,in unsigned long aFormatVersion);
/** /**
* Deserialize the object this conatiner holds, returning it wrapped as * Deserialize the object this container holds, returning it wrapped as
* an nsIVariant. * an nsIVariant.
*/ */
[implicit_jscontext] [implicit_jscontext]

Просмотреть файл

@ -4,7 +4,7 @@
#include "domstubs.idl" #include "domstubs.idl"
[scriptable, uuid(fb089720-1c5c-11e3-b773-0800200c9a66)] [scriptable, uuid(9b12f566-2c7f-48ef-990d-e5092a71d11e)]
interface nsINotificationStorageCallback : nsISupports interface nsINotificationStorageCallback : nsISupports
{ {
/** /**
@ -26,7 +26,8 @@ interface nsINotificationStorageCallback : nsISupports
in DOMString lang, in DOMString lang,
in DOMString body, in DOMString body,
in DOMString tag, in DOMString tag,
in DOMString icon); in DOMString icon,
in DOMString data);
/** /**
* Callback function used to notify C++ the we have returned * Callback function used to notify C++ the we have returned
@ -39,7 +40,7 @@ interface nsINotificationStorageCallback : nsISupports
/** /**
* Interface for notification persistence layer. * Interface for notification persistence layer.
*/ */
[scriptable, uuid(cc4656d7-2a2a-47f1-8016-55891e833d64)] [scriptable, uuid(1be733d9-d614-43f2-9fd4-8f573a33b215)]
interface nsINotificationStorage : nsISupports interface nsINotificationStorage : nsISupports
{ {
@ -68,7 +69,8 @@ interface nsINotificationStorage : nsISupports
in DOMString body, in DOMString body,
in DOMString tag, in DOMString tag,
in DOMString icon, in DOMString icon,
in DOMString alertName); in DOMString alertName,
in DOMString data);
/** /**
* Retrieve a list of notifications. * Retrieve a list of notifications.

Просмотреть файл

@ -3361,6 +3361,7 @@ ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsStri
const nsString& aText, const bool& aTextClickable, const nsString& aText, const bool& aTextClickable,
const nsString& aCookie, const nsString& aName, const nsString& aCookie, const nsString& aName,
const nsString& aBidi, const nsString& aLang, const nsString& aBidi, const nsString& aLang,
const nsString& aData,
const IPC::Principal& aPrincipal) const IPC::Principal& aPrincipal)
{ {
#ifdef MOZ_CHILD_PERMISSIONS #ifdef MOZ_CHILD_PERMISSIONS
@ -3374,7 +3375,8 @@ ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsStri
nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
if (sysAlerts) { if (sysAlerts) {
sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable, sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable,
aCookie, this, aName, aBidi, aLang, aPrincipal); aCookie, this, aName, aBidi, aLang,
aData, aPrincipal);
} }
return true; return true;
} }

Просмотреть файл

@ -530,6 +530,7 @@ private:
const nsString& aText, const bool& aTextClickable, const nsString& aText, const bool& aTextClickable,
const nsString& aCookie, const nsString& aName, const nsString& aCookie, const nsString& aName,
const nsString& aBidi, const nsString& aLang, const nsString& aBidi, const nsString& aLang,
const nsString& aData,
const IPC::Principal& aPrincipal) MOZ_OVERRIDE; const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
virtual bool RecvCloseAlert(const nsString& aName, virtual bool RecvCloseAlert(const nsString& aName,

Просмотреть файл

@ -542,6 +542,7 @@ parent:
nsString name, nsString name,
nsString bidi, nsString bidi,
nsString lang, nsString lang,
nsString data,
Principal principal); Principal principal);
CloseAlert(nsString name, Principal principal); CloseAlert(nsString name, Principal principal);

Просмотреть файл

@ -60,7 +60,8 @@ ChromeNotifications.prototype = {
lang: notification.lang, lang: notification.lang,
tag: notification.tag, tag: notification.tag,
dbId: notification.id, dbId: notification.id,
timestamp: notification.timestamp timestamp: notification.timestamp,
data: notification.data
} }
); );
resentNotifications++; resentNotifications++;

Просмотреть файл

@ -111,6 +111,7 @@ DesktopNotification::PostDesktopNotification()
uniqueName, uniqueName,
NS_LITERAL_STRING("auto"), NS_LITERAL_STRING("auto"),
EmptyString(), EmptyString(),
EmptyString(),
principal); principal);
} }

Просмотреть файл

@ -4,6 +4,7 @@
#include "mozilla/dom/Notification.h" #include "mozilla/dom/Notification.h"
#include "mozilla/dom/AppNotificationServiceOptionsBinding.h" #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/OwningNonNull.h" #include "mozilla/dom/OwningNonNull.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
@ -16,10 +17,12 @@
#include "nsIPermissionManager.h" #include "nsIPermissionManager.h"
#include "nsIUUIDGenerator.h" #include "nsIUUIDGenerator.h"
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsStructuredCloneContainer.h"
#include "nsToolkitCompsCID.h" #include "nsToolkitCompsCID.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "nsDOMJSUtils.h" #include "nsDOMJSUtils.h"
#include "nsIScriptSecurityManager.h" #include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
#include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "nsContentPermissionHelper.h" #include "nsContentPermissionHelper.h"
@ -57,11 +60,12 @@ public:
const nsAString& aBody, const nsAString& aBody,
const nsAString& aTag, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData,
JSContext* aCx) JSContext* aCx)
{ {
MOZ_ASSERT(!aID.IsEmpty()); MOZ_ASSERT(!aID.IsEmpty());
NotificationOptions options; RootedDictionary<NotificationOptions> options(aCx);
options.mDir = Notification::StringToDirection(nsString(aDir)); options.mDir = Notification::StringToDirection(nsString(aDir));
options.mLang = aLang; options.mLang = aLang;
options.mBody = aBody; options.mBody = aBody;
@ -71,6 +75,12 @@ public:
aID, aID,
aTitle, aTitle,
options); options);
ErrorResult rv;
notification->InitFromBase64(aCx, aData, rv);
if (rv.Failed()) {
return rv.ErrorCode();
}
JSAutoCompartment ac(aCx, mGlobal); JSAutoCompartment ac(aCx, mGlobal);
JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx)); JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx));
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
@ -371,7 +381,7 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
NotificationDirection aDir, const nsAString& aLang, NotificationDirection aDir, const nsAString& aLang,
const nsAString& aTag, const nsAString& aIconUrl, const nsAString& aTag, const nsAString& aIconUrl,
nsPIDOMWindow* aWindow) nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow), : DOMEventTargetHelper(aWindow),
mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false) mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
@ -404,11 +414,19 @@ Notification::Constructor(const GlobalObject& aGlobal,
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window, "Window should not be null."); MOZ_ASSERT(window, "Window should not be null.");
nsRefPtr<Notification> notification = CreateInternal(window, nsRefPtr<Notification> notification = CreateInternal(window,
EmptyString(), EmptyString(),
aTitle, aTitle,
aOptions); aOptions);
// Make a structured clone of the aOptions.mData object
JS::Rooted<JS::Value> data(aGlobal.Context(), aOptions.mData);
notification->InitFromJSVal(aGlobal.Context(), data, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Queue a task to show the notification. // Queue a task to show the notification.
nsCOMPtr<nsIRunnable> showNotificationTask = nsCOMPtr<nsIRunnable> showNotificationTask =
new NotificationTask(notification, NotificationTask::eShow); new NotificationTask(notification, NotificationTask::eShow);
@ -435,6 +453,13 @@ Notification::Constructor(const GlobalObject& aGlobal,
nsString alertName; nsString alertName;
notification->GetAlertName(alertName); notification->GetAlertName(alertName);
nsString dataString;
nsCOMPtr<nsIStructuredCloneContainer> scContainer;
scContainer = notification->GetDataCloneContainer();
if (scContainer) {
scContainer->GetDataAsBase64(dataString);
}
aRv = notificationStorage->Put(origin, aRv = notificationStorage->Put(origin,
id, id,
aTitle, aTitle,
@ -443,7 +468,9 @@ Notification::Constructor(const GlobalObject& aGlobal,
aOptions.mBody, aOptions.mBody,
aOptions.mTag, aOptions.mTag,
aOptions.mIcon, aOptions.mIcon,
alertName); alertName,
dataString);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }
@ -481,10 +508,29 @@ Notification::CreateInternal(nsPIDOMWindow* aWindow,
aOptions.mLang, aOptions.mLang,
aOptions.mTag, aOptions.mTag,
aOptions.mIcon, aOptions.mIcon,
aWindow); aWindow);
return notification.forget(); return notification.forget();
} }
Notification::~Notification() {}
NS_IMPL_CYCLE_COLLECTION_CLASS(Notification)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataObjectContainer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataObjectContainer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
nsIPrincipal* nsIPrincipal*
Notification::GetPrincipal() Notification::GetPrincipal()
{ {
@ -531,6 +577,13 @@ Notification::ShowInternal()
nsCOMPtr<nsIObserver> observer = new NotificationObserver(this); nsCOMPtr<nsIObserver> observer = new NotificationObserver(this);
// mDataObjectContainer might be uninitialized here because the notification
// was constructed with an undefined data property.
nsString dataStr;
if (mDataObjectContainer) {
mDataObjectContainer->GetDataAsBase64(dataStr);
}
#ifdef MOZ_B2G #ifdef MOZ_B2G
nsCOMPtr<nsIAppNotificationService> appNotifier = nsCOMPtr<nsIAppNotificationService> appNotifier =
do_GetService("@mozilla.org/system-alerts-service;1"); do_GetService("@mozilla.org/system-alerts-service;1");
@ -553,6 +606,7 @@ Notification::ShowInternal()
ops.mDir = DirectionToString(mDir); ops.mDir = DirectionToString(mDir);
ops.mLang = mLang; ops.mLang = mLang;
ops.mTag = mTag; ops.mTag = mTag;
ops.mData = dataStr;
if (!ToJSValue(cx, ops, &val)) { if (!ToJSValue(cx, ops, &val)) {
NS_WARNING("Converting dict to object failed!"); NS_WARNING("Converting dict to object failed!");
@ -574,7 +628,7 @@ Notification::ShowInternal()
alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true, alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
uniqueCookie, observer, mAlertName, uniqueCookie, observer, mAlertName,
DirectionToString(mDir), mLang, DirectionToString(mDir), mLang,
GetPrincipal()); dataStr, GetPrincipal());
} }
void void
@ -780,6 +834,54 @@ Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
return NS_OK; return NS_OK;
} }
nsIStructuredCloneContainer* Notification::GetDataCloneContainer()
{
return mDataObjectContainer;
}
void
Notification::GetData(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval)
{
if (!mData && mDataObjectContainer) {
nsresult rv;
rv = mDataObjectContainer->DeserializeToVariant(aCx, getter_AddRefs(mData));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRetval.setNull();
return;
}
}
if (!mData) {
aRetval.setNull();
return;
}
VariantToJsval(aCx, mData, aRetval);
}
void
Notification::InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
ErrorResult& aRv)
{
if (mDataObjectContainer || aData.isNull()) {
return;
}
mDataObjectContainer = new nsStructuredCloneContainer();
aRv = mDataObjectContainer->InitFromJSVal(aData);
}
void Notification::InitFromBase64(JSContext* aCx, const nsAString& aData,
ErrorResult& aRv)
{
if (mDataObjectContainer || aData.IsEmpty()) {
return;
}
auto container = new nsStructuredCloneContainer();
aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION,
aCx);
mDataObjectContainer = container;
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -13,6 +13,8 @@
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
class nsIPrincipal; class nsIPrincipal;
class nsIStructuredCloneContainer;
class nsIVariant;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -34,6 +36,9 @@ public:
IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(error)
IMPL_EVENT_HANDLER(close) IMPL_EVENT_HANDLER(close)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Notification, DOMEventTargetHelper)
static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal, static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
const nsAString& aTitle, const nsAString& aTitle,
const NotificationOptions& aOption, const NotificationOptions& aOption,
@ -72,6 +77,8 @@ public:
aRetval = mIconUrl; aRetval = mIconUrl;
} }
nsIStructuredCloneContainer* GetDataCloneContainer();
static void RequestPermission(const GlobalObject& aGlobal, static void RequestPermission(const GlobalObject& aGlobal,
const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback, const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
ErrorResult& aRv); ErrorResult& aRv);
@ -91,11 +98,17 @@ public:
} }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData, ErrorResult& aRv);
void InitFromBase64(JSContext* aCx, const nsAString& aData, ErrorResult& aRv);
protected: protected:
Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
NotificationDirection aDir, const nsAString& aLang, NotificationDirection aDir, const nsAString& aLang,
const nsAString& aTag, const nsAString& aIconUrl, const nsAString& aTag, const nsAString& aIconUrl,
nsPIDOMWindow* aWindow); nsPIDOMWindow* aWindow);
static already_AddRefed<Notification> CreateInternal(nsPIDOMWindow* aWindow, static already_AddRefed<Notification> CreateInternal(nsPIDOMWindow* aWindow,
const nsAString& aID, const nsAString& aID,
@ -145,6 +158,10 @@ protected:
nsString mLang; nsString mLang;
nsString mTag; nsString mTag;
nsString mIconUrl; nsString mIconUrl;
nsCOMPtr<nsIStructuredCloneContainer> mDataObjectContainer;
// It's null until GetData is first called
nsCOMPtr<nsIVariant> mData;
nsString mAlertName; nsString mAlertName;
@ -153,6 +170,8 @@ protected:
static uint32_t sCount; static uint32_t sCount;
private: private:
virtual ~Notification();
nsIPrincipal* GetPrincipal(); nsIPrincipal* GetPrincipal();
}; };

Просмотреть файл

@ -71,7 +71,8 @@ NotificationStorage.prototype = {
} }
}, },
put: function(origin, id, title, dir, lang, body, tag, icon, alertName) { put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
data) {
if (DEBUG) { debug("PUT: " + id + ": " + title); } if (DEBUG) { debug("PUT: " + id + ": " + title); }
var notification = { var notification = {
id: id, id: id,
@ -83,7 +84,8 @@ NotificationStorage.prototype = {
icon: icon, icon: icon,
alertName: alertName, alertName: alertName,
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
origin: origin origin: origin,
data: data
}; };
this._notifications[id] = notification; this._notifications[id] = notification;
@ -202,7 +204,8 @@ NotificationStorage.prototype = {
notification.lang, notification.lang,
notification.body, notification.body,
notification.tag, notification.tag,
notification.icon); notification.icon,
notification.data);
} catch (e) { } catch (e) {
if (DEBUG) { debug("Error calling callback handle: " + e); } if (DEBUG) { debug("Error calling callback handle: " + e); }
} }

Просмотреть файл

@ -48,7 +48,8 @@ var MockServices = (function () {
dbId: aDetails.dbId || undefined, dbId: aDetails.dbId || undefined,
dir: aDetails.dir || undefined, dir: aDetails.dir || undefined,
tag: aDetails.tag || undefined, tag: aDetails.tag || undefined,
timestamp: aDetails.timestamp || undefined timestamp: aDetails.timestamp || undefined,
data: aDetails.data || undefined
}; };
this.showAlertNotification(aImageUrl, aTitle, aText, true, "", listener, aDetails.id); this.showAlertNotification(aImageUrl, aTitle, aText, true, "", listener, aDetails.id);
}, },

Просмотреть файл

@ -43,6 +43,34 @@ var NotificationTest = (function () {
})(tests); })(tests);
} }
var fakeCustomData = (function () {
var buffer = new ArrayBuffer(2);
var dv = new DataView(buffer).setInt16(0, 42, true);
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
var context = canvas.getContext("2d");
var map = new Map();
var set = new Set();
map.set("test", 42);
set.add(4); set.add(2);
return {
primitives: {
a: 123,
b: "test",
c: true,
d: [1, 2, 3]
},
date: new Date(2013, 2, 1, 1, 10),
regexp: new RegExp("[^.]+"),
arrayBuffer: buffer,
imageData: context.createImageData(100, 100),
map: map,
set: set
};
})();
// NotificationTest API // NotificationTest API
return { return {
run: function (tests, callback) { run: function (tests, callback) {
@ -68,6 +96,33 @@ var NotificationTest = (function () {
// TODO: how?? // TODO: how??
}, },
info: info info: info,
customDataMatches: function(dataObj) {
var url = "http://www.domain.com";
try {
return (JSON.stringify(dataObj.primitives) ===
JSON.stringify(fakeCustomData.primitives)) &&
(dataObj.date.toDateString() ===
fakeCustomData.date.toDateString()) &&
(dataObj.regexp.exec(url)[0].substr(7) === "www") &&
(new Int16Array(dataObj.arrayBuffer)[0] === 42) &&
(JSON.stringify(dataObj.imageData.data) ===
JSON.stringify(fakeCustomData.imageData.data)) &&
(dataObj.map.get("test") == 42) &&
(dataObj.set.has(4) && dataObj.set.has(2));
} catch(e) {
return false;
}
},
payload: {
body: "Body",
tag: "fakeTag",
icon: "icon.jpg",
lang: "en-US",
dir: "ltr",
data: fakeCustomData
}
}; };
})(); })();

Просмотреть файл

@ -9,7 +9,8 @@ var registrar = SpecialPowers.wrap(SpecialPowers.Components).manager.
var mockAlertsService = { var mockAlertsService = {
showAlertNotification: function(imageUrl, title, text, textClickable, showAlertNotification: function(imageUrl, title, text, textClickable,
cookie, alertListener, name, bidi, lang) { cookie, alertListener, name, bidi,
lang, data) {
// probably should do this async.... // probably should do this async....
SpecialPowers.wrap(alertListener).observe(null, "alertshow", cookie); SpecialPowers.wrap(alertListener).observe(null, "alertshow", cookie);
@ -22,7 +23,8 @@ var mockAlertsService = {
showAppNotification: function(imageUrl, title, text, alertListener, details) { showAppNotification: function(imageUrl, title, text, alertListener, details) {
this.showAlertNotification(imageUrl, title, text, details.textClickable, "", this.showAlertNotification(imageUrl, title, text, details.textClickable, "",
alertListener, details.name, details.dir, details.lang); alertListener, details.name, details.dir,
details.lang, details.data);
}, },
QueryInterface: function(aIID) { QueryInterface: function(aIID) {

Просмотреть файл

@ -24,7 +24,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=782211
var mockAlertsService = { var mockAlertsService = {
showAlertNotification: function(imageUrl, title, text, textClickable, showAlertNotification: function(imageUrl, title, text, textClickable,
cookie, alertListener, name, dir, lang) { cookie, alertListener, name, dir,
lang, data) {
notificationsCreated.push(name); notificationsCreated.push(name);
if (notificationsCreated.length == 3) { if (notificationsCreated.length == 3) {
checkNotifications(); checkNotifications();

Просмотреть файл

@ -24,7 +24,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=874090
var mockAlertsService = { var mockAlertsService = {
showAlertNotification: function(imageUrl, title, text, textClickable, showAlertNotification: function(imageUrl, title, text, textClickable,
cookie, alertListener, name, dir, lang) { cookie, alertListener, name, dir, lang, data) {
ok(true, "System principal was granted permission and is able to call showAlertNotification."); ok(true, "System principal was granted permission and is able to call showAlertNotification.");
unregisterMock(); unregisterMock();
SimpleTest.finish(); SimpleTest.finish();

Просмотреть файл

@ -14,6 +14,7 @@
<script type="text/javascript"> <script type="text/javascript">
var info = NotificationTest.info; var info = NotificationTest.info;
var options;
var steps = [ var steps = [
function () { function () {
@ -62,13 +63,8 @@
function (done) { function (done) {
info("Test create notification"); info("Test create notification");
var options = { options = NotificationTest.payload;
dir: "auto",
lang: "",
body: "This is a notification body",
tag: "sometag",
icon: "icon.png"
};
var notification = new Notification("This is a title", options); var notification = new Notification("This is a title", options);
ok(notification, "Notification exists"); ok(notification, "Notification exists");
@ -77,12 +73,15 @@
is(notification.onerror, null, "onerror() should be null"); is(notification.onerror, null, "onerror() should be null");
is(notification.onclose, null, "onclose() should be null"); is(notification.onclose, null, "onclose() should be null");
is(typeof notification.close, "function", "close() should exist"); is(typeof notification.close, "function", "close() should exist");
is(typeof notification.data, "object", "data should be a JS object");
is(notification.dir, options.dir, "auto should get set"); is(notification.dir, options.dir, "auto should get set");
is(notification.lang, options.lang, "lang should get set"); is(notification.lang, options.lang, "lang should get set");
is(notification.body, options.body, "body should get set"); is(notification.body, options.body, "body should get set");
is(notification.tag, options.tag, "tag should get set"); is(notification.tag, options.tag, "tag should get set");
is(notification.icon, options.icon, "icon should get set"); is(notification.icon, options.icon, "icon should get set");
ok(NotificationTest.customDataMatches(notification.data),
"data should get set");
// store notification in test context // store notification in test context
this.notification = notification; this.notification = notification;

Просмотреть файл

@ -94,13 +94,8 @@
done(); done();
}; };
var payload = { var payload = NotificationTest.payload;
body: "Body",
tag: "fakeTag",
icon: "icon.jpg",
lang: "en-US",
dir: "ltr"
};
var notif2 = new Notification("Title2", payload); var notif2 = new Notification("Title2", payload);
ok(notif2, "Notification object is valid"); ok(notif2, "Notification object is valid");
notifications.push(notif2); notifications.push(notif2);
@ -156,6 +151,16 @@
is(notif.dir, "ltr", "Notification dir is valid: " + notif.dir); is(notif.dir, "ltr", "Notification dir is valid: " + notif.dir);
is(notif.tag, "fakeTag", "Notification tag is valid: " + notif.tag); is(notif.tag, "fakeTag", "Notification tag is valid: " + notif.tag);
ok((notif.timestamp >= now), "Notification timestamp is valid: (" + notif.timestamp + " >= " + now + ")"); ok((notif.timestamp >= now), "Notification timestamp is valid: (" + notif.timestamp + " >= " + now + ")");
var scContainer =
SpecialPowers.Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(SpecialPowers.Ci.nsIStructuredCloneContainer);
scContainer.initFromBase64(notif.data, 4);
var dataObj = scContainer.deserializeToVariant().SpecialPowers_wrappedObject;
ok(NotificationTest.customDataMatches(dataObj),
"Notification data is valid");
notif2.close(); notif2.close();
}); });
} }

Просмотреть файл

@ -45,13 +45,8 @@
function (done) { function (done) {
info("Test adding a notification, and making sure get returns it"); info("Test adding a notification, and making sure get returns it");
NotificationTest.allowNotifications(); NotificationTest.allowNotifications();
var options = { var options = NotificationTest.payload;
dir: "auto",
lang: "",
body: "This is a notification body",
tag: "sometag",
icon: "icon.png"
};
var notification = new Notification("This is a title", options); var notification = new Notification("This is a title", options);
var promise = Notification.get(); var promise = Notification.get();
promise.then(function (notifications) { promise.then(function (notifications) {
@ -61,6 +56,11 @@
if (notification.tag === options.tag) { if (notification.tag === options.tag) {
ok(true, "should contain newly created notification"); ok(true, "should contain newly created notification");
for (var key in options) { for (var key in options) {
if (key === "data") {
ok(NotificationTest.customDataMatches(notification.data),
"data property should match");
continue;
}
is(notification[key], options[key], key + " property should match"); is(notification[key], options[key], key + " property should match");
} }
notification.close(); notification.close();

Просмотреть файл

@ -14,4 +14,5 @@ dictionary AppNotificationServiceOptions {
DOMString dir = ""; DOMString dir = "";
DOMString lang = ""; DOMString lang = "";
DOMString tag = ""; DOMString tag = "";
DOMString data = "";
}; };

Просмотреть файл

@ -49,6 +49,9 @@ interface Notification : EventTarget {
[Pure] [Pure]
readonly attribute DOMString? icon; readonly attribute DOMString? icon;
[Constant]
readonly attribute any data;
void close(); void close();
}; };
@ -58,6 +61,7 @@ dictionary NotificationOptions {
DOMString body = ""; DOMString body = "";
DOMString tag = ""; DOMString tag = "";
DOMString icon = ""; DOMString icon = "";
any data = null;
}; };
dictionary GetNotificationOptions { dictionary GetNotificationOptions {

Просмотреть файл

@ -69,6 +69,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl
const nsAString & aAlertName, const nsAString & aAlertName,
const nsAString & aBidi, const nsAString & aBidi,
const nsAString & aLang, const nsAString & aLang,
const nsAString & aData,
nsIPrincipal * aPrincipal) nsIPrincipal * aPrincipal)
{ {
if (XRE_GetProcessType() == GeckoProcessType_Content) { if (XRE_GetProcessType() == GeckoProcessType_Content) {
@ -85,6 +86,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl
PromiseFlatString(aAlertName), PromiseFlatString(aAlertName),
PromiseFlatString(aBidi), PromiseFlatString(aBidi),
PromiseFlatString(aLang), PromiseFlatString(aLang),
PromiseFlatString(aData),
IPC::Principal(aPrincipal)); IPC::Principal(aPrincipal));
return NS_OK; return NS_OK;
} }
@ -100,7 +102,8 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl
if (sysAlerts) { if (sysAlerts) {
return sysAlerts->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable, return sysAlerts->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
aAlertCookie, aAlertListener, aAlertName, aAlertCookie, aAlertListener, aAlertName,
aBidi, aLang, IPC::Principal(aPrincipal)); aBidi, aLang, aData,
IPC::Principal(aPrincipal));
} }
if (!ShouldShowAlert()) { if (!ShouldShowAlert()) {

Просмотреть файл

@ -9,7 +9,7 @@
interface nsIPrincipal; interface nsIPrincipal;
[scriptable, uuid(160e87e1-d57d-456b-b6ea-17826f6ea7a8)] [scriptable, uuid(d446bede-fcf7-403d-b6b6-5fd67b19ba58)]
interface nsIAlertsService : nsISupports interface nsIAlertsService : nsISupports
{ {
/** /**
@ -61,6 +61,7 @@ interface nsIAlertsService : nsISupports
[optional] in AString name, [optional] in AString name,
[optional] in AString dir, [optional] in AString dir,
[optional] in AString lang, [optional] in AString lang,
[optional] in AString data,
[optional] in nsIPrincipal principal); [optional] in nsIPrincipal principal);
/** /**

Просмотреть файл

@ -2750,7 +2750,7 @@ nsDownload::SetState(DownloadState aState)
message, !removeWhenDone, message, !removeWhenDone,
mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"), mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"),
mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"), mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"),
EmptyString(), nullptr); EmptyString(), EmptyString(), nullptr);
} }
} }
} }

Просмотреть файл

@ -202,6 +202,7 @@ OSXNotificationCenter::ShowAlertNotification(const nsAString & aImageUrl, const
const nsAString & aAlertName, const nsAString & aAlertName,
const nsAString & aBidi, const nsAString & aBidi,
const nsAString & aLang, const nsAString & aLang,
const nsAString & aData,
nsIPrincipal * aPrincipal) nsIPrincipal * aPrincipal)
{ {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;