--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -851,16 +851,17 @@ GK_ATOM(onmoznetworkdownload, "onmoznetw
GK_ATOM(onmapfolderlistingreq, "onmapfolderlistingreq")
GK_ATOM(onmapmessageslistingreq, "onmapmessageslistingreq")
GK_ATOM(onmapgetmessagereq, "onmapgetmessagereq")
GK_ATOM(onmapsetmessagestatusreq, "onmapsetmessagestatusreq")
GK_ATOM(onmapsendmessagereq, "onmapsendmessagereq")
GK_ATOM(onmapmessageupdatereq, "onmapmessageupdatereq")
GK_ATOM(onnewrdsgroup, "onnewrdsgroup")
GK_ATOM(onnotificationclick, "onnotificationclick")
+GK_ATOM(onnotificationclose, "onnotificationclose")
GK_ATOM(onnoupdate, "onnoupdate")
GK_ATOM(onobexpasswordreq, "onobexpasswordreq")
GK_ATOM(onobsolete, "onobsolete")
GK_ATOM(ononline, "ononline")
GK_ATOM(onoffline, "onoffline")
GK_ATOM(onopen, "onopen")
GK_ATOM(onorientationchange, "onorientationchange")
GK_ATOM(onotastatuschange, "onotastatuschange")
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -180,16 +180,29 @@ interface nsIServiceWorkerManager : nsIS
in AString aTitle,
in AString aDir,
in AString aLang,
in AString aBody,
in AString aTag,
in AString aIcon,
in AString aData,
in AString aBehavior);
+
+ void sendNotificationCloseEvent(in ACString aOriginSuffix,
+ in ACString scope,
+ in AString aID,
+ in AString aTitle,
+ in AString aDir,
+ in AString aLang,
+ in AString aBody,
+ in AString aTag,
+ in AString aIcon,
+ in AString aData,
+ in AString aBehavior);
+
[optional_argc] void sendPushEvent(in ACString aOriginAttributes,
in ACString aScope,
[optional] in uint32_t aDataLength,
[optional, array, size_is(aDataLength)] in uint8_t aDataBytes);
void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
in ACString scope);
void addListener(in nsIServiceWorkerManagerListener aListener);
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -1585,55 +1585,71 @@ WorkerNotificationObserver::Observe(nsIS
NS_IMETHODIMP
ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
AssertIsOnMainThread();
+ nsAutoCString originSuffix;
+ nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIServiceWorkerManager> swm =
+ mozilla::services::GetServiceWorkerManager();
+ if (NS_WARN_IF(!swm)) {
+ return NS_ERROR_FAILURE;
+ }
+
if (!strcmp("alertclickcallback", aTopic)) {
- nsAutoCString originSuffix;
- nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- nsCOMPtr<nsIServiceWorkerManager> swm =
- mozilla::services::GetServiceWorkerManager();
-
- if (swm) {
- swm->SendNotificationClickEvent(originSuffix,
- NS_ConvertUTF16toUTF8(mScope),
- mID,
- mTitle,
- mDir,
- mLang,
- mBody,
- mTag,
- mIcon,
- mData,
- mBehavior);
- }
+ rv = swm->SendNotificationClickEvent(originSuffix,
+ NS_ConvertUTF16toUTF8(mScope),
+ mID,
+ mTitle,
+ mDir,
+ mLang,
+ mBody,
+ mTag,
+ mIcon,
+ mData,
+ mBehavior);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
return NS_OK;
}
if (!strcmp("alertfinished", aTopic)) {
nsString origin;
nsresult rv = Notification::GetOrigin(mPrincipal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Remove closed or dismissed persistent notifications.
nsCOMPtr<nsINotificationStorage> notificationStorage =
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
if (notificationStorage) {
notificationStorage->Delete(origin, mID);
}
+
+ rv = swm->SendNotificationCloseEvent(originSuffix,
+ NS_ConvertUTF16toUTF8(mScope),
+ mID,
+ mTitle,
+ mDir,
+ mLang,
+ mBody,
+ mTag,
+ mIcon,
+ mData,
+ mBehavior);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return NS_OK;
}
return NS_OK;
}
bool
Notification::IsInPrivateBrowsing()
{
--- a/dom/webidl/NotificationEvent.webidl
+++ b/dom/webidl/NotificationEvent.webidl
@@ -18,9 +18,10 @@ interface NotificationEvent : Extendable
};
dictionary NotificationEventInit : ExtendableEventInit {
required Notification notification;
};
partial interface ServiceWorkerGlobalScope {
attribute EventHandler onnotificationclick;
+ attribute EventHandler onnotificationclose;
};
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -954,44 +954,81 @@ ServiceWorkerManager::SendPushSubscripti
ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
if (!info) {
return NS_ERROR_FAILURE;
}
return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
#endif
}
+nsresult
+ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
+ const nsACString& aOriginSuffix,
+ const nsACString& aScope,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior)
+{
+ PrincipalOriginAttributes attrs;
+ if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
+ if (!info) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
+ return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
+ aLang, aBody, aTag,
+ aIcon, aData, aBehavior,
+ NS_ConvertUTF8toUTF16(aScope));
+}
+
NS_IMETHODIMP
ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
const nsACString& aScope,
const nsAString& aID,
const nsAString& aTitle,
const nsAString& aDir,
const nsAString& aLang,
const nsAString& aBody,
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aBehavior)
{
- PrincipalOriginAttributes attrs;
- if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
- return NS_ERROR_INVALID_ARG;
- }
-
- ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
- if (!info) {
- return NS_ERROR_FAILURE;
- }
-
- ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
- return workerPrivate->SendNotificationClickEvent(aID, aTitle, aDir,
- aLang, aBody, aTag,
- aIcon, aData, aBehavior,
- NS_ConvertUTF8toUTF16(aScope));
+ return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
+ aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
+ aBody, aTag, aIcon, aData, aBehavior);
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
+ const nsACString& aScope,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior)
+{
+ return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
+ aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
+ aBody, aTag, aIcon, aData, aBehavior);
}
NS_IMETHODIMP
ServiceWorkerManager::GetReadyPromise(mozIDOMWindow* aWindow,
nsISupports** aPromise)
{
AssertIsOnMainThread();
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -426,15 +426,29 @@ private:
void
ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
void
UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
void
MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+ nsresult
+ SendNotificationEvent(const nsAString& aEventName,
+ const nsACString& aOriginSuffix,
+ const nsACString& aScope,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior);
};
} // namespace workers
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_workers_serviceworkermanager_h
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1074,43 +1074,46 @@ NS_IMPL_ISUPPORTS0(AllowWindowInteractio
bool
ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
mHandler->ClearWindowAllowed(aWorkerPrivate);
mHandler = nullptr;
return true;
}
-class SendNotificationClickEventRunnable final : public ExtendableEventWorkerRunnable
+class SendNotificationEventRunnable final : public ExtendableEventWorkerRunnable
{
+ const nsString mEventName;
const nsString mID;
const nsString mTitle;
const nsString mDir;
const nsString mLang;
const nsString mBody;
const nsString mTag;
const nsString mIcon;
const nsString mData;
const nsString mBehavior;
const nsString mScope;
public:
- SendNotificationClickEventRunnable(WorkerPrivate* aWorkerPrivate,
- KeepAliveToken* aKeepAliveToken,
- const nsAString& aID,
- const nsAString& aTitle,
- const nsAString& aDir,
- const nsAString& aLang,
- const nsAString& aBody,
- const nsAString& aTag,
- const nsAString& aIcon,
- const nsAString& aData,
- const nsAString& aBehavior,
- const nsAString& aScope)
+ SendNotificationEventRunnable(WorkerPrivate* aWorkerPrivate,
+ KeepAliveToken* aKeepAliveToken,
+ const nsAString& aEventName,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior,
+ const nsAString& aScope)
: ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
+ , mEventName(aEventName)
, mID(aID)
, mTitle(aTitle)
, mDir(aDir)
, mLang(aLang)
, mBody(aBody)
, mTag(aTag)
, mIcon(aIcon)
, mData(aData)
@@ -1139,18 +1142,17 @@ public:
}
NotificationEventInit nei;
nei.mNotification = notification;
nei.mBubbles = false;
nei.mCancelable = false;
RefPtr<NotificationEvent> event =
- NotificationEvent::Constructor(target,
- NS_LITERAL_STRING("notificationclick"),
+ NotificationEvent::Constructor(target, mEventName,
nei, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
event->SetTrusted(true);
RefPtr<Promise> waitUntil;
aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
@@ -1165,37 +1167,47 @@ public:
return true;
}
};
} // namespace anonymous
nsresult
-ServiceWorkerPrivate::SendNotificationClickEvent(const nsAString& aID,
- const nsAString& aTitle,
- const nsAString& aDir,
- const nsAString& aLang,
- const nsAString& aBody,
- const nsAString& aTag,
- const nsAString& aIcon,
- const nsAString& aData,
- const nsAString& aBehavior,
- const nsAString& aScope)
+ServiceWorkerPrivate::SendNotificationEvent(const nsAString& aEventName,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior,
+ const nsAString& aScope)
{
- nsresult rv = SpawnWorkerIfNeeded(NotificationClickEvent, nullptr);
+ WakeUpReason why;
+ if (aEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) {
+ why = NotificationClickEvent;
+ gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
+ } else if (aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) {
+ why = NotificationCloseEvent;
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = SpawnWorkerIfNeeded(why, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
- gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
-
RefPtr<WorkerRunnable> r =
- new SendNotificationClickEventRunnable(mWorkerPrivate, mKeepAliveToken,
- aID, aTitle, aDir, aLang,
- aBody, aTag, aIcon, aData,
- aBehavior, aScope);
+ new SendNotificationEventRunnable(mWorkerPrivate, mKeepAliveToken,
+ aEventName, aID, aTitle, aDir, aLang,
+ aBody, aTag, aIcon, aData, aBehavior,
+ aScope);
if (NS_WARN_IF(!r->Dispatch())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
namespace {
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -6,16 +6,19 @@
#ifndef mozilla_dom_workers_serviceworkerprivate_h
#define mozilla_dom_workers_serviceworkerprivate_h
#include "nsCOMPtr.h"
#include "WorkerPrivate.h"
+#define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
+#define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
+
class nsIInterceptedChannel;
namespace mozilla {
namespace dom {
namespace workers {
class ServiceWorkerInfo;
class ServiceWorkerRegistrationInfo;
@@ -88,26 +91,27 @@ public:
SendPushEvent(const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData,
ServiceWorkerRegistrationInfo* aRegistration);
nsresult
SendPushSubscriptionChangeEvent();
nsresult
- SendNotificationClickEvent(const nsAString& aID,
- const nsAString& aTitle,
- const nsAString& aDir,
- const nsAString& aLang,
- const nsAString& aBody,
- const nsAString& aTag,
- const nsAString& aIcon,
- const nsAString& aData,
- const nsAString& aBehavior,
- const nsAString& aScope);
+ SendNotificationEvent(const nsAString& aEventName,
+ const nsAString& aID,
+ const nsAString& aTitle,
+ const nsAString& aDir,
+ const nsAString& aLang,
+ const nsAString& aBody,
+ const nsAString& aTag,
+ const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior,
+ const nsAString& aScope);
nsresult
SendFetchEvent(nsIInterceptedChannel* aChannel,
nsILoadGroup* aLoadGroup,
const nsAString& aDocumentId,
bool aIsReload);
void
@@ -144,16 +148,17 @@ public:
private:
enum WakeUpReason {
FetchEvent = 0,
PushEvent,
PushSubscriptionChangeEvent,
MessageEvent,
NotificationClickEvent,
+ NotificationCloseEvent,
LifeCycleEvent,
AttachEvent
};
// Timer callbacks
static void
NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate);
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -238,16 +238,17 @@ class ServiceWorkerGlobalScope final : p
~ServiceWorkerGlobalScope();
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
WorkerGlobalScope)
IMPL_EVENT_HANDLER(notificationclick)
+ IMPL_EVENT_HANDLER(notificationclose)
ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
virtual bool
WrapGlobalObject(JSContext* aCx,
JS::MutableHandle<JSObject*> aReflector) override;
static bool
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -128,16 +128,18 @@ support-files =
bug1151916_worker.js
bug1151916_driver.html
bug1240436_worker.js
notificationclick.html
notificationclick-otherwindow.html
notificationclick.js
notificationclick_focus.html
notificationclick_focus.js
+ notificationclose.html
+ notificationclose.js
worker_updatefoundevent.js
worker_updatefoundevent2.js
updatefoundevent.html
empty.js
notification_constructor_error.js
notification_get_sw.js
notification/register.html
notification/unregister.html
@@ -244,16 +246,17 @@ tags = mcb
[test_match_all_client_properties.html]
[test_navigator.html]
[test_not_intercept_plugin.html]
[test_notification_constructor_error.html]
[test_notification_get.html]
[test_notificationclick.html]
[test_notificationclick_focus.html]
[test_notificationclick-otherwindow.html]
+[test_notificationclose.html]
[test_opaque_intercept.html]
[test_openWindow.html]
[test_origin_after_redirect.html]
[test_origin_after_redirect_cached.html]
[test_origin_after_redirect_to_https.html]
[test_origin_after_redirect_to_https_cached.html]
[test_post_message.html]
[test_post_message_advanced.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.html
@@ -0,0 +1,37 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1265841 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ return swr.showNotification(
+ "Hi there. The ServiceWorker should receive a close event for this.",
+ { data: { complex: ["jsval", 5] }}).then(function() {
+ return swr;
+ });
+ }).then(function(swr) {
+ return swr.getNotifications();
+ }).then(function(notifications) {
+ notifications.forEach(function(notification) {
+ notification.close();
+ });
+ });
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ testWindow.callback(msg.data.result);
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.js
@@ -0,0 +1,19 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+onnotificationclose = function(e) {
+ self.clients.matchAll().then(function(clients) {
+ if (clients.length === 0) {
+ dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+ return;
+ }
+
+ clients.forEach(function(client) {
+ client.postMessage({ result: e.notification.data &&
+ e.notification.data['complex'] &&
+ e.notification.data['complex'][0] == "jsval" &&
+ e.notification.data['complex'][1] == 5 });
+
+ });
+ });
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notificationclose.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265841
+-->
+<head>
+ <title>Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event.</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=1265841">Bug 1265841</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, click, and close events.");
+
+ function testFrame(src) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ window.callback = null;
+ document.body.removeChild(iframe);
+ iframe = null;
+ ok(result, "Got notificationclose event with correct data.");
+ MockServices.unregister();
+ registration.unregister().then(function() {
+ SimpleTest.finish();
+ });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ var registration;
+
+ function runTest() {
+ MockServices.register();
+ testFrame('notificationclose.html');
+ navigator.serviceWorker.register("notificationclose.js", { scope: "notificationclose.html" }).then(function(reg) {
+ registration = reg;
+ }, function(e) {
+ ok(false, "registration should have passed!");
+ });
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, runTest);
+</script>
+</body>
+</html>