sourcemonkey 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/bytes.js +37 -0
- package/dist/channel-placeholder.js +5 -0
- package/dist/collection-scan.js +17 -0
- package/dist/collections.html +353 -0
- package/dist/compare.html +358 -0
- package/dist/composed-source.js +205 -0
- package/dist/connect-prompt.html +47 -0
- package/dist/debug/icons.html +211 -0
- package/dist/debug/storage.html +163 -0
- package/dist/debug-log.js +75 -0
- package/dist/diagnostic-location.js +64 -0
- package/dist/eval-hook.js +308 -0
- package/dist/extension/background.js +5263 -0
- package/dist/extension/channel.js +133 -0
- package/dist/extension/code-links.js +187 -0
- package/dist/extension/code-tabs.js +460 -0
- package/dist/extension/collection-dialog.js +619 -0
- package/dist/extension/collection-display.js +322 -0
- package/dist/extension/collections.js +2905 -0
- package/dist/extension/compare.js +867 -0
- package/dist/extension/concurrency.js +186 -0
- package/dist/extension/connect-grants.js +109 -0
- package/dist/extension/connect-prompt-page.js +70 -0
- package/dist/extension/connect-prompt.js +266 -0
- package/dist/extension/copy-button.js +63 -0
- package/dist/extension/copy-dialog.js +502 -0
- package/dist/extension/copy-preference.js +66 -0
- package/dist/extension/debug/icons.js +203 -0
- package/dist/extension/debug/storage.js +365 -0
- package/dist/extension/deep-link.js +77 -0
- package/dist/extension/delete-dialog.js +432 -0
- package/dist/extension/delete-icon.js +40 -0
- package/dist/extension/details-tooltips.js +66 -0
- package/dist/extension/directories.js +679 -0
- package/dist/extension/export-backup.js +407 -0
- package/dist/extension/export-dialog.js +142 -0
- package/dist/extension/file-access.js +74 -0
- package/dist/extension/file-changes.js +346 -0
- package/dist/extension/frame-info.js +85 -0
- package/dist/extension/github-provider.js +336 -0
- package/dist/extension/glyph.js +84 -0
- package/dist/extension/gm-bindings.js +601 -0
- package/dist/extension/gm-download.js +419 -0
- package/dist/extension/gm-menu.js +228 -0
- package/dist/extension/gm-notification.js +141 -0
- package/dist/extension/gm-originator-map.js +23 -0
- package/dist/extension/gm-tab-data.js +121 -0
- package/dist/extension/gm-tab.js +160 -0
- package/dist/extension/gm-url-change.js +111 -0
- package/dist/extension/gm-url-scheme.js +27 -0
- package/dist/extension/gm-values.js +176 -0
- package/dist/extension/gm-window-control.js +104 -0
- package/dist/extension/gm-xhr-headers.js +333 -0
- package/dist/extension/gm-xhr.js +584 -0
- package/dist/extension/injector.js +3145 -0
- package/dist/extension/install-capture.js +277 -0
- package/dist/extension/line-diff.js +233 -0
- package/dist/extension/local-copy.js +560 -0
- package/dist/extension/local-delete.js +276 -0
- package/dist/extension/location-kinds.js +108 -0
- package/dist/extension/log-format.js +158 -0
- package/dist/extension/managed-changes.js +361 -0
- package/dist/extension/managed-items.js +57 -0
- package/dist/extension/match-index.js +375 -0
- package/dist/extension/missing-scripts.js +66 -0
- package/dist/extension/more-menu.js +109 -0
- package/dist/extension/move-dialog.js +197 -0
- package/dist/extension/offscreen.js +37 -0
- package/dist/extension/options.js +504 -0
- package/dist/extension/page-heading.js +170 -0
- package/dist/extension/page-links.js +199 -0
- package/dist/extension/parse-check-client.js +70 -0
- package/dist/extension/parse-check.js +161 -0
- package/dist/extension/parse-sandbox-host.js +172 -0
- package/dist/extension/parse-sandbox.js +175 -0
- package/dist/extension/popup-commands-open.js +41 -0
- package/dist/extension/popup-view.js +387 -0
- package/dist/extension/popup.js +703 -0
- package/dist/extension/prism-userscript.js +53 -0
- package/dist/extension/refresh-error.js +136 -0
- package/dist/extension/refresh-targets.js +160 -0
- package/dist/extension/related-versions.js +1144 -0
- package/dist/extension/resize-observer-loop.js +19 -0
- package/dist/extension/row-cache.js +387 -0
- package/dist/extension/script-blobs.js +413 -0
- package/dist/extension/script-details.js +658 -0
- package/dist/extension/script-enablement.js +285 -0
- package/dist/extension/script-identity.js +310 -0
- package/dist/extension/script-keys.js +54 -0
- package/dist/extension/script-log-id.js +155 -0
- package/dist/extension/script-log.js +290 -0
- package/dist/extension/script-rejection.js +104 -0
- package/dist/extension/script-sites.js +158 -0
- package/dist/extension/script-store.js +3507 -0
- package/dist/extension/script-updater.js +991 -0
- package/dist/extension/script-url-hash.js +61 -0
- package/dist/extension/select-all.js +37 -0
- package/dist/extension/select-open-guard.js +90 -0
- package/dist/extension/site-icon.js +1261 -0
- package/dist/extension/sm-dev-link.js +302 -0
- package/dist/extension/solo-click.js +58 -0
- package/dist/extension/sticky-numbers.js +188 -0
- package/dist/extension/storage-breakdown.js +253 -0
- package/dist/extension/storage-scan.js +86 -0
- package/dist/extension/storage-usage.js +194 -0
- package/dist/extension/structural-keys.js +29 -0
- package/dist/extension/sw-teardown.js +36 -0
- package/dist/extension/tab-status.js +403 -0
- package/dist/extension/toolbar-summary.js +105 -0
- package/dist/extension/user-scripts-access.js +43 -0
- package/dist/extension/userscript.js +3672 -0
- package/dist/extension/value-store.js +204 -0
- package/dist/extension/version-probe-fetch.js +68 -0
- package/dist/extension/worker-client.js +109 -0
- package/dist/extension/worker-ops.js +171 -0
- package/dist/extension/zip-writer.js +24 -0
- package/dist/external-files.js +878 -0
- package/dist/file-loader.js +436 -0
- package/dist/file-locations.js +439 -0
- package/dist/generated-wrappers.js +5 -0
- package/dist/github-auth.js +148 -0
- package/dist/github-urls.js +46 -0
- package/dist/gm-api-catalog.js +217 -0
- package/dist/gm-connect.js +273 -0
- package/dist/gm-xhr-types.js +5 -0
- package/dist/http-read.js +142 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/local-file-overrides.js +558 -0
- package/dist/manifest.json +38 -0
- package/dist/markup-response.js +126 -0
- package/dist/match-targets.js +305 -0
- package/dist/menu-captions.js +22 -0
- package/dist/offscreen.html +39 -0
- package/dist/options.html +197 -0
- package/dist/parse-check-protocol.js +51 -0
- package/dist/parse-location.js +171 -0
- package/dist/parse-sandbox.html +56 -0
- package/dist/phase-timing.js +155 -0
- package/dist/popup.html +197 -0
- package/dist/public-suffixes.js +6957 -0
- package/dist/run-context.js +31 -0
- package/dist/runtime-messages.js +34 -0
- package/dist/sandboxed-page.js +35 -0
- package/dist/script-build.js +787 -0
- package/dist/script-display.js +383 -0
- package/dist/script-icon.js +43 -0
- package/dist/script-links.js +204 -0
- package/dist/script-source-url.js +214 -0
- package/dist/script-world.js +65 -0
- package/dist/sm-dev-protocol.js +84 -0
- package/dist/source-map.js +177 -0
- package/dist/stack-link.js +138 -0
- package/dist/styles.css +825 -0
- package/dist/unsafe-window-use.js +229 -0
- package/dist/url-match.js +734 -0
- package/dist/userscript-header.js +248 -0
- package/dist/userscript.html +1165 -0
- package/dist/vendor/fflate.LICENSE +21 -0
- package/dist/vendor/fflate.js +2692 -0
- package/dist/vendor/prism/prism-line-numbers.css +1 -0
- package/dist/vendor/prism/prism-line-numbers.js +1 -0
- package/dist/vendor/prism/prism.css +1 -0
- package/dist/vendor/prism/prism.js +1946 -0
- package/dist/version-compare.js +141 -0
- package/dist/version-probe.js +120 -0
- package/lib/build-info.json +1 -0
- package/lib/bytes.d.ts +3 -0
- package/lib/bytes.js +37 -0
- package/lib/channel-placeholder.d.ts +1 -0
- package/lib/channel-placeholder.js +5 -0
- package/lib/collection-scan.d.ts +8 -0
- package/lib/collection-scan.js +17 -0
- package/lib/composed-source.d.ts +28 -0
- package/lib/composed-source.js +205 -0
- package/lib/debug-log.d.ts +9 -0
- package/lib/debug-log.js +75 -0
- package/lib/diagnostic-location.d.ts +8 -0
- package/lib/diagnostic-location.js +64 -0
- package/lib/eval-hook.d.ts +34 -0
- package/lib/eval-hook.js +308 -0
- package/lib/external-files.d.ts +111 -0
- package/lib/external-files.js +878 -0
- package/lib/file-loader.d.ts +48 -0
- package/lib/file-loader.js +436 -0
- package/lib/file-locations.d.ts +44 -0
- package/lib/file-locations.js +439 -0
- package/lib/generated-wrappers.d.ts +11 -0
- package/lib/generated-wrappers.js +5 -0
- package/lib/github-auth.d.ts +12 -0
- package/lib/github-auth.js +148 -0
- package/lib/github-urls.d.ts +1 -0
- package/lib/github-urls.js +46 -0
- package/lib/gm-api-catalog.d.ts +24 -0
- package/lib/gm-api-catalog.js +217 -0
- package/lib/gm-connect.d.ts +34 -0
- package/lib/gm-connect.js +273 -0
- package/lib/gm-xhr-types.d.ts +68 -0
- package/lib/gm-xhr-types.js +5 -0
- package/lib/harness/browser-extensions.d.ts +73 -0
- package/lib/harness/browser-extensions.js +285 -0
- package/lib/harness/browser-lang.d.ts +20 -0
- package/lib/harness/browser-lang.js +86 -0
- package/lib/harness/browser-launch.d.ts +14 -0
- package/lib/harness/browser-launch.js +184 -0
- package/lib/harness/checks.d.ts +110 -0
- package/lib/harness/checks.js +321 -0
- package/lib/harness/cli.d.ts +2 -0
- package/lib/harness/cli.js +2259 -0
- package/lib/harness/command-args.d.ts +91 -0
- package/lib/harness/command-args.js +369 -0
- package/lib/harness/command-lines.d.ts +80 -0
- package/lib/harness/command-lines.js +180 -0
- package/lib/harness/command-list.d.ts +206 -0
- package/lib/harness/command-list.js +1475 -0
- package/lib/harness/console-commands.d.ts +463 -0
- package/lib/harness/console-commands.js +3216 -0
- package/lib/harness/download-watch.d.ts +21 -0
- package/lib/harness/download-watch.js +156 -0
- package/lib/harness/extension-backend.d.ts +41 -0
- package/lib/harness/extension-backend.js +100 -0
- package/lib/harness/extension-host.d.ts +112 -0
- package/lib/harness/extension-host.js +552 -0
- package/lib/harness/extension-launch.d.ts +67 -0
- package/lib/harness/extension-launch.js +285 -0
- package/lib/harness/extension-link.d.ts +315 -0
- package/lib/harness/extension-link.js +607 -0
- package/lib/harness/external-manager.d.ts +146 -0
- package/lib/harness/external-manager.js +414 -0
- package/lib/harness/fixture.d.ts +165 -0
- package/lib/harness/fixture.js +461 -0
- package/lib/harness/frame-sessions.d.ts +71 -0
- package/lib/harness/frame-sessions.js +248 -0
- package/lib/harness/frames.d.ts +68 -0
- package/lib/harness/frames.js +216 -0
- package/lib/harness/help-format.d.ts +17 -0
- package/lib/harness/help-format.js +127 -0
- package/lib/harness/host.d.ts +352 -0
- package/lib/harness/host.js +423 -0
- package/lib/harness/index.d.ts +8 -0
- package/lib/harness/index.js +10 -0
- package/lib/harness/input-file.d.ts +18 -0
- package/lib/harness/input-file.js +61 -0
- package/lib/harness/json-output.d.ts +565 -0
- package/lib/harness/json-output.js +71 -0
- package/lib/harness/manager-actions.d.ts +21 -0
- package/lib/harness/manager-actions.js +129 -0
- package/lib/harness/manager-commands.d.ts +142 -0
- package/lib/harness/manager-commands.js +1196 -0
- package/lib/harness/manager-fetch.d.ts +33 -0
- package/lib/harness/manager-fetch.js +239 -0
- package/lib/harness/managers.d.ts +24 -0
- package/lib/harness/managers.js +27 -0
- package/lib/harness/output-style.d.ts +125 -0
- package/lib/harness/output-style.js +322 -0
- package/lib/harness/page-commands.d.ts +134 -0
- package/lib/harness/page-commands.js +1141 -0
- package/lib/harness/page-output.d.ts +67 -0
- package/lib/harness/page-output.js +139 -0
- package/lib/harness/page-watch.d.ts +171 -0
- package/lib/harness/page-watch.js +607 -0
- package/lib/harness/parse-check-node.d.ts +3 -0
- package/lib/harness/parse-check-node.js +132 -0
- package/lib/harness/path-args.d.ts +22 -0
- package/lib/harness/path-args.js +148 -0
- package/lib/harness/playwright-chromium.d.ts +16 -0
- package/lib/harness/playwright-chromium.js +86 -0
- package/lib/harness/prepare.d.ts +168 -0
- package/lib/harness/prepare.js +364 -0
- package/lib/harness/prompt-editor.d.ts +17 -0
- package/lib/harness/prompt-editor.js +354 -0
- package/lib/harness/prompt-history.d.ts +13 -0
- package/lib/harness/prompt-history.js +89 -0
- package/lib/harness/rebuild-advice.d.ts +6 -0
- package/lib/harness/rebuild-advice.js +39 -0
- package/lib/harness/run-log.d.ts +47 -0
- package/lib/harness/run-log.js +140 -0
- package/lib/harness/run-logs.d.ts +132 -0
- package/lib/harness/run-logs.js +241 -0
- package/lib/harness/run.d.ts +194 -0
- package/lib/harness/run.js +1973 -0
- package/lib/harness/runs.d.ts +229 -0
- package/lib/harness/runs.js +726 -0
- package/lib/harness/save-html.d.ts +39 -0
- package/lib/harness/save-html.js +89 -0
- package/lib/harness/saved-pages.d.ts +120 -0
- package/lib/harness/saved-pages.js +275 -0
- package/lib/harness/script-mark.d.ts +17 -0
- package/lib/harness/script-mark.js +56 -0
- package/lib/harness/snapshot.d.ts +143 -0
- package/lib/harness/snapshot.js +474 -0
- package/lib/harness/stuck-tabs.d.ts +19 -0
- package/lib/harness/stuck-tabs.js +131 -0
- package/lib/harness/tab-refs.d.ts +20 -0
- package/lib/harness/tab-refs.js +80 -0
- package/lib/harness/tampermonkey.d.ts +71 -0
- package/lib/harness/tampermonkey.js +356 -0
- package/lib/harness/tm-ids.d.ts +18 -0
- package/lib/harness/tm-ids.js +88 -0
- package/lib/harness/violentmonkey.d.ts +34 -0
- package/lib/harness/violentmonkey.js +162 -0
- package/lib/harness/wait.d.ts +77 -0
- package/lib/harness/wait.js +458 -0
- package/lib/http-read.d.ts +30 -0
- package/lib/http-read.js +142 -0
- package/lib/local-file-overrides.d.ts +35 -0
- package/lib/local-file-overrides.js +558 -0
- package/lib/manifest.json +38 -0
- package/lib/markup-response.d.ts +11 -0
- package/lib/markup-response.js +126 -0
- package/lib/match-targets.d.ts +18 -0
- package/lib/match-targets.js +305 -0
- package/lib/menu-captions.d.ts +1 -0
- package/lib/menu-captions.js +22 -0
- package/lib/parse-check-protocol.d.ts +27 -0
- package/lib/parse-check-protocol.js +51 -0
- package/lib/parse-location.d.ts +12 -0
- package/lib/parse-location.js +171 -0
- package/lib/phase-timing.d.ts +12 -0
- package/lib/phase-timing.js +155 -0
- package/lib/public-suffixes.d.ts +1 -0
- package/lib/public-suffixes.js +6957 -0
- package/lib/run-context.d.ts +2 -0
- package/lib/run-context.js +31 -0
- package/lib/runtime-messages.d.ts +10 -0
- package/lib/runtime-messages.js +34 -0
- package/lib/sandboxed-page.d.ts +5 -0
- package/lib/sandboxed-page.js +35 -0
- package/lib/script-build.d.ts +111 -0
- package/lib/script-build.js +787 -0
- package/lib/script-display.d.ts +51 -0
- package/lib/script-display.js +383 -0
- package/lib/script-icon.d.ts +16 -0
- package/lib/script-icon.js +43 -0
- package/lib/script-links.d.ts +24 -0
- package/lib/script-links.js +204 -0
- package/lib/script-source-url.d.ts +8 -0
- package/lib/script-source-url.js +214 -0
- package/lib/script-world.d.ts +6 -0
- package/lib/script-world.js +65 -0
- package/lib/sm-dev-protocol.d.ts +251 -0
- package/lib/sm-dev-protocol.js +84 -0
- package/lib/source-map.d.ts +32 -0
- package/lib/source-map.js +177 -0
- package/lib/stack-link.d.ts +15 -0
- package/lib/stack-link.js +138 -0
- package/lib/unsafe-window-use.d.ts +1 -0
- package/lib/unsafe-window-use.js +229 -0
- package/lib/url-match.d.ts +40 -0
- package/lib/url-match.js +734 -0
- package/lib/userscript-header.d.ts +33 -0
- package/lib/userscript-header.js +248 -0
- package/lib/version-compare.d.ts +6 -0
- package/lib/version-compare.js +141 -0
- package/lib/version-probe.d.ts +17 -0
- package/lib/version-probe.js +120 -0
- package/package.json +55 -3
- package/skills/SourceMonkey-dev/SKILL.md +868 -0
- package/skills/SourceMonkey-dev/references/cdp.md +293 -0
- package/skills/SourceMonkey-dev/references/commands.md +297 -0
- package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
- package/skills/SourceMonkey-dev/references/specs.md +46 -0
- package/skills/SourceMonkey-friction/SKILL.md +103 -0
- package/skills/SourceMonkey-friction/references/template.md +59 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Pure helpers for the userscript viewer's link rendering. Kept
|
|
2
|
+
// DOM-free so the URL-detection + tag-classification rules can be
|
|
3
|
+
// unit tested in Node without a Prism-shaped DOM.
|
|
4
|
+
//
|
|
5
|
+
// Two consumers in src/extension/userscript.ts:
|
|
6
|
+
// - The Details panel renderer (Match / Location / etc.).
|
|
7
|
+
// - The post-Prism walk over the highlighted code box.
|
|
8
|
+
// Both call the same classifiers so a header line in the body is
|
|
9
|
+
// linkified by the same rules as the corresponding details row.
|
|
10
|
+
// Tags whose value is a single resource reference. The body-linkifier
|
|
11
|
+
// passes the value through file-loader's `resolveScriptResource`,
|
|
12
|
+
// which both linkifies absolute http(s) URLs and resolves relative
|
|
13
|
+
// paths against the script's own URL (the same rule the `@icon`
|
|
14
|
+
// favicon path uses). Anything the resolver rejects falls
|
|
15
|
+
// back to plain text.
|
|
16
|
+
//
|
|
17
|
+
// `@resource` is *not* in this set even though its second token is a
|
|
18
|
+
// URL — its grammar is two args (`<name> <url>`), so it gets its own
|
|
19
|
+
// carve-out in `linkForHeaderTag` (src/extension/userscript.ts) that linkifies
|
|
20
|
+
// only the URL portion. Treating it here would mis-resolve
|
|
21
|
+
// `myCSS https://example.com/x.css` as a single relative path
|
|
22
|
+
// `<parent>/myCSS%20https://...`.
|
|
23
|
+
export const RESOLVABLE_HEADER_TAGS = new Set([
|
|
24
|
+
'icon', 'iconurl', 'defaulticon', 'icon64', 'icon64url',
|
|
25
|
+
'require',
|
|
26
|
+
'updateurl', 'downloadurl',
|
|
27
|
+
'homepage', 'homepageurl', 'website', 'source',
|
|
28
|
+
'supporturl',
|
|
29
|
+
]);
|
|
30
|
+
// Tags whose value is shaped like a Chrome `@match` pattern. Formally
|
|
31
|
+
// `@include` and `@exclude` are free-form globs over the whole URL
|
|
32
|
+
// (or `/regex/` literals), but in practice almost every excluded /
|
|
33
|
+
// included pattern is written as a match pattern, and giving the
|
|
34
|
+
// user a click target is more useful than rendering plain text.
|
|
35
|
+
// `matchTargetInfo` returns null for shapes it can't dewildcard
|
|
36
|
+
// (regex literals, `<all_urls>`, host wildcards) and those still
|
|
37
|
+
// fall back to plain text.
|
|
38
|
+
export const MATCH_HEADER_TAGS = new Set([
|
|
39
|
+
'match', 'exclude', 'include',
|
|
40
|
+
]);
|
|
41
|
+
// Word-boundary anchor keeps `https://…` inside a longer identifier
|
|
42
|
+
// (rare but possible inside a string literal) from being picked up
|
|
43
|
+
// mid-token. The character class excludes whitespace, quotes, angle
|
|
44
|
+
// brackets, and backticks so a URL inside `"…"` or `<…>` stops at
|
|
45
|
+
// the delimiter. `*` is also excluded so a `@match` / `@exclude`
|
|
46
|
+
// pattern like `https://example.com/athletic/*` yields a clickable
|
|
47
|
+
// `https://example.com/athletic/` followed by a literal `*` — `*` is
|
|
48
|
+
// technically a legal URL character but in userscript headers it's
|
|
49
|
+
// always a glob wildcard, and including it in the href produces a
|
|
50
|
+
// link that doesn't navigate anywhere useful.
|
|
51
|
+
const URL_RE = /\b(?:https?|file):\/\/[^\s'"<>`*]+/g;
|
|
52
|
+
// Trailing punctuation that's commonly adjacent to URLs in prose.
|
|
53
|
+
// `)` and `]` are conditional (see peelTrailingPunctuation) so a
|
|
54
|
+
// URL with balanced brackets like `…/Foo_(disambig)` keeps them.
|
|
55
|
+
const ALWAYS_PEEL_RE = /[.,;:!?]+$/;
|
|
56
|
+
export function splitTextByUrls(text) {
|
|
57
|
+
const out = [];
|
|
58
|
+
let cursor = 0;
|
|
59
|
+
for (const m of text.matchAll(URL_RE)) {
|
|
60
|
+
const start = m.index ?? 0;
|
|
61
|
+
if (start > cursor) {
|
|
62
|
+
out.push({ kind: 'text', value: text.slice(cursor, start) });
|
|
63
|
+
}
|
|
64
|
+
const { url, trailing } = peelTrailingPunctuation(m[0]);
|
|
65
|
+
out.push({ kind: 'url', value: url });
|
|
66
|
+
if (trailing)
|
|
67
|
+
out.push({ kind: 'text', value: trailing });
|
|
68
|
+
cursor = start + m[0].length;
|
|
69
|
+
}
|
|
70
|
+
if (cursor < text.length) {
|
|
71
|
+
out.push({ kind: 'text', value: text.slice(cursor) });
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
// Peel trailing prose punctuation off a candidate URL. Closing
|
|
76
|
+
// brackets `)` and `]` only peel when the URL has no matching open
|
|
77
|
+
// bracket — so `(https://example.com/x)` peels the `)` (likely
|
|
78
|
+
// surrounding parens in prose) but `https://en.wikipedia.org/wiki/Foo_(disambig)`
|
|
79
|
+
// keeps it (URL contains its own `(`). Always-peelable chars
|
|
80
|
+
// (`.,;:!?`) come off first.
|
|
81
|
+
function peelTrailingPunctuation(raw) {
|
|
82
|
+
let url = raw;
|
|
83
|
+
let trailing = '';
|
|
84
|
+
// Always-peelable run first; close-brackets one-at-a-time after,
|
|
85
|
+
// since a single URL might end in `…)?` and we want both gone.
|
|
86
|
+
let progressed = true;
|
|
87
|
+
while (progressed) {
|
|
88
|
+
progressed = false;
|
|
89
|
+
const m = ALWAYS_PEEL_RE.exec(url);
|
|
90
|
+
if (m) {
|
|
91
|
+
trailing = m[0] + trailing;
|
|
92
|
+
url = url.slice(0, url.length - m[0].length);
|
|
93
|
+
progressed = true;
|
|
94
|
+
}
|
|
95
|
+
const last = url.charAt(url.length - 1);
|
|
96
|
+
if ((last === ')' && !url.includes('(')) ||
|
|
97
|
+
(last === ']' && !url.includes('['))) {
|
|
98
|
+
trailing = last + trailing;
|
|
99
|
+
url = url.slice(0, url.length - 1);
|
|
100
|
+
progressed = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { url, trailing };
|
|
104
|
+
}
|
|
105
|
+
// Trailing-whitespace class includes `\r` so a CRLF-encoded line
|
|
106
|
+
// that reaches us with a stray `\r` at the end still matches. The
|
|
107
|
+
// rest of the SourceMonkey pipeline appears LF-normalised but the
|
|
108
|
+
// regex stays cheap, and the `\r` fallback keeps `parseTagLine`
|
|
109
|
+
// robust against any future input that bypasses normalisation.
|
|
110
|
+
const TAG_LINE_RE = /^([ \t]*\/\/[ \t]*@([A-Za-z][\w-]*)(?::([\w-]+))?[ \t]+)(\S.*?)([ \t\r]*)$/;
|
|
111
|
+
export function parseTagLine(line) {
|
|
112
|
+
const m = TAG_LINE_RE.exec(line);
|
|
113
|
+
if (!m)
|
|
114
|
+
return null;
|
|
115
|
+
const [, before, tag, locale, value, after] = m;
|
|
116
|
+
const out = {
|
|
117
|
+
before,
|
|
118
|
+
tag: tag.toLowerCase(),
|
|
119
|
+
value,
|
|
120
|
+
after,
|
|
121
|
+
};
|
|
122
|
+
if (locale)
|
|
123
|
+
out.locale = locale;
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
export function splitResourceValue(value) {
|
|
127
|
+
const gap = value.search(/[ \t]/);
|
|
128
|
+
if (gap < 0)
|
|
129
|
+
return null;
|
|
130
|
+
const afterGap = value.slice(gap);
|
|
131
|
+
const gapLen = afterGap.match(/^[ \t]+/)[0].length;
|
|
132
|
+
const urlStart = gap + gapLen;
|
|
133
|
+
const rest = value.slice(urlStart);
|
|
134
|
+
const trail = rest.search(/[ \t]/);
|
|
135
|
+
const urlEnd = trail < 0 ? value.length : urlStart + trail;
|
|
136
|
+
const url = value.slice(urlStart, urlEnd);
|
|
137
|
+
if (!url)
|
|
138
|
+
return null;
|
|
139
|
+
return {
|
|
140
|
+
prefix: value.slice(0, urlStart),
|
|
141
|
+
url,
|
|
142
|
+
suffix: value.slice(urlEnd),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Build a click-through URL for one `@connect` value. The
|
|
146
|
+
// body-linkifier wraps the verbatim value text in an anchor pointing
|
|
147
|
+
// here; the anchor's `href` is normalised via the URL parser so the
|
|
148
|
+
// click goes somewhere navigable even when the header value was just
|
|
149
|
+
// a bare host.
|
|
150
|
+
//
|
|
151
|
+
// Rules:
|
|
152
|
+
// - `*` and `self` → null (no single site to point at).
|
|
153
|
+
// - `<scheme>://…` → verbatim URL (only http/https accepted).
|
|
154
|
+
// - `*.example.com` → `https://example.com/` (dewildcarded apex,
|
|
155
|
+
// matching the same rule `matchTargetInfo` applies for @match
|
|
156
|
+
// subdomain wildcards).
|
|
157
|
+
// - bare host (`example.com`, `localhost`, `1.2.3.4`) →
|
|
158
|
+
// `https://<host>/`.
|
|
159
|
+
// - Anything that doesn't parse as a URL after that → null.
|
|
160
|
+
//
|
|
161
|
+
// HTTPS is the assumed default for bare hosts even though the
|
|
162
|
+
// userscript may issue HTTP requests via GM_xmlhttpRequest — the
|
|
163
|
+
// click-through is just "visit this site to look it up", and
|
|
164
|
+
// HTTPS is the right default for that.
|
|
165
|
+
export function connectLinkUrl(raw) {
|
|
166
|
+
const t = raw.trim();
|
|
167
|
+
if (!t || t === '*')
|
|
168
|
+
return null;
|
|
169
|
+
if (t.toLowerCase() === 'self')
|
|
170
|
+
return null;
|
|
171
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(t)) {
|
|
172
|
+
try {
|
|
173
|
+
const u = new URL(t);
|
|
174
|
+
if (u.protocol === 'http:' || u.protocol === 'https:') {
|
|
175
|
+
return u.toString();
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
let host = t;
|
|
184
|
+
if (host.startsWith('*.'))
|
|
185
|
+
host = host.slice(2);
|
|
186
|
+
if (!host || host.includes('*'))
|
|
187
|
+
return null;
|
|
188
|
+
try {
|
|
189
|
+
return new URL(`https://${host}/`).toString();
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Sentinel line shapes that bracket the metadata block. Used by the
|
|
196
|
+
// body-linkifier to flip its "inside header" state.
|
|
197
|
+
const HEADER_OPEN_RE = /^[ \t]*\/\/[ \t]*==UserScript==[ \t]*$/;
|
|
198
|
+
const HEADER_CLOSE_RE = /^[ \t]*\/\/[ \t]*==\/UserScript==[ \t]*$/;
|
|
199
|
+
export function isHeaderOpenLine(line) {
|
|
200
|
+
return HEADER_OPEN_RE.test(line);
|
|
201
|
+
}
|
|
202
|
+
export function isHeaderCloseLine(line) {
|
|
203
|
+
return HEADER_CLOSE_RE.test(line);
|
|
204
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const ROOT = "sourcemonkey://SourceMonkey";
|
|
2
|
+
export declare function buildSourceUrl(logLabel: string): string;
|
|
3
|
+
export declare function buildConfigSourceUrl(logLabel: string): string;
|
|
4
|
+
export declare function buildSealSourceUrl(logLabel: string): string;
|
|
5
|
+
export declare const PRELUDE_SOURCE_URL = "sourcemonkey://SourceMonkey/__prelude.js";
|
|
6
|
+
export declare const BRIDGE_SOURCE_URL = "sourcemonkey://SourceMonkey/__bridge.js";
|
|
7
|
+
export declare const GM_RUNTIME_SOURCE_URL = "sourcemonkey://SourceMonkey/__gm-runtime.js";
|
|
8
|
+
export declare function buildRequireSourceUrl(logLabel: string, requireUrl: string): string;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Canonical `sourcemonkey://SourceMonkey/...` URLs the injector
|
|
2
|
+
// emits as `//# sourceURL=` directives, and that the viewer matches
|
|
3
|
+
// stack-trace refs against.
|
|
4
|
+
//
|
|
5
|
+
// Why this URL shape:
|
|
6
|
+
//
|
|
7
|
+
// - The `sourcemonkey:` scheme is arbitrary — it just has to be
|
|
8
|
+
// URL-shaped, unique to us (the wrapper's async-error filter
|
|
9
|
+
// greps stack frames for it), and recognizable in stack traces.
|
|
10
|
+
// - The literal `SourceMonkey` host puts everything we register
|
|
11
|
+
// under a single top folder in devtools' Sources tree. Devtools
|
|
12
|
+
// strips the scheme and uses the URL's authority as the first
|
|
13
|
+
// visible folder; without the host slot, our scripts would
|
|
14
|
+
// scatter at the top level.
|
|
15
|
+
// - The next folder is the script's human-readable log label
|
|
16
|
+
// (`bar`, `bar%232`, etc.) — see `script-log-id.ts`. We use the
|
|
17
|
+
// log label rather than the script's UUID because the UUID is a
|
|
18
|
+
// debug-grade hash that's useless when you're scanning the tree.
|
|
19
|
+
// Per-script artifacts (`__config.js`, the main body, each
|
|
20
|
+
// `@require`) live under that folder so they cluster together
|
|
21
|
+
// by script.
|
|
22
|
+
// - Page-wide URLs (`__bridge.js`, `__gm-runtime.js`, `__prelude.js`)
|
|
23
|
+
// live directly under `SourceMonkey/`. The bridge and GM runtime
|
|
24
|
+
// are true singletons (one chrome.userScripts registration each);
|
|
25
|
+
// the prelude is inlined per-script but every inclusion uses the
|
|
26
|
+
// same `__prelude.js` URL so devtools' Sources tree dedups them
|
|
27
|
+
// into a single visible entry.
|
|
28
|
+
//
|
|
29
|
+
// Why we percent-encode the label: it is a *display* string
|
|
30
|
+
// (`script-log-id.ts` decodes URL-derived filenames so the user sees
|
|
31
|
+
// `My Script.user.js`), and several of the characters that survive
|
|
32
|
+
// that decode would break a URL. A bare `#` is the fragment
|
|
33
|
+
// separator, so the `#N` dedup suffix would split the path; a space
|
|
34
|
+
// or a paren ends the URL run that `stack-link.ts` matches, so a
|
|
35
|
+
// stack-trace ref would stop linkifying to its tab. Everything
|
|
36
|
+
// outside an unreserved-character whitelist is re-encoded — devtools
|
|
37
|
+
// shows the encoded form, same as it always has. The viewer's Log tab
|
|
38
|
+
// decodes it back for display (`displayRefText` in `stack-link.ts`),
|
|
39
|
+
// so the two disagree on purpose: devtools indexes the file under
|
|
40
|
+
// `main%232`, the Log tab reads `main#2` and keeps the wire form in a
|
|
41
|
+
// tooltip.
|
|
42
|
+
// Exported so consumers can recognize our own URLs by the exact prefix
|
|
43
|
+
// rather than by the scheme alone — the scheme is unregistered, so a
|
|
44
|
+
// hostile script can put `sourcemonkey://anything/…` in a log message
|
|
45
|
+
// and have it parsed as a ref.
|
|
46
|
+
export const ROOT = 'sourcemonkey://SourceMonkey';
|
|
47
|
+
// Strip characters that would break out of the `//# sourceURL=<url>`
|
|
48
|
+
// comment these URLs are spliced into. The log label is derived from
|
|
49
|
+
// the on-disk filename (`script-log-id.ts`), which is attacker-
|
|
50
|
+
// controlled for an untrusted script bundle. A JS line terminator
|
|
51
|
+
// (LF/CR/U+2028/U+2029) ends the single-line `//` comment, so anything
|
|
52
|
+
// after it on the line would become live code in the config blob —
|
|
53
|
+
// which runs at document_start, before the script's own gating. Other
|
|
54
|
+
// control chars can't break the comment but have no business in a
|
|
55
|
+
// devtools tree path either; replace the lot with `_`. (The @require
|
|
56
|
+
// URL builder already does per-segment whitelisting; this covers the
|
|
57
|
+
// main/config/prelude labels that don't go through it.)
|
|
58
|
+
function sanitizeLabel(label) {
|
|
59
|
+
// eslint-disable-next-line no-control-regex
|
|
60
|
+
return label.replace(/[\u0000-\u001f\u007f\u2028\u2029]/g, '_');
|
|
61
|
+
}
|
|
62
|
+
// Percent-encode everything outside the URL unreserved set, so the
|
|
63
|
+
// segment is safe in a path AND carries nothing that would terminate
|
|
64
|
+
// the stack-trace ref pattern in `stack-link.ts`. Applied last, after
|
|
65
|
+
// the `.user.js` / `#N` surgery below has matched against the literal
|
|
66
|
+
// characters.
|
|
67
|
+
// The `u` flag matters: without it the character class matches single
|
|
68
|
+
// UTF-16 code units, so an emoji (surrogate pair — GreasyFork names
|
|
69
|
+
// are full of them) would be handed to encodeURIComponent half at a
|
|
70
|
+
// time and throw URIError on the lone surrogate.
|
|
71
|
+
function encodeSegment(segment) {
|
|
72
|
+
return segment.replace(/[^A-Za-z0-9._~-]/gu, (c) => {
|
|
73
|
+
let enc;
|
|
74
|
+
try {
|
|
75
|
+
enc = encodeURIComponent(c);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// An UNPAIRED surrogate — `u` makes the class match it as a
|
|
79
|
+
// single code point, but there's no UTF-8 encoding for it and
|
|
80
|
+
// encodeURIComponent throws. A manifest entry is user-authored
|
|
81
|
+
// JSON, so `"\ud800.user.js"` really can reach us. Letting the
|
|
82
|
+
// throw escape would reject the whole `Promise.all` in
|
|
83
|
+
// collectActiveSpecs and register NOTHING.
|
|
84
|
+
return '_';
|
|
85
|
+
}
|
|
86
|
+
// encodeURIComponent passes `!'()*` through untouched, and parens
|
|
87
|
+
// are exactly what would truncate a `at fn (URL:L:C)` frame — so
|
|
88
|
+
// hex-encode anything it declined to. All such characters are
|
|
89
|
+
// single-byte ASCII.
|
|
90
|
+
return enc === c ? `%${c.charCodeAt(0).toString(16).toUpperCase()}` : enc;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Folder name for a script's artifacts. Strips the conventional
|
|
94
|
+
// `.user.js` extension from the log label (the folder sits ABOVE the
|
|
95
|
+
// main-body file, so the `.js` looks misleading on a folder), then
|
|
96
|
+
// encodes what's left — which is where the `#E4` script-number suffix
|
|
97
|
+
// (or an unnumbered script's `#N`) becomes `%23E4`.
|
|
98
|
+
//
|
|
99
|
+
// Examples:
|
|
100
|
+
// `foo.user.js` → `foo`
|
|
101
|
+
// `foo.user.js#E4` → `foo%23E4`
|
|
102
|
+
// `foo.user.js#2` → `foo%232`
|
|
103
|
+
// `My foo.user.js` → `My%20foo`
|
|
104
|
+
// `foo` → `foo` (no .user.js to strip)
|
|
105
|
+
function folderForLabel(label) {
|
|
106
|
+
return encodeSegment(sanitizeLabel(label).replace(/\.user\.js(?=$|#)/, ''));
|
|
107
|
+
}
|
|
108
|
+
// Main-body file basename for a script. Same as the folder but with
|
|
109
|
+
// the suffix dropped and `.user.js` restored — so the file's name
|
|
110
|
+
// reflects the script's natural filename without the disambiguator
|
|
111
|
+
// (each script lives in a different folder anyway).
|
|
112
|
+
//
|
|
113
|
+
// Examples:
|
|
114
|
+
// `foo.user.js` → `foo.user.js`
|
|
115
|
+
// `foo.user.js#E4` → `foo.user.js`
|
|
116
|
+
// `foo.user.js#2` → `foo.user.js`
|
|
117
|
+
// `foo` → `foo.user.js`
|
|
118
|
+
function mainBaseForLabel(label) {
|
|
119
|
+
// Suffix first, extension second. The other order would eat a `#2`
|
|
120
|
+
// that's part of the name itself (`Chapter #2.user.js`) — which a
|
|
121
|
+
// decoded `%23` now makes reachable.
|
|
122
|
+
const base = sanitizeLabel(label).replace(/#E?\d+$/, '').replace(/\.user\.js$/, '');
|
|
123
|
+
return encodeSegment(base) + '.user.js';
|
|
124
|
+
}
|
|
125
|
+
// Per-script folder root.
|
|
126
|
+
function scriptRoot(logLabel) {
|
|
127
|
+
return `${ROOT}/${folderForLabel(logLabel)}`;
|
|
128
|
+
}
|
|
129
|
+
// Canonical URL for the main script body.
|
|
130
|
+
export function buildSourceUrl(logLabel) {
|
|
131
|
+
return `${scriptRoot(logLabel)}/${mainBaseForLabel(logLabel)}`;
|
|
132
|
+
}
|
|
133
|
+
// Canonical URL for the per-script config blob — the tiny {code}
|
|
134
|
+
// entry the injector emits as the first js[] entry.
|
|
135
|
+
export function buildConfigSourceUrl(logLabel) {
|
|
136
|
+
return `${scriptRoot(logLabel)}/__config.js`;
|
|
137
|
+
}
|
|
138
|
+
// Canonical URL for the per-script parse-failure sentinel — the last
|
|
139
|
+
// js[] entry. Distinct from the config blob's URL on purpose: devtools
|
|
140
|
+
// dedups Sources-tree nodes by URL, which is safe for the prelude only
|
|
141
|
+
// because every copy of it is byte-identical. The sentinel and the
|
|
142
|
+
// config blob are different code, so sharing a URL would show one node
|
|
143
|
+
// whose text matches just one of them.
|
|
144
|
+
export function buildSealSourceUrl(logLabel) {
|
|
145
|
+
return `${scriptRoot(logLabel)}/__seal.js`;
|
|
146
|
+
}
|
|
147
|
+
// Canonical URL for the prelude. Bytes (WRAPPER_PRELUDE) are
|
|
148
|
+
// byte-identical for every script, so every per-script inclusion
|
|
149
|
+
// tags itself with this same URL — devtools' Sources tree groups
|
|
150
|
+
// the N inclusions into a single visible entry alongside the
|
|
151
|
+
// bridge / GM runtime at the SourceMonkey/ top level. See
|
|
152
|
+
// wrapPreludeEntry in injector.ts.
|
|
153
|
+
export const PRELUDE_SOURCE_URL = `${ROOT}/__prelude.js`;
|
|
154
|
+
// Canonical URL for the page-wide MAIN-world bridge.
|
|
155
|
+
export const BRIDGE_SOURCE_URL = `${ROOT}/__bridge.js`;
|
|
156
|
+
// Canonical URL for the page-wide GM_* runtime.
|
|
157
|
+
export const GM_RUNTIME_SOURCE_URL = `${ROOT}/__gm-runtime.js`;
|
|
158
|
+
// Canonical URL for one of a script's `@require` entries. We map
|
|
159
|
+
// the require's real fetch URL into our synthetic scheme so:
|
|
160
|
+
// - the entry clusters with its owning script in devtools, and
|
|
161
|
+
// - stack frames are honest about the fact that we stored a
|
|
162
|
+
// snapshot of the bytes (we're not re-fetching at run time).
|
|
163
|
+
//
|
|
164
|
+
// Shape: `sourcemonkey://SourceMonkey/<folder>/<host>/<path>` — the
|
|
165
|
+
// original URL's host + path become subfolders so the user can still
|
|
166
|
+
// see the provenance in the Sources tree. Query strings and fragments
|
|
167
|
+
// are dropped (the cached bytes don't depend on them). Two requires
|
|
168
|
+
// on the same script with the same host+path but different `?…`
|
|
169
|
+
// query strings would land at the same synthetic URL. The viewer's
|
|
170
|
+
// tab lookup takes the FIRST match, so a mapped stack frame from the
|
|
171
|
+
// second require opens the first require's tab — the source map picks
|
|
172
|
+
// the right segment, only the tab lookup is ambiguous. Rare in
|
|
173
|
+
// practice; the file-loader already dedups by canonical URL with
|
|
174
|
+
// query, so it'd take two legitimately different `@require` lines
|
|
175
|
+
// pointing at distinct stored copies. Worth revisiting if it ever
|
|
176
|
+
// shows up: including a hash of the query in the path would fix it at
|
|
177
|
+
// the cost of an uglier devtools tree.
|
|
178
|
+
//
|
|
179
|
+
// `file://` requires (resolved local files) use the synthetic host
|
|
180
|
+
// `file` so they land under `<folder>/file/<path>` rather than
|
|
181
|
+
// dropping out of the tree.
|
|
182
|
+
//
|
|
183
|
+
// On a URL we can't parse (shouldn't happen — the file-loader
|
|
184
|
+
// validates before storage) we fall back to `<folder>/require` so we
|
|
185
|
+
// still emit *something* parseable.
|
|
186
|
+
export function buildRequireSourceUrl(logLabel, requireUrl) {
|
|
187
|
+
let host;
|
|
188
|
+
let path;
|
|
189
|
+
try {
|
|
190
|
+
const u = new URL(requireUrl);
|
|
191
|
+
host = u.protocol === 'file:' ? 'file' : u.hostname;
|
|
192
|
+
if (!host)
|
|
193
|
+
host = u.protocol.replace(/:$/, '');
|
|
194
|
+
path = u.pathname.replace(/^\/+/, '');
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return `${scriptRoot(logLabel)}/require`;
|
|
198
|
+
}
|
|
199
|
+
// Sanitize each path segment. `new URL()` above already normalized
|
|
200
|
+
// `a/../b` to `b`, so the `..` / `.` filter is belt-and-suspenders
|
|
201
|
+
// — kept in case the input ever reaches us by a path that bypasses
|
|
202
|
+
// URL parsing (it doesn't today). The real work here is the
|
|
203
|
+
// per-segment character whitelist, which keeps spaces / special
|
|
204
|
+
// chars from confusing the devtools tree.
|
|
205
|
+
const cleanPath = path
|
|
206
|
+
.split('/')
|
|
207
|
+
.map((seg) => seg.replace(/[^a-zA-Z0-9._-]/g, '_'))
|
|
208
|
+
.filter((seg) => seg && seg !== '.' && seg !== '..')
|
|
209
|
+
.join('/');
|
|
210
|
+
const safeHost = host.replace(/[^a-zA-Z0-9._-]/g, '_') || 'require';
|
|
211
|
+
return cleanPath
|
|
212
|
+
? `${scriptRoot(logLabel)}/${safeHost}/${cleanPath}`
|
|
213
|
+
: `${scriptRoot(logLabel)}/${safeHost}/require`;
|
|
214
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ParsedHeader } from './userscript-header.js';
|
|
2
|
+
import type { StoredScript } from './script-build.js';
|
|
3
|
+
export type InjectionWorld = 'MAIN' | 'USER_SCRIPT';
|
|
4
|
+
export declare function resolveWorld(header: ParsedHeader | null | undefined): InjectionWorld;
|
|
5
|
+
export declare function resolveScriptWorld(script: Pick<StoredScript, 'header'>): InjectionWorld;
|
|
6
|
+
export declare function runsSandboxed(script: Pick<StoredScript, 'header'>): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Which execution world a script runs in, from its header. Its own
|
|
2
|
+
// module so the harness can ask without loading the injector.
|
|
3
|
+
import { getValue, getValues } from './userscript-header.js';
|
|
4
|
+
import { resolveGrants } from './gm-api-catalog.js';
|
|
5
|
+
// Decide which execution world a script should run in. Like
|
|
6
|
+
// Tampermonkey and Violentmonkey, every script starts in the page's own
|
|
7
|
+
// world (MAIN) and leaves it only when its header asks:
|
|
8
|
+
//
|
|
9
|
+
// - `@inject-into page` → MAIN
|
|
10
|
+
// - `@inject-into content` → USER_SCRIPT (isolated)
|
|
11
|
+
// - `@sandbox raw` → MAIN
|
|
12
|
+
// - `@sandbox JavaScript` → MAIN (TM's choice on Chrome too; only
|
|
13
|
+
// Firefox has a separate world for it)
|
|
14
|
+
// - `@sandbox DOM` → USER_SCRIPT
|
|
15
|
+
// - anything else, unset, `@inject-into auto` → MAIN
|
|
16
|
+
//
|
|
17
|
+
// `@inject-into` (VM's) is read first when a script has both.
|
|
18
|
+
//
|
|
19
|
+
// Why the page by default: scripts written against TM and VM read the
|
|
20
|
+
// page's globals (its `$`, an app's state, `customElements`) without
|
|
21
|
+
// `unsafeWindow`, since there they are in the page. In USER_SCRIPT world
|
|
22
|
+
// those read as `undefined` and the script silently breaks. Any script
|
|
23
|
+
// but a `@grant none` one still keeps its writes off the page's
|
|
24
|
+
// `window`: it runs in a sandbox `window` (`runsSandboxed` below).
|
|
25
|
+
//
|
|
26
|
+
// What the page costs a script, and why `@inject-into content` exists:
|
|
27
|
+
// see docs/injection.md, "Execution world".
|
|
28
|
+
//
|
|
29
|
+
// `@inject-into content` with `unsafeWindow` (by grant or by use) gets
|
|
30
|
+
// that world's own `window` as `unsafeWindow`, matching Violentmonkey's
|
|
31
|
+
// content mode, plus a startup-log warning; see `extractStartupLogs`.
|
|
32
|
+
export function resolveWorld(header) {
|
|
33
|
+
if (!header)
|
|
34
|
+
return 'MAIN';
|
|
35
|
+
const injectInto = (getValue(header, 'inject-into') ?? '').trim().toLowerCase();
|
|
36
|
+
if (injectInto === 'page')
|
|
37
|
+
return 'MAIN';
|
|
38
|
+
if (injectInto === 'content')
|
|
39
|
+
return 'USER_SCRIPT';
|
|
40
|
+
const sandbox = (getValue(header, 'sandbox') ?? '').trim().toLowerCase();
|
|
41
|
+
if (sandbox === 'dom')
|
|
42
|
+
return 'USER_SCRIPT';
|
|
43
|
+
return 'MAIN';
|
|
44
|
+
}
|
|
45
|
+
// The world for a stored row, the form every production caller uses:
|
|
46
|
+
// the spec build, the match index and the reload-swap world check all
|
|
47
|
+
// have to agree.
|
|
48
|
+
export function resolveScriptWorld(script) {
|
|
49
|
+
return resolveWorld(script.header);
|
|
50
|
+
}
|
|
51
|
+
// Whether the script's user program runs inside a sandbox `window`
|
|
52
|
+
// (the prelude's `sandbox`): a page-world script, unless it has
|
|
53
|
+
// `@grant none`. A script with no `@grant` line at all is sandboxed
|
|
54
|
+
// too, as Tampermonkey 5.5.0 and Violentmonkey 2.49.0 run it
|
|
55
|
+
// (measured). That's how both run a script in the page: bare names
|
|
56
|
+
// reach the page's globals, but the sandbox `window` shows only the
|
|
57
|
+
// browser's built-ins, and the script's own writes, a `@require`d
|
|
58
|
+
// library's `window.jQuery = …` included, stay off the page's
|
|
59
|
+
// `window`. A `@grant none` script gets the page's `window` itself, as
|
|
60
|
+
// under both. See docs/injection.md, "The sandbox window".
|
|
61
|
+
export function runsSandboxed(script) {
|
|
62
|
+
if (!script.header || resolveScriptWorld(script) !== 'MAIN')
|
|
63
|
+
return false;
|
|
64
|
+
return !resolveGrants(getValues(script.header, 'grant')).hasNone;
|
|
65
|
+
}
|