Service Worker doesn't terminate after a period of time when thread blocking
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Dec 2019 14:19:45 +0000 (14:19 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Dec 2019 14:19:45 +0000 (14:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202992
<rdar://problem/56298596>

Reviewed by Chris Dumez.

Source/WebCore:

Whenever running a service worker task, running script or posting events (install, activate, message and fetch),
start a timer to check that the service worker is not spinning.
This is done by posting a task to service worker thread and hopping back to the main thread.
If this post/hop is done before the heartbeat timer is fired, the service worker is considered live.
Otherwise, the check is failed and the task is considered as failing.
The service worker will be terminated.
Timeout is 60 seconds by default and 1 second for test purposes.

Add settings to have short heartbeat timeout for testing purposes.
Add internals API to check whether a service worker is running.

Tests: http/wpt/service-workers/service-worker-spinning-activate.https.html
       http/wpt/service-workers/service-worker-spinning-fetch.https.html
       http/wpt/service-workers/service-worker-spinning-install.https.html
       http/wpt/service-workers/service-worker-spinning-message.https.html

* page/Settings.yaml:
* testing/Internals.cpp:
(WebCore::Internals::isServiceWorkerRunning):
* testing/Internals.h:
* testing/Internals.idl:
* workers/service/SWClientConnection.h:
(WebCore::SWClientConnection::isServiceWorkerRunning):
* workers/service/context/SWContextManager.h:
(WebCore::SWContextManager::Connection::isTestMode const):
(WebCore::SWContextManager::Connection::setIsTestMode):
* workers/service/context/ServiceWorkerThread.cpp:
(WebCore::ServiceWorkerThread::ServiceWorkerThread):
(WebCore::ServiceWorkerThread::postFetchTask):
(WebCore::ServiceWorkerThread::postMessageToServiceWorker):
(WebCore::ServiceWorkerThread::fireInstallEvent):
(WebCore::ServiceWorkerThread::finishedFiringInstallEvent):
(WebCore::ServiceWorkerThread::fireActivateEvent):
(WebCore::ServiceWorkerThread::finishedFiringActivateEvent):
(WebCore::ServiceWorkerThread::finishedEvaluatingScript):
(WebCore::ServiceWorkerThread::start):
(WebCore::ServiceWorkerThread::finishedStarting):
(WebCore::ServiceWorkerThread::startFetchEventMonitoring):
(WebCore::ServiceWorkerThread::startHeartBeatTimer):
(WebCore::ServiceWorkerThread::heartBeatTimerFired):
* workers/service/context/ServiceWorkerThread.h:
(WebCore::ServiceWorkerThread::stopFetchEventMonitoring):
* workers/service/context/ServiceWorkerThreadProxy.cpp:
(WebCore::ServiceWorkerThreadProxy::startFetch):
(WebCore::ServiceWorkerThreadProxy::cancelFetch):
(WebCore::ServiceWorkerThreadProxy::removeFetch):
* workers/service/server/SWServerToContextConnection.cpp:
(WebCore::SWServerToContextConnection::didFailHeartBeatCheck):
* workers/service/server/SWServerToContextConnection.h:
* workers/service/server/SWServerWorker.cpp:
(WebCore::SWServerWorker::didFailHeartBeatCheck):
* workers/service/server/SWServerWorker.h:

Source/WebKit:

Add a preference to enable/disable service worker short timeouts.
Add IPC handling for passing service worker heart beat failures and to get from WebProcess
whether a given service worker is running or not.

* NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::contextConnectionCreated):
(WebKit::WebSWServerConnection::syncTerminateWorkerFromClient):
(WebKit::WebSWServerConnection::isServiceWorkerRunning):
* NetworkProcess/ServiceWorker/WebSWServerConnection.h:
* NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
* Shared/WebPreferences.yaml:
* UIProcess/API/C/WKPreferences.cpp:
(WKPreferencesGetServiceWorkerTestMode):
(WKPreferencesSetServiceWorkerTestMode):
* UIProcess/API/C/WKPreferencesRef.h:
* WebProcess/Storage/WebSWClientConnection.cpp:
(WebKit::WebSWClientConnection::isServiceWorkerRunning):
* WebProcess/Storage/WebSWClientConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::updatePreferencesStore):
(WebKit::WebSWContextManagerConnection::didFailHeartBeatCheck):
* WebProcess/Storage/WebSWContextManagerConnection.h:

Tools:

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetPreferencesToConsistentValues):
Enable small timeout values.

LayoutTests:

* http/wpt/service-workers/resources/routines.js:
(async.waitForServiceWorkerNoLongerRunning):
* http/wpt/service-workers/service-worker-spinning-activate.https-expected.txt: Added.
* http/wpt/service-workers/service-worker-spinning-activate.https.html: Added.
* http/wpt/service-workers/service-worker-spinning-fetch.https-expected.txt: Added.
* http/wpt/service-workers/service-worker-spinning-fetch.https.html: Added.
* http/wpt/service-workers/service-worker-spinning-install.https-expected.txt: Added.
* http/wpt/service-workers/service-worker-spinning-install.https.html: Added.
* http/wpt/service-workers/service-worker-spinning-message.https-expected.txt: Added.
* http/wpt/service-workers/service-worker-spinning-message.https.html: Added.
* http/wpt/service-workers/service-worker-spinning-worker.js: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@253898 268f45cc-cd09-0410-ab3c-d52691b4dbfc

41 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/service-workers/resources/routines.js
LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https.html [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https.html [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https.html [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https.html [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/service-worker-spinning-worker.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/Settings.yaml
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebCore/workers/service/SWClientConnection.h
Source/WebCore/workers/service/context/SWContextManager.cpp
Source/WebCore/workers/service/context/SWContextManager.h
Source/WebCore/workers/service/context/ServiceWorkerThread.cpp
Source/WebCore/workers/service/context/ServiceWorkerThread.h
Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.cpp
Source/WebCore/workers/service/context/ServiceWorkerThreadProxy.h
Source/WebCore/workers/service/server/SWServerToContextConnection.cpp
Source/WebCore/workers/service/server/SWServerToContextConnection.h
Source/WebCore/workers/service/server/SWServerWorker.cpp
Source/WebCore/workers/service/server/SWServerWorker.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/UIProcess/API/C/WKPreferences.cpp
Source/WebKit/UIProcess/API/C/WKPreferencesRef.h
Source/WebKit/WebProcess/Storage/WebSWClientConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWClientConnection.h
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h
Tools/ChangeLog
Tools/WebKitTestRunner/TestController.cpp

index 0fa35ca..b2cd899 100644 (file)
@@ -1,3 +1,23 @@
+2019-12-24  youenn fablet  <youenn@apple.com>
+
+        Service Worker doesn't terminate after a period of time when thread blocking
+        https://bugs.webkit.org/show_bug.cgi?id=202992
+        <rdar://problem/56298596>
+
+        Reviewed by Chris Dumez.
+
+        * http/wpt/service-workers/resources/routines.js:
+        (async.waitForServiceWorkerNoLongerRunning):
+        * http/wpt/service-workers/service-worker-spinning-activate.https-expected.txt: Added.
+        * http/wpt/service-workers/service-worker-spinning-activate.https.html: Added.
+        * http/wpt/service-workers/service-worker-spinning-fetch.https-expected.txt: Added.
+        * http/wpt/service-workers/service-worker-spinning-fetch.https.html: Added.
+        * http/wpt/service-workers/service-worker-spinning-install.https-expected.txt: Added.
+        * http/wpt/service-workers/service-worker-spinning-install.https.html: Added.
+        * http/wpt/service-workers/service-worker-spinning-message.https-expected.txt: Added.
+        * http/wpt/service-workers/service-worker-spinning-message.https.html: Added.
+        * http/wpt/service-workers/service-worker-spinning-worker.js: Added.
+
 2019-12-23  Daniel Bates  <dabates@apple.com>
 
         REGRESSION (r212693): getClientRects(), getBoundingClientRect() for range that spans multi-lines differs depending on whether text is selected
index df23925..c6db82c 100644 (file)
@@ -27,3 +27,17 @@ function waitForState(worker, state)
         });
     });
 }
+
+async function waitForServiceWorkerNoLongerRunning(worker)
+{
+    if (!window.internals)
+        return Promise.reject("requires internals");
+
+    let count = 100;
+    while (--count > 0 && await internals.isServiceWorkerRunning(worker)) {
+        worker.postMessage("test");
+        await new Promise(resolve => setTimeout(resolve, 50));
+    }
+    if (count === 0)
+        return Promise.reject("service worker is still running");
+}
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https-expected.txt b/LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https-expected.txt
new file mode 100644 (file)
index 0000000..0580a9c
--- /dev/null
@@ -0,0 +1,4 @@
+
+PASS Spin in activate 
+PASS Spin after activate 
+
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https.html b/LayoutTests/http/wpt/service-workers/service-worker-spinning-activate.https.html
new file mode 100644 (file)
index 0000000..fe77c4b
--- /dev/null
@@ -0,0 +1,28 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/routines.js"></script>
+</head>
+<body>
+<script>
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-activate" });
+    const worker = registration.installing;
+
+    await waitForState(registration.installing, "activating");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin in activate");
+
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-after-activate" });
+    const worker = registration.installing;
+
+    await waitForState(worker, "activated");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin after activate");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https-expected.txt b/LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https-expected.txt
new file mode 100644 (file)
index 0000000..9039f02
--- /dev/null
@@ -0,0 +1,5 @@
+
+
+PASS Spin in fetch 
+PASS Spin after fetch 
+
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https.html b/LayoutTests/http/wpt/service-workers/service-worker-spinning-fetch.https.html
new file mode 100644 (file)
index 0000000..7b523ec
--- /dev/null
@@ -0,0 +1,32 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/routines.js"></script>
+</head>
+<body>
+<script>
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-fetch" });
+    const worker = registration.installing;
+
+    await waitForState(registration.installing, "activated");
+
+    withIframe("spin-fetch");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin in fetch");
+
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-after-fetch" });
+    const worker = registration.installing;
+
+    await waitForState(registration.installing, "activated");
+
+    withIframe("spin-after-fetch");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin after fetch");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https-expected.txt b/LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https-expected.txt
new file mode 100644 (file)
index 0000000..78c182c
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS Spin at running  
+PASS Spin in install 
+PASS Spin after install 
+
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https.html b/LayoutTests/http/wpt/service-workers/service-worker-spinning-install.https.html
new file mode 100644 (file)
index 0000000..eefc676
--- /dev/null
@@ -0,0 +1,25 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/routines.js"></script>
+</head>
+<body>
+<script>
+promise_test(async (test) => {
+    return promise_rejects(test, new TypeError, navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-run" }));
+}, "Spin at running ");
+
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-install" });
+    const worker = registration.installing;
+    await waitForState(registration.installing, "redundant");
+}, "Spin in install");
+
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-after-install" });
+    return waitForServiceWorkerNoLongerRunning(registration.installing);
+}, "Spin after install");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https-expected.txt b/LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https-expected.txt
new file mode 100644 (file)
index 0000000..91a4558
--- /dev/null
@@ -0,0 +1,4 @@
+
+PASS Spin in message 
+PASS Spin after message 
+
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https.html b/LayoutTests/http/wpt/service-workers/service-worker-spinning-message.https.html
new file mode 100644 (file)
index 0000000..7b8d589
--- /dev/null
@@ -0,0 +1,28 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/routines.js"></script>
+</head>
+<body>
+<script>
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-message" });
+    const worker = registration.installing;
+
+    await waitForState(registration.installing, "activated");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin in message");
+
+promise_test(async (test) => {
+    const registration = await navigator.serviceWorker.register("service-worker-spinning-worker.js", { scope : "spin-after-message" });
+    const worker = registration.installing;
+
+    await waitForState(registration.installing, "activated");
+
+    return waitForServiceWorkerNoLongerRunning(worker);
+}, "Spin after message");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/wpt/service-workers/service-worker-spinning-worker.js b/LayoutTests/http/wpt/service-workers/service-worker-spinning-worker.js
new file mode 100644 (file)
index 0000000..5ef8379
--- /dev/null
@@ -0,0 +1,41 @@
+if (self.registration.scope.includes("spin-run"))
+    while(true) { };
+
+function installTest(event)
+{
+    if (self.registration.scope.includes("spin-install"))
+        while(true) { };
+    if (self.registration.scope.includes("spin-after-install"))
+        self.setTimeout(() => { while(true) { }; }, 0);
+}
+
+function activateTest(event)
+{
+    if (self.registration.scope.includes("spin-activate"))
+        while(true) { };
+    if (self.registration.scope.includes("spin-after-activate"))
+        self.setTimeout(() => { while(true) { }; }, 0);
+}
+
+function messageTest(event)
+{
+    if (self.registration.scope.includes("spin-message"))
+        while(true) { };
+    if (self.registration.scope.includes("spin-after-message"))
+        self.setTimeout(() => { while(true) { }; }, 0);
+}
+
+function fetchTest(event)
+{
+    if (self.registration.scope.includes("spin-fetch"))
+        while(true) { };
+    if (self.registration.scope.includes("spin-after-fetch"))
+        self.setTimeout(() => { while(true) { }; }, 0);
+    event.respondWith(new Response("ok"));
+}
+
+
+self.addEventListener("install", installTest);
+self.addEventListener("activate", activateTest);
+self.addEventListener("message", messageTest);
+self.addEventListener("fetch", fetchTest);
index 4782d83..e527641 100644 (file)
@@ -1,3 +1,64 @@
+2019-12-24  youenn fablet  <youenn@apple.com>
+
+        Service Worker doesn't terminate after a period of time when thread blocking
+        https://bugs.webkit.org/show_bug.cgi?id=202992
+        <rdar://problem/56298596>
+
+        Reviewed by Chris Dumez.
+
+        Whenever running a service worker task, running script or posting events (install, activate, message and fetch),
+        start a timer to check that the service worker is not spinning.
+        This is done by posting a task to service worker thread and hopping back to the main thread.
+        If this post/hop is done before the heartbeat timer is fired, the service worker is considered live.
+        Otherwise, the check is failed and the task is considered as failing.
+        The service worker will be terminated.
+        Timeout is 60 seconds by default and 1 second for test purposes.
+
+        Add settings to have short heartbeat timeout for testing purposes.
+        Add internals API to check whether a service worker is running.
+
+        Tests: http/wpt/service-workers/service-worker-spinning-activate.https.html
+               http/wpt/service-workers/service-worker-spinning-fetch.https.html
+               http/wpt/service-workers/service-worker-spinning-install.https.html
+               http/wpt/service-workers/service-worker-spinning-message.https.html
+
+        * page/Settings.yaml:
+        * testing/Internals.cpp:
+        (WebCore::Internals::isServiceWorkerRunning):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * workers/service/SWClientConnection.h:
+        (WebCore::SWClientConnection::isServiceWorkerRunning):
+        * workers/service/context/SWContextManager.h:
+        (WebCore::SWContextManager::Connection::isTestMode const):
+        (WebCore::SWContextManager::Connection::setIsTestMode):
+        * workers/service/context/ServiceWorkerThread.cpp:
+        (WebCore::ServiceWorkerThread::ServiceWorkerThread):
+        (WebCore::ServiceWorkerThread::postFetchTask):
+        (WebCore::ServiceWorkerThread::postMessageToServiceWorker):
+        (WebCore::ServiceWorkerThread::fireInstallEvent):
+        (WebCore::ServiceWorkerThread::finishedFiringInstallEvent):
+        (WebCore::ServiceWorkerThread::fireActivateEvent):
+        (WebCore::ServiceWorkerThread::finishedFiringActivateEvent):
+        (WebCore::ServiceWorkerThread::finishedEvaluatingScript):
+        (WebCore::ServiceWorkerThread::start):
+        (WebCore::ServiceWorkerThread::finishedStarting):
+        (WebCore::ServiceWorkerThread::startFetchEventMonitoring):
+        (WebCore::ServiceWorkerThread::startHeartBeatTimer):
+        (WebCore::ServiceWorkerThread::heartBeatTimerFired):
+        * workers/service/context/ServiceWorkerThread.h:
+        (WebCore::ServiceWorkerThread::stopFetchEventMonitoring):
+        * workers/service/context/ServiceWorkerThreadProxy.cpp:
+        (WebCore::ServiceWorkerThreadProxy::startFetch):
+        (WebCore::ServiceWorkerThreadProxy::cancelFetch):
+        (WebCore::ServiceWorkerThreadProxy::removeFetch):
+        * workers/service/server/SWServerToContextConnection.cpp:
+        (WebCore::SWServerToContextConnection::didFailHeartBeatCheck):
+        * workers/service/server/SWServerToContextConnection.h:
+        * workers/service/server/SWServerWorker.cpp:
+        (WebCore::SWServerWorker::didFailHeartBeatCheck):
+        * workers/service/server/SWServerWorker.h:
+
 2019-12-23  Simon Fraser  <simon.fraser@apple.com>
 
         REGRESSION (r253634): Reproducible crash going back and forward on goodreads.com in Page::setPageScaleFactor
index 3be2ef7..6d59c29 100644 (file)
@@ -928,3 +928,6 @@ mediaCaptureRequiresSecureConnection:
   initial: true
   conditional: MEDIA_STREAM
   inspectorOverride: true
+
+shouldUseServiceWorkerShortTimeout:
+  initial: false
index e5d5139..f22837c 100644 (file)
@@ -5070,6 +5070,13 @@ void Internals::terminateServiceWorker(ServiceWorker& worker)
 
     ServiceWorkerProvider::singleton().serviceWorkerConnection().syncTerminateWorker(worker.identifier());
 }
+
+void Internals::isServiceWorkerRunning(ServiceWorker& worker, DOMPromiseDeferred<IDLBoolean>&& promise)
+{
+    return ServiceWorkerProvider::singleton().serviceWorkerConnection().isServiceWorkerRunning(worker.identifier(), [promise = WTFMove(promise)](bool result) mutable {
+        promise.resolve(result);
+    });
+}
 #endif
 
 #if ENABLE(APPLE_PAY)
index d4b2657..fe4a856 100644 (file)
@@ -768,6 +768,7 @@ public:
     using HasRegistrationPromise = DOMPromiseDeferred<IDLBoolean>;
     void hasServiceWorkerRegistration(const String& clientURL, HasRegistrationPromise&&);
     void terminateServiceWorker(ServiceWorker&);
+    void isServiceWorkerRunning(ServiceWorker&, DOMPromiseDeferred<IDLBoolean>&&);
 #endif
 
 #if ENABLE(APPLE_PAY)
index aff76ff..f759cc9 100644 (file)
@@ -756,6 +756,7 @@ enum CompositingPolicy {
 
     [Conditional=SERVICE_WORKER] Promise<boolean> hasServiceWorkerRegistration(DOMString scopeURL);
     [Conditional=SERVICE_WORKER] void terminateServiceWorker(ServiceWorker worker);
+    [Conditional=SERVICE_WORKER] Promise<boolean> isServiceWorkerRunning(ServiceWorker worker);
 
     [CallWith=Document, Conditional=APPLE_PAY] readonly attribute MockPaymentCoordinator mockPaymentCoordinator;
 
index 2e9ced4..d922cf7 100644 (file)
@@ -85,6 +85,7 @@ public:
     virtual void finishFetchingScriptInServer(const ServiceWorkerFetchResult&) = 0;
 
     virtual void storeRegistrationsOnDiskForTesting(CompletionHandler<void()>&& callback) { callback(); }
+    virtual void isServiceWorkerRunning(ServiceWorkerIdentifier, CompletionHandler<void(bool)>&& callback) { callback(false); }
 
 protected:
     WEBCORE_EXPORT SWClientConnection();
index a385daa..dd5f378 100644 (file)
@@ -87,9 +87,7 @@ void SWContextManager::postMessageToServiceWorker(ServiceWorkerIdentifier destin
     ASSERT(!serviceWorker->isTerminatingOrTerminated());
 
     // FIXME: We should pass valid MessagePortChannels.
-    serviceWorker->thread().runLoop().postTask([serviceWorker = makeRef(*serviceWorker), message = WTFMove(message), sourceData = WTFMove(sourceData)](auto&) mutable {
-        serviceWorker->thread().queueTaskToPostMessage(WTFMove(message), WTFMove(sourceData));
-    });
+    serviceWorker->postMessageToServiceWorker(WTFMove(message), WTFMove(sourceData));
 }
 
 void SWContextManager::fireInstallEvent(ServiceWorkerIdentifier identifier)
@@ -98,9 +96,7 @@ void SWContextManager::fireInstallEvent(ServiceWorkerIdentifier identifier)
     if (!serviceWorker)
         return;
 
-    serviceWorker->thread().runLoop().postTask([serviceWorker = makeRef(*serviceWorker)](auto&) mutable {
-        serviceWorker->thread().queueTaskToFireInstallEvent();
-    });
+    serviceWorker->fireInstallEvent();
 }
 
 void SWContextManager::fireActivateEvent(ServiceWorkerIdentifier identifier)
@@ -109,9 +105,7 @@ void SWContextManager::fireActivateEvent(ServiceWorkerIdentifier identifier)
     if (!serviceWorker)
         return;
 
-    serviceWorker->thread().runLoop().postTask([serviceWorker = makeRef(*serviceWorker)](auto&) mutable {
-        serviceWorker->thread().queueTaskToFireActivateEvent();
-    });
+    serviceWorker->fireActivateEvent();
 }
 
 void SWContextManager::terminateWorker(ServiceWorkerIdentifier identifier, Seconds timeout, Function<void()>&& completionHandler)
index f778e23..8b499e7 100644 (file)
@@ -64,15 +64,20 @@ public:
         virtual void matchAll(ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&) = 0;
         virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void()>&&) = 0;
 
+        virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
+
         virtual bool isThrottleable() const = 0;
 
         bool isClosed() const { return m_isClosed; }
+        bool shouldUseShortTimeout() const { return m_shouldUseShortTimeout; }
 
     protected:
         void setAsClosed() { m_isClosed = true; }
+        void setShouldUseShortTimeout(bool value) { m_shouldUseShortTimeout = value; }
 
     private:
         bool m_isClosed { false };
+        bool m_shouldUseShortTimeout { false };
     };
 
     WEBCORE_EXPORT void setConnection(std::unique_ptr<Connection>&&);
index e98e71f..3bf7139 100644 (file)
@@ -76,6 +76,8 @@ ServiceWorkerThread::ServiceWorkerThread(const ServiceWorkerContextData& data, S
     : WorkerThread(data.scriptURL, emptyString(), "serviceworker:" + Inspector::IdentifiersFactory::createIdentifier(), WTFMove(userAgent), platformStrategies()->loaderStrategy()->isOnLine(), data.script, loaderProxy, debuggerProxy, DummyServiceWorkerThreadProxy::shared(), WorkerThreadStartMode::Normal, data.contentSecurityPolicy, false, data.registration.key.topOrigin().securityOrigin().get(), MonotonicTime::now(), idbConnectionProxy, socketProvider, JSC::RuntimeFlags::createAllEnabled())
     , m_data(data.isolatedCopy())
     , m_workerObjectProxy(DummyServiceWorkerThreadProxy::shared())
+    , m_heartBeatTimeout(SWContextManager::singleton().connection()->shouldUseShortTimeout() ? heartBeatTimeoutForTest : heartBeatTimeout)
+    , m_heartBeatTimer { *this, &ServiceWorkerThread::heartBeatTimerFired }
 {
     AtomString::init();
 }
@@ -114,7 +116,7 @@ static void fireMessageEvent(ServiceWorkerGlobalScope& scope, MessageWithMessage
 void ServiceWorkerThread::queueTaskToPostMessage(MessageWithMessagePorts&& message, ServiceWorkerOrClientData&& sourceData)
 {
     auto serviceWorkerGlobalScope = makeRef(downcast<ServiceWorkerGlobalScope>(*workerGlobalScope()));
-    serviceWorkerGlobalScope->eventLoop().queueTask(TaskSource::DOMManipulation, [serviceWorkerGlobalScope = serviceWorkerGlobalScope.copyRef(), message = WTFMove(message), sourceData = WTFMove(sourceData)]() mutable {
+    serviceWorkerGlobalScope->eventLoop().queueTask(TaskSource::DOMManipulation, [serviceWorkerGlobalScope = serviceWorkerGlobalScope.copyRef(), message = WTFMove(message), sourceData = WTFMove(sourceData), serviceWorkerIdentifier = this->identifier()]() mutable {
         URL sourceURL;
         ExtendableMessageEventSource source;
         if (WTF::holds_alternative<ServiceWorkerClientData>(sourceData)) {
@@ -133,6 +135,10 @@ void ServiceWorkerThread::queueTaskToPostMessage(MessageWithMessagePorts&& messa
             source = WTFMove(sourceWorker);
         }
         fireMessageEvent(serviceWorkerGlobalScope, WTFMove(message), ExtendableMessageEventSource { source }, sourceURL);
+        callOnMainThread([serviceWorkerIdentifier] {
+            if (auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier))
+                serviceWorkerThreadProxy->thread().finishedFiringMessageEvent();
+        });
     });
 }
 
@@ -151,9 +157,9 @@ void ServiceWorkerThread::queueTaskToFireInstallEvent()
                     break;
                 }
             }
-            callOnMainThread([jobDataIdentifier, serviceWorkerIdentifier, hasRejectedAnyPromise] () mutable {
-                if (auto* connection = SWContextManager::singleton().connection())
-                    connection->didFinishInstall(jobDataIdentifier, serviceWorkerIdentifier, !hasRejectedAnyPromise);
+            callOnMainThread([serviceWorkerIdentifier, hasRejectedAnyPromise] {
+                if (auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier))
+                    serviceWorkerThreadProxy->thread().finishedFiringInstallEvent(hasRejectedAnyPromise);
             });
         });
     });
@@ -167,9 +173,9 @@ void ServiceWorkerThread::queueTaskToFireActivateEvent()
         serviceWorkerGlobalScope->dispatchEvent(activateEvent);
 
         activateEvent->whenAllExtendLifetimePromisesAreSettled([serviceWorkerIdentifier](HashSet<Ref<DOMPromise>>&&) {
-            callOnMainThread([serviceWorkerIdentifier] () mutable {
-                if (auto* connection = SWContextManager::singleton().connection())
-                    connection->didFinishActivation(serviceWorkerIdentifier);
+            callOnMainThread([serviceWorkerIdentifier] {
+                if (auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier))
+                    serviceWorkerThreadProxy->thread().finishedFiringActivateEvent();
             });
         });
     });
@@ -177,19 +183,121 @@ void ServiceWorkerThread::queueTaskToFireActivateEvent()
 
 void ServiceWorkerThread::finishedEvaluatingScript()
 {
+    ASSERT(!isMainThread());
     m_doesHandleFetch = workerGlobalScope()->hasEventListeners(eventNames().fetchEvent);
 }
 
 void ServiceWorkerThread::start(Function<void(const String&, bool)>&& callback)
 {
+    m_state = State::Starting;
+    startHeartBeatTimer();
+
     WorkerThread::start([callback = WTFMove(callback), serviceWorkerIdentifier = this->identifier()](auto& errorMessage) mutable {
         bool doesHandleFetch = true;
-        if (auto* threadProxy = SWContextManager::singleton().workerByID(serviceWorkerIdentifier))
+        if (auto* threadProxy = SWContextManager::singleton().workerByID(serviceWorkerIdentifier)) {
+            threadProxy->thread().finishedStarting();
             doesHandleFetch = threadProxy->thread().doesHandleFetch();
+        }
         callback(errorMessage, doesHandleFetch);
     });
 }
 
+void ServiceWorkerThread::finishedStarting()
+{
+    m_state = State::Idle;
+}
+
+void ServiceWorkerThread::startFetchEventMonitoring()
+{
+    m_isHandlingFetchEvent = true;
+    startHeartBeatTimer();
+}
+
+void ServiceWorkerThread::startHeartBeatTimer()
+{
+    if (m_heartBeatTimer.isActive())
+        return;
+
+    m_ongoingHeartBeatCheck = true;
+    runLoop().postTask([this, protectedThis = makeRef(*this)](auto&) mutable {
+        callOnMainThread([this, protectedThis = WTFMove(protectedThis)]() {
+            m_ongoingHeartBeatCheck = false;
+        });
+    });
+
+    m_heartBeatTimer.startOneShot(m_heartBeatTimeout);
+}
+
+void ServiceWorkerThread::heartBeatTimerFired()
+{
+    if (!m_ongoingHeartBeatCheck) {
+        if (m_state == State::Installing || m_state == State::Activating || m_isHandlingFetchEvent || m_messageEventCount)
+            startHeartBeatTimer();
+        return;
+    }
+
+    auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(identifier());
+    if (!serviceWorkerThreadProxy || serviceWorkerThreadProxy->isTerminatingOrTerminated())
+        return;
+
+    auto* connection = SWContextManager::singleton().connection();
+    if (!connection)
+        return;
+
+    switch (m_state) {
+    case State::Idle:
+    case State::Activating:
+        connection->didFailHeartBeatCheck(identifier());
+        break;
+    case State::Starting:
+        connection->serviceWorkerFailedToStart(m_data.jobDataIdentifier, identifier(), "Service Worker script execution timed out"_s);
+        break;
+    case State::Installing:
+        connection->didFinishInstall(m_data.jobDataIdentifier, identifier(), false);
+        break;
+    }
+}
+
+void ServiceWorkerThread::willPostTaskToFireInstallEvent()
+{
+    m_state = State::Installing;
+    startHeartBeatTimer();
+}
+
+void ServiceWorkerThread::finishedFiringInstallEvent(bool hasRejectedAnyPromise)
+{
+    m_state = State::Idle;
+
+    if (auto* connection = SWContextManager::singleton().connection())
+        connection->didFinishInstall(m_data.jobDataIdentifier, identifier(), !hasRejectedAnyPromise);
+}
+
+void ServiceWorkerThread::willPostTaskToFireActivateEvent()
+{
+    m_state = State::Activating;
+    startHeartBeatTimer();
+}
+
+void ServiceWorkerThread::finishedFiringActivateEvent()
+{
+    m_state = State::Idle;
+
+    if (auto* connection = SWContextManager::singleton().connection())
+        connection->didFinishActivation(identifier());
+}
+
+void ServiceWorkerThread::willPostTaskToFireMessageEvent()
+{
+    if (!m_messageEventCount++)
+        startHeartBeatTimer();
+}
+
+void ServiceWorkerThread::finishedFiringMessageEvent()
+{
+    ASSERT(m_messageEventCount);
+    --m_messageEventCount;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index d1a6576..02ebea6 100644 (file)
@@ -30,6 +30,7 @@
 #include "ServiceWorkerContextData.h"
 #include "ServiceWorkerFetch.h"
 #include "ServiceWorkerIdentifier.h"
+#include "Timer.h"
 #include "WorkerThread.h"
 
 namespace WebCore {
@@ -56,9 +57,13 @@ public:
     WorkerObjectProxy& workerObjectProxy() const { return m_workerObjectProxy; }
 
     void start(Function<void(const String&, bool)>&&);
-    WEBCORE_EXPORT void queueTaskToFireFetchEvent(Ref<ServiceWorkerFetch::Client>&&, Optional<ServiceWorkerClientIdentifier>&&, ResourceRequest&&, String&& referrer, FetchOptions&&);
-    WEBCORE_EXPORT void queueTaskToPostMessage(MessageWithMessagePorts&&, ServiceWorkerOrClientData&& sourceData);
 
+    void willPostTaskToFireInstallEvent();
+    void willPostTaskToFireActivateEvent();
+    void willPostTaskToFireMessageEvent();
+
+    void queueTaskToFireFetchEvent(Ref<ServiceWorkerFetch::Client>&&, Optional<ServiceWorkerClientIdentifier>&&, ResourceRequest&&, String&& referrer, FetchOptions&&);
+    void queueTaskToPostMessage(MessageWithMessagePorts&&, ServiceWorkerOrClientData&& sourceData);
     void queueTaskToFireInstallEvent();
     void queueTaskToFireActivateEvent();
 
@@ -67,6 +72,9 @@ public:
     ServiceWorkerIdentifier identifier() const { return m_data.serviceWorkerIdentifier; }
     bool doesHandleFetch() const { return m_doesHandleFetch; }
 
+    void startFetchEventMonitoring();
+    void stopFetchEventMonitoring() { m_isHandlingFetchEvent = false; }
+
 protected:
     Ref<WorkerGlobalScope> createWorkerGlobalScope(const URL&, Ref<SecurityOrigin>&&, const String& name, const String& identifier, const String& userAgent, bool isOnline, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, Ref<SecurityOrigin>&& topOrigin, MonotonicTime timeOrigin) final;
     void runEventLoop() override;
@@ -77,9 +85,29 @@ private:
     bool isServiceWorkerThread() const final { return true; }
     void finishedEvaluatingScript() final;
 
+    void finishedFiringInstallEvent(bool hasRejectedAnyPromise);
+    void finishedFiringActivateEvent();
+    void finishedFiringMessageEvent();
+    void finishedStarting();
+
+    void startHeartBeatTimer();
+    void heartBeatTimerFired();
+    void installEventTimerFired();
+
     ServiceWorkerContextData m_data;
     WorkerObjectProxy& m_workerObjectProxy;
     bool m_doesHandleFetch { false };
+
+    bool m_isHandlingFetchEvent { false };
+    uint64_t m_messageEventCount { 0 };
+    enum class State { Idle, Starting, Installing, Activating };
+    State m_state { State::Idle };
+    bool m_ongoingHeartBeatCheck { false };
+
+    static constexpr Seconds heartBeatTimeout { 60_s };
+    static constexpr Seconds heartBeatTimeoutForTest { 1_s };
+    Seconds m_heartBeatTimeout { heartBeatTimeout };
+    Timer m_heartBeatTimer;
 };
 
 } // namespace WebCore
index 4adb091..92cdf31 100644 (file)
@@ -197,6 +197,9 @@ void ServiceWorkerThreadProxy::startFetch(SWServerConnectionIdentifier connectio
 {
     auto key = std::make_pair(connectionIdentifier, fetchIdentifier);
 
+    if (m_ongoingFetchTasks.isEmpty())
+        thread().startFetchEventMonitoring();
+
     ASSERT(!m_ongoingFetchTasks.contains(key));
     m_ongoingFetchTasks.add(key, client.copyRef());
     postTaskForModeToWorkerGlobalScope([this, protectedThis = makeRef(*this), client = WTFMove(client), clientId, request = request.isolatedCopy(), referrer = referrer.isolatedCopy(), options = options.isolatedCopy()](auto&) mutable {
@@ -210,6 +213,9 @@ void ServiceWorkerThreadProxy::cancelFetch(SWServerConnectionIdentifier connecti
     if (!client)
         return;
 
+    if (m_ongoingFetchTasks.isEmpty())
+        thread().stopFetchEventMonitoring();
+
     postTaskForModeToWorkerGlobalScope([client = WTFMove(client.value())] (ScriptExecutionContext&) {
         client->cancel();
     }, WorkerRunLoop::defaultMode());
@@ -229,6 +235,33 @@ void ServiceWorkerThreadProxy::continueDidReceiveFetchResponse(SWServerConnectio
 void ServiceWorkerThreadProxy::removeFetch(SWServerConnectionIdentifier connectionIdentifier, FetchIdentifier fetchIdentifier)
 {
     m_ongoingFetchTasks.remove(std::make_pair(connectionIdentifier, fetchIdentifier));
+
+    if (m_ongoingFetchTasks.isEmpty())
+        thread().stopFetchEventMonitoring();
+}
+
+void ServiceWorkerThreadProxy::postMessageToServiceWorker(MessageWithMessagePorts&& message, ServiceWorkerOrClientData&& sourceData)
+{
+    thread().willPostTaskToFireMessageEvent();
+    thread().runLoop().postTask([this, protectedThis = makeRef(*this), message = WTFMove(message), sourceData = WTFMove(sourceData)](auto&) mutable {
+        thread().queueTaskToPostMessage(WTFMove(message), WTFMove(sourceData));
+    });
+}
+
+void ServiceWorkerThreadProxy::fireInstallEvent()
+{
+    thread().willPostTaskToFireInstallEvent();
+    thread().runLoop().postTask([this, protectedThis = makeRef(*this)](auto&) mutable {
+        thread().queueTaskToFireInstallEvent();
+    });
+}
+
+void ServiceWorkerThreadProxy::fireActivateEvent()
+{
+    thread().willPostTaskToFireActivateEvent();
+    thread().runLoop().postTask([this, protectedThis = makeRef(*this)](auto&) mutable {
+        thread().queueTaskToFireActivateEvent();
+    });
 }
 
 } // namespace WebCore
index e9bd8a1..8545e84 100644 (file)
@@ -75,6 +75,10 @@ public:
     WEBCORE_EXPORT void continueDidReceiveFetchResponse(SWServerConnectionIdentifier, FetchIdentifier);
     WEBCORE_EXPORT void removeFetch(SWServerConnectionIdentifier, FetchIdentifier);
 
+    void postMessageToServiceWorker(MessageWithMessagePorts&&, ServiceWorkerOrClientData&&);
+    void fireInstallEvent();
+    void fireActivateEvent();
+
 private:
     WEBCORE_EXPORT ServiceWorkerThreadProxy(PageConfiguration&&, const ServiceWorkerContextData&, String&& userAgent, CacheStorageProvider&, SecurityOrigin::StorageBlockingPolicy);
 
index a367371..5703e85 100644 (file)
@@ -123,6 +123,12 @@ void SWServerToContextConnection::setScriptResource(ServiceWorkerIdentifier serv
         worker->setScriptResource(WTFMove(scriptURL), ServiceWorkerContextData::ImportedScript { WTFMove(script), WTFMove(responseURL), WTFMove(mimeType) });
 }
 
+void SWServerToContextConnection::didFailHeartBeatCheck(ServiceWorkerIdentifier identifier)
+{
+    if (auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier))
+        worker->didFailHeartBeatCheck();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 33f12c6..0981da0 100644 (file)
@@ -70,6 +70,7 @@ public:
     WEBCORE_EXPORT void matchAll(uint64_t requestIdentifier, ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&);
     WEBCORE_EXPORT void claim(uint64_t requestIdentifier, ServiceWorkerIdentifier);
     WEBCORE_EXPORT void setScriptResource(ServiceWorkerIdentifier, URL&& scriptURL, String&& script, URL&& responseURL, String&& mimeType);
+    WEBCORE_EXPORT void didFailHeartBeatCheck(ServiceWorkerIdentifier);
 
     const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
 
index 655d70d..f216cb6 100644 (file)
@@ -258,6 +258,12 @@ SWServerRegistration* SWServerWorker::registration() const
     return m_registration.get();
 }
 
+void SWServerWorker::didFailHeartBeatCheck()
+{
+    if (m_server && isRunning())
+        m_server->terminateWorker(*this);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 1b99cc9..f2effee 100644 (file)
@@ -118,6 +118,7 @@ public:
 
     void setHasTimedOutAnyFetchTasks() { m_hasTimedOutAnyFetchTasks = true; }
     bool hasTimedOutAnyFetchTasks() const { return m_hasTimedOutAnyFetchTasks; }
+    void didFailHeartBeatCheck();
 
 private:
     SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const String& script, const ContentSecurityPolicyResponseHeaders&, String&& referrerPolicy, WorkerType, ServiceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&);
index 77c46d7..09c040b 100644 (file)
@@ -1,3 +1,35 @@
+2019-12-24  youenn fablet  <youenn@apple.com>
+
+        Service Worker doesn't terminate after a period of time when thread blocking
+        https://bugs.webkit.org/show_bug.cgi?id=202992
+        <rdar://problem/56298596>
+
+        Reviewed by Chris Dumez.
+
+        Add a preference to enable/disable service worker short timeouts.
+        Add IPC handling for passing service worker heart beat failures and to get from WebProcess
+        whether a given service worker is running or not.
+
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::contextConnectionCreated):
+        (WebKit::WebSWServerConnection::syncTerminateWorkerFromClient):
+        (WebKit::WebSWServerConnection::isServiceWorkerRunning):
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.h:
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
+        * Shared/WebPreferences.yaml:
+        * UIProcess/API/C/WKPreferences.cpp:
+        (WKPreferencesGetServiceWorkerTestMode):
+        (WKPreferencesSetServiceWorkerTestMode):
+        * UIProcess/API/C/WKPreferencesRef.h:
+        * WebProcess/Storage/WebSWClientConnection.cpp:
+        (WebKit::WebSWClientConnection::isServiceWorkerRunning):
+        * WebProcess/Storage/WebSWClientConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::updatePreferencesStore):
+        (WebKit::WebSWContextManagerConnection::didFailHeartBeatCheck):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+
 2019-12-23  Alexey Proskuryakov  <ap@apple.com>
 
         watchOS build fix attempt
index 2581e12..8e8cf51 100644 (file)
@@ -396,7 +396,7 @@ void WebSWServerConnection::updateThrottleState()
     }
 }
 
-void WebSWServerConnection::contextConnectionCreated(WebCore::SWServerToContextConnection& contextConnection)
+void WebSWServerConnection::contextConnectionCreated(SWServerToContextConnection& contextConnection)
 {
     auto& connection =  static_cast<WebSWServerToContextConnection&>(contextConnection);
     connection.setThrottleState(computeThrottleState(connection.registrableDomain()));
@@ -405,12 +405,18 @@ void WebSWServerConnection::contextConnectionCreated(WebCore::SWServerToContextC
         m_networkProcess->parentProcessConnection()->send(Messages::NetworkProcessProxy::RegisterServiceWorkerClientProcess { identifier(), connection.webProcessIdentifier() }, 0);
 }
 
-void WebSWServerConnection::syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&& identifier, CompletionHandler<void()>&& completionHandler)
+void WebSWServerConnection::syncTerminateWorkerFromClient(ServiceWorkerIdentifier identifier, CompletionHandler<void()>&& completionHandler)
 {
     syncTerminateWorker(WTFMove(identifier));
     completionHandler();
 }
 
+void WebSWServerConnection::isServiceWorkerRunning(ServiceWorkerIdentifier identifier, CompletionHandler<void(bool)>&& completionHandler)
+{
+    auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier);
+    completionHandler(worker ? worker->isRunning() : false);
+}
+
 PAL::SessionID WebSWServerConnection::sessionID() const
 {
     return server().sessionID();
index fc0ba3a..16b1976 100644 (file)
@@ -93,7 +93,8 @@ private:
 
     void registerServiceWorkerClient(WebCore::SecurityOriginData&& topOrigin, WebCore::ServiceWorkerClientData&&, const Optional<WebCore::ServiceWorkerRegistrationIdentifier>&, String&& userAgent);
     void unregisterServiceWorkerClient(const WebCore::ServiceWorkerClientIdentifier&);
-    void syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&&, CompletionHandler<void()>&&);
+    void syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&);
+    void isServiceWorkerRunning(WebCore::ServiceWorkerIdentifier, CompletionHandler<void(bool)>&&);
 
     void postMessageToServiceWorkerClient(WebCore::DocumentIdentifier destinationContextIdentifier, const WebCore::MessageWithMessagePorts&, WebCore::ServiceWorkerIdentifier sourceServiceWorkerIdentifier, const String& sourceOrigin) final;
 
index a92eede..fdbceb8 100644 (file)
@@ -43,6 +43,7 @@ messages -> WebSWServerConnection NotRefCounted {
 
     SetThrottleState(bool isThrottleable)
     StoreRegistrationsOnDisk() -> () Async
+    IsServiceWorkerRunning(WebCore::ServiceWorkerIdentifier workerIdentifier) -> (bool isRunning) Async
 }
 
 #endif // ENABLE(SERVICE_WORKER)
index 25e96f3..8e2a7a7 100644 (file)
@@ -37,6 +37,7 @@ messages -> WebSWServerToContextConnection NotRefCounted {
     Claim(uint64_t claimRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier);
     SetScriptResource(WebCore::ServiceWorkerIdentifier identifier, URL scriptURL, String script, URL responseURL, String mimeType);
     PostMessageToServiceWorkerClient(struct WebCore::ServiceWorkerClientIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
+    DidFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier identifier);
 }
 
 #endif // ENABLE(SERVICE_WORKER)
index fb208e2..f15c07d 100644 (file)
@@ -1234,6 +1234,10 @@ ShouldDecidePolicyBeforeLoadingQuickLookPreview:
   defaultValue: false
   type: bool
 
+ShouldUseServiceWorkerShortTimeout:
+  defaultValue: false
+  type: bool
+
 # For experimental features:
 # The type should be boolean.
 # You must provide a humanReadableName and humanReadableDescription for all experimental features. They
index 88ca770..ac31a6e 100644 (file)
@@ -2188,3 +2188,13 @@ void WKPreferencesSetRemotePlaybackEnabled(WKPreferencesRef preferencesRef, bool
 {
     WebKit::toImpl(preferencesRef)->setRemotePlaybackEnabled(enabled);
 }
+
+bool WKPreferencesGetShouldUseServiceWorkerShortTimeout(WKPreferencesRef preferencesRef)
+{
+    return WebKit::toImpl(preferencesRef)->shouldUseServiceWorkerShortTimeout();
+}
+
+void WKPreferencesSetShouldUseServiceWorkerShortTimeout(WKPreferencesRef preferencesRef, bool enabled)
+{
+    WebKit::toImpl(preferencesRef)->setShouldUseServiceWorkerShortTimeout(enabled);
+}
index b658f56..108df79 100644 (file)
@@ -344,6 +344,10 @@ WK_EXPORT bool WKPreferencesGetCaptureVideoInUIProcessEnabled(WKPreferencesRef p
 WK_EXPORT bool WKPreferencesGetRemotePlaybackEnabled(WKPreferencesRef preferencesRef);
 WK_EXPORT void WKPreferencesSetRemotePlaybackEnabled(WKPreferencesRef preferencesRef, bool enabled);
 
+// Defaults to false.
+WK_EXPORT bool WKPreferencesGetShouldUseServiceWorkerShortTimeout(WKPreferencesRef preferencesRef);
+WK_EXPORT void WKPreferencesSetShouldUseServiceWorkerShortTimeout(WKPreferencesRef preferencesRef, bool enabled);
+
 #ifdef __cplusplus
 }
 #endif
index 8e33dfb..6af6ffb 100644 (file)
@@ -242,6 +242,11 @@ void WebSWClientConnection::syncTerminateWorker(ServiceWorkerIdentifier identifi
     sendSync(Messages::WebSWServerConnection::SyncTerminateWorkerFromClient { identifier }, Messages::WebSWServerConnection::SyncTerminateWorkerFromClient::Reply());
 }
 
+void WebSWClientConnection::isServiceWorkerRunning(ServiceWorkerIdentifier identifier, CompletionHandler<void(bool)>&& callback)
+{
+    sendWithAsyncReply(Messages::WebSWServerConnection::IsServiceWorkerRunning { identifier }, WTFMove(callback));
+}
+
 void WebSWClientConnection::updateThrottleState()
 {
     m_isThrottleable = WebProcess::singleton().areAllPagesThrottleable();
index 439910f..9dbfbab 100644 (file)
@@ -85,6 +85,7 @@ private:
     void setDocumentIsControlled(WebCore::DocumentIdentifier, WebCore::ServiceWorkerRegistrationData&&, CompletionHandler<void(bool)>&&);
 
     void getRegistrations(WebCore::SecurityOriginData&& topOrigin, const URL& clientURL, GetRegistrationsCallback&&) final;
+    void isServiceWorkerRunning(WebCore::ServiceWorkerIdentifier, CompletionHandler<void(bool)>&&) final;
 
     void didResolveRegistrationPromise(const WebCore::ServiceWorkerRegistrationKey&) final;
     void storeRegistrationsOnDiskForTesting(CompletionHandler<void()>&&) final;
index b50ebf9..b74a6ef 100644 (file)
@@ -126,6 +126,7 @@ void WebSWContextManagerConnection::updatePreferencesStore(const WebPreferencesS
     RuntimeEnabledFeatures::sharedFeatures().setSecureContextChecksEnabled(store.getBoolValueForKey(WebPreferencesKey::secureContextChecksEnabledKey()));
 
     m_storageBlockingPolicy = static_cast<SecurityOrigin::StorageBlockingPolicy>(store.getUInt32ValueForKey(WebPreferencesKey::storageBlockingPolicyKey()));
+    setShouldUseShortTimeout(store.getBoolValueForKey(WebPreferencesKey::shouldUseServiceWorkerShortTimeoutKey()));
 }
 
 void WebSWContextManagerConnection::installServiceWorker(const ServiceWorkerContextData& data, String&& userAgent)
@@ -367,6 +368,11 @@ bool WebSWContextManagerConnection::isThrottleable() const
     return m_isThrottleable;
 }
 
+void WebSWContextManagerConnection::didFailHeartBeatCheck(ServiceWorkerIdentifier serviceWorkerIdentifier)
+{
+    m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::DidFailHeartBeatCheck { serviceWorkerIdentifier }, 0);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 6f7dc75..422a979 100644 (file)
@@ -79,6 +79,7 @@ private:
     void skipWaiting(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&) final;
     void setScriptResource(WebCore::ServiceWorkerIdentifier, const URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final;
     bool isThrottleable() const final;
+    void didFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier) final;
 
     // IPC messages.
     void serviceWorkerStarted(Optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, bool doesHandleFetch) final;
index 3b16ace..c7dc790 100644 (file)
@@ -1,3 +1,15 @@
+2019-12-24  youenn fablet  <youenn@apple.com>
+
+        Service Worker doesn't terminate after a period of time when thread blocking
+        https://bugs.webkit.org/show_bug.cgi?id=202992
+        <rdar://problem/56298596>
+
+        Reviewed by Chris Dumez.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetPreferencesToConsistentValues):
+        Enable small timeout values.
+
 2019-12-23  Keith Miller  <keith_miller@apple.com>
 
         DFG/FTL should be able to exit to the middle of a bytecode
index a1e2ff9..946c1fc 100644 (file)
@@ -932,6 +932,8 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio
     WKPreferencesSetVideoPlaybackRequiresUserGesture(preferences, false);
     WKPreferencesSetAudioPlaybackRequiresUserGesture(preferences, false);
 
+    WKPreferencesSetShouldUseServiceWorkerShortTimeout(preferences, true);
+
     platformResetPreferencesToConsistentValues();
 }