Delay WebProcess launch until a load is triggered in a Web view
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKit / NetworkProcessCrashWithPendingConnection.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 "TestNavigationDelegate.h"
31 #import <WebKit/WKContextPrivate.h>
32 #import <WebKit/WKProcessGroupPrivate.h>
33 #import <WebKit/WKProcessPoolPrivate.h>
34 #import <WebKit/WKWebViewPrivate.h>
35 #import <WebKit/WebKit.h>
36 #import <wtf/RetainPtr.h>
37
38 static bool loadedOrCrashed = false;
39 static bool loaded = false;
40 static bool webProcessCrashed = false;
41 static bool networkProcessCrashed = false;
42 static WKWebView* testView;
43
44 @interface MonitorWebContentCrashNavigationDelegate : NSObject <WKNavigationDelegate>
45 @end
46
47 @implementation MonitorWebContentCrashNavigationDelegate
48
49 - (void)_webView:(WKWebView *)webView webContentProcessDidTerminateWithReason:(_WKProcessTerminationReason)reason
50 {
51     webProcessCrashed = true;
52 }
53
54 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
55 {
56     loaded = true;
57 }
58
59 @end
60
61 namespace TestWebKitAPI {
62
63 TEST(WebKit, NetworkProcessCrashWithPendingConnection)
64 {
65     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
66     RetainPtr<WKProcessPool> processPool = adoptNS([[WKProcessPool alloc] init]);
67     [configuration setProcessPool:processPool.get()];
68
69     // FIXME: Adopt the new API once it's added in webkit.org/b/174061.
70     WKContextClientV0 client;
71     memset(&client, 0, sizeof(client));
72     client.networkProcessDidCrash = [](WKContextRef context, const void* clientInfo) {
73         networkProcessCrashed = true;
74     };
75     WKContextSetClient(static_cast<WKContextRef>(processPool.get()), &client.base);
76
77     RetainPtr<WKWebView> webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
78     RetainPtr<WKWebView> webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
79
80     [webView1.get() loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
81     [webView1.get() _test_waitForDidFinishNavigation];
82
83     [webView2.get() loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
84     [webView2.get() _test_waitForDidFinishNavigation];
85
86     pid_t webView1PID = [webView1.get() _webProcessIdentifier];
87     pid_t webView2PID = [webView2.get() _webProcessIdentifier];
88     EXPECT_NE(webView1PID, webView2PID);
89
90     pid_t initialNetworkProcessIdentififer = [processPool.get() _networkProcessIdentifier];
91     EXPECT_NE(initialNetworkProcessIdentififer, 0);
92     kill(initialNetworkProcessIdentififer, SIGKILL);
93     Util::run(&networkProcessCrashed);
94     networkProcessCrashed = false;
95
96     [webView1.get() loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
97     [webView1.get() _test_waitForDidFinishNavigation];
98
99     pid_t relaunchedNetworkProcessIdentifier = [processPool.get() _networkProcessIdentifier];
100     EXPECT_NE(initialNetworkProcessIdentififer, relaunchedNetworkProcessIdentifier);
101     EXPECT_FALSE(networkProcessCrashed);
102
103     kill(relaunchedNetworkProcessIdentifier, SIGSTOP);
104
105     [webView2.get() loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
106     Util::sleep(0.5); // Wait for the WebContent process to send CreateNetworkConnectionToWebProcess
107     kill(relaunchedNetworkProcessIdentifier, SIGKILL);
108     Util::run(&networkProcessCrashed);
109
110     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
111     loadedOrCrashed = false;
112     [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
113         loadedOrCrashed = true;
114         loaded = true;
115     }];
116     [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) {
117         loadedOrCrashed = true;
118         webProcessCrashed = true;
119     }];
120     [webView2 setNavigationDelegate:navigationDelegate.get()];
121     TestWebKitAPI::Util::run(&loadedOrCrashed);
122     EXPECT_TRUE(loaded);
123     EXPECT_FALSE(webProcessCrashed);
124 }
125
126 TEST(WebKit, NetworkProcessRelaunchOnLaunchFailure)
127 {
128     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
129     auto processPool = adoptNS([[WKProcessPool alloc] init]);
130
131     [configuration setProcessPool:processPool.get()];
132
133     webProcessCrashed = false;
134     loaded = false;
135     networkProcessCrashed = false;
136
137     WKContextClientV0 client;
138     memset(&client, 0, sizeof(client));
139     client.networkProcessDidCrash = [](WKContextRef context, const void* clientInfo) {
140         networkProcessCrashed = true;
141     };
142     WKContextSetClient(static_cast<WKContextRef>(processPool.get()), &client.base);
143
144     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
145     testView = webView.get();
146
147     // Constucting a WebView starts a network process so terminate this one. The page load below will then request a network process and we
148     // make this new network process launch crash on startup.
149     [processPool _terminateNetworkProcess];
150     [processPool _makeNextNetworkProcessLaunchFailForTesting];
151
152     auto delegate = adoptNS([[MonitorWebContentCrashNavigationDelegate alloc] init]);
153     [webView setNavigationDelegate:delegate.get()];
154
155     [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
156     TestWebKitAPI::Util::run(&loaded);
157
158     EXPECT_TRUE(networkProcessCrashed);
159     EXPECT_FALSE(webProcessCrashed);
160     EXPECT_GT([webView _webProcessIdentifier], 0);
161     EXPECT_GT([processPool.get() _networkProcessIdentifier], 0);
162 }
163
164 } // namespace TestWebKitAPI