Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#7649: introduce platform protocol to define clear platform boundary and decouple bricks from messenger/webext APIs #7650

Merged
merged 25 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
966f0fd
#7649: [WIP] platform protocol
twschiller Feb 17, 2024
47b5c32
Fix lint
twschiller Feb 17, 2024
4e30dbe
Fix alerts/page state tests
twschiller Feb 17, 2024
bb01e05
[WIP]: form/panel refactoring for content script specific logic
twschiller Feb 17, 2024
aa1356f
Rename pageState to stateController
twschiller Feb 17, 2024
6119795
Cleanup dom module names
twschiller Feb 17, 2024
bc5025e
Merge remote-tracking branch 'origin/main' into feature/7649-platform…
twschiller Feb 17, 2024
4908da4
Fix tests
twschiller Feb 18, 2024
fda97dd
More refactoring; fix bug where showing multiple forms for component …
twschiller Feb 18, 2024
7469d3e
Starter Brick capabilities
twschiller Feb 18, 2024
b252a00
Lint cleanup
twschiller Feb 18, 2024
4023e30
Fix strict null checks file
twschiller Feb 18, 2024
276058d
Fix javascript capabilities; cleanup getRequiredCapabalities
twschiller Feb 18, 2024
152f63f
Remove form bugfix
twschiller Feb 18, 2024
8f9e54f
Remove bug fix test
twschiller Feb 18, 2024
897699a
Add clipboard to protocol
twschiller Feb 19, 2024
34a5a00
Merge with main; add selection tooltip to the protocol
twschiller Feb 20, 2024
1d18c86
Merge with main
twschiller Feb 20, 2024
e104bb0
Declare capabilities
twschiller Feb 20, 2024
db2dd16
Merge remote-tracking branch 'origin/main' into feature/7649-platform…
twschiller Feb 21, 2024
d7cc217
Make platform name explicit
twschiller Feb 21, 2024
eedeb0c
Address PR comments
twschiller Feb 21, 2024
a319a58
Add missing capabilities
twschiller Feb 21, 2024
0f35972
Add context to error
twschiller Feb 21, 2024
06a5889
Fix lint
twschiller Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions eslint-local-rules/persistBackgroundData.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@
./src/contentScript/context.ts
./src/contentScript/contextMenus.ts
./src/contentScript/elementReference.ts
./src/contentScript/ephemeralFormProtocol.ts
./src/contentScript/formController.ts
./src/contentScript/ephemeralPanelController.tsx
./src/contentScript/lifecycle.ts
./src/contentScript/messenger/api.ts
./src/contentScript/messenger/runBrickTypes.ts
./src/contentScript/pageEditor/elementPicker.ts
./src/contentScript/pageEditor/types.ts
./src/contentScript/pageState.ts
./src/contentScript/stateController.ts
./src/contentScript/performanceMonitoring.ts
./src/contentScript/ready.ts
./src/contentScript/sidebarController.tsx
Expand Down
1 change: 0 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const config = {
testEnvironment: "./src/testUtils/FixJsdomEnvironment.js",
modulePaths: ["/src"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "yaml", "yml", "json"],
testPathIgnorePatterns: ["<rootDir>/selenium/"],
modulePathIgnorePatterns: ["<rootDir>/headers.json", "<rootDir>/dist/"],
transform: {
"^.+\\.[jt]sx?$": "@swc/jest",
Expand Down
7 changes: 5 additions & 2 deletions src/analysis/analysisVisitors/formBrickAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
*/

import { AnalysisVisitorABC } from "./baseAnalysisVisitors";
import { type BrickConfig, type BrickPosition } from "@/bricks/types";
import {
type BrickConfig,
type BrickPosition,
PipelineFlavor,
} from "@/bricks/types";
import { type VisitBlockExtra } from "@/bricks/PipelineVisitor";
import { FORM_MODAL_ID } from "@/pageEditor/fields/FormModalOptions";
import { AnnotationType } from "@/types/annotationTypes";
import { PipelineFlavor } from "@/pageEditor/pageEditorTypes";

class FormBrickAnalysis extends AnalysisVisitorABC {
get id() {
Expand Down
7 changes: 5 additions & 2 deletions src/analysis/analysisVisitors/renderersAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
*/

import { AnalysisVisitorWithResolvedBricksABC } from "./baseAnalysisVisitors";
import { type BrickConfig, type BrickPosition } from "@/bricks/types";
import {
type BrickConfig,
type BrickPosition,
PipelineFlavor,
} from "@/bricks/types";
import {
nestedPosition,
type VisitPipelineExtra,
} from "@/bricks/PipelineVisitor";
import { PipelineFlavor } from "@/pageEditor/pageEditorTypes";
import { AnnotationType } from "@/types/annotationTypes";

export const MULTIPLE_RENDERERS_ERROR_MESSAGE =
Expand Down
7 changes: 4 additions & 3 deletions src/background/activateBrowserActionIcon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import MockAdapter from "axios-mock-adapter";
import { browserAction } from "@/mv3/api";

jest.mock("@/mv3/api", () => ({
isMV3: jest.fn().mockReturnValue(false),
browserAction: {
setIcon: jest.fn(),
},
Expand All @@ -35,7 +36,7 @@ describe("activateBrowserActionIcon", () => {

const drawImageMock = jest.fn();
const getImageDataMock = jest.fn().mockReturnValue("image data");
const getContextmock = jest.fn().mockReturnValue({
const getContextMock = jest.fn().mockReturnValue({
drawImage: drawImageMock,
getImageData: getImageDataMock,
});
Expand All @@ -55,7 +56,7 @@ describe("activateBrowserActionIcon", () => {
};
// @ts-expect-error -- No need to mock the whole class for the test
global.OffscreenCanvas = class {
getContext = getContextmock;
getContext = getContextMock;
};

URL.createObjectURL = jest.fn();
Expand Down Expand Up @@ -107,7 +108,7 @@ describe("activateBrowserActionIcon", () => {
const result = await blobToImageData(blob);

expect(result).toBe("image data");
expect(getContextmock).toHaveBeenCalledWith("2d");
expect(getContextMock).toHaveBeenCalledWith("2d");
expect(drawImageMock).toHaveBeenCalledWith(
expect.any(Image),
0,
Expand Down
20 changes: 12 additions & 8 deletions src/background/contextMenus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,16 @@ import {
type ResolvedModComponent,
} from "@/types/modComponentTypes";
import { allSettled, memoizeUntilSettled } from "@/utils/promiseUtils";
import type { SelectionMenuOptions } from "@/platform/platformProtocol";
import { ContextError } from "@/errors/genericErrors";
import { selectEventData } from "@/telemetry/deployments";

const MENU_PREFIX = "pixiebrix-";

// This constant must be high enough to give Chrome time to inject the content script. ensureContentScript can take
// >= 1 seconds because it also waits for the content script to be ready
const CONTEXT_SCRIPT_INSTALL_MS = 5000;

type SelectionMenuOptions = {
extensionId: UUID;
title: string;
contexts: Menus.ContextType[];
documentUrlPatterns: string[];
};

/**
* Return a unique context menu item id for the given extension id.
* @param extensionId
Expand Down Expand Up @@ -193,8 +189,16 @@ export async function preloadContextMenus(
resolved.extensionPointId,
);
if (extensionPoint instanceof ContextMenuStarterBrickABC) {
await extensionPoint.ensureMenu(
await extensionPoint.registerMenuItem(
definition as unknown as ResolvedModComponent<ContextMenuConfig>,
() => {
throw new ContextError(
"Context menu was preloaded, but no handler was registered",
{
context: selectEventData(resolved),
},
);
},
);
}
});
Expand Down
9 changes: 6 additions & 3 deletions src/bricks/PipelineExpressionVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { type BrickConfig, type BrickPosition } from "@/bricks/types";
import {
type BrickConfig,
type BrickPosition,
PipelineFlavor,
} from "@/bricks/types";
import { type Expression } from "@/types/runtimeTypes";
import PipelineVisitor, {
nestedPosition,
type VisitBlockExtra,
} from "./PipelineVisitor";
import { type DocumentElement } from "@/components/documentBuilder/documentBuilderTypes";
import { PipelineFlavor } from "@/pageEditor/pageEditorTypes";
import { isExpression, isPipelineExpression } from "@/utils/expressionUtils";
import { joinPathParts } from "@/utils/formUtils";

Expand Down Expand Up @@ -94,7 +97,7 @@ abstract class PipelineExpressionVisitor extends PipelineVisitor {
}

default: {
return PipelineFlavor.AllBlocks;
return PipelineFlavor.AllBricks;
}
}
}
Expand Down
9 changes: 6 additions & 3 deletions src/bricks/PipelineVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { type BrickConfig, type BrickPosition } from "@/bricks/types";
import {
type BrickConfig,
type BrickPosition,
PipelineFlavor,
} from "@/bricks/types";
import { type UUID } from "@/types/stringTypes";
import { type TypedBrickPair } from "@/bricks/registry";
import { DocumentRenderer } from "@/bricks/renderers/document";
Expand All @@ -26,7 +30,6 @@ import {
getSubPipelineFlavor,
} from "@/bricks/blockFilterHelpers";
import { type StarterBrickType } from "@/types/starterBrickTypes";
import { PipelineFlavor } from "@/pageEditor/pageEditorTypes";
import { PIPELINE_BLOCKS_FIELD_NAME } from "@/pageEditor/consts";
import { isPipelineExpression } from "@/utils/expressionUtils";
import { joinPathParts } from "@/utils/formUtils";
Expand Down Expand Up @@ -175,7 +178,7 @@ class PipelineVisitor {
): void {
const flavor = extra?.extensionPointType
? getRootPipelineFlavor(extra.extensionPointType)
: PipelineFlavor.AllBlocks;
: PipelineFlavor.AllBricks;
this.visitPipeline(ROOT_POSITION, pipeline, { flavor });
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/bricks/blockFilterHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import { type TypedBrickPair } from "@/bricks/registry";
import { type StarterBrickType } from "@/types/starterBrickTypes";
import { PipelineFlavor } from "@/pageEditor/pageEditorTypes";
import { stubTrue } from "lodash";
import { DocumentRenderer } from "@/bricks/renderers/document";
import { type BrickType } from "@/runtime/runtimeTypes";
Expand All @@ -27,6 +26,7 @@ import TourStepTransformer from "@/bricks/transformers/tourStep/tourStep";
import { ErrorEffect } from "@/bricks/effects/error";
import { CancelEffect } from "@/bricks/effects/cancel";
import CommentEffect from "@/bricks/effects/comment";
import { PipelineFlavor } from "@/bricks/types";

const PANEL_TYPES = ["actionPanel", "panel"];

Expand Down Expand Up @@ -82,7 +82,7 @@ export function getSubPipelineFlavor(
export function makeIsBlockAllowedForPipeline(
pipelineFlavor: PipelineFlavor,
): IsBlockAllowedPredicate {
if (pipelineFlavor === PipelineFlavor.AllBlocks) {
if (pipelineFlavor === PipelineFlavor.AllBricks) {
return stubTrue;
}

Expand Down
17 changes: 5 additions & 12 deletions src/bricks/effects/AddQuickBarAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,15 @@
*/

import AddQuickBarAction from "@/bricks/effects/AddQuickBarAction";
import quickbarRegistry from "@/components/quickBar/quickBarRegistry";
import { unsafeAssumeValidArg } from "@/runtime/runtimeTypes";
import ConsoleLogger from "@/utils/ConsoleLogger";
import { uuidv4, validateRegistryId } from "@/types/helpers";
import { brickOptionsFactory } from "@/testUtils/factories/runtimeFactories";
import { platformMock } from "@/testUtils/platformMock";

const brick = new AddQuickBarAction();

jest.mock("@/components/quickBar/quickBarRegistry", () => ({
__esModule: true,
default: {
addAction: jest.fn(),
knownGeneratorRootIds: new Set<string>(),
},
}));

const addActionMock = jest.mocked(quickbarRegistry.addAction);
const platform = platformMock;

const logger = new ConsoleLogger({
extensionId: uuidv4(),
Expand All @@ -41,7 +33,7 @@ const logger = new ConsoleLogger({

describe("AddQuickBarAction", () => {
beforeEach(() => {
addActionMock.mockReset();
jest.clearAllMocks();
});

test("is root aware", async () => {
Expand All @@ -58,12 +50,13 @@ describe("AddQuickBarAction", () => {
await brick.run(
unsafeAssumeValidArg({ title: "test" }),
brickOptionsFactory({
platform,
logger,
root: document,
abortSignal: abortController.signal,
}),
);
expect(addActionMock).toHaveBeenCalledWith({
expect(platform.quickBar.addAction).toHaveBeenCalledWith({
id: expect.toBeString(),
extensionPointId: logger.context.extensionPointId,
extensionId: logger.context.extensionId,
Expand Down
21 changes: 17 additions & 4 deletions src/bricks/effects/AddQuickBarAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import { validateRegistryId } from "@/types/helpers";
import { propertiesToSchema } from "@/validators/generic";
import quickBarRegistry from "@/components/quickBar/quickBarRegistry";
import Icon from "@/icons/Icon";
import React from "react";
import {
Expand All @@ -29,6 +28,9 @@ import { type CustomAction } from "@/components/quickBar/quickbarTypes";
import { type IconConfig } from "@/types/iconTypes";
import { type Schema } from "@/types/schemaTypes";
import { EffectABC } from "@/types/bricks/effectTypes";
import type { BrickConfig } from "@/bricks/types";
import type { PlatformCapability } from "@/platform/capabilities";
import { uniq } from "lodash";

type ActionConfig = {
/**
Expand Down Expand Up @@ -85,6 +87,15 @@ class AddQuickBarAction extends EffectABC {
return true;
}

override async getRequiredCapabilities(
_config: BrickConfig,
): Promise<PlatformCapability[]> {
return uniq([
...(await super.getRequiredCapabilities(_config)),
"quickBar",
]);
}

inputSchema: Schema = propertiesToSchema(
{
title: {
Expand Down Expand Up @@ -126,8 +137,10 @@ class AddQuickBarAction extends EffectABC {
// Be explicit about the default priority if non is provided
priority = DEFAULT_PRIORITY,
}: BrickArgs<ActionConfig>,
{ root, logger, runPipeline, abortSignal }: BrickOptions,
{ root, logger, runPipeline, abortSignal, platform }: BrickOptions,
): Promise<void> {
const { quickBar } = platform;

// The runtime checks the abortSignal for each brick. But check here too to avoid flickering in the Quick Bar
if (abortSignal.aborted) {
return;
Expand All @@ -146,7 +159,7 @@ class AddQuickBarAction extends EffectABC {
extensionPointId: logger.context.extensionPointId,
extensionId: logger.context.extensionId,
// Can only provide a parent if the parent exists
parent: quickBarRegistry.knownGeneratorRootIds.has(parentId)
parent: quickBar.knownGeneratorRootIds.has(parentId)
? parentId
: undefined,
name: title,
Expand Down Expand Up @@ -176,7 +189,7 @@ class AddQuickBarAction extends EffectABC {
},
};

quickBarRegistry.addAction(action);
quickBar.addAction(action);
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/bricks/effects/AddTextCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ import type {
} from "@/types/runtimeTypes";
import { type Schema } from "@/types/schemaTypes";
import { EffectABC } from "@/types/bricks/effectTypes";
import {
commandRegistry,
initCommandController,
} from "@/contentScript/commandPopover/commandController";
import { initCommandController } from "@/contentScript/commandPopover/commandController";
import { BusinessError } from "@/errors/businessErrors";
import { getSettingsState } from "@/store/settings/settingsStorage";
import { validateOutputKey } from "@/runtime/runtimeTypes";
import type { PlatformCapability } from "@/platform/capabilities";

type CommandArgs = {
/**
Expand Down Expand Up @@ -88,9 +86,13 @@ class AddTextCommand extends EffectABC {
["shortcut", "title", "generate"],
);

override async getRequiredCapabilities(): Promise<PlatformCapability[]> {
return ["commandPopover"];
}

async effect(
{ shortcut, title, generate: generatePipeline }: BrickArgs<CommandArgs>,
{ logger, runPipeline, abortSignal }: BrickOptions,
{ logger, runPipeline, platform, abortSignal }: BrickOptions,
): Promise<void> {
// The runtime checks the abortSignal for each brick. But check here too to avoid flickering in the popover
if (abortSignal?.aborted) {
Expand All @@ -104,7 +106,7 @@ class AddTextCommand extends EffectABC {
// Counter to keep track of the action run number for tracing
let counter = 0;

commandRegistry.register({
platform.commandPopover.register({
componentId: logger.context.extensionId,
// Trim leading slash to be resilient to user input
shortcut: shortcut.replace(/^\//, ""),
Expand Down
Loading
Loading