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