blob: 644415d39b9be1d3c6b6d2f85f0e050ce7014c09 [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 "WebExtensionUtilities.h"
#import <WebKit/_WKWebExtensionWebNavigationURLFilter.h>
#import <wtf/text/MakeString.h>
namespace TestWebKitAPI {
static auto *webNavigationManifest = @{ @"manifest_version": @3, @"permissions": @[ @"webNavigation" ], @"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO } };
TEST(WKWebExtensionAPIWebNavigation, EventListenerRegistration)
{
auto *backgroundScript = Util::constructScript(@[
@"function listener() { browser.test.notifyFail('This listener should not have been called') }",
@"browser.test.assertFalse(browser.webNavigation.onBeforeNavigate.hasListener(listener), 'Should not have listener')",
@"browser.webNavigation.onBeforeNavigate.addListener(listener)",
@"browser.test.assertTrue(browser.webNavigation.onBeforeNavigate.hasListener(listener), 'Should have listener')",
@"browser.webNavigation.onBeforeNavigate.removeListener(listener)",
@"browser.test.assertFalse(browser.webNavigation.onBeforeNavigate.hasListener(listener), 'Should not have listener')",
@"browser.test.notifyPass()"
]);
Util::loadAndRunExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
}
TEST(WKWebExtensionAPIWebNavigation, BeforeNavigateEvent)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.webNavigation.onBeforeNavigate.addListener((details) => {",
@" browser.test.assertEq(details?.frameId, 0, 'details.frameId should be')",
@" browser.test.assertEq(details?.parentFrameId, -1, 'details.parentFrameId should be')",
@" browser.test.assertEq(typeof details?.url, 'string', 'details.url should be')",
@" browser.test.assertTrue(details?.url?.includes('localhost'), 'details.url should include localhost')",
@" browser.test.assertEq(typeof details?.tabId, 'number', 'details.tabId should be')",
@" browser.test.assertEq(typeof details?.timeStamp, 'number', 'details.timeStamp should be')",
@" browser.test.assertEq(details?.documentId, undefined, 'details.documentId should be')",
@" browser.test.notifyPass()",
@"})",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, CommittedEvent)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.webNavigation.onCommitted.addListener((details) => {",
@" browser.test.assertEq(details?.frameId, 0, 'details.frameId should be')",
@" browser.test.assertEq(details?.parentFrameId, -1, 'details.parentFrameId should be')",
@" browser.test.assertEq(typeof details?.url, 'string', 'details.url should be')",
@" browser.test.assertTrue(details?.url?.includes('localhost'), 'details.url should include localhost')",
@" browser.test.assertEq(typeof details?.tabId, 'number', 'details.tabId should be')",
@" browser.test.assertEq(typeof details?.timeStamp, 'number', 'details.timeStamp should be')",
@" browser.test.assertEq(typeof details?.documentId, 'string', 'details.documentId should be')",
@" browser.test.assertEq(details?.documentId?.length, 36, 'details.documentId.length should be')",
@" browser.test.notifyPass()",
@"})",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, DOMContentLoadedEvent)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.webNavigation.onDOMContentLoaded.addListener((details) => {",
@" browser.test.assertEq(details?.frameId, 0, 'details.frameId should be')",
@" browser.test.assertEq(details?.parentFrameId, -1, 'details.parentFrameId should be')",
@" browser.test.assertEq(typeof details?.url, 'string', 'details.url should be')",
@" browser.test.assertTrue(details?.url?.includes('localhost'), 'details.url should include localhost')",
@" browser.test.assertEq(typeof details?.tabId, 'number', 'details.tabId should be')",
@" browser.test.assertEq(typeof details?.timeStamp, 'number', 'details.timeStamp should be')",
@" browser.test.assertEq(typeof details?.documentId, 'string', 'details.documentId should be')",
@" browser.test.assertEq(details?.documentId?.length, 36, 'details.documentId.length should be')",
@" browser.test.notifyPass()",
@"})",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, CompletedEvent)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"browser.webNavigation.onCompleted.addListener((details) => {",
@" browser.test.assertEq(details?.frameId, 0, 'details.frameId should be')",
@" browser.test.assertEq(details?.parentFrameId, -1, 'details.parentFrameId should be')",
@" browser.test.assertEq(typeof details?.url, 'string', 'details.url should be')",
@" browser.test.assertTrue(details?.url?.includes('localhost'), 'details.url should include localhost')",
@" browser.test.assertEq(typeof details?.tabId, 'number', 'details.tabId should be')",
@" browser.test.assertEq(typeof details?.timeStamp, 'number', 'details.timeStamp should be')",
@" browser.test.assertEq(typeof details?.documentId, 'string', 'details.documentId should be')",
@" browser.test.assertEq(details?.documentId?.length, 36, 'details.documentId.length should be')",
@" browser.test.notifyPass()",
@"})",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, AllowedFilter)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"function passListener() { browser.test.notifyPass() }",
@"browser.webNavigation.onCommitted.addListener(passListener, { 'url': [ {'hostContains': 'localhost'} ] })",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, DeniedFilter)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"function passListener() { browser.test.notifyPass() }",
@"function failListener() { browser.test.notifyFail('This listener should not have been called') }",
@"browser.webNavigation.onCommitted.addListener(failListener, { 'url': [ {'hostContains': 'example'} ] })",
@"browser.webNavigation.onCommitted.addListener(passListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, AllEventsFired)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"let beforeNavigateEventFired = false",
@"let onCommittedEventFired = false",
@"let onDOMContentLoadedEventFired = false",
@"function beforeNavigateHandler() { beforeNavigateEventFired = true }",
@"function onCommittedHandler() { onCommittedEventFired = true }",
@"function onDOMContentLoadedHandler() { onDOMContentLoadedEventFired = true }",
@"function onCompletedHandler() {",
@" browser.test.assertTrue(beforeNavigateEventFired)",
@" browser.test.assertTrue(onCommittedEventFired)",
@" browser.test.assertTrue(onDOMContentLoadedEventFired)",
@" browser.test.notifyPass()",
@"}",
@"browser.webNavigation.onBeforeNavigate.addListener(beforeNavigateHandler)",
@"browser.webNavigation.onCommitted.addListener(onCommittedHandler)",
@"browser.webNavigation.onDOMContentLoaded.addListener(onDOMContentLoadedHandler)",
@"browser.webNavigation.onCompleted.addListener(onCompletedHandler)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, DocumentIdAcrossEvents)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"let documentId = null",
@"browser.webNavigation.onBeforeNavigate.addListener((details) => {",
@" browser.test.assertEq(details?.documentId, undefined, 'details.documentId should be')",
@"})",
@"browser.webNavigation.onCommitted.addListener((details) => {",
@" browser.test.assertEq(documentId, null, 'documentId should be')",
@" browser.test.assertEq(typeof details?.documentId, 'string', 'details.documentId should be')",
@" browser.test.assertEq(details?.documentId?.length, 36, 'details.documentId.length should be')",
@" documentId = details?.documentId",
@"})",
@"browser.webNavigation.onDOMContentLoaded.addListener((details) => {",
@" browser.test.assertEq(documentId, details?.documentId, 'details.documentId should stay consistent in onDOMContentLoaded')",
@"})",
@"browser.webNavigation.onCompleted.addListener((details) => {",
@" browser.test.assertEq(documentId, details?.documentId, 'details.documentId should stay consistent in onCompleted')",
@" browser.test.notifyPass()",
@"})",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, RemoveListenerDuringEvent)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"function navigationListener() {",
@" browser.webNavigation.onCommitted.removeListener(navigationListener)",
@" browser.test.assertFalse(browser.webNavigation.onCommitted.hasListener(navigationListener), 'Listener should be removed')",
@"}",
@"browser.webNavigation.onCommitted.addListener(navigationListener)",
@"browser.webNavigation.onCommitted.addListener(() => browser.test.notifyPass())",
@"browser.test.assertTrue(browser.webNavigation.onCommitted.hasListener(navigationListener), 'Listener should be registered')",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
auto *urlRequest = server.requestWithLocalhost();
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:urlRequest.URL];
[manager runUntilTestMessage:@"Load Tab"];
[manager.get().defaultTab.webView loadRequest:urlRequest];
[manager run];
}
TEST(WKWebExtensionAPIWebNavigation, ErrorOccurredEventDuringProvisionalLoad)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { HTTPResponse::Behavior::TerminateConnectionAfterReceivingRequest } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"function errorListener(details) {",
@" browser.test.assertTrue(details?.frameId != 0)",
@" browser.test.assertTrue(details?.url.includes('localhost'))",
@" browser.test.assertTrue(details?.url.includes('frame'))",
@" browser.test.notifyPass()",
@"}",
@"browser.webNavigation.onErrorOccurred.addListener(errorListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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];
}
template<size_t length>
String longString(Latin1Character c)
{
Vector<Latin1Character> vector(length, c);
return vector.span();
}
TEST(WKWebExtensionAPIWebNavigation, ErrorOccurredEventDuringLoad)
{
TestWebKitAPI::HTTPServer server(TestWebKitAPI::HTTPServer::UseCoroutines::Yes, [&](auto connection) -> TestWebKitAPI::ConnectionTask {
while (1) {
auto request = co_await connection.awaitableReceiveHTTPRequest();
auto path = TestWebKitAPI::HTTPServer::parsePath(request);
if (path == "/"_s) {
constexpr auto body = "<iframe src='/frame.html'></iframe>"_s;
auto reply = makeString(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: "_s, body.length(), "\r\n"
"Connection: close\r\n"
"\r\n"_s, body
);
co_await connection.awaitableSend(WTF::move(reply));
continue;
}
if (path == "/frame.html"_s) {
auto response = makeString(
"HTTP/1.1 200 OK\r\n"_s,
"Content-Length: 1000000\r\n"
"\r\n"_s, longString<500000>(' ')
);
co_await connection.awaitableSend(WTF::move(response));
connection.terminate();
continue;
}
EXPECT_FALSE(true);
}
});
auto *backgroundScript = Util::constructScript(@[
@"async function errorListener(details) {",
// This should be a subframe
@" browser.test.assertTrue(details.frameId != 0)",
// The URL of the frame that failed loading should include localhost and frame.
@" browser.test.assertTrue(details.url.includes('localhost'))",
@" browser.test.assertTrue(details.url.includes('frame'))",
@" const frame = await browser.webNavigation.getFrame({ tabId: details.tabId, frameId: details.frameId })",
@" browser.test.assertEq(frame.parentFrameId, 0)",
@" browser.test.assertTrue(frame.errorOccurred)",
// And since the failure happened after the load had been committed, the frame object has the URL set as well.
@" browser.test.assertTrue(frame.url.includes('localhost'))",
@" browser.test.assertTrue(frame.url.includes('frame'))",
@" browser.test.notifyPass()",
@"}",
// The passListener firing will consider the test passed.
@"browser.webNavigation.onErrorOccurred.addListener(errorListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, GetFrameWithMainFrame)
{
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(@[
@"function completedListener(details) {",
@" if (details?.frameId !== 0)",
@" return",
@" browser.webNavigation.getFrame({ tabId: details?.tabId, frameId: 0 }, (frame) => {",
@" browser.test.assertEq(frame?.parentFrameId, -1)",
@" browser.test.assertTrue(frame?.url?.includes('localhost'))",
@" browser.test.assertFalse(frame?.url?.includes('frame'))",
@" browser.test.assertEq(typeof frame?.documentId, 'string', 'frame.documentId should be')",
@" browser.test.assertEq(frame?.documentId?.length, 36, 'frame.documentId.length should be')",
@" browser.test.notifyPass()",
@" })",
@"}",
@"browser.webNavigation.onCompleted.addListener(completedListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, GetFrameWithSubframe)
{
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(@[
@"function completedListener(details) {",
@" if (details?.frameId === 0)",
@" return",
@" browser.webNavigation.getFrame({ tabId: details?.tabId, frameId: details?.frameId }, (frame) => {",
@" browser.test.assertEq(frame.parentFrameId, 0)",
@" browser.test.assertTrue(frame.url.includes('localhost'))",
@" browser.test.assertTrue(frame.url.includes('frame'))",
@" browser.test.assertEq(typeof frame?.documentId, 'string', 'frame.documentId should be')",
@" browser.test.assertEq(frame?.documentId?.length, 36, 'frame.documentId.length should be')",
@" browser.test.notifyPass()",
@" })",
@"}",
@"browser.webNavigation.onCompleted.addListener(completedListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, GetAllFrames)
{
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(@[
@"function completedListener(details) {",
@" if (details.frameId !== 0)",
@" return",
@" browser.webNavigation.getAllFrames({ tabId: details?.tabId }, (frames) => {",
@" browser.test.assertEq(frames?.length, 2)",
@" for (let frame of frames) {",
@" if (frame?.frameId === 0) {",
@" browser.test.assertEq(frame?.parentFrameId, -1)",
@" browser.test.assertTrue(frame?.url.includes('localhost'))",
@" browser.test.assertFalse(frame?.url.includes('frame'))",
@" } else {",
@" browser.test.assertEq(frame?.parentFrameId, 0)",
@" browser.test.assertTrue(frame?.url.includes('localhost'))",
@" browser.test.assertTrue(frame?.url.includes('frame'))",
@" }",
@" browser.test.assertEq(typeof frame?.documentId, 'string', 'frame.documentId should be')",
@" browser.test.assertEq(frame?.documentId?.length, 36, 'frame.documentId.length should be')",
@" }",
@" browser.test.notifyPass()",
@" })",
@"}",
@"browser.webNavigation.onCompleted.addListener(completedListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, ErrorOccurred)
{
TestWebKitAPI::HTTPServer server({
{ "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<iframe src='/frame.html'></iframe>"_s } },
{ "/frame.html"_s, { HTTPResponse::Behavior::TerminateConnectionAfterReceivingRequest } },
}, TestWebKitAPI::HTTPServer::Protocol::Http);
auto *backgroundScript = Util::constructScript(@[
@"function errorListener(details) {",
// A subframe should have been the one to have the error.
@" browser.test.assertFalse(details.frameId == 0)",
@" browser.test.assertEq(details.parentFrameId, 0)",
// Get more information about the frame to verify the errorOccurred bit was set.
@" browser.webNavigation.getFrame({ tabId: details.tabId, frameId: details.frameId }, function(frame) {",
@" browser.test.assertEq(frame.parentFrameId, 0)",
@" browser.test.assertTrue(frame.errorOccurred)",
// One thing to note here is that if the provisional load fails, there won't be a URL in the details.
@" browser.test.assertEq(frame.url, '')",
@" browser.test.notifyPass()",
@" })",
@"}",
// The passListener firing will consider the test passed.
@"browser.webNavigation.onErrorOccurred.addListener(errorListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, Errors)
{
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(@[
@"async function completedListener(details) {",
// Only listen for when the main frame loads so we don't call this method more than once.
@" if (details.frameId !== 0)",
@" return",
@" const activeTab = await browser.tabs.query({ active: true })",
// Make sure invalid tab/frame IDs vend an error message - use arbitrary frame and tabIds.
@" await browser.test.assertRejects(browser.webNavigation.getFrame({tabId: (details.tabId + 1), frameId: 0}), /tab not found/i)",
@" await browser.test.assertRejects(browser.webNavigation.getFrame({tabId: details.tabId, frameId: 42}), /frame not found/i)",
@" browser.test.notifyPass()",
@"}",
// The passListener firing will consider the test passed.
@"browser.webNavigation.onCompleted.addListener(completedListener)",
@"browser.test.sendMessage('Load Tab')"
]);
auto manager = Util::loadExtension(webNavigationManifest, @{ @"background.js": backgroundScript });
[manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation];
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(WKWebExtensionAPIWebNavigation, URLFilterTestMatchAllPredicates)
{
NSString *errorString = nil;
NSDictionary *filterDictionary = @{
@"url": @[
@{
@"schemes": @[ @"https" ],
@"hostEquals": @"apple.com",
}
]
};
_WKWebExtensionWebNavigationURLFilter *filter = [[_WKWebExtensionWebNavigationURLFilter alloc] initWithDictionary:filterDictionary outErrorMessage:&errorString];
EXPECT_NULL(errorString);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"https://apple.com"]]);
EXPECT_FALSE([filter matchesURL:[NSURL URLWithString:@"http://apple.com"]]);
EXPECT_FALSE([filter matchesURL:[NSURL URLWithString:@"https://example.com"]]);
[filter release];
}
TEST(WKWebExtensionAPIWebNavigation, URLFilterMatchesOnePredicate)
{
NSString *errorString = nil;
NSDictionary *filterDictionary = @{
@"url": @[
@{ @"hostEquals": @"apple.com" },
@{ @"hostEquals": @"example.com" },
]
};
_WKWebExtensionWebNavigationURLFilter *filter = [[_WKWebExtensionWebNavigationURLFilter alloc] initWithDictionary:filterDictionary outErrorMessage:&errorString];
EXPECT_NULL(errorString);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"http://apple.com"]]);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"http://example.com"]]);
EXPECT_FALSE([filter matchesURL:[NSURL URLWithString:@"about:blank"]]);
EXPECT_FALSE([filter matchesURL:[NSURL URLWithString:@"file:///dev/null"]]);
[filter release];
}
TEST(WKWebExtensionAPIWebNavigation, EmptyFilterMatchesEverything)
{
NSString *errorString = nil;
NSDictionary *filterDictionary = @{
@"url": @[ ]
};
_WKWebExtensionWebNavigationURLFilter *filter = [[_WKWebExtensionWebNavigationURLFilter alloc] initWithDictionary:filterDictionary outErrorMessage:&errorString];
EXPECT_NULL(errorString);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"about:blank"]]);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"http://example.com"]]);
EXPECT_TRUE([filter matchesURL:[NSURL URLWithString:@"file:///dev/null"]]);
[filter release];
}
TEST(WKWebExtensionAPIWebNavigation, URLKeyTypeChecking)
{
__auto_type test = ^(NSDictionary *inputDictionary, NSString *expectedError) {
_WKWebExtensionWebNavigationURLFilter *filter;
NSString *error = nil;
filter = [[_WKWebExtensionWebNavigationURLFilter alloc] initWithDictionary:inputDictionary outErrorMessage:&error];
if (expectedError) {
EXPECT_NS_EQUAL(error, expectedError);
EXPECT_NULL(filter);
} else {
EXPECT_NULL(error);
EXPECT_NOT_NULL(filter);
}
[filter release];
};
test(@{ }, @"The 'filters' value is invalid, because it is missing required keys: 'url'.");
test(@{ @"a": @"b" }, @"The 'filters' value is invalid, because it is missing required keys: 'url'.");
test(@{ @"a": @"b", @"url": @[ ] }, nil);
test(@{ @"url": [NSNull null] }, @"The 'filters' value is invalid, because 'url' is expected to be an array of objects, but null was provided.");
test(@{ @"url": @[ ] }, nil);
test(@{ @"url": @[ @"A" ] }, @"The 'filters' value is invalid, because 'url' is expected to be an array of objects, but a string was provided in the array.");
test(@{ @"url": @[ @[ ] ] }, @"The 'filters' value is invalid, because 'url' is expected to be an array of objects, but an array was provided in the array.");
test(@{ @"url": @[ @{ } ] }, nil);
}
} // namespace TestWebKitAPI
#endif // ENABLE(WK_WEB_EXTENSIONS)