| /* |
| * 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 "TestWebExtensionsDelegate.h" |
| #import "WebExtensionUtilities.h" |
| #import <WebKit/WKWebExtensionWindowConfiguration.h> |
| |
| @interface DummyWebExtensionWindow : NSObject <WKWebExtensionWindow> |
| @end |
| |
| @implementation DummyWebExtensionWindow |
| @end |
| |
| namespace TestWebKitAPI { |
| |
| static auto *windowsManifest = @{ |
| @"manifest_version": @3, |
| |
| @"name": @"Windows Test", |
| @"description": @"Windows Test", |
| @"version": @"1", |
| |
| @"options_page": @"options.html", |
| |
| @"background": @{ |
| @"scripts": @[ @"background.js" ], |
| @"type": @"module", |
| @"persistent": @NO, |
| }, |
| }; |
| |
| TEST(WKWebExtensionAPIWindows, Errors) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertThrows(() => browser.windows.get('bad'), /'windowId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(NaN), /'windowId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(Infinity), /'windowId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(-Infinity), /'windowId' value is invalid, because a number is expected/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(-3), /'windowId' value is invalid, because it is not a window identifier/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(1.2), /'windowId' value is invalid, because it is not a window identifier/i)", |
| @"browser.test.assertThrows(() => browser.windows.get(browser.windows.WINDOW_ID_NONE), /'windowId' value is invalid, because 'windows.WINDOW_ID_NONE' is not allowed/i)", |
| |
| @"await browser.test.assertRejects(browser.windows.get(42), /window not found/i)", |
| |
| #if PLATFORM(MAC) |
| // iOS does not support create() and update(). |
| @"await browser.test.assertRejects(browser.windows.update(99, { focused: true }), /window not found/i)", |
| |
| @"browser.test.assertThrows(() => browser.windows.create({ url: 42 }), /'url' is expected to be a string or an array of strings, but a number was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ left: 'bad' }), /'left' is expected to be a number/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ top: 'bad' }), /'top' is expected to be a number/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ width: 'bad' }), /'width' is expected to be a number/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ height: 'bad' }), /'height' is expected to be a number/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ incognito: 'bad' }), /'incognito' is expected to be a boolean/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ focused: 'bad' }), /'focused' is expected to be a boolean/i)", |
| @"browser.test.assertThrows(() => browser.windows.create({ tabId: 'bad' }), /'tabId' is expected to be a number/i)", |
| |
| @"const window = await browser.test.assertSafeResolve(() => browser.windows.getCurrent())", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { left: 'bad' }), /'left' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { top: 'bad' }), /'top' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { width: 'bad' }), /'width' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { height: 'bad' }), /'height' is expected to be a number, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { focused: 'bad' }), /'focused' is expected to be a boolean, but a string was provided/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { state: 'bad' }), /'state' value is invalid, because it must specify 'normal', 'minimized', 'maximized', or 'fullscreen'/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { top: 100, state: 'fullscreen' }), /'properties' value is invalid, because when 'top', 'left', 'width', or 'height' are specified, 'state' must specify 'normal'/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { left: 100, state: 'minimized' }), /'properties' value is invalid, because when 'top', 'left', 'width', or 'height' are specified, 'state' must specify 'normal'/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { width: 100, state: 'maximized' }), /'properties' value is invalid, because when 'top', 'left', 'width', or 'height' are specified, 'state' must specify 'normal'/i)", |
| @"browser.test.assertThrows(() => browser.windows.update(window.id, { height: 100, state: 'fullscreen' }), /'properties' value is invalid, because when 'top', 'left', 'width', or 'height' are specified, 'state' must specify 'normal'/i)", |
| #endif |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| Util::loadAndRunExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetCurrent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const window = await browser.windows.getCurrent();", |
| |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object');", |
| @"browser.test.assertEq(typeof window.id, 'number', 'The window id should be a number');", |
| @"browser.test.assertEq(window.type, 'normal', 'Window type should be normal');", |
| @"browser.test.assertEq(window.state, 'normal', 'Window state should be normal');", |
| @"browser.test.assertTrue(window.focused, 'Window should be focused');", |
| @"browser.test.assertFalse(window.incognito, 'Window should not be in incognito mode');", |
| @"browser.test.assertFalse(window.alwaysOnTop, 'Window should not be always on top');", |
| @"browser.test.assertEq(window.top, 50, 'Window top position should be 50');", |
| @"browser.test.assertEq(window.left, 100, 'Window left position should be 100');", |
| @"browser.test.assertEq(window.width, 800, 'Window width should be 800');", |
| @"browser.test.assertEq(window.height, 600, 'Window height should be 600');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| Util::loadAndRunExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetCurrentFromOptionsPage) |
| { |
| auto *optionsScript = Util::constructScript(@[ |
| @"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 *resources = @{ |
| @"background.js": @"// Not Used", |
| @"options.html": @"<script type='module' src='options.js'></script>", |
| @"options.js": optionsScript |
| }; |
| |
| auto manager = Util::loadExtension(windowsManifest, resources); |
| |
| [manager.get().defaultWindow openNewTab]; |
| |
| EXPECT_EQ(manager.get().defaultWindow.tabs.count, 2lu); |
| |
| auto *optionsPageURL = manager.get().context.optionsPageURL; |
| EXPECT_NOT_NULL(optionsPageURL); |
| |
| auto *defaultTab = manager.get().defaultTab; |
| EXPECT_NOT_NULL(defaultTab); |
| |
| [defaultTab changeWebViewIfNeededForURL:optionsPageURL forExtensionContext:manager.get().context]; |
| [defaultTab.webView loadRequest:[NSURLRequest requestWithURL:optionsPageURL]]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetCurrentWindowAfterTabMove) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const initialWindow = await browser.windows.getCurrent()", |
| @"browser.test.assertEq(typeof initialWindow, 'object', 'Initial window should be an object')", |
| @"browser.test.assertEq(typeof initialWindow.id, 'number', 'Initial window ID should be a number')", |
| |
| @"browser.tabs.onAttached.addListener(async (tabId, attachInfo) => {", |
| @" const currentWindowAfterMove = await browser.windows.getCurrent()", |
| @" browser.test.assertEq(currentWindowAfterMove.id, initialWindow.id, 'Current window ID should remain the initial window')", |
| |
| @" browser.test.notifyPass()", |
| @"})", |
| |
| @"browser.test.sendMessage('Move Tab')", |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *newTab = [manager.get().defaultWindow openNewTab]; |
| auto *newWindow = [manager openNewWindow]; |
| |
| [manager runUntilTestMessage:@"Move Tab"]; |
| |
| [newWindow moveTab:newTab toIndex:0]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetLastFocused) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const window = await browser.windows.getLastFocused();", |
| |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object');", |
| @"browser.test.assertEq(typeof window.id, 'number', 'The window id should be a number');", |
| @"browser.test.assertEq(window.type, 'normal', 'Window type should be normal');", |
| @"browser.test.assertEq(window.state, 'normal', 'Window state should be normal');", |
| @"browser.test.assertFalse(window.focused, 'Window should be focused');", |
| @"browser.test.assertFalse(window.incognito, 'Window should not be in incognito mode');", |
| @"browser.test.assertFalse(window.alwaysOnTop, 'Window should not be always on top');", |
| @"browser.test.assertEq(window.top, 50, 'Window top position should be 50');", |
| @"browser.test.assertEq(window.left, 100, 'Window left position should be 100');", |
| @"browser.test.assertEq(window.width, 800, 'Window width should be 800');", |
| @"browser.test.assertEq(window.height, 600, 'Window height should be 600');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| auto manager = Util::parseExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().internalDelegate.focusedWindow = ^id<WKWebExtensionWindow>(WKWebExtensionContext *) { |
| return nil; |
| }; |
| |
| [manager loadAndRun]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetLastFocusedWithPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const lastFocusedWindow = await browser.windows.getLastFocused()", |
| |
| @"browser.test.assertEq(typeof lastFocusedWindow, 'object', 'Last focused window should be an object')", |
| @"browser.test.assertTrue(lastFocusedWindow.incognito, 'Last focused window should be incognito')", |
| |
| @"browser.test.notifyPass()", |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.hasAccessToPrivateData = YES; |
| |
| auto *privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES]; |
| [manager focusWindow:privateWindow]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetLastFocusedWithoutPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const lastFocusedWindow = await browser.windows.getLastFocused()", |
| |
| @"browser.test.assertEq(typeof lastFocusedWindow, 'object', 'Last focused window should be an object')", |
| @"browser.test.assertFalse(lastFocusedWindow.incognito, 'Last focused window should not be incognito')", |
| |
| @"browser.test.notifyPass()", |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.hasAccessToPrivateData = NO; |
| |
| auto *privateWindow = [manager openNewWindowUsingPrivateBrowsing:YES]; |
| [manager focusWindow:privateWindow]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetAll) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windows = await browser.windows.getAll();", |
| @"const windowOne = windows[0];", |
| |
| @"browser.test.assertEq(windows.length, 1, 'One window should be returned');", |
| |
| @"browser.test.assertEq(typeof windowOne, 'object', 'windowOne should be an object');", |
| @"browser.test.assertEq(typeof windowOne.id, 'number', 'windowOne id should be a number');", |
| @"browser.test.assertEq(windowOne.type, 'normal', 'windowOne type should be normal');", |
| @"browser.test.assertEq(windowOne.state, 'normal', 'windowOne state should be normal');", |
| @"browser.test.assertTrue(windowOne.focused, 'windowOne should be focused');", |
| @"browser.test.assertFalse(windowOne.incognito, 'windowOne should not be in incognito mode');", |
| @"browser.test.assertFalse(windowOne.alwaysOnTop, 'windowOne should not be always on top');", |
| @"browser.test.assertEq(windowOne.top, 50, 'windowOne top position should be 50');", |
| @"browser.test.assertEq(windowOne.left, 100, 'windowOne left position should be 100');", |
| @"browser.test.assertEq(windowOne.width, 800, 'windowOne width should be 800');", |
| @"browser.test.assertEq(windowOne.height, 600, 'windowOne height should be 600');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| auto manager = Util::parseExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| EXPECT_FALSE(manager.get().context.hasAccessToPrivateData); |
| |
| [manager openNewWindowUsingPrivateBrowsing:YES]; |
| |
| [manager loadAndRun]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, GetAllWithPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windows = await browser.windows.getAll();", |
| @"const windowOne = windows[0];", |
| @"const windowTwo = windows[1];", |
| |
| @"browser.test.assertEq(windows.length, 2, 'Two windows should be returned');", |
| |
| // Validate first window's properties |
| @"browser.test.assertEq(typeof windowOne, 'object', 'windowOne should be an object');", |
| @"browser.test.assertEq(typeof windowOne.id, 'number', 'windowOne id should be a number');", |
| @"browser.test.assertEq(windowOne.type, 'normal', 'windowOne type should be normal');", |
| @"browser.test.assertEq(windowOne.state, 'normal', 'windowOne state should be normal');", |
| @"browser.test.assertTrue(windowOne.focused, 'windowOne should be focused');", |
| @"browser.test.assertFalse(windowOne.incognito, 'windowOne should not be in incognito mode');", |
| @"browser.test.assertFalse(windowOne.alwaysOnTop, 'windowOne should not be always on top');", |
| @"browser.test.assertEq(windowOne.top, 50, 'windowOne top position should be 50');", |
| @"browser.test.assertEq(windowOne.left, 100, 'windowOne left position should be 100');", |
| @"browser.test.assertEq(windowOne.width, 800, 'windowOne width should be 800');", |
| @"browser.test.assertEq(windowOne.height, 600, 'windowOne height should be 600');", |
| |
| // Validate second window's properties |
| @"browser.test.assertEq(typeof windowTwo, 'object', 'windowTwo should be an object');", |
| @"browser.test.assertEq(typeof windowTwo.id, 'number', 'windowTwo id should be a number');", |
| @"browser.test.assertEq(windowTwo.type, 'popup', 'windowTwo type should be popup');", |
| @"browser.test.assertEq(windowTwo.state, 'minimized', 'windowTwo state should be minimized');", |
| @"browser.test.assertFalse(windowTwo.focused, 'windowTwo should not be focused');", |
| @"browser.test.assertTrue(windowTwo.incognito, 'windowTwo should be in incognito mode');", |
| @"browser.test.assertEq(windowTwo.top, 75, 'windowTwo top position should be 75');", |
| @"browser.test.assertEq(windowTwo.left, 110, 'windowTwo left position should be 110');", |
| @"browser.test.assertEq(windowTwo.width, 300, 'windowTwo width should be 300');", |
| @"browser.test.assertEq(windowTwo.height, 700, 'windowTwo height should be 700');", |
| |
| // Validate that windowOne.id and windowTwo.id are different |
| @"browser.test.assertTrue(windowOne.id !== windowTwo.id, 'windowOne.id and windowTwo.id should be different');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| auto manager = Util::parseExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.hasAccessToPrivateData = YES; |
| |
| auto *windowTwo = [manager openNewWindowUsingPrivateBrowsing:YES]; |
| |
| #if PLATFORM(MAC) |
| // This is 75pt from top on a screen of 1920 x 1080 in Mac screen coordinates. |
| windowTwo.frame = CGRectMake(110, 305, 300, 700); |
| #else |
| windowTwo.frame = CGRectMake(110, 75, 300, 700); |
| #endif |
| windowTwo.windowState = WKWebExtensionWindowStateMinimized; |
| windowTwo.windowType = WKWebExtensionWindowTypePopup; |
| |
| [manager loadAndRun]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, CreatedEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.windows.onCreated.addListener((window) => {", |
| |
| @" browser.test.assertEq(typeof window, 'object', 'The window should be an object');", |
| @" browser.test.assertEq(typeof window.id, 'number', 'The window id should be a number');", |
| @" browser.test.assertEq(window.type, 'normal', 'Window type should be normal');", |
| @" browser.test.assertEq(window.state, 'normal', 'Window state should be normal');", |
| @" browser.test.assertTrue(window.focused, 'Window should be focused');", |
| @" browser.test.assertFalse(window.incognito, 'Window should not be in incognito mode');", |
| @" browser.test.assertFalse(window.alwaysOnTop, 'Window should not be always on top');", |
| @" browser.test.assertEq(window.top, 50, 'Window top position should be 50');", |
| @" browser.test.assertEq(window.left, 100, 'Window left position should be 100');", |
| @" browser.test.assertEq(window.width, 800, 'Window width should be 800');", |
| @" browser.test.assertEq(window.height, 600, 'Window height should be 600');", |
| |
| @" browser.test.notifyPass();", |
| |
| @"});", |
| |
| @"browser.test.sendMessage('Open Window');" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| [manager runUntilTestMessage:@"Open Window"]; |
| |
| [manager openNewWindow]; |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, FocusChangedEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"let focusedTwice = false;", |
| |
| @"browser.windows.onFocusChanged.addListener((windowId) => {", |
| @" if (windowId !== browser.windows.WINDOW_ID_NONE) {", |
| @" if (!focusedTwice) {", |
| @" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number when a window is focused');", |
| @" browser.test.sendMessage('Focus None');", |
| @" focusedTwice = true;", |
| @" } else {", |
| @" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number when a window is focused again');", |
| @" browser.test.notifyPass();", |
| @" }", |
| @" } else {", |
| @" browser.test.assertEq(windowId, browser.windows.WINDOW_ID_NONE, 'The window id should be WINDOW_ID_NONE when nil is focused');", |
| @" browser.test.sendMessage('Focus Window Again');", |
| @" }", |
| @"});", |
| |
| @"browser.test.sendMessage('Focus Window');" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| auto *windowOne = manager.get().defaultWindow; |
| auto *windowTwo = [manager openNewWindow]; |
| |
| [manager runUntilTestMessage:@"Focus Window"]; |
| |
| [manager focusWindow:windowOne]; |
| |
| [manager runUntilTestMessage:@"Focus None"]; |
| |
| [manager focusWindow:nil]; |
| |
| [manager runUntilTestMessage:@"Focus Window Again"]; |
| |
| [manager focusWindow:windowTwo]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, RemovedEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.windows.onRemoved.addListener((windowId) => {", |
| @" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number');", |
| |
| @" browser.test.notifyPass();", |
| @"});", |
| |
| @"browser.test.sendMessage('Close Window');" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| [manager openNewWindow]; |
| |
| [manager runUntilTestMessage:@"Close Window"]; |
| |
| [manager closeWindow:manager.get().defaultWindow]; |
| |
| [manager run]; |
| } |
| |
| TEST(WKWebExtensionAPIWindows, RemoveListenerDuringEvent) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"function windowListener() {", |
| @" browser.windows.onCreated.removeListener(windowListener)", |
| @" browser.test.assertFalse(browser.windows.onCreated.hasListener(windowListener), 'Listener should be removed')", |
| @"}", |
| |
| @"browser.windows.onCreated.addListener(windowListener)", |
| @"browser.windows.onCreated.addListener(() => browser.test.notifyPass())", |
| |
| @"browser.test.assertTrue(browser.windows.onCreated.hasListener(windowListener), 'Listener should be registered')", |
| |
| @"browser.test.sendMessage('Open Window')" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| [manager runUntilTestMessage:@"Open Window"]; |
| |
| [manager openNewWindow]; |
| [manager run]; |
| } |
| |
| #if PLATFORM(MAC) |
| |
| // iOS does not support create() and update(). |
| |
| TEST(WKWebExtensionAPIWindows, Create) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windowOptions = {", |
| @" focused: true,", |
| @" left: 300,", |
| @" height: 400,", |
| @" incognito: false,", |
| @" state: 'normal',", |
| @" type: 'popup',", |
| @" url: 'http://example.com/',", |
| @"}", |
| |
| @"const window = await browser.windows.create(windowOptions)", |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object')", |
| @"browser.test.assertEq(window?.top, 50, 'The window should have the specified top')", |
| @"browser.test.assertEq(window?.left, 300, 'The window should have the specified left')", |
| @"browser.test.assertEq(window?.width, 800, 'The window should have the specified width')", |
| @"browser.test.assertEq(window?.height, 400, 'The window should have the specified height')", |
| @"browser.test.assertFalse(window?.incognito, 'The window should not be in incognito mode')", |
| @"browser.test.assertEq(window?.type, 'popup', 'The window should be of type popup')", |
| @"browser.test.assertEq(window?.state, 'normal', 'The window state should be normal')", |
| @"browser.test.assertTrue(window?.focused, 'The window should be focused')", |
| |
| @"browser.test.assertEq(typeof window?.tabs, 'object', 'The window should have a tabs array')", |
| @"browser.test.assertEq(window?.tabs?.length, 1, 'The tabs array should contain one tab')", |
| |
| @"const tab = window?.tabs?.[0]", |
| @"browser.test.assertEq(typeof tab, 'object', 'The tab should be an object')", |
| @"browser.test.assertEq(tab?.active, true, 'The tab should be active')", |
| @"browser.test.assertEq(tab?.incognito, false, 'The tab should not be in incognito mode')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| auto originalOpenNewWindow = manager.get().internalDelegate.openNewWindow; |
| |
| manager.get().internalDelegate.openNewWindow = ^(WKWebExtensionWindowConfiguration *configuration, WKWebExtensionContext *context, void (^completionHandler)(id<WKWebExtensionWindow>, NSError *)) { |
| EXPECT_EQ(configuration.frame.origin.x, 300); |
| EXPECT_EQ(configuration.frame.size.height, 400); |
| EXPECT_TRUE(std::isnan(configuration.frame.origin.y)); |
| EXPECT_TRUE(std::isnan(configuration.frame.size.width)); |
| |
| EXPECT_TRUE(configuration.shouldBeFocused); |
| EXPECT_FALSE(configuration.shouldBePrivate); |
| |
| EXPECT_EQ(configuration.windowType, WKWebExtensionWindowTypePopup); |
| EXPECT_EQ(configuration.windowState, WKWebExtensionWindowStateNormal); |
| |
| EXPECT_EQ(configuration.tabs.count, 0lu); |
| |
| EXPECT_EQ(configuration.tabURLs.count, 1lu); |
| EXPECT_NS_EQUAL(configuration.tabURLs.firstObject, [NSURL URLWithString:@"http://example.com/"]); |
| |
| originalOpenNewWindow(configuration, context, completionHandler); |
| }; |
| |
| EXPECT_FALSE(manager.get().context.hasAccessToPrivateData); |
| EXPECT_EQ(manager.get().windows.count, 1lu); |
| |
| [manager run]; |
| |
| EXPECT_EQ(manager.get().windows.count, 2lu); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, CreateWithRelativeURL) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windowOptions = {", |
| @" url: 'test.html'", |
| @"}", |
| |
| @"const window = await browser.windows.create(windowOptions)", |
| |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object')", |
| @"browser.test.assertEq(typeof window?.tabs, 'object', 'The window should have a tabs array')", |
| @"browser.test.assertEq(window?.tabs?.length, 1, 'The tabs array should contain one tab')", |
| |
| @"const tab = window?.tabs?.[0]", |
| @"browser.test.assertEq(typeof tab, 'object', 'The tab should be an object')", |
| @"browser.test.assertEq(tab?.url, browser.runtime.getURL('/test.html'), 'The tab URL should match the runtime-generated test.html URL')", |
| @"browser.test.assertEq(tab?.active, true, 'The tab should be active')", |
| @"browser.test.assertEq(tab?.incognito, false, 'The tab should not be in incognito mode')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript, @"test.html": @"Hello world!" }); |
| |
| 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:@"test.html" relativeToURL:manager.get().context.baseURL].absoluteURL); |
| |
| originalOpenNewWindow(configuration, context, completionHandler); |
| }; |
| |
| EXPECT_EQ(manager.get().windows.count, 1lu); |
| |
| [manager run]; |
| |
| EXPECT_EQ(manager.get().windows.count, 2lu); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, CreateWithRelativeURLs) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windowOptions = {", |
| @" url: [ 'one.html', 'two.html' ]", |
| @"}", |
| |
| @"const window = await browser.windows.create(windowOptions)", |
| |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object')", |
| @"browser.test.assertEq(typeof window?.tabs, 'object', 'The window should have a tabs array')", |
| @"browser.test.assertEq(window?.tabs?.length, 2, 'The tabs array should contain two tabs')", |
| |
| @"const firstTab = window?.tabs?.[0]", |
| @"browser.test.assertEq(typeof firstTab, 'object', 'The first tab should be an object')", |
| @"browser.test.assertEq(firstTab?.url, browser.runtime.getURL('/one.html'), 'The first tab URL should match the runtime-generated one.html URL')", |
| @"browser.test.assertEq(firstTab?.active, true, 'The first tab should be active')", |
| @"browser.test.assertEq(firstTab?.incognito, false, 'The first tab should not be in incognito mode')", |
| |
| @"const secondTab = window?.tabs?.[1]", |
| @"browser.test.assertEq(typeof secondTab, 'object', 'The second tab should be an object')", |
| @"browser.test.assertEq(secondTab?.url, browser.runtime.getURL('/two.html'), 'The second tab URL should match the runtime-generated two.html URL')", |
| @"browser.test.assertEq(secondTab?.active, false, 'The second tab should not be active')", |
| @"browser.test.assertEq(secondTab?.incognito, false, 'The second tab should not be in incognito mode')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript, @"one.html": @"Hello one!", @"two.html": @"Hello two!" }); |
| |
| auto originalOpenNewWindow = manager.get().internalDelegate.openNewWindow; |
| |
| manager.get().internalDelegate.openNewWindow = ^(WKWebExtensionWindowConfiguration *configuration, WKWebExtensionContext *context, void (^completionHandler)(id<WKWebExtensionWindow>, NSError *)) { |
| EXPECT_EQ(configuration.tabURLs.count, 2lu); |
| EXPECT_NS_EQUAL(configuration.tabURLs.firstObject, [NSURL URLWithString:@"one.html" relativeToURL:manager.get().context.baseURL].absoluteURL); |
| EXPECT_NS_EQUAL(configuration.tabURLs.lastObject, [NSURL URLWithString:@"two.html" relativeToURL:manager.get().context.baseURL].absoluteURL); |
| |
| originalOpenNewWindow(configuration, context, completionHandler); |
| }; |
| |
| EXPECT_EQ(manager.get().windows.count, 1lu); |
| |
| [manager run]; |
| |
| EXPECT_EQ(manager.get().windows.count, 2lu); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, CreateIncognitoWithoutPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windowOptions = {", |
| @" focused: true,", |
| @" left: 300,", |
| @" height: 400,", |
| @" incognito: true,", |
| @" state: 'normal',", |
| @" type: 'popup',", |
| @"};", |
| |
| @"const window = await browser.windows.create(windowOptions);", |
| @"browser.test.assertEq(window, null, 'The window should be created but null without access');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| EXPECT_FALSE(manager.get().context.hasAccessToPrivateData); |
| EXPECT_EQ(manager.get().windows.count, 1lu); |
| |
| [manager run]; |
| |
| EXPECT_EQ(manager.get().windows.count, 2lu); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, CreateIncognitoWithPrivateAccess) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const windowOptions = {", |
| @" focused: true,", |
| @" left: 300,", |
| @" height: 400,", |
| @" incognito: true,", |
| @" state: 'normal',", |
| @" type: 'popup',", |
| @"}", |
| |
| @"const window = await browser.windows.create(windowOptions)", |
| @"browser.test.assertEq(typeof window, 'object', 'The window should be an object')", |
| @"browser.test.assertEq(window?.top, 50, 'The window should have the specified top')", |
| @"browser.test.assertEq(window?.left, 300, 'The window should have the specified left')", |
| @"browser.test.assertEq(window?.width, 800, 'The window should have the specified width')", |
| @"browser.test.assertEq(window?.height, 400, 'The window should have the specified height')", |
| @"browser.test.assertTrue(window?.incognito, 'The window should be in incognito mode')", |
| @"browser.test.assertEq(window?.type, 'popup', 'The window should be of type popup')", |
| @"browser.test.assertEq(window?.state, 'normal', 'The window state should be normal')", |
| @"browser.test.assertTrue(window?.focused, 'The window should be focused')", |
| |
| @"browser.test.assertEq(typeof window?.tabs, 'object', 'The window should have a tabs array')", |
| @"browser.test.assertEq(window?.tabs?.length, 1, 'The tabs array should contain one tab')", |
| |
| @"const tab = window?.tabs?.[0]", |
| @"browser.test.assertEq(typeof tab, 'object', 'The tab should be an object')", |
| @"browser.test.assertEq(tab?.active, true, 'The tab should be active')", |
| @"browser.test.assertTrue(tab?.incognito, 'The tab should be in incognito mode')", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::loadExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.hasAccessToPrivateData = YES; |
| |
| EXPECT_EQ(manager.get().windows.count, 1lu); |
| |
| [manager run]; |
| |
| EXPECT_EQ(manager.get().windows.count, 2lu); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, Update) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"const newWindow = await browser.windows.create();", |
| |
| @"browser.test.assertEq(newWindow.top, 50, 'The new window top position should be 50 initially');", |
| @"browser.test.assertEq(newWindow.left, 100, 'The new window left position should be 100 initially');", |
| @"browser.test.assertEq(newWindow.width, 800, 'The new window width should be 800 initially');", |
| @"browser.test.assertEq(newWindow.height, 600, 'The new window height should be 600 initially');", |
| @"browser.test.assertTrue(newWindow.focused, 'The new window should be focused initially');", |
| @"browser.test.assertEq(newWindow.state, 'normal', 'The window state should be normal initially');", |
| |
| @"let updatedWindow = await browser.windows.update(newWindow.id, { top: 10, width: 500, focused: true });", |
| |
| @"browser.test.assertEq(updatedWindow.top, 10, 'The window top position should be updated to 10');", |
| @"browser.test.assertEq(updatedWindow.left, 100, 'The window left position should remain 100 after the first update');", |
| @"browser.test.assertEq(updatedWindow.width, 500, 'The window width should be updated to 500');", |
| @"browser.test.assertEq(updatedWindow.height, 600, 'The window height should remain 600 after the first update');", |
| @"browser.test.assertTrue(updatedWindow.focused, 'The window should be focused');", |
| @"browser.test.assertEq(updatedWindow.state, 'normal', 'The window state should remain normal after the first update');", |
| |
| @"updatedWindow = await browser.windows.update(newWindow.id, { state: 'fullscreen' });", |
| |
| @"browser.test.assertEq(updatedWindow.state, 'fullscreen', 'The window state should be updated to fullscreen');", |
| @"browser.test.assertEq(updatedWindow.top, 0, 'The window top position should be 0 in fullscreen');", |
| @"browser.test.assertEq(updatedWindow.left, 0, 'The window left position should be 0 in fullscreen');", |
| @"browser.test.assertEq(updatedWindow.width, 1920, 'The window width should be 1920 in fullscreen');", |
| @"browser.test.assertEq(updatedWindow.height, 1080, 'The window height should be 1080 in fullscreen');", |
| |
| @"updatedWindow = await browser.windows.update(newWindow.id, { state: 'normal' });", |
| |
| @"browser.test.assertEq(updatedWindow.top, 10, 'The window top position should be reverted back to 10');", |
| @"browser.test.assertEq(updatedWindow.left, 100, 'The window left position should remain 100');", |
| @"browser.test.assertEq(updatedWindow.width, 500, 'The window width should be reverted back to 500');", |
| @"browser.test.assertEq(updatedWindow.height, 600, 'The window height should be reverted back to 600');", |
| @"browser.test.assertEq(updatedWindow.state, 'normal', 'The window state should be reverted back to normal');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| Util::loadAndRunExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, Remove) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| // Create a new window and verify the count |
| @"const initialWindows = await browser.windows.getAll();", |
| @"const newWindow = await browser.windows.create();", |
| |
| @"const windowsAfterCreate = await browser.windows.getAll();", |
| @"browser.test.assertEq(windowsAfterCreate.length, initialWindows.length + 1, 'One window should be added after creation');", |
| |
| // Remove the window and verify the count |
| @"await browser.windows.remove(newWindow.id);", |
| |
| @"const windowsAfterRemove = await browser.windows.getAll();", |
| @"browser.test.assertEq(windowsAfterRemove.length, initialWindows.length, 'Window count should match the initial count after removal');", |
| |
| @"browser.test.notifyPass();" |
| ]); |
| |
| Util::loadAndRunExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| } |
| |
| TEST(WKWebExtensionAPIWindows, RemoveUnsupported) |
| { |
| auto *backgroundScript = Util::constructScript(@[ |
| @"browser.test.assertEq(browser.windows.remove, undefined)", |
| |
| @"browser.test.notifyPass()" |
| ]); |
| |
| auto manager = Util::parseExtension(windowsManifest, @{ @"background.js": backgroundScript }); |
| |
| manager.get().context.unsupportedAPIs = [NSSet setWithObject:@"browser.windows.remove"]; |
| |
| [manager loadAndRun]; |
| } |
| |
| #endif // PLATFORM(MAC) |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WK_WEB_EXTENSIONS) |