mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 916893 - Patch 1 - Notification on workers. r=khuey,wchen
Does not implement the Service Worker API - https://notifications.spec.whatwg.org/#service-worker-api *** Folded: Bug 916893 - Better ownership model. r=khuey Fix for bug found by ASan where we were touching the NotificationFeature after releasing it.
This commit is contained in:
parent
7c43b1ac8d
commit
5aa559655d
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,9 @@
|
||||
#define mozilla_dom_notification_h__
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/dom/NotificationBinding.h"
|
||||
#include "mozilla/dom/workers/bindings/WorkerFeature.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
|
||||
@ -21,16 +23,92 @@ class nsIVariant;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
||||
class NotificationObserver;
|
||||
class NotificationRef;
|
||||
class WorkerNotificationObserver;
|
||||
class Promise;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
class Notification;
|
||||
class NotificationFeature final : public workers::WorkerFeature
|
||||
{
|
||||
// Held alive by Notification itself before feature is added, and only
|
||||
// released after feature is removed.
|
||||
Notification* mNotification;
|
||||
|
||||
public:
|
||||
explicit NotificationFeature(Notification* aNotification);
|
||||
|
||||
bool
|
||||
Notify(JSContext* aCx, workers::Status aStatus) override;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Notifications on workers introduce some liveness issues. The property we
|
||||
* are trying to satisfy is:
|
||||
* Whenever a task is dispatched to the main thread to operate on
|
||||
* a Notification, the Notification should be addrefed on the worker thread
|
||||
* and a feature should be added to observe the worker lifetime. This main
|
||||
* thread owner should ensure it properly releases the reference to the
|
||||
* Notification, additionally removing the feature if necessary.
|
||||
*
|
||||
* To enforce the correct addref and release, along with managing the feature,
|
||||
* we introduce a NotificationRef. Only one object may ever own
|
||||
* a NotificationRef, so UniquePtr<> is used throughout. The NotificationRef
|
||||
* constructor calls AddRefObject(). When it is destroyed (on any thread) it
|
||||
* releases the Notification on the correct thread.
|
||||
*
|
||||
* Code should only access the underlying Notification object when it can
|
||||
* guarantee that it retains ownership of the NotificationRef while doing so.
|
||||
*
|
||||
* The one kink in this mechanism is that the worker feature may be Notify()ed
|
||||
* if the worker stops running script, even if the Notification's corresponding
|
||||
* UI is still visible to the user. We handle this case with the following
|
||||
* steps:
|
||||
* a) Close the notification. This is done by blocking the worker on the main
|
||||
* thread.
|
||||
* b) Ask the observer to let go of its NotificationRef's underlying
|
||||
* Notification without proper cleanup since the feature will handle the
|
||||
* release. This is only OK because every notification has only one
|
||||
* associated observer. The NotificationRef itself is still owned by the
|
||||
* observer and deleted by the UniquePtr, but it doesn't do anything since
|
||||
* the underlying Notification is null.
|
||||
*
|
||||
* To unify code-paths, we use the same NotificationRef in the main
|
||||
* thread implementation too.
|
||||
*
|
||||
* Note that the Notification's JS wrapper does it's standard
|
||||
* AddRef()/Release() and is not affected by any of this.
|
||||
*
|
||||
* There is one case related to the WorkerNotificationObserver having to
|
||||
* dispatch WorkerRunnables to the worker thread which will use the
|
||||
* Notification object. We can end up in a situation where an event runnable is
|
||||
* dispatched to the worker, gets queued in the worker's event queue, but then,
|
||||
* the worker yields to the main thread. Here the main thread observer is
|
||||
* destroyed, which frees its NotificationRef. The NotificationRef dispatches
|
||||
* a ControlRunnable to the worker, which runs before the event runnable,
|
||||
* leading to the event runnable possibly not having a valid Notification
|
||||
* reference.
|
||||
* We solve this problem by having WorkerNotificationObserver's dtor
|
||||
* dispatching a standard WorkerRunnable to do the release (this guarantees the
|
||||
* ordering of the release is after the event runnables). All WorkerRunnables
|
||||
* that get dispatched successfully are guaranteed to run on the worker before
|
||||
* it shuts down. If that dispatch fails, the standard ControlRunnable based
|
||||
* shutdown is acceptable since the already dispatched event runnables have
|
||||
* already run or canceled (the worker is already past Running).
|
||||
*
|
||||
*/
|
||||
class Notification : public DOMEventTargetHelper
|
||||
{
|
||||
friend class CloseNotificationRunnable;
|
||||
friend class NotificationTask;
|
||||
friend class NotificationPermissionRequest;
|
||||
friend class NotificationObserver;
|
||||
friend class NotificationStorageCallback;
|
||||
friend class WorkerNotificationObserver;
|
||||
|
||||
public:
|
||||
IMPL_EVENT_HANDLER(click)
|
||||
@ -41,6 +119,10 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Notification, DOMEventTargetHelper)
|
||||
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
// Returns if Notification.get() is allowed for the current global.
|
||||
static bool IsGetEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOption,
|
||||
@ -91,6 +173,8 @@ public:
|
||||
|
||||
nsIStructuredCloneContainer* GetDataCloneContainer();
|
||||
|
||||
static bool RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
static void RequestPermission(const GlobalObject& aGlobal,
|
||||
const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
|
||||
ErrorResult& aRv);
|
||||
@ -117,13 +201,36 @@ public:
|
||||
|
||||
void InitFromBase64(JSContext* aCx, const nsAString& aData, ErrorResult& aRv);
|
||||
|
||||
void AssertIsOnTargetThread() const
|
||||
{
|
||||
MOZ_ASSERT(IsTargetThread());
|
||||
}
|
||||
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
// Main thread only.
|
||||
WorkerNotificationObserver* mObserver;
|
||||
|
||||
// The NotificationTask calls ShowInternal()/CloseInternal() on the
|
||||
// Notification. At this point the task has ownership of the Notification. It
|
||||
// passes this on to the Notification itself via mTempRef so that
|
||||
// ShowInternal()/CloseInternal() may pass it along appropriately (or release
|
||||
// it).
|
||||
UniquePtr<NotificationRef> mTempRef;
|
||||
|
||||
void AddRefObject();
|
||||
void ReleaseObject();
|
||||
|
||||
static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
|
||||
ErrorResult& rv);
|
||||
|
||||
bool DispatchClickEvent();
|
||||
protected:
|
||||
Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
|
||||
NotificationDirection aDir, const nsAString& aLang,
|
||||
const nsAString& aTag, const nsAString& aIconUrl,
|
||||
const NotificationBehavior& aBehavior, nsPIDOMWindow* aWindow);
|
||||
const NotificationBehavior& aBehavior, nsIGlobalObject* aGlobal);
|
||||
|
||||
static already_AddRefed<Notification> CreateInternal(nsPIDOMWindow* aWindow,
|
||||
static already_AddRefed<Notification> CreateInternal(nsIGlobalObject* aGlobal,
|
||||
const nsAString& aID,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOptions);
|
||||
@ -158,27 +265,29 @@ protected:
|
||||
}
|
||||
|
||||
static nsresult GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin);
|
||||
nsresult GetOriginWorker(nsString& aOrigin);
|
||||
|
||||
void GetAlertName(nsAString& aRetval)
|
||||
{
|
||||
aRetval = mAlertName;
|
||||
}
|
||||
|
||||
nsString mID;
|
||||
nsString mTitle;
|
||||
nsString mBody;
|
||||
NotificationDirection mDir;
|
||||
nsString mLang;
|
||||
nsString mTag;
|
||||
nsString mIconUrl;
|
||||
const nsString mID;
|
||||
const nsString mTitle;
|
||||
const nsString mBody;
|
||||
const NotificationDirection mDir;
|
||||
const nsString mLang;
|
||||
const nsString mTag;
|
||||
const nsString mIconUrl;
|
||||
nsCOMPtr<nsIStructuredCloneContainer> mDataObjectContainer;
|
||||
NotificationBehavior mBehavior;
|
||||
const NotificationBehavior mBehavior;
|
||||
|
||||
// It's null until GetData is first called
|
||||
nsCOMPtr<nsIVariant> mData;
|
||||
|
||||
nsString mAlertName;
|
||||
|
||||
// Main thread only.
|
||||
bool mIsClosed;
|
||||
|
||||
// We need to make a distinction between the notification being closed i.e.
|
||||
@ -193,6 +302,22 @@ private:
|
||||
virtual ~Notification();
|
||||
|
||||
nsIPrincipal* GetPrincipal();
|
||||
|
||||
nsresult PersistNotification();
|
||||
void UnpersistNotification();
|
||||
|
||||
bool IsTargetThread() const
|
||||
{
|
||||
return NS_IsMainThread() == !mWorkerPrivate;
|
||||
}
|
||||
|
||||
void RegisterFeature();
|
||||
void UnregisterFeature();
|
||||
|
||||
nsresult ResolveIconAndSoundURL(nsString&, nsString&);
|
||||
|
||||
UniquePtr<NotificationFeature> mFeature;
|
||||
uint32_t mTaskCount;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -33,6 +33,7 @@ FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/dom/ipc',
|
||||
'/dom/workers',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
|
@ -11,17 +11,18 @@
|
||||
* related or neighboring rights to this work.
|
||||
*/
|
||||
|
||||
[Pref="dom.webnotifications.enabled",
|
||||
Constructor(DOMString title, optional NotificationOptions options),
|
||||
[Constructor(DOMString title, optional NotificationOptions options),
|
||||
Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::Notification::PrefEnabled",
|
||||
UnsafeInPrerendering]
|
||||
interface Notification : EventTarget {
|
||||
[GetterThrows]
|
||||
static readonly attribute NotificationPermission permission;
|
||||
|
||||
[Throws]
|
||||
[Throws, Func="mozilla::dom::Notification::RequestPermissionEnabledForScope"]
|
||||
static void requestPermission(optional NotificationPermissionCallback permissionCallback);
|
||||
|
||||
[Throws]
|
||||
[Throws, Func="mozilla::dom::Notification::IsGetEnabled"]
|
||||
static Promise<sequence<Notification>> get(optional GetNotificationOptions filter);
|
||||
|
||||
attribute EventHandler onclick;
|
||||
|
@ -158,6 +158,7 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
|
||||
#endif
|
||||
|
||||
#define PREF_DOM_CACHES_ENABLED "dom.caches.enabled"
|
||||
#define PREF_DOM_WORKERNOTIFICATION_ENABLED "dom.webnotifications.workers.enabled"
|
||||
#define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
|
||||
#define PREF_INTL_ACCEPT_LANGUAGES "intl.accept_languages"
|
||||
#define PREF_SERVICEWORKERS_ENABLED "dom.serviceWorkers.enabled"
|
||||
@ -1902,6 +1903,10 @@ RuntimeService::Init()
|
||||
WorkerPrefChanged,
|
||||
PREF_DOM_CACHES_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_DOM_CACHES))) ||
|
||||
NS_FAILED(Preferences::RegisterCallbackAndCall(
|
||||
WorkerPrefChanged,
|
||||
PREF_DOM_WORKERNOTIFICATION_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_DOM_WORKERNOTIFICATION))) ||
|
||||
NS_FAILED(Preferences::RegisterCallbackAndCall(
|
||||
WorkerPrefChanged,
|
||||
PREF_SERVICEWORKERS_ENABLED,
|
||||
@ -2117,6 +2122,10 @@ RuntimeService::Cleanup()
|
||||
WorkerPrefChanged,
|
||||
PREF_DOM_CACHES_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_DOM_CACHES))) ||
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
PREF_DOM_WORKERNOTIFICATION_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_DOM_WORKERNOTIFICATION))) ||
|
||||
#if DUMP_CONTROLLED_BY_PREF
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
@ -2651,16 +2660,17 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
|
||||
|
||||
#ifdef DUMP_CONTROLLED_BY_PREF
|
||||
if (key == WORKERPREF_DUMP) {
|
||||
key = WORKERPREF_DUMP;
|
||||
sDefaultPreferences[WORKERPREF_DUMP] =
|
||||
sDefaultPreferences[key] =
|
||||
Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (key == WORKERPREF_DOM_CACHES) {
|
||||
key = WORKERPREF_DOM_CACHES;
|
||||
sDefaultPreferences[WORKERPREF_DOM_CACHES] =
|
||||
Preferences::GetBool(PREF_DOM_CACHES_ENABLED, false);
|
||||
} else if (key == WORKERPREF_DOM_WORKERNOTIFICATION) {
|
||||
sDefaultPreferences[key] =
|
||||
Preferences::GetBool(PREF_DOM_WORKERNOTIFICATION_ENABLED, false);
|
||||
} else if (key == WORKERPREF_SERVICEWORKERS) {
|
||||
key = WORKERPREF_SERVICEWORKERS;
|
||||
sDefaultPreferences[WORKERPREF_SERVICEWORKERS] =
|
||||
@ -2670,9 +2680,6 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
|
||||
sDefaultPreferences[key] =
|
||||
Preferences::GetBool(PREF_INTERCEPTION_ENABLED, false);
|
||||
}
|
||||
// This function should never be registered as a callback for a preference it
|
||||
// does not handle.
|
||||
MOZ_ASSERT(key != WORKERPREF_COUNT);
|
||||
|
||||
RuntimeService* rts = RuntimeService::GetService();
|
||||
if (rts) {
|
||||
|
@ -1245,6 +1245,13 @@ public:
|
||||
AssertIsOnWorkerThread();
|
||||
return mPreferences[WORKERPREF_INTERCEPTION_ENABLED];
|
||||
}
|
||||
|
||||
bool
|
||||
DOMWorkerNotificationEnabled() const
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
return mPreferences[WORKERPREF_DOM_WORKERNOTIFICATION];
|
||||
}
|
||||
|
||||
bool
|
||||
OnLine() const
|
||||
|
@ -199,6 +199,7 @@ enum WorkerPreference
|
||||
WORKERPREF_DOM_CACHES, // dom.caches.enabled
|
||||
WORKERPREF_SERVICEWORKERS, // dom.serviceWorkers.enabled
|
||||
WORKERPREF_INTERCEPTION_ENABLED, // dom.serviceWorkers.interception.enabled
|
||||
WORKERPREF_DOM_WORKERNOTIFICATION, // dom.webnotifications.workers.enabled
|
||||
WORKERPREF_COUNT
|
||||
};
|
||||
|
||||
|
@ -41,6 +41,10 @@ support-files =
|
||||
navigator_languages_worker.js
|
||||
navigator_worker.js
|
||||
newError_worker.js
|
||||
notification_worker.js
|
||||
notification_worker_child-child.js
|
||||
notification_worker_child-parent.js
|
||||
notification_permission_worker.js
|
||||
onLine_worker.js
|
||||
onLine_worker_child.js
|
||||
onLine_worker_head.js
|
||||
@ -158,6 +162,9 @@ skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
[test_navigator_languages.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_newError.html]
|
||||
[test_notification.html]
|
||||
[test_notification_child.html]
|
||||
[test_notification_permission.html]
|
||||
[test_onLine.html]
|
||||
skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
[test_performance_user_timing.html]
|
||||
|
56
dom/workers/test/notification_permission_worker.js
Normal file
56
dom/workers/test/notification_permission_worker.js
Normal file
@ -0,0 +1,56 @@
|
||||
function info(message) {
|
||||
dump("INFO: " + message + "\n");
|
||||
}
|
||||
|
||||
function ok(test, message) {
|
||||
postMessage({ type: 'ok', test: test, message: message });
|
||||
}
|
||||
|
||||
function is(a, b, message) {
|
||||
postMessage({ type: 'is', test1: a, test2: b, message: message });
|
||||
}
|
||||
|
||||
if (self.Notification) {
|
||||
var steps = [
|
||||
function (done) {
|
||||
info("Test notification permission");
|
||||
ok(typeof Notification === "function", "Notification constructor exists");
|
||||
ok(Notification.permission === "denied", "Notification.permission is denied.");
|
||||
|
||||
var n = new Notification("Hello");
|
||||
n.onerror = function(e) {
|
||||
ok(true, "error called due to permission denied.");
|
||||
done();
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
onmessage = function(e) {
|
||||
var context = {};
|
||||
(function executeRemainingTests(remainingTests) {
|
||||
if (!remainingTests.length) {
|
||||
postMessage({type: 'finish'});
|
||||
return;
|
||||
}
|
||||
|
||||
var nextTest = remainingTests.shift();
|
||||
var finishTest = executeRemainingTests.bind(null, remainingTests);
|
||||
var startTest = nextTest.call.bind(nextTest, context, finishTest);
|
||||
|
||||
try {
|
||||
startTest();
|
||||
// if no callback was defined for test function,
|
||||
// we must manually invoke finish to continue
|
||||
if (nextTest.length === 0) {
|
||||
finishTest();
|
||||
}
|
||||
} catch (e) {
|
||||
ok(false, "Test threw exception! " + nextTest + " " + e);
|
||||
finishTest();
|
||||
}
|
||||
})(steps);
|
||||
}
|
||||
} else {
|
||||
ok(true, "Notifications are not enabled in workers on the platform.");
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
89
dom/workers/test/notification_worker.js
Normal file
89
dom/workers/test/notification_worker.js
Normal file
@ -0,0 +1,89 @@
|
||||
function ok(test, message) {
|
||||
postMessage({ type: 'ok', test: test, message: message });
|
||||
}
|
||||
|
||||
function is(a, b, message) {
|
||||
postMessage({ type: 'is', test1: a, test2: b, message: message });
|
||||
}
|
||||
|
||||
if (self.Notification) {
|
||||
var steps = [
|
||||
function () {
|
||||
ok(typeof Notification === "function", "Notification constructor exists");
|
||||
ok(Notification.permission, "Notification.permission exists");
|
||||
ok(typeof Notification.requestPermission === "undefined", "Notification.requestPermission should not exist");
|
||||
},
|
||||
|
||||
function (done) {
|
||||
var options = {
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "This is a notification body",
|
||||
tag: "sometag",
|
||||
icon: "icon.png"
|
||||
};
|
||||
var notification = new Notification("This is a title", options);
|
||||
|
||||
ok(notification !== undefined, "Notification exists");
|
||||
is(notification.onclick, null, "onclick() should be null");
|
||||
is(notification.onshow, null, "onshow() should be null");
|
||||
is(notification.onerror, null, "onerror() should be null");
|
||||
is(notification.onclose, null, "onclose() should be null");
|
||||
is(typeof notification.close, "function", "close() should exist");
|
||||
|
||||
is(notification.dir, options.dir, "auto should get set");
|
||||
is(notification.lang, options.lang, "lang should get set");
|
||||
is(notification.body, options.body, "body should get set");
|
||||
is(notification.tag, options.tag, "tag should get set");
|
||||
is(notification.icon, options.icon, "icon should get set");
|
||||
|
||||
// store notification in test context
|
||||
this.notification = notification;
|
||||
|
||||
notification.onshow = function () {
|
||||
ok(true, "onshow handler should be called");
|
||||
done();
|
||||
};
|
||||
},
|
||||
|
||||
function (done) {
|
||||
var notification = this.notification;
|
||||
|
||||
notification.onclose = function () {
|
||||
ok(true, "onclose handler should be called");
|
||||
done();
|
||||
};
|
||||
|
||||
notification.close();
|
||||
},
|
||||
];
|
||||
|
||||
onmessage = function(e) {
|
||||
var context = {};
|
||||
(function executeRemainingTests(remainingTests) {
|
||||
if (!remainingTests.length) {
|
||||
postMessage({type: 'finish'});
|
||||
return;
|
||||
}
|
||||
|
||||
var nextTest = remainingTests.shift();
|
||||
var finishTest = executeRemainingTests.bind(null, remainingTests);
|
||||
var startTest = nextTest.call.bind(nextTest, context, finishTest);
|
||||
|
||||
try {
|
||||
startTest();
|
||||
// if no callback was defined for test function,
|
||||
// we must manually invoke finish to continue
|
||||
if (nextTest.length === 0) {
|
||||
finishTest();
|
||||
}
|
||||
} catch (e) {
|
||||
ok(false, "Test threw exception! " + nextTest + " " + e);
|
||||
finishTest();
|
||||
}
|
||||
})(steps);
|
||||
}
|
||||
} else {
|
||||
ok(true, "Notifications are not enabled in workers on the platform.");
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
92
dom/workers/test/notification_worker_child-child.js
Normal file
92
dom/workers/test/notification_worker_child-child.js
Normal file
@ -0,0 +1,92 @@
|
||||
function ok(test, message) {
|
||||
postMessage({ type: 'ok', test: test, message: message });
|
||||
}
|
||||
|
||||
function is(a, b, message) {
|
||||
postMessage({ type: 'is', test1: a, test2: b, message: message });
|
||||
}
|
||||
|
||||
if (self.Notification) {
|
||||
var steps = [
|
||||
function () {
|
||||
ok(typeof Notification === "function", "Notification constructor exists");
|
||||
ok(Notification.permission, "Notification.permission exists");
|
||||
ok(typeof Notification.requestPermission === "undefined", "Notification.requestPermission should not exist");
|
||||
//ok(typeof Notification.get === "function", "Notification.get exists");
|
||||
},
|
||||
|
||||
function (done) {
|
||||
|
||||
var options = {
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "This is a notification body",
|
||||
tag: "sometag",
|
||||
icon: "icon.png"
|
||||
};
|
||||
var notification = new Notification("This is a title", options);
|
||||
|
||||
ok(notification !== undefined, "Notification exists");
|
||||
is(notification.onclick, null, "onclick() should be null");
|
||||
is(notification.onshow, null, "onshow() should be null");
|
||||
is(notification.onerror, null, "onerror() should be null");
|
||||
is(notification.onclose, null, "onclose() should be null");
|
||||
is(typeof notification.close, "function", "close() should exist");
|
||||
|
||||
is(notification.dir, options.dir, "auto should get set");
|
||||
is(notification.lang, options.lang, "lang should get set");
|
||||
is(notification.body, options.body, "body should get set");
|
||||
is(notification.tag, options.tag, "tag should get set");
|
||||
is(notification.icon, options.icon, "icon should get set");
|
||||
|
||||
// store notification in test context
|
||||
this.notification = notification;
|
||||
|
||||
notification.onshow = function () {
|
||||
ok(true, "onshow handler should be called");
|
||||
done();
|
||||
};
|
||||
},
|
||||
|
||||
function (done) {
|
||||
var notification = this.notification;
|
||||
|
||||
notification.onclose = function () {
|
||||
ok(true, "onclose handler should be called");
|
||||
done();
|
||||
};
|
||||
|
||||
notification.close();
|
||||
},
|
||||
];
|
||||
|
||||
onmessage = function(e) {
|
||||
var context = {};
|
||||
(function executeRemainingTests(remainingTests) {
|
||||
if (!remainingTests.length) {
|
||||
postMessage({type: 'finish'});
|
||||
return;
|
||||
}
|
||||
|
||||
var nextTest = remainingTests.shift();
|
||||
var finishTest = executeRemainingTests.bind(null, remainingTests);
|
||||
var startTest = nextTest.call.bind(nextTest, context, finishTest);
|
||||
|
||||
try {
|
||||
startTest();
|
||||
// if no callback was defined for test function,
|
||||
// we must manually invoke finish to continue
|
||||
if (nextTest.length === 0) {
|
||||
finishTest();
|
||||
}
|
||||
} catch (e) {
|
||||
ok(false, "Test threw exception! " + nextTest + " " + e);
|
||||
finishTest();
|
||||
}
|
||||
})(steps);
|
||||
}
|
||||
} else {
|
||||
ok(true, "Notifications are not enabled in workers on the platform.");
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
||||
|
26
dom/workers/test/notification_worker_child-parent.js
Normal file
26
dom/workers/test/notification_worker_child-parent.js
Normal file
@ -0,0 +1,26 @@
|
||||
function ok(test, message) {
|
||||
postMessage({ type: 'ok', test: test, message: message });
|
||||
}
|
||||
|
||||
function is(a, b, message) {
|
||||
postMessage({ type: 'is', test1: a, test2: b, message: message });
|
||||
}
|
||||
|
||||
if (self.Notification) {
|
||||
var child = new Worker("notification_worker_child-child.js");
|
||||
child.onerror = function(e) {
|
||||
ok(false, "Error loading child worker " + e);
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
||||
|
||||
child.onmessage = function(e) {
|
||||
postMessage(e.data);
|
||||
}
|
||||
|
||||
onmessage = function(e) {
|
||||
child.postMessage('start');
|
||||
}
|
||||
} else {
|
||||
ok(true, "Notifications are not enabled in workers on the platform.");
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
50
dom/workers/test/test_notification.html
Normal file
50
dom/workers/test/test_notification.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=916893
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 916893</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
|
||||
|
||||
function runTest() {
|
||||
MockServices.register();
|
||||
var w = new Worker("notification_worker.js");
|
||||
w.onmessage = function(e) {
|
||||
if (e.data.type === 'finish') {
|
||||
MockServices.unregister();
|
||||
SimpleTest.finish();
|
||||
} else if (e.data.type === 'ok') {
|
||||
ok(e.data.test, e.data.message);
|
||||
} else if (e.data.type === 'is') {
|
||||
is(e.data.test1, e.data.test2, e.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// turn on testing pref (used by notification.cpp, and mock the alerts
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing", true);
|
||||
w.postMessage('start')
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set": [["dom.webnotifications.workers.enabled", true]]},
|
||||
runTest
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
49
dom/workers/test/test_notification_child.html
Normal file
49
dom/workers/test/test_notification_child.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=916893
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 916893 - Test Notifications in child workers.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
|
||||
function runTest() {
|
||||
MockServices.register();
|
||||
var w = new Worker("notification_worker_child-parent.js");
|
||||
w.onmessage = function(e) {
|
||||
if (e.data.type === 'finish') {
|
||||
MockServices.unregister();
|
||||
SimpleTest.finish();
|
||||
} else if (e.data.type === 'ok') {
|
||||
ok(e.data.test, e.data.message);
|
||||
} else if (e.data.type === 'is') {
|
||||
is(e.data.test1, e.data.test2, e.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// turn on testing pref (used by notification.cpp, and mock the alerts
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing", true);
|
||||
w.postMessage('start')
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set": [["dom.webnotifications.workers.enabled", true]]},
|
||||
runTest
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
51
dom/workers/test/test_notification_permission.html
Normal file
51
dom/workers/test/test_notification_permission.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=916893
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 916893 - Make sure error is fired on Notification if permission is denied.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
|
||||
function runTest() {
|
||||
MockServices.register();
|
||||
var w = new Worker("notification_permission_worker.js");
|
||||
w.onmessage = function(e) {
|
||||
if (e.data.type === 'finish') {
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
|
||||
MockServices.unregister();
|
||||
SimpleTest.finish();
|
||||
} else if (e.data.type === 'ok') {
|
||||
ok(e.data.test, e.data.message);
|
||||
} else if (e.data.type === 'is') {
|
||||
is(e.data.test1, e.data.test2, e.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// turn on testing pref (used by notification.cpp, and mock the alerts
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing", true);
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing.allow", false);
|
||||
w.postMessage('start')
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set": [["dom.webnotifications.workers.enabled", true]]},
|
||||
runTest
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -4323,6 +4323,7 @@ pref("notification.feature.enabled", false);
|
||||
|
||||
// Web Notification
|
||||
pref("dom.webnotifications.enabled", true);
|
||||
pref("dom.webnotifications.workers.enabled", false);
|
||||
|
||||
// Alert animation effect, name is disableSlidingEffect for backwards-compat.
|
||||
pref("alerts.disableSlidingEffect", false);
|
||||
|
Loading…
Reference in New Issue
Block a user