Title: [291492] trunk
Revision
291492
Author
[email protected]
Date
2022-03-18 12:19:35 -0700 (Fri, 18 Mar 2022)

Log Message

Remove push subscriptions when associated service worker registrations are removed
https://bugs.webkit.org/show_bug.cgi?id=237983

Reviewed by Youenn Fablet.

Source/WebKit:

When a ServiceWorkerRegistration is removed, we also need to remove its associated
PushSubscription. This can occur when:

  1. The application calls ServiceWorkerRegistration.unregister. This is implemented by
     having resolveUnregistrationJobInClient call unsubscribeFromPushService in webpushd.
     The identifier passed to unsubscribeFromPushService is now optional; if the identifier
     is not present, then we delete whatever PushSubscription is associated with the given
     scope URL.

  2. The user clears website data for a particular origin. This is implemented by having
     deleteWebsiteDataForOrigins invoke removePushSubscriptionsForOrigin in webpushd.

  3. The user clears all website data. This is implemented by having deleteWebsiteData
     invoke removeAllPushSubscriptions in webpushd.

Covered by new API tests.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::deleteWebsiteData):
(WebKit::NetworkProcess::deleteWebsiteDataForOrigins):
(WebKit::NetworkProcess::deleteAndRestrictWebsiteDataForRegistrableDomains):
(WebKit::NetworkProcess::hasPushSubscriptionForTesting):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* NetworkProcess/Notifications/NetworkNotificationManager.cpp:
(WebKit::NetworkNotificationManager::unsubscribeFromPushService):
(WebKit::NetworkNotificationManager::removeAllPushSubscriptions):
(WebKit::NetworkNotificationManager::removePushSubscriptionsForOrigin):
* NetworkProcess/Notifications/NetworkNotificationManager.h:
* NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::resolveUnregistrationJobInClient):
* Shared/WebPushDaemonConstants.h:
(WebKit::WebPushD::messageTypeSendsReply):
* UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
(-[WKWebsiteDataStore _scopeURL:hasPushSubscriptionForTesting:]):
* UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::hasPushSubscriptionForTesting):
* UIProcess/Network/NetworkProcessProxy.h:
* webpushd/PushService.h:
* webpushd/PushService.mm:
(WebPushD::UnsubscribeRequest::UnsubscribeRequest):
(WebPushD::UnsubscribeRequest::startInternal):
(WebPushD::PushService::unsubscribe):
(WebPushD::PushService::incrementSilentPushCount):
(WebPushD::PushService::removeRecordsForBundleIdentifier):
(WebPushD::PushService::removeRecordsForBundleIdentifierAndOrigin):
(WebPushD::PushService::removeRecordsImpl):
* webpushd/WebPushDaemon.h:
* webpushd/WebPushDaemon.mm:
(WebPushD::MessageInfo::removeAllPushSubscriptions::encodeReply):
(WebPushD::MessageInfo::removePushSubscriptionsForOrigin::encodeReply):
(WebPushD::Daemon::decodeAndHandleMessage):
(WebPushD::Daemon::unsubscribeFromPushService):
(WebPushD::Daemon::removeAllPushSubscriptions):
(WebPushD::Daemon::removePushSubscriptionsForOrigin):

Tools:

Add new tests to make sure that we delete push subscriptions when unregistering a service
worker or deleting website data.

* TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm:

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (291491 => 291492)


--- trunk/Source/WebKit/ChangeLog	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/ChangeLog	2022-03-18 19:19:35 UTC (rev 291492)
@@ -1,3 +1,67 @@
+2022-03-18  Ben Nham  <[email protected]>
+
+        Remove push subscriptions when associated service worker registrations are removed
+        https://bugs.webkit.org/show_bug.cgi?id=237983
+
+        Reviewed by Youenn Fablet.
+
+        When a ServiceWorkerRegistration is removed, we also need to remove its associated
+        PushSubscription. This can occur when:
+
+          1. The application calls ServiceWorkerRegistration.unregister. This is implemented by
+             having resolveUnregistrationJobInClient call unsubscribeFromPushService in webpushd.
+             The identifier passed to unsubscribeFromPushService is now optional; if the identifier
+             is not present, then we delete whatever PushSubscription is associated with the given
+             scope URL.
+
+          2. The user clears website data for a particular origin. This is implemented by having
+             deleteWebsiteDataForOrigins invoke removePushSubscriptionsForOrigin in webpushd.
+
+          3. The user clears all website data. This is implemented by having deleteWebsiteData
+             invoke removeAllPushSubscriptions in webpushd.
+
+        Covered by new API tests.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::deleteWebsiteData):
+        (WebKit::NetworkProcess::deleteWebsiteDataForOrigins):
+        (WebKit::NetworkProcess::deleteAndRestrictWebsiteDataForRegistrableDomains):
+        (WebKit::NetworkProcess::hasPushSubscriptionForTesting):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcess.messages.in:
+        * NetworkProcess/Notifications/NetworkNotificationManager.cpp:
+        (WebKit::NetworkNotificationManager::unsubscribeFromPushService):
+        (WebKit::NetworkNotificationManager::removeAllPushSubscriptions):
+        (WebKit::NetworkNotificationManager::removePushSubscriptionsForOrigin):
+        * NetworkProcess/Notifications/NetworkNotificationManager.h:
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::resolveUnregistrationJobInClient):
+        * Shared/WebPushDaemonConstants.h:
+        (WebKit::WebPushD::messageTypeSendsReply):
+        * UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
+        (-[WKWebsiteDataStore _scopeURL:hasPushSubscriptionForTesting:]):
+        * UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::hasPushSubscriptionForTesting):
+        * UIProcess/Network/NetworkProcessProxy.h:
+        * webpushd/PushService.h:
+        * webpushd/PushService.mm:
+        (WebPushD::UnsubscribeRequest::UnsubscribeRequest):
+        (WebPushD::UnsubscribeRequest::startInternal):
+        (WebPushD::PushService::unsubscribe):
+        (WebPushD::PushService::incrementSilentPushCount):
+        (WebPushD::PushService::removeRecordsForBundleIdentifier):
+        (WebPushD::PushService::removeRecordsForBundleIdentifierAndOrigin):
+        (WebPushD::PushService::removeRecordsImpl):
+        * webpushd/WebPushDaemon.h:
+        * webpushd/WebPushDaemon.mm:
+        (WebPushD::MessageInfo::removeAllPushSubscriptions::encodeReply):
+        (WebPushD::MessageInfo::removePushSubscriptionsForOrigin::encodeReply):
+        (WebPushD::Daemon::decodeAndHandleMessage):
+        (WebPushD::Daemon::unsubscribeFromPushService):
+        (WebPushD::Daemon::removeAllPushSubscriptions):
+        (WebPushD::Daemon::removePushSubscriptionsForOrigin):
+
 2022-03-18  J Pascoe  <[email protected]>
 
         Trigger PDF download in captive portal mode instead of using PDF viewer

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2022-03-18 19:19:35 UTC (rev 291492)
@@ -1511,9 +1511,14 @@
 
 #if ENABLE(SERVICE_WORKER)
     bool clearServiceWorkers = websiteDataTypes.contains(WebsiteDataType::DOMCache) || websiteDataTypes.contains(WebsiteDataType::ServiceWorkerRegistrations);
-    if (clearServiceWorkers && !sessionID.isEphemeral() && session)
+    if (clearServiceWorkers && !sessionID.isEphemeral() && session) {
         session->ensureSWServer().clearAll([clearTasksHandler] { });
+
+#if ENABLE(BUILT_IN_NOTIFICATIONS)
+        session->notificationManager().removeAllPushSubscriptions([clearTasksHandler](auto&&) { });
 #endif
+    }
+#endif
 
 #if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
     if (websiteDataTypes.contains(WebsiteDataType::ResourceLoadStatistics)) {
@@ -1605,8 +1610,13 @@
     bool clearServiceWorkers = websiteDataTypes.contains(WebsiteDataType::DOMCache) || websiteDataTypes.contains(WebsiteDataType::ServiceWorkerRegistrations);
     if (clearServiceWorkers && !sessionID.isEphemeral() && session) {
         auto& server = session->ensureSWServer();
-        for (auto& originData : originDatas)
+        for (auto& originData : originDatas) {
             server.clear(originData, [clearTasksHandler] { });
+
+#if ENABLE(BUILT_IN_NOTIFICATIONS)
+            session->notificationManager().removePushSubscriptionsForOrigin(SecurityOriginData { originData }, [clearTasksHandler](auto&&) { });
+#endif
+        }
     }
 #endif
 
@@ -1798,8 +1808,13 @@
                 if (!domainsToDeleteAllNonCookieWebsiteDataFor.contains(RegistrableDomain::uncheckedCreateFromHost(securityOrigin.host)))
                     continue;
                 callbackAggregator->m_domains.add(RegistrableDomain::uncheckedCreateFromHost(securityOrigin.host));
-                if (session)
+                if (session) {
                     session->ensureSWServer().clear(securityOrigin, [callbackAggregator] { });
+
+#if ENABLE(BUILT_IN_NOTIFICATIONS)
+                    session->notificationManager().removePushSubscriptionsForOrigin(SecurityOriginData { securityOrigin }, [callbackAggregator](auto&&) { });
+#endif
+                }
             }
         });
     }
@@ -2314,6 +2329,20 @@
     callback({ });
 }
 
+void NetworkProcess::hasPushSubscriptionForTesting(PAL::SessionID sessionID, URL&& scopeURL, CompletionHandler<void(bool)>&& callback)
+{
+#if ENABLE(BUILT_IN_NOTIFICATIONS)
+    if (auto* session = networkSession(sessionID)) {
+        session->notificationManager().getPushSubscription(WTFMove(scopeURL), [callback = WTFMove(callback)](auto &&result) mutable {
+            callback(result && result->has_value());
+        });
+        return;
+    }
+#endif
+
+    callback(false);
+}
+
 void NetworkProcess::requestStorageSpace(PAL::SessionID sessionID, const ClientOrigin& origin, uint64_t quota, uint64_t currentSize, uint64_t spaceRequired, CompletionHandler<void(std::optional<uint64_t>)>&& callback)
 {
     parentProcessConnection()->sendWithAsyncReply(Messages::NetworkProcessProxy::RequestStorageSpace { sessionID, origin, quota, currentSize, spaceRequired }, WTFMove(callback), 0);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -376,6 +376,7 @@
 
     void deletePushAndNotificationRegistration(PAL::SessionID, const WebCore::SecurityOriginData&, CompletionHandler<void(const String&)>&&);
     void getOriginsWithPushAndNotificationPermissions(PAL::SessionID, CompletionHandler<void(const Vector<WebCore::SecurityOriginData>&)>&&);
+    void hasPushSubscriptionForTesting(PAL::SessionID, URL&&, CompletionHandler<void(bool)>&&);
 
 private:
     void platformInitializeNetworkProcess(const NetworkProcessCreationParameters&);

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in	2022-03-18 19:19:35 UTC (rev 291492)
@@ -215,4 +215,5 @@
 #endif
     DeletePushAndNotificationRegistration(PAL::SessionID sessionID, struct WebCore::SecurityOriginData origin) -> (String errorMessage)
     GetOriginsWithPushAndNotificationPermissions(PAL::SessionID sessionID) -> (Vector<WebCore::SecurityOriginData> origins)
+    HasPushSubscriptionForTesting(PAL::SessionID sessionID, URL scopeURL) -> (bool hasSubscription)
 }

Modified: trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.cpp (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.cpp	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.cpp	2022-03-18 19:19:35 UTC (rev 291492)
@@ -145,7 +145,7 @@
     sendMessageWithReply<WebPushD::MessageType::SubscribeToPushService>(WTFMove(completionHandler), WTFMove(scopeURL), WTFMove(applicationServerKey));
 }
 
-void NetworkNotificationManager::unsubscribeFromPushService(URL&& scopeURL, PushSubscriptionIdentifier pushSubscriptionIdentifier, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
+void NetworkNotificationManager::unsubscribeFromPushService(URL&& scopeURL, std::optional<PushSubscriptionIdentifier> pushSubscriptionIdentifier, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
 {
     if (!m_connection) {
         completionHandler(makeUnexpected(ExceptionData { AbortError, "No connection to push daemon"_s }));
@@ -185,6 +185,26 @@
     sendMessageWithReply<WebPushD::MessageType::IncrementSilentPushCount>(WTFMove(completionHandler), WTFMove(origin));
 }
 
+void NetworkNotificationManager::removeAllPushSubscriptions(CompletionHandler<void(unsigned)>&& completionHandler)
+{
+    if (!m_connection) {
+        completionHandler(0);
+        return;
+    }
+
+    sendMessageWithReply<WebPushD::MessageType::RemoveAllPushSubscriptions>(WTFMove(completionHandler));
+}
+
+void NetworkNotificationManager::removePushSubscriptionsForOrigin(WebCore::SecurityOriginData&& origin, CompletionHandler<void(unsigned)>&& completionHandler)
+{
+    if (!m_connection) {
+        completionHandler(0);
+        return;
+    }
+
+    sendMessageWithReply<WebPushD::MessageType::RemovePushSubscriptionsForOrigin>(WTFMove(completionHandler), WTFMove(origin));
+}
+
 template<WebPushD::MessageType messageType, typename... Args>
 void NetworkNotificationManager::sendMessage(Args&&... args) const
 {

Modified: trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.h (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/Notifications/NetworkNotificationManager.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -58,10 +58,12 @@
     void getPendingPushMessages(CompletionHandler<void(const Vector<WebPushMessage>&)>&&);
 
     void subscribeToPushService(URL&& scopeURL, Vector<uint8_t>&& applicationServerKey, CompletionHandler<void(Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>&&)>&&);
-    void unsubscribeFromPushService(URL&& scopeURL, WebCore::PushSubscriptionIdentifier, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&&);
+    void unsubscribeFromPushService(URL&& scopeURL, std::optional<WebCore::PushSubscriptionIdentifier>, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&&);
     void getPushSubscription(URL&& scopeURL, CompletionHandler<void(Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>&&)>&&);
     void getPushPermissionState(URL&& scopeURL, CompletionHandler<void(Expected<uint8_t, WebCore::ExceptionData>&&)>&&);
     void incrementSilentPushCount(WebCore::SecurityOriginData&&, CompletionHandler<void(unsigned)>&&);
+    void removeAllPushSubscriptions(CompletionHandler<void(unsigned)>&&);
+    void removePushSubscriptionsForOrigin(WebCore::SecurityOriginData&&, CompletionHandler<void(unsigned)>&&);
 
 private:
     NetworkNotificationManager(NetworkSession&, const String& webPushMachServiceName);

Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp (291491 => 291492)


--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp	2022-03-18 19:19:35 UTC (rev 291492)
@@ -111,8 +111,22 @@
 void WebSWServerConnection::resolveUnregistrationJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
 {
     ASSERT(m_unregisterJobs.contains(jobIdentifier));
-    if (auto completionHandler = m_unregisterJobs.take(jobIdentifier))
+    if (auto completionHandler = m_unregisterJobs.take(jobIdentifier)) {
+#if ENABLE(BUILT_IN_NOTIFICATIONS)
+        if (!session()) {
+            completionHandler(unregistrationResult);
+            return;
+        }
+        
+        auto scopeURL = registrationKey.scope();
+        session()->notificationManager().unsubscribeFromPushService(WTFMove(scopeURL), std::nullopt, [completionHandler = WTFMove(completionHandler), unregistrationResult](auto&&) mutable {
+            completionHandler(unregistrationResult);
+        });
+        
+#else
         completionHandler(unregistrationResult);
+#endif
+    }
 }
 
 void WebSWServerConnection::startScriptFetchInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy)

Modified: trunk/Source/WebKit/Shared/WebPushDaemonConstants.h (291491 => 291492)


--- trunk/Source/WebKit/Shared/WebPushDaemonConstants.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/Shared/WebPushDaemonConstants.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -53,6 +53,8 @@
     GetPushSubscription,
     GetPushPermissionState,
     IncrementSilentPushCount,
+    RemoveAllPushSubscriptions,
+    RemovePushSubscriptionsForOrigin,
 };
 
 inline bool messageTypeSendsReply(MessageType messageType)
@@ -70,6 +72,8 @@
     case MessageType::GetPushSubscription:
     case MessageType::GetPushPermissionState:
     case MessageType::IncrementSilentPushCount:
+    case MessageType::RemoveAllPushSubscriptions:
+    case MessageType::RemovePushSubscriptionsForOrigin:
         return true;
     case MessageType::SetDebugModeIsEnabled:
     case MessageType::UpdateConnectionConfiguration:

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm (291491 => 291492)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm	2022-03-18 19:19:35 UTC (rev 291492)
@@ -835,4 +835,13 @@
     });
 }
 
+-(void)_scopeURL:(NSURL *)scopeURL hasPushSubscriptionForTesting:(void(^)(BOOL))completionHandler
+{
+    auto completionHandlerCopy = makeBlockPtr(completionHandler);
+    _websiteDataStore->networkProcess()
+        .hasPushSubscriptionForTesting(_websiteDataStore->sessionID(), scopeURL, [completionHandlerCopy](bool result) {
+            completionHandlerCopy(result);
+        });
+}
+
 @end

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h (291491 => 291492)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -115,6 +115,7 @@
 -(void)_processPushMessage:(NSDictionary *)pushMessage completionHandler:(void(^)(bool))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 -(void)_deletePushAndNotificationRegistration:(WKSecurityOrigin *)securityOrigin completionHandler:(void(^)(NSError *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 -(void)_getOriginsWithPushAndNotificationPermissions:(void(^)(NSSet<WKSecurityOrigin *> *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+-(void)_scopeURL:(NSURL *)scopeURL hasPushSubscriptionForTesting:(void(^)(BOOL))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 @end
 
 NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp (291491 => 291492)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp	2022-03-18 19:19:35 UTC (rev 291492)
@@ -1791,6 +1791,11 @@
     sendWithAsyncReply(Messages::NetworkProcess::GetOriginsWithPushAndNotificationPermissions { sessionID }, WTFMove(callback));
 }
 
+void NetworkProcessProxy::hasPushSubscriptionForTesting(PAL::SessionID sessionID, const URL& scopeURL, CompletionHandler<void(bool)>&& callback)
+{
+    sendWithAsyncReply(Messages::NetworkProcess::HasPushSubscriptionForTesting { sessionID, scopeURL }, WTFMove(callback));
+}
+
 void NetworkProcessProxy::terminateRemoteWorkerContextConnectionWhenPossible(RemoteWorkerType workerType, PAL::SessionID sessionID, const WebCore::RegistrableDomain& registrableDomain, WebCore::ProcessIdentifier processIdentifier)
 {
     send(Messages::NetworkProcess::TerminateRemoteWorkerContextConnectionWhenPossible(workerType, sessionID, registrableDomain, processIdentifier), 0);

Modified: trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h (291491 => 291492)


--- trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/UIProcess/Network/NetworkProcessProxy.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -291,6 +291,7 @@
 
     void deletePushAndNotificationRegistration(PAL::SessionID, const WebCore::SecurityOriginData&, CompletionHandler<void(const String&)>&&);
     void getOriginsWithPushAndNotificationPermissions(PAL::SessionID, CompletionHandler<void(const Vector<WebCore::SecurityOriginData>&)>&&);
+    void hasPushSubscriptionForTesting(PAL::SessionID, const URL&, CompletionHandler<void(bool)>&&);
 
     void dataTaskReceivedChallenge(DataTaskIdentifier, WebCore::AuthenticationChallenge&&, CompletionHandler<void(AuthenticationChallengeDisposition, WebCore::Credential&&)>&&);
     void dataTaskWillPerformHTTPRedirection(DataTaskIdentifier, WebCore::ResourceResponse&&, WebCore::ResourceRequest&&, CompletionHandler<void(bool)>&&);

Modified: trunk/Source/WebKit/webpushd/PushService.h (291491 => 291492)


--- trunk/Source/WebKit/webpushd/PushService.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/webpushd/PushService.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -59,8 +59,11 @@
 
     void getSubscription(const String& bundleIdentifier, const String& scope, CompletionHandler<void(const Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>&)>&&);
     void subscribe(const String& bundleIdentifier, const String& scope, const Vector<uint8_t>& vapidPublicKey, CompletionHandler<void(const Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>&)>&&);
-    void unsubscribe(const String& bundleIdentifier, const String& scope, WebCore::PushSubscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&&);
+    void unsubscribe(const String& bundleIdentifier, const String& scope, std::optional<WebCore::PushSubscriptionIdentifier>, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&&);
 
+    void removeRecordsForBundleIdentifier(const String& bundleIdentifier, CompletionHandler<void(unsigned)>&&);
+    void removeRecordsForBundleIdentifierAndOrigin(const String& bundleIdentifier, const String& securityOrigin, CompletionHandler<void(unsigned)>&&);
+
     void incrementSilentPushCount(const String& bundleIdentifier, const String& securityOrigin, CompletionHandler<void(unsigned)>&&);
 
     void didCompleteGetSubscriptionRequest(GetSubscriptionRequest&);
@@ -75,6 +78,8 @@
     using PushServiceRequestMap = HashMap<std::tuple<String, String>, Deque<std::unique_ptr<PushServiceRequest>>>;
     void enqueuePushServiceRequest(PushServiceRequestMap&, std::unique_ptr<PushServiceRequest>&&);
     void finishedPushServiceRequest(PushServiceRequestMap&, PushServiceRequest&);
+    
+    void removeRecordsImpl(const String& bundleIdentifier, const std::optional<String>& securityOrigin, CompletionHandler<void(unsigned)>&&);
 
     UniqueRef<PushServiceConnection> m_connection;
     UniqueRef<WebCore::PushDatabase> m_database;

Modified: trunk/Source/WebKit/webpushd/PushService.mm (291491 => 291492)


--- trunk/Source/WebKit/webpushd/PushService.mm	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/webpushd/PushService.mm	2022-03-18 19:19:35 UTC (rev 291492)
@@ -349,7 +349,7 @@
 
 class UnsubscribeRequest : public PushServiceRequestImpl<bool> {
 public:
-    UnsubscribeRequest(PushService&, const String& bundleIdentifier, const String& scope, PushSubscriptionIdentifier, ResultHandler&&);
+    UnsubscribeRequest(PushService&, const String& bundleIdentifier, const String& scope, std::optional<PushSubscriptionIdentifier>, ResultHandler&&);
     virtual ~UnsubscribeRequest() = default;
 
 protected:
@@ -358,10 +358,10 @@
     void finish() final { m_service.didCompleteUnsubscribeRequest(*this); }
 
 private:
-    PushSubscriptionIdentifier m_subscriptionIdentifier;
+    std::optional<PushSubscriptionIdentifier> m_subscriptionIdentifier;
 };
 
-UnsubscribeRequest::UnsubscribeRequest(PushService& service, const String& bundleIdentifier, const String& scope, PushSubscriptionIdentifier subscriptionIdentifier, ResultHandler&& resultHandler)
+UnsubscribeRequest::UnsubscribeRequest(PushService& service, const String& bundleIdentifier, const String& scope, std::optional<PushSubscriptionIdentifier> subscriptionIdentifier, ResultHandler&& resultHandler)
     : PushServiceRequestImpl(service, bundleIdentifier, scope, WTFMove(resultHandler))
     , m_subscriptionIdentifier(subscriptionIdentifier)
 {
@@ -371,32 +371,25 @@
 void UnsubscribeRequest::startInternal()
 {
     m_database.getRecordByBundleIdentifierAndScope(m_bundleIdentifier, m_scope, [this](auto&& result) mutable {
-        if (!result || m_subscriptionIdentifier != result->identifier) {
+        if (!result || (m_subscriptionIdentifier && *m_subscriptionIdentifier != result->identifier)) {
             fulfill(false);
             return;
         }
+        
+        m_database.removeRecordByIdentifier(result->identifier, [this, serverVAPIDPublicKey = result->serverVAPIDPublicKey](bool removed) mutable {
+            if (!removed) {
+                fulfill(false);
+                return;
+            }
 
-        auto topic = makePushTopic(m_bundleIdentifier, m_scope);
-        m_connection.unsubscribe(topic, result->serverVAPIDPublicKey, [this](bool unsubscribed, NSError *error) mutable {
-#if !RELEASE_LOG_DISABLED
-            // If we fail to unsubscribe from apsd, just drop a log. We still want to continue and remove the record from our database in case there's a state mismatch between our database and apsd's database.
-            if (!unsubscribed)
-                RELEASE_LOG(Push, "PushSubscription.unsubscribe(bundleID: %{public}s, scope: %{sensitive}s) failed with domain: %{public}s code: %lld)", m_bundleIdentifier.utf8().data(), m_scope.utf8().data(), error.domain.UTF8String ?: "none", static_cast<int64_t>(error.code));
-#else
-            UNUSED_PARAM(unsubscribed);
-            UNUSED_PARAM(error);
-#endif
+            // FIXME: support partial topic list updates.
+            updateTopicLists(m_connection, m_database, [this]() mutable {
+                fulfill(true);
+            });
 
-            m_database.removeRecordByIdentifier(m_subscriptionIdentifier, [this](bool removed) mutable {
-                if (!removed) {
-                    fulfill(false);
-                    return;
-                }
-
-                // FIXME: support partial topic list updates.
-                updateTopicLists(m_connection, m_database, [this]() mutable {
-                    fulfill(true);
-                });
+            auto topic = makePushTopic(m_bundleIdentifier, m_scope);
+            m_connection.unsubscribe(topic, serverVAPIDPublicKey, [this](bool unsubscribed, NSError *error) mutable {
+                RELEASE_LOG_ERROR_IF(!unsubscribed, Push, "PushSubscription.unsubscribe(bundleID: %{public}s, scope: %{sensitive}s) failed with domain: %{public}s code: %lld)", m_bundleIdentifier.utf8().data(), m_scope.utf8().data(), error.domain.UTF8String ?: "none", static_cast<int64_t>(error.code));
             });
         });
     });
@@ -463,7 +456,7 @@
     finishedPushServiceRequest(m_subscribeRequests, request);
 }
 
-void PushService::unsubscribe(const String& bundleIdentifier, const String& scope, PushSubscriptionIdentifier subscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& completionHandler)
+void PushService::unsubscribe(const String& bundleIdentifier, const String& scope, std::optional<PushSubscriptionIdentifier> subscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& completionHandler)
 {
     enqueuePushServiceRequest(m_unsubscribeRequests, makeUnique<UnsubscribeRequest>(*this, bundleIdentifier, scope, subscriptionIdentifier, WTFMove(completionHandler)));
 }
@@ -483,19 +476,40 @@
 
         RELEASE_LOG(Push, "Removing all subscriptions associated with %{public}s %{sensitive}s since it processed %u silent pushes", bundleIdentifier.utf8().data(), securityOrigin.utf8().data(), silentPushCount);
 
-        m_database->removeRecordsByBundleIdentifierAndSecurityOrigin(bundleIdentifier, securityOrigin, [this, bundleIdentifier, securityOrigin, silentPushCount, handler = WTFMove(handler)](auto&& removedRecords) mutable {
-            for (auto& record : removedRecords) {
-                m_connection->unsubscribe(record.topic, record.serverVAPIDPublicKey, [topic = record.topic](bool unsubscribed, NSError* error) {
-                    if (!unsubscribed)
-                        RELEASE_LOG_ERROR(Push, "IncrementSilentPushRequest couldn't remove subscription for topic %{sensitive}s: %{public}s code: %lld)", topic.utf8().data(), error.domain.UTF8String ?: "none", static_cast<int64_t>(error.code));
-                });
-            }
+        removeRecordsImpl(bundleIdentifier, securityOrigin, [handler = WTFMove(handler), silentPushCount](auto&&) mutable {
+            handler(silentPushCount);
+        });
+    });
+}
 
-            updateTopicLists(m_connection, m_database, [silentPushCount, handler = WTFMove(handler)]() mutable {
-                handler(silentPushCount);
+void PushService::removeRecordsForBundleIdentifier(const String& bundleIdentifier, CompletionHandler<void(unsigned)>&& handler)
+{
+    removeRecordsImpl(bundleIdentifier, std::nullopt, WTFMove(handler));
+}
+
+void PushService::removeRecordsForBundleIdentifierAndOrigin(const String& bundleIdentifier, const String& securityOrigin, CompletionHandler<void(unsigned)>&& handler)
+{
+    removeRecordsImpl(bundleIdentifier, securityOrigin, WTFMove(handler));
+}
+
+void PushService::removeRecordsImpl(const String& bundleIdentifier, const std::optional<String>& securityOrigin, CompletionHandler<void(unsigned)>&& handler)
+{
+    auto removedRecordsHandler = [this, bundleIdentifier, securityOrigin, handler = WTFMove(handler)](Vector<RemovedPushRecord>&& removedRecords) mutable {
+        for (auto& record : removedRecords) {
+            m_connection->unsubscribe(record.topic, record.serverVAPIDPublicKey, [topic = record.topic](bool unsubscribed, NSError* error) {
+                RELEASE_LOG_ERROR_IF(!unsubscribed, Push, "removeRecordsImpl couldn't remove subscription for topic %{sensitive}s: %{public}s code: %lld)", topic.utf8().data(), error.domain.UTF8String ?: "none", static_cast<int64_t>(error.code));
             });
+        }
+
+        updateTopicLists(m_connection, m_database, [count = removedRecords.size(), handler = WTFMove(handler)]() mutable {
+            handler(count);
         });
-    });
+    };
+
+    if (securityOrigin)
+        m_database->removeRecordsByBundleIdentifierAndSecurityOrigin(bundleIdentifier, *securityOrigin, WTFMove(removedRecordsHandler));
+    else
+        m_database->removeRecordsByBundleIdentifier(bundleIdentifier, WTFMove(removedRecordsHandler));
 }
 
 enum class ContentEncoding {

Modified: trunk/Source/WebKit/webpushd/WebPushDaemon.h (291491 => 291492)


--- trunk/Source/WebKit/webpushd/WebPushDaemon.h	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/webpushd/WebPushDaemon.h	2022-03-18 19:19:35 UTC (rev 291492)
@@ -81,10 +81,12 @@
     void injectEncryptedPushMessageForTesting(ClientConnection*, const String&, CompletionHandler<void(bool)>&&);
     void getPendingPushMessages(ClientConnection*, CompletionHandler<void(const Vector<WebKit::WebPushMessage>&)>&& replySender);
     void subscribeToPushService(ClientConnection*, const URL& scopeURL, const Vector<uint8_t>& applicationServerKey, CompletionHandler<void(const Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>&)>&& replySender);
-    void unsubscribeFromPushService(ClientConnection*, const URL& scopeURL, WebCore::PushSubscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& replySender);
+    void unsubscribeFromPushService(ClientConnection*, const URL& scopeURL, std::optional<WebCore::PushSubscriptionIdentifier>, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& replySender);
     void getPushSubscription(ClientConnection*, const URL& scopeURL, CompletionHandler<void(const Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>&)>&& replySender);
     void getPushPermissionState(ClientConnection*, const URL& scopeURL, CompletionHandler<void(const Expected<uint8_t, WebCore::ExceptionData>&)>&& replySender);
     void incrementSilentPushCount(ClientConnection*, const WebCore::SecurityOriginData&, CompletionHandler<void(unsigned)>&&);
+    void removeAllPushSubscriptions(ClientConnection*, CompletionHandler<void(unsigned)>&&);
+    void removePushSubscriptionsForOrigin(ClientConnection*, const WebCore::SecurityOriginData&, CompletionHandler<void(unsigned)>&&);
 
     void broadcastDebugMessage(const String&);
     void broadcastAllConnectionIdentities();

Modified: trunk/Source/WebKit/webpushd/WebPushDaemon.mm (291491 => 291492)


--- trunk/Source/WebKit/webpushd/WebPushDaemon.mm	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Source/WebKit/webpushd/WebPushDaemon.mm	2022-03-18 19:19:35 UTC (rev 291492)
@@ -105,7 +105,7 @@
 END
 
 FUNCTION(unsubscribeFromPushService)
-ARGUMENTS(URL, WebCore::PushSubscriptionIdentifier)
+ARGUMENTS(URL, std::optional<WebCore::PushSubscriptionIdentifier>)
 REPLY(const Expected<bool, WebCore::ExceptionData>&)
 END
 
@@ -124,6 +124,16 @@
 REPLY(unsigned)
 END
 
+FUNCTION(removeAllPushSubscriptions)
+ARGUMENTS()
+REPLY(unsigned)
+END
+
+FUNCTION(removePushSubscriptionsForOrigin)
+ARGUMENTS(WebCore::SecurityOriginData)
+REPLY(unsigned)
+END
+
 #undef FUNCTION
 #undef ARGUMENTS
 #undef REPLY
@@ -213,6 +223,20 @@
     return encoder.takeBuffer();
 }
 
+WebPushD::EncodedMessage removeAllPushSubscriptions::encodeReply(unsigned reply)
+{
+    WebKit::Daemon::Encoder encoder;
+    encoder << reply;
+    return encoder.takeBuffer();
+}
+
+WebPushD::EncodedMessage removePushSubscriptionsForOrigin::encodeReply(unsigned reply)
+{
+    WebKit::Daemon::Encoder encoder;
+    encoder << reply;
+    return encoder.takeBuffer();
+}
+
 } // namespace MessageInfo
 
 template<typename Info>
@@ -415,6 +439,12 @@
     case MessageType::IncrementSilentPushCount:
         handleWebPushDMessageWithReply<MessageInfo::incrementSilentPushCount>(clientConnection, encodedMessage, WTFMove(replySender));
         break;
+    case MessageType::RemoveAllPushSubscriptions:
+        handleWebPushDMessageWithReply<MessageInfo::removeAllPushSubscriptions>(clientConnection, encodedMessage, WTFMove(replySender));
+        break;
+    case MessageType::RemovePushSubscriptionsForOrigin:
+        handleWebPushDMessageWithReply<MessageInfo::removePushSubscriptionsForOrigin>(clientConnection, encodedMessage, WTFMove(replySender));
+        break;
     }
 }
 
@@ -609,7 +639,7 @@
     });
 }
 
-void Daemon::unsubscribeFromPushService(ClientConnection* connection, const URL& scopeURL, WebCore::PushSubscriptionIdentifier subscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& replySender)
+void Daemon::unsubscribeFromPushService(ClientConnection* connection, const URL& scopeURL, std::optional<WebCore::PushSubscriptionIdentifier> subscriptionIdentifier, CompletionHandler<void(const Expected<bool, WebCore::ExceptionData>&)>&& replySender)
 {
     runAfterStartingPushService([this, bundleIdentifier = connection->hostAppCodeSigningIdentifier(), scope = scopeURL.string(), subscriptionIdentifier, replySender = WTFMove(replySender)]() mutable {
         if (!m_pushService) {
@@ -653,6 +683,30 @@
     });
 }
 
+void Daemon::removeAllPushSubscriptions(ClientConnection* connection, CompletionHandler<void(unsigned)>&& replySender)
+{
+    runAfterStartingPushService([this, bundleIdentifier = connection->hostAppCodeSigningIdentifier(), replySender = WTFMove(replySender)]() mutable {
+        if (!m_pushService) {
+            replySender(0);
+            return;
+        }
+
+        m_pushService->removeRecordsForBundleIdentifier(bundleIdentifier, WTFMove(replySender));
+    });
+}
+
+void Daemon::removePushSubscriptionsForOrigin(ClientConnection* connection, const WebCore::SecurityOriginData& securityOrigin, CompletionHandler<void(unsigned)>&& replySender)
+{
+    runAfterStartingPushService([this, bundleIdentifier = connection->hostAppCodeSigningIdentifier(), securityOrigin = securityOrigin.toString(), replySender = WTFMove(replySender)]() mutable {
+        if (!m_pushService) {
+            replySender(0);
+            return;
+        }
+
+        m_pushService->removeRecordsForBundleIdentifierAndOrigin(bundleIdentifier, securityOrigin, WTFMove(replySender));
+    });
+}
+
 ClientConnection* Daemon::toClientConnection(xpc_connection_t connection)
 {
     auto clientConnection = m_connectionMap.get(connection);

Modified: trunk/Tools/ChangeLog (291491 => 291492)


--- trunk/Tools/ChangeLog	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Tools/ChangeLog	2022-03-18 19:19:35 UTC (rev 291492)
@@ -1,3 +1,15 @@
+2022-03-18  Ben Nham  <[email protected]>
+
+        Remove push subscriptions when associated service worker registrations are removed
+        https://bugs.webkit.org/show_bug.cgi?id=237983
+
+        Reviewed by Youenn Fablet.
+
+        Add new tests to make sure that we delete push subscriptions when unregistering a service
+        worker or deleting website data.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm:
+
 2022-03-18  J Pascoe  <[email protected]>
 
         Trigger PDF download in captive portal mode instead of using PDF viewer

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm (291491 => 291492)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm	2022-03-18 19:11:11 UTC (rev 291491)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebPushDaemon.mm	2022-03-18 19:19:35 UTC (rev 291492)
@@ -36,6 +36,7 @@
 #import <WebKit/WKPreferencesPrivate.h>
 #import <WebKit/WKProcessPoolPrivate.h>
 #import <WebKit/WKUIDelegatePrivate.h>
+#import <WebKit/WKWebsiteDataRecordPrivate.h>
 #import <WebKit/WKWebsiteDataStorePrivate.h>
 #import <WebKit/WebPushDaemonConstants.h>
 #import <WebKit/_WKExperimentalFeature.h>
@@ -434,6 +435,20 @@
         [m_webView loadRequest:m_server->request()];
     }
 
+    bool hasPushSubscription()
+    {
+        __block bool done = false;
+        __block bool result = false;
+
+        [m_dataStore _scopeURL:m_server->request().URL hasPushSubscriptionForTesting:^(BOOL fetchedResult) {
+            result = fetchedResult;
+            done = true;
+        }];
+
+        TestWebKitAPI::Util::run(&done);
+        return result;
+    }
+
     ~WebPushDTest()
     {
         cleanUpTestWebPushD(m_tempDirectory.get());
@@ -629,6 +644,8 @@
 
     // Client public key should be 65 bytes (87 bytes in unpadded base64url).
     ASSERT_EQ([subscription[@"keys"][@"p256dh"] length], 87u);
+
+    ASSERT_TRUE(hasPushSubscription());
 }
 
 TEST_F(WebPushDTest, SubscribeFailureTest)
@@ -666,6 +683,8 @@
     // Spec says that an error in the push service should be an AbortError.
     ASSERT_TRUE([obj isKindOfClass:[NSString class]]);
     ASSERT_TRUE([obj hasPrefix:@"Error: AbortError"]);
+
+    ASSERT_FALSE(hasPushSubscription());
 }
 
 TEST_F(WebPushDTest, UnsubscribeTest)
@@ -705,8 +724,151 @@
     // First unsubscribe should succeed. Second one should fail since the first one removed the record from the database.
     id expected = @[@(1), @(0)];
     ASSERT_TRUE([obj isEqual:expected]);
+
+    ASSERT_FALSE(hasPushSubscription());
 }
 
+TEST_F(WebPushDTest, UnsubscribesOnServiceWorkerUnregisterTest)
+{
+    static const char* source = R"HTML(
+    <script src=""
+    <script>
+    navigator.serviceWorker.register('/sw.js').then(async () => {
+        const registration = await navigator.serviceWorker.ready;
+        let result = null;
+        try {
+            let subscription = await registration.pushManager.subscribe({
+                userVisibleOnly: true,
+                applicationServerKey: VALID_SERVER_KEY
+            });
+            result = await registration.unregister();
+        } catch (e) {
+            result = "Error: " + e;
+        }
+        window.webkit.messageHandlers.note.postMessage(result);
+    });
+    </script>
+    )HTML";
+
+    __block RetainPtr<id> unregisterSucceeded = nil;
+    __block bool done = false;
+    [m_notificationMessageHandler setMessageHandler:^(id message) {
+        unregisterSucceeded = message;
+        done = true;
+    }];
+
+    loadRequest(source, "");
+    TestWebKitAPI::Util::run(&done);
+
+    ASSERT_TRUE([unregisterSucceeded isEqual:@YES]);
+    ASSERT_FALSE(hasPushSubscription());
+}
+
+TEST_F(WebPushDTest, UnsubscribesOnClearingAllWebsiteData)
+{
+    static const char* source = R"HTML(
+    <script src=""
+    <script>
+    navigator.serviceWorker.register('/sw.js').then(async () => {
+        const registration = await navigator.serviceWorker.ready;
+        let result = null;
+        try {
+            let subscription = await registration.pushManager.subscribe({
+                userVisibleOnly: true,
+                applicationServerKey: VALID_SERVER_KEY
+            });
+            result = "Subscribed";
+        } catch (e) {
+            result = "Error: " + e;
+        }
+        window.webkit.messageHandlers.note.postMessage(result);
+    });
+    </script>
+    )HTML";
+
+    __block RetainPtr<id> result = nil;
+    __block bool done = false;
+    [m_notificationMessageHandler setMessageHandler:^(id message) {
+        result = message;
+        done = true;
+    }];
+
+    loadRequest(source, "");
+    TestWebKitAPI::Util::run(&done);
+
+    ASSERT_TRUE([result isEqualToString:@"Subscribed"]);
+
+    __block bool removedData = false;
+    [m_dataStore removeDataOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] modifiedSince:[NSDate distantPast] completionHandler:^(void) {
+        removedData = true;
+    }];
+    TestWebKitAPI::Util::run(&removedData);
+
+    ASSERT_FALSE(hasPushSubscription());
+}
+
+TEST_F(WebPushDTest, UnsubscribesOnClearingWebsiteDataForOrigin)
+{
+    static const char* source = R"HTML(
+    <script src=""
+    <script>
+    navigator.serviceWorker.register('/sw.js').then(async () => {
+        const registration = await navigator.serviceWorker.ready;
+        let result = null;
+        try {
+            let subscription = await registration.pushManager.subscribe({
+                userVisibleOnly: true,
+                applicationServerKey: VALID_SERVER_KEY
+            });
+            result = "Subscribed";
+        } catch (e) {
+            result = "Error: " + e;
+        }
+        window.webkit.messageHandlers.note.postMessage(result);
+    });
+    </script>
+    )HTML";
+
+    __block RetainPtr<id> result = nil;
+    __block bool done = false;
+    [m_notificationMessageHandler setMessageHandler:^(id message) {
+        result = message;
+        done = true;
+    }];
+
+    loadRequest(source, "");
+    TestWebKitAPI::Util::run(&done);
+
+    ASSERT_TRUE([result isEqualToString:@"Subscribed"]);
+
+    __block bool fetchedRecords = false;
+    __block RetainPtr<NSArray<WKWebsiteDataRecord *>> records;
+    [m_dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
+        records = dataRecords;
+        fetchedRecords = true;
+    }];
+    TestWebKitAPI::Util::run(&fetchedRecords);
+
+    WKWebsiteDataRecord *filteredRecord = nil;
+    for (WKWebsiteDataRecord *record in records.get()) {
+        for (NSString *originString in record._originsStrings) {
+            if ([originString isEqualToString:m_server->origin()]) {
+                filteredRecord = record;
+                break;
+            }
+        }
+    }
+    ASSERT_TRUE(filteredRecord);
+
+    __block bool removedData = false;
+    [m_dataStore removeDataOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] forDataRecords:[NSArray arrayWithObject:filteredRecord] completionHandler:^(void) {
+        removedData = true;
+    }];
+    TestWebKitAPI::Util::run(&removedData);
+
+    ASSERT_FALSE(hasPushSubscription());
+}
+
 #if ENABLE(INSTALL_COORDINATION_BUNDLES)
 #if USE(APPLE_INTERNAL_SDK)
 static void deleteAllRegistrationsForDataStore(WKWebsiteDataStore *dataStore)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to