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