Diff
Modified: trunk/LayoutTests/ChangeLog (289865 => 289866)
--- trunk/LayoutTests/ChangeLog 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/LayoutTests/ChangeLog 2022-02-16 01:25:22 UTC (rev 289866)
@@ -1,3 +1,17 @@
+2022-02-15 Brady Eidson <beid...@apple.com>
+
+ Implement ServiceWorkerRegistration.getNotifications().
+ https://bugs.webkit.org/show_bug.cgi?id=236545
+
+ Reviewed by Alex Christensen.
+
+ * http/tests/workers/service/getnotifications-expected.txt: Added.
+ * http/tests/workers/service/getnotifications.html: Added.
+ * http/tests/workers/service/resources/shownotification-worker.js:
+ (async tryShow):
+ (async getNotes):
+ (async event):
+
2022-02-15 Fujii Hironori <hironori.fu...@sony.com>
fast/filter-image/filter-image-svg.html is failing to load the image due to a wrong path
Added: trunk/LayoutTests/http/tests/workers/service/getnotifications-expected.txt (0 => 289866)
--- trunk/LayoutTests/http/tests/workers/service/getnotifications-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/getnotifications-expected.txt 2022-02-16 01:25:22 UTC (rev 289866)
@@ -0,0 +1,43 @@
+This tests that persistent notifications created by a service worker are visible to getNotifications()
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Notification.permission is "granted"
+Got notifications
+There are 0 notifications
+
+Notification was shown
+Notification was shown
+Notification was shown
+Got notifications
+There are 3 notifications
+Title: Hello
+Body: Body1
+Tag: tag-a
+Title: There
+Body: Body2
+Tag: tag-b
+Title: Buddy
+Body: Body3
+Tag: tag-b
+
+Got notifications
+There are 1 notifications
+Title: Hello
+Body: Body1
+Tag: tag-a
+
+Got notifications
+There are 2 notifications
+Title: There
+Body: Body2
+Tag: tag-b
+Title: Buddy
+Body: Body3
+Tag: tag-b
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/http/tests/workers/service/getnotifications.html (0 => 289866)
--- trunk/LayoutTests/http/tests/workers/service/getnotifications.html (rev 0)
+++ trunk/LayoutTests/http/tests/workers/service/getnotifications.html 2022-02-16 01:25:22 UTC (rev 289866)
@@ -0,0 +1,91 @@
+<head>
+<script src=""
+<script src=""
+<script>
+
+function failTheTest(msg)
+{
+ testFailed(msg);
+ if (testRunner)
+ testRunner.notifyDone();
+}
+
+if (window.testRunner) {
+ testRunner.waitUntilDone();
+ testRunner.grantWebNotificationPermission(testURL);
+}
+
+description("This tests that persistent notifications created by a service worker are visible to getNotifications()");
+
+shouldBeEqualToString("Notification.permission", "granted");
+
+async function registerServiceWorker() {
+ var registration = await navigator.serviceWorker.register('resources/shownotification-worker.js');
+
+ if (!registration)
+ return testFailed("No registration");
+
+ if (registration.active) {
+ registration.active.postMessage("Start");
+ return;
+ }
+
+ var installingWorker = registration.installing;
+ if (!installingWorker)
+ failTheTest("No active *or* installing worker");
+
+ installingWorker.addEventListener("statechange", () => {
+ if (installingWorker.state === "activated") {
+ installingWorker.postMessage("getnotes");
+ }
+ });
+}
+
+var numberShown = 0;
+var numberGot = 0;
+
+function gotNotes(event)
+{
+ ++numberGot;
+
+ debug("Got notifications");
+ message = event.data.split('|')
+ for (var i = 1; i < message.length; ++i)
+ debug(message[i]);
+
+ if (numberGot == 1) {
+ event.source.postMessage("tryshow|Hello|Body1|tag-a");
+ event.source.postMessage("tryshow|There|Body2|tag-b");
+ event.source.postMessage("tryshow|Buddy|Body3|tag-b");
+ } else if (numberGot == 4) {
+ testCompleted();
+ }
+}
+
+function shown(event)
+{
+ debug("Notification was shown");
+ ++numberShown;
+ if (numberShown == 2) {
+ event.source.postMessage("getnotes");
+ event.source.postMessage("getnotes|tag-a");
+ event.source.postMessage("getnotes|tag-b");
+ }
+}
+
+navigator.serviceWorker.addEventListener('message', event => {
+ var messageName = event.data.split('|')[0];
+
+ if (messageName == "gotnotes") {
+ gotNotes(event);
+ } else if (messageName == "shown") {
+ shown(event);
+ } else {
+ debug("Unexpected message: " + event.data);
+ }
+});
+
+</script>
+</head>
+<body _onload_="registerServiceWorker()">
+</body>
Modified: trunk/LayoutTests/http/tests/workers/service/resources/shownotification-worker.js (289865 => 289866)
--- trunk/LayoutTests/http/tests/workers/service/resources/shownotification-worker.js 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/LayoutTests/http/tests/workers/service/resources/shownotification-worker.js 2022-02-16 01:25:22 UTC (rev 289866)
@@ -17,10 +17,24 @@
await messageClients("closed");
});
-self.addEventListener('message', async function(event) {
- // Reply if the show succeeded or failed.
+async function tryShow(message)
+{
+ var title, body, tag;
+ var components = message.split('|');
+
+ if (components.length == 1) {
+ title = "This is a notification";
+ } else {
+ title = components[1];
+ body = components[2];
+ tag = components[3];
+ }
+
try {
- await registration.showNotification("This is a notification");
+ await registration.showNotification(title, {
+ body: body,
+ tag: tag
+ });
} catch(error) {
await messageClients("showFailed");
return;
@@ -27,4 +41,36 @@
}
await messageClients("shown");
+}
+
+var seenNotes = new Set();
+
+async function getNotes(message)
+{
+ var tag = undefined;
+ var components = message.split('|');
+ if (components.length == 2)
+ tag = components[1];
+
+ var notifications = await registration.getNotifications({ tag: tag });
+ var reply = "gotnotes|There are " + notifications.length + " notifications|";
+
+ for (notification of notifications) {
+ if (seenNotes.has(notification))
+ messageClients("Saw the same notifcation twice through getNotifications(), this should not happen");
+ seenNotes.add(notification)
+
+ reply += "Title: " + notification.title + "|";
+ reply += "Body: " + notification.body + "|";
+ reply += "Tag: " + notification.tag + "|";
+ }
+ await messageClients(reply);
+}
+
+self.addEventListener('message', async function(event) {
+ var messageName = event.data.split('|')[0];
+ if (messageName == "tryshow")
+ await tryShow(event.data);
+ if (messageName == "getnotes")
+ await getNotes(event.data);
});
Modified: trunk/Source/WebCore/ChangeLog (289865 => 289866)
--- trunk/Source/WebCore/ChangeLog 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/Source/WebCore/ChangeLog 2022-02-16 01:25:22 UTC (rev 289866)
@@ -1,3 +1,30 @@
+2022-02-15 Brady Eidson <beid...@apple.com>
+
+ Implement ServiceWorkerRegistration.getNotifications().
+ https://bugs.webkit.org/show_bug.cgi?id=236545
+
+ Reviewed by Alex Christensen.
+
+ Test: http/tests/workers/service/getnotifications.html
+
+ Until we figure out just how "persistent" we want persistent notifications from service workers to be,
+ this is an impementation of getNotifications() that works for the current running of the user agent.
+
+ * Modules/notifications/Notification.cpp:
+ (WebCore::Notification::Notification):
+ (WebCore::Notification::~Notification):
+ (WebCore::Notification::copyForGetNotifications const):
+ (WebCore::Notification::contextDestroyed):
+ (WebCore::Notification::close):
+ * Modules/notifications/Notification.h:
+
+ * workers/service/ServiceWorkerRegistration.cpp:
+ (WebCore::ServiceWorkerRegistration::getNotifications):
+ (WebCore::ServiceWorkerRegistration::addNotificationToList):
+ (WebCore::ServiceWorkerRegistration::removeNotificationFromList):
+ (WebCore::ServiceWorkerRegistration::filteredNotificationList):
+ * workers/service/ServiceWorkerRegistration.h:
+
2022-02-15 Mark Lam <mark....@apple.com>
Make HeapType an enum class.
Modified: trunk/Source/WebCore/Modules/notifications/Notification.cpp (289865 => 289866)
--- trunk/Source/WebCore/Modules/notifications/Notification.cpp 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/Source/WebCore/Modules/notifications/Notification.cpp 2022-02-16 01:25:22 UTC (rev 289866)
@@ -72,9 +72,10 @@
{
if (context.isDocument())
m_notificationSource = NotificationSource::Document;
- else if (context.isServiceWorkerGlobalScope())
+ else if (context.isServiceWorkerGlobalScope()) {
m_notificationSource = NotificationSource::ServiceWorker;
- else
+ downcast<ServiceWorkerGlobalScope>(context).registration().addNotificationToList(*this);
+ } else
RELEASE_ASSERT_NOT_REACHED();
if (!options.icon.isEmpty()) {
@@ -84,9 +85,45 @@
}
}
-Notification::~Notification() = default;
+Notification::Notification(const Notification& other)
+ : ActiveDOMObject(other.scriptExecutionContext())
+ , m_title(other.m_title.isolatedCopy())
+ , m_direction(other.m_direction)
+ , m_lang(other.m_lang.isolatedCopy())
+ , m_body(other.m_body.isolatedCopy())
+ , m_tag(other.m_tag.isolatedCopy())
+ , m_icon(other.m_icon.isolatedCopy())
+ , m_state(other.m_state)
+ , m_notificationSource(other.m_notificationSource)
+ , m_contextIdentifier(other.m_contextIdentifier)
+{
+ suspendIfNeeded();
+}
+Notification::~Notification()
+{
+ if (auto* context = scriptExecutionContext()) {
+ if (context->isServiceWorkerGlobalScope())
+ downcast<ServiceWorkerGlobalScope>(context)->registration().removeNotificationFromList(*this);
+ }
+}
+Ref<Notification> Notification::copyForGetNotifications() const
+{
+ return adoptRef(*new Notification(*this));
+}
+
+void Notification::contextDestroyed()
+{
+ auto* context = scriptExecutionContext();
+ RELEASE_ASSERT(context);
+ if (context->isServiceWorkerGlobalScope())
+ downcast<ServiceWorkerGlobalScope>(context)->registration().removeNotificationFromList(*this);
+
+ ActiveDOMObject::contextDestroyed();
+}
+
+
void Notification::showSoon()
{
queueTaskKeepingObjectAlive(*this, TaskSource::UserInteraction, [this] {
@@ -130,6 +167,10 @@
case Showing: {
if (auto* client = clientFromContext())
client->cancel(*this);
+ if (auto* context = scriptExecutionContext()) {
+ if (context->isServiceWorkerGlobalScope())
+ downcast<ServiceWorkerGlobalScope>(context)->registration().removeNotificationFromList(*this);
+ }
break;
}
case Closed:
Modified: trunk/Source/WebCore/Modules/notifications/Notification.h (289865 => 289866)
--- trunk/Source/WebCore/Modules/notifications/Notification.h 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/Source/WebCore/Modules/notifications/Notification.h 2022-02-16 01:25:22 UTC (rev 289866)
@@ -94,12 +94,17 @@
WEBCORE_EXPORT NotificationData data() const;
+ Ref<Notification> copyForGetNotifications() const;
+
using ThreadSafeRefCounted::ref;
using ThreadSafeRefCounted::deref;
private:
Notification(ScriptExecutionContext&, const String& title, const Options&);
+ Notification(const Notification&);
+ void contextDestroyed() final;
+
NotificationClient* clientFromContext();
EventTargetInterface eventTargetInterface() const final { return NotificationEventTargetInterfaceType; }
Modified: trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp (289865 => 289866)
--- trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp 2022-02-16 01:25:22 UTC (rev 289866)
@@ -33,6 +33,7 @@
#include "EventLoop.h"
#include "EventNames.h"
#include "JSDOMPromiseDeferred.h"
+#include "JSNotification.h"
#include "Logging.h"
#include "NavigationPreloadManager.h"
#include "NotificationClient.h"
@@ -294,12 +295,49 @@
});
}
-void ServiceWorkerRegistration::getNotifications(ScriptExecutionContext&, const GetNotificationOptions& filter, DOMPromiseDeferred<IDLSequence<IDLDOMString>> promise)
+void ServiceWorkerRegistration::getNotifications(ScriptExecutionContext& context, const GetNotificationOptions& filter, DOMPromiseDeferred<IDLSequence<IDLInterface<Notification>>> promise)
{
- UNUSED_PARAM(filter);
+ auto notifications = filteredNotificationList(filter.tag);
+ context.eventLoop().queueTask(TaskSource::DOMManipulation, [promise = WTFMove(promise), notifications = WTFMove(notifications)]() mutable {
+ promise.resolve(WTFMove(notifications));
+ });
+}
- promise.reject(Exception { NotSupportedError, "ServiceWorkerRegistration.getNotifications not yet implemented"_s });
+void ServiceWorkerRegistration::addNotificationToList(Notification& notification)
+{
+ // A Notification at this exact address might previously have been in the list but not successfully removed.
+ // The WeakHashSet captures that possibility here to help us maintain consistency.
+ auto iter = m_notificationList.find(¬ification);
+ if (iter != m_notificationList.end()) {
+ RELEASE_ASSERT(!m_notificationSet.contains(notification));
+ m_notificationList.remove(iter);
+ }
+
+ m_notificationList.add(¬ification);
+ auto result = m_notificationSet.add(notification);
+ ASSERT_UNUSED(result, result.isNewEntry);
}
+
+void ServiceWorkerRegistration::removeNotificationFromList(Notification& notification)
+{
+ // The same notification might try to remove itself from this list more than once, and that's fine.
+ m_notificationList.remove(¬ification);
+ m_notificationSet.remove(notification);
+}
+
+Vector<Ref<Notification>> ServiceWorkerRegistration::filteredNotificationList(const String& filteredTag)
+{
+ Vector<Ref<Notification>> results;
+ for (auto* notification : m_notificationList) {
+ if (!m_notificationSet.contains(*notification))
+ continue;
+ if (filteredTag.isEmpty() || notification->tag() == filteredTag)
+ results.append(notification->copyForGetNotifications());
+ }
+
+ return results;
+}
+
#endif // ENABLE(NOTIFICATION_EVENT)
} // namespace WebCore
Modified: trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.h (289865 => 289866)
--- trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.h 2022-02-16 00:51:49 UTC (rev 289865)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerRegistration.h 2022-02-16 01:25:22 UTC (rev 289866)
@@ -30,6 +30,7 @@
#include "ActiveDOMObject.h"
#include "EventTarget.h"
#include "JSDOMPromiseDeferred.h"
+#include "Notification.h"
#include "NotificationOptions.h"
#include "PushPermissionState.h"
#include "PushSubscription.h"
@@ -37,6 +38,9 @@
#include "ServiceWorkerRegistrationData.h"
#include "Supplementable.h"
#include "Timer.h"
+#include <wtf/ListHashSet.h>
+#include <wtf/Vector.h>
+#include <wtf/WeakHashSet.h>
namespace WebCore {
@@ -96,7 +100,11 @@
};
void showNotification(ScriptExecutionContext&, const String& title, const NotificationOptions&, DOMPromiseDeferred<void>&&);
- void getNotifications(ScriptExecutionContext&, const GetNotificationOptions& filter, DOMPromiseDeferred<IDLSequence<IDLDOMString>>);
+ void getNotifications(ScriptExecutionContext&, const GetNotificationOptions& filter, DOMPromiseDeferred<IDLSequence<IDLInterface<Notification>>>);
+
+ void addNotificationToList(Notification&);
+ void removeNotificationFromList(Notification&);
+ Vector<Ref<Notification>> filteredNotificationList(const String& filteredTag);
#endif
private:
@@ -120,6 +128,11 @@
RefPtr<ServiceWorker> m_activeWorker;
std::unique_ptr<NavigationPreloadManager> m_navigationPreload;
+
+#if ENABLE(NOTIFICATION_EVENT)
+ ListHashSet<Notification*> m_notificationList;
+ WeakHashSet<Notification> m_notificationSet;
+#endif
};
} // namespace WebCore