Diff
Modified: trunk/Source/WebCore/ChangeLog (291466 => 291467)
--- trunk/Source/WebCore/ChangeLog 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/ChangeLog 2022-03-18 07:33:15 UTC (rev 291467)
@@ -1,3 +1,41 @@
+2022-03-18 Youenn Fablet <[email protected]>
+
+ Keep service workers alive when they are inspected even though they should be terminated
+ https://bugs.webkit.org/show_bug.cgi?id=237827
+ <rdar://88313935>
+
+ Reviewed by Alex Christensen.
+
+ Store in SWServerWorker whether a worker is inspected or is processing push events.
+ In that case, we delay termination of workers until it is no longer inspected or no longer processing push events.
+ Two code paths are happening:
+ 1. A service worker was triggered with service worker clients, and all service worker clients are removed.
+ At that point, we were previously terminating service workers after a delay.
+ Instead, we now only terminate workers that are no longer inspected or no longer processing push events.
+ We reschedule the timer to continue trying removing the context connection.
+ 2. A service worker is not stopped by removal of service worker clients. In that case, we need to terminate
+ the service workers when inspected and/or push counter gets back to regular (not inspected, no push counter).
+ When terminating such a service worker, we try removing the context connection as well.
+
+ To make sure SWServerWorker knows whether inspectable or not, we add connection support to transmit whether inspected from WebProcess.
+ ServiceWorkerInspectorProxy is responsible to update the inspectable value.
+ Introduce internals API to set inspected state of a service worker.
+
+ Covered by new API tests.
+
+ * testing/ServiceWorkerInternals.cpp:
+ * testing/ServiceWorkerInternals.h:
+ * testing/ServiceWorkerInternals.idl:
+ * workers/service/context/SWContextManager.cpp:
+ * workers/service/context/SWContextManager.h:
+ * workers/service/context/ServiceWorkerInspectorProxy.cpp:
+ * workers/service/server/SWServer.cpp:
+ * workers/service/server/SWServer.h:
+ * workers/service/server/SWServerToContextConnection.cpp:
+ * workers/service/server/SWServerToContextConnection.h:
+ * workers/service/server/SWServerWorker.cpp:
+ * workers/service/server/SWServerWorker.h:
+
2022-03-17 Matt Woodrow <[email protected]>
Subgrid items should always be stretched.
Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp (291466 => 291467)
--- trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -199,6 +199,11 @@
return client.identifier().toString();
}
+void ServiceWorkerInternals::setAsInspected(bool isInspected)
+{
+ SWContextManager::singleton().setAsInspected(m_identifier, isInspected);
+}
+
} // namespace WebCore
#endif
Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.h (291466 => 291467)
--- trunk/Source/WebCore/testing/ServiceWorkerInternals.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -73,6 +73,7 @@
bool fetchEventIsSameSite(FetchEvent&);
String serviceWorkerClientInternalIdentifier(const ServiceWorkerClient&);
+ void setAsInspected(bool);
private:
explicit ServiceWorkerInternals(ServiceWorkerIdentifier);
Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.idl (291466 => 291467)
--- trunk/Source/WebCore/testing/ServiceWorkerInternals.idl 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.idl 2022-03-18 07:33:15 UTC (rev 291467)
@@ -52,4 +52,6 @@
boolean fetchEventIsSameSite(FetchEvent event);
DOMString serviceWorkerClientInternalIdentifier(ServiceWorkerClient client);
+
+ undefined setAsInspected(boolean isInspected);
};
Modified: trunk/Source/WebCore/workers/service/context/SWContextManager.cpp (291466 => 291467)
--- trunk/Source/WebCore/workers/service/context/SWContextManager.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -208,6 +208,12 @@
stopWorker(serviceWorker, workerTerminationTimeout, [] { });
}
+void SWContextManager::setAsInspected(ServiceWorkerIdentifier identifier, bool isInspected)
+{
+ if (m_connection)
+ m_connection->setAsInspected(identifier, isInspected);
+}
+
} // namespace WebCore
#endif
Modified: trunk/Source/WebCore/workers/service/context/SWContextManager.h (291466 => 291467)
--- trunk/Source/WebCore/workers/service/context/SWContextManager.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/context/SWContextManager.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -69,6 +69,7 @@
virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&&) = 0;
virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
+ virtual void setAsInspected(ServiceWorkerIdentifier, bool) = 0;
virtual bool isThrottleable() const = 0;
virtual PageIdentifier pageIdentifier() const = 0;
@@ -110,6 +111,8 @@
static constexpr Seconds workerTerminationTimeout { 10_s };
static constexpr Seconds syncWorkerTerminationTimeout { 100_ms }; // Only used by layout tests.
+ WEBCORE_EXPORT void setAsInspected(ServiceWorkerIdentifier, bool);
+
private:
SWContextManager() = default;
Modified: trunk/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.cpp (291466 => 291467)
--- trunk/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -61,6 +61,7 @@
{
m_channel = &channel;
+ SWContextManager::singleton().setAsInspected(m_serviceWorkerThreadProxy.identifier(), true);
m_serviceWorkerThreadProxy.thread().runLoop().postDebuggerTask([] (ScriptExecutionContext& context) {
downcast<WorkerGlobalScope>(context).inspectorController().connectFrontend();
});
@@ -71,6 +72,7 @@
ASSERT_UNUSED(channel, &channel == m_channel);
m_channel = nullptr;
+ SWContextManager::singleton().setAsInspected(m_serviceWorkerThreadProxy.identifier(), false);
m_serviceWorkerThreadProxy.thread().runLoop().postDebuggerTask([] (ScriptExecutionContext& context) {
downcast<WorkerGlobalScope>(context).inspectorController().disconnectFrontend(DisconnectReason::InspectorDestroyed);
Modified: trunk/Source/WebCore/workers/service/server/SWServer.cpp (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServer.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServer.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -1038,17 +1038,17 @@
iterator->value.terminateServiceWorkersTimer = makeUnique<Timer>([clientOrigin, clientRegistrableDomain, this] {
Vector<SWServerWorker*> workersToTerminate;
for (auto& worker : m_runningOrTerminatingWorkers.values()) {
- if (worker->isRunning() && worker->origin() == clientOrigin)
+ if (worker->isRunning() && worker->origin() == clientOrigin && !worker->shouldContinue())
workersToTerminate.append(worker.ptr());
}
for (auto* worker : workersToTerminate)
worker->terminate();
- if (!m_clientsByRegistrableDomain.contains(clientRegistrableDomain)) {
- if (auto* connection = contextConnectionForRegistrableDomain(clientRegistrableDomain)) {
- removeContextConnection(*connection);
- connection->connectionIsNoLongerNeeded();
- }
+ if (removeContextConnectionIfPossible(clientRegistrableDomain) == ShouldDelayRemoval::Yes) {
+ auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
+ ASSERT(iterator != m_clientIdentifiersPerOrigin.end());
+ iterator->value.terminateServiceWorkersTimer->startOneShot(m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : defaultPushMessageDuration);
+ return;
}
m_clientIdentifiersPerOrigin.remove(clientOrigin);
@@ -1074,6 +1074,25 @@
m_clientToControllingRegistration.remove(registrationIterator);
}
+SWServer::ShouldDelayRemoval SWServer::removeContextConnectionIfPossible(const RegistrableDomain& domain)
+{
+ if (m_clientsByRegistrableDomain.contains(domain))
+ return ShouldDelayRemoval::No;
+
+ auto* connection = contextConnectionForRegistrableDomain(domain);
+ if (!connection)
+ return ShouldDelayRemoval::No;
+
+ for (auto& worker : m_runningOrTerminatingWorkers.values()) {
+ if (worker->isRunning() && worker->registrableDomain() == domain && worker->shouldContinue())
+ return ShouldDelayRemoval::Yes;
+ }
+
+ removeContextConnection(*connection);
+ connection->connectionIsNoLongerNeeded();
+ return ShouldDelayRemoval::No;
+}
+
void SWServer::handleLowMemoryWarning()
{
// Accelerating the delayed termination of unused service workers due to memory pressure.
@@ -1281,14 +1300,18 @@
}
auto serviceWorkerIdentifier = worker->identifier();
- auto terminateWorkerTimer = makeUnique<Timer>([worker = WTFMove(worker)] {
- RELEASE_LOG_ERROR(ServiceWorker, "Terminating service worker as processing push event took too much time");
- worker->terminate();
+
+ worker->incrementPushEventCounter();
+ auto terminateWorkerTimer = makeUnique<Timer>([worker] {
+ RELEASE_LOG_ERROR(ServiceWorker, "Service worker is taking too much time to process a push event");
+ worker->decrementPushEventCounter();
});
- terminateWorkerTimer->startOneShot(weakThis && weakThis->m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : 2_s);
- connectionOrStatus.value()->firePushEvent(serviceWorkerIdentifier, data, [callback = WTFMove(callback), terminateWorkerTimer = WTFMove(terminateWorkerTimer)](bool succeeded) mutable {
- if (terminateWorkerTimer->isActive())
+ terminateWorkerTimer->startOneShot(weakThis && weakThis->m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : defaultPushMessageDuration);
+ connectionOrStatus.value()->firePushEvent(serviceWorkerIdentifier, data, [callback = WTFMove(callback), terminateWorkerTimer = WTFMove(terminateWorkerTimer), worker = WTFMove(worker)](bool succeeded) mutable {
+ if (terminateWorkerTimer->isActive()) {
+ worker->decrementPushEventCounter();
terminateWorkerTimer->stop();
+ }
callback(succeeded);
});
Modified: trunk/Source/WebCore/workers/service/server/SWServer.h (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServer.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServer.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -218,6 +218,7 @@
WEBCORE_EXPORT void handleLowMemoryWarning();
static constexpr Seconds defaultTerminationDelay = 10_s;
+ static constexpr Seconds defaultPushMessageDuration = 2_s;
LastNavigationWasAppInitiated clientIsAppInitiatedForRegistrableDomain(const RegistrableDomain&);
bool shouldRunServiceWorkersOnMainThreadForTesting() const { return m_shouldRunServiceWorkersOnMainThreadForTesting; }
@@ -230,7 +231,11 @@
ScriptExecutionContextIdentifier clientIdFromVisibleClientId(const String& visibleIdentifier) const { return m_visibleClientIdToInternalClientIdMap.get(visibleIdentifier); }
void forEachServiceWorker(const Function<bool(const SWServerWorker&)>&) const;
+ bool hasClientsWithOrigin(const ClientOrigin& origin) { return m_clientIdentifiersPerOrigin.contains(origin); }
+ enum class ShouldDelayRemoval : bool { No, Yes };
+ ShouldDelayRemoval removeContextConnectionIfPossible(const RegistrableDomain&);
+
private:
void validateRegistrationDomain(WebCore::RegistrableDomain, ServiceWorkerJobType, CompletionHandler<void(bool)>&&);
Modified: trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -137,6 +137,12 @@
worker->didFailHeartBeatCheck();
}
+void SWServerToContextConnection::setAsInspected(ServiceWorkerIdentifier identifier, bool isInspected)
+{
+ if (auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier))
+ worker->setAsInspected(isInspected);
+}
+
void SWServerToContextConnection::terminateWhenPossible()
{
m_shouldTerminateWhenPossible = true;
Modified: trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -79,6 +79,7 @@
WEBCORE_EXPORT void claim(ServiceWorkerIdentifier, CompletionHandler<void(std::optional<ExceptionData>&&)>&&);
WEBCORE_EXPORT void setScriptResource(ServiceWorkerIdentifier, URL&& scriptURL, ServiceWorkerContextData::ImportedScript&&);
WEBCORE_EXPORT void didFailHeartBeatCheck(ServiceWorkerIdentifier);
+ WEBCORE_EXPORT void setAsInspected(ServiceWorkerIdentifier, bool);
WEBCORE_EXPORT void findClientByVisibleIdentifier(ServiceWorkerIdentifier, const String& clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
Modified: trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -397,6 +397,28 @@
return m_registration->serviceWorkerPageIdentifier();
}
+void SWServerWorker::decrementPushEventCounter()
+{
+ ASSERT(m_pushEventCounter);
+ --m_pushEventCounter;
+ terminateIfPossible();
+}
+
+void SWServerWorker::setAsInspected(bool isInspected)
+{
+ m_isInspected = isInspected;
+ terminateIfPossible();
+}
+
+void SWServerWorker::terminateIfPossible()
+{
+ if (m_pushEventCounter || m_isInspected || !m_server || m_server->hasClientsWithOrigin(origin()))
+ return;
+
+ terminate();
+ m_server->removeContextConnectionIfPossible(registrableDomain());
+}
+
} // namespace WebCore
#endif // ENABLE(SERVICE_WORKER)
Modified: trunk/Source/WebCore/workers/service/server/SWServerWorker.h (291466 => 291467)
--- trunk/Source/WebCore/workers/service/server/SWServerWorker.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebCore/workers/service/server/SWServerWorker.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -135,6 +135,12 @@
WorkerThreadMode workerThreadMode() const;
+ void incrementPushEventCounter() { ++m_pushEventCounter; }
+ void decrementPushEventCounter();
+ void setAsInspected(bool);
+
+ bool shouldContinue() const { return !!m_pushEventCounter || m_isInspected; }
+
private:
SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const ScriptBuffer&, const CertificateInfo&, const ContentSecurityPolicyResponseHeaders&, const CrossOriginEmbedderPolicy&, String&& referrerPolicy, WorkerType, ServiceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&);
@@ -144,6 +150,7 @@
void terminationCompleted();
void terminationTimerFired();
void callTerminationCallbacks();
+ void terminateIfPossible();
WeakPtr<SWServer> m_server;
ServiceWorkerRegistrationKey m_registrationKey;
@@ -166,6 +173,8 @@
Vector<CompletionHandler<void()>> m_terminationCallbacks;
Timer m_terminationTimer;
LastNavigationWasAppInitiated m_lastNavigationWasAppInitiated;
+ int m_pushEventCounter { 0 };
+ bool m_isInspected { false };
};
} // namespace WebCore
Modified: trunk/Source/WebKit/ChangeLog (291466 => 291467)
--- trunk/Source/WebKit/ChangeLog 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebKit/ChangeLog 2022-03-18 07:33:15 UTC (rev 291467)
@@ -1,3 +1,15 @@
+2022-03-18 Youenn Fablet <[email protected]>
+
+ Keep service workers alive when they are inspected even though they should be terminated
+ https://bugs.webkit.org/show_bug.cgi?id=237827
+ <rdar://88313935>
+
+ Reviewed by Alex Christensen.
+
+ * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+ * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+ * WebProcess/Storage/WebSWContextManagerConnection.h:
+
2022-03-17 Alex Christensen <[email protected]>
Call doDailyActivityInManager on main thread in adattributiond
Modified: trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in (291466 => 291467)
--- trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in 2022-03-18 07:33:15 UTC (rev 291467)
@@ -38,6 +38,7 @@
SetScriptResource(WebCore::ServiceWorkerIdentifier identifier, URL scriptURL, WebCore::ServiceWorkerContextData::ImportedScript script)
PostMessageToServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
DidFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier identifier)
+ SetAsInspected(WebCore::ServiceWorkerIdentifier identifier, bool isInspected)
}
#endif // ENABLE(SERVICE_WORKER)
Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp (291466 => 291467)
--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp 2022-03-18 07:33:15 UTC (rev 291467)
@@ -372,6 +372,11 @@
m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::DidFailHeartBeatCheck { serviceWorkerIdentifier }, 0);
}
+void WebSWContextManagerConnection::setAsInspected(WebCore::ServiceWorkerIdentifier identifier, bool isInspected)
+{
+ m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::SetAsInspected { identifier, isInspected }, 0);
+}
+
} // namespace WebCore
#endif // ENABLE(SERVICE_WORKER)
Modified: trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h (291466 => 291467)
--- trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h 2022-03-18 07:33:15 UTC (rev 291467)
@@ -85,6 +85,7 @@
void setScriptResource(WebCore::ServiceWorkerIdentifier, const URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final;
bool isThrottleable() const final;
void didFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier) final;
+ void setAsInspected(WebCore::ServiceWorkerIdentifier, bool) final;
// IPC messages.
void serviceWorkerStarted(std::optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, bool doesHandleFetch) final;
Modified: trunk/Tools/ChangeLog (291466 => 291467)
--- trunk/Tools/ChangeLog 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Tools/ChangeLog 2022-03-18 07:33:15 UTC (rev 291467)
@@ -1,3 +1,13 @@
+2022-03-18 Youenn Fablet <[email protected]>
+
+ Keep service workers alive when they are inspected even though they should be terminated
+ https://bugs.webkit.org/show_bug.cgi?id=237827
+ <rdar://88313935>
+
+ Reviewed by Alex Christensen.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm:
+
2022-03-17 Jonathan Bedard <[email protected]>
configure-xcode-for-embedded-development fails with Xcode 13.3
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm (291466 => 291467)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm 2022-03-18 04:23:25 UTC (rev 291466)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm 2022-03-18 07:33:15 UTC (rev 291467)
@@ -367,3 +367,232 @@
clearWebsiteDataStore([configuration websiteDataStore]);
}
+
+#if WK_HAVE_C_SPI
+
+static const char* pushEventsAndInspectedServiceWorkerScriptBytes = R"SWRESOURCE(
+let port;
+self.addEventListener("message", (event) => {
+ port = event.data.port;
+ port.postMessage(self.internals ? "Ready" : "No internals");
+});
+self.addEventListener("push", (event) => {
+ self.registration.showNotification("notification");
+ if (event.data.text() === 'first') {
+ self.internals.setAsInspected(true);
+ self.previousMessageData = 'first';
+ return;
+ }
+ if (event.data.text() === 'second') {
+ self.internals.setAsInspected(false);
+ if (self.previousMessageData !== 'first')
+ event.waitUntil(Promise.reject('expected first'));
+ return;
+ }
+ if (event.data.text() === 'third') {
+ if (self.previousMessageData !== undefined)
+ event.waitUntil(Promise.reject('expected undefined'));
+ return;
+ }
+ self.previousMessageData = event.data.text();
+ event.waitUntil(Promise.reject('expected a known message'));
+});
+)SWRESOURCE";
+
+TEST(PushAPI, pushEventsAndInspectedServiceWorker)
+{
+ TestWebKitAPI::HTTPServer server({
+ { "/", { mainBytes } },
+ { "/sw.js", { {{ "Content-Type", "application/_javascript_" }}, pushEventsAndInspectedServiceWorkerScriptBytes } }
+ }, TestWebKitAPI::HTTPServer::Protocol::Http);
+
+ [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ clearWebsiteDataStore([configuration websiteDataStore]);
+
+ auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
+ [configuration setProcessPool:(WKProcessPool *)context.get()];
+
+ auto provider = TestWebKitAPI::TestNotificationProvider({ [[configuration processPool] _notificationManagerForTesting], WKNotificationManagerGetSharedServiceWorkerNotificationManager() });
+ provider.setPermission(server.origin(), true);
+
+ auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
+
+ expectedMessage = "Ready";
+ auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+ [webView loadRequest:server.request()];
+
+ TestWebKitAPI::Util::run(&done);
+
+ [webView _close];
+ webView = nullptr;
+
+ terminateNetworkProcessWhileRegistrationIsStored(configuration.get());
+
+ // Push event for service worker without any related page.
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ NSString *message = @"first";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_TRUE(pushMessageSuccessful);
+
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ message = @"second";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_TRUE(pushMessageSuccessful);
+
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ message = @"third";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_TRUE(pushMessageSuccessful);
+}
+
+static const char* inspectedServiceWorkerWithoutPageScriptBytes = R"SWRESOURCE(
+let port;
+self.addEventListener("message", (event) => {
+ port = event.data.port;
+ port.postMessage(self.internals ? "Ready" : "No internals");
+});
+self.addEventListener("push", async (event) => {
+ self.registration.showNotification("notification");
+ if (event.data.text() === 'firstmessage-inspected' || event.data.text() === 'firstmessage-not-inspected') {
+ if (event.data.text() === 'firstmessage-inspected')
+ self.internals.setAsInspected(true);
+ if (self.previousMessageData !== undefined)
+ event.waitUntil(Promise.reject('unexpected state with inspected message'));
+ self.previousMessageData = 'inspected';
+ // Wait for client to go away before resolving the event promise;
+ let resolve;
+ event.waitUntil(new Promise(r => resolve = r));
+ while (true) {
+ const clients = await self.clients.matchAll({ includeUncontrolled : true });
+ if (!clients.length)
+ resolve();
+ }
+ return;
+ }
+ if (event.data.text() === 'not inspected') {
+ setTimeout(() => self.internals.setAsInspected(false), 10);
+ if (self.previousMessageData !== 'inspected')
+ event.waitUntil(Promise.reject('unexpected state with not inspected message'));
+ self.previousMessageData = 'not inspected';
+ return;
+ }
+ if (event.data.text() === 'last') {
+ if (self.previousMessageData !== undefined)
+ event.waitUntil(Promise.reject('unexpected state with last message'));
+ }
+});
+)SWRESOURCE";
+
+static void testInspectedServiceWorkerWithoutPage(bool enableServiceWorkerInspection)
+{
+ TestWebKitAPI::HTTPServer server({
+ { "/", { mainBytes } },
+ { "/sw.js", { {{ "Content-Type", "application/_javascript_" }}, inspectedServiceWorkerWithoutPageScriptBytes } }
+ }, TestWebKitAPI::HTTPServer::Protocol::Http);
+
+ [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+ auto dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
+ [dataStoreConfiguration setServiceWorkerProcessTerminationDelayEnabled:NO];
+ auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
+
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ configuration.get().websiteDataStore = dataStore.get();
+ clearWebsiteDataStore([configuration websiteDataStore]);
+
+ auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
+ [configuration setProcessPool:(WKProcessPool *)context.get()];
+
+ auto provider = TestWebKitAPI::TestNotificationProvider({ [[configuration processPool] _notificationManagerForTesting], WKNotificationManagerGetSharedServiceWorkerNotificationManager() });
+ provider.setPermission(server.origin(), true);
+
+ auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
+
+ expectedMessage = "Ready";
+ auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+ [webView loadRequest:server.request()];
+
+ TestWebKitAPI::Util::run(&done);
+
+ // Push event for service worker without any related page.
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ NSString *message = @"firstmessage-inspected";
+ if (!enableServiceWorkerInspection)
+ message = @"firstmessage-not-inspected";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+
+ // We delay so that the push message will happen before the unregistration of the service worker client.
+ sleep(0.5);
+
+ // Closing the web view should not terminate the service worker as service worker is inspected.
+ [webView _close];
+ webView = nullptr;
+
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_TRUE(pushMessageSuccessful);
+
+ // We delay so that the timer to terminate service worker kicks in, at most up to the max push message allowed time, aka 2 seconds.
+ sleep(3);
+
+ // Send message at which point the service worker will not be inspected anymore and will be closed.
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ message = @"not inspected";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_EQ(pushMessageSuccessful, enableServiceWorkerInspection);
+
+ // We delay so that the timer to terminate service worker kicks in, at most up to the max push message allowed time, aka 2 seconds.
+ sleep(3);
+
+ pushMessageProcessed = false;
+ pushMessageSuccessful = false;
+ message = @"last";
+ [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
+ pushMessageSuccessful = result;
+ pushMessageProcessed = true;
+ }];
+
+ TestWebKitAPI::Util::run(&pushMessageProcessed);
+ EXPECT_TRUE(pushMessageSuccessful);
+}
+
+TEST(PushAPI, inspectedServiceWorkerWithoutPage)
+{
+ bool enableServiceWorkerInspection = true;
+ testInspectedServiceWorkerWithoutPage(enableServiceWorkerInspection);
+}
+
+TEST(PushAPI, uninspectedServiceWorkerWithoutPage)
+{
+ bool enableServiceWorkerInspection = false;
+ testInspectedServiceWorkerWithoutPage(enableServiceWorkerInspection);
+}
+
+#endif // WK_HAVE_C_SPI