Do not timeout a load intercepted by service worker that receives a response
https://bugs.webkit.org/show_bug.cgi?id=202787
Reviewed by Chris Dumez.
Source/WebKit:
Stop making ServiceWorkerFetchTask ref counted since it is not needed and
can potentially make ServiceWorkerFetchTask oulive its WebSWServerToContextConnection member.
Stop the ServiceWorkerFetchTask timeout timer whenever receiving a response so that the load will not timeout in that case.
This ensures that a load that is starting in a service worker will not be failing.
Instead the load will go to network process.
Removed m_didReachTerminalState which is not needed as WebSWServerToContextConnection unregisters the ServiceWorkerFetchTask
as an IPC listener for all terminating messages.
* NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp:
(WebKit::ServiceWorkerFetchTask::didReceiveRedirectResponse):
(WebKit::ServiceWorkerFetchTask::didReceiveResponse):
(WebKit::ServiceWorkerFetchTask::didReceiveData):
(WebKit::ServiceWorkerFetchTask::didReceiveFormData):
(WebKit::ServiceWorkerFetchTask::didFinish):
(WebKit::ServiceWorkerFetchTask::didFail):
(WebKit::ServiceWorkerFetchTask::didNotHandle):
(WebKit::ServiceWorkerFetchTask::timeoutTimerFired):
* NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::startFetch):
(WebKit::WebSWServerToContextConnection::fetchTaskTimedOut):
Use a Vector instead of a HasSet for performance reasons.
Update according fetch map using unique_ptr instead of Ref<>.
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
LayoutTests:
* http/wpt/service-workers/fetch-timeout-worker.js: Added.
(async.doTest):
* http/wpt/service-workers/fetch-timeout.https-expected.txt: Added.
* http/wpt/service-workers/fetch-timeout.https.html: Added.
* http/wpt/service-workers/resources/lengthy-pass.py:
(main):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@250985 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 953287f..5812f13 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,17 @@
+2019-10-10 Youenn Fablet <youenn@apple.com>
+
+ Do not timeout a load intercepted by service worker that receives a response
+ https://bugs.webkit.org/show_bug.cgi?id=202787
+
+ Reviewed by Chris Dumez.
+
+ * http/wpt/service-workers/fetch-timeout-worker.js: Added.
+ (async.doTest):
+ * http/wpt/service-workers/fetch-timeout.https-expected.txt: Added.
+ * http/wpt/service-workers/fetch-timeout.https.html: Added.
+ * http/wpt/service-workers/resources/lengthy-pass.py:
+ (main):
+
2019-10-10 Myles C. Maxfield <mmaxfield@apple.com>
FontFaceSet's ready promise is not always resolved
diff --git a/LayoutTests/http/wpt/service-workers/fetch-timeout-worker.js b/LayoutTests/http/wpt/service-workers/fetch-timeout-worker.js
new file mode 100644
index 0000000..081cc8d
--- /dev/null
+++ b/LayoutTests/http/wpt/service-workers/fetch-timeout-worker.js
@@ -0,0 +1,6 @@
+async function doTest(event)
+{
+ event.respondWith(fetch(event.request));
+}
+
+self.addEventListener("fetch", doTest);
diff --git a/LayoutTests/http/wpt/service-workers/fetch-timeout.https-expected.txt b/LayoutTests/http/wpt/service-workers/fetch-timeout.https-expected.txt
new file mode 100644
index 0000000..8022b2d
--- /dev/null
+++ b/LayoutTests/http/wpt/service-workers/fetch-timeout.https-expected.txt
@@ -0,0 +1,4 @@
+
+PASS Setup worker
+PASS Make sure a load that makes progress does not time out
+
diff --git a/LayoutTests/http/wpt/service-workers/fetch-timeout.https.html b/LayoutTests/http/wpt/service-workers/fetch-timeout.https.html
new file mode 100644
index 0000000..4663e4e
--- /dev/null
+++ b/LayoutTests/http/wpt/service-workers/fetch-timeout.https.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+</head>
+<body>
+<script>
+var scope = "resources";
+var activeWorker;
+
+promise_test(async (test) => {
+ var registration = await navigator.serviceWorker.register("fetch-timeout-worker.js", { scope : scope });
+ activeWorker = registration.active;
+ if (activeWorker)
+ return;
+ activeWorker = registration.installing;
+ return new Promise(resolve => {
+ activeWorker.addEventListener('statechange', () => {
+ if (activeWorker.state === "activated")
+ resolve();
+ });
+ });
+}, "Setup worker");
+
+promise_test(async (test) => {
+ const iframe = await with_iframe(scope);
+
+ if (window.testRunner)
+ testRunner.setServiceWorkerFetchTimeout(1);
+
+ const response = await iframe.contentWindow.fetch("/WebKit/service-workers/resources/lengthy-pass.py?delay=0.5");
+ const text = await response.text();
+ assert_equals(text, "document.body.innerHTML = 'PASS'");
+ iframe.remove();
+}, "Make sure a load that makes progress does not time out");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/wpt/service-workers/resources/lengthy-pass.py b/LayoutTests/http/wpt/service-workers/resources/lengthy-pass.py
index 04ef57a..14dd790 100644
--- a/LayoutTests/http/wpt/service-workers/resources/lengthy-pass.py
+++ b/LayoutTests/http/wpt/service-workers/resources/lengthy-pass.py
@@ -2,6 +2,9 @@
def main(request, response):
delay = 0.05
+ headers = []
+ if "delay" in request.GET:
+ delay = float(request.GET.first("delay"))
response.headers.set("Content-type", "text/javascript")
response.headers.append("Access-Control-Allow-Origin", "*")
response.write_status_headers()
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 5e1a11b..a9aa697f 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,37 @@
+2019-10-10 Youenn Fablet <youenn@apple.com>
+
+ Do not timeout a load intercepted by service worker that receives a response
+ https://bugs.webkit.org/show_bug.cgi?id=202787
+
+ Reviewed by Chris Dumez.
+
+ Stop making ServiceWorkerFetchTask ref counted since it is not needed and
+ can potentially make ServiceWorkerFetchTask oulive its WebSWServerToContextConnection member.
+
+ Stop the ServiceWorkerFetchTask timeout timer whenever receiving a response so that the load will not timeout in that case.
+ This ensures that a load that is starting in a service worker will not be failing.
+ Instead the load will go to network process.
+
+ Removed m_didReachTerminalState which is not needed as WebSWServerToContextConnection unregisters the ServiceWorkerFetchTask
+ as an IPC listener for all terminating messages.
+
+ * NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp:
+ (WebKit::ServiceWorkerFetchTask::didReceiveRedirectResponse):
+ (WebKit::ServiceWorkerFetchTask::didReceiveResponse):
+ (WebKit::ServiceWorkerFetchTask::didReceiveData):
+ (WebKit::ServiceWorkerFetchTask::didReceiveFormData):
+ (WebKit::ServiceWorkerFetchTask::didFinish):
+ (WebKit::ServiceWorkerFetchTask::didFail):
+ (WebKit::ServiceWorkerFetchTask::didNotHandle):
+ (WebKit::ServiceWorkerFetchTask::timeoutTimerFired):
+ * NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h:
+ * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+ (WebKit::WebSWServerToContextConnection::startFetch):
+ (WebKit::WebSWServerToContextConnection::fetchTaskTimedOut):
+ Use a Vector instead of a HasSet for performance reasons.
+ Update according fetch map using unique_ptr instead of Ref<>.
+ * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
+
2019-10-10 Rob Buis <rbuis@igalia.com>
SpeculativeLoad should use CompletionHandler
diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp
index 0dcb3b3..834b569 100644
--- a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp
+++ b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.cpp
@@ -59,6 +59,7 @@
void ServiceWorkerFetchTask::didReceiveRedirectResponse(const ResourceResponse& response)
{
RELEASE_LOG_IF_ALLOWED("didReceiveRedirectResponse: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
+ m_timeoutTimer.stop();
m_wasHandled = true;
if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidReceiveRedirectResponse { response }, m_identifier.fetchIdentifier);
@@ -67,6 +68,7 @@
void ServiceWorkerFetchTask::didReceiveResponse(const ResourceResponse& response, bool needsContinueDidReceiveResponseMessage)
{
RELEASE_LOG_IF_ALLOWED("didReceiveResponse: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
+ m_timeoutTimer.stop();
m_wasHandled = true;
if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidReceiveResponse { response, needsContinueDidReceiveResponseMessage }, m_identifier.fetchIdentifier);
@@ -74,51 +76,47 @@
void ServiceWorkerFetchTask::didReceiveData(const IPC::DataReference& data, int64_t encodedDataLength)
{
+ ASSERT(!m_timeoutTimer.isActive());
if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidReceiveData { data, encodedDataLength }, m_identifier.fetchIdentifier);
}
void ServiceWorkerFetchTask::didReceiveFormData(const IPC::FormDataReference& formData)
{
+ ASSERT(!m_timeoutTimer.isActive());
if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidReceiveFormData { formData }, m_identifier.fetchIdentifier);
}
void ServiceWorkerFetchTask::didFinish()
{
+ ASSERT(!m_timeoutTimer.isActive());
RELEASE_LOG_IF_ALLOWED("didFinishFetch: fetchIdentifier: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
m_timeoutTimer.stop();
- if (!m_didReachTerminalState && m_connection)
+ if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidFinish { }, m_identifier.fetchIdentifier);
- m_didReachTerminalState = true;
}
void ServiceWorkerFetchTask::didFail(const ResourceError& error)
{
RELEASE_LOG_ERROR_IF_ALLOWED("didFailFetch: fetchIdentifier: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
m_timeoutTimer.stop();
- if (!m_didReachTerminalState && m_connection)
+ if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidFail { error }, m_identifier.fetchIdentifier);
- m_didReachTerminalState = true;
}
void ServiceWorkerFetchTask::didNotHandle()
{
RELEASE_LOG_IF_ALLOWED("didNotHandleFetch: fetchIdentifier: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
m_timeoutTimer.stop();
- if (!m_didReachTerminalState && m_connection)
+ if (m_connection)
m_connection->send(Messages::ServiceWorkerClientFetch::DidNotHandle { }, m_identifier.fetchIdentifier);
- m_didReachTerminalState = true;
}
void ServiceWorkerFetchTask::timeoutTimerFired()
{
RELEASE_LOG_IF_ALLOWED("timeoutTimerFired: fetchIdentifier: %s", m_identifier.fetchIdentifier.loggingString().utf8().data());
- if (!m_wasHandled)
- didNotHandle();
- else
- didFail({ errorDomainWebKitInternal, 0, { }, "Service Worker fetch timed out"_s });
-
+ didNotHandle();
m_contextConnection.fetchTaskTimedOut(*this);
}
diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h
index 6add0d0..d8b9d6f 100644
--- a/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h
+++ b/Source/WebKit/NetworkProcess/ServiceWorker/ServiceWorkerFetchTask.h
@@ -31,7 +31,6 @@
#include <WebCore/ServiceWorkerTypes.h>
#include <WebCore/Timer.h>
#include <pal/SessionID.h>
-#include <wtf/RefCounted.h>
namespace WebCore {
class ResourceError;
@@ -50,12 +49,10 @@
class WebSWServerConnection;
class WebSWServerToContextConnection;
-class ServiceWorkerFetchTask : public RefCounted<ServiceWorkerFetchTask> {
+class ServiceWorkerFetchTask {
+ WTF_MAKE_FAST_ALLOCATED;
public:
- template<typename... Args> static Ref<ServiceWorkerFetchTask> create(Args&&... args)
- {
- return adoptRef(*new ServiceWorkerFetchTask(std::forward<Args>(args)...));
- }
+ ServiceWorkerFetchTask(PAL::SessionID, WebSWServerConnection&, WebSWServerToContextConnection&, WebCore::FetchIdentifier, WebCore::ServiceWorkerIdentifier, Seconds timeout);
void didNotHandle();
void fail(const WebCore::ResourceError& error) { didFail(error); }
@@ -79,8 +76,6 @@
bool wasHandled() const { return m_wasHandled; }
private:
- ServiceWorkerFetchTask(PAL::SessionID, WebSWServerConnection&, WebSWServerToContextConnection&, WebCore::FetchIdentifier, WebCore::ServiceWorkerIdentifier, Seconds timeout);
-
void didReceiveRedirectResponse(const WebCore::ResourceResponse&);
void didReceiveResponse(const WebCore::ResourceResponse&, bool needsContinueDidReceiveResponseMessage);
void didReceiveData(const IPC::DataReference&, int64_t encodedDataLength);
@@ -97,7 +92,6 @@
Seconds m_timeout;
WebCore::Timer m_timeoutTimer;
bool m_wasHandled { false };
- bool m_didReachTerminalState { false };
};
inline bool operator==(const ServiceWorkerFetchTask::Identifier& a, const ServiceWorkerFetchTask::Identifier& b)
diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp b/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp
index e36bb12..85d9e95 100644
--- a/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp
+++ b/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp
@@ -157,7 +157,7 @@
auto serverConnectionIdentifier = contentConnection.identifier();
auto fetchIdentifier = FetchIdentifier::generate();
- auto result = m_ongoingFetches.add(fetchIdentifier, ServiceWorkerFetchTask::create(sessionID, contentConnection, *this, contentFetchIdentifier, serviceWorkerIdentifier, m_networkProcess->serviceWorkerFetchTimeout()));
+ auto result = m_ongoingFetches.add(fetchIdentifier, makeUnique<ServiceWorkerFetchTask>(sessionID, contentConnection, *this, contentFetchIdentifier, serviceWorkerIdentifier, m_networkProcess->serviceWorkerFetchTimeout()));
ASSERT(!m_ongoingFetchIdentifiers.contains({ serverConnectionIdentifier, contentFetchIdentifier }));
m_ongoingFetchIdentifiers.add({ serverConnectionIdentifier, contentFetchIdentifier }, fetchIdentifier);
@@ -215,17 +215,17 @@
ASSERT(m_ongoingFetches.contains(takenIdentifier));
auto takenTask = m_ongoingFetches.take(takenIdentifier);
ASSERT(takenTask);
- ASSERT(takenTask->ptr() == &task);
+ ASSERT(takenTask.get() == &task);
// Gather all other fetches in this service worker
- HashSet<Ref<ServiceWorkerFetchTask>> otherFetches;
+ Vector<ServiceWorkerFetchTask*> otherFetches;
for (auto& fetchTask : m_ongoingFetches.values()) {
if (fetchTask->serviceWorkerIdentifier() == task.serviceWorkerIdentifier())
- otherFetches.add(fetchTask.copyRef());
+ otherFetches.append(fetchTask.get());
}
// Signal load failure for them
- for (auto& fetchTask : otherFetches) {
+ for (auto* fetchTask : otherFetches) {
if (fetchTask->wasHandled())
fetchTask->fail({ errorDomainWebKitInternal, 0, { }, "Service Worker context closed"_s });
else
diff --git a/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h b/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h
index 57bb4f3..e30f341 100644
--- a/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h
+++ b/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h
@@ -103,7 +103,7 @@
WeakPtr<WebCore::SWServer> m_server;
HashMap<ServiceWorkerFetchTask::Identifier, WebCore::FetchIdentifier> m_ongoingFetchIdentifiers;
- HashMap<WebCore::FetchIdentifier, Ref<ServiceWorkerFetchTask>> m_ongoingFetches;
+ HashMap<WebCore::FetchIdentifier, std::unique_ptr<ServiceWorkerFetchTask>> m_ongoingFetches;
bool m_isThrottleable { true };
}; // class WebSWServerToContextConnection