Add API test to validate setting of service worker and cache storage directories
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / ServiceWorkerBasic.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "Test.h"
30 #import <WebKit/WKPreferencesPrivate.h>
31 #import <WebKit/WKProcessPoolPrivate.h>
32 #import <WebKit/WKURLSchemeHandler.h>
33 #import <WebKit/WKURLSchemeTaskPrivate.h>
34 #import <WebKit/WKWebViewConfigurationPrivate.h>
35 #import <WebKit/WKWebsiteDataStorePrivate.h>
36 #import <WebKit/WKWebsiteDataStoreRef.h>
37 #import <WebKit/WebKit.h>
38 #import <WebKit/_WKExperimentalFeature.h>
39 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
40 #import <wtf/Deque.h>
41 #import <wtf/HashMap.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/Vector.h>
44 #import <wtf/text/StringHash.h>
45 #import <wtf/text/WTFString.h>
46
47 #if WK_API_ENABLED
48
49 struct ResourceInfo {
50     RetainPtr<NSString> mimeType;
51     const char* data;
52 };
53
54 static bool done;
55
56 static String expectedMessage;
57 static String retrievedString;
58
59 @interface SWMessageHandler : NSObject <WKScriptMessageHandler>
60 @end
61
62 @implementation SWMessageHandler
63 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
64 {
65     EXPECT_TRUE([[message body] isEqualToString:@"Message from worker: ServiceWorker received: Hello from the web page"]);
66     done = true;
67 }
68 @end
69
70 @interface SWMessageHandlerForFetchTest : NSObject <WKScriptMessageHandler>
71 @end
72
73 @implementation SWMessageHandlerForFetchTest
74 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
75 {
76     EXPECT_TRUE([[message body] isEqualToString:@"Intercepted by worker"]);
77     done = true;
78 }
79 @end
80
81 @interface SWMessageHandlerForRestoreFromDiskTest : NSObject <WKScriptMessageHandler> {
82     NSString *_expectedMessage;
83 }
84 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
85 @end
86
87 @interface SWMessageHandlerWithExpectedMessage : NSObject <WKScriptMessageHandler>
88 @end
89
90 @implementation SWMessageHandlerWithExpectedMessage
91 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
92 {
93     EXPECT_TRUE([[message body] isEqualToString:expectedMessage]);
94     done = true;
95 }
96 @end
97
98 @implementation SWMessageHandlerForRestoreFromDiskTest
99
100 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage
101 {
102     if (!(self = [super init]))
103         return nil;
104
105     _expectedMessage = expectedMessage;
106
107     return self;
108 }
109
110 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
111 {
112     EXPECT_TRUE([[message body] isEqualToString:_expectedMessage]);
113     done = true;
114 }
115 @end
116
117 @interface SWSchemes : NSObject <WKURLSchemeHandler> {
118 @public
119     HashMap<String, ResourceInfo> resources;
120 }
121
122 -(size_t)handledRequests;
123 @end
124
125 @implementation SWSchemes {
126     size_t _handledRequests;
127 }
128
129 -(size_t)handledRequests
130 {
131     return _handledRequests;
132 }
133
134 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
135 {
136     auto entry = resources.find([task.request.URL absoluteString]);
137     if (entry == resources.end()) {
138         NSLog(@"Did not find resource entry for URL %@", task.request.URL);
139         return;
140     }
141
142     ++_handledRequests;
143     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]);
144     [task didReceiveResponse:response.get()];
145
146     [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]];
147     [task didFinish];
148 }
149
150 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
151 {
152 }
153
154 @end
155
156 static const char* mainBytes = R"SWRESOURCE(
157 <script>
158
159 function log(msg)
160 {
161     window.webkit.messageHandlers.sw.postMessage(msg);
162 }
163
164 navigator.serviceWorker.addEventListener("message", function(event) {
165     log("Message from worker: " + event.data);
166 });
167
168 try {
169
170 navigator.serviceWorker.register('/sw.js').then(function(reg) {
171     reg.installing.postMessage("Hello from the web page");
172 }).catch(function(error) {
173     log("Registration failed with: " + error);
174 });
175 } catch(e) {
176     log("Exception: " + e);
177 }
178
179 </script>
180 )SWRESOURCE";
181
182 static const char* scriptBytes = R"SWRESOURCE(
183
184 self.addEventListener("message", (event) => {
185     event.source.postMessage("ServiceWorker received: " + event.data);
186 });
187
188 )SWRESOURCE";
189
190 static const char* mainForFetchTestBytes = R"SWRESOURCE(
191 <html>
192 <body>
193 <script>
194 function log(msg)
195 {
196     window.webkit.messageHandlers.sw.postMessage(msg);
197 }
198
199 try {
200
201 function addFrame()
202 {
203     frame = document.createElement('iframe');
204     frame.src = "/test.html";
205     frame.onload = function() { window.webkit.messageHandlers.sw.postMessage(frame.contentDocument.body.innerHTML); }
206     document.body.appendChild(frame);
207 }
208
209 navigator.serviceWorker.register('/sw.js').then(function(reg) {
210     if (reg.active) {
211         addFrame();
212         return;
213     }
214     worker = reg.installing;
215     worker.addEventListener('statechange', function() {
216         if (worker.state == 'activated')
217             addFrame();
218     });
219 }).catch(function(error) {
220     log("Registration failed with: " + error);
221 });
222 } catch(e) {
223     log("Exception: " + e);
224 }
225
226 </script>
227 </body>
228 </html>
229 )SWRESOURCE";
230
231 static const char* scriptHandlingFetchBytes = R"SWRESOURCE(
232
233 self.addEventListener("fetch", (event) => {
234     if (event.request.url.indexOf("test.html") !== -1) {
235         event.respondWith(new Response(new Blob(['Intercepted by worker'], {type: 'text/html'})));
236     }
237 });
238
239 )SWRESOURCE";
240
241 static const char* scriptInterceptingFirstLoadBytes = R"SWRESOURCE(
242
243 self.addEventListener("fetch", (event) => {
244     if (event.request.url.indexOf("main.html") !== -1) {
245         event.respondWith(new Response(new Blob(['Intercepted by worker <script>window.webkit.messageHandlers.sw.postMessage(\'Intercepted by worker\');</script>'], {type: 'text/html'})));
246     }
247 });
248
249 )SWRESOURCE";
250
251 static const char* mainForFirstLoadInterceptTestBytes = R"SWRESOURCE(
252  <html>
253 <body>
254 <script>
255 function log(msg)
256 {
257     window.webkit.messageHandlers.sw.postMessage(msg);
258 }
259
260 try {
261
262 navigator.serviceWorker.register('/sw.js').then(function(reg) {
263     if (reg.active) {
264         window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
265         return;
266     }
267
268     worker = reg.installing;
269     worker.addEventListener('statechange', function() {
270         if (worker.state == 'activated')
271             window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
272     });
273 }).catch(function(error) {
274     log("Registration failed with: " + error);
275 });
276 } catch(e) {
277     log("Exception: " + e);
278 }
279
280 </script>
281 </body>
282 </html>
283 )SWRESOURCE";
284
285 static const char* testBytes = R"SWRESOURCE(
286 <body>
287 NOT intercepted by worker
288 </body>
289 )SWRESOURCE";
290
291 static const char* mainRegisteringWorkerBytes = R"SWRESOURCE(
292 <script>
293 try {
294 function log(msg)
295 {
296     window.webkit.messageHandlers.sw.postMessage(msg);
297 }
298
299 navigator.serviceWorker.register('/sw.js').then(function(reg) {
300     if (reg.active) {
301         log("FAIL: Registration already has an active worker");
302         return;
303     }
304     worker = reg.installing;
305     worker.addEventListener('statechange', function() {
306         if (worker.state == 'activated')
307             log("PASS: Registration was successful and service worker was activated");
308     });
309 }).catch(function(error) {
310     log("Registration failed with: " + error);
311 });
312 } catch(e) {
313     log("Exception: " + e);
314 }
315 </script>
316 )SWRESOURCE";
317
318 static const char* mainRegisteringAlreadyExistingWorkerBytes = R"SWRESOURCE(
319 <script>
320 try {
321 function log(msg)
322 {
323     window.webkit.messageHandlers.sw.postMessage(msg);
324 }
325
326 navigator.serviceWorker.register('/sw.js').then(function(reg) {
327     if (reg.installing) {
328         log("FAIL: Registration had an installing worker");
329         return;
330     }
331     if (reg.active) {
332         if (reg.active.state == "activated")
333             log("PASS: Registration already has an active worker");
334         else
335             log("FAIL: Registration has an active worker but its state is not activated");
336     } else
337         log("FAIL: Registration does not have an active worker");
338 }).catch(function(error) {
339     log("Registration failed with: " + error);
340 });
341 } catch(e) {
342     log("Exception: " + e);
343 }
344 </script>
345 )SWRESOURCE";
346
347 static const char* mainBytesForSessionIDTest = R"SWRESOURCE(
348 <script>
349
350 function log(msg)
351 {
352     window.webkit.messageHandlers.sw.postMessage(msg);
353 }
354
355 navigator.serviceWorker.addEventListener("message", function(event) {
356     log(event.data);
357 });
358
359 try {
360
361 navigator.serviceWorker.register('/sw.js').then(function(reg) {
362     worker = reg.installing;
363     worker.addEventListener('statechange', function() {
364         if (worker.state == 'activated')
365             worker.postMessage("TEST");
366     });
367 }).catch(function(error) {
368     log("Registration failed with: " + error);
369 });
370 } catch(e) {
371     log("Exception: " + e);
372 }
373
374 </script>
375 )SWRESOURCE";
376
377 static const char* scriptBytesForSessionIDTest = R"SWRESOURCE(
378
379 var wasActivated = false;
380
381 self.addEventListener("activate", event => {
382     event.waitUntil(clients.claim().then( () => {
383         wasActivated = true;
384     }));
385 });
386
387 self.addEventListener("message", (event) => {
388     if (wasActivated && registration.active)
389         event.source.postMessage("PASS: activation successful");
390     else
391         event.source.postMessage("FAIL: failed to activate");
392 });
393
394 )SWRESOURCE";
395
396 TEST(ServiceWorkers, Basic)
397 {
398     ASSERT(mainBytes);
399     ASSERT(scriptBytes);
400
401     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
402
403     // Start with a clean slate data store
404     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
405         done = true;
406     }];
407     TestWebKitAPI::Util::run(&done);
408     done = false;
409
410     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
411
412     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
413     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
414
415     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
416     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
417     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
418     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
419
420     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
421     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
422
423     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
424     [webView loadRequest:request];
425
426     TestWebKitAPI::Util::run(&done);
427     done = false;
428
429     webView = nullptr;
430
431     [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
432         EXPECT_EQ(1u, [websiteDataRecords count]);
433         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
434
435         done = true;
436     }];
437
438     TestWebKitAPI::Util::run(&done);
439     done = false;
440 }
441
442 TEST(ServiceWorkers, RestoreFromDisk)
443 {
444     ASSERT(mainRegisteringWorkerBytes);
445     ASSERT(scriptBytes);
446     ASSERT(mainRegisteringAlreadyExistingWorkerBytes);
447
448     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
449
450     // Start with a clean slate data store
451     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
452         done = true;
453     }];
454     TestWebKitAPI::Util::run(&done);
455     done = false;
456
457     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
458
459     RetainPtr<SWMessageHandlerForRestoreFromDiskTest> messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration was successful and service worker was activated"]);
460     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
461
462     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
463     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringWorkerBytes });
464     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
465     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
466
467     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
468     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
469
470     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
471     [webView loadRequest:request];
472
473     TestWebKitAPI::Util::run(&done);
474
475     webView = nullptr;
476     configuration = nullptr;
477     messageHandler = nullptr;
478     handler = nullptr;
479
480     done = false;
481
482     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
483     messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration already has an active worker"]);
484     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
485
486     handler = adoptNS([[SWSchemes alloc] init]);
487     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringAlreadyExistingWorkerBytes });
488     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
489     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
490
491     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
492     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
493
494     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
495     [webView loadRequest:request];
496
497     TestWebKitAPI::Util::run(&done);
498     done = false;
499 }
500
501 TEST(ServiceWorkers, FetchAfterRestoreFromDisk)
502 {
503     ASSERT(mainForFetchTestBytes);
504     ASSERT(scriptHandlingFetchBytes);
505
506     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
507
508     // Start with a clean slate data store
509     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
510         done = true;
511     }];
512     TestWebKitAPI::Util::run(&done);
513     done = false;
514
515     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
516
517     RetainPtr<SWMessageHandlerForFetchTest> messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
518     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
519
520     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
521     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
522     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
523     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
524     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
525
526     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
527     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
528
529     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
530     [webView loadRequest:request];
531
532     TestWebKitAPI::Util::run(&done);
533
534     webView = nullptr;
535     configuration = nullptr;
536     messageHandler = nullptr;
537     handler = nullptr;
538
539     done = false;
540
541     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
542     messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
543     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
544
545     handler = adoptNS([[SWSchemes alloc] init]);
546     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
547     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
548     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
549     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
550
551     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
552     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
553
554     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
555     [webView loadRequest:request];
556
557     TestWebKitAPI::Util::run(&done);
558     done = false;
559 }
560
561 TEST(ServiceWorkers, InterceptFirstLoadAfterRestoreFromDisk)
562 {
563     ASSERT(mainForFirstLoadInterceptTestBytes);
564     ASSERT(scriptHandlingFetchBytes);
565
566     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
567
568     // Start with a clean slate data store
569     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
570         done = true;
571     }];
572     TestWebKitAPI::Util::run(&done);
573     done = false;
574
575     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
576
577     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
578     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
579
580     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
581     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
582     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
583     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
584
585     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
586     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
587
588     expectedMessage = "Service Worker activated";
589     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
590     [webView loadRequest:request];
591
592     TestWebKitAPI::Util::run(&done);
593
594     webView = nullptr;
595     configuration = nullptr;
596     messageHandler = nullptr;
597     handler = nullptr;
598
599     done = false;
600
601     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
602     messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
603     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
604
605     handler = adoptNS([[SWSchemes alloc] init]);
606     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
607     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
608     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
609
610     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
611     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
612
613     expectedMessage = "Intercepted by worker";
614     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
615     [webView loadRequest:request];
616
617     TestWebKitAPI::Util::run(&done);
618     done = false;
619 }
620
621 #if WK_HAVE_C_SPI
622
623 void setConfigurationInjectedBundlePath(WKWebViewConfiguration* configuration)
624 {
625     WKRetainPtr<WKContextRef> context(AdoptWK, TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
626     configuration.processPool = (WKProcessPool *)context.get();
627     auto pool = configuration.processPool;
628     [pool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
629     [pool _setMaximumNumberOfProcesses:5];
630
631     configuration.websiteDataStore = (WKWebsiteDataStore *)WKContextGetWebsiteDataStore(context.get());
632 }
633
634 @interface RegularPageMessageHandler : NSObject <WKScriptMessageHandler>
635 @end
636
637 @implementation RegularPageMessageHandler
638 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
639 {
640     EXPECT_TRUE([[message body] isEqualToString:@"PASS"]);
641     done = true;
642 }
643 @end
644
645 static const char* regularPageWithoutConnectionBytes = R"SWRESOURCE(
646 <script>
647 var result = window.internals.hasServiceWorkerConnection() ? "FAIL" : "PASS";
648 window.webkit.messageHandlers.regularPage.postMessage(result);
649 </script>
650 )SWRESOURCE";
651
652 static const char* regularPageWithConnectionBytes = R"SWRESOURCE(
653 <script>
654 var result = window.internals.hasServiceWorkerConnection() ? "PASS" : "FAIL";
655 window.webkit.messageHandlers.regularPage.postMessage(result);
656 </script>
657 )SWRESOURCE";
658
659 TEST(ServiceWorkers, SWProcessConnectionCreation)
660 {
661     ASSERT(mainBytes);
662     ASSERT(scriptBytes);
663
664     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
665
666     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
667     setConfigurationInjectedBundlePath(configuration.get());
668
669     done = false;
670
671     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
672         done = true;
673     }];
674     TestWebKitAPI::Util::run(&done);
675     done = false;
676
677     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
678         EXPECT_EQ(0u, [websiteDataRecords count]);
679
680         done = true;
681     }];
682     TestWebKitAPI::Util::run(&done);
683     done = false;
684
685     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
686     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
687     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
688     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
689
690     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
691     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
692     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
693     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
694     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
695     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
696
697     RetainPtr<WKWebView> regularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
698     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
699     RetainPtr<WKWebView> newRegularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
700
701     // Test that a regular page does not trigger a service worker connection to storage process if there is no registered service worker.
702     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
703
704     [regularPageWebView loadRequest:request];
705     TestWebKitAPI::Util::run(&done);
706     done = false;
707
708     // Test that a sw scheme page can register a service worker.
709     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
710
711     [webView loadRequest:request];
712     TestWebKitAPI::Util::run(&done);
713     done = false;
714     webView = nullptr;
715
716     // Now that a service worker is registered, the regular page should have a service worker connection.
717     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
718
719     [regularPageWebView loadRequest:request];
720     TestWebKitAPI::Util::run(&done);
721     done = false;
722     regularPageWebView = nullptr;
723
724     [newRegularPageWebView loadRequest:request];
725     TestWebKitAPI::Util::run(&done);
726     done = false;
727     newRegularPageWebView = nullptr;
728
729     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
730         EXPECT_EQ(1u, [websiteDataRecords count]);
731         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
732
733         done = true;
734     }];
735
736     TestWebKitAPI::Util::run(&done);
737     done = false;
738
739     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
740         done = true;
741     }];
742     TestWebKitAPI::Util::run(&done);
743 }
744
745 TEST(ServiceWorkers, StorageProcessConnectionCreation)
746 {
747     ASSERT(mainBytes);
748     ASSERT(scriptBytes);
749
750     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
751
752     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
753
754     done = false;
755     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
756         done = true;
757     }];
758     TestWebKitAPI::Util::run(&done);
759     done = false;
760
761     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
762     setConfigurationInjectedBundlePath(configuration.get());
763
764     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
765     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
766
767     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
768     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
769     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
770
771     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
772
773     EXPECT_EQ(0, webView.get().configuration.processPool._storageProcessIdentifier);
774
775     // Test that a regular page does not trigger a storage process if there is no registered service worker.
776     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
777
778     [webView loadRequest:request];
779     TestWebKitAPI::Util::run(&done);
780     done = false;
781
782     // Make sure than loading the simple page did not start a storage process.
783     EXPECT_EQ(0, webView.get().configuration.processPool._storageProcessIdentifier);
784 }
785
786 static const char* mainBytesWithScope = R"SWRESOURCE(
787 <script>
788
789 function log(msg)
790 {
791     window.webkit.messageHandlers.sw.postMessage(msg);
792 }
793
794 navigator.serviceWorker.addEventListener("message", function(event) {
795     log("Message from worker: " + event.data);
796 });
797
798 try {
799
800 navigator.serviceWorker.register('/sw.js', {scope: 'whateverscope'}).then(function(reg) {
801     reg.installing.postMessage("Hello from the web page");
802 }).catch(function(error) {
803     log("Registration failed with: " + error);
804 });
805 } catch(e) {
806     log("Exception: " + e);
807 }
808
809 </script>
810 )SWRESOURCE";
811
812 TEST(ServiceWorkers, ServiceWorkerProcessCreation)
813 {
814     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
815
816     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
817     setConfigurationInjectedBundlePath(configuration.get());
818
819     done = false;
820
821     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
822         done = true;
823     }];
824     TestWebKitAPI::Util::run(&done);
825     done = false;
826
827     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
828
829         done = true;
830     }];
831     TestWebKitAPI::Util::run(&done);
832     done = false;
833
834     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
835     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
836     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
837     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
838
839     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
840     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
841     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
842     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
843     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
844     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
845     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
846
847     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
848
849     // Load a page that registers a service worker.
850     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
851
852     [webView loadRequest:request];
853     TestWebKitAPI::Util::run(&done);
854     done = false;
855     webView = nullptr;
856
857     // Now that a sw is registered, let's create a new configuration and try loading a regular page, there should be no service worker process created.
858     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
859     setConfigurationInjectedBundlePath(newConfiguration.get());
860     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
861
862     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
863     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
864     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
865
866     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
867     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCount);
868     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
869     [webView loadRequest:request];
870     TestWebKitAPI::Util::run(&done);
871     done = false;
872
873     // Make sure that loading the simple page did not start the service worker process.
874     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCount);
875
876     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
877     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCount);
878     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
879     [webView loadRequest:request];
880     TestWebKitAPI::Util::run(&done);
881     done = false;
882
883     // Make sure that loading this page did start the service worker process.
884     EXPECT_EQ(3u, webView.get().configuration.processPool._webProcessCount);
885
886     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
887         done = true;
888     }];
889     TestWebKitAPI::Util::run(&done);
890     done = false;
891 }
892
893 TEST(ServiceWorkers, HasServiceWorkerRegistrationBit)
894 {
895     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
896
897     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
898     setConfigurationInjectedBundlePath(configuration.get());
899
900     done = false;
901
902     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
903         done = true;
904     }];
905     TestWebKitAPI::Util::run(&done);
906     done = false;
907
908     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
909
910         done = true;
911     }];
912     TestWebKitAPI::Util::run(&done);
913     done = false;
914
915     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
916     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
917     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
918     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
919
920     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
921     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
922     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
923     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
924     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
925     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
926     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
927
928     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
929
930     // Load a page that registers a service worker.
931     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
932
933     [webView loadRequest:request];
934     TestWebKitAPI::Util::run(&done);
935     done = false;
936     webView = nullptr;
937
938     // Now that a sw is registered, let's create a new configuration and try loading a regular page
939     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
940     setConfigurationInjectedBundlePath(newConfiguration.get());
941
942     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
943     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
944     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
945
946     newConfiguration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
947     [newConfiguration.get().websiteDataStore _setServiceWorkerRegistrationDirectory: @"~/nonexistingfolder"];
948
949     [newConfiguration.get().processPool _setMaximumNumberOfProcesses:1];
950
951     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
952     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
953     [webView loadRequest:request];
954     TestWebKitAPI::Util::run(&done);
955     done = false;
956
957     // There should be no storage process created.
958     EXPECT_EQ(0, webView.get().configuration.processPool._storageProcessIdentifier);
959
960     // Let's use the web site data store that has service worker and load a page.
961     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
962
963     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
964     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCount);
965     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
966     [webView loadRequest:request];
967     TestWebKitAPI::Util::run(&done);
968     done = false;
969
970     // Make sure that storage process is launched.
971     EXPECT_NE(0, webView.get().configuration.processPool._storageProcessIdentifier);
972
973     // Make sure that loading the simple page did not start the service worker process.
974     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCount);
975
976     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
977         done = true;
978     }];
979     TestWebKitAPI::Util::run(&done);
980     done = false;
981 }
982
983 static const char* regularPageGrabbingCacheStorageDirectory = R"SWRESOURCE(
984 <script>
985 async function getResult()
986 {
987     var result = await window.internals.cacheStorageEngineRepresentation();
988     window.webkit.messageHandlers.sw.postMessage(result);
989 }
990 getResult();
991 </script>
992 )SWRESOURCE";
993
994 @interface DirectoryPageMessageHandler : NSObject <WKScriptMessageHandler>
995 @end
996
997 @implementation DirectoryPageMessageHandler
998 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
999 {
1000     retrievedString = [message body];
1001     done = true;
1002 }
1003 @end
1004
1005 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageDefaultDirectories)
1006 {
1007     ASSERT(mainBytes);
1008     ASSERT(scriptBytes);
1009
1010     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1011
1012     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1013
1014     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1015     setConfigurationInjectedBundlePath(configuration.get());
1016
1017     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1018     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1019
1020     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1021     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1022     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1023     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1024     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1025
1026     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1027     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1028
1029     [webView loadRequest:request];
1030     TestWebKitAPI::Util::run(&done);
1031     done = false;
1032     EXPECT_TRUE([[configuration websiteDataStore] _hasRegisteredServiceWorker]);
1033
1034     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1035     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1036
1037     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1038     [webView loadRequest:request];
1039     TestWebKitAPI::Util::run(&done);
1040     done = false;
1041     EXPECT_TRUE(retrievedString.contains("/Caches/TestWebKitAPI/WebKit/CacheStorage"));
1042
1043     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1044         done = true;
1045     }];
1046     TestWebKitAPI::Util::run(&done);
1047     done = false;
1048 }
1049
1050 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageSpecificDirectories)
1051 {
1052     ASSERT(mainBytes);
1053     ASSERT(scriptBytes);
1054
1055     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1056
1057     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1058
1059     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1060     setConfigurationInjectedBundlePath(configuration.get());
1061     auto websiteDataStore = [configuration websiteDataStore];
1062     [websiteDataStore _setCacheStorageDirectory:@"/var/tmp"];
1063     [websiteDataStore _setServiceWorkerRegistrationDirectory:@"/var/tmp"];
1064
1065     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1066     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1067
1068     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1069     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1070     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1071     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1072     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1073
1074     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1075     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1076
1077     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1078     [webView loadRequest:request];
1079     TestWebKitAPI::Util::run(&done);
1080     done = false;
1081     EXPECT_TRUE([websiteDataStore _hasRegisteredServiceWorker]);
1082
1083     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1084     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1085
1086     [webView loadRequest:request];
1087     TestWebKitAPI::Util::run(&done);
1088     done = false;
1089     EXPECT_TRUE(retrievedString.contains("\"path\": \"/var/tmp\""));
1090
1091     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1092         done = true;
1093     }];
1094     TestWebKitAPI::Util::run(&done);
1095     done = false;
1096 }
1097
1098 #endif // WK_HAVE_C_SPI
1099
1100 TEST(ServiceWorkers, NonDefaultSessionID)
1101 {
1102     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1103
1104     NSURL *serviceWorkersPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ServiceWorkers/" stringByExpandingTildeInPath] isDirectory:YES];
1105     [[NSFileManager defaultManager] removeItemAtURL:serviceWorkersPath error:nil];
1106     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1107     NSURL *idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
1108     [[NSFileManager defaultManager] removeItemAtURL:idbPath error:nil];
1109     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
1110
1111     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1112     RetainPtr<_WKWebsiteDataStoreConfiguration> websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
1113     websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = serviceWorkersPath;
1114     websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath;
1115
1116     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
1117
1118     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
1119     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1120
1121     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1122     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytesForSessionIDTest });
1123     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytesForSessionIDTest });
1124     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1125
1126     expectedMessage = "PASS: activation successful";
1127     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1128     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1129
1130     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1131     [webView loadRequest:request];
1132
1133     TestWebKitAPI::Util::run(&done);
1134     done = false;
1135
1136     webView = nullptr;
1137
1138     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1139
1140     [configuration.get().websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1141         EXPECT_EQ(1u, [websiteDataRecords count]);
1142         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
1143
1144         done = true;
1145     }];
1146
1147     TestWebKitAPI::Util::run(&done);
1148     done = false;
1149 }
1150
1151 #endif // WK_API_ENABLED