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