Reproducible "Unhanded web process message 'WebUserContentController:AddUserScripts...
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKit2Cocoa / UserContentController.mm
1 /*
2  * Copyright (C) 2014 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/WebKit.h>
31 #import <WebKit/WKProcessPoolPrivate.h>
32 #import <WebKit/WKUserContentControllerPrivate.h>
33 #import <WebKit/_WKProcessPoolConfiguration.h>
34 #import <WebKit/_WKUserStyleSheet.h>
35 #import <wtf/RetainPtr.h>
36
37 #if WK_API_ENABLED
38
39 static bool isDoneWithNavigation;
40
41 @interface SimpleNavigationDelegate : NSObject <WKNavigationDelegate>
42 @end
43
44 @implementation SimpleNavigationDelegate
45
46 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
47 {
48     isDoneWithNavigation = true;
49 }
50
51 @end
52
53 static bool receivedScriptMessage;
54 static RetainPtr<WKScriptMessage> lastScriptMessage;
55
56 @interface ScriptMessageHandler : NSObject <WKScriptMessageHandler>
57 @end
58
59 @implementation ScriptMessageHandler
60
61 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
62 {
63     receivedScriptMessage = true;
64     lastScriptMessage = message;
65 }
66
67 @end
68
69 TEST(WKUserContentController, ScriptMessageHandlerBasicPost)
70 {
71     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
72     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
73     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
74
75     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
76
77     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
78     [webView setNavigationDelegate:delegate.get()];
79
80     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
81
82     [webView loadRequest:request];
83
84     TestWebKitAPI::Util::run(&isDoneWithNavigation);
85
86     [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('Hello')" completionHandler:nil];
87
88     TestWebKitAPI::Util::run(&receivedScriptMessage);
89     receivedScriptMessage = false;
90
91     EXPECT_WK_STREQ(@"Hello", (NSString *)[lastScriptMessage body]);
92 }
93
94 TEST(WKUserContentController, ScriptMessageHandlerBasicRemove)
95 {
96     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
97     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
98     RetainPtr<WKUserContentController> userContentController = [configuration userContentController];
99     [userContentController addScriptMessageHandler:handler.get() name:@"handlerToRemove"];
100     [userContentController addScriptMessageHandler:handler.get() name:@"handlerToPost"];
101
102     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
103
104     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
105     [webView setNavigationDelegate:delegate.get()];
106
107     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
108
109     [webView loadRequest:request];
110
111     TestWebKitAPI::Util::run(&isDoneWithNavigation);
112
113     // Test that handlerToRemove was succesfully added.
114     [webView evaluateJavaScript:
115         @"if (window.webkit.messageHandlers.handlerToRemove) {"
116          "    window.webkit.messageHandlers.handlerToPost.postMessage('PASS');"
117          "} else {"
118          "    window.webkit.messageHandlers.handlerToPost.postMessage('FAIL');"
119          "}" completionHandler:nil];
120
121     TestWebKitAPI::Util::run(&receivedScriptMessage);
122     receivedScriptMessage = false;
123
124     EXPECT_WK_STREQ(@"PASS", (NSString *)[lastScriptMessage body]);
125
126     [userContentController removeScriptMessageHandlerForName:@"handlerToRemove"];
127
128     // Test that handlerToRemove has been removed.
129     [webView evaluateJavaScript:
130         @"if (window.webkit.messageHandlers.handlerToRemove) {"
131          "    window.webkit.messageHandlers.handlerToPost.postMessage('FAIL');"
132          "} else {"
133          "    window.webkit.messageHandlers.handlerToPost.postMessage('PASS');"
134          "}" completionHandler:nil];
135
136     TestWebKitAPI::Util::run(&receivedScriptMessage);
137     receivedScriptMessage = false;
138
139     EXPECT_WK_STREQ(@"PASS", (NSString *)[lastScriptMessage body]);
140 }
141
142 TEST(WKUserContentController, ScriptMessageHandlerCallRemovedHandler)
143 {
144     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
145     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
146     RetainPtr<WKUserContentController> userContentController = [configuration userContentController];
147     [userContentController addScriptMessageHandler:handler.get() name:@"handlerToRemove"];
148     [userContentController addScriptMessageHandler:handler.get() name:@"handlerToPost"];
149
150     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
151
152     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
153     [webView setNavigationDelegate:delegate.get()];
154
155     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
156
157     [webView loadRequest:request];
158
159     TestWebKitAPI::Util::run(&isDoneWithNavigation);
160
161     [webView evaluateJavaScript:@"var handlerToRemove = window.webkit.messageHandlers.handlerToRemove;" completionHandler:nil];
162
163     [userContentController removeScriptMessageHandlerForName:@"handlerToRemove"];
164
165     // Test that we throw an exception if you try to use a message handler that has been removed.
166     [webView evaluateJavaScript:
167         @"try {"
168          "    handlerToRemove.postMessage('FAIL');"
169          "} catch (e) {"
170          "    window.webkit.messageHandlers.handlerToPost.postMessage('PASS');"
171          "}" completionHandler:nil];
172
173     TestWebKitAPI::Util::run(&receivedScriptMessage);
174     receivedScriptMessage = false;
175
176     EXPECT_WK_STREQ(@"PASS", (NSString *)[lastScriptMessage body]);
177 }
178
179 static RetainPtr<WKWebView> webViewForScriptMessageHandlerMultipleHandlerRemovalTest(WKWebViewConfiguration *configuration)
180 {
181     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
182     RetainPtr<WKWebViewConfiguration> configurationCopy = adoptNS([configuration copy]);
183     [configurationCopy setUserContentController:[[[WKUserContentController alloc] init] autorelease]];
184     [[configurationCopy userContentController] addScriptMessageHandler:handler.get() name:@"handlerToRemove"];
185     [[configurationCopy userContentController] addScriptMessageHandler:handler.get() name:@"handlerToPost"];
186     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configurationCopy.get()]);
187     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
188     [webView setNavigationDelegate:delegate.get()];
189     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
190     [webView loadRequest:request];
191     TestWebKitAPI::Util::run(&isDoneWithNavigation);
192     isDoneWithNavigation = false;
193
194     return webView;
195 }
196
197 TEST(WKUserContentController, ScriptMessageHandlerMultipleHandlerRemoval)
198 {
199     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
200     RetainPtr<_WKProcessPoolConfiguration> processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
201     [processPoolConfiguration setMaximumProcessCount:1];
202     [configuration setProcessPool:[[[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()] autorelease]];
203
204     RetainPtr<WKWebView> webView = webViewForScriptMessageHandlerMultipleHandlerRemovalTest(configuration.get());
205     RetainPtr<WKWebView> webView2 = webViewForScriptMessageHandlerMultipleHandlerRemovalTest(configuration.get());
206
207     [[[webView configuration] userContentController] removeScriptMessageHandlerForName:@"handlerToRemove"];
208     [[[webView2 configuration] userContentController] removeScriptMessageHandlerForName:@"handlerToRemove"];
209
210     [webView evaluateJavaScript:
211      @"try {"
212      "    handlerToRemove.postMessage('FAIL');"
213      "} catch (e) {"
214      "    window.webkit.messageHandlers.handlerToPost.postMessage('PASS');"
215      "}" completionHandler:nil];
216
217     TestWebKitAPI::Util::run(&receivedScriptMessage);
218     receivedScriptMessage = false;
219
220     EXPECT_WK_STREQ(@"PASS", (NSString *)[lastScriptMessage body]);
221 }
222
223 #if !PLATFORM(IOS) // FIXME: hangs in the iOS simulator
224 TEST(WKUserContentController, ScriptMessageHandlerWithNavigation)
225 {
226     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
227     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
228     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
229
230     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
231
232     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
233     [webView setNavigationDelegate:delegate.get()];
234
235     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
236     [webView loadRequest:request];
237
238     TestWebKitAPI::Util::run(&isDoneWithNavigation);
239     isDoneWithNavigation = false;
240
241     [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('First Message')" completionHandler:nil];
242
243     TestWebKitAPI::Util::run(&receivedScriptMessage);
244
245     EXPECT_WK_STREQ(@"First Message", (NSString *)[lastScriptMessage body]);
246     
247     receivedScriptMessage = false;
248     lastScriptMessage = nullptr;
249
250
251     [webView loadRequest:request];
252     TestWebKitAPI::Util::run(&isDoneWithNavigation);
253     isDoneWithNavigation = false;
254
255     [webView evaluateJavaScript:@"window.webkit.messageHandlers.testHandler.postMessage('Second Message')" completionHandler:nil];
256
257     TestWebKitAPI::Util::run(&receivedScriptMessage);
258
259     EXPECT_WK_STREQ(@"Second Message", (NSString *)[lastScriptMessage body]);    
260 }
261 #endif
262
263 static NSString *styleSheetSource = @"body { background-color: green !important; }";
264 static NSString *backgroundColorScript = @"window.getComputedStyle(document.body, null).getPropertyValue('background-color')";
265 static NSString *frameBackgroundColorScript = @"window.getComputedStyle(document.getElementsByTagName('iframe')[0].contentDocument.body, null).getPropertyValue('background-color')";
266 static const char* greenInRGB = "rgb(0, 128, 0)";
267 static const char* redInRGB = "rgb(255, 0, 0)";
268 static const char* whiteInRGB = "rgba(0, 0, 0, 0)";
269
270 static void expectScriptEvaluatesToColor(WKWebView *webView, NSString *script, const char* color)
271 {
272     static bool didCheckBackgroundColor;
273
274     [webView evaluateJavaScript:script completionHandler:^ (id value, NSError * error) {
275         EXPECT_TRUE([value isKindOfClass:[NSString class]]);
276         EXPECT_WK_STREQ(color, value);
277         didCheckBackgroundColor = true;
278     }];
279
280     TestWebKitAPI::Util::run(&didCheckBackgroundColor);
281     didCheckBackgroundColor = false;
282 }
283
284 TEST(WKUserContentController, AddUserStyleSheetBeforeCreatingView)
285 {
286     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
287
288     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]);
289     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
290
291     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
292
293     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
294     [webView setNavigationDelegate:delegate.get()];
295
296     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
297     TestWebKitAPI::Util::run(&isDoneWithNavigation);
298     isDoneWithNavigation = false;
299
300     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB);
301 }
302
303 TEST(WKUserContentController, AddUserStyleSheetAfterCreatingView)
304 {
305     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
306
307     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
308     [webView setNavigationDelegate:delegate.get()];
309
310     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
311     TestWebKitAPI::Util::run(&isDoneWithNavigation);
312     isDoneWithNavigation = false;
313
314     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB);
315
316     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]);
317     [[webView configuration].userContentController _addUserStyleSheet:styleSheet.get()];
318
319     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB);
320 }
321
322 TEST(WKUserContentController, UserStyleSheetAffectingOnlyMainFrame)
323 {
324     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
325
326     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]);
327     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
328
329     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
330
331     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
332     [webView setNavigationDelegate:delegate.get()];
333
334     [webView loadHTMLString:@"<body style='background-color: red;'><iframe></iframe></body>" baseURL:nil];
335     TestWebKitAPI::Util::run(&isDoneWithNavigation);
336     isDoneWithNavigation = false;
337
338     // The main frame should be affected.
339     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB);
340
341     // The subframe shouldn't be affected.
342     expectScriptEvaluatesToColor(webView.get(), frameBackgroundColorScript, whiteInRGB);
343 }
344
345 TEST(WKUserContentController, UserStyleSheetAffectingAllFrames)
346 {
347     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
348
349     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]);
350     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
351
352     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
353
354     RetainPtr<SimpleNavigationDelegate> delegate = adoptNS([[SimpleNavigationDelegate alloc] init]);
355     [webView setNavigationDelegate:delegate.get()];
356
357     [webView loadHTMLString:@"<body style='background-color: red;'><iframe></iframe></body>" baseURL:nil];
358     TestWebKitAPI::Util::run(&isDoneWithNavigation);
359     isDoneWithNavigation = false;
360
361     // The main frame should be affected.
362     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, greenInRGB);
363
364     // The subframe should also be affected.
365     expectScriptEvaluatesToColor(webView.get(), frameBackgroundColorScript, greenInRGB);
366 }
367
368 #endif