App-bound JavaScript and Navigation failures should have specific error codes.
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / InAppBrowserPrivacy.mm
1 /*
2  * Copyright (C) 2020 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 "ServiceWorkerTCPServer.h"
30 #import "TestNavigationDelegate.h"
31 #import "TestWKWebView.h"
32 #import "WKWebViewConfigurationExtras.h"
33 #import <WebCore/RegistrableDomain.h>
34 #import <WebCore/RuntimeApplicationChecks.h>
35 #import <WebKit/WKHTTPCookieStorePrivate.h>
36 #import <WebKit/WKPreferencesPrivate.h>
37 #import <WebKit/WKUserContentControllerPrivate.h>
38 #import <WebKit/WKWebsiteDataStorePrivate.h>
39 #import <WebKit/_WKUserContentWorld.h>
40 #import <WebKit/_WKUserStyleSheet.h>
41 #import <wtf/RunLoop.h>
42 #import <wtf/text/WTFString.h>
43
44 #if PLATFORM(IOS_FAMILY)
45
46 #if USE(APPLE_INTERNAL_SDK)
47 #import <WebKitAdditions/InAppBrowserPrivacyTestAdditions.h>
48
49 static bool isDone;
50
51 @interface AppBoundDomainDelegate : NSObject <WKNavigationDelegate>
52 - (void)waitForDidFinishNavigation;
53 - (NSError *)waitForDidFailProvisionalNavigationError;
54 @end
55
56 @implementation AppBoundDomainDelegate {
57     bool _navigationFinished;
58     RetainPtr<NSError> _provisionalNavigationFailedError;
59 }
60
61 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
62 {
63     _navigationFinished = true;
64 }
65
66 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
67 {
68     _provisionalNavigationFailedError = error;
69 }
70
71 - (void)waitForDidFinishNavigation
72 {
73     TestWebKitAPI::Util::run(&_navigationFinished);
74 }
75
76 - (NSError *)waitForDidFailProvisionalNavigationError
77 {
78     while (!_provisionalNavigationFailedError)
79         TestWebKitAPI::Util::spinRunLoop();
80     return _provisionalNavigationFailedError.autorelease();
81 }
82
83 @end
84
85 static NSString * const userScriptSource = @"window.wkUserScriptInjected = true";
86
87 @interface InAppBrowserSchemeHandler : NSObject <WKURLSchemeHandler>
88 @end
89
90 @implementation InAppBrowserSchemeHandler
91
92 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
93 {
94     NSString *response = nil;
95     if ([task.request.URL.path isEqualToString:@"/in-app-browser-privacy-test-user-script"])
96         response = @"<script>window.wkUserScriptInjected = false;</script>";
97     else if ([task.request.URL.path isEqualToString:@"/in-app-browser-privacy-test-user-agent-script"])
98         response = @"<script> window.wkUserScriptInjected = true; </script>";
99     else if ([task.request.URL.path isEqualToString:@"/in-app-browser-privacy-test-user-style-sheets"])
100         response = @"<body style='background-color: red;'></body>";
101     else if ([task.request.URL.path isEqualToString:@"/in-app-browser-privacy-test-user-style-sheets-iframe"])
102         response = @"<body style='background-color: red;'><iframe src='in-app-browser:///in-app-browser-privacy-test-user-style-sheets'></iframe></body>";
103     else if ([task.request.URL.path isEqualToString:@"/in-app-browser-privacy-test-message-handler"])
104         response = @"<body style='background-color: green;'></body><script>if (window.webkit.messageHandlers)\nwindow.webkit.messageHandlers.testHandler.postMessage('Failed'); \nelse \n document.body.style.background = 'red';</script>";
105     else if ([task.request.URL.path isEqualToString:@"/app-bound-domain-load"])
106         response = @"<body></body>";
107
108     [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:response.length textEncodingName:nil] autorelease]];
109     [task didReceiveData:[response dataUsingEncoding:NSUTF8StringEncoding]];
110     [task didFinish];
111 }
112
113 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
114 {
115 }
116
117 @end
118
119 static void cleanUpInAppBrowserPrivacyTestSettings()
120 {
121     WebCore::clearApplicationBundleIdentifierTestingOverride();
122     IN_APP_BROWSER_PRIVACY_ADDITIONS_2
123 }
124
125 static void initializeInAppBrowserPrivacyTestSettings()
126 {
127     WTF::initializeMainThread();
128     WebCore::clearApplicationBundleIdentifierTestingOverride();
129     IN_APP_BROWSER_PRIVACY_ADDITIONS
130 }
131
132 TEST(InAppBrowserPrivacy, NonAppBoundDomainFailedUserScriptAtStart)
133 {
134     isDone = false;
135     initializeInAppBrowserPrivacyTestSettings();
136     auto userScript = adoptNS([[WKUserScript alloc] initWithSource:userScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]);
137
138     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
139     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
140     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
141     [configuration.userContentController addUserScript:userScript.get()];
142
143     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration]);
144     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-script"]];
145     [webView loadRequest:request];
146     [webView _test_waitForDidFinishNavigation];
147
148     // Check that request to read this variable is rejected.
149     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
150         EXPECT_FALSE(result);
151         EXPECT_TRUE(!!error);
152         EXPECT_EQ(error.code, WKErrorJavaScriptAppBoundDomain);
153         isDone = true;
154     }];
155
156     isDone = false;
157     TestWebKitAPI::Util::run(&isDone);
158
159     // Disable script injection blocking to check that original attempt to set this variable was rejected.
160     [[[webView configuration] preferences] _setNeedsInAppBrowserPrivacyQuirks:YES];
161     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
162         EXPECT_EQ(NO, [result boolValue]);
163         EXPECT_FALSE(!!error);
164         cleanUpInAppBrowserPrivacyTestSettings();
165         isDone = true;
166     }];
167
168     isDone = false;
169     TestWebKitAPI::Util::run(&isDone);
170 }
171
172 TEST(InAppBrowserPrivacy, NonAppBoundDomainFailedUserScriptAtEnd)
173 {
174     isDone = false;
175     initializeInAppBrowserPrivacyTestSettings();
176     auto userScript = adoptNS([[WKUserScript alloc] initWithSource:userScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]);
177     
178     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
179     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
180     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
181     [configuration.userContentController addUserScript:userScript.get()];
182
183     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration]);
184     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-script"]];
185     [webView loadRequest:request];
186     [webView _test_waitForDidFinishNavigation];
187
188     // Check that request to read this variable is rejected.
189     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
190         EXPECT_FALSE(result);
191         EXPECT_TRUE(!!error);
192         EXPECT_EQ(error.code, WKErrorJavaScriptAppBoundDomain);
193         isDone = true;
194     }];
195
196     isDone = false;
197     TestWebKitAPI::Util::run(&isDone);
198
199     // Disable script injection blocking to check that original attempt to set this variable was rejected.
200     [[[webView configuration] preferences] _setNeedsInAppBrowserPrivacyQuirks:YES];
201     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
202         EXPECT_EQ(NO, [result boolValue]);
203         EXPECT_FALSE(!!error);
204         cleanUpInAppBrowserPrivacyTestSettings();
205         isDone = true;
206     }];
207
208     isDone = false;
209     TestWebKitAPI::Util::run(&isDone);
210 }
211
212 TEST(InAppBrowserPrivacy, NonAppBoundDomainFailedUserAgentScripts)
213 {
214     initializeInAppBrowserPrivacyTestSettings();
215
216     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
217     // Disable blocking of restricted APIs for test setup.
218     [[configuration preferences] _setNeedsInAppBrowserPrivacyQuirks:YES];
219
220     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
221     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
222     
223     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration]);
224     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-agent-script"]];
225     [webView loadRequest:request];
226     [webView _test_waitForDidFinishNavigation];
227
228     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
229         EXPECT_EQ(YES, [result boolValue]);
230         isDone = true;
231     }];
232
233     isDone = false;
234     TestWebKitAPI::Util::run(&isDone);
235     
236     // Enable blocking of restricted APIs.
237     [[configuration preferences] _setNeedsInAppBrowserPrivacyQuirks:NO];
238
239     auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration]);
240     isDone = false;
241     [webView2 loadRequest:request];
242     [webView2 _test_waitForDidFinishNavigation];
243
244     [webView2 evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
245         EXPECT_FALSE(result);
246         EXPECT_TRUE(!!error);
247         EXPECT_EQ(error.code, WKErrorJavaScriptAppBoundDomain);
248         cleanUpInAppBrowserPrivacyTestSettings();
249         isDone = true;
250     }];
251
252     isDone = false;
253     TestWebKitAPI::Util::run(&isDone);
254 }
255
256 TEST(InAppBrowserPrivacy, AppBoundDomains)
257 {
258     initializeInAppBrowserPrivacyTestSettings();
259     isDone = false;
260     [[WKWebsiteDataStore defaultDataStore] _appBoundDomains:^(NSArray<NSString *> *domains) {
261         NSArray *domainsToCompare = @[@"127.0.0.1", @"apple.com", @"bar.com", @"example.com", @"foo.com", @"localhost", @"testdomain1",  @"webkit.org"];
262
263         NSArray *sortedDomains = [domains sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
264
265         int length = [sortedDomains count];
266         EXPECT_EQ(length, 8);
267         for (int i = 0; i < length; i++)
268             EXPECT_WK_STREQ([sortedDomains objectAtIndex:i], [domainsToCompare objectAtIndex:i]);
269
270         cleanUpInAppBrowserPrivacyTestSettings();
271         isDone = true;
272     }];
273     TestWebKitAPI::Util::run(&isDone);
274 }
275
276 TEST(InAppBrowserPrivacy, LocalFilesAreAppBound)
277 {
278     initializeInAppBrowserPrivacyTestSettings();
279     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
280
281     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
282     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"in-app-browser-privacy-local-file" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
283     [webView loadRequest:request];
284     [webView _test_waitForDidFinishNavigation];
285
286     isDone = false;
287     [webView _isForcedIntoAppBoundMode:^(BOOL isForcedIntoAppBoundMode) {
288         EXPECT_TRUE(isForcedIntoAppBoundMode);
289         cleanUpInAppBrowserPrivacyTestSettings();
290         isDone = true;
291     }];
292     TestWebKitAPI::Util::run(&isDone);
293 }
294
295 TEST(InAppBrowserPrivacy, DataFilesAreAppBound)
296 {
297     initializeInAppBrowserPrivacyTestSettings();
298     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
299
300     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
301     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"data:text/html,start"]]];
302     [webView _test_waitForDidFinishNavigation];
303
304     isDone = false;
305     [webView _isForcedIntoAppBoundMode:^(BOOL isForcedIntoAppBoundMode) {
306         EXPECT_TRUE(isForcedIntoAppBoundMode);
307         cleanUpInAppBrowserPrivacyTestSettings();
308         isDone = true;
309     }];
310     TestWebKitAPI::Util::run(&isDone);
311 }
312
313 TEST(InAppBrowserPrivacy, AboutFilesAreAppBound)
314 {
315     initializeInAppBrowserPrivacyTestSettings();
316     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
317
318     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
319     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
320     [webView _test_waitForDidFinishNavigation];
321
322     isDone = false;
323     [webView _isForcedIntoAppBoundMode:^(BOOL isForcedIntoAppBoundMode) {
324         EXPECT_TRUE(isForcedIntoAppBoundMode);
325         cleanUpInAppBrowserPrivacyTestSettings();
326         isDone = true;
327     }];
328     TestWebKitAPI::Util::run(&isDone);
329 }
330
331 static NSString *styleSheetSource = @"body { background-color: green !important; }";
332 static NSString *backgroundColorScript = @"window.getComputedStyle(document.body, null).getPropertyValue('background-color')";
333 static const char* redInRGB = "rgb(255, 0, 0)";
334 static const char* blackInRGB = "rgba(0, 0, 0, 0)";
335
336 static void expectScriptEvaluatesToColor(WKWebView *webView, NSString *script, const char* color)
337 {
338     static bool didCheckBackgroundColor;
339
340     [webView evaluateJavaScript:script completionHandler:^(id value, NSError * error) {
341         EXPECT_TRUE([value isKindOfClass:[NSString class]]);
342         EXPECT_WK_STREQ(color, value);
343         didCheckBackgroundColor = true;
344     }];
345
346     TestWebKitAPI::Util::run(&didCheckBackgroundColor);
347     didCheckBackgroundColor = false;
348 }
349
350 TEST(InAppBrowserPrivacy, NonAppBoundUserStyleSheetForSpecificWebViewFails)
351 {
352     initializeInAppBrowserPrivacyTestSettings();
353
354     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
355
356     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
357     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
358     RetainPtr<_WKUserContentWorld> world = [_WKUserContentWorld worldWithName:@"TestWorld"];
359
360     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
361     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forWKWebView:webView.get() forMainFrameOnly:YES level:_WKUserStyleUserLevel userContentWorld:world.get()]);
362     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
363
364     auto delegate = adoptNS([AppBoundDomainDelegate new]);
365     [webView setNavigationDelegate:delegate.get()];
366
367     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
368     [webView loadRequest:request];
369     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
370     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
371     cleanUpInAppBrowserPrivacyTestSettings();
372 }
373
374 TEST(InAppBrowserPrivacy, NonAppBoundUserStyleSheetForAllWebViewsFails)
375 {
376     initializeInAppBrowserPrivacyTestSettings();
377
378     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
379
380     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
381     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
382     
383     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:YES]);
384     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
385     
386     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
387     auto delegate = adoptNS([AppBoundDomainDelegate new]);
388     [webView setNavigationDelegate:delegate.get()];
389
390     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
391     [webView loadRequest:request];
392     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
393     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
394     cleanUpInAppBrowserPrivacyTestSettings();
395 }
396
397 TEST(InAppBrowserPrivacy, NonAppBoundUserStyleSheetAffectingAllFramesFails)
398 {
399     initializeInAppBrowserPrivacyTestSettings();
400
401     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
402
403     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
404     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
405     RetainPtr<_WKUserStyleSheet> styleSheet = adoptNS([[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO]);
406     [[configuration userContentController] _addUserStyleSheet:styleSheet.get()];
407
408     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
409     auto delegate = adoptNS([AppBoundDomainDelegate new]);
410     [webView setNavigationDelegate:delegate.get()];
411
412     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets-iframe"]];
413     [webView loadRequest:request];
414     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
415     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
416     cleanUpInAppBrowserPrivacyTestSettings();
417 }
418
419 TEST(InAppBrowserPrivacy, NonAppBoundDomainCannotAccessMessageHandlers)
420 {
421     initializeInAppBrowserPrivacyTestSettings();
422     isDone = false;
423
424     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
425
426     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
427     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
428
429     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
430
431     [webView performAfterReceivingMessage:@"Failed" action:^() {
432         FAIL();
433     }];
434
435     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-message-handler"]];
436     [webView loadRequest:request];
437     [webView _test_waitForDidFinishNavigation];
438     
439     // Disable script injection blocking to check that the request for message
440     // handlers returned null.
441     [[configuration preferences] _setNeedsInAppBrowserPrivacyQuirks:YES];
442
443     // Set the background color to red if message handlers returned null so we can
444     // check without needing a message handler.
445     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, redInRGB);
446     cleanUpInAppBrowserPrivacyTestSettings();
447 }
448
449 TEST(InAppBrowserPrivacy, AppBoundDomainCanAccessMessageHandlers)
450 {
451     initializeInAppBrowserPrivacyTestSettings();
452     isDone = false;
453
454     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
455
456     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
457     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
458     [configuration setLimitsNavigationsToAppBoundDomains:YES];
459
460     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
461
462     [webView performAfterReceivingMessage:@"Passed" action:^() {
463         isDone = true;
464     }];
465
466     // Navigate to an app-bound domain and wait for the 'pass' message to test the handler is working fine.
467     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"in-app-browser-privacy-local-file" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
468     [webView loadRequest:request];
469     [webView _test_waitForDidFinishNavigation];
470
471     TestWebKitAPI::Util::run(&isDone);
472
473     [webView performAfterReceivingMessage:@"Failed" action:^() {
474         FAIL();
475     }];
476
477     cleanUpInAppBrowserPrivacyTestSettings();
478 }
479
480 static RetainPtr<WKHTTPCookieStore> globalCookieStore;
481 static bool gotFlag = false;
482
483 static void setUpCookieTest()
484 {
485     globalCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
486     NSArray<NSHTTPCookie *> *cookies = nil;
487     [globalCookieStore getAllCookies:[cookiesPtr = &cookies](NSArray<NSHTTPCookie *> *nsCookies) {
488         *cookiesPtr = [nsCookies retain];
489         gotFlag = true;
490     }];
491
492     TestWebKitAPI::Util::run(&gotFlag);
493
494     for (id cookie in cookies) {
495         gotFlag = false;
496         [globalCookieStore deleteCookie:cookie completionHandler:[]() {
497             gotFlag = true;
498         }];
499         TestWebKitAPI::Util::run(&gotFlag);
500     }
501
502     cookies = nil;
503     gotFlag = false;
504     [globalCookieStore getAllCookies:[cookiesPtr = &cookies](NSArray<NSHTTPCookie *> *nsCookies) {
505         *cookiesPtr = [nsCookies retain];
506         gotFlag = true;
507     }];
508
509     TestWebKitAPI::Util::run(&gotFlag);
510
511     // Make sure the cookie store starts out empty.
512     ASSERT_EQ(cookies.count, 0u);
513     [cookies release];
514
515     gotFlag = false;
516 }
517
518 TEST(InAppBrowserPrivacy, SetCookieForNonAppBoundDomainFails)
519 {
520     initializeInAppBrowserPrivacyTestSettings();
521     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
522
523     auto dataStore = [WKWebsiteDataStore defaultDataStore];
524     auto webView = adoptNS([TestWKWebView new]);
525     [webView loadHTMLString:@"Oh hello" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
526     [webView _test_waitForDidFinishNavigation];
527
528     setUpCookieTest();
529     globalCookieStore = [dataStore httpCookieStore];
530
531     NSArray<NSHTTPCookie *> *cookies = nil;
532
533     // Non app-bound cookie.
534     RetainPtr<NSHTTPCookie> nonAppBoundCookie = [NSHTTPCookie cookieWithProperties:@{
535         NSHTTPCookiePath: @"/",
536         NSHTTPCookieName: @"nonAppBoundName",
537         NSHTTPCookieValue: @"nonAppBoundValue",
538         NSHTTPCookieDomain: @"nonAppBoundDomain.com",
539     }];
540
541     // App-bound cookie.
542     RetainPtr<NSHTTPCookie> appBoundCookie = [NSHTTPCookie cookieWithProperties:@{
543         NSHTTPCookiePath: @"/",
544         NSHTTPCookieName: @"webKitName",
545         NSHTTPCookieValue: @"webKitValue",
546         NSHTTPCookieDomain: @"www.webkit.org",
547     }];
548
549     gotFlag = false;
550     // This should not actually set a cookie.
551     [globalCookieStore setCookie:nonAppBoundCookie.get() completionHandler:[]() {
552         gotFlag = true;
553     }];
554
555     TestWebKitAPI::Util::run(&gotFlag);
556     gotFlag = false;
557
558     // This should successfully set a cookie.
559     [globalCookieStore setCookie:appBoundCookie.get() completionHandler:[]() {
560         gotFlag = true;
561     }];
562
563     TestWebKitAPI::Util::run(&gotFlag);
564
565     cleanUpInAppBrowserPrivacyTestSettings();
566     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
567     gotFlag = false;
568
569     // Check the cookie store to make sure only one cookie was set.
570     [globalCookieStore getAllCookies:[cookiesPtr = &cookies](NSArray<NSHTTPCookie *> *nsCookies) {
571         *cookiesPtr = [nsCookies retain];
572         gotFlag = true;
573     }];
574
575     TestWebKitAPI::Util::run(&gotFlag);
576
577     ASSERT_EQ(cookies.count, 1u);
578
579     [cookies release];
580     gotFlag = false;
581     [globalCookieStore deleteCookie:appBoundCookie.get() completionHandler:[]() {
582         gotFlag = true;
583     }];
584
585     TestWebKitAPI::Util::run(&gotFlag);
586 }
587
588 TEST(InAppBrowserPrivacy, GetCookieForNonAppBoundDomainFails)
589 {
590     // Since we can't set non-app-bound cookies with In-App Browser privacy protections on,
591     // we can turn the protections off to set a cookie we will then try to get with protections enabled.
592     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
593
594     setUpCookieTest();
595     globalCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
596     NSArray<NSHTTPCookie *> *cookies = nil;
597
598     // Non app-bound cookie.
599     RetainPtr<NSHTTPCookie> nonAppBoundCookie = [NSHTTPCookie cookieWithProperties:@{
600         NSHTTPCookiePath: @"/",
601         NSHTTPCookieName: @"CookieName",
602         NSHTTPCookieValue: @"CookieValue",
603         NSHTTPCookieDomain: @"nonAppBoundDomain.com",
604     }];
605
606     // App-bound cookie.
607     RetainPtr<NSHTTPCookie> appBoundCookie = [NSHTTPCookie cookieWithProperties:@{
608         NSHTTPCookiePath: @"/",
609         NSHTTPCookieName: @"OtherCookieName",
610         NSHTTPCookieValue: @"OtherCookieValue",
611         NSHTTPCookieDomain: @"www.webkit.org",
612     }];
613
614     auto webView = adoptNS([TestWKWebView new]);
615     [webView synchronouslyLoadHTMLString:@"start network process"];
616
617     // This should successfully set a cookie because protections are off.
618     [globalCookieStore setCookie:nonAppBoundCookie.get() completionHandler:[]() {
619         gotFlag = true;
620     }];
621
622     TestWebKitAPI::Util::run(&gotFlag);
623     gotFlag = false;
624
625     [globalCookieStore setCookie:appBoundCookie.get() completionHandler:[]() {
626         gotFlag = true;
627     }];
628
629     TestWebKitAPI::Util::run(&gotFlag);
630
631     gotFlag = false;
632     [globalCookieStore getAllCookies:[cookiesPtr = &cookies](NSArray<NSHTTPCookie *> *nsCookies) {
633         *cookiesPtr = [nsCookies retain];
634         gotFlag = true;
635     }];
636
637     TestWebKitAPI::Util::run(&gotFlag);
638
639     // Confirm both cookies are in the store.
640     ASSERT_EQ(cookies.count, 2u);
641
642     // Now enable protections and ensure we can only retrieve the app-bound cookies.
643     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
644     initializeInAppBrowserPrivacyTestSettings();
645
646     gotFlag = false;
647     [globalCookieStore getAllCookies:[cookiesPtr = &cookies](NSArray<NSHTTPCookie *> *nsCookies) {
648         *cookiesPtr = [nsCookies retain];
649         gotFlag = true;
650     }];
651
652     TestWebKitAPI::Util::run(&gotFlag);
653
654     ASSERT_EQ(cookies.count, 1u);
655
656     [cookies release];
657
658     gotFlag = false;
659     [globalCookieStore deleteCookie:nonAppBoundCookie.get() completionHandler:[]() {
660         gotFlag = true;
661     }];
662
663     TestWebKitAPI::Util::run(&gotFlag);
664
665     gotFlag = false;
666     [globalCookieStore deleteCookie:appBoundCookie.get() completionHandler:[]() {
667         // Reset flag.
668         [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
669         cleanUpInAppBrowserPrivacyTestSettings();
670         gotFlag = true;
671     }];
672
673     TestWebKitAPI::Util::run(&gotFlag);
674 }
675
676 TEST(InAppBrowserPrivacy, GetCookieForURLFails)
677 {
678     // Since we can't set non-app-bound cookies with In-App Browser privacy protections on,
679     // we can turn the protections off to set a cookie we will then try to get with protections enabled.
680     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
681     setUpCookieTest();
682
683     globalCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
684     NSHTTPCookie *nonAppBoundCookie = [NSHTTPCookie cookieWithProperties:@{
685         NSHTTPCookiePath: @"/",
686         NSHTTPCookieName: @"nonAppBoundName",
687         NSHTTPCookieValue: @"nonAppBoundValue",
688         NSHTTPCookieDomain: @"nonAppBoundDomain.com",
689     }];
690     
691     NSHTTPCookie *appBoundCookie = [NSHTTPCookie cookieWithProperties:@{
692         NSHTTPCookiePath: @"/",
693         NSHTTPCookieName: @"webKitName",
694         NSHTTPCookieValue: @"webKitValue",
695         NSHTTPCookieDomain: @"webkit.org",
696     }];
697
698     __block bool done = false;
699     auto webView = adoptNS([TestWKWebView new]);
700     [webView synchronouslyLoadHTMLString:@"start network process"];
701     [globalCookieStore setCookie:nonAppBoundCookie completionHandler:^{
702         [globalCookieStore setCookie:appBoundCookie completionHandler:^{
703
704             // Now enable protections and ensure we can only retrieve the app-bound cookies.
705             [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
706             initializeInAppBrowserPrivacyTestSettings();
707
708             [globalCookieStore _getCookiesForURL:[NSURL URLWithString:@"https://webkit.org/"] completionHandler:^(NSArray<NSHTTPCookie *> *cookies) {
709                 EXPECT_EQ(cookies.count, 1ull);
710                 EXPECT_WK_STREQ(cookies[0].name, "webKitName");
711                 [globalCookieStore _getCookiesForURL:[NSURL URLWithString:@"https://nonAppBoundDomain.com/"] completionHandler:^(NSArray<NSHTTPCookie *> *cookies) {
712                     EXPECT_EQ(cookies.count, 0u);
713                     [globalCookieStore deleteCookie:nonAppBoundCookie completionHandler:^{
714                         [globalCookieStore deleteCookie:appBoundCookie completionHandler:^{
715                             [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDebugIsInAppBrowserPrivacyEnabled"];
716                             cleanUpInAppBrowserPrivacyTestSettings();
717                             done = true;
718                         }];
719                     }];
720                 }];
721             }];
722         }];
723     }];
724     TestWebKitAPI::Util::run(&done);
725 }
726
727 @interface SWInAppBrowserPrivacyMessageHandler : NSObject <WKScriptMessageHandler>
728 @end
729
730 @implementation SWInAppBrowserPrivacyMessageHandler
731 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
732 {
733     EXPECT_WK_STREQ(@"Message from worker: ServiceWorker received: Hello from an app-bound domain", [message body]);
734     isDone = true;
735 }
736 @end
737
738 static const char* mainBytes = R"SWRESOURCE(
739 <script>
740
741 function log(msg)
742 {
743     window.webkit.messageHandlers.sw.postMessage(msg);
744 }
745
746 navigator.serviceWorker.addEventListener("message", function(event) {
747     log("Message from worker: " + event.data);
748 });
749
750 try {
751
752 navigator.serviceWorker.register('/sw.js').then(function(reg) {
753     worker = reg.installing ? reg.installing : reg.active;
754     worker.postMessage("Hello from an app-bound domain");
755 }).catch(function(error) {
756     log("Registration failed with: " + error);
757 });
758 } catch(e) {
759     log("Exception: " + e);
760 }
761
762 </script>
763 )SWRESOURCE";
764
765 static const char* scriptBytes = R"SWRESOURCE(
766
767 self.addEventListener("message", (event) => {
768     event.source.postMessage("ServiceWorker received: " + event.data);
769 });
770
771 )SWRESOURCE";
772
773 TEST(InAppBrowserPrivacy, AppBoundDomainAllowsServiceWorkers)
774 {
775     initializeInAppBrowserPrivacyTestSettings();
776     isDone = false;
777
778     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
779     auto messageHandler = adoptNS([[SWInAppBrowserPrivacyMessageHandler alloc] init]);
780     [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
781     [configuration preferences]._serviceWorkerEntitlementDisabledForTesting = YES;
782     [configuration setLimitsNavigationsToAppBoundDomains:YES];
783
784     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
785
786     ServiceWorkerTCPServer server1({
787         { "text/html", mainBytes },
788         { "application/javascript", scriptBytes},
789     });
790
791     [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
792
793     // Start with a clean slate data store
794     [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
795         isDone = true;
796     }];
797     TestWebKitAPI::Util::run(&isDone);
798     isDone = false;
799     
800     // Expect the service worker load to complete successfully.
801     [webView loadRequest:server1.requestWithLocalhost()];
802     TestWebKitAPI::Util::run(&isDone);
803     isDone = false;
804
805     [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeServiceWorkerRegistrations] completionHandler:^(NSArray<WKWebsiteDataRecord *> *websiteDataRecords) {
806         EXPECT_EQ(1u, [websiteDataRecords count]);
807         EXPECT_WK_STREQ(websiteDataRecords[0].displayName, "localhost");
808         isDone = true;
809     }];
810
811     TestWebKitAPI::Util::run(&isDone);
812     isDone = false;
813
814     // Reset service worker entitlement.
815     [webView _clearServiceWorkerEntitlementOverride:^(void) {
816         cleanUpInAppBrowserPrivacyTestSettings();
817         isDone = true;
818     }];
819     TestWebKitAPI::Util::run(&isDone);
820     isDone = false;
821 }
822
823 TEST(InAppBrowserPrivacy, NonAppBoundDomainDoesNotAllowServiceWorkers)
824 {
825     initializeInAppBrowserPrivacyTestSettings();
826     isDone = false;
827
828     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
829     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
830     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
831     // Disable entitlement which allows service workers.
832     [configuration preferences]._serviceWorkerEntitlementDisabledForTesting = YES;
833
834     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
835
836     // Load a non-app bound domain.
837     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
838     [webView loadRequest:request];
839     [webView _test_waitForDidFinishNavigation];
840     
841     // Expect that service workers are disabled for this webView.
842     isDone = false;
843     [webView _serviceWorkersEnabled:^(BOOL enabled) {
844         EXPECT_FALSE(enabled);
845         isDone = true;
846     }];
847     TestWebKitAPI::Util::run(&isDone);
848     
849     // Reset service worker entitlement.
850     [webView _clearServiceWorkerEntitlementOverride:^(void) {
851         cleanUpInAppBrowserPrivacyTestSettings();
852         isDone = true;
853     }];
854     TestWebKitAPI::Util::run(&isDone);
855     isDone = false;
856 }
857
858 TEST(InAppBrowserPrivacy, AppBoundFlagForNonAppBoundDomainFails)
859 {
860     initializeInAppBrowserPrivacyTestSettings();
861     isDone = false;
862
863     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
864     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
865     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
866     [configuration setLimitsNavigationsToAppBoundDomains:YES];
867
868     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
869     auto delegate = adoptNS([AppBoundDomainDelegate new]);
870     [webView setNavigationDelegate:delegate.get()];
871
872     // Load a non-app bound domain.
873     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
874     [webView loadRequest:request];
875     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
876     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
877
878     // Make sure the load didn't complete by checking the background color.
879     // Red would indicate it finished loading.
880     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, blackInRGB);
881     cleanUpInAppBrowserPrivacyTestSettings();
882 }
883
884 TEST(InAppBrowserPrivacy, NavigateAwayFromAppBoundDomainWithAppBoundFlagFails)
885 {
886     initializeInAppBrowserPrivacyTestSettings();
887     isDone = false;
888
889     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
890     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
891     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
892     [configuration setLimitsNavigationsToAppBoundDomains:YES];
893
894     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
895     auto delegate = adoptNS([AppBoundDomainDelegate new]);
896     [webView setNavigationDelegate:delegate.get()];
897     
898     // Navigate to an app-bound domain and expect a successful load.
899     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser://apple.com/app-bound-domain-load"]];
900     [webView loadRequest:request];
901     [delegate waitForDidFinishNavigation];
902     
903     // Now try to load a non-app bound domain and make sure it fails.
904     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
905     [webView loadRequest:request];
906     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
907     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
908
909     // Make sure the load didn't complete by checking the background color.
910     // Red would indicate it finished loading.
911     expectScriptEvaluatesToColor(webView.get(), backgroundColorScript, blackInRGB);
912     cleanUpInAppBrowserPrivacyTestSettings();
913 }
914
915 TEST(InAppBrowserPrivacy, AppBoundDomainWithoutFlagTreatedAsNonAppBound)
916 {
917     initializeInAppBrowserPrivacyTestSettings();
918     isDone = false;
919
920     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
921     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
922     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
923
924     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
925     auto delegate = adoptNS([AppBoundDomainDelegate new]);
926     [webView setNavigationDelegate:delegate.get()];
927     
928     // Navigate to an app-bound domain and expect a successful load.
929     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser://apple.com/app-bound-domain-load"]];
930     [webView loadRequest:request];
931     [delegate waitForDidFinishNavigation];
932
933     // But the navigation should not have been considered app-bound because the WebView configuration
934     // specification was not set.
935     isDone = false;
936     [webView _isNavigatingToAppBoundDomain:^(BOOL isAppBound) {
937         EXPECT_FALSE(isAppBound);
938         cleanUpInAppBrowserPrivacyTestSettings();
939         isDone = true;
940     }];
941     TestWebKitAPI::Util::run(&isDone);
942 }
943
944 TEST(InAppBrowserPrivacy, WebViewWithoutAppBoundFlagCanFreelyNavigate)
945 {
946     initializeInAppBrowserPrivacyTestSettings();
947     isDone = false;
948
949     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
950     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
951     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
952
953     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
954     auto delegate = adoptNS([AppBoundDomainDelegate new]);
955     [webView setNavigationDelegate:delegate.get()];
956     
957     // Navigate to an app-bound domain and expect a successful load.
958     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser://apple.com/app-bound-domain-load"]];
959     [webView loadRequest:request];
960     [delegate waitForDidFinishNavigation];
961
962     isDone = false;
963     [webView _isNavigatingToAppBoundDomain:^(BOOL isAppBound) {
964         EXPECT_FALSE(isAppBound);
965         isDone = true;
966     }];
967     TestWebKitAPI::Util::run(&isDone);
968
969     // Navigate to an non app-bound domain and expect a successful load.
970     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser://in-app-browser-privacy-test-user-style-sheets"]];
971     [webView loadRequest:request];
972     [delegate waitForDidFinishNavigation];
973     
974     isDone = false;
975     [webView _isNavigatingToAppBoundDomain:^(BOOL isAppBound) {
976         EXPECT_FALSE(isAppBound);
977         isDone = true;
978     }];
979     TestWebKitAPI::Util::run(&isDone);
980
981     // Navigation should be successful, but this WebView should not get app-bound domain
982     // privileges like script injection.
983     isDone = false;
984     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
985         EXPECT_TRUE(!!error);
986         EXPECT_EQ(error.code, WKErrorJavaScriptAppBoundDomain);
987         isDone = true;
988     }];
989
990     TestWebKitAPI::Util::run(&isDone);
991 }
992
993 TEST(InAppBrowserPrivacy, WebViewCannotUpdateAppBoundFlagOnceSet)
994 {
995     initializeInAppBrowserPrivacyTestSettings();
996     isDone = false;
997
998     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
999     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
1000     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
1001     [configuration setLimitsNavigationsToAppBoundDomains:YES];
1002
1003     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1004     auto delegate = adoptNS([AppBoundDomainDelegate new]);
1005     [webView setNavigationDelegate:delegate.get()];
1006     
1007     // Navigate to an app-bound domain and expect a successful load.
1008     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser://apple.com/app-bound-domain-load"]];
1009     [webView loadRequest:request];
1010     [delegate waitForDidFinishNavigation];
1011
1012     isDone = false;
1013     [webView _isNavigatingToAppBoundDomain:^(BOOL isAppBound) {
1014         EXPECT_TRUE(isAppBound);
1015         isDone = true;
1016     }];
1017     TestWebKitAPI::Util::run(&isDone);
1018
1019     [[webView configuration] setLimitsNavigationsToAppBoundDomains:NO];
1020     // Now try to load a non-app bound domain and make sure it fails.
1021     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-style-sheets"]];
1022     [webView loadRequest:request];
1023     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
1024     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
1025
1026     cleanUpInAppBrowserPrivacyTestSettings();
1027 }
1028
1029 TEST(InAppBrowserPrivacy, InjectScriptThenNavigateToNonAppBoundDomainFails)
1030 {
1031     isDone = false;
1032     initializeInAppBrowserPrivacyTestSettings();
1033     auto userScript = adoptNS([[WKUserScript alloc] initWithSource:userScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]);
1034
1035     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
1036     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
1037     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
1038     [configuration.userContentController addUserScript:userScript.get()];
1039     [[configuration preferences] _setNeedsInAppBrowserPrivacyQuirks:NO];
1040
1041     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration]);
1042     auto delegate = adoptNS([AppBoundDomainDelegate new]);
1043     [webView setNavigationDelegate:delegate.get()];
1044
1045     isDone = false;
1046     [webView evaluateJavaScript:@"window.wkUserScriptInjected" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
1047         EXPECT_FALSE(!!error);
1048         isDone = true;
1049     }];
1050
1051     TestWebKitAPI::Util::run(&isDone);
1052     
1053     // Load a non-app bound domain.
1054     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-agent-script"]];
1055     [webView loadRequest:request];
1056     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
1057     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
1058 }
1059
1060 TEST(InAppBrowserPrivacy, WebViewCategory)
1061 {
1062     initializeInAppBrowserPrivacyTestSettings();
1063
1064     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1065     EXPECT_EQ([configuration _webViewCategory], _WKWebViewCategoryAppBoundDomain);
1066
1067     [configuration _setWebViewCategory:_WKWebViewCategoryInAppBrowser];
1068     EXPECT_EQ([configuration _webViewCategory], _WKWebViewCategoryInAppBrowser);
1069
1070     [configuration _setWebViewCategory:_WKWebViewCategoryWebBrowser];
1071     EXPECT_EQ([configuration _webViewCategory], _WKWebViewCategoryWebBrowser);
1072
1073     [configuration _setWebViewCategory:_WKWebViewCategoryAppBoundDomain];
1074     EXPECT_EQ([configuration _webViewCategory], _WKWebViewCategoryAppBoundDomain);
1075
1076     cleanUpInAppBrowserPrivacyTestSettings();
1077 }
1078
1079 TEST(InAppBrowserPrivacy, LoadFromHTMLStringsSucceedsIfAppBound)
1080 {
1081     initializeInAppBrowserPrivacyTestSettings();
1082     isDone = false;
1083
1084     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1085     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
1086     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
1087
1088     auto delegate = adoptNS([AppBoundDomainDelegate new]);
1089
1090     NSString *HTML = @"<html><head></head><body><img src='in-app-browser:///in-app-browser-privacy-test-user-style-sheets/'></img></body></html>";
1091     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1092     [webView setNavigationDelegate:delegate.get()];
1093
1094     [webView loadHTMLString:HTML baseURL:[NSURL URLWithString:@"in-app-browser://apple.com/"]];
1095     [delegate waitForDidFinishNavigation];
1096
1097     cleanUpInAppBrowserPrivacyTestSettings();
1098 }
1099
1100 TEST(InAppBrowserPrivacy, LoadFromHTMLStringNoBaseURLIsAppBound)
1101 {
1102     initializeInAppBrowserPrivacyTestSettings();
1103     isDone = false;
1104
1105     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1106     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
1107     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
1108
1109     auto delegate = adoptNS([AppBoundDomainDelegate new]);
1110
1111     NSString *HTML = @"<html><head></head><body><img src='in-app-browser:///in-app-browser-privacy-test-user-style-sheets/'></img></body></html>";
1112     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1113     [webView setNavigationDelegate:delegate.get()];
1114
1115     [webView loadHTMLString:HTML baseURL:nil];
1116     [delegate waitForDidFinishNavigation];
1117
1118     isDone = false;
1119     [webView _isForcedIntoAppBoundMode:^(BOOL isForcedIntoAppBoundMode) {
1120         EXPECT_TRUE(isForcedIntoAppBoundMode);
1121         cleanUpInAppBrowserPrivacyTestSettings();
1122         isDone = true;
1123     }];
1124     TestWebKitAPI::Util::run(&isDone);
1125 }
1126
1127 TEST(InAppBrowserPrivacy, LoadFromHTMLStringsFailsIfNotAppBound)
1128 {
1129     initializeInAppBrowserPrivacyTestSettings();
1130     isDone = false;
1131
1132     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1133     auto schemeHandler = adoptNS([[InAppBrowserSchemeHandler alloc] init]);
1134     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"in-app-browser"];
1135
1136     auto delegate = adoptNS([AppBoundDomainDelegate new]);
1137
1138     NSString *HTML = @"<html><head></head><body><img src='in-app-browser:///in-app-browser-privacy-test-user-style-sheets/'></img></body></html>";
1139     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1140     [webView setNavigationDelegate:delegate.get()];
1141
1142     [webView loadHTMLString:HTML baseURL:[NSURL URLWithString:@"in-app-browser:///in-app-browser-privacy-test-user-agent-script"]];
1143     NSError *error = [delegate waitForDidFailProvisionalNavigationError];
1144     EXPECT_EQ(error.code, WKErrorNavigationAppBoundDomain);
1145
1146     isDone = false;
1147     [webView _isForcedIntoAppBoundMode:^(BOOL isForcedIntoAppBoundMode) {
1148         EXPECT_TRUE(isForcedIntoAppBoundMode);
1149         cleanUpInAppBrowserPrivacyTestSettings();
1150         isDone = true;
1151     }];
1152     TestWebKitAPI::Util::run(&isDone);
1153 }
1154
1155 #endif // USE(APPLE_INTERNAL_SDK)
1156
1157 #endif // PLATFORM(IOS_FAMILY)