2f51b15696dc5fa26af5bd29e2a4508d3e99ba5d
[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 didCreateWebView;
58 static int numberOfDecidePolicyCalls;
59
60 static RetainPtr<NSMutableArray> receivedMessages = adoptNS([@[] mutableCopy]);
61 static bool receivedMessage;
62 static bool serverRedirected;
63 static HashSet<pid_t> seenPIDs;
64 @interface PSONMessageHandler : NSObject <WKScriptMessageHandler>
65 @end
66
67 @implementation PSONMessageHandler
68 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
69 {
70     if ([message body])
71         [receivedMessages addObject:[message body]];
72     else
73         [receivedMessages addObject:@""];
74
75     receivedMessage = true;
76 }
77 @end
78
79 @interface PSONNavigationDelegate : NSObject <WKNavigationDelegate>
80 @end
81
82 @implementation PSONNavigationDelegate
83
84 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
85 {
86     seenPIDs.add([webView _webProcessIdentifier]);
87     done = true;
88 }
89
90 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
91 {
92     ++numberOfDecidePolicyCalls;
93     seenPIDs.add([webView _webProcessIdentifier]);
94     decisionHandler(WKNavigationActionPolicyAllow);
95 }
96
97 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
98 {
99     seenPIDs.add([webView _webProcessIdentifier]);
100     serverRedirected = true;
101 }
102
103 @end
104
105 static RetainPtr<WKWebView> createdWebView;
106
107 @interface PSONUIDelegate : NSObject <WKUIDelegatePrivate>
108 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate;
109 @end
110
111 @implementation PSONUIDelegate {
112     RetainPtr<PSONNavigationDelegate> _navigationDelegate;
113 }
114
115 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate
116 {
117     if (!(self = [super init]))
118         return nil;
119
120     _navigationDelegate = navigationDelegate;
121     return self;
122 }
123
124 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
125 {
126     createdWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
127     [createdWebView setNavigationDelegate:_navigationDelegate.get()];
128     didCreateWebView = true;
129     return createdWebView.get();
130 }
131
132 @end
133
134 @interface PSONScheme : NSObject <WKURLSchemeHandler> {
135     const char* _bytes;
136     HashMap<String, String> _redirects;
137 }
138 - (instancetype)initWithBytes:(const char*)bytes;
139 - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString;
140 @end
141
142 @implementation PSONScheme
143
144 - (instancetype)initWithBytes:(const char*)bytes
145 {
146     self = [super init];
147     _bytes = bytes;
148     return self;
149 }
150
151 - (void)addRedirectFromURLString:(NSString *)sourceURLString toURLString:(NSString *)destinationURLString
152 {
153     _redirects.set(sourceURLString, destinationURLString);
154 }
155
156 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
157 {
158     NSURL *finalURL = task.request.URL;
159     auto target = _redirects.get(task.request.URL.absoluteString);
160     if (!target.isEmpty()) {
161         auto redirectResponse = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:nil expectedContentLength:0 textEncodingName:nil]);
162
163         finalURL = [NSURL URLWithString:(NSString *)target];
164         auto request = adoptNS([[NSURLRequest alloc] initWithURL:finalURL]);
165
166         [(id<WKURLSchemeTaskPrivate>)task _didPerformRedirection:redirectResponse.get() newRequest:request.get()];
167     }
168
169     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:finalURL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
170     [task didReceiveResponse:response.get()];
171
172     if (_bytes) {
173         RetainPtr<NSData> data = adoptNS([[NSData alloc] initWithBytesNoCopy:(void *)_bytes length:strlen(_bytes) freeWhenDone:NO]);
174         [task didReceiveData:data.get()];
175     } else
176         [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
177
178     [task didFinish];
179 }
180
181 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
182 {
183 }
184
185 @end
186
187 static const char* testBytes = R"PSONRESOURCE(
188 <head>
189 <script>
190
191 function log(msg)
192 {
193     window.webkit.messageHandlers.pson.postMessage(msg);
194 }
195
196 window.onload = function(evt) {
197     if (window.history.state != "onloadCalled")
198         setTimeout('window.history.replaceState("onloadCalled", "");', 0);
199 }
200
201 window.onpageshow = function(evt) {
202     log("PageShow called. Persisted: " + evt.persisted + ", and window.history.state is: " + window.history.state);
203 }
204
205 </script>
206 </head>
207 )PSONRESOURCE";
208
209 #if PLATFORM(MAC)
210
211 static const char* windowOpenCrossOriginNoOpenerTestBytes = R"PSONRESOURCE(
212 <script>
213 window.onload = function() {
214     window.open("pson2://host/main2.html", "_blank", "noopener");
215 }
216 </script>
217 )PSONRESOURCE";
218
219 static const char* windowOpenCrossOriginWithOpenerTestBytes = R"PSONRESOURCE(
220 <script>
221 window.onload = function() {
222     window.open("pson2://host/main2.html");
223 }
224 </script>
225 )PSONRESOURCE";
226
227 static const char* windowOpenSameOriginNoOpenerTestBytes = R"PSONRESOURCE(
228 <script>
229 window.onload = function() {
230     if (!opener)
231         window.open("pson1://host/main2.html", "_blank", "noopener");
232 }
233 </script>
234 )PSONRESOURCE";
235
236 static const char* dummyBytes = R"PSONRESOURCE(
237 <body>TEST</body>
238 )PSONRESOURCE";
239
240 #endif // PLATFORM(MAC)
241
242 TEST(ProcessSwap, Basic)
243 {
244     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
245     processPoolConfiguration.get().processSwapsOnNavigation = YES;
246     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
247
248     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
249     [webViewConfiguration setProcessPool:processPool.get()];
250     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] init]);
251     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
252     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
253
254     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
255     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
256     [webView setNavigationDelegate:delegate.get()];
257
258     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
259     [webView loadRequest:request];
260
261     TestWebKitAPI::Util::run(&done);
262     done = false;
263
264     auto pid1 = [webView _webProcessIdentifier];
265
266     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main2.html"]];
267     [webView loadRequest:request];
268
269     TestWebKitAPI::Util::run(&done);
270     done = false;
271
272     auto pid2 = [webView _webProcessIdentifier];
273
274     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
275     [webView loadRequest:request];
276
277     TestWebKitAPI::Util::run(&done);
278     done = false;
279
280     auto pid3 = [webView _webProcessIdentifier];
281
282     EXPECT_EQ(pid1, pid2);
283     EXPECT_FALSE(pid2 == pid3);
284
285     // 3 loads, 3 decidePolicy calls (e.g. the load that did perform a process swap should not have generated an additional decidePolicy call)
286     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
287 }
288
289 TEST(ProcessSwap, Back)
290 {
291     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
292     processPoolConfiguration.get().processSwapsOnNavigation = YES;
293     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
294
295     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
296     [webViewConfiguration setProcessPool:processPool.get()];
297     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:testBytes]);
298     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
299     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
300     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
301
302     RetainPtr<PSONMessageHandler> messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
303     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
304
305     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
306     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
307     [webView setNavigationDelegate:delegate.get()];
308
309     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
310     [webView loadRequest:request];
311
312     TestWebKitAPI::Util::run(&receivedMessage);
313     receivedMessage = false;
314     TestWebKitAPI::Util::run(&done);
315     done = false;
316
317     auto pid1 = [webView _webProcessIdentifier];
318
319     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
320     [webView loadRequest:request];
321
322     TestWebKitAPI::Util::run(&done);
323     done = false;
324
325     auto pid2 = [webView _webProcessIdentifier];
326
327     [webView goBack];
328
329     TestWebKitAPI::Util::run(&receivedMessage);
330     receivedMessage = false;
331     TestWebKitAPI::Util::run(&done);
332     done = false;
333
334     auto pid3 = [webView _webProcessIdentifier];
335
336     // 3 loads, 3 decidePolicy calls (e.g. any load that performs a process swap should not have generated an
337     // additional decidePolicy call as a result of the process swap)
338     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
339
340     EXPECT_EQ([receivedMessages count], 2u);
341     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"PageShow called. Persisted: false, and window.history.state is: null"]);
342
343     // FIXME: We'd like to get the page restoring from the page cache like before process swapping, which will make Persisted be "true"
344     // For now it's expected to be false"
345     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"PageShow called. Persisted: false, and window.history.state is: onloadCalled"]);
346
347     EXPECT_FALSE(pid1 == pid2);
348     EXPECT_FALSE(pid2 == pid3);
349
350     // FIXME: Ideally we'd like to get process caching happening such that pid1 and pid3 are equal.
351     // But for now they should not be.
352     EXPECT_FALSE(pid1 == pid3);
353 }
354
355 #if PLATFORM(MAC)
356
357 TEST(ProcessSwap, CrossOriginWindowOpenNoOpener)
358 {
359     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
360     processPoolConfiguration.get().processSwapsOnNavigation = YES;
361     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
362
363     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
364     [webViewConfiguration setProcessPool:processPool.get()];
365     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginNoOpenerTestBytes]);
366     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
367     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
368     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
369
370     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
371     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
372     [webView setNavigationDelegate:navigationDelegate.get()];
373     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
374     [webView setUIDelegate:uiDelegate.get()];
375
376     numberOfDecidePolicyCalls = 0;
377     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
378     [webView loadRequest:request];
379
380     TestWebKitAPI::Util::run(&done);
381     done = false;
382
383     TestWebKitAPI::Util::run(&didCreateWebView);
384     didCreateWebView = false;
385
386     TestWebKitAPI::Util::run(&done);
387
388     EXPECT_EQ(2, numberOfDecidePolicyCalls);
389
390     auto pid1 = [webView _webProcessIdentifier];
391     EXPECT_TRUE(!!pid1);
392     auto pid2 = [createdWebView _webProcessIdentifier];
393     EXPECT_TRUE(!!pid2);
394
395     EXPECT_NE(pid1, pid2);
396 }
397
398 TEST(ProcessSwap, CrossOriginWindowOpenWithOpener)
399 {
400     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
401     processPoolConfiguration.get().processSwapsOnNavigation = YES;
402     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
403
404     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
405     [webViewConfiguration setProcessPool:processPool.get()];
406     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginWithOpenerTestBytes]);
407     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
408     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
409     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
410
411     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
412     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
413     [webView setNavigationDelegate:navigationDelegate.get()];
414     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
415     [webView setUIDelegate:uiDelegate.get()];
416
417     numberOfDecidePolicyCalls = 0;
418     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
419     [webView loadRequest:request];
420
421     TestWebKitAPI::Util::run(&done);
422     done = false;
423
424     TestWebKitAPI::Util::run(&didCreateWebView);
425     didCreateWebView = false;
426
427     TestWebKitAPI::Util::run(&done);
428
429     EXPECT_EQ(2, numberOfDecidePolicyCalls);
430
431     auto pid1 = [webView _webProcessIdentifier];
432     EXPECT_TRUE(!!pid1);
433     auto pid2 = [createdWebView _webProcessIdentifier];
434     EXPECT_TRUE(!!pid2);
435
436     // FIXME: This should eventually be false once we support process swapping when there is an opener.
437     EXPECT_EQ(pid1, pid2);
438 }
439
440 TEST(ProcessSwap, SameOriginWindowOpenNoOpener)
441 {
442     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
443     processPoolConfiguration.get().processSwapsOnNavigation = YES;
444     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
445
446     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
447     [webViewConfiguration setProcessPool:processPool.get()];
448     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenSameOriginNoOpenerTestBytes]);
449     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
450
451     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
452     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
453     [webView setNavigationDelegate:navigationDelegate.get()];
454     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
455     [webView setUIDelegate:uiDelegate.get()];
456
457     numberOfDecidePolicyCalls = 0;
458     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
459     [webView loadRequest:request];
460
461     TestWebKitAPI::Util::run(&done);
462     done = false;
463
464     TestWebKitAPI::Util::run(&didCreateWebView);
465     didCreateWebView = false;
466
467     TestWebKitAPI::Util::run(&done);
468
469     EXPECT_EQ(2, numberOfDecidePolicyCalls);
470
471     auto pid1 = [webView _webProcessIdentifier];
472     EXPECT_TRUE(!!pid1);
473     auto pid2 = [createdWebView _webProcessIdentifier];
474     EXPECT_TRUE(!!pid2);
475
476     EXPECT_EQ(pid1, pid2);
477 }
478
479 #endif // PLATFORM(MAC)
480
481 TEST(ProcessSwap, ServerRedirectFromNewWebView)
482 {
483     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
484     processPoolConfiguration.get().processSwapsOnNavigation = YES;
485     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
486
487     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
488     [webViewConfiguration setProcessPool:processPool.get()];
489     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] init]);
490     [handler addRedirectFromURLString:@"pson://host/main1.html" toURLString:@"psonredirected://host/main1.html"];
491     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
492
493     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
494     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
495     [webView setNavigationDelegate:delegate.get()];
496
497     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
498     [webView loadRequest:request];
499
500     TestWebKitAPI::Util::run(&serverRedirected);
501     serverRedirected = false;
502
503     seenPIDs.add([webView _webProcessIdentifier]);
504
505     TestWebKitAPI::Util::run(&done);
506     done = false;
507
508     seenPIDs.add([webView _webProcessIdentifier]);
509
510     EXPECT_FALSE(serverRedirected);
511     EXPECT_EQ(2, numberOfDecidePolicyCalls);
512     EXPECT_EQ(1u, seenPIDs.size());
513 }
514
515 TEST(ProcessSwap, ServerRedirect)
516 {
517     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
518     processPoolConfiguration.get().processSwapsOnNavigation = YES;
519     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
520
521     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
522     [webViewConfiguration setProcessPool:processPool.get()];
523     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] init]);
524     [handler1 addRedirectFromURLString:@"pson://host/main1.html" toURLString:@"psonredirected://host/main1.html"];
525     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"pson"];
526     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
527     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"originalload"];
528
529     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
530     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
531     [webView setNavigationDelegate:delegate.get()];
532
533     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"originalload://host/main1.html"]];
534     [webView loadRequest:request];
535
536     TestWebKitAPI::Util::run(&done);
537     done = false;
538
539     auto pidAfterFirstLoad = [webView _webProcessIdentifier];
540
541     EXPECT_EQ(1, numberOfDecidePolicyCalls);
542     EXPECT_EQ(1u, seenPIDs.size());
543     EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad);
544
545     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
546     [webView loadRequest:request];
547
548     TestWebKitAPI::Util::run(&serverRedirected);
549     serverRedirected = false;
550
551     seenPIDs.add([webView _webProcessIdentifier]);
552
553     TestWebKitAPI::Util::run(&done);
554     done = false;
555
556     seenPIDs.add([webView _webProcessIdentifier]);
557
558     EXPECT_FALSE(serverRedirected);
559     EXPECT_EQ(3, numberOfDecidePolicyCalls);
560     EXPECT_EQ(2u, seenPIDs.size());
561 }
562
563 TEST(ProcessSwap, ServerRedirect2)
564 {
565     // This tests a load that *starts out* to the same origin as the previous load, but then redirects to a new origin.
566     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
567     processPoolConfiguration.get().processSwapsOnNavigation = YES;
568     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
569
570     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
571     [webViewConfiguration setProcessPool:processPool.get()];
572     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] init]);
573     [handler1 addRedirectFromURLString:@"pson://host/main2.html" toURLString:@"psonredirected://host/main1.html"];
574     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"pson"];
575     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
576     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"psonredirected"];
577
578     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
579     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
580     [webView setNavigationDelegate:delegate.get()];
581
582     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main1.html"]];
583     [webView loadRequest:request];
584
585     TestWebKitAPI::Util::run(&done);
586     done = false;
587
588     auto pidAfterFirstLoad = [webView _webProcessIdentifier];
589
590     EXPECT_FALSE(serverRedirected);
591     EXPECT_EQ(1, numberOfDecidePolicyCalls);
592     EXPECT_EQ(1u, seenPIDs.size());
593     EXPECT_TRUE(*seenPIDs.begin() == pidAfterFirstLoad);
594
595     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host/main2.html"]];
596     [webView loadRequest:request];
597
598     TestWebKitAPI::Util::run(&serverRedirected);
599     serverRedirected = false;
600
601     seenPIDs.add([webView _webProcessIdentifier]);
602
603     TestWebKitAPI::Util::run(&done);
604     done = false;
605
606     seenPIDs.add([webView _webProcessIdentifier]);
607
608     EXPECT_FALSE(serverRedirected);
609     EXPECT_EQ(3, numberOfDecidePolicyCalls);
610     EXPECT_EQ(2u, seenPIDs.size());
611 }
612
613 static const char* sessionStorageTestBytes = R"PSONRESOURCE(
614 <head>
615 <script>
616
617 function log(msg)
618 {
619     window.webkit.messageHandlers.pson.postMessage(msg);
620 }
621
622 window.onload = function(evt) {
623     log(sessionStorage.psonKey);
624     sessionStorage.psonKey = "I exist!";
625 }
626
627 </script>
628 </head>
629 )PSONRESOURCE";
630
631 TEST(ProcessSwap, SessionStorage)
632 {
633     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
634     [processPoolConfiguration setProcessSwapsOnNavigation:YES];
635     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
636
637     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
638     [webViewConfiguration setProcessPool:processPool.get()];
639     auto handler1 = adoptNS([[PSONScheme alloc] initWithBytes:sessionStorageTestBytes]);
640     auto handler2 = adoptNS([[PSONScheme alloc] init]);
641     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
642     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
643
644     auto messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
645     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
646
647     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
648     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
649     [webView setNavigationDelegate:delegate.get()];
650
651     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
652     [webView loadRequest:request];
653
654     TestWebKitAPI::Util::run(&receivedMessage);
655     receivedMessage = false;
656     TestWebKitAPI::Util::run(&done);
657     done = false;
658
659     auto pid1 = [webView _webProcessIdentifier];
660
661     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main.html"]];
662     [webView loadRequest:request];
663
664     TestWebKitAPI::Util::run(&done);
665     done = false;
666
667     auto pid2 = [webView _webProcessIdentifier];
668
669     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main.html"]];
670     [webView loadRequest:request];
671
672     TestWebKitAPI::Util::run(&receivedMessage);
673     receivedMessage = false;
674     TestWebKitAPI::Util::run(&done);
675     done = false;
676
677     auto pid3 = [webView _webProcessIdentifier];
678
679     // Verify the web pages are in different processes
680     EXPECT_NE(pid1, pid2);
681     EXPECT_NE(pid1, pid3);
682     EXPECT_NE(pid2, pid3);
683
684     // Verify the sessionStorage values were as expected
685     EXPECT_EQ([receivedMessages count], 2u);
686     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@""]);
687     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"I exist!"]);
688 }
689
690 #endif // WK_API_ENABLED