blob: 150bc70880f01800450d2f3f4b023514de4b3b24 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*/
#import "config.h"
#import <WebKit/WKFoundation.h>
#import "PlatformUtilities.h"
#import "Test.h"
#import <WebKit/WKContentRuleList.h>
#import <WebKit/WKContentRuleListStorePrivate.h>
#import <WebKit/_WKUserContentExtensionStore.h>
#import <WebKit/_WKUserContentFilter.h>
#import <wtf/RetainPtr.h>
class WKContentRuleListStoreTest : public testing::Test {
public:
virtual void SetUp()
{
[[WKContentRuleListStore defaultStore] _removeAllContentRuleLists];
}
};
static NSString *basicFilter = @"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*webkit.org\"}}]";
TEST_F(WKContentRuleListStoreTest, Compile)
{
__block bool doneCompiling = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
}
static NSString *invalidFilter = @"[";
static void checkDomain(NSError *error)
{
EXPECT_STREQ([[error domain] UTF8String], [WKErrorDomain UTF8String]);
}
TEST_F(WKContentRuleListStoreTest, InvalidRuleList)
{
__block bool doneCompiling = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestRuleList" encodedContentRuleList:invalidFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NULL(filter);
EXPECT_NOT_NULL(error);
checkDomain(error);
EXPECT_EQ(error.code, WKErrorContentRuleListStoreCompileFailed);
EXPECT_STREQ("Rule list compilation failed: Failed to parse the JSON String.", [[error helpAnchor] UTF8String]);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
}
TEST_F(WKContentRuleListStoreTest, Lookup)
{
__block bool doneCompiling = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
__block bool doneLookingUp = false;
[[WKContentRuleListStore defaultStore] lookUpContentRuleListForIdentifier:@"TestRuleList" completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_STREQ(filter.identifier.UTF8String, "TestRuleList");
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
}
TEST_F(WKContentRuleListStoreTest, EncodedIdentifier)
{
// FIXME: U+00C4 causes problems here. Using the output of encodeForFileName with
// the filesystem changes it to U+0041 followed by U+0308
NSString *identifier = @":;%25%+25И😍";
__block bool done = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:identifier encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_STREQ(filter.identifier.UTF8String, identifier.UTF8String);
[[WKContentRuleListStore defaultStore] getAvailableContentRuleListIdentifiers:^(NSArray<NSString *> *identifiers) {
EXPECT_EQ(identifiers.count, 1u);
EXPECT_EQ(identifiers[0].length, identifier.length);
EXPECT_STREQ(identifiers[0].UTF8String, identifier.UTF8String);
done = true;
}];
}];
TestWebKitAPI::Util::run(&done);
}
TEST_F(WKContentRuleListStoreTest, NonExistingIdentifierLookup)
{
__block bool doneLookingUp = false;
[[WKContentRuleListStore defaultStore] lookUpContentRuleListForIdentifier:@"DoesNotExist" completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NULL(filter);
EXPECT_NOT_NULL(error);
checkDomain(error);
EXPECT_EQ(error.code, WKErrorContentRuleListStoreLookUpFailed);
EXPECT_STREQ("Rule list lookup failed: Unspecified error during lookup.", [[error helpAnchor] UTF8String]);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
}
TEST_F(WKContentRuleListStoreTest, VersionMismatch)
{
__block bool doneCompiling = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error)
{
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
[[WKContentRuleListStore defaultStore] _invalidateContentRuleListVersionForIdentifier:@"TestRuleList"];
__block bool doneLookingUp = false;
[[WKContentRuleListStore defaultStore] lookUpContentRuleListForIdentifier:@"TestRuleList" completionHandler:^(WKContentRuleList *filter, NSError *error)
{
EXPECT_NULL(filter);
EXPECT_NOT_NULL(error);
checkDomain(error);
EXPECT_EQ(error.code, WKErrorContentRuleListStoreVersionMismatch);
EXPECT_STREQ("Rule list lookup failed: Version of file does not match version of interpreter.", [[error helpAnchor] UTF8String]);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
__block bool doneGettingSource = false;
[[WKContentRuleListStore defaultStore] _getContentRuleListSourceForIdentifier:@"TestRuleList" completionHandler:^(NSString* source) {
EXPECT_NULL(source);
doneGettingSource = true;
}];
TestWebKitAPI::Util::run(&doneGettingSource);
}
TEST_F(WKContentRuleListStoreTest, Removal)
{
__block bool doneCompiling = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
__block bool doneRemoving = false;
[[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"TestRuleList" completionHandler:^(NSError *error) {
EXPECT_NULL(error);
doneRemoving = true;
}];
TestWebKitAPI::Util::run(&doneRemoving);
}
TEST_F(WKContentRuleListStoreTest, NonExistingIdentifierRemove)
{
__block bool doneRemoving = false;
[[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:@"DoesNotExist" completionHandler:^(NSError *error) {
EXPECT_NOT_NULL(error);
checkDomain(error);
EXPECT_EQ(error.code, WKErrorContentRuleListStoreRemoveFailed);
EXPECT_STREQ("Rule list removal failed: Unspecified error during remove.", [[error helpAnchor] UTF8String]);
doneRemoving = true;
}];
TestWebKitAPI::Util::run(&doneRemoving);
}
TEST_F(WKContentRuleListStoreTest, NonDefaultStore)
{
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"ContentRuleListTest"] isDirectory:YES];
WKContentRuleListStore *store = [WKContentRuleListStore storeWithURL:tempDir];
NSString *identifier = @"TestRuleList";
NSString *fileName = @"ContentRuleList-TestRuleList";
__block bool doneGettingAvailableIdentifiers = false;
[store getAvailableContentRuleListIdentifiers:^(NSArray<NSString *> *identifiers) {
EXPECT_NOT_NULL(identifiers);
EXPECT_EQ(identifiers.count, 0u);
doneGettingAvailableIdentifiers = true;
}];
TestWebKitAPI::Util::run(&doneGettingAvailableIdentifiers);
__block bool doneCompiling = false;
[store compileContentRuleListForIdentifier:identifier encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
doneGettingAvailableIdentifiers = false;
[store getAvailableContentRuleListIdentifiers:^(NSArray<NSString *> *identifiers) {
EXPECT_NOT_NULL(identifiers);
EXPECT_EQ(identifiers.count, 1u);
EXPECT_STREQ(identifiers[0].UTF8String, "TestRuleList");
doneGettingAvailableIdentifiers = true;
}];
TestWebKitAPI::Util::run(&doneGettingAvailableIdentifiers);
NSData *data = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
EXPECT_NOT_NULL(data);
EXPECT_EQ(data.length, 228u);
__block bool doneCheckingSource = false;
[store _getContentRuleListSourceForIdentifier:identifier completionHandler:^(NSString *source) {
EXPECT_NOT_NULL(source);
EXPECT_STREQ(basicFilter.UTF8String, source.UTF8String);
doneCheckingSource = true;
}];
TestWebKitAPI::Util::run(&doneCheckingSource);
__block bool doneRemoving = false;
[store removeContentRuleListForIdentifier:identifier completionHandler:^(NSError *error) {
EXPECT_NULL(error);
doneRemoving = true;
}];
TestWebKitAPI::Util::run(&doneRemoving);
NSData *dataAfterRemoving = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
EXPECT_NULL(dataAfterRemoving);
}
TEST_F(WKContentRuleListStoreTest, MultipleRuleLists)
{
__block bool done = false;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"FirstRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"SecondRuleList" encodedContentRuleList:basicFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
[[WKContentRuleListStore defaultStore] getAvailableContentRuleListIdentifiers:^(NSArray<NSString *> *identifiers) {
EXPECT_NOT_NULL(identifiers);
EXPECT_EQ(identifiers.count, 2u);
EXPECT_STREQ(identifiers[0].UTF8String, "FirstRuleList");
EXPECT_STREQ(identifiers[1].UTF8String, "SecondRuleList");
done = true;
}];
}];
}];
TestWebKitAPI::Util::run(&done);
}
TEST_F(WKContentRuleListStoreTest, NonASCIISource)
{
static NSString *nonASCIIFilter = @"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*webkit.org\"}, \"unused\":\"💩\"}]";
NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"ContentRuleListTest"] isDirectory:YES];
WKContentRuleListStore *store = [WKContentRuleListStore storeWithURL:tempDir];
NSString *identifier = @"TestRuleList";
NSString *fileName = @"ContentRuleList-TestRuleList";
__block bool done = false;
[store compileContentRuleListForIdentifier:identifier encodedContentRuleList:nonASCIIFilter completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
[store _getContentRuleListSourceForIdentifier:identifier completionHandler:^(NSString *source) {
EXPECT_NOT_NULL(source);
EXPECT_STREQ(nonASCIIFilter.UTF8String, source.UTF8String);
[store _removeAllContentRuleLists];
NSData *dataAfterRemoving = [NSData dataWithContentsOfURL:[tempDir URLByAppendingPathComponent:fileName]];
EXPECT_NULL(dataAfterRemoving);
done = true;
}];
}];
TestWebKitAPI::Util::run(&done);
}
static size_t alertCount { 0 };
static bool receivedAlert { false };
@interface ContentRuleListDelegate : NSObject <WKUIDelegate>
@end
@implementation ContentRuleListDelegate
- (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 removed the content RuleList.
EXPECT_STREQ("content blockers disabled", message.UTF8String);
break;
default:
EXPECT_TRUE(false);
}
receivedAlert = true;
completionHandler();
}
@end
TEST_F(WKContentRuleListStoreTest, AddRemove)
{
[[WKContentRuleListStore defaultStore] _removeAllContentRuleLists];
__block bool doneCompiling = false;
NSString* contentBlocker = @"[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]";
__block RetainPtr<WKContentRuleList> ruleList;
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:@"TestAddRemove" encodedContentRuleList:contentBlocker completionHandler:^(WKContentRuleList *compiledRuleList, NSError *error) {
EXPECT_TRUE(error == nil);
ruleList = compiledRuleList;
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
EXPECT_NOT_NULL(ruleList);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addContentRuleList:ruleList.get()];
auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
auto delegate = adoptNS([[ContentRuleListDelegate alloc] init]);
[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);
[[configuration userContentController] removeContentRuleList:ruleList.get()];
receivedAlert = false;
[webView reload];
TestWebKitAPI::Util::run(&receivedAlert);
}
#if PLATFORM(IOS_FAMILY)
TEST_F(WKContentRuleListStoreTest, UnsafeMMap)
{
RetainPtr<NSString> tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:@"UnsafeMMapTest"];
RetainPtr<WKContentRuleListStore> store = [WKContentRuleListStore storeWithURL:[NSURL fileURLWithPath:tempDir.get() isDirectory:YES]];
static NSString *compiledIdentifier = @"CompiledRuleList";
static NSString *copiedIdentifier = @"CopiedRuleList";
static NSString *ruleListSourceString = @"[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"blockedsubresource\"}}]";
RetainPtr<NSString> compiledFilePath = [tempDir stringByAppendingPathComponent:@"ContentRuleList-CompiledRuleList"];
RetainPtr<NSString> copiedFilePath = [tempDir stringByAppendingPathComponent:@"ContentRuleList-CopiedRuleList"];
__block bool doneCompiling = false;
[store compileContentRuleListForIdentifier:compiledIdentifier encodedContentRuleList:ruleListSourceString completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
auto hasCompleteProtection = [] (const RetainPtr<NSString>& path) {
NSError *error = nil;
NSDictionary<NSFileAttributeKey, id> *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path.get() error:&error];
EXPECT_NULL(error);
return [[attributes objectForKey:NSFileProtectionKey] isEqualToString:NSFileProtectionComplete];
};
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:compiledFilePath.get() toPath:copiedFilePath.get() error:&error];
EXPECT_NULL(error);
EXPECT_FALSE(hasCompleteProtection(copiedFilePath));
[[NSFileManager defaultManager] setAttributes:@{ NSFileProtectionKey: NSFileProtectionComplete } ofItemAtPath:copiedFilePath.get() error:&error];
EXPECT_NULL(error);
#if !PLATFORM(IOS_FAMILY_SIMULATOR)
EXPECT_TRUE(hasCompleteProtection(copiedFilePath));
#endif
__block bool doneLookingUp = false;
[store lookUpContentRuleListForIdentifier:copiedIdentifier completionHandler:^(WKContentRuleList *filter, NSError *error) {
EXPECT_NOT_NULL(filter);
EXPECT_NULL(error);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
EXPECT_FALSE(hasCompleteProtection(copiedFilePath));
__block bool doneRemoving = false;
[store removeContentRuleListForIdentifier:compiledIdentifier completionHandler:^(NSError *error) {
EXPECT_NULL(error);
[store removeContentRuleListForIdentifier:copiedIdentifier completionHandler:^(NSError *error) {
EXPECT_NULL(error);
doneRemoving = true;
}];
}];
TestWebKitAPI::Util::run(&doneRemoving);
}
#endif // PLATFORM(IOS_FAMILY)
TEST_F(WKContentRuleListStoreTest, _WKUserContentExtensionStoreSelectors)
{
[[WKContentRuleListStore defaultStore] _removeAllContentRuleLists];
NSString *identifier = @"TestOldSPIMixup";
__block bool doneCompiling = false;
[(_WKUserContentExtensionStore *)[WKContentRuleListStore defaultStore] compileContentExtensionForIdentifier:identifier encodedContentExtension:basicFilter completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
EXPECT_NULL(error);
EXPECT_NOT_NULL(filter);
doneCompiling = true;
}];
TestWebKitAPI::Util::run(&doneCompiling);
__block bool doneLookingUp = false;
[(_WKUserContentExtensionStore *)[WKContentRuleListStore defaultStore] lookupContentExtensionForIdentifier:identifier completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
EXPECT_NULL(error);
EXPECT_NOT_NULL(filter);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
__block bool domeRemoving = false;
[(_WKUserContentExtensionStore *)[WKContentRuleListStore defaultStore] removeContentExtensionForIdentifier:identifier completionHandler:^(NSError *error) {
EXPECT_NULL(error);
domeRemoving = true;
}];
TestWebKitAPI::Util::run(&domeRemoving);
doneLookingUp = false;
[(_WKUserContentExtensionStore *)[WKContentRuleListStore defaultStore] lookupContentExtensionForIdentifier:identifier completionHandler:^(_WKUserContentFilter *filter, NSError *error) {
EXPECT_WK_STREQ(error.domain, WKErrorDomain);
EXPECT_EQ(error.code, WKErrorContentRuleListStoreLookUpFailed);
EXPECT_NULL(filter);
doneLookingUp = true;
}];
TestWebKitAPI::Util::run(&doneLookingUp);
}