blob: 2211f41faa3a9d6a1a8616e4f3f35e29d6a46a12 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022-2023 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 "TestCocoa.h"
#import "TestWKWebView.h"
#import "TestWebExtensionsDelegate.h"
#import "WebExtensionUtilities.h"
#import <WebCore/UserGestureIndicator.h>
#import <WebKit/WKFoundation.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
#import <WebKit/_WKWebExtensionContextPrivate.h>
#import <WebKit/_WKWebExtensionControllerDelegate.h>
namespace TestWebKitAPI {
static void runScriptWithUserGesture(const String& script, WKWebView *backgroundWebView)
{
bool callbackComplete = false;
RetainPtr<id> evalResult;
[backgroundWebView callAsyncJavaScript:script arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
evalResult = result;
callbackComplete = true;
EXPECT_TRUE(!error);
if (error)
NSLog(@"Encountered error: %@ while evaluating script: %@", error, static_cast<NSString *>(script));
}];
TestWebKitAPI::Util::run(&callbackComplete);
}
TEST(WKWebExtensionAPIPermissions, PermissionsTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"alarms", @"activeTab" ],
@"optional_permissions": @[ @"webNavigation", @"cookies" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// contains() should return true for all named permissions and granted match patterns.
@"browser.test.assertTrue(await browser.permissions.contains({'permissions': ['alarms', 'activeTab']}))",
@"browser.test.assertTrue(await browser.permissions.contains({'origins': ['*://webkit.org/*']}))",
// contains() should return false for non granted match patterns.
@"browser.test.assertFalse(await browser.permissions.contains({'origins': ['*://apple.com/*']}))",
// Removing optional permissions should pass.
@"browser.test.assertTrue(await browser.permissions.remove({'permissions': ['cookies']}))",
// Removing functional permissions should fail.
@"browser.test.assertRejects(browser.permissions.remove({'permissions': ['alarms']}))",
@"browser.test.assertRejects(browser.permissions.remove({'permissions': ['alarms', 'activeTab']}))",
@"browser.test.assertRejects(browser.permissions.remove({'permissions': ['cookies', 'activeTab']}))",
@"browser.test.assertRejects(browser.permissions.remove({'permissions': ['scripting']}))",
// getALL() should return all named permissions and granted match patterns.
@"let permissions = {'origins': ['*://webkit.org/*'], 'permissions': ['alarms', 'activeTab']}",
@"const result = await browser.permissions.getAll()",
@"browser.test.assertDeepEq(result, permissions)",
// Finish
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
// Grant "permissions" in the manifest.
for (NSString *permission in extension.get().requestedPermissions)
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:permission];
// Grant extension access to webkit.org.
_WKWebExtensionMatchPattern *matchPattern = [_WKWebExtensionMatchPattern matchPatternWithString:@"*://webkit.org/*"];
[manager.get().context setPermissionStatus:_WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern];
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
[manager loadAndRun];
}
TEST(WKWebExtensionAPIPermissions, AcceptPermissionsRequestTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"permissions": @[ @"alarms", @"activeTab" ],
@"optional_permissions": @[ @"webNavigation", @"cookies", @"declarativeNetRequest", @"*://*.apple.com/*" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// Finish
@"browser.test.yield()",
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
NSSet<NSString *> *permissions = [NSSet setWithArray:@[ @"declarativeNetRequest" ]];
NSSet<_WKWebExtensionMatchPattern *> *matchPatterns = [NSSet setWithArray:@[ [_WKWebExtensionMatchPattern matchPatternWithString:@"*://*.apple.com/*" ]]];
auto requestDelegate = adoptNS([[TestWebExtensionsDelegate alloc] init]);
__block bool requestComplete = false;
// Implement the delegate methods that're called when a call to permissions.request() is made.
requestDelegate.get().promptForPermissions = ^(id<_WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *)) {
EXPECT_EQ(requestedPermissions.count, permissions.count);
EXPECT_TRUE([requestedPermissions isEqualToSet:permissions]);
callback(requestedPermissions);
};
requestDelegate.get().promptForPermissionMatchPatterns = ^(id<_WKWebExtensionTab> tab, NSSet<_WKWebExtensionMatchPattern *> *requestedMatchPatterns, void (^callback)(NSSet<_WKWebExtensionMatchPattern *> *)) {
EXPECT_EQ(requestedMatchPatterns.count, matchPatterns.count);
EXPECT_TRUE([requestedMatchPatterns isEqualToSet:matchPatterns]);
callback(requestedMatchPatterns);
requestComplete = true;
};
manager.get().controllerDelegate = requestDelegate.get();
[manager loadAndRun];
runScriptWithUserGesture("await browser.test.assertTrue(await browser.permissions.request({'permissions': ['declarativeNetRequest'], 'origins': ['*://*.apple.com/*']}))"_s, manager.get().context._backgroundWebView);
TestWebKitAPI::Util::run(&requestComplete);
}
TEST(WKWebExtensionAPIPermissions, DenyPermissionsRequestTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"optional_permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"optional_host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// Finish
@"browser.test.yield()",
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
auto requestDelegate = adoptNS([[TestWebExtensionsDelegate alloc] init]);
__block bool requestComplete = false;
// Implement the delegate methods, but don't grant the permissions.
requestDelegate.get().promptForPermissions = ^(id<_WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *)) {
requestComplete = true;
callback(nil);
};
requestDelegate.get().promptForPermissionMatchPatterns = ^(id<_WKWebExtensionTab> tab, NSSet<_WKWebExtensionMatchPattern *> *requestedMatchPatterns, void (^callback)(NSSet<_WKWebExtensionMatchPattern *> *)) {
ASSERT_NOT_REACHED();
callback(nil);
};
manager.get().controllerDelegate = requestDelegate.get();
[manager loadAndRun];
runScriptWithUserGesture("await browser.test.assertFalse(await browser.permissions.request({'permissions': ['declarativeNetRequest'], 'origins': ['*://*.apple.com/*']}))"_s, manager.get().context._backgroundWebView);
TestWebKitAPI::Util::run(&requestComplete);
}
TEST(WKWebExtensionAPIPermissions, AcceptPermissionsDenyMatchPatternsRequestTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"optional_permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"optional_host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// Finish
@"browser.test.yield()",
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
auto requestDelegate = adoptNS([[TestWebExtensionsDelegate alloc] init]);
__block bool requestComplete = false;
// Grant the requested permissions.
requestDelegate.get().promptForPermissions = ^(id<_WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *)) {
callback(requestedPermissions);
};
// Deny the requested match patterns.
requestDelegate.get().promptForPermissionMatchPatterns = ^(id<_WKWebExtensionTab> tab, NSSet<_WKWebExtensionMatchPattern *> *requestedMatchPatterns, void (^callback)(NSSet<_WKWebExtensionMatchPattern *> *)) {
requestComplete = true;
callback(nil);
};
manager.get().controllerDelegate = requestDelegate.get();
[manager loadAndRun];
runScriptWithUserGesture("await browser.test.assertFalse(await browser.permissions.request({'permissions': ['declarativeNetRequest'], 'origins': ['*://*.apple.com/*']}))"_s, manager.get().context._backgroundWebView);
TestWebKitAPI::Util::run(&requestComplete);
}
TEST(WKWebExtensionAPIPermissions, RequestPermissionsOnlyTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"optional_permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"optional_host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// Finish
@"browser.test.yield()",
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
auto requestDelegate = adoptNS([[TestWebExtensionsDelegate alloc] init]);
__block bool requestComplete = false;
// Grant the requested permissions.
requestDelegate.get().promptForPermissions = ^(id<_WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *)) {
callback(requestedPermissions);
};
// Grant the requested match patterns.
requestDelegate.get().promptForPermissionMatchPatterns = ^(id<_WKWebExtensionTab> tab, NSSet<_WKWebExtensionMatchPattern *> *requestedMatchPatterns, void (^callback)(NSSet<_WKWebExtensionMatchPattern *> *)) {
requestComplete = true;
callback(requestedMatchPatterns);
};
manager.get().controllerDelegate = requestDelegate.get();
[manager loadAndRun];
runScriptWithUserGesture("await browser.test.assertTrue(await browser.permissions.request({'permissions': ['declarativeNetRequest']}))"_s, manager.get().context._backgroundWebView);
TestWebKitAPI::Util::run(&requestComplete);
}
TEST(WKWebExtensionAPIPermissions, RequestMatchPatternsOnlyTest)
{
auto *manifest = @{
@"manifest_version": @3,
@"optional_permissions": @[ @"declarativeNetRequest" ],
@"background": @{ @"scripts": @[ @"background.js" ], @"type": @"module", @"persistent": @NO },
@"optional_host_permissions": @[ @"*://*.apple.com/*" ]
};
auto *backgroundScript = Util::constructScript(@[
// Finish
@"browser.test.yield()",
@"browser.test.notifyPass()"
]);
auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:manifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get()._webExtensionController = manager.get().controller;
auto requestDelegate = adoptNS([[TestWebExtensionsDelegate alloc] init]);
__block bool requestComplete = false;
// Grant the requested permissions.
requestDelegate.get().promptForPermissions = ^(id<_WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *)) {
callback(requestedPermissions);
};
// Grant the requested match patterns.
requestDelegate.get().promptForPermissionMatchPatterns = ^(id<_WKWebExtensionTab> tab, NSSet<_WKWebExtensionMatchPattern *> *requestedMatchPatterns, void (^callback)(NSSet<_WKWebExtensionMatchPattern *> *)) {
requestComplete = true;
callback(requestedMatchPatterns);
};
manager.get().controllerDelegate = requestDelegate.get();
[manager loadAndRun];
runScriptWithUserGesture("await browser.test.assertTrue(await browser.permissions.request({'origins': ['*://*.apple.com/*']}))"_s, manager.get().context._backgroundWebView);
TestWebKitAPI::Util::run(&requestComplete);
}
} // namespace TestWebKitAPI
#endif // ENABLE(WK_WEB_EXTENSIONS)