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,133 @@
|
|
|
1
|
+
// The channel key: a random secret, one per install, baked into the code
|
|
2
|
+
// we register, that keeps the page out of what our page-world (MAIN)
|
|
3
|
+
// pieces share. See docs/injection.md#the-channel-key.
|
|
4
|
+
//
|
|
5
|
+
// Two things use it:
|
|
6
|
+
// - The registry. Our MAIN-world entries (each script's config, prelude
|
|
7
|
+
// and program, the GM runtime) are separate scripts, so they meet
|
|
8
|
+
// through the document. Each gets the one shared registry object from
|
|
9
|
+
// `document.__smGate(key)`, which hands it out only for the key. The
|
|
10
|
+
// page can see the gate but can't call it usefully, and can't
|
|
11
|
+
// replace it: it's defined at document_start, before any page script,
|
|
12
|
+
// non-writable and non-configurable.
|
|
13
|
+
// - The bridge's events. Their names end in the key, so the page can
|
|
14
|
+
// neither send nor hear them.
|
|
15
|
+
//
|
|
16
|
+
// The page never sees the key: it is only ever in code Chrome injects
|
|
17
|
+
// (which the page can't read), and only in top-level statements and in
|
|
18
|
+
// functions that run before any page script, never in one the page could
|
|
19
|
+
// get hold of later (through `.caller`, or a stack trace's call sites)
|
|
20
|
+
// and `toString`. See docs/injection.md#the-channel-key.
|
|
21
|
+
import { CHANNEL_KEY_PLACEHOLDER } from '../channel-placeholder.js';
|
|
22
|
+
import { assertWorkerContext } from '../run-context.js';
|
|
23
|
+
const STORAGE_KEY = 'channelKey';
|
|
24
|
+
let key = null;
|
|
25
|
+
// Whether this is the extension itself, rather than unit tests building
|
|
26
|
+
// code under Node (with whatever `chrome` stub a test installs).
|
|
27
|
+
function inExtension() {
|
|
28
|
+
return globalThis.location?.protocol === 'chrome-extension:';
|
|
29
|
+
}
|
|
30
|
+
// The key, made and stored the first time, and again for each new
|
|
31
|
+
// extension version: a key that leaked stops working at the next update,
|
|
32
|
+
// which places every registration again anyway. Every spec build awaits
|
|
33
|
+
// this before building. One load at a time, so two first callers can't
|
|
34
|
+
// each make a key.
|
|
35
|
+
let loading = null;
|
|
36
|
+
export function loadChannelKey() {
|
|
37
|
+
if (key)
|
|
38
|
+
return Promise.resolve(key);
|
|
39
|
+
if (!inExtension())
|
|
40
|
+
return Promise.resolve(CHANNEL_KEY_PLACEHOLDER);
|
|
41
|
+
loading ??= readOrMakeKey().finally(() => { loading = null; });
|
|
42
|
+
return loading;
|
|
43
|
+
}
|
|
44
|
+
async function readOrMakeKey() {
|
|
45
|
+
const version = chrome.runtime.getManifest().version;
|
|
46
|
+
const got = (await chrome.storage.local.get(STORAGE_KEY))[STORAGE_KEY];
|
|
47
|
+
if (got && typeof got.key === 'string' && /^[0-9a-f]{32}$/.test(got.key) && got.version === version) {
|
|
48
|
+
key = got.key;
|
|
49
|
+
return key;
|
|
50
|
+
}
|
|
51
|
+
assertWorkerContext('write storage');
|
|
52
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
53
|
+
const made = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
54
|
+
await chrome.storage.local.set({ [STORAGE_KEY]: { key: made, version } });
|
|
55
|
+
key = made;
|
|
56
|
+
return key;
|
|
57
|
+
}
|
|
58
|
+
// The key, once `loadChannelKey` has run. Under Node (unit tests) the
|
|
59
|
+
// placeholder.
|
|
60
|
+
export function channelKey() {
|
|
61
|
+
if (key)
|
|
62
|
+
return key;
|
|
63
|
+
if (!inExtension())
|
|
64
|
+
return CHANNEL_KEY_PLACEHOLDER;
|
|
65
|
+
throw new Error('Internal error: The channel key was used before it was loaded');
|
|
66
|
+
}
|
|
67
|
+
// `code` (a static wrapper file) with the key in place of the stand-in.
|
|
68
|
+
// Throws if the stand-in isn't there: every install would otherwise
|
|
69
|
+
// share the stand-in as its key.
|
|
70
|
+
export function withChannelKey(code) {
|
|
71
|
+
const stand = JSON.stringify(CHANNEL_KEY_PLACEHOLDER);
|
|
72
|
+
if (!code.includes(stand))
|
|
73
|
+
throw new Error('Internal error: A wrapper has no channel key stand-in');
|
|
74
|
+
return code.split(stand).join(JSON.stringify(channelKey()));
|
|
75
|
+
}
|
|
76
|
+
// A JavaScript expression for the shared registry, for generated code.
|
|
77
|
+
// The first of our entries to run in a document defines the gate; the
|
|
78
|
+
// rest call it. The static wrapper files carry the same code (`reg` in
|
|
79
|
+
// each), and `tests/unit/channel.test.mjs` checks they stay in step.
|
|
80
|
+
//
|
|
81
|
+
// - On `document`, not `window`: a navigation from a frame's initial
|
|
82
|
+
// `about:blank` to a same-origin page keeps the Window, so a parent
|
|
83
|
+
// could plant a gate there before our code runs. Each navigation gets
|
|
84
|
+
// a new Document.
|
|
85
|
+
// - One registry per key (a manual run's code has a key of its own; see
|
|
86
|
+
// `documentKey`), in an object with no prototype, read by bracket
|
|
87
|
+
// access: no `Map`, whose `get` the page could replace to see the
|
|
88
|
+
// key. A caller with any other string, the page included, gets an
|
|
89
|
+
// empty registry of its own.
|
|
90
|
+
// - Registries have no prototype either, so reading a field one lacks
|
|
91
|
+
// can't hand it to a getter the page put on `Object.prototype`.
|
|
92
|
+
// - Each carries built-ins as they were when the gate was made, for
|
|
93
|
+
// generated code that runs after the page's: `apply`
|
|
94
|
+
// (Reflect.apply), `resolve` / `reject` (a promise made with the
|
|
95
|
+
// `Promise` constructor of then), `info` (console.info). See
|
|
96
|
+
// `emitGmBindings`.
|
|
97
|
+
export function registryExpr() {
|
|
98
|
+
return '(function(k){var g=document.__smGate;if(typeof g!=="function"){'
|
|
99
|
+
+ 'var m=Object.create(null),c=Object.create,a=Reflect.apply,p=Promise;'
|
|
100
|
+
+ 'var i=console.info.bind(console);'
|
|
101
|
+
+ 'var rs=function(v){return new p(function(y){y(v)})};var rj=function(e){return new p(function(y,n){n(e)})};'
|
|
102
|
+
+ 'g=function(x){if(typeof x!=="string")return void 0;var r=m[x];'
|
|
103
|
+
+ 'if(!r){r=c(null);r.apply=a;r.resolve=rs;r.reject=rj;r.info=i;m[x]=r}return r};'
|
|
104
|
+
+ 'var d=c(null);d.value=g;Object.defineProperty(document,"__smGate",d)}return g(k)})'
|
|
105
|
+
+ `(${JSON.stringify(channelKey())})`;
|
|
106
|
+
}
|
|
107
|
+
// The key a manual run of script `scriptId` gets in document
|
|
108
|
+
// `documentId`, in place of the install's. A manual run's page-world code is injected long after
|
|
109
|
+
// the page's own, so the built-ins it captures may be the page's
|
|
110
|
+
// replacements, and either could hand the page whatever key the code
|
|
111
|
+
// carries. This one opens only that document's bridge (the run injects
|
|
112
|
+
// its own, `onlyScript` limiting it to the run's script), so a page that
|
|
113
|
+
// got it reaches only that script there. Per script, so each script's
|
|
114
|
+
// runs have a bridge of their own (another script's would filter them
|
|
115
|
+
// out). The same in every run of the script in a document, so a run
|
|
116
|
+
// replaces the last one's state. See docs/manual-scripts.md.
|
|
117
|
+
export async function documentKey(documentId, scriptId) {
|
|
118
|
+
const data = new TextEncoder().encode(`${await loadChannelKey()}:${documentId}:${scriptId}`);
|
|
119
|
+
const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', data));
|
|
120
|
+
return Array.from(digest.slice(0, 16), (b) => b.toString(16).padStart(2, '0')).join('');
|
|
121
|
+
}
|
|
122
|
+
// `code` built under the install's key, rekeyed to `to`.
|
|
123
|
+
export function rekey(code, to) {
|
|
124
|
+
return code.split(channelKey()).join(to);
|
|
125
|
+
}
|
|
126
|
+
// The bridge's code (built under the install's key) as a manual run's
|
|
127
|
+
// bridge: relaying only `scriptId`'s traffic.
|
|
128
|
+
export function onlyScript(bridgeCode, scriptId) {
|
|
129
|
+
const stand = JSON.stringify('__SM_ONLY__');
|
|
130
|
+
if (!bridgeCode.includes(stand))
|
|
131
|
+
throw new Error('Internal error: The bridge has no script stand-in');
|
|
132
|
+
return bridgeCode.split(stand).join(JSON.stringify(scriptId));
|
|
133
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Linkification of a Prism-highlighted code block, plus the anchor
|
|
2
|
+
// builder both it and the details panel use.
|
|
3
|
+
//
|
|
4
|
+
// Split out of `userscript.ts` so the same pass runs over installed
|
|
5
|
+
// scripts and over a not-yet-installed preview — the two share one
|
|
6
|
+
// tabbed code view (see `code-tabs.ts`).
|
|
7
|
+
//
|
|
8
|
+
// Pure DOM helpers: everything takes the elements it operates on. No
|
|
9
|
+
// chrome.* calls, no module state.
|
|
10
|
+
import { appendWithSlashBreaks } from '../script-display.js';
|
|
11
|
+
import { resolveScriptResource } from '../file-loader.js';
|
|
12
|
+
import { matchTargetInfo, sameDirLinkUrl } from '../match-targets.js';
|
|
13
|
+
import { MATCH_HEADER_TAGS, RESOLVABLE_HEADER_TAGS, connectLinkUrl, splitResourceValue, splitTextByUrls, } from '../script-links.js';
|
|
14
|
+
export function openInNewTabAnchor(href, text,
|
|
15
|
+
// When true, insert `<wbr>` markers after each `/` so a long URL
|
|
16
|
+
// wraps at slash boundaries — desired in the details panel (Match
|
|
17
|
+
// patterns, Location row). The code box passes false: with
|
|
18
|
+
// `white-space: pre`, Chrome still honors `<wbr>` as a break
|
|
19
|
+
// opportunity, so a URL inside a single source line could wrap
|
|
20
|
+
// mid-line and misalign the line-numbers gutter.
|
|
21
|
+
slashBreaks = true) {
|
|
22
|
+
const a = document.createElement('a');
|
|
23
|
+
a.className = 'code-link';
|
|
24
|
+
a.href = href;
|
|
25
|
+
a.target = '_blank';
|
|
26
|
+
a.rel = 'noopener noreferrer';
|
|
27
|
+
if (slashBreaks) {
|
|
28
|
+
appendWithSlashBreaks(a, text);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
a.textContent = text;
|
|
32
|
+
}
|
|
33
|
+
return a;
|
|
34
|
+
}
|
|
35
|
+
// Post-Prism walk over the highlighted code: replace eligible text
|
|
36
|
+
// runs with anchors. Two passes:
|
|
37
|
+
//
|
|
38
|
+
// 1. Metadata blocks are recognised as `.token.userscript-header`
|
|
39
|
+
// spans by the Prism grammar extension in `userscript.ts`; inside
|
|
40
|
+
// each, the `@tag` is a `.header-tag-name` span and its value is a
|
|
41
|
+
// `.header-tag-value` span. For tags we know how to linkify
|
|
42
|
+
// (@match → wildcard-trimmed link, @icon/@require/@updateURL/…
|
|
43
|
+
// → resolved against the script's fileUrl) the value span's
|
|
44
|
+
// contents become an anchor; non-linkable tags fall through to
|
|
45
|
+
// URL mode on the value text.
|
|
46
|
+
// 2. Every other text node has its absolute http(s) / file:// URLs
|
|
47
|
+
// turned into anchors — covers comment URLs outside the header,
|
|
48
|
+
// URLs inside strings, and `@require` source bodies (no header).
|
|
49
|
+
//
|
|
50
|
+
// Driving the header detection off the Prism token class (rather
|
|
51
|
+
// than line-shape matching) means a single-line string literal like
|
|
52
|
+
// `"// ==UserScript=="` can't trick the linkifier — Prism tokenises
|
|
53
|
+
// it as a string, not as our header block. A multi-line template
|
|
54
|
+
// literal that contains BOTH markers can still be picked up because
|
|
55
|
+
// our pattern is inserted before `string`/template tokens; in
|
|
56
|
+
// practice no userscript embeds a fake metadata block that way, but
|
|
57
|
+
// it's worth knowing.
|
|
58
|
+
export function linkifyCodeBlock(root, fileUrl) {
|
|
59
|
+
for (const headerEl of Array.from(root.querySelectorAll('.userscript-header'))) {
|
|
60
|
+
linkifyHeaderBlock(headerEl, fileUrl);
|
|
61
|
+
}
|
|
62
|
+
// Walk text nodes AFTER the header pass so the tree mutations from
|
|
63
|
+
// linkifyHeaderBlock are reflected. Skip nodes inside marker /
|
|
64
|
+
// tag-name / tag-value spans — they were either handled by
|
|
65
|
+
// linkifyHeaderBlock or are anchor labels we just inserted and
|
|
66
|
+
// don't want to re-process.
|
|
67
|
+
const walker = root.ownerDocument.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
68
|
+
const nodes = [];
|
|
69
|
+
let n;
|
|
70
|
+
while ((n = walker.nextNode()) !== null) {
|
|
71
|
+
const tn = n;
|
|
72
|
+
const parent = tn.parentElement;
|
|
73
|
+
if (parent && parent.closest('.header-marker, .header-tag-name, .header-tag-value')) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
nodes.push(tn);
|
|
77
|
+
}
|
|
78
|
+
for (const tn of nodes)
|
|
79
|
+
linkifyTextNodeUrls(tn);
|
|
80
|
+
}
|
|
81
|
+
// Process one `.userscript-header` span: for each `.header-tag-value`
|
|
82
|
+
// inside it, look back for the matching `.header-tag-name` and turn
|
|
83
|
+
// the value into an anchor (or URL-linkified text for tags we don't
|
|
84
|
+
// know how to resolve).
|
|
85
|
+
function linkifyHeaderBlock(headerEl, fileUrl) {
|
|
86
|
+
const valueEls = headerEl.querySelectorAll('.header-tag-value');
|
|
87
|
+
for (const valueEl of Array.from(valueEls)) {
|
|
88
|
+
const tag = findPrecedingTagName(valueEl);
|
|
89
|
+
if (!tag)
|
|
90
|
+
continue;
|
|
91
|
+
const value = valueEl.textContent ?? '';
|
|
92
|
+
const link = linkForHeaderTag(tag, value, fileUrl);
|
|
93
|
+
if (link) {
|
|
94
|
+
const doc = headerEl.ownerDocument;
|
|
95
|
+
valueEl.replaceChildren(doc.createTextNode(link.prefix), openInNewTabAnchor(link.href, link.linkText, false), doc.createTextNode(link.suffix));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Fall back to URL-only linkification (e.g. an http URL inside
|
|
99
|
+
// a `@description` value). Only touch the leaf text node so we
|
|
100
|
+
// don't disturb any nested tokens (shouldn't exist today, but
|
|
101
|
+
// safe if Prism's grammar evolves).
|
|
102
|
+
const tn = valueEl.firstChild;
|
|
103
|
+
if (tn && tn.nodeType === Node.TEXT_NODE) {
|
|
104
|
+
linkifyTextNodeUrls(tn);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Walk back from a tag-value span past whitespace text nodes to find
|
|
110
|
+
// the immediately-preceding tag-name span; returns the lowercased
|
|
111
|
+
// canonical tag (no `@`, no locale suffix), or null if the structure
|
|
112
|
+
// doesn't match (defensive — Prism should always pair them).
|
|
113
|
+
function findPrecedingTagName(valueEl) {
|
|
114
|
+
let prev = valueEl.previousSibling;
|
|
115
|
+
while (prev) {
|
|
116
|
+
if (prev.nodeType === Node.ELEMENT_NODE) {
|
|
117
|
+
const el = prev;
|
|
118
|
+
if (!el.classList.contains('header-tag-name'))
|
|
119
|
+
return null;
|
|
120
|
+
const text = el.textContent ?? '';
|
|
121
|
+
const m = /^@([A-Za-z][\w-]*)(?::[\w-]+)?$/.exec(text);
|
|
122
|
+
return m ? m[1].toLowerCase() : null;
|
|
123
|
+
}
|
|
124
|
+
prev = prev.previousSibling;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function linkifyTextNodeUrls(tn) {
|
|
129
|
+
const runs = splitTextByUrls(tn.data);
|
|
130
|
+
if (runs.length === 1 && runs[0].kind === 'text')
|
|
131
|
+
return;
|
|
132
|
+
const doc = tn.ownerDocument;
|
|
133
|
+
const replacement = runs.map((r) => r.kind === 'url'
|
|
134
|
+
? openInNewTabAnchor(r.value, r.value, false)
|
|
135
|
+
: doc.createTextNode(r.value));
|
|
136
|
+
tn.replaceWith(...replacement);
|
|
137
|
+
}
|
|
138
|
+
function linkForHeaderTag(tag, value, fileUrl) {
|
|
139
|
+
if (MATCH_HEADER_TAGS.has(tag)) {
|
|
140
|
+
// Site-less patterns (`*://*/…`, `file:///*/…`) fall through to the
|
|
141
|
+
// same-directory guess against the script's own location.
|
|
142
|
+
const linkUrl = matchTargetInfo(value)?.linkUrl ?? sameDirLinkUrl(value, fileUrl);
|
|
143
|
+
if (!linkUrl)
|
|
144
|
+
return null;
|
|
145
|
+
return { prefix: '', linkText: value, href: linkUrl, suffix: '' };
|
|
146
|
+
}
|
|
147
|
+
if (RESOLVABLE_HEADER_TAGS.has(tag)) {
|
|
148
|
+
const resolved = resolveScriptResource(value, fileUrl);
|
|
149
|
+
if (!resolved)
|
|
150
|
+
return null;
|
|
151
|
+
// A `data:` URL is technically navigable but its href would be
|
|
152
|
+
// many KB long inside the anchor, drowning the code box. Inline
|
|
153
|
+
// data URLs are already rendered via the favicon path; in the
|
|
154
|
+
// body we just show the value as plain text.
|
|
155
|
+
if (resolved.kind === 'data')
|
|
156
|
+
return null;
|
|
157
|
+
return { prefix: '', linkText: value, href: resolved.url, suffix: '' };
|
|
158
|
+
}
|
|
159
|
+
if (tag === 'resource') {
|
|
160
|
+
// `@resource <name> <url>`: keep the name + gap as plain text;
|
|
161
|
+
// linkify only the URL portion. The pure splitter in
|
|
162
|
+
// script-links.ts owns the round-trip — see `splitResourceValue`.
|
|
163
|
+
const parts = splitResourceValue(value);
|
|
164
|
+
if (!parts)
|
|
165
|
+
return null;
|
|
166
|
+
const resolved = resolveScriptResource(parts.url, fileUrl);
|
|
167
|
+
if (!resolved || resolved.kind === 'data')
|
|
168
|
+
return null;
|
|
169
|
+
return {
|
|
170
|
+
prefix: parts.prefix,
|
|
171
|
+
linkText: parts.url,
|
|
172
|
+
href: resolved.url,
|
|
173
|
+
suffix: parts.suffix,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (tag === 'connect') {
|
|
177
|
+
// `@connect` values are hosts (`example.com`, `*.example.com`)
|
|
178
|
+
// or full URLs, not match patterns. `connectLinkUrl` picks the
|
|
179
|
+
// click target; `*` and `self` return null and fall through to
|
|
180
|
+
// plain text since neither names a single site.
|
|
181
|
+
const href = connectLinkUrl(value);
|
|
182
|
+
if (!href)
|
|
183
|
+
return null;
|
|
184
|
+
return { prefix: '', linkText: value, href, suffix: '' };
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|