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