Unreviewed, disable new window.open() API tests introduced in r230051 on iOS.
[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/WKPreferencesPrivate.h>
32 #import <WebKit/WKProcessPoolPrivate.h>
33 #import <WebKit/WKUIDelegatePrivate.h>
34 #import <WebKit/WKURLSchemeHandler.h>
35 #import <WebKit/WKURLSchemeTaskPrivate.h>
36 #import <WebKit/WKWebViewConfigurationPrivate.h>
37 #import <WebKit/WKWebViewPrivate.h>
38 #import <WebKit/WKWebsiteDataStorePrivate.h>
39 #import <WebKit/WKWebsiteDataStoreRef.h>
40 #import <WebKit/WebKit.h>
41 #import <WebKit/_WKExperimentalFeature.h>
42 #import <WebKit/_WKProcessPoolConfiguration.h>
43 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
44 #import <WebKit/_WKWebsitePolicies.h>
45 #import <wtf/Deque.h>
46 #import <wtf/HashMap.h>
47 #import <wtf/RetainPtr.h>
48 #import <wtf/Vector.h>
49 #import <wtf/text/StringHash.h>
50 #import <wtf/text/WTFString.h>
51
52 #if WK_API_ENABLED
53
54 static bool done;
55 static bool didCreateWebView;
56 static int numberOfDecidePolicyCalls;
57
58 static RetainPtr<NSMutableArray> receivedMessages = adoptNS([@[] mutableCopy]);
59 static bool receivedMessage;
60 @interface PSONMessageHandler : NSObject <WKScriptMessageHandler>
61 @end
62
63 @implementation PSONMessageHandler
64 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
65 {
66     [receivedMessages addObject:[message body]];
67     receivedMessage = true;
68 }
69 @end
70
71 @interface PSONNavigationDelegate : NSObject <WKNavigationDelegate>
72 @end
73
74 @implementation PSONNavigationDelegate
75
76 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
77 {
78     done = true;
79 }
80
81 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
82 {
83     ++numberOfDecidePolicyCalls;
84     decisionHandler(WKNavigationActionPolicyAllow);
85 }
86
87 @end
88
89 static RetainPtr<WKWebView> createdWebView;
90
91 @interface PSONUIDelegate : NSObject <WKUIDelegatePrivate>
92 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate;
93 @end
94
95 @implementation PSONUIDelegate {
96     RetainPtr<PSONNavigationDelegate> _navigationDelegate;
97 }
98
99 - (instancetype)initWithNavigationDelegate:(PSONNavigationDelegate *)navigationDelegate
100 {
101     if (!(self = [super init]))
102         return nil;
103
104     _navigationDelegate = navigationDelegate;
105     return self;
106 }
107
108 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
109 {
110     createdWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
111     [createdWebView setNavigationDelegate:_navigationDelegate.get()];
112     didCreateWebView = true;
113     return createdWebView.get();
114 }
115
116 @end
117
118 @interface PSONScheme : NSObject <WKURLSchemeHandler> {
119     const char* _bytes;
120 }
121 - (instancetype)initWithBytes:(const char*)bytes;
122 @end
123
124 @implementation PSONScheme
125
126 - (instancetype)initWithBytes:(const char*)bytes
127 {
128     self = [super init];
129     _bytes = bytes;
130     return self;
131 }
132
133 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
134 {
135     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
136     [task didReceiveResponse:response.get()];
137
138     if (_bytes) {
139         RetainPtr<NSData> data = adoptNS([[NSData alloc] initWithBytesNoCopy:(void *)_bytes length:strlen(_bytes) freeWhenDone:NO]);
140         [task didReceiveData:data.get()];
141     } else
142         [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
143
144     [task didFinish];
145 }
146
147 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
148 {
149 }
150
151 @end
152
153 static const char* testBytes = R"PSONRESOURCE(
154 <head>
155 <script>
156
157 function log(msg)
158 {
159     window.webkit.messageHandlers.pson.postMessage(msg);
160 }
161
162 window.onload = function(evt) {
163     if (window.history.state != "onloadCalled")
164         setTimeout('window.history.replaceState("onloadCalled", "");', 0);
165 }
166
167 window.onpageshow = function(evt) {
168     log("PageShow called. Persisted: " + evt.persisted + ", and window.history.state is: " + window.history.state);
169 }
170
171 </script>
172 </head>
173 )PSONRESOURCE";
174
175 static const char* windowOpenCrossOriginNoOpenerTestBytes = R"PSONRESOURCE(
176 <script>
177 window.onload = function() {
178     window.open("pson2://host/main2.html", "_blank", "noopener");
179 }
180 </script>
181 )PSONRESOURCE";
182
183 static const char* windowOpenCrossOriginWithOpenerTestBytes = R"PSONRESOURCE(
184 <script>
185 window.onload = function() {
186     window.open("pson2://host/main2.html");
187 }
188 </script>
189 )PSONRESOURCE";
190
191 static const char* windowOpenSameOriginNoOpenerTestBytes = R"PSONRESOURCE(
192 <script>
193 window.onload = function() {
194     if (!opener)
195         window.open("pson1://host/main2.html", "_blank", "noopener");
196 }
197 </script>
198 )PSONRESOURCE";
199
200 static const char* dummyBytes = R"PSONRESOURCE(
201 <body>TEST</body>
202 )PSONRESOURCE";
203
204 TEST(ProcessSwap, Basic)
205 {
206     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
207     processPoolConfiguration.get().processSwapsOnNavigation = YES;
208     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
209
210     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
211     [webViewConfiguration setProcessPool:processPool.get()];
212     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] init]);
213     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
214     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON2"];
215
216     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
217     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
218     [webView setNavigationDelegate:delegate.get()];
219
220     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
221     [webView loadRequest:request];
222
223     TestWebKitAPI::Util::run(&done);
224     done = false;
225
226     auto pid1 = [webView _webProcessIdentifier];
227
228     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main2.html"]];
229     [webView loadRequest:request];
230
231     TestWebKitAPI::Util::run(&done);
232     done = false;
233
234     auto pid2 = [webView _webProcessIdentifier];
235
236     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
237     [webView loadRequest:request];
238
239     TestWebKitAPI::Util::run(&done);
240     done = false;
241
242     auto pid3 = [webView _webProcessIdentifier];
243
244     EXPECT_EQ(pid1, pid2);
245     EXPECT_FALSE(pid2 == pid3);
246
247     // 3 loads, 3 decidePolicy calls (e.g. the load that did perform a process swap should not have generated an additional decidePolicy call)
248     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
249 }
250
251 TEST(ProcessSwap, Back)
252 {
253     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
254     processPoolConfiguration.get().processSwapsOnNavigation = YES;
255     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
256
257     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
258     [webViewConfiguration setProcessPool:processPool.get()];
259     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:testBytes]);
260     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] init]);
261     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
262     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
263
264     RetainPtr<PSONMessageHandler> messageHandler = adoptNS([[PSONMessageHandler alloc] init]);
265     [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"pson"];
266
267     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
268     auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
269     [webView setNavigationDelegate:delegate.get()];
270
271     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
272     [webView loadRequest:request];
273
274     TestWebKitAPI::Util::run(&receivedMessage);
275     receivedMessage = false;
276     TestWebKitAPI::Util::run(&done);
277     done = false;
278
279     auto pid1 = [webView _webProcessIdentifier];
280
281     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson2://host/main2.html"]];
282     [webView loadRequest:request];
283
284     TestWebKitAPI::Util::run(&done);
285     done = false;
286
287     auto pid2 = [webView _webProcessIdentifier];
288
289     [webView goBack];
290
291     TestWebKitAPI::Util::run(&receivedMessage);
292     receivedMessage = false;
293     TestWebKitAPI::Util::run(&done);
294     done = false;
295
296     auto pid3 = [webView _webProcessIdentifier];
297
298     // 3 loads, 3 decidePolicy calls (e.g. any load that performs a process swap should not have generated an
299     // additional decidePolicy call as a result of the process swap)
300     EXPECT_EQ(numberOfDecidePolicyCalls, 3);
301
302     EXPECT_EQ([receivedMessages count], 2u);
303     EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"PageShow called. Persisted: false, and window.history.state is: null"]);
304
305     // FIXME: We'd like to get the page restoring from the page cache like before process swapping, which will make Persisted be "true"
306     // For now it's expected to be false"
307     EXPECT_TRUE([receivedMessages.get()[1] isEqualToString:@"PageShow called. Persisted: false, and window.history.state is: onloadCalled"]);
308
309     EXPECT_FALSE(pid1 == pid2);
310     EXPECT_FALSE(pid2 == pid3);
311
312     // FIXME: Ideally we'd like to get process caching happening such that pid1 and pid3 are equal.
313     // But for now they should not be.
314     EXPECT_FALSE(pid1 == pid3);
315 }
316
317 #if PLATFORM(MAC)
318
319 TEST(ProcessSwap, CrossOriginWindowOpenNoOpener)
320 {
321     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
322     processPoolConfiguration.get().processSwapsOnNavigation = YES;
323     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
324
325     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
326     [webViewConfiguration setProcessPool:processPool.get()];
327     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginNoOpenerTestBytes]);
328     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
329     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
330     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
331
332     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
333     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
334     [webView setNavigationDelegate:navigationDelegate.get()];
335     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
336     [webView setUIDelegate:uiDelegate.get()];
337
338     numberOfDecidePolicyCalls = 0;
339     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
340     [webView loadRequest:request];
341
342     TestWebKitAPI::Util::run(&done);
343     done = false;
344
345     TestWebKitAPI::Util::run(&didCreateWebView);
346     didCreateWebView = false;
347
348     TestWebKitAPI::Util::run(&done);
349
350     EXPECT_EQ(2, numberOfDecidePolicyCalls);
351
352     auto pid1 = [webView _webProcessIdentifier];
353     EXPECT_TRUE(!!pid1);
354     auto pid2 = [createdWebView _webProcessIdentifier];
355     EXPECT_TRUE(!!pid2);
356
357     EXPECT_NE(pid1, pid2);
358 }
359
360 TEST(ProcessSwap, CrossOriginWindowOpenWithOpener)
361 {
362     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
363     processPoolConfiguration.get().processSwapsOnNavigation = YES;
364     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
365
366     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
367     [webViewConfiguration setProcessPool:processPool.get()];
368     RetainPtr<PSONScheme> handler1 = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenCrossOriginWithOpenerTestBytes]);
369     RetainPtr<PSONScheme> handler2 = adoptNS([[PSONScheme alloc] initWithBytes:dummyBytes]);
370     [webViewConfiguration setURLSchemeHandler:handler1.get() forURLScheme:@"PSON1"];
371     [webViewConfiguration setURLSchemeHandler:handler2.get() forURLScheme:@"PSON2"];
372
373     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
374     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
375     [webView setNavigationDelegate:navigationDelegate.get()];
376     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
377     [webView setUIDelegate:uiDelegate.get()];
378
379     numberOfDecidePolicyCalls = 0;
380     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
381     [webView loadRequest:request];
382
383     TestWebKitAPI::Util::run(&done);
384     done = false;
385
386     TestWebKitAPI::Util::run(&didCreateWebView);
387     didCreateWebView = false;
388
389     TestWebKitAPI::Util::run(&done);
390
391     EXPECT_EQ(2, numberOfDecidePolicyCalls);
392
393     auto pid1 = [webView _webProcessIdentifier];
394     EXPECT_TRUE(!!pid1);
395     auto pid2 = [createdWebView _webProcessIdentifier];
396     EXPECT_TRUE(!!pid2);
397
398     // FIXME: This should eventually be false once we support process swapping when there is an opener.
399     EXPECT_EQ(pid1, pid2);
400 }
401
402 TEST(ProcessSwap, SameOriginWindowOpenNoOpener)
403 {
404     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
405     processPoolConfiguration.get().processSwapsOnNavigation = YES;
406     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
407
408     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
409     [webViewConfiguration setProcessPool:processPool.get()];
410     RetainPtr<PSONScheme> handler = adoptNS([[PSONScheme alloc] initWithBytes:windowOpenSameOriginNoOpenerTestBytes]);
411     [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON1"];
412
413     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
414     auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
415     [webView setNavigationDelegate:navigationDelegate.get()];
416     auto uiDelegate = adoptNS([[PSONUIDelegate alloc] initWithNavigationDelegate:navigationDelegate.get()]);
417     [webView setUIDelegate:uiDelegate.get()];
418
419     numberOfDecidePolicyCalls = 0;
420     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson1://host/main1.html"]];
421     [webView loadRequest:request];
422
423     TestWebKitAPI::Util::run(&done);
424     done = false;
425
426     TestWebKitAPI::Util::run(&didCreateWebView);
427     didCreateWebView = false;
428
429     TestWebKitAPI::Util::run(&done);
430
431     EXPECT_EQ(2, numberOfDecidePolicyCalls);
432
433     auto pid1 = [webView _webProcessIdentifier];
434     EXPECT_TRUE(!!pid1);
435     auto pid2 = [createdWebView _webProcessIdentifier];
436     EXPECT_TRUE(!!pid2);
437
438     EXPECT_EQ(pid1, pid2);
439 }
440
441 #endif // PLATFORM(MAC)
442
443 #endif // WK_API_ENABLED