@mefistofelix/cdp.js 0.7.0 → 0.8.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/README.md +128 -26
- package/cdp.js +32 -8
- package/cdp_ext/cdp_ext.js +37 -3
- package/cdp_ext/manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,6 +246,7 @@ local configuration.
|
|
|
246
246
|
| `websocket_url` | string, unset | Attach directly to a browser WebSocket endpoint. |
|
|
247
247
|
| `cdp_ext` | boolean, `false` | Use the extension transport under Deno/Bun: start the shared reverse listener and open a local bootstrap URL containing the logical browser name and WebSocket endpoint. No extension ID is required. |
|
|
248
248
|
| `cdp_ext_install` | boolean, presence of an effective user-data directory | Prepare an extension copy and startup loading arguments. Defaults on for an explicit custom directory, off for the system profile. `false` uses an already installed extension. Only applies with `cdp_ext: true`. |
|
|
249
|
+
| `cdp_ext_user_scripts` | boolean, `cdp_ext_install` plus an effective custom directory | Automatically enable Allow User Scripts for the managed bridge. False preserves the current grant. True requires installation and a custom directory; system/passive browsers use manual consent. See activation below. |
|
|
249
250
|
| `reverse` | boolean, `false` | Attach to an externally launched extension instead of launching or discovering a browser. Starts the shared listener on first use under Deno or Bun. |
|
|
250
251
|
| `extension_id` | string, browser name for passive `reverse: true` attachment | Optional legacy incoming extension identity. Not needed with `cdp_ext: true`; observed automatically from the connection. |
|
|
251
252
|
| `http_url` | string, unset | Discover the endpoint from an HTTP(S) base URL or exact `/json/version` URL. |
|
|
@@ -924,7 +925,8 @@ every past attachment or identify the caller by itself.
|
|
|
924
925
|
For native script-text injection without changing the page's CSP,
|
|
925
926
|
`chrome.userScripts.execute` (Chrome 135+) accepts `js[].code`. Its default
|
|
926
927
|
`USER_SCRIPT` world shares the DOM but has separate JavaScript globals and is exempt
|
|
927
|
-
from the page's CSP. `MAIN`
|
|
928
|
+
from the page's CSP. `MAIN` shares the page's globals; source injection and later
|
|
929
|
+
calls to eval/new Function have different CSP behavior, explained below.
|
|
928
930
|
|
|
929
931
|
```js
|
|
930
932
|
const frames = await client.call({
|
|
@@ -944,18 +946,102 @@ const frames = await client.call({
|
|
|
944
946
|
console.log(frames[0].result) // 42
|
|
945
947
|
```
|
|
946
948
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
949
|
+
### Arbitrary client code in MAIN, without debugger
|
|
950
|
+
|
|
951
|
+
`chrome.userScripts.execute` also accepts `world: 'MAIN'`. With user-script access
|
|
952
|
+
enabled, Chrome compiles the supplied `js[].code` directly in the page's JavaScript
|
|
953
|
+
world. The source can arrive from the JSON-RPC client at runtime. It can read/write
|
|
954
|
+
page globals and the DOM, including on a page with `script-src 'none'`, without
|
|
955
|
+
debugger attachment or changing the page's CSP:
|
|
956
|
+
|
|
957
|
+
```js
|
|
958
|
+
const frames = await client.call({
|
|
959
|
+
method: 'chrome.userScripts.execute',
|
|
960
|
+
params: {
|
|
961
|
+
browser: 'work',
|
|
962
|
+
args: [{
|
|
963
|
+
target: { tabId },
|
|
964
|
+
world: 'MAIN',
|
|
965
|
+
js: [{
|
|
966
|
+
code: 'window.clientValue = 42; document.querySelector("h1").dataset.source = "client"; Promise.resolve(window.clientValue)',
|
|
967
|
+
}],
|
|
968
|
+
}],
|
|
969
|
+
},
|
|
970
|
+
})
|
|
971
|
+
console.log(frames[0].result) // 42
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
This is native source injection, not a call to `eval`. If the injected source
|
|
975
|
+
itself calls `eval` or `new Function` in MAIN, those operations still obey the
|
|
976
|
+
page's CSP and can remain blocked. The Allow User Scripts toggle enables this
|
|
977
|
+
API; it does not globally enable string evaluation in MAIN, the worker, or normal
|
|
978
|
+
`chrome.scripting.executeScript` calls. No additional RPC method is needed.
|
|
979
|
+
|
|
980
|
+
In `USER_SCRIPT`, the DOM is shared but page JavaScript globals are separate.
|
|
981
|
+
That world's own CSP can permit eval/new Function through
|
|
982
|
+
`chrome.userScripts.configureWorld`, with a CSP such as
|
|
983
|
+
`script-src 'self' 'unsafe-eval'; object-src 'self'`. Configure the world before its
|
|
984
|
+
first injection into a document, or configure a fresh `worldId` and use that ID
|
|
985
|
+
when injecting. Changing the configuration after a world already exists did not
|
|
986
|
+
relax that existing world's CSP in our Chrome test. `configureWorld` does not
|
|
987
|
+
configure MAIN's CSP. Plain `js[].code` injection needs neither eval nor this
|
|
988
|
+
additional world configuration.
|
|
989
|
+
|
|
990
|
+
These behaviors were verified on real Chrome with a restrictive page CSP:
|
|
991
|
+
MAIN source execution returned a Promise result, shared page globals and changed
|
|
992
|
+
the DOM while the tab remained debugger-detached; eval and new Function inside
|
|
993
|
+
that MAIN source remained blocked. The native API and result arrays already pass
|
|
994
|
+
through the bridge unchanged. See [Chrome userScripts](https://developer.chrome.com/docs/extensions/reference/api/userScripts).
|
|
995
|
+
The migration discussion in [Tampermonkey #644](https://github.com/Tampermonkey/tampermonkey/issues/644#issuecomment-2094925643)
|
|
996
|
+
and its [Content Script API documentation](https://www.tampermonkey.net/documentation.php?locale=en&q=content_script_api)
|
|
997
|
+
describe the corresponding native user-script injection mechanism.
|
|
998
|
+
|
|
999
|
+
### User-script consent: system and managed profiles
|
|
1000
|
+
|
|
1001
|
+
The shipped manifest already declares `userScripts`, but a manifest permission
|
|
1002
|
+
alone cannot turn on Chrome's user consent. Before Chrome 138 the gate is Developer
|
|
1003
|
+
mode; from Chrome 138 it is **Allow User Scripts** for the individual extension.
|
|
1004
|
+
|
|
1005
|
+
For a **system profile**, passive `reverse: true`, or `cdp_ext_install: false`,
|
|
1006
|
+
enable it manually in `chrome://extensions/?id=<extension_id>` using the observed
|
|
1007
|
+
`client.browsers.work.extension_id`. This is a per-extension/profile choice, not
|
|
1008
|
+
something to repeat at every connection. Reload the extension if the API is still
|
|
1009
|
+
unavailable after enabling it. Use the shipped `cdp_ext/` folder for manual installs.
|
|
1010
|
+
|
|
1011
|
+
For **managed custom profiles**, `cdp_ext_user_scripts` defaults to true when
|
|
1012
|
+
`cdp_ext: true`, `cdp_ext_install: true` and an effective custom user-data directory
|
|
1013
|
+
are all present. Effective raw directory overrides count, including null/false
|
|
1014
|
+
overrides that remove the directory. Before announcing a new bootstrap binding,
|
|
1015
|
+
the bridge checks access and, if needed, uses Chrome's own extensions-settings UI
|
|
1016
|
+
API to enable it for its own extension. It briefly opens an inactive internal
|
|
1017
|
+
settings tab, runs a fixed native scripting function, and closes that tab. This
|
|
1018
|
+
uses no debugger and no remote-debugging flags.
|
|
1019
|
+
|
|
1020
|
+
This developer setup needs generated `extensions-on-chrome-urls: true` and
|
|
1021
|
+
`DisableExtensionsOnChromeUrlsSwitch` in `disable-features`. The manager adds
|
|
1022
|
+
`chrome://extensions/*` only to the **managed copy** of the manifest. The shipped
|
|
1023
|
+
manifest remains suitable for a normal manual installation. The worker and sidebar
|
|
1024
|
+
files are copied unchanged. Chrome writes its own protected permission settings;
|
|
1025
|
+
the library does not edit `Secure Preferences` or compute preference signatures.
|
|
1026
|
+
Legacy Chrome uses the internal Developer mode setting instead of the newer
|
|
1027
|
+
per-extension setting; the automatic path is tested on Chrome 154.
|
|
1028
|
+
|
|
1029
|
+
Set `cdp_ext_user_scripts: false` to skip the permission step, extra host access
|
|
1030
|
+
and development flags. Existing grants are preserved. Explicit true without both
|
|
1031
|
+
installation and a custom directory rejects before launching; it never infers or
|
|
1032
|
+
modifies the system profile. `update_preferences: false` controls JSON file writes
|
|
1033
|
+
independently and does not turn off this native permission setup. Raw CLI overrides
|
|
1034
|
+
still win; disabling a required switch can make setup fail. Such failures reach
|
|
1035
|
+
the caller through the initial reverse announcement and appear in the local
|
|
1036
|
+
trigger history, rather than being reported as a successful connection.
|
|
1037
|
+
An already running Chrome cannot adopt new command-line capabilities or a changed
|
|
1038
|
+
loaded manifest; restart that custom-profile browser after enabling this option.
|
|
1039
|
+
|
|
1040
|
+
The internal settings API and developer switches are Chrome implementation
|
|
1041
|
+
details; unsupported browsers/policies produce errors. Simply writing
|
|
1042
|
+
`extensions.settings.<extension_id>.user_scripts_enabled` into `Preferences` did
|
|
1043
|
+
not enable access in our Windows test because Chrome protects these settings.
|
|
1044
|
+
See the native [activation steps](https://developer.chrome.com/docs/extensions/reference/api/userScripts).
|
|
959
1045
|
|
|
960
1046
|
Chrome's native `scripting.executeScript` still rejects strings supplied directly
|
|
961
1047
|
as `func`; the bridge's fixed function makes this adaptation possible. Overriding
|
|
@@ -1307,6 +1393,10 @@ See [Chrome's debugging-port restrictions](https://developer.chrome.com/blog/rem
|
|
|
1307
1393
|
| `login: true` plus `args['disable-sync']: true` | Allow profile sign-in through generated preferences while still requesting sync to be disabled. |
|
|
1308
1394
|
| Any high-level settings plus a raw `disable-features` array | Replace the generated list, including translation/sign-in additions; an active `disable-extensions-except` still appends its required feature ID. |
|
|
1309
1395
|
| `user_data_dir` plus `args['user-data-dir']` | The raw argument determines both the profile write location and the launched browser directory. |
|
|
1396
|
+
| Managed extension plus effective custom directory | Default `cdp_ext_user_scripts` to true; grant through Chrome's own UI API before accepting the reverse binding. |
|
|
1397
|
+
| `cdp_ext_user_scripts: false` plus an existing grant | Preserve the grant, skip the permission step and omit its extra generated flags/manifest host. |
|
|
1398
|
+
| `cdp_ext_user_scripts: true` without custom directory or installation | Reject before launch; the system profile remains a manual choice. |
|
|
1399
|
+
| `update_preferences: false` plus automatic user-script consent | Skip JSON preference-file writes; still let Chrome apply its native per-extension grant. |
|
|
1310
1400
|
| `port` plus `args['remote-debugging-port']` | Attach using `port`; no launch occurs, so the raw launch argument is unused. |
|
|
1311
1401
|
| Constructor options plus an existing browser label | Reuse the stored record; configuration is not merged again on every call. |
|
|
1312
1402
|
| `initialize: false` plus `runtime: true` or a binding name | Skip optional target setup, including domain enabling and binding installation. |
|
|
@@ -1658,7 +1748,13 @@ XPath snapshot length, even when `items` is limited or empty.
|
|
|
1658
1748
|
not a complete parser; arbitrary nested or comma-containing arguments are outside
|
|
1659
1749
|
that rewriting logic. Ordinary XPath expressions pass through unchanged.
|
|
1660
1750
|
|
|
1661
|
-
`
|
|
1751
|
+
`_.find` and `_.click` already contain fixed browser-side functions. In API mode
|
|
1752
|
+
only the XPath string and data options are sent to them; neither needs user-script
|
|
1753
|
+
consent, eval or new Function. This is the appropriate path for those two built-ins.
|
|
1754
|
+
|
|
1755
|
+
`client.evaluate_xpath(params, fn, options)` is a separate, general-purpose callback
|
|
1756
|
+
helper: its `fn` parameter is executable code supplied by its caller, not just an
|
|
1757
|
+
XPath string. It is available for other helpers:
|
|
1662
1758
|
|
|
1663
1759
|
```js
|
|
1664
1760
|
const text = await client.evaluate_xpath({
|
|
@@ -1679,14 +1775,17 @@ console.log(text.result.value)
|
|
|
1679
1775
|
the target as `fn(normalizedXPath, options)`; it cannot capture Node.js variables
|
|
1680
1776
|
or imported helpers. `options` must be JSON-serializable; omission becomes null
|
|
1681
1777
|
in the argument array. Direct/debugger results retain native `exceptionDetails`
|
|
1682
|
-
if page code throws. In reverse API mode this arbitrary-function helper
|
|
1683
|
-
|
|
1684
|
-
`{ result: { type, value } }`.
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1778
|
+
if page code throws. In reverse API mode this arbitrary-function helper compiles
|
|
1779
|
+
the serialized invocation directly through `chrome.userScripts.execute` in the
|
|
1780
|
+
**MAIN** world and returns `{ result: { type, value } }`. It requires user-script
|
|
1781
|
+
access, automatically prepared for managed profiles as described above. The
|
|
1782
|
+
wrapper awaits the callback and preserves undefined values; callback exceptions
|
|
1783
|
+
reject with their message. It does not use eval/new Function, change CSP, or fall
|
|
1784
|
+
back to the debugger. A callback that itself invokes eval still obeys page CSP.
|
|
1785
|
+
Choose `execution_mode: 'debugger'` explicitly when native CDP evaluation is
|
|
1786
|
+
required. Native `chrome.scripting.executeScript` calls keep their existing string
|
|
1787
|
+
adapter, native world default and native result array; the fixed XPath built-ins
|
|
1788
|
+
do not use that string adapter either.
|
|
1690
1789
|
|
|
1691
1790
|
### Register, replace or remove handlers
|
|
1692
1791
|
|
|
@@ -1987,10 +2086,13 @@ cover native string rejection, ignored custom string conversions, the bridge's
|
|
|
1987
2086
|
string-function adapter and async arguments/results. Real page CSP checks cover
|
|
1988
2087
|
both evaluators in both worlds, explicit debugger bypass after reload, and explicit
|
|
1989
2088
|
header removal through session rules. User-script checks cover default denied
|
|
1990
|
-
access
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2089
|
+
access in external/opt-out profiles, automatic consent in managed custom profiles,
|
|
2090
|
+
MAIN globals, DOM changes and Promise results under restrictive page CSP, and
|
|
2091
|
+
continued CSP restrictions on eval/new Function. Generic XPath callbacks run through
|
|
2092
|
+
native userScripts while fixed XPath commands work even with userScripts denied.
|
|
2093
|
+
Only disposable incognito-grant preparation uses direct CDP to operate Chrome's
|
|
2094
|
+
Allow in incognito toggle; reverse execution and automatic user-script consent
|
|
2095
|
+
use no remote-debugging flags or debugger attachment.
|
|
1994
2096
|
Managed-launch coverage checks concurrent first use, profile-specific identity,
|
|
1995
2097
|
connection timeout without process replacement, lazy listener startup/shutdown
|
|
1996
2098
|
independent of direct browsers, and relaunch after confirmed exit. External reverse
|
package/cdp.js
CHANGED
|
@@ -188,6 +188,10 @@ export class browser {
|
|
|
188
188
|
}
|
|
189
189
|
if (options.cdp_ext) {
|
|
190
190
|
args['remote-debugging-port'] = false
|
|
191
|
+
if (options.cdp_ext_user_scripts) {
|
|
192
|
+
args['extensions-on-chrome-urls'] = true
|
|
193
|
+
args['disable-features'].push('DisableExtensionsOnChromeUrlsSwitch')
|
|
194
|
+
}
|
|
191
195
|
delete args['disable-extensions']
|
|
192
196
|
delete args['disable-component-extensions-with-background-pages']
|
|
193
197
|
if (options.extension_path) args['disable-extensions-except'] = [
|
|
@@ -383,7 +387,7 @@ export class cdp extends EventTarget {
|
|
|
383
387
|
const error = new Error(message.error)
|
|
384
388
|
if (record) record.reverse_error = error
|
|
385
389
|
util.emit(this, 'reverse_' + encodeURIComponent(id), error)
|
|
386
|
-
return socket.close(1008, '
|
|
390
|
+
return socket.close(1008, 'Reverse browser initialization failed')
|
|
387
391
|
}
|
|
388
392
|
if (record) record.extension_id = extension_id
|
|
389
393
|
this.reverse_sockets[id] = socket
|
|
@@ -489,18 +493,25 @@ export class cdp extends EventTarget {
|
|
|
489
493
|
sessionId: entry.sessionId,
|
|
490
494
|
})
|
|
491
495
|
const results = await record.socket.req({
|
|
492
|
-
method: 'chrome.
|
|
496
|
+
method: 'chrome.userScripts.execute',
|
|
493
497
|
params: {
|
|
494
498
|
args: [{
|
|
495
499
|
target: { tabId: Number(entry.targetId) },
|
|
496
500
|
world: 'MAIN',
|
|
497
|
-
|
|
498
|
-
|
|
501
|
+
js: [{
|
|
502
|
+
code: `(async () => {
|
|
503
|
+
try {
|
|
504
|
+
return { value: await (${fn})(...${JSON.stringify([xpath, options])}) }
|
|
505
|
+
} catch (error) {
|
|
506
|
+
return { error: String(error) }
|
|
507
|
+
}
|
|
508
|
+
})()`,
|
|
509
|
+
}],
|
|
499
510
|
}],
|
|
500
511
|
},
|
|
501
512
|
})
|
|
502
|
-
const result = results[0]
|
|
503
|
-
if (!result) throw new Error(
|
|
513
|
+
const result = results[0].result
|
|
514
|
+
if (!result || Object.hasOwn(result, 'error')) throw new Error(result?.error ?? results[0].error ?? 'XPath callback did not execute')
|
|
504
515
|
return {
|
|
505
516
|
result: {
|
|
506
517
|
type: typeof result.value,
|
|
@@ -648,6 +659,14 @@ export class cdp extends EventTarget {
|
|
|
648
659
|
}
|
|
649
660
|
|
|
650
661
|
async _connect(record) {
|
|
662
|
+
const args = browser.build_args(record)
|
|
663
|
+
if (record.cdp_ext) {
|
|
664
|
+
record.cdp_ext_install ??= !!args['user-data-dir']
|
|
665
|
+
record.cdp_ext_user_scripts ??= record.cdp_ext_install && !!args['user-data-dir']
|
|
666
|
+
if (record.cdp_ext_user_scripts && (!record.cdp_ext_install || !args['user-data-dir'])) {
|
|
667
|
+
throw new Error('Automatic user-script access requires cdp_ext_install and a custom user-data-dir')
|
|
668
|
+
}
|
|
669
|
+
}
|
|
651
670
|
if (record.reverse || record.cdp_ext) {
|
|
652
671
|
await this.server_closing
|
|
653
672
|
if (!this.server) this.listen_reverse()
|
|
@@ -663,6 +682,7 @@ export class cdp extends EventTarget {
|
|
|
663
682
|
bootstrap.searchParams.set('browser', record.name)
|
|
664
683
|
bootstrap.searchParams.set('websocket_url', record.reverse_url)
|
|
665
684
|
bootstrap.searchParams.set('token', record.reverse_token)
|
|
685
|
+
if (record.cdp_ext_user_scripts) bootstrap.searchParams.set('user_scripts', '1')
|
|
666
686
|
record.bootstrap_url = bootstrap.href
|
|
667
687
|
}
|
|
668
688
|
delete record.reverse_error
|
|
@@ -676,8 +696,6 @@ export class cdp extends EventTarget {
|
|
|
676
696
|
url = (await response.json()).webSocketDebuggerUrl
|
|
677
697
|
}
|
|
678
698
|
if (record.cdp_ext || (!record.reverse && !url)) {
|
|
679
|
-
const args = browser.build_args(record)
|
|
680
|
-
if (record.cdp_ext) record.cdp_ext_install ??= !!args['user-data-dir']
|
|
681
699
|
if ((record.proc?.exitCode === null && record.proc.signalCode === null) ||
|
|
682
700
|
(record.opener?.exitCode === null && record.opener.signalCode === null)) url = record.socket?.url
|
|
683
701
|
else {
|
|
@@ -693,6 +711,12 @@ export class cdp extends EventTarget {
|
|
|
693
711
|
for (const filename of ['manifest.json', 'cdp_ext.js', 'sidebar.html', 'sidebar.js']) {
|
|
694
712
|
await fs.copyFile(new URL('./cdp_ext/' + filename, import.meta.url), path.join(record.extension_path, filename))
|
|
695
713
|
}
|
|
714
|
+
if (record.cdp_ext_user_scripts) {
|
|
715
|
+
const filename = path.join(record.extension_path, 'manifest.json')
|
|
716
|
+
const manifest = JSON.parse(await fs.readFile(filename, 'utf8'))
|
|
717
|
+
manifest.host_permissions.push('chrome://extensions/*')
|
|
718
|
+
await fs.writeFile(filename, JSON.stringify(manifest, null, 2) + '\n')
|
|
719
|
+
}
|
|
696
720
|
}
|
|
697
721
|
if (args['user-data-dir'] && record.update_preferences !== false) {
|
|
698
722
|
await browser.update_profile_preferences(path.join(args['user-data-dir'], args['profile-directory'] || 'Default', 'Preferences'), {
|
package/cdp_ext/cdp_ext.js
CHANGED
|
@@ -12,6 +12,7 @@ let auto_attach = {}
|
|
|
12
12
|
let bootstrapping
|
|
13
13
|
let checking = false
|
|
14
14
|
let connection_message
|
|
15
|
+
let setup_error
|
|
15
16
|
const debug_commands = []
|
|
16
17
|
const debug_ports = []
|
|
17
18
|
|
|
@@ -65,6 +66,36 @@ function report_connection(message) {
|
|
|
65
66
|
debug_update()
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
async function enable_user_scripts() {
|
|
70
|
+
try {
|
|
71
|
+
await chrome.userScripts.getScripts()
|
|
72
|
+
return
|
|
73
|
+
} catch {}
|
|
74
|
+
const tab = await chrome.tabs.create({
|
|
75
|
+
url: 'chrome://extensions/?id=' + chrome.runtime.id,
|
|
76
|
+
active: false,
|
|
77
|
+
})
|
|
78
|
+
try {
|
|
79
|
+
const results = await chrome.scripting.executeScript({
|
|
80
|
+
target: { tabId: tab.id },
|
|
81
|
+
world: 'MAIN',
|
|
82
|
+
args: [chrome.runtime.id],
|
|
83
|
+
func: async id => {
|
|
84
|
+
const version = Number(navigator.userAgent.match(/(?:Chrome|Chromium)\/(\d+)/)?.[1])
|
|
85
|
+
if (version < 138) await chrome.developerPrivate.updateProfileConfiguration({ inDeveloperMode: true })
|
|
86
|
+
else await chrome.developerPrivate.updateExtensionConfiguration({
|
|
87
|
+
extensionId: id,
|
|
88
|
+
userScriptsAccess: true,
|
|
89
|
+
})
|
|
90
|
+
return true
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
if (results[0]?.result !== true) throw new Error('Chrome did not enable user-script access')
|
|
94
|
+
} finally {
|
|
95
|
+
await chrome.tabs.remove(tab.id)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
async function bootstrap(tab) {
|
|
69
100
|
const text = tab.pendingUrl ?? tab.url ?? ''
|
|
70
101
|
if (!/^http:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?\/cdp_ext\?/.test(text)) return
|
|
@@ -101,6 +132,7 @@ async function bootstrap(tab) {
|
|
|
101
132
|
error: trigger.error,
|
|
102
133
|
}))
|
|
103
134
|
} else {
|
|
135
|
+
setup_error = undefined
|
|
104
136
|
if (changed) {
|
|
105
137
|
Object.assign(config, settings)
|
|
106
138
|
connection_message = undefined
|
|
@@ -114,7 +146,7 @@ async function bootstrap(tab) {
|
|
|
114
146
|
}
|
|
115
147
|
await disconnecting
|
|
116
148
|
}
|
|
117
|
-
await
|
|
149
|
+
if (url.searchParams.get('user_scripts') === '1') await enable_user_scripts()
|
|
118
150
|
}
|
|
119
151
|
const current_tabs = await chrome.tabs.query({})
|
|
120
152
|
const current = current_tabs.find(current => current.id === tab.id)
|
|
@@ -131,13 +163,14 @@ async function bootstrap(tab) {
|
|
|
131
163
|
} catch (error) {
|
|
132
164
|
trigger.status = 'error'
|
|
133
165
|
trigger.error = error.message
|
|
134
|
-
|
|
166
|
+
setup_error = 'Extension setup failed: ' + error.message
|
|
135
167
|
} finally {
|
|
136
168
|
trigger.duration_ms = Date.now() - trigger.time
|
|
137
169
|
debug_update()
|
|
138
170
|
}
|
|
139
171
|
})().finally(() => {
|
|
140
172
|
bootstrapping = null
|
|
173
|
+
return connect()
|
|
141
174
|
})
|
|
142
175
|
}
|
|
143
176
|
|
|
@@ -344,7 +377,7 @@ for (const [name, options] of Object.entries({
|
|
|
344
377
|
async function connect() {
|
|
345
378
|
await configured
|
|
346
379
|
if (!config.browser || !config.websocket_url || !config.token) return
|
|
347
|
-
if (disconnecting || checking) return
|
|
380
|
+
if (bootstrapping || disconnecting || checking) return
|
|
348
381
|
if (socket?.readyState === WebSocket.CONNECTING) return
|
|
349
382
|
if (socket?.readyState === WebSocket.OPEN) return socket.send('')
|
|
350
383
|
const endpoint = config.websocket_url
|
|
@@ -378,6 +411,7 @@ async function connect() {
|
|
|
378
411
|
browser: config.browser,
|
|
379
412
|
extension_id: chrome.runtime.id,
|
|
380
413
|
token: config.token,
|
|
414
|
+
error: setup_error,
|
|
381
415
|
}))
|
|
382
416
|
}
|
|
383
417
|
connection.onmessage = async event => {
|
package/cdp_ext/manifest.json
CHANGED
package/package.json
CHANGED