Move WKProcessPool._registerURLSchemeServiceWorkersCanHandle to _WKWebsiteDataStoreCo...
[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 "TCPServer.h"
30 #import "Test.h"
31 #import "TestNavigationDelegate.h"
32 #import "TestWKWebView.h"
33 #import <WebKit/WKPreferencesPrivate.h>
34 #import <WebKit/WKProcessPoolPrivate.h>
35 #import <WebKit/WKURLSchemeHandler.h>
36 #import <WebKit/WKURLSchemeTaskPrivate.h>
37 #import <WebKit/WKWebViewConfigurationPrivate.h>
38 #import <WebKit/WKWebViewPrivate.h>
39 #import <WebKit/WKWebsiteDataStorePrivate.h>
40 #import <WebKit/WKWebsiteDataStoreRef.h>
41 #import <WebKit/WebKit.h>
42 #import <WebKit/_WKExperimentalFeature.h>
43 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
44 #import <WebKit/_WKWebsitePolicies.h>
45 #import <wtf/Deque.h>
46 #import <wtf/HashMap.h>
47 #import <wtf/RetainPtr.h>
48 #import <wtf/Vector.h>
49 #import <wtf/text/StringHash.h>
50 #import <wtf/text/WTFString.h>
51
52 struct ResourceInfo {
53     RetainPtr<NSString> mimeType;
54     const char* data;
55 };
56
57 static bool done;
58 static bool didFinishNavigation;
59
60 static String expectedMessage;
61 static String retrievedString;
62
63 @interface SWMessageHandler : NSObject <WKScriptMessageHandler>
64 @end
65
66 @implementation SWMessageHandler
67 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
68 {
69     EXPECT_WK_STREQ(@"Message from worker: ServiceWorker received: Hello from the web page", [message body]);
70     done = true;
71 }
72 @end
73
74 @interface SWMessageHandlerForFetchTest : NSObject <WKScriptMessageHandler>
75 @end
76
77 @implementation SWMessageHandlerForFetchTest
78 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
79 {
80     EXPECT_TRUE([[message body] isEqualToString:@"Intercepted by worker"]);
81     done = true;
82 }
83 @end
84
85 @interface SWMessageHandlerForRestoreFromDiskTest : NSObject <WKScriptMessageHandler> {
86     NSString *_expectedMessage;
87 }
88 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
89 @end
90
91 @interface SWMessageHandlerWithExpectedMessage : NSObject <WKScriptMessageHandler>
92 @end
93
94 @implementation SWMessageHandlerWithExpectedMessage
95 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
96 {
97     EXPECT_TRUE([[message body] isEqualToString:expectedMessage]);
98     done = true;
99 }
100 @end
101
102 @implementation SWMessageHandlerForRestoreFromDiskTest
103
104 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage
105 {
106     if (!(self = [super init]))
107         return nil;
108
109     _expectedMessage = expectedMessage;
110
111     return self;
112 }
113
114 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
115 {
116     EXPECT_TRUE([[message body] isEqualToString:_expectedMessage]);
117     done = true;
118 }
119 @end
120
121 @interface SWSchemes : NSObject <WKURLSchemeHandler> {
122 @public
123     HashMap<String, ResourceInfo> resources;
124     NSString *expectedUserAgent;
125 }
126
127 -(size_t)handledRequests;
128 @end
129
130 @implementation SWSchemes {
131     size_t _handledRequests;
132 }
133
134 -(size_t)handledRequests
135 {
136     return _handledRequests;
137 }
138
139 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
140 {
141     if (expectedUserAgent)
142         EXPECT_WK_STREQ(expectedUserAgent, [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
143
144     auto entry = resources.find([task.request.URL absoluteString]);
145     if (entry == resources.end()) {
146         NSLog(@"Did not find resource entry for URL %@", task.request.URL);
147         return;
148     }
149
150     ++_handledRequests;
151     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]);
152     [task didReceiveResponse:response.get()];
153
154     [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]];
155     [task didFinish];
156 }
157
158 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
159 {
160 }
161
162 @end
163
164 static bool shouldAccept = true;
165 static bool navigationComplete = false;
166 static bool navigationFailed = false;
167
168 @interface TestSWAsyncNavigationDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
169 @end
170
171 @implementation TestSWAsyncNavigationDelegate
172
173 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
174 {
175     navigationComplete = true;
176 }
177
178 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
179 {
180     navigationFailed = true;
181     navigationComplete = true;
182 }
183
184 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
185 {
186     navigationFailed = true;
187     navigationComplete = true;
188 }
189
190 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
191 {
192     decisionHandler(WKNavigationActionPolicyAllow);
193 }
194
195 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
196 {
197     int64_t deferredWaitTime = 100 * NSEC_PER_MSEC;
198     dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, deferredWaitTime);
199     dispatch_after(when, dispatch_get_main_queue(), ^{
200         decisionHandler(shouldAccept ? WKNavigationResponsePolicyAllow : WKNavigationResponsePolicyCancel);
201     });
202 }
203 @end
204
205 static const char* mainCacheStorageBytes = R"SWRESOURCE(
206 <script>
207
208 function log(msg)
209 {
210     window.webkit.messageHandlers.sw.postMessage(msg);
211 }
212
213 async function doTest()
214 {
215     const keys = await window.caches.keys();
216     if (!keys.length) {
217         const cache = await window.caches.open("my cache");
218         log("No cache storage data");
219         return;
220     }
221     if (keys.length !== 1) {
222         log("Unexpected cache number");
223         return;
224     }
225     log("Some cache storage data: " + keys[0]);
226 }
227 doTest();
228
229 </script>
230 )SWRESOURCE";
231
232 static const char* mainBytes = R"SWRESOURCE(
233 <script>
234
235 function log(msg)
236 {
237     window.webkit.messageHandlers.sw.postMessage(msg);
238 }
239
240 navigator.serviceWorker.addEventListener("message", function(event) {
241     log("Message from worker: " + event.data);
242 });
243
244 try {
245
246 navigator.serviceWorker.register('/sw.js').then(function(reg) {
247     worker = reg.installing ? reg.installing : reg.active;
248     worker.postMessage("Hello from the web page");
249 }).catch(function(error) {
250     log("Registration failed with: " + error);
251 });
252 } catch(e) {
253     log("Exception: " + e);
254 }
255
256 </script>
257 )SWRESOURCE";
258
259 static const char* scriptBytes = R"SWRESOURCE(
260
261 self.addEventListener("message", (event) => {
262     event.source.postMessage("ServiceWorker received: " + event.data);
263 });
264
265 )SWRESOURCE";
266
267 static const char* mainForFetchTestBytes = R"SWRESOURCE(
268 <html>
269 <body>
270 <script>
271 function log(msg)
272 {
273     window.webkit.messageHandlers.sw.postMessage(msg);
274 }
275
276 try {
277
278 function addFrame()
279 {
280     frame = document.createElement('iframe');
281     frame.src = "/test.html";
282     frame.onload = function() { window.webkit.messageHandlers.sw.postMessage(frame.contentDocument.body.innerHTML); }
283     document.body.appendChild(frame);
284 }
285
286 navigator.serviceWorker.register('/sw.js').then(function(reg) {
287     if (reg.active) {
288         addFrame();
289         return;
290     }
291     worker = reg.installing;
292     worker.addEventListener('statechange', function() {
293         if (worker.state == 'activated')
294             addFrame();
295     });
296 }).catch(function(error) {
297     log("Registration failed with: " + error);
298 });
299 } catch(e) {
300     log("Exception: " + e);
301 }
302
303 </script>
304 </body>
305 </html>
306 )SWRESOURCE";
307
308 static const char* scriptHandlingFetchBytes = R"SWRESOURCE(
309
310 self.addEventListener("fetch", (event) => {
311     if (event.request.url.indexOf("test.html") !== -1) {
312         event.respondWith(new Response(new Blob(['Intercepted by worker'], {type: 'text/html'})));
313     }
314 });
315
316 )SWRESOURCE";
317
318 static const char* scriptInterceptingFirstLoadBytes = R"SWRESOURCE(
319
320 self.addEventListener("fetch", (event) => {
321     if (event.request.url.indexOf("main.html") !== -1) {
322         event.respondWith(new Response(new Blob(['Intercepted by worker <script>window.webkit.messageHandlers.sw.postMessage(\'Intercepted by worker\');</script>'], {type: 'text/html'})));
323     }
324 });
325
326 )SWRESOURCE";
327
328 static const char* mainForFirstLoadInterceptTestBytes = R"SWRESOURCE(
329  <html>
330 <body>
331 <script>
332 function log(msg)
333 {
334     window.webkit.messageHandlers.sw.postMessage(msg);
335 }
336
337 try {
338
339 navigator.serviceWorker.register('/sw.js').then(function(reg) {
340     if (reg.active) {
341         window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
342         return;
343     }
344
345     worker = reg.installing;
346     worker.addEventListener('statechange', function() {
347         if (worker.state == 'activated')
348             window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
349     });
350 }).catch(function(error) {
351     log("Registration failed with: " + error);
352 });
353 } catch(e) {
354     log("Exception: " + e);
355 }
356
357 </script>
358 </body>
359 </html>
360 )SWRESOURCE";
361
362 static const char* testBytes = R"SWRESOURCE(
363 <body>
364 NOT intercepted by worker
365 </body>
366 )SWRESOURCE";
367
368 static const char* mainRegisteringWorkerBytes = R"SWRESOURCE(
369 <script>
370 try {
371 function log(msg)
372 {
373     window.webkit.messageHandlers.sw.postMessage(msg);
374 }
375
376 navigator.serviceWorker.register('/sw.js').then(function(reg) {
377     if (reg.active) {
378         log("FAIL: Registration already has an active worker");
379         return;
380     }
381     worker = reg.installing;
382     worker.addEventListener('statechange', function() {
383         if (worker.state == 'activated')
384             log("PASS: Registration was successful and service worker was activated");
385     });
386 }).catch(function(error) {
387     log("Registration failed with: " + error);
388 });
389 } catch(e) {
390     log("Exception: " + e);
391 }
392 </script>
393 )SWRESOURCE";
394
395 static const char* mainRegisteringAlreadyExistingWorkerBytes = R"SWRESOURCE(
396 <script>
397 try {
398 function log(msg)
399 {
400     window.webkit.messageHandlers.sw.postMessage(msg);
401 }
402
403 navigator.serviceWorker.register('/sw.js').then(function(reg) {
404     if (reg.installing) {
405         log("FAIL: Registration had an installing worker");
406         return;
407     }
408     if (reg.active) {
409         if (reg.active.state == "activated")
410             log("PASS: Registration already has an active worker");
411         else
412             log("FAIL: Registration has an active worker but its state is not activated");
413     } else
414         log("FAIL: Registration does not have an active worker");
415 }).catch(function(error) {
416     log("Registration failed with: " + error);
417 });
418 } catch(e) {
419     log("Exception: " + e);
420 }
421 </script>
422 )SWRESOURCE";
423
424 static const char* mainBytesForSessionIDTest = R"SWRESOURCE(
425 <script>
426
427 function log(msg)
428 {
429     window.webkit.messageHandlers.sw.postMessage(msg);
430 }
431
432 navigator.serviceWorker.addEventListener("message", function(event) {
433     log(event.data);
434 });
435
436 try {
437
438 navigator.serviceWorker.register('/sw.js').then(function(reg) {
439     worker = reg.installing;
440     worker.addEventListener('statechange', function() {
441         if (worker.state == 'activated')
442             worker.postMessage("TEST");
443     });
444 }).catch(function(error) {
445     log("Registration failed with: " + error);
446 });
447 } catch(e) {
448     log("Exception: " + e);
449 }
450
451 </script>
452 )SWRESOURCE";
453
454 static const char* scriptBytesForSessionIDTest = R"SWRESOURCE(
455
456 var wasActivated = false;
457
458 self.addEventListener("activate", event => {
459     event.waitUntil(clients.claim().then( () => {
460         wasActivated = true;
461     }));
462 });
463
464 self.addEventListener("message", (event) => {
465     if (wasActivated && registration.active)
466         event.source.postMessage("PASS: activation successful");
467     else
468         event.source.postMessage("FAIL: failed to activate");
469 });
470
471 )SWRESOURCE";
472
473 static WKWebsiteDataStore *dataStoreWithRegisteredServiceWorkerScheme(NSString *scheme)
474 {
475     _WKWebsiteDataStoreConfiguration *configuration = [[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease];
476     [configuration registerURLSchemeServiceWorkersCanHandleForTesting:scheme];
477     return [[[WKWebsiteDataStore alloc] _initWithConfiguration:configuration] autorelease];
478 }
479
480 TEST(ServiceWorkers, Basic)
481 {
482     ASSERT(mainBytes);
483     ASSERT(scriptBytes);
484
485     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
486
487     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
488     
489     // Start with a clean slate data store
490     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
491         done = true;
492     }];
493     TestWebKitAPI::Util::run(&done);
494     done = false;
495
496     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
497     [configuration setWebsiteDataStore:dataStore];
498
499     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
500     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
501
502     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
503     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
504     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
505     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
506
507     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
508
509     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
510     [webView loadRequest:request];
511
512     TestWebKitAPI::Util::run(&done);
513     done = false;
514
515     webView = nullptr;
516
517     [dataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
518         EXPECT_EQ(1u, [websiteDataRecords count]);
519         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
520
521         done = true;
522     }];
523
524     TestWebKitAPI::Util::run(&done);
525     done = false;
526 }
527
528 TEST(ServiceWorkers, BasicDefaultSession)
529 {
530     using namespace TestWebKitAPI;
531     TCPServer server([] (int socket) {
532         NSString *format = @"HTTP/1.1 200 OK\r\n"
533         "Content-Type: %s\r\n"
534         "Content-Length: %d\r\n\r\n"
535         "%s";
536         NSString *firstResponse = [NSString stringWithFormat:format, "text/html", strlen(mainBytes), mainBytes];
537         NSString *secondResponse = [NSString stringWithFormat:format, "application/javascript", strlen(scriptBytes), scriptBytes];
538
539         TCPServer::read(socket);
540         TCPServer::write(socket, firstResponse.UTF8String, firstResponse.length);
541         TCPServer::read(socket);
542         TCPServer::write(socket, secondResponse.UTF8String, secondResponse.length);
543     });
544
545     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
546
547     // Start with a clean slate data store
548     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
549         done = true;
550     }];
551     TestWebKitAPI::Util::run(&done);
552     done = false;
553
554     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
555
556     auto messageHandler = adoptNS([[SWMessageHandler alloc] init]);
557     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
558
559     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
560
561     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
562
563     TestWebKitAPI::Util::run(&done);
564     done = false;
565
566     webView = nullptr;
567
568     [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
569         EXPECT_EQ(1u, [websiteDataRecords count]);
570         EXPECT_WK_STREQ(websiteDataRecords[0].displayName, "127.0.0.1");
571
572         done = true;
573     }];
574
575     TestWebKitAPI::Util::run(&done);
576     done = false;
577 }
578
579 @interface SWCustomUserAgentDelegate : NSObject <WKNavigationDelegate> {
580     NSString *_userAgent;
581 }
582 - (instancetype)initWithUserAgent:(NSString *)userAgent;
583 @end
584
585 @implementation SWCustomUserAgentDelegate
586
587 - (instancetype)initWithUserAgent:(NSString *)userAgent
588 {
589     self = [super init];
590     _userAgent = userAgent;
591     return self;
592 }
593
594 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
595 {
596     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
597     if (navigationAction.targetFrame.mainFrame)
598         [websitePolicies setCustomUserAgent:_userAgent];
599
600     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
601 }
602
603 @end
604
605 @interface SWUserAgentMessageHandler : NSObject <WKScriptMessageHandler> {
606 @public
607     NSString *expectedMessage;
608 }
609 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
610 @end
611
612 @implementation SWUserAgentMessageHandler
613
614 - (instancetype)initWithExpectedMessage:(NSString *)_expectedMessage
615 {
616     self = [super init];
617     expectedMessage = _expectedMessage;
618     return self;
619 }
620
621 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
622 {
623     EXPECT_WK_STREQ(expectedMessage, [message body]);
624     done = true;
625 }
626 @end
627
628 static const char* userAgentSWBytes = R"SWRESOURCE(
629
630 self.addEventListener("message", (event) => {
631     event.source.postMessage(navigator.userAgent);
632 });
633
634 )SWRESOURCE";
635
636 TEST(ServiceWorkers, UserAgentOverride)
637 {
638     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
639
640     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
641
642     // Start with a clean slate data store
643     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
644         done = true;
645     }];
646     TestWebKitAPI::Util::run(&done);
647     done = false;
648
649     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
650     [configuration setWebsiteDataStore:dataStore];
651
652     auto messageHandler = adoptNS([[SWUserAgentMessageHandler alloc] initWithExpectedMessage:@"Message from worker: Foo Custom UserAgent"]);
653     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
654
655     auto handler = adoptNS([[SWSchemes alloc] init]);
656     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
657     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", userAgentSWBytes });
658     handler->expectedUserAgent = @"Foo Custom UserAgent";
659     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
660
661     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
662
663     auto delegate = adoptNS([[SWCustomUserAgentDelegate alloc] initWithUserAgent:@"Foo Custom UserAgent"]);
664     [webView setNavigationDelegate:delegate.get()];
665
666     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
667     [webView loadRequest:request];
668
669     TestWebKitAPI::Util::run(&done);
670     done = false;
671
672     // Restore from disk.
673     webView = nullptr;
674     delegate = nullptr;
675     handler = nullptr;
676     messageHandler = nullptr;
677     configuration = nullptr;
678
679     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
680     [configuration setWebsiteDataStore:dataStore];
681
682     messageHandler = adoptNS([[SWUserAgentMessageHandler alloc] initWithExpectedMessage:@"Message from worker: Bar Custom UserAgent"]);
683     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
684
685     handler = adoptNS([[SWSchemes alloc] init]);
686     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
687     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", userAgentSWBytes });
688     handler->expectedUserAgent = @"Bar Custom UserAgent";
689     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
690
691     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
692
693     delegate = adoptNS([[SWCustomUserAgentDelegate alloc] initWithUserAgent:@"Bar Custom UserAgent"]);
694     [webView setNavigationDelegate:delegate.get()];
695
696     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
697     [webView loadRequest:request];
698
699     TestWebKitAPI::Util::run(&done);
700     done = false;
701 }
702
703 TEST(ServiceWorkers, RestoreFromDisk)
704 {
705     ASSERT(mainRegisteringWorkerBytes);
706     ASSERT(scriptBytes);
707     ASSERT(mainRegisteringAlreadyExistingWorkerBytes);
708
709     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
710
711     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
712     
713     // Start with a clean slate data store
714     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
715         done = true;
716     }];
717     TestWebKitAPI::Util::run(&done);
718     done = false;
719
720     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
721     [configuration setWebsiteDataStore:dataStore];
722
723     RetainPtr<SWMessageHandlerForRestoreFromDiskTest> messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration was successful and service worker was activated"]);
724     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
725
726     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
727     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringWorkerBytes });
728     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
729     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
730
731     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
732
733     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
734     [webView loadRequest:request];
735
736     TestWebKitAPI::Util::run(&done);
737
738     webView = nullptr;
739     configuration = nullptr;
740     messageHandler = nullptr;
741     handler = nullptr;
742
743     done = false;
744
745     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
746     [configuration setWebsiteDataStore:dataStore];
747     messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration already has an active worker"]);
748     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
749
750     handler = adoptNS([[SWSchemes alloc] init]);
751     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringAlreadyExistingWorkerBytes });
752     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
753     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
754
755     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
756
757     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
758     [webView loadRequest:request];
759
760     TestWebKitAPI::Util::run(&done);
761     done = false;
762 }
763
764 TEST(ServiceWorkers, CacheStorageRestoreFromDisk)
765 {
766     ASSERT(mainCacheStorageBytes);
767     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
768
769     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
770     
771     // Start with a clean slate data store
772     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
773         done = true;
774     }];
775
776     TestWebKitAPI::Util::run(&done);
777     done = false;
778
779     [WKWebsiteDataStore _deleteDefaultDataStoreForTesting];
780
781     auto handler = adoptNS([[SWSchemes alloc] init]);
782     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainCacheStorageBytes });
783
784     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
785     [configuration setWebsiteDataStore:dataStore];
786     auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"No cache storage data"]);
787
788     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
789     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
790
791     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
792
793     // Trigger creation of network process.
794     [webView.get().configuration.processPool _syncNetworkProcessCookies];
795
796     auto *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
797     [webView loadRequest:request];
798
799     TestWebKitAPI::Util::run(&done);
800     done = false;
801
802     webView = nullptr;
803     configuration = nullptr;
804     messageHandler = nullptr;
805
806     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
807     [configuration setWebsiteDataStore:dataStore];
808     messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"Some cache storage data: my cache"]);
809
810     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
811     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
812
813     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
814
815     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
816     [webView loadRequest:request];
817
818     TestWebKitAPI::Util::run(&done);
819     done = false;
820 }
821
822
823 TEST(ServiceWorkers, FetchAfterRestoreFromDisk)
824 {
825     ASSERT(mainForFetchTestBytes);
826     ASSERT(scriptHandlingFetchBytes);
827
828     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
829
830     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
831     
832     // Start with a clean slate data store
833     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
834         done = true;
835     }];
836     TestWebKitAPI::Util::run(&done);
837     done = false;
838
839     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
840     [configuration setWebsiteDataStore:dataStore];
841
842     RetainPtr<SWMessageHandlerForFetchTest> messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
843     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
844
845     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
846     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
847     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
848     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
849     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
850
851     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
852
853     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
854     [webView loadRequest:request];
855
856     TestWebKitAPI::Util::run(&done);
857
858     webView = nullptr;
859     configuration = nullptr;
860     messageHandler = nullptr;
861     handler = nullptr;
862
863     done = false;
864
865     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
866     [configuration setWebsiteDataStore:dataStore];
867     messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
868     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
869
870     handler = adoptNS([[SWSchemes alloc] init]);
871     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
872     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
873     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
874     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
875
876     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
877
878     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
879     [webView loadRequest:request];
880
881     TestWebKitAPI::Util::run(&done);
882     done = false;
883 }
884
885 TEST(ServiceWorkers, InterceptFirstLoadAfterRestoreFromDisk)
886 {
887     ASSERT(mainForFirstLoadInterceptTestBytes);
888     ASSERT(scriptHandlingFetchBytes);
889
890     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
891
892     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
893
894     // Start with a clean slate data store
895     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
896         done = true;
897     }];
898     TestWebKitAPI::Util::run(&done);
899     done = false;
900
901     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
902     [configuration setWebsiteDataStore:dataStore];
903
904     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
905     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
906
907     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
908     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
909     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
910     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
911
912     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
913
914     expectedMessage = "Service Worker activated";
915     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
916     [webView loadRequest:request];
917
918     TestWebKitAPI::Util::run(&done);
919
920     webView = nullptr;
921     configuration = nullptr;
922     messageHandler = nullptr;
923     handler = nullptr;
924
925     done = false;
926
927     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
928     [configuration setWebsiteDataStore:dataStore];
929     messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
930     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
931
932     handler = adoptNS([[SWSchemes alloc] init]);
933     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
934     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
935     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
936
937     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
938
939     expectedMessage = "Intercepted by worker";
940     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
941     [webView loadRequest:request];
942
943     TestWebKitAPI::Util::run(&done);
944     done = false;
945 }
946
947 TEST(ServiceWorkers, WaitForPolicyDelegate)
948 {
949     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
950
951     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
952
953     // Start with a clean slate data store
954     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
955         done = true;
956     }];
957     TestWebKitAPI::Util::run(&done);
958     done = false;
959
960     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
961     [configuration setWebsiteDataStore:dataStore];
962
963     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
964     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
965
966     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
967     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
968     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
969     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
970
971     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
972
973     // Register a service worker and activate it.
974     expectedMessage = "Service Worker activated";
975     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
976     [webView loadRequest:request];
977
978     TestWebKitAPI::Util::run(&done);
979
980     webView = nullptr;
981     configuration = nullptr;
982     messageHandler = nullptr;
983     handler = nullptr;
984
985     done = false;
986
987     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
988     [configuration setWebsiteDataStore:dataStore];
989     messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
990     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
991
992     handler = adoptNS([[SWSchemes alloc] init]);
993     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
994     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
995     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
996
997     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
998
999     // Verify service worker is intercepting load.
1000     expectedMessage = "Intercepted by worker";
1001     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1002     [webView loadRequest:request];
1003
1004     TestWebKitAPI::Util::run(&done);
1005     done = false;
1006
1007     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1008     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
1009     [webView setNavigationDelegate:delegate.get()];
1010     [webView setUIDelegate:delegate.get()];
1011
1012     shouldAccept = true;
1013     navigationFailed = false;
1014     navigationComplete = false;
1015
1016     // Verify service worker load goes well when policy delegate is ok.
1017     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1018     [webView loadRequest:request];
1019     TestWebKitAPI::Util::run(&navigationComplete);
1020
1021     EXPECT_FALSE(navigationFailed);
1022
1023     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1024     [webView setNavigationDelegate:delegate.get()];
1025     [webView setUIDelegate:delegate.get()];
1026
1027     shouldAccept = false;
1028     navigationFailed = false;
1029     navigationComplete = false;
1030
1031     // Verify service worker load fails well when policy delegate is not ok.
1032     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1033     [webView loadRequest:request];
1034     TestWebKitAPI::Util::run(&navigationComplete);
1035
1036     EXPECT_TRUE(navigationFailed);
1037 }
1038 #if WK_HAVE_C_SPI
1039
1040 void setConfigurationInjectedBundlePath(WKWebViewConfiguration* configuration)
1041 {
1042     WKRetainPtr<WKContextRef> context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
1043     configuration.processPool = (WKProcessPool *)context.get();
1044     configuration.websiteDataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
1045 }
1046
1047 @interface RegularPageMessageHandler : NSObject <WKScriptMessageHandler>
1048 @end
1049
1050 @implementation RegularPageMessageHandler
1051 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
1052 {
1053     EXPECT_TRUE([[message body] isEqualToString:@"PASS"]);
1054     done = true;
1055 }
1056 @end
1057
1058 static const char* regularPageWithoutConnectionBytes = R"SWRESOURCE(
1059 <script>
1060 var result = window.internals.hasServiceWorkerConnection() ? "FAIL" : "PASS";
1061 window.webkit.messageHandlers.regularPage.postMessage(result);
1062 </script>
1063 )SWRESOURCE";
1064
1065 static const char* regularPageWithConnectionBytes = R"SWRESOURCE(
1066 <script>
1067 var result = window.internals.hasServiceWorkerConnection() ? "PASS" : "FAIL";
1068 window.webkit.messageHandlers.regularPage.postMessage(result);
1069 </script>
1070 )SWRESOURCE";
1071
1072 TEST(ServiceWorkers, SWProcessConnectionCreation)
1073 {
1074     ASSERT(mainBytes);
1075     ASSERT(scriptBytes);
1076
1077     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1078
1079     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1080     setConfigurationInjectedBundlePath(configuration.get());
1081
1082     done = false;
1083
1084     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1085         done = true;
1086     }];
1087     TestWebKitAPI::Util::run(&done);
1088     done = false;
1089
1090     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1091         EXPECT_EQ(0u, [websiteDataRecords count]);
1092
1093         done = true;
1094     }];
1095     TestWebKitAPI::Util::run(&done);
1096     done = false;
1097
1098     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1099     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1100     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1101     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1102
1103     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1104     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1105     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1106     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1107     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1108     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1109
1110     RetainPtr<WKWebView> regularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1111     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1112     RetainPtr<WKWebView> newRegularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1113
1114     // Test that a regular page does not trigger a service worker connection to network process if there is no registered service worker.
1115     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
1116
1117     [regularPageWebView loadRequest:request];
1118     TestWebKitAPI::Util::run(&done);
1119     done = false;
1120
1121     // Test that a sw scheme page can register a service worker.
1122     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1123
1124     [webView loadRequest:request];
1125     TestWebKitAPI::Util::run(&done);
1126     done = false;
1127     webView = nullptr;
1128
1129     // Now that a service worker is registered, the regular page should have a service worker connection.
1130     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1131
1132     [regularPageWebView loadRequest:request];
1133     TestWebKitAPI::Util::run(&done);
1134     done = false;
1135     regularPageWebView = nullptr;
1136
1137     [newRegularPageWebView loadRequest:request];
1138     TestWebKitAPI::Util::run(&done);
1139     done = false;
1140     newRegularPageWebView = nullptr;
1141
1142     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1143         EXPECT_EQ(1u, [websiteDataRecords count]);
1144         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
1145
1146         done = true;
1147     }];
1148
1149     TestWebKitAPI::Util::run(&done);
1150     done = false;
1151
1152     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1153         done = true;
1154     }];
1155     TestWebKitAPI::Util::run(&done);
1156 }
1157
1158 static const char* mainBytesWithScope = R"SWRESOURCE(
1159 <script>
1160
1161 function log(msg)
1162 {
1163     window.webkit.messageHandlers.sw.postMessage(msg);
1164 }
1165
1166 navigator.serviceWorker.addEventListener("message", function(event) {
1167     log("Message from worker: " + event.data);
1168 });
1169
1170 try {
1171
1172 navigator.serviceWorker.register('/sw.js', {scope: 'whateverscope'}).then(function(reg) {
1173     reg.installing.postMessage("Hello from the web page");
1174 }).catch(function(error) {
1175     log("Registration failed with: " + error);
1176 });
1177 } catch(e) {
1178     log("Exception: " + e);
1179 }
1180
1181 </script>
1182 )SWRESOURCE";
1183
1184 TEST(ServiceWorkers, ServiceWorkerProcessCreation)
1185 {
1186     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1187
1188     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1189     setConfigurationInjectedBundlePath(configuration.get());
1190
1191     done = false;
1192
1193     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1194         done = true;
1195     }];
1196     TestWebKitAPI::Util::run(&done);
1197     done = false;
1198
1199     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1200
1201         done = true;
1202     }];
1203     TestWebKitAPI::Util::run(&done);
1204     done = false;
1205
1206     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1207     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1208     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1209     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1210
1211     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1212     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1213     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1214     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
1215     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1216     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1217     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1218
1219     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1220
1221     // Load a page that registers a service worker.
1222     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
1223
1224     [webView loadRequest:request];
1225     TestWebKitAPI::Util::run(&done);
1226     done = false;
1227     webView = nullptr;
1228
1229     // 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.
1230     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1231     setConfigurationInjectedBundlePath(newConfiguration.get());
1232     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
1233
1234     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1235     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1236     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1237
1238     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1239     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1240     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1241     [webView loadRequest:request];
1242     TestWebKitAPI::Util::run(&done);
1243     done = false;
1244
1245     // Make sure that loading the simple page did not start the service worker process.
1246     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1247
1248     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1249     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1250     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1251     [webView loadRequest:request];
1252     TestWebKitAPI::Util::run(&done);
1253     done = false;
1254
1255     // Make sure that loading this page did start the service worker process.
1256     EXPECT_EQ(3u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1257
1258     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1259         done = true;
1260     }];
1261     TestWebKitAPI::Util::run(&done);
1262     done = false;
1263 }
1264
1265 TEST(ServiceWorkers, HasServiceWorkerRegistrationBit)
1266 {
1267     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1268
1269     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1270     setConfigurationInjectedBundlePath(configuration.get());
1271
1272     done = false;
1273
1274     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1275         done = true;
1276     }];
1277     TestWebKitAPI::Util::run(&done);
1278     done = false;
1279
1280     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1281
1282         done = true;
1283     }];
1284     TestWebKitAPI::Util::run(&done);
1285     done = false;
1286
1287     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1288     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1289     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1290     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1291
1292     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1293     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1294     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1295     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
1296     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1297     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1298     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1299
1300     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1301
1302     // Load a page that registers a service worker.
1303     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
1304
1305     [webView loadRequest:request];
1306     TestWebKitAPI::Util::run(&done);
1307     done = false;
1308     webView = nullptr;
1309
1310     // Now that a sw is registered, let's create a new configuration and try loading a regular page
1311     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1312     setConfigurationInjectedBundlePath(newConfiguration.get());
1313
1314     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1315     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1316     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1317
1318     newConfiguration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1319     [newConfiguration.get().websiteDataStore _setServiceWorkerRegistrationDirectory: @"~/nonexistingfolder"];
1320
1321     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1322     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
1323     [webView loadRequest:request];
1324     TestWebKitAPI::Util::run(&done);
1325     done = false;
1326
1327     // Let's use the web site data store that has service worker and load a page.
1328     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
1329
1330     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1331     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1332     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1333     [webView loadRequest:request];
1334     TestWebKitAPI::Util::run(&done);
1335     done = false;
1336
1337     // Make sure that loading the simple page did not start the service worker process.
1338     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1339
1340     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1341         done = true;
1342     }];
1343     TestWebKitAPI::Util::run(&done);
1344     done = false;
1345 }
1346
1347 static const char* readCacheBytes = R"SWRESOURCE(
1348 <script>
1349
1350 function log(msg)
1351 {
1352     window.webkit.messageHandlers.sw.postMessage(msg);
1353 }
1354
1355 window.caches.keys().then(keys => {
1356     log(keys.length && keys[0] === "test" ? "PASS" : "FAIL");
1357 }, () => {
1358     log("FAIL");
1359 });
1360
1361 </script>
1362 )SWRESOURCE";
1363
1364 static const char* writeCacheBytes = R"SWRESOURCE(
1365 <script>
1366
1367 function log(msg)
1368 {
1369     window.webkit.messageHandlers.sw.postMessage(msg);
1370 }
1371
1372 window.caches.open("test").then(() => {
1373     log("PASS");
1374 }, () => {
1375     log("FAIL");
1376 });
1377
1378 </script>
1379 )SWRESOURCE";
1380
1381 @interface SWMessageHandlerForCacheStorage : NSObject <WKScriptMessageHandler>
1382 @end
1383
1384 @implementation SWMessageHandlerForCacheStorage
1385 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
1386 {
1387     EXPECT_WK_STREQ(@"PASS", [message body]);
1388     done = true;
1389 }
1390 @end
1391
1392 TEST(ServiceWorkers, CacheStorageInPrivateBrowsingMode)
1393 {
1394     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1395
1396     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1397
1398     auto messageHandler = adoptNS([[SWMessageHandlerForCacheStorage alloc] init]);
1399     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1400
1401     auto handler = adoptNS([[SWSchemes alloc] init]);
1402     handler->resources.set("sw://host/readCache.html", ResourceInfo { @"text/html", readCacheBytes });
1403     handler->resources.set("sw://host/writeCache.html", ResourceInfo { @"text/html", writeCacheBytes });
1404     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1405
1406     _WKWebsiteDataStoreConfiguration *dataStoreConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease];
1407     [dataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw"];
1408     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration] autorelease];
1409
1410     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1411     auto *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/writeCache.html"]];
1412     [webView loadRequest:request];
1413     TestWebKitAPI::Util::run(&done);
1414     done = false;
1415
1416     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1417     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/readCache.html"]];
1418     [webView loadRequest:request];
1419     TestWebKitAPI::Util::run(&done);
1420     done = false;
1421 }
1422
1423 static const char* serviceWorkerCacheAccessEphemeralSessionMainBytes = R"SWRESOURCE(
1424 <script>
1425 try {
1426     navigator.serviceWorker.addEventListener("message", (event) => {
1427         webkit.messageHandlers.sw.postMessage(event.data);
1428     });
1429
1430     navigator.serviceWorker.register("serviceworker-private-browsing-worker.js", { scope : "my private backyard" }).then((registration) => {
1431         activeWorker = registration.installing;
1432         activeWorker.addEventListener('statechange', () => {
1433             if (activeWorker.state === "activated") {
1434                 activeWorker.postMessage("TESTCACHE");
1435             }
1436         });
1437     });
1438 } catch (e) {
1439     webkit.messageHandlers.sw.postMessage("" + e);
1440 }
1441 </script>
1442 )SWRESOURCE";
1443
1444 static const char* serviceWorkerCacheAccessEphemeralSessionSWBytes = R"SWRESOURCE(
1445 self.addEventListener("message", (event) => {
1446     try {
1447         self.caches.keys().then((keys) => {
1448             event.source.postMessage(keys.length === 0 ? "PASS" : "FAIL: caches is not empty, got: " + JSON.stringify(keys));
1449         });
1450     } catch (e) {
1451          event.source.postMessage("" + e);
1452     }
1453 });
1454 )SWRESOURCE";
1455
1456 // Opens a cache in the default session and checks that an ephemeral service worker
1457 // does not have access to it.
1458 TEST(ServiceWorkers, ServiceWorkerCacheAccessEphemeralSession)
1459 {
1460     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1461
1462     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
1463     
1464     // Start with a clean slate data store
1465     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1466         done = true;
1467     }];
1468     TestWebKitAPI::Util::run(&done);
1469     done = false;
1470     
1471     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1472
1473     auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
1474     [configuration setProcessPool:(WKProcessPool *)context.get()];
1475     [configuration setWebsiteDataStore:dataStore];
1476     
1477     auto defaultPreferences = [configuration preferences];
1478     [defaultPreferences _setSecureContextChecksEnabled:NO];
1479     
1480     auto handler = adoptNS([[SWSchemes alloc] init]);
1481     handler->resources.set("sw://host/openCache.html", ResourceInfo { @"text/html", "foo" });
1482     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", serviceWorkerCacheAccessEphemeralSessionMainBytes });
1483     handler->resources.set("sw://host/serviceworker-private-browsing-worker.js", ResourceInfo { @"application/javascript", serviceWorkerCacheAccessEphemeralSessionSWBytes });
1484     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1485
1486     auto defaultWebView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1487     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/openCache.html"]];
1488     [defaultWebView synchronouslyLoadRequest:request];
1489     
1490     bool openedCache = false;
1491     [defaultWebView evaluateJavaScript:@"self.caches.open('test');" completionHandler: [&] (id innerText, NSError *error) {
1492         openedCache = true;
1493     }];
1494     TestWebKitAPI::Util::run(&openedCache);
1495
1496     _WKWebsiteDataStoreConfiguration *nonPersistentConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease];
1497     [nonPersistentConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw"];
1498     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:nonPersistentConfiguration] autorelease];
1499
1500     auto messageHandler = adoptNS([[SWMessageHandlerForCacheStorage alloc] init]);
1501     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1502
1503     auto ephemeralWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1504     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1505
1506     [ephemeralWebView loadRequest:request];
1507     TestWebKitAPI::Util::run(&done);
1508     done = false;
1509 }
1510
1511 static const char* differentSessionsUseDifferentRegistrationsMainBytes = R"SWRESOURCE(
1512 <script>
1513 try {
1514     navigator.serviceWorker.register("empty-worker.js", { scope : "/test" }).then((registration) => {
1515         activeWorker = registration.installing;
1516         activeWorker.addEventListener('statechange', () => {
1517             if (activeWorker.state === "activated")
1518                 webkit.messageHandlers.sw.postMessage("PASS");
1519         });
1520     });
1521 } catch (e) {
1522     webkit.messageHandlers.sw.postMessage("" + e);
1523 }
1524 </script>
1525 )SWRESOURCE";
1526
1527 static const char* defaultPageMainBytes = R"SWRESOURCE(
1528 <script>
1529 async function getResult()
1530 {
1531     var result = await internals.hasServiceWorkerRegistration("/test");
1532     window.webkit.messageHandlers.sw.postMessage(result ? "PASS" : "FAIL");
1533 }
1534 getResult();
1535 </script>
1536 )SWRESOURCE";
1537
1538 static const char* privatePageMainBytes = R"SWRESOURCE(
1539 <script>
1540 async function getResult()
1541 {
1542     var result = await internals.hasServiceWorkerRegistration("/test");
1543     window.webkit.messageHandlers.sw.postMessage(result ? "FAIL" : "PASS");
1544 }
1545 getResult();
1546 </script>
1547 )SWRESOURCE";
1548
1549 TEST(ServiceWorkers, DifferentSessionsUseDifferentRegistrations)
1550 {
1551     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1552
1553     // Start with a clean slate data store
1554     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1555         done = true;
1556     }];
1557     TestWebKitAPI::Util::run(&done);
1558     done = false;
1559     
1560     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1561     setConfigurationInjectedBundlePath(configuration.get());
1562     
1563     auto messageHandler = adoptNS([[SWMessageHandlerForCacheStorage alloc] init]);
1564     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1565     
1566     auto handler = adoptNS([[SWSchemes alloc] init]);
1567     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", differentSessionsUseDifferentRegistrationsMainBytes });
1568     handler->resources.set("sw://host/main2.html", ResourceInfo { @"text/html", defaultPageMainBytes });
1569     handler->resources.set("sw://host/empty-worker.js", ResourceInfo { @"application/javascript", "" });
1570     handler->resources.set("sw://host/private.html", ResourceInfo { @"text/html", privatePageMainBytes });
1571     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1572
1573     auto defaultWebView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1574     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1575     [defaultWebView synchronouslyLoadRequest:request];
1576     
1577     TestWebKitAPI::Util::run(&done);
1578     done = false;
1579     
1580     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main2.html"]];
1581     [defaultWebView synchronouslyLoadRequest:request];
1582     
1583     TestWebKitAPI::Util::run(&done);
1584     done = false;
1585     
1586     configuration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1587     auto ephemeralWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1588     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/private.html"]];
1589     [ephemeralWebView synchronouslyLoadRequest:request];
1590
1591     TestWebKitAPI::Util::run(&done);
1592     done = false;
1593 }
1594
1595 static const char* regularPageGrabbingCacheStorageDirectory = R"SWRESOURCE(
1596 <script>
1597 async function getResult()
1598 {
1599     var result = await window.internals.cacheStorageEngineRepresentation();
1600     window.webkit.messageHandlers.sw.postMessage(result);
1601 }
1602 getResult();
1603 </script>
1604 )SWRESOURCE";
1605
1606 @interface DirectoryPageMessageHandler : NSObject <WKScriptMessageHandler>
1607 @end
1608
1609 @implementation DirectoryPageMessageHandler
1610 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
1611 {
1612     retrievedString = [message body];
1613     done = true;
1614 }
1615 @end
1616
1617 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageDefaultDirectories)
1618 {
1619     ASSERT(mainBytes);
1620     ASSERT(scriptBytes);
1621
1622     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1623
1624     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
1625
1626     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1627     [configuration setWebsiteDataStore:dataStore];
1628     setConfigurationInjectedBundlePath(configuration.get());
1629
1630     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1631     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1632
1633     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1634     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1635     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1636     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1637     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1638
1639     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1640     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1641
1642     [webView loadRequest:request];
1643     TestWebKitAPI::Util::run(&done);
1644     done = false;
1645     while (![[configuration websiteDataStore] _hasRegisteredServiceWorker])
1646         TestWebKitAPI::Util::spinRunLoop(0.1);
1647
1648     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1649
1650     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1651     [webView loadRequest:request];
1652     TestWebKitAPI::Util::run(&done);
1653     done = false;
1654     EXPECT_TRUE(retrievedString.contains("/Caches/TestWebKitAPI/WebKit/CacheStorage"));
1655
1656     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1657         done = true;
1658     }];
1659     TestWebKitAPI::Util::run(&done);
1660     done = false;
1661 }
1662
1663 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageSpecificDirectories)
1664 {
1665     ASSERT(mainBytes);
1666     ASSERT(scriptBytes);
1667
1668     [WKWebsiteDataStore defaultDataStore];
1669     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1670
1671     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
1672
1673     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1674     [configuration setWebsiteDataStore:dataStore];
1675
1676     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1677     setConfigurationInjectedBundlePath(configuration.get());
1678     auto websiteDataStore = [configuration websiteDataStore];
1679     [websiteDataStore _setCacheStorageDirectory:@"/var/tmp"];
1680     [websiteDataStore _setServiceWorkerRegistrationDirectory:@"/var/tmp"];
1681
1682     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1683     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1684
1685     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1686     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1687     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1688     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1689     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1690
1691     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1692
1693     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1694     [webView loadRequest:request];
1695     TestWebKitAPI::Util::run(&done);
1696     done = false;
1697     while (![websiteDataStore _hasRegisteredServiceWorker])
1698         TestWebKitAPI::Util::spinRunLoop(0.1);
1699
1700     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1701     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1702
1703     [webView loadRequest:request];
1704     TestWebKitAPI::Util::run(&done);
1705     done = false;
1706     EXPECT_TRUE(retrievedString.contains("\"path\": \"/var/tmp\""));
1707
1708     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1709         done = true;
1710     }];
1711     TestWebKitAPI::Util::run(&done);
1712     done = false;
1713 }
1714
1715 #endif // WK_HAVE_C_SPI
1716
1717 TEST(ServiceWorkers, NonDefaultSessionID)
1718 {
1719     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1720
1721     NSURL *serviceWorkersPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ServiceWorkers/" stringByExpandingTildeInPath] isDirectory:YES];
1722     [[NSFileManager defaultManager] removeItemAtURL:serviceWorkersPath error:nil];
1723     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1724     NSURL *idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
1725     [[NSFileManager defaultManager] removeItemAtURL:idbPath error:nil];
1726     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
1727
1728     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1729     RetainPtr<_WKWebsiteDataStoreConfiguration> websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
1730     websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = serviceWorkersPath;
1731     websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath;
1732     [websiteDataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw"];
1733
1734     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
1735
1736     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
1737     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1738
1739     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1740     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytesForSessionIDTest });
1741     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytesForSessionIDTest });
1742     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1743
1744     expectedMessage = "PASS: activation successful";
1745     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1746
1747     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1748     [webView loadRequest:request];
1749
1750     TestWebKitAPI::Util::run(&done);
1751     done = false;
1752
1753     [webView _close];
1754     webView = nullptr;
1755
1756     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1757
1758     [configuration.get().websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1759         EXPECT_EQ(1u, [websiteDataRecords count]);
1760         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
1761
1762         done = true;
1763     }];
1764
1765     TestWebKitAPI::Util::run(&done);
1766     done = false;
1767 }
1768
1769 void waitUntilServiceWorkerProcessCount(WKProcessPool *processPool, unsigned processCount)
1770 {
1771     do {
1772         if (processPool._serviceWorkerProcessCount == processCount)
1773             return;
1774         TestWebKitAPI::Util::spinRunLoop(1);
1775     } while (true);
1776 }
1777
1778 TEST(ServiceWorkers, ProcessPerSite)
1779 {
1780     ASSERT(mainBytes);
1781     ASSERT(scriptBytes);
1782
1783     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1784
1785     // Normally, service workers get terminated several seconds after their clients are gone.
1786     // Disable this delay for the purpose of testing.
1787     _WKWebsiteDataStoreConfiguration *dataStoreConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease];
1788     dataStoreConfiguration.serviceWorkerProcessTerminationDelayEnabled = NO;
1789     [dataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw1"];
1790     [dataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw2"];
1791
1792     auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration]);
1793     
1794     // Start with a clean slate data store
1795     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1796         done = true;
1797     }];
1798     TestWebKitAPI::Util::run(&done);
1799     done = false;
1800
1801     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1802     configuration.get().websiteDataStore = dataStore.get();
1803
1804     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1805     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1806
1807     RetainPtr<SWSchemes> handler1 = adoptNS([[SWSchemes alloc] init]);
1808     handler1->resources.set("sw1://host/main.html", ResourceInfo { @"text/html", mainBytes });
1809     handler1->resources.set("sw1://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1810     handler1->resources.set("sw1://host2/main.html", ResourceInfo { @"text/html", mainBytes });
1811     handler1->resources.set("sw1://host2/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1812     [configuration setURLSchemeHandler:handler1.get() forURLScheme:@"sw1"];
1813
1814     RetainPtr<SWSchemes> handler2 = adoptNS([[SWSchemes alloc] init]);
1815     handler2->resources.set("sw2://host/main.html", ResourceInfo { @"text/html", mainBytes });
1816     handler2->resources.set("sw2://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1817     [configuration setURLSchemeHandler:handler2.get() forURLScheme:@"sw2"];
1818
1819     WKProcessPool *processPool = configuration.get().processPool;
1820
1821     RetainPtr<WKWebView> webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1822
1823     NSURLRequest *request1 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host/main.html"]];
1824     [webView1 loadRequest:request1];
1825
1826     TestWebKitAPI::Util::run(&done);
1827     done = false;
1828
1829     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1830
1831     RetainPtr<WKWebView> webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1832     [webView2 loadRequest:request1];
1833
1834     TestWebKitAPI::Util::run(&done);
1835     done = false;
1836
1837     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1838
1839     RetainPtr<WKWebView> webView3 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1840     NSURLRequest *request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw2://host/main.html"]];
1841     [webView3 loadRequest:request2];
1842
1843     TestWebKitAPI::Util::run(&done);
1844     done = false;
1845
1846     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1847
1848     RetainPtr<WKWebView> webView4 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1849     NSURLRequest *request3 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host2/main.html"]];
1850     [webView4 loadRequest:request3];
1851
1852     TestWebKitAPI::Util::run(&done);
1853     done = false;
1854
1855     EXPECT_EQ(2U, processPool._serviceWorkerProcessCount);
1856
1857     NSURLRequest *aboutBlankRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]];
1858     [webView4 loadRequest:aboutBlankRequest];
1859
1860     waitUntilServiceWorkerProcessCount(processPool, 1);
1861     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1862
1863     [webView2 loadRequest:aboutBlankRequest];
1864     TestWebKitAPI::Util::spinRunLoop(10);
1865     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1866
1867     [webView1 loadRequest:aboutBlankRequest];
1868     [webView3 loadRequest:aboutBlankRequest];
1869     waitUntilServiceWorkerProcessCount(processPool, 0);
1870     EXPECT_EQ(0U, processPool._serviceWorkerProcessCount);
1871 }
1872
1873 TEST(ServiceWorkers, ParallelProcessLaunch)
1874 {
1875     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1876
1877     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw1");
1878
1879     // Start with a clean slate data store
1880     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1881         done = true;
1882     }];
1883     TestWebKitAPI::Util::run(&done);
1884     done = false;
1885
1886     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1887     [configuration setWebsiteDataStore:dataStore];
1888
1889     auto messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1890     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1891
1892     auto handler1 = adoptNS([[SWSchemes alloc] init]);
1893     handler1->resources.set("sw1://host/main.html", ResourceInfo { @"text/html", mainBytes });
1894     handler1->resources.set("sw1://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1895     handler1->resources.set("sw1://host2/main.html", ResourceInfo { @"text/html", mainBytes });
1896     handler1->resources.set("sw1://host2/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1897     [configuration setURLSchemeHandler:handler1.get() forURLScheme:@"sw1"];
1898
1899     auto *processPool = configuration.get().processPool;
1900
1901     auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1902     NSURLRequest *request1 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host/main.html"]];
1903
1904     auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1905     NSURLRequest *request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host2/main.html"]];
1906
1907     [webView1 loadRequest:request1];
1908     [webView2 loadRequest:request2];
1909
1910     waitUntilServiceWorkerProcessCount(processPool, 2);
1911 }
1912
1913 TEST(ServiceWorkers, ThrottleCrash)
1914 {
1915     ASSERT(mainBytes);
1916     ASSERT(scriptBytes);
1917
1918     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1919
1920     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw1");
1921
1922     // Start with a clean slate data store
1923     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1924         done = true;
1925     }];
1926     TestWebKitAPI::Util::run(&done);
1927     done = false;
1928
1929     auto messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1930
1931     auto handler = adoptNS([[SWSchemes alloc] init]);
1932     handler->resources.set("sw1://host/main.html", ResourceInfo { @"text/html", mainBytes });
1933     handler->resources.set("sw1://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1934
1935     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
1936     [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
1937         didFinishNavigation = true;
1938     }];
1939
1940     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1941     [configuration setWebsiteDataStore:dataStore];
1942 #if PLATFORM(MAC)
1943     [[configuration preferences] _setAppNapEnabled:YES];
1944 #endif
1945     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1946     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"sw1"];
1947
1948     auto *processPool = configuration.get().processPool;
1949
1950     auto webView1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get() addToWindow: YES]);
1951     [webView1 setNavigationDelegate:navigationDelegate.get()];
1952
1953     // Let's make it so that webView1 be app nappable after loading is completed.
1954     [webView1.get().window resignKeyWindow];
1955 #if PLATFORM(MAC)
1956     [webView1.get().window orderOut:nil];
1957 #endif
1958
1959     auto *request1 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host/main.html"]];
1960     [webView1 loadRequest:request1];
1961
1962     didFinishNavigation = false;
1963     TestWebKitAPI::Util::run(&didFinishNavigation);
1964
1965     auto webView2Configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1966 #if PLATFORM(MAC)
1967     [[webView2Configuration preferences] _setAppNapEnabled:NO];
1968 #endif
1969     [webView2Configuration setProcessPool:processPool];
1970     [webView2Configuration setWebsiteDataStore:dataStore];
1971     [[webView2Configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1972     [webView2Configuration setURLSchemeHandler:handler.get() forURLScheme:@"sw1"];
1973     webView2Configuration.get()._relatedWebView = webView1.get();
1974
1975     auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webView2Configuration.get()]);
1976     [webView2 setNavigationDelegate:navigationDelegate.get()];
1977
1978     [webView2 loadRequest:request1];
1979
1980     didFinishNavigation = false;
1981     TestWebKitAPI::Util::run(&didFinishNavigation);
1982 }
1983
1984 TEST(ServiceWorkers, LoadData)
1985 {
1986     ASSERT(mainBytes);
1987     ASSERT(scriptBytes);
1988
1989     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1990
1991     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
1992
1993     // Start with a clean slate data store
1994     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1995         done = true;
1996     }];
1997     TestWebKitAPI::Util::run(&done);
1998     done = false;
1999
2000     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
2001     [configuration setWebsiteDataStore:dataStore];
2002
2003     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
2004     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
2005
2006     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
2007     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
2008     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2009     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
2010
2011     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2012
2013     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
2014     [webView setNavigationDelegate:delegate.get()];
2015     [webView setUIDelegate:delegate.get()];
2016
2017     done = false;
2018
2019     // Normal load to get SW registered.
2020     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
2021     [webView loadRequest:request];
2022
2023     TestWebKitAPI::Util::run(&done);
2024     done = false;
2025
2026     // Now try a data load.
2027     NSData *data = [NSData dataWithBytes:mainBytes length:strlen(mainBytes)];
2028     [webView loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@"sw://host/main.html"]];
2029
2030     TestWebKitAPI::Util::run(&done);
2031     done = false;
2032 }
2033
2034 TEST(ServiceWorkers, RestoreFromDiskNonDefaultStore)
2035 {
2036     ASSERT(mainRegisteringWorkerBytes);
2037     ASSERT(scriptBytes);
2038     ASSERT(mainRegisteringAlreadyExistingWorkerBytes);
2039
2040     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
2041
2042     NSURL *swDBPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ServiceWorkers/" stringByExpandingTildeInPath]];
2043     [[NSFileManager defaultManager] removeItemAtURL:swDBPath error:nil];
2044     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
2045     [[NSFileManager defaultManager] createDirectoryAtURL:swDBPath withIntermediateDirectories:YES attributes:nil error:nil];
2046     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
2047
2048     // We protect the process pool so that it outlives the WebsiteDataStore.
2049     RetainPtr<WKProcessPool> protectedProcessPool;
2050
2051     @autoreleasepool {
2052         auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
2053
2054         auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
2055         websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swDBPath;
2056         [websiteDataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw"];
2057         auto nonDefaultDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
2058         configuration.get().websiteDataStore = nonDefaultDataStore.get();
2059
2060         auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration was successful and service worker was activated"]);
2061         [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
2062
2063         auto handler = adoptNS([[SWSchemes alloc] init]);
2064         handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringWorkerBytes });
2065         handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2066         [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
2067
2068         auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2069         protectedProcessPool = webView.get().configuration.processPool;
2070
2071         [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]]];
2072
2073         TestWebKitAPI::Util::run(&done);
2074         done = false;
2075
2076         [webView.get().configuration.processPool _terminateServiceWorkerProcesses];
2077     }
2078
2079     @autoreleasepool {
2080         auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
2081
2082         auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
2083         websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swDBPath;
2084         [websiteDataStoreConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw"];
2085         auto nonDefaultDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
2086         configuration.get().websiteDataStore = nonDefaultDataStore.get();
2087
2088         auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration already has an active worker"]);
2089         [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
2090
2091         auto handler = adoptNS([[SWSchemes alloc] init]);
2092         handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringAlreadyExistingWorkerBytes });
2093         handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2094         [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
2095
2096         auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2097
2098         [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]]];
2099
2100         TestWebKitAPI::Util::run(&done);
2101         done = false;
2102     }
2103 }
2104
2105 TEST(ServiceWorkers, SuspendNetworkProcess)
2106 {
2107     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
2108
2109     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw");
2110     
2111     // Start with a clean slate data store
2112     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
2113         done = true;
2114     }];
2115     TestWebKitAPI::Util::run(&done);
2116     done = false;
2117
2118     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
2119     [configuration setWebsiteDataStore:dataStore];
2120
2121     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
2122     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
2123
2124     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
2125     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
2126     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2127     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
2128
2129     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2130
2131     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
2132     [webView setNavigationDelegate:delegate.get()];
2133     [webView setUIDelegate:delegate.get()];
2134
2135     done = false;
2136
2137     // Normal load to get SW registered.
2138     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
2139     [webView loadRequest:request];
2140
2141     TestWebKitAPI::Util::run(&done);
2142     done = false;
2143
2144     auto store = [configuration websiteDataStore];
2145     auto path = store._serviceWorkerRegistrationDirectory;
2146
2147     NSURL* directory = [NSURL fileURLWithPath:path isDirectory:YES];
2148     NSURL *swDBPath = [directory URLByAppendingPathComponent:@"ServiceWorkerRegistrations-4.sqlite3"];
2149
2150     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
2151
2152     [ webView.get().configuration.processPool _sendNetworkProcessWillSuspendImminently];
2153
2154     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
2155
2156     [ webView.get().configuration.processPool _sendNetworkProcessDidResume];
2157
2158     [webView loadRequest:request];
2159     TestWebKitAPI::Util::run(&done);
2160     done = false;
2161
2162     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
2163 }
2164
2165 TEST(WebKit, ServiceWorkerDatabaseWithRecordsTableButUnexpectedSchema)
2166 {
2167     // Copy the baked database files to the database directory
2168     NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"BadServiceWorkerRegistrations-4" withExtension:@"sqlite3" subdirectory:@"TestWebKitAPI.resources"];
2169
2170     NSURL *swPath = [NSURL fileURLWithPath:[@"~/Library/Caches/TestWebKitAPI/WebKit/ServiceWorkers/" stringByExpandingTildeInPath]];
2171     [[NSFileManager defaultManager] removeItemAtURL:swPath error:nil];
2172     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:swPath.path]);
2173
2174     [[NSFileManager defaultManager] createDirectoryAtURL:swPath withIntermediateDirectories:YES attributes:nil error:nil];
2175     [[NSFileManager defaultManager] copyItemAtURL:url1 toURL:[swPath URLByAppendingPathComponent:@"ServiceWorkerRegistrations-4.sqlite3"] error:nil];
2176
2177     auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
2178     websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swPath;
2179     auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
2180
2181     // Fetch SW records
2182     auto websiteDataTypes = adoptNS([[NSSet alloc] initWithArray:@[WKWebsiteDataTypeServiceWorkerRegistrations]]);
2183     static bool readyToContinue;
2184     [dataStore fetchDataRecordsOfTypes:websiteDataTypes.get() completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
2185         EXPECT_EQ(0U, dataRecords.count);
2186         readyToContinue = true;
2187     }];
2188     TestWebKitAPI::Util::run(&readyToContinue);
2189     readyToContinue = false;
2190 }
2191
2192 TEST(ServiceWorkers, ProcessPerSession)
2193 {
2194     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
2195
2196     auto* dataStore = dataStoreWithRegisteredServiceWorkerScheme(@"sw1");
2197     
2198     // Start with a clean slate data store
2199     [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
2200         done = true;
2201     }];
2202     TestWebKitAPI::Util::run(&done);
2203     done = false;
2204
2205     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
2206     [configuration setWebsiteDataStore:dataStore];
2207
2208     auto messageHandler = adoptNS([[SWMessageHandler alloc] init]);
2209     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
2210
2211     auto handler1 = adoptNS([[SWSchemes alloc] init]);
2212     handler1->resources.set("sw1://host/main.html", ResourceInfo { @"text/html", mainBytes });
2213     handler1->resources.set("sw1://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2214     handler1->resources.set("sw1://host2/main.html", ResourceInfo { @"text/html", mainBytes });
2215     handler1->resources.set("sw1://host2/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
2216     [configuration setURLSchemeHandler:handler1.get() forURLScheme:@"sw1"];
2217
2218     WKProcessPool *processPool = configuration.get().processPool;
2219
2220     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host/main.html"]];
2221
2222     configuration.get().websiteDataStore = dataStore;
2223
2224     auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2225     [webView1 loadRequest:request];
2226     TestWebKitAPI::Util::run(&done);
2227     done = false;
2228
2229     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
2230
2231     _WKWebsiteDataStoreConfiguration *nonPersistentConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease];
2232     [nonPersistentConfiguration registerURLSchemeServiceWorkersCanHandleForTesting:@"sw1"];
2233     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:nonPersistentConfiguration] autorelease];
2234
2235     auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
2236     [webView2 loadRequest:request];
2237     TestWebKitAPI::Util::run(&done);
2238     done = false;
2239
2240     EXPECT_EQ(2U, processPool._serviceWorkerProcessCount);
2241 }