Skip to content
Permalink
Browse files
Add an efficient data structure for WebCore to query if there is a Se…
…rvice Worker registered for a given origin

https://bugs.webkit.org/show_bug.cgi?id=177876
<rdar://problem/34813129>

Reviewed by Ryosuke Niwa.

Source/WebCore:

No new tests, updatdd existing test.

* dom/Document.h:
* testing/Internals.cpp:
(WebCore::Internals::hasServiceWorkerRegisteredForOrigin):
* testing/Internals.h:
* testing/Internals.idl:
* workers/service/ServiceWorkerProvider.h:
* workers/service/context/SWContextManager.cpp:
(WebCore::SWContextManager::startServiceWorkerContext):
* workers/service/server/SWClientConnection.h:
* workers/service/server/SWServer.cpp:
(WebCore::SWServer::Connection::scriptContextStarted):
(WebCore::SWServer::scriptContextStarted):
* workers/service/server/SWServer.h:
* workers/service/server/SWServerRegistration.cpp:
(WebCore::SWServerRegistration::scriptContextFailedToStart):
(WebCore::SWServerRegistration::scriptContextStarted):
* workers/service/server/SWServerRegistration.h:

Source/WebKit:

Introduce a Service Worker origin store which gets populated / updated on the StorageProcess side
and queried on the WebContent process side via the WebSWOriginTable so that the WebProcess can
efficiently check if there is a ServiceWorker registered for a given origin without actually doing
an IPC to the StorageProcess.

For efficiency, the hash table is backed by SharedMemory so we only pass shared memory handles
between the StorageProcess and the WebProcesses.

We currently add entries to the WebSWOriginStore whenever a service worker registration succeeds
on the StorageProcess side. We also clear this store whenever the API to clear service worker
registrations is called. Code to query the WebSWOriginTable from the WebContent process side is
there but currently only used by Internals for testing. We will later leverage this code when
integrating with Fetch API.

* CMakeLists.txt:
* Shared/SharedStringHashStore.h:
(WebKit::SharedStringHashStore::Client::didUpdateSharedStringHashes):
* Shared/SharedStringHashTable.cpp:
(WebKit::SharedStringHashTable::clear):
* StorageProcess/ServiceWorker/WebSWOriginStore.cpp: Copied from Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.cpp.
(WebKit::WebSWOriginStore::WebSWOriginStore):
(WebKit::WebSWOriginStore::add):
(WebKit::WebSWOriginStore::remove):
(WebKit::WebSWOriginStore::clear):
(WebKit::WebSWOriginStore::registerSWServerConnection):
(WebKit::WebSWOriginStore::unregisterSWServerConnection):
(WebKit::WebSWOriginStore::sendStoreHandle):
(WebKit::WebSWOriginStore::didInvalidateSharedMemory):
* StorageProcess/ServiceWorker/WebSWOriginStore.h: Copied from Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.h.
* StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::WebSWServerConnection):
(WebKit::WebSWServerConnection::resolveJobInClient):
* StorageProcess/ServiceWorker/WebSWServerConnection.h:
(WebKit::WebSWServerConnection::sessionID const):
* StorageProcess/StorageProcess.cpp:
(WebKit::StorageProcess::deleteWebsiteData):
(WebKit::StorageProcess::deleteWebsiteDataForOrigins):
(WebKit::StorageProcess::ensureSWOriginStoreForSession):
(WebKit::StorageProcess::swOriginStoreForSession const):
(WebKit::StorageProcess::serviceWorkerContextStarted):
(WebKit::StorageProcess::registerSWServerConnection):
(WebKit::StorageProcess::unregisterSWServerConnection):
* StorageProcess/StorageProcess.h:
* StorageProcess/StorageProcess.messages.in:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/Storage/WebSWClientConnection.cpp:
(WebKit::WebSWClientConnection::WebSWClientConnection):
(WebKit::WebSWClientConnection::hasServiceWorkerRegisteredForOrigin const):
(WebKit::WebSWClientConnection::setSWOriginTableSharedMemory):
* WebProcess/Storage/WebSWClientConnection.h:
* WebProcess/Storage/WebSWClientConnection.messages.in:
* WebProcess/Storage/WebSWOriginTable.cpp: Copied from Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.h.
(WebKit::WebSWOriginTable::contains const):
(WebKit::WebSWOriginTable::setSharedMemory):
* WebProcess/Storage/WebSWOriginTable.h: Copied from Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.h.
* WebProcess/Storage/WebServiceWorkerProvider.cpp:
(WebKit::WebServiceWorkerProvider::serviceWorkerConnectionForSession):
* WebProcess/Storage/WebServiceWorkerProvider.h:
* WebProcess/Storage/WebToStorageProcessConnection.cpp:
(WebKit::WebToStorageProcessConnection::serviceWorkerConnectionForSession):
* WebProcess/Storage/WebToStorageProcessConnection.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::startServiceWorkerContext):

Tools:

Clear service worker registrations between test runs to avoid flakiness.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetStateToConsistentValues):

LayoutTests:

Add layout test coverage. Also rebaseline a few tests now that registration succeeds.

* http/tests/workers/service/basic-register-exceptions-expected.txt:
* http/tests/workers/service/basic-register-expected.txt:
* http/tests/workers/service/registration-task-queue-scheduling-1-expected.txt:
* http/tests/workers/service/resources/basic-register.js:
* http/tests/workers/service/resources/registration-task-queue-scheduling-1.js:

Canonical link: https://commits.webkit.org/194662@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223608 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
cdumez committed Oct 18, 2017
1 parent a116a06 commit 4517c2231d8a62cdf61cf27dab743cb9639dfac6
Showing 42 changed files with 621 additions and 57 deletions.
@@ -1,3 +1,19 @@
2017-10-18 Chris Dumez <cdumez@apple.com>

Add an efficient data structure for WebCore to query if there is a Service Worker registered for a given origin
https://bugs.webkit.org/show_bug.cgi?id=177876
<rdar://problem/34813129>

Reviewed by Ryosuke Niwa.

Add layout test coverage. Also rebaseline a few tests now that registration succeeds.

* http/tests/workers/service/basic-register-exceptions-expected.txt:
* http/tests/workers/service/basic-register-expected.txt:
* http/tests/workers/service/registration-task-queue-scheduling-1-expected.txt:
* http/tests/workers/service/resources/basic-register.js:
* http/tests/workers/service/resources/registration-task-queue-scheduling-1.js:

2017-10-18 Antti Koivisto <antti@apple.com>

Resolve ::before and ::after pseudo elements during style resolution
@@ -4,7 +4,7 @@ CONSOLE MESSAGE: line 40: Registration failed with error: TypeError: serviceWork
CONSOLE MESSAGE: line 50: Registration failed with error: TypeError: serviceWorker.register() must be called with a script URL whose path does not contain '%2f' or '%5c'
CONSOLE MESSAGE: line 60: Registration failed with error: TypeError: Scope URL provided to serviceWorker.register() must be either HTTP or HTTPS
CONSOLE MESSAGE: line 70: Registration failed with error: TypeError: Scope URL provided to serviceWorker.register() cannot have a path that contains '%2f' or '%5c'
CONSOLE MESSAGE: line 10: Registration failed with error: UnknownError: Worker script successfully started, but it has no way to communicate yet
CONSOLE MESSAGE: line 8: Registered! (unexpectedly)
CONSOLE MESSAGE: line 80: Registration failed with error: SecurityError: Script origin does not match the registering client's origin
CONSOLE MESSAGE: line 91: Registration failed with error: SecurityError: Scope origin does not match the registering client's origin

@@ -1,3 +1,7 @@
CONSOLE MESSAGE: line 10: Registration failed with error: UnknownError: Worker script successfully started, but it has no way to communicate yet
CONSOLE MESSAGE: line 21: Registration failed with error: UnknownError: Worker script successfully started, but it has no way to communicate yet
PASS: No service worker is initially registered for this origin
PASS: No service worker is initially registered for this origin in private session
Registered!
PASS: A service worker is now registered for this origin
PASS: No service worker is registered for this origin in private session
Registered!

@@ -1,2 +1,2 @@
ALERT: Unexpected error received from server: UnknownError: Worker script successfully started, but it has no way to communicate yet
CONSOLE MESSAGE: line 48: Original window resolved successfully (unexpected)

@@ -3,25 +3,66 @@ function done()
finishSWTest();
}

function log(msg)
{
let console = document.getElementById("console");
if (!console) {
console = document.createElement("div");
console.id = "console";
document.body.appendChild(console);
}
let span = document.createElement("span");
span.innerHTML = msg + "<br>";
console.appendChild(span);
}

if (!internals.hasServiceWorkerRegisteredForOrigin(self.origin))
log("PASS: No service worker is initially registered for this origin");
else
log("FAIL: A service worker is initially registered for this origin");

testRunner.setPrivateBrowsingEnabled(true);

if (!internals.hasServiceWorkerRegisteredForOrigin(self.origin))
log("PASS: No service worker is initially registered for this origin in private session");
else
log("FAIL: A service worker is initially registered for this origin in private session");

testRunner.setPrivateBrowsingEnabled(false);

navigator.serviceWorker.register("resources/empty-worker.js", { })
.then(function(r) {
console.log("Registered!");
log("Registered!");

if (internals.hasServiceWorkerRegisteredForOrigin(self.origin))
log("PASS: A service worker is now registered for this origin");
else
log("FAIL: No service worker is registered for this origin");

testRunner.setPrivateBrowsingEnabled(true);

if (!internals.hasServiceWorkerRegisteredForOrigin(self.origin))
log("PASS: No service worker is registered for this origin in private session");
else
log("FAIL: A service worker is registered for this origin in private session");

testRunner.setPrivateBrowsingEnabled(false);
}, function(e) {
console.log("Registration failed with error: " + e);
log("Registration failed with error: " + e);
})
.catch(function(e) {
console.log("Exception registering: " + e);
log("Exception registering: " + e);
});

navigator.serviceWorker.register("resources/empty-worker-doesnt-exist.js", { })
.then(function(r) {
console.log("Registered!");
log("Registered!");
done();
}, function(e) {
console.log("Registration failed with error: " + e);
log("Registration failed with error: " + e);
done();
})
.catch(function(e) {
console.log("Exception registering: " + e);
log("Exception registering: " + e);
done();
});
@@ -46,7 +46,7 @@ for (var i = 0; i < 1000; ++i) {
navigator.serviceWorker.register("resources/empty-worker.js", { })
.then(function(r) {
console.log("Original window resolved successfully (unexpected)")
done();
finishSWTest();
}, function(e) {
if (e+"" != "UnknownError: Script URL http://127.0.0.1:8000/workers/service/resources/empty-worker.js fetched with 41 characters, but we're not using the result yet") {
alert("Unexpected error received from server: " + e);
@@ -1,3 +1,31 @@
2017-10-18 Chris Dumez <cdumez@apple.com>

Add an efficient data structure for WebCore to query if there is a Service Worker registered for a given origin
https://bugs.webkit.org/show_bug.cgi?id=177876
<rdar://problem/34813129>

Reviewed by Ryosuke Niwa.

No new tests, updatdd existing test.

* dom/Document.h:
* testing/Internals.cpp:
(WebCore::Internals::hasServiceWorkerRegisteredForOrigin):
* testing/Internals.h:
* testing/Internals.idl:
* workers/service/ServiceWorkerProvider.h:
* workers/service/context/SWContextManager.cpp:
(WebCore::SWContextManager::startServiceWorkerContext):
* workers/service/server/SWClientConnection.h:
* workers/service/server/SWServer.cpp:
(WebCore::SWServer::Connection::scriptContextStarted):
(WebCore::SWServer::scriptContextStarted):
* workers/service/server/SWServer.h:
* workers/service/server/SWServerRegistration.cpp:
(WebCore::SWServerRegistration::scriptContextFailedToStart):
(WebCore::SWServerRegistration::scriptContextStarted):
* workers/service/server/SWServerRegistration.h:

2017-10-18 Sam Weinig <sam@webkit.org>

[Settings] Replace macros in Settings.h/cpp with generated code
@@ -643,7 +643,7 @@ class Document

WEBCORE_EXPORT URL completeURL(const String&) const final;
URL completeURL(const String&, const URL& baseURLOverride) const;
PAL::SessionID sessionID() const final;
WEBCORE_EXPORT PAL::SessionID sessionID() const final;

String userAgent(const URL&) const final;

@@ -123,11 +123,14 @@
#include "SVGDocumentExtensions.h"
#include "SVGPathStringBuilder.h"
#include "SVGSVGElement.h"
#include "SWClientConnection.h"
#include "SchemeRegistry.h"
#include "ScriptedAnimationController.h"
#include "ScrollingCoordinator.h"
#include "ScrollingMomentumCalculator.h"
#include "SecurityOrigin.h"
#include "SerializedScriptValue.h"
#include "ServiceWorkerProvider.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "SourceBuffer.h"
@@ -4187,6 +4190,19 @@ void Internals::setConsoleMessageListener(RefPtr<StringCallback>&& listener)
contextDocument()->setConsoleMessageListener(WTFMove(listener));
}

bool Internals::hasServiceWorkerRegisteredForOrigin(const String& origin)
{
#if ENABLE(SERVICE_WORKER)
if (!contextDocument())
return false;

return ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(contextDocument()->sessionID()).hasServiceWorkerRegisteredForOrigin(SecurityOrigin::createFromString(origin));
#else
UNUSED_PARAM(origin);
return false;
#endif
}

void Internals::setResponseSizeWithPadding(FetchResponse& response, uint64_t size)
{
response.setBodySizeWithPadding(size);
@@ -614,6 +614,8 @@ class Internals final : public RefCounted<Internals>, private ContextDestructio
Ref<ExtendableEvent> createTrustedExtendableEvent();
#endif

bool hasServiceWorkerRegisteredForOrigin(const String&);

private:
explicit Internals(Document&);
Document* contextDocument() const;
@@ -557,4 +557,6 @@ enum EventThrottlingBehavior {
[Conditional=SERVICE_WORKER] Promise<Response> waitForFetchEventToFinish(FetchEvent event);
[Conditional=SERVICE_WORKER] Promise<void> waitForExtendableEventToFinish(ExtendableEvent event);
[Conditional=SERVICE_WORKER] ExtendableEvent createTrustedExtendableEvent();

boolean hasServiceWorkerRegisteredForOrigin(DOMString origin);
};
@@ -43,7 +43,7 @@ class WEBCORE_EXPORT ServiceWorkerProvider {
WEBCORE_EXPORT static ServiceWorkerProvider& singleton();
WEBCORE_EXPORT static void setSharedProvider(ServiceWorkerProvider&);

virtual SWClientConnection& serviceWorkerConnectionForSession(const PAL::SessionID&) = 0;
virtual SWClientConnection& serviceWorkerConnectionForSession(PAL::SessionID) = 0;
};

} // namespace WebCore
@@ -51,17 +51,15 @@ ExceptionOr<uint64_t> SWContextManager::startServiceWorkerContext(uint64_t serve
// FIXME: Provide a sensical session ID

auto thread = ServiceWorkerThread::create(serverConnectionIdentifier, data, SessionID::defaultSessionID());
auto result = m_workerThreadMap.add(thread->identifier(), WTFMove(thread));
auto threadIdentifier = thread->identifier();
auto result = m_workerThreadMap.add(threadIdentifier, WTFMove(thread));
ASSERT(result.isNewEntry);

result.iterator->value->start();

LOG(ServiceWorker, "Context process PID: %i started worker thread %s\n", getpid(), data.workerID.utf8().data());

// FIXME: For testing purposes we need to signal a failure with an exception payload.
// Later with more APIs and infrastructure filled in, testing will be much easier.

return Exception { UnknownError, "Worker script successfully started, but it has no way to communicate yet" };
return threadIdentifier;
}

} // namespace WebCore
@@ -34,6 +34,7 @@
namespace WebCore {

class ResourceError;
class SecurityOrigin;
class SharedBuffer;
struct ExceptionData;
struct ServiceWorkerFetchResult;
@@ -49,6 +50,7 @@ class SWClientConnection : public ThreadSafeRefCounted<SWClientConnection> {
void failedFetchingScript(ServiceWorkerJob&, const ResourceError&);

virtual uint64_t identifier() const = 0;
virtual bool hasServiceWorkerRegisteredForOrigin(const SecurityOrigin&) const = 0;

protected:
WEBCORE_EXPORT void jobRejectedInServer(uint64_t jobIdentifier, const ExceptionData&);
@@ -86,6 +86,11 @@ void SWServer::Connection::scriptContextFailedToStart(const ServiceWorkerRegistr
m_server.scriptContextFailedToStart(*this, registrationKey, workerID, message);
}

void SWServer::Connection::scriptContextStarted(const ServiceWorkerRegistrationKey& registrationKey, uint64_t identifier, const String& workerID)
{
m_server.scriptContextStarted(*this, registrationKey, identifier, workerID);
}

SWServer::SWServer()
{
m_taskThread = Thread::create(ASCIILiteral("ServiceWorker Task Thread"), [this] {
@@ -157,6 +162,14 @@ void SWServer::scriptContextFailedToStart(Connection& connection, const ServiceW
registration->scriptContextFailedToStart(connection, workerID, message);
}

void SWServer::scriptContextStarted(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, uint64_t identifier, const String& workerID)
{
ASSERT(m_connections.contains(connection.identifier()));

if (auto* registration = m_registrations.get(registrationKey))
registration->scriptContextStarted(connection, identifier, workerID);
}

Ref<SWServerWorker> SWServer::createWorker(Connection& connection, const ServiceWorkerRegistrationKey& registrationKey, const URL& url, const String& script, WorkerType type)
{
String workerID = createCanonicalUUIDString();
@@ -55,6 +55,7 @@ class SWServer {
WEBCORE_EXPORT virtual ~Connection();

WEBCORE_EXPORT void scriptContextFailedToStart(const ServiceWorkerRegistrationKey&, const String& workerID, const String& message);
WEBCORE_EXPORT void scriptContextStarted(const ServiceWorkerRegistrationKey&, uint64_t identifier, const String& workerID);

protected:
WEBCORE_EXPORT Connection(SWServer&, uint64_t identifier);
@@ -97,6 +98,7 @@ class SWServer {

void scriptFetchFinished(Connection&, const ServiceWorkerFetchResult&);
void scriptContextFailedToStart(Connection&, const ServiceWorkerRegistrationKey&, const String& workerID, const String& message);
void scriptContextStarted(Connection&, const ServiceWorkerRegistrationKey&, uint64_t identifier, const String& workerID);

HashMap<uint64_t, Connection*> m_connections;
HashMap<ServiceWorkerRegistrationKey, std::unique_ptr<SWServerRegistration>> m_registrations;
@@ -88,12 +88,18 @@ void SWServerRegistration::scriptFetchFinished(SWServer::Connection& connection,

void SWServerRegistration::scriptContextFailedToStart(SWServer::Connection&, const String& workerID, const String& message)
{
ASSERT(m_currentJob);
UNUSED_PARAM(workerID);

rejectCurrentJob(ExceptionData { UnknownError, message });
}

void SWServerRegistration::scriptContextStarted(SWServer::Connection&, uint64_t identifier, const String& workerID)
{
UNUSED_PARAM(workerID);

resolveCurrentJob(ServiceWorkerRegistrationData { m_registrationKey, identifier });
}

void SWServerRegistration::startNextJob()
{
ASSERT(isMainThread());
@@ -50,6 +50,7 @@ class SWServerRegistration : public ThreadSafeIdentified<SWServerRegistration> {
void enqueueJob(const ServiceWorkerJobData&);
void scriptFetchFinished(SWServer::Connection&, const ServiceWorkerFetchResult&);
void scriptContextFailedToStart(SWServer::Connection&, const String& workerID, const String& message);
void scriptContextStarted(SWServer::Connection&, uint64_t identifier, const String& workerID);

ServiceWorkerRegistrationData data() const;

@@ -282,6 +282,7 @@ set(WebKit_SOURCES

StorageProcess/IndexedDB/WebIDBConnectionToClient.cpp

StorageProcess/ServiceWorker/WebSWOriginStore.cpp
StorageProcess/ServiceWorker/WebSWServerConnection.cpp

UIProcess/BackgroundProcessResponsivenessTimer.cpp
@@ -539,6 +540,7 @@ set(WebKit_SOURCES
WebProcess/Plugins/Netscape/NetscapePluginStream.cpp

WebProcess/Storage/WebSWClientConnection.cpp
WebProcess/Storage/WebSWOriginTable.cpp
WebProcess/Storage/WebServiceWorkerProvider.cpp
WebProcess/Storage/WebToStorageProcessConnection.cpp

0 comments on commit 4517c22

Please sign in to comment.