| /* |
| * Copyright (C) 2023-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 "WebExtensionUtilities.h" |
| #import <WebKit/WKWebExtensionCommand.h> |
| #import <wtf/darwin/DispatchExtras.h> |
| |
| namespace TestWebKitAPI { |
| |
| NSString * const NSHTTPCookieHTTPOnly = @"HttpOnly"; |
| |
| static auto *cookiesManifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Test Cookies", |
| @"description": @"Test Cookies", |
| @"version": @"1.0", |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"permissions": @[ @"cookies" ], |
| }; |
| |
| TEST(WKWebExtensionAPICookies, ErrorsRead) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: 123, name: 'Test' }), /'url' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: '', name: 'Test' }), /'url' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: 'bad', name: 'Test' }), /'url' value is invalid, because 'bad' is not a valid URL/i)", |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: 'http://example.com', name: 123 }), /'name' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: 'http://example.com', name: '' }), /'name' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.get({ url: 'http://example.com', storeId: 123 }), /'storeId' is expected to be a string, but a number was provided/i)", |
| |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ url: 123 }), /'url' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ url: '' }), /'url' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ url: 'bad' }), /'url' value is invalid, because 'bad' is not a valid URL/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ domain: 123 }), /'domain' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ name: 123 }), /'name' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ path: 123 }), /'path' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ secure: 'bad' }), /'secure' is expected to be a boolean, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ session: 'bad' }), /'session' is expected to be a boolean, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.getAll({ storeId: 123 }), /'storeId' is expected to be a string, but a number was provided/i)", |
| |
| @"browser.test.notifyPass()", |
| ]); |
| |
| Util::loadAndRunExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPICookies, ErrorsWrite) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 123 }), /'url' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: '' }), /'url' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'bad' }), /'url' value is invalid, because 'bad' is not a valid URL/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', name: 123 }), /'name' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', value: 123 }), /'value' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', domain: 123 }), /'domain' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', path: 123 }), /'path' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', secure: 'bad' }), /'secure' is expected to be a boolean, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', httpOnly: 'bad' }), /'httpOnly' is expected to be a boolean, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', sameSite: 123 }), /'sameSite' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', expirationDate: 'bad' }), /'expirationDate' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.set({ url: 'http://example.com', storeId: 123 }), /'storeId' is expected to be a string, but a number was provided/i)", |
| |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: 123 }), /'url' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: '', name: 'Test' }), /'url' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: 'bad', name: 'Test' }), /'url' value is invalid, because 'bad' is not a valid URL/i)", |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: 'http://example.com', name: 123 }), /'name' is expected to be a string, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: 'http://example.com', name: '' }), /'name' value is invalid, because it must not be empty/i)", |
| @"browser.test.assertThrows(() => browser.cookies.remove({ url: 'http://example.com', storeId: 123 }), /'storeId' is expected to be a string, but a number was provided/i)", |
| |
| @"browser.test.notifyPass()", |
| ]); |
| |
| Util::loadAndRunExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAllCookieStores) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const cookieStores = await browser.test.assertSafeResolve(() => browser.cookies.getAllCookieStores())", |
| @"browser.test.assertEq(cookieStores?.length, 1, 'Should have one cookie store')", |
| |
| @"const defaultStore = cookieStores?.find(store => !store.incognito)", |
| @"const privateStore = cookieStores?.find(store => store.incognito)", |
| |
| @"browser.test.assertEq(typeof defaultStore?.id, 'string', 'Default store id should be a string')", |
| @"browser.test.assertTrue(Array.isArray(defaultStore?.tabIds), 'Default store tabIds should be an array')", |
| @"browser.test.assertEq(defaultStore?.tabIds?.length, 1, 'Default store should have one tabId')", |
| @"browser.test.assertEq(defaultStore?.incognito, false, 'Default store should not be incognito')", |
| |
| @"browser.test.assertEq(privateStore, undefined, 'Private store should be undefined')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| [manager openNewWindowUsingPrivateBrowsing:YES]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAllCookieStoresWithPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const cookieStores = await browser.test.assertSafeResolve(() => browser.cookies.getAllCookieStores())", |
| @"browser.test.assertEq(cookieStores?.length, 2, 'Should have two cookie stores')", |
| |
| @"const defaultStore = cookieStores?.find(store => !store.incognito)", |
| @"const privateStore = cookieStores?.find(store => store.incognito)", |
| |
| @"browser.test.assertEq(typeof defaultStore?.id, 'string', 'Default store id should be a string')", |
| @"browser.test.assertTrue(Array.isArray(defaultStore?.tabIds), 'Default store tabIds should be an array')", |
| @"browser.test.assertEq(defaultStore?.tabIds?.length, 1, 'Default store should have one tabId')", |
| @"browser.test.assertEq(defaultStore?.incognito, false, 'Default store should not be incognito')", |
| |
| @"browser.test.assertEq(typeof privateStore?.id, 'string', 'Private store id should be a string')", |
| @"browser.test.assertTrue(Array.isArray(privateStore?.tabIds), 'Private store tabIds should be an array')", |
| @"browser.test.assertEq(privateStore?.tabIds.length, 1, 'Private store should have one tabId')", |
| @"browser.test.assertEq(privateStore?.incognito, true, 'Private store should be incognito')", |
| |
| @"browser.test.assertTrue(defaultStore?.id !== privateStore?.id, 'The store ids should be different')", |
| @"browser.test.assertTrue(defaultStore?.tabIds[0] !== privateStore?.tabIds[0], 'The tabIds for each store should be different')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.hasAccessToPrivateData = YES; |
| |
| [manager openNewWindowUsingPrivateBrowsing:YES]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAll) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const cookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ }))", |
| @"browser.test.assertTrue(Array.isArray(cookies), 'Cookies should be an array')", |
| @"browser.test.assertTrue(cookies?.length >= 2, 'There should be 2 or more cookies')", |
| |
| @"const cookie1 = cookies.find(cookie => cookie.name === 'testCookie1')", |
| @"browser.test.assertEq(typeof cookie1, 'object')", |
| @"browser.test.assertEq(cookie1?.value, 'value1')", |
| @"browser.test.assertEq(cookie1?.domain, '.example.com')", |
| @"browser.test.assertEq(cookie1?.path, '/')", |
| @"browser.test.assertEq(cookie1?.secure, false)", |
| |
| @"const cookie2 = cookies.find(cookie => cookie.name === 'testCookie2')", |
| @"browser.test.assertEq(typeof cookie2, 'object')", |
| @"browser.test.assertEq(cookie2?.value, 'value2')", |
| @"browser.test.assertEq(cookie2?.domain, 'www.example.com')", |
| @"browser.test.assertEq(cookie2?.path, '/test')", |
| @"browser.test.assertEq(cookie2?.secure, true)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *cookie1 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie1", |
| NSHTTPCookieValue: @"value1", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| auto *cookie2 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie2", |
| NSHTTPCookieValue: @"value2", |
| NSHTTPCookieDomain: @"www.example.com", |
| NSHTTPCookiePath: @"/test", |
| NSHTTPCookieSecure: @YES |
| }]; |
| |
| auto cookieGroup = dispatch_group_create(); |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie1 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie2 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| __block bool done = false; |
| dispatch_group_notify(cookieGroup, mainDispatchQueueSingleton(), ^{ |
| done = true; |
| }); |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAllIncognito) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const allCookieStores = await browser.test.assertSafeResolve(() => browser.cookies.getAllCookieStores())", |
| @"const incognitoStore = allCookieStores.find(store => store.incognito)", |
| @"browser.test.assertEq(incognitoStore, undefined, 'Incognito store should not be found')", |
| |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-1' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-2' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-3' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-4' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-5' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-6' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-7' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-8' }), /cookie store not found/i)", |
| @"await browser.test.assertRejects(browser.cookies.getAll({ storeId: 'ephemeral-9' }), /cookie store not found/i)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.private.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES]; |
| auto *cookieStore = privateWindow.activeTab.webView.configuration.websiteDataStore.httpCookieStore; |
| |
| auto *cookie1 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie1", |
| NSHTTPCookieValue: @"value1", |
| NSHTTPCookieDomain: @".private.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| auto *cookie2 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie2", |
| NSHTTPCookieValue: @"value2", |
| NSHTTPCookieDomain: @"www.private.com", |
| NSHTTPCookiePath: @"/test", |
| NSHTTPCookieSecure: @YES |
| }]; |
| |
| auto cookieGroup = dispatch_group_create(); |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie1 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie2 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| __block bool done = false; |
| dispatch_group_notify(cookieGroup, mainDispatchQueueSingleton(), ^{ |
| done = true; |
| }); |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAllIncognitoWithPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const allCookieStores = await browser.test.assertSafeResolve(() => browser.cookies.getAllCookieStores())", |
| @"const incognitoStore = allCookieStores.find(store => store.incognito)", |
| @"browser.test.assertEq(typeof incognitoStore, 'object', 'Incognito store should be found')", |
| |
| @"const cookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ storeId: incognitoStore?.id }))", |
| @"browser.test.assertTrue(Array.isArray(cookies), 'Cookies should be an array')", |
| @"browser.test.assertEq(cookies.length, 2)", |
| |
| @"const cookie1 = cookies.find(cookie => cookie.name === 'testCookie1')", |
| @"browser.test.assertEq(typeof cookie1, 'object')", |
| @"browser.test.assertEq(cookie1?.value, 'value1')", |
| @"browser.test.assertEq(cookie1?.domain, '.private.com')", |
| @"browser.test.assertEq(cookie1?.path, '/')", |
| @"browser.test.assertEq(cookie1?.secure, false)", |
| |
| @"const cookie2 = cookies.find(cookie => cookie.name === 'testCookie2')", |
| @"browser.test.assertEq(typeof cookie2, 'object')", |
| @"browser.test.assertEq(cookie2?.value, 'value2')", |
| @"browser.test.assertEq(cookie2?.domain, 'www.private.com')", |
| @"browser.test.assertEq(cookie2?.path, '/test')", |
| @"browser.test.assertEq(cookie2?.secure, true)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.private.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| manager.get().context.hasAccessToPrivateData = YES; |
| |
| auto *privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES]; |
| auto *cookieStore = privateWindow.activeTab.webView.configuration.websiteDataStore.httpCookieStore; |
| |
| auto *cookie1 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie1", |
| NSHTTPCookieValue: @"value1", |
| NSHTTPCookieDomain: @".private.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| auto *cookie2 = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie2", |
| NSHTTPCookieValue: @"value2", |
| NSHTTPCookieDomain: @"www.private.com", |
| NSHTTPCookiePath: @"/test", |
| NSHTTPCookieSecure: @YES |
| }]; |
| |
| auto cookieGroup = dispatch_group_create(); |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie1 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:cookie2 completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| __block bool done = false; |
| dispatch_group_notify(cookieGroup, mainDispatchQueueSingleton(), ^{ |
| done = true; |
| }); |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetAllWithFilters) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const nameCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ name: 'nameCookie' }))", |
| @"browser.test.assertEq(nameCookies?.length, 1, 'Should find one cookie with name `nameCookie`')", |
| @"browser.test.assertEq(nameCookies?.[0]?.value, 'nameValue', 'Value of `nameCookie` should be `nameValue`')", |
| |
| @"const sessionCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ session: true }))", |
| @"browser.test.assertEq(sessionCookies?.length, 1, 'Should find one session cookie')", |
| @"browser.test.assertEq(sessionCookies?.[0]?.name, 'sessionCookie', 'Name of session cookie should be `sessionCookie`')", |
| |
| @"const secureCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ secure: true }))", |
| @"browser.test.assertEq(secureCookies?.length, 1, 'Should find one secure cookie')", |
| @"browser.test.assertEq(secureCookies?.[0]?.name, 'secureCookie', 'Name of secure cookie should be `secureCookie`')", |
| |
| @"const pathCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ path: '/testPath' }))", |
| @"browser.test.assertEq(pathCookies?.length, 1, 'Should find one cookie with path `/testPath`')", |
| @"browser.test.assertEq(pathCookies?.[0]?.name, 'pathCookie', 'Name of path cookie should be `pathCookie`')", |
| |
| @"const domainCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ domain: '.example.com' }))", |
| @"browser.test.assertEq(domainCookies?.length, 4, 'Should find four cookies for domain `.example.com`')", |
| |
| @"const urlCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ url: 'http://www.example.com/testPath' }))", |
| @"browser.test.assertEq(urlCookies?.length, 3, 'Should find three cookies for URL `http://www.example.com/testPath`')", |
| |
| @"const nameAndSecureCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ name: 'secureCookie', secure: true }))", |
| @"browser.test.assertEq(nameAndSecureCookies.length, 1, 'Should find one cookie with name `secureCookie` and secure attribute')", |
| @"browser.test.assertEq(nameAndSecureCookies[0].value, 'secureValue', 'Value of secure cookie should be `secureValue`')", |
| |
| @"const domainAndPathCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ domain: 'www.example.com', path: '/testPath' }))", |
| @"browser.test.assertEq(domainAndPathCookies.length, 1, 'Should find one cookie for domain `www.example.com` and path `/testPath`')", |
| @"browser.test.assertEq(domainAndPathCookies[0].name, 'pathCookie', 'Name of cookie should be `pathCookie`')", |
| |
| @"const allCookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ }))", |
| @"browser.test.assertEq(allCookies?.length, 4, 'Should find all cookies')", |
| @"const expectedCookieNames = ['nameCookie', 'sessionCookie', 'secureCookie', 'pathCookie']", |
| @"expectedCookieNames.forEach(name => {", |
| @" browser.test.assertTrue(allCookies.some(cookie => cookie.name === name), `Should find cookie with name ${name}`)", |
| @"})", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *nameCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"nameCookie", |
| NSHTTPCookieValue: @"nameValue", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/", |
| NSHTTPCookieExpires: [NSDate dateWithTimeIntervalSinceNow:30] |
| }]; |
| |
| auto *sessionCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"sessionCookie", |
| NSHTTPCookieValue: @"sessionValue", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/", |
| NSHTTPCookieHTTPOnly: @YES |
| }]; |
| |
| auto *secureCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"secureCookie", |
| NSHTTPCookieValue: @"secureValue", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/", |
| NSHTTPCookieSecure: @YES, |
| NSHTTPCookieExpires: [NSDate dateWithTimeIntervalSinceNow:30] |
| }]; |
| |
| auto *pathCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"pathCookie", |
| NSHTTPCookieValue: @"pathValue", |
| NSHTTPCookieDomain: @"www.example.com", |
| NSHTTPCookiePath: @"/testPath", |
| NSHTTPCookieExpires: [NSDate dateWithTimeIntervalSinceNow:30], |
| NSHTTPCookieHTTPOnly: @YES |
| }]; |
| |
| auto cookieGroup = dispatch_group_create(); |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:nameCookie completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:sessionCookie completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:secureCookie completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| dispatch_group_enter(cookieGroup); |
| [cookieStore setCookie:pathCookie completionHandler:^{ |
| dispatch_group_leave(cookieGroup); |
| }]; |
| |
| __block bool done = false; |
| dispatch_group_notify(cookieGroup, mainDispatchQueueSingleton(), ^{ |
| done = true; |
| }); |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, RemoveCookie) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const removedCookie = await browser.test.assertSafeResolve(() => browser.cookies.remove({ url: 'http://www.example.com/', name: 'testCookie' }))", |
| @"browser.test.assertEq(removedCookie?.name, 'testCookie', 'Removed cookie name should be `testCookie`')", |
| @"browser.test.assertEq(removedCookie?.value, 'value1', 'Removed cookie value should be `value1`')", |
| @"browser.test.assertEq(removedCookie?.domain, 'www.example.com', 'Removed cookie domain should be `www.example.com`')", |
| @"browser.test.assertEq(removedCookie?.path, '/', 'Removed cookie path should be `/`')", |
| |
| @"const cookies = await browser.test.assertSafeResolve(() => browser.cookies.getAll({ }))", |
| @"const cookieExists = cookies.some(cookie => cookie.name === 'testCookie')", |
| @"browser.test.assertFalse(cookieExists, 'testCookie should be removed')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *testCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie", |
| NSHTTPCookieValue: @"value1", |
| NSHTTPCookieDomain: @"www.example.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| __block bool done = false; |
| [cookieStore setCookie:testCookie completionHandler:^{ |
| done = true; |
| }]; |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, RemoveCookieNotFound) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const removedCookie = await browser.test.assertSafeResolve(() => browser.cookies.remove({ url: 'http://www.example.com/', name: 'notFoundName' }))", |
| @"browser.test.assertEq(removedCookie, null)", |
| |
| @"const testCookie = await browser.test.assertSafeResolve(() => browser.cookies.get({ url: 'http://www.example.com/', name: 'testCookie' }))", |
| @"browser.test.assertEq(typeof testCookie, 'object', 'testCookie should still exist')", |
| @"browser.test.assertEq(testCookie?.value, 'value1', 'testCookie should have the correct value')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *testCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testCookie", |
| NSHTTPCookieValue: @"value1", |
| NSHTTPCookieDomain: @"www.example.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| __block bool done = false; |
| [cookieStore setCookie:testCookie completionHandler:^{ |
| done = true; |
| }]; |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, SetCookie) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const initialCookies = await browser.cookies.getAll({ url: 'http://www.example.com/' })", |
| @"browser.test.assertTrue(!initialCookies.some(cookie => cookie.name === 'testCookie1' || cookie.name === 'testCookie2'), 'Cookies should not exist initially')", |
| @"browser.test.assertTrue(initialCookies.some(cookie => cookie.name === 'replacedCookie'), 'Replaced cookie should exist')", |
| |
| @"await browser.test.assertSafeResolve(() => browser.cookies.set({ url: 'http://www.example.com/', name: 'testCookie1', value: 'value1' }))", |
| @"await browser.test.assertSafeResolve(() => browser.cookies.set({ url: 'http://example.com/', name: 'testCookie2', value: 'value2', path: '/', domain: '.example.com' }))", |
| |
| @"const cookies = await browser.cookies.getAll({ url: 'http://www.example.com/' })", |
| @"const cookie1 = cookies.find(cookie => cookie.name === 'testCookie1')", |
| @"const cookie2 = cookies.find(cookie => cookie.name === 'testCookie2')", |
| @"browser.test.assertEq(cookie1?.value, 'value1', 'testCookie1 should have the correct value')", |
| @"browser.test.assertEq(cookie2?.value, 'value2', 'testCookie2 should have the correct value')", |
| |
| @"const replacedCookie = await browser.test.assertSafeResolve(() => browser.cookies.set({ url: 'http://www.example.com/', name: 'replacedCookie', value: 'newValue' }))", |
| @"browser.test.assertEq(replacedCookie?.value, 'newValue', 'replacedCookie should have the new value')", |
| @"browser.test.assertEq(replacedCookie?.domain, 'www.example.com', 'replacedCookie should have the new domain')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *testCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"replacedCookie", |
| NSHTTPCookieValue: @"originalValue", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| __block bool done = false; |
| [cookieStore setCookie:testCookie completionHandler:^{ |
| done = true; |
| }]; |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, SetCookieWithExpirationDate) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const cookieName = 'token'", |
| @"const testUrl = 'http://www.example.com/'", |
| @"const expirationDate = Math.floor(Date.now() / 1000) + 2", |
| |
| @"await browser.cookies.set({", |
| @" url: testUrl,", |
| @" name: cookieName,", |
| @" value: 'value',", |
| @" domain: 'www.example.com',", |
| @" sameSite: 'lax',", |
| @" expirationDate", |
| @"})", |
| |
| @"let cookie = await browser.cookies.get({", |
| @" url: testUrl,", |
| @" name: cookieName", |
| @"})", |
| |
| @"browser.test.assertEq(cookie?.value, 'value', 'Cookie should be set successfully')", |
| @"browser.test.assertEq(cookie?.expirationDate, expirationDate, 'expirationDate should be the same as it was set')", |
| |
| @"await new Promise(resolve => setTimeout(resolve, 2500))", |
| |
| @"cookie = await browser.cookies.get({", |
| @" url: testUrl,", |
| @" name: cookieName", |
| @"})", |
| |
| @"browser.test.assertEq(cookie, null, 'Cookie should be expired and no longer available')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, GetCookie) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const cookie = await browser.test.assertSafeResolve(() => browser.cookies.get({ url: 'http://www.example.com/', name: 'testGetCookie' }))", |
| @"browser.test.assertEq(cookie?.name, 'testGetCookie', 'The cookie name should match')", |
| @"browser.test.assertEq(cookie?.value, 'getValue', 'The cookie value should match')", |
| @"browser.test.assertEq(cookie?.domain, '.example.com', 'The cookie domain should match')", |
| @"browser.test.assertEq(cookie?.path, '/', 'The cookie path should match')", |
| |
| @"const notFoundCookie = await browser.test.assertSafeResolve(() => browser.cookies.get({ url: 'http://www.example.com/', name: 'notFoundName' }))", |
| @"browser.test.assertEq(notFoundCookie, null)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| auto *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; |
| |
| auto *testCookie = [NSHTTPCookie cookieWithProperties:@{ |
| NSHTTPCookieName: @"testGetCookie", |
| NSHTTPCookieValue: @"getValue", |
| NSHTTPCookieDomain: @".example.com", |
| NSHTTPCookiePath: @"/" |
| }]; |
| |
| __block bool done = false; |
| [cookieStore setCookie:testCookie completionHandler:^{ |
| done = true; |
| }]; |
| |
| Util::run(&done); |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPICookies, ChangedEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.cookies.onChanged.addListener(() => {", |
| @" browser.test.notifyPass()", |
| @"})", |
| |
| @"await browser.test.assertSafeResolve(() => browser.cookies.set({ url: 'http://www.example.com/', name: 'testCookie', value: 'testValue' }))" |
| ]); |
| |
| auto manager = Util::loadExtension(cookiesManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *matchPattern = [WKWebExtensionMatchPattern matchPatternWithString:@"*://*.example.com/*"]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forMatchPattern:matchPattern]; |
| |
| [manager run]; |
| } |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WK_WEB_EXTENSIONS) |