Add ability to API test Service Workers via a custom protocol.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Dec 2017 01:16:57 +0000 (01:16 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 Dec 2017 01:16:57 +0000 (01:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180911

Reviewed by Chris Dumez.

Source/WebCore:

Covered by API test ServiceWorkers.Basic

This adds a set of "Service Workers can handle this" schemes to the scheme registry
and uses it for SW decisions instead of a handful of previous techniques.

* bindings/scripts/CodeGeneratorJS.pm:
(NeedsRuntimeCheck):
(GenerateRuntimeEnableConditionalString):
* bindings/scripts/IDLAttributes.json:

* dom/ScriptExecutionContext.cpp:
(WebCore::ScriptExecutionContext::hasServiceWorkerScheme):
* dom/ScriptExecutionContext.h:

* page/NavigatorServiceWorker.idl:

* platform/SchemeRegistry.cpp:
(WebCore::serviceWorkerSchemesLock):
(WebCore::serviceWorkerSchemes):
(WebCore::SchemeRegistry::registerURLSchemeServiceWorkersCanHandle):
(WebCore::SchemeRegistry::canServiceWorkersHandleURLScheme):
(WebCore::SchemeRegistry::isServiceWorkerContainerCustomScheme):
* platform/SchemeRegistry.h:

* workers/service/ServiceWorkerContainer.cpp:
(WebCore::ServiceWorkerContainer::addRegistration):

* workers/service/server/SWServerJobQueue.cpp:
(WebCore::SWServerJobQueue::runRegisterJob):

Source/WebKit:

This adds a set of "Service Workers can handle this" schemes to the scheme registry
and most of these WebKit changes are to support getting those values out to all processes.

Additionally, WebsiteDataRecords used to be file/http(s)-only. That seems bizarre and definitely
got in the way of testing. So I also added a way to allow any scheme to result in a valid record.

* Shared/ChildProcess.cpp:
(WebKit::ChildProcess::registerURLSchemeServiceWorkersCanHandle const):
* Shared/ChildProcess.h:
* Shared/ChildProcess.messages.in:

* Shared/Storage/StorageProcessCreationParameters.cpp:
(WebKit::StorageProcessCreationParameters::encode const):
(WebKit::StorageProcessCreationParameters::decode):
* Shared/Storage/StorageProcessCreationParameters.h:

* Shared/WebProcessCreationParameters.cpp:
(WebKit::WebProcessCreationParameters::encode const):
(WebKit::WebProcessCreationParameters::decode):
* Shared/WebProcessCreationParameters.h:

* StorageProcess/StorageProcess.cpp:
(WebKit::StorageProcess::initializeWebsiteDataStore):

* UIProcess/API/Cocoa/WKProcessPool.mm:
(-[WKProcessPool _registerURLSchemeServiceWorkersCanHandle:]):
* UIProcess/API/Cocoa/WKProcessPoolPrivate.h:

* UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
(+[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins]):
* UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:

* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::ensureStorageProcessAndWebsiteDataStore):
(WebKit::WebProcessPool::initializeNewWebProcess):
(WebKit::WebProcessPool::registerURLSchemeServiceWorkersCanHandle):
* UIProcess/WebProcessPool.h:

* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins):
(WebKit::WebsiteDataStore::fetchDataAndApply):
* UIProcess/WebsiteData/WebsiteDataStore.h:

* WebProcess/Storage/WebServiceWorkerProvider.cpp:
(WebKit::WebServiceWorkerProvider::handleFetch):

* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::initializeWebProcess):

Tools:

Adds a very basic SW test:
- Verify WebsiteDataStore can wipe all SW registration data.
- Fire up a web page with a service worker
- Verify SW registration data for that page exists in the WebsiteDataStore.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm: Added.

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

32 files changed:
Source/WebCore/ChangeLog
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/IDLAttributes.json
Source/WebCore/dom/ScriptExecutionContext.cpp
Source/WebCore/dom/ScriptExecutionContext.h
Source/WebCore/page/NavigatorServiceWorker.idl
Source/WebCore/platform/SchemeRegistry.cpp
Source/WebCore/platform/SchemeRegistry.h
Source/WebCore/workers/service/ServiceWorkerContainer.cpp
Source/WebCore/workers/service/server/SWServerJobQueue.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/ChildProcess.cpp
Source/WebKit/Shared/ChildProcess.h
Source/WebKit/Shared/ChildProcess.messages.in
Source/WebKit/Shared/Storage/StorageProcessCreationParameters.cpp
Source/WebKit/Shared/Storage/StorageProcessCreationParameters.h
Source/WebKit/Shared/WebProcessCreationParameters.cpp
Source/WebKit/Shared/WebProcessCreationParameters.h
Source/WebKit/StorageProcess/StorageProcess.cpp
Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm
Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h
Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.cpp
Source/WebKit/WebProcess/WebProcess.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm [new file with mode: 0644]

index ec31b07..b8d5998 100644 (file)
@@ -1,3 +1,40 @@
+2017-12-18  Brady Eidson  <beidson@apple.com>
+
+        Add ability to API test Service Workers via a custom protocol.
+        https://bugs.webkit.org/show_bug.cgi?id=180911
+
+        Reviewed by Chris Dumez.
+
+        Covered by API test ServiceWorkers.Basic
+
+        This adds a set of "Service Workers can handle this" schemes to the scheme registry
+        and uses it for SW decisions instead of a handful of previous techniques.
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (NeedsRuntimeCheck):
+        (GenerateRuntimeEnableConditionalString):
+        * bindings/scripts/IDLAttributes.json:
+
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ScriptExecutionContext::hasServiceWorkerScheme):
+        * dom/ScriptExecutionContext.h:
+
+        * page/NavigatorServiceWorker.idl:
+
+        * platform/SchemeRegistry.cpp:
+        (WebCore::serviceWorkerSchemesLock):
+        (WebCore::serviceWorkerSchemes):
+        (WebCore::SchemeRegistry::registerURLSchemeServiceWorkersCanHandle):
+        (WebCore::SchemeRegistry::canServiceWorkersHandleURLScheme):
+        (WebCore::SchemeRegistry::isServiceWorkerContainerCustomScheme):
+        * platform/SchemeRegistry.h:
+
+        * workers/service/ServiceWorkerContainer.cpp:
+        (WebCore::ServiceWorkerContainer::addRegistration):
+
+        * workers/service/server/SWServerJobQueue.cpp:
+        (WebCore::SWServerJobQueue::runRegisterJob):
+
 2017-12-18  Chris Dumez  <cdumez@apple.com>
 
         We should use "error" redirect mode for fetching service worker scripts
index a828956..0cff7a8 100644 (file)
@@ -1696,7 +1696,8 @@ sub NeedsRuntimeCheck
     return $context->extendedAttributes->{EnabledAtRuntime}
         || $context->extendedAttributes->{EnabledForWorld}
         || $context->extendedAttributes->{EnabledBySetting}
-        || $context->extendedAttributes->{SecureContext};
+        || $context->extendedAttributes->{SecureContext}
+        || $context->extendedAttributes->{ContextHasServiceWorkerScheme};
 }
 
 # https://heycam.github.io/webidl/#es-operations
@@ -3649,7 +3650,17 @@ sub GenerateRuntimeEnableConditionalString
     if ($context->extendedAttributes->{SecureContext}) {
         AddToImplIncludes("ScriptExecutionContext.h");
 
-        push(@conjuncts, "jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext()");
+        if ($context->extendedAttributes->{ContextHasServiceWorkerScheme}) {
+            push(@conjuncts, "(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext() || jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->hasServiceWorkerScheme())");
+        } else {
+            push(@conjuncts, "jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext()");
+        }
+    } else {
+        if ($context->extendedAttributes->{ContextHasServiceWorkerScheme}) {
+            AddToImplIncludes("ScriptExecutionContext.h");
+
+            push(@conjuncts, "jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->hasServiceWorkerScheme()");
+        }
     }
 
     if ($context->extendedAttributes->{Exposed}) {
index 7ce48e6..c332b11 100644 (file)
@@ -85,6 +85,9 @@
         "ConstructorMayThrowException": {
             "contextsAllowed": ["interface"]
         },
+        "ContextHasServiceWorkerScheme": {
+            "contextsAllowed": ["attribute"]
+        },
         "Custom": {
             "contextsAllowed": ["attribute", "operation"]
         },
index 023e44b..a8c90be 100644 (file)
@@ -45,6 +45,7 @@
 #include "ResourceRequest.h"
 #include "SWClientConnection.h"
 #include "SWContextManager.h"
+#include "SchemeRegistry.h"
 #include "ScriptState.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerGlobalScope.h"
@@ -538,6 +539,12 @@ JSC::ExecState* ScriptExecutionContext::execState()
 
 #if ENABLE(SERVICE_WORKER)
 
+bool ScriptExecutionContext::hasServiceWorkerScheme()
+{
+    ASSERT(securityOrigin());
+    return SchemeRegistry::isServiceWorkerContainerCustomScheme(securityOrigin()->protocol());
+}
+
 ServiceWorker* ScriptExecutionContext::activeServiceWorker() const
 {
     return m_activeServiceWorker.get();
index 1c15772..af84ca0 100644 (file)
@@ -238,6 +238,7 @@ public:
     JSC::ExecState* execState();
 
 #if ENABLE(SERVICE_WORKER)
+    bool hasServiceWorkerScheme();
     ServiceWorker* activeServiceWorker() const;
     void setActiveServiceWorker(RefPtr<ServiceWorker>&&);
 
index 4acbf26..0c31178 100644 (file)
@@ -28,5 +28,5 @@
     Conditional=SERVICE_WORKER,
     EnabledAtRuntime=ServiceWorker
 ] interface NavigatorServiceWorker {
-    [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
+    [SecureContext, ContextHasServiceWorkerScheme, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
 };
index 9d48421..cb93a78 100644 (file)
@@ -27,6 +27,8 @@
 #include "SchemeRegistry.h"
 
 #include "URLParser.h"
+#include <wtf/Lock.h>
+#include <wtf/Locker.h>
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
 
@@ -252,6 +254,19 @@ static URLSchemesMap& cachePartitioningSchemes()
     return schemes;
 }
 
+static Lock& serviceWorkerSchemesLock()
+{
+    static NeverDestroyed<Lock> lock;
+    return lock;
+}
+
+static URLSchemesMap& serviceWorkerSchemes()
+{
+    ASSERT(serviceWorkerSchemesLock().isHeld());
+    static NeverDestroyed<URLSchemesMap> schemes;
+    return schemes;
+}
+
 static URLSchemesMap& alwaysRevalidatedSchemes()
 {
     static NeverDestroyed<URLSchemesMap> schemes;
@@ -430,6 +445,37 @@ bool SchemeRegistry::shouldPartitionCacheForURLScheme(const String& scheme)
     return !scheme.isNull() && cachePartitioningSchemes().contains(scheme);
 }
 
+void SchemeRegistry::registerURLSchemeServiceWorkersCanHandle(const String& scheme)
+{
+    if (scheme.isNull())
+        return;
+
+    Locker<Lock> locker(serviceWorkerSchemesLock());
+    serviceWorkerSchemes().add(scheme);
+}
+
+bool SchemeRegistry::canServiceWorkersHandleURLScheme(const String& scheme)
+{
+    if (scheme.isNull())
+        return false;
+
+    if (scheme.startsWithIgnoringASCIICase(ASCIILiteral("http"))) {
+        if (scheme.length() == 4)
+            return true;
+        if (scheme.length() == 5 && isASCIIAlphaCaselessEqual(scheme[4], 's'))
+            return true;
+    }
+
+    Locker<Lock> locker(serviceWorkerSchemesLock());
+    return serviceWorkerSchemes().contains(scheme);
+}
+
+bool SchemeRegistry::isServiceWorkerContainerCustomScheme(const String& scheme)
+{
+    Locker<Lock> locker(serviceWorkerSchemesLock());
+    return !scheme.isNull() && serviceWorkerSchemes().contains(scheme);
+}
+
 bool SchemeRegistry::isUserExtensionScheme(const String& scheme)
 {
 #if PLATFORM(MAC)
index 9c0a60a..62be3c4 100644 (file)
@@ -99,6 +99,11 @@ public:
     WEBCORE_EXPORT static void registerURLSchemeAsCachePartitioned(const String& scheme);
     static bool shouldPartitionCacheForURLScheme(const String& scheme);
 
+    // Schemes besides http(s) that service workers are allowed to handle
+    WEBCORE_EXPORT static void registerURLSchemeServiceWorkersCanHandle(const String& scheme);
+    WEBCORE_EXPORT static bool canServiceWorkersHandleURLScheme(const String& scheme);
+    static bool isServiceWorkerContainerCustomScheme(const String& scheme);
+
     static bool isUserExtensionScheme(const String& scheme);
 };
 
index c9e1911..b0fe9f8 100644 (file)
@@ -38,6 +38,7 @@
 #include "Logging.h"
 #include "NavigatorBase.h"
 #include "ResourceError.h"
+#include "SchemeRegistry.h"
 #include "ScriptExecutionContext.h"
 #include "SecurityOrigin.h"
 #include "ServiceWorker.h"
@@ -129,8 +130,7 @@ void ServiceWorkerContainer::addRegistration(const String& relativeScriptURL, co
         return;
     }
 
-    // FIXME: The spec disallows scripts outside of HTTP(S), but we'll likely support app custom URL schemes in WebKit.
-    if (!jobData.scriptURL.protocolIsInHTTPFamily()) {
+    if (!SchemeRegistry::canServiceWorkersHandleURLScheme(jobData.scriptURL.protocol().toStringWithoutCopying())) {
         promise->reject(Exception { TypeError, ASCIILiteral("serviceWorker.register() must be called with a script URL whose protocol is either HTTP or HTTPS") });
         return;
     }
@@ -145,7 +145,7 @@ void ServiceWorkerContainer::addRegistration(const String& relativeScriptURL, co
     if (!scope.isEmpty())
         jobData.scopeURL = context->completeURL(scope);
 
-    if (!jobData.scopeURL.isNull() && !jobData.scopeURL.protocolIsInHTTPFamily()) {
+    if (!jobData.scopeURL.isNull() && !SchemeRegistry::canServiceWorkersHandleURLScheme(jobData.scopeURL.protocol().toStringWithoutCopying())) {
         promise->reject(Exception { TypeError, ASCIILiteral("Scope URL provided to serviceWorker.register() must be either HTTP or HTTPS") });
         return;
     }
index 9edbe0b..8eba7a0 100644 (file)
@@ -31,6 +31,7 @@
 #include "ExceptionData.h"
 #include "SWServer.h"
 #include "SWServerWorker.h"
+#include "SchemeRegistry.h"
 #include "SecurityOrigin.h"
 #include "ServiceWorkerFetchResult.h"
 #include "ServiceWorkerRegistrationData.h"
@@ -239,7 +240,7 @@ void SWServerJobQueue::runRegisterJob(const ServiceWorkerJobData& job)
 {
     ASSERT(job.type == ServiceWorkerJobType::Register);
 
-    if (!shouldTreatAsPotentiallyTrustworthy(job.scriptURL))
+    if (!shouldTreatAsPotentiallyTrustworthy(job.scriptURL) && !SchemeRegistry::isServiceWorkerContainerCustomScheme(job.scriptURL.protocol().toStringWithoutCopying()))
         return rejectCurrentJob(ExceptionData { SecurityError, ASCIILiteral("Script URL is not potentially trustworthy") });
 
     // If the origin of job's script url is not job's referrer's origin, then:
index a692ca9..6e8c144 100644 (file)
@@ -1,5 +1,61 @@
 2017-12-18  Brady Eidson  <beidson@apple.com>
 
+        Add ability to API test Service Workers via a custom protocol.
+        https://bugs.webkit.org/show_bug.cgi?id=180911
+
+        Reviewed by Chris Dumez.
+
+        This adds a set of "Service Workers can handle this" schemes to the scheme registry
+        and most of these WebKit changes are to support getting those values out to all processes.
+        
+        Additionally, WebsiteDataRecords used to be file/http(s)-only. That seems bizarre and definitely
+        got in the way of testing. So I also added a way to allow any scheme to result in a valid record.
+
+        * Shared/ChildProcess.cpp:
+        (WebKit::ChildProcess::registerURLSchemeServiceWorkersCanHandle const):
+        * Shared/ChildProcess.h:
+        * Shared/ChildProcess.messages.in:
+        
+        * Shared/Storage/StorageProcessCreationParameters.cpp:
+        (WebKit::StorageProcessCreationParameters::encode const):
+        (WebKit::StorageProcessCreationParameters::decode):
+        * Shared/Storage/StorageProcessCreationParameters.h:
+        
+        * Shared/WebProcessCreationParameters.cpp:
+        (WebKit::WebProcessCreationParameters::encode const):
+        (WebKit::WebProcessCreationParameters::decode):
+        * Shared/WebProcessCreationParameters.h:
+        
+        * StorageProcess/StorageProcess.cpp:
+        (WebKit::StorageProcess::initializeWebsiteDataStore):
+        
+        * UIProcess/API/Cocoa/WKProcessPool.mm:
+        (-[WKProcessPool _registerURLSchemeServiceWorkersCanHandle:]):
+        * UIProcess/API/Cocoa/WKProcessPoolPrivate.h:
+        
+        * UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
+        (+[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins]):
+        * UIProcess/API/Cocoa/WKWebsiteDataStorePrivate.h:
+        
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::ensureStorageProcessAndWebsiteDataStore):
+        (WebKit::WebProcessPool::initializeNewWebProcess):
+        (WebKit::WebProcessPool::registerURLSchemeServiceWorkersCanHandle):
+        * UIProcess/WebProcessPool.h:
+        
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins):
+        (WebKit::WebsiteDataStore::fetchDataAndApply):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        
+        * WebProcess/Storage/WebServiceWorkerProvider.cpp:
+        (WebKit::WebServiceWorkerProvider::handleFetch):
+        
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::initializeWebProcess):
+
+2017-12-18  Brady Eidson  <beidson@apple.com>
+
         Apps that use both WK1 and WK2 can crash creating a WKWebsiteDataStore.
         https://bugs.webkit.org/show_bug.cgi?id=180953
 
index 2c0228c..5b0b5d7 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "Logging.h"
 #include "SandboxInitializationParameters.h"
+#include <WebCore/SchemeRegistry.h>
 #include <pal/SessionID.h>
 #include <unistd.h>
 
@@ -191,6 +192,11 @@ void ChildProcess::shutDown()
     terminate();
 }
 
+void ChildProcess::registerURLSchemeServiceWorkersCanHandle(const String& urlScheme) const
+{
+    WebCore::SchemeRegistry::registerURLSchemeServiceWorkersCanHandle(urlScheme);
+}
+
 #if !PLATFORM(COCOA)
 void ChildProcess::platformInitialize()
 {
index f1a32ec..ce98d73 100644 (file)
@@ -104,6 +104,8 @@ protected:
 
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
+    void registerURLSchemeServiceWorkersCanHandle(const String&) const;
+
 private:
     // IPC::MessageSender
     IPC::Connection* messageSenderConnection() override;
index 49dfb05..062691f 100644 (file)
@@ -22,4 +22,5 @@
 
 messages -> ChildProcess {
     ShutDown()
+    RegisterURLSchemeServiceWorkersCanHandle(String scheme)
 }
index d23efb0..6e5eabe 100644 (file)
@@ -41,7 +41,7 @@ void StorageProcessCreationParameters::encode(IPC::Encoder& encoder) const
     encoder << indexedDatabaseDirectory << indexedDatabaseDirectoryExtensionHandle;
 #endif
 #if ENABLE(SERVICE_WORKER)
-    encoder << serviceWorkerRegistrationDirectory << serviceWorkerRegistrationDirectoryExtensionHandle;
+    encoder << serviceWorkerRegistrationDirectory << serviceWorkerRegistrationDirectoryExtensionHandle << urlSchemesServiceWorkersCanHandle;
 #endif
 }
 
@@ -68,6 +68,9 @@ bool StorageProcessCreationParameters::decode(IPC::Decoder& decoder, StorageProc
     if (!serviceWorkerRegistrationDirectoryExtensionHandle)
         return false;
     result.serviceWorkerRegistrationDirectoryExtensionHandle = WTFMove(*serviceWorkerRegistrationDirectoryExtensionHandle);
+
+    if (!decoder.decode(result.urlSchemesServiceWorkersCanHandle))
+        return false;
 #endif
 
     return true;
index f60b503..67680df 100644 (file)
@@ -53,6 +53,7 @@ struct StorageProcessCreationParameters {
 #if ENABLE(SERVICE_WORKER)
     String serviceWorkerRegistrationDirectory;
     SandboxExtension::Handle serviceWorkerRegistrationDirectoryExtensionHandle;
+    Vector<String> urlSchemesServiceWorkersCanHandle;
 #endif
 };
 
index 700c14e..813a4bb 100644 (file)
@@ -84,6 +84,7 @@ void WebProcessCreationParameters::encode(IPC::Encoder& encoder) const
     encoder << urlSchemesRegisteredAsCORSEnabled;
     encoder << urlSchemesRegisteredAsAlwaysRevalidated;
     encoder << urlSchemesRegisteredAsCachePartitioned;
+    encoder << urlSchemesServiceWorkersCanHandle;
     encoder.encodeEnum(cacheModel);
     encoder << shouldAlwaysUseComplexTextCodePath;
     encoder << shouldEnableMemoryPressureReliefLogging;
@@ -271,6 +272,8 @@ bool WebProcessCreationParameters::decode(IPC::Decoder& decoder, WebProcessCreat
         return false;
     if (!decoder.decode(parameters.urlSchemesRegisteredAsCachePartitioned))
         return false;
+    if (!decoder.decode(parameters.urlSchemesServiceWorkersCanHandle))
+        return false;
     if (!decoder.decodeEnum(parameters.cacheModel))
         return false;
     if (!decoder.decode(parameters.shouldAlwaysUseComplexTextCodePath))
index bcb40ad..07be472 100644 (file)
@@ -107,6 +107,7 @@ struct WebProcessCreationParameters {
     Vector<String> urlSchemesRegisteredAsCORSEnabled;
     Vector<String> urlSchemesRegisteredAsAlwaysRevalidated;
     Vector<String> urlSchemesRegisteredAsCachePartitioned;
+    Vector<String> urlSchemesServiceWorkersCanHandle;
 
     Vector<String> fontWhitelist;
     Vector<String> languages;
index 172e2ac..7f2bc1b 100644 (file)
@@ -191,6 +191,9 @@ void StorageProcess::initializeWebsiteDataStore(const StorageProcessCreationPara
         SandboxExtension::consumePermanently(parameters.serviceWorkerRegistrationDirectoryExtensionHandle);
         postStorageTask(createCrossThreadTask(*this, &StorageProcess::ensurePathExists, parameters.serviceWorkerRegistrationDirectory));
     }
+
+    for (auto& scheme : parameters.urlSchemesServiceWorkersCanHandle)
+        registerURLSchemeServiceWorkersCanHandle(scheme);
 #endif
 }
 
index a38cba2..a9bcbae 100644 (file)
@@ -177,6 +177,11 @@ static WKProcessPool *sharedProcessPool;
     _processPool->allowSpecificHTTPSCertificateForHost(WebKit::WebCertificateInfo::create(WebCore::CertificateInfo((CFArrayRef)certificateChain)).ptr(), host);
 }
 
+- (void)_registerURLSchemeServiceWorkersCanHandle:(NSString *)scheme
+{
+    _processPool->registerURLSchemeServiceWorkersCanHandle(scheme);
+}
+
 - (void)_setCanHandleHTTPSServerTrustEvaluation:(BOOL)value
 {
     _processPool->setCanHandleHTTPSServerTrustEvaluation(value);
index 5233bc6..b96343a 100644 (file)
@@ -95,6 +95,7 @@
 
 // Test only.
 - (void)_setAllowsAnySSLCertificateForServiceWorker:(BOOL)allows WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (void)_registerURLSchemeServiceWorkersCanHandle:(NSString *)scheme WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @property (nonatomic, getter=_isCookieStoragePartitioningEnabled, setter=_setCookieStoragePartitioningEnabled:) BOOL _cookieStoragePartitioningEnabled WK_API_AVAILABLE(macosx(10.12.3), ios(10.3));
 @property (nonatomic, getter=_isStorageAccessAPIEnabled, setter=_setStorageAccessAPIEnabled:) BOOL _storageAccessAPIEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
index 31e965a..c6a0fb1 100644 (file)
@@ -590,6 +590,11 @@ static Vector<WebKit::WebsiteDataRecord> toWebsiteDataRecords(NSArray *dataRecor
     store->setStatisticsTestingCallback(nullptr);
 }
 
++ (void)_allowWebsiteDataRecordsForAllOrigins
+{
+    WebKit::WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins();
+}
+
 @end
 
 #endif // WK_API_ENABLED
index 664f2d8..2cb3cb7 100644 (file)
@@ -83,7 +83,7 @@ typedef NS_OPTIONS(NSUInteger, _WKWebsiteDataStoreFetchOptions) {
 - (void)_resourceLoadStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours:(unsigned)hours WK_API_AVAILABLE(macosx(10.13), ios(11.0));
 - (void)_resourceLoadStatisticsResetToConsistentState WK_API_AVAILABLE(macosx(10.13), ios(11.0));
 - (void)_setResourceLoadStatisticsTestingCallback:(nullable void (^)(WKWebsiteDataStore *, NSString *))callback WK_API_AVAILABLE(macosx(10.13), ios(11.0));
-
++ (void)_allowWebsiteDataRecordsForAllOrigins WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @end
 
 NS_ASSUME_NONNULL_END
index c82adb2..5352611 100644 (file)
@@ -35,6 +35,7 @@
 #include "APILegacyContextHistoryClient.h"
 #include "APIPageConfiguration.h"
 #include "APIProcessPoolConfiguration.h"
+#include "ChildProcessMessages.h"
 #include "DownloadProxy.h"
 #include "DownloadProxyMessages.h"
 #include "GamepadData.h"
@@ -557,6 +558,9 @@ void WebProcessPool::ensureStorageProcessAndWebsiteDataStore(WebsiteDataStore* r
             parameters.serviceWorkerRegistrationDirectory = m_configuration->serviceWorkerRegistrationDirectory();
             SandboxExtension::createHandleForReadWriteDirectory(parameters.serviceWorkerRegistrationDirectory, parameters.serviceWorkerRegistrationDirectoryExtensionHandle);
         }
+
+        if (!m_schemesServiceWorkersCanHandle.isEmpty())
+            parameters.urlSchemesServiceWorkersCanHandle = copyToVector(m_schemesServiceWorkersCanHandle);
 #endif
 
         m_storageProcess = StorageProcessProxy::create(*this);
@@ -777,6 +781,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa
     parameters.urlSchemesRegisteredAsCORSEnabled = copyToVector(m_schemesToRegisterAsCORSEnabled);
     parameters.urlSchemesRegisteredAsAlwaysRevalidated = copyToVector(m_schemesToRegisterAsAlwaysRevalidated);
     parameters.urlSchemesRegisteredAsCachePartitioned = copyToVector(m_schemesToRegisterAsCachePartitioned);
+    parameters.urlSchemesServiceWorkersCanHandle = copyToVector(m_schemesServiceWorkersCanHandle);
 
     parameters.shouldAlwaysUseComplexTextCodePath = m_alwaysUsesComplexTextCodePath;
     parameters.shouldUseFontSmoothing = m_shouldUseFontSmoothing;
@@ -1290,6 +1295,14 @@ void WebProcessPool::registerURLSchemeAsCachePartitioned(const String& urlScheme
     sendToAllProcesses(Messages::WebProcess::RegisterURLSchemeAsCachePartitioned(urlScheme));
 }
 
+void WebProcessPool::registerURLSchemeServiceWorkersCanHandle(const String& urlScheme)
+{
+    m_schemesServiceWorkersCanHandle.add(urlScheme);
+    sendToAllProcesses(Messages::ChildProcess::RegisterURLSchemeServiceWorkersCanHandle(urlScheme));
+    if (m_storageProcess)
+        m_storageProcess->send(Messages::ChildProcess::RegisterURLSchemeServiceWorkersCanHandle(urlScheme), 0);
+}
+
 void WebProcessPool::setCacheModel(CacheModel cacheModel)
 {
     m_configuration->setCacheModel(cacheModel);
index 07ef110..ee63359 100644 (file)
@@ -218,6 +218,7 @@ public:
     void registerURLSchemeAsDisplayIsolated(const String&);
     void registerURLSchemeAsCORSEnabled(const String&);
     void registerURLSchemeAsCachePartitioned(const String&);
+    void registerURLSchemeServiceWorkersCanHandle(const String&);
     void preconnectToServer(const WebCore::URL&);
 
     VisitedLinkStore& visitedLinkStore() { return m_visitedLinkStore.get(); }
@@ -538,6 +539,7 @@ private:
     HashSet<String> m_schemesToRegisterAsCORSEnabled;
     HashSet<String> m_schemesToRegisterAsAlwaysRevalidated;
     HashSet<String> m_schemesToRegisterAsCachePartitioned;
+    HashSet<String> m_schemesServiceWorkersCanHandle;
 
     bool m_alwaysUsesComplexTextCodePath { false };
     bool m_shouldUseFontSmoothing { true };
index deab0bc..b373f99 100644 (file)
 
 namespace WebKit {
 
+static bool allowsWebsiteDataRecordsForAllOrigins;
+void WebsiteDataStore::allowWebsiteDataRecordsForAllOrigins()
+{
+    allowsWebsiteDataRecordsForAllOrigins = true;
+}
+
 static HashMap<PAL::SessionID, WebsiteDataStore*>& nonDefaultDataStores()
 {
     RELEASE_ASSERT(isUIThread());
@@ -238,8 +244,12 @@ void WebsiteDataStore::fetchDataAndApply(OptionSet<WebsiteDataType> dataTypes, O
 
             for (auto& entry : websiteData.entries) {
                 auto displayName = WebsiteDataRecord::displayNameForOrigin(entry.origin);
-                if (!displayName)
-                    continue;
+                if (!displayName) {
+                    if (!allowsWebsiteDataRecordsForAllOrigins)
+                        continue;
+
+                    displayName = makeString(entry.origin.protocol, " ", entry.origin.host);
+                }
 
                 auto& record = m_websiteDataRecords.add(displayName, WebsiteDataRecord { }).iterator->value;
                 if (!record.displayName)
index 7a1ddb9..78e6595 100644 (file)
@@ -156,6 +156,8 @@ public:
     void setAllowsCellularAccess(AllowsCellularAccess allows) { m_allowsCellularAccess = allows; }
     AllowsCellularAccess allowsCellularAccess() { return m_allowsCellularAccess; }
 
+    static void allowWebsiteDataRecordsForAllOrigins();
+
 private:
     explicit WebsiteDataStore(PAL::SessionID);
     explicit WebsiteDataStore(Configuration, PAL::SessionID);
index 46b5523..aff8c2b 100644 (file)
@@ -34,6 +34,7 @@
 #include <WebCore/CachedResource.h>
 #include <WebCore/Exception.h>
 #include <WebCore/ExceptionCode.h>
+#include <WebCore/SchemeRegistry.h>
 #include <WebCore/ServiceWorkerJob.h>
 #include <pal/SessionID.h>
 #include <wtf/text/WTFString.h>
@@ -71,7 +72,7 @@ static inline bool shouldHandleFetch(const ResourceLoaderOptions& options)
 
 void WebServiceWorkerProvider::handleFetch(ResourceLoader& loader, CachedResource* resource, PAL::SessionID sessionID, bool shouldClearReferrerOnHTTPSToHTTPRedirect, ServiceWorkerClientFetch::Callback&& callback)
 {
-    if (!loader.request().url().protocolIsInHTTPFamily() || !shouldHandleFetch(loader.options())) {
+    if (!SchemeRegistry::canServiceWorkersHandleURLScheme(loader.request().url().protocol().toStringWithoutCopying()) || !shouldHandleFetch(loader.options())) {
         callback(ServiceWorkerClientFetch::Result::Unhandled);
         return;
     }
index 3832dfe..74b51df 100644 (file)
@@ -367,6 +367,9 @@ void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters)
     for (auto& scheme : parameters.urlSchemesRegisteredAsCachePartitioned)
         registerURLSchemeAsCachePartitioned(scheme);
 
+    for (auto& scheme : parameters.urlSchemesServiceWorkersCanHandle)
+        registerURLSchemeServiceWorkersCanHandle(scheme);
+
     setDefaultRequestTimeoutInterval(parameters.defaultRequestTimeoutInterval);
 
     setResourceLoadStatisticsEnabled(parameters.resourceLoadStatisticsEnabled);
index 587318d..c7ed9f1 100644 (file)
@@ -1,3 +1,18 @@
+2017-12-18  Brady Eidson  <beidson@apple.com>
+
+        Add ability to API test Service Workers via a custom protocol.
+        https://bugs.webkit.org/show_bug.cgi?id=180911
+
+        Reviewed by Chris Dumez.
+
+        Adds a very basic SW test:
+        - Verify WebsiteDataStore can wipe all SW registration data.
+        - Fire up a web page with a service worker
+        - Verify SW registration data for that page exists in the WebsiteDataStore.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm: Added.
+
 2017-12-18  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [Attachment Support] Insert images as inline attachments when pasting and dropping
index 5789c29..70ffd71 100644 (file)
                51E5C7031919C3B200D8B3E1 /* simple3.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 51E780371919AFF8001829A2 /* simple3.html */; };
                51E6A8941D2F1C0A00C004B6 /* LocalStorageClear.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51E6A8921D2F1BEC00C004B6 /* LocalStorageClear.mm */; };
                51E6A8961D2F1CA700C004B6 /* LocalStorageClear.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 51E6A8951D2F1C7700C004B6 /* LocalStorageClear.html */; };
+               51EB12941FDF052500A5A1BD /* ServiceWorkerBasic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */; };
                51FCF7A11534B2A000104491 /* ShouldGoToBackForwardListItem_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51FCF7971534AC6D00104491 /* ShouldGoToBackForwardListItem_Bundle.cpp */; };
                520BCF4C141EB09E00937EA8 /* WebArchive_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 520BCF4A141EB09E00937EA8 /* WebArchive_Bundle.cpp */; };
                524BBC9E19DF72C0002F1AF1 /* file-with-video.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 524BBC9B19DF3714002F1AF1 /* file-with-video.html */; };
                51E780361919AFF8001829A2 /* simple2.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = simple2.html; sourceTree = "<group>"; };
                51E780371919AFF8001829A2 /* simple3.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = simple3.html; sourceTree = "<group>"; };
                51E93016156B13E1004C99DF /* WKPageGetScaleFactorNotZero.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKPageGetScaleFactorNotZero.cpp; sourceTree = "<group>"; };
+               51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ServiceWorkerBasic.mm; sourceTree = "<group>"; };
                51FBBB4C1513D4E900822738 /* WebViewCanPasteURL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebViewCanPasteURL.mm; sourceTree = "<group>"; };
                51FCF7971534AC6D00104491 /* ShouldGoToBackForwardListItem_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShouldGoToBackForwardListItem_Bundle.cpp; sourceTree = "<group>"; };
                51FCF7981534AC6D00104491 /* ShouldGoToBackForwardListItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShouldGoToBackForwardListItem.cpp; sourceTree = "<group>"; };
                                A180C0F91EE67DF000468F47 /* RunOpenPanel.mm */,
                                37BCA61B1B596BA9002012CA /* ShouldOpenExternalURLsInNewWindowActions.mm */,
                                2D9A53AE1B31FA8D0074D5AA /* ShrinkToFit.mm */,
+                               51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */,
                                2DFF7B6C1DA487AF00814614 /* SnapshotStore.mm */,
                                515BE1701D428BD100DD7C68 /* StoreBlobThenDelete.mm */,
                                5CB40B4D1F4B98BE007DC7B9 /* UIDelegate.mm */,
                                5C0BF8931DD599BD00B00328 /* IsNavigationActionTrusted.mm in Sources */,
                                5C69BDD51F82A7EF000F4F4B /* JavaScriptDuringNavigation.mm in Sources */,
                                7CCE7EAD1A411A3400447C4C /* JavaScriptTest.cpp in Sources */,
+                               51EB12941FDF052500A5A1BD /* ServiceWorkerBasic.mm in Sources */,
                                7CCE7EA51A411A0800447C4C /* JavaScriptTestMac.mm in Sources */,
                                7CCE7EC41A411A7E00447C4C /* JSWrapperForNodeInWebFrame.mm in Sources */,
                                7CCE7F061A411AE600447C4C /* LayoutMilestonesWithAllContentInFrame.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm
new file mode 100644 (file)
index 0000000..7272410
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKURLSchemeHandler.h>
+#import <WebKit/WKURLSchemeTaskPrivate.h>
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebsiteDataStorePrivate.h>
+#import <WebKit/WebKit.h>
+#import <WebKit/_WKExperimentalFeature.h>
+#import <wtf/Deque.h>
+#import <wtf/HashMap.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/Vector.h>
+#import <wtf/text/StringHash.h>
+#import <wtf/text/WTFString.h>
+
+#if WK_API_ENABLED
+
+struct ResourceInfo {
+    RetainPtr<NSString> mimeType;
+    const char* data;
+};
+
+static bool done;
+
+static Deque<String> expectedMessages;
+
+@interface SWMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation SWMessageHandler
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    EXPECT_TRUE([[message body] isEqualToString:@"Message from worker: ServiceWorker received: Hello from the web page"]);
+    done = true;
+}
+@end
+
+@interface SWSchemes : NSObject <WKURLSchemeHandler> {
+@public
+    HashMap<String, ResourceInfo> resources;
+}
+
+-(size_t)handledRequests;
+@end
+
+@implementation SWSchemes {
+    size_t _handledRequests;
+}
+
+-(size_t)handledRequests
+{
+    return _handledRequests;
+}
+
+- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+    auto entry = resources.find([task.request.URL absoluteString]);
+    if (entry == resources.end()) {
+        NSLog(@"Did not find resource entry for URL %@", task.request.URL);
+        return;
+    }
+
+    ++_handledRequests;
+    RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]);
+    [task didReceiveResponse:response.get()];
+
+    [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]];
+    [task didFinish];
+}
+
+- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+}
+
+@end
+
+static const char* mainBytes = R"SWRESOURCE(
+<script>
+
+function log(msg)
+{
+    window.webkit.messageHandlers.sw.postMessage(msg);
+}
+
+navigator.serviceWorker.addEventListener("message", function(event) {
+    log("Message from worker: " + event.data);
+});
+
+try {
+
+navigator.serviceWorker.register('/sw.js').then(function(reg) {
+    reg.installing.postMessage("Hello from the web page");
+}).catch(function(error) {
+    log("Registration failed with: " + error);
+});
+} catch(e) {
+    log("Exception: " + e);
+}
+
+</script>
+)SWRESOURCE";
+
+static const char* scriptBytes = R"SWRESOURCE(
+
+self.addEventListener("message", (event) => {
+    event.source.postMessage("ServiceWorker received: " + event.data);
+});
+
+)SWRESOURCE";
+
+TEST(ServiceWorkers, Basic)
+{
+    ASSERT(mainBytes);
+    ASSERT(scriptBytes);
+
+    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
+
+    // Start with a clean slate data store
+    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
+    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
+
+    RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
+    handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
+    handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+    [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    webView = nullptr;
+
+    [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
+        EXPECT_EQ(1u, [websiteDataRecords count]);
+        EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
+
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
+
+#endif // WK_API_ENABLED