PSON: Triggering a navigation to an invalid URL creates a new WebContent process
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / ProcessSwapOnNavigation.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "Test.h"
30 #import <WebKit/WKNavigationDelegate.h>
31 #import <WebKit/WKNavigationPrivate.h>
32 #import <WebKit/WKPreferencesPrivate.h>
33 #import <WebKit/WKProcessPoolPrivate.h>
34 #import <WebKit/WKUIDelegatePrivate.h>
35 #import <WebKit/WKURLSchemeHandler.h>
36 #import <WebKit/WKURLSchemeTaskPrivate.h>
37 #import <WebKit/WKWebViewConfigurationPrivate.h>
38 #import <WebKit/WKWebViewPrivate.h>
39 #import <WebKit/WKWebsiteDataStorePrivate.h>
40 #import <WebKit/WKWebsiteDataStoreRef.h>
41 #import <WebKit/WebKit.h>
42 #import <WebKit/_WKExperimentalFeature.h>
43 #import <WebKit/_WKProcessPoolConfiguration.h>
44 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
45 #import <WebKit/_WKWebsitePolicies.h>
46 #import <wtf/Deque.h>
47 #import <wtf/HashMap.h>
48 #import <wtf/HashSet.h>
49 #import <wtf/RetainPtr.h>
50 #import <wtf/Vector.h>
51 #import <wtf/text/StringHash.h>
52 #import <wtf/text/WTFString.h>
53
54 #if WK_API_ENABLED
55
56 static bool done;
57 static bool failed;
58 static bool didCreateWebView;
59 static int numberOfDecidePolicyCalls;
60
61 static RetainPtr<NSMutableArray> receivedMessages = adoptNS([@[] mutableCopy]);
62 bool didReceiveAlert;
63 static bool receivedMessage;
64 static bool serverRedirected;
65 static HashSet<pid_t> seenPIDs;
66 @interface PSONMessageHandler : NSObject <WKScriptMessageHandler>
67 @end
68
69 @implementation PSONMessageHandler
70 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
71 {
72     if ([message body])
73         [receivedMessages addObject:[message body]];
74     else
75         [receivedMessages addObject:@""];
76
77     receivedMessage = true;
78     if ([message.webView _webProcessIdentifier])
79         seenPIDs.add([message.webView _webProcessIdentifier]);
80 }
81 @end
82
83 @interface PSONNavigationDelegate : NSObject <WKNavigationDelegate>
84 @end
85
86 @implementation PSONNavigationDelegate
87
88 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
89 {
90     seenPIDs.add([webView _webProcessIdentifier]);
91     failed = true;
92 }
93
94 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
95 {
96     seenPIDs.add([webView _webProcessIdentifier]);
97     done = true;
98 }
99
100 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
101 {
102     ++numberOfDecidePolicyCalls;
103     seenPIDs.add([webView _webProcessIdentifier]);
104     decisionHandler(WKNavigationActionPolicyAllow);
105 }
106
107 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
108 {
109     seenPIDs.add([webView _webProcessIdentifier]);
110     serverRedirected = true;
111 }
112
113 @end
114
115 static RetainPtr<WKWebView> createdWebView;
116
117 @interface PSONUIDelegate : NSObject <WKUIDelegatePrivate>
118 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate;
119 @end
120
121 @implementation PSONUIDelegate {
122     RetainPtr<PSONNavigationDelegate> _navigationDelegate;
123 }
124
125 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate
126 {
127     if (!(self = [super init]))
128         return nil;
129
130     _navigationDelegate = navigationDelegate;
131     return self;
132 }
133
134 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
135 {
136     createdWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
137     [createdWebView setNavigationDelegate:_navigationDelegate.get()];
138     didCreateWebView = true;
139     return createdWebView.get();
140 }
141
142 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
143 {
144     didReceiveAlert = true;
145     completionHandler();
146 }
147
148 @end
149
150 @interface PSONScheme : NSObject <WKURLSchemeHandler> {
151     const char* _bytes;
152     HashMap<String, String> _redirects;
153     HashMap<String, RetainPtr<NSData *>> _dataMappings;
154 }
155 - (instancetype)initWithBytes:(const char*)bytes;
156 - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString;
157 - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data;
158 @end
159
160 @implementation PSONScheme
161
162 - (instancetype)initWithBytes:(const char*)bytes
163 {
164     self = [super init];
165     _bytes = bytes;
166     return self;
167 }
168
169 - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString
170 {
171     _redirects.set(sourceURLString, destinationURLString);
172 }
173
174 - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data
175 {
176     _dataMappings.set(urlString, [NSData dataWithBytesNoCopy:(void*)data length:strlen(data) freeWhenDone:NO]);
177 }
178
179 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
180 {
181     NSURL *finalURL = task.request.URL;
182     auto target = _redirects.get(task.request.URL.absoluteString);
183     if (!target.isEmpty()) {
184         auto redirectResponse = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:nil expectedContentLength:0 textEncodingName:nil]);
185
186         finalURL = [NSURL URLWithString:(NSString *)target];
187         auto request = adoptNS([[NSURLRequest alloc] initWithURL:finalURL]);
188
189         [(id<WKURLSchemeTaskPrivate>)task _didPerformRedirection:redirectResponse.get() newRequest:request.get()];
190     }
191
192     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:finalURL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
193     [task didReceiveResponse:response.get()];
194
195     if (auto data = _dataMappings.get([finalURL absoluteString]))
196         [task didReceiveData:data.get()];
197     else if (_bytes) {
198         RetainPtr<NSData> data = adoptNS([[NSData alloc] initWithBytesNoCopy:(void *)_bytes length:strlen(_bytes) freeWhenDone:NO]);
199         [task didReceiveData:data.get()];
200     } else
201         [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
202
203     [task didFinish];
204 }
205
206 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
207 {
208 }
209
210 @end
211
212 static const char* testBytes = R"PSONRESOURCE(
213 <head>
214 <script>
215
216 function log(msg)
217 {
218     window.webkit.messageHandlers.pson.postMessage(msg);
219 }
220
221 window.onload = function(evt) {
222     if (window.history.state != "onloadCalled")
223         setTimeout('window.history.replaceState("onloadCalled", "");', 0);
224 }
225
226 window.onpageshow = function(evt) {
227     log("PageShow called. Persisted: " + evt.persisted + ", and window.history.state is: " + window.history.state);
228 }
229
230 </script>
231 </head>
232 )PSONRESOURCE";
233
234 #if PLATFORM(MAC)
235
236 static const char* windowOpenCrossOriginNoOpenerTestBytes = R"PSONRESOURCE(
237 <script>
238 window.onload = function() {
239     window.open("pson2://host/main2.html", "_blank", "noopener");
240 }
241 </script>
242 )PSONRESOURCE";
243
244 static const char* windowOpenCrossOriginWithOpenerTestBytes = R"PSONRESOURCE(
245 <script>
246 window.onload = function() {
247     window.open("pson2://host/main2.html");
248 }
249 </script>
250 )PSONRESOURCE";
251
252 static const char* windowOpenSameOriginNoOpenerTestBytes = R"PSONRESOURCE(
253 <script>
254 window.onload = function() {
255     if (!opener)
256         window.open("pson1://host/main2.html", "_blank", "noopener");
257 }
258 </script>
259 )PSONRESOURCE";
260
261 static const char* dummyBytes = R"PSONRESOURCE(
262 <body>TEST</body>
263 )PSONRESOURCE";
264
265 #endif // PLATFORM(MAC)
266
267 TEST(ProcessSwap, Basic)
268 {
269     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
270     processPoolConfiguration.get().processSwapsOnNavigation = YES;
271     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
272
273     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
274     [webViewConfiguration setProcessPool:processPool.get()];
275     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] init]);
276     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
277     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
278
279     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
280     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
281     [webView setNavigationDelegate:delegate.get()];
282
283     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
284     [webView loadRequest:request];
285
286     TestWebKitAPI::Util::run(&done);
287     done = false;
288
289     auto pid1 = [webView _webProcessIdentifier];
290
291     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main2.html"]];
292     [webView loadRequest:request];
293
294     TestWebKitAPI::Util::run(&done);
295     done = false;
296
297     auto pid2 = [webView _webProcessIdentifier];
298
299     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
300     [webView loadRequest:request];
301
302     TestWebKitAPI::Util::run(&done);
303     done = false;
304
305     auto pid3 = [webView _webProcessIdentifier];
306
307     EXPECT_EQ(pid1, pid2);
308     EXPECT_FALSE(pid2 == pid3);
309
310     // 3 loads, 3 decidePolicy calls (e.g. the load that did perform a process swap should not have generated an additional decidePolicy call)
311     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
312 }
313
314 TEST(ProcessSwap, Back)
315 {
316     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
317     processPoolConfiguration.get().processSwapsOnNavigation = YES;
318     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
319
320     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
321     [webViewConfiguration setProcessPool:processPool.get()];
322     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:testBytes]);
323     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
324     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
325     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
326
327     RetainPtr<PSONMessageHandler> messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
328     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
329
330     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
331     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
332     [webView setNavigationDelegate:delegate.get()];
333
334     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
335     [webView loadRequest:request];
336
337     TestWebKitAPI::Util::run(&receivedMessage);
338     receivedMessage = false;
339     TestWebKitAPI::Util::run(&done);
340     done = false;
341
342     auto pid1 = [webView _webProcessIdentifier];
343
344     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
345     [webView loadRequest:request];
346
347     TestWebKitAPI::Util::run(&done);
348     done = false;
349
350     auto pid2 = [webView _webProcessIdentifier];
351
352     [webView goBack];
353
354     TestWebKitAPI::Util::run(&receivedMessage);
355     receivedMessage = false;
356     TestWebKitAPI::Util::run(&done);
357     done = false;
358
359     auto pid3 = [webView _webProcessIdentifier];
360
361     // 3 loads, 3 decidePolicy calls (e.g. any load that performs a process swap should not have generated an
362     // additional decidePolicy call as a result of the process swap)
363     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
364
365     EXPECT_EQ([receivedMessages count], 2u);
366     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"PageShow called. Persisted: false, and window.history.state is: null"]);
367     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"PageShow called. Persisted: true, and window.history.state is: onloadCalled"]);
368
369     EXPECT_EQ(2u, seenPIDs.size());
370
371     EXPECT_FALSE(pid1 == pid2);
372     EXPECT_FALSE(pid2 == pid3);
373     EXPECT_TRUE(pid1 == pid3);
374 }
375
376 #if PLATFORM(MAC)
377
378 TEST(ProcessSwap, CrossOriginWindowOpenNoOpener)
379 {
380     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
381     processPoolConfiguration.get().processSwapsOnNavigation = YES;
382     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
383
384     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
385     [webViewConfiguration setProcessPool:processPool.get()];
386     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginNoOpenerTestBytes]);
387     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
388     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
389     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
390
391     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
392     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
393     [webView setNavigationDelegate:navigationDelegate.get()];
394     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
395     [webView setUIDelegate:uiDelegate.get()];
396
397     numberOfDecidePolicyCalls = 0;
398     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
399     [webView loadRequest:request];
400
401     TestWebKitAPI::Util::run(&done);
402     done = false;
403
404     TestWebKitAPI::Util::run(&didCreateWebView);
405     didCreateWebView = false;
406
407     TestWebKitAPI::Util::run(&done);
408
409     EXPECT_EQ(2, numberOfDecidePolicyCalls);
410
411     auto pid1 = [webView _webProcessIdentifier];
412     EXPECT_TRUE(!!pid1);
413     auto pid2 = [createdWebView _webProcessIdentifier];
414     EXPECT_TRUE(!!pid2);
415
416     EXPECT_NE(pid1, pid2);
417 }
418
419 TEST(ProcessSwap, CrossOriginWindowOpenWithOpener)
420 {
421     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
422     processPoolConfiguration.get().processSwapsOnNavigation = YES;
423     processPoolConfiguration.get().processSwapsOnWindowOpenWithOpener = YES;
424     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
425
426     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
427     [webViewConfiguration setProcessPool:processPool.get()];
428     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginWithOpenerTestBytes]);
429     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
430     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
431     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
432
433     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
434     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
435     [webView setNavigationDelegate:navigationDelegate.get()];
436     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
437     [webView setUIDelegate:uiDelegate.get()];
438
439     numberOfDecidePolicyCalls = 0;
440     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
441     [webView loadRequest:request];
442
443     TestWebKitAPI::Util::run(&done);
444     done = false;
445
446     TestWebKitAPI::Util::run(&didCreateWebView);
447     didCreateWebView = false;
448
449     TestWebKitAPI::Util::run(&done);
450
451     EXPECT_EQ(2, numberOfDecidePolicyCalls);
452
453     auto pid1 = [webView _webProcessIdentifier];
454     EXPECT_TRUE(!!pid1);
455     auto pid2 = [createdWebView _webProcessIdentifier];
456     EXPECT_TRUE(!!pid2);
457
458     EXPECT_NE(pid1, pid2);
459 }
460
461 TEST(ProcessSwap, SameOriginWindowOpenNoOpener)
462 {
463     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
464     processPoolConfiguration.get().processSwapsOnNavigation = YES;
465     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
466
467     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
468     [webViewConfiguration setProcessPool:processPool.get()];
469     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenSameOriginNoOpenerTestBytes]);
470     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
471
472     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
473     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
474     [webView setNavigationDelegate:navigationDelegate.get()];
475     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
476     [webView setUIDelegate:uiDelegate.get()];
477
478     numberOfDecidePolicyCalls = 0;
479     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
480     [webView loadRequest:request];
481
482     TestWebKitAPI::Util::run(&done);
483     done = false;
484
485     TestWebKitAPI::Util::run(&didCreateWebView);
486     didCreateWebView = false;
487
488     TestWebKitAPI::Util::run(&done);
489
490     EXPECT_EQ(2, numberOfDecidePolicyCalls);
491
492     auto pid1 = [webView _webProcessIdentifier];
493     EXPECT_TRUE(!!pid1);
494     auto pid2 = [createdWebView _webProcessIdentifier];
495     EXPECT_TRUE(!!pid2);
496
497     EXPECT_EQ(pid1, pid2);
498 }
499
500 #endif // PLATFORM(MAC)
501
502 TEST(ProcessSwap, ServerRedirectFromNewWebView)
503 {
504     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
505     processPoolConfiguration.get().processSwapsOnNavigation = YES;
506     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
507
508     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
509     [webViewConfiguration setProcessPool:processPool.get()];
510     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] init]);
511     [handler addRedirectFromURLString:@"pson://host/main1.html" toURLString:@"psonredirected://host/main1.html"];
512     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
513
514     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
515     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
516     [webView setNavigationDelegate:delegate.get()];
517
518     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
519     [webView loadRequest:request];
520
521     TestWebKitAPI::Util::run(&serverRedirected);
522     serverRedirected = false;
523
524     seenPIDs.add([webView _webProcessIdentifier]);
525
526     TestWebKitAPI::Util::run(&done);
527     done = false;
528
529     seenPIDs.add([webView _webProcessIdentifier]);
530
531     EXPECT_FALSE(serverRedirected);
532     EXPECT_EQ(2, numberOfDecidePolicyCalls);
533     EXPECT_EQ(1u, seenPIDs.size());
534 }
535
536 TEST(ProcessSwap, ServerRedirect)
537 {
538     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
539     processPoolConfiguration.get().processSwapsOnNavigation = YES;
540     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
541
542     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
543     [webViewConfiguration setProcessPool:processPool.get()];
544     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] init]);
545     [handler1 addRedirectFromURLString:@"pson://host/main1.html" toURLString:@"psonredirected://host/main1.html"];
546     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"pson"];
547     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
548     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"originalload"];
549
550     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
551     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
552     [webView setNavigationDelegate:delegate.get()];
553
554     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"originalload://host/main1.html"]];
555     [webView loadRequest:request];
556
557     TestWebKitAPI::Util::run(&done);
558     done = false;
559
560     auto pidAfterFirstLoad = [webView _webProcessIdentifier];
561
562     EXPECT_EQ(1, numberOfDecidePolicyCalls);
563     EXPECT_EQ(1u, seenPIDs.size());
564     EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad);
565
566     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
567     [webView loadRequest:request];
568
569     TestWebKitAPI::Util::run(&serverRedirected);
570     serverRedirected = false;
571
572     seenPIDs.add([webView _webProcessIdentifier]);
573
574     TestWebKitAPI::Util::run(&done);
575     done = false;
576
577     seenPIDs.add([webView _webProcessIdentifier]);
578
579     EXPECT_FALSE(serverRedirected);
580     EXPECT_EQ(3, numberOfDecidePolicyCalls);
581     EXPECT_EQ(2u, seenPIDs.size());
582 }
583
584 TEST(ProcessSwap, ServerRedirect2)
585 {
586     // This tests a load that *starts out* to the same origin as the previous load, but then redirects to a new origin.
587     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
588     processPoolConfiguration.get().processSwapsOnNavigation = YES;
589     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
590
591     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
592     [webViewConfiguration setProcessPool:processPool.get()];
593     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] init]);
594     [handler1 addRedirectFromURLString:@"pson://host/main2.html" toURLString:@"psonredirected://host/main1.html"];
595     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"pson"];
596     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
597     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"psonredirected"];
598
599     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
600     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
601     [webView setNavigationDelegate:delegate.get()];
602
603     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
604     [webView loadRequest:request];
605
606     TestWebKitAPI::Util::run(&done);
607     done = false;
608
609     auto pidAfterFirstLoad = [webView _webProcessIdentifier];
610
611     EXPECT_FALSE(serverRedirected);
612     EXPECT_EQ(1, numberOfDecidePolicyCalls);
613     EXPECT_EQ(1u, seenPIDs.size());
614     EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad);
615
616     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main2.html"]];
617     [webView loadRequest:request];
618
619     TestWebKitAPI::Util::run(&serverRedirected);
620     serverRedirected = false;
621
622     seenPIDs.add([webView _webProcessIdentifier]);
623
624     TestWebKitAPI::Util::run(&done);
625     done = false;
626
627     seenPIDs.add([webView _webProcessIdentifier]);
628
629     EXPECT_FALSE(serverRedirected);
630     EXPECT_EQ(3, numberOfDecidePolicyCalls);
631     EXPECT_EQ(2u, seenPIDs.size());
632 }
633
634 static const char* sessionStorageTestBytes = R"PSONRESOURCE(
635 <head>
636 <script>
637
638 function log(msg)
639 {
640     window.webkit.messageHandlers.pson.postMessage(msg);
641 }
642
643 window.onload = function(evt) {
644     log(sessionStorage.psonKey);
645     sessionStorage.psonKey = "I exist!";
646 }
647
648 </script>
649 </head>
650 )PSONRESOURCE";
651
652 TEST(ProcessSwap, SessionStorage)
653 {
654     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
655     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
656     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
657
658     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
659     [webViewConfiguration setProcessPool:processPool.get()];
660     auto handler1 = adoptNS([[PSONScheme alloc] initWithBytes:sessionStorageTestBytes]);
661     auto handler2 = adoptNS([[PSONScheme alloc] init]);
662     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
663     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
664
665     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
666     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
667
668     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
669     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
670     [webView setNavigationDelegate:delegate.get()];
671
672     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
673     [webView loadRequest:request];
674
675     TestWebKitAPI::Util::run(&receivedMessage);
676     receivedMessage = false;
677     TestWebKitAPI::Util::run(&done);
678     done = false;
679
680     auto pid1 = [webView _webProcessIdentifier];
681
682     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main.html"]];
683     [webView loadRequest:request];
684
685     TestWebKitAPI::Util::run(&done);
686     done = false;
687
688     auto pid2 = [webView _webProcessIdentifier];
689
690     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
691     [webView loadRequest:request];
692
693     TestWebKitAPI::Util::run(&receivedMessage);
694     receivedMessage = false;
695     TestWebKitAPI::Util::run(&done);
696     done = false;
697
698     auto pid3 = [webView _webProcessIdentifier];
699
700     // Verify the web pages are in different processes
701     EXPECT_NE(pid1, pid2);
702     EXPECT_NE(pid1, pid3);
703     EXPECT_NE(pid2, pid3);
704
705     // Verify the sessionStorage values were as expected
706     EXPECT_EQ([receivedMessages count], 2u);
707     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@""]);
708     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"I exist!"]);
709 }
710
711 static const char* mainFramesOnlyMainFrame = R"PSONRESOURCE(
712 <script>
713 function loaded() {
714     setTimeout('window.frames[0].location.href = "pson2://host2/main.html"', 0);
715 }
716 </script>
717 <body onload="loaded();">
718 Some text
719 <iframe src="pson1://host/iframe.html"></iframe>
720 </body>
721 )PSONRESOURCE";
722
723 static const char* mainFramesOnlySubframe = R"PSONRESOURCE(
724 Some content
725 )PSONRESOURCE";
726
727
728 static const char* mainFramesOnlySubframe2 = R"PSONRESOURCE(
729 <script>
730     window.webkit.messageHandlers.pson.postMessage("Done");
731 </script>
732 )PSONRESOURCE";
733
734 TEST(ProcessSwap, MainFramesOnly)
735 {
736     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
737     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
738     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
739
740     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
741     [webViewConfiguration setProcessPool:processPool.get()];
742     auto handler = adoptNS([[PSONScheme alloc] init]);
743     [handler addMappingFromURLString:@"pson1://host/main.html" toData:mainFramesOnlyMainFrame];
744     [handler addMappingFromURLString:@"pson1://host/iframe" toData:mainFramesOnlySubframe];
745     [handler addMappingFromURLString:@"pson2://host2/main.html" toData:mainFramesOnlySubframe2];
746     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
747     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
748
749     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
750     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
751
752     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
753     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
754     [webView setNavigationDelegate:delegate.get()];
755
756     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
757     [webView loadRequest:request];
758
759     TestWebKitAPI::Util::run(&receivedMessage);
760     receivedMessage = false;
761
762     EXPECT_EQ(1u, seenPIDs.size());
763 }
764
765 TEST(ProcessSwap, OnePreviousProcessRemains)
766 {
767     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
768     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
769     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
770
771     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
772     [webViewConfiguration setProcessPool:processPool.get()];
773     auto handler = adoptNS([[PSONScheme alloc] init]);
774     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
775
776     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
777     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
778     [webView setNavigationDelegate:delegate.get()];
779
780     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host1/main.html"]];
781     [webView loadRequest:request];
782
783     TestWebKitAPI::Util::run(&done);
784     done = false;
785
786     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host2/main.html"]];
787     [webView loadRequest:request];
788
789     TestWebKitAPI::Util::run(&done);
790     done = false;
791
792     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host3/main.html"]];
793     [webView loadRequest:request];
794
795     TestWebKitAPI::Util::run(&done);
796     done = false;
797
798     // Navigations to 3 different domains, we expect to have seen 3 different PIDs
799     EXPECT_EQ(3u, seenPIDs.size());
800
801     // But only 2 of those processes should still be alive
802     EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmed]);
803 }
804
805 static const char* pageCache1Bytes = R"PSONRESOURCE(
806 <script>
807 window.addEventListener('pageshow', function(event) {
808     if (event.persisted)
809         window.webkit.messageHandlers.pson.postMessage("Was persisted");
810 });
811 </script>
812 )PSONRESOURCE";
813
814 TEST(ProcessSwap, PageCache1)
815 {
816     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
817     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
818     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
819
820     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
821     [webViewConfiguration setProcessPool:processPool.get()];
822     auto handler = adoptNS([[PSONScheme alloc] init]);
823     [handler addMappingFromURLString:@"pson1://host/main.html" toData:pageCache1Bytes];
824     [handler addMappingFromURLString:@"pson2://host/main.html" toData:pageCache1Bytes];
825     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
826     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
827
828     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
829     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
830
831     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
832     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
833     [webView setNavigationDelegate:delegate.get()];
834
835     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
836
837     [webView loadRequest:request];
838     TestWebKitAPI::Util::run(&done);
839     done = false;
840
841     auto pidAfterLoad1 = [webView _webProcessIdentifier];
842
843     EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmed]);
844
845     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main.html"]];
846
847     [webView loadRequest:request];
848     TestWebKitAPI::Util::run(&done);
849     done = false;
850
851     auto pidAfterLoad2 = [webView _webProcessIdentifier];
852
853     EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmed]);
854     EXPECT_NE(pidAfterLoad1, pidAfterLoad2);
855
856     [webView goBack];
857     TestWebKitAPI::Util::run(&receivedMessage);
858     receivedMessage = false;
859     TestWebKitAPI::Util::run(&done);
860     done = false;
861
862     auto pidAfterLoad3 = [webView _webProcessIdentifier];
863
864     EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmed]);
865     EXPECT_EQ(pidAfterLoad1, pidAfterLoad3);
866     EXPECT_EQ(1u, [receivedMessages count]);
867     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"Was persisted" ]);
868     EXPECT_EQ(2u, seenPIDs.size());
869
870     [webView goForward];
871     TestWebKitAPI::Util::run(&receivedMessage);
872     receivedMessage = false;
873     TestWebKitAPI::Util::run(&done);
874     done = false;
875
876     auto pidAfterLoad4 = [webView _webProcessIdentifier];
877
878     EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmed]);
879     EXPECT_EQ(pidAfterLoad2, pidAfterLoad4);
880     EXPECT_EQ(2u, [receivedMessages count]);
881     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"Was persisted" ]);
882     EXPECT_EQ(2u, seenPIDs.size());
883 }
884
885 TEST(ProcessSwap, NumberOfPrewarmedProcesses)
886 {
887     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
888     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
889     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
890
891     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
892     [webViewConfiguration setProcessPool:processPool.get()];
893     auto handler = adoptNS([[PSONScheme alloc] init]);
894     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
895
896     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
897     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
898     [webView setNavigationDelegate:delegate.get()];
899
900     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host1/main.html"]];
901     [webView loadRequest:request];
902     TestWebKitAPI::Util::run(&done);
903     done = false;
904
905     EXPECT_EQ(2u, [processPool _webProcessCount]);
906     EXPECT_EQ(1u, [processPool _webProcessCountIgnoringPrewarmed]);
907     EXPECT_EQ(1u, [processPool _prewarmedWebProcessCount]);
908
909     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host3/main.html"]];
910     [webView loadRequest:request];
911     TestWebKitAPI::Util::run(&done);
912     done = false;
913
914     EXPECT_EQ(3u, [processPool _webProcessCount]);
915     EXPECT_EQ(2u, [processPool _webProcessCountIgnoringPrewarmed]);
916     EXPECT_EQ(1u, [processPool _prewarmedWebProcessCount]);
917 }
918
919 static const char* visibilityBytes = R"PSONRESOURCE(
920 <script>
921 window.addEventListener('pageshow', function(event) {
922     var msg = window.location.href + " - pageshow ";
923     msg += event.persisted ? "persisted" : "NOT persisted";
924     window.webkit.messageHandlers.pson.postMessage(msg);
925 });
926
927 window.addEventListener('pagehide', function(event) {
928     var msg = window.location.href + " - pagehide ";
929     msg += event.persisted ? "persisted" : "NOT persisted";
930     window.webkit.messageHandlers.pson.postMessage(msg);
931 });
932 </script>
933 )PSONRESOURCE";
934
935 TEST(ProcessSwap, PageShowHide)
936 {
937     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
938     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
939     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
940
941     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
942     [webViewConfiguration setProcessPool:processPool.get()];
943     auto handler = adoptNS([[PSONScheme alloc] init]);
944     [handler addMappingFromURLString:@"pson1://host/main.html" toData:visibilityBytes];
945     [handler addMappingFromURLString:@"pson2://host/main.html" toData:visibilityBytes];
946     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
947     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
948
949     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
950     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
951
952     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
953     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
954     [webView setNavigationDelegate:delegate.get()];
955
956     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
957
958     [webView loadRequest:request];
959     TestWebKitAPI::Util::run(&receivedMessage);
960     receivedMessage = false;
961     TestWebKitAPI::Util::run(&done);
962     done = false;
963
964     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main.html"]];
965
966     [webView loadRequest:request];
967     TestWebKitAPI::Util::run(&receivedMessage);
968     receivedMessage = false;
969     TestWebKitAPI::Util::run(&done);
970     done = false;
971
972     [webView goBack];
973     TestWebKitAPI::Util::run(&receivedMessage);
974     receivedMessage = false;
975     TestWebKitAPI::Util::run(&done);
976     done = false;
977
978     [webView goForward];
979     TestWebKitAPI::Util::run(&receivedMessage);
980     receivedMessage = false;
981     TestWebKitAPI::Util::run(&done);
982     done = false;
983
984     EXPECT_EQ(7u, [receivedMessages count]);
985     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"pson1://host/main.html - pageshow NOT persisted" ]);
986     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"pson1://host/main.html - pagehide persisted" ]);
987     EXPECT_TRUE([receivedMessages.get()[2] isEqualToString:@"pson2://host/main.html - pageshow NOT persisted" ]);
988     EXPECT_TRUE([receivedMessages.get()[3] isEqualToString:@"pson2://host/main.html - pagehide persisted" ]);
989     EXPECT_TRUE([receivedMessages.get()[4] isEqualToString:@"pson1://host/main.html - pageshow persisted" ]);
990     EXPECT_TRUE([receivedMessages.get()[5] isEqualToString:@"pson1://host/main.html - pagehide persisted" ]);
991     EXPECT_TRUE([receivedMessages.get()[6] isEqualToString:@"pson2://host/main.html - pageshow persisted" ]);
992 }
993
994 // Disabling the page cache explicitly is (for some reason) not available on iOS.
995 #if !TARGET_OS_IPHONE
996 static const char* loadUnloadBytes = R"PSONRESOURCE(
997 <script>
998 window.addEventListener('unload', function(event) {
999     var msg = window.location.href + " - unload";
1000     window.webkit.messageHandlers.pson.postMessage(msg);
1001 });
1002
1003 window.addEventListener('load', function(event) {
1004     var msg = window.location.href + " - load";
1005     window.webkit.messageHandlers.pson.postMessage(msg);
1006 });
1007 </script>
1008 )PSONRESOURCE";
1009
1010 TEST(ProcessSwap, LoadUnload)
1011 {
1012     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1013     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
1014     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1015
1016     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1017     [webViewConfiguration setProcessPool:processPool.get()];
1018     auto handler = adoptNS([[PSONScheme alloc] init]);
1019     [handler addMappingFromURLString:@"pson1://host/main.html" toData:loadUnloadBytes];
1020     [handler addMappingFromURLString:@"pson2://host/main.html" toData:loadUnloadBytes];
1021     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
1022     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
1023
1024     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
1025     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
1026     [[webViewConfiguration preferences] _setUsesPageCache:NO];
1027
1028     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1029     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1030     [webView setNavigationDelegate:delegate.get()];
1031
1032     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
1033
1034     [webView loadRequest:request];
1035     TestWebKitAPI::Util::run(&receivedMessage);
1036     receivedMessage = false;
1037     TestWebKitAPI::Util::run(&done);
1038     done = false;
1039
1040     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main.html"]];
1041
1042     [webView loadRequest:request];
1043     TestWebKitAPI::Util::run(&receivedMessage);
1044     receivedMessage = false;
1045     TestWebKitAPI::Util::run(&done);
1046     done = false;
1047
1048     [webView goBack];
1049     TestWebKitAPI::Util::run(&receivedMessage);
1050     receivedMessage = false;
1051     TestWebKitAPI::Util::run(&done);
1052     done = false;
1053
1054     [webView goForward];
1055     TestWebKitAPI::Util::run(&receivedMessage);
1056     receivedMessage = false;
1057     TestWebKitAPI::Util::run(&done);
1058     done = false;
1059
1060     EXPECT_EQ(7u, [receivedMessages count]);
1061     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"pson1://host/main.html - load" ]);
1062     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"pson1://host/main.html - unload" ]);
1063     EXPECT_TRUE([receivedMessages.get()[2] isEqualToString:@"pson2://host/main.html - load" ]);
1064     EXPECT_TRUE([receivedMessages.get()[3] isEqualToString:@"pson2://host/main.html - unload" ]);
1065     EXPECT_TRUE([receivedMessages.get()[4] isEqualToString:@"pson1://host/main.html - load" ]);
1066     EXPECT_TRUE([receivedMessages.get()[5] isEqualToString:@"pson1://host/main.html - unload" ]);
1067     EXPECT_TRUE([receivedMessages.get()[6] isEqualToString:@"pson2://host/main.html - load" ]);
1068 }
1069 #endif // !TARGET_OS_IPHONE
1070
1071 static const char* sameOriginBlobNavigationTestBytes = R"PSONRESOURCE(
1072 <!DOCTYPE html>
1073 <html>
1074 <body>
1075 <p><a id="link">Click here</a></p>
1076 <script>
1077 const blob = new Blob(['<!DOCTYPE html><html><p>PASS</p></html>'], {type: 'text/html'});
1078 link.href = URL.createObjectURL(blob);
1079 </script>
1080 )PSONRESOURCE";
1081
1082 TEST(ProcessSwap, SameOriginBlobNavigation)
1083 {
1084     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1085     processPoolConfiguration.get().processSwapsOnNavigation = YES;
1086     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1087
1088     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1089     [webViewConfiguration setProcessPool:processPool.get()];
1090     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]);
1091     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
1092
1093     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1094     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1095     [webView setNavigationDelegate:navigationDelegate.get()];
1096     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
1097     [webView setUIDelegate:uiDelegate.get()];
1098
1099     numberOfDecidePolicyCalls = 0;
1100     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]]];
1101
1102     TestWebKitAPI::Util::run(&done);
1103     done = false;
1104     auto pid1 = [webView _webProcessIdentifier];
1105     EXPECT_TRUE(!!pid1);
1106
1107     [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').click()" completionHandler: nil];
1108
1109     TestWebKitAPI::Util::run(&done);
1110     done = false;
1111     auto pid2 = [webView _webProcessIdentifier];
1112     EXPECT_TRUE(!!pid2);
1113     EXPECT_EQ(2, numberOfDecidePolicyCalls);
1114     EXPECT_EQ(pid1, pid2);
1115 }
1116
1117 TEST(ProcessSwap, CrossOriginBlobNavigation)
1118 {
1119     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1120     processPoolConfiguration.get().processSwapsOnNavigation = YES;
1121     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1122
1123     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1124     [webViewConfiguration setProcessPool:processPool.get()];
1125     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]);
1126     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
1127     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
1128
1129     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1130     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1131     [webView setNavigationDelegate:navigationDelegate.get()];
1132     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
1133     [webView setUIDelegate:uiDelegate.get()];
1134
1135     numberOfDecidePolicyCalls = 0;
1136     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]]];
1137     TestWebKitAPI::Util::run(&done);
1138     done = false;
1139     auto pid1 = [webView _webProcessIdentifier];
1140     EXPECT_TRUE(!!pid1);
1141
1142     bool finishedRunningScript = false;
1143     String blobURL;
1144     [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').href" completionHandler: [&] (id result, NSError *error) {
1145         blobURL = String([NSString stringWithFormat:@"%@", result]);
1146         finishedRunningScript = true;
1147     }];
1148     TestWebKitAPI::Util::run(&finishedRunningScript);
1149
1150     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main1.html"]]];
1151     TestWebKitAPI::Util::run(&done);
1152     done = false;
1153     auto pid2 = [webView _webProcessIdentifier];
1154     EXPECT_TRUE(!!pid2);
1155
1156     finishedRunningScript = false;
1157     String script = "document.getElementById('link').href = '" + blobURL + "'";
1158     [webView _evaluateJavaScriptWithoutUserGesture:(NSString *)script completionHandler: [&] (id result, NSError *error) {
1159         finishedRunningScript = true;
1160     }];
1161     TestWebKitAPI::Util::run(&finishedRunningScript);
1162
1163     // This navigation will fail.
1164     [webView _evaluateJavaScriptWithoutUserGesture:@"document.getElementById('link').click()" completionHandler: [&] (id result, NSError *error) {
1165         done = true;
1166     }];
1167     TestWebKitAPI::Util::run(&done);
1168     done = false;
1169     auto pid3 = [webView _webProcessIdentifier];
1170     EXPECT_TRUE(!!pid3);
1171
1172     EXPECT_EQ(2, numberOfDecidePolicyCalls);
1173     EXPECT_NE(pid1, pid2);
1174     EXPECT_EQ(pid2, pid3);
1175 }
1176
1177 TEST(ProcessSwap, NavigateToAboutBlank)
1178 {
1179     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1180     processPoolConfiguration.get().processSwapsOnNavigation = YES;
1181     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1182
1183     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1184     [webViewConfiguration setProcessPool:processPool.get()];
1185     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]);
1186     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
1187
1188     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1189     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1190     [webView setNavigationDelegate:navigationDelegate.get()];
1191     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
1192     [webView setUIDelegate:uiDelegate.get()];
1193
1194     numberOfDecidePolicyCalls = 0;
1195     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]]];
1196     TestWebKitAPI::Util::run(&done);
1197     done = false;
1198     auto pid1 = [webView _webProcessIdentifier];
1199     EXPECT_TRUE(!!pid1);
1200
1201     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
1202     TestWebKitAPI::Util::run(&done);
1203     done = false;
1204     auto pid2 = [webView _webProcessIdentifier];
1205     EXPECT_TRUE(!!pid2);
1206
1207     EXPECT_EQ(2, numberOfDecidePolicyCalls);
1208     EXPECT_EQ(pid1, pid2);
1209 }
1210
1211 TEST(ProcessSwap, NavigateToDataURL)
1212 {
1213     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1214     processPoolConfiguration.get().processSwapsOnNavigation = YES;
1215     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1216
1217     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1218     [webViewConfiguration setProcessPool:processPool.get()];
1219     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:sameOriginBlobNavigationTestBytes]);
1220     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
1221
1222     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1223     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1224     [webView setNavigationDelegate:navigationDelegate.get()];
1225     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
1226     [webView setUIDelegate:uiDelegate.get()];
1227
1228     numberOfDecidePolicyCalls = 0;
1229     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]]];
1230     TestWebKitAPI::Util::run(&done);
1231     done = false;
1232     auto pid1 = [webView _webProcessIdentifier];
1233     EXPECT_TRUE(!!pid1);
1234
1235     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"data:text/plain,PASS"]]];
1236     TestWebKitAPI::Util::run(&done);
1237     done = false;
1238     auto pid2 = [webView _webProcessIdentifier];
1239     EXPECT_TRUE(!!pid2);
1240
1241     EXPECT_EQ(2, numberOfDecidePolicyCalls);
1242     EXPECT_EQ(pid1, pid2);
1243 }
1244
1245 TEST(ProcessSwap, ProcessReuse)
1246 {
1247     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1248     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
1249     [processPoolConfiguration setAlwaysKeepAndReuseSwappedProcesses:YES];
1250     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1251
1252     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1253     [webViewConfiguration setProcessPool:processPool.get()];
1254     auto handler = adoptNS([[PSONScheme alloc] init]);
1255     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
1256
1257     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1258     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1259     [webView setNavigationDelegate:delegate.get()];
1260
1261     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host1/main.html"]];
1262     [webView loadRequest:request];
1263
1264     TestWebKitAPI::Util::run(&done);
1265     done = false;
1266
1267     auto pid1 = [webView _webProcessIdentifier];
1268
1269     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host2/main.html"]];
1270     [webView loadRequest:request];
1271
1272     TestWebKitAPI::Util::run(&done);
1273     done = false;
1274
1275     auto pid2 = [webView _webProcessIdentifier];
1276
1277     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host1/main2.html"]];
1278     [webView loadRequest:request];
1279
1280     TestWebKitAPI::Util::run(&done);
1281     done = false;
1282
1283     auto pid3 = [webView _webProcessIdentifier];
1284
1285     // Two process swaps have occurred, but we should only have ever seen 2 pids.
1286     EXPECT_EQ(2u, seenPIDs.size());
1287     EXPECT_NE(pid1, pid2);
1288     EXPECT_NE(pid2, pid3);
1289     EXPECT_EQ(pid1, pid3);
1290 }
1291
1292 static const char* navigateToInvalidURLTestBytes = R"PSONRESOURCE(
1293 <!DOCTYPE html>
1294 <html>
1295 <body onload="setTimeout(() => alert('DONE'), 0); location.href = 'http://A=a%B=b'">
1296 )PSONRESOURCE";
1297
1298 TEST(ProcessSwap, NavigateToInvalidURL)
1299 {
1300     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
1301     processPoolConfiguration.get().processSwapsOnNavigation = YES;
1302     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
1303
1304     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
1305     [webViewConfiguration setProcessPool:processPool.get()];
1306     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:navigateToInvalidURLTestBytes]);
1307     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
1308
1309     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
1310     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
1311     [webView setNavigationDelegate:navigationDelegate.get()];
1312     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
1313     [webView setUIDelegate:uiDelegate.get()];
1314
1315     numberOfDecidePolicyCalls = 0;
1316     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]]];
1317     TestWebKitAPI::Util::run(&done);
1318     done = false;
1319     auto pid1 = [webView _webProcessIdentifier];
1320     EXPECT_TRUE(!!pid1);
1321
1322     TestWebKitAPI::Util::run(&didReceiveAlert);
1323     didReceiveAlert = false;
1324     auto pid2 = [webView _webProcessIdentifier];
1325     EXPECT_TRUE(!!pid2);
1326
1327     EXPECT_EQ(2, numberOfDecidePolicyCalls);
1328     EXPECT_EQ(pid1, pid2);
1329 }
1330
1331 #endif // WK_API_ENABLED