Title: [289866] trunk
Revision
289866
Author
beid...@apple.com
Date
2022-02-15 17:25:22 -0800 (Tue, 15 Feb 2022)

Log Message

Implement ServiceWorkerRegistration.getNotifications().
https://bugs.webkit.org/show_bug.cgi?id=236545

Reviewed by Alex Christensen.

Source/WebCore:

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:

LayoutTests:

* 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):

Modified Paths

Added Paths

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(&notification);
+    if (iter != m_notificationList.end()) {
+        RELEASE_ASSERT(!m_notificationSet.contains(notification));
+        m_notificationList.remove(iter);
+    }
+
+    m_notificationList.add(&notification);
+    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(&notification);
+    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
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to