@browserbridge/bbx 1.0.0 → 1.0.1
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 +3 -1
- package/docs/api-reference.md +33 -33
- package/docs/mcp-vs-cli.md +104 -104
- package/docs/publishing.md +1 -3
- package/docs/quickstart.md +6 -6
- package/docs/unpacked-extension.md +72 -0
- package/manifest.json +3 -17
- package/package.json +44 -42
- package/packages/agent-client/src/cli-helpers.js +10 -5
- package/packages/agent-client/src/cli.js +65 -135
- package/packages/agent-client/src/client.js +37 -17
- package/packages/agent-client/src/command-registry.js +101 -69
- package/packages/agent-client/src/detect.js +3 -6
- package/packages/agent-client/src/install.js +10 -27
- package/packages/agent-client/src/mcp-config.js +11 -30
- package/packages/agent-client/src/runtime.js +41 -20
- package/packages/agent-client/src/setup-status.js +13 -28
- package/packages/extension/src/background-helpers.js +51 -36
- package/packages/extension/src/background-routing.js +11 -13
- package/packages/extension/src/background.js +562 -299
- package/packages/extension/src/content-script-helpers.js +17 -16
- package/packages/extension/src/content-script.js +175 -109
- package/packages/extension/src/sidepanel-helpers.js +3 -1
- package/packages/extension/ui/popup.js +39 -20
- package/packages/extension/ui/sidepanel.js +108 -191
- package/packages/extension/ui/ui.css +2 -1
- package/packages/mcp-server/src/handlers.js +546 -250
- package/packages/mcp-server/src/server.js +558 -257
- package/packages/native-host/bin/bridge-daemon.js +6 -2
- package/packages/native-host/bin/install-manifest.js +2 -2
- package/packages/native-host/bin/postinstall.js +4 -2
- package/packages/native-host/src/config.js +11 -7
- package/packages/native-host/src/daemon.js +143 -92
- package/packages/native-host/src/install-manifest.js +73 -22
- package/packages/native-host/src/native-host.js +55 -40
- package/packages/protocol/src/budget.js +3 -7
- package/packages/protocol/src/capabilities.js +3 -3
- package/packages/protocol/src/errors.js +11 -11
- package/packages/protocol/src/protocol.js +104 -71
- package/packages/protocol/src/registry.js +300 -45
- package/packages/protocol/src/summary.js +249 -106
- package/packages/protocol/src/types.js +1 -1
- package/skills/browser-bridge/SKILL.md +1 -1
- package/skills/browser-bridge/agents/openai.yaml +3 -3
- package/skills/browser-bridge/references/interaction.md +33 -11
- package/skills/browser-bridge/references/patch-workflow.md +3 -0
- package/skills/browser-bridge/references/protocol.md +125 -70
- package/skills/browser-bridge/references/tailwind.md +12 -11
- package/skills/browser-bridge/references/token-efficiency.md +23 -22
- package/skills/browser-bridge/references/ui-workflows.md +8 -0
- package/packages/extension/ui/offscreen.html +0 -6
- package/packages/extension/ui/offscreen.js +0 -61
|
@@ -32,11 +32,9 @@ import {
|
|
|
32
32
|
normalizeViewportResizeParams,
|
|
33
33
|
normalizeWaitForLoadStateParams,
|
|
34
34
|
normalizeWaitForParams,
|
|
35
|
-
SUPPORTED_VERSIONS
|
|
36
|
-
} from '../../protocol/src/index.js';
|
|
37
|
-
import {
|
|
38
|
-
summarizeBridgeResponse,
|
|
35
|
+
SUPPORTED_VERSIONS,
|
|
39
36
|
} from '../../protocol/src/index.js';
|
|
37
|
+
import { summarizeBridgeResponse } from '../../protocol/src/index.js';
|
|
40
38
|
import {
|
|
41
39
|
enforceTokenBudget,
|
|
42
40
|
getResponseDiagnostics,
|
|
@@ -47,13 +45,13 @@ import {
|
|
|
47
45
|
shouldLogAction,
|
|
48
46
|
simplifyAXNode,
|
|
49
47
|
summarizeActionResult,
|
|
50
|
-
summarizeTabResult
|
|
48
|
+
summarizeTabResult,
|
|
51
49
|
} from './background-helpers.js';
|
|
52
50
|
import {
|
|
53
51
|
isRestrictedAutomationUrl,
|
|
54
52
|
normalizeRequestedAccessTab,
|
|
55
53
|
resolveWindowScopedTab,
|
|
56
|
-
selectRequestTabCandidate
|
|
54
|
+
selectRequestTabCandidate,
|
|
57
55
|
} from './background-routing.js';
|
|
58
56
|
import { TabDebuggerCoordinator } from './debugger-coordinator.js';
|
|
59
57
|
|
|
@@ -212,7 +210,9 @@ function sendIdentity(port) {
|
|
|
212
210
|
void getProfileLabel().then((profileLabel) => {
|
|
213
211
|
try {
|
|
214
212
|
port.postMessage({ type: 'host.identity', browserName, profileLabel });
|
|
215
|
-
} catch {
|
|
213
|
+
} catch {
|
|
214
|
+
/* port may have disconnected */
|
|
215
|
+
}
|
|
216
216
|
});
|
|
217
217
|
}
|
|
218
218
|
|
|
@@ -227,7 +227,9 @@ function sendActivityUpdate(port = state.nativePort) {
|
|
|
227
227
|
if (!port) return;
|
|
228
228
|
try {
|
|
229
229
|
port.postMessage({ type: 'host.activity', at: Date.now() });
|
|
230
|
-
} catch {
|
|
230
|
+
} catch {
|
|
231
|
+
/* port may have disconnected */
|
|
232
|
+
}
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
/**
|
|
@@ -239,8 +241,13 @@ function sendActivityUpdate(port = state.nativePort) {
|
|
|
239
241
|
function sendAccessUpdate(enabled) {
|
|
240
242
|
if (!state.nativePort) return;
|
|
241
243
|
try {
|
|
242
|
-
state.nativePort.postMessage({
|
|
243
|
-
|
|
244
|
+
state.nativePort.postMessage({
|
|
245
|
+
type: 'host.access_update',
|
|
246
|
+
accessEnabled: enabled,
|
|
247
|
+
});
|
|
248
|
+
} catch {
|
|
249
|
+
/* port may have disconnected */
|
|
250
|
+
}
|
|
244
251
|
}
|
|
245
252
|
|
|
246
253
|
const NATIVE_APP_NAME = 'com.browserbridge.browser_bridge';
|
|
@@ -296,7 +303,7 @@ function getVersionNegotiationPayload(requestedVersion) {
|
|
|
296
303
|
...(localIsNewer ? { deprecated_since: latestSupported } : {}),
|
|
297
304
|
migration_hint: localIsNewer
|
|
298
305
|
? `Browser Bridge extension is newer than the client protocol ${requestedVersion}. Update the Browser Bridge CLI/npm package to ${latestSupported} or later.`
|
|
299
|
-
: `Browser Bridge extension is older than the client protocol ${requestedVersion}. Update the extension to a build that supports ${requestedVersion}
|
|
306
|
+
: `Browser Bridge extension is older than the client protocol ${requestedVersion}. Update the extension to a build that supports ${requestedVersion}.`,
|
|
300
307
|
};
|
|
301
308
|
}
|
|
302
309
|
|
|
@@ -322,13 +329,13 @@ const state = {
|
|
|
322
329
|
setupInstallPendingRequestId: null,
|
|
323
330
|
setupInstallPendingAction: null,
|
|
324
331
|
setupInstallPendingKey: null,
|
|
325
|
-
setupInstallError: null
|
|
332
|
+
setupInstallError: null,
|
|
326
333
|
};
|
|
327
334
|
|
|
328
335
|
const tabDebugger = new TabDebuggerCoordinator({
|
|
329
336
|
attach: (target, protocolVersion) => chrome.debugger.attach(target, protocolVersion),
|
|
330
337
|
detach: (target) => chrome.debugger.detach(target),
|
|
331
|
-
protocolVersion: DEBUGGER_PROTOCOL_VERSION
|
|
338
|
+
protocolVersion: DEBUGGER_PROTOCOL_VERSION,
|
|
332
339
|
});
|
|
333
340
|
|
|
334
341
|
void initializeState().catch(reportAsyncError);
|
|
@@ -388,27 +395,35 @@ chrome.runtime.onConnect.addListener((port) => {
|
|
|
388
395
|
});
|
|
389
396
|
|
|
390
397
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
+
if (
|
|
399
|
+
message?.type === 'bridge.open-sidepanel' &&
|
|
400
|
+
typeof message.tabId === 'number' &&
|
|
401
|
+
typeof message.windowId === 'number'
|
|
402
|
+
) {
|
|
403
|
+
void openSidePanelForTab(message.tabId, message.windowId)
|
|
404
|
+
.then(() => {
|
|
405
|
+
sendResponse({ ok: true });
|
|
406
|
+
})
|
|
407
|
+
.catch((error) => {
|
|
408
|
+
sendResponse({
|
|
409
|
+
ok: false,
|
|
410
|
+
error: error instanceof Error ? error.message : String(error),
|
|
411
|
+
});
|
|
398
412
|
});
|
|
399
|
-
});
|
|
400
413
|
return true;
|
|
401
414
|
}
|
|
402
415
|
|
|
403
416
|
if (message?.type === 'bridge.open-sidepanel' && sender.tab?.id && sender.tab.windowId) {
|
|
404
|
-
void openSidePanelForTab(sender.tab.id, sender.tab.windowId)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
417
|
+
void openSidePanelForTab(sender.tab.id, sender.tab.windowId)
|
|
418
|
+
.then(() => {
|
|
419
|
+
sendResponse({ ok: true });
|
|
420
|
+
})
|
|
421
|
+
.catch((error) => {
|
|
422
|
+
sendResponse({
|
|
423
|
+
ok: false,
|
|
424
|
+
error: error instanceof Error ? error.message : String(error),
|
|
425
|
+
});
|
|
410
426
|
});
|
|
411
|
-
});
|
|
412
427
|
return true;
|
|
413
428
|
}
|
|
414
429
|
|
|
@@ -453,9 +468,8 @@ function clearNativeReconnectTimer() {
|
|
|
453
468
|
*/
|
|
454
469
|
function scheduleNativeReconnect(errorMessage, options = {}) {
|
|
455
470
|
const method = typeof options.method === 'string' ? options.method : 'native.disconnect';
|
|
456
|
-
const summaryPrefix =
|
|
457
|
-
? options.summaryPrefix
|
|
458
|
-
: 'Native host disconnected';
|
|
471
|
+
const summaryPrefix =
|
|
472
|
+
typeof options.summaryPrefix === 'string' ? options.summaryPrefix : 'Native host disconnected';
|
|
459
473
|
const updateDisconnectedUi = options.updateDisconnectedUi === true;
|
|
460
474
|
|
|
461
475
|
state.nativeReconnectAttempts += 1;
|
|
@@ -467,7 +481,7 @@ function scheduleNativeReconnect(errorMessage, options = {}) {
|
|
|
467
481
|
broadcastUi({
|
|
468
482
|
type: 'native.status',
|
|
469
483
|
connected: false,
|
|
470
|
-
error: errorMessage
|
|
484
|
+
error: errorMessage,
|
|
471
485
|
});
|
|
472
486
|
}
|
|
473
487
|
void emitUiState().catch(reportAsyncError);
|
|
@@ -476,7 +490,7 @@ function scheduleNativeReconnect(errorMessage, options = {}) {
|
|
|
476
490
|
method,
|
|
477
491
|
source: 'extension',
|
|
478
492
|
ok: false,
|
|
479
|
-
summary: `${summaryPrefix} (attempt ${reconnectAttempt}): ${errorMessage}. Reconnecting in ${nativeReconnectDelay}ms
|
|
493
|
+
summary: `${summaryPrefix} (attempt ${reconnectAttempt}): ${errorMessage}. Reconnecting in ${nativeReconnectDelay}ms.`,
|
|
480
494
|
});
|
|
481
495
|
|
|
482
496
|
clearNativeReconnectTimer();
|
|
@@ -514,7 +528,7 @@ function connectNative() {
|
|
|
514
528
|
method: 'native.reconnect',
|
|
515
529
|
source: 'extension',
|
|
516
530
|
ok: true,
|
|
517
|
-
summary: `Native host reconnected after ${reconnectAttempts} attempt${reconnectAttempts === 1 ? '' : 's'}
|
|
531
|
+
summary: `Native host reconnected after ${reconnectAttempts} attempt${reconnectAttempts === 1 ? '' : 's'}.`,
|
|
518
532
|
});
|
|
519
533
|
}
|
|
520
534
|
}, 500);
|
|
@@ -530,14 +544,14 @@ function connectNative() {
|
|
|
530
544
|
scheduleNativeReconnect(disconnectError, {
|
|
531
545
|
method: 'native.disconnect',
|
|
532
546
|
summaryPrefix: 'Native host disconnected',
|
|
533
|
-
updateDisconnectedUi: state.nativePort === candidatePort
|
|
547
|
+
updateDisconnectedUi: state.nativePort === candidatePort,
|
|
534
548
|
});
|
|
535
549
|
});
|
|
536
550
|
} catch (error) {
|
|
537
551
|
scheduleNativeReconnect(getErrorMessage(error), {
|
|
538
552
|
method: 'native.connect',
|
|
539
553
|
summaryPrefix: 'Native host connection failed',
|
|
540
|
-
updateDisconnectedUi: !state.nativePort
|
|
554
|
+
updateDisconnectedUi: !state.nativePort,
|
|
541
555
|
});
|
|
542
556
|
}
|
|
543
557
|
}
|
|
@@ -550,9 +564,7 @@ function connectNative() {
|
|
|
550
564
|
* @returns {Promise<void>}
|
|
551
565
|
*/
|
|
552
566
|
async function handleBridgeRequest(request) {
|
|
553
|
-
const actionContext = shouldLogAction(request.method)
|
|
554
|
-
? await getActionContext(request)
|
|
555
|
-
: null;
|
|
567
|
+
const actionContext = shouldLogAction(request.method) ? await getActionContext(request) : null;
|
|
556
568
|
/** @type {BridgeResponse} */
|
|
557
569
|
let response;
|
|
558
570
|
|
|
@@ -585,15 +597,21 @@ async function handleBridgeRequest(request) {
|
|
|
585
597
|
async function dispatchBridgeRequest(request) {
|
|
586
598
|
switch (request.method) {
|
|
587
599
|
case 'health.ping':
|
|
588
|
-
return createSuccess(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
600
|
+
return createSuccess(
|
|
601
|
+
request.id,
|
|
602
|
+
{
|
|
603
|
+
extension: 'ok',
|
|
604
|
+
access: await getAccessStatus(),
|
|
605
|
+
...getVersionNegotiationPayload(request.meta?.protocol_version),
|
|
606
|
+
},
|
|
607
|
+
{ method: request.method }
|
|
608
|
+
);
|
|
593
609
|
case 'access.request':
|
|
594
610
|
return handleAccessRequest(request);
|
|
595
611
|
case 'skill.get_runtime_context':
|
|
596
|
-
return createSuccess(request.id, createRuntimeContext(), {
|
|
612
|
+
return createSuccess(request.id, createRuntimeContext(), {
|
|
613
|
+
method: request.method,
|
|
614
|
+
});
|
|
597
615
|
case 'tabs.list':
|
|
598
616
|
return handleListTabs(request);
|
|
599
617
|
case 'tabs.create':
|
|
@@ -659,7 +677,11 @@ async function dispatchBridgeRequest(request) {
|
|
|
659
677
|
case 'cdp.get_computed_styles_for_node':
|
|
660
678
|
return handleCdpRequest(request);
|
|
661
679
|
default:
|
|
662
|
-
return createFailure(
|
|
680
|
+
return createFailure(
|
|
681
|
+
request.id,
|
|
682
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
683
|
+
`Unhandled method ${request.method}`
|
|
684
|
+
);
|
|
663
685
|
}
|
|
664
686
|
}
|
|
665
687
|
|
|
@@ -739,7 +761,9 @@ async function clearEnabledWindowIfGone() {
|
|
|
739
761
|
if (msg.includes('no window') || msg.includes('not found') || msg.includes('window closed')) {
|
|
740
762
|
gone = true;
|
|
741
763
|
} else {
|
|
742
|
-
await new Promise((r) => {
|
|
764
|
+
await new Promise((r) => {
|
|
765
|
+
setTimeout(r, 300);
|
|
766
|
+
});
|
|
743
767
|
try {
|
|
744
768
|
await chrome.windows.get(state.enabledWindow.windowId);
|
|
745
769
|
} catch (_e2) {
|
|
@@ -842,10 +866,14 @@ async function getAccessStatus() {
|
|
|
842
866
|
*/
|
|
843
867
|
async function handleListTabs(request) {
|
|
844
868
|
if (!state.enabledWindow) {
|
|
845
|
-
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
869
|
+
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
870
|
+
method: request.method,
|
|
871
|
+
});
|
|
846
872
|
}
|
|
847
873
|
|
|
848
|
-
const tabs = await chrome.tabs.query({
|
|
874
|
+
const tabs = await chrome.tabs.query({
|
|
875
|
+
windowId: state.enabledWindow.windowId,
|
|
876
|
+
});
|
|
849
877
|
const summarized = tabs
|
|
850
878
|
.map((tab) => {
|
|
851
879
|
if (!isNumber(tab.id) || typeof tab.url !== 'string') {
|
|
@@ -857,7 +885,7 @@ async function handleListTabs(request) {
|
|
|
857
885
|
active: Boolean(tab.active),
|
|
858
886
|
title: tab.title ?? '',
|
|
859
887
|
origin: safeOrigin(tab.url),
|
|
860
|
-
url: tab.url
|
|
888
|
+
url: tab.url,
|
|
861
889
|
};
|
|
862
890
|
})
|
|
863
891
|
.filter((tab) => tab !== null);
|
|
@@ -893,7 +921,7 @@ const TAB_BOUND_NORMALIZERS = {
|
|
|
893
921
|
'patch.rollback': normalizePatchOperation,
|
|
894
922
|
'patch.commit_session_baseline': normalizePatchOperation,
|
|
895
923
|
'page.get_storage': normalizeStorageParams,
|
|
896
|
-
'page.get_text': normalizePageTextParams
|
|
924
|
+
'page.get_text': normalizePageTextParams,
|
|
897
925
|
};
|
|
898
926
|
|
|
899
927
|
/**
|
|
@@ -915,11 +943,15 @@ async function handleTabBoundRequest(request) {
|
|
|
915
943
|
}
|
|
916
944
|
|
|
917
945
|
const timeoutMs = getContentScriptTimeout(request.method, payload);
|
|
918
|
-
const response = await sendTabMessage(
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
946
|
+
const response = await sendTabMessage(
|
|
947
|
+
target.tabId,
|
|
948
|
+
{
|
|
949
|
+
type: 'bridge.execute',
|
|
950
|
+
method: request.method,
|
|
951
|
+
params: payload,
|
|
952
|
+
},
|
|
953
|
+
timeoutMs
|
|
954
|
+
);
|
|
923
955
|
if (response?.error) {
|
|
924
956
|
return toFailureResponse(request, response.error);
|
|
925
957
|
}
|
|
@@ -955,7 +987,9 @@ async function handleNavigationRequest(request) {
|
|
|
955
987
|
: await chrome.tabs.get(target.tabId);
|
|
956
988
|
await emitUiState();
|
|
957
989
|
|
|
958
|
-
return createSuccess(request.id, summarizeTabResult(tab, request.method), {
|
|
990
|
+
return createSuccess(request.id, summarizeTabResult(tab, request.method), {
|
|
991
|
+
method: request.method,
|
|
992
|
+
});
|
|
959
993
|
}
|
|
960
994
|
|
|
961
995
|
/**
|
|
@@ -987,7 +1021,9 @@ async function handlePageEvaluate(request) {
|
|
|
987
1021
|
const target = await resolveRequestTarget(request);
|
|
988
1022
|
const params = normalizeEvaluateParams(request.params);
|
|
989
1023
|
if (!params.expression) {
|
|
990
|
-
return createFailure(request.id, ERROR_CODES.INVALID_REQUEST, 'expression is required.', null, {
|
|
1024
|
+
return createFailure(request.id, ERROR_CODES.INVALID_REQUEST, 'expression is required.', null, {
|
|
1025
|
+
method: request.method,
|
|
1026
|
+
});
|
|
991
1027
|
}
|
|
992
1028
|
return tabDebugger.run(target.tabId, async (debugTarget) => {
|
|
993
1029
|
const result = await chrome.debugger.sendCommand(debugTarget, 'Runtime.evaluate', {
|
|
@@ -997,19 +1033,29 @@ async function handlePageEvaluate(request) {
|
|
|
997
1033
|
timeout: params.timeoutMs,
|
|
998
1034
|
userGesture: true,
|
|
999
1035
|
generatePreview: false,
|
|
1000
|
-
replMode: true
|
|
1036
|
+
replMode: true,
|
|
1001
1037
|
});
|
|
1002
|
-
const cdpResult =
|
|
1038
|
+
const cdpResult =
|
|
1039
|
+
/** @type {{ result?: { type?: string, value?: unknown, description?: string }, exceptionDetails?: { text?: string, exception?: { description?: string } } }} */ (
|
|
1040
|
+
result
|
|
1041
|
+
);
|
|
1003
1042
|
if (cdpResult.exceptionDetails) {
|
|
1004
|
-
const errText =
|
|
1005
|
-
|
|
1006
|
-
||
|
|
1007
|
-
|
|
1043
|
+
const errText =
|
|
1044
|
+
cdpResult.exceptionDetails.exception?.description ||
|
|
1045
|
+
cdpResult.exceptionDetails.text ||
|
|
1046
|
+
'Evaluation failed.';
|
|
1047
|
+
return createFailure(request.id, ERROR_CODES.INTERNAL_ERROR, errText, null, {
|
|
1048
|
+
method: request.method,
|
|
1049
|
+
});
|
|
1008
1050
|
}
|
|
1009
|
-
return createSuccess(
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1051
|
+
return createSuccess(
|
|
1052
|
+
request.id,
|
|
1053
|
+
{
|
|
1054
|
+
value: cdpResult.result?.value ?? null,
|
|
1055
|
+
type: cdpResult.result?.type ?? 'undefined',
|
|
1056
|
+
},
|
|
1057
|
+
{ method: request.method }
|
|
1058
|
+
);
|
|
1013
1059
|
});
|
|
1014
1060
|
}
|
|
1015
1061
|
|
|
@@ -1026,12 +1072,19 @@ async function handlePageGetConsole(request) {
|
|
|
1026
1072
|
|
|
1027
1073
|
await primeTabConsoleCapture(target.tabId);
|
|
1028
1074
|
const { entries, dropped } = await readConsoleBuffer(target.tabId, params.clear);
|
|
1029
|
-
const filtered =
|
|
1030
|
-
|
|
1031
|
-
|
|
1075
|
+
const filtered =
|
|
1076
|
+
params.level === 'all'
|
|
1077
|
+
? entries
|
|
1078
|
+
: entries.filter((/** @type {{ level: string }} */ e) =>
|
|
1079
|
+
matchesConsoleLevel(params.level, e.level)
|
|
1080
|
+
);
|
|
1032
1081
|
const limited = filtered.slice(-params.limit);
|
|
1033
1082
|
|
|
1034
|
-
return createSuccess(
|
|
1083
|
+
return createSuccess(
|
|
1084
|
+
request.id,
|
|
1085
|
+
{ entries: limited, count: limited.length, total: entries.length, dropped },
|
|
1086
|
+
{ method: request.method }
|
|
1087
|
+
);
|
|
1035
1088
|
}
|
|
1036
1089
|
|
|
1037
1090
|
/**
|
|
@@ -1042,15 +1095,19 @@ async function handlePageGetConsole(request) {
|
|
|
1042
1095
|
*/
|
|
1043
1096
|
async function handleCreateTab(request) {
|
|
1044
1097
|
if (!state.enabledWindow) {
|
|
1045
|
-
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
1098
|
+
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
1099
|
+
method: request.method,
|
|
1100
|
+
});
|
|
1046
1101
|
}
|
|
1047
1102
|
const params = normalizeTabCreateParams(request.params);
|
|
1048
1103
|
const tab = await chrome.tabs.create({
|
|
1049
1104
|
url: params.url,
|
|
1050
1105
|
active: params.active,
|
|
1051
|
-
windowId: state.enabledWindow.windowId
|
|
1106
|
+
windowId: state.enabledWindow.windowId,
|
|
1107
|
+
});
|
|
1108
|
+
return createSuccess(request.id, summarizeTabResult(tab, request.method), {
|
|
1109
|
+
method: request.method,
|
|
1052
1110
|
});
|
|
1053
|
-
return createSuccess(request.id, summarizeTabResult(tab, request.method), { method: request.method });
|
|
1054
1111
|
}
|
|
1055
1112
|
|
|
1056
1113
|
/**
|
|
@@ -1062,19 +1119,33 @@ async function handleCreateTab(request) {
|
|
|
1062
1119
|
async function handleCloseTab(request) {
|
|
1063
1120
|
const params = normalizeTabCloseParams(request.params);
|
|
1064
1121
|
if (!state.enabledWindow) {
|
|
1065
|
-
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
1122
|
+
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_WINDOW_OFF, null, {
|
|
1123
|
+
method: request.method,
|
|
1124
|
+
});
|
|
1066
1125
|
}
|
|
1067
1126
|
let tab;
|
|
1068
1127
|
try {
|
|
1069
1128
|
tab = await chrome.tabs.get(params.tabId);
|
|
1070
1129
|
} catch {
|
|
1071
|
-
return createFailure(
|
|
1130
|
+
return createFailure(
|
|
1131
|
+
request.id,
|
|
1132
|
+
ERROR_CODES.TAB_MISMATCH,
|
|
1133
|
+
`Tab ${params.tabId} not found.`,
|
|
1134
|
+
null,
|
|
1135
|
+
{ method: request.method }
|
|
1136
|
+
);
|
|
1072
1137
|
}
|
|
1073
1138
|
if (tab.windowId !== state.enabledWindow.windowId) {
|
|
1074
|
-
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_TAB_CLOSE, null, {
|
|
1139
|
+
return createFailure(request.id, ERROR_CODES.ACCESS_DENIED, ACCESS_DENIED_TAB_CLOSE, null, {
|
|
1140
|
+
method: request.method,
|
|
1141
|
+
});
|
|
1075
1142
|
}
|
|
1076
1143
|
await chrome.tabs.remove(params.tabId);
|
|
1077
|
-
return createSuccess(
|
|
1144
|
+
return createSuccess(
|
|
1145
|
+
request.id,
|
|
1146
|
+
{ closed: true, tabId: params.tabId },
|
|
1147
|
+
{ method: request.method }
|
|
1148
|
+
);
|
|
1078
1149
|
}
|
|
1079
1150
|
|
|
1080
1151
|
/**
|
|
@@ -1091,18 +1162,22 @@ async function handleAccessibilityTree(request) {
|
|
|
1091
1162
|
return tabDebugger.run(target.tabId, async (debugTarget) => {
|
|
1092
1163
|
await chrome.debugger.sendCommand(debugTarget, 'Accessibility.enable', {});
|
|
1093
1164
|
const result = await chrome.debugger.sendCommand(debugTarget, 'Accessibility.getFullAXTree', {
|
|
1094
|
-
depth: params.maxDepth
|
|
1165
|
+
depth: params.maxDepth,
|
|
1095
1166
|
});
|
|
1096
1167
|
const cdpResult = /** @type {{ nodes?: Array<Record<string, unknown>> }} */ (result);
|
|
1097
1168
|
const rawNodes = cdpResult.nodes || [];
|
|
1098
1169
|
const pruned = rawNodes.slice(0, params.maxNodes).map(simplifyAXNode);
|
|
1099
1170
|
await chrome.debugger.sendCommand(debugTarget, 'Accessibility.disable', {});
|
|
1100
|
-
return createSuccess(
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1171
|
+
return createSuccess(
|
|
1172
|
+
request.id,
|
|
1173
|
+
{
|
|
1174
|
+
nodes: pruned,
|
|
1175
|
+
count: pruned.length,
|
|
1176
|
+
total: rawNodes.length,
|
|
1177
|
+
truncated: rawNodes.length > params.maxNodes,
|
|
1178
|
+
},
|
|
1179
|
+
{ method: request.method }
|
|
1180
|
+
);
|
|
1106
1181
|
});
|
|
1107
1182
|
}
|
|
1108
1183
|
|
|
@@ -1123,7 +1198,11 @@ async function handleGetNetwork(request) {
|
|
|
1123
1198
|
? entries.filter((/** @type {{ url: string }} */ e) => e.url.includes(urlPattern))
|
|
1124
1199
|
: entries;
|
|
1125
1200
|
const limited = filtered.slice(-params.limit);
|
|
1126
|
-
return createSuccess(
|
|
1201
|
+
return createSuccess(
|
|
1202
|
+
request.id,
|
|
1203
|
+
{ entries: limited, count: limited.length, total: entries.length, dropped },
|
|
1204
|
+
{ method: request.method }
|
|
1205
|
+
);
|
|
1127
1206
|
}
|
|
1128
1207
|
|
|
1129
1208
|
/**
|
|
@@ -1155,7 +1234,15 @@ async function ensureNetworkInterceptor(tabId) {
|
|
|
1155
1234
|
globalThis.fetch = async function (...args) {
|
|
1156
1235
|
// @ts-ignore
|
|
1157
1236
|
const req = new Request(...args);
|
|
1158
|
-
const entry = {
|
|
1237
|
+
const entry = {
|
|
1238
|
+
method: req.method,
|
|
1239
|
+
url: req.url,
|
|
1240
|
+
status: 0,
|
|
1241
|
+
duration: 0,
|
|
1242
|
+
type: 'fetch',
|
|
1243
|
+
ts: Date.now(),
|
|
1244
|
+
size: 0,
|
|
1245
|
+
};
|
|
1159
1246
|
const startTime = performance.now();
|
|
1160
1247
|
try {
|
|
1161
1248
|
const resp = await origFetch.apply(globalThis, args);
|
|
@@ -1171,8 +1258,10 @@ async function ensureNetworkInterceptor(tabId) {
|
|
|
1171
1258
|
} finally {
|
|
1172
1259
|
buffer.push(entry);
|
|
1173
1260
|
if (buffer.length > MAX) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1261
|
+
const dropped =
|
|
1262
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_network_dropped;
|
|
1263
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_network_dropped =
|
|
1264
|
+
(typeof dropped === 'number' ? dropped : 0) + (buffer.length - MAX);
|
|
1176
1265
|
buffer.splice(0, buffer.length - MAX);
|
|
1177
1266
|
}
|
|
1178
1267
|
}
|
|
@@ -1201,7 +1290,15 @@ async function ensureNetworkInterceptor(tabId) {
|
|
|
1201
1290
|
*/
|
|
1202
1291
|
XMLHttpRequest.prototype.send = function (...args) {
|
|
1203
1292
|
// @ts-ignore
|
|
1204
|
-
const entry = {
|
|
1293
|
+
const entry = {
|
|
1294
|
+
method: this.__bb_method || 'GET',
|
|
1295
|
+
url: this.__bb_url || '',
|
|
1296
|
+
status: 0,
|
|
1297
|
+
duration: 0,
|
|
1298
|
+
type: 'xhr',
|
|
1299
|
+
ts: Date.now(),
|
|
1300
|
+
size: 0,
|
|
1301
|
+
};
|
|
1205
1302
|
const startTime = performance.now();
|
|
1206
1303
|
this.addEventListener('loadend', () => {
|
|
1207
1304
|
entry.status = this.status;
|
|
@@ -1210,14 +1307,16 @@ async function ensureNetworkInterceptor(tabId) {
|
|
|
1210
1307
|
if (cl) entry.size = Number(cl);
|
|
1211
1308
|
buffer.push(entry);
|
|
1212
1309
|
if (buffer.length > MAX) {
|
|
1213
|
-
|
|
1214
|
-
|
|
1310
|
+
const dropped =
|
|
1311
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_network_dropped;
|
|
1312
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_network_dropped =
|
|
1313
|
+
(typeof dropped === 'number' ? dropped : 0) + (buffer.length - MAX);
|
|
1215
1314
|
buffer.splice(0, buffer.length - MAX);
|
|
1216
1315
|
}
|
|
1217
1316
|
});
|
|
1218
1317
|
return /** @type {any} */ (origSend).apply(this, args);
|
|
1219
1318
|
};
|
|
1220
|
-
}
|
|
1319
|
+
},
|
|
1221
1320
|
});
|
|
1222
1321
|
}
|
|
1223
1322
|
|
|
@@ -1246,7 +1345,7 @@ async function readNetworkBuffer(tabId, clear) {
|
|
|
1246
1345
|
}
|
|
1247
1346
|
return { entries: copy, dropped };
|
|
1248
1347
|
},
|
|
1249
|
-
args: [clear]
|
|
1348
|
+
args: [clear],
|
|
1250
1349
|
});
|
|
1251
1350
|
return /** @type {any} */ (results?.[0]?.result) || { entries: [], dropped: 0 };
|
|
1252
1351
|
}
|
|
@@ -1269,16 +1368,20 @@ async function handleViewportResize(request) {
|
|
|
1269
1368
|
width: params.width,
|
|
1270
1369
|
height: params.height,
|
|
1271
1370
|
deviceScaleFactor: params.deviceScaleFactor,
|
|
1272
|
-
mobile: params.width < 768
|
|
1371
|
+
mobile: params.width < 768,
|
|
1273
1372
|
});
|
|
1274
1373
|
}
|
|
1275
|
-
return createSuccess(
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1374
|
+
return createSuccess(
|
|
1375
|
+
request.id,
|
|
1376
|
+
{
|
|
1377
|
+
resized: true,
|
|
1378
|
+
width: params.width,
|
|
1379
|
+
height: params.height,
|
|
1380
|
+
deviceScaleFactor: params.deviceScaleFactor,
|
|
1381
|
+
reset: params.reset,
|
|
1382
|
+
},
|
|
1383
|
+
{ method: request.method }
|
|
1384
|
+
);
|
|
1282
1385
|
});
|
|
1283
1386
|
}
|
|
1284
1387
|
|
|
@@ -1291,7 +1394,9 @@ async function handleViewportResize(request) {
|
|
|
1291
1394
|
async function handlePerformanceMetrics(request) {
|
|
1292
1395
|
const target = await resolveRequestTarget(request);
|
|
1293
1396
|
return tabDebugger.run(target.tabId, async (debugTarget) => {
|
|
1294
|
-
await chrome.debugger.sendCommand(debugTarget, 'Performance.enable', {
|
|
1397
|
+
await chrome.debugger.sendCommand(debugTarget, 'Performance.enable', {
|
|
1398
|
+
timeDomain: 'timeTicks',
|
|
1399
|
+
});
|
|
1295
1400
|
const result = await chrome.debugger.sendCommand(debugTarget, 'Performance.getMetrics', {});
|
|
1296
1401
|
await chrome.debugger.sendCommand(debugTarget, 'Performance.disable', {});
|
|
1297
1402
|
const cdpResult = /** @type {{ metrics?: Array<{ name: string, value: number }> }} */ (result);
|
|
@@ -1315,7 +1420,9 @@ async function handleWaitForLoadState(request) {
|
|
|
1315
1420
|
const tab = params.waitForLoad
|
|
1316
1421
|
? await waitForTabComplete(target.tabId, params.timeoutMs)
|
|
1317
1422
|
: await chrome.tabs.get(target.tabId);
|
|
1318
|
-
return createSuccess(request.id, summarizeTabResult(tab, request.method), {
|
|
1423
|
+
return createSuccess(request.id, summarizeTabResult(tab, request.method), {
|
|
1424
|
+
method: request.method,
|
|
1425
|
+
});
|
|
1319
1426
|
}
|
|
1320
1427
|
|
|
1321
1428
|
/**
|
|
@@ -1343,21 +1450,31 @@ async function ensureConsoleInterceptor(tabId) {
|
|
|
1343
1450
|
globalThis.__bb_console_dropped = 0;
|
|
1344
1451
|
const MAX = 200;
|
|
1345
1452
|
const orig = /** @type {Record<string, Function>} */ ({});
|
|
1346
|
-
const consoleMethods =
|
|
1453
|
+
const consoleMethods =
|
|
1454
|
+
/** @type {Record<string, (...args: unknown[]) => void>} */ (
|
|
1455
|
+
/** @type {unknown} */ (console)
|
|
1456
|
+
);
|
|
1347
1457
|
for (const level of ['log', 'warn', 'error', 'info', 'debug']) {
|
|
1348
1458
|
orig[level] = consoleMethods[level];
|
|
1349
1459
|
consoleMethods[level] = (...args) => {
|
|
1350
1460
|
buffer.push({
|
|
1351
1461
|
level,
|
|
1352
1462
|
args: args.map((a) => {
|
|
1353
|
-
try {
|
|
1354
|
-
|
|
1463
|
+
try {
|
|
1464
|
+
return typeof a === 'object'
|
|
1465
|
+
? JSON.stringify(a).slice(0, 500)
|
|
1466
|
+
: String(a).slice(0, 500);
|
|
1467
|
+
} catch {
|
|
1468
|
+
return String(a).slice(0, 500);
|
|
1469
|
+
}
|
|
1355
1470
|
}),
|
|
1356
|
-
ts: Date.now()
|
|
1471
|
+
ts: Date.now(),
|
|
1357
1472
|
});
|
|
1358
1473
|
if (buffer.length > MAX) {
|
|
1359
|
-
|
|
1360
|
-
|
|
1474
|
+
const dropped =
|
|
1475
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped;
|
|
1476
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped =
|
|
1477
|
+
(typeof dropped === 'number' ? dropped : 0) + (buffer.length - MAX);
|
|
1361
1478
|
buffer.splice(0, buffer.length - MAX);
|
|
1362
1479
|
}
|
|
1363
1480
|
orig[level].apply(console, args);
|
|
@@ -1366,12 +1483,16 @@ async function ensureConsoleInterceptor(tabId) {
|
|
|
1366
1483
|
globalThis.addEventListener('error', (e) => {
|
|
1367
1484
|
buffer.push({
|
|
1368
1485
|
level: 'exception',
|
|
1369
|
-
args: [
|
|
1370
|
-
|
|
1486
|
+
args: [
|
|
1487
|
+
e.message || 'Unknown error',
|
|
1488
|
+
e.filename ? `${e.filename}:${e.lineno}:${e.colno}` : '',
|
|
1489
|
+
],
|
|
1490
|
+
ts: Date.now(),
|
|
1371
1491
|
});
|
|
1372
1492
|
if (buffer.length > MAX) {
|
|
1373
|
-
|
|
1374
|
-
|
|
1493
|
+
const dropped = /** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped;
|
|
1494
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped =
|
|
1495
|
+
(typeof dropped === 'number' ? dropped : 0) + (buffer.length - MAX);
|
|
1375
1496
|
buffer.splice(0, buffer.length - MAX);
|
|
1376
1497
|
}
|
|
1377
1498
|
});
|
|
@@ -1379,15 +1500,16 @@ async function ensureConsoleInterceptor(tabId) {
|
|
|
1379
1500
|
buffer.push({
|
|
1380
1501
|
level: 'rejection',
|
|
1381
1502
|
args: [String(e.reason).slice(0, 500)],
|
|
1382
|
-
ts: Date.now()
|
|
1503
|
+
ts: Date.now(),
|
|
1383
1504
|
});
|
|
1384
1505
|
if (buffer.length > MAX) {
|
|
1385
|
-
|
|
1386
|
-
|
|
1506
|
+
const dropped = /** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped;
|
|
1507
|
+
/** @type {Record<string, unknown>} */ (globalThis).__bb_console_dropped =
|
|
1508
|
+
(typeof dropped === 'number' ? dropped : 0) + (buffer.length - MAX);
|
|
1387
1509
|
buffer.splice(0, buffer.length - MAX);
|
|
1388
1510
|
}
|
|
1389
1511
|
});
|
|
1390
|
-
}
|
|
1512
|
+
},
|
|
1391
1513
|
});
|
|
1392
1514
|
}
|
|
1393
1515
|
|
|
@@ -1419,15 +1541,17 @@ async function primeTabConsoleCapture(tabId, resetBuffer = false) {
|
|
|
1419
1541
|
*/
|
|
1420
1542
|
function isRecoverableInstrumentationError(error) {
|
|
1421
1543
|
const message = normalizeRuntimeErrorMessage(getErrorMessage(error));
|
|
1422
|
-
return
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1544
|
+
return (
|
|
1545
|
+
message === ERROR_CODES.TAB_MISMATCH ||
|
|
1546
|
+
/Cannot access contents of/i.test(message) ||
|
|
1547
|
+
/The extensions gallery cannot be scripted/i.test(message) ||
|
|
1548
|
+
/Cannot access a chrome:\/\//i.test(message) ||
|
|
1549
|
+
/Cannot script/i.test(message) ||
|
|
1550
|
+
/CONTENT_SCRIPT_UNAVAILABLE/i.test(message) ||
|
|
1551
|
+
/No tab with id/i.test(message) ||
|
|
1552
|
+
/Cannot attach to this target/i.test(message) ||
|
|
1553
|
+
/Another debugger is already attached/i.test(message)
|
|
1554
|
+
);
|
|
1431
1555
|
}
|
|
1432
1556
|
|
|
1433
1557
|
/**
|
|
@@ -1455,7 +1579,7 @@ async function readConsoleBuffer(tabId, clear) {
|
|
|
1455
1579
|
}
|
|
1456
1580
|
return { entries: copy, dropped };
|
|
1457
1581
|
},
|
|
1458
|
-
args: [clear]
|
|
1582
|
+
args: [clear],
|
|
1459
1583
|
});
|
|
1460
1584
|
return /** @type {any} */ (results?.[0]?.result) || { entries: [], dropped: 0 };
|
|
1461
1585
|
}
|
|
@@ -1469,7 +1593,9 @@ async function readConsoleBuffer(tabId, clear) {
|
|
|
1469
1593
|
*/
|
|
1470
1594
|
async function primeWindowConsoleCapture(windowId, resetBuffer = false) {
|
|
1471
1595
|
const tabs = await chrome.tabs.query({ windowId });
|
|
1472
|
-
const tabIds = tabs
|
|
1596
|
+
const tabIds = tabs
|
|
1597
|
+
.map((tab) => (isNumber(tab.id) ? tab.id : null))
|
|
1598
|
+
.filter((tabId) => tabId !== null);
|
|
1473
1599
|
await Promise.allSettled(tabIds.map((tabId) => primeTabConsoleCapture(tabId, resetBuffer)));
|
|
1474
1600
|
}
|
|
1475
1601
|
|
|
@@ -1527,27 +1653,36 @@ async function clearWindowBridgeState(windowId) {
|
|
|
1527
1653
|
async function rollbackAllPatchesForTab(tabId) {
|
|
1528
1654
|
try {
|
|
1529
1655
|
await ensureContentScript(tabId);
|
|
1530
|
-
const listed = await sendTabMessage(
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1656
|
+
const listed = await sendTabMessage(
|
|
1657
|
+
tabId,
|
|
1658
|
+
{
|
|
1659
|
+
type: 'bridge.execute',
|
|
1660
|
+
method: 'patch.list',
|
|
1661
|
+
params: {},
|
|
1662
|
+
},
|
|
1663
|
+
CONTENT_SCRIPT_TIMEOUT_MS
|
|
1664
|
+
);
|
|
1535
1665
|
const patches = Array.isArray(listed) ? listed : listed?.patches;
|
|
1536
1666
|
if (!Array.isArray(patches)) {
|
|
1537
1667
|
return;
|
|
1538
1668
|
}
|
|
1539
1669
|
for (const patch of patches) {
|
|
1540
|
-
const patchId =
|
|
1541
|
-
|
|
1542
|
-
|
|
1670
|
+
const patchId =
|
|
1671
|
+
patch && typeof patch === 'object'
|
|
1672
|
+
? /** @type {Record<string, unknown>} */ (patch).patchId
|
|
1673
|
+
: null;
|
|
1543
1674
|
if (typeof patchId !== 'string' || !patchId) {
|
|
1544
1675
|
continue;
|
|
1545
1676
|
}
|
|
1546
|
-
await sendTabMessage(
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1677
|
+
await sendTabMessage(
|
|
1678
|
+
tabId,
|
|
1679
|
+
{
|
|
1680
|
+
type: 'bridge.execute',
|
|
1681
|
+
method: 'patch.rollback',
|
|
1682
|
+
params: { patchId },
|
|
1683
|
+
},
|
|
1684
|
+
CONTENT_SCRIPT_TIMEOUT_MS
|
|
1685
|
+
);
|
|
1551
1686
|
}
|
|
1552
1687
|
} catch (error) {
|
|
1553
1688
|
if (!isRecoverableInstrumentationError(error)) {
|
|
@@ -1588,16 +1723,28 @@ async function handleScreenshot(target, method, params) {
|
|
|
1588
1723
|
if (method === 'screenshot.capture_element') {
|
|
1589
1724
|
await ensureContentScript(target.tabId);
|
|
1590
1725
|
try {
|
|
1591
|
-
clip = await sendTabMessage(
|
|
1592
|
-
|
|
1593
|
-
|
|
1726
|
+
clip = await sendTabMessage(
|
|
1727
|
+
target.tabId,
|
|
1728
|
+
{
|
|
1729
|
+
type: 'bridge.execute',
|
|
1730
|
+
method,
|
|
1731
|
+
params,
|
|
1732
|
+
},
|
|
1733
|
+
CONTENT_SCRIPT_TIMEOUT_MS
|
|
1734
|
+
);
|
|
1594
1735
|
} catch (err) {
|
|
1595
1736
|
// Retry once after a brief pause - the page may have been mid-render
|
|
1596
1737
|
if (err instanceof Error && /stale/i.test(err.message)) {
|
|
1597
|
-
await new Promise(r => setTimeout(r, 250));
|
|
1598
|
-
clip = await sendTabMessage(
|
|
1599
|
-
|
|
1600
|
-
|
|
1738
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
1739
|
+
clip = await sendTabMessage(
|
|
1740
|
+
target.tabId,
|
|
1741
|
+
{
|
|
1742
|
+
type: 'bridge.execute',
|
|
1743
|
+
method,
|
|
1744
|
+
params,
|
|
1745
|
+
},
|
|
1746
|
+
CONTENT_SCRIPT_TIMEOUT_MS
|
|
1747
|
+
);
|
|
1601
1748
|
} else {
|
|
1602
1749
|
throw err;
|
|
1603
1750
|
}
|
|
@@ -1609,19 +1756,24 @@ async function handleScreenshot(target, method, params) {
|
|
|
1609
1756
|
y: Math.max(0, Number(clip.y) || 0),
|
|
1610
1757
|
width: Math.max(0, Number(clip.width) || 0),
|
|
1611
1758
|
height: Math.max(0, Number(clip.height) || 0),
|
|
1612
|
-
scale: Number(clip.scale) || 1
|
|
1759
|
+
scale: Number(clip.scale) || 1,
|
|
1613
1760
|
};
|
|
1614
1761
|
} else if (method === 'screenshot.capture_full_page') {
|
|
1615
1762
|
await ensureContentScript(target.tabId);
|
|
1616
|
-
const dims =
|
|
1617
|
-
|
|
1618
|
-
|
|
1763
|
+
const dims =
|
|
1764
|
+
/** @type {{ scrollWidth: number, scrollHeight: number, devicePixelRatio: number }} */ (
|
|
1765
|
+
await sendTabMessage(
|
|
1766
|
+
target.tabId,
|
|
1767
|
+
{ type: 'bridge.execute', method, params },
|
|
1768
|
+
CONTENT_SCRIPT_TIMEOUT_MS
|
|
1769
|
+
)
|
|
1770
|
+
);
|
|
1619
1771
|
clip = {
|
|
1620
1772
|
x: 0,
|
|
1621
1773
|
y: 0,
|
|
1622
1774
|
width: Math.min(Math.max(1, Number(dims.scrollWidth) || 1), 16384),
|
|
1623
1775
|
height: Math.min(Math.max(1, Number(dims.scrollHeight) || 1), 16384),
|
|
1624
|
-
scale: Number(dims.devicePixelRatio) || 1
|
|
1776
|
+
scale: Number(dims.devicePixelRatio) || 1,
|
|
1625
1777
|
};
|
|
1626
1778
|
} else {
|
|
1627
1779
|
// capture_region: params already carry viewport coordinates
|
|
@@ -1631,14 +1783,14 @@ async function handleScreenshot(target, method, params) {
|
|
|
1631
1783
|
y: Number(params.y) || 0,
|
|
1632
1784
|
width: Math.max(1, Number(params.width) || 1),
|
|
1633
1785
|
height: Math.max(1, Number(params.height) || 1),
|
|
1634
|
-
scale
|
|
1786
|
+
scale,
|
|
1635
1787
|
};
|
|
1636
1788
|
}
|
|
1637
1789
|
|
|
1638
1790
|
if (clip.width < 1 || clip.height < 1) {
|
|
1639
1791
|
throw new Error(
|
|
1640
1792
|
`Capture target has no visible area (${clip.width}\u00d7${clip.height}px). ` +
|
|
1641
|
-
|
|
1793
|
+
'It may be hidden, collapsed, or not yet rendered.'
|
|
1642
1794
|
);
|
|
1643
1795
|
}
|
|
1644
1796
|
|
|
@@ -1654,9 +1806,9 @@ async function handleScreenshot(target, method, params) {
|
|
|
1654
1806
|
y: Math.max(0, clip.y),
|
|
1655
1807
|
width: clip.width,
|
|
1656
1808
|
height: clip.height,
|
|
1657
|
-
scale: dpr
|
|
1809
|
+
scale: dpr,
|
|
1658
1810
|
},
|
|
1659
|
-
captureBeyondViewport: method === 'screenshot.capture_full_page'
|
|
1811
|
+
captureBeyondViewport: method === 'screenshot.capture_full_page',
|
|
1660
1812
|
})
|
|
1661
1813
|
);
|
|
1662
1814
|
if (!cdpResult?.data) {
|
|
@@ -1664,7 +1816,7 @@ async function handleScreenshot(target, method, params) {
|
|
|
1664
1816
|
}
|
|
1665
1817
|
return {
|
|
1666
1818
|
rect: clip,
|
|
1667
|
-
image: `data:image/png;base64,${cdpResult.data}
|
|
1819
|
+
image: `data:image/png;base64,${cdpResult.data}`,
|
|
1668
1820
|
};
|
|
1669
1821
|
});
|
|
1670
1822
|
}
|
|
@@ -1681,7 +1833,11 @@ async function sendTabMessage(tabId, message, timeoutMs) {
|
|
|
1681
1833
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
1682
1834
|
let timeoutId;
|
|
1683
1835
|
const timeout = new Promise((_, reject) => {
|
|
1684
|
-
timeoutId = setTimeout(
|
|
1836
|
+
timeoutId = setTimeout(
|
|
1837
|
+
() =>
|
|
1838
|
+
reject(new Error(`Timed out waiting for content script response after ${timeoutMs}ms.`)),
|
|
1839
|
+
timeoutMs
|
|
1840
|
+
);
|
|
1685
1841
|
});
|
|
1686
1842
|
try {
|
|
1687
1843
|
return await Promise.race([chrome.tabs.sendMessage(tabId, message), timeout]);
|
|
@@ -1702,7 +1858,9 @@ async function injectContentScriptsForWindow(windowId) {
|
|
|
1702
1858
|
const tabs = await chrome.tabs.query({ windowId });
|
|
1703
1859
|
await Promise.allSettled(
|
|
1704
1860
|
tabs
|
|
1705
|
-
.map((tab) =>
|
|
1861
|
+
.map((tab) =>
|
|
1862
|
+
isNumber(tab.id) && tab.url && !isRestrictedAutomationUrl(tab.url) ? tab.id : null
|
|
1863
|
+
)
|
|
1706
1864
|
.filter((tabId) => tabId !== null)
|
|
1707
1865
|
.map((tabId) => ensureContentScript(tabId))
|
|
1708
1866
|
);
|
|
@@ -1715,10 +1873,12 @@ async function injectContentScriptsForWindow(windowId) {
|
|
|
1715
1873
|
* @returns {boolean}
|
|
1716
1874
|
*/
|
|
1717
1875
|
function isRestrictedScriptingError(message) {
|
|
1718
|
-
return
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1876
|
+
return (
|
|
1877
|
+
/Cannot access contents of/i.test(message) ||
|
|
1878
|
+
/The extensions gallery cannot be scripted/i.test(message) ||
|
|
1879
|
+
/Cannot access a chrome:\/\//i.test(message) ||
|
|
1880
|
+
/Cannot script/i.test(message)
|
|
1881
|
+
);
|
|
1722
1882
|
}
|
|
1723
1883
|
|
|
1724
1884
|
/**
|
|
@@ -1739,8 +1899,8 @@ async function ensureContentScript(tabId) {
|
|
|
1739
1899
|
target: { tabId },
|
|
1740
1900
|
files: [
|
|
1741
1901
|
'packages/extension/src/content-script-helpers.js',
|
|
1742
|
-
'packages/extension/src/content-script.js'
|
|
1743
|
-
]
|
|
1902
|
+
'packages/extension/src/content-script.js',
|
|
1903
|
+
],
|
|
1744
1904
|
});
|
|
1745
1905
|
} catch (injectError) {
|
|
1746
1906
|
const msg = injectError instanceof Error ? injectError.message : String(injectError);
|
|
@@ -1776,14 +1936,26 @@ async function handleCdpRequest(request) {
|
|
|
1776
1936
|
} else if (request.method === 'cdp.get_box_model') {
|
|
1777
1937
|
const nodeId = request.params?.nodeId;
|
|
1778
1938
|
if (typeof nodeId !== 'number' || !Number.isFinite(nodeId)) {
|
|
1779
|
-
return createFailure(
|
|
1939
|
+
return createFailure(
|
|
1940
|
+
request.id,
|
|
1941
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1942
|
+
'nodeId must be a finite number.',
|
|
1943
|
+
null,
|
|
1944
|
+
{ method: request.method }
|
|
1945
|
+
);
|
|
1780
1946
|
}
|
|
1781
1947
|
command = 'DOM.getBoxModel';
|
|
1782
1948
|
params = { nodeId };
|
|
1783
1949
|
} else {
|
|
1784
1950
|
const nodeId = request.params?.nodeId;
|
|
1785
1951
|
if (typeof nodeId !== 'number' || !Number.isFinite(nodeId)) {
|
|
1786
|
-
return createFailure(
|
|
1952
|
+
return createFailure(
|
|
1953
|
+
request.id,
|
|
1954
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1955
|
+
'nodeId must be a finite number.',
|
|
1956
|
+
null,
|
|
1957
|
+
{ method: request.method }
|
|
1958
|
+
);
|
|
1787
1959
|
}
|
|
1788
1960
|
command = 'CSS.getComputedStyleForNode';
|
|
1789
1961
|
params = { nodeId };
|
|
@@ -1814,7 +1986,9 @@ async function waitForTabComplete(tabId, timeoutMs) {
|
|
|
1814
1986
|
let finished = false;
|
|
1815
1987
|
const timeoutId = setTimeout(() => {
|
|
1816
1988
|
cleanup();
|
|
1817
|
-
reject(
|
|
1989
|
+
reject(
|
|
1990
|
+
new Error(`Timed out waiting for tab ${tabId} to finish loading after ${timeoutMs}ms.`)
|
|
1991
|
+
);
|
|
1818
1992
|
}, timeoutMs);
|
|
1819
1993
|
|
|
1820
1994
|
/**
|
|
@@ -1896,7 +2070,9 @@ async function resolveRequestTarget(request, options = {}) {
|
|
|
1896
2070
|
});
|
|
1897
2071
|
const tab = selectRequestTabCandidate(request.tab_id, explicitTab, activeTab ?? null);
|
|
1898
2072
|
|
|
1899
|
-
return resolveWindowScopedTab(tab, state.enabledWindow.windowId, {
|
|
2073
|
+
return resolveWindowScopedTab(tab, state.enabledWindow.windowId, {
|
|
2074
|
+
requireScriptable,
|
|
2075
|
+
});
|
|
1900
2076
|
}
|
|
1901
2077
|
|
|
1902
2078
|
/**
|
|
@@ -1906,7 +2082,10 @@ async function resolveRequestTarget(request, options = {}) {
|
|
|
1906
2082
|
* @returns {Promise<CurrentTabState | null>}
|
|
1907
2083
|
*/
|
|
1908
2084
|
async function getCurrentTabState() {
|
|
1909
|
-
const [activeTab] = await chrome.tabs.query({
|
|
2085
|
+
const [activeTab] = await chrome.tabs.query({
|
|
2086
|
+
active: true,
|
|
2087
|
+
lastFocusedWindow: true,
|
|
2088
|
+
});
|
|
1910
2089
|
if (!activeTab?.id || typeof activeTab.windowId !== 'number' || !activeTab.url) {
|
|
1911
2090
|
return null;
|
|
1912
2091
|
}
|
|
@@ -1918,7 +2097,7 @@ async function getCurrentTabState() {
|
|
|
1918
2097
|
url: activeTab.url,
|
|
1919
2098
|
enabled: isWindowEnabled(activeTab.windowId),
|
|
1920
2099
|
accessRequested: isAccessRequestedWindow(activeTab.windowId),
|
|
1921
|
-
restricted: isRestrictedAutomationUrl(activeTab.url)
|
|
2100
|
+
restricted: isRestrictedAutomationUrl(activeTab.url),
|
|
1922
2101
|
};
|
|
1923
2102
|
}
|
|
1924
2103
|
|
|
@@ -1946,7 +2125,7 @@ async function getTabState(tabId) {
|
|
|
1946
2125
|
url: tab.url,
|
|
1947
2126
|
enabled: isWindowEnabled(tab.windowId),
|
|
1948
2127
|
accessRequested: isAccessRequestedWindow(tab.windowId),
|
|
1949
|
-
restricted: isRestrictedAutomationUrl(tab.url)
|
|
2128
|
+
restricted: isRestrictedAutomationUrl(tab.url),
|
|
1950
2129
|
};
|
|
1951
2130
|
} catch {
|
|
1952
2131
|
return null;
|
|
@@ -1981,13 +2160,13 @@ async function setWindowEnabled(windowId, title, enabled) {
|
|
|
1981
2160
|
const access = {
|
|
1982
2161
|
windowId,
|
|
1983
2162
|
title,
|
|
1984
|
-
enabledAt: Date.now()
|
|
2163
|
+
enabledAt: Date.now(),
|
|
1985
2164
|
};
|
|
1986
2165
|
|
|
1987
2166
|
if (enabled) {
|
|
1988
2167
|
state.enabledWindow = access;
|
|
1989
2168
|
await chrome.storage.session.set({
|
|
1990
|
-
[ENABLED_WINDOW_STORAGE_KEY]: access
|
|
2169
|
+
[ENABLED_WINDOW_STORAGE_KEY]: access,
|
|
1991
2170
|
});
|
|
1992
2171
|
} else {
|
|
1993
2172
|
if (state.enabledWindow && state.enabledWindow.windowId === windowId) {
|
|
@@ -1998,7 +2177,9 @@ async function setWindowEnabled(windowId, title, enabled) {
|
|
|
1998
2177
|
|
|
1999
2178
|
try {
|
|
2000
2179
|
await refreshActionIndicators();
|
|
2001
|
-
} catch {
|
|
2180
|
+
} catch {
|
|
2181
|
+
/* Badge updates can fail for closed or restricted tabs. */
|
|
2182
|
+
}
|
|
2002
2183
|
await emitUiState();
|
|
2003
2184
|
|
|
2004
2185
|
if (enabled) {
|
|
@@ -2006,7 +2187,7 @@ async function setWindowEnabled(windowId, title, enabled) {
|
|
|
2006
2187
|
await chrome.alarms.create(KEEPALIVE_ALARM_NAME, { periodInMinutes: 0.4 });
|
|
2007
2188
|
await Promise.allSettled([
|
|
2008
2189
|
injectContentScriptsForWindow(access.windowId),
|
|
2009
|
-
primeWindowConsoleCapture(access.windowId, true)
|
|
2190
|
+
primeWindowConsoleCapture(access.windowId, true),
|
|
2010
2191
|
]);
|
|
2011
2192
|
} else {
|
|
2012
2193
|
sendAccessUpdate(false);
|
|
@@ -2029,18 +2210,30 @@ async function setWindowEnabled(windowId, title, enabled) {
|
|
|
2029
2210
|
* @returns {Promise<void>}
|
|
2030
2211
|
*/
|
|
2031
2212
|
async function handleTabUpdated(tabId, changeInfo, tab) {
|
|
2032
|
-
if (
|
|
2213
|
+
if (
|
|
2214
|
+
typeof changeInfo.title === 'string' &&
|
|
2215
|
+
state.enabledWindow &&
|
|
2216
|
+
tab.windowId === state.enabledWindow.windowId
|
|
2217
|
+
) {
|
|
2033
2218
|
state.enabledWindow = {
|
|
2034
2219
|
...state.enabledWindow,
|
|
2035
|
-
title: changeInfo.title
|
|
2220
|
+
title: changeInfo.title,
|
|
2036
2221
|
};
|
|
2037
2222
|
await chrome.storage.session.set({
|
|
2038
|
-
[ENABLED_WINDOW_STORAGE_KEY]: state.enabledWindow
|
|
2223
|
+
[ENABLED_WINDOW_STORAGE_KEY]: state.enabledWindow,
|
|
2039
2224
|
});
|
|
2040
2225
|
}
|
|
2041
2226
|
|
|
2042
|
-
if (
|
|
2043
|
-
|
|
2227
|
+
if (
|
|
2228
|
+
typeof changeInfo.url === 'string' ||
|
|
2229
|
+
typeof changeInfo.title === 'string' ||
|
|
2230
|
+
changeInfo.status === 'complete'
|
|
2231
|
+
) {
|
|
2232
|
+
if (
|
|
2233
|
+
changeInfo.status === 'complete' &&
|
|
2234
|
+
state.enabledWindow &&
|
|
2235
|
+
tab.windowId === state.enabledWindow.windowId
|
|
2236
|
+
) {
|
|
2044
2237
|
await primeTabConsoleCapture(tabId);
|
|
2045
2238
|
}
|
|
2046
2239
|
await updateActionIndicatorForTab(tabId);
|
|
@@ -2056,7 +2249,11 @@ async function handleTabUpdated(tabId, changeInfo, tab) {
|
|
|
2056
2249
|
* @returns {Promise<void>}
|
|
2057
2250
|
*/
|
|
2058
2251
|
async function handleTabRemoved(tabId, removeInfo) {
|
|
2059
|
-
if (
|
|
2252
|
+
if (
|
|
2253
|
+
state.enabledWindow &&
|
|
2254
|
+
removeInfo.isWindowClosing &&
|
|
2255
|
+
removeInfo.windowId === state.enabledWindow.windowId
|
|
2256
|
+
) {
|
|
2060
2257
|
state.enabledWindow = null;
|
|
2061
2258
|
await chrome.storage.session.remove(ENABLED_WINDOW_STORAGE_KEY);
|
|
2062
2259
|
sendAccessUpdate(false);
|
|
@@ -2074,11 +2271,11 @@ async function handleTabRemoved(tabId, removeInfo) {
|
|
|
2074
2271
|
* @returns {Promise<void>}
|
|
2075
2272
|
*/
|
|
2076
2273
|
async function refreshActionIndicators() {
|
|
2077
|
-
const query = state.enabledWindow
|
|
2078
|
-
? { windowId: state.enabledWindow.windowId }
|
|
2079
|
-
: {};
|
|
2274
|
+
const query = state.enabledWindow ? { windowId: state.enabledWindow.windowId } : {};
|
|
2080
2275
|
const tabs = await chrome.tabs.query(query);
|
|
2081
|
-
const tabIds = tabs
|
|
2276
|
+
const tabIds = tabs
|
|
2277
|
+
.map((tab) => (isNumber(tab.id) ? tab.id : null))
|
|
2278
|
+
.filter((tabId) => tabId !== null);
|
|
2082
2279
|
await Promise.allSettled(tabIds.map((tabId) => updateActionIndicatorForTab(tabId)));
|
|
2083
2280
|
|
|
2084
2281
|
// Some Chromium-based browsers (e.g. Edge) do not visually refresh the toolbar
|
|
@@ -2096,24 +2293,43 @@ async function refreshActionIndicators() {
|
|
|
2096
2293
|
*/
|
|
2097
2294
|
async function syncGlobalBadgeToActiveTab() {
|
|
2098
2295
|
try {
|
|
2099
|
-
const [activeTab] = await chrome.tabs.query({
|
|
2296
|
+
const [activeTab] = await chrome.tabs.query({
|
|
2297
|
+
active: true,
|
|
2298
|
+
lastFocusedWindow: true,
|
|
2299
|
+
});
|
|
2100
2300
|
if (!activeTab?.id) return;
|
|
2101
2301
|
const enabled = await isTabEnabled(activeTab.id);
|
|
2102
|
-
const accessRequested = !enabled && await isAccessRequestedTab(activeTab.id);
|
|
2302
|
+
const accessRequested = !enabled && (await isAccessRequestedTab(activeTab.id));
|
|
2103
2303
|
const restricted = enabled && isRestrictedAutomationUrl(activeTab.url ?? '');
|
|
2104
2304
|
const text = enabled
|
|
2105
|
-
?
|
|
2106
|
-
|
|
2305
|
+
? restricted
|
|
2306
|
+
? RESTRICTED_BADGE_TEXT
|
|
2307
|
+
: ENABLED_BADGE_TEXT
|
|
2308
|
+
: accessRequested
|
|
2309
|
+
? ACCESS_REQUEST_BADGE_TEXT
|
|
2310
|
+
: '';
|
|
2107
2311
|
const bgColor = enabled
|
|
2108
|
-
?
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2312
|
+
? restricted
|
|
2313
|
+
? '#e07020'
|
|
2314
|
+
: '#787878'
|
|
2315
|
+
: accessRequested
|
|
2316
|
+
? '#f2cf2f'
|
|
2317
|
+
: '#464646';
|
|
2318
|
+
const textColor = enabled ? '#ffffff' : accessRequested ? '#000000' : '#ffffff';
|
|
2113
2319
|
await chrome.action.setBadgeText({ text });
|
|
2114
|
-
try {
|
|
2115
|
-
|
|
2116
|
-
|
|
2320
|
+
try {
|
|
2321
|
+
await chrome.action.setBadgeBackgroundColor({ color: bgColor });
|
|
2322
|
+
} catch {
|
|
2323
|
+
/* unsupported */
|
|
2324
|
+
}
|
|
2325
|
+
try {
|
|
2326
|
+
await chrome.action.setBadgeTextColor({ color: textColor });
|
|
2327
|
+
} catch {
|
|
2328
|
+
/* unsupported */
|
|
2329
|
+
}
|
|
2330
|
+
} catch {
|
|
2331
|
+
/* non-critical */
|
|
2332
|
+
}
|
|
2117
2333
|
}
|
|
2118
2334
|
|
|
2119
2335
|
/**
|
|
@@ -2125,40 +2341,64 @@ async function syncGlobalBadgeToActiveTab() {
|
|
|
2125
2341
|
*/
|
|
2126
2342
|
async function updateActionIndicatorForTab(tabId) {
|
|
2127
2343
|
const enabled = await isTabEnabled(tabId);
|
|
2128
|
-
const accessRequested = !enabled && await isAccessRequestedTab(tabId);
|
|
2344
|
+
const accessRequested = !enabled && (await isAccessRequestedTab(tabId));
|
|
2129
2345
|
let restricted = false;
|
|
2130
2346
|
if (enabled) {
|
|
2131
2347
|
try {
|
|
2132
2348
|
const tab = await chrome.tabs.get(tabId);
|
|
2133
2349
|
restricted = isRestrictedAutomationUrl(tab.url ?? '');
|
|
2134
|
-
} catch {
|
|
2350
|
+
} catch {
|
|
2351
|
+
/* ignore */
|
|
2352
|
+
}
|
|
2135
2353
|
}
|
|
2136
2354
|
const badgeText = enabled
|
|
2137
|
-
?
|
|
2138
|
-
|
|
2355
|
+
? restricted
|
|
2356
|
+
? RESTRICTED_BADGE_TEXT
|
|
2357
|
+
: ENABLED_BADGE_TEXT
|
|
2358
|
+
: accessRequested
|
|
2359
|
+
? ACCESS_REQUEST_BADGE_TEXT
|
|
2360
|
+
: '';
|
|
2139
2361
|
const bgColor = enabled
|
|
2140
|
-
?
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2362
|
+
? restricted
|
|
2363
|
+
? '#e07020'
|
|
2364
|
+
: '#787878'
|
|
2365
|
+
: accessRequested
|
|
2366
|
+
? '#f2cf2f'
|
|
2367
|
+
: '#464646';
|
|
2368
|
+
const textColor = enabled ? '#ffffff' : accessRequested ? '#000000' : '#ffffff';
|
|
2145
2369
|
try {
|
|
2146
2370
|
await chrome.action.setBadgeBackgroundColor({ tabId, color: bgColor });
|
|
2147
|
-
} catch {
|
|
2371
|
+
} catch {
|
|
2372
|
+
/* color APIs may be unsupported */
|
|
2373
|
+
}
|
|
2148
2374
|
try {
|
|
2149
2375
|
await chrome.action.setBadgeTextColor({ tabId, color: textColor });
|
|
2150
|
-
} catch {
|
|
2376
|
+
} catch {
|
|
2377
|
+
/* setBadgeTextColor not supported everywhere */
|
|
2378
|
+
}
|
|
2151
2379
|
try {
|
|
2152
2380
|
if (enabled && restricted) {
|
|
2153
|
-
await chrome.action.setTitle({
|
|
2381
|
+
await chrome.action.setTitle({
|
|
2382
|
+
tabId,
|
|
2383
|
+
title: 'Browser Bridge is enabled, but this page cannot be interacted with.',
|
|
2384
|
+
});
|
|
2154
2385
|
} else if (enabled) {
|
|
2155
|
-
await chrome.action.setTitle({
|
|
2386
|
+
await chrome.action.setTitle({
|
|
2387
|
+
tabId,
|
|
2388
|
+
title: 'Browser Bridge is enabled for this window.',
|
|
2389
|
+
});
|
|
2156
2390
|
} else if (accessRequested) {
|
|
2157
|
-
await chrome.action.setTitle({
|
|
2391
|
+
await chrome.action.setTitle({
|
|
2392
|
+
tabId,
|
|
2393
|
+
title:
|
|
2394
|
+
'Agent requested Browser Bridge access for this window. Click to open Browser Bridge, then click Enable.',
|
|
2395
|
+
});
|
|
2158
2396
|
} else {
|
|
2159
2397
|
await chrome.action.setTitle({ tabId, title: 'Browser Bridge' });
|
|
2160
2398
|
}
|
|
2161
|
-
} catch {
|
|
2399
|
+
} catch {
|
|
2400
|
+
/* title can fail for closed tabs */
|
|
2401
|
+
}
|
|
2162
2402
|
try {
|
|
2163
2403
|
await chrome.action.setBadgeText({ tabId, text: badgeText });
|
|
2164
2404
|
} catch (error) {
|
|
@@ -2256,13 +2496,15 @@ function checkAccessRequestAvailability(target) {
|
|
|
2256
2496
|
if (state.requestedAccessWindowId === target.windowId) {
|
|
2257
2497
|
return {
|
|
2258
2498
|
allowed: false,
|
|
2259
|
-
message:
|
|
2499
|
+
message:
|
|
2500
|
+
'Browser Bridge access is already pending for this window. Ask the user to click Enable before requesting access again.',
|
|
2260
2501
|
};
|
|
2261
2502
|
}
|
|
2262
2503
|
|
|
2263
2504
|
return {
|
|
2264
2505
|
allowed: false,
|
|
2265
|
-
message:
|
|
2506
|
+
message:
|
|
2507
|
+
'Browser Bridge access is already pending for another window. Ask the user to click Enable for that window before requesting access again.',
|
|
2266
2508
|
};
|
|
2267
2509
|
}
|
|
2268
2510
|
|
|
@@ -2279,10 +2521,14 @@ async function handleAccessRequest(request) {
|
|
|
2279
2521
|
|
|
2280
2522
|
if (state.enabledWindow) {
|
|
2281
2523
|
const access = await getAccessStatus();
|
|
2282
|
-
return createSuccess(
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2524
|
+
return createSuccess(
|
|
2525
|
+
request.id,
|
|
2526
|
+
{
|
|
2527
|
+
enabled: true,
|
|
2528
|
+
access,
|
|
2529
|
+
},
|
|
2530
|
+
{ method: request.method }
|
|
2531
|
+
);
|
|
2286
2532
|
}
|
|
2287
2533
|
|
|
2288
2534
|
if (!target) {
|
|
@@ -2304,7 +2550,7 @@ async function handleAccessRequest(request) {
|
|
|
2304
2550
|
{
|
|
2305
2551
|
requestedWindowId: state.requestedAccessWindowId,
|
|
2306
2552
|
requestedTargetWindowId: target.windowId,
|
|
2307
|
-
requestedTargetTabId: target.tabId
|
|
2553
|
+
requestedTargetTabId: target.tabId,
|
|
2308
2554
|
},
|
|
2309
2555
|
{ method: request.method }
|
|
2310
2556
|
);
|
|
@@ -2315,14 +2561,18 @@ async function handleAccessRequest(request) {
|
|
|
2315
2561
|
await emitUiState();
|
|
2316
2562
|
await openRequestedAccessUi(target);
|
|
2317
2563
|
|
|
2318
|
-
return createSuccess(
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2564
|
+
return createSuccess(
|
|
2565
|
+
request.id,
|
|
2566
|
+
{
|
|
2567
|
+
enabled: false,
|
|
2568
|
+
requested: true,
|
|
2569
|
+
windowId: target.windowId,
|
|
2570
|
+
tabId: target.tabId,
|
|
2571
|
+
title: target.title,
|
|
2572
|
+
url: target.url,
|
|
2573
|
+
},
|
|
2574
|
+
{ method: request.method }
|
|
2575
|
+
);
|
|
2326
2576
|
}
|
|
2327
2577
|
|
|
2328
2578
|
/**
|
|
@@ -2385,7 +2635,9 @@ async function openRequestedAccessPopupWindow(target) {
|
|
|
2385
2635
|
|
|
2386
2636
|
if (state.requestedAccessPopupWindowId != null) {
|
|
2387
2637
|
try {
|
|
2388
|
-
const existingWindow = await chrome.windows.get(state.requestedAccessPopupWindowId, {
|
|
2638
|
+
const existingWindow = await chrome.windows.get(state.requestedAccessPopupWindowId, {
|
|
2639
|
+
populate: true,
|
|
2640
|
+
});
|
|
2389
2641
|
const existingWindowId = typeof existingWindow.id === 'number' ? existingWindow.id : null;
|
|
2390
2642
|
const popupTabId = existingWindow.tabs?.find((tab) => typeof tab.id === 'number')?.id ?? null;
|
|
2391
2643
|
if (existingWindowId == null || popupTabId == null) {
|
|
@@ -2394,7 +2646,7 @@ async function openRequestedAccessPopupWindow(target) {
|
|
|
2394
2646
|
await chrome.tabs.update(popupTabId, { url: popupUrl });
|
|
2395
2647
|
await chrome.windows.update(existingWindowId, {
|
|
2396
2648
|
focused: true,
|
|
2397
|
-
...(popupPlacement ?? {})
|
|
2649
|
+
...(popupPlacement ?? {}),
|
|
2398
2650
|
});
|
|
2399
2651
|
return;
|
|
2400
2652
|
} catch {
|
|
@@ -2407,20 +2659,18 @@ async function openRequestedAccessPopupWindow(target) {
|
|
|
2407
2659
|
type: 'popup',
|
|
2408
2660
|
focused: true,
|
|
2409
2661
|
width: popupWidth,
|
|
2410
|
-
height: popupHeight
|
|
2662
|
+
height: popupHeight,
|
|
2411
2663
|
});
|
|
2412
2664
|
|
|
2413
2665
|
if (popupPlacement) {
|
|
2414
2666
|
createData = {
|
|
2415
2667
|
...createData,
|
|
2416
|
-
...popupPlacement
|
|
2668
|
+
...popupPlacement,
|
|
2417
2669
|
};
|
|
2418
2670
|
}
|
|
2419
2671
|
|
|
2420
2672
|
const popupWindow = await chrome.windows.create(createData);
|
|
2421
|
-
state.requestedAccessPopupWindowId = typeof popupWindow?.id === 'number'
|
|
2422
|
-
? popupWindow.id
|
|
2423
|
-
: null;
|
|
2673
|
+
state.requestedAccessPopupWindowId = typeof popupWindow?.id === 'number' ? popupWindow.id : null;
|
|
2424
2674
|
}
|
|
2425
2675
|
|
|
2426
2676
|
/**
|
|
@@ -2432,13 +2682,13 @@ async function getRequestedAccessPopupPlacement(targetWindowId, popupWidth) {
|
|
|
2432
2682
|
try {
|
|
2433
2683
|
const browserWindow = await chrome.windows.get(targetWindowId);
|
|
2434
2684
|
if (
|
|
2435
|
-
typeof browserWindow.left === 'number'
|
|
2436
|
-
|
|
2437
|
-
|
|
2685
|
+
typeof browserWindow.left === 'number' &&
|
|
2686
|
+
typeof browserWindow.top === 'number' &&
|
|
2687
|
+
typeof browserWindow.width === 'number'
|
|
2438
2688
|
) {
|
|
2439
2689
|
return {
|
|
2440
2690
|
left: browserWindow.left + Math.max(24, browserWindow.width - popupWidth - 40),
|
|
2441
|
-
top: browserWindow.top + 72
|
|
2691
|
+
top: browserWindow.top + 72,
|
|
2442
2692
|
};
|
|
2443
2693
|
}
|
|
2444
2694
|
} catch {
|
|
@@ -2498,18 +2748,18 @@ async function getActionContext(request) {
|
|
|
2498
2748
|
const tab = await chrome.tabs.get(params.tabId);
|
|
2499
2749
|
return {
|
|
2500
2750
|
tabId: params.tabId,
|
|
2501
|
-
url: tab.url ?? ''
|
|
2751
|
+
url: tab.url ?? '',
|
|
2502
2752
|
};
|
|
2503
2753
|
}
|
|
2504
2754
|
if (!bridgeMethodNeedsTab(request.method)) {
|
|
2505
2755
|
return null;
|
|
2506
2756
|
}
|
|
2507
2757
|
const tab = await resolveRequestTarget(request, {
|
|
2508
|
-
requireScriptable: request.method !== 'tabs.create'
|
|
2758
|
+
requireScriptable: request.method !== 'tabs.create',
|
|
2509
2759
|
});
|
|
2510
2760
|
return {
|
|
2511
2761
|
tabId: tab.tabId,
|
|
2512
|
-
url: tab.url
|
|
2762
|
+
url: tab.url,
|
|
2513
2763
|
};
|
|
2514
2764
|
} catch {
|
|
2515
2765
|
return null;
|
|
@@ -2552,9 +2802,8 @@ async function logBridgeAction(request, response, actionContext) {
|
|
|
2552
2802
|
overBudget: response.meta?.budget_truncated === true,
|
|
2553
2803
|
hasScreenshot: diagnostics.hasScreenshot,
|
|
2554
2804
|
nodeCount: diagnostics.nodeCount,
|
|
2555
|
-
continuationHint:
|
|
2556
|
-
? response.meta.continuation_hint
|
|
2557
|
-
: null,
|
|
2805
|
+
continuationHint:
|
|
2806
|
+
typeof response.meta?.continuation_hint === 'string' ? response.meta.continuation_hint : null,
|
|
2558
2807
|
});
|
|
2559
2808
|
await emitUiState();
|
|
2560
2809
|
}
|
|
@@ -2607,14 +2856,14 @@ async function appendActionLogEntry(entry) {
|
|
|
2607
2856
|
overBudget: entry.overBudget === true,
|
|
2608
2857
|
hasScreenshot: entry.hasScreenshot ?? false,
|
|
2609
2858
|
nodeCount: entry.nodeCount ?? null,
|
|
2610
|
-
continuationHint: entry.continuationHint ?? null
|
|
2859
|
+
continuationHint: entry.continuationHint ?? null,
|
|
2611
2860
|
});
|
|
2612
2861
|
while (state.actionLog.length > MAX_ACTION_LOG_ENTRIES) {
|
|
2613
2862
|
state.actionLog.shift();
|
|
2614
2863
|
}
|
|
2615
2864
|
|
|
2616
2865
|
await chrome.storage.session.set({
|
|
2617
|
-
[ACTION_LOG_STORAGE_KEY]: state.actionLog
|
|
2866
|
+
[ACTION_LOG_STORAGE_KEY]: state.actionLog,
|
|
2618
2867
|
});
|
|
2619
2868
|
}
|
|
2620
2869
|
|
|
@@ -2652,20 +2901,27 @@ function normalizeActionLogEntry(entry) {
|
|
|
2652
2901
|
responseBytes: Number(candidate.responseBytes) || 0,
|
|
2653
2902
|
approxTokens: Number(candidate.approxTokens) || 0,
|
|
2654
2903
|
imageApproxTokens: Number(candidate.imageApproxTokens) || 0,
|
|
2655
|
-
costClass:
|
|
2656
|
-
|
|
2657
|
-
|
|
2904
|
+
costClass:
|
|
2905
|
+
candidate.costClass === 'moderate' ||
|
|
2906
|
+
candidate.costClass === 'heavy' ||
|
|
2907
|
+
candidate.costClass === 'extreme'
|
|
2908
|
+
? candidate.costClass
|
|
2909
|
+
: 'cheap',
|
|
2658
2910
|
imageBytes: Number(candidate.imageBytes) || 0,
|
|
2659
2911
|
summaryBytes: Number(candidate.summaryBytes) || 0,
|
|
2660
2912
|
summaryTokens: Number(candidate.summaryTokens) || 0,
|
|
2661
|
-
summaryCostClass:
|
|
2662
|
-
|
|
2663
|
-
|
|
2913
|
+
summaryCostClass:
|
|
2914
|
+
candidate.summaryCostClass === 'moderate' ||
|
|
2915
|
+
candidate.summaryCostClass === 'heavy' ||
|
|
2916
|
+
candidate.summaryCostClass === 'extreme'
|
|
2917
|
+
? candidate.summaryCostClass
|
|
2918
|
+
: 'cheap',
|
|
2664
2919
|
debuggerBacked: candidate.debuggerBacked === true,
|
|
2665
2920
|
overBudget: candidate.overBudget === true,
|
|
2666
2921
|
hasScreenshot: candidate.hasScreenshot === true,
|
|
2667
2922
|
nodeCount: typeof candidate.nodeCount === 'number' ? candidate.nodeCount : null,
|
|
2668
|
-
continuationHint:
|
|
2923
|
+
continuationHint:
|
|
2924
|
+
typeof candidate.continuationHint === 'string' ? candidate.continuationHint : null,
|
|
2669
2925
|
};
|
|
2670
2926
|
}
|
|
2671
2927
|
|
|
@@ -2686,7 +2942,9 @@ function toFailureResponse(request, error) {
|
|
|
2686
2942
|
? ERROR_CODES.ELEMENT_STALE
|
|
2687
2943
|
: ERROR_CODES.INTERNAL_ERROR;
|
|
2688
2944
|
|
|
2689
|
-
return createFailure(request.id, code, message, null, {
|
|
2945
|
+
return createFailure(request.id, code, message, null, {
|
|
2946
|
+
method: request.method,
|
|
2947
|
+
});
|
|
2690
2948
|
}
|
|
2691
2949
|
|
|
2692
2950
|
/**
|
|
@@ -2698,11 +2956,7 @@ function toFailureResponse(request, error) {
|
|
|
2698
2956
|
* @returns {BridgeResponse}
|
|
2699
2957
|
*/
|
|
2700
2958
|
function enrichBridgeResponse(request, response) {
|
|
2701
|
-
const budgetedResponse = enforceTokenBudget(
|
|
2702
|
-
request.method,
|
|
2703
|
-
response,
|
|
2704
|
-
request.meta?.token_budget,
|
|
2705
|
-
);
|
|
2959
|
+
const budgetedResponse = enforceTokenBudget(request.method, response, request.meta?.token_budget);
|
|
2706
2960
|
const diagnostics = getResponseDiagnostics(request.method, budgetedResponse);
|
|
2707
2961
|
return {
|
|
2708
2962
|
...budgetedResponse,
|
|
@@ -2804,8 +3058,8 @@ async function emitUiStateForPort(port) {
|
|
|
2804
3058
|
setupInstallError: state.setupInstallError,
|
|
2805
3059
|
actionLog: [...state.actionLog]
|
|
2806
3060
|
.filter((entry) => scopedTabId == null || entry.tabId === scopedTabId)
|
|
2807
|
-
.reverse()
|
|
2808
|
-
}
|
|
3061
|
+
.reverse(),
|
|
3062
|
+
},
|
|
2809
3063
|
});
|
|
2810
3064
|
}
|
|
2811
3065
|
|
|
@@ -2820,9 +3074,10 @@ function handleHostStatusMessage(message) {
|
|
|
2820
3074
|
|
|
2821
3075
|
const candidate = /** @type {Record<string, unknown>} */ (message);
|
|
2822
3076
|
if (candidate.type === 'host.bridge_response') {
|
|
2823
|
-
const response =
|
|
2824
|
-
|
|
2825
|
-
|
|
3077
|
+
const response =
|
|
3078
|
+
candidate.response && typeof candidate.response === 'object'
|
|
3079
|
+
? /** @type {BridgeResponse} */ (candidate.response)
|
|
3080
|
+
: null;
|
|
2826
3081
|
if (response?.id === state.setupInstallPendingRequestId) {
|
|
2827
3082
|
const action = state.setupInstallPendingAction;
|
|
2828
3083
|
state.setupInstallPendingRequestId = null;
|
|
@@ -2833,7 +3088,7 @@ function handleHostStatusMessage(message) {
|
|
|
2833
3088
|
void appendActionLogEntry({
|
|
2834
3089
|
method: getSetupActionMethodLabel(action),
|
|
2835
3090
|
ok: true,
|
|
2836
|
-
summary: getSetupActionSuccessSummary(action)
|
|
3091
|
+
summary: getSetupActionSuccessSummary(action),
|
|
2837
3092
|
}).catch(reportAsyncError);
|
|
2838
3093
|
}
|
|
2839
3094
|
refreshSetupStatus(true);
|
|
@@ -2843,7 +3098,7 @@ function handleHostStatusMessage(message) {
|
|
|
2843
3098
|
void appendActionLogEntry({
|
|
2844
3099
|
method: getSetupActionMethodLabel(action),
|
|
2845
3100
|
ok: false,
|
|
2846
|
-
summary: getSetupActionErrorSummary(action, response.error.message)
|
|
3101
|
+
summary: getSetupActionErrorSummary(action, response.error.message),
|
|
2847
3102
|
}).catch(reportAsyncError);
|
|
2848
3103
|
}
|
|
2849
3104
|
state.setupInstallPendingKey = null;
|
|
@@ -2874,16 +3129,17 @@ function handleHostStatusMessage(message) {
|
|
|
2874
3129
|
state.setupInstallPendingRequestId = null;
|
|
2875
3130
|
state.setupInstallPendingAction = null;
|
|
2876
3131
|
state.setupInstallPendingKey = null;
|
|
2877
|
-
state.setupInstallError =
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
3132
|
+
state.setupInstallError =
|
|
3133
|
+
typeof candidate.error === 'object' &&
|
|
3134
|
+
candidate.error &&
|
|
3135
|
+
typeof (/** @type {Record<string, unknown>} */ (candidate.error).message) === 'string'
|
|
3136
|
+
? /** @type {Record<string, string>} */ (candidate.error).message
|
|
3137
|
+
: 'Could not install host setup.';
|
|
2882
3138
|
if (action) {
|
|
2883
3139
|
void appendActionLogEntry({
|
|
2884
3140
|
method: getSetupActionMethodLabel(action),
|
|
2885
3141
|
ok: false,
|
|
2886
|
-
summary: getSetupActionErrorSummary(action, state.setupInstallError)
|
|
3142
|
+
summary: getSetupActionErrorSummary(action, state.setupInstallError),
|
|
2887
3143
|
}).catch(reportAsyncError);
|
|
2888
3144
|
}
|
|
2889
3145
|
void emitUiState().catch(reportAsyncError);
|
|
@@ -2895,11 +3151,12 @@ function handleHostStatusMessage(message) {
|
|
|
2895
3151
|
state.setupStatusPendingRequestId = null;
|
|
2896
3152
|
state.setupInstallPendingAction = null;
|
|
2897
3153
|
state.setupInstallPendingKey = null;
|
|
2898
|
-
state.setupStatusError =
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3154
|
+
state.setupStatusError =
|
|
3155
|
+
typeof candidate.error === 'object' &&
|
|
3156
|
+
candidate.error &&
|
|
3157
|
+
typeof (/** @type {Record<string, unknown>} */ (candidate.error).message) === 'string'
|
|
3158
|
+
? /** @type {Record<string, string>} */ (candidate.error).message
|
|
3159
|
+
: 'Could not inspect host setup.';
|
|
2903
3160
|
void emitUiState().catch(reportAsyncError);
|
|
2904
3161
|
}
|
|
2905
3162
|
return true;
|
|
@@ -2927,11 +3184,12 @@ function handleHostStatusMessage(message) {
|
|
|
2927
3184
|
state.setupStatusPendingRequestId = null;
|
|
2928
3185
|
state.setupInstallPendingAction = null;
|
|
2929
3186
|
state.setupInstallPendingKey = null;
|
|
2930
|
-
state.setupStatusError =
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3187
|
+
state.setupStatusError =
|
|
3188
|
+
typeof candidate.error === 'object' &&
|
|
3189
|
+
candidate.error &&
|
|
3190
|
+
typeof (/** @type {Record<string, unknown>} */ (candidate.error).message) === 'string'
|
|
3191
|
+
? /** @type {Record<string, string>} */ (candidate.error).message
|
|
3192
|
+
: 'Could not inspect host setup.';
|
|
2935
3193
|
void emitUiState().catch(reportAsyncError);
|
|
2936
3194
|
}
|
|
2937
3195
|
return true;
|
|
@@ -2950,8 +3208,9 @@ function refreshSetupStatus(force = false) {
|
|
|
2950
3208
|
return;
|
|
2951
3209
|
}
|
|
2952
3210
|
|
|
2953
|
-
const isFresh =
|
|
2954
|
-
|
|
3211
|
+
const isFresh =
|
|
3212
|
+
state.setupStatusUpdatedAt > 0 &&
|
|
3213
|
+
Date.now() - state.setupStatusUpdatedAt < SETUP_STATUS_STALE_MS;
|
|
2955
3214
|
if (state.setupStatusPending || (!force && isFresh && !state.setupStatusError)) {
|
|
2956
3215
|
return;
|
|
2957
3216
|
}
|
|
@@ -2965,8 +3224,8 @@ function refreshSetupStatus(force = false) {
|
|
|
2965
3224
|
type: 'host.bridge_request',
|
|
2966
3225
|
request: createRequest({
|
|
2967
3226
|
id: requestId,
|
|
2968
|
-
method: 'setup.get_status'
|
|
2969
|
-
})
|
|
3227
|
+
method: 'setup.get_status',
|
|
3228
|
+
}),
|
|
2970
3229
|
});
|
|
2971
3230
|
state.setupStatusTimeoutId = setTimeout(() => {
|
|
2972
3231
|
if (state.setupStatusPendingRequestId !== requestId) {
|
|
@@ -3038,7 +3297,7 @@ async function handleUiMessage(port, message) {
|
|
|
3038
3297
|
}
|
|
3039
3298
|
state.uiPorts.set(port, {
|
|
3040
3299
|
surface: currentPortState.surface,
|
|
3041
|
-
scopeTabId: Number.isFinite(scopeTabId) && scopeTabId > 0 ? scopeTabId : null
|
|
3300
|
+
scopeTabId: Number.isFinite(scopeTabId) && scopeTabId > 0 ? scopeTabId : null,
|
|
3042
3301
|
});
|
|
3043
3302
|
refreshSetupStatus();
|
|
3044
3303
|
await emitUiStateForPort(port);
|
|
@@ -3055,7 +3314,7 @@ async function handleUiMessage(port, message) {
|
|
|
3055
3314
|
const requestedTabId = Number(message.tabId);
|
|
3056
3315
|
try {
|
|
3057
3316
|
// ── DEBUG: simulate slow/error toggles. Set to "delay", "error", or "" ──
|
|
3058
|
-
const _TOGGLE_SIM = /** @type {
|
|
3317
|
+
const _TOGGLE_SIM = /** @type {'delay' | 'error' | ''} */ ('');
|
|
3059
3318
|
if (_TOGGLE_SIM === 'delay') {
|
|
3060
3319
|
await new Promise((r) => setTimeout(r, 6000));
|
|
3061
3320
|
} else if (_TOGGLE_SIM === 'error') {
|
|
@@ -3075,7 +3334,11 @@ async function handleUiMessage(port, message) {
|
|
|
3075
3334
|
}
|
|
3076
3335
|
} catch (error) {
|
|
3077
3336
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3078
|
-
try {
|
|
3337
|
+
try {
|
|
3338
|
+
port.postMessage({ type: 'toggle.error', error: errorMessage });
|
|
3339
|
+
} catch {
|
|
3340
|
+
/* port may have disconnected */
|
|
3341
|
+
}
|
|
3079
3342
|
throw error;
|
|
3080
3343
|
}
|
|
3081
3344
|
return;
|
|
@@ -3096,7 +3359,7 @@ async function handleSetupInstallAction(message) {
|
|
|
3096
3359
|
await appendActionLogEntry({
|
|
3097
3360
|
method: 'Host setup',
|
|
3098
3361
|
ok: false,
|
|
3099
|
-
summary: 'Install failed: Native host is not connected.'
|
|
3362
|
+
summary: 'Install failed: Native host is not connected.',
|
|
3100
3363
|
});
|
|
3101
3364
|
await emitUiState();
|
|
3102
3365
|
return;
|
|
@@ -3113,15 +3376,15 @@ async function handleSetupInstallAction(message) {
|
|
|
3113
3376
|
await appendActionLogEntry({
|
|
3114
3377
|
method: getSetupActionMethodLabel(action),
|
|
3115
3378
|
ok: true,
|
|
3116
|
-
summary: getSetupActionStartSummary(action)
|
|
3379
|
+
summary: getSetupActionStartSummary(action),
|
|
3117
3380
|
});
|
|
3118
3381
|
state.nativePort.postMessage({
|
|
3119
3382
|
type: 'host.bridge_request',
|
|
3120
3383
|
request: createRequest({
|
|
3121
3384
|
id: requestId,
|
|
3122
3385
|
method: 'setup.install',
|
|
3123
|
-
params: action
|
|
3124
|
-
})
|
|
3386
|
+
params: action,
|
|
3387
|
+
}),
|
|
3125
3388
|
});
|
|
3126
3389
|
await emitUiState();
|
|
3127
3390
|
}
|
|
@@ -3204,11 +3467,11 @@ async function openSidePanelForTab(tabId, windowId) {
|
|
|
3204
3467
|
await chrome.sidePanel.setOptions({
|
|
3205
3468
|
tabId,
|
|
3206
3469
|
path: `${SIDEPANEL_PATH}?tabId=${encodeURIComponent(String(tabId))}`,
|
|
3207
|
-
enabled: true
|
|
3470
|
+
enabled: true,
|
|
3208
3471
|
});
|
|
3209
3472
|
await chrome.sidePanel.open({
|
|
3210
3473
|
tabId,
|
|
3211
|
-
windowId
|
|
3474
|
+
windowId,
|
|
3212
3475
|
});
|
|
3213
3476
|
}
|
|
3214
3477
|
|