| /* |
| * 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 "HTTPServer.h" |
| #import "TestCocoaImageAndCocoaColor.h" |
| #import "TestWebExtensionsDelegate.h" |
| #import "WebExtensionUtilities.h" |
| |
| #if USE(APPKIT) |
| #import "AppKitSPI.h" |
| #endif |
| |
| namespace TestWebKitAPI { |
| |
| static auto *actionPopupManifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Action Test", |
| @"description": @"Action Test", |
| @"version": @"1", |
| |
| @"permissions": @[ @"webNavigation" ], |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"action": @{ |
| @"default_title": @"Test Action", |
| @"default_popup": @"popup.html", |
| @"default_icon": @{ |
| @"16": @"toolbar-16.png", |
| @"32": @"toolbar-32.png", |
| }, |
| }, |
| }; |
| |
| TEST(WKWebExtensionAPIAction, Errors) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertThrows(() => browser.action.setTitle({tabId: 'bad', title: 'Test'}), /'tabId' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setTitle({windowId: 'bad', title: 'Test'}), /'windowId' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setIcon({path: 123}), /'path' is expected to be a string or an object or null, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setIcon({tabId: true, path: 'icon.png'}), /'tabId' is expected to be a number, but a boolean was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setIcon({windowId: true, path: 'icon.png'}), /'windowId' is expected to be a number, but a boolean was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setPopup({tabId: 'bad', popup: 'popup.html'}), /'tabId' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setPopup({windowId: 'bad', popup: 'popup.html'}), /'windowId' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.setBadgeText({tabId: 1.2, text: '1'}), /'tabId' value is invalid, because it is not a tab identifier/i)", |
| @"browser.test.assertThrows(() => browser.action.setBadgeText({windowId: -3, text: '2'}), /'windowId' value is invalid, because it is not a window identifier/i)", |
| @"browser.test.assertThrows(() => browser.action.enable('bad'), /'tabId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.action.disable('bad'), /'tabId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.action.isEnabled({tabId: Infinity}), /'tabId' is expected to be a number, but Infinity was provided/i)", |
| @"browser.test.assertThrows(() => browser.action.isEnabled({windowId: NaN}), /'windowId' is expected to be a number, but NaN was provided/i)", |
| |
| @"browser.test.assertThrows(() => browser.action.setTitle({tabId: 1}), /'details' value is invalid, because it is missing required keys: 'title'/i)", |
| @"browser.test.assertThrows(() => browser.action.setTitle({windowId: 1}), /'details' value is invalid, because it is missing required keys: 'title'/i)", |
| @"browser.test.assertThrows(() => browser.action.setPopup({tabId: 1}), /'details' value is invalid, because it is missing required keys: 'popup'/i)", |
| @"browser.test.assertThrows(() => browser.action.setPopup({windowId: 1}), /'details' value is invalid, because it is missing required keys: 'popup'/i)", |
| @"browser.test.assertThrows(() => browser.action.setBadgeText({tabId: 1}), /'details' value is invalid, because it is missing required keys: 'text'/i)", |
| @"browser.test.assertThrows(() => browser.action.setBadgeText({windowId: 1}), /'details' value is invalid, because it is missing required keys: 'text'/i)", |
| |
| @"browser.test.assertThrows(() => browser.action.setTitle({tabId: 1, windowId: 1, title: null}), /'details' value is invalid, because it cannot specify both 'tabId' and 'windowId'/i)", |
| @"browser.test.assertThrows(() => browser.action.setIcon({tabId: 1, windowId: 1, path: null}), /'details' value is invalid, because it cannot specify both 'tabId' and 'windowId'/i)", |
| @"browser.test.assertThrows(() => browser.action.setPopup({tabId: 1, windowId: 1, popup: null}), /'details' value is invalid, because it cannot specify both 'tabId' and 'windowId'/i)", |
| @"browser.test.assertThrows(() => browser.action.setBadgeText({tabId: 1, windowId: 1, text: null}), /'details' value is invalid, because it cannot specify both 'tabId' and 'windowId'/i)", |
| |
| @"await browser.test.assertRejects(browser.action.getTitle({tabId: 9999}), /tab not found/i)", |
| @"await browser.test.assertRejects(browser.action.getTitle({windowId: 9999}), /window not found/i)", |
| @"await browser.test.assertRejects(browser.action.getPopup({tabId: 9999}), /tab not found/i)", |
| @"await browser.test.assertRejects(browser.action.getPopup({windowId: 9999}), /window not found/i)", |
| @"await browser.test.assertRejects(browser.action.getBadgeText({tabId: 9999}), /tab not found/i)", |
| @"await browser.test.assertRejects(browser.action.getBadgeText({windowId: 9999}), /window not found/i)", |
| @"await browser.test.assertRejects(browser.action.isEnabled({tabId: 9999}), /tab not found/i)", |
| @"await browser.test.assertRejects(browser.action.isEnabled({windowId: 9999}), /window not found/i)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| Util::loadAndRunExtension(actionPopupManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIAction, ClickedEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.action.setPopup({ popup: '' })", |
| |
| @"browser.action.onClicked.addListener((tab) => {", |
| @" browser.test.assertEq(typeof tab, 'object', 'The tab should be an object')", |
| |
| @" browser.test.notifyPass()", |
| @"})", |
| |
| @"browser.test.sendMessage('Test Action')" |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript }); |
| |
| [manager runUntilTestMessage:@"Test Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, PresentPopupForAction) |
| { |
| auto *popupPage = @"<b>Hello World!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Test Popup Action')" |
| ]); |
| |
| auto *smallToolbarIcon = Util::makePNGData(CGSizeMake(16, 16), @selector(redColor)); |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": popupPage, |
| @"toolbar-16.png": smallToolbarIcon, |
| @"toolbar-32.png": largeToolbarIcon, |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_TRUE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.badgeText, @""); |
| |
| EXPECT_NS_EQUAL(action.label, @"Test Action"); |
| |
| auto *smallIcon = [action iconForSize:CGSizeMake(16, 16)]; |
| EXPECT_NOT_NULL(smallIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(smallIcon.size, CGSizeMake(16, 16))); |
| |
| auto *largeIcon = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(largeIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(largeIcon.size, CGSizeMake(32, 32))); |
| |
| #if PLATFORM(IOS_FAMILY) |
| EXPECT_NOT_NULL(action.popupViewController); |
| #endif |
| |
| #if PLATFORM(MAC) |
| EXPECT_NOT_NULL(action.popupPopover); |
| #endif |
| |
| EXPECT_NOT_NULL(action.popupWebView); |
| EXPECT_FALSE(action.popupWebView.loading); |
| |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/popup.html"); |
| |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager runUntilTestMessage:@"Test Popup Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, GetCurrentTabAndWindowFromPopupPage) |
| { |
| auto *popupScript = Util::constructScript(@[ |
| @"const tab = await browser.tabs.getCurrent()", |
| @"browser.test.assertEq(typeof tab, 'object', 'The tab should be')", |
| @"browser.test.assertTrue(tab.active, 'The current tab should be active')", |
| |
| @"const window = await browser.windows.getCurrent()", |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be')", |
| @"browser.test.assertTrue(window.focused, 'The current window should be focused')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Test Popup Action')" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"<script type='module' src='popup.js'></script>", |
| @"popup.js": popupScript |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_NOT_NULL(action.popupWebView); |
| }; |
| |
| [manager runUntilTestMessage:@"Test Popup Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, UpdateTabFromPopupPage) |
| { |
| auto *popupScript = Util::constructScript(@[ |
| @"const tab = await browser.tabs.getCurrent()", |
| @"browser.test.assertEq(typeof tab, 'object', 'The tab should be')", |
| @"browser.test.assertTrue(tab.active, 'The current tab should be active')", |
| |
| @"browser.test.assertFalse(tab.mutedInfo.muted, 'The tab should not be initially muted')", |
| |
| @"const updatedTab = await browser.tabs.update({", |
| @" muted: true,", |
| @"})", |
| |
| @"browser.test.assertTrue(updatedTab.mutedInfo.muted, 'The tab should be muted after update')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Test Popup Action')" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"<script type='module' src='popup.js'></script>", |
| @"popup.js": popupScript |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_NOT_NULL(action.popupWebView); |
| }; |
| |
| [manager runUntilTestMessage:@"Test Popup Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetDefaultActionProperties) |
| { |
| auto *popupPage = @"<b>Hello World!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertEq(await browser.action.getTitle({ }), 'Test Action', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ }), 'popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ }), '', 'Badge text should be')", |
| @"browser.test.assertTrue(await browser.action.isEnabled({ }), 'Action should be enabled')", |
| |
| @"await browser.action.setTitle({ title: 'Modified Title' })", |
| @"await browser.action.setIcon({ path: 'toolbar-48.png' })", |
| @"await browser.action.setPopup({ popup: 'alt-popup.html' })", |
| @"await browser.action.setBadgeText({ text: '42' })", |
| @"await browser.action.disable()", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ }), 'Modified Title', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ }), 'alt-popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ }), '42', 'Badge text should be')", |
| @"browser.test.assertFalse(await browser.action.isEnabled({ }), 'Action should be disabled')", |
| |
| @"browser.action.openPopup()" |
| ]); |
| |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"alt-popup.html": popupPage, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| |
| EXPECT_TRUE(defaultAction.presentsPopup); |
| EXPECT_FALSE(defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(defaultAction.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(defaultAction.badgeText, @"42"); |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| |
| EXPECT_NS_EQUAL(action.associatedTab, manager.get().defaultTab); |
| |
| EXPECT_FALSE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(action.badgeText, @"42"); |
| EXPECT_FALSE(action.hasUnreadBadgeText); |
| |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_FALSE(action.isEnabled); |
| |
| EXPECT_NOT_NULL(action.popupWebView); |
| EXPECT_FALSE(action.popupWebView.loading); |
| |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/alt-popup.html"); |
| |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, TabSpecificActionProperties) |
| { |
| auto *popupPage = @"<b>Hello World!</b>"; |
| auto *altPopupPage = @"<b>Hello Alternate World!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true })", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ tabId: currentTab.id }), 'Test Action', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ tabId: currentTab.id }), 'popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ tabId: currentTab.id }), '', 'Badge text should be')", |
| @"browser.test.assertTrue(await browser.action.isEnabled({ tabId: currentTab.id }), 'Action should be enabled')", |
| |
| @"browser.action.setTitle({ title: 'Tab Title', tabId: currentTab.id })", |
| @"browser.action.setIcon({ path: 'toolbar-48.png', tabId: currentTab.id })", |
| @"browser.action.setPopup({ popup: 'alt-popup.html', tabId: currentTab.id })", |
| @"browser.action.setBadgeText({ text: '42', tabId: currentTab.id })", |
| @"browser.action.disable(currentTab.id)", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ tabId: currentTab.id }), 'Tab Title', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ tabId: currentTab.id }), 'alt-popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ tabId: currentTab.id }), '42', 'Badge text should be')", |
| @"browser.test.assertFalse(await browser.action.isEnabled({ tabId: currentTab.id }), 'Action should be disabled')", |
| |
| @"browser.action.openPopup({ windowId: currentTab.windowId })" |
| ]); |
| |
| auto *smallToolbarIcon = Util::makePNGData(CGSizeMake(16, 16), @selector(redColor)); |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": popupPage, |
| @"alt-popup.html": altPopupPage, |
| @"toolbar-16.png": smallToolbarIcon, |
| @"toolbar-32.png": largeToolbarIcon, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| }; |
| |
| auto manager = Util::parseExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| |
| EXPECT_TRUE(defaultAction.presentsPopup); |
| EXPECT_TRUE(defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(defaultAction.label, @"Test Action"); |
| EXPECT_NS_EQUAL(defaultAction.badgeText, @""); |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| |
| auto *defaultIcon = [defaultAction iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(defaultIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(defaultIcon.size, CGSizeMake(32, 32))); |
| |
| EXPECT_FALSE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.label, @"Tab Title"); |
| EXPECT_NS_EQUAL(action.badgeText, @"42"); |
| EXPECT_FALSE(action.hasUnreadBadgeText); |
| |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_FALSE(action.isEnabled); |
| |
| EXPECT_NOT_NULL(action.popupWebView); |
| EXPECT_FALSE(action.popupWebView.loading); |
| |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/alt-popup.html"); |
| |
| auto *secondTabAction = [manager.get().context actionForTab:manager.get().defaultWindow.tabs.lastObject]; |
| |
| EXPECT_EQ(secondTabAction.presentsPopup, defaultAction.presentsPopup); |
| EXPECT_EQ(secondTabAction.isEnabled, defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(secondTabAction.label, defaultAction.label); |
| EXPECT_NS_EQUAL(secondTabAction.badgeText, defaultAction.badgeText); |
| EXPECT_FALSE(secondTabAction.hasUnreadBadgeText); |
| |
| icon = [secondTabAction iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, defaultIcon.size)); |
| |
| webViewURL = secondTabAction.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/popup.html"); |
| |
| auto *secondWindowAction = [manager.get().context actionForTab:manager.get().windows[1].tabs[0]]; |
| EXPECT_EQ(secondWindowAction.presentsPopup, defaultAction.presentsPopup); |
| EXPECT_EQ(secondWindowAction.isEnabled, defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(secondWindowAction.label, defaultAction.label); |
| EXPECT_NS_EQUAL(secondWindowAction.badgeText, defaultAction.badgeText); |
| EXPECT_FALSE(secondWindowAction.hasUnreadBadgeText); |
| |
| icon = [secondWindowAction iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, defaultIcon.size)); |
| |
| webViewURL = secondWindowAction.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/popup.html"); |
| |
| [secondTabAction closePopup]; |
| [secondWindowAction closePopup]; |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager.get().defaultWindow openNewTab]; |
| [manager openNewWindow]; |
| |
| [manager loadAndRun]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, WindowSpecificActionProperties) |
| { |
| auto *popupPage = @"<b>Hello World!</b>"; |
| auto *windowPopupPage = @"<b>Window-Specific Popup!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true })", |
| @"const currentWindowId = currentTab.windowId", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ windowId: currentWindowId }), 'Test Action', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ windowId: currentWindowId }), 'popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ windowId: currentWindowId }), '', 'Badge text should be')", |
| |
| @"browser.action.setTitle({ title: 'Window Title', windowId: currentWindowId })", |
| @"browser.action.setIcon({ path: 'window-toolbar-48.png', windowId: currentWindowId })", |
| @"browser.action.setPopup({ popup: 'window-popup.html', windowId: currentWindowId })", |
| @"browser.action.setBadgeText({ text: 'W', windowId: currentWindowId })", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ windowId: currentWindowId }), 'Window Title', 'Title should be')", |
| @"browser.test.assertEq(await browser.action.getPopup({ windowId: currentWindowId }), 'window-popup.html', 'Popup should be')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ windowId: currentWindowId }), 'W', 'Badge text should be')", |
| |
| @"browser.action.openPopup({ windowId: currentWindowId })" |
| ]); |
| |
| auto *defaultToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(redColor)); |
| auto *windowToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(greenColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": popupPage, |
| @"window-popup.html": windowPopupPage, |
| @"toolbar-32.png": defaultToolbarIcon, |
| @"window-toolbar-48.png": windowToolbarIcon, |
| }; |
| |
| auto manager = Util::parseExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| |
| EXPECT_TRUE(defaultAction.presentsPopup); |
| EXPECT_TRUE(defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(defaultAction.label, @"Test Action"); |
| EXPECT_NS_EQUAL(defaultAction.badgeText, @""); |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| |
| auto *defaultIcon = [defaultAction iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(defaultIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(defaultIcon.size, CGSizeMake(32, 32))); |
| |
| EXPECT_TRUE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.label, @"Window Title"); |
| EXPECT_NS_EQUAL(action.badgeText, @"W"); |
| EXPECT_FALSE(action.hasUnreadBadgeText); |
| |
| auto *windowIcon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(windowIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(windowIcon.size, CGSizeMake(48, 48))); |
| |
| EXPECT_TRUE(action.presentsPopup); |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/window-popup.html"); |
| |
| auto *secondWindowAction = [manager.get().context actionForTab:manager.get().windows[1].tabs[0]]; |
| EXPECT_EQ(secondWindowAction.presentsPopup, defaultAction.presentsPopup); |
| EXPECT_EQ(secondWindowAction.isEnabled, defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(secondWindowAction.label, defaultAction.label); |
| EXPECT_NS_EQUAL(secondWindowAction.badgeText, defaultAction.badgeText); |
| EXPECT_FALSE(secondWindowAction.hasUnreadBadgeText); |
| |
| auto *secondWindowIcon = [secondWindowAction iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(secondWindowIcon); |
| EXPECT_TRUE(CGSizeEqualToSize(secondWindowIcon.size, defaultIcon.size)); |
| |
| webViewURL = secondWindowAction.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/popup.html"); |
| |
| [secondWindowAction closePopup]; |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager openNewWindow]; |
| |
| [manager loadAndRun]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconSinglePath) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: 'toolbar-48.png' })", |
| ]); |
| |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconSinglePathRelative) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: '../icons/toolbar-48.png' })", |
| ]); |
| |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background/index.html": @"<script type='module' src='script.js'></script>", |
| @"background/script.js": backgroundScript, |
| @"icons/toolbar-48.png": extraLargeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto *manifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Action Test", |
| @"description": @"Action Test", |
| @"version": @"1", |
| |
| @"background": @{ |
| @"page": @"background/index.html", |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"action": @{ |
| @"default_title": @"Test Action", |
| @"default_popup": @"popup.html", |
| @"default_icon": @{ |
| @"16": @"icons/toolbar-16.png", |
| @"32": @"icons/toolbar-32.png", |
| }, |
| }, |
| }; |
| |
| auto manager = Util::loadExtension(manifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconMultipleSizes) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: { '48': 'toolbar-48.png', '96': 'toolbar-96.png', '128': 'toolbar-128.png' } })", |
| ]); |
| |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(96, 96), @selector(greenColor)); |
| auto *superExtraLargeToolbarIcon = Util::makePNGData(CGSizeMake(128, 128), @selector(purpleColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-48.png": largeToolbarIcon, |
| @"toolbar-96.png": extraLargeToolbarIcon, |
| @"toolbar-128.png": superExtraLargeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon48 = [action iconForSize:CGSizeMake(48, 48)]; |
| auto *icon96 = [action iconForSize:CGSizeMake(96, 96)]; |
| auto *icon128 = [action iconForSize:CGSizeMake(128, 128)]; |
| |
| EXPECT_NOT_NULL(icon48); |
| EXPECT_TRUE(CGSizeEqualToSize(icon48.size, CGSizeMake(48, 48))); |
| |
| EXPECT_NOT_NULL(icon96); |
| EXPECT_TRUE(CGSizeEqualToSize(icon96.size, CGSizeMake(96, 96))); |
| |
| EXPECT_NOT_NULL(icon128); |
| EXPECT_TRUE(CGSizeEqualToSize(icon128.size, CGSizeMake(128, 128))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconMultipleSizesRelative) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: { '48': '../icons/toolbar-48.png', '96': '../icons/toolbar-96.png', '128': '../icons/toolbar-128.png' } })", |
| ]); |
| |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(96, 96), @selector(greenColor)); |
| auto *superExtraLargeToolbarIcon = Util::makePNGData(CGSizeMake(128, 128), @selector(purpleColor)); |
| |
| auto *resources = @{ |
| @"background/index.html": @"<script type='module' src='script.js'></script>", |
| @"background/script.js": backgroundScript, |
| @"icons/toolbar-48.png": largeToolbarIcon, |
| @"icons/toolbar-96.png": extraLargeToolbarIcon, |
| @"icons/toolbar-128.png": superExtraLargeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto *manifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Action Test", |
| @"description": @"Action Test", |
| @"version": @"1", |
| |
| @"background": @{ |
| @"page": @"background/index.html", |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"action": @{ |
| @"default_title": @"Test Action", |
| @"default_popup": @"popup.html", |
| @"default_icon": @{ |
| @"16": @"icons/toolbar-16.png", |
| @"32": @"icons/toolbar-32.png", |
| }, |
| }, |
| }; |
| |
| auto manager = Util::loadExtension(manifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon48 = [action iconForSize:CGSizeMake(48, 48)]; |
| auto *icon96 = [action iconForSize:CGSizeMake(96, 96)]; |
| auto *icon128 = [action iconForSize:CGSizeMake(128, 128)]; |
| |
| EXPECT_NOT_NULL(icon48); |
| EXPECT_TRUE(CGSizeEqualToSize(icon48.size, CGSizeMake(48, 48))); |
| |
| EXPECT_NOT_NULL(icon96); |
| EXPECT_TRUE(CGSizeEqualToSize(icon96.size, CGSizeMake(96, 96))); |
| |
| EXPECT_NOT_NULL(icon128); |
| EXPECT_TRUE(CGSizeEqualToSize(icon128.size, CGSizeMake(128, 128))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithBadPath) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: 'bad.png' })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NULL(icon); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithImageData) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const context = new OffscreenCanvas(48, 48).getContext('2d')", |
| @"context.fillStyle = 'green'", |
| @"context.fillRect(0, 0, 48, 48)", |
| |
| @"const imageData = context.getImageData(0, 0, 48, 48)", |
| @"await browser.action.setIcon({ imageData })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithBadImageData) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertThrows(() => browser.action.setIcon({ imageData: { 16: { data: [ 'bad' ] } } }))", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| Util::loadAndRunExtension(actionPopupManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithMultipleImageDataSizes) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const createImageData = (size, color) => {", |
| @" const context = new OffscreenCanvas(size, size).getContext('2d')", |
| @" context.fillStyle = color", |
| @" context.fillRect(0, 0, size, size)", |
| |
| @" return context.getImageData(0, 0, size, size)", |
| @"}", |
| |
| @"const imageData48 = createImageData(48, 'green')", |
| @"const imageData96 = createImageData(96, 'blue')", |
| @"const imageData128 = createImageData(128, 'red')", |
| |
| @"await browser.action.setIcon({ imageData: { '48': imageData48, '96': imageData96, '128': imageData128 } })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon48 = [action iconForSize:CGSizeMake(48, 48)]; |
| auto *icon96 = [action iconForSize:CGSizeMake(96, 96)]; |
| auto *icon128 = [action iconForSize:CGSizeMake(128, 128)]; |
| |
| EXPECT_NOT_NULL(icon48); |
| EXPECT_TRUE(CGSizeEqualToSize(icon48.size, CGSizeMake(48, 48))); |
| |
| EXPECT_NOT_NULL(icon96); |
| EXPECT_TRUE(CGSizeEqualToSize(icon96.size, CGSizeMake(96, 96))); |
| |
| EXPECT_NOT_NULL(icon128); |
| EXPECT_TRUE(CGSizeEqualToSize(icon128.size, CGSizeMake(128, 128))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithDataURL) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const canvas = document.createElement('canvas')", |
| @"canvas.width = 48", |
| @"canvas.height = 48", |
| |
| @"const context = canvas.getContext('2d')", |
| @"context.fillStyle = 'blue'", |
| @"context.fillRect(0, 0, 48, 48)", |
| |
| @"const pngDataURL48 = canvas.toDataURL('image/png')", |
| |
| @"await browser.action.setIcon({ path: pngDataURL48 })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithBadDataURL) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const invalidDataURL = 'data:image/png;base64,INVALIDDATA'", |
| |
| @"await browser.action.setIcon({ path: invalidDataURL })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NULL(icon); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithNullPath) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: null })", |
| @"browser.test.sendMessage('Icon Set')", |
| ]); |
| |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-32.png": largeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| [manager runUntilTestMessage:@"Icon Set"]; |
| |
| auto *action = [manager.get().context actionForTab:nil]; |
| auto *icon = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(32, 32))); |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithNullImageData) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ imageData: null })", |
| @"browser.test.sendMessage('Icon Set')", |
| ]); |
| |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-32.png": largeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| [manager runUntilTestMessage:@"Icon Set"]; |
| |
| auto *action = [manager.get().context actionForTab:nil]; |
| auto *icon = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(32, 32))); |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithNullVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ variants: null })", |
| @"browser.test.sendMessage('Icon Set')", |
| ]); |
| |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-32.png": largeToolbarIcon, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| [manager runUntilTestMessage:@"Icon Set"]; |
| |
| auto *action = [manager.get().context actionForTab:nil]; |
| auto *icon = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(32, 32))); |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithMultipleDataURLs) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const canvas = document.createElement('canvas')", |
| |
| @"canvas.width = 48", |
| @"canvas.height = 48", |
| |
| @"let context = canvas.getContext('2d')", |
| @"context.fillStyle = 'blue'", |
| @"context.fillRect(0, 0, 48, 48)", |
| |
| @"const pngDataURL48 = canvas.toDataURL('image/png')", |
| |
| @"canvas.width = 96", |
| @"canvas.height = 96", |
| |
| @"context = canvas.getContext('2d')", |
| @"context.fillStyle = 'red'", |
| @"context.fillRect(0, 0, 96, 96)", |
| |
| @"const pngDataURL96 = canvas.toDataURL('image/png')", |
| |
| @"await browser.action.setIcon({ path: { '48': pngDataURL48, '96': pngDataURL96 } })", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon48 = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon48); |
| EXPECT_TRUE(CGSizeEqualToSize(icon48.size, CGSizeMake(48, 48))); |
| |
| auto *icon96 = [action iconForSize:CGSizeMake(96, 96)]; |
| EXPECT_NOT_NULL(icon96); |
| EXPECT_TRUE(CGSizeEqualToSize(icon96.size, CGSizeMake(96, 96))); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconSymbolSinglePath) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: 'symbol:star' })", |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript, @"popup.html": @"Hello world!" }); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(16, 16)]; |
| EXPECT_NOT_NULL(icon); |
| #if PLATFORM(MAC) |
| EXPECT_TRUE([icon isKindOfClass:NSImage.class]); |
| EXPECT_TRUE(icon._isSymbolImage); |
| #else |
| EXPECT_TRUE([icon isKindOfClass:UIImage.class]); |
| EXPECT_TRUE(icon.isSymbolImage); |
| #endif |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconSymbolIconsDictionary) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setIcon({ path: { '16': 'symbol:heart.fill' } })", |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript, @"popup.html": @"Hello world!" }); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(16, 16)]; |
| EXPECT_NOT_NULL(icon); |
| #if PLATFORM(MAC) |
| EXPECT_TRUE([icon isKindOfClass:NSImage.class]); |
| EXPECT_TRUE(icon._isSymbolImage); |
| #else |
| EXPECT_TRUE([icon isKindOfClass:UIImage.class]); |
| EXPECT_TRUE(icon.isSymbolImage); |
| #endif |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| #if ENABLE(WK_WEB_EXTENSIONS_ICON_VARIANTS) |
| TEST(WKWebExtensionAPIAction, SetIconWithVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.test.assertSafeResolve(() => browser.action.setIcon({", |
| @" variants: [", |
| @" { 32: 'action-dark-32.png', 64: 'action-dark-64.png', 'colorSchemes': [ 'dark' ] },", |
| @" { 32: 'action-light-32.png', 64: 'action-light-64.png', 'colorSchemes': [ 'light' ] }", |
| @" ]", |
| @"}))", |
| ]); |
| |
| auto *dark32Icon = Util::makePNGData(CGSizeMake(32, 32), @selector(whiteColor)); |
| auto *dark64Icon = Util::makePNGData(CGSizeMake(64, 64), @selector(whiteColor)); |
| auto *light32Icon = Util::makePNGData(CGSizeMake(32, 32), @selector(blackColor)); |
| auto *light64Icon = Util::makePNGData(CGSizeMake(64, 64), @selector(blackColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| @"action-dark-32.png": dark32Icon, |
| @"action-dark-64.png": dark64Icon, |
| @"action-light-32.png": light32Icon, |
| @"action-light-64.png": light64Icon, |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon32 = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon32); |
| EXPECT_TRUE(CGSizeEqualToSize(icon32.size, CGSizeMake(32, 32))); |
| |
| auto *icon64 = [action iconForSize:CGSizeMake(64, 64)]; |
| EXPECT_NOT_NULL(icon64); |
| EXPECT_TRUE(CGSizeEqualToSize(icon64.size, CGSizeMake(64, 64))); |
| |
| Util::performWithAppearance(Util::Appearance::Dark, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor whiteColor])); |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon64), [CocoaColor whiteColor])); |
| }); |
| |
| Util::performWithAppearance(Util::Appearance::Light, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor blackColor])); |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon64), [CocoaColor blackColor])); |
| }); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithImageDataAndVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const createImageData = (size, color) => {", |
| @" const context = new OffscreenCanvas(size, size).getContext('2d')", |
| @" context.fillStyle = color", |
| @" context.fillRect(0, 0, size, size)", |
| |
| @" return context.getImageData(0, 0, size, size)", |
| @"}", |
| |
| @"const imageDataDark32 = createImageData(32, 'white')", |
| @"const imageDataDark64 = createImageData(64, 'white')", |
| @"const imageDataLight32 = createImageData(32, 'black')", |
| @"const imageDataLight64 = createImageData(64, 'black')", |
| |
| @"await browser.test.assertSafeResolve(() => browser.action.setIcon({", |
| @" variants: [", |
| @" { 32: imageDataDark32, 64: imageDataDark64, 'colorSchemes': [ 'dark' ] },", |
| @" { 32: imageDataLight32, 64: imageDataLight64, 'colorSchemes': [ 'light' ] }", |
| @" ]", |
| @"}))", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon32 = [action iconForSize:CGSizeMake(32, 32)]; |
| auto *icon64 = [action iconForSize:CGSizeMake(64, 64)]; |
| |
| EXPECT_NOT_NULL(icon32); |
| EXPECT_TRUE(CGSizeEqualToSize(icon32.size, CGSizeMake(32, 32))); |
| |
| EXPECT_NOT_NULL(icon64); |
| EXPECT_TRUE(CGSizeEqualToSize(icon64.size, CGSizeMake(64, 64))); |
| |
| Util::performWithAppearance(Util::Appearance::Dark, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor whiteColor])); |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon64), [CocoaColor whiteColor])); |
| }); |
| |
| Util::performWithAppearance(Util::Appearance::Light, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor blackColor])); |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon64), [CocoaColor blackColor])); |
| }); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconThrowsWithNoValidVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const createImageData = (size, color) => {", |
| @" const context = new OffscreenCanvas(size, size).getContext('2d')", |
| @" context.fillStyle = color", |
| @" context.fillRect(0, 0, size, size)", |
| |
| @" return context.getImageData(0, 0, size, size)", |
| @"}", |
| |
| @"const invalidImageData = createImageData(32, 'white')", |
| |
| @"await browser.test.assertThrows(() => browser.action.setIcon({", |
| @" variants: [ { 'thirtytwo': invalidImageData, 'colorSchemes': [ 'light' ] } ]", |
| @"}), /'variants\\[0\\]' value is invalid, because 'thirtytwo' is not a valid dimension/s)", |
| |
| @"await browser.test.assertThrows(() => browser.action.setIcon({", |
| @" variants: [ { 32: invalidImageData, 'colorSchemes': [ 'bad' ] } ]", |
| @"}), /'variants\\[0\\]\\['colorSchemes'\\]' value is invalid, because it must specify either 'light' or 'dark'/s)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| Util::loadAndRunExtension(actionPopupManifest, resources); |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithMixedValidAndInvalidVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const createImageData = (size, color) => {", |
| @" const context = new OffscreenCanvas(size, size).getContext('2d')", |
| @" context.fillStyle = color", |
| @" context.fillRect(0, 0, size, size)", |
| |
| @" return context.getImageData(0, 0, size, size)", |
| @"}", |
| |
| @"const imageDataLight32 = createImageData(32, 'black')", |
| @"const invalidImageData = createImageData(32, 'white')", |
| |
| @"await browser.test.assertSafeResolve(() => browser.action.setIcon({", |
| @" variants: [", |
| @" { '32': imageDataLight32, 'colorSchemes': ['light'] },", |
| @" { '32.5': invalidImageData, 'colorSchemes': ['dark'] }", |
| @" ]", |
| @"}))", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello world!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon32 = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon32); |
| EXPECT_TRUE(CGSizeEqualToSize(icon32.size, CGSizeMake(32, 32))); |
| |
| Util::performWithAppearance(Util::Appearance::Light, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor blackColor])); |
| }); |
| |
| Util::performWithAppearance(Util::Appearance::Dark, ^{ |
| // Should still be black, as light variant is used. |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(icon32), [CocoaColor blackColor])); |
| }); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithAnySizeVariantAndSVGDataURL) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const whiteSVGData = 'data:image/svg+xml;base64,' + btoa(`", |
| @" <svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\">", |
| @" <rect width=\"100\" height=\"100\" fill=\"white\" />", |
| @" </svg>`)", |
| |
| @"const blackSVGData = 'data:image/svg+xml;base64,' + btoa(`", |
| @" <svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\">", |
| @" <rect width=\"100\" height=\"100\" fill=\"black\" />", |
| @" </svg>`)", |
| |
| @"await browser.test.assertSafeResolve(() => browser.action.setIcon({", |
| @" variants: [", |
| @" { any: whiteSVGData, 'colorSchemes': [ 'dark' ] },", |
| @" { any: blackSVGData, 'colorSchemes': [ 'light' ] }", |
| @" ]", |
| @"}))", |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"Hello World!", |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *iconAnySize = [action iconForSize:CGSizeMake(48, 48)]; |
| |
| EXPECT_NOT_NULL(iconAnySize); |
| EXPECT_TRUE(CGSizeEqualToSize(iconAnySize.size, CGSizeMake(48, 48))); |
| |
| Util::performWithAppearance(Util::Appearance::Dark, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(iconAnySize), [CocoaColor whiteColor])); |
| }); |
| |
| Util::performWithAppearance(Util::Appearance::Light, ^{ |
| EXPECT_TRUE(Util::compareColors(Util::pixelColor(iconAnySize), [CocoaColor blackColor])); |
| }); |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SetIconWithSymbolVariants) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.test.assertSafeResolve(() => browser.action.setIcon({", |
| @" variants: [", |
| @" { any: 'symbol:star' }", |
| @" ]", |
| @"}))", |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript, @"popup.html": @"Hello world!" }); |
| |
| manager.get().internalDelegate.didUpdateAction = ^(WKWebExtensionAction *action) { |
| auto *icon = [action iconForSize:CGSizeMake(32, 32)]; |
| EXPECT_NOT_NULL(icon); |
| #if PLATFORM(MAC) |
| EXPECT_TRUE([icon isKindOfClass:NSImage.class]); |
| EXPECT_TRUE(icon._isSymbolImage); |
| #elif PLATFORM(IOS_FAMILY) |
| EXPECT_TRUE([icon isKindOfClass:UIImage.class]); |
| EXPECT_TRUE(icon.isSymbolImage); |
| #endif |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| #endif // ENABLE(WK_WEB_EXTENSIONS_ICON_VARIANTS) |
| |
| TEST(WKWebExtensionAPIAction, BrowserAction) |
| { |
| auto *browserActionManifest = @{ |
| @"manifest_version": @2, |
| |
| @"name": @"Browser Action Test", |
| @"description": @"Browser Action Test", |
| @"version": @"1", |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"browser_action": @{ |
| @"default_title": @"Test Browser Action", |
| @"default_popup": @"popup.html", |
| @"default_icon": @{ |
| @"16": @"toolbar-16.png", |
| @"32": @"toolbar-32.png", |
| }, |
| }, |
| }; |
| |
| auto *popupPage = @"<b>Hello World!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.browserAction.setTitle({ title: 'Modified Title' })", |
| @"await browser.browserAction.setIcon({ path: 'toolbar-48.png' })", |
| @"await browser.browserAction.setPopup({ popup: 'alt-popup.html' })", |
| @"await browser.browserAction.setBadgeText({ text: '42' })", |
| @"await browser.browserAction.disable()", |
| |
| @"browser.browserAction.openPopup()" |
| ]); |
| |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"alt-popup.html": popupPage, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| }; |
| |
| auto manager = Util::loadExtension(browserActionManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| |
| EXPECT_TRUE(defaultAction.presentsPopup); |
| EXPECT_FALSE(defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(defaultAction.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(defaultAction.badgeText, @"42"); |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| |
| EXPECT_NS_EQUAL(action.associatedTab, manager.get().defaultTab); |
| |
| EXPECT_FALSE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(action.badgeText, @"42"); |
| EXPECT_FALSE(action.hasUnreadBadgeText); |
| |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_FALSE(action.isEnabled); |
| |
| #if PLATFORM(IOS_FAMILY) |
| EXPECT_NOT_NULL(action.popupViewController); |
| #endif |
| |
| #if PLATFORM(MAC) |
| EXPECT_NOT_NULL(action.popupPopover); |
| #endif |
| |
| EXPECT_NOT_NULL(action.popupWebView); |
| EXPECT_FALSE(action.popupWebView.loading); |
| |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/alt-popup.html"); |
| |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, PageAction) |
| { |
| auto *pageActionManifest = @{ |
| @"manifest_version": @2, |
| |
| @"name": @"Page Action Test", |
| @"description": @"Page Action Test", |
| @"version": @"1", |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"page_action": @{ |
| @"default_title": @"Test Page Action", |
| @"default_popup": @"popup.html", |
| @"default_icon": @{ |
| @"16": @"toolbar-16.png", |
| @"32": @"toolbar-32.png", |
| }, |
| }, |
| }; |
| |
| auto *popupPage = @"<b>Hello World!</b>"; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.pageAction.setTitle({ title: 'Modified Title' })", |
| @"await browser.pageAction.setIcon({ path: 'toolbar-48.png' })", |
| @"await browser.pageAction.setPopup({ popup: 'alt-popup.html' })", |
| @"await browser.pageAction.setBadgeText({ text: '42' })", |
| @"await browser.pageAction.disable()", |
| |
| @"browser.pageAction.openPopup()" |
| ]); |
| |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"alt-popup.html": popupPage, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| }; |
| |
| auto manager = Util::loadExtension(pageActionManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| |
| EXPECT_TRUE(defaultAction.presentsPopup); |
| EXPECT_FALSE(defaultAction.isEnabled); |
| EXPECT_NS_EQUAL(defaultAction.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(defaultAction.badgeText, @"42"); |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| |
| EXPECT_NS_EQUAL(action.associatedTab, manager.get().defaultTab); |
| |
| EXPECT_FALSE(action.isEnabled); |
| EXPECT_NS_EQUAL(action.label, @"Modified Title"); |
| EXPECT_NS_EQUAL(action.badgeText, @"42"); |
| EXPECT_FALSE(action.hasUnreadBadgeText); |
| |
| auto *icon = [action iconForSize:CGSizeMake(48, 48)]; |
| EXPECT_NOT_NULL(icon); |
| EXPECT_TRUE(CGSizeEqualToSize(icon.size, CGSizeMake(48, 48))); |
| |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_FALSE(action.isEnabled); |
| |
| #if PLATFORM(IOS_FAMILY) |
| EXPECT_NOT_NULL(action.popupViewController); |
| #endif |
| |
| #if PLATFORM(MAC) |
| EXPECT_NOT_NULL(action.popupPopover); |
| #endif |
| |
| EXPECT_NOT_NULL(action.popupWebView); |
| EXPECT_FALSE(action.popupWebView.loading); |
| |
| NSURL *webViewURL = action.popupWebView.URL; |
| EXPECT_NS_EQUAL(webViewURL.scheme, @"webkit-extension"); |
| EXPECT_NS_EQUAL(webViewURL.path, @"/alt-popup.html"); |
| |
| [action closePopup]; |
| |
| [manager done]; |
| }; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, ClearTabSpecificActionPropertiesOnNavigation) |
| { |
| TestWebKitAPI::HTTPServer server({ |
| { "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } }, |
| }, TestWebKitAPI::HTTPServer::Protocol::Http); |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true })", |
| |
| @"browser.action.setTitle({ title: 'Tab Title', tabId: currentTab.id })", |
| @"browser.action.setIcon({ path: 'toolbar-48.png', tabId: currentTab.id })", |
| @"browser.action.setPopup({ popup: 'alt-popup.html', tabId: currentTab.id })", |
| @"browser.action.setBadgeText({ text: '42', tabId: currentTab.id })", |
| @"browser.action.disable(currentTab.id)", |
| |
| @"browser.test.assertEq(await browser.action.getTitle({ tabId: currentTab.id }), 'Tab Title', 'Title should be before navigation')", |
| @"browser.test.assertEq(await browser.action.getPopup({ tabId: currentTab.id }), 'alt-popup.html', 'Popup should be before navigation')", |
| @"browser.test.assertEq(await browser.action.getBadgeText({ tabId: currentTab.id }), '42', 'Badge text should be before navigation')", |
| @"browser.test.assertFalse(await browser.action.isEnabled({ tabId: currentTab.id }), 'Action should be disabled before navigation')", |
| |
| @"browser.webNavigation.onCompleted.addListener(async (details) => {", |
| @" browser.test.assertEq(details.tabId, currentTab.id, 'Only the tab we expect should be changing')", |
| @" browser.test.assertEq(details.frameId, 0, 'Only main frame should be changing')", |
| |
| @" browser.test.assertEq(await browser.action.getTitle({ tabId: currentTab.id }), 'Test Action', 'Title should be after navigation')", |
| @" browser.test.assertEq(await browser.action.getPopup({ tabId: currentTab.id }), 'popup.html', 'Popup should be after navigation')", |
| @" browser.test.assertEq(await browser.action.getBadgeText({ tabId: currentTab.id }), '', 'Badge text should be after navigation')", |
| @" browser.test.assertTrue(await browser.action.isEnabled({ tabId: currentTab.id }), 'Action should be enabled after navigation')", |
| |
| @" browser.test.notifyPass()", |
| @"})", |
| |
| @"browser.test.sendMessage('Load Tab')", |
| ]); |
| |
| auto *smallToolbarIcon = Util::makePNGData(CGSizeMake(16, 16), @selector(redColor)); |
| auto *largeToolbarIcon = Util::makePNGData(CGSizeMake(32, 32), @selector(blueColor)); |
| auto *extraLargeToolbarIcon = Util::makePNGData(CGSizeMake(48, 48), @selector(yellowColor)); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"toolbar-16.png": smallToolbarIcon, |
| @"toolbar-32.png": largeToolbarIcon, |
| @"toolbar-48.png": extraLargeToolbarIcon, |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forPermission:WKWebExtensionPermissionWebNavigation]; |
| |
| auto *localhostRequest = server.requestWithLocalhost(); |
| auto *addressRequest = server.request(); |
| |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:localhostRequest.URL]; |
| [manager.get().context setPermissionStatus:WKWebExtensionContextPermissionStatusGrantedExplicitly forURL:addressRequest.URL]; |
| |
| [manager.get().defaultTab.webView loadRequest:localhostRequest]; |
| |
| [manager runUntilTestMessage:@"Load Tab"]; |
| |
| [manager.get().defaultTab.webView loadRequest:addressRequest]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, HasUnreadBadgeText) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"await browser.action.setBadgeText({ text: 'New' })", |
| |
| @"browser.test.sendMessage('Check Unread Badge Text')" |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript }); |
| [manager runUntilTestMessage:@"Check Unread Badge Text"]; |
| |
| auto *defaultAction = [manager.get().context actionForTab:nil]; |
| auto *tabAction = [manager.get().context actionForTab:manager.get().defaultTab]; |
| EXPECT_TRUE(defaultAction.hasUnreadBadgeText); |
| EXPECT_TRUE(tabAction.hasUnreadBadgeText); |
| |
| tabAction.hasUnreadBadgeText = NO; |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| EXPECT_FALSE(tabAction.hasUnreadBadgeText); |
| |
| tabAction.hasUnreadBadgeText = YES; |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| EXPECT_TRUE(tabAction.hasUnreadBadgeText); |
| |
| tabAction.hasUnreadBadgeText = NO; |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| EXPECT_FALSE(tabAction.hasUnreadBadgeText); |
| |
| defaultAction.hasUnreadBadgeText = YES; |
| EXPECT_TRUE(defaultAction.hasUnreadBadgeText); |
| EXPECT_FALSE(tabAction.hasUnreadBadgeText); |
| |
| defaultAction.hasUnreadBadgeText = NO; |
| EXPECT_FALSE(defaultAction.hasUnreadBadgeText); |
| EXPECT_FALSE(tabAction.hasUnreadBadgeText); |
| } |
| |
| TEST(WKWebExtensionAPIAction, NavigationOpensInNewTab) |
| { |
| TestWebKitAPI::HTTPServer server({ |
| { "/"_s, { { { "Content-Type"_s, "text/html"_s } }, ""_s } }, |
| }, TestWebKitAPI::HTTPServer::Protocol::Http); |
| |
| auto *localhostRequest = server.requestWithLocalhost(); |
| |
| auto *popupScript = Util::constructScript(@[ |
| [NSString stringWithFormat:@"document.location.href = '%@'", localhostRequest.URL.absoluteString], |
| ]); |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Open Popup')" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"<script type='module' src='popup.js'></script>", |
| @"popup.js": popupScript |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| auto originalOpenNewTab = manager.get().internalDelegate.openNewTab; |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_NOT_NULL(action); |
| }; |
| |
| manager.get().internalDelegate.openNewTab = ^(WKWebExtensionTabConfiguration *configuration, WKWebExtensionContext *context, void (^completionHandler)(id<WKWebExtensionTab>, NSError *)) { |
| EXPECT_NS_EQUAL(configuration.url, localhostRequest.URL); |
| EXPECT_NS_EQUAL(configuration.window, manager.get().defaultWindow); |
| EXPECT_EQ(configuration.index, 1ul); |
| EXPECT_EQ(configuration.shouldBeActive, YES); |
| |
| originalOpenNewTab(configuration, context, completionHandler); |
| |
| [manager done]; |
| }; |
| |
| [manager runUntilTestMessage:@"Open Popup"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, WindowOpenOpensInNewWindow) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Open Popup')" |
| ]); |
| |
| auto *popupScript = Util::constructScript(@[ |
| @"browser.test.runWithUserGesture(() => {", |
| @" window.open('https://example.com/', '_blank', 'popup, width=100, height=50')", |
| @"})" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": @"<script type='module' src='popup.js'></script>", |
| @"popup.js": popupScript |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_NOT_NULL(action); |
| }; |
| |
| #if PLATFORM(MAC) |
| auto originalOpenNewWindow = manager.get().internalDelegate.openNewWindow; |
| manager.get().internalDelegate.openNewWindow = ^(WKWebExtensionWindowConfiguration *configuration, WKWebExtensionContext *context, void (^completionHandler)(id<WKWebExtensionWindow>, NSError *)) { |
| EXPECT_EQ(configuration.tabURLs.count, 1lu); |
| EXPECT_NS_EQUAL(configuration.tabURLs.firstObject, [NSURL URLWithString:@"https://example.com/"]); |
| |
| EXPECT_EQ(configuration.windowType, WKWebExtensionWindowTypePopup); |
| EXPECT_EQ(configuration.windowState, WKWebExtensionWindowStateNormal); |
| |
| EXPECT_EQ(configuration.frame.size.width, 100); |
| EXPECT_EQ(configuration.frame.size.height, 50); |
| EXPECT_TRUE(std::isnan(configuration.frame.origin.x)); |
| EXPECT_TRUE(std::isnan(configuration.frame.origin.y)); |
| |
| originalOpenNewWindow(configuration, context, completionHandler); |
| |
| [manager done]; |
| }; |
| #else |
| auto originalOpenNewTab = manager.get().internalDelegate.openNewTab; |
| manager.get().internalDelegate.openNewTab = ^(WKWebExtensionTabConfiguration *configuration, WKWebExtensionContext *context, void (^completionHandler)(id<WKWebExtensionTab>, NSError *)) { |
| EXPECT_NS_EQUAL(configuration.url, [NSURL URLWithString:@"https://example.com/"]); |
| EXPECT_NS_EQUAL(configuration.window, manager.get().defaultWindow); |
| EXPECT_EQ(configuration.index, 1ul); |
| EXPECT_EQ(configuration.shouldBeActive, YES); |
| |
| originalOpenNewTab(configuration, context, completionHandler); |
| |
| [manager done]; |
| }; |
| #endif |
| |
| [manager runUntilTestMessage:@"Open Popup"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, EmptyAction) |
| { |
| static auto *actionManifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Test Action", |
| @"description": @"Test Action", |
| @"version": @"1.0", |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| |
| @"action": @{ } |
| }; |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"if (browser.action) {", |
| @" browser.test.notifyPass()", |
| @"} else {", |
| @" browser.test.notifyFail('browser.action should be defined when it is empty.')", |
| @"}" |
| ]); |
| |
| Util::loadAndRunExtension(actionManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIAction, ClickedEventAndPermissionsRequest) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.action.setPopup({ popup: '' })", |
| |
| @"browser.action.onClicked.addListener(async (tab) => {", |
| @" try {", |
| @" const result = await browser.permissions.request({ 'permissions': [ 'webNavigation' ] })", |
| @" if (result)", |
| @" browser.test.notifyPass()", |
| @" else", |
| @" browser.test.notifyFail('Permissions request was rejected')", |
| @" } catch (error) {", |
| @" browser.test.notifyFail('Permissions request failed')", |
| @" }", |
| @"})", |
| |
| @"browser.test.sendMessage('Test Action')" |
| ]); |
| |
| auto manager = Util::loadExtension(actionPopupManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().internalDelegate.promptForPermissions = ^(id<WKWebExtensionTab> tab, NSSet<NSString *> *requestedPermissions, void (^callback)(NSSet<NSString *> *, NSDate *)) { |
| EXPECT_EQ(requestedPermissions.count, 1lu); |
| EXPECT_TRUE([requestedPermissions isEqualToSet:[NSSet setWithObject:@"webNavigation"]]); |
| callback(requestedPermissions, nil); |
| }; |
| |
| [manager runUntilTestMessage:@"Test Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIAction, SubframeNavigation) |
| { |
| TestWebKitAPI::HTTPServer server({ |
| { "/"_s, { { { "Content-Type"_s, "text/html"_s } }, "<script>browser.test.notifyPass()</script>"_s } }, |
| }, TestWebKitAPI::HTTPServer::Protocol::Http); |
| |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.sendMessage('Test Popup Action')" |
| ]); |
| |
| auto *resources = @{ |
| @"background.js": backgroundScript, |
| @"popup.html": [NSString stringWithFormat:@"<iframe src='%@'></iframe>", server.requestWithLocalhost("/"_s).URL], |
| }; |
| |
| auto manager = Util::loadExtension(actionPopupManifest, resources); |
| |
| manager.get().internalDelegate.presentPopupForAction = ^(WKWebExtensionAction *action) { |
| EXPECT_TRUE(action.presentsPopup); |
| EXPECT_NOT_NULL(action.popupWebView); |
| }; |
| |
| [manager runUntilTestMessage:@"Test Popup Action"]; |
| |
| [manager.get().context performActionForTab:manager.get().defaultTab]; |
| |
| [manager run]; |
| } |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WK_WEB_EXTENSIONS) |