[Service Workers] Implement "Soft Update" algorithm
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Dec 2017 21:17:46 +0000 (21:17 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Dec 2017 21:17:46 +0000 (21:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180702
<rdar://problem/36163461>

Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

Rebaseline Soft Update WPT test now that it is passing.

* web-platform-tests/service-workers/service-worker/update-after-navigation-fetch-event.https-expected.txt:

Source/WebCore:

Implement "Soft Update" algorithm:
- https://w3c.github.io/ServiceWorker/#soft-update-algorithm

Call softUpdate at the end of "Handle Fetch", as per:
- https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm

No new tests, rebaselined existing test.

* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::postTaskTo):
* dom/ScriptExecutionContext.h:
* workers/service/SWClientConnection.cpp:
(WebCore::SWClientConnection::failedFetchingScript):
(WebCore::SWClientConnection::registrationJobResolvedInServer):
(WebCore::SWClientConnection::startScriptFetchForServer):
* workers/service/SWClientConnection.h:
* workers/service/ServiceWorkerContainer.cpp:
(WebCore::ServiceWorkerContainer::updateRegistration):
(WebCore::ServiceWorkerContainer::jobFailedWithException):
(WebCore::ServiceWorkerContainer::jobResolvedWithRegistration):
(WebCore::ServiceWorkerContainer::jobResolvedWithUnregistrationResult):
(WebCore::ServiceWorkerContainer::startScriptFetchForJob):
(WebCore::ServiceWorkerContainer::jobFailedLoadingScript):
* workers/service/ServiceWorkerContainer.h:
* workers/service/ServiceWorkerJob.cpp:
(WebCore::ServiceWorkerJob::ServiceWorkerJob):
* workers/service/ServiceWorkerJob.h:
(WebCore::ServiceWorkerJob::create):
(WebCore::ServiceWorkerJob::data const):
(WebCore::ServiceWorkerJob::promise):
* workers/service/ServiceWorkerRegistration.cpp:
(WebCore::ServiceWorkerRegistration::softUpdate):
* workers/service/ServiceWorkerRegistration.h:
* workers/service/context/SWContextManager.cpp:
(WebCore::SWContextManager::terminateWorker):
(WebCore::SWContextManager::postTaskToServiceWorker):
* workers/service/context/SWContextManager.h:
* workers/service/context/ServiceWorkerFetch.cpp:
(WebCore::ServiceWorkerFetch::dispatchFetchEvent):
* workers/service/context/ServiceWorkerFetch.h:
* workers/service/server/SWServer.cpp:
(WebCore::SWServer::startScriptFetch):
* workers/service/server/SWServer.h:

Source/WebKit:

* StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::startScriptFetchInClient):
* StorageProcess/ServiceWorker/WebSWServerConnection.h:
* WebProcess/Storage/WebSWClientConnection.messages.in:

LayoutTests:

Unskip soft update WPT test that no longer times out.

* TestExpectations:

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

26 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/update-after-navigation-fetch-event.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/ScriptExecutionContext.cpp
Source/WebCore/dom/ScriptExecutionContext.h
Source/WebCore/workers/WorkerGlobalScope.cpp
Source/WebCore/workers/service/SWClientConnection.cpp
Source/WebCore/workers/service/SWClientConnection.h
Source/WebCore/workers/service/ServiceWorkerContainer.cpp
Source/WebCore/workers/service/ServiceWorkerContainer.h
Source/WebCore/workers/service/ServiceWorkerJob.cpp
Source/WebCore/workers/service/ServiceWorkerJob.h
Source/WebCore/workers/service/ServiceWorkerRegistration.cpp
Source/WebCore/workers/service/ServiceWorkerRegistration.h
Source/WebCore/workers/service/context/SWContextManager.cpp
Source/WebCore/workers/service/context/SWContextManager.h
Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp
Source/WebCore/workers/service/context/ServiceWorkerFetch.h
Source/WebCore/workers/service/server/SWServer.cpp
Source/WebCore/workers/service/server/SWServer.h
Source/WebKit/ChangeLog
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.cpp
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.h
Source/WebKit/WebProcess/Storage/WebSWClientConnection.messages.in

index 1cfdbf4..32297e4 100644 (file)
@@ -1,3 +1,15 @@
+2017-12-22  Chris Dumez  <cdumez@apple.com>
+
+        [Service Workers] Implement "Soft Update" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=180702
+        <rdar://problem/36163461>
+
+        Reviewed by Youenn Fablet.
+
+        Unskip soft update WPT test that no longer times out.
+
+        * TestExpectations:
+
 2017-12-21  Ryosuke Niwa  <rniwa@webkit.org>
 
         REGRESSION(r223678): Cannot copy & paste a web page content into Yahoo! Mail
index 36f450d..f206a04 100644 (file)
@@ -154,7 +154,6 @@ imported/w3c/web-platform-tests/service-workers/service-worker/postmessage.https
 imported/w3c/web-platform-tests/service-workers/service-worker/postmessage-msgport-to-client.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/respond-with-body-accessed-response.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/sandboxed-iframe-fetch-event.https.html [ Skip ]
-imported/w3c/web-platform-tests/service-workers/service-worker/update-after-navigation-fetch-event.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/update-after-oneday.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/update-bytecheck.https.html [ Skip ]
 imported/w3c/web-platform-tests/service-workers/service-worker/update-recovery.https.html [ Skip ]
index 85411bd..d592d21 100644 (file)
@@ -1,3 +1,15 @@
+2017-12-22  Chris Dumez  <cdumez@apple.com>
+
+        [Service Workers] Implement "Soft Update" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=180702
+        <rdar://problem/36163461>
+
+        Reviewed by Youenn Fablet.
+
+        Rebaseline Soft Update WPT test now that it is passing.
+
+        * web-platform-tests/service-workers/service-worker/update-after-navigation-fetch-event.https-expected.txt:
+
 2017-12-21  Youenn Fablet  <youenn@apple.com>
 
         ServiceWorkerThreadProxy should set the correct cookie and cache partitioning options
index 167e411..6daf8e7 100644 (file)
@@ -1,3 +1,55 @@
+2017-12-22  Chris Dumez  <cdumez@apple.com>
+
+        [Service Workers] Implement "Soft Update" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=180702
+        <rdar://problem/36163461>
+
+        Reviewed by Youenn Fablet.
+
+        Implement "Soft Update" algorithm:
+        - https://w3c.github.io/ServiceWorker/#soft-update-algorithm
+
+        Call softUpdate at the end of "Handle Fetch", as per:
+        - https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
+
+        No new tests, rebaselined existing test.
+
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::postTaskTo):
+        * dom/ScriptExecutionContext.h:
+        * workers/service/SWClientConnection.cpp:
+        (WebCore::SWClientConnection::failedFetchingScript):
+        (WebCore::SWClientConnection::registrationJobResolvedInServer):
+        (WebCore::SWClientConnection::startScriptFetchForServer):
+        * workers/service/SWClientConnection.h:
+        * workers/service/ServiceWorkerContainer.cpp:
+        (WebCore::ServiceWorkerContainer::updateRegistration):
+        (WebCore::ServiceWorkerContainer::jobFailedWithException):
+        (WebCore::ServiceWorkerContainer::jobResolvedWithRegistration):
+        (WebCore::ServiceWorkerContainer::jobResolvedWithUnregistrationResult):
+        (WebCore::ServiceWorkerContainer::startScriptFetchForJob):
+        (WebCore::ServiceWorkerContainer::jobFailedLoadingScript):
+        * workers/service/ServiceWorkerContainer.h:
+        * workers/service/ServiceWorkerJob.cpp:
+        (WebCore::ServiceWorkerJob::ServiceWorkerJob):
+        * workers/service/ServiceWorkerJob.h:
+        (WebCore::ServiceWorkerJob::create):
+        (WebCore::ServiceWorkerJob::data const):
+        (WebCore::ServiceWorkerJob::promise):
+        * workers/service/ServiceWorkerRegistration.cpp:
+        (WebCore::ServiceWorkerRegistration::softUpdate):
+        * workers/service/ServiceWorkerRegistration.h:
+        * workers/service/context/SWContextManager.cpp:
+        (WebCore::SWContextManager::terminateWorker):
+        (WebCore::SWContextManager::postTaskToServiceWorker):
+        * workers/service/context/SWContextManager.h:
+        * workers/service/context/ServiceWorkerFetch.cpp:
+        (WebCore::ServiceWorkerFetch::dispatchFetchEvent):
+        * workers/service/context/ServiceWorkerFetch.h:
+        * workers/service/server/SWServer.cpp:
+        (WebCore::SWServer::startScriptFetch):
+        * workers/service/server/SWServer.h:
+
 2017-12-22  Zalan Bujtas  <zalan@apple.com>
 
         [RenderTreeBuilder] Move RenderMenuList::addChild() tree mutation to RenderTreeBuilder
index d3f448f..fc3f096 100644 (file)
@@ -570,10 +570,11 @@ ServiceWorkerContainer* ScriptExecutionContext::serviceWorkerContainer()
     return navigator ? &navigator->serviceWorker() : nullptr;
 }
 
-void ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void(ScriptExecutionContext&)>&& task)
+bool ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void(ScriptExecutionContext&)>&& task)
 {
     ASSERT(isMainThread());
 
+    bool wasPosted = false;
     switchOn(contextIdentifier, [&] (DocumentIdentifier identifier) {
         auto* document = Document::allDocumentsMap().get(identifier);
         if (!document)
@@ -581,11 +582,13 @@ void ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contex
         document->postTask([task = WTFMove(task)](auto& scope) {
             task(scope);
         });
+        wasPosted= true;
     }, [&](ServiceWorkerIdentifier identifier) {
-        SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](auto& scope) {
+        wasPosted = SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](auto& scope) {
             task(scope);
         });
     });
+    return wasPosted;
 }
 #endif
 
index f3ecfae..ae14a15 100644 (file)
@@ -251,7 +251,7 @@ public:
 
     ServiceWorkerContainer* serviceWorkerContainer();
 
-    WEBCORE_EXPORT static void postTaskTo(const DocumentOrWorkerIdentifier&, WTF::Function<void(ScriptExecutionContext&)>&&);
+    WEBCORE_EXPORT static bool postTaskTo(const DocumentOrWorkerIdentifier&, WTF::Function<void(ScriptExecutionContext&)>&&);
 #endif
 
 protected:
index 8a66647..2f94814 100644 (file)
@@ -269,10 +269,8 @@ ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls)
         // FIXME: Fully implement https://w3c.github.io/ServiceWorker/#importscripts.
         auto& serviceWorkerGlobalScope = downcast<ServiceWorkerGlobalScope>(*this);
         auto& registration = serviceWorkerGlobalScope.registration();
-        if (registration.updateViaCache() == ServiceWorkerUpdateViaCache::None
-            || (registration.lastUpdateTime() && (WallTime::now() - registration.lastUpdateTime()) > 86400_s)) {
+        if (registration.updateViaCache() == ServiceWorkerUpdateViaCache::None || registration.needsUpdate())
             cachePolicy = FetchOptions::Cache::NoCache;
-        }
     }
 #endif
 
index 6fe0611..1f3221f 100644 (file)
@@ -38,6 +38,7 @@
 #include "ServiceWorkerJobData.h"
 #include "ServiceWorkerRegistration.h"
 #include <wtf/CrossThreadCopier.h>
+#include <wtf/Scope.h>
 
 namespace WebCore {
 
@@ -63,12 +64,11 @@ void SWClientConnection::finishedFetchingScript(ServiceWorkerJob& job, const Str
     finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), script, { } });
 }
 
-void SWClientConnection::failedFetchingScript(ServiceWorkerJob& job, const ResourceError& error)
+void SWClientConnection::failedFetchingScript(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ServiceWorkerRegistrationKey& registrationKey, const ResourceError& error)
 {
     ASSERT(isMainThread());
-    ASSERT(m_scheduledJobs.get(job.identifier()) == &job);
 
-    finishFetchingScriptInServer({ job.data().identifier(), job.data().registrationKey(), { }, error });
+    finishFetchingScriptInServer({ jobDataIdentifier, registrationKey, { }, error });
 }
 
 void SWClientConnection::jobRejectedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ExceptionData& exceptionData)
@@ -90,15 +90,23 @@ void SWClientConnection::registrationJobResolvedInServer(const ServiceWorkerJobD
 {
     ASSERT(isMainThread());
 
+    auto guard = WTF::makeScopeExit([this, shouldNotifyWhenResolved, registrationKey = registrationData.key] {
+        if (shouldNotifyWhenResolved == ShouldNotifyWhenResolved::Yes)
+            didResolveRegistrationPromise(registrationKey);
+    });
+
     auto job = m_scheduledJobs.take(jobDataIdentifier.jobIdentifier);
     if (!job) {
         LOG_ERROR("Job %s resolved in server, but was not found", jobDataIdentifier.loggingString().utf8().data());
         return;
     }
 
-    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved](ScriptExecutionContext&) mutable {
+    bool wasPosted = ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, registrationData = registrationData.isolatedCopy(), shouldNotifyWhenResolved](ScriptExecutionContext&) mutable {
         job->resolvedWithRegistration(WTFMove(registrationData), shouldNotifyWhenResolved);
     });
+
+    if (wasPosted)
+        guard.release();
 }
 
 void SWClientConnection::unregistrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, bool unregistrationResult)
@@ -116,7 +124,7 @@ void SWClientConnection::unregistrationJobResolvedInServer(const ServiceWorkerJo
     });
 }
 
-void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, FetchOptions::Cache cachePolicy)
+void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy)
 {
     ASSERT(isMainThread());
 
@@ -124,16 +132,15 @@ void SWClientConnection::startScriptFetchForServer(const ServiceWorkerJobDataIde
     if (!job) {
         LOG_ERROR("Job %s instructed to start fetch from server, but job was not found", jobDataIdentifier.loggingString().utf8().data());
 
-        // FIXME: Should message back to the server here to signal failure to fetch,
-        // but we currently need the registration key to do so, and don't have it here.
-        // In the future we'll refactor to have a global, cross-process job identifier that can be used to overcome this.
-
+        failedFetchingScript(jobDataIdentifier, registrationKey, ResourceError { errorDomainWebKitInternal, 0, URL(), ASCIILiteral("Failed to fetch service worker script") });
         return;
     }
 
-    ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, cachePolicy](ScriptExecutionContext&) {
+    bool wasPosted = ScriptExecutionContext::postTaskTo(job->contextIdentifier(), [job, cachePolicy](ScriptExecutionContext&) {
         job->startScriptFetch(cachePolicy);
     });
+    if (!wasPosted)
+        failedFetchingScript(jobDataIdentifier, registrationKey, ResourceError { errorDomainWebKitInternal, 0, job->data().scriptURL, ASCIILiteral("Failed to fetch service worker script") });
 }
 
 void SWClientConnection::postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, Ref<SerializedScriptValue>&& message, ServiceWorkerData&& sourceData, const String& sourceOrigin)
index 7875f64..4ae78a3 100644 (file)
@@ -70,7 +70,7 @@ public:
 
     void scheduleJob(ServiceWorkerJob&);
     void finishedFetchingScript(ServiceWorkerJob&, const String&);
-    void failedFetchingScript(ServiceWorkerJob&, const ResourceError&);
+    void failedFetchingScript(const ServiceWorkerJobDataIdentifier&, const ServiceWorkerRegistrationKey&, const ResourceError&);
 
     virtual void didResolveRegistrationPromise(const ServiceWorkerRegistrationKey&) = 0;
 
@@ -89,7 +89,7 @@ protected:
     WEBCORE_EXPORT void jobRejectedInServer(const ServiceWorkerJobDataIdentifier&, const ExceptionData&);
     WEBCORE_EXPORT void registrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier&, ServiceWorkerRegistrationData&&, ShouldNotifyWhenResolved);
     WEBCORE_EXPORT void unregistrationJobResolvedInServer(const ServiceWorkerJobDataIdentifier&, bool unregistrationResult);
-    WEBCORE_EXPORT void startScriptFetchForServer(const ServiceWorkerJobDataIdentifier&, FetchOptions::Cache);
+    WEBCORE_EXPORT void startScriptFetchForServer(const ServiceWorkerJobDataIdentifier&, const ServiceWorkerRegistrationKey&, FetchOptions::Cache);
     WEBCORE_EXPORT void postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, Ref<SerializedScriptValue>&& message, ServiceWorkerData&& source, const String& sourceOrigin);
     WEBCORE_EXPORT void updateRegistrationState(ServiceWorkerRegistrationIdentifier, ServiceWorkerRegistrationState, const std::optional<ServiceWorkerData>&);
     WEBCORE_EXPORT void updateWorkerState(ServiceWorkerIdentifier, ServiceWorkerState);
index 7b9c54a..85c1c48 100644 (file)
@@ -189,24 +189,23 @@ void ServiceWorkerContainer::removeRegistration(const URL& scopeURL, Ref<Deferre
     scheduleJob(ServiceWorkerJob::create(*this, WTFMove(promise), WTFMove(jobData)));
 }
 
-void ServiceWorkerContainer::updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, Ref<DeferredPromise>&& promise)
+void ServiceWorkerContainer::updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, RefPtr<DeferredPromise>&& promise)
 {
-    auto* context = scriptExecutionContext();
-    if (!context || !context->sessionID().isValid()) {
-        ASSERT_NOT_REACHED();
-        promise->reject(Exception(InvalidStateError));
-        return;
-    }
+    ASSERT(!m_isStopped);
+
+    auto& context = *scriptExecutionContext();
+    ASSERT(context.sessionID().isValid());
 
     if (!m_swConnection) {
         ASSERT_NOT_REACHED();
-        promise->reject(Exception(InvalidStateError));
+        if (promise)
+            promise->reject(Exception(InvalidStateError));
         return;
     }
 
     ServiceWorkerJobData jobData(m_swConnection->serverConnectionIdentifier());
-    jobData.clientCreationURL = context->url();
-    jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context->topOrigin());
+    jobData.clientCreationURL = context.url();
+    jobData.topOrigin = SecurityOriginData::fromSecurityOrigin(context.topOrigin());
     jobData.type = ServiceWorkerJobType::Update;
     jobData.scopeURL = scopeURL;
     jobData.scriptURL = scriptURL;
@@ -353,12 +352,20 @@ void ServiceWorkerContainer::jobFailedWithException(ServiceWorkerJob& job, const
     ASSERT(m_creationThread.ptr() == &Thread::current());
 #endif
 
+    ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
+
+    auto guard = WTF::makeScopeExit([this, &job] {
+        jobDidFinish(job);
+    });
+
+    if (!job.promise())
+        return;
+
     if (auto* context = scriptExecutionContext()) {
         context->postTask([job = makeRef(job), exception](ScriptExecutionContext&) {
-            job->promise().reject(exception);
+            job->promise()->reject(exception);
         });
     }
-    jobDidFinish(job);
 }
 
 void ServiceWorkerContainer::scheduleTaskToFireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
@@ -376,6 +383,7 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job,
 #ifndef NDEBUG
     ASSERT(m_creationThread.ptr() == &Thread::current());
 #endif
+    ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
 
     auto guard = WTF::makeScopeExit([this, &job] {
         jobDidFinish(job);
@@ -395,6 +403,11 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job,
         return;
     }
 
+    if (!job.promise()) {
+        notifyWhenResolvedIfNeeded();
+        return;
+    }
+
     scriptExecutionContext()->postTask([this, protectedThis = makeRef(*this), job = makeRef(job), data = WTFMove(data), notifyWhenResolvedIfNeeded = WTFMove(notifyWhenResolvedIfNeeded)](ScriptExecutionContext& context) mutable {
         if (isStopped()) {
             notifyWhenResolvedIfNeeded();
@@ -405,7 +418,7 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job,
 
         LOG(ServiceWorker, "Container %p resolved job with registration %p", this, registration.ptr());
 
-        job->promise().resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
+        job->promise()->resolve<IDLInterface<ServiceWorkerRegistration>>(WTFMove(registration));
 
         notifyWhenResolvedIfNeeded();
     });
@@ -417,6 +430,8 @@ void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJo
     ASSERT(m_creationThread.ptr() == &Thread::current());
 #endif
 
+    ASSERT(job.promise());
+
     auto guard = WTF::makeScopeExit([this, &job] {
         jobDidFinish(job);
     });
@@ -428,7 +443,7 @@ void ServiceWorkerContainer::jobResolvedWithUnregistrationResult(ServiceWorkerJo
     }
 
     context->postTask([job = makeRef(job), unregistrationResult](ScriptExecutionContext&) mutable {
-        job->promise().resolve<IDLBoolean>(unregistrationResult);
+        job->promise()->resolve<IDLBoolean>(unregistrationResult);
     });
 }
 
@@ -444,7 +459,7 @@ void ServiceWorkerContainer::startScriptFetchForJob(ServiceWorkerJob& job, Fetch
     if (!context) {
         LOG_ERROR("ServiceWorkerContainer::jobResolvedWithRegistration called but the container's ScriptExecutionContext is gone");
         callOnMainThread([connection = m_swConnection, job = makeRef(job)] {
-            connection->failedFetchingScript(job, { errorDomainWebKitInternal, 0, job->data().scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
+            connection->failedFetchingScript(job->data().identifier(), job->data().registrationKey(), { errorDomainWebKitInternal, 0, job->data().scriptURL, ASCIILiteral("Attempt to fetch service worker script with no ScriptExecutionContext") });
         });
         jobDidFinish(job);
         return;
@@ -471,14 +486,15 @@ void ServiceWorkerContainer::jobFailedLoadingScript(ServiceWorkerJob& job, const
 #ifndef NDEBUG
     ASSERT(m_creationThread.ptr() == &Thread::current());
 #endif
+    ASSERT_WITH_MESSAGE(job.promise() || job.data().type == ServiceWorkerJobType::Update, "Only soft updates have no promise");
 
     LOG(ServiceWorker, "SeviceWorkerContainer %p failed fetching script for job %s", this, job.identifier().loggingString().utf8().data());
 
-    if (exception)
-        job.promise().reject(*exception);
+    if (exception && job.promise())
+        job.promise()->reject(*exception);
 
     callOnMainThread([connection = m_swConnection, job = makeRef(job), error = error.isolatedCopy()] {
-        connection->failedFetchingScript(job, error);
+        connection->failedFetchingScript(job->data().identifier(), job->data().registrationKey(), error);
     });
 }
 
index dc68439..19d25f5 100644 (file)
@@ -61,7 +61,7 @@ public:
     using RegistrationOptions = ServiceWorkerRegistrationOptions;
     void addRegistration(const String& scriptURL, const RegistrationOptions&, Ref<DeferredPromise>&&);
     void removeRegistration(const URL& scopeURL, Ref<DeferredPromise>&&);
-    void updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, Ref<DeferredPromise>&&);
+    void updateRegistration(const URL& scopeURL, const URL& scriptURL, WorkerType, RefPtr<DeferredPromise>&&);
 
     void getRegistration(const String& clientURL, Ref<DeferredPromise>&&);
     void scheduleTaskToUpdateRegistrationState(ServiceWorkerRegistrationIdentifier, ServiceWorkerRegistrationState, const std::optional<ServiceWorkerData>&);
index c1082c8..2024479 100644 (file)
@@ -38,7 +38,7 @@
 
 namespace WebCore {
 
-ServiceWorkerJob::ServiceWorkerJob(ServiceWorkerJobClient& client, Ref<DeferredPromise>&& promise, ServiceWorkerJobData&& jobData)
+ServiceWorkerJob::ServiceWorkerJob(ServiceWorkerJobClient& client, RefPtr<DeferredPromise>&& promise, ServiceWorkerJobData&& jobData)
     : m_client(client)
     , m_jobData(WTFMove(jobData))
     , m_promise(WTFMove(promise))
index ea7a7a3..62568da 100644 (file)
@@ -48,7 +48,7 @@ struct ServiceWorkerRegistrationData;
 
 class ServiceWorkerJob : public ThreadSafeRefCounted<ServiceWorkerJob>, public WorkerScriptLoaderClient {
 public:
-    static Ref<ServiceWorkerJob> create(ServiceWorkerJobClient& client, Ref<DeferredPromise>&& promise, ServiceWorkerJobData&& jobData)
+    static Ref<ServiceWorkerJob> create(ServiceWorkerJobClient& client, RefPtr<DeferredPromise>&& promise, ServiceWorkerJobData&& jobData)
     {
         return adoptRef(*new ServiceWorkerJob(client, WTFMove(promise), WTFMove(jobData)));
     }
@@ -63,15 +63,15 @@ public:
     using Identifier = ServiceWorkerJobIdentifier;
     Identifier identifier() const { return m_jobData.identifier().jobIdentifier; }
 
-    ServiceWorkerJobData data() const { return m_jobData; }
-    DeferredPromise& promise() { return m_promise.get(); }
+    const ServiceWorkerJobData& data() const { return m_jobData; }
+    DeferredPromise* promise() { return m_promise.get(); }
 
     void fetchScriptWithContext(ScriptExecutionContext&, FetchOptions::Cache);
 
     const DocumentOrWorkerIdentifier& contextIdentifier() { return m_contextIdentifier; }
 
 private:
-    ServiceWorkerJob(ServiceWorkerJobClient&, Ref<DeferredPromise>&&, ServiceWorkerJobData&&);
+    ServiceWorkerJob(ServiceWorkerJobClient&, RefPtr<DeferredPromise>&&, ServiceWorkerJobData&&);
 
     // WorkerScriptLoaderClient
     void didReceiveResponse(unsigned long identifier, const ResourceResponse&) final;
@@ -81,7 +81,7 @@ private:
 
     Ref<ServiceWorkerJobClient> m_client;
     ServiceWorkerJobData m_jobData;
-    Ref<DeferredPromise> m_promise;
+    RefPtr<DeferredPromise> m_promise;
 
     bool m_completed { false };
 
index 8666e86..093ea75 100644 (file)
@@ -144,6 +144,19 @@ void ServiceWorkerRegistration::update(Ref<DeferredPromise>&& promise)
     m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, WTFMove(promise));
 }
 
+void ServiceWorkerRegistration::softUpdate()
+{
+    if (m_isStopped)
+        return;
+
+    auto* newestWorker = getNewestWorker();
+    if (!newestWorker)
+        return;
+
+    // FIXME: Support worker types.
+    m_container->updateRegistration(m_registrationData.scopeURL, newestWorker->scriptURL(), WorkerType::Classic, nullptr);
+}
+
 void ServiceWorkerRegistration::unregister(Ref<DeferredPromise>&& promise)
 {
     if (m_isStopped) {
index 5f2841f..4372887 100644 (file)
@@ -61,9 +61,13 @@ public:
     WallTime lastUpdateTime() const;
     void setLastUpdateTime(WallTime);
 
+    bool needsUpdate() const { return lastUpdateTime() && (WallTime::now() - lastUpdateTime()) > 86400_s; }
+
     void update(Ref<DeferredPromise>&&);
     void unregister(Ref<DeferredPromise>&&);
 
+    void softUpdate();
+
     using RefCounted::ref;
     using RefCounted::deref;
     
index 5b70595..41032db 100644 (file)
@@ -97,25 +97,22 @@ void SWContextManager::fireActivateEvent(ServiceWorkerIdentifier identifier)
 
 void SWContextManager::terminateWorker(ServiceWorkerIdentifier identifier, Function<void()>&& completionHandler)
 {
-    auto* serviceWorker = m_workerMap.get(identifier);
+    auto serviceWorker = m_workerMap.take(identifier);
     if (!serviceWorker)
         return;
 
     serviceWorker->setTerminatingOrTerminated(true);
 
-    serviceWorker->thread().stop([this, identifier, completionHandler = WTFMove(completionHandler)] {
+    serviceWorker->thread().stop([this, identifier, serviceWorker = WTFMove(serviceWorker), completionHandler = WTFMove(completionHandler)]() mutable {
         if (auto* connection = SWContextManager::singleton().connection())
             connection->workerTerminated(identifier);
 
         if (completionHandler)
             completionHandler();
         
-        auto worker = m_workerMap.take(identifier);
-        ASSERT(worker);
-        
         // Spin the runloop before releasing the worker thread proxy, as there would otherwise be
         // a race towards its destruction.
-        callOnMainThread([worker = WTFMove(worker)] { });
+        callOnMainThread([serviceWorker = WTFMove(serviceWorker)] { });
     });
 }
 
@@ -125,13 +122,16 @@ void SWContextManager::forEachServiceWorkerThread(const WTF::Function<void(Servi
         apply(*workerThread);
 }
 
-void SWContextManager::postTaskToServiceWorker(ServiceWorkerIdentifier identifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&& task)
+bool SWContextManager::postTaskToServiceWorker(ServiceWorkerIdentifier identifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&& task)
 {
-    if (auto* serviceWorker = m_workerMap.get(identifier)) {
-        serviceWorker->thread().runLoop().postTask([task = WTFMove(task)] (auto& context) {
-            task(downcast<ServiceWorkerGlobalScope>(context));
-        });
-    }
+    auto* serviceWorker = m_workerMap.get(identifier);
+    if (!serviceWorker)
+        return false;
+
+    serviceWorker->thread().runLoop().postTask([task = WTFMove(task)] (auto& context) {
+        task(downcast<ServiceWorkerGlobalScope>(context));
+    });
+    return true;
 }
 
 } // namespace WebCore
index b252909..f403d4d 100644 (file)
@@ -74,7 +74,7 @@ public:
 
     void forEachServiceWorkerThread(const WTF::Function<void(ServiceWorkerThreadProxy&)>&);
 
-    WEBCORE_EXPORT void postTaskToServiceWorker(ServiceWorkerIdentifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&&);
+    WEBCORE_EXPORT bool postTaskToServiceWorker(ServiceWorkerIdentifier, WTF::Function<void(ServiceWorkerGlobalScope&)>&&);
 
 private:
     SWContextManager() = default;
index 937ed4d..1646805 100644 (file)
@@ -93,10 +93,11 @@ static void processResponse(Ref<Client>&& client, FetchResponse* response)
 
 void dispatchFetchEvent(Ref<Client>&& client, ServiceWorkerGlobalScope& globalScope, std::optional<ServiceWorkerClientIdentifier> clientId, ResourceRequest&& request, String&& referrer, FetchOptions&& options)
 {
-    ASSERT(globalScope.isServiceWorkerGlobalScope());
-
     auto requestHeaders = FetchHeaders::create(FetchHeaders::Guard::Immutable, HTTPHeaderMap { request.httpHeaderFields() });
 
+    bool isNavigation = options.mode == FetchOptions::Mode::Navigate;
+    bool isNonSubresourceRequest = WebCore::isNonSubresourceRequest(options.destination);
+
     auto* formData = request.httpBody();
     std::optional<FetchBody> body;
     if (formData && !formData->isEmpty()) {
@@ -111,7 +112,7 @@ void dispatchFetchEvent(Ref<Client>&& client, ServiceWorkerGlobalScope& globalSc
 
     FetchEvent::Init init;
     init.request = WTFMove(fetchRequest);
-    if (options.mode == FetchOptions::Mode::Navigate) {
+    if (isNavigation) {
         // FIXME: Set reservedClientId.
         if (clientId)
             init.targetClientId = clientId->toString();
@@ -132,10 +133,13 @@ void dispatchFetchEvent(Ref<Client>&& client, ServiceWorkerGlobalScope& globalSc
             return;
         }
         client->didNotHandle();
-        // FIXME: Handle soft update.
     }
 
     globalScope.updateExtendedEventsSet(event.ptr());
+
+    auto& registration = globalScope.registration();
+    if (isNonSubresourceRequest || registration.needsUpdate())
+        registration.softUpdate();
 }
 
 } // namespace ServiceWorkerFetch
index ea98ff8..7c06c83 100644 (file)
@@ -39,6 +39,7 @@ class ResourceRequest;
 class ResourceResponse;
 struct ServiceWorkerClientIdentifier;
 class ServiceWorkerGlobalScope;
+class ServiceWorkerGlobalScope;
 class SharedBuffer;
 
 namespace ServiceWorkerFetch {
index 3e2cc5b..68addee 100644 (file)
@@ -302,7 +302,7 @@ void SWServer::startScriptFetch(const ServiceWorkerJobData& jobData, FetchOption
     if (!connection)
         return;
 
-    connection->startScriptFetchInClient(jobData.identifier(), cachePolicy);
+    connection->startScriptFetchInClient(jobData.identifier(), jobData.registrationKey(), cachePolicy);
 }
 
 void SWServer::scriptFetchFinished(Connection& connection, const ServiceWorkerFetchResult& result)
index f76cb94..66d6e5e 100644 (file)
@@ -106,7 +106,7 @@ public:
         virtual void rejectJobInClient(const ServiceWorkerJobDataIdentifier&, const ExceptionData&) = 0;
         virtual void resolveRegistrationJobInClient(const ServiceWorkerJobDataIdentifier&, const ServiceWorkerRegistrationData&, ShouldNotifyWhenResolved) = 0;
         virtual void resolveUnregistrationJobInClient(const ServiceWorkerJobDataIdentifier&, const ServiceWorkerRegistrationKey&, bool registrationResult) = 0;
-        virtual void startScriptFetchInClient(const ServiceWorkerJobDataIdentifier&, FetchOptions::Cache) = 0;
+        virtual void startScriptFetchInClient(const ServiceWorkerJobDataIdentifier&, const ServiceWorkerRegistrationKey&, FetchOptions::Cache) = 0;
 
         struct RegistrationReadyRequest {
             SecurityOriginData topOrigin;
index 65683cb..9cd0db9 100644 (file)
@@ -1,3 +1,16 @@
+2017-12-22  Chris Dumez  <cdumez@apple.com>
+
+        [Service Workers] Implement "Soft Update" algorithm
+        https://bugs.webkit.org/show_bug.cgi?id=180702
+        <rdar://problem/36163461>
+
+        Reviewed by Youenn Fablet.
+
+        * StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::startScriptFetchInClient):
+        * StorageProcess/ServiceWorker/WebSWServerConnection.h:
+        * WebProcess/Storage/WebSWClientConnection.messages.in:
+
 2017-12-22  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] Duplicated symbols in libjavascriptcoregtk and libwebkit2gtk can cause crashes in production builds
index f3a2224..d4d9137 100644 (file)
@@ -91,9 +91,9 @@ void WebSWServerConnection::resolveUnregistrationJobInClient(const ServiceWorker
     send(Messages::WebSWClientConnection::UnregistrationJobResolvedInServer(jobDataIdentifier, unregistrationResult));
 }
 
-void WebSWServerConnection::startScriptFetchInClient(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, FetchOptions::Cache cachePolicy)
+void WebSWServerConnection::startScriptFetchInClient(const ServiceWorkerJobDataIdentifier& jobDataIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy)
 {
-    send(Messages::WebSWClientConnection::StartScriptFetchForServer(jobDataIdentifier, cachePolicy));
+    send(Messages::WebSWClientConnection::StartScriptFetchForServer(jobDataIdentifier, registrationKey, cachePolicy));
 }
 
 void WebSWServerConnection::updateRegistrationStateInClient(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const std::optional<ServiceWorkerData>& serviceWorkerData)
index 2092a77..3b93e38 100644 (file)
@@ -74,7 +74,7 @@ private:
     void rejectJobInClient(const WebCore::ServiceWorkerJobDataIdentifier&, const WebCore::ExceptionData&) final;
     void resolveRegistrationJobInClient(const WebCore::ServiceWorkerJobDataIdentifier&, const WebCore::ServiceWorkerRegistrationData&, WebCore::ShouldNotifyWhenResolved) final;
     void resolveUnregistrationJobInClient(const WebCore::ServiceWorkerJobDataIdentifier&, const WebCore::ServiceWorkerRegistrationKey&, bool unregistrationResult) final;
-    void startScriptFetchInClient(const WebCore::ServiceWorkerJobDataIdentifier&, WebCore::FetchOptions::Cache) final;
+    void startScriptFetchInClient(const WebCore::ServiceWorkerJobDataIdentifier&, const WebCore::ServiceWorkerRegistrationKey&, WebCore::FetchOptions::Cache) final;
     void updateRegistrationStateInClient(WebCore::ServiceWorkerRegistrationIdentifier, WebCore::ServiceWorkerRegistrationState, const std::optional<WebCore::ServiceWorkerData>&) final;
     void updateWorkerStateInClient(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerState) final;
     void fireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier) final;
index 8a6006b..dba647e 100644 (file)
@@ -27,7 +27,7 @@ messages -> WebSWClientConnection {
     JobRejectedInServer(struct WebCore::ServiceWorkerJobDataIdentifier jobDataIdentifier, struct WebCore::ExceptionData exception)
     RegistrationJobResolvedInServer(struct WebCore::ServiceWorkerJobDataIdentifier jobDataIdentifier, struct WebCore::ServiceWorkerRegistrationData registration, enum WebCore::ShouldNotifyWhenResolved shouldNotifyWhenResolved)
     UnregistrationJobResolvedInServer(struct WebCore::ServiceWorkerJobDataIdentifier jobDataIdentifier, bool unregistrationResult)
-    StartScriptFetchForServer(struct WebCore::ServiceWorkerJobDataIdentifier jobDataIdentifier, WebCore::FetchOptions::Cache cachePolicy)
+    StartScriptFetchForServer(struct WebCore::ServiceWorkerJobDataIdentifier jobDataIdentifier, WebCore::ServiceWorkerRegistrationKey registrationKey, WebCore::FetchOptions::Cache cachePolicy)
     UpdateRegistrationState(WebCore::ServiceWorkerRegistrationIdentifier identifier, enum WebCore::ServiceWorkerRegistrationState state, std::optional<WebCore::ServiceWorkerData> serviceWorkerIdentifier)
     UpdateWorkerState(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, enum WebCore::ServiceWorkerState state)
     FireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier identifier)