blob: 8e8235cfe38259a1c8d3980e6779fd7fd717b910 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022 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"
#if ENABLE(WK_WEB_EXTENSIONS)
#import "HTTPServer.h"
#import "TestNavigationDelegate.h"
#import "WebExtensionUtilities.h"
#import <WebKit/_WKWebExtensionDeclarativeNetRequestRule.h>
#import <WebKit/_WKWebExtensionDeclarativeNetRequestTranslator.h>
namespace TestWebKitAPI {
TEST(WKWebExtensionAPIDeclarativeNetRequest, BlockedLoadTest)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
// Yield after the background page has loaded so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
auto webView = manager.get().defaultTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, BlockedLoadInPrivateBrowsingTest)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
// Yield after the background page has loaded so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
manager.get().context.hasAccessInPrivateBrowsing = YES;
[manager closeWindow:manager.get().defaultWindow];
auto privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES];
auto privateTab = privateWindow.tabs.firstObject;
auto webView = privateTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, GetEnabledRulesets)
{
auto *backgroundScript = Util::constructScript(@[
@"const enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 1, 'One ruleset should have been enabled')",
@"browser.test.assertEq(enabledRulesets[0], 'blockFrame', 'blockFrame should have been enabled')",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
},
@{
@"id": @"blockFrame2",
@"enabled": @NO,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UpdateEnabledRulesets)
{
auto *backgroundScript = Util::constructScript(@[
// Test invalid argument types
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: 5 }), /'options' value is invalid, because 'enableRulesetIds' is expected to be an array of strings/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: '5' }), /'options' value is invalid, because 'enableRulesetIds' is expected to be an array of strings/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: [5] }), /'options' value is invalid, because 'enableRulesetIds' is expected to be an array of strings/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ disableRulesetIds: 5 }), /'options' value is invalid, because 'disableRulesetIds' is expected to be an array of strings/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ disableRulesetIds: '5' }), /'options' value is invalid, because 'disableRulesetIds' is expected to be an array of strings/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.updateEnabledRulesets({ disableRulesetIds: [5] }), /'options' value is invalid, because 'disableRulesetIds' is expected to be an array of strings/i)",
// The given identifiers must be specified in the manifest.
@"await browser.test.assertRejects(browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: ['notPresentIdentifier'] }), /Invalid ruleset id/i)",
@"await browser.test.assertRejects(browser.declarativeNetRequest.updateEnabledRulesets({ disableRulesetIds: ['notPresentIdentifier'] }), /Invalid ruleset id/i)",
// Before any modifications
@"let enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 1, 'One ruleset should have been enabled')",
@"browser.test.assertEq(enabledRulesets[0], 'blockFrame', 'blockFrame should have been enabled')",
// Turn off `blockFrame` and turn on `blockFrame2`.
@"await browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: ['blockFrame2'], disableRulesetIds: ['blockFrame'] })",
// After the modifications
@"enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 1, 'One ruleset should have been enabled')",
@"browser.test.assertEq(enabledRulesets[0], 'blockFrame2', 'blockFrame2 should have been enabled')",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
},
@{
@"id": @"blockFrame2",
@"enabled": @NO,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UpdateEnabledRulesetsPerformsCompilation)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
// Before any modifications
@"let enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 0, 'No rulesets should have been enabled')",
// Turn on `blockFrame`.
@"await browser.declarativeNetRequest.updateEnabledRulesets({ enableRulesetIds: ['blockFrame'] })",
// After the modifications
@"enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 1, 'One ruleset should have been enabled')",
@"browser.test.assertEq(enabledRulesets[0], 'blockFrame', 'blockFrame should have been enabled')",
// Yield after the background page has finished updating the rulesets (and performing a compilation) so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @NO,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
auto webView = manager.get().defaultTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, IsRegexSupported)
{
auto *backgroundScript = Util::constructScript(@[
// Invalid arguments
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ }), /'regexOptions' value is invalid, because it is missing required keys: 'regex'/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: 5 }), /'regexOptions' value is invalid, because 'regex' is expected to be a string/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: ['.*'] }), /'regexOptions' value is invalid, because 'regex' is expected to be a string/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: '.*', isCaseSensitive: 'foo' }), /'regexOptions' value is invalid, because 'isCaseSensitive' is expected to be a boolean/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: '.*', isCaseSensitive: 5 }), /'regexOptions' value is invalid, because 'isCaseSensitive' is expected to be a boolean/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: '.*', requireCapturing: 'bar' }), /'regexOptions' value is invalid, because 'requireCapturing' is expected to be a boolean/i)",
@"browser.test.assertThrows(() => browser.declarativeNetRequest.isRegexSupported({ regex: '.*', requireCapturing: 5 }), /'regexOptions' value is invalid, because 'requireCapturing' is expected to be a boolean/i)",
// Passing cases
@"browser.test.assertTrue((await browser.declarativeNetRequest.isRegexSupported({ regex: '.*' })).isSupported)",
@"browser.test.assertTrue((await browser.declarativeNetRequest.isRegexSupported({ regex: 'a.*b' })).isSupported)",
// Failing cases
@"browser.test.assertFalse((await browser.declarativeNetRequest.isRegexSupported({ regex: 'Ä' })).isSupported)",
@"browser.test.assertFalse((await browser.declarativeNetRequest.isRegexSupported({ regex: '' })).isSupported)",
@"browser.test.assertFalse((await browser.declarativeNetRequest.isRegexSupported({ regex: 'a^' })).isSupported)",
@"browser.test.assertFalse((await browser.declarativeNetRequest.isRegexSupported({ regex: 'this|that' })).isSupported)",
@"browser.test.assertFalse((await browser.declarativeNetRequest.isRegexSupported({ regex: '$$' })).isSupported)",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager loadAndRun];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, SetExtensionActionOptions)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true })",
@"browser.declarativeNetRequest.setExtensionActionOptions({ displayActionCountAsBadgeText: true })",
@"setTimeout(() => {",
@" browser.declarativeNetRequest.setExtensionActionOptions({ tabUpdate: { tabId: currentTab.id, increment: 2 } })",
@" browser.test.yield('Check badge text')",
@"}, 1000)",
// Yield after the background page has loaded so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"tabs" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest and tabs permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionTabs];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
auto *defaultTab = manager.get().defaultTab;
auto *webView = defaultTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
auto *action = [manager.get().context actionForTab:defaultTab];
// The badge text should be "1" to match the one resource that was blocked.
EXPECT_NS_EQUAL(action.badgeText, @"1");
[manager run];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Check badge text");
// The badge text should now be "3" since we incremented it by two.
EXPECT_NS_EQUAL(action.badgeText, @"3");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, GetMatchedRules)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"setTimeout(async () => {",
@" const matchedRules = await browser.declarativeNetRequest.getMatchedRules()",
@" browser.test.assertEq(matchedRules.rulesMatchedInfo.length, 1)",
@" const matchedURL = matchedRules.rulesMatchedInfo[0].request.url",
@" browser.test.assertTrue(matchedURL.includes('localhost'), 'URL should include localhost')",
@" browser.test.assertTrue(matchedURL.includes('frame'), 'URL should include frame')",
@" browser.test.notifyPass()",
@"}, 1000)",
// Yield after the background page has loaded so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockFrame",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript, @"rules.json": rules }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequestFeedback permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequestFeedback];
auto *urlRequest = server.requestWithLocalhost();
NSURL *requestURL = urlRequest.URL;
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:requestURL];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:[requestURL URLByAppendingPathComponent:@"frame.html"]];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, SessionRules)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"let sessionRules = await browser.declarativeNetRequest.getSessionRules()",
@"browser.test.assertEq(sessionRules.length, 0)",
@"await browser.declarativeNetRequest.updateSessionRules({ addRules: [{ id: 1, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'frame' } }] })",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules()",
@"browser.test.assertEq(sessionRules.length, 1)",
// Yield after the background page has loaded so we can load a tab.
@"browser.test.yield('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
EXPECT_FALSE(manager.get().context.hasContentModificationRules);
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
EXPECT_TRUE(manager.get().context.hasContentModificationRules);
auto webView = manager.get().defaultTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, DynamicRules)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<body style='background-color: blue'></body>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"let dynamicRules = await browser.declarativeNetRequest.getDynamicRules()",
@"if (dynamicRules.length == 0) {",
@" await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 1, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'frame' } }] })",
@" dynamicRules = await browser.declarativeNetRequest.getDynamicRules()",
@" browser.test.assertEq(dynamicRules.length, 1)",
// Yield after updating the dynamic rules so we can unload and re-load the extension.
@" browser.test.yield('Unload extension')",
@"} else {",
@" browser.test.assertEq(dynamicRules.length, 1)",
@" browser.test.yield('Load Tab')",
@"}"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:declarativeNetRequestManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get() extensionControllerConfiguration:_WKWebExtensionControllerConfiguration._temporaryConfiguration]);
// Give the extension a unique identifier so it opts into saving data in the temporary configuration.
manager.get().context.uniqueIdentifier = @"org.webkit.test.extension (76C788B8)";
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequest];
EXPECT_FALSE(manager.get().context.hasContentModificationRules);
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Unload extension");
EXPECT_TRUE(manager.get().context.hasContentModificationRules);
auto *storageDirectory = manager.get().controller.configuration._storageDirectoryPath;
storageDirectory = [storageDirectory stringByAppendingPathComponent:manager.get().context.uniqueIdentifier];
EXPECT_TRUE([NSFileManager.defaultManager fileExistsAtPath:[storageDirectory stringByAppendingPathComponent:@"DeclarativeNetRequestContentRuleList.data"]]);
[manager.get().controller unloadExtensionContext:manager.get().context error:nullptr];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
auto webView = manager.get().defaultTab.mainWebView;
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
__block bool receivedActionNotification { false };
navigationDelegate.get().contentRuleListPerformedAction = ^(WKWebView *, NSString *identifier, _WKContentRuleListAction *action, NSURL *url) {
receivedActionNotification = true;
};
webView.navigationDelegate = navigationDelegate.get();
[webView loadRequest:server.requestWithLocalhost()];
Util::run(&receivedActionNotification);
}
// FIXME: rdar://116459903 (Web Process is crashing when using declarativeNetRequest to redirect a page)
TEST(WKWebExtensionAPIDeclarativeNetRequest, DISABLED_RedirectRule)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertTrue(location.href.startsWith('http://127.0.0.1'), 'Final load should be via IP address')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *redirectURL = server.request().URL.absoluteString;
auto *rules = @[ @{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"redirect",
@"redirect": @{
@"url": redirectURL
}
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
} ];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"redirectRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RedirectRuleWithoutHostAccessPermission)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertTrue(location.href.startsWith('http://localhost'), 'Final load should be via localhost')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *redirectURL = server.request().URL.absoluteString;
auto *rules = @[ @{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"redirect",
@"redirect": @{
@"url": redirectURL
}
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
} ];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"redirectRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusDeniedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequestWithHostAccess];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RedirectRuleWithoutHostPermission)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertTrue(location.href.startsWith('http://localhost'), 'Final load should be via localhost')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *redirectURL = server.request().URL.absoluteString;
auto *rules = @[ @{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"redirect",
@"redirect": @{
@"url": redirectURL
}
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
} ];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"redirectRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ModifyHeadersRule)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertEq(document.referrer, 'https://example.com/')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *rules = @[@{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"modifyHeaders",
@"requestHeaders": @[ @{
@"header": @"Referer",
@"operation": @"set",
@"value": @"https://example.com/"
} ]
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
}];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"modifyHeadersRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ModifyHeadersRuleWithoutHostAccessPermission)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertEq(document.referrer, '')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *rules = @[@{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"modifyHeaders",
@"requestHeaders": @[ @{
@"header": @"Referer",
@"operation": @"set",
@"value": @"https://example.com/"
} ]
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
}];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"modifyHeadersRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusDeniedExplicitly forPermission:_WKWebExtensionPermissionDeclarativeNetRequestWithHostAccess];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ModifyHeadersRuleWithoutHostPermission)
{
auto *pageScript = Util::constructScript(@[
@"<script>",
@" browser.test.assertEq(document.referrer, '')",
@" browser.test.notifyPass()",
@"</script>"
]);
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, pageScript } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *urlRequest = server.requestWithLocalhost();
auto *rules = @[@{
@"id": @1,
@"priority": @1,
@"action": @{
@"type": @"modifyHeaders",
@"requestHeaders": @[ @{
@"header": @"Referer",
@"operation": @"set",
@"value": @"https://example.com/"
} ]
},
@"condition": @{
@"urlFilter": @"localhost",
@"resourceTypes": @[ @"main_frame" ]
}
}];
auto *manifest = @{
@"manifest_version": @3,
@"name": @"Test",
@"description": @"Test",
@"version": @"1",
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"permissions": @[ @"declarativeNetRequestWithHostAccess" ],
@"host_permissions": @[ @"*://localhost/*" ],
@"declarative_net_request": @{
@"rule_resources": @[ @{
@"id": @"modifyHeadersRule",
@"enabled": @YES,
@"path": @"rules.json"
} ]
}
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.yield('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:resources]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
[manager loadAndRun];
EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Load Tab");
[manager.get().defaultTab.mainWebView loadRequest:urlRequest];
[manager run];
}
// MARK: Rule translation tests
TEST(WKWebExtensionAPIDeclarativeNetRequest, RequiredAndOptionalKeys)
{
NSDictionary *rule1 = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule1 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule1 errorString:nil];
EXPECT_NOT_NULL(validatedRule1);
NSDictionary *rule2 = @{
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule2 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule2 errorString:nil];
EXPECT_NULL(validatedRule2);
NSDictionary *rule3 = @{
@"id": @1,
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule3 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule3 errorString:nil];
EXPECT_NULL(validatedRule3);
NSDictionary *rule4 = @{
@"id": @1,
@"action": @{ @"type": @"block" },
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule4 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule4 errorString:nil];
EXPECT_NULL(validatedRule4);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, PropertiesHaveCorrectType)
{
NSDictionary *rule1 = @{
@"id": @"rule_id",
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule1 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule1 errorString:nil];
EXPECT_NULL(validatedRule1);
NSDictionary *rule2 = @{
@"id": @1,
@"priority": @1,
@"action": @"block",
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule2 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule2 errorString:nil];
EXPECT_NULL(validatedRule2);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NumbersArePositiveIntegers)
{
NSDictionary *rule1 = @{
@"id": @-1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule1 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule1 errorString:nil];
EXPECT_NULL(validatedRule1);
NSDictionary *rule2 = @{
@"id": @1,
@"priority": @0,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule2 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule2 errorString:nil];
EXPECT_NULL(validatedRule2);
NSDictionary *ruleWithNonIntegerPriority = @{
@"id": @80,
@"priority": @5.8,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRuleWithNonIntegerPriority = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:ruleWithNonIntegerPriority errorString:nil];
EXPECT_NOT_NULL(validatedRuleWithNonIntegerPriority);
EXPECT_EQ(validatedRuleWithNonIntegerPriority.ruleID, 80);
EXPECT_EQ(validatedRuleWithNonIntegerPriority.priority, 5);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnlyOneOfResourceTypesAndExcludedResourceTypesIsSpecified)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"boatnerd.com",
@"resourceTypes": @[ @"font" ],
@"excludedResourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RegexRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"regexFilter": @".*\\.com",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*\\.com",
@"resource-type": @[ @"script" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, BasicValidRuleParsing)
{
NSDictionary *conditionDictionary = @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
};
NSDictionary *rule = @{
@"id": @1,
@"priority": @3,
@"action": @{ @"type": @"block" },
@"condition": conditionDictionary,
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NOT_NULL(validatedRule);
EXPECT_EQ(validatedRule.ruleID, 1);
EXPECT_EQ(validatedRule.priority, 3);
EXPECT_NS_EQUAL(validatedRule.action, @{ @"type": @"block" });
EXPECT_NS_EQUAL(validatedRule.condition, conditionDictionary);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, BasicRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"font" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, MainFrameResourceRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, SubFrameResourceRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"sub_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"child-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RepeatedMainFrameResourceRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"main_frame", @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, CaseSensitiveConversion)
{
NSDictionary *rule1 = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"isUrlFilterCaseSensitive": @YES,
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule1 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule1 errorString:nil];
NSDictionary *convertedRule1 = validatedRule1.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule1);
NSDictionary *correctRuleConversion1 = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"url-filter-is-case-sensitive": @YES,
@"resource-type": @[ @"font" ],
},
};
EXPECT_NS_EQUAL(convertedRule1, correctRuleConversion1);
NSDictionary *rule2 = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"isUrlFilterCaseSensitive": @NO,
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule2 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule2 errorString:nil];
NSDictionary *convertedRule2 = validatedRule2.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule2);
NSDictionary *correctRuleConversion2 = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"font" ],
},
};
EXPECT_NS_EQUAL(convertedRule2, correctRuleConversion2);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ConvertingMultipleResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script", @"stylesheet", @"ping" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"script", @"style-sheet", @"ping" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ConvertingXHRWebSocketAndOtherTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"xmlhttprequest", @"websocket", @"other" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"fetch", @"websocket", @"other" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UpgradeSchemeRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"image" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *sortingRule = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
};
NSDictionary *makeHTTPSRule = @{
@"action": @{
@"type": @"make-https",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
};
EXPECT_NS_EQUAL(convertedRules[0], sortingRule);
EXPECT_NS_EQUAL(convertedRules[1], makeHTTPSRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UpgradeSchemeForMainFrameRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"main_frame", @"image" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 4ul);
NSDictionary *sortingRuleForMainFrame = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRules[0], sortingRuleForMainFrame);
NSDictionary *sortingRuleForImage = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
};
EXPECT_NS_EQUAL(convertedRules[1], sortingRuleForImage);
NSDictionary *makeHTTPSRuleForMainFrame = @{
@"action": @{
@"type": @"make-https",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRules[2], makeHTTPSRuleForMainFrame);
NSDictionary *makeHTTPSRuleForImage = @{
@"action": @{
@"type": @"make-https",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
};
EXPECT_NS_EQUAL(convertedRules[3], makeHTTPSRuleForImage);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleWithoutAPriority)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NOT_NULL(validatedRule);
EXPECT_EQ(validatedRule.priority, 1);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleWithInvalidDomainType)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"domainType": @"secondParty",
@"resourceTypes": @[ @"ping" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithDomainType)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"boatnerd.com",
@"resourceTypes": @[ @"script", @"image", @"stylesheet" ],
@"domainType": @"firstParty",
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"boatnerd\\.com",
@"resource-type": @[ @"script", @"image", @"style-sheet" ],
@"load-type": @[ @"first-party" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithNoSpecifiedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"regexFilter": @".*",
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *documentRule = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"child-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRules[0], documentRule);
NSDictionary *otherResourceTypeRules = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"image", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket" ],
},
};
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRules[1][@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:otherResourceTypeRules[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithUnsupportedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"resourceTypes": @[ @"script", @"not_a_real_resource_type", @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:@[ @"script", @"font" ]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExactlyOneUnsupportedResourceType)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"resourceTypes": @[ @"not_a_real_resource_type" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NULL(convertedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, EmptyResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"resourceTypes": @[ ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"excludedResourceTypes": @[ @"main_frame", @"sub_frame", @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:@[ @"fetch", @"image", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket" ]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithUnsupportedExcludedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"*",
@"excludedResourceTypes": @[ @"image", @"🦠" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *documentRule = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame", @"child-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRules[0], documentRule);
NSDictionary *otherResourceTypeRules = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket" ],
},
};
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRules[1][@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:otherResourceTypeRules[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithEmptyExcludedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"*",
@"excludedResourceTypes": @[ ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *documentRule = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"document" ],
@"load-context": @[ @"top-frame", @"child-frame" ],
},
};
EXPECT_NS_EQUAL(convertedRules[0], documentRule);
NSDictionary *otherResourceTypeRules = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"image", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket" ],
},
};
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRules[1][@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:otherResourceTypeRules[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, EmptyDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"domains": @[ ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NonASCIIDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"domains": @[ @"🙃🙃🙃", @"apple.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"domains": @[ @"apple.com", @"facebook.com", @"google.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"if-domain": @[ @"*apple.com", @"*facebook.com", @"*google.com" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithRequestDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
// Some extensions will have added leading * to match subdomains to work around Safari's bug in the past (rdar://113865040), make sure we still handle that case correctly.
@"requestDomains": @[ @"apple.com", @"facebook.com", @"*google.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"if-domain": @[ @"*apple.com", @"*facebook.com", @"*google.com" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithEmptyExcludedDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedDomains": @[ ],
@"resourceTypes": @[ @"media" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"unless-domain": @[ ],
@"resource-type": @[ @"media" ],
@"url-filter": @".*",
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NonASCIIExcludedDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"domains": @[ @"🧭" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedDomains": @[ @"apple.com" ],
@"resourceTypes": @[ @"media" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"unless-domain": @[ @"*apple.com" ],
@"resource-type": @[ @"media" ],
@"url-filter": @".*",
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedRequestDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedRequestDomains": @[ @"apple.com" ],
@"resourceTypes": @[ @"media" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"unless-domain": @[ @"*apple.com" ],
@"resource-type": @[ @"media" ],
@"url-filter": @".*",
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NonASCIIURLFilter)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"🔮.com",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NonASCIIRegexFilter)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"regexFilter": @"〽️.com",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, URLFilterSpecialCharacters)
{
__auto_type testPattern = ^(NSString *chromePattern, NSString *expectedRegexPattern) {
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": chromePattern,
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": expectedRegexPattern,
@"resource-type": @[ @"script" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
};
// Testing domain anchor.
testPattern(@"||apple.com", @"^[^:]+://+([^:/]+\\.)?apple\\.com");
testPattern(@"apple||.com", @"apple\\|\\|\\.com");
// Testing start and end anchors.
testPattern(@"|apple|.com|", @"^apple\\|\\.com$");
testPattern(@"|apple.com", @"^apple\\.com");
testPattern(@"apple.com|", @"apple\\.com$");
testPattern(@"apple|com", @"apple\\|com");
// Testing wildcard.
testPattern(@"*apple.com", @".*apple\\.com");
testPattern(@"*apple*com", @".*apple.*com");
testPattern(@"apple.com*", @"apple\\.com.*");
// Testing separator character.
testPattern(@"apple^com", @"apple[^a-zA-Z0-9_.%-]com");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UnacceptableResourceTypeForAllowAllRequests)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"urlFilter": @"apple.com",
@"resourceTypes": @[ @"main_frame", @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, ExcludedResourceTypeForAllowAllRequests)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"urlFilter": @"apple.com",
@"excludedResourceTypes": @[ @"sub_frame", @"stylesheet", @"script", @"image", @"font", @"object", @"xmlhttprequest", @"ping", @"csp_report", @"media", @"websocket", @"other"],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NoResourceTypeForAllowAllRequests)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"urlFilter": @"apple.com",
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithAllowAllRequests)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"urlFilter": @"apple.com",
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"ignore-previous-rules",
},
@"trigger": @{
@"url-filter": @".*",
@"if-top-url": @[ @"apple\\.com" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithRedirect)
{
__auto_type testRedirect = ^(NSDictionary<NSString *, id> *inputRedirect, NSDictionary<NSString *, id> *expectedRedirect) {
NSDictionary *rule = @{
@"id": @1,
@"action": @{
@"type": @"redirect",
@"redirect": inputRedirect,
},
@"condition": @{
@"regexFilter": @".*",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
if (!expectedRedirect) {
EXPECT_NULL(convertedRule);
return;
}
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"redirect",
@"redirect": expectedRedirect,
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"script" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
};
testRedirect(@{ }, nil);
testRedirect(@{ @"url": @"" }, nil);
testRedirect(@{ @"url": @[ ] }, nil);
testRedirect(@{ @"url": @"ftp://example.com" }, nil);
testRedirect(@{ @"url": @"https://example.com" }, @{ @"url": @"https://example.com" });
testRedirect(@{ @"extensionPath": @"" }, nil);
testRedirect(@{ @"extensionPath": @[ ] }, nil);
testRedirect(@{ @"extensionPath": @"foo.js" }, nil);
testRedirect(@{ @"extensionPath": @"/foo.js" }, @{ @"extension-path": @"/foo.js" });
testRedirect(@{ @"regexSubstitution": @"" }, nil);
testRedirect(@{ @"regexSubstitution": @[ ] }, nil);
testRedirect(@{ @"regexSubstitution": @"http://example.com/\\1" }, @{ @"regex-substitution": @"http://example.com/\\1" });
testRedirect(@{ @"transform": @"" }, nil);
testRedirect(@{ @"transform": @[ ] }, nil);
testRedirect(@{ @"transform": @{ } }, nil);
testRedirect(@{ @"transform": @{ @"scheme": @"https", @"host": @"new.example.com" } }, @{ @"transform": @{ @"scheme": @"https", @"host": @"new.example.com" } });
testRedirect(@{ @"transform": @{ @"username": @"foo", @"password": @"bar" } }, @{ @"transform": @{ @"username": @"foo", @"password": @"bar" } });
testRedirect(@{ @"transform": @{ @"query": @"foo" } }, @{ @"transform": @{ @"query": @"foo" } });
testRedirect(@{ @"transform": @{ @"queryTransform": @"" } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @[ ] } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @"" } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ ] } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ @{ } ] } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ @{ @"key": @"foo" } ] } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ @{ @"replaceOnly": @"" } ] } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"removeParams": @"" } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"removeParams": @[ ] } } }, nil);
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ @{ @"key": @"foo", @"value": @"bar" } ] } } }, @{ @"transform": @{ @"query-transform": @{ @"add-or-replace-parameters": @[ @{ @"key": @"foo", @"value": @"bar" } ] } } });
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"addOrReplaceParams": @[ @{ @"key": @"foo", @"value": @"bar", @"replaceOnly": @YES } ] } } }, @{ @"transform": @{ @"query-transform": @{ @"add-or-replace-parameters": @[ @{ @"key": @"foo", @"value": @"bar", @"replace-only": @YES } ] } } });
testRedirect(@{ @"transform": @{ @"queryTransform": @{ @"removeParams": @[ @"foo" ] } } }, @{ @"transform": @{ @"query-transform": @{ @"remove-parameters": @[ @"foo" ] } } });
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithModifyHeaders)
{
__auto_type testModifyHeaders = ^(NSString *inputHeaderType, NSArray<NSDictionary *> *inputModifyHeadersInfo, NSString *expectedHeaderType, NSArray<NSDictionary *> *expectedModifyHeadersInfo) {
NSDictionary *rule = @{
@"id": @10,
@"priority": @2,
@"action": @{
@"type": @"modifyHeaders",
inputHeaderType: inputModifyHeadersInfo,
},
@"condition": @{
@"urlFilter": @"*",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
if (!expectedModifyHeadersInfo) {
EXPECT_NULL(convertedRule);
return;
}
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"modify-headers",
@"priority": @2,
expectedHeaderType: expectedModifyHeadersInfo,
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"script" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
};
testModifyHeaders(@"responseHeaders", @[ ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ } ], nil, nil);
testModifyHeaders(@"requestHeaders", @[ @{ } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"wrong dictionary structure": @[ @{ } ] } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"header": @"accept", @"operation": @"invalid header operation", @"value": @"v1" } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"operation": @"set", @"value": @"v1" } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"header": @"accept", @"operation": @"set" } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"header": @"accept", @"operation": @"append" } ], nil, nil);
testModifyHeaders(@"responseHeaders", @[ @{ @"header": @"accept", @"operation": @"remove", @"value": @"v1" } ], nil, nil);
testModifyHeaders(@"requestHeaders", @[ @{ @"operation": @"set", @"value": @"v1" } ], nil, nil);
testModifyHeaders(@"requestHeaders", @[ @{ @"header": @"ellie's custom header", @"operation": @"set", @"value": @"v1" } ], nil, nil);
testModifyHeaders(@"requestHeaders", @[ @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ], @"request-headers", @[ @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ]);
testModifyHeaders(@"requestHeaders", @[ @{ @"header": @"accept", @"operation": @"remove" } ], @"request-headers", @[ @{ @"header": @"accept", @"operation": @"remove" } ]);
testModifyHeaders(@"requestHeaders", @[ @{ @"header": @"accept", @"operation": @"append", @"value": @"v1" } ], @"request-headers", @[ @{ @"header": @"accept", @"operation": @"append", @"value": @"v1" } ]);
testModifyHeaders(@"requestHeaders", @[ @{ @"header": @"accept", @"operation": @"append", @"value": @"v1" }, @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ], @"request-headers", @[ @{ @"header": @"accept", @"operation": @"append", @"value": @"v1" }, @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ]);
testModifyHeaders(@"responseHeaders", @[ @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ], @"response-headers", @[ @{ @"header": @"accept", @"operation": @"set", @"value": @"v1" } ]);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithModifyHeadersWithBothResponseAndRequestHeaders)
{
NSDictionary *rule = @{
@"id": @10,
@"action": @{
@"type": @"modifyHeaders",
@"responseHeaders": @[
@{ @"header": @"age", @"operation": @"set", @"value": @"5" },
@{ @"header": @"date", @"operation": @"remove" },
],
@"requestHeaders": @[
@{ @"header": @"cache-control", @"operation": @"set", @"value": @"something" },
@{ @"header": @"Cookie", @"operation": @"remove" },
],
},
@"condition": @{
@"urlFilter": @"*",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"modify-headers",
@"priority": @1,
@"response-headers": @[
@{ @"header": @"age", @"operation": @"set", @"value": @"5" },
@{ @"header": @"date", @"operation": @"remove" },
],
@"request-headers": @[
@{ @"header": @"cache-control", @"operation": @"set", @"value": @"something" },
@{ @"header": @"Cookie", @"operation": @"remove" },
],
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"script" ],
},
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithModifyHeadersWithoutAnyHeadersSpecified)
{
NSDictionary *rule = @{
@"id": @10,
@"priority": @2,
@"action": @{
@"type": @"modifyHeaders",
},
@"condition": @{
@"urlFilter": @"*",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NULL(convertedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortByPriorityFromDifferentRulesets)
{
NSArray *rules = @[
@[ @{
@"id": @1,
@"priority": @2,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"regexFilter": @"apple.com",
@"resourceTypes": @[ @"script" ],
},
} ],
@[ @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
} ],
];
NSArray *sortedTranslatedRules = [_WKWebExtensionDeclarativeNetRequestTranslator translateRules:rules errorStrings:nil];
EXPECT_NOT_NULL(sortedTranslatedRules);
EXPECT_NS_EQUAL(sortedTranslatedRules[0][@"action"][@"type"], @"block");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"ignore-previous-rules");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortWithoutExplicitPriority)
{
NSArray *rules = @[
@[
@{
@"id": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
@{
@"id": @1,
@"priority": @3,
@"action": @{ @"type": @"block" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
]
];
NSArray *sortedTranslatedRules = [_WKWebExtensionDeclarativeNetRequestTranslator translateRules:rules errorStrings:nil];
EXPECT_NOT_NULL(sortedTranslatedRules);
EXPECT_NS_EQUAL(sortedTranslatedRules[0][@"action"][@"type"], @"ignore-previous-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"make-https");
EXPECT_NS_EQUAL(sortedTranslatedRules[2][@"action"][@"type"], @"block");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortByActionType)
{
NSArray *rules = @[
@[
@{
@"id": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
@{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"main_frame" ],
},
},
@{
@"id": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
@{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
@{
@"id": @1,
@"action": @{
@"type": @"modifyHeaders",
@"requestHeaders": @[
@{ @"header": @"DNT", @"operation": @"set", @"value": @"1" },
],
},
@"condition": @{
@"regexFilter": @"bananas.com",
@"resourceTypes": @[ @"script" ],
},
},
]
];
NSArray *sortedTranslatedRules = [_WKWebExtensionDeclarativeNetRequestTranslator translateRules:rules errorStrings:nil];
EXPECT_NOT_NULL(sortedTranslatedRules);
EXPECT_NS_EQUAL(sortedTranslatedRules[0][@"action"][@"type"], @"modify-headers");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"ignore-previous-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[2][@"action"][@"type"], @"make-https");
EXPECT_NS_EQUAL(sortedTranslatedRules[3][@"action"][@"type"], @"block");
EXPECT_NS_EQUAL(sortedTranslatedRules[4][@"action"][@"type"], @"ignore-previous-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[5][@"action"][@"type"], @"ignore-previous-rules");
}
} // namespace TestWebKitAPI
#endif // ENABLE(WK_WEB_EXTENSIONS)