RUN JAVASCRIPT action — execute rule scripts in the sticky tab #3

Open
lksz wants to merge 6 commits from feature/run-javascript-action into dev
Owner

A rule can optionally execute user-authored JavaScript in the existing sticky tab
(with the destination URL available as url) instead of reloading it. Rules without
a script keep today's reload behavior — fully backward compatible. The incoming tab
is still closed and the sticky tab focused exactly as before.

How it works

  • Execution (MV2): browser.tabs.executeScript(stickyTab.id, { code: wrapper, runAt: 'document_idle' }) — top frame only. No new manifest permissions, no 'unsafe-eval' CSP.
  • Wrapper: user code runs inside an async IIFE with const url = JSON.stringify(destUrl) — the URL is never concatenated raw, top-level await works, and errors/rejections surface as a rejected promise in the background.
  • Fallback: on script failure, the sticky tab falls back to tabs.update(url) so navigation is never lost.
  • Duplicate-URL clicks (incoming URL === sticky tab URL) also trigger the script — re-clicking a link is a primary use case.
  • New optional rule fields: action ('reload' default | 'script'), actionScript, snippetId (reserved for a future snippet library, stubbed).

Changes

  • background.js: buildScriptWrapper, resolveRuleScript, executeStickyScript, shared closeAndFocus, dispatch in handleNavigation; MV3-migration note above buildScriptWrapper
  • options.html/js/css: collapsed "Advanced: Action" section per rule (action select + monospace script editor, visible only for script)
  • lib/acorn.min.js (vendored, MIT — Acorn 8.19.0 via terser, + acorn.LICENSE): save-time syntax validation with line/column in the error, debounced advisory outline while typing; accepts top-level await/return to match the async-IIFE execution context
  • Export payload version marker bumped to 0.2.0; whole rule objects round-trip via export/import
  • Docs: README feature/usage section + AMO reviewer note in docs/misc/AMO_SUBMISSION.md (user-authored code, never fetched remotely)

Testing

  • scripts/test-build.sh: 21/21 pass
  • web-ext lint: 0 errors / 0 warnings / 0 notices
  • Functional harness (mocked browser API): 25/25 — script path (close-first, no reload, focus), wrapper contract, top-level await, throw → fallback reload, duplicate-URL trigger, legacy rules unchanged, empty-script treated as reload
  • Options validation harness: 11/11 — save blocking for empty/bad scripts, line/column reporting, legacy rules unaffected
  • Verified textually and semantically compatible with feature/local-rule-toggles (merge either way is clean; all checks pass on the merged integration)
  • Manual about:debugging smoke test in Firefox still recommended before release (steps in the feature plan)

Notes for reviewers

  • No manifest changes: version stays as-is; no new permissions
  • The executed code originates solely from user input in the user's own extension storage — never fetched remotely (AMO note included)
A rule can optionally execute user-authored JavaScript **in the existing sticky tab** (with the destination URL available as `url`) instead of reloading it. Rules without a script keep today's reload behavior — fully backward compatible. The incoming tab is still closed and the sticky tab focused exactly as before. ### How it works - **Execution (MV2):** `browser.tabs.executeScript(stickyTab.id, { code: wrapper, runAt: 'document_idle' })` — top frame only. No new manifest permissions, no `'unsafe-eval'` CSP. - **Wrapper:** user code runs inside an async IIFE with `const url = JSON.stringify(destUrl)` — the URL is never concatenated raw, top-level `await` works, and errors/rejections surface as a rejected promise in the background. - **Fallback:** on script failure, the sticky tab falls back to `tabs.update(url)` so navigation is never lost. - **Duplicate-URL clicks** (incoming URL === sticky tab URL) also trigger the script — re-clicking a link is a primary use case. - **New optional rule fields:** `action` (`'reload'` default | `'script'`), `actionScript`, `snippetId` (reserved for a future snippet library, stubbed). ### Changes - `background.js`: `buildScriptWrapper`, `resolveRuleScript`, `executeStickyScript`, shared `closeAndFocus`, dispatch in `handleNavigation`; MV3-migration note above `buildScriptWrapper` - `options.html/js/css`: collapsed "Advanced: Action" section per rule (action select + monospace script editor, visible only for `script`) - `lib/acorn.min.js` (vendored, MIT — Acorn 8.19.0 via terser, + `acorn.LICENSE`): save-time syntax validation with line/column in the error, debounced advisory outline while typing; accepts top-level `await`/`return` to match the async-IIFE execution context - Export payload `version` marker bumped to `0.2.0`; whole rule objects round-trip via export/import - Docs: README feature/usage section + AMO reviewer note in `docs/misc/AMO_SUBMISSION.md` (user-authored code, never fetched remotely) ### Testing - `scripts/test-build.sh`: 21/21 pass - `web-ext lint`: 0 errors / 0 warnings / 0 notices - Functional harness (mocked `browser` API): 25/25 — script path (close-first, no reload, focus), wrapper contract, top-level `await`, `throw` → fallback reload, duplicate-URL trigger, legacy rules unchanged, empty-script treated as reload - Options validation harness: 11/11 — save blocking for empty/bad scripts, line/column reporting, legacy rules unaffected - Verified textually **and** semantically compatible with `feature/local-rule-toggles` (merge either way is clean; all checks pass on the merged integration) - Manual `about:debugging` smoke test in Firefox still recommended before release (steps in the feature plan) ### Notes for reviewers - No manifest changes: version stays as-is; no new permissions - The executed code originates solely from user input in the user's own extension storage — never fetched remotely (AMO note included)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
fix(options): accept top-level await/return in script validation to match async IIFE execution
Some checks failed
PR Check / Lint (pull_request) Failing after 42s
PR Check / Build Preview (pull_request) Has been skipped
eaef1dc327
Co-authored-by: Cursor <cursoragent@cursor.com>
lksz force-pushed feature/run-javascript-action from eaef1dc327
Some checks failed
PR Check / Lint (pull_request) Failing after 42s
PR Check / Build Preview (pull_request) Has been skipped
to 251fb26423
All checks were successful
PR Check / Check PR Target (pull_request) Has been skipped
PR Check / Lint (pull_request) Successful in 1m31s
PR Check / Build Preview (pull_request) Has been skipped
2026-10-26 01:34:26 +00:00
Compare
test
Some checks failed
PR Check / Check PR Target (pull_request) Has been skipped
PR Check / Lint (pull_request) Has been cancelled
PR Check / Build Preview (pull_request) Has been cancelled
c4e1f6dc89
lksz force-pushed feature/run-javascript-action from c4e1f6dc89
Some checks failed
PR Check / Check PR Target (pull_request) Has been skipped
PR Check / Lint (pull_request) Has been cancelled
PR Check / Build Preview (pull_request) Has been cancelled
to 6e64ae664d
All checks were successful
PR Check / Check PR Target (pull_request) Has been skipped
PR Check / Lint (pull_request) Successful in 40s
PR Check / Build Preview (pull_request) Has been skipped
2026-10-26 02:12:19 +00:00
Compare
All checks were successful
PR Check / Check PR Target (pull_request) Has been skipped
PR Check / Lint (pull_request) Successful in 40s
PR Check / Build Preview (pull_request) Has been skipped
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin "feature/run-javascript-action:feature/run-javascript-action"
git switch "feature/run-javascript-action"

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch "dev"
git merge --no-ff "feature/run-javascript-action"
git switch "feature/run-javascript-action"
git rebase "dev"
git switch "dev"
git merge --ff-only "feature/run-javascript-action"
git switch "feature/run-javascript-action"
git rebase "dev"
git switch "dev"
git merge --no-ff "feature/run-javascript-action"
git switch "dev"
git merge --squash "feature/run-javascript-action"
git switch "dev"
git merge --ff-only "feature/run-javascript-action"
git switch "dev"
git merge "feature/run-javascript-action"
git push origin "dev"
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lksz/sticky-tabsz!3
No description provided.