Close service worker database on network process suspension
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / ServiceWorkerBasic.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "Test.h"
30 #import <WebKit/WKPreferencesPrivate.h>
31 #import <WebKit/WKProcessPoolPrivate.h>
32 #import <WebKit/WKURLSchemeHandler.h>
33 #import <WebKit/WKURLSchemeTaskPrivate.h>
34 #import <WebKit/WKWebViewConfigurationPrivate.h>
35 #import <WebKit/WKWebsiteDataStorePrivate.h>
36 #import <WebKit/WKWebsiteDataStoreRef.h>
37 #import <WebKit/WebKit.h>
38 #import <WebKit/_WKExperimentalFeature.h>
39 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
40 #import <WebKit/_WKWebsitePolicies.h>
41 #import <wtf/Deque.h>
42 #import <wtf/HashMap.h>
43 #import <wtf/RetainPtr.h>
44 #import <wtf/Vector.h>
45 #import <wtf/text/StringHash.h>
46 #import <wtf/text/WTFString.h>
47
48 struct ResourceInfo {
49     RetainPtr<NSString> mimeType;
50     const char* data;
51 };
52
53 static bool done;
54
55 static String expectedMessage;
56 static String retrievedString;
57
58 @interface SWMessageHandler : NSObject <WKScriptMessageHandler>
59 @end
60
61 @implementation SWMessageHandler
62 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
63 {
64     EXPECT_WK_STREQ(@"Message from worker: ServiceWorker received: Hello from the web page", [message body]);
65     done = true;
66 }
67 @end
68
69 @interface SWMessageHandlerForFetchTest : NSObject <WKScriptMessageHandler>
70 @end
71
72 @implementation SWMessageHandlerForFetchTest
73 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
74 {
75     EXPECT_TRUE([[message body] isEqualToString:@"Intercepted by worker"]);
76     done = true;
77 }
78 @end
79
80 @interface SWMessageHandlerForRestoreFromDiskTest : NSObject <WKScriptMessageHandler> {
81     NSString *_expectedMessage;
82 }
83 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
84 @end
85
86 @interface SWMessageHandlerWithExpectedMessage : NSObject <WKScriptMessageHandler>
87 @end
88
89 @implementation SWMessageHandlerWithExpectedMessage
90 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
91 {
92     EXPECT_TRUE([[message body] isEqualToString:expectedMessage]);
93     done = true;
94 }
95 @end
96
97 @implementation SWMessageHandlerForRestoreFromDiskTest
98
99 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage
100 {
101     if (!(self = [super init]))
102         return nil;
103
104     _expectedMessage = expectedMessage;
105
106     return self;
107 }
108
109 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
110 {
111     EXPECT_TRUE([[message body] isEqualToString:_expectedMessage]);
112     done = true;
113 }
114 @end
115
116 @interface SWSchemes : NSObject <WKURLSchemeHandler> {
117 @public
118     HashMap<String, ResourceInfo> resources;
119     NSString *expectedUserAgent;
120 }
121
122 -(size_t)handledRequests;
123 @end
124
125 @implementation SWSchemes {
126     size_t _handledRequests;
127 }
128
129 -(size_t)handledRequests
130 {
131     return _handledRequests;
132 }
133
134 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
135 {
136     if (expectedUserAgent)
137         EXPECT_WK_STREQ(expectedUserAgent, [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
138
139     auto entry = resources.find([task.request.URL absoluteString]);
140     if (entry == resources.end()) {
141         NSLog(@"Did not find resource entry for URL %@", task.request.URL);
142         return;
143     }
144
145     ++_handledRequests;
146     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]);
147     [task didReceiveResponse:response.get()];
148
149     [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]];
150     [task didFinish];
151 }
152
153 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
154 {
155 }
156
157 @end
158
159 static bool shouldAccept = true;
160 static bool navigationComplete = false;
161 static bool navigationFailed = false;
162
163 @interface TestSWAsyncNavigationDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
164 @end
165
166 @implementation TestSWAsyncNavigationDelegate
167
168 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
169 {
170     navigationComplete = true;
171 }
172
173 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
174 {
175     navigationFailed = true;
176     navigationComplete = true;
177 }
178
179 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
180 {
181     navigationFailed = true;
182     navigationComplete = true;
183 }
184
185 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
186 {
187     decisionHandler(WKNavigationActionPolicyAllow);
188 }
189
190 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
191 {
192     int64_t deferredWaitTime = 100 * NSEC_PER_MSEC;
193     dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, deferredWaitTime);
194     dispatch_after(when, dispatch_get_main_queue(), ^{
195         decisionHandler(shouldAccept ? WKNavigationResponsePolicyAllow : WKNavigationResponsePolicyCancel);
196     });
197 }
198 @end
199
200 static const char* mainCacheStorageBytes = R"SWRESOURCE(
201 <script>
202
203 function log(msg)
204 {
205     window.webkit.messageHandlers.sw.postMessage(msg);
206 }
207
208 async function doTest()
209 {
210     const keys = await window.caches.keys();
211     if (!keys.length) {
212         const cache = await window.caches.open("my cache");
213         log("No cache storage data");
214         return;
215     }
216     if (keys.length !== 1) {
217         log("Unexpected cache number");
218         return;
219     }
220     log("Some cache storage data: " + keys[0]);
221 }
222 doTest();
223
224 </script>
225 )SWRESOURCE";
226
227 static const char* mainBytes = R"SWRESOURCE(
228 <script>
229
230 function log(msg)
231 {
232     window.webkit.messageHandlers.sw.postMessage(msg);
233 }
234
235 navigator.serviceWorker.addEventListener("message", function(event) {
236     log("Message from worker: " + event.data);
237 });
238
239 try {
240
241 navigator.serviceWorker.register('/sw.js').then(function(reg) {
242     worker = reg.installing ? reg.installing : reg.active;
243     worker.postMessage("Hello from the web page");
244 }).catch(function(error) {
245     log("Registration failed with: " + error);
246 });
247 } catch(e) {
248     log("Exception: " + e);
249 }
250
251 </script>
252 )SWRESOURCE";
253
254 static const char* scriptBytes = R"SWRESOURCE(
255
256 self.addEventListener("message", (event) => {
257     event.source.postMessage("ServiceWorker received: " + event.data);
258 });
259
260 )SWRESOURCE";
261
262 static const char* mainForFetchTestBytes = R"SWRESOURCE(
263 <html>
264 <body>
265 <script>
266 function log(msg)
267 {
268     window.webkit.messageHandlers.sw.postMessage(msg);
269 }
270
271 try {
272
273 function addFrame()
274 {
275     frame = document.createElement('iframe');
276     frame.src = "/test.html";
277     frame.onload = function() { window.webkit.messageHandlers.sw.postMessage(frame.contentDocument.body.innerHTML); }
278     document.body.appendChild(frame);
279 }
280
281 navigator.serviceWorker.register('/sw.js').then(function(reg) {
282     if (reg.active) {
283         addFrame();
284         return;
285     }
286     worker = reg.installing;
287     worker.addEventListener('statechange', function() {
288         if (worker.state == 'activated')
289             addFrame();
290     });
291 }).catch(function(error) {
292     log("Registration failed with: " + error);
293 });
294 } catch(e) {
295     log("Exception: " + e);
296 }
297
298 </script>
299 </body>
300 </html>
301 )SWRESOURCE";
302
303 static const char* scriptHandlingFetchBytes = R"SWRESOURCE(
304
305 self.addEventListener("fetch", (event) => {
306     if (event.request.url.indexOf("test.html") !== -1) {
307         event.respondWith(new Response(new Blob(['Intercepted by worker'], {type: 'text/html'})));
308     }
309 });
310
311 )SWRESOURCE";
312
313 static const char* scriptInterceptingFirstLoadBytes = R"SWRESOURCE(
314
315 self.addEventListener("fetch", (event) => {
316     if (event.request.url.indexOf("main.html") !== -1) {
317         event.respondWith(new Response(new Blob(['Intercepted by worker <script>window.webkit.messageHandlers.sw.postMessage(\'Intercepted by worker\');</script>'], {type: 'text/html'})));
318     }
319 });
320
321 )SWRESOURCE";
322
323 static const char* mainForFirstLoadInterceptTestBytes = R"SWRESOURCE(
324  <html>
325 <body>
326 <script>
327 function log(msg)
328 {
329     window.webkit.messageHandlers.sw.postMessage(msg);
330 }
331
332 try {
333
334 navigator.serviceWorker.register('/sw.js').then(function(reg) {
335     if (reg.active) {
336         window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
337         return;
338     }
339
340     worker = reg.installing;
341     worker.addEventListener('statechange', function() {
342         if (worker.state == 'activated')
343             window.webkit.messageHandlers.sw.postMessage('Service Worker activated');
344     });
345 }).catch(function(error) {
346     log("Registration failed with: " + error);
347 });
348 } catch(e) {
349     log("Exception: " + e);
350 }
351
352 </script>
353 </body>
354 </html>
355 )SWRESOURCE";
356
357 static const char* testBytes = R"SWRESOURCE(
358 <body>
359 NOT intercepted by worker
360 </body>
361 )SWRESOURCE";
362
363 static const char* mainRegisteringWorkerBytes = R"SWRESOURCE(
364 <script>
365 try {
366 function log(msg)
367 {
368     window.webkit.messageHandlers.sw.postMessage(msg);
369 }
370
371 navigator.serviceWorker.register('/sw.js').then(function(reg) {
372     if (reg.active) {
373         log("FAIL: Registration already has an active worker");
374         return;
375     }
376     worker = reg.installing;
377     worker.addEventListener('statechange', function() {
378         if (worker.state == 'activated')
379             log("PASS: Registration was successful and service worker was activated");
380     });
381 }).catch(function(error) {
382     log("Registration failed with: " + error);
383 });
384 } catch(e) {
385     log("Exception: " + e);
386 }
387 </script>
388 )SWRESOURCE";
389
390 static const char* mainRegisteringAlreadyExistingWorkerBytes = R"SWRESOURCE(
391 <script>
392 try {
393 function log(msg)
394 {
395     window.webkit.messageHandlers.sw.postMessage(msg);
396 }
397
398 navigator.serviceWorker.register('/sw.js').then(function(reg) {
399     if (reg.installing) {
400         log("FAIL: Registration had an installing worker");
401         return;
402     }
403     if (reg.active) {
404         if (reg.active.state == "activated")
405             log("PASS: Registration already has an active worker");
406         else
407             log("FAIL: Registration has an active worker but its state is not activated");
408     } else
409         log("FAIL: Registration does not have an active worker");
410 }).catch(function(error) {
411     log("Registration failed with: " + error);
412 });
413 } catch(e) {
414     log("Exception: " + e);
415 }
416 </script>
417 )SWRESOURCE";
418
419 static const char* mainBytesForSessionIDTest = R"SWRESOURCE(
420 <script>
421
422 function log(msg)
423 {
424     window.webkit.messageHandlers.sw.postMessage(msg);
425 }
426
427 navigator.serviceWorker.addEventListener("message", function(event) {
428     log(event.data);
429 });
430
431 try {
432
433 navigator.serviceWorker.register('/sw.js').then(function(reg) {
434     worker = reg.installing;
435     worker.addEventListener('statechange', function() {
436         if (worker.state == 'activated')
437             worker.postMessage("TEST");
438     });
439 }).catch(function(error) {
440     log("Registration failed with: " + error);
441 });
442 } catch(e) {
443     log("Exception: " + e);
444 }
445
446 </script>
447 )SWRESOURCE";
448
449 static const char* scriptBytesForSessionIDTest = R"SWRESOURCE(
450
451 var wasActivated = false;
452
453 self.addEventListener("activate", event => {
454     event.waitUntil(clients.claim().then( () => {
455         wasActivated = true;
456     }));
457 });
458
459 self.addEventListener("message", (event) => {
460     if (wasActivated && registration.active)
461         event.source.postMessage("PASS: activation successful");
462     else
463         event.source.postMessage("FAIL: failed to activate");
464 });
465
466 )SWRESOURCE";
467
468 TEST(ServiceWorkers, Basic)
469 {
470     ASSERT(mainBytes);
471     ASSERT(scriptBytes);
472
473     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
474
475     // Start with a clean slate data store
476     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
477         done = true;
478     }];
479     TestWebKitAPI::Util::run(&done);
480     done = false;
481
482     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
483
484     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
485     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
486
487     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
488     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
489     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
490     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
491
492     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
493     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
494
495     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
496     [webView loadRequest:request];
497
498     TestWebKitAPI::Util::run(&done);
499     done = false;
500
501     webView = nullptr;
502
503     [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
504         EXPECT_EQ(1u, [websiteDataRecords count]);
505         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
506
507         done = true;
508     }];
509
510     TestWebKitAPI::Util::run(&done);
511     done = false;
512 }
513
514 @interface SWCustomUserAgentDelegate : NSObject <WKNavigationDelegate> {
515     NSString *_userAgent;
516 }
517 - (instancetype)initWithUserAgent:(NSString *)userAgent;
518 @end
519
520 @implementation SWCustomUserAgentDelegate
521
522 - (instancetype)initWithUserAgent:(NSString *)userAgent
523 {
524     self = [super init];
525     _userAgent = userAgent;
526     return self;
527 }
528
529 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
530 {
531     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
532     if (navigationAction.targetFrame.mainFrame)
533         [websitePolicies setCustomUserAgent:_userAgent];
534
535     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
536 }
537
538 @end
539
540 @interface SWUserAgentMessageHandler : NSObject <WKScriptMessageHandler> {
541 @public
542     NSString *expectedMessage;
543 }
544 - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
545 @end
546
547 @implementation SWUserAgentMessageHandler
548
549 - (instancetype)initWithExpectedMessage:(NSString *)_expectedMessage
550 {
551     self = [super init];
552     expectedMessage = _expectedMessage;
553     return self;
554 }
555
556 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
557 {
558     EXPECT_WK_STREQ(expectedMessage, [message body]);
559     done = true;
560 }
561 @end
562
563 static const char* userAgentSWBytes = R"SWRESOURCE(
564
565 self.addEventListener("message", (event) => {
566     event.source.postMessage(navigator.userAgent);
567 });
568
569 )SWRESOURCE";
570
571 TEST(ServiceWorkers, UserAgentOverride)
572 {
573     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
574
575     // Start with a clean slate data store
576     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
577         done = true;
578     }];
579     TestWebKitAPI::Util::run(&done);
580     done = false;
581
582     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
583
584     auto messageHandler = adoptNS([[SWUserAgentMessageHandler alloc] initWithExpectedMessage:@"Message from worker: Foo Custom UserAgent"]);
585     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
586
587     auto handler = adoptNS([[SWSchemes alloc] init]);
588     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
589     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", userAgentSWBytes });
590     handler->expectedUserAgent = @"Foo Custom UserAgent";
591     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
592
593     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
594     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
595
596     auto delegate = adoptNS([[SWCustomUserAgentDelegate alloc] initWithUserAgent:@"Foo Custom UserAgent"]);
597     [webView setNavigationDelegate:delegate.get()];
598
599     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
600     [webView loadRequest:request];
601
602     TestWebKitAPI::Util::run(&done);
603     done = false;
604
605     // Restore from disk.
606     webView = nullptr;
607     delegate = nullptr;
608     handler = nullptr;
609     messageHandler = nullptr;
610     configuration = nullptr;
611
612     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
613
614     messageHandler = adoptNS([[SWUserAgentMessageHandler alloc] initWithExpectedMessage:@"Message from worker: Bar Custom UserAgent"]);
615     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
616
617     handler = adoptNS([[SWSchemes alloc] init]);
618     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
619     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", userAgentSWBytes });
620     handler->expectedUserAgent = @"Bar Custom UserAgent";
621     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
622
623     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
624     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
625
626     delegate = adoptNS([[SWCustomUserAgentDelegate alloc] initWithUserAgent:@"Bar Custom UserAgent"]);
627     [webView setNavigationDelegate:delegate.get()];
628
629     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
630     [webView loadRequest:request];
631
632     TestWebKitAPI::Util::run(&done);
633     done = false;
634 }
635
636 TEST(ServiceWorkers, RestoreFromDisk)
637 {
638     ASSERT(mainRegisteringWorkerBytes);
639     ASSERT(scriptBytes);
640     ASSERT(mainRegisteringAlreadyExistingWorkerBytes);
641
642     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
643
644     // Start with a clean slate data store
645     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
646         done = true;
647     }];
648     TestWebKitAPI::Util::run(&done);
649     done = false;
650
651     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
652
653     RetainPtr<SWMessageHandlerForRestoreFromDiskTest> messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration was successful and service worker was activated"]);
654     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
655
656     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
657     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringWorkerBytes });
658     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
659     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
660
661     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
662     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
663
664     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
665     [webView loadRequest:request];
666
667     TestWebKitAPI::Util::run(&done);
668
669     webView = nullptr;
670     configuration = nullptr;
671     messageHandler = nullptr;
672     handler = nullptr;
673
674     done = false;
675
676     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
677     messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration already has an active worker"]);
678     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
679
680     handler = adoptNS([[SWSchemes alloc] init]);
681     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringAlreadyExistingWorkerBytes });
682     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
683     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
684
685     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
686     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
687
688     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
689     [webView loadRequest:request];
690
691     TestWebKitAPI::Util::run(&done);
692     done = false;
693 }
694
695 TEST(ServiceWorkers, CacheStorageRestoreFromDisk)
696 {
697     ASSERT(mainCacheStorageBytes);
698     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
699
700     // Start with a clean slate data store
701     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
702         done = true;
703     }];
704
705     TestWebKitAPI::Util::run(&done);
706     done = false;
707
708     [WKWebsiteDataStore _deleteDefaultDataStoreForTesting];
709
710     auto handler = adoptNS([[SWSchemes alloc] init]);
711     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainCacheStorageBytes });
712
713     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
714     auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"No cache storage data"]);
715
716     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
717     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
718
719     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
720     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
721
722     // Trigger creation of network process.
723     [webView.get().configuration.processPool _syncNetworkProcessCookies];
724
725     auto *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
726     [webView loadRequest:request];
727
728     TestWebKitAPI::Util::run(&done);
729     done = false;
730
731     webView = nullptr;
732     configuration = nullptr;
733     messageHandler = nullptr;
734
735     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
736     messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"Some cache storage data: my cache"]);
737
738     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
739     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
740
741     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
742     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
743
744     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
745     [webView loadRequest:request];
746
747     TestWebKitAPI::Util::run(&done);
748     done = false;
749 }
750
751
752 TEST(ServiceWorkers, FetchAfterRestoreFromDisk)
753 {
754     ASSERT(mainForFetchTestBytes);
755     ASSERT(scriptHandlingFetchBytes);
756
757     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
758
759     // Start with a clean slate data store
760     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
761         done = true;
762     }];
763     TestWebKitAPI::Util::run(&done);
764     done = false;
765
766     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
767
768     RetainPtr<SWMessageHandlerForFetchTest> messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
769     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
770
771     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
772     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
773     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
774     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
775     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
776
777     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
778     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
779
780     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
781     [webView loadRequest:request];
782
783     TestWebKitAPI::Util::run(&done);
784
785     webView = nullptr;
786     configuration = nullptr;
787     messageHandler = nullptr;
788     handler = nullptr;
789
790     done = false;
791
792     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
793     messageHandler = adoptNS([[SWMessageHandlerForFetchTest alloc] init]);
794     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
795
796     handler = adoptNS([[SWSchemes alloc] init]);
797     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFetchTestBytes });
798     handler->resources.set("sw://host/test.html", ResourceInfo { @"text/html", testBytes });
799     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptHandlingFetchBytes });
800     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
801
802     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
803     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
804
805     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
806     [webView loadRequest:request];
807
808     TestWebKitAPI::Util::run(&done);
809     done = false;
810 }
811
812 TEST(ServiceWorkers, InterceptFirstLoadAfterRestoreFromDisk)
813 {
814     ASSERT(mainForFirstLoadInterceptTestBytes);
815     ASSERT(scriptHandlingFetchBytes);
816
817     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
818
819     // Start with a clean slate data store
820     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
821         done = true;
822     }];
823     TestWebKitAPI::Util::run(&done);
824     done = false;
825
826     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
827
828     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
829     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
830
831     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
832     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
833     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
834     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
835
836     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
837     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
838
839     expectedMessage = "Service Worker activated";
840     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
841     [webView loadRequest:request];
842
843     TestWebKitAPI::Util::run(&done);
844
845     webView = nullptr;
846     configuration = nullptr;
847     messageHandler = nullptr;
848     handler = nullptr;
849
850     done = false;
851
852     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
853     messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
854     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
855
856     handler = adoptNS([[SWSchemes alloc] init]);
857     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
858     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
859     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
860
861     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
862     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
863
864     expectedMessage = "Intercepted by worker";
865     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
866     [webView loadRequest:request];
867
868     TestWebKitAPI::Util::run(&done);
869     done = false;
870 }
871
872 TEST(ServiceWorkers, WaitForPolicyDelegate)
873 {
874     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
875
876     // Start with a clean slate data store
877     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
878         done = true;
879     }];
880     TestWebKitAPI::Util::run(&done);
881     done = false;
882
883     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
884
885     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
886     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
887
888     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
889     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
890     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
891     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
892
893     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
894     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
895
896     // Register a service worker and activate it.
897     expectedMessage = "Service Worker activated";
898     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
899     [webView loadRequest:request];
900
901     TestWebKitAPI::Util::run(&done);
902
903     webView = nullptr;
904     configuration = nullptr;
905     messageHandler = nullptr;
906     handler = nullptr;
907
908     done = false;
909
910     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
911     messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
912     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
913
914     handler = adoptNS([[SWSchemes alloc] init]);
915     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainForFirstLoadInterceptTestBytes });
916     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptInterceptingFirstLoadBytes });
917     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
918
919     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
920     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
921
922     // Verify service worker is intercepting load.
923     expectedMessage = "Intercepted by worker";
924     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
925     [webView loadRequest:request];
926
927     TestWebKitAPI::Util::run(&done);
928     done = false;
929
930     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
931     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
932     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
933     [webView setNavigationDelegate:delegate.get()];
934     [webView setUIDelegate:delegate.get()];
935
936     shouldAccept = true;
937     navigationFailed = false;
938     navigationComplete = false;
939
940     // Verify service worker load goes well when policy delegate is ok.
941     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
942     [webView loadRequest:request];
943     TestWebKitAPI::Util::run(&navigationComplete);
944
945     EXPECT_FALSE(navigationFailed);
946
947     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
948     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
949     [webView setNavigationDelegate:delegate.get()];
950     [webView setUIDelegate:delegate.get()];
951
952     shouldAccept = false;
953     navigationFailed = false;
954     navigationComplete = false;
955
956     // Verify service worker load fails well when policy delegate is not ok.
957     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
958     [webView loadRequest:request];
959     TestWebKitAPI::Util::run(&navigationComplete);
960
961     EXPECT_TRUE(navigationFailed);
962 }
963 #if WK_HAVE_C_SPI
964
965 void setConfigurationInjectedBundlePath(WKWebViewConfiguration* configuration)
966 {
967     WKRetainPtr<WKContextRef> context(AdoptWK, TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
968     configuration.processPool = (WKProcessPool *)context.get();
969     auto pool = configuration.processPool;
970     [pool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
971
972     configuration.websiteDataStore = (WKWebsiteDataStore *)WKContextGetWebsiteDataStore(context.get());
973 }
974
975 @interface RegularPageMessageHandler : NSObject <WKScriptMessageHandler>
976 @end
977
978 @implementation RegularPageMessageHandler
979 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
980 {
981     EXPECT_TRUE([[message body] isEqualToString:@"PASS"]);
982     done = true;
983 }
984 @end
985
986 static const char* regularPageWithoutConnectionBytes = R"SWRESOURCE(
987 <script>
988 var result = window.internals.hasServiceWorkerConnection() ? "FAIL" : "PASS";
989 window.webkit.messageHandlers.regularPage.postMessage(result);
990 </script>
991 )SWRESOURCE";
992
993 static const char* regularPageWithConnectionBytes = R"SWRESOURCE(
994 <script>
995 var result = window.internals.hasServiceWorkerConnection() ? "PASS" : "FAIL";
996 window.webkit.messageHandlers.regularPage.postMessage(result);
997 </script>
998 )SWRESOURCE";
999
1000 TEST(ServiceWorkers, SWProcessConnectionCreation)
1001 {
1002     ASSERT(mainBytes);
1003     ASSERT(scriptBytes);
1004
1005     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1006
1007     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1008     setConfigurationInjectedBundlePath(configuration.get());
1009
1010     done = false;
1011
1012     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1013         done = true;
1014     }];
1015     TestWebKitAPI::Util::run(&done);
1016     done = false;
1017
1018     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1019         EXPECT_EQ(0u, [websiteDataRecords count]);
1020
1021         done = true;
1022     }];
1023     TestWebKitAPI::Util::run(&done);
1024     done = false;
1025
1026     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1027     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1028     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1029     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1030
1031     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1032     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1033     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1034     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1035     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1036     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1037
1038     RetainPtr<WKWebView> regularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1039     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1040     RetainPtr<WKWebView> newRegularPageWebView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1041
1042     // Test that a regular page does not trigger a service worker connection to network process if there is no registered service worker.
1043     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
1044
1045     [regularPageWebView loadRequest:request];
1046     TestWebKitAPI::Util::run(&done);
1047     done = false;
1048
1049     // Test that a sw scheme page can register a service worker.
1050     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1051
1052     [webView loadRequest:request];
1053     TestWebKitAPI::Util::run(&done);
1054     done = false;
1055     webView = nullptr;
1056
1057     // Now that a service worker is registered, the regular page should have a service worker connection.
1058     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1059
1060     [regularPageWebView loadRequest:request];
1061     TestWebKitAPI::Util::run(&done);
1062     done = false;
1063     regularPageWebView = nullptr;
1064
1065     [newRegularPageWebView loadRequest:request];
1066     TestWebKitAPI::Util::run(&done);
1067     done = false;
1068     newRegularPageWebView = nullptr;
1069
1070     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1071         EXPECT_EQ(1u, [websiteDataRecords count]);
1072         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
1073
1074         done = true;
1075     }];
1076
1077     TestWebKitAPI::Util::run(&done);
1078     done = false;
1079
1080     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1081         done = true;
1082     }];
1083     TestWebKitAPI::Util::run(&done);
1084 }
1085
1086 static const char* mainBytesWithScope = R"SWRESOURCE(
1087 <script>
1088
1089 function log(msg)
1090 {
1091     window.webkit.messageHandlers.sw.postMessage(msg);
1092 }
1093
1094 navigator.serviceWorker.addEventListener("message", function(event) {
1095     log("Message from worker: " + event.data);
1096 });
1097
1098 try {
1099
1100 navigator.serviceWorker.register('/sw.js', {scope: 'whateverscope'}).then(function(reg) {
1101     reg.installing.postMessage("Hello from the web page");
1102 }).catch(function(error) {
1103     log("Registration failed with: " + error);
1104 });
1105 } catch(e) {
1106     log("Exception: " + e);
1107 }
1108
1109 </script>
1110 )SWRESOURCE";
1111
1112 TEST(ServiceWorkers, ServiceWorkerProcessCreation)
1113 {
1114     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1115
1116     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1117     setConfigurationInjectedBundlePath(configuration.get());
1118
1119     done = false;
1120
1121     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1122         done = true;
1123     }];
1124     TestWebKitAPI::Util::run(&done);
1125     done = false;
1126
1127     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1128
1129         done = true;
1130     }];
1131     TestWebKitAPI::Util::run(&done);
1132     done = false;
1133
1134     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1135     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1136     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1137     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1138
1139     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1140     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1141     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1142     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
1143     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1144     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1145     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1146
1147     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1148
1149     // Load a page that registers a service worker.
1150     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
1151
1152     [webView loadRequest:request];
1153     TestWebKitAPI::Util::run(&done);
1154     done = false;
1155     webView = nullptr;
1156
1157     // 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.
1158     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1159     setConfigurationInjectedBundlePath(newConfiguration.get());
1160     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
1161
1162     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1163     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1164     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1165
1166     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1167     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1168     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1169     [webView loadRequest:request];
1170     TestWebKitAPI::Util::run(&done);
1171     done = false;
1172
1173     // Make sure that loading the simple page did not start the service worker process.
1174     EXPECT_EQ(1u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1175
1176     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1177     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1178     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1179     [webView loadRequest:request];
1180     TestWebKitAPI::Util::run(&done);
1181     done = false;
1182
1183     // Make sure that loading this page did start the service worker process.
1184     EXPECT_EQ(3u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1185
1186     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1187         done = true;
1188     }];
1189     TestWebKitAPI::Util::run(&done);
1190     done = false;
1191 }
1192
1193 TEST(ServiceWorkers, HasServiceWorkerRegistrationBit)
1194 {
1195     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1196
1197     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1198     setConfigurationInjectedBundlePath(configuration.get());
1199
1200     done = false;
1201
1202     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1203         done = true;
1204     }];
1205     TestWebKitAPI::Util::run(&done);
1206     done = false;
1207
1208     [[configuration websiteDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1209
1210         done = true;
1211     }];
1212     TestWebKitAPI::Util::run(&done);
1213     done = false;
1214
1215     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1216     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1217     RetainPtr<RegularPageMessageHandler> regularPageMessageHandler = adoptNS([[RegularPageMessageHandler alloc] init]);
1218     [[configuration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1219
1220     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1221     handler->resources.set("sw://host/regularPageWithoutConnection.html", ResourceInfo { @"text/html", regularPageWithoutConnectionBytes });
1222     handler->resources.set("sw://host/regularPageWithConnection.html", ResourceInfo { @"text/html", regularPageWithConnectionBytes });
1223     handler->resources.set("sw://host/mainWithScope.html", ResourceInfo { @"text/html", mainBytesWithScope });
1224     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1225     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1226     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1227
1228     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1229
1230     // Load a page that registers a service worker.
1231     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/mainWithScope.html"]];
1232
1233     [webView loadRequest:request];
1234     TestWebKitAPI::Util::run(&done);
1235     done = false;
1236     webView = nullptr;
1237
1238     // Now that a sw is registered, let's create a new configuration and try loading a regular page
1239     RetainPtr<WKWebViewConfiguration> newConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1240     setConfigurationInjectedBundlePath(newConfiguration.get());
1241
1242     [[newConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1243     [[newConfiguration userContentController] addScriptMessageHandler:regularPageMessageHandler.get() name:@"regularPage"];
1244     [newConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1245
1246     newConfiguration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1247     [newConfiguration.get().websiteDataStore _setServiceWorkerRegistrationDirectory: @"~/nonexistingfolder"];
1248
1249     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1250     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithoutConnection.html"]];
1251     [webView loadRequest:request];
1252     TestWebKitAPI::Util::run(&done);
1253     done = false;
1254
1255     // Let's use the web site data store that has service worker and load a page.
1256     newConfiguration.get().websiteDataStore = [configuration websiteDataStore];
1257
1258     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:newConfiguration.get()]);
1259     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1260     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageWithConnection.html"]];
1261     [webView loadRequest:request];
1262     TestWebKitAPI::Util::run(&done);
1263     done = false;
1264
1265     // Make sure that loading the simple page did not start the service worker process.
1266     EXPECT_EQ(2u, webView.get().configuration.processPool._webProcessCountIgnoringPrewarmed);
1267
1268     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1269         done = true;
1270     }];
1271     TestWebKitAPI::Util::run(&done);
1272     done = false;
1273 }
1274
1275 static const char* readCacheBytes = R"SWRESOURCE(
1276 <script>
1277
1278 function log(msg)
1279 {
1280     window.webkit.messageHandlers.sw.postMessage(msg);
1281 }
1282
1283 window.caches.keys().then(keys => {
1284     log(keys.length && keys[0] === "test" ? "PASS" : "FAIL");
1285 }, () => {
1286     log("FAIL");
1287 });
1288
1289 </script>
1290 )SWRESOURCE";
1291
1292 static const char* writeCacheBytes = R"SWRESOURCE(
1293 <script>
1294
1295 function log(msg)
1296 {
1297     window.webkit.messageHandlers.sw.postMessage(msg);
1298 }
1299
1300 window.caches.open("test").then(() => {
1301     log("PASS");
1302 }, () => {
1303     log("FAIL");
1304 });
1305
1306 </script>
1307 )SWRESOURCE";
1308
1309 @interface SWMessageHandlerForCacheStorage : NSObject <WKScriptMessageHandler>
1310 @end
1311
1312 @implementation SWMessageHandlerForCacheStorage
1313 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
1314 {
1315     EXPECT_WK_STREQ(@"PASS", [message body]);
1316     done = true;
1317 }
1318 @end
1319
1320 TEST(ServiceWorkers, CacheStorageInPrivateBrowsingMode)
1321 {
1322     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1323
1324     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1325
1326     auto messageHandler = adoptNS([[SWMessageHandlerForCacheStorage alloc] init]);
1327     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1328
1329     auto handler = adoptNS([[SWSchemes alloc] init]);
1330     handler->resources.set("sw://host/readCache.html", ResourceInfo { @"text/html", readCacheBytes });
1331     handler->resources.set("sw://host/writeCache.html", ResourceInfo { @"text/html", writeCacheBytes });
1332     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1333
1334     configuration.get().websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1335     [configuration.get().processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1336
1337     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1338     auto *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/writeCache.html"]];
1339     [webView loadRequest:request];
1340     TestWebKitAPI::Util::run(&done);
1341     done = false;
1342
1343     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1344     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/readCache.html"]];
1345     [webView loadRequest:request];
1346     TestWebKitAPI::Util::run(&done);
1347     done = false;
1348 }
1349
1350 static const char* regularPageGrabbingCacheStorageDirectory = R"SWRESOURCE(
1351 <script>
1352 async function getResult()
1353 {
1354     var result = await window.internals.cacheStorageEngineRepresentation();
1355     window.webkit.messageHandlers.sw.postMessage(result);
1356 }
1357 getResult();
1358 </script>
1359 )SWRESOURCE";
1360
1361 @interface DirectoryPageMessageHandler : NSObject <WKScriptMessageHandler>
1362 @end
1363
1364 @implementation DirectoryPageMessageHandler
1365 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
1366 {
1367     retrievedString = [message body];
1368     done = true;
1369 }
1370 @end
1371
1372 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageDefaultDirectories)
1373 {
1374     ASSERT(mainBytes);
1375     ASSERT(scriptBytes);
1376
1377     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1378
1379     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1380
1381     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1382     setConfigurationInjectedBundlePath(configuration.get());
1383
1384     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1385     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1386
1387     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1388     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1389     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1390     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1391     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1392
1393     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1394     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1395
1396     [webView loadRequest:request];
1397     TestWebKitAPI::Util::run(&done);
1398     done = false;
1399     EXPECT_TRUE([[configuration websiteDataStore] _hasRegisteredServiceWorker]);
1400
1401     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1402     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1403
1404     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1405     [webView loadRequest:request];
1406     TestWebKitAPI::Util::run(&done);
1407     done = false;
1408     EXPECT_TRUE(retrievedString.contains("/Caches/TestWebKitAPI/WebKit/CacheStorage"));
1409
1410     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1411         done = true;
1412     }];
1413     TestWebKitAPI::Util::run(&done);
1414     done = false;
1415 }
1416
1417 TEST(ServiceWorkers, ServiceWorkerAndCacheStorageSpecificDirectories)
1418 {
1419     ASSERT(mainBytes);
1420     ASSERT(scriptBytes);
1421
1422     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1423
1424     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1425
1426     configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1427     setConfigurationInjectedBundlePath(configuration.get());
1428     auto websiteDataStore = [configuration websiteDataStore];
1429     [websiteDataStore _setCacheStorageDirectory:@"/var/tmp"];
1430     [websiteDataStore _setServiceWorkerRegistrationDirectory:@"/var/tmp"];
1431
1432     RetainPtr<DirectoryPageMessageHandler> directoryPageMessageHandler = adoptNS([[DirectoryPageMessageHandler alloc] init]);
1433     [[configuration userContentController] addScriptMessageHandler:directoryPageMessageHandler.get() name:@"sw"];
1434
1435     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1436     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1437     handler->resources.set("sw://host/regularPageGrabbingCacheStorageDirectory.html", ResourceInfo { @"text/html", regularPageGrabbingCacheStorageDirectory });
1438     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1439     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1440
1441     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1442     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1443
1444     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1445     [webView loadRequest:request];
1446     TestWebKitAPI::Util::run(&done);
1447     done = false;
1448     while (![websiteDataStore _hasRegisteredServiceWorker])
1449         TestWebKitAPI::Util::spinRunLoop(0.1);
1450
1451     webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1452     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/regularPageGrabbingCacheStorageDirectory.html"]];
1453
1454     [webView loadRequest:request];
1455     TestWebKitAPI::Util::run(&done);
1456     done = false;
1457     EXPECT_TRUE(retrievedString.contains("\"path\": \"/var/tmp\""));
1458
1459     [[configuration websiteDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1460         done = true;
1461     }];
1462     TestWebKitAPI::Util::run(&done);
1463     done = false;
1464 }
1465
1466 #endif // WK_HAVE_C_SPI
1467
1468 TEST(ServiceWorkers, NonDefaultSessionID)
1469 {
1470     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1471
1472     NSURL *serviceWorkersPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ServiceWorkers/" stringByExpandingTildeInPath] isDirectory:YES];
1473     [[NSFileManager defaultManager] removeItemAtURL:serviceWorkersPath error:nil];
1474     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1475     NSURL *idbPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/IndexedDB/" stringByExpandingTildeInPath] isDirectory:YES];
1476     [[NSFileManager defaultManager] removeItemAtURL:idbPath error:nil];
1477     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:idbPath.path]);
1478
1479     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1480     RetainPtr<_WKWebsiteDataStoreConfiguration> websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
1481     websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = serviceWorkersPath;
1482     websiteDataStoreConfiguration.get()._indexedDBDatabaseDirectory = idbPath;
1483
1484     configuration.get().websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()] autorelease];
1485
1486     RetainPtr<SWMessageHandlerWithExpectedMessage> messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
1487     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1488
1489     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1490     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytesForSessionIDTest });
1491     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytesForSessionIDTest });
1492     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1493
1494     expectedMessage = "PASS: activation successful";
1495     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1496     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1497
1498     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1499     [webView loadRequest:request];
1500
1501     TestWebKitAPI::Util::run(&done);
1502     done = false;
1503
1504     webView = nullptr;
1505
1506     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:serviceWorkersPath.path]);
1507
1508     [configuration.get().websiteDataStore fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
1509         EXPECT_EQ(1u, [websiteDataRecords count]);
1510         EXPECT_TRUE([websiteDataRecords[0].displayName isEqualToString:@"sw host"]);
1511
1512         done = true;
1513     }];
1514
1515     TestWebKitAPI::Util::run(&done);
1516     done = false;
1517 }
1518
1519 void waitUntilServiceWorkerProcessCount(WKProcessPool *processPool, unsigned processCount)
1520 {
1521     do {
1522         if (processPool._serviceWorkerProcessCount == processCount)
1523             return;
1524         TestWebKitAPI::Util::spinRunLoop(1);
1525     } while (true);
1526 }
1527
1528 TEST(ServiceWorkers, ProcessPerSite)
1529 {
1530     ASSERT(mainBytes);
1531     ASSERT(scriptBytes);
1532
1533     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1534
1535     // Start with a clean slate data store
1536     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1537         done = true;
1538     }];
1539     TestWebKitAPI::Util::run(&done);
1540     done = false;
1541
1542     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1543
1544     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1545     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1546
1547     RetainPtr<SWSchemes> handler1 = adoptNS([[SWSchemes alloc] init]);
1548     handler1->resources.set("sw1://host/main.html", ResourceInfo { @"text/html", mainBytes });
1549     handler1->resources.set("sw1://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1550     handler1->resources.set("sw1://host2/main.html", ResourceInfo { @"text/html", mainBytes });
1551     handler1->resources.set("sw1://host2/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1552     [configuration setURLSchemeHandler:handler1.get() forURLScheme:@"sw1"];
1553
1554     RetainPtr<SWSchemes> handler2 = adoptNS([[SWSchemes alloc] init]);
1555     handler2->resources.set("sw2://host/main.html", ResourceInfo { @"text/html", mainBytes });
1556     handler2->resources.set("sw2://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1557     [configuration setURLSchemeHandler:handler2.get() forURLScheme:@"sw2"];
1558
1559     WKProcessPool *processPool = configuration.get().processPool;
1560     [processPool _registerURLSchemeServiceWorkersCanHandle:@"sw1"];
1561     [processPool _registerURLSchemeServiceWorkersCanHandle:@"sw2"];
1562
1563     // Normally, service workers get terminated several seconds after their clients are gone.
1564     // Disable this delay for the purpose of testing.
1565     [processPool _disableServiceWorkerProcessTerminationDelay];
1566
1567     RetainPtr<WKWebView> webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1568
1569     NSURLRequest *request1 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host/main.html"]];
1570     [webView1 loadRequest:request1];
1571
1572     TestWebKitAPI::Util::run(&done);
1573     done = false;
1574
1575     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1576
1577     RetainPtr<WKWebView> webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1578     [webView2 loadRequest:request1];
1579
1580     TestWebKitAPI::Util::run(&done);
1581     done = false;
1582
1583     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1584
1585     RetainPtr<WKWebView> webView3 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1586     NSURLRequest *request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw2://host/main.html"]];
1587     [webView3 loadRequest:request2];
1588
1589     TestWebKitAPI::Util::run(&done);
1590     done = false;
1591
1592     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1593
1594     RetainPtr<WKWebView> webView4 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1595     NSURLRequest *request3 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw1://host2/main.html"]];
1596     [webView4 loadRequest:request3];
1597
1598     TestWebKitAPI::Util::run(&done);
1599     done = false;
1600
1601     EXPECT_EQ(2U, processPool._serviceWorkerProcessCount);
1602
1603     NSURLRequest *aboutBlankRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]];
1604     [webView4 loadRequest:aboutBlankRequest];
1605
1606     waitUntilServiceWorkerProcessCount(processPool, 1);
1607     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1608
1609     [webView2 loadRequest:aboutBlankRequest];
1610     TestWebKitAPI::Util::spinRunLoop(10);
1611     EXPECT_EQ(1U, processPool._serviceWorkerProcessCount);
1612
1613     [webView1 loadRequest:aboutBlankRequest];
1614     [webView3 loadRequest:aboutBlankRequest];
1615     waitUntilServiceWorkerProcessCount(processPool, 0);
1616     EXPECT_EQ(0U, processPool._serviceWorkerProcessCount);
1617 }
1618
1619 TEST(ServiceWorkers, LoadData)
1620 {
1621     ASSERT(mainBytes);
1622     ASSERT(scriptBytes);
1623
1624     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1625
1626     // Start with a clean slate data store
1627     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1628         done = true;
1629     }];
1630     TestWebKitAPI::Util::run(&done);
1631     done = false;
1632
1633     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1634
1635     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1636     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1637
1638     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1639     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1640     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1641     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1642
1643     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1644     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1645
1646     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
1647     [webView setNavigationDelegate:delegate.get()];
1648     [webView setUIDelegate:delegate.get()];
1649
1650     done = false;
1651
1652     // Normal load to get SW registered.
1653     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1654     [webView loadRequest:request];
1655
1656     TestWebKitAPI::Util::run(&done);
1657     done = false;
1658
1659     // Now try a data load.
1660     NSData *data = [NSData dataWithBytes:mainBytes length:strlen(mainBytes)];
1661     [webView loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@"sw://host/main.html"]];
1662
1663     TestWebKitAPI::Util::run(&done);
1664     done = false;
1665 }
1666
1667 TEST(ServiceWorkers, RestoreFromDiskNonDefaultStore)
1668 {
1669     ASSERT(mainRegisteringWorkerBytes);
1670     ASSERT(scriptBytes);
1671     ASSERT(mainRegisteringAlreadyExistingWorkerBytes);
1672
1673     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1674
1675     NSURL *swDBPath = [NSURL fileURLWithPath:[@"~/Library/WebKit/TestWebKitAPI/CustomWebsiteData/ServiceWorkers/" stringByExpandingTildeInPath]];
1676     [[NSFileManager defaultManager] removeItemAtURL:swDBPath error:nil];
1677     EXPECT_FALSE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
1678     [[NSFileManager defaultManager] createDirectoryAtURL:swDBPath withIntermediateDirectories:YES attributes:nil error:nil];
1679     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
1680
1681     // We protect the process pool so that it outlives the WebsiteDataStore.
1682     RetainPtr<WKProcessPool> protectedProcessPool;
1683
1684     @autoreleasepool {
1685         auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1686
1687         auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
1688         websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swDBPath;
1689         auto nonDefaultDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
1690         configuration.get().websiteDataStore = nonDefaultDataStore.get();
1691
1692         auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration was successful and service worker was activated"]);
1693         [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1694
1695         auto handler = adoptNS([[SWSchemes alloc] init]);
1696         handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringWorkerBytes });
1697         handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1698         [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1699
1700         auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1701         [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1702         protectedProcessPool = webView.get().configuration.processPool;
1703
1704         [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]]];
1705
1706         TestWebKitAPI::Util::run(&done);
1707         done = false;
1708
1709         [webView.get().configuration.processPool _terminateServiceWorkerProcesses];
1710     }
1711
1712     @autoreleasepool {
1713         auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1714
1715         auto websiteDataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
1716         websiteDataStoreConfiguration.get()._serviceWorkerRegistrationDirectory = swDBPath;
1717         auto nonDefaultDataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:websiteDataStoreConfiguration.get()]);
1718         configuration.get().websiteDataStore = nonDefaultDataStore.get();
1719
1720         auto messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"PASS: Registration already has an active worker"]);
1721         [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1722
1723         auto handler = adoptNS([[SWSchemes alloc] init]);
1724         handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainRegisteringAlreadyExistingWorkerBytes });
1725         handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1726         [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1727
1728         auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1729         [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1730
1731         [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]]];
1732
1733         TestWebKitAPI::Util::run(&done);
1734         done = false;
1735     }
1736 }
1737
1738 TEST(ServiceWorkers, SuspendNetworkProcess)
1739 {
1740     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
1741
1742     // Start with a clean slate data store
1743     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
1744         done = true;
1745     }];
1746     TestWebKitAPI::Util::run(&done);
1747     done = false;
1748
1749     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1750
1751     RetainPtr<SWMessageHandler> messageHandler = adoptNS([[SWMessageHandler alloc] init]);
1752     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
1753
1754     RetainPtr<SWSchemes> handler = adoptNS([[SWSchemes alloc] init]);
1755     handler->resources.set("sw://host/main.html", ResourceInfo { @"text/html", mainBytes });
1756     handler->resources.set("sw://host/sw.js", ResourceInfo { @"application/javascript", scriptBytes });
1757     [configuration setURLSchemeHandler:handler.get() forURLScheme:@"SW"];
1758
1759     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1760     [webView.get().configuration.processPool _registerURLSchemeServiceWorkersCanHandle:@"sw"];
1761
1762     auto delegate = adoptNS([[TestSWAsyncNavigationDelegate alloc] init]);
1763     [webView setNavigationDelegate:delegate.get()];
1764     [webView setUIDelegate:delegate.get()];
1765
1766     done = false;
1767
1768     // Normal load to get SW registered.
1769     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"sw://host/main.html"]];
1770     [webView loadRequest:request];
1771
1772     TestWebKitAPI::Util::run(&done);
1773     done = false;
1774
1775     auto store = [configuration websiteDataStore];
1776     auto path = store._serviceWorkerRegistrationDirectory;
1777
1778     NSURL* directory = [NSURL fileURLWithPath:path isDirectory:YES];
1779     NSURL *swDBPath = [directory URLByAppendingPathComponent:@"ServiceWorkerRegistrations-4.sqlite3"];
1780
1781     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
1782
1783     [ webView.get().configuration.processPool _sendNetworkProcessWillSuspendImminently];
1784
1785     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
1786
1787     [ webView.get().configuration.processPool _sendNetworkProcessDidResume];
1788
1789     [webView loadRequest:request];
1790     TestWebKitAPI::Util::run(&done);
1791     done = false;
1792
1793     EXPECT_TRUE([[NSFileManager defaultManager] fileExistsAtPath:swDBPath.path]);
1794 }