Skip to content

chore(content-script): inject the perps widget only when it is enabled - #4239

Draft
cs1707 wants to merge 1 commit into
developfrom
chore/perps-widget-on-demand
Draft

cs1707 wants to merge 1 commit into
developfrom
chore/perps-widget-on-demand

Conversation

@cs1707

@cs1707 cs1707 commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

What

  • The perps widget is built as its own perps-widget entry (src/content-script/perps-widget/entry.ts), declared with dependOn: 'content-script' in both the common and hot webpack configs.
  • content-script.js no longer bundles the widget. On load it sends RABBY_INJECT_PERPS_WIDGET to the background.
  • The background (injectPerpsWidget in src/background/index.ts) injects perps-widget.js into the sender's tab with chrome.scripting.executeScript (MV3) or browser.tabs.executeScript (MV2). It only does so when:
    • the sender is our own extension,
    • the message comes from the tab's top frame,
    • and the widget is enabled.
  • SecSDK/policy.json regenerated.

Why

content-script.js runs on every page. It carried the widget's React UI and react-dom eagerly (webpackMode: "eager"), because strict-CSP pages such as Google and GitHub block a chunk fetched from chrome-extension://. The widget is off by default, so nearly every page load paid for code it never ran. Scripts injected by the extension aren't subject to the page's CSP, so the eager bundling is no longer needed.

Results

build:pro develop this PR
content-script.js (chrome-mv3 and firefox-mv2) 421K (gzip 120K) 251K (gzip 67K), -40.5%
perps-widget.js — 170K, injected only when enabled
all JS 42.84MB 42.84MB

Notes for reviewers

  • Why dependOn: Warden inlines its module-require policy only for the content-script and pageProvider runtimes. That name check is hard-coded in warden-for-js's runtime-assets.ts. Any other entry looks for warden-policy-runtime.js, which only extension pages load, so an independent widget entry threw "Missing webpack require policy" inside the isolated world and never booted. With dependOn, the widget chunk runs on the content script's webpack runtime, which is already loaded in that isolated world, and Warden inlines its policy there. Shared modules (polyfill, messaging) are not duplicated either; content-script.js grows about 7K for the chunk loader and the widget's policy.
  • Same timing as before: enabling the widget still takes effect on the next page load, as before.
  • Message cost unchanged: one runtime message per top-frame load, same as the old getPerpsWidgetEnabled check.
  • Firefox: the Firefox split-chunks filter also exempts perps-widget, so its vendor code isn't split into a chunk that would never load in the page.
  • Chrome MV2 has only activeTab, so tabs.executeScript won't work there. Chrome no longer runs MV2 extensions; Firefox MV2 has http/https host permissions.
  • Permissions: this sits on the content-script ↔ background message path, so please include it in the AGENTS.md security review. It only injects the extension's own file into the requesting tab's top frame, and only when the user setting is on.

Testing

  • yarn typecheck and ESLint on the changed files pass.
  • build:pro (chrome-mv3) and build:pro:mv2 (firefox-mv2) succeed. In both, content-script.js has no react-dom and perps-widget.js is a chunk that starts after the content script's polyfill chunk (222).
  • Loaded the MV3 build unpacked in Ego Lite (Chromium 156) with a test wallet:
    • widget off (default): nothing is injected on example.com or github.com; window.ethereum is Rabby and eth_chainId answers.
    • widget on via Settings → 永续合约悬浮窗: the widget host mounts on example.com and github.com (strict CSP), with the React container inside its closed shadow root (checked over CDP). It does not mount on app.hyperliquid.xyz (blocked host).
    • widget off again: nothing is injected after reload.
  • Not tested yet:
    • the widget's visible UI, which only renders when the account has open perps positions (the test account has none);
    • running the Firefox MV2 build.

content-script.js runs on every page, and it bundled the perps widget
(React UI plus react-dom) eagerly, because strict-CSP pages such as
GitHub block a chunk fetched from chrome-extension://. The widget is off
by default, so almost every page paid for code it never ran.

Build the widget as its own perps-widget entry. On load the content
script sends RABBY_INJECT_PERPS_WIDGET; the background checks that the
sender is our own content script in a tab's top frame and that the widget
is enabled, then injects perps-widget.js with scripting.executeScript
(tabs.executeScript on MV2). Extension-injected scripts are not subject
to the page's CSP.

The entry uses dependOn: 'content-script', so it runs on the content
script's webpack runtime, which is already loaded in that isolated world.
That is also required by Warden: it inlines module policy only for the
content-script and pageProvider runtimes; any other entry looks for
warden-policy-runtime.js, which only extension pages load, and throws.

build:pro (chrome-mv3 and firefox-mv2): content-script.js 421K -> 251K
(-40.5%, gzip 120K -> 67K); the widget is a 170K chunk; total JS
unchanged.
@ddddhm1234

Copy link
Copy Markdown
Contributor

PR扫描结果

依赖变更

无依赖变更

@rabby-code-reviewer rabby-code-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated inline review.

// which is also the runtime Warden inlines its module policy for.
'perps-widget': {
import: paths.rootResolve('src/content-script/perps-widget/entry.ts'),
dependOn: 'content-script',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking/security: @ddddhm1234 Please review the build/supply-chain changes: the widget now has a separate entry sharing the content-script runtime, with a regenerated module policy.

@cs1707
cs1707 marked this pull request as draft November 4, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants