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,126 @@
|
|
|
1
|
+
// Recognizing an HTML page served where JavaScript was expected.
|
|
2
|
+
//
|
|
3
|
+
// The failure this catches is a *soft* one: the request succeeded, the
|
|
4
|
+
// status was 200, and the bytes are a perfectly good web page — just
|
|
5
|
+
// not the script we asked for. Left alone it flows onward and is
|
|
6
|
+
// reported as a syntax error in the user's script, which blames the
|
|
7
|
+
// wrong file. The classic shapes:
|
|
8
|
+
// - a forge's file-browser page (`github.com/<o>/<r>/blob/…`) instead
|
|
9
|
+
// of the raw bytes;
|
|
10
|
+
// - a login / SSO wall or captive portal answering at 200;
|
|
11
|
+
// - a `@require` URL that has rotted into a 200-with-error-page;
|
|
12
|
+
// - a local *directory* whose name matches the ref — Chrome answers
|
|
13
|
+
// `file://` directory reads with a generated HTML listing.
|
|
14
|
+
//
|
|
15
|
+
// Two independent tests, because each catches what the other misses:
|
|
16
|
+
// - `isMarkupContentType` — the server told us. Free, and right even
|
|
17
|
+
// when the body is unreadable.
|
|
18
|
+
// - `looksLikeMarkup` — the bytes tell us. Needed when there is no
|
|
19
|
+
// Content-Type at all (the `file://` XHR leg often has none) or the
|
|
20
|
+
// server mislabels HTML as `text/plain`.
|
|
21
|
+
//
|
|
22
|
+
// Deliberately NOT applied to `@resource` (an HTML resource is a
|
|
23
|
+
// legitimate thing to fetch and hand to `GM_getResourceText`), to
|
|
24
|
+
// `@icon` (already gated on `image/*`), or to `file://` directory
|
|
25
|
+
// listings (which are HTML *by design* — see `parseListing` in
|
|
26
|
+
// directories.ts). The rule is "this text is about to be treated as
|
|
27
|
+
// JavaScript", not "this text arrived over the network".
|
|
28
|
+
//
|
|
29
|
+
// See [docs/requires.md] "Markup gate" for the two tests written up
|
|
30
|
+
// together, plus [docs/install-capture.md] and [docs/diagnostics.md].
|
|
31
|
+
// True for the Content-Type families that mean markup. A denylist, not
|
|
32
|
+
// an allowlist: `application/octet-stream` and friends are common and
|
|
33
|
+
// legitimate for JavaScript (GitHub serves every release asset that
|
|
34
|
+
// way), so only the types that can't be a script are rejected.
|
|
35
|
+
// `text/html-sandboxed` is caught by the prefix and deserves to be;
|
|
36
|
+
// XHTML is spelled `application/xhtml+xml`, so it needs its own clause
|
|
37
|
+
// to match the same intent.
|
|
38
|
+
//
|
|
39
|
+
// `application/json` is deliberately absent: a JSON error envelope at
|
|
40
|
+
// 200 is a real soft-failure shape, but so is a legitimate JS module
|
|
41
|
+
// served by a mislabelling host, and the eval-side try/catch already
|
|
42
|
+
// contains the damage.
|
|
43
|
+
//
|
|
44
|
+
// Trims defensively so the rule holds for a caller that hands over a
|
|
45
|
+
// raw header rather than the lowercased/trimmed one every producer in
|
|
46
|
+
// file-loader.ts passes.
|
|
47
|
+
export function isMarkupContentType(contentType) {
|
|
48
|
+
const type = contentType.trim().toLowerCase();
|
|
49
|
+
return type.startsWith('text/html') || type.startsWith('application/xhtml');
|
|
50
|
+
}
|
|
51
|
+
// True when the text itself is a markup document: the first thing in
|
|
52
|
+
// it, past any BOM and leading whitespace, is `<`.
|
|
53
|
+
//
|
|
54
|
+
// Why the FIRST character and not "does it contain HTML" — a forge's
|
|
55
|
+
// blob page embeds the file's own lines, metadata block and all, so a
|
|
56
|
+
// search anywhere in the body finds `// ==UserScript==` and concludes
|
|
57
|
+
// the page IS the script. Position is the only reliable signal, and it
|
|
58
|
+
// is exactly the signal a served `.js` file gives: JavaScript starts
|
|
59
|
+
// with code or a comment, never with a tag.
|
|
60
|
+
//
|
|
61
|
+
// The check is on the leading character, not on the metadata block's
|
|
62
|
+
// position, because a script may legitimately carry leading text — a
|
|
63
|
+
// license banner above the block is common, is preserved
|
|
64
|
+
// (`leadingText`, userscript-header.ts), and must keep working.
|
|
65
|
+
//
|
|
66
|
+
// Known false positive, accepted: `<!--` at the very top of a file is
|
|
67
|
+
// valid JavaScript (Annex B HTML-like comments), so a script opening
|
|
68
|
+
// with one is called markup. That spelling belongs to 1990s inline
|
|
69
|
+
// `<script>` blocks and does not occur in userscripts.
|
|
70
|
+
export function looksLikeMarkup(text) {
|
|
71
|
+
// A leading BOM needs no special handling: ECMAScript's `WhiteSpace`
|
|
72
|
+
// production includes U+FEFF, so `\s` matches it here exactly as
|
|
73
|
+
// `trim` would. (`userscript-header.ts` strips one explicitly for a
|
|
74
|
+
// different reason — a `/m` regex's `^` won't match past it.)
|
|
75
|
+
//
|
|
76
|
+
// Anchored regex rather than `trimStart().startsWith('<')`: the
|
|
77
|
+
// rejected inputs are whole web pages, and trimming one allocates a
|
|
78
|
+
// second copy of it to look at a single character.
|
|
79
|
+
return /^\s*</.test(text);
|
|
80
|
+
}
|
|
81
|
+
// The one wording for "we got a web page instead of JavaScript",
|
|
82
|
+
// shared by every caller so the same problem never gets two spellings.
|
|
83
|
+
//
|
|
84
|
+
// `localUrl` splits it into two sentences on purpose: blaming the
|
|
85
|
+
// server when the bytes came off local disk would be a flatly wrong
|
|
86
|
+
// diagnosis, and the common way to hit the local case is a directory
|
|
87
|
+
// whose name matches the ref.
|
|
88
|
+
//
|
|
89
|
+
// `contentType` is the *evidence*, not decoration — pass it only when
|
|
90
|
+
// the header is what proved this, and leave it out when the body did.
|
|
91
|
+
// A parenthetical reading `(Content-Type "text/plain")` next to a
|
|
92
|
+
// claim that the response is HTML reads as a contradiction.
|
|
93
|
+
//
|
|
94
|
+
// `expected` names what we wanted instead. Almost always JavaScript,
|
|
95
|
+
// but the same fetcher reads collection manifests, and telling someone
|
|
96
|
+
// their manifest isn't JavaScript names the wrong thing.
|
|
97
|
+
export function markupResponseMessage(opts = {}) {
|
|
98
|
+
const evidence = opts.contentType ? ` (Content-Type "${opts.contentType}")` : '';
|
|
99
|
+
const expected = opts.expected ?? 'JavaScript';
|
|
100
|
+
return opts.localUrl
|
|
101
|
+
? `${LOCAL_MARKUP_PREFIX}${opts.localUrl}${LOCAL_MARKUP_MIDDLE}${expected}${evidence}; a folder with that name would look like this`
|
|
102
|
+
: `${REMOTE_MARKUP_PREFIX}${expected}${evidence}; the link may be dead or behind a login`;
|
|
103
|
+
}
|
|
104
|
+
const REMOTE_MARKUP_PREFIX = 'The server returned an HTML page, not ';
|
|
105
|
+
const LOCAL_MARKUP_PREFIX = 'The local file ';
|
|
106
|
+
const LOCAL_MARKUP_MIDDLE = ' is an HTML page, not ';
|
|
107
|
+
// Did this text come out of `markupResponseMessage`? For the corpus
|
|
108
|
+
// validator, which tags each invalid script with why it failed.
|
|
109
|
+
export function isMarkupResponseMessage(text) {
|
|
110
|
+
return text.startsWith(REMOTE_MARKUP_PREFIX)
|
|
111
|
+
|| (text.startsWith(LOCAL_MARKUP_PREFIX) && text.includes(LOCAL_MARKUP_MIDDLE));
|
|
112
|
+
}
|
|
113
|
+
// Thrown by the script-text fetchers so a caller can tell this apart
|
|
114
|
+
// from every other read failure without matching on wording (which
|
|
115
|
+
// docs/diagnostics.md forbids, and which would go stale the first time
|
|
116
|
+
// the message is reworded).
|
|
117
|
+
//
|
|
118
|
+
// The preview is the caller that cares: for a captured navigation,
|
|
119
|
+
// "this is a web page" is not an error to report but a sign the
|
|
120
|
+
// capture should never have fired, so it continues to the page instead.
|
|
121
|
+
export class MarkupResponseError extends Error {
|
|
122
|
+
constructor(message) {
|
|
123
|
+
super(message);
|
|
124
|
+
this.name = 'MarkupResponseError';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// Pure helpers for converting a Chrome `@match` pattern into a
|
|
2
|
+
// "target" the rest of the app can act on: the pattern's representative
|
|
3
|
+
// origin and a click-through / page URL (the same URL serves both
|
|
4
|
+
// purposes — favicon HTML discovery and open-in-new-tab click).
|
|
5
|
+
//
|
|
6
|
+
// Used by:
|
|
7
|
+
// - site-icon.ts — takes `representativeMatches(matching)` (the
|
|
8
|
+
// first few distinct sites) to drive favicon discovery (origin
|
|
9
|
+
// caching, page URL for HTML probe, click target) — one icon per
|
|
10
|
+
// site.
|
|
11
|
+
// - script-details.ts / code-links.ts — call `matchTargetInfo(pattern)`
|
|
12
|
+
// per-pattern when linkifying `@match`/`@exclude`/`@include` values,
|
|
13
|
+
// falling back to `sameDirLinkUrl` for patterns that name no site.
|
|
14
|
+
//
|
|
15
|
+
// No fetching, no DOM, no chrome.storage — that all lives in the
|
|
16
|
+
// modules that consume these primitives.
|
|
17
|
+
import { canonicalMatchPatterns } from './url-match.js';
|
|
18
|
+
// Pure regex rewrite, no fetching — imported so the same-directory
|
|
19
|
+
// fallback below points a github-hosted script's neighbour at the
|
|
20
|
+
// github.com page instead of its raw bytes.
|
|
21
|
+
import { githubRawToBlobUrl } from './github-urls.js';
|
|
22
|
+
// Pull a Chrome match pattern apart and dewildcard its host. Returns
|
|
23
|
+
// null when the pattern can't identify a single site (`<all_urls>`,
|
|
24
|
+
// host of `*`, non-http(s) schemes, embedded `*` after dewildcard,
|
|
25
|
+
// or syntactically invalid).
|
|
26
|
+
//
|
|
27
|
+
// Port handling: an explicit `:port` in the pattern is preserved
|
|
28
|
+
// verbatim. The favicon almost certainly is served from the same
|
|
29
|
+
// port the page is on (`localhost:3000`, a dev server, etc.), and
|
|
30
|
+
// the cache key needs to include the port to avoid collisions
|
|
31
|
+
// between two different servers sharing a hostname on different
|
|
32
|
+
// ports. Patterns without a port stay portless — the icon resolver
|
|
33
|
+
// will hit the default port, which is the right behavior for the
|
|
34
|
+
// common case.
|
|
35
|
+
function parseMatchPattern(pattern) {
|
|
36
|
+
if (pattern === '<all_urls>')
|
|
37
|
+
return null;
|
|
38
|
+
// Read the pattern the way the matcher and the registration do — a
|
|
39
|
+
// scheme-less `*.example.com/*` names a site, and should get an icon
|
|
40
|
+
// and a click-through like any other. An expansion only varies the
|
|
41
|
+
// scheme, and this picks one representative origin, so prefer a form
|
|
42
|
+
// that isn't `http://`: `*` is read as https below, and a port-pinned
|
|
43
|
+
// `*://host:8443/*` (which canonicalizes to the http + https pair)
|
|
44
|
+
// must keep pointing at the https member — fetching the icon over
|
|
45
|
+
// http would fail on a TLS-only port.
|
|
46
|
+
const canonical = canonicalMatchPatterns(pattern);
|
|
47
|
+
const preferred = canonical.find((p) => !p.startsWith('http://')) ?? canonical[0] ?? pattern;
|
|
48
|
+
const parts = /^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)?$/i.exec(preferred);
|
|
49
|
+
if (!parts)
|
|
50
|
+
return null;
|
|
51
|
+
const [, rawScheme, rawHost] = parts;
|
|
52
|
+
const scheme = rawScheme === '*' ? 'https' : rawScheme.toLowerCase();
|
|
53
|
+
if (scheme !== 'http' && scheme !== 'https')
|
|
54
|
+
return null;
|
|
55
|
+
// Split host:port so the host-wildcard rules apply only to the
|
|
56
|
+
// host portion. A numeric port is preserved verbatim. A `:*` port
|
|
57
|
+
// (any port) covers the default one, so the icon comes from there.
|
|
58
|
+
const colon = rawHost.lastIndexOf(':');
|
|
59
|
+
const hasPort = colon > 0 && /^(\d+|\*)$/.test(rawHost.slice(colon + 1));
|
|
60
|
+
const port = hasPort && rawHost.slice(colon + 1) !== '*' ? rawHost.slice(colon) : '';
|
|
61
|
+
let host = (hasPort ? rawHost.slice(0, colon) : rawHost).toLowerCase();
|
|
62
|
+
if (host === '*' || host === '')
|
|
63
|
+
return null;
|
|
64
|
+
if (host.startsWith('*.'))
|
|
65
|
+
host = host.slice(2);
|
|
66
|
+
if (!host || host.includes('*'))
|
|
67
|
+
return null;
|
|
68
|
+
// `.tld` means "any public suffix" (see url-match.ts); `.com` is
|
|
69
|
+
// the one most such sites answer on, so the icon and link use it.
|
|
70
|
+
if (host.endsWith('.tld'))
|
|
71
|
+
host = host.slice(0, -4) + '.com';
|
|
72
|
+
// The host slot has to hold a host we could actually navigate to.
|
|
73
|
+
// Worth checking since the scheme-less reading above will supply a
|
|
74
|
+
// scheme for any text at all (`not a pattern` → `*://not a pattern`),
|
|
75
|
+
// and this origin becomes a fetch and an href.
|
|
76
|
+
try {
|
|
77
|
+
new URL(`${scheme}://${host}${port}/`);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return { scheme, host: host + port, path: parts[3] ?? '/' };
|
|
83
|
+
}
|
|
84
|
+
export function originFromMatchPattern(pattern) {
|
|
85
|
+
const p = parseMatchPattern(pattern);
|
|
86
|
+
return p ? `${p.scheme}://${p.host}` : null;
|
|
87
|
+
}
|
|
88
|
+
// Iterate `matching.matches` and return the first origin we can
|
|
89
|
+
// extract. Input is `@match`-only by construction (`@include` /
|
|
90
|
+
// `@exclude` live in separate fields on `UrlMatching`); we'd skip
|
|
91
|
+
// them here anyway since they're free-form globs/regexes over the
|
|
92
|
+
// whole URL, not URL templates.
|
|
93
|
+
export function representativeOrigin(matching) {
|
|
94
|
+
return representativeMatch(matching)?.origin ?? null;
|
|
95
|
+
}
|
|
96
|
+
// Like representativeOrigin, but also yields a representative URL —
|
|
97
|
+
// the page to fetch for icon discovery AND the click-through target.
|
|
98
|
+
// Built segment-aware (see `linkPathFromMatchPath`):
|
|
99
|
+
// https://example.com/* → /
|
|
100
|
+
// https://example.com/app/home/* → /app/home/
|
|
101
|
+
// https://example.com/posts/*/edit → /posts/
|
|
102
|
+
// https://example.com/foo/bar* → /foo/bar
|
|
103
|
+
export function representativeMatch(matching) {
|
|
104
|
+
return representativeMatches(matching, 1)[0] ?? null;
|
|
105
|
+
}
|
|
106
|
+
// How many distinct sites a script's icon slot shows by default — the
|
|
107
|
+
// collections-table limit, where the slot sits in a column beside the
|
|
108
|
+
// name. The viewer heading has room for more and passes its own.
|
|
109
|
+
export const MAX_SITE_ICONS = 3;
|
|
110
|
+
// The first `limit` DISTINCT sites named by `matching.matches`, in
|
|
111
|
+
// header order. Two patterns are the same site when their hosts match
|
|
112
|
+
// after dropping a leading `www.`; the scheme is ignored, so
|
|
113
|
+
// `http://www.example.com/*` and `https://example.com/*` yield one
|
|
114
|
+
// entry (the first seen). Ports still distinguish — `localhost:3000`
|
|
115
|
+
// and `localhost:8080` are different servers with different icons.
|
|
116
|
+
export function representativeMatches(matching, limit = MAX_SITE_ICONS) {
|
|
117
|
+
const out = [];
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
for (const m of matching.matches) {
|
|
120
|
+
if (out.length >= limit)
|
|
121
|
+
break;
|
|
122
|
+
const got = matchTargetInfo(m);
|
|
123
|
+
if (!got)
|
|
124
|
+
continue;
|
|
125
|
+
const key = siteKey(got.origin);
|
|
126
|
+
if (seen.has(key))
|
|
127
|
+
continue;
|
|
128
|
+
seen.add(key);
|
|
129
|
+
out.push(got);
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
// Identity of a site for the purpose of "one icon per site": the
|
|
134
|
+
// host[:port] with a leading `www.` removed and the scheme dropped.
|
|
135
|
+
function siteKey(origin) {
|
|
136
|
+
const host = origin.slice(origin.indexOf('://') + 3).toLowerCase();
|
|
137
|
+
return host.startsWith('www.') ? host.slice(4) : host;
|
|
138
|
+
}
|
|
139
|
+
// Per-pattern variant of `representativeMatch`. The linkifier calls
|
|
140
|
+
// this once per `@match`/`@exclude`/`@include` value rather than
|
|
141
|
+
// taking the first usable one.
|
|
142
|
+
export function matchTargetInfo(pattern) {
|
|
143
|
+
const p = parseMatchPattern(pattern);
|
|
144
|
+
if (!p)
|
|
145
|
+
return null;
|
|
146
|
+
const origin = `${p.scheme}://${p.host}`;
|
|
147
|
+
const linkUrl = origin + linkPathFromMatchPath(p.path);
|
|
148
|
+
return { origin, linkUrl };
|
|
149
|
+
}
|
|
150
|
+
// Fallback for patterns that name no site: link to a page sitting in
|
|
151
|
+
// the same directory as the script itself. Full rule, worked examples
|
|
152
|
+
// and rationale in docs/script-viewer.md#same-directory-fallback; in
|
|
153
|
+
// short, a pattern with no host whose literal trailing directories
|
|
154
|
+
// (and any literal text ending a wildcard directory, like `*gm-menu`)
|
|
155
|
+
// match the tail of the script's own directory D links to D + its
|
|
156
|
+
// filename.
|
|
157
|
+
//
|
|
158
|
+
// It exists for the site-less patterns a script uses to name the page
|
|
159
|
+
// it ships beside, such as:
|
|
160
|
+
//
|
|
161
|
+
// @match *://*/gm-menu/gm-menu.html
|
|
162
|
+
// @match *://*/*gm-menu/gm-menu.html
|
|
163
|
+
// @match file:///*/gm-menu/gm-menu.html
|
|
164
|
+
//
|
|
165
|
+
// None yields a target above (the `*://` ones have a host of `*`, the
|
|
166
|
+
// other a `file:` scheme), so their rows rendered as plain text even
|
|
167
|
+
// though the page is sitting next to the script we're viewing.
|
|
168
|
+
//
|
|
169
|
+
// Two properties worth keeping in mind when editing:
|
|
170
|
+
// - The result is D plus one literal filename, so no pattern can
|
|
171
|
+
// walk out of the script's own directory.
|
|
172
|
+
// - Existence is never checked, so a script linkifies identically
|
|
173
|
+
// whether it was loaded from disk or from a server.
|
|
174
|
+
export function sameDirLinkUrl(pattern, scriptUrl) {
|
|
175
|
+
if (!scriptUrl)
|
|
176
|
+
return null;
|
|
177
|
+
// Only the schemes a site-less pattern is written with. Anchoring
|
|
178
|
+
// the scheme (rather than accepting any `x://`) keeps
|
|
179
|
+
// `/^https?:\/\/x/`-style regex literals — legal in `@include` /
|
|
180
|
+
// `@exclude` — out of here, and `https?\*?` admits the
|
|
181
|
+
// `http*://` shorthand those tags are often written with.
|
|
182
|
+
const parts = /^(?:\*|https?\*?|file):\/\/([^/]*)(\/.*)$/i.exec(pattern.trim());
|
|
183
|
+
if (!parts)
|
|
184
|
+
return null;
|
|
185
|
+
const [, host, path] = parts;
|
|
186
|
+
if (host !== '' && host !== '*')
|
|
187
|
+
return null;
|
|
188
|
+
const segments = path.split('/').slice(1);
|
|
189
|
+
const filename = segments.pop() ?? '';
|
|
190
|
+
// A query or fragment means the page is parametrised, not a plain
|
|
191
|
+
// file next door — and `?` is legal in a `@match` path glob
|
|
192
|
+
// (`ol.php?type=precip*`), so it has to be rejected explicitly
|
|
193
|
+
// rather than left to the extension check.
|
|
194
|
+
if (!filename || /[*?#]/.test(filename) || !/\.[A-Za-z0-9]+$/.test(filename))
|
|
195
|
+
return null;
|
|
196
|
+
// Everything at or before the last wildcard segment is absorbed by
|
|
197
|
+
// that wildcard; what follows has to be matched literally.
|
|
198
|
+
// (A hand-rolled findLastIndex — the project's lib target predates it.)
|
|
199
|
+
let lastWildcard = -1;
|
|
200
|
+
segments.forEach((seg, i) => {
|
|
201
|
+
if (seg.includes('*'))
|
|
202
|
+
lastWildcard = i;
|
|
203
|
+
});
|
|
204
|
+
const literalDirs = segments.slice(lastWildcard + 1);
|
|
205
|
+
// The text after that segment's last `*` (`gm-menu` in `*gm-menu`)
|
|
206
|
+
// still has to end the directory the wildcard stands in for.
|
|
207
|
+
const wildSuffix = lastWildcard < 0
|
|
208
|
+
? ''
|
|
209
|
+
: segments[lastWildcard].slice(segments[lastWildcard].lastIndexOf('*') + 1);
|
|
210
|
+
if (literalDirs.length === 0 && !wildSuffix)
|
|
211
|
+
return null;
|
|
212
|
+
if (literalDirs.some((s) => s === ''))
|
|
213
|
+
return null;
|
|
214
|
+
const dir = scriptDirUrl(scriptUrl);
|
|
215
|
+
if (!dir)
|
|
216
|
+
return null;
|
|
217
|
+
const dirSegments = dir.pathname.split('/').filter(Boolean).map(decodeSegment);
|
|
218
|
+
const needed = literalDirs.length + (wildSuffix ? 1 : 0);
|
|
219
|
+
if (dirSegments.length < needed)
|
|
220
|
+
return null;
|
|
221
|
+
const tail = dirSegments.slice(dirSegments.length - literalDirs.length);
|
|
222
|
+
if (tail.some((seg, i) => seg !== decodeSegment(literalDirs[i])))
|
|
223
|
+
return null;
|
|
224
|
+
if (wildSuffix
|
|
225
|
+
&& !dirSegments[dirSegments.length - needed].endsWith(decodeSegment(wildSuffix))) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
// Resolved through the URL parser rather than concatenated, so a
|
|
229
|
+
// filename carrying a space or other raw character is escaped the
|
|
230
|
+
// same way the directory around it already is. Safe against
|
|
231
|
+
// traversal: the checks above leave a filename with no `/`.
|
|
232
|
+
let href;
|
|
233
|
+
try {
|
|
234
|
+
href = new URL(filename, dir).href;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
// Same reasoning as `collectionClickUrl`: a github collection fetches
|
|
240
|
+
// `raw.githubusercontent.com`, which serves HTML as `text/plain`, so
|
|
241
|
+
// the raw neighbour of a script is a plaintext dump. Humans clicking
|
|
242
|
+
// want the github.com page — the same URL the Location row shows.
|
|
243
|
+
return githubRawToBlobUrl(href) ?? href;
|
|
244
|
+
}
|
|
245
|
+
// The script's parent directory as a URL ending in `/`. Only the
|
|
246
|
+
// schemes a script can be loaded from — the href becomes an anchor.
|
|
247
|
+
function scriptDirUrl(scriptUrl) {
|
|
248
|
+
try {
|
|
249
|
+
const dir = new URL('.', scriptUrl);
|
|
250
|
+
if (!['http:', 'https:', 'file:'].includes(dir.protocol))
|
|
251
|
+
return null;
|
|
252
|
+
return dir;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// A directory read off a URL is percent-encoded; the same directory
|
|
259
|
+
// written into a `@match` pattern usually isn't. Compare both decoded
|
|
260
|
+
// so `my dir` and `my%20dir` are the same segment.
|
|
261
|
+
function decodeSegment(seg) {
|
|
262
|
+
try {
|
|
263
|
+
return decodeURIComponent(seg);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return seg;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Build the click-through path for a `@match` pattern. Walk segments
|
|
270
|
+
// left-to-right and stop at the first one containing a `*`:
|
|
271
|
+
//
|
|
272
|
+
// - Last segment, `*` at the end with a non-empty literal prefix
|
|
273
|
+
// (`foo*`, `ol.php?type=precip*`): keep the literal prefix
|
|
274
|
+
// verbatim. The user is targeting one specific page parametrised
|
|
275
|
+
// on the tail, so the prefix typically is the real URL minus a
|
|
276
|
+
// fragment and navigating to it works.
|
|
277
|
+
// - Otherwise (whole-segment wildcard, mid-segment wildcard, or
|
|
278
|
+
// wildcard segment followed by more path): trim back to the last
|
|
279
|
+
// clean directory boundary — a partial prefix in the middle of a
|
|
280
|
+
// path isn't likely to navigate, but the parent directory often
|
|
281
|
+
// does.
|
|
282
|
+
//
|
|
283
|
+
// Pure-literal paths (no `*` anywhere) pass through unchanged.
|
|
284
|
+
export function linkPathFromMatchPath(path) {
|
|
285
|
+
const segments = path.split('/');
|
|
286
|
+
const kept = [];
|
|
287
|
+
for (let i = 1; i < segments.length; i++) {
|
|
288
|
+
const seg = segments[i];
|
|
289
|
+
const star = seg.indexOf('*');
|
|
290
|
+
if (star < 0) {
|
|
291
|
+
kept.push(seg);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const isLastSegment = i === segments.length - 1;
|
|
295
|
+
const starAtSegmentEnd = star === seg.length - 1;
|
|
296
|
+
if (isLastSegment && starAtSegmentEnd && star > 0) {
|
|
297
|
+
kept.push(seg.slice(0, star));
|
|
298
|
+
return '/' + kept.join('/');
|
|
299
|
+
}
|
|
300
|
+
// Trailing slash when we kept at least one full segment; bare `/`
|
|
301
|
+
// when the wildcard segment was the first one.
|
|
302
|
+
return '/' + (kept.length ? kept.join('/') + '/' : '');
|
|
303
|
+
}
|
|
304
|
+
return '/' + kept.join('/');
|
|
305
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Which of a script's menu commands show, shared by the worker's
|
|
2
|
+
// registry (`gm-menu.ts`) and sm-dev's `menu` listing.
|
|
3
|
+
//
|
|
4
|
+
// As in TM: a script's commands with one caption show once in a tab,
|
|
5
|
+
// however many times it registered that caption (each with its own id)
|
|
6
|
+
// and in however many of the tab's frames. Picking the entry runs all of
|
|
7
|
+
// them (`menuCommandGroup` in `gm-menu.ts`). Every registration is kept,
|
|
8
|
+
// so unregistering one leaves the entry for the rest. The entry shows
|
|
9
|
+
// the caption's first registration, so the menu doesn't reorder.
|
|
10
|
+
//
|
|
11
|
+
// `key` says which commands share an entry: the caller's list may hold
|
|
12
|
+
// several scripts' commands, which never merge.
|
|
13
|
+
export function visibleCommands(list, key) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
return list.filter((command) => {
|
|
16
|
+
const k = key(command);
|
|
17
|
+
if (seen.has(k))
|
|
18
|
+
return false;
|
|
19
|
+
seen.add(k);
|
|
20
|
+
return true;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>SourceMonkey offscreen host</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<!--
|
|
9
|
+
Offscreen document: the extension's only way to get a DOM without a
|
|
10
|
+
visible tab. It exists solely to embed the sandboxed parse page and
|
|
11
|
+
relay messages to it, because the two halves each lack something the
|
|
12
|
+
other has:
|
|
13
|
+
|
|
14
|
+
- The service worker can call `chrome.*` but has no DOM, so it
|
|
15
|
+
cannot host an iframe — and its CSP forbids `eval`.
|
|
16
|
+
- The sandboxed page may use `eval` (so it can compile) but has an
|
|
17
|
+
opaque origin and no `chrome.*`, so the service worker cannot
|
|
18
|
+
message it directly.
|
|
19
|
+
|
|
20
|
+
This document has both a DOM and `chrome.runtime`, so it bridges
|
|
21
|
+
them: `chrome.runtime.onMessage` in, `postMessage` to the iframe,
|
|
22
|
+
and back.
|
|
23
|
+
|
|
24
|
+
The iframe is `src`-loaded rather than `srcdoc` so it resolves to the
|
|
25
|
+
URL declared in the manifest's `sandbox.pages` list — a srcdoc frame
|
|
26
|
+
would inherit this document's CSP and lose the `'unsafe-eval'` that
|
|
27
|
+
is the entire point.
|
|
28
|
+
-->
|
|
29
|
+
<!--
|
|
30
|
+
The `sandbox` attribute is belt-and-braces. The page is already
|
|
31
|
+
sandboxed by the manifest's `sandbox.pages` key; if that key were
|
|
32
|
+
ever dropped in an edit, this attribute keeps the frame from
|
|
33
|
+
silently becoming a fully privileged extension page with `chrome.*`
|
|
34
|
+
available to whatever it compiles.
|
|
35
|
+
-->
|
|
36
|
+
<iframe id="parse-sandbox" src="parse-sandbox.html" sandbox="allow-scripts"></iframe>
|
|
37
|
+
<script type="module" src="extension/offscreen.js"></script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|