WebKit.WebsitePoliciesAutoplayQuirks API test times out with async policy delegates
[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/WKPreferencesRefPrivate.h>
33 #import <WebKit/WKUIDelegatePrivate.h>
34 #import <WebKit/WKURLSchemeTaskPrivate.h>
35 #import <WebKit/WKUserContentControllerPrivate.h>
36 #import <WebKit/WKWebViewPrivate.h>
37 #import <WebKit/WKWebsiteDataStorePrivate.h>
38 #import <WebKit/_WKUserContentExtensionStorePrivate.h>
39 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
40 #import <WebKit/_WKWebsitePolicies.h>
41 #import <wtf/MainThread.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/text/WTFString.h>
44
45 #if PLATFORM(IOS)
46 #import <WebKit/WKWebViewConfigurationPrivate.h>
47 #endif
48
49 #if WK_API_ENABLED
50
51 @interface WKWebView ()
52 - (WKPageRef)_pageForTesting;
53 @end
54
55 static bool doneCompiling;
56 static bool receivedAlert;
57
58 #if PLATFORM(MAC)
59 static std::optional<_WKAutoplayEvent> receivedAutoplayEvent;
60 static std::optional<_WKAutoplayEventFlags> receivedAutoplayEventFlags;
61 #endif
62
63 static size_t alertCount;
64
65 @interface ContentBlockingWebsitePoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
66 @end
67
68 @implementation ContentBlockingWebsitePoliciesDelegate
69
70 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
71 {
72     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
73     EXPECT_TRUE(false);
74     decisionHandler(WKNavigationActionPolicyAllow);
75 }
76
77 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
78 {
79     switch (alertCount++) {
80     case 0:
81         // Default behavior.
82         EXPECT_STREQ("content blockers enabled", message.UTF8String);
83         break;
84     case 1:
85         // After having set websitePolicies.contentBlockersEnabled to false.
86         EXPECT_STREQ("content blockers disabled", message.UTF8String);
87         break;
88     case 2:
89         // After having reloaded without content blockers.
90         EXPECT_STREQ("content blockers disabled", message.UTF8String);
91         break;
92     default:
93         EXPECT_TRUE(false);
94     }
95     receivedAlert = true;
96     completionHandler();
97 }
98
99 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
100 {
101     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
102     switch (alertCount) {
103     case 0:
104         // Verify the content blockers behave correctly with the default behavior.
105         break;
106     case 1:
107         {
108             // Verify disabling content blockers works correctly.
109             websitePolicies.contentBlockersEnabled = false;
110             
111             // Verify calling the decisionHandler asynchronously works correctly.
112             auto decisionHandlerCopy = Block_copy(decisionHandler);
113             callOnMainThread([decisionHandlerCopy, websitePolicies = RetainPtr<_WKWebsitePolicies>(websitePolicies)] {
114                 decisionHandlerCopy(WKNavigationActionPolicyAllow, websitePolicies.get());
115                 Block_release(decisionHandlerCopy);
116             });
117         }
118         return;
119     case 2:
120         // Verify enabling content blockers has no effect when reloading without content blockers.
121         websitePolicies.contentBlockersEnabled = true;
122         break;
123     default:
124         EXPECT_TRUE(false);
125     }
126     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
127 }
128
129 @end
130
131 TEST(WebKit, WebsitePoliciesContentBlockersEnabled)
132 {
133     [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
134
135     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
136
137     doneCompiling = false;
138     NSString* contentBlocker = @"[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]";
139     [[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"WebsitePoliciesTest" encodedContentExtension:contentBlocker completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
140         EXPECT_TRUE(error == nil);
141         [[configuration userContentController] _addUserContentFilter:filter];
142         doneCompiling = true;
143     }];
144     TestWebKitAPI::Util::run(&doneCompiling);
145
146     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
147
148     auto delegate = adoptNS([[ContentBlockingWebsitePoliciesDelegate alloc] init]);
149     [webView setNavigationDelegate:delegate.get()];
150     [webView setUIDelegate:delegate.get()];
151
152     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"contentBlockerCheck" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
153     alertCount = 0;
154     receivedAlert = false;
155     [webView loadRequest:request];
156     TestWebKitAPI::Util::run(&receivedAlert);
157
158     receivedAlert = false;
159     [webView reload];
160     TestWebKitAPI::Util::run(&receivedAlert);
161
162     receivedAlert = false;
163     [webView _reloadWithoutContentBlockers];
164     TestWebKitAPI::Util::run(&receivedAlert);
165
166     [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
167 }
168
169 @interface AutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
170 @property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
171 @property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
172 @end
173
174 @implementation AutoplayPoliciesDelegate
175
176 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
177 {
178     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
179     EXPECT_TRUE(false);
180     decisionHandler(WKNavigationActionPolicyAllow);
181 }
182
183 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
184 {
185     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
186     if (_allowedAutoplayQuirksForURL)
187         websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
188     if (_autoplayPolicyForURL)
189         websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
190     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
191 }
192
193 #if PLATFORM(MAC)
194 - (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
195 {
196     receivedAutoplayEventFlags = flags;
197     receivedAutoplayEvent = event;
198 }
199 #endif
200
201 @end
202
203 @interface AsyncAutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
204 @property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
205 @property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
206 @end
207
208 @implementation AsyncAutoplayPoliciesDelegate
209
210 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
211 {
212     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
213     EXPECT_TRUE(false);
214     decisionHandler(WKNavigationActionPolicyAllow);
215 }
216
217 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
218 {
219     dispatch_async(dispatch_get_main_queue(), ^{
220         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
221         if (_allowedAutoplayQuirksForURL)
222             websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
223         if (_autoplayPolicyForURL)
224             websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
225         decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
226     });
227 }
228
229 #if PLATFORM(MAC)
230 - (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
231 {
232     receivedAutoplayEventFlags = flags;
233     receivedAutoplayEvent = event;
234 }
235 #endif
236
237 @end
238
239 TEST(WebKit, WebsitePoliciesAutoplayEnabled)
240 {
241     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
242
243 #if PLATFORM(IOS)
244     [configuration setAllowsInlineMediaPlayback:YES];
245 #endif
246
247     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
248
249     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
250     [webView setNavigationDelegate:delegate.get()];
251
252     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
253     NSURLRequest *requestWithoutAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-no-audio-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
254
255     // iOS does not support volume changes to media elements.
256 #if PLATFORM(MAC)
257     NSURLRequest *requestWithoutVolume = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-zero-volume-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
258 #endif
259
260     [delegate setAutoplayPolicyForURL:^(NSURL *) {
261         return _WKWebsiteAutoplayPolicyAllowWithoutSound;
262     }];
263     [webView loadRequest:requestWithAudio];
264     [webView waitForMessage:@"did-not-play"];
265
266     [webView loadRequest:requestWithoutAudio];
267     [webView waitForMessage:@"autoplayed"];
268
269 #if PLATFORM(MAC)
270     [webView loadRequest:requestWithoutVolume];
271     [webView waitForMessage:@"autoplayed"];
272 #endif
273
274     [delegate setAutoplayPolicyForURL:^(NSURL *) {
275         return _WKWebsiteAutoplayPolicyDeny;
276     }];
277
278 #if PLATFORM(MAC)
279     [webView loadRequest:requestWithoutVolume];
280     [webView waitForMessage:@"did-not-play"];
281 #endif
282
283     [webView loadRequest:requestWithAudio];
284     [webView waitForMessage:@"did-not-play"];
285
286     // Test updating website policies.
287     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
288     websitePolicies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
289     [webView _updateWebsitePolicies:websitePolicies];
290     [webView stringByEvaluatingJavaScript:@"playVideo()"];
291     [webView waitForMessage:@"autoplayed"];
292
293     [webView loadRequest:requestWithoutAudio];
294     [webView waitForMessage:@"did-not-play"];
295
296     [delegate setAutoplayPolicyForURL:^(NSURL *) {
297         return _WKWebsiteAutoplayPolicyAllow;
298     }];
299     [webView loadRequest:requestWithAudio];
300     [webView waitForMessage:@"autoplayed"];
301
302     [webView loadRequest:requestWithoutAudio];
303     [webView waitForMessage:@"autoplayed"];
304
305 #if PLATFORM(MAC)
306     [webView loadRequest:requestWithoutVolume];
307     [webView waitForMessage:@"autoplayed"];
308 #endif
309
310     NSURLRequest *requestWithAudioInIFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
311
312     // If the top-level document allows autoplay, any iframes should also autoplay.
313     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
314         if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
315             return _WKWebsiteAutoplayPolicyAllow;
316         return _WKWebsiteAutoplayPolicyDeny;
317     }];
318
319     [webView loadRequest:requestWithAudioInIFrame];
320     [webView waitForMessage:@"autoplayed"];
321
322     // If the top-level document disallows autoplay, any iframes should also not autoplay.
323     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
324         if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
325             return _WKWebsiteAutoplayPolicyDeny;
326         return _WKWebsiteAutoplayPolicyAllow;
327     }];
328
329     [webView loadRequest:requestWithAudioInIFrame];
330     [webView waitForMessage:@"did-not-play"];
331 }
332
333 #if PLATFORM(MAC)
334 static void runUntilReceivesAutoplayEvent(WKAutoplayEvent event)
335 {
336     while (!receivedAutoplayEvent || *receivedAutoplayEvent != event)
337         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
338 }
339
340 TEST(WebKit, WebsitePoliciesPlayAfterPreventedAutoplay)
341 {
342     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
343     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
344
345     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
346     [delegate setAutoplayPolicyForURL:^(NSURL *) {
347         return _WKWebsiteAutoplayPolicyDeny;
348     }];
349     [webView setNavigationDelegate:delegate.get()];
350     [webView setUIDelegate:delegate.get()];
351
352     NSPoint playButtonClickPoint = NSMakePoint(20, 256);
353
354     receivedAutoplayEvent = std::nullopt;
355     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
356     [webView loadRequest:jsPlayRequest];
357     [webView waitForMessage:@"loaded"];
358     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
359
360     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
361     [webView mouseUpAtPoint:playButtonClickPoint];
362     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaPreventedFromAutoplaying);
363     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
364
365     receivedAutoplayEvent = std::nullopt;
366     [webView loadHTMLString:@"" baseURL:nil];
367
368     NSURLRequest *autoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
369     [webView loadRequest:autoplayRequest];
370     [webView waitForMessage:@"loaded"];
371     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
372     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
373
374     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
375     [webView mouseUpAtPoint:playButtonClickPoint];
376     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaPreventedFromAutoplaying);
377     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
378
379     receivedAutoplayEvent = std::nullopt;
380     [webView loadHTMLString:@"" baseURL:nil];
381
382     NSURLRequest *noAutoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"no-autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
383     [webView loadRequest:noAutoplayRequest];
384     [webView waitForMessage:@"loaded"];
385     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
386     [webView mouseUpAtPoint:playButtonClickPoint];
387     [webView waitForMessage:@"played"];
388     ASSERT_TRUE(receivedAutoplayEvent == std::nullopt);
389
390     receivedAutoplayEvent = std::nullopt;
391     [webView loadHTMLString:@"" baseURL:nil];
392
393     [delegate setAutoplayPolicyForURL:^(NSURL *) {
394         return _WKWebsiteAutoplayPolicyAllowWithoutSound;
395     }];
396
397     NSURLRequest *autoplayMutedRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-muted-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
398     [webView loadRequest:autoplayMutedRequest];
399     [webView waitForMessage:@"loaded"];
400     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
401
402     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
403     [webView mouseUpAtPoint:playButtonClickPoint];
404     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaPreventedFromAutoplaying);
405     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
406 }
407
408 TEST(WebKit, WebsitePoliciesPlayingWithoutInterference)
409 {
410     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
411     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
412
413     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
414     [delegate setAutoplayPolicyForURL:^(NSURL *) {
415         return _WKWebsiteAutoplayPolicyAllow;
416     }];
417     [webView setNavigationDelegate:delegate.get()];
418     [webView setUIDelegate:delegate.get()];
419
420     receivedAutoplayEvent = std::nullopt;
421     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-autoplay-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
422     [webView loadRequest:jsPlayRequest];
423     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidAutoplayMediaPastThresholdWithoutUserInterference);
424     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
425 }
426
427 TEST(WebKit, WebsitePoliciesUserInterferenceWithPlaying)
428 {
429     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
430     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
431
432     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
433     [delegate setAutoplayPolicyForURL:^(NSURL *) {
434         return _WKWebsiteAutoplayPolicyAllow;
435     }];
436     [webView setNavigationDelegate:delegate.get()];
437     [webView setUIDelegate:delegate.get()];
438
439     receivedAutoplayEvent = std::nullopt;
440     NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
441     [webView loadRequest:jsPlayRequest];
442     [webView waitForMessage:@"playing"];
443     ASSERT_TRUE(receivedAutoplayEvent == std::nullopt);
444
445     WKPageSetMuted([webView _pageForTesting], kWKMediaAudioMuted);
446     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
447     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
448
449     receivedAutoplayEvent = std::nullopt;
450     [webView loadRequest:jsPlayRequest];
451     [webView waitForMessage:@"playing"];
452     ASSERT_TRUE(receivedAutoplayEvent == std::nullopt);
453
454     const NSPoint muteButtonClickPoint = NSMakePoint(80, 256);
455     [webView mouseDownAtPoint:muteButtonClickPoint simulatePressure:NO];
456     [webView mouseUpAtPoint:muteButtonClickPoint];
457     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
458     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
459
460     receivedAutoplayEvent = std::nullopt;
461     [webView loadRequest:jsPlayRequest];
462     [webView waitForMessage:@"playing"];
463     ASSERT_TRUE(receivedAutoplayEvent == std::nullopt);
464
465     const NSPoint playButtonClickPoint = NSMakePoint(20, 256);
466     [webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
467     [webView mouseUpAtPoint:playButtonClickPoint];
468     runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
469     ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
470 }
471
472 struct ParsedRange {
473     ParsedRange(String string)
474     {
475         // This is a strict and unsafe Range header parser adequate only for tests.
476         bool parsingMin = true;
477         size_t min = 0;
478         size_t max = 0;
479         ASSERT(string.length() > 6);
480         ASSERT(string.startsWith("bytes="));
481         for (size_t i = 6; i < string.length(); ++i) {
482             if (isASCIIDigit(string[i])) {
483                 if (parsingMin)
484                     min = min * 10 + string[i] - '0';
485                 else
486                     max = max * 10 + string[i] - '0';
487             } else if (string[i] == '-') {
488                 if (parsingMin)
489                     parsingMin = false;
490                 else
491                     return;
492             } else
493                 return;
494         }
495         if (min <= max)
496             range = std::make_pair(min, max);
497     }
498     std::optional<std::pair<size_t, size_t>> range;
499 };
500
501 @interface TestSchemeHandler : NSObject <WKURLSchemeHandler>
502 - (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data;
503 @end
504
505 @implementation TestSchemeHandler {
506     RetainPtr<NSData> videoData;
507 }
508
509 - (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data
510 {
511     self = [super init];
512     if (!self)
513         return nil;
514     
515     videoData = WTFMove(data);
516     
517     return self;
518 }
519
520 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
521 {
522     if ([task.request.URL.path isEqualToString:@"/should-redirect"]) {
523         [(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"]]];
524         
525         NSData *data = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
526         [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
527         [task didReceiveData:data];
528         [task didFinish];
529         return;
530     }
531     
532     ASSERT_TRUE([task.request.URL.path isEqualToString:@"/test.mp4"]);
533     ParsedRange parsedRange([task.request valueForHTTPHeaderField:@"Range"]);
534     ASSERT_TRUE(!!parsedRange.range);
535     auto& range = *parsedRange.range;
536     
537     NSDictionary *headerFields = @{ @"Content-Length": [@(range.second - range.first) stringValue], @"Content-Range": [NSString stringWithFormat:@"bytes %lu-%lu/%lu", range.first, range.second, [videoData length]] };
538     NSURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:task.request.URL statusCode:200 HTTPVersion:(NSString *)kCFHTTPVersion1_1 headerFields:headerFields] autorelease];
539     [task didReceiveResponse:response];
540     [task didReceiveData:[videoData subdataWithRange:NSMakeRange(range.first, range.second - range.first)]];
541     [task didFinish];
542     
543 }
544
545 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
546 {
547 }
548
549 @end
550
551 TEST(WebKit, WebsitePoliciesDuringRedirect)
552 {
553     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
554     auto videoData = adoptNS([[NSData alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"]]);
555     [configuration setURLSchemeHandler:[[[TestSchemeHandler alloc] initWithVideoData:WTFMove(videoData)] autorelease] forURLScheme:@"test"];
556     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
557     
558     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
559     [delegate setAutoplayPolicyForURL:^(NSURL *url) {
560         if ([url.path isEqualToString:@"/should-redirect"])
561             return _WKWebsiteAutoplayPolicyDeny;
562         return _WKWebsiteAutoplayPolicyAllow;
563     }];
564     [webView setNavigationDelegate:delegate.get()];
565     [webView setUIDelegate:delegate.get()];
566     
567     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///should-redirect"]]];
568     [webView waitForMessage:@"autoplayed"];
569 }
570
571 TEST(WebKit, WebsitePoliciesUpdates)
572 {
573     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
574     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
575     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
576     [webView setNavigationDelegate:delegate.get()];
577     [webView setUIDelegate:delegate.get()];
578
579     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
580
581     [delegate setAutoplayPolicyForURL:^(NSURL *) {
582         return _WKWebsiteAutoplayPolicyDeny;
583     }];
584     [webView loadRequest:requestWithAudio];
585     [webView waitForMessage:@"did-not-play"];
586
587     _WKWebsitePolicies *policies = [[[_WKWebsitePolicies alloc] init] autorelease];
588     policies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
589     [webView _updateWebsitePolicies:policies];
590
591     // Now that we updated our policies, a script should be able to autoplay media.
592     [webView stringByEvaluatingJavaScript:@"playVideo()"];
593     [webView waitForMessage:@"autoplayed"];
594
595     [webView stringByEvaluatingJavaScript:@"pauseVideo()"];
596
597     policies = [[[_WKWebsitePolicies alloc] init] autorelease];
598     policies.autoplayPolicy = _WKWebsiteAutoplayPolicyDeny;
599     [webView _updateWebsitePolicies:policies];
600
601     // A script should no longer be able to autoplay media.
602     receivedAutoplayEvent = std::nullopt;
603     [webView stringByEvaluatingJavaScript:@"playVideo()"];
604     runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
605 }
606
607 TEST(WebKit, WebsitePoliciesArbitraryUserGestureQuirk)
608 {
609     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
610     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
611
612     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
613     [webView setNavigationDelegate:delegate.get()];
614
615     WKRetainPtr<WKPreferencesRef> preferences(AdoptWK, WKPreferencesCreate());
616     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
617     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
618
619     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url)
620     {
621         return _WKWebsiteAutoplayQuirkArbitraryUserGestures;
622     }];
623     [delegate setAutoplayPolicyForURL:^(NSURL *)
624     {
625         return _WKWebsiteAutoplayPolicyDeny;
626     }];
627
628     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
629     [webView loadRequest:request];
630     [webView waitForMessage:@"did-not-play"];
631
632     const NSPoint clickPoint = NSMakePoint(760, 560);
633     [webView mouseDownAtPoint:clickPoint simulatePressure:NO];
634     [webView mouseUpAtPoint:clickPoint];
635
636     [webView stringByEvaluatingJavaScript:@"playVideo()"];
637     [webView waitForMessage:@"autoplayed"];
638 }
639
640 TEST(WebKit, WebsitePoliciesAutoplayQuirks)
641 {
642     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
643     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
644
645     auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
646     [webView setNavigationDelegate:delegate.get()];
647
648     WKRetainPtr<WKPreferencesRef> preferences(AdoptWK, WKPreferencesCreate());
649     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
650     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
651
652     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
653
654     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
655         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
656     }];
657     [delegate setAutoplayPolicyForURL:^(NSURL *) {
658         return _WKWebsiteAutoplayPolicyDeny;
659     }];
660     [webView loadRequest:requestWithAudio];
661     [webView waitForMessage:@"did-not-play"];
662     [webView waitForMessage:@"on-pause"];
663
664     receivedAutoplayEvent = std::nullopt;
665     [webView loadHTMLString:@"" baseURL:nil];
666
667     NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
668
669     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
670         if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
671             return _WKWebsiteAutoplayQuirkInheritedUserGestures;
672         
673         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
674     }];
675     [delegate setAutoplayPolicyForURL:^(NSURL *) {
676         return _WKWebsiteAutoplayPolicyDeny;
677     }];
678     [webView loadRequest:requestWithAudioInFrame];
679     [webView waitForMessage:@"did-not-play"];
680     [webView waitForMessage:@"on-pause"];
681
682     receivedAutoplayEvent = std::nullopt;
683     [webView loadHTMLString:@"" baseURL:nil];
684
685     NSURLRequest *requestThatInheritsGesture = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-inherits-gesture-from-document" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
686     [webView loadRequest:requestThatInheritsGesture];
687     [webView waitForMessage:@"loaded"];
688
689     // Click in the document, but not in the media element.
690     const NSPoint clickPoint = NSMakePoint(760, 560);
691     [webView mouseDownAtPoint:clickPoint simulatePressure:NO];
692     [webView mouseUpAtPoint:clickPoint];
693
694     [webView stringByEvaluatingJavaScript:@"play()"];
695     [webView waitForMessage:@"playing"];
696 }
697
698 TEST(WebKit, WebsitePoliciesAutoplayQuirksAsyncPolicyDelegate)
699 {
700     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
701     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
702
703     auto delegate = adoptNS([[AsyncAutoplayPoliciesDelegate alloc] init]);
704     [webView setNavigationDelegate:delegate.get()];
705
706     WKRetainPtr<WKPreferencesRef> preferences(AdoptWK, WKPreferencesCreate());
707     WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
708     WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
709
710     NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
711
712     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
713         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
714     }];
715     [delegate setAutoplayPolicyForURL:^(NSURL *) {
716         return _WKWebsiteAutoplayPolicyDeny;
717     }];
718     [webView loadRequest:requestWithAudio];
719     [webView waitForMessage:@"did-not-play"];
720     [webView waitForMessage:@"on-pause"];
721
722     receivedAutoplayEvent = std::nullopt;
723     [webView loadHTMLString:@"" baseURL:nil];
724
725     NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
726
727     [delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
728         if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
729             return _WKWebsiteAutoplayQuirkInheritedUserGestures;
730
731         return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
732     }];
733     [delegate setAutoplayPolicyForURL:^(NSURL *) {
734         return _WKWebsiteAutoplayPolicyDeny;
735     }];
736     [webView loadRequest:requestWithAudioInFrame];
737     [webView waitForMessage:@"did-not-play"];
738     [webView waitForMessage:@"on-pause"];
739 }
740 #endif // PLATFORM(MAC)
741
742 TEST(WebKit, InvalidCustomHeaders)
743 {
744     auto websitePolicies = adoptNS([[_WKWebsitePolicies alloc] init]);
745     [websitePolicies setCustomHeaderFields:@{@"invalidheader" : @"", @"noncustom" : @"header", @"    x-Custom ":@"  Needs Canonicalization\t ", @"x-other" : @"other value"}];
746     NSDictionary<NSString *, NSString *> *canonicalized = [websitePolicies customHeaderFields];
747     EXPECT_EQ(canonicalized.count, 2u);
748     EXPECT_STREQ([canonicalized objectForKey:@"x-Custom"].UTF8String, "Needs Canonicalization");
749     EXPECT_STREQ([canonicalized objectForKey:@"x-other"].UTF8String, "other value");
750 }
751
752 static bool firstTestDone;
753 static bool secondTestDone;
754 static bool thirdTestDone;
755 static bool fourthTestDone;
756
757 static void expectHeaders(id <WKURLSchemeTask> task, bool expected)
758 {
759     NSURLRequest *request = task.request;
760     if (expected) {
761         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key1"] UTF8String], "value1");
762         EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key2"] UTF8String], "value2");
763     } else {
764         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key1"] == nil);
765         EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key2"] == nil);
766     }
767 }
768
769 static void respond(id <WKURLSchemeTask>task, NSString *html = nil)
770 {
771     [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil] autorelease]];
772     [task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
773     [task didFinish];
774 }
775
776 @interface CustomHeaderFieldsDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler>
777 @end
778
779 @implementation CustomHeaderFieldsDelegate
780
781 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
782 {
783     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
784     [websitePolicies setCustomHeaderFields:@{@"X-key1": @"value1", @"X-key2": @"value2"}];
785     if ([navigationAction.request.URL.path isEqualToString:@"/mainresource"]) {
786         dispatch_async(dispatch_get_main_queue(), ^{
787             decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
788         });
789     } else
790         decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
791 }
792
793 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
794 {
795     NSString *path = urlSchemeTask.request.URL.path;
796     if ([path isEqualToString:@"/mainresource"]) {
797         expectHeaders(urlSchemeTask, true);
798         respond(urlSchemeTask, @"<script>fetch('subresource').then(function(response){fetch('test://differentsecurityorigin/crossoriginsubresource',{mode:'no-cors'})})</script>");
799     } else if ([path isEqualToString:@"/subresource"]) {
800         expectHeaders(urlSchemeTask, true);
801         respond(urlSchemeTask);
802     } else if ([path isEqualToString:@"/crossoriginsubresource"]) {
803         expectHeaders(urlSchemeTask, false);
804         respond(urlSchemeTask);
805         firstTestDone = true;
806     } else if ([path isEqualToString:@"/mainresourcewithiframe"]) {
807         expectHeaders(urlSchemeTask, true);
808         respond(urlSchemeTask, @"<iframe src='test://iframeorigin/iframemainresource'></iframe>");
809     } else if ([path isEqualToString:@"/iframemainresource"]) {
810         expectHeaders(urlSchemeTask, false);
811         respond(urlSchemeTask, @"<script>fetch('iframesubresource').then(function(response){fetch('test://mainframeorigin/originaloriginsubresource',{mode:'no-cors'})})</script>");
812     } else if ([path isEqualToString:@"/iframesubresource"]) {
813         expectHeaders(urlSchemeTask, false);
814         respond(urlSchemeTask);
815     } else if ([path isEqualToString:@"/originaloriginsubresource"]) {
816         expectHeaders(urlSchemeTask, false);
817         respond(urlSchemeTask);
818         secondTestDone = true;
819     } else if ([path isEqualToString:@"/nestedtop"]) {
820         expectHeaders(urlSchemeTask, true);
821         respond(urlSchemeTask, @"<iframe src='test://otherorigin/nestedmid'></iframe>");
822     } else if ([path isEqualToString:@"/nestedmid"]) {
823         expectHeaders(urlSchemeTask, false);
824         respond(urlSchemeTask, @"<iframe src='test://toporigin/nestedbottom'></iframe>");
825     } else if ([path isEqualToString:@"/nestedbottom"]) {
826         expectHeaders(urlSchemeTask, true);
827         respond(urlSchemeTask);
828         thirdTestDone = true;
829     } else if ([path isEqualToString:@"/requestfromaboutblank"]) {
830         expectHeaders(urlSchemeTask, true);
831         respond(urlSchemeTask);
832         fourthTestDone = true;
833     } else
834         EXPECT_TRUE(false);
835 }
836
837 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
838 {
839 }
840
841 @end
842
843 TEST(WebKit, CustomHeaderFields)
844 {
845     auto delegate = adoptNS([[CustomHeaderFieldsDelegate alloc] init]);
846     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
847     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
848     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
849     [webView setNavigationDelegate:delegate.get()];
850     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///mainresource"]]];
851     TestWebKitAPI::Util::run(&firstTestDone);
852     
853     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://mainframeorigin/mainresourcewithiframe"]]];
854     TestWebKitAPI::Util::run(&secondTestDone);
855
856     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://toporigin/nestedtop"]]];
857     TestWebKitAPI::Util::run(&thirdTestDone);
858 }
859
860 @interface PopUpPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
861 @property (nonatomic, copy) _WKWebsitePopUpPolicy(^popUpPolicyForURL)(NSURL *);
862 @end
863
864 @implementation PopUpPoliciesDelegate
865
866 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
867 {
868     // _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
869     EXPECT_TRUE(false);
870     decisionHandler(WKNavigationActionPolicyAllow);
871 }
872
873 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
874 {
875     _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
876     if (_popUpPolicyForURL)
877         websitePolicies.popUpPolicy = _popUpPolicyForURL(navigationAction.request.URL);
878     decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
879 }
880
881 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
882 {
883     return [[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration];
884 }
885
886 @end
887
888 TEST(WebKit, WebsitePoliciesPopUp)
889 {
890     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
891
892     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
893
894     auto delegate = adoptNS([[PopUpPoliciesDelegate alloc] init]);
895     [webView setNavigationDelegate:delegate.get()];
896     [webView setUIDelegate:delegate.get()];
897
898     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"pop-up-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
899
900     [delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
901         return _WKWebsitePopUpPolicyBlock;
902     }];
903
904     [webView loadRequest:request];
905     [webView waitForMessage:@"pop-up-blocked"];
906
907     [delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
908         return _WKWebsitePopUpPolicyAllow;
909     }];
910
911     [webView loadRequest:request];
912     [webView waitForMessage:@"pop-up-allowed"];
913 }
914
915 static bool done;
916
917 @interface WebsitePoliciesWebsiteDataStoreDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler, WKUIDelegate>
918 @end
919
920 @implementation WebsitePoliciesWebsiteDataStoreDelegate
921
922 - (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
923 {
924     NSURL *url = navigationAction.request.URL;
925     if ([url.path isEqualToString:@"/invalid"]) {
926         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
927         websitePolicies.websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:[[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease]] autorelease];
928
929         bool sawException = false;
930         @try {
931             decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
932         } @catch (NSException *exception) {
933             sawException = true;
934         }
935         EXPECT_TRUE(sawException);
936
937         done = true;
938     }
939     if ([url.path isEqualToString:@"/checkStorage"]
940         || [url.path isEqualToString:@"/checkCookies"]
941         || [url.path isEqualToString:@"/mainFrame"]) {
942         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
943         websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
944         decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
945     }
946     if ([url.path isEqualToString:@"/subFrame"]) {
947         _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
948         websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
949         bool sawException = false;
950         @try {
951             decisionHandler(WKNavigationActionPolicyCancel, websitePolicies);
952         } @catch (NSException *exception) {
953             sawException = true;
954         }
955         EXPECT_TRUE(sawException);
956         done = true;
957     }
958 }
959
960 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
961 {
962     NSURL *url = task.request.URL;
963     if ([url.path isEqualToString:@"/checkStorage"]) {
964         NSString *html = @"<script>var oldValue = window.sessionStorage['storageKey']; window.sessionStorage['storageKey'] = 'value'; alert('old value: <' + (oldValue ? 'fail' : '') + '>');</script>";
965         [task didReceiveResponse:[[NSURLResponse alloc] initWithURL:url MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil]];
966         [task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
967         [task didFinish];
968     }
969 }
970
971 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
972 {
973 }
974
975 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
976 {
977     EXPECT_STREQ(message.UTF8String, "old value: <>");
978     completionHandler();
979     done = true;
980 }
981
982 @end
983
984 RetainPtr<WKWebView> websiteDataStoreTestWebView()
985 {
986     auto delegate = adoptNS([[WebsitePoliciesWebsiteDataStoreDelegate alloc] init]);
987     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
988     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
989     [configuration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
990     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
991     [webView setNavigationDelegate:delegate.get()];
992     [webView setUIDelegate:delegate.get()];
993     return webView;
994 }
995
996 TEST(WebKit, UpdateWebsitePoliciesInvalid)
997 {
998     auto webView = websiteDataStoreTestWebView();
999     auto policies = adoptNS([[_WKWebsitePolicies alloc] init]);
1000     [policies setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
1001     bool sawException = false;
1002     @try {
1003         [webView _updateWebsitePolicies:policies.get()];
1004     } @catch (NSException *exception) {
1005         sawException = true;
1006     }
1007     EXPECT_TRUE(sawException);
1008     
1009     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///invalid"]]];
1010     TestWebKitAPI::Util::run(&done);
1011     
1012     done = false;
1013     [webView loadHTMLString:@"<iframe src='subFrame'></iframe>" baseURL:[NSURL URLWithString:@"http://webkit.org/mainFrame"]];
1014     TestWebKitAPI::Util::run(&done);
1015 }
1016
1017 TEST(WebKit, WebsitePoliciesDataStore)
1018 {
1019     auto cookieWebView = websiteDataStoreTestWebView();
1020     NSString *alertOldCookie = @"<script>var oldCookie = document.cookie; document.cookie = 'key=value'; alert('old value: <' + oldCookie + '>');</script>";
1021     [cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
1022     TestWebKitAPI::Util::run(&done);
1023     done = false;
1024     [cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
1025     TestWebKitAPI::Util::run(&done);
1026 }
1027
1028 #endif // WK_API_ENABLED