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