blob: 8a28ab6cff0b71f4fd0b0e05bbde168d50697a24 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022-2024 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/WKPreferencesPrivate.h>
#import <WebKit/_WKFeature.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(@[
@"browser.test.sendMessage('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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
auto webView = manager.get().defaultTab.webView;
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);
}
#if !defined(NDEBUG)
TEST(WKWebExtensionAPIDeclarativeNetRequest, DISABLED_BlockedLoadInPrivateBrowsingTest)
#else
TEST(WKWebExtensionAPIDeclarativeNetRequest, BlockedLoadInPrivateBrowsingTest)
#endif
{
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(@[
@"browser.test.sendMessage('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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
manager.get().context.hasAccessToPrivateData = YES;
[manager closeWindow:manager.get().defaultWindow];
auto privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES];
auto privateTab = privateWindow.tabs.firstObject;
auto webView = privateTab.webView;
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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager run];
}
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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager run];
}
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')",
@"browser.test.sendMessage('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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
auto webView = manager.get().defaultTab.webView;
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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager run];
}
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.sendMessage('Check badge text')",
@"}, 1000)",
@"browser.test.sendMessage('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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest and tabs permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionTabs];
[manager runUntilTestMessage:@"Load Tab"];
auto *defaultTab = manager.get().defaultTab;
auto *webView = defaultTab.webView;
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 runUntilTestMessage:@"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)",
@"browser.test.sendMessage('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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// 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 runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView 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)",
@"browser.test.sendMessage('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
EXPECT_FALSE(manager.get().context.hasContentModificationRules);
[manager runUntilTestMessage:@"Load Tab"];
EXPECT_TRUE(manager.get().context.hasContentModificationRules);
auto webView = manager.get().defaultTab.webView;
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, GetSessionRules)
{
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: 'foo' } }] })",
@"await browser.declarativeNetRequest.updateSessionRules({ addRules: [{ id: 2, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'bar' } }] })",
@"await browser.declarativeNetRequest.updateSessionRules({ addRules: [{ id: 3, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'baz' } }] })",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: 1 }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: '' }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: true }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: { } }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: function foo() { } }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: [ '' ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: [ true ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: [ { } ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getSessionRules({ ruleIds: [ function foo() { } ] }))",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ })",
@"sessionRules = sessionRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(sessionRules.length, 3)",
@"browser.test.assertEq(sessionRules[0].id, 1)",
@"browser.test.assertEq(sessionRules[1].id, 2)",
@"browser.test.assertEq(sessionRules[2].id, 3)",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ ruleIds: [ ] })",
@"sessionRules = sessionRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(sessionRules.length, 3)",
@"browser.test.assertEq(sessionRules[0].id, 1)",
@"browser.test.assertEq(sessionRules[1].id, 2)",
@"browser.test.assertEq(sessionRules[2].id, 3)",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ ruleIds: [ 1 ] })",
@"browser.test.assertEq(sessionRules.length, 1)",
@"browser.test.assertEq(sessionRules[0].id, 1)",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ ruleIds: [ 1, 2 ] })",
@"sessionRules = sessionRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(sessionRules.length, 2)",
@"browser.test.assertEq(sessionRules[0].id, 1)",
@"browser.test.assertEq(sessionRules[1].id, 2)",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ ruleIds: [ 1, 2, 3 ] })",
@"sessionRules = sessionRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(sessionRules.length, 3)",
@"browser.test.assertEq(sessionRules[0].id, 1)",
@"browser.test.assertEq(sessionRules[1].id, 2)",
@"browser.test.assertEq(sessionRules[2].id, 3)",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
Util::loadAndRunExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RemoveSessionRules)
{
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: 'foo' } }] })",
@"await browser.declarativeNetRequest.updateSessionRules({ addRules: [{ id: 2, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'bar' } }] })",
@"await browser.declarativeNetRequest.updateSessionRules({ addRules: [{ id: 3, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'baz' } }] })",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ })",
@"browser.test.assertEq(sessionRules.length, 3)",
@"await browser.declarativeNetRequest.updateSessionRules({ removeRuleIds: [1, 2, 3] })",
@"sessionRules = await browser.declarativeNetRequest.getSessionRules({ })",
@"browser.test.assertEq(sessionRules.length, 0)",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
Util::loadAndRunExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
}
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)",
@" browser.test.sendMessage('Unload extension')",
@"} else {",
@" browser.test.assertEq(dynamicRules.length, 1)",
@" browser.test.sendMessage('Load Tab')",
@"}"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto manager = Util::parseExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript }, 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 load];
[manager runUntilTestMessage:@"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 unload];
[manager load];
[manager runUntilTestMessage:@"Load Tab"];
auto webView = manager.get().defaultTab.webView;
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, GetDynamicRules)
{
auto *backgroundScript = Util::constructScript(@[
@"let dynamicRules = await browser.declarativeNetRequest.getDynamicRules()",
@"browser.test.assertEq(dynamicRules.length, 0)",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 1, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'foo' } }] })",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 2, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'bar' } }] })",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 3, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'baz' } }] })",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: 1 }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: '' }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: true }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: { } }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: function foo() { } }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ '' ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ true ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ { } ] }))",
@"await browser.test.assertRejects(browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ function foo() { } ] }))",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ })",
@"dynamicRules = dynamicRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(dynamicRules.length, 3)",
@"browser.test.assertEq(dynamicRules[0].id, 1)",
@"browser.test.assertEq(dynamicRules[1].id, 2)",
@"browser.test.assertEq(dynamicRules[2].id, 3)",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ ] })",
@"dynamicRules = dynamicRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(dynamicRules.length, 3)",
@"browser.test.assertEq(dynamicRules[0].id, 1)",
@"browser.test.assertEq(dynamicRules[1].id, 2)",
@"browser.test.assertEq(dynamicRules[2].id, 3)",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ 1 ] })",
@"browser.test.assertEq(dynamicRules.length, 1)",
@"browser.test.assertEq(dynamicRules[0].id, 1)",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ 1, 2 ] })",
@"dynamicRules = dynamicRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(dynamicRules.length, 2)",
@"browser.test.assertEq(dynamicRules[0].id, 1)",
@"browser.test.assertEq(dynamicRules[1].id, 2)",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ ruleIds: [ 1, 2, 3 ] })",
@"dynamicRules = dynamicRules.sort((a, b) => { a.id < b.id })",
@"browser.test.assertEq(dynamicRules.length, 3)",
@"browser.test.assertEq(dynamicRules[0].id, 1)",
@"browser.test.assertEq(dynamicRules[1].id, 2)",
@"browser.test.assertEq(dynamicRules[2].id, 3)",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
Util::loadAndRunExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RemoveDynamicRules)
{
auto *backgroundScript = Util::constructScript(@[
@"let dynamicRules = await browser.declarativeNetRequest.getDynamicRules()",
@"browser.test.assertEq(dynamicRules.length, 0)",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 1, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'foo' } }] })",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 2, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'bar' } }] })",
@"await browser.declarativeNetRequest.updateDynamicRules({ addRules: [{ id: 3, priority: 1, action: {type: 'block'}, condition: { urlFilter: 'baz' } }] })",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ })",
@"browser.test.assertEq(dynamicRules.length, 3)",
@"await browser.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [1, 2, 3] })",
@"dynamicRules = await browser.declarativeNetRequest.getDynamicRules({ })",
@"browser.test.assertEq(dynamicRules.length, 0)",
@"browser.test.notifyPass()"
]);
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
Util::loadAndRunExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript });
}
static void runRedirectRule(bool useEnhancedSecurity)
{
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"
} ]
}
};
RetainPtr backgroundScript = Util::constructScript(@[
@"browser.test.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript.get(),
@"rules.json": rules
};
auto extensionConfiguration = WKWebExtensionControllerConfiguration.nonPersistentConfiguration;
if (useEnhancedSecurity) {
auto preferences = [extensionConfiguration.webViewConfiguration preferences];
for (_WKFeature *feature in [WKPreferences _features]) {
if ([feature.key isEqualToString:@"EnhancedSecurityHeuristicsEnabled"])
[preferences _setEnabled:YES forFeature:feature];
}
}
auto manager = Util::loadExtension(manifest, resources, extensionConfiguration, useEnhancedSecurity);
auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
navigationDelegate.get().decidePolicyForNavigationAction = ^(WKNavigationAction *navigationAction, void (^decisionHandler)(WKNavigationActionPolicy)) {
decisionHandler(WKNavigationActionPolicyAllow);
if ([navigationAction.request.URL.absoluteString isEqual:redirectURL])
EXPECT_TRUE(navigationAction.isContentRuleListRedirect);
};
manager.get().defaultTab.webView.navigationDelegate = navigationDelegate.get();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RedirectRule)
{
runRedirectRule(false);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RedirectRuleWithEnhancedSecurity)
{
runRedirectRule(true);
}
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.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto manager = Util::loadExtension(manifest, resources);
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusDeniedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestWithHostAccess];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView 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.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto manager = Util::loadExtension(manifest, resources);
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView 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.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto manager = Util::loadExtension(manifest, resources);
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView 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.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto manager = Util::loadExtension(manifest, resources);
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusDeniedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestWithHostAccess];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView 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.sendMessage('Load Tab')"
]);
auto *resources = @{
@"background.js": backgroundScript,
@"rules.json": rules
};
auto manager = Util::loadExtension(manifest, resources);
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, MainFrameAllowAllRequests)
{
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 } }, "<script>browser.test.notifyPass()</script>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.test.sendMessage('Load Tab')"
]);
auto *rules = @"[ { \"id\" : 1, \"priority\": 2, \"action\" : { \"type\" : \"allowAllRequests\" }, \"condition\" : { \"urlFilter\" : \"*\", \"resourceTypes\" : [ \"main_frame\" ] } }, { \"id\" : 2, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"frame\" } } ]";
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"blockAndAllowRules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
// Grant the declarativeNetRequest permission.
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
auto webView = manager.get().defaultTab.webView;
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"]];
[webView loadRequest:urlRequest];
[manager run];
}
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugWithoutPermission)
{
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto *backgroundScript = Util::constructScript(@[
@"browser.test.assertEq(typeof browser.declarativeNetRequest, 'object')",
@"browser.test.assertEq(typeof browser.declarativeNetRequest.onRuleMatchedDebug, 'undefined')",
@"browser.test.notifyPass()"
]);
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript });
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebug)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
{ "/script.html"_s, { { { "Content-Type"_s, "text/html"_s } }, "<script type='module' src='/script.js'></script>"_s } },
{ "/script.js"_s, { { { "Content-Type"_s, "application/javascript"_s } }, "browser.test.notifyFail('This script shouldn't load')"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
// FIXME: <rdar://159289161> Add checks for parentDocumentId once we support it
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" switch (info.rule.rulesetId) {",
@" case 'sub_frame rules':",
@" browser.test.assertEq(typeof info.request.documentId, 'string')",
@" browser.test.assertEq(info.request.documentLifecycle, undefined)",
@" browser.test.assertTrue(info.request.frameId > 0)",
@" browser.test.assertEq(info.request.frameType, 'sub_frame')",
// FIXME: <rdar://159231459> Initiator of a sub-frame is null; it should be src of the iframe.
@" browser.test.assertEq(info.request.initiator, undefined)",
@" browser.test.assertEq(info.request.method, 'GET')",
@" browser.test.assertTrue(info.request.parentFrameId > 0)",
@" browser.test.assertTrue(info.request.tabId > 0)",
@" browser.test.assertEq(info.request.type, 'sub_frame')",
@" browser.test.assertEq(new URL(info.request.url).pathname, '/frame.html')",
@" browser.test.assertEq(info.rule.ruleId, 1)",
@" browser.test.assertEq(info.rule.extensionId, undefined)",
@" browser.test.sendMessage('Done')",
@" break",
@" case 'main_frame rules':",
@" browser.test.assertEq(typeof info.request.documentId, 'string')",
@" browser.test.assertEq(info.request.documentLifecycle, undefined)",
@" browser.test.assertEq(info.request.frameId, 0)",
@" browser.test.assertEq(info.request.frameType, 'outermost_frame')",
@" browser.test.assertEq(info.request.initiator, 'localhost')",
@" browser.test.assertEq(info.request.method, 'GET')",
@" browser.test.assertEq(info.request.parentFrameId, -1)",
@" browser.test.assertTrue(info.request.tabId > 0)",
@" browser.test.assertEq(info.request.type, 'main_frame')",
@" browser.test.assertEq(new URL(info.request.url).pathname, '/frame.html')",
@" browser.test.assertEq(info.rule.ruleId, 2)",
@" browser.test.assertEq(info.rule.extensionId, undefined)",
@" browser.test.sendMessage('Done')",
@" break",
@" case 'non_frame rules':",
@" browser.test.assertEq(info.request.documentId, undefined)",
@" browser.test.assertEq(info.request.documentLifecycle, undefined)",
@" browser.test.assertEq(info.request.frameId, 0)",
@" browser.test.assertEq(info.request.frameType, undefined)",
@" browser.test.assertEq(info.request.initiator, 'localhost')",
@" browser.test.assertEq(info.request.method, 'GET')",
@" browser.test.assertEq(info.request.parentFrameId, -1)",
@" browser.test.assertTrue(info.request.tabId > 0)",
@" browser.test.assertEq(info.request.type, 'script')",
@" browser.test.assertEq(new URL(info.request.url).pathname, '/script.js')",
@" browser.test.assertEq(info.rule.ruleId, 3)",
@" browser.test.assertEq(info.rule.extensionId, undefined)",
@" browser.test.notifyPass()",
@" break",
@" default:",
@" browser.test.notifyFail('Received an unexpected rulesetId.')",
@" }",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *subFrameRules = @"[{\"id\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"resourceTypes\":[\"sub_frame\"]}}]";
auto *mainFrameRules = @"[{\"id\":2,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"resourceTypes\":[\"main_frame\"]}}]";
auto *nonFrameRules = @"[{\"id\":3,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"script.js\",\"resourceTypes\":[\"script\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"sub_frame rules",
@"enabled": @YES,
@"path": @"sub_frame.json"
},
@{
@"id": @"main_frame rules",
@"enabled": @YES,
@"path": @"main_frame.json"
},
@{
@"id": @"non_frame rules",
@"enabled": @YES,
@"path": @"non_frame.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"sub_frame.json": subFrameRules, @"main_frame.json": mainFrameRules, @"non_frame.json": nonFrameRules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
// Test sub_frame rules
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.get().defaultTab.webView loadRequest:urlRequest];
[manager runUntilTestMessage:@"Done"];
// Test main_frame rules
[manager.get().defaultTab.webView loadRequest:server.requestWithLocalhost("/frame.html"_s)];
[manager runUntilTestMessage:@"Done"];
// Test non_frame rules
[manager.get().defaultTab.webView loadRequest:server.requestWithLocalhost("/script.html"_s)];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugSessionRules)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.assertEq(info.rule.rulesetId, '_session')",
@" browser.test.assertEq(info.rule.ruleId, 1)",
@" browser.test.notifyPass()",
@"})",
@"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)",
@"browser.test.sendMessage('Load Tab')",
]);
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugDynamicRules)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.assertEq(info.rule.rulesetId, '_dynamic')",
@" browser.test.assertEq(info.rule.ruleId, 1)",
@" browser.test.notifyPass()",
@"})",
@"let dynamicRules = await browser.declarativeNetRequest.getDynamicRules()",
@"browser.test.assertEq(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)",
@"browser.test.sendMessage('Load Tab')",
]);
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, DuplicatedRuleIDs)
{
auto *backgroundScript = Util::constructScript(@[
@"let enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 1, 'The static ruleset should be enabled.')",
@"browser.test.sendMessage('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"name": @"Test",
@"description": @"Test dNR extension",
@"version": @"1",
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"duplicated_rule_id",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto *rules = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"foo\" } }, { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"bar\" } } ]";
auto manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
NSArray<NSError *> *errors = manager.get().context.errors;
ASSERT_EQ(errors.count, 1ul);
ASSERT_TRUE([errors.firstObject.localizedDescription isEqualToString:@"`declarative_net_request` ruleset with id `duplicated_rule_id` duplicates the rule id `1`."]);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, DuplicatedRuleIDsInDifferentRulesets)
{
auto *backgroundScript = Util::constructScript(@[
@"let enabledRulesets = await browser.declarativeNetRequest.getEnabledRulesets()",
@"browser.test.assertEq(enabledRulesets.length, 2, 'The static rulesets should be enabled.')",
@"browser.test.sendMessage('Load Tab')"
]);
auto *declarativeNetRequestManifest = @{
@"name": @"Test",
@"description": @"Test dNR extension",
@"version": @"1",
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"ruleset_1",
@"enabled": @YES,
@"path": @"rules1.json"
},
@{
@"id": @"ruleset_2",
@"enabled": @YES,
@"path": @"rules2.json"
}
]
}
};
auto *rules1 = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"foo\" } } ]";
auto *rules2 = @"[ { \"id\" : 1, \"priority\": 1, \"action\" : { \"type\" : \"block\" }, \"condition\" : { \"urlFilter\" : \"bar\" } } ]";
auto manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules1.json": rules1, @"rules2.json": rules2 });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Load Tab"];
ASSERT_EQ(manager.get().context.errors.count, 0ul);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugExcludedRequestDomains)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.notifyFail('onRuleMatchedDebug should not be called for an excluded request domain.')",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"excludedRequestDomains\":[\"localhost\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager.get().defaultTab.webView _test_waitForDidFinishNavigation];
[manager done];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugExcludedRequestMethods)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.notifyFail('onRuleMatchedDebug should not be called for an excluded request method.')",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"excludedRequestMethods\":[\"get\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager.get().defaultTab.webView _test_waitForDidFinishNavigation];
[manager done];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugExcludedRequestDomainsAndExcludedRequestMethods)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.notifyFail('onRuleMatchedDebug should not be called for an excluded request domain or an excluded request method.')",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"excludedRequestDomains\":[\"localhost\"],\"excludedRequestMethods\":[\"get\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager.get().defaultTab.webView _test_waitForDidFinishNavigation];
[manager done];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugRequestDomains)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.assertEq(info.rule.rulesetId, 'rules');",
@" browser.test.assertEq(info.rule.ruleId, 1);",
@" browser.test.assertEq(info.request.type, 'sub_frame');",
@" browser.test.notifyPass();",
@"})",
@"browser.test.sendMessage('Load Tab');",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"requestDomains\":[\"localhost\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugRequestMethods)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.assertEq(info.rule.rulesetId, 'rules');",
@" browser.test.assertEq(info.rule.ruleId, 1);",
@" browser.test.assertEq(info.request.type, 'sub_frame');",
@" browser.test.assertEq(info.request.method, 'GET');",
@" browser.test.notifyPass();",
@"})",
@"browser.test.sendMessage('Load Tab');",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"requestMethods\":[\"get\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugRequestDomainsAndRequestMethods)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.assertEq(info.rule.rulesetId, 'rules');",
@" browser.test.assertEq(info.rule.ruleId, 1);",
@" browser.test.assertEq(info.request.type, 'sub_frame');",
@" browser.test.assertEq(info.request.method, 'GET');",
@" browser.test.notifyPass();",
@"})",
@"browser.test.sendMessage('Load Tab');",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"requestDomains\":[\"localhost\"],\"requestMethods\":[\"get\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugRequestInitiatorDomainsAndExcludedInitiatorDomains)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" browser.test.notifyFail('onRuleMatchedDebug should not be called for an excluded initiator domain.')",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"block\"},\"condition\":{\"urlFilter\":\"frame\",\"initiatorDomains\":[\"example.com\"],\"excludedInitiatorDomains\":[\"localhost\"],\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager.get().defaultTab.webView _test_waitForDidFinishNavigation];
[manager done];
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, OnRuleMatchedDebugRequestUpgradeScheme)
{
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 } }, "<h1>Hello, world!</h1>"_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"var onRuleMatchedDebugCount = 0",
@"browser.declarativeNetRequest.onRuleMatchedDebug.addListener((info) => {",
@" onRuleMatchedDebugCount++",
@" browser.test.assertEq(onRuleMatchedDebugCount, 1, 'onRuleMatchedDebug should only be called once an upgrade action type.')",
@"})",
@"browser.test.sendMessage('Load Tab')",
]);
auto *rules = @"[{\"id\":1,\"priority\":1,\"action\":{\"type\":\"upgradeScheme\"},\"condition\":{\"urlFilter\":\"frame\",\"resourceTypes\":[\"sub_frame\"]}}]";
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest", @"declarativeNetRequestFeedback" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"declarative_net_request": @{
@"rule_resources": @[
@{
@"id": @"rules",
@"enabled": @YES,
@"path": @"rules.json"
}
]
}
};
auto manager = Util::loadExtension(manifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequestFeedback];
[manager runUntilTestMessage:@"Load Tab"];
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.get().defaultTab.webView loadRequest:urlRequest];
[manager.get().defaultTab.webView _test_waitForDidFinishNavigation];
[manager done];
}
#endif
// 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 rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NOT_NULL(validatedRule1);
NSDictionary *rule2 = @{
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule2 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule2 rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule2);
NSDictionary *rule3 = @{
@"id": @1,
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule3 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule3 rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule3);
NSDictionary *rule4 = @{
@"id": @1,
@"action": @{ @"type": @"block" },
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule4 = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule4 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*\\.com",
@"resource-type": @[ @"script" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"font" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"top-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"child-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"top-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule2 = validatedRule2.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule2);
NSDictionary *correctRuleConversion2 = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"font" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSMutableDictionary *convertedRule = [validatedRule.ruleInWebKitFormat.firstObject mutableCopy];
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"block",
},
@"trigger": [@{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"script", @"style-sheet", @"ping" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:correctRuleConversion[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
convertedRule[@"trigger"][@"resource-type"] = nil;
correctRuleConversion[@"trigger"][@"resource-type"] = nil;
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 rulesetID:@"Test Ruleset" errorString:nil];
NSMutableDictionary *convertedRule = [validatedRule.ruleInWebKitFormat.firstObject mutableCopy];
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"block",
},
@"trigger": [@{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"fetch", @"websocket", @"other" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:correctRuleConversion[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
convertedRule[@"trigger"][@"resource-type"] = nil;
correctRuleConversion[@"trigger"][@"resource-type"] = nil;
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 rulesetID:@"Test Ruleset" errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *makeHTTPSRule = @{
@"action": @{
@"type": @"make-https",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
NSDictionary *sortingRule = @{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"image" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[0], makeHTTPSRule);
EXPECT_NS_EQUAL(convertedRules[1], sortingRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, UpgradeSchemeForMainFrameRuleConversion)
{
NSDictionary *rule = @{
@"id": @1,
@"priority": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *makeHTTPSRule = @{
@"action": @{
@"type": @"make-https",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"top-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[0], makeHTTPSRule);
NSDictionary *sortingRule = @{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @"crouton\\.net",
@"resource-type": @[ @"top-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[1], sortingRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleWithoutAPriority)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"upgradeScheme" },
@"condition": @{
@"urlFilter": @"crouton.net",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithDomainType)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"boatnerd.com",
@"resourceTypes": @[ @"script" ],
@"domainType": @"firstParty",
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"boatnerd\\.com",
@"resource-type": @[ @"script" ],
@"load-type": @[ @"first-party" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithNoSpecifiedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"regexFilter": @".*",
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSMutableDictionary *convertedRule = [validatedRule.ruleInWebKitFormat.firstObject mutableCopy];
EXPECT_NOT_NULL(convertedRule);
NSMutableDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": [@{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"image", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket", @"child-document" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:correctRuleConversion[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
convertedRule[@"trigger"][@"resource-type"] = nil;
correctRuleConversion[@"trigger"][@"resource-type"] = nil;
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSMutableDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"block",
},
@"trigger": [@{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket", @"top-document", @"child-document" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:correctRuleConversion[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
convertedRule[@"trigger"][@"resource-type"] = nil;
correctRuleConversion[@"trigger"][@"resource-type"] = nil;
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithEmptyExcludedResourceTypes)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"*",
@"excludedResourceTypes": @[ ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSMutableDictionary *convertedRule = [validatedRule.ruleInWebKitFormat.firstObject mutableCopy];
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"block",
},
@"trigger": [@{
@"url-filter": @".*",
@"resource-type": @[ @"fetch", @"font", @"image", @"media", @"other", @"ping", @"script", @"style-sheet", @"websocket", @"top-document", @"child-document" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
NSSet *actualResourceTypes = [NSSet setWithArray:convertedRule[@"trigger"][@"resource-type"]];
NSSet *expectedResourceTypes = [NSSet setWithArray:correctRuleConversion[@"trigger"][@"resource-type"]];
EXPECT_NS_EQUAL(actualResourceTypes, expectedResourceTypes);
convertedRule[@"trigger"][@"resource-type"] = nil;
correctRuleConversion[@"trigger"][@"resource-type"] = nil;
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, EmptyDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @".*",
@"domains": @[ ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithURLFilterAndRequestDomains)
{
__auto_type testPattern = ^(NSString *requestDomain, NSString *urlFilter, NSString *expectedRegexPattern) {
NSMutableDictionary *rule = [@{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": [@{
@"resourceTypes": @[ @"script" ],
@"requestDomains": @[ requestDomain ],
} mutableCopy],
} mutableCopy];
if (urlFilter)
rule[@"condition"][@"urlFilter"] = urlFilter;
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": expectedRegexPattern,
@"resource-type": @[ @"script" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
};
// No URL filter
testPattern(@"apple.com", nil, @"^[^:]+://+([^:/]+\\.)?apple\\.com");
// URL filter contains the request domain
testPattern(@"com", @".com/foo/bar", @"^[^:]+://+([^:/]+\\.)?\\.com/foo/bar");
// Part of the request domain is in the URL filter
testPattern(@"foo.com", @"bar-*.com", @"^[^:]+://+([^:/]+\\.)?bar-.*foo\\.com");
testPattern(@"foo.com", @"foo.*/bar/baz", @"^[^:]+://+([^:/]+\\.)?foo\\.com.*/bar/baz");
// || or :// prefix in the URL filter
testPattern(@"com", @"||foo", @"^[^:]+://+([^:/]+\\.)?foo.*com");
testPattern(@"com", @"://www.", @"://www\\..*com");
testPattern(@"com", @"://www.*", @"://www\\..*com");
testPattern(@"com", @"://www./foo/bar", @"://www\\..*com/foo/bar");
testPattern(@"com", @"://www.*/foo/bar", @"://www\\..*com/foo/bar");
// Concatenating the request domain and URL filter
testPattern(@"com", @"/foo/bar", @"^[^:]+://+([^:/]+\\.)?com.*/foo/bar");
testPattern(@"com", @"&foo=bar|", @"^[^:]+://+([^:/]+\\.)?com.*&foo=bar$");
}
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 rulesetID:@"Test Ruleset" errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 3ul);
NSDictionary *appleURLFilterRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"resource-type": @[ @"font" ],
@"url-filter": @"^[^:]+://+([^:/]+\\.)?apple\\.com",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[0], appleURLFilterRuleConversion);
NSDictionary *facebookURLFilterRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"resource-type": @[ @"font" ],
@"url-filter": @"^[^:]+://+([^:/]+\\.)?facebook\\.com",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[1], facebookURLFilterRuleConversion);
NSDictionary *googleURLFilterRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"resource-type": @[ @"font" ],
@"url-filter": @"^[^:]+://+([^:/]+\\.)?.*google\\.com",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[2], googleURLFilterRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithInitiatorDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"initiatorDomains": @[ @"apple.com", @"facebook.com", @"google.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"if-frame-url": @[ @"^[^:]+://+([^:/]+\\.)?apple\\.com/.*", @"^[^:]+://+([^:/]+\\.)?facebook\\.com/.*", @"^[^:]+://+([^:/]+\\.)?google\\.com/.*" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedInitiatorDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedInitiatorDomains": @[ @"apple.com", @"facebook.com", @"google.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"unless-frame-url": @[ @"^[^:]+://+([^:/]+\\.)?apple\\.com/.*", @"^[^:]+://+([^:/]+\\.)?facebook\\.com/.*", @"^[^:]+://+([^:/]+\\.)?google\\.com/.*" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRule, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedInitiatorDomainsAndInitiatorDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"initiatorDomains": @[ @"example.com" ],
@"excludedInitiatorDomains": @[ @"blog.example.com" ],
@"resourceTypes": @[ @"font" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSArray *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_NOT_NULL(convertedRules);
NSArray *correctRuleConversion = @[
@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"if-frame-url": @[ @"^[^:]+://+([^:/]+\\.)?blog\\.example\\.com/.*" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
},
@{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"if-frame-url": @[ @"^[^:]+://+([^:/]+\\.)?example\\.com/.*" ],
@"resource-type": @[ @"font" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
}
];
EXPECT_NS_EQUAL(convertedRules, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithEmptyExcludedDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedDomains": @[ ],
@"resourceTypes": @[ @"media" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"unless-domain": @[ ],
@"resource-type": @[ @"media" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSArray<NSDictionary *> *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_EQ(convertedRules.count, 2ul);
NSDictionary *passRuleConversion = @{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"resource-type": @[ @"media" ],
@"url-filter": @"apple\\.com",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[0], passRuleConversion);
NSDictionary *blockRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"resource-type": @[ @"media" ],
@"url-filter": @".*",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
EXPECT_NS_EQUAL(convertedRules[1], blockRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithInvalidRequestMethods)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"requestMethods": @[ @"bad" ],
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithInvalidExcludedRequestMethods)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedRequestMethods": @[ @"bad" ],
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithRequestMethods)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"requestMethods": @[ @"get", @"post" ],
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSArray *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_NOT_NULL(convertedRules);
NSArray *correctRuleConversion = @[
@{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"top-document" ],
@"request-method": @"get",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
},
@{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"top-document" ],
@"request-method": @"post",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
}
];
EXPECT_NS_EQUAL(convertedRules, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithExcludedRequestMethods)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"excludedRequestMethods": @[ @"get", @"post" ],
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSArray *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_NOT_NULL(convertedRules);
NSArray *correctRuleConversion = @[
@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"top-document" ],
@"request-method": @"get",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
},
@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"top-document" ],
@"request-method": @"post",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
},
@{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @".*",
@"resource-type": @[ @"top-document" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
}
];
EXPECT_NS_EQUAL(convertedRules, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithRequestMethodsAndExcludedRequestMethodsAndRequestDomainsAndExcludedRequestDomains)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"requestDomains": @[ @"apple.com" ],
@"excludedRequestDomains": @[ @"google.com" ],
@"requestMethods": @[ @"get" ],
@"excludedRequestMethods": @[ @"post" ],
@"resourceTypes": @[ @"main_frame" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSArray *convertedRules = validatedRule.ruleInWebKitFormat;
EXPECT_NOT_NULL(convertedRules);
NSArray *correctRuleConversion = @[
@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": @{
@"url-filter": @"google\\.com",
@"resource-type": @[ @"top-document" ],
@"request-method": @"post",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
},
@{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": @"^[^:]+://+([^:/]+\\.)?apple\\.com",
@"resource-type": @[ @"top-document" ],
@"request-method": @"get",
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
}
];
EXPECT_NS_EQUAL(convertedRules, correctRuleConversion);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, NonASCIIURLFilter)
{
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"block" },
@"condition": @{
@"urlFilter": @"🔮.com",
@"resourceTypes": @[ @"script" ],
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NOT_NULL(convertedRule);
NSDictionary *correctRuleConversion = @{
@"action": @{
@"type": @"block",
},
@"trigger": @{
@"url-filter": expectedRegexPattern,
@"resource-type": @[ @"script" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" 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 rulesetID:@"Test Ruleset" errorString:nil];
EXPECT_NULL(validatedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RuleConversionWithMainFrameAllowAllRequests)
{
// FIXME: rdar://154124673 (Write tests for sub_frame allowAllRequests rules)
NSDictionary *rule = @{
@"id": @1,
@"action": @{ @"type": @"allowAllRequests" },
@"condition": @{
@"urlFilter": @"apple.com",
@"resourceTypes": @[ @"main_frame" ]
},
};
_WKWebExtensionDeclarativeNetRequestRule *validatedRule = [[_WKWebExtensionDeclarativeNetRequestRule alloc] initWithDictionary:rule rulesetID:@"Test Ruleset" errorString:nil];
NSMutableDictionary *convertedRule = [validatedRule.ruleInWebKitFormat.firstObject mutableCopy];
EXPECT_NOT_NULL(convertedRule);
NSMutableDictionary *correctRuleConversion = [@{
@"action": @{
@"type": @"ignore-following-rules",
},
@"trigger": [@{
@"url-filter": @".*",
@"if-top-url": @[ @"apple\\.com" ],
} mutableCopy],
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
} mutableCopy];
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 rulesetID:@"Test Ruleset" 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" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @1,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @10,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" 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" ],
},
#if ENABLE(DNR_ON_RULE_MATCHED_DEBUG)
@"_identifier": @10,
@"_rulesetIdentifier": @"Test Ruleset",
#endif
};
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 rulesetID:@"Test Ruleset" errorString:nil];
NSDictionary *convertedRule = validatedRule.ruleInWebKitFormat.firstObject;
EXPECT_NULL(convertedRule);
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortByPriorityFromDifferentRulesets)
{
NSDictionary *rules = @{
@"Test Ruleset 1": @[ @{
@"id": @1,
@"priority": @2,
@"action": @{ @"type": @"allow" },
@"condition": @{
@"regexFilter": @"apple.com",
@"resourceTypes": @[ @"script" ],
},
} ],
@"Test Ruleset 2": @[ @{
@"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"], @"ignore-following-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"block");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortWithoutExplicitPriority)
{
NSDictionary *rules = @{
@"Test Ruleset": @[
@{
@"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"], @"block");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"make-https");
EXPECT_NS_EQUAL(sortedTranslatedRules[2][@"action"][@"type"], @"ignore-following-rules");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RulesSortByActionType)
{
NSDictionary *rules = @{
@"Test Ruleset": @[
@{
@"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"], @"ignore-following-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[1][@"action"][@"type"], @"ignore-following-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[2][@"action"][@"type"], @"block");
EXPECT_NS_EQUAL(sortedTranslatedRules[3][@"action"][@"type"], @"make-https");
EXPECT_NS_EQUAL(sortedTranslatedRules[4][@"action"][@"type"], @"ignore-following-rules");
EXPECT_NS_EQUAL(sortedTranslatedRules[5][@"action"][@"type"], @"modify-headers");
}
TEST(WKWebExtensionAPIDeclarativeNetRequest, RemoveAllContentRuleListsDoesNotRemoveWebExtensionRuleLists)
{
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(@[
@"browser.test.sendMessage('Remove RuleLists and 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 manager = Util::loadExtension(declarativeNetRequestManifest, @{ @"background.js": backgroundScript, @"rules.json": rules });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionDeclarativeNetRequest];
[manager runUntilTestMessage:@"Remove RuleLists and Load Tab"];
[manager.get().defaultTab.webView.configuration.userContentController removeAllContentRuleLists];
auto webView = manager.get().defaultTab.webView;
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, MigrateDeclarativeNetRequestDataToNewFormat)
{
auto *backgroundScript = Util::constructScript(@[
@"var expectedResults = [{",
@" 'id': 1,",
@" 'condition': {",
@" 'urlFilter': 'blocksub'",
@" },",
@" 'action': {",
@" 'type': 'block'",
@" }",
@"}]",
@"var results",
@"results = await browser.declarativeNetRequest.getDynamicRules()",
@"browser.test.assertDeepEq(results, expectedResults)",
@"browser.test.notifyPass()",
]);
static auto *resources = @{
@"background.js": backgroundScript,
};
auto *declarativeNetRequestManifest = @{
@"manifest_version": @3,
@"permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
};
auto manager = Util::parseExtension(declarativeNetRequestManifest, resources, 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)";
[manager load];
auto *storageDirectory = manager.get().controller.configuration._storageDirectoryPath;
storageDirectory = [storageDirectory stringByAppendingPathComponent:manager.get().context.uniqueIdentifier];
static auto *files = @[
[NSBundle.test_resourcesBundle URLForResource:@"DeclarativeNetRequestRules" withExtension:@"db"],
[NSBundle.test_resourcesBundle URLForResource:@"DeclarativeNetRequestRules" withExtension:@"db-shm"],
[NSBundle.test_resourcesBundle URLForResource:@"DeclarativeNetRequestRules" withExtension:@"db-wal"]
];
for (NSURL *file in files) {
NSString *combinedPath = [storageDirectory stringByAppendingPathComponent:[file lastPathComponent]];
[NSFileManager.defaultManager copyItemAtURL:file toURL:[NSURL fileURLWithPath:combinedPath] error:nil];
}
[manager run];
}
} // namespace TestWebKitAPI
#endif // ENABLE(WK_WEB_EXTENSIONS)