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