From d73837e6771469ab81dbc3cc831987fa6c5f5381 Mon Sep 17 00:00:00 2001 From: Graham Langford <30706330+grahamlangford@users.noreply.github.com> Date: Wed, 29 May 2024 15:25:08 -0500 Subject: [PATCH] Remove mv2 code final (#8523) * src/background/browserAction * src/background/sidePanel * simplify comments, remove unused code paths * fix tests * code comments * cleanup comments * remove mv2 code from sidebar * lint and ts fixes * removes more mv3 checks * further cleanup * remove mv2 sessionStorage shim * fix strictNullChecks issue * reverts bad deletion * cleanup * cleanup --- src/background/setToolbarBadge.test.ts | 2 - .../ephemeralForm/formTransformer.test.ts | 1 - .../browserActionInstantHandler.ts | 21 --- src/contentScript/contentScriptCore.ts | 20 --- src/contentScript/sidebarController.tsx | 127 +++------------ src/contentScript/sidebarDomControllerLite.ts | 150 ------------------ .../pages/settings/ExperimentalSettings.tsx | 18 +-- src/mv3/SessionStorage.test.ts | 24 +++ src/mv3/SessionStorage.ts | 25 +-- src/mv3/api.ts | 12 +- src/sandbox/messenger/api.ts | 49 ++---- .../sidebar/sidebarExtension.test.ts | 13 +- src/store/settings/settingsTypes.ts | 8 +- src/tsconfig.strictNullChecks.json | 2 - src/utils/notify.tsx | 5 - src/utils/sidePanelUtils.ts | 14 +- webpack.config.mjs | 1 - 17 files changed, 85 insertions(+), 407 deletions(-) delete mode 100644 src/contentScript/browserActionInstantHandler.ts delete mode 100644 src/contentScript/sidebarDomControllerLite.ts diff --git a/src/background/setToolbarBadge.test.ts b/src/background/setToolbarBadge.test.ts index 12cd673370..bd65bde251 100644 --- a/src/background/setToolbarBadge.test.ts +++ b/src/background/setToolbarBadge.test.ts @@ -27,8 +27,6 @@ jest.mock("@/mv3/api", () => ({ setBadgeBackgroundColor: jest.fn(), setBadgeText: jest.fn(), }, - // TODO: Remove when MV2 code is dropped from src/contentScript - isMV3: jest.fn(() => true), })); describe("setToolbarBadge", () => { diff --git a/src/bricks/transformers/ephemeralForm/formTransformer.test.ts b/src/bricks/transformers/ephemeralForm/formTransformer.test.ts index 0c2eddf88b..18409714d5 100644 --- a/src/bricks/transformers/ephemeralForm/formTransformer.test.ts +++ b/src/bricks/transformers/ephemeralForm/formTransformer.test.ts @@ -27,7 +27,6 @@ import { showModal } from "@/contentScript/modalDom"; jest.mock("@/utils/iframeUtils"); jest.mock("@/contentScript/modalDom"); -jest.mock("@/contentScript/sidebarDomControllerLite"); const showModalMock = jest.mocked(showModal); diff --git a/src/contentScript/browserActionInstantHandler.ts b/src/contentScript/browserActionInstantHandler.ts deleted file mode 100644 index 44c3576ca1..0000000000 --- a/src/contentScript/browserActionInstantHandler.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 PixieBrix, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -/** @file This file MUST be lightweight and free of any logic and dependencies, it's meant to be instant */ -import { toggleSidebarFrame } from "@/contentScript/sidebarDomControllerLite"; - -toggleSidebarFrame(); diff --git a/src/contentScript/contentScriptCore.ts b/src/contentScript/contentScriptCore.ts index 610caa7cc5..9c9c4459bb 100644 --- a/src/contentScript/contentScriptCore.ts +++ b/src/contentScript/contentScriptCore.ts @@ -40,13 +40,6 @@ import initFloatingActions from "@/components/floatingActions/initFloatingAction import { initSidebarActivation } from "@/contentScript/sidebarActivation"; import { initPerformanceMonitoring } from "@/contentScript/performanceMonitoring"; import { initRuntime } from "@/runtime/reducePipeline"; -import { initSidebarFocusEvents } from "./sidebarController"; -import { - isSidebarFrameVisible, - removeSidebarFrame, -} from "@/contentScript/sidebarDomControllerLite"; -import { isMV3 } from "@/mv3/api"; -import { onContextInvalidated } from "webext-events"; import { setPlatform } from "@/platform/platformContext"; import { markDocumentAsFocusableByUser } from "@/utils/focusTracker"; import contentScriptPlatform from "@/contentScript/contentScriptPlatform"; @@ -95,7 +88,6 @@ export async function init(): Promise { void initNavigation(); - initSidebarFocusEvents(); void initSidebarActivation(); // Let the partner page know @@ -103,16 +95,4 @@ export async function init(): Promise { void initFloatingActions(); void initPerformanceMonitoring(); - - onContextInvalidated.addListener(() => { - // The sidebar breaks when the context is invalidated, so it's best to close it - // In MV3, this happens automatically - if (!isMV3() && isSidebarFrameVisible()) { - removeSidebarFrame(); - // TODO: Also notify closure in MV3. - // There it's more complicated to show this message ONLY if the sidebar was open - // because the sidebar is closed before this listener is called. - void notifyContextInvalidated(); - } - }); } diff --git a/src/contentScript/sidebarController.tsx b/src/contentScript/sidebarController.tsx index af671b957e..bd8c3b750c 100644 --- a/src/contentScript/sidebarController.tsx +++ b/src/contentScript/sidebarController.tsx @@ -23,8 +23,6 @@ import * as contentScriptApi from "@/contentScript/messenger/strict/api"; import { isEmpty, throttle } from "lodash"; import { signalFromEvent } from "abort-utils"; import { SimpleEventTarget } from "@/utils/SimpleEventTarget"; -import * as sidebarMv2 from "@/contentScript/sidebarDomControllerLite"; -import { getSidebarElement } from "@/contentScript/sidebarDomControllerLite"; import { type Except } from "type-fest"; import { type RunArgs, RunReason } from "@/types/runtimeTypes"; import { type UUID } from "@/types/stringTypes"; @@ -42,12 +40,9 @@ import { getTemporaryPanelSidebarEntries } from "@/platform/panels/panelControll import { getFormPanelSidebarEntries } from "@/platform/forms/formController"; import { memoizeUntilSettled } from "@/utils/promiseUtils"; import { getTimedSequence } from "@/types/helpers"; -import { isMV3 } from "@/mv3/api"; import { focusCaptureDialog } from "@/contentScript/focusCaptureDialog"; import { isLoadedInIframe } from "@/utils/iframeUtils"; import { showMySidePanel } from "@/background/messenger/strict/api"; -import focusController from "@/utils/focusController"; -import selectionController from "@/utils/selectionController"; import { getTopLevelFrame, messenger } from "webext-messenger"; import { getSidebarTargetForCurrentTab, @@ -56,8 +51,6 @@ import { import pRetry from "p-retry"; import { hideNotification, showNotification } from "@/utils/notify"; -const HIDE_SIDEBAR_EVENT_NAME = "pixiebrix:hideSidebar"; - /** * Event listeners triggered when the sidebar shows and is ready to receive messages. */ @@ -72,7 +65,7 @@ let modActivationPanelEntry: ModActivationPanelEntry | null = null; * Only one check at a time * Cannot throttle because subsequent checks need to be able to be made immediately */ -const isSidePanelOpenMv3 = memoizeUntilSettled(async () => { +export const isSidePanelOpen = memoizeUntilSettled(async () => { try { await messenger( "SIDEBAR_PING", @@ -85,10 +78,6 @@ const isSidePanelOpenMv3 = memoizeUntilSettled(async () => { } }); -export const isSidePanelOpen = isMV3() - ? isSidePanelOpenMv3 - : sidebarMv2.isSidebarFrameVisible; - // - Only start one ping at a time // - Limit to one request every second (if the user closes the sidebar that quickly, we likely see those errors anyway) // - Throw custom error if the sidebar doesn't respond in time @@ -155,30 +144,25 @@ export async function showSidebarInTopFrame() { // We do not await the promise here since we want to show the sidebar as soon as possible to avoid the possibility // of the user needing to provide another user gesture to open the sidebar. - const sidebarInitiallyOpenPromise = isSidePanelOpenMv3(); + const sidebarInitiallyOpenPromise = isSidePanelOpen(); if (isLoadedInIframe()) { console.warn("showSidebarInTopFrame should not be called in an iframe"); } - // Defensively handle accidental calls from iframes - if (isMV3() || isLoadedInIframe()) { - try { - await showMySidePanel(); - } catch (error) { - if (!isUserGestureRequiredError(error)) { - throw error; - } - - await focusCaptureDialog({ - message: 'Click "Open Sidebar" to open the mod sidebar', - buttonText: "Open Sidebar", - signal: signalFromEvent(sidebarShowEvents, sidebarShowEvents.coreEvent), - }); - await showMySidePanel(); + try { + await showMySidePanel(); + } catch (error) { + if (!isUserGestureRequiredError(error)) { + throw error; } - } else if (!sidebarMv2.isSidebarFrameVisible()) { - sidebarMv2.insertSidebarFrame(); + + await focusCaptureDialog({ + message: 'Click "Open Sidebar" to open the mod sidebar', + buttonText: "Open Sidebar", + signal: signalFromEvent(sidebarShowEvents, sidebarShowEvents.coreEvent), + }); + await showMySidePanel(); } await pingSidebar(); @@ -587,29 +571,17 @@ export function getReservedPanelEntries(): { function sidePanelOnCloseSignal(): AbortSignal { const controller = new AbortController(); expectContext("contentScript"); - if (isMV3()) { - window.addEventListener( - "resize", - async () => { - // TODO: Replace with official event when available - // Official event requested in https://github.com/w3c/webextensions/issues/517 - if (!(await isSidePanelOpenMv3())) { - controller.abort(); - } - }, - { signal: controller.signal }, - ); - } else { - window.addEventListener( - HIDE_SIDEBAR_EVENT_NAME, - () => { + window.addEventListener( + "resize", + async () => { + // TODO: Replace with official event when available + // Official event requested in https://github.com/w3c/webextensions/issues/517 + if (!(await isSidePanelOpen())) { controller.abort(); - }, - { - signal: controller.signal, - }, - ); - } + } + }, + { signal: controller.signal }, + ); return controller.signal; } @@ -618,54 +590,3 @@ export function sidePanelOnClose(callback: () => void): void { const signal = sidePanelOnCloseSignal(); signal.addEventListener("abort", callback, { once: true }); } - -export function initSidebarFocusEvents(): void { - if (!isMV3()) { - // Add listeners to track keep track of focus with the MV2 sidebar. When the user interacts - // with the MV2 sidebar, the sidebar gets set as the document.activeElement. Required for brick - // functionality such as InsertAtCursorEffect - sidebarShowEvents.add(() => { - const sidebar = getSidebarElement(); - - if (!sidebar) { - // Should always exist because sidebarShowEvents is called on Sidebar App initialization - return; - } - - // Save focus on initial load, because the user may have `mouseenter`ed the sidebar before the React App - // fired the sidebarShowEvent event. For example, if the user clicked the browserAction toolbar button and - // immediately `mouseenter`ed the sidebar (because the top of the sidebar is very close to the top browserAction) - if (document.activeElement !== sidebar) { - focusController.save(); - } - - const closeSignal = sidePanelOnCloseSignal(); - - // Can't detect clicks in the sidebar itself. So need to just watch for enter/leave the sidebar element - sidebar.addEventListener( - "mouseenter", - () => { - // If the user clicks into the sidebar and then leaves the sidebar, don't set the focus to the sidebar - // when they re-enter the sidebar - if (document.activeElement !== sidebar) { - // FIXME: If the user closes the sidebar when these two items are stored, - // both controllers will be stuck that way until some other .restore()/.clear() call resets it. It will need a "sidebar hide" listener to ensure it doesn't happen - // https://github.com/pixiebrix/pixiebrix-extension/pull/7842#discussion_r1516015396 - focusController.save(); - selectionController.save(); - } - }, - { passive: true, capture: true, signal: closeSignal }, - ); - - sidebar.addEventListener( - "mouseleave", - () => { - focusController.clear(); - selectionController.clear(); - }, - { passive: true, capture: true, signal: closeSignal }, - ); - }); - } -} diff --git a/src/contentScript/sidebarDomControllerLite.ts b/src/contentScript/sidebarDomControllerLite.ts deleted file mode 100644 index b5dac2bf63..0000000000 --- a/src/contentScript/sidebarDomControllerLite.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2024 PixieBrix, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -/** - * @file This file MUST not have dependencies as it's meant to be tiny and imported by browserActionInstantHandler.ts. - * Because browserActionInstantHandler.ts is a separate content script, any modules will be duplicated - * between browserActionInstantHandler and the main content script. - */ - -import { MAX_Z_INDEX, PANEL_FRAME_ID } from "@/domConstants"; -import shadowWrap from "@/utils/shadowWrap"; -import { expectContext } from "@/utils/expectContext"; -import { uuidv4 } from "@/types/helpers"; - -export const SIDEBAR_WIDTH_CSS_PROPERTY = "--pb-sidebar-width"; -const ORIGINAL_MARGIN_CSS_PROPERTY = "--pb-original-margin-right"; - -const SIDEBAR_WIDTH_PX = 400; - -function storeOriginalCSSOnce() { - const html = document.documentElement; - - if (html.style.getPropertyValue(ORIGINAL_MARGIN_CSS_PROPERTY)) { - return; - } - - // Store the original margin, so it can be reused in future calculations. It must also persist across sessions - html.style.setProperty( - ORIGINAL_MARGIN_CSS_PROPERTY, - getComputedStyle(html).getPropertyValue("margin-right"), - ); - - // Make margin dynamic, so it always follows the original margin AND the sidebar width, if open - html.style.setProperty( - "margin-right", - `calc(var(${ORIGINAL_MARGIN_CSS_PROPERTY}) + var(${SIDEBAR_WIDTH_CSS_PROPERTY}))`, - ); - - // Some websites like https://www.nespresso.com/us/en/ have `width: 100%` on the HTML. - // This resets it as it prevents the margin from working (and it's the default already) - html.style.setProperty("width", "auto"); -} - -function setSidebarWidth(pixels: number): void { - const html = document.documentElement; - html.style.setProperty(SIDEBAR_WIDTH_CSS_PROPERTY, `${pixels}px`); -} - -/** - * Returns the sidebar frame if it's in the DOM, or null otherwise. The sidebar might not be initialized yet. - */ -export function getSidebarElement(): Element | null { - expectContext("contentScript"); - - return document.documentElement.querySelector(`#${PANEL_FRAME_ID}`); -} - -/** - * Return true if the sidebar frame is in the DOM. The sidebar might not be initialized yet. - */ -export function isSidebarFrameVisible(): boolean { - return Boolean(getSidebarElement()); -} - -/** Removes the element; Returns false if no element was found */ -export function removeSidebarFrame(): boolean { - const sidebar = getSidebarElement(); - if (sidebar) { - sidebar.remove(); - setSidebarWidth(0); - } - - return Boolean(sidebar); -} - -/** Inserts the element; Returns false if it already existed */ -export function insertSidebarFrame(): boolean { - if (isSidebarFrameVisible()) { - return false; - } - - storeOriginalCSSOnce(); - const nonce = uuidv4(); - const actionUrl = new URL(browser.runtime.getURL("sidebar.html")); - actionUrl.searchParams.set("nonce", nonce); - - setSidebarWidth(SIDEBAR_WIDTH_PX); - - const iframe = document.createElement("iframe"); - iframe.src = actionUrl.href; - - Object.assign(iframe.style, { - position: "fixed", - top: 0, - right: 0, - // `-1` keeps it under the QuickBar #4130 - zIndex: MAX_Z_INDEX - 1, - - // Note that it can't use the variable because the frame is in the shadow DOM - width: CSS.px(SIDEBAR_WIDTH_PX), - height: "100%", - border: 0, - borderLeft: "1px solid lightgray", - - // Note that it can't use our CSS variables because this element lives on the host - background: "#f9f8fa", - }); - - const wrapper = shadowWrap(iframe); - wrapper.id = PANEL_FRAME_ID; - document.documentElement.append(wrapper); - - iframe.animate([{ translate: "50%" }, { translate: 0 }], { - duration: 500, - easing: "cubic-bezier(0.23, 1, 0.32, 1)", - }); - - if (!isSidebarFrameVisible()) { - console.error( - "Post-condition failed: isSidebarFrameVisible is false after insertSidebarFrame", - ); - } - - return true; -} - -/** - * Toggle the sidebar frame. Returns true if the sidebar is now visible, false otherwise. - */ -export function toggleSidebarFrame(): void { - if (isSidebarFrameVisible()) { - removeSidebarFrame(); - } else { - insertSidebarFrame(); - } -} diff --git a/src/extensionConsole/pages/settings/ExperimentalSettings.tsx b/src/extensionConsole/pages/settings/ExperimentalSettings.tsx index 57db4613ce..b01f5c24b5 100644 --- a/src/extensionConsole/pages/settings/ExperimentalSettings.tsx +++ b/src/extensionConsole/pages/settings/ExperimentalSettings.tsx @@ -23,15 +23,10 @@ import { faFlask } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { selectSettings } from "@/store/settings/settingsSelectors"; import SettingToggle from "@/extensionConsole/pages/settings/SettingToggle"; -import { isMV3 } from "@/mv3/api"; const ExperimentalSettings: React.FunctionComponent = () => { - const { - suggestElements, - excludeRandomClasses, - performanceTracing, - sandboxedCode, - } = useSelector(selectSettings); + const { suggestElements, excludeRandomClasses, performanceTracing } = + useSelector(selectSettings); return ( @@ -63,15 +58,6 @@ const ExperimentalSettings: React.FunctionComponent = () => { isEnabled={performanceTracing ?? false} flag="performanceTracing" /> - {!isMV3() && ( - - )} diff --git a/src/mv3/SessionStorage.test.ts b/src/mv3/SessionStorage.test.ts index ff91dd319b..a9fbe64b16 100644 --- a/src/mv3/SessionStorage.test.ts +++ b/src/mv3/SessionStorage.test.ts @@ -17,6 +17,30 @@ import { SessionMap, SessionValue } from "./SessionStorage"; +const _map = new Map(); + +// Workaround until https://github.com/RickyMarou/jest-webextension-mock/issues/6 is resolved +browser.storage.session = { + get: jest.fn(async (key: string) => ({ [key]: _map.get(key) })), + set: jest.fn(async (obj) => { + _map.set(...Object.entries(obj)[0]!); + }), + remove: jest.fn(async (key) => { + _map.delete(key); + }), + clear: jest.fn(), + onChanged: { + addListener: jest.fn(), + removeListener: jest.fn(), + hasListener: jest.fn(), + hasListeners: jest.fn(), + }, +}; + +beforeEach(() => { + _map.clear(); +}); + test("SessionMap", async () => { const map = new SessionMap("jester", import.meta.url); await expect(map.get("alpha")).resolves.toBeUndefined(); diff --git a/src/mv3/SessionStorage.ts b/src/mv3/SessionStorage.ts index 493b6f0211..7cba28b977 100644 --- a/src/mv3/SessionStorage.ts +++ b/src/mv3/SessionStorage.ts @@ -28,12 +28,6 @@ import { type ManualStorageKey } from "@/utils/storageUtils"; import { once } from "lodash"; import pMemoize from "p-memoize"; -// Just like chrome.storage.session, this must be "global" -// eslint-disable-next-line local-rules/persistBackgroundData -- MV2-only -const storage = new Map(); - -// eslint-disable-next-line local-rules/persistBackgroundData -- Static -const hasSession = "session" in chrome.storage; function validateContext(): void { expectContext( "background", @@ -62,9 +56,6 @@ export class SessionMap { async has(secondaryKey: string): Promise { this.validateContext(); const rawStorageKey = this.getRawStorageKey(secondaryKey); - if (!hasSession) { - return storage.has(rawStorageKey); - } const result = await browser.storage.session.get(rawStorageKey); // OK to check for undefined because it's not a valid JsonValue. The `set` method calls `delete` if @@ -76,11 +67,9 @@ export class SessionMap { async get(secondaryKey: string): Promise { this.validateContext(); const rawStorageKey = this.getRawStorageKey(secondaryKey); - if (!hasSession) { - return storage.get(rawStorageKey) as Value | undefined; - } const result = await browser.storage.session.get(rawStorageKey); + // eslint-disable-next-line security/detect-object-injection -- `getRawStorageKey` ensures the format return result[rawStorageKey] as Value | undefined; } @@ -96,22 +85,14 @@ export class SessionMap { } const rawStorageKey = this.getRawStorageKey(secondaryKey); - if (hasSession) { - await browser.storage.session.set({ [rawStorageKey]: value }); - } else { - storage.set(rawStorageKey, value); - } + await browser.storage.session.set({ [rawStorageKey]: value }); } async delete(secondaryKey: string): Promise { this.validateContext(); const rawStorageKey = this.getRawStorageKey(secondaryKey); - if (hasSession) { - await browser.storage.session.remove(rawStorageKey); - } else { - storage.delete(rawStorageKey); - } + await browser.storage.session.remove(rawStorageKey); } } diff --git a/src/mv3/api.ts b/src/mv3/api.ts index 10a7729f19..6410f7daab 100644 --- a/src/mv3/api.ts +++ b/src/mv3/api.ts @@ -16,18 +16,10 @@ */ /** @file Temporary helpers useful for the MV3 transition */ +// TODO: Determine if we want to keep this file import { type Tabs } from "webextension-polyfill"; -import { once } from "lodash"; - -export const isMV3 = once((): boolean => { - // https://github.com/pixiebrix/pixiebrix-extension/issues/8273 - if (!chrome.runtime?.getManifest) { - return false; - } - - return chrome.runtime.getManifest().manifest_version === 3; -}); export const browserAction = globalThis.chrome?.action; +// XXX: Not sure if we still need both Tab types export type Tab = Tabs.Tab | chrome.tabs.Tab; diff --git a/src/sandbox/messenger/api.ts b/src/sandbox/messenger/api.ts index f5e8cd5ef8..6d823d2abb 100644 --- a/src/sandbox/messenger/api.ts +++ b/src/sandbox/messenger/api.ts @@ -20,11 +20,7 @@ import injectIframe, { hiddenIframeStyle } from "@/utils/injectIframe"; import postMessage from "@/utils/postMessage"; import pMemoize from "p-memoize"; -import * as directApi from "@/sandbox/messenger/executor"; import { type JsonObject } from "type-fest"; -import { getSettingsState } from "@/store/settings/settingsStorage"; -import { once } from "lodash"; -import { isMV3 } from "@/mv3/api"; // Uses pMemoize to allow retries after a failure const loadSandbox = pMemoize(async () => { @@ -35,15 +31,6 @@ const loadSandbox = pMemoize(async () => { return iframe.contentWindow; }); -const isSandboxed = once(async (): Promise => { - if (isMV3()) { - return true; - } - - const { sandboxedCode } = await getSettingsState(); - return Boolean(sandboxedCode); -}); - export type TemplateRenderPayload = { template: string; context: JsonObject; @@ -55,37 +42,31 @@ export type TemplateValidatePayload = string; export async function renderNunjucksTemplate( payload: TemplateRenderPayload, ): Promise { - return (await isSandboxed()) - ? postMessage({ - recipient: await loadSandbox(), - payload, - type: "RENDER_NUNJUCKS", - }) - : directApi.renderNunjucksTemplate(payload); + return postMessage({ + recipient: await loadSandbox(), + payload, + type: "RENDER_NUNJUCKS", + }); } export async function validateNunjucksTemplate( payload: TemplateValidatePayload, ): Promise { - return (await isSandboxed()) - ? postMessage({ - recipient: await loadSandbox(), - payload, - type: "VALIDATE_NUNJUCKS", - }) - : directApi.validateNunjucksTemplate(payload); + return postMessage({ + recipient: await loadSandbox(), + payload, + type: "VALIDATE_NUNJUCKS", + }); } export async function renderHandlebarsTemplate( payload: TemplateRenderPayload, ): Promise { - return (await isSandboxed()) - ? postMessage({ - recipient: await loadSandbox(), - payload, - type: "RENDER_HANDLEBARS", - }) - : directApi.renderHandlebarsTemplate(payload); + return postMessage({ + recipient: await loadSandbox(), + payload, + type: "RENDER_HANDLEBARS", + }); } export type JavaScriptPayload = { diff --git a/src/starterBricks/sidebar/sidebarExtension.test.ts b/src/starterBricks/sidebar/sidebarExtension.test.ts index 6a0a50eb72..ea8d05a19c 100644 --- a/src/starterBricks/sidebar/sidebarExtension.test.ts +++ b/src/starterBricks/sidebar/sidebarExtension.test.ts @@ -31,10 +31,10 @@ import { RootReader, tick } from "@/starterBricks/starterBrickTestUtils"; import { getReservedPanelEntries, sidebarShowEvents, + isSidePanelOpen, } from "@/contentScript/sidebarController"; import { setState } from "@/platform/state/stateController"; import { modMetadataFactory } from "@/testUtils/factories/modComponentFactories"; -import { PANEL_FRAME_ID } from "@/domConstants"; import brickRegistry from "@/bricks/registry"; import { sleep } from "@/utils/timeUtils"; import { getPlatform } from "@/platform/platformContext"; @@ -43,6 +43,12 @@ import { type SidebarConfig, } from "@/starterBricks/sidebar/types"; +jest.mock("@/contentScript/sidebarController", () => ({ + ...jest.requireActual("@/contentScript/sidebarController"), + isSidePanelOpen: jest.fn(), +})); +const isSidePanelOpenMock = jest.mocked(isSidePanelOpen); + const rootReader = new RootReader(); const starterBrickFactory = (definitionOverrides: UnknownObject = {}) => @@ -83,6 +89,7 @@ describe("sidebarExtension", () => { brickRegistry.clear(); brickRegistry.register([rootReader]); rootReader.readCount = 0; + isSidePanelOpenMock.mockResolvedValue(false); }); it("reserves panel on load", async () => { @@ -189,7 +196,7 @@ describe("sidebarExtension", () => { expect(rootReader.readCount).toBe(0); // Fake the sidebar being added to the page - $(document.body).append(`
`); + isSidePanelOpenMock.mockResolvedValue(true); sidebarShowEvents.emit({ reason: RunReason.MANUAL }); await tick(); @@ -251,7 +258,7 @@ describe("sidebarExtension", () => { await extensionPoint.install(); // Fake the sidebar being added to the page - $(document.body).append(`
`); + isSidePanelOpenMock.mockResolvedValue(true); sidebarShowEvents.emit({ reason: RunReason.MANUAL }); await tick(); diff --git a/src/store/settings/settingsTypes.ts b/src/store/settings/settingsTypes.ts index 09f2479179..f841c8e319 100644 --- a/src/store/settings/settingsTypes.ts +++ b/src/store/settings/settingsTypes.ts @@ -41,11 +41,6 @@ export type SkunkworksSettingsFlags = { * Experimental setting to track runtime performance */ performanceTracing?: boolean; - - /** - * Experimental setting to run some code in a sandbox - */ - sandboxedCode?: boolean; }; export type GeneralSettingsFlags = { @@ -167,6 +162,9 @@ export type SettingsStateV3 = SettingsStateV2 & { varAutosuggest: boolean; }; +/** + * @deprecated - Do not use versioned state types directly + */ export type SettingsStateV4 = SettingsStateV3 & { /** * @since 1.8.11 SettingsStateV4 makes textSelectionMenu and snippetShortcutMenu required diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 14e0bfc83a..16b9ae21a0 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -427,7 +427,6 @@ "./components/walkthroughModal/WalkthroughModalApp.tsx", "./components/walkthroughModal/showWalkthroughModal.ts", "./contentScript/activationConstants.ts", - "./contentScript/browserActionInstantHandler.ts", "./contentScript/contentScriptPlatform.ts", "./contentScript/context.ts", "./contentScript/contextMenus.ts", @@ -468,7 +467,6 @@ "./contentScript/ready.ts", "./contentScript/setExtensionIdInApp.ts", "./contentScript/sidebarController.tsx", - "./contentScript/sidebarDomControllerLite.ts", "./contentScript/snippetShortcutMenu/SnippetShortcutMenu.stories.tsx", "./contentScript/snippetShortcutMenu/SnippetShortcutMenu.test.tsx", "./contentScript/snippetShortcutMenu/SnippetShortcutMenu.tsx", diff --git a/src/utils/notify.tsx b/src/utils/notify.tsx index 78a071a7d3..341234a1b5 100644 --- a/src/utils/notify.tsx +++ b/src/utils/notify.tsx @@ -25,9 +25,6 @@ import reportError from "@/telemetry/reportError"; import { type Except, type RequireAtLeastOne } from "type-fest"; import { getErrorMessage } from "@/errors/errorHelpers"; import { merge, truncate } from "lodash"; -/* eslint-disable-next-line local-rules/noCrossBoundaryImports -- While correct, the `sidebarDomControllerLite` name -implies that it's a small, pure module, and it's unlikely to cause issues */ -import { SIDEBAR_WIDTH_CSS_PROPERTY } from "@/contentScript/sidebarDomControllerLite"; import ErrorIcon from "@/icons/error.svg?loadAsComponent"; import WarningIcon from "@/icons/warning.svg?loadAsComponent"; import type { Notification, NotificationType } from "@/utils/notificationTypes"; @@ -145,8 +142,6 @@ export function showNotification({ const options: ToastOptions = { id, duration, - // Keep the notification centered on the document even when the sidebar is open - style: { marginLeft: `calc(var(${SIDEBAR_WIDTH_CSS_PROPERTY}, 0) * -1)` }, }; const component = ; diff --git a/src/utils/sidePanelUtils.ts b/src/utils/sidePanelUtils.ts index ac2d838c88..3d81d77892 100644 --- a/src/utils/sidePanelUtils.ts +++ b/src/utils/sidePanelUtils.ts @@ -18,11 +18,9 @@ /** @file This file contains utilities to deal with the sidePanel from other contexts */ import { getErrorMessage } from "@/errors/errorHelpers"; -import { isMV3 } from "@/mv3/api"; import { forbidContext, isBrowserSidebarTopFrame } from "@/utils/expectContext"; import { type PageTarget, messenger, getThisFrame } from "webext-messenger"; import { isContentScript } from "webext-detect-page"; -import { showSidebar } from "@/contentScript/messenger/strict/api"; /** * Returns true if an error showing sidebar is due to a missing user gesture. @@ -44,14 +42,10 @@ export async function openSidePanel(tabId: number): Promise { "The content script doesn't have direct access to the `sidePanel` API. Call `showMySidePanel` instead", ); - if (isMV3()) { - await openSidePanelMv3(tabId); - } else { - await showSidebar({ tabId }); - } + await _openSidePanel(tabId); } -async function openSidePanelMv3(tabId: number): Promise { +async function _openSidePanel(tabId: number): Promise { // Simultaneously enable and open the side panel. // If we wait too long before calling .open(), we will lose the "user gesture" permission // There is no way to know whether the side panel is open yet, so we call it regardless. @@ -97,10 +91,6 @@ export function getSidebarPath(tabId: number): string { } export function getSidebarTarget(tabId: number): PageTarget { - if (!isMV3()) { - return { tabId, page: "/sidebar.html" }; - } - return { page: getSidebarPath(tabId), }; diff --git a/webpack.config.mjs b/webpack.config.mjs index b6284a3f65..ea1b4e8807 100644 --- a/webpack.config.mjs +++ b/webpack.config.mjs @@ -117,7 +117,6 @@ const createConfig = (env, options) => "contentScript/contentScript", "contentScript/loadActivationEnhancements", - "contentScript/browserActionInstantHandler", "contentScript/setExtensionIdInApp", "pageEditor/pageEditor",