Add SPI to set a list of hosts to which to send custom header fields cross-origin
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / WebsitePolicies.mm
1 /*
2  * Copyright (C) 2016 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 #include "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "TestNavigationDelegate.h"
30 #import "TestWKWebView.h"
31 #import <WebKit/WKMutableDictionary.h>
32 #import <WebKit/WKNavigationDelegatePrivate.h>
33 #import <WebKit/WKPagePrivate.h>
34 #import <WebKit/WKPreferencesPrivate.h>
35 #import <WebKit/WKPreferencesRefPrivate.h>
36 #import <WebKit/WKString.h>
37 #import <WebKit/WKUIDelegatePrivate.h>
38 #import <WebKit/WKURLSchemeTaskPrivate.h>
39 #import <WebKit/WKUserContentControllerPrivate.h>
40 #import <WebKit/WKWebViewPrivate.h>
41 #import <WebKit/WKWebsiteDataStorePrivate.h>
42 #import <WebKit/WKWebsitePolicies.h>
43 #import <WebKit/_WKCustomHeaderFields.h>
44 #import <WebKit/_WKUserContentExtensionStorePrivate.h>
45 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
46 #import <WebKit/_WKWebsitePolicies.h>
47 #import <wtf/Function.h>
48 #import <wtf/HashMap.h>
49 #import <wtf/MainThread.h>
50 #import <wtf/RetainPtr.h>
51 #import <wtf/text/StringHash.h>
52 #import <wtf/text/WTFString.h>
53
54 #if PLATFORM(IOS_FAMILY)
55 #import <WebKit/WKWebViewConfigurationPrivate.h>
56 #endif
57
58 @interface WKWebView ()
59 - (WKPageRef)_pageForTesting;
60 @end
61
62 static bool doneCompiling;
63 static bool receivedAlert;
64 static bool finishedNavigation;
65
66 #if PLATFORM(MAC)
67 static Optional<_WKAutoplayEvent> receivedAutoplayEvent;
68 static Optional<_WKAutoplayEventFlags> receivedAutoplayEventFlags;
69 #endif
70
71 static size_t alertCount;
72
73 @interface ContentBlockingWebsitePoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
74 @end
75
76 @implementation ContentBlockingWebsitePoliciesDelegate
77
78 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
79 {
80     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
81     EXPECT_TRUE(false);
82     decisionHandler(WKNavigationActionPolicyAllow);
83 }
84
85 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
86 {
87     switch (alertCount++) {
88     case 0:
89         // Default behavior.
90         EXPECT_STREQ("content blockers enabled", message.UTF8String);
91         break;
92     case 1:
93         // After having set websitePolicies.contentBlockersEnabled to false.
94         EXPECT_STREQ("content blockers disabled", message.UTF8String);
95         break;
96     case 2:
97         // After having reloaded without content blockers.
98         EXPECT_STREQ("content blockers disabled", message.UTF8String);
99         break;
100     default:
101         EXPECT_TRUE(false);
102     }
103     receivedAlert = true;
104     completionHandler();
105 }
106
107 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
108 {
109     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
110     switch (alertCount) {
111     case 0:
112         // Verify the content blockers behave correctly with the default behavior.
113         break;
114     case 1:
115         {
116             // Verify disabling content blockers works correctly.
117             websitePolicies.contentBlockersEnabled = false;
118             
119             // Verify calling the decisionHandler asynchronously works correctly.
120             auto decisionHandlerCopy = Block_copy(decisionHandler);
121             callOnMainThread([decisionHandlerCopy, websitePolicies = RetainPtr<_WKWebsitePolicies>(websitePolicies)] {
122                 decisionHandlerCopy(WKNavigationActionPolicyAllow, websitePolicies.get());
123                 Block_release(decisionHandlerCopy);
124             });
125         }
126         return;
127     case 2:
128         // Verify enabling content blockers has no effect when reloading without content blockers.
129         websitePolicies.contentBlockersEnabled = true;
130         break;
131     default:
132         EXPECT_TRUE(false);
133     }
134     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
135 }
136
137 @end
138
139 TEST(WebKit, WebsitePoliciesContentBlockersEnabled)
140 {
141     [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
142
143     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
144
145     doneCompiling = false;
146     NSString* contentBlocker = @"[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]";
147     [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"WebsitePoliciesTest" encodedContentExtension:contentBlocker completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
148         EXPECT_TRUE(error == nil);
149         [[configuration userContentController] _addUserContentFilter:filter];
150         doneCompiling = true;
151     }];
152     TestWebKitAPI::Util::run(&doneCompiling);
153
154     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
155
156     auto delegate = adoptNS([[ContentBlockingWebsitePoliciesDelegate alloc] init]);
157     [webView setNavigationDelegate:delegate.get()];
158     [webView setUIDelegate:delegate.get()];
159
160     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"contentBlockerCheck" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
161     alertCount = 0;
162     receivedAlert = false;
163     [webView loadRequest:request];
164     TestWebKitAPI::Util::run(&receivedAlert);
165
166     receivedAlert = false;
167     [webView reload];
168     TestWebKitAPI::Util::run(&receivedAlert);
169
170     receivedAlert = false;
171     [webView _reloadWithoutContentBlockers];
172     TestWebKitAPI::Util::run(&receivedAlert);
173
174     [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
175 }
176
177 @interface AutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
178 @property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
179 @property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
180 @end
181
182 @implementation AutoplayPoliciesDelegate
183
184 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
185 {
186     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
187     EXPECT_TRUE(false);
188     decisionHandler(WKNavigationActionPolicyAllow);
189 }
190
191 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
192 {
193     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
194     if (_allowedAutoplayQuirksForURL)
195         websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
196     if (_autoplayPolicyForURL)
197         websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
198     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
199 }
200
201 #if PLATFORM(MAC)
202 - (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
203 {
204     receivedAutoplayEventFlags = flags;
205     receivedAutoplayEvent = event;
206 }
207 #endif
208
209 @end
210
211 @interface AsyncAutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
212 @property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
213 @property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
214 @end
215
216 @implementation AsyncAutoplayPoliciesDelegate
217
218 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
219 {
220     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
221     EXPECT_TRUE(false);
222     decisionHandler(WKNavigationActionPolicyAllow);
223 }
224
225 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
226 {
227     dispatch_async(dispatch_get_main_queue(), ^{
228         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
229         if (_allowedAutoplayQuirksForURL)
230             websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
231         if (_autoplayPolicyForURL)
232             websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
233         decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
234     });
235 }
236
237 #if PLATFORM(MAC)
238 - (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
239 {
240     receivedAutoplayEventFlags = flags;
241     receivedAutoplayEvent = event;
242 }
243 #endif
244
245 @end
246
247 @interface WebsitePoliciesNavigationDelegate : TestNavigationDelegate <WKNavigationDelegatePrivate>
248 @property (nonatomic, copy) void (^decidePolicyForNavigationActionWithWebsitePolicies)(WKNavigationAction *, id <NSSecureCoding>, void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *));
249 @end
250
251 @implementation WebsitePoliciesNavigationDelegate
252
253 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
254 {
255     if (_decidePolicyForNavigationActionWithWebsitePolicies)
256         _decidePolicyForNavigationActionWithWebsitePolicies(navigationAction, userInfo, decisionHandler);
257     else
258         decisionHandler(WKNavigationActionPolicyAllow, nil);
259 }
260
261 @end
262
263 TEST(WebKit, WebsitePoliciesAutoplayEnabled)
264 {
265     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
266
267 #if PLATFORM(IOS_FAMILY)
268     [configuration setAllowsInlineMediaPlayback:YES];
269 #endif
270
271     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
272
273     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
274     [webView setNavigationDelegate:delegate.get()];
275
276     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
277     NSURLRequest *requestWithoutAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-no-audio-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
278
279     // iOS does not support volume changes to media elements.
280 #if PLATFORM(MAC)
281     NSURLRequest *requestWithoutVolume = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-zero-volume-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
282 #endif
283
284     [delegate setAutoplayPolicyForURL:^(NSURL *) {
285         return _WKWebsiteAutoplayPolicyAllowWithoutSound;
286     }];
287     [webView loadRequest:requestWithAudio];
288     [webView waitForMessage:@"did-not-play"];
289
290     [webView loadRequest:requestWithoutAudio];
291     [webView waitForMessage:@"autoplayed"];
292
293 #if PLATFORM(MAC)
294     [webView loadRequest:requestWithoutVolume];
295     [webView waitForMessage:@"autoplayed"];
296 #endif
297
298     [delegate setAutoplayPolicyForURL:^(NSURL *) {
299         return _WKWebsiteAutoplayPolicyDeny;
300     }];
301
302 #if PLATFORM(MAC)
303     [webView loadRequest:requestWithoutVolume];
304     [webView waitForMessage:@"did-not-play"];
305 #endif
306
307     [webView loadRequest:requestWithAudio];
308     [webView waitForMessage:@"did-not-play"];
309
310     // Test updating website policies.
311     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
312     websitePolicies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
313     [webView _updateWebsitePolicies:websitePolicies];
314     [webView stringByEvaluatingJavaScript:@"playVideo()"];
315     [webView waitForMessage:@"autoplayed"];
316
317     [webView loadRequest:requestWithoutAudio];
318     [webView waitForMessage:@"did-not-play"];
319
320     [delegate setAutoplayPolicyForURL:^(NSURL *) {
321         return _WKWebsiteAutoplayPolicyAllow;
322     }];
323     [webView loadRequest:requestWithAudio];
324     [webView waitForMessage:@"autoplayed"];
325
326     [webView loadRequest:requestWithoutAudio];
327     [webView waitForMessage:@"autoplayed"];
328
329 #if PLATFORM(MAC)
330     [webView loadRequest:requestWithoutVolume];
331     [webView waitForMessage:@"autoplayed"];
332 #endif
333
334     NSURLRequest *requestWithAudioInIFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
335
336     // If the top-level document allows autoplay, any iframes should also autoplay.
337     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
338         if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
339             return _WKWebsiteAutoplayPolicyAllow;
340         return _WKWebsiteAutoplayPolicyDeny;
341     }];
342
343     [webView loadRequest:requestWithAudioInIFrame];
344     [webView waitForMessage:@"autoplayed"];
345
346     // If the top-level document disallows autoplay, any iframes should also not autoplay.
347     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
348         if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
349             return _WKWebsiteAutoplayPolicyDeny;
350         return _WKWebsiteAutoplayPolicyAllow;
351     }];
352
353     [webView loadRequest:requestWithAudioInIFrame];
354     [webView waitForMessage:@"did-not-play"];
355 }
356
357 #if PLATFORM(MAC)
358 static void runUntilReceivesAutoplayEvent(WKAutoplayEvent event)
359 {
360     while (!receivedAutoplayEvent || *receivedAutoplayEvent != event)
361         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
362 }
363
364 TEST(WebKit, WebsitePoliciesPlayAfterPreventedAutoplay)
365 {
366     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
367     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
368
369     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
370     [delegate setAutoplayPolicyForURL:^(NSURL *) {
371         return _WKWebsiteAutoplayPolicyDeny;
372     }];
373     [webView setNavigationDelegate:delegate.get()];
374     [webView setUIDelegate:delegate.get()];
375
376     NSPoint playButtonClickPoint = NSMakePoint(20, 256);
377
378     receivedAutoplayEvent = WTF::nullopt;
379     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
380     [webView loadRequest:jsPlayRequest];
381     [webView waitForMessage:@"loaded"];
382     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
383
384     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
385     [webView mouseUpAtPoint:playButtonClickPoint];
386     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
387     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
388     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
389
390     receivedAutoplayEvent = WTF::nullopt;
391     [webView loadHTMLString:@"" baseURL:nil];
392
393     NSURLRequest *autoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
394     [webView loadRequest:autoplayRequest];
395     [webView waitForMessage:@"loaded"];
396     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
397     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
398
399     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
400     [webView mouseUpAtPoint:playButtonClickPoint];
401     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
402     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
403     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
404
405     receivedAutoplayEvent = WTF::nullopt;
406     [webView loadHTMLString:@"" baseURL:nil];
407
408     NSURLRequest *noAutoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"no-autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
409     [webView loadRequest:noAutoplayRequest];
410     [webView waitForMessage:@"loaded"];
411     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
412     [webView mouseUpAtPoint:playButtonClickPoint];
413     [webView waitForMessage:@"played"];
414     ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
415
416     receivedAutoplayEvent = WTF::nullopt;
417     [webView loadHTMLString:@"" baseURL:nil];
418
419     [delegate setAutoplayPolicyForURL:^(NSURL *) {
420         return _WKWebsiteAutoplayPolicyAllowWithoutSound;
421     }];
422
423     NSURLRequest *autoplayMutedRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-muted-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
424     [webView loadRequest:autoplayMutedRequest];
425     [webView waitForMessage:@"loaded"];
426     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
427
428     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
429     [webView mouseUpAtPoint:playButtonClickPoint];
430     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
431     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
432     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
433 }
434
435 TEST(WebKit, WebsitePoliciesPlayingWithUserGesture)
436 {
437     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
438     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
439
440     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
441     [delegate setAutoplayPolicyForURL:^(NSURL *) {
442         return _WKWebsiteAutoplayPolicyAllow;
443     }];
444     [webView setNavigationDelegate:delegate.get()];
445     [webView setUIDelegate:delegate.get()];
446
447     receivedAutoplayEvent = WTF::nullopt;
448
449     NSPoint playButtonClickPoint = NSMakePoint(20, 580);
450
451     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"audio-with-play-button" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
452     [webView loadRequest:request];
453     [webView waitForMessage:@"loaded"];
454     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
455     [webView mouseUpAtPoint:playButtonClickPoint];
456
457     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
458     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
459     ASSERT_FALSE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsMediaIsMainContent);
460
461     receivedAutoplayEvent = WTF::nullopt;
462
463     request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"video-with-play-button" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
464     [webView loadRequest:request];
465     [webView waitForMessage:@"loaded"];
466     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
467     [webView mouseUpAtPoint:playButtonClickPoint];
468
469     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
470     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
471     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsMediaIsMainContent);
472 }
473
474 TEST(WebKit, WebsitePoliciesPlayingWithoutInterference)
475 {
476     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
477     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
478
479     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
480     [delegate setAutoplayPolicyForURL:^(NSURL *) {
481         return _WKWebsiteAutoplayPolicyAllow;
482     }];
483     [webView setNavigationDelegate:delegate.get()];
484     [webView setUIDelegate:delegate.get()];
485
486     receivedAutoplayEvent = WTF::nullopt;
487     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-autoplay-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
488     [webView loadRequest:jsPlayRequest];
489     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidAutoplayMediaPastThresholdWithoutUserInterference);
490     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
491 }
492
493 TEST(WebKit, WebsitePoliciesUserInterferenceWithPlaying)
494 {
495     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
496     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
497
498     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
499     [delegate setAutoplayPolicyForURL:^(NSURL *) {
500         return _WKWebsiteAutoplayPolicyAllow;
501     }];
502     [webView setNavigationDelegate:delegate.get()];
503     [webView setUIDelegate:delegate.get()];
504
505     receivedAutoplayEvent = WTF::nullopt;
506     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
507     [webView loadRequest:jsPlayRequest];
508     [webView waitForMessage:@"playing"];
509     ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
510
511     WKPageSetMuted([webView _pageForTesting], kWKMediaAudioMuted);
512     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
513     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
514
515     receivedAutoplayEvent = WTF::nullopt;
516     [webView loadRequest:jsPlayRequest];
517     [webView waitForMessage:@"playing"];
518     ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
519
520     const NSPoint muteButtonClickPoint = NSMakePoint(80, 256);
521     [webView mouseDownAtPoint:muteButtonClickPoint simulatePressure:NO];
522     [webView mouseUpAtPoint:muteButtonClickPoint];
523     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
524     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
525
526     receivedAutoplayEvent = WTF::nullopt;
527     [webView loadRequest:jsPlayRequest];
528     [webView waitForMessage:@"playing"];
529     ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
530
531     const NSPoint playButtonClickPoint = NSMakePoint(20, 256);
532     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
533     [webView mouseUpAtPoint:playButtonClickPoint];
534     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
535     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
536 }
537
538 TEST(WebKit, WebsitePoliciesWithBridgingCast)
539 {
540     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
541     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
542     auto delegate = adoptNS([[WebsitePoliciesNavigationDelegate alloc] init]);
543
544     __block bool didInvokeDecisionHandler = false;
545     [delegate setDecidePolicyForNavigationActionWithWebsitePolicies:^(WKNavigationAction *, id <NSSecureCoding>, void (^decisionHandler)(WKNavigationActionPolicy, _WKWebsitePolicies *)) {
546         auto policies = adoptWK(WKWebsitePoliciesCreate());
547         decisionHandler(WKNavigationActionPolicyAllow, (__bridge _WKWebsitePolicies *)policies.get());
548         didInvokeDecisionHandler = true;
549     }];
550
551     [webView setNavigationDelegate:delegate.get()];
552     [webView loadTestPageNamed:@"simple"];
553     [delegate waitForDidFinishNavigation];
554     EXPECT_TRUE(didInvokeDecisionHandler);
555 }
556
557 TEST(WebKit, WebsitePoliciesWithUnexpectedType)
558 {
559     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
560     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
561     auto delegate = adoptNS([[WebsitePoliciesNavigationDelegate alloc] init]);
562
563     __block bool didCatchException = false;
564     [delegate setDecidePolicyForNavigationActionWithWebsitePolicies:^(WKNavigationAction *, id <NSSecureCoding>, void (^decisionHandler)(WKNavigationActionPolicy, _WKWebsitePolicies *)) {
565         @try {
566             id fakePolicies = @"Hello";
567             decisionHandler(WKNavigationActionPolicyAllow, (_WKWebsitePolicies *)fakePolicies);
568         } @catch (NSException *exception) {
569             didCatchException = true;
570         }
571     }];
572
573     [webView setNavigationDelegate:delegate.get()];
574     [webView loadTestPageNamed:@"simple"];
575     TestWebKitAPI::Util::run(&didCatchException);
576 }
577
578 struct ParsedRange {
579     ParsedRange(String string)
580     {
581         // This is a strict and unsafe Range header parser adequate only for tests.
582         bool parsingMin = true;
583         size_t min = 0;
584         size_t max = 0;
585         ASSERT(string.length() > 6);
586         ASSERT(string.startsWith("bytes="));
587         for (size_t i = 6; i < string.length(); ++i) {
588             if (isASCIIDigit(string[i])) {
589                 if (parsingMin)
590                     min = min * 10 + string[i] - '0';
591                 else
592                     max = max * 10 + string[i] - '0';
593             } else if (string[i] == '-') {
594                 if (parsingMin)
595                     parsingMin = false;
596                 else
597                     return;
598             } else
599                 return;
600         }
601         if (min <= max)
602             range = std::make_pair(min, max);
603     }
604     Optional<std::pair<size_t, size_t>> range;
605 };
606
607 @interface TestSchemeHandler : NSObject <WKURLSchemeHandler>
608 - (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data;
609 @end
610
611 @implementation TestSchemeHandler {
612     RetainPtr<NSData> videoData;
613 }
614
615 - (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data
616 {
617     self = [super init];
618     if (!self)
619         return nil;
620     
621     videoData = WTFMove(data);
622     
623     return self;
624 }
625
626 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
627 {
628     if ([task.request.URL.path isEqualToString:@"/should-redirect"]) {
629         [(id<WKURLSchemeTaskPrivate>)task _didPerformRedirection:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:nil expectedContentLength:0 textEncodingName:nil] autorelease] newRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///autoplay-check.html"]]];
630         
631         NSData *data = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
632         [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
633         [task didReceiveData:data];
634         [task didFinish];
635         return;
636     }
637     
638     ASSERT_TRUE([task.request.URL.path isEqualToString:@"/test.mp4"]);
639     ParsedRange parsedRange([task.request valueForHTTPHeaderField:@"Range"]);
640     ASSERT_TRUE(!!parsedRange.range);
641     auto& range = *parsedRange.range;
642     
643     NSDictionary *headerFields = @{ @"Content-Length": [@(range.second - range.first) stringValue], @"Content-Range": [NSString stringWithFormat:@"bytes %lu-%lu/%lu", range.first, range.second, [videoData length]] };
644     NSURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:task.request.URL statusCode:200 HTTPVersion:(NSString *)kCFHTTPVersion1_1 headerFields:headerFields] autorelease];
645     [task didReceiveResponse:response];
646     [task didReceiveData:[videoData subdataWithRange:NSMakeRange(range.first, range.second - range.first)]];
647     [task didFinish];
648     
649 }
650
651 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
652 {
653 }
654
655 @end
656
657 TEST(WebKit, WebsitePoliciesDuringRedirect)
658 {
659     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
660     auto videoData = adoptNS([[NSData alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"]]);
661     [configuration setURLSchemeHandler:[[[TestSchemeHandler alloc] initWithVideoData:WTFMove(videoData)] autorelease] forURLScheme:@"test"];
662     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
663     
664     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
665     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
666         if ([url.path isEqualToString:@"/should-redirect"])
667             return _WKWebsiteAutoplayPolicyDeny;
668         return _WKWebsiteAutoplayPolicyAllow;
669     }];
670     [webView setNavigationDelegate:delegate.get()];
671     [webView setUIDelegate:delegate.get()];
672     
673     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///should-redirect"]]];
674     [webView waitForMessage:@"autoplayed"];
675 }
676
677 TEST(WebKit, WebsitePoliciesUpdates)
678 {
679     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
680     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
681     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
682     [webView setNavigationDelegate:delegate.get()];
683     [webView setUIDelegate:delegate.get()];
684
685     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
686
687     [delegate setAutoplayPolicyForURL:^(NSURL *) {
688         return _WKWebsiteAutoplayPolicyDeny;
689     }];
690     [webView loadRequest:requestWithAudio];
691     [webView waitForMessage:@"did-not-play"];
692
693     _WKWebsitePolicies *policies = [[[_WKWebsitePolicies alloc] init] autorelease];
694     policies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
695     [webView _updateWebsitePolicies:policies];
696
697     // Now that we updated our policies, a script should be able to autoplay media.
698     [webView stringByEvaluatingJavaScript:@"playVideo()"];
699     [webView waitForMessage:@"autoplayed"];
700
701     [webView stringByEvaluatingJavaScript:@"pauseVideo()"];
702
703     policies = [[[_WKWebsitePolicies alloc] init] autorelease];
704     policies.autoplayPolicy = _WKWebsiteAutoplayPolicyDeny;
705     [webView _updateWebsitePolicies:policies];
706
707     // A script should no longer be able to autoplay media.
708     receivedAutoplayEvent = WTF::nullopt;
709     [webView stringByEvaluatingJavaScript:@"playVideo()"];
710     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
711 }
712
713 TEST(WebKit, WebsitePoliciesArbitraryUserGestureQuirk)
714 {
715     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
716     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
717
718     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
719     [webView setNavigationDelegate:delegate.get()];
720
721     WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
722     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
723     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
724
725     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url)
726     {
727         return _WKWebsiteAutoplayQuirkArbitraryUserGestures;
728     }];
729     [delegate setAutoplayPolicyForURL:^(NSURL *)
730     {
731         return _WKWebsiteAutoplayPolicyDeny;
732     }];
733
734     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
735     [webView loadRequest:request];
736     [webView waitForMessage:@"did-not-play"];
737
738     const NSPoint clickPoint = NSMakePoint(760, 560);
739     [webView mouseDownAtPoint:clickPoint simulatePressure:NO];
740     [webView mouseUpAtPoint:clickPoint];
741
742     [webView stringByEvaluatingJavaScript:@"playVideo()"];
743     [webView waitForMessage:@"autoplayed"];
744 }
745
746 TEST(WebKit, WebsitePoliciesAutoplayQuirks)
747 {
748     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
749     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
750
751     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
752     [webView setNavigationDelegate:delegate.get()];
753
754     WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
755     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
756     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
757
758     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
759
760     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
761         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
762     }];
763     [delegate setAutoplayPolicyForURL:^(NSURL *) {
764         return _WKWebsiteAutoplayPolicyDeny;
765     }];
766     [webView loadRequest:requestWithAudio];
767     [webView waitForMessage:@"did-not-play"];
768     [webView waitForMessage:@"on-pause"];
769
770     receivedAutoplayEvent = WTF::nullopt;
771     [webView loadHTMLString:@"" baseURL:nil];
772
773     NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
774
775     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
776         if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
777             return _WKWebsiteAutoplayQuirkInheritedUserGestures;
778         
779         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
780     }];
781     [delegate setAutoplayPolicyForURL:^(NSURL *) {
782         return _WKWebsiteAutoplayPolicyDeny;
783     }];
784     [webView loadRequest:requestWithAudioInFrame];
785     [webView waitForMessage:@"did-not-play"];
786     [webView waitForMessage:@"on-pause"];
787
788     receivedAutoplayEvent = WTF::nullopt;
789     [webView loadHTMLString:@"" baseURL:nil];
790
791     NSURLRequest *requestThatInheritsGesture = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-inherits-gesture-from-document" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
792     [webView loadRequest:requestThatInheritsGesture];
793     [webView waitForMessage:@"loaded"];
794
795     // Click in the document, but not in the media element.
796     const NSPoint clickPoint = NSMakePoint(760, 560);
797     [webView mouseDownAtPoint:clickPoint simulatePressure:NO];
798     [webView mouseUpAtPoint:clickPoint];
799
800     [webView stringByEvaluatingJavaScript:@"play()"];
801     [webView waitForMessage:@"playing"];
802 }
803
804 TEST(WebKit, WebsitePoliciesPerDocumentAutoplayBehaviorQuirks)
805 {
806     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
807     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
808
809     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
810     [webView setNavigationDelegate:delegate.get()];
811
812     WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
813     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
814     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
815
816     receivedAutoplayEvent = WTF::nullopt;
817
818     NSURLRequest *requestWithMultipleMediaElements = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplaying-multiple-media-elements" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
819
820     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
821         return _WKWebsiteAutoplayQuirkPerDocumentAutoplayBehavior;
822     }];
823     [delegate setAutoplayPolicyForURL:^(NSURL *) {
824         return _WKWebsiteAutoplayPolicyDeny;
825     }];
826     [webView loadRequest:requestWithMultipleMediaElements];
827     [webView waitForMessage:@"loaded"];
828
829     // We should not be allowed to play without a user gesture.
830     [webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video1')" completionHandler:nil];
831     [webView waitForMessage:@"did-not-play-video1"];
832
833     // Now try again with a user gesture...
834     const NSPoint playButtonClickPoint = NSMakePoint(20, 580);
835     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
836     [webView mouseUpAtPoint:playButtonClickPoint];
837     [webView waitForMessage:@"did-play-video1"];
838
839     // Now video2 should also be allowed to autoplay without a user gesture because of the quirk.
840     [webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video2')" completionHandler:nil];
841     [webView waitForMessage:@"did-play-video2"];
842
843     // Now let's test this without the quirk.
844     [webView loadHTMLString:@"" baseURL:nil];
845     receivedAutoplayEvent = WTF::nullopt;
846
847     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
848         return 0;
849     }];
850     [webView loadRequest:requestWithMultipleMediaElements];
851     [webView waitForMessage:@"loaded"];
852
853     // We should not be allowed to play without a user gesture.
854     [webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video1')" completionHandler:nil];
855     [webView waitForMessage:@"did-not-play-video1"];
856
857     // Now try again with a user gesture...
858     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
859     [webView mouseUpAtPoint:playButtonClickPoint];
860     [webView waitForMessage:@"did-play-video1"];
861
862     // Now video2 should not be allowed to autoplay without a user gesture.
863     [webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video2')" completionHandler:nil];
864     [webView waitForMessage:@"did-not-play-video2"];
865 }
866
867 TEST(WebKit, WebsitePoliciesAutoplayQuirksAsyncPolicyDelegate)
868 {
869     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
870     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
871
872     auto delegate = adoptNS([[AsyncAutoplayPoliciesDelegate alloc] init]);
873     [webView setNavigationDelegate:delegate.get()];
874
875     WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
876     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
877     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
878
879     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
880
881     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
882         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
883     }];
884     [delegate setAutoplayPolicyForURL:^(NSURL *) {
885         return _WKWebsiteAutoplayPolicyDeny;
886     }];
887     [webView loadRequest:requestWithAudio];
888     [webView waitForMessage:@"did-not-play"];
889     [webView waitForMessage:@"on-pause"];
890
891     receivedAutoplayEvent = WTF::nullopt;
892     [webView loadHTMLString:@"" baseURL:nil];
893
894     NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
895
896     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
897         if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
898             return _WKWebsiteAutoplayQuirkInheritedUserGestures;
899
900         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
901     }];
902     [delegate setAutoplayPolicyForURL:^(NSURL *) {
903         return _WKWebsiteAutoplayPolicyDeny;
904     }];
905     [webView loadRequest:requestWithAudioInFrame];
906     [webView waitForMessage:@"did-not-play"];
907     [webView waitForMessage:@"on-pause"];
908 }
909 #endif // PLATFORM(MAC)
910
911 TEST(WebKit, InvalidCustomHeaders)
912 {
913     auto customHeaderFields = adoptNS([[_WKCustomHeaderFields alloc] init]);
914     [customHeaderFields setFields:@{@"invalidheader" : @"", @"noncustom" : @"header", @"    x-Custom ":@"  Needs Canonicalization\t ", @"x-other" : @"other value"}];
915     NSDictionary<NSString *, NSString *> *canonicalized = [customHeaderFields fields];
916     EXPECT_EQ(canonicalized.count, 2u);
917     EXPECT_STREQ([canonicalized objectForKey:@"x-Custom"].UTF8String, "Needs Canonicalization");
918     EXPECT_STREQ([canonicalized objectForKey:@"x-other"].UTF8String, "other value");
919 }
920
921 static bool firstTestDone;
922 static bool secondTestDone;
923 static bool thirdTestDone;
924 static bool fourthTestDone;
925 static bool fifthTestDone;
926
927 static void expectLegacyHeaders(id <WKURLSchemeTask> task, bool expected)
928 {
929     NSURLRequest *request = task.request;
930     if (expected) {
931         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key1"] UTF8String], "value1");
932         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key2"] UTF8String], "value2");
933     } else {
934         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key1"] == nil);
935         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key2"] == nil);
936     }
937 }
938
939 static void expectHeaders(id <WKURLSchemeTask> task, bool expected)
940 {
941     NSURLRequest *request = task.request;
942     if (expected) {
943         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key3"] UTF8String], "value3");
944         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key4"] UTF8String], "value4");
945     } else {
946         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key3"] == nil);
947         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key4"] == nil);
948     }
949 }
950
951 static void respond(id <WKURLSchemeTask>task, NSString *html = nil)
952 {
953     [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil] autorelease]];
954     [task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
955     [task didFinish];
956 }
957
958 @interface CustomHeaderFieldsDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler>
959 @end
960
961 @implementation CustomHeaderFieldsDelegate
962
963 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler
964 {
965     auto legacyHeaderFieldDictionary = adoptWK(WKMutableDictionaryCreate());
966     WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key1")).get(), adoptWK(WKStringCreateWithUTF8CString("value1")).get());
967     WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key2")).get(), adoptWK(WKStringCreateWithUTF8CString("value2")).get());
968     WKWebsitePoliciesSetCustomHeaderFields((WKWebsitePoliciesRef)preferences, legacyHeaderFieldDictionary.get());
969
970     _WKCustomHeaderFields *headerFields = [[[_WKCustomHeaderFields alloc] init] autorelease];
971     [headerFields setFields:@{@"X-key3": @"value3", @"X-key4": @"value4"}];
972     [headerFields setThirdPartyDomains:@[
973         @"*.hostwithasterisk.com",
974         @"hostwithoutasterisk.com",
975         @"*.com" // should be ignored.
976     ]];
977     
978     [preferences _setCustomHeaderFields:@[headerFields]];
979     
980     if ([navigationAction.request.URL.path isEqualToString:@"/mainresource"]) {
981         dispatch_async(dispatch_get_main_queue(), ^{
982             decisionHandler(WKNavigationActionPolicyAllow, preferences);
983         });
984     } else
985         decisionHandler(WKNavigationActionPolicyAllow, preferences);
986 }
987
988 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
989 {
990     NSString *path = urlSchemeTask.request.URL.path;
991     if ([path isEqualToString:@"/mainresource"]) {
992         expectLegacyHeaders(urlSchemeTask, true);
993         respond(urlSchemeTask, @"<script>fetch('subresource').then(function(response){fetch('test://differentsecurityorigin/crossoriginsubresource',{mode:'no-cors'})})</script>");
994     } else if ([path isEqualToString:@"/subresource"]) {
995         expectLegacyHeaders(urlSchemeTask, true);
996         respond(urlSchemeTask);
997     } else if ([path isEqualToString:@"/crossoriginsubresource"]) {
998         expectLegacyHeaders(urlSchemeTask, false);
999         respond(urlSchemeTask);
1000         firstTestDone = true;
1001     } else if ([path isEqualToString:@"/mainresourcewithiframe"]) {
1002         expectLegacyHeaders(urlSchemeTask, true);
1003         respond(urlSchemeTask, @"<iframe src='test://iframeorigin/iframemainresource'></iframe>");
1004     } else if ([path isEqualToString:@"/iframemainresource"]) {
1005         expectLegacyHeaders(urlSchemeTask, false);
1006         respond(urlSchemeTask, @"<script>fetch('iframesubresource').then(function(response){fetch('test://mainframeorigin/originaloriginsubresource',{mode:'no-cors'})})</script>");
1007     } else if ([path isEqualToString:@"/iframesubresource"]) {
1008         expectLegacyHeaders(urlSchemeTask, false);
1009         respond(urlSchemeTask);
1010     } else if ([path isEqualToString:@"/originaloriginsubresource"]) {
1011         expectLegacyHeaders(urlSchemeTask, false);
1012         respond(urlSchemeTask);
1013         secondTestDone = true;
1014     } else if ([path isEqualToString:@"/nestedtop"]) {
1015         expectLegacyHeaders(urlSchemeTask, true);
1016         respond(urlSchemeTask, @"<iframe src='test://otherorigin/nestedmid'></iframe>");
1017     } else if ([path isEqualToString:@"/nestedmid"]) {
1018         expectLegacyHeaders(urlSchemeTask, false);
1019         respond(urlSchemeTask, @"<iframe src='test://toporigin/nestedbottom'></iframe>");
1020     } else if ([path isEqualToString:@"/nestedbottom"]) {
1021         expectLegacyHeaders(urlSchemeTask, true);
1022         respond(urlSchemeTask);
1023         thirdTestDone = true;
1024     } else if ([path isEqualToString:@"/requestfromaboutblank"]) {
1025         expectLegacyHeaders(urlSchemeTask, true);
1026         respond(urlSchemeTask);
1027         fourthTestDone = true;
1028     } else if ([path isEqualToString:@"/testcustomheaderfieldhosts"]) {
1029         expectHeaders(urlSchemeTask, true);
1030         NSString *html = @"<script>fetch('test://a.b.c.sub.hostwithasterisk.com/hosttest1',{mode:'no-cors'})"
1031             ".then(function(response){fetch('test://subhostwithasterisk.com/hosttest2',{mode:'no-cors'})})"
1032             ".then(function(response){fetch('test://hostwithoutasterisk.com/hosttest3',{mode:'no-cors'})})"
1033             ".then(function(response){fetch('test://a.b.c.sub.hostwithoutasterisk.com/hosttest4',{mode:'no-cors'})})</script>";
1034         respond(urlSchemeTask, html);
1035     } else if ([path isEqualToString:@"/hosttest1"]) {
1036         expectHeaders(urlSchemeTask, true);
1037         respond(urlSchemeTask);
1038     } else if ([path isEqualToString:@"/hosttest2"]) {
1039         expectHeaders(urlSchemeTask, false);
1040         respond(urlSchemeTask);
1041     } else if ([path isEqualToString:@"/hosttest3"]) {
1042         expectHeaders(urlSchemeTask, true);
1043         respond(urlSchemeTask);
1044     } else if ([path isEqualToString:@"/hosttest4"]) {
1045         expectHeaders(urlSchemeTask, false);
1046         respond(urlSchemeTask);
1047         fifthTestDone = true;
1048     } else if ([path isEqualToString:@"/testcustomheaderfieldhosts"])
1049         EXPECT_TRUE(false);
1050 }
1051
1052 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
1053 {
1054 }
1055
1056 @end
1057
1058 TEST(WebKit, CustomHeaderFields)
1059 {
1060     auto delegate = adoptNS([[CustomHeaderFieldsDelegate alloc] init]);
1061     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1062     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
1063     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1064     [webView setNavigationDelegate:delegate.get()];
1065     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///mainresource"]]];
1066     TestWebKitAPI::Util::run(&firstTestDone);
1067     
1068     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://mainframeorigin/mainresourcewithiframe"]]];
1069     TestWebKitAPI::Util::run(&secondTestDone);
1070
1071     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://toporigin/nestedtop"]]];
1072     TestWebKitAPI::Util::run(&thirdTestDone);
1073
1074     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://host/testcustomheaderfieldhosts"]]];
1075     TestWebKitAPI::Util::run(&fifthTestDone);
1076 }
1077
1078 static unsigned loadCount;
1079
1080 @interface DataMappingSchemeHandler : NSObject <WKURLSchemeHandler> {
1081     HashMap<String, RetainPtr<NSData>> _dataMappings;
1082     Function<void(id <WKURLSchemeTask>)> _taskHandler;
1083 }
1084 - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data;
1085 - (void)setTaskHandler:(Function<void(id <WKURLSchemeTask>)>&&)handler;
1086 @end
1087
1088 @implementation DataMappingSchemeHandler
1089
1090 - (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data
1091 {
1092     _dataMappings.set(urlString, [NSData dataWithBytesNoCopy:(void*)data length:strlen(data) freeWhenDone:NO]);
1093 }
1094
1095 - (void)setTaskHandler:(Function<void(id <WKURLSchemeTask>)>&&)handler
1096 {
1097     _taskHandler = WTFMove(handler);
1098 }
1099
1100 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
1101 {
1102     NSURL *finalURL = task.request.URL;
1103
1104     ++loadCount;
1105     if (_taskHandler)
1106         _taskHandler(task);
1107
1108     auto response = adoptNS([[NSURLResponse alloc] initWithURL:finalURL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
1109     [task didReceiveResponse:response.get()];
1110
1111     if (auto data = _dataMappings.get([finalURL absoluteString]))
1112         [task didReceiveData:data.get()];
1113     else
1114         [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
1115     [task didFinish];
1116 }
1117
1118 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
1119 {
1120 }
1121
1122 @end
1123
1124 @interface CustomUserAgentDelegate : NSObject <WKNavigationDelegate> {
1125 }
1126 @end
1127
1128 @implementation CustomUserAgentDelegate
1129
1130 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1131 {
1132     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1133     if (navigationAction.targetFrame.mainFrame)
1134         [websitePolicies setCustomUserAgent:@"Foo Custom UserAgent"];
1135
1136     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1137 }
1138
1139 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
1140 {
1141     finishedNavigation = true;
1142 }
1143
1144 @end
1145
1146 static const char* customUserAgentMainFrameTestBytes = R"TESTRESOURCE(
1147 <script src="test://www.webkit.org/script.js"></script>
1148 <img src="test://www.webkit.org/image.png"></img>
1149 <iframe src="test://www.apple.com/subframe.html"></iframe>
1150 <script>
1151 onload = () => {
1152     setTimeout(() => {
1153         fetch("test://www.webkit.org/fetchResource.html");
1154     }, 0);
1155 }
1156 onmessage = (event) => {
1157     window.subframeUserAgent = event.data;
1158 }
1159 </script>
1160 )TESTRESOURCE";
1161
1162 static const char* customUserAgentSubFrameTestBytes = R"TESTRESOURCE(
1163 <script src="test://www.apple.com/script.js"></script>
1164 <img src="test://www.apple.com/image.png"></img>
1165 <iframe src="test://www.apple.com/subframe2.html"></iframe>
1166 <script>
1167 onload = () => {
1168     setTimeout(() => {
1169         fetch("test://www.apple.com/fetchResource.html");
1170     }, 0);
1171     top.postMessage(navigator.userAgent, '*');
1172 }
1173 </script>
1174 )TESTRESOURCE";
1175
1176 TEST(WebKit, WebsitePoliciesCustomUserAgent)
1177 {
1178     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1179
1180     auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
1181     [schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
1182     [schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
1183     [schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
1184         EXPECT_STREQ("Foo Custom UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
1185     }];
1186     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
1187
1188     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1189
1190     auto delegate = adoptNS([[CustomUserAgentDelegate alloc] init]);
1191     [webView setNavigationDelegate:delegate.get()];
1192
1193     loadCount = 0;
1194     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
1195     [webView loadRequest:request];
1196
1197     TestWebKitAPI::Util::run(&finishedNavigation);
1198     finishedNavigation = false;
1199
1200     while (loadCount != 9U)
1201         TestWebKitAPI::Util::spinRunLoop();
1202     loadCount = 0;
1203
1204     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.google.com/main.html"]];
1205     [webView loadRequest:request];
1206
1207     TestWebKitAPI::Util::run(&finishedNavigation);
1208     finishedNavigation = false;
1209
1210     EXPECT_EQ(1U, loadCount);
1211     loadCount = 0;
1212 }
1213
1214 @interface CustomJavaScriptUserAgentDelegate : NSObject <WKNavigationDelegate>
1215 @property (nonatomic) BOOL setCustomUserAgent;
1216 @end
1217
1218 @implementation CustomJavaScriptUserAgentDelegate
1219
1220 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1221 {
1222     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1223     if (navigationAction.targetFrame.mainFrame) {
1224         [websitePolicies setCustomJavaScriptUserAgentAsSiteSpecificQuirks:@"Foo Custom JavaScript UserAgent"];
1225         if (_setCustomUserAgent)
1226             [websitePolicies setCustomUserAgent:@"Foo Custom Request UserAgent"];
1227     }
1228
1229     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1230 }
1231
1232 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
1233 {
1234     finishedNavigation = true;
1235 }
1236
1237 @end
1238
1239 TEST(WebKit, WebsitePoliciesCustomJavaScriptUserAgent)
1240 {
1241     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1242
1243     auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
1244     [schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
1245     [schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
1246     [schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
1247         auto* userAgentString = [task.request valueForHTTPHeaderField:@"User-Agent"];
1248         EXPECT_TRUE([userAgentString hasPrefix:@"Mozilla/5.0 "]);
1249         EXPECT_TRUE([userAgentString containsString:@"(KHTML, like Gecko)"]);
1250     }];
1251     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
1252     [configuration preferences]._needsSiteSpecificQuirks = YES;
1253
1254     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1255
1256     auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
1257     [webView setNavigationDelegate:delegate.get()];
1258
1259     loadCount = 0;
1260     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
1261     [webView loadRequest:request];
1262
1263     TestWebKitAPI::Util::run(&finishedNavigation);
1264     finishedNavigation = false;
1265
1266     while (loadCount != 9U)
1267         TestWebKitAPI::Util::spinRunLoop();
1268
1269     EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
1270     EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
1271 }
1272
1273 TEST(WebKit, WebsitePoliciesCustomUserAgents)
1274 {
1275     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1276
1277     auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
1278     [schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
1279     [schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
1280     [schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
1281         EXPECT_STREQ("Foo Custom Request UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
1282     }];
1283     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
1284     [configuration preferences]._needsSiteSpecificQuirks = YES;
1285
1286     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1287
1288     auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
1289     delegate.get().setCustomUserAgent = YES;
1290     [webView setNavigationDelegate:delegate.get()];
1291
1292     loadCount = 0;
1293     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
1294     [webView loadRequest:request];
1295
1296     TestWebKitAPI::Util::run(&finishedNavigation);
1297     finishedNavigation = false;
1298
1299     while (loadCount != 9U)
1300         TestWebKitAPI::Util::spinRunLoop();
1301
1302     EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
1303     EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
1304 }
1305
1306 @interface CustomNavigatorPlatformDelegate : NSObject <WKNavigationDelegate> {
1307 }
1308 @end
1309
1310 @implementation CustomNavigatorPlatformDelegate
1311
1312 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1313 {
1314     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1315     if (navigationAction.targetFrame.mainFrame)
1316         [websitePolicies setCustomNavigatorPlatform:@"Test Custom Platform"];
1317
1318     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1319 }
1320
1321 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
1322 {
1323     finishedNavigation = true;
1324 }
1325
1326 @end
1327
1328 TEST(WebKit, WebsitePoliciesCustomNavigatorPlatform)
1329 {
1330     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1331     
1332     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1333
1334     auto delegate = adoptNS([[CustomNavigatorPlatformDelegate alloc] init]);
1335     [webView setNavigationDelegate:delegate.get()];
1336     
1337     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"data:text/html,hello"]];
1338     [webView loadRequest:request];
1339     
1340     TestWebKitAPI::Util::run(&finishedNavigation);
1341     finishedNavigation = false;
1342
1343     EXPECT_STREQ("Test Custom Platform", [[webView stringByEvaluatingJavaScript:@"navigator.platform"] UTF8String]);
1344 }
1345
1346 #if PLATFORM(IOS_FAMILY)
1347
1348 static const char* deviceOrientationEventTestBytes = R"TESTRESOURCE(
1349 <script>
1350 addEventListener("deviceorientation", (event) => {
1351     webkit.messageHandlers.testHandler.postMessage("received-device-orientation-event");
1352 });
1353 </script>
1354 )TESTRESOURCE";
1355
1356 @interface WebsitePoliciesDeviceOrientationDelegate : NSObject <WKNavigationDelegate> {
1357     _WKWebsiteDeviceOrientationAndMotionAccessPolicy _accessPolicy;
1358 }
1359 - (instancetype)initWithDeviceOrientationAccessPolicy:(_WKWebsiteDeviceOrientationAndMotionAccessPolicy)accessPolicy;
1360 @end
1361
1362 @implementation WebsitePoliciesDeviceOrientationDelegate
1363
1364 - (instancetype)initWithDeviceOrientationAccessPolicy:(_WKWebsiteDeviceOrientationAndMotionAccessPolicy)accessPolicy
1365 {
1366     self = [super init];
1367     _accessPolicy = accessPolicy;
1368     return self;
1369 }
1370
1371 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1372 {
1373     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1374     [websitePolicies setDeviceOrientationAndMotionAccessPolicy:_accessPolicy];
1375
1376     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1377 }
1378
1379 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
1380 {
1381     finishedNavigation = true;
1382 }
1383
1384 @end
1385
1386 static bool calledShouldAllowDeviceOrientationAndMotionAccessDelegate = false;
1387
1388 @interface WebsitePoliciesDeviceOrientationUIDelegate : NSObject <WKUIDelegatePrivate> {
1389 }
1390 @end
1391
1392 @implementation WebsitePoliciesDeviceOrientationUIDelegate
1393
1394 - (void)_webView:(WKWebView *)webView shouldAllowDeviceOrientationAndMotionAccessRequestedByFrame:(WKFrameInfo *)requestingFrame decisionHandler:(void (^)(BOOL))decisionHandler
1395 {
1396     calledShouldAllowDeviceOrientationAndMotionAccessDelegate = true;
1397     decisionHandler(YES);
1398 }
1399
1400 @end
1401
1402 static void runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicy accessPolicy)
1403 {
1404     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1405
1406     auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
1407     [schemeHandler addMappingFromURLString:@"test://localhost/main.html" toData:deviceOrientationEventTestBytes];
1408     [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
1409
1410     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1411
1412     auto delegate = adoptNS([[WebsitePoliciesDeviceOrientationDelegate alloc] initWithDeviceOrientationAccessPolicy:accessPolicy]);
1413     [webView setNavigationDelegate:delegate.get()];
1414     auto uiDelegate = adoptNS([[WebsitePoliciesDeviceOrientationUIDelegate alloc] init]);
1415     [webView setUIDelegate:uiDelegate.get()];
1416
1417     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://localhost/main.html"]];
1418     [webView loadRequest:request];
1419     TestWebKitAPI::Util::run(&finishedNavigation);
1420     finishedNavigation = false;
1421
1422     bool askedForPermission = false;
1423     [webView evaluateJavaScript:@"DeviceOrientationEvent.requestPermission()" completionHandler: [&] (id result, NSError *error) {
1424         askedForPermission = true;
1425     }];
1426     TestWebKitAPI::Util::run(&askedForPermission);
1427
1428     __block bool didReceiveMessage = false;
1429     [webView performAfterReceivingMessage:@"received-device-orientation-event" action:^{
1430         didReceiveMessage = true;
1431     }];
1432
1433     [webView _simulateDeviceOrientationChangeWithAlpha:1.0 beta:2.0 gamma:3.0];
1434
1435     if (accessPolicy == _WKWebsiteDeviceOrientationAndMotionAccessPolicyAsk)
1436         EXPECT_TRUE(calledShouldAllowDeviceOrientationAndMotionAccessDelegate);
1437     else
1438         EXPECT_FALSE(calledShouldAllowDeviceOrientationAndMotionAccessDelegate);
1439
1440     if (accessPolicy != _WKWebsiteDeviceOrientationAndMotionAccessPolicyDeny)
1441         TestWebKitAPI::Util::run(&didReceiveMessage);
1442     else {
1443         TestWebKitAPI::Util::sleep(0.1);
1444         EXPECT_FALSE(didReceiveMessage);
1445     }
1446 }
1447
1448 TEST(WebKit, WebsitePoliciesDeviceOrientationGrantAccess)
1449 {
1450     runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyGrant);
1451 }
1452
1453 TEST(WebKit, WebsitePoliciesDeviceOrientationDenyAccess)
1454 {
1455     runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyDeny);
1456 }
1457
1458 TEST(WebKit, WebsitePoliciesDeviceOrientationAskAccess)
1459 {
1460     runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyAsk);
1461 }
1462
1463 #endif // PLATFORM(IOS_FAMILY)
1464
1465 @interface PopUpPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
1466 @property (nonatomic, copy) _WKWebsitePopUpPolicy(^popUpPolicyForURL)(NSURL *);
1467 @end
1468
1469 @implementation PopUpPoliciesDelegate
1470
1471 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
1472 {
1473     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
1474     EXPECT_TRUE(false);
1475     decisionHandler(WKNavigationActionPolicyAllow);
1476 }
1477
1478 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1479 {
1480     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1481     if (_popUpPolicyForURL)
1482         websitePolicies.popUpPolicy = _popUpPolicyForURL(navigationAction.request.URL);
1483     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1484 }
1485
1486 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
1487 {
1488     return [[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration];
1489 }
1490
1491 @end
1492
1493 TEST(WebKit, WebsitePoliciesPopUp)
1494 {
1495     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1496
1497     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1498
1499     auto delegate = adoptNS([[PopUpPoliciesDelegate alloc] init]);
1500     [webView setNavigationDelegate:delegate.get()];
1501     [webView setUIDelegate:delegate.get()];
1502
1503     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"pop-up-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
1504
1505     [delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
1506         return _WKWebsitePopUpPolicyBlock;
1507     }];
1508
1509     [webView loadRequest:request];
1510     [webView waitForMessage:@"pop-up-blocked"];
1511
1512     [delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
1513         return _WKWebsitePopUpPolicyAllow;
1514     }];
1515
1516     [webView loadRequest:request];
1517     [webView waitForMessage:@"pop-up-allowed"];
1518 }
1519
1520 static bool done;
1521
1522 @interface WebsitePoliciesWebsiteDataStoreDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler, WKUIDelegate>
1523 @end
1524
1525 @implementation WebsitePoliciesWebsiteDataStoreDelegate
1526
1527 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
1528 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
1529 IGNORE_WARNINGS_END
1530 {
1531     NSURL *url = navigationAction.request.URL;
1532     if ([url.path isEqualToString:@"/invalid"]) {
1533         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1534         websitePolicies.websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:[[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease]] autorelease];
1535
1536         bool sawException = false;
1537         @try {
1538             decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1539         } @catch (NSException *exception) {
1540             sawException = true;
1541         }
1542         EXPECT_TRUE(sawException);
1543
1544         done = true;
1545     }
1546     if ([url.path isEqualToString:@"/checkStorage"]
1547         || [url.path isEqualToString:@"/checkCookies"]
1548         || [url.path isEqualToString:@"/mainFrame"]) {
1549         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1550         websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1551         decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
1552     }
1553     if ([url.path isEqualToString:@"/subFrame"]) {
1554         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
1555         websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1556         bool sawException = false;
1557         @try {
1558             decisionHandler(WKNavigationActionPolicyCancel, websitePolicies);
1559         } @catch (NSException *exception) {
1560             sawException = true;
1561         }
1562         EXPECT_TRUE(sawException);
1563         done = true;
1564     }
1565 }
1566
1567 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
1568 {
1569     NSURL *url = task.request.URL;
1570     if ([url.path isEqualToString:@"/checkStorage"]) {
1571         NSString *html = @"<script>var oldValue = window.sessionStorage['storageKey']; window.sessionStorage['storageKey'] = 'value'; alert('old value: <' + (oldValue ? 'fail' : '') + '>');</script>";
1572         [task didReceiveResponse:[[NSURLResponse alloc] initWithURL:url MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil]];
1573         [task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
1574         [task didFinish];
1575     }
1576 }
1577
1578 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
1579 {
1580 }
1581
1582 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
1583 {
1584     EXPECT_STREQ(message.UTF8String, "old value: <>");
1585     completionHandler();
1586     done = true;
1587 }
1588
1589 @end
1590
1591 RetainPtr<WKWebView> websiteDataStoreTestWebView()
1592 {
1593     auto delegate = adoptNS([[WebsitePoliciesWebsiteDataStoreDelegate alloc] init]);
1594     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1595     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
1596     [configuration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
1597     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
1598     [webView setNavigationDelegate:delegate.get()];
1599     [webView setUIDelegate:delegate.get()];
1600     return webView;
1601 }
1602
1603 TEST(WebKit, UpdateWebsitePoliciesInvalid)
1604 {
1605     auto webView = websiteDataStoreTestWebView();
1606     auto policies = adoptNS([[_WKWebsitePolicies alloc] init]);
1607     [policies setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
1608     bool sawException = false;
1609     @try {
1610         [webView _updateWebsitePolicies:policies.get()];
1611     } @catch (NSException *exception) {
1612         sawException = true;
1613     }
1614     EXPECT_TRUE(sawException);
1615     
1616     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///invalid"]]];
1617     TestWebKitAPI::Util::run(&done);
1618     
1619     done = false;
1620     [webView loadHTMLString:@"<iframe src='subFrame'></iframe>" baseURL:[NSURL URLWithString:@"http://webkit.org/mainFrame"]];
1621     TestWebKitAPI::Util::run(&done);
1622 }
1623
1624 TEST(WebKit, WebsitePoliciesDataStore)
1625 {
1626     auto cookieWebView = websiteDataStoreTestWebView();
1627     NSString *alertOldCookie = @"<script>var oldCookie = document.cookie; document.cookie = 'key=value'; alert('old value: <' + oldCookie + '>');</script>";
1628     [cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
1629     TestWebKitAPI::Util::run(&done);
1630     done = false;
1631
1632     auto pid1 = [cookieWebView _webProcessIdentifier];
1633
1634     [cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
1635     TestWebKitAPI::Util::run(&done);
1636
1637     EXPECT_NE(pid1, [cookieWebView _webProcessIdentifier]);
1638 }