blob: beb312aec73ebc47367948e24b171dfec5ca4e3e [file] [log] [blame]
/*
* Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#import "PlatformUtilities.h"
#import "TestNavigationDelegate.h"
#import "TestWKWebView.h"
#import <WebKit/WKMutableDictionary.h>
#import <WebKit/WKNavigationDelegatePrivate.h>
#import <WebKit/WKPagePrivate.h>
#import <WebKit/WKPreferencesPrivate.h>
#import <WebKit/WKPreferencesRefPrivate.h>
#import <WebKit/WKString.h>
#import <WebKit/WKUIDelegatePrivate.h>
#import <WebKit/WKURLSchemeTaskPrivate.h>
#import <WebKit/WKUserContentControllerPrivate.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WKWebsiteDataStorePrivate.h>
#import <WebKit/WKWebsitePolicies.h>
#import <WebKit/_WKCustomHeaderFields.h>
#import <WebKit/_WKUserContentExtensionStorePrivate.h>
#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
#import <WebKit/_WKWebsitePolicies.h>
#import <wtf/Function.h>
#import <wtf/HashMap.h>
#import <wtf/MainThread.h>
#import <wtf/RetainPtr.h>
#import <wtf/text/StringHash.h>
#import <wtf/text/WTFString.h>
#if PLATFORM(IOS_FAMILY)
#import <WebKit/WKWebViewConfigurationPrivate.h>
#endif
@interface WKWebView ()
- (WKPageRef)_pageForTesting;
@end
static bool doneCompiling;
static bool receivedAlert;
static bool finishedNavigation;
#if PLATFORM(MAC)
static Optional<_WKAutoplayEvent> receivedAutoplayEvent;
static Optional<_WKAutoplayEventFlags> receivedAutoplayEventFlags;
#endif
static size_t alertCount;
@interface ContentBlockingWebsitePoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
@end
@implementation ContentBlockingWebsitePoliciesDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
EXPECT_TRUE(false);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
switch (alertCount++) {
case 0:
// Default behavior.
EXPECT_STREQ("content blockers enabled", message.UTF8String);
break;
case 1:
// After having set websitePolicies.contentBlockersEnabled to false.
EXPECT_STREQ("content blockers disabled", message.UTF8String);
break;
case 2:
// After having reloaded without content blockers.
EXPECT_STREQ("content blockers disabled", message.UTF8String);
break;
default:
EXPECT_TRUE(false);
}
receivedAlert = true;
completionHandler();
}
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
switch (alertCount) {
case 0:
// Verify the content blockers behave correctly with the default behavior.
break;
case 1:
{
// Verify disabling content blockers works correctly.
websitePolicies.contentBlockersEnabled = false;
// Verify calling the decisionHandler asynchronously works correctly.
auto decisionHandlerCopy = Block_copy(decisionHandler);
callOnMainThread([decisionHandlerCopy, websitePolicies = RetainPtr<_WKWebsitePolicies>(websitePolicies)] {
decisionHandlerCopy(WKNavigationActionPolicyAllow, websitePolicies.get());
Block_release(decisionHandlerCopy);
});
}
return;
case 2:
// Verify enabling content blockers has no effect when reloading without content blockers.
websitePolicies.contentBlockersEnabled = true;
break;
default:
EXPECT_TRUE(false);
}
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
@end
TEST(WebKit, WebsitePoliciesContentBlockersEnabled)
{
[[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
doneCompiling = false;
NSString* contentBlocker = @"[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]";
[[_WKUserContentExtensionStore defaultStore] compileContentExtensionForIdentifier:@"WebsitePoliciesTest" encodedContentExtension:contentBlocker completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
EXPECT_TRUE(error == nil);
[[configuration userContentController] _addUserContentFilter:filter];
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[ContentBlockingWebsitePoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"contentBlockerCheck" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
alertCount = 0;
receivedAlert = false;
[webView loadRequest:request];
TestWebKitAPI::Util::run(&receivedAlert);
receivedAlert = false;
[webView reload];
TestWebKitAPI::Util::run(&receivedAlert);
receivedAlert = false;
[webView _reloadWithoutContentBlockers];
TestWebKitAPI::Util::run(&receivedAlert);
[[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
}
@interface AutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
@property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
@property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
@end
@implementation AutoplayPoliciesDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
EXPECT_TRUE(false);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (_allowedAutoplayQuirksForURL)
websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
if (_autoplayPolicyForURL)
websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
#if PLATFORM(MAC)
- (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
{
receivedAutoplayEventFlags = flags;
receivedAutoplayEvent = event;
}
#endif
@end
@interface AsyncAutoplayPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
@property (nonatomic, copy) _WKWebsiteAutoplayPolicy(^autoplayPolicyForURL)(NSURL *);
@property (nonatomic, copy) _WKWebsiteAutoplayQuirk(^allowedAutoplayQuirksForURL)(NSURL *);
@end
@implementation AsyncAutoplayPoliciesDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
EXPECT_TRUE(false);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
dispatch_async(dispatch_get_main_queue(), ^{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (_allowedAutoplayQuirksForURL)
websitePolicies.allowedAutoplayQuirks = _allowedAutoplayQuirksForURL(navigationAction.request.URL);
if (_autoplayPolicyForURL)
websitePolicies.autoplayPolicy = _autoplayPolicyForURL(navigationAction.request.URL);
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
});
}
#if PLATFORM(MAC)
- (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags
{
receivedAutoplayEventFlags = flags;
receivedAutoplayEvent = event;
}
#endif
@end
@interface WebsitePoliciesNavigationDelegate : TestNavigationDelegate <WKNavigationDelegatePrivate>
@property (nonatomic, copy) void (^decidePolicyForNavigationActionWithWebsitePolicies)(WKNavigationAction *, id <NSSecureCoding>, void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *));
@end
@implementation WebsitePoliciesNavigationDelegate
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
if (_decidePolicyForNavigationActionWithWebsitePolicies)
_decidePolicyForNavigationActionWithWebsitePolicies(navigationAction, userInfo, decisionHandler);
else
decisionHandler(WKNavigationActionPolicyAllow, nil);
}
@end
TEST(WebKit, WebsitePoliciesAutoplayEnabled)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
#if PLATFORM(IOS_FAMILY)
[configuration setAllowsInlineMediaPlayback:YES];
#endif
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
NSURLRequest *requestWithoutAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-no-audio-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
// iOS does not support volume changes to media elements.
#if PLATFORM(MAC)
NSURLRequest *requestWithoutVolume = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-zero-volume-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
#endif
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllowWithoutSound;
}];
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"did-not-play"];
[webView loadRequest:requestWithoutAudio];
[webView waitForMessage:@"autoplayed"];
#if PLATFORM(MAC)
[webView loadRequest:requestWithoutVolume];
[webView waitForMessage:@"autoplayed"];
#endif
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
#if PLATFORM(MAC)
[webView loadRequest:requestWithoutVolume];
[webView waitForMessage:@"did-not-play"];
#endif
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"did-not-play"];
// Test updating website policies.
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
websitePolicies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
[webView _updateWebsitePolicies:websitePolicies];
[webView stringByEvaluatingJavaScript:@"playVideo()"];
[webView waitForMessage:@"autoplayed"];
[webView loadRequest:requestWithoutAudio];
[webView waitForMessage:@"did-not-play"];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"autoplayed"];
[webView loadRequest:requestWithoutAudio];
[webView waitForMessage:@"autoplayed"];
#if PLATFORM(MAC)
[webView loadRequest:requestWithoutVolume];
[webView waitForMessage:@"autoplayed"];
#endif
NSURLRequest *requestWithAudioInIFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
// If the top-level document allows autoplay, any iframes should also autoplay.
[delegate setAutoplayPolicyForURL:^(NSURL *url) {
if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
return _WKWebsiteAutoplayPolicyAllow;
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudioInIFrame];
[webView waitForMessage:@"autoplayed"];
// If the top-level document disallows autoplay, any iframes should also not autoplay.
[delegate setAutoplayPolicyForURL:^(NSURL *url) {
if ([url.lastPathComponent isEqualToString:@"autoplay-check-in-iframe.html"])
return _WKWebsiteAutoplayPolicyDeny;
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView loadRequest:requestWithAudioInIFrame];
[webView waitForMessage:@"did-not-play"];
}
#if PLATFORM(MAC)
static void runUntilReceivesAutoplayEvent(WKAutoplayEvent event)
{
while (!receivedAutoplayEvent || *receivedAutoplayEvent != event)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
}
TEST(WebKit, WebsitePoliciesPlayAfterPreventedAutoplay)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
NSPoint playButtonClickPoint = NSMakePoint(20, 256);
receivedAutoplayEvent = WTF::nullopt;
NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:jsPlayRequest];
[webView waitForMessage:@"loaded"];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
NSURLRequest *autoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:autoplayRequest];
[webView waitForMessage:@"loaded"];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
NSURLRequest *noAutoplayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"no-autoplay-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:noAutoplayRequest];
[webView waitForMessage:@"loaded"];
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
[webView waitForMessage:@"played"];
ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllowWithoutSound;
}];
NSURLRequest *autoplayMutedRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-muted-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:autoplayMutedRequest];
[webView waitForMessage:@"loaded"];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsPlaybackWasPrevented);
}
TEST(WebKit, WebsitePoliciesPlayingWithUserGesture)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
receivedAutoplayEvent = WTF::nullopt;
NSPoint playButtonClickPoint = NSMakePoint(20, 580);
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"audio-with-play-button" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView waitForMessage:@"loaded"];
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
ASSERT_FALSE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsMediaIsMainContent);
receivedAutoplayEvent = WTF::nullopt;
request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"video-with-play-button" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView waitForMessage:@"loaded"];
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPlayMediaWithUserGesture);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsMediaIsMainContent);
}
TEST(WebKit, WebsitePoliciesPlayingWithoutInterference)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
receivedAutoplayEvent = WTF::nullopt;
NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-autoplay-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:jsPlayRequest];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidAutoplayMediaPastThresholdWithoutUserInterference);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
}
TEST(WebKit, WebsitePoliciesUserInterferenceWithPlaying)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
receivedAutoplayEvent = WTF::nullopt;
NSURLRequest *jsPlayRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"js-play-with-controls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:jsPlayRequest];
[webView waitForMessage:@"playing"];
ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
WKPageSetMuted([webView _pageForTesting], kWKMediaAudioMuted);
runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
receivedAutoplayEvent = WTF::nullopt;
[webView loadRequest:jsPlayRequest];
[webView waitForMessage:@"playing"];
ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
const NSPoint muteButtonClickPoint = NSMakePoint(80, 256);
[webView mouseDownAtPoint:muteButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:muteButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
receivedAutoplayEvent = WTF::nullopt;
[webView loadRequest:jsPlayRequest];
[webView waitForMessage:@"playing"];
ASSERT_TRUE(receivedAutoplayEvent == WTF::nullopt);
const NSPoint playButtonClickPoint = NSMakePoint(20, 256);
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
runUntilReceivesAutoplayEvent(kWKAutoplayEventUserDidInterfereWithPlayback);
ASSERT_TRUE(*receivedAutoplayEventFlags & kWKAutoplayEventFlagsHasAudio);
}
TEST(WebKit, WebsitePoliciesWithBridgingCast)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
auto delegate = adoptNS([[WebsitePoliciesNavigationDelegate alloc] init]);
__block bool didInvokeDecisionHandler = false;
[delegate setDecidePolicyForNavigationActionWithWebsitePolicies:^(WKNavigationAction *, id <NSSecureCoding>, void (^decisionHandler)(WKNavigationActionPolicy, _WKWebsitePolicies *)) {
auto policies = adoptWK(WKWebsitePoliciesCreate());
decisionHandler(WKNavigationActionPolicyAllow, (__bridge _WKWebsitePolicies *)policies.get());
didInvokeDecisionHandler = true;
}];
[webView setNavigationDelegate:delegate.get()];
[webView loadTestPageNamed:@"simple"];
[delegate waitForDidFinishNavigation];
EXPECT_TRUE(didInvokeDecisionHandler);
}
TEST(WebKit, WebsitePoliciesWithUnexpectedType)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 336, 276) configuration:configuration.get()]);
auto delegate = adoptNS([[WebsitePoliciesNavigationDelegate alloc] init]);
__block bool didCatchException = false;
[delegate setDecidePolicyForNavigationActionWithWebsitePolicies:^(WKNavigationAction *, id <NSSecureCoding>, void (^decisionHandler)(WKNavigationActionPolicy, _WKWebsitePolicies *)) {
@try {
id fakePolicies = @"Hello";
decisionHandler(WKNavigationActionPolicyAllow, (_WKWebsitePolicies *)fakePolicies);
} @catch (NSException *exception) {
didCatchException = true;
}
}];
[webView setNavigationDelegate:delegate.get()];
[webView loadTestPageNamed:@"simple"];
TestWebKitAPI::Util::run(&didCatchException);
}
struct ParsedRange {
ParsedRange(String string)
{
// This is a strict and unsafe Range header parser adequate only for tests.
bool parsingMin = true;
size_t min = 0;
size_t max = 0;
ASSERT(string.length() > 6);
ASSERT(string.startsWith("bytes="));
for (size_t i = 6; i < string.length(); ++i) {
if (isASCIIDigit(string[i])) {
if (parsingMin)
min = min * 10 + string[i] - '0';
else
max = max * 10 + string[i] - '0';
} else if (string[i] == '-') {
if (parsingMin)
parsingMin = false;
else
return;
} else
return;
}
if (min <= max)
range = std::make_pair(min, max);
}
Optional<std::pair<size_t, size_t>> range;
};
@interface TestSchemeHandler : NSObject <WKURLSchemeHandler>
- (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data;
@end
@implementation TestSchemeHandler {
RetainPtr<NSData> videoData;
}
- (instancetype)initWithVideoData:(RetainPtr<NSData>&&)data
{
self = [super init];
if (!self)
return nil;
videoData = WTFMove(data);
return self;
}
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
{
if ([task.request.URL.path isEqualToString:@"/should-redirect"]) {
[(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"]]];
NSData *data = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
[task didReceiveData:data];
[task didFinish];
return;
}
ASSERT_TRUE([task.request.URL.path isEqualToString:@"/test.mp4"]);
ParsedRange parsedRange([task.request valueForHTTPHeaderField:@"Range"]);
ASSERT_TRUE(!!parsedRange.range);
auto& range = *parsedRange.range;
NSDictionary *headerFields = @{ @"Content-Length": [@(range.second - range.first) stringValue], @"Content-Range": [NSString stringWithFormat:@"bytes %lu-%lu/%lu", range.first, range.second, [videoData length]] };
NSURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:task.request.URL statusCode:200 HTTPVersion:(NSString *)kCFHTTPVersion1_1 headerFields:headerFields] autorelease];
[task didReceiveResponse:response];
[task didReceiveData:[videoData subdataWithRange:NSMakeRange(range.first, range.second - range.first)]];
[task didFinish];
}
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
{
}
@end
TEST(WebKit, WebsitePoliciesDuringRedirect)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto videoData = adoptNS([[NSData alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"]]);
[configuration setURLSchemeHandler:[[[TestSchemeHandler alloc] initWithVideoData:WTFMove(videoData)] autorelease] forURLScheme:@"test"];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[delegate setAutoplayPolicyForURL:^(NSURL *url) {
if ([url.path isEqualToString:@"/should-redirect"])
return _WKWebsiteAutoplayPolicyDeny;
return _WKWebsiteAutoplayPolicyAllow;
}];
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///should-redirect"]]];
[webView waitForMessage:@"autoplayed"];
}
TEST(WebKit, WebsitePoliciesUpdates)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"did-not-play"];
_WKWebsitePolicies *policies = [[[_WKWebsitePolicies alloc] init] autorelease];
policies.autoplayPolicy = _WKWebsiteAutoplayPolicyAllow;
[webView _updateWebsitePolicies:policies];
// Now that we updated our policies, a script should be able to autoplay media.
[webView stringByEvaluatingJavaScript:@"playVideo()"];
[webView waitForMessage:@"autoplayed"];
[webView stringByEvaluatingJavaScript:@"pauseVideo()"];
policies = [[[_WKWebsitePolicies alloc] init] autorelease];
policies.autoplayPolicy = _WKWebsiteAutoplayPolicyDeny;
[webView _updateWebsitePolicies:policies];
// A script should no longer be able to autoplay media.
receivedAutoplayEvent = WTF::nullopt;
[webView stringByEvaluatingJavaScript:@"playVideo()"];
runUntilReceivesAutoplayEvent(kWKAutoplayEventDidPreventFromAutoplaying);
}
TEST(WebKit, WebsitePoliciesArbitraryUserGestureQuirk)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url)
{
return _WKWebsiteAutoplayQuirkArbitraryUserGestures;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *)
{
return _WKWebsiteAutoplayPolicyDeny;
}];
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:request];
[webView waitForMessage:@"did-not-play"];
const NSPoint clickPoint = NSMakePoint(760, 560);
[webView mouseDownAtPoint:clickPoint simulatePressure:NO];
[webView mouseUpAtPoint:clickPoint];
[webView stringByEvaluatingJavaScript:@"playVideo()"];
[webView waitForMessage:@"autoplayed"];
}
TEST(WebKit, WebsitePoliciesAutoplayQuirks)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"did-not-play"];
[webView waitForMessage:@"on-pause"];
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
return _WKWebsiteAutoplayQuirkInheritedUserGestures;
return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudioInFrame];
[webView waitForMessage:@"did-not-play"];
[webView waitForMessage:@"on-pause"];
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
NSURLRequest *requestThatInheritsGesture = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-inherits-gesture-from-document" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[webView loadRequest:requestThatInheritsGesture];
[webView waitForMessage:@"loaded"];
// Click in the document, but not in the media element.
const NSPoint clickPoint = NSMakePoint(760, 560);
[webView mouseDownAtPoint:clickPoint simulatePressure:NO];
[webView mouseUpAtPoint:clickPoint];
[webView stringByEvaluatingJavaScript:@"play()"];
[webView waitForMessage:@"playing"];
}
TEST(WebKit, WebsitePoliciesPerDocumentAutoplayBehaviorQuirks)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
receivedAutoplayEvent = WTF::nullopt;
NSURLRequest *requestWithMultipleMediaElements = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplaying-multiple-media-elements" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
return _WKWebsiteAutoplayQuirkPerDocumentAutoplayBehavior;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithMultipleMediaElements];
[webView waitForMessage:@"loaded"];
// We should not be allowed to play without a user gesture.
[webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video1')" completionHandler:nil];
[webView waitForMessage:@"did-not-play-video1"];
// Now try again with a user gesture...
const NSPoint playButtonClickPoint = NSMakePoint(20, 580);
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
[webView waitForMessage:@"did-play-video1"];
// Now video2 should also be allowed to autoplay without a user gesture because of the quirk.
[webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video2')" completionHandler:nil];
[webView waitForMessage:@"did-play-video2"];
// Now let's test this without the quirk.
[webView loadHTMLString:@"" baseURL:nil];
receivedAutoplayEvent = WTF::nullopt;
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
return 0;
}];
[webView loadRequest:requestWithMultipleMediaElements];
[webView waitForMessage:@"loaded"];
// We should not be allowed to play without a user gesture.
[webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video1')" completionHandler:nil];
[webView waitForMessage:@"did-not-play-video1"];
// Now try again with a user gesture...
[webView mouseDownAtPoint:playButtonClickPoint simulatePressure:NO];
[webView mouseUpAtPoint:playButtonClickPoint];
[webView waitForMessage:@"did-play-video1"];
// Now video2 should not be allowed to autoplay without a user gesture.
[webView _evaluateJavaScriptWithoutUserGesture:@"playVideo('video2')" completionHandler:nil];
[webView waitForMessage:@"did-not-play-video2"];
}
TEST(WebKit, WebsitePoliciesAutoplayQuirksAsyncPolicyDelegate)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[AsyncAutoplayPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
WKRetainPtr<WKPreferencesRef> preferences = adoptWK(WKPreferencesCreate());
WKPreferencesSetNeedsSiteSpecificQuirks(preferences.get(), true);
WKPageGroupSetPreferences(WKPageGetPageGroup([webView _pageForTesting]), preferences.get());
NSURLRequest *requestWithAudio = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudio];
[webView waitForMessage:@"did-not-play"];
[webView waitForMessage:@"on-pause"];
receivedAutoplayEvent = WTF::nullopt;
[webView loadHTMLString:@"" baseURL:nil];
NSURLRequest *requestWithAudioInFrame = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"autoplay-check-in-iframe" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setAllowedAutoplayQuirksForURL:^_WKWebsiteAutoplayQuirk(NSURL *url) {
if ([url.lastPathComponent isEqualToString:@"autoplay-check-frame.html"])
return _WKWebsiteAutoplayQuirkInheritedUserGestures;
return _WKWebsiteAutoplayQuirkSynthesizedPauseEvents | _WKWebsiteAutoplayQuirkInheritedUserGestures;
}];
[delegate setAutoplayPolicyForURL:^(NSURL *) {
return _WKWebsiteAutoplayPolicyDeny;
}];
[webView loadRequest:requestWithAudioInFrame];
[webView waitForMessage:@"did-not-play"];
[webView waitForMessage:@"on-pause"];
}
#endif // PLATFORM(MAC)
TEST(WebKit, InvalidCustomHeaders)
{
auto customHeaderFields = adoptNS([[_WKCustomHeaderFields alloc] init]);
[customHeaderFields setFields:@{@"invalidheader" : @"", @"noncustom" : @"header", @" x-Custom ":@" Needs Canonicalization\t ", @"x-other" : @"other value"}];
NSDictionary<NSString *, NSString *> *canonicalized = [customHeaderFields fields];
EXPECT_EQ(canonicalized.count, 2u);
EXPECT_STREQ([canonicalized objectForKey:@"x-Custom"].UTF8String, "Needs Canonicalization");
EXPECT_STREQ([canonicalized objectForKey:@"x-other"].UTF8String, "other value");
}
static bool firstTestDone;
static bool secondTestDone;
static bool thirdTestDone;
static bool fourthTestDone;
static bool fifthTestDone;
static void expectLegacyHeaders(id <WKURLSchemeTask> task, bool expected)
{
NSURLRequest *request = task.request;
if (expected) {
EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key1"] UTF8String], "value1");
EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key2"] UTF8String], "value2");
} else {
EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key1"] == nil);
EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key2"] == nil);
}
}
static void expectHeaders(id <WKURLSchemeTask> task, bool expected)
{
NSURLRequest *request = task.request;
if (expected) {
EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key3"] UTF8String], "value3");
EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key4"] UTF8String], "value4");
} else {
EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key3"] == nil);
EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key4"] == nil);
}
}
static void respond(id <WKURLSchemeTask>task, NSString *html = nil)
{
[task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil] autorelease]];
[task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
[task didFinish];
}
@interface CustomHeaderFieldsDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler>
@end
@implementation CustomHeaderFieldsDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler
{
auto legacyHeaderFieldDictionary = adoptWK(WKMutableDictionaryCreate());
WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key1")).get(), adoptWK(WKStringCreateWithUTF8CString("value1")).get());
WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key2")).get(), adoptWK(WKStringCreateWithUTF8CString("value2")).get());
WKWebsitePoliciesSetCustomHeaderFields((WKWebsitePoliciesRef)preferences, legacyHeaderFieldDictionary.get());
_WKCustomHeaderFields *headerFields = [[[_WKCustomHeaderFields alloc] init] autorelease];
[headerFields setFields:@{@"X-key3": @"value3", @"X-key4": @"value4"}];
[headerFields setThirdPartyDomains:@[
@"*.hostwithasterisk.com",
@"hostwithoutasterisk.com",
@"*.com" // should be ignored.
]];
[preferences _setCustomHeaderFields:@[headerFields]];
if ([navigationAction.request.URL.path isEqualToString:@"/mainresource"]) {
dispatch_async(dispatch_get_main_queue(), ^{
decisionHandler(WKNavigationActionPolicyAllow, preferences);
});
} else
decisionHandler(WKNavigationActionPolicyAllow, preferences);
}
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
NSString *path = urlSchemeTask.request.URL.path;
if ([path isEqualToString:@"/mainresource"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask, @"<script>fetch('subresource').then(function(response){fetch('test://differentsecurityorigin/crossoriginsubresource',{mode:'no-cors'})})</script>");
} else if ([path isEqualToString:@"/subresource"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask);
} else if ([path isEqualToString:@"/crossoriginsubresource"]) {
expectLegacyHeaders(urlSchemeTask, false);
respond(urlSchemeTask);
firstTestDone = true;
} else if ([path isEqualToString:@"/mainresourcewithiframe"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask, @"<iframe src='test://iframeorigin/iframemainresource'></iframe>");
} else if ([path isEqualToString:@"/iframemainresource"]) {
expectLegacyHeaders(urlSchemeTask, false);
respond(urlSchemeTask, @"<script>fetch('iframesubresource').then(function(response){fetch('test://mainframeorigin/originaloriginsubresource',{mode:'no-cors'})})</script>");
} else if ([path isEqualToString:@"/iframesubresource"]) {
expectLegacyHeaders(urlSchemeTask, false);
respond(urlSchemeTask);
} else if ([path isEqualToString:@"/originaloriginsubresource"]) {
expectLegacyHeaders(urlSchemeTask, false);
respond(urlSchemeTask);
secondTestDone = true;
} else if ([path isEqualToString:@"/nestedtop"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask, @"<iframe src='test://otherorigin/nestedmid'></iframe>");
} else if ([path isEqualToString:@"/nestedmid"]) {
expectLegacyHeaders(urlSchemeTask, false);
respond(urlSchemeTask, @"<iframe src='test://toporigin/nestedbottom'></iframe>");
} else if ([path isEqualToString:@"/nestedbottom"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask);
thirdTestDone = true;
} else if ([path isEqualToString:@"/requestfromaboutblank"]) {
expectLegacyHeaders(urlSchemeTask, true);
respond(urlSchemeTask);
fourthTestDone = true;
} else if ([path isEqualToString:@"/testcustomheaderfieldhosts"]) {
expectHeaders(urlSchemeTask, true);
NSString *html = @"<script>fetch('test://a.b.c.sub.hostwithasterisk.com/hosttest1',{mode:'no-cors'})"
".then(function(response){fetch('test://subhostwithasterisk.com/hosttest2',{mode:'no-cors'})})"
".then(function(response){fetch('test://hostwithoutasterisk.com/hosttest3',{mode:'no-cors'})})"
".then(function(response){fetch('test://a.b.c.sub.hostwithoutasterisk.com/hosttest4',{mode:'no-cors'})})</script>";
respond(urlSchemeTask, html);
} else if ([path isEqualToString:@"/hosttest1"]) {
expectHeaders(urlSchemeTask, true);
respond(urlSchemeTask);
} else if ([path isEqualToString:@"/hosttest2"]) {
expectHeaders(urlSchemeTask, false);
respond(urlSchemeTask);
} else if ([path isEqualToString:@"/hosttest3"]) {
expectHeaders(urlSchemeTask, true);
respond(urlSchemeTask);
} else if ([path isEqualToString:@"/hosttest4"]) {
expectHeaders(urlSchemeTask, false);
respond(urlSchemeTask);
fifthTestDone = true;
} else if ([path isEqualToString:@"/testcustomheaderfieldhosts"])
EXPECT_TRUE(false);
}
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
}
@end
TEST(WebKit, CustomHeaderFields)
{
auto delegate = adoptNS([[CustomHeaderFieldsDelegate alloc] init]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView setNavigationDelegate:delegate.get()];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///mainresource"]]];
TestWebKitAPI::Util::run(&firstTestDone);
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://mainframeorigin/mainresourcewithiframe"]]];
TestWebKitAPI::Util::run(&secondTestDone);
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://toporigin/nestedtop"]]];
TestWebKitAPI::Util::run(&thirdTestDone);
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://host/testcustomheaderfieldhosts"]]];
TestWebKitAPI::Util::run(&fifthTestDone);
}
static unsigned loadCount;
@interface DataMappingSchemeHandler : NSObject <WKURLSchemeHandler> {
HashMap<String, RetainPtr<NSData>> _dataMappings;
Function<void(id <WKURLSchemeTask>)> _taskHandler;
}
- (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data;
- (void)setTaskHandler:(Function<void(id <WKURLSchemeTask>)>&&)handler;
@end
@implementation DataMappingSchemeHandler
- (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data
{
_dataMappings.set(urlString, [NSData dataWithBytesNoCopy:(void*)data length:strlen(data) freeWhenDone:NO]);
}
- (void)setTaskHandler:(Function<void(id <WKURLSchemeTask>)>&&)handler
{
_taskHandler = WTFMove(handler);
}
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
{
NSURL *finalURL = task.request.URL;
++loadCount;
if (_taskHandler)
_taskHandler(task);
auto response = adoptNS([[NSURLResponse alloc] initWithURL:finalURL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
[task didReceiveResponse:response.get()];
if (auto data = _dataMappings.get([finalURL absoluteString]))
[task didReceiveData:data.get()];
else
[task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
[task didFinish];
}
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
{
}
@end
@interface CustomUserAgentDelegate : NSObject <WKNavigationDelegate> {
}
@end
@implementation CustomUserAgentDelegate
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (navigationAction.targetFrame.mainFrame)
[websitePolicies setCustomUserAgent:@"Foo Custom UserAgent"];
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
finishedNavigation = true;
}
@end
static const char* customUserAgentMainFrameTestBytes = R"TESTRESOURCE(
<script src="test://www.webkit.org/script.js"></script>
<img src="test://www.webkit.org/image.png"></img>
<iframe src="test://www.apple.com/subframe.html"></iframe>
<script>
onload = () => {
setTimeout(() => {
fetch("test://www.webkit.org/fetchResource.html");
}, 0);
}
onmessage = (event) => {
window.subframeUserAgent = event.data;
}
</script>
)TESTRESOURCE";
static const char* customUserAgentSubFrameTestBytes = R"TESTRESOURCE(
<script src="test://www.apple.com/script.js"></script>
<img src="test://www.apple.com/image.png"></img>
<iframe src="test://www.apple.com/subframe2.html"></iframe>
<script>
onload = () => {
setTimeout(() => {
fetch("test://www.apple.com/fetchResource.html");
}, 0);
top.postMessage(navigator.userAgent, '*');
}
</script>
)TESTRESOURCE";
TEST(WebKit, WebsitePoliciesCustomUserAgent)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
[schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
[schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
[schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
EXPECT_STREQ("Foo Custom UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
}];
[configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[CustomUserAgentDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
loadCount = 0;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
while (loadCount != 9U)
TestWebKitAPI::Util::spinRunLoop();
loadCount = 0;
request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.google.com/main.html"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
EXPECT_EQ(1U, loadCount);
loadCount = 0;
}
@interface CustomJavaScriptUserAgentDelegate : NSObject <WKNavigationDelegate>
@property (nonatomic) BOOL setCustomUserAgent;
@end
@implementation CustomJavaScriptUserAgentDelegate
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (navigationAction.targetFrame.mainFrame) {
[websitePolicies setCustomJavaScriptUserAgentAsSiteSpecificQuirks:@"Foo Custom JavaScript UserAgent"];
if (_setCustomUserAgent)
[websitePolicies setCustomUserAgent:@"Foo Custom Request UserAgent"];
}
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
finishedNavigation = true;
}
@end
TEST(WebKit, WebsitePoliciesCustomJavaScriptUserAgent)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
[schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
[schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
[schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
auto* userAgentString = [task.request valueForHTTPHeaderField:@"User-Agent"];
EXPECT_TRUE([userAgentString hasPrefix:@"Mozilla/5.0 "]);
EXPECT_TRUE([userAgentString containsString:@"(KHTML, like Gecko)"]);
}];
[configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
[configuration preferences]._needsSiteSpecificQuirks = YES;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
loadCount = 0;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
while (loadCount != 9U)
TestWebKitAPI::Util::spinRunLoop();
EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
}
TEST(WebKit, WebsitePoliciesCustomUserAgents)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
[schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
[schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
[schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
EXPECT_STREQ("Foo Custom Request UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
}];
[configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
[configuration preferences]._needsSiteSpecificQuirks = YES;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
delegate.get().setCustomUserAgent = YES;
[webView setNavigationDelegate:delegate.get()];
loadCount = 0;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
while (loadCount != 9U)
TestWebKitAPI::Util::spinRunLoop();
EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
}
@interface CustomNavigatorPlatformDelegate : NSObject <WKNavigationDelegate> {
}
@end
@implementation CustomNavigatorPlatformDelegate
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (navigationAction.targetFrame.mainFrame)
[websitePolicies setCustomNavigatorPlatform:@"Test Custom Platform"];
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
finishedNavigation = true;
}
@end
TEST(WebKit, WebsitePoliciesCustomNavigatorPlatform)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[CustomNavigatorPlatformDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"data:text/html,hello"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
EXPECT_STREQ("Test Custom Platform", [[webView stringByEvaluatingJavaScript:@"navigator.platform"] UTF8String]);
}
#if PLATFORM(IOS_FAMILY)
static const char* deviceOrientationEventTestBytes = R"TESTRESOURCE(
<script>
addEventListener("deviceorientation", (event) => {
webkit.messageHandlers.testHandler.postMessage("received-device-orientation-event");
});
</script>
)TESTRESOURCE";
@interface WebsitePoliciesDeviceOrientationDelegate : NSObject <WKNavigationDelegate> {
_WKWebsiteDeviceOrientationAndMotionAccessPolicy _accessPolicy;
}
- (instancetype)initWithDeviceOrientationAccessPolicy:(_WKWebsiteDeviceOrientationAndMotionAccessPolicy)accessPolicy;
@end
@implementation WebsitePoliciesDeviceOrientationDelegate
- (instancetype)initWithDeviceOrientationAccessPolicy:(_WKWebsiteDeviceOrientationAndMotionAccessPolicy)accessPolicy
{
self = [super init];
_accessPolicy = accessPolicy;
return self;
}
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
[websitePolicies setDeviceOrientationAndMotionAccessPolicy:_accessPolicy];
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
finishedNavigation = true;
}
@end
static bool calledShouldAllowDeviceOrientationAndMotionAccessDelegate = false;
@interface WebsitePoliciesDeviceOrientationUIDelegate : NSObject <WKUIDelegatePrivate> {
}
@end
@implementation WebsitePoliciesDeviceOrientationUIDelegate
- (void)_webView:(WKWebView *)webView shouldAllowDeviceOrientationAndMotionAccessRequestedByFrame:(WKFrameInfo *)requestingFrame decisionHandler:(void (^)(BOOL))decisionHandler
{
calledShouldAllowDeviceOrientationAndMotionAccessDelegate = true;
decisionHandler(YES);
}
@end
static void runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicy accessPolicy)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
[schemeHandler addMappingFromURLString:@"test://localhost/main.html" toData:deviceOrientationEventTestBytes];
[configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[WebsitePoliciesDeviceOrientationDelegate alloc] initWithDeviceOrientationAccessPolicy:accessPolicy]);
[webView setNavigationDelegate:delegate.get()];
auto uiDelegate = adoptNS([[WebsitePoliciesDeviceOrientationUIDelegate alloc] init]);
[webView setUIDelegate:uiDelegate.get()];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://localhost/main.html"]];
[webView loadRequest:request];
TestWebKitAPI::Util::run(&finishedNavigation);
finishedNavigation = false;
bool askedForPermission = false;
[webView evaluateJavaScript:@"DeviceOrientationEvent.requestPermission()" completionHandler: [&] (id result, NSError *error) {
askedForPermission = true;
}];
TestWebKitAPI::Util::run(&askedForPermission);
__block bool didReceiveMessage = false;
[webView performAfterReceivingMessage:@"received-device-orientation-event" action:^{
didReceiveMessage = true;
}];
[webView _simulateDeviceOrientationChangeWithAlpha:1.0 beta:2.0 gamma:3.0];
if (accessPolicy == _WKWebsiteDeviceOrientationAndMotionAccessPolicyAsk)
EXPECT_TRUE(calledShouldAllowDeviceOrientationAndMotionAccessDelegate);
else
EXPECT_FALSE(calledShouldAllowDeviceOrientationAndMotionAccessDelegate);
if (accessPolicy != _WKWebsiteDeviceOrientationAndMotionAccessPolicyDeny)
TestWebKitAPI::Util::run(&didReceiveMessage);
else {
TestWebKitAPI::Util::sleep(0.1);
EXPECT_FALSE(didReceiveMessage);
}
}
TEST(WebKit, WebsitePoliciesDeviceOrientationGrantAccess)
{
runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyGrant);
}
TEST(WebKit, WebsitePoliciesDeviceOrientationDenyAccess)
{
runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyDeny);
}
TEST(WebKit, WebsitePoliciesDeviceOrientationAskAccess)
{
runWebsitePoliciesDeviceOrientationEventTest(_WKWebsiteDeviceOrientationAndMotionAccessPolicyAsk);
}
#endif // PLATFORM(IOS_FAMILY)
@interface PopUpPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
@property (nonatomic, copy) _WKWebsitePopUpPolicy(^popUpPolicyForURL)(NSURL *);
@end
@implementation PopUpPoliciesDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// _webView:decidePolicyForNavigationAction:decisionHandler: should be called instead if it is implemented.
EXPECT_TRUE(false);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
{
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
if (_popUpPolicyForURL)
websitePolicies.popUpPolicy = _popUpPolicyForURL(navigationAction.request.URL);
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
return [[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration];
}
@end
TEST(WebKit, WebsitePoliciesPopUp)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[PopUpPoliciesDelegate alloc] init]);
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"pop-up-check" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
[delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
return _WKWebsitePopUpPolicyBlock;
}];
[webView loadRequest:request];
[webView waitForMessage:@"pop-up-blocked"];
[delegate setPopUpPolicyForURL:^_WKWebsitePopUpPolicy(NSURL *) {
return _WKWebsitePopUpPolicyAllow;
}];
[webView loadRequest:request];
[webView waitForMessage:@"pop-up-allowed"];
}
static bool done;
@interface WebsitePoliciesWebsiteDataStoreDelegate : NSObject <WKNavigationDelegatePrivate, WKURLSchemeHandler, WKUIDelegate>
@end
@implementation WebsitePoliciesWebsiteDataStoreDelegate
IGNORE_WARNINGS_BEGIN("deprecated-implementations")
- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
IGNORE_WARNINGS_END
{
NSURL *url = navigationAction.request.URL;
if ([url.path isEqualToString:@"/invalid"]) {
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
websitePolicies.websiteDataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:[[[_WKWebsiteDataStoreConfiguration alloc] init] autorelease]] autorelease];
bool sawException = false;
@try {
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
} @catch (NSException *exception) {
sawException = true;
}
EXPECT_TRUE(sawException);
done = true;
}
if ([url.path isEqualToString:@"/checkStorage"]
|| [url.path isEqualToString:@"/checkCookies"]
|| [url.path isEqualToString:@"/mainFrame"]) {
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
}
if ([url.path isEqualToString:@"/subFrame"]) {
_WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
websitePolicies.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
bool sawException = false;
@try {
decisionHandler(WKNavigationActionPolicyCancel, websitePolicies);
} @catch (NSException *exception) {
sawException = true;
}
EXPECT_TRUE(sawException);
done = true;
}
}
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
{
NSURL *url = task.request.URL;
if ([url.path isEqualToString:@"/checkStorage"]) {
NSString *html = @"<script>var oldValue = window.sessionStorage['storageKey']; window.sessionStorage['storageKey'] = 'value'; alert('old value: <' + (oldValue ? 'fail' : '') + '>');</script>";
[task didReceiveResponse:[[NSURLResponse alloc] initWithURL:url MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil]];
[task didReceiveData:[html dataUsingEncoding:NSUTF8StringEncoding]];
[task didFinish];
}
}
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
}
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
EXPECT_STREQ(message.UTF8String, "old value: <>");
completionHandler();
done = true;
}
@end
RetainPtr<WKWebView> websiteDataStoreTestWebView()
{
auto delegate = adoptNS([[WebsitePoliciesWebsiteDataStoreDelegate alloc] init]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
[configuration setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
[webView setNavigationDelegate:delegate.get()];
[webView setUIDelegate:delegate.get()];
return webView;
}
TEST(WebKit, UpdateWebsitePoliciesInvalid)
{
auto webView = websiteDataStoreTestWebView();
auto policies = adoptNS([[_WKWebsitePolicies alloc] init]);
[policies setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
bool sawException = false;
@try {
[webView _updateWebsitePolicies:policies.get()];
} @catch (NSException *exception) {
sawException = true;
}
EXPECT_TRUE(sawException);
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///invalid"]]];
TestWebKitAPI::Util::run(&done);
done = false;
[webView loadHTMLString:@"<iframe src='subFrame'></iframe>" baseURL:[NSURL URLWithString:@"http://webkit.org/mainFrame"]];
TestWebKitAPI::Util::run(&done);
}
TEST(WebKit, WebsitePoliciesDataStore)
{
auto cookieWebView = websiteDataStoreTestWebView();
NSString *alertOldCookie = @"<script>var oldCookie = document.cookie; document.cookie = 'key=value'; alert('old value: <' + oldCookie + '>');</script>";
[cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
TestWebKitAPI::Util::run(&done);
done = false;
auto pid1 = [cookieWebView _webProcessIdentifier];
[cookieWebView loadHTMLString:alertOldCookie baseURL:[NSURL URLWithString:@"http://example.com/checkCookies"]];
TestWebKitAPI::Util::run(&done);
EXPECT_NE(pid1, [cookieWebView _webProcessIdentifier]);
}