local-operator-ui 0.39.0 → 0.39.2
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/out/main/index.js +1177 -141
- package/out/preload/index.js +12 -2
- package/out/renderer/assets/{_basePickBy-CBn_dsaM.js → _basePickBy-Qdpquxtc.js} +1 -1
- package/out/renderer/assets/{_baseUniq-B-kmAdKj.js → _baseUniq-DEyY3DUN.js} +1 -1
- package/out/renderer/assets/addressOverlayList-8wdZRD_t.css +1 -0
- package/out/renderer/assets/addressOverlayList-BEh9WU4R.js +1 -0
- package/out/renderer/assets/{agent-details-page-CjksC_ZM.js → agent-details-page-Cty_jRqh.js} +1 -1
- package/out/renderer/assets/agent-hub-page-BbOsMVuP.js +6 -0
- package/out/renderer/assets/{agents-page-vmm4VKcI.js → agents-page-DUU667aX.js} +3 -3
- package/out/renderer/assets/{arc-BSYPx6kR.js → arc-CilXJbRl.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-IEHRJDOE-C-dAuUYf.js → architectureDiagram-IEHRJDOE-BzyxccR6.js} +1 -1
- package/out/renderer/assets/{arrow-up-BaHSg-fV.js → arrow-up-B5uAoxB1.js} +1 -1
- package/out/renderer/assets/{blockDiagram-JOT3LUYC-Dw9X_Asu.js → blockDiagram-JOT3LUYC-BLGHgHQS.js} +1 -1
- package/out/renderer/assets/{book-open-CwoTuMOa.js → book-open-DdgHaS4y.js} +1 -1
- package/out/renderer/assets/browser-page-BsOWZv2K.js +1 -0
- package/out/renderer/assets/{browser-webauthn-prompt-CFEuJXkX.js → browser-webauthn-prompt-BhaoE8es.js} +1 -1
- package/out/renderer/assets/{building-2-D06wTZQj.js → building-2-OmcVyW28.js} +1 -1
- package/out/renderer/assets/{c4Diagram-VJAJSXHY-Bz93LlZc.js → c4Diagram-VJAJSXHY-Ct7NzLx1.js} +1 -1
- package/out/renderer/assets/channel-Cvsze0de.js +1 -0
- package/out/renderer/assets/{chunk-4BMEZGHF-H3mECOID.js → chunk-4BMEZGHF-DZiBYbBY.js} +1 -1
- package/out/renderer/assets/{chunk-A2AXSNBT-BNJGDJK2.js → chunk-A2AXSNBT-DUhnw3B0.js} +1 -1
- package/out/renderer/assets/{chunk-AEK57VVT-Baj26p0I.js → chunk-AEK57VVT-C44kfTnw.js} +1 -1
- package/out/renderer/assets/{chunk-D6G4REZN-B5inAuRK.js → chunk-D6G4REZN-DzPZZIvL.js} +1 -1
- package/out/renderer/assets/{chunk-RZ5BOZE2-BK_F-zoT.js → chunk-RZ5BOZE2-BrkL4yZO.js} +1 -1
- package/out/renderer/assets/{chunk-XZIHB7SX-DipW3oXc.js → chunk-XZIHB7SX-COnOpaJS.js} +1 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-BxhS8Hin.js +1 -0
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-BxhS8Hin.js +1 -0
- package/out/renderer/assets/clone-C89DRkra.js +1 -0
- package/out/renderer/assets/{console-mirror-DB5yXAr9.js → console-mirror-BFQaHp1b.js} +1 -1
- package/out/renderer/assets/{consoleCapture-Bz69ju0Z.js → consoleCapture-L2qaS8a8.js} +1 -1
- package/out/renderer/assets/{conversation-input-sync-BVPZQjhD.js → conversation-input-sync-_NzTot9t.js} +270 -270
- package/out/renderer/assets/{dagre-OKDRZEBW-3ECMKEeo.js → dagre-OKDRZEBW-B8qxetkN.js} +1 -1
- package/out/renderer/assets/{diagram-SSKATNLV-CmAf5Fvk.js → diagram-SSKATNLV-D72ZT1vh.js} +1 -1
- package/out/renderer/assets/{diagram-VNBRO52H-D_21NrT-.js → diagram-VNBRO52H-BSA42de6.js} +1 -1
- package/out/renderer/assets/{erDiagram-Q7BY3M3F-CaUQ9PtP.js → erDiagram-Q7BY3M3F-DsBpC9lb.js} +1 -1
- package/out/renderer/assets/{error-boundary-h9802RBt.js → error-boundary-BniUpzhs.js} +2 -2
- package/out/renderer/assets/{flowDiagram-4HSFHLVR-CVLxJB7Y.js → flowDiagram-4HSFHLVR-CNp3dtNS.js} +1 -1
- package/out/renderer/assets/{ganttDiagram-APWFNJXF-Dt6KPnJQ.js → ganttDiagram-APWFNJXF-D0Z3UZq7.js} +1 -1
- package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-D1BdvS2j.js → gitGraphDiagram-7IBYFJ6S-Duk_g3O3.js} +1 -1
- package/out/renderer/assets/{graph-wsNGWWCD.js → graph-kqr432Cg.js} +1 -1
- package/out/renderer/assets/{hash-C32vAdag.js → hash-Ys0vWpoG.js} +1 -1
- package/out/renderer/assets/{index-3RPijdn_.css → index-BUzMwM-n.css} +1 -1
- package/out/renderer/assets/{index-CvofVM43.js → index-CXemEU4V.js} +1 -1
- package/out/renderer/assets/{index-CiOOz1tp.js → index-QGYemVaa.js} +187 -187
- package/out/renderer/assets/{index-6JB-jSFn.js → index-ubJ6MXMG.js} +1 -1
- package/out/renderer/assets/{infoDiagram-PH2N3AL5-CWPf3FZn.js → infoDiagram-PH2N3AL5-DW7coKCC.js} +1 -1
- package/out/renderer/assets/{installer-DKAnScPb.js → installer-tfJaRCRe.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-U35MCT3I-B5eLtG46.js → journeyDiagram-U35MCT3I-BAcyRs5p.js} +1 -1
- package/out/renderer/assets/{kanban-definition-NDS4AKOZ-BtJFd9zq.js → kanban-definition-NDS4AKOZ-CfVe1cqX.js} +1 -1
- package/out/renderer/assets/{layout-B3W_M4xE.js → layout-D1gldfQG.js} +1 -1
- package/out/renderer/assets/{legacy-agents-page-aqbs_qcV.js → legacy-agents-page-q5OrNo8n.js} +1 -1
- package/out/renderer/assets/{library-page-BIa8sD1w.js → library-page-C-i5iom-.js} +3 -3
- package/out/renderer/assets/{mermaid.core-DzmjLSHb.js → mermaid.core-DkrS1cGg.js} +5 -5
- package/out/renderer/assets/{mesh-page-De8Abvyb.js → mesh-page-DoS_fqsV.js} +1 -1
- package/out/renderer/assets/{mindmap-definition-ALO5MXBD-C1aylJ0P.js → mindmap-definition-ALO5MXBD-cmMkFD0k.js} +1 -1
- package/out/renderer/assets/{mini-mPtvVJCT.js → mini-CdO-ASc4.js} +1 -1
- package/out/renderer/assets/{notebook-pen-CvyIerwZ.js → notebook-pen-BLGuNdTQ.js} +1 -1
- package/out/renderer/assets/{org-access-Cpt-vs-I.js → org-access-D1kp9Jif.js} +1 -1
- package/out/renderer/assets/{org-surface-gate-C8Gs3DNG.js → org-surface-gate-N7Y8SC7k.js} +1 -1
- package/out/renderer/assets/{page-header-DBIgQcaM.js → page-header-BY6LFXsV.js} +1 -1
- package/out/renderer/assets/{pieDiagram-IB7DONF6-CjnobVYZ.js → pieDiagram-IB7DONF6-DSVb7eBC.js} +1 -1
- package/out/renderer/assets/{projects-page-041auzXW.js → projects-page-DVltkJEz.js} +1 -1
- package/out/renderer/assets/{publication-validation-JvAtW-AY.js → publication-validation-D2g0-R8P.js} +1 -1
- package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-B0TLJJFL.js → quadrantDiagram-7GDLP6J5-DekDLECq.js} +1 -1
- package/out/renderer/assets/{radar-MK3ICKWK-D6MZmkB6.js → radar-MK3ICKWK-CTja4z0x.js} +1 -1
- package/out/renderer/assets/{radient-auth-buttons-quggTTqK.js → radient-auth-buttons-BU-HVWCQ.js} +1 -1
- package/out/renderer/assets/{requirementDiagram-KVF5MWMF-BSAJQlo8.js → requirementDiagram-KVF5MWMF-581467zi.js} +1 -1
- package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-CQqGLTr_.js → sankeyDiagram-QLVOVGJD-K8f8OUIc.js} +1 -1
- package/out/renderer/assets/{schedules-page-B51F-ADQ.js → schedules-page-CDlxlDY-.js} +1 -1
- package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-B0LPyEfC.js → sequenceDiagram-X6HHIX6F-C06-5Hcb.js} +1 -1
- package/out/renderer/assets/{settings-page-BpTAK-RZ.js → settings-page-DXulqnG6.js} +1 -1
- package/out/renderer/assets/{smartart-B1Hg0uN6.js → smartart-DWggAVTn.js} +1 -1
- package/out/renderer/assets/{stateDiagram-DGXRK772-hL4-Mzxx.js → stateDiagram-DGXRK772-CfD6HLnB.js} +1 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-UvIPmJGE.js +1 -0
- package/out/renderer/assets/team-bridge-page-DGaJ91ak.js +11 -0
- package/out/renderer/assets/{timeline-definition-BDJGKUSR-BOE8E_XE.js → timeline-definition-BDJGKUSR-DaV84ix9.js} +1 -1
- package/out/renderer/assets/{ui-preferences-store-Cws2cozv.js → ui-preferences-store-YyZ50qSr.js} +1 -1
- package/out/renderer/assets/{use-download-agent-mutation-HcVu2YgL.js → use-download-agent-mutation-ChjAYORO.js} +1 -1
- package/out/renderer/assets/{use-session-snapshot-CMQBHevf.js → use-session-snapshot-MdV65ww5.js} +1 -1
- package/out/renderer/assets/{xychartDiagram-VJFVF3MP-D3cI5mnO.js → xychartDiagram-VJFVF3MP-Dy0ryam9.js} +1 -1
- package/out/renderer/browser-address-overlay-list.html +35 -0
- package/out/renderer/console-capture.html +5 -5
- package/out/renderer/index.html +7 -7
- package/out/renderer/installer.html +4 -4
- package/out/renderer/mini.html +7 -7
- package/package.json +8 -3
- package/out/renderer/assets/agent-hub-page-D3y5dujt.js +0 -6
- package/out/renderer/assets/browser-page-DmYAxbWu.js +0 -1
- package/out/renderer/assets/channel-BJdem-Qj.js +0 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-BQLeT9Qk.js +0 -1
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-BQLeT9Qk.js +0 -1
- package/out/renderer/assets/clone-BaxJyMvd.js +0 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-Bczp9bth.js +0 -1
- package/out/renderer/assets/team-bridge-page-xhQelITo.js +0 -11
- /package/out/renderer/assets/{index-COgOH-4N.js → index-YWHAePtd.js} +0 -0
package/out/main/index.js
CHANGED
|
@@ -15004,10 +15004,24 @@ function resolveLauncherWatchPlan(input) {
|
|
|
15004
15004
|
};
|
|
15005
15005
|
}
|
|
15006
15006
|
if (input.mode !== "headless") {
|
|
15007
|
+
if (input.mode !== "inactive" || input.boundedVisibleProbe !== true) {
|
|
15008
|
+
return {
|
|
15009
|
+
watch: false,
|
|
15010
|
+
launcherPid: null,
|
|
15011
|
+
reason: `window mode ${input.mode} is not launcher-bound: a person can close it`
|
|
15012
|
+
};
|
|
15013
|
+
}
|
|
15014
|
+
if (!Number.isInteger(input.launcherPid) || input.launcherPid <= 1) {
|
|
15015
|
+
return {
|
|
15016
|
+
watch: false,
|
|
15017
|
+
launcherPid: null,
|
|
15018
|
+
reason: "visible probe already detached (no launcher to outlive)"
|
|
15019
|
+
};
|
|
15020
|
+
}
|
|
15007
15021
|
return {
|
|
15008
|
-
watch:
|
|
15009
|
-
launcherPid:
|
|
15010
|
-
reason: `
|
|
15022
|
+
watch: true,
|
|
15023
|
+
launcherPid: input.launcherPid,
|
|
15024
|
+
reason: `visible probe launched by pid ${input.launcherPid}; it is capped and quits when that process goes`
|
|
15011
15025
|
};
|
|
15012
15026
|
}
|
|
15013
15027
|
if (!Number.isInteger(input.launcherPid) || input.launcherPid <= 1) {
|
|
@@ -26052,11 +26066,11 @@ class ConsoleHost {
|
|
|
26052
26066
|
{ rendered: null }
|
|
26053
26067
|
);
|
|
26054
26068
|
}
|
|
26055
|
-
const
|
|
26069
|
+
const rect2 = cropToWindow(
|
|
26056
26070
|
report.contentRect,
|
|
26057
26071
|
window.getContentBounds()
|
|
26058
26072
|
);
|
|
26059
|
-
const png = await this.captureWithRetry(window,
|
|
26073
|
+
const png = await this.captureWithRetry(window, rect2);
|
|
26060
26074
|
const grid = runtime.emulator.grid;
|
|
26061
26075
|
return {
|
|
26062
26076
|
image_base64: png.toString("base64"),
|
|
@@ -26092,10 +26106,10 @@ class ConsoleHost {
|
|
|
26092
26106
|
* renderer paints antialiased text and a cursor — and the empty-terminal case is the
|
|
26093
26107
|
* offscreen path's problem too, so this is not a new ceiling.
|
|
26094
26108
|
*/
|
|
26095
|
-
async captureWithRetry(window,
|
|
26109
|
+
async captureWithRetry(window, rect2) {
|
|
26096
26110
|
let bytes = 0;
|
|
26097
26111
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
26098
|
-
const image2 = await window.webContents.capturePage(
|
|
26112
|
+
const image2 = await window.webContents.capturePage(rect2);
|
|
26099
26113
|
const png = framePng(image2);
|
|
26100
26114
|
bytes = png.length;
|
|
26101
26115
|
if (hasTerminalContent(image2.toBitmap())) {
|
|
@@ -27060,14 +27074,14 @@ function wrapBracketedPaste(bytes) {
|
|
|
27060
27074
|
out.set(end, start.length + bytes.length);
|
|
27061
27075
|
return out;
|
|
27062
27076
|
}
|
|
27063
|
-
function cropToWindow(
|
|
27064
|
-
const x = Math.max(Math.floor(
|
|
27065
|
-
const y = Math.max(Math.floor(
|
|
27077
|
+
function cropToWindow(rect2, bounds) {
|
|
27078
|
+
const x = Math.max(Math.floor(rect2.x), 0);
|
|
27079
|
+
const y = Math.max(Math.floor(rect2.y), 0);
|
|
27066
27080
|
return {
|
|
27067
27081
|
x,
|
|
27068
27082
|
y,
|
|
27069
|
-
width: Math.max(Math.min(Math.floor(
|
|
27070
|
-
height: Math.max(Math.min(Math.floor(
|
|
27083
|
+
width: Math.max(Math.min(Math.floor(rect2.width), bounds.width - x), 1),
|
|
27084
|
+
height: Math.max(Math.min(Math.floor(rect2.height), bounds.height - y), 1)
|
|
27071
27085
|
};
|
|
27072
27086
|
}
|
|
27073
27087
|
function framePng(image2) {
|
|
@@ -27836,12 +27850,12 @@ function contentReportOrThrow(value) {
|
|
|
27836
27850
|
throw new Error("A console content report is required.");
|
|
27837
27851
|
}
|
|
27838
27852
|
const report = value;
|
|
27839
|
-
const
|
|
27840
|
-
if (!
|
|
27853
|
+
const rect2 = report.contentRect;
|
|
27854
|
+
if (!rect2 || typeof rect2 !== "object") {
|
|
27841
27855
|
throw new Error("A console content report needs a contentRect.");
|
|
27842
27856
|
}
|
|
27843
27857
|
const numbers = ["x", "y", "width", "height"].map((key) => {
|
|
27844
|
-
const raw =
|
|
27858
|
+
const raw = rect2[key];
|
|
27845
27859
|
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
27846
27860
|
throw new Error(`contentRect.${key} must be a number.`);
|
|
27847
27861
|
}
|
|
@@ -28341,6 +28355,60 @@ async function startConsoleHost(options) {
|
|
|
28341
28355
|
};
|
|
28342
28356
|
}
|
|
28343
28357
|
}
|
|
28358
|
+
const DEV_DRIVER_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER";
|
|
28359
|
+
const DEV_DRIVER_OUT_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER_OUT";
|
|
28360
|
+
const DEV_DRIVER_ARG = "--lo-dev-driver";
|
|
28361
|
+
const DEV_DRIVER_CAPTURE = "dev-driver-capture";
|
|
28362
|
+
const DEV_DRIVER_FACTS = "dev-driver-facts";
|
|
28363
|
+
const DEV_DRIVER_ADDRESS_OVERLAY = "dev-driver-address-overlay";
|
|
28364
|
+
const DEV_DRIVER_ADDRESS_OVERLAY_PROJECT = "dev-driver-address-overlay-project";
|
|
28365
|
+
const DEV_DRIVER_ADDRESS_OVERLAY_EVENT = "dev-driver-address-overlay-event";
|
|
28366
|
+
const TRUTHY = /* @__PURE__ */ new Set(["1", "true"]);
|
|
28367
|
+
function readOptIn(env) {
|
|
28368
|
+
const raw = env[DEV_DRIVER_ENV];
|
|
28369
|
+
if (raw === void 0 || raw.trim() === "")
|
|
28370
|
+
return { asked: false, value: null };
|
|
28371
|
+
return { asked: true, value: raw.trim() };
|
|
28372
|
+
}
|
|
28373
|
+
function resolveDevDriverArming(input) {
|
|
28374
|
+
const off = (problems = []) => ({
|
|
28375
|
+
armed: false,
|
|
28376
|
+
outDir: null,
|
|
28377
|
+
problems
|
|
28378
|
+
});
|
|
28379
|
+
const optIn = readOptIn(input.env);
|
|
28380
|
+
if (!optIn.asked) return off();
|
|
28381
|
+
if (!TRUTHY.has(optIn.value.toLowerCase())) {
|
|
28382
|
+
return off([
|
|
28383
|
+
`${DEV_DRIVER_ENV}=${optIn.value} is not an opt-in (accepted: 1, true); the renderer dev driver stayed off`
|
|
28384
|
+
]);
|
|
28385
|
+
}
|
|
28386
|
+
if (input.windowMode !== "headless" && input.windowMode !== "inactive") {
|
|
28387
|
+
return off([
|
|
28388
|
+
`the renderer dev driver refused to arm in a "${input.windowMode}" window; it never raises one, so use --window-mode=headless (or inactive)`
|
|
28389
|
+
]);
|
|
28390
|
+
}
|
|
28391
|
+
const outDir = input.env[DEV_DRIVER_OUT_ENV]?.trim() ?? "";
|
|
28392
|
+
if (outDir === "" || !outDir.startsWith("/")) {
|
|
28393
|
+
return off([
|
|
28394
|
+
`${DEV_DRIVER_ENV} was set but ${DEV_DRIVER_OUT_ENV} is ${outDir === "" ? "missing" : `"${outDir}"`}; an absolute frames directory is required, so the driver did not arm`
|
|
28395
|
+
]);
|
|
28396
|
+
}
|
|
28397
|
+
return { armed: true, outDir, problems: [] };
|
|
28398
|
+
}
|
|
28399
|
+
function headlessExerciserAllowed(input) {
|
|
28400
|
+
return input.arming.armed && input.windowMode === "headless";
|
|
28401
|
+
}
|
|
28402
|
+
function devDriverArgument(outDir) {
|
|
28403
|
+
return `${DEV_DRIVER_ARG}=${outDir}`;
|
|
28404
|
+
}
|
|
28405
|
+
function describeDevDriverArming(arming) {
|
|
28406
|
+
if (arming.armed) {
|
|
28407
|
+
return `[dev-driver] ARMED; frames are written to ${arming.outDir}`;
|
|
28408
|
+
}
|
|
28409
|
+
if (arming.problems.length === 0) return null;
|
|
28410
|
+
return `[dev-driver] not armed: ${arming.problems.join("; ")}`;
|
|
28411
|
+
}
|
|
28344
28412
|
const WEBAUTHN_GROUP_SUFFIX = ".webauthn";
|
|
28345
28413
|
const WEBAUTHN_PROMPT_REASON = "use a passkey for $1";
|
|
28346
28414
|
const WEBAUTHN_CHOOSER_TIMEOUT_MS = 6e4;
|
|
@@ -28763,7 +28831,368 @@ function labelOf(account) {
|
|
|
28763
28831
|
if (!account) return "an unnamed passkey";
|
|
28764
28832
|
return account.displayName || account.name || "an unnamed passkey";
|
|
28765
28833
|
}
|
|
28834
|
+
const ADDRESS_LIST_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_S2";
|
|
28835
|
+
const ADDRESS_LIST_DOCUMENT = "browser-address-overlay-list.html";
|
|
28836
|
+
const ADDRESS_LIST_HEADER_HEIGHT = 28;
|
|
28837
|
+
const ADDRESS_LIST_ROW_HEIGHT = 35.5;
|
|
28838
|
+
const ADDRESS_LIST_ROWS_MAX = 5;
|
|
28839
|
+
const ADDRESS_LIST_MAX_HEIGHT_DIP = 212;
|
|
28840
|
+
const ADDRESS_LIST_NOTICE_HEIGHT_DIP = 104;
|
|
28841
|
+
const ADDRESS_LIST_ANCHOR_GAP_MAX = 24;
|
|
28842
|
+
const ADDRESS_LIST_MAX_PAYLOAD_BYTES = 16 * 1024;
|
|
28843
|
+
const ADDRESS_LIST_MAX_TEXT = 512;
|
|
28844
|
+
const ADDRESS_LIST_MAX_TITLE = 256;
|
|
28845
|
+
const ADDRESS_LIST_MAX_LABEL = 256;
|
|
28846
|
+
const ADDRESS_LIST_THEME_FALLBACK = "#1b1f24";
|
|
28847
|
+
const DIP_INT32_MAX$1 = 2 ** 31 - 1;
|
|
28848
|
+
const NOTICE_KINDS = [
|
|
28849
|
+
"error",
|
|
28850
|
+
"hint",
|
|
28851
|
+
"empty",
|
|
28852
|
+
"no_match"
|
|
28853
|
+
];
|
|
28854
|
+
const HOSTILE_STRING = /[\p{Cc}\u2028\u2029\u202A-\u202E\u2066-\u2069]/gu;
|
|
28855
|
+
const COLOR = /^(#[0-9a-f]{6}|rgb\(\d{1,3}, ?\d{1,3}, ?\d{1,3}\))$/i;
|
|
28856
|
+
const PROJECTION_KEYS = /* @__PURE__ */ new Set([
|
|
28857
|
+
"v",
|
|
28858
|
+
"seq",
|
|
28859
|
+
"tabId",
|
|
28860
|
+
"visible",
|
|
28861
|
+
"pageRect",
|
|
28862
|
+
"field",
|
|
28863
|
+
"listTop",
|
|
28864
|
+
"active",
|
|
28865
|
+
"view",
|
|
28866
|
+
"label",
|
|
28867
|
+
"dir",
|
|
28868
|
+
"theme"
|
|
28869
|
+
]);
|
|
28870
|
+
const RECT_KEYS = ["x", "y", "width", "height"];
|
|
28871
|
+
const THEME_KEYS = [
|
|
28872
|
+
"elevated",
|
|
28873
|
+
"ink",
|
|
28874
|
+
"inkMuted",
|
|
28875
|
+
"accent",
|
|
28876
|
+
"accentWash",
|
|
28877
|
+
"rowHover",
|
|
28878
|
+
"hairline",
|
|
28879
|
+
"control"
|
|
28880
|
+
];
|
|
28881
|
+
function isRecord$1(value) {
|
|
28882
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28883
|
+
}
|
|
28884
|
+
function hasOnlyKeys(value, allowed) {
|
|
28885
|
+
const set2 = allowed instanceof Set ? allowed : new Set(allowed);
|
|
28886
|
+
for (const key of Object.keys(value)) if (!set2.has(key)) return false;
|
|
28887
|
+
return true;
|
|
28888
|
+
}
|
|
28889
|
+
function finite(value) {
|
|
28890
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
28891
|
+
}
|
|
28892
|
+
function rect(value) {
|
|
28893
|
+
if (!isRecord$1(value)) return null;
|
|
28894
|
+
if (!hasOnlyKeys(value, RECT_KEYS)) return null;
|
|
28895
|
+
const x = finite(value.x);
|
|
28896
|
+
const y = finite(value.y);
|
|
28897
|
+
const width = finite(value.width);
|
|
28898
|
+
const height = finite(value.height);
|
|
28899
|
+
if (x === null || y === null || width === null || height === null)
|
|
28900
|
+
return null;
|
|
28901
|
+
return { x, y, width, height };
|
|
28902
|
+
}
|
|
28903
|
+
function sanitizeOverlayText(value, cap) {
|
|
28904
|
+
if (typeof value !== "string") return null;
|
|
28905
|
+
const stripped = value.replace(HOSTILE_STRING, "");
|
|
28906
|
+
const points = [...stripped];
|
|
28907
|
+
return points.length > cap ? points.slice(0, cap).join("") : stripped;
|
|
28908
|
+
}
|
|
28909
|
+
function sanitizeRows(value) {
|
|
28910
|
+
if (!Array.isArray(value)) return null;
|
|
28911
|
+
if (value.length === 0 || value.length > ADDRESS_LIST_ROWS_MAX) return null;
|
|
28912
|
+
const rows = [];
|
|
28913
|
+
for (const entry of value) {
|
|
28914
|
+
if (!isRecord$1(entry) || !hasOnlyKeys(entry, ["text", "title"])) return null;
|
|
28915
|
+
const text = sanitizeOverlayText(entry.text, ADDRESS_LIST_MAX_TEXT);
|
|
28916
|
+
const title = sanitizeOverlayText(entry.title, ADDRESS_LIST_MAX_TITLE);
|
|
28917
|
+
if (text === null || title === null) return null;
|
|
28918
|
+
if (text.length === 0) return null;
|
|
28919
|
+
rows.push({ text, title });
|
|
28920
|
+
}
|
|
28921
|
+
return rows;
|
|
28922
|
+
}
|
|
28923
|
+
function sanitizeTheme(value) {
|
|
28924
|
+
if (value === void 0) return { theme: null, fact: "absent" };
|
|
28925
|
+
if (!isRecord$1(value) || !hasOnlyKeys(value, THEME_KEYS)) {
|
|
28926
|
+
return { theme: null, fact: "fallback" };
|
|
28927
|
+
}
|
|
28928
|
+
const theme = {};
|
|
28929
|
+
for (const key of THEME_KEYS) {
|
|
28930
|
+
const raw = value[key];
|
|
28931
|
+
if (typeof raw !== "string" || !COLOR.test(raw)) {
|
|
28932
|
+
return { theme: null, fact: "fallback" };
|
|
28933
|
+
}
|
|
28934
|
+
theme[key] = raw;
|
|
28935
|
+
}
|
|
28936
|
+
return { theme, fact: "resolved" };
|
|
28937
|
+
}
|
|
28938
|
+
function jsonBytes(value) {
|
|
28939
|
+
try {
|
|
28940
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
28941
|
+
} catch {
|
|
28942
|
+
return Number.POSITIVE_INFINITY;
|
|
28943
|
+
}
|
|
28944
|
+
}
|
|
28945
|
+
function sanitizeProjection(raw) {
|
|
28946
|
+
if (jsonBytes(raw) > ADDRESS_LIST_MAX_PAYLOAD_BYTES) {
|
|
28947
|
+
return { ok: false, reason: "payload_too_large" };
|
|
28948
|
+
}
|
|
28949
|
+
if (!isRecord$1(raw)) return { ok: false, reason: "not_an_object" };
|
|
28950
|
+
if (!hasOnlyKeys(raw, PROJECTION_KEYS)) {
|
|
28951
|
+
return { ok: false, reason: "unknown_key" };
|
|
28952
|
+
}
|
|
28953
|
+
if (raw.v !== 1) return { ok: false, reason: "bad_version" };
|
|
28954
|
+
if (!Number.isInteger(raw.seq) || raw.seq < 1) {
|
|
28955
|
+
return { ok: false, reason: "bad_seq" };
|
|
28956
|
+
}
|
|
28957
|
+
if (!Number.isInteger(raw.tabId)) return { ok: false, reason: "bad_tab" };
|
|
28958
|
+
if (typeof raw.visible !== "boolean") {
|
|
28959
|
+
return { ok: false, reason: "bad_visible" };
|
|
28960
|
+
}
|
|
28961
|
+
if (!raw.visible) {
|
|
28962
|
+
return {
|
|
28963
|
+
ok: true,
|
|
28964
|
+
projection: {
|
|
28965
|
+
v: 1,
|
|
28966
|
+
seq: raw.seq,
|
|
28967
|
+
tabId: raw.tabId,
|
|
28968
|
+
visible: false,
|
|
28969
|
+
pageRect: { x: 0, y: 0, width: 0, height: 0 },
|
|
28970
|
+
field: { x: 0, y: 0, width: 0, height: 0 },
|
|
28971
|
+
listTop: 0,
|
|
28972
|
+
active: -1,
|
|
28973
|
+
view: { kind: "notice", notice: "empty", text: "" },
|
|
28974
|
+
label: "",
|
|
28975
|
+
dir: "ltr",
|
|
28976
|
+
theme: null,
|
|
28977
|
+
themeFact: "absent"
|
|
28978
|
+
}
|
|
28979
|
+
};
|
|
28980
|
+
}
|
|
28981
|
+
const pageRect = rect(raw.pageRect);
|
|
28982
|
+
const field = rect(raw.field);
|
|
28983
|
+
if (pageRect === null || field === null) {
|
|
28984
|
+
return { ok: false, reason: "bad_rect" };
|
|
28985
|
+
}
|
|
28986
|
+
const listTop = finite(raw.listTop);
|
|
28987
|
+
if (listTop === null) return { ok: false, reason: "bad_list_top" };
|
|
28988
|
+
if (field.width <= 0 || field.height <= 0) {
|
|
28989
|
+
return { ok: false, reason: "bad_field" };
|
|
28990
|
+
}
|
|
28991
|
+
if (!Number.isInteger(raw.active)) return { ok: false, reason: "bad_active" };
|
|
28992
|
+
const active = raw.active;
|
|
28993
|
+
const viewRaw = raw.view;
|
|
28994
|
+
if (!isRecord$1(viewRaw)) return { ok: false, reason: "bad_view" };
|
|
28995
|
+
let view;
|
|
28996
|
+
if (viewRaw.kind === "rows") {
|
|
28997
|
+
if (!hasOnlyKeys(viewRaw, ["kind", "rows"])) {
|
|
28998
|
+
return { ok: false, reason: "bad_view" };
|
|
28999
|
+
}
|
|
29000
|
+
const rows = sanitizeRows(viewRaw.rows);
|
|
29001
|
+
if (rows === null) return { ok: false, reason: "bad_rows" };
|
|
29002
|
+
view = { kind: "rows", rows };
|
|
29003
|
+
} else if (viewRaw.kind === "notice") {
|
|
29004
|
+
if (!hasOnlyKeys(viewRaw, ["kind", "notice", "text"])) {
|
|
29005
|
+
return { ok: false, reason: "bad_view" };
|
|
29006
|
+
}
|
|
29007
|
+
if (!NOTICE_KINDS.includes(viewRaw.notice)) {
|
|
29008
|
+
return { ok: false, reason: "bad_notice" };
|
|
29009
|
+
}
|
|
29010
|
+
const text = sanitizeOverlayText(viewRaw.text, ADDRESS_LIST_MAX_LABEL);
|
|
29011
|
+
if (text === null) return { ok: false, reason: "bad_notice_text" };
|
|
29012
|
+
view = {
|
|
29013
|
+
kind: "notice",
|
|
29014
|
+
notice: viewRaw.notice,
|
|
29015
|
+
text
|
|
29016
|
+
};
|
|
29017
|
+
} else {
|
|
29018
|
+
return { ok: false, reason: "bad_view" };
|
|
29019
|
+
}
|
|
29020
|
+
if (view.kind === "rows" ? active < -1 || active >= view.rows.length : active !== -1) {
|
|
29021
|
+
return { ok: false, reason: "bad_active" };
|
|
29022
|
+
}
|
|
29023
|
+
const label = sanitizeOverlayText(raw.label, ADDRESS_LIST_MAX_LABEL);
|
|
29024
|
+
if (label === null) return { ok: false, reason: "bad_label" };
|
|
29025
|
+
if (raw.dir !== "ltr" && raw.dir !== "rtl") {
|
|
29026
|
+
return { ok: false, reason: "bad_dir" };
|
|
29027
|
+
}
|
|
29028
|
+
const { theme, fact } = sanitizeTheme(raw.theme);
|
|
29029
|
+
return {
|
|
29030
|
+
ok: true,
|
|
29031
|
+
projection: {
|
|
29032
|
+
v: 1,
|
|
29033
|
+
seq: raw.seq,
|
|
29034
|
+
tabId: raw.tabId,
|
|
29035
|
+
visible: true,
|
|
29036
|
+
pageRect,
|
|
29037
|
+
field,
|
|
29038
|
+
listTop,
|
|
29039
|
+
active,
|
|
29040
|
+
view,
|
|
29041
|
+
label,
|
|
29042
|
+
dir: raw.dir,
|
|
29043
|
+
theme,
|
|
29044
|
+
themeFact: fact
|
|
29045
|
+
}
|
|
29046
|
+
};
|
|
29047
|
+
}
|
|
29048
|
+
function int32$1(value) {
|
|
29049
|
+
if (!Number.isFinite(value)) return null;
|
|
29050
|
+
const rounded = Math.round(value);
|
|
29051
|
+
return Math.abs(rounded) > DIP_INT32_MAX$1 ? null : rounded;
|
|
29052
|
+
}
|
|
29053
|
+
function addressListHeightFor(view) {
|
|
29054
|
+
if (view.kind === "notice") return ADDRESS_LIST_NOTICE_HEIGHT_DIP;
|
|
29055
|
+
return Math.ceil(
|
|
29056
|
+
ADDRESS_LIST_HEADER_HEIGHT + 2 + view.rows.length * ADDRESS_LIST_ROW_HEIGHT + 2 + 1
|
|
29057
|
+
);
|
|
29058
|
+
}
|
|
29059
|
+
function addressListBoundsFor(field, listTop, view, owner) {
|
|
29060
|
+
const fx = int32$1(field.x);
|
|
29061
|
+
const fy = int32$1(field.y);
|
|
29062
|
+
const fw = int32$1(field.width);
|
|
29063
|
+
const fh = int32$1(field.height);
|
|
29064
|
+
const top = int32$1(listTop);
|
|
29065
|
+
const ownerWidth = int32$1(owner.width);
|
|
29066
|
+
const ownerHeight = int32$1(owner.height);
|
|
29067
|
+
if (fx === null || fy === null || fw === null || fh === null || top === null || ownerWidth === null || ownerHeight === null) {
|
|
29068
|
+
return null;
|
|
29069
|
+
}
|
|
29070
|
+
if (fw <= 0 || fh <= 0 || ownerWidth <= 0 || ownerHeight <= 0) return null;
|
|
29071
|
+
const gap = top - (fy + fh);
|
|
29072
|
+
if (gap < 0 || gap > ADDRESS_LIST_ANCHOR_GAP_MAX) return null;
|
|
29073
|
+
const height = addressListHeightFor(view);
|
|
29074
|
+
if (height > ADDRESS_LIST_MAX_HEIGHT_DIP) return null;
|
|
29075
|
+
const left = Math.max(0, fx);
|
|
29076
|
+
const right = Math.min(ownerWidth, fx + fw);
|
|
29077
|
+
const bottom = Math.min(ownerHeight, top + height);
|
|
29078
|
+
if (right <= left) return null;
|
|
29079
|
+
if (bottom - top !== height) return null;
|
|
29080
|
+
return { x: left, y: top, width: right - left, height };
|
|
29081
|
+
}
|
|
29082
|
+
function addressListRowIndexAt(view, offsetY) {
|
|
29083
|
+
if (view.kind !== "rows") return null;
|
|
29084
|
+
if (!Number.isFinite(offsetY)) return null;
|
|
29085
|
+
const withinRows = offsetY - ADDRESS_LIST_HEADER_HEIGHT - 2;
|
|
29086
|
+
if (withinRows < 0) return null;
|
|
29087
|
+
const index = Math.floor(withinRows / ADDRESS_LIST_ROW_HEIGHT);
|
|
29088
|
+
return index >= 0 && index < view.rows.length ? index : null;
|
|
29089
|
+
}
|
|
29090
|
+
function measurementValue(value) {
|
|
29091
|
+
if (!isRecord$1(value)) return null;
|
|
29092
|
+
if (!Number.isInteger(value.appliedSeq)) return null;
|
|
29093
|
+
if (!Number.isInteger(value.rowCount)) return null;
|
|
29094
|
+
const headerHeight = finite(value.headerHeight);
|
|
29095
|
+
const viewportHeight = finite(value.viewportHeight);
|
|
29096
|
+
const scrollHeight = finite(value.scrollHeight);
|
|
29097
|
+
if (headerHeight === null || viewportHeight === null || scrollHeight === null)
|
|
29098
|
+
return null;
|
|
29099
|
+
if (!Array.isArray(value.rowRects)) return null;
|
|
29100
|
+
const rowRects = [];
|
|
29101
|
+
for (const entry of value.rowRects) {
|
|
29102
|
+
if (!isRecord$1(entry)) return null;
|
|
29103
|
+
const top = finite(entry.top);
|
|
29104
|
+
const height = finite(entry.height);
|
|
29105
|
+
if (top === null || height === null) return null;
|
|
29106
|
+
rowRects.push({ top, height });
|
|
29107
|
+
}
|
|
29108
|
+
const innerHeight = finite(value.innerHeight);
|
|
29109
|
+
const visualHeight = finite(value.visualHeight);
|
|
29110
|
+
let activeRowRect = null;
|
|
29111
|
+
if (value.activeRowRect !== null && value.activeRowRect !== void 0) {
|
|
29112
|
+
if (!isRecord$1(value.activeRowRect)) return null;
|
|
29113
|
+
const top = finite(value.activeRowRect.top);
|
|
29114
|
+
const height = finite(value.activeRowRect.height);
|
|
29115
|
+
if (top === null || height === null) return null;
|
|
29116
|
+
activeRowRect = { top, height };
|
|
29117
|
+
}
|
|
29118
|
+
const appliedSeq = value.appliedSeq;
|
|
29119
|
+
const rowCount = value.rowCount;
|
|
29120
|
+
if (typeof appliedSeq !== "number" || typeof rowCount !== "number")
|
|
29121
|
+
return null;
|
|
29122
|
+
return {
|
|
29123
|
+
appliedSeq,
|
|
29124
|
+
rowCount,
|
|
29125
|
+
headerHeight,
|
|
29126
|
+
rowRects,
|
|
29127
|
+
viewportHeight,
|
|
29128
|
+
scrollHeight,
|
|
29129
|
+
...innerHeight === null ? {} : { innerHeight },
|
|
29130
|
+
...visualHeight === null ? {} : { visualHeight },
|
|
29131
|
+
activeRowRect
|
|
29132
|
+
};
|
|
29133
|
+
}
|
|
29134
|
+
function addressListMeasurementProblem(raw, projection, requestedHeight) {
|
|
29135
|
+
const measurement = measurementValue(raw);
|
|
29136
|
+
if (measurement === null) return "measure_invalid";
|
|
29137
|
+
if (measurement.appliedSeq !== projection.seq) return "measure_stale";
|
|
29138
|
+
const reported = measurement.visualHeight ?? measurement.innerHeight;
|
|
29139
|
+
if (reported !== void 0 && Math.abs(reported - requestedHeight) > 1) {
|
|
29140
|
+
return "measure_viewport";
|
|
29141
|
+
}
|
|
29142
|
+
if (projection.view.kind !== "rows") {
|
|
29143
|
+
return measurement.rowCount === 0 ? null : "measure_extra_rows";
|
|
29144
|
+
}
|
|
29145
|
+
const rows = projection.view.rows;
|
|
29146
|
+
if (measurement.rowCount !== rows.length) return "measure_row_count";
|
|
29147
|
+
if (measurement.rowRects.length !== rows.length) return "measure_row_rects";
|
|
29148
|
+
if (Math.abs(measurement.headerHeight - ADDRESS_LIST_HEADER_HEIGHT) > 0.5) {
|
|
29149
|
+
return "measure_header";
|
|
29150
|
+
}
|
|
29151
|
+
for (const row of measurement.rowRects) {
|
|
29152
|
+
if (Math.abs(row.height - ADDRESS_LIST_ROW_HEIGHT) > 0.5) {
|
|
29153
|
+
return "measure_row_height";
|
|
29154
|
+
}
|
|
29155
|
+
}
|
|
29156
|
+
if (measurement.scrollHeight > measurement.viewportHeight + 1) {
|
|
29157
|
+
return "measure_scroll";
|
|
29158
|
+
}
|
|
29159
|
+
if (projection.active >= 0) {
|
|
29160
|
+
if (measurement.activeRowRect === null) return "measure_active_missing";
|
|
29161
|
+
const paintBox = measurement.visualHeight ?? measurement.innerHeight ?? requestedHeight;
|
|
29162
|
+
const { top, height } = measurement.activeRowRect;
|
|
29163
|
+
const documentBottom = measurement.headerHeight + top + height;
|
|
29164
|
+
if (top < -0.5 || documentBottom > paintBox + 0.5) {
|
|
29165
|
+
return "active_not_visible";
|
|
29166
|
+
}
|
|
29167
|
+
}
|
|
29168
|
+
return null;
|
|
29169
|
+
}
|
|
29170
|
+
function addressListViewModel(projection) {
|
|
29171
|
+
const fallback = {
|
|
29172
|
+
elevated: ADDRESS_LIST_THEME_FALLBACK,
|
|
29173
|
+
ink: "#e6e8ea",
|
|
29174
|
+
inkMuted: "#9aa0a6",
|
|
29175
|
+
accent: "#5b9dd9",
|
|
29176
|
+
accentWash: "#23303d",
|
|
29177
|
+
rowHover: "#24282e",
|
|
29178
|
+
hairline: "#2f343b",
|
|
29179
|
+
control: "#3a4048"
|
|
29180
|
+
};
|
|
29181
|
+
const theme = projection.theme ?? fallback;
|
|
29182
|
+
return {
|
|
29183
|
+
v: 1,
|
|
29184
|
+
seq: projection.seq,
|
|
29185
|
+
dir: projection.dir,
|
|
29186
|
+
label: projection.label,
|
|
29187
|
+
active: projection.active,
|
|
29188
|
+
view: projection.view,
|
|
29189
|
+
height: addressListHeightFor(projection.view),
|
|
29190
|
+
theme
|
|
29191
|
+
};
|
|
29192
|
+
}
|
|
28766
29193
|
const ADDRESS_OVERLAY_S1_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_S1";
|
|
29194
|
+
const ADDRESS_OVERLAY_VISIBLE_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_VISIBLE";
|
|
29195
|
+
const ADDRESS_OVERLAY_VISIBLE_MAX_MS = 12e4;
|
|
28767
29196
|
const ADDRESS_OVERLAY_DOCUMENT = "browser-address-overlay.html";
|
|
28768
29197
|
const ADDRESS_OVERLAY_MAX_HEIGHT_DIP = 160;
|
|
28769
29198
|
const ADDRESS_OVERLAY_READY_TIMEOUT_MS = 5e3;
|
|
@@ -28771,25 +29200,132 @@ const DIP_INT32_MAX = 2 ** 31 - 1;
|
|
|
28771
29200
|
const INDEX_DOCUMENT_SUFFIX$2 = /\/?index\.html(\?.*)?$/;
|
|
28772
29201
|
const TRAILING_SLASH$1 = /\/$/;
|
|
28773
29202
|
function addressOverlayDocumentUrlFor(rendererUrl) {
|
|
28774
|
-
return
|
|
29203
|
+
return documentUrlFor(rendererUrl, ADDRESS_OVERLAY_DOCUMENT);
|
|
29204
|
+
}
|
|
29205
|
+
function addressListDocumentUrlFor(rendererUrl) {
|
|
29206
|
+
return documentUrlFor(rendererUrl, ADDRESS_LIST_DOCUMENT);
|
|
29207
|
+
}
|
|
29208
|
+
function documentUrlFor(rendererUrl, document) {
|
|
29209
|
+
return INDEX_DOCUMENT_SUFFIX$2.test(rendererUrl) ? rendererUrl.replace(INDEX_DOCUMENT_SUFFIX$2, `/${document}`) : `${rendererUrl.replace(TRAILING_SLASH$1, "")}/${document}`;
|
|
28775
29210
|
}
|
|
28776
29211
|
function resolveAddressOverlayGate(input) {
|
|
28777
29212
|
const raw = input.env[ADDRESS_OVERLAY_S1_ENV];
|
|
29213
|
+
const listRaw = input.env[ADDRESS_LIST_ENV];
|
|
29214
|
+
const visibleRaw = input.env[ADDRESS_OVERLAY_VISIBLE_ENV];
|
|
29215
|
+
const listAsked = listRaw !== void 0 && listRaw.trim() !== "";
|
|
29216
|
+
const visibleAsked = visibleRaw !== void 0 && visibleRaw.trim() !== "";
|
|
28778
29217
|
if (raw === void 0 || raw.trim() === "") {
|
|
28779
|
-
|
|
29218
|
+
if (visibleAsked) {
|
|
29219
|
+
return {
|
|
29220
|
+
asked: true,
|
|
29221
|
+
enabled: false,
|
|
29222
|
+
list: false,
|
|
29223
|
+
visible: false,
|
|
29224
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} needs ${ADDRESS_OVERLAY_S1_ENV}=1 and ${ADDRESS_LIST_ENV}=1: the visible probe is an increment on S1's child and S2's mirror, not a third arm`
|
|
29225
|
+
};
|
|
29226
|
+
}
|
|
29227
|
+
return listAsked ? {
|
|
29228
|
+
asked: true,
|
|
29229
|
+
enabled: false,
|
|
29230
|
+
list: false,
|
|
29231
|
+
visible: false,
|
|
29232
|
+
reason: `${ADDRESS_LIST_ENV} needs ${ADDRESS_OVERLAY_S1_ENV}=1: S2's list is a mirror inside S1's child, not a second child`
|
|
29233
|
+
} : {
|
|
29234
|
+
asked: false,
|
|
29235
|
+
enabled: false,
|
|
29236
|
+
list: false,
|
|
29237
|
+
visible: false,
|
|
29238
|
+
reason: null
|
|
29239
|
+
};
|
|
28780
29240
|
}
|
|
28781
29241
|
const asked = true;
|
|
28782
29242
|
if (raw.trim() !== "1") {
|
|
28783
29243
|
return {
|
|
28784
29244
|
asked,
|
|
28785
29245
|
enabled: false,
|
|
29246
|
+
list: false,
|
|
29247
|
+
visible: false,
|
|
28786
29248
|
reason: `${ADDRESS_OVERLAY_S1_ENV}=${raw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
28787
29249
|
};
|
|
28788
29250
|
}
|
|
29251
|
+
if (listAsked && listRaw.trim() !== "1") {
|
|
29252
|
+
return {
|
|
29253
|
+
asked,
|
|
29254
|
+
enabled: false,
|
|
29255
|
+
list: false,
|
|
29256
|
+
visible: false,
|
|
29257
|
+
reason: `${ADDRESS_LIST_ENV}=${listRaw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
29258
|
+
};
|
|
29259
|
+
}
|
|
29260
|
+
if (visibleAsked) {
|
|
29261
|
+
if (visibleRaw.trim() !== "1") {
|
|
29262
|
+
return {
|
|
29263
|
+
asked,
|
|
29264
|
+
enabled: false,
|
|
29265
|
+
list: false,
|
|
29266
|
+
visible: false,
|
|
29267
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV}=${visibleRaw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
29268
|
+
};
|
|
29269
|
+
}
|
|
29270
|
+
if (!listAsked) {
|
|
29271
|
+
return {
|
|
29272
|
+
asked,
|
|
29273
|
+
enabled: false,
|
|
29274
|
+
list: false,
|
|
29275
|
+
visible: false,
|
|
29276
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} needs ${ADDRESS_LIST_ENV}=1: S3's visible probe shows S2's mirror, never a bare S1 child`
|
|
29277
|
+
};
|
|
29278
|
+
}
|
|
29279
|
+
if (input.windowMode === "headless") {
|
|
29280
|
+
return {
|
|
29281
|
+
asked,
|
|
29282
|
+
enabled: false,
|
|
29283
|
+
list: false,
|
|
29284
|
+
visible: false,
|
|
29285
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a "headless" window: a headless run labelled visible would misattribute its evidence; use --window-mode=inactive`
|
|
29286
|
+
};
|
|
29287
|
+
}
|
|
29288
|
+
if (input.windowMode !== "inactive") {
|
|
29289
|
+
return {
|
|
29290
|
+
asked,
|
|
29291
|
+
enabled: false,
|
|
29292
|
+
list: false,
|
|
29293
|
+
visible: false,
|
|
29294
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a "${input.windowMode}" window: a visible probe never runs in the product's own mode; use --window-mode=inactive`
|
|
29295
|
+
};
|
|
29296
|
+
}
|
|
29297
|
+
if (!input.devDriverArmed) {
|
|
29298
|
+
return {
|
|
29299
|
+
asked,
|
|
29300
|
+
enabled: false,
|
|
29301
|
+
list: false,
|
|
29302
|
+
visible: false,
|
|
29303
|
+
reason: "the visible address overlay needs the armed renderer dev driver; it has no product path"
|
|
29304
|
+
};
|
|
29305
|
+
}
|
|
29306
|
+
if (input.packaged === true) {
|
|
29307
|
+
return {
|
|
29308
|
+
asked,
|
|
29309
|
+
enabled: false,
|
|
29310
|
+
list: false,
|
|
29311
|
+
visible: false,
|
|
29312
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a packaged build: S3 is an unpackaged probe, never product surface`
|
|
29313
|
+
};
|
|
29314
|
+
}
|
|
29315
|
+
return {
|
|
29316
|
+
asked,
|
|
29317
|
+
enabled: true,
|
|
29318
|
+
list: true,
|
|
29319
|
+
visible: true,
|
|
29320
|
+
reason: null
|
|
29321
|
+
};
|
|
29322
|
+
}
|
|
28789
29323
|
if (!input.devDriverArmed) {
|
|
28790
29324
|
return {
|
|
28791
29325
|
asked,
|
|
28792
29326
|
enabled: false,
|
|
29327
|
+
list: false,
|
|
29328
|
+
visible: false,
|
|
28793
29329
|
reason: "the address overlay needs the armed renderer dev driver; it has no user-facing trigger in S1"
|
|
28794
29330
|
};
|
|
28795
29331
|
}
|
|
@@ -28797,10 +29333,18 @@ function resolveAddressOverlayGate(input) {
|
|
|
28797
29333
|
return {
|
|
28798
29334
|
asked,
|
|
28799
29335
|
enabled: false,
|
|
29336
|
+
list: false,
|
|
29337
|
+
visible: false,
|
|
28800
29338
|
reason: `the address overlay refused to arm in a "${input.windowMode}" window; S1 has no user-facing path, so use --window-mode=headless`
|
|
28801
29339
|
};
|
|
28802
29340
|
}
|
|
28803
|
-
return {
|
|
29341
|
+
return {
|
|
29342
|
+
asked,
|
|
29343
|
+
enabled: true,
|
|
29344
|
+
list: listAsked,
|
|
29345
|
+
visible: false,
|
|
29346
|
+
reason: null
|
|
29347
|
+
};
|
|
28804
29348
|
}
|
|
28805
29349
|
function int32(value) {
|
|
28806
29350
|
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
@@ -28844,11 +29388,15 @@ function copyLayout(snapshot2) {
|
|
|
28844
29388
|
};
|
|
28845
29389
|
}
|
|
28846
29390
|
function layoutKeyOf(snapshot2) {
|
|
28847
|
-
const
|
|
28848
|
-
return `${snapshot2.activeTabId}:${snapshot2.presented ? 1 : 0}:${
|
|
29391
|
+
const rect2 = snapshot2.contentRect;
|
|
29392
|
+
return `${snapshot2.activeTabId}:${snapshot2.presented ? 1 : 0}:${rect2 ? `${rect2.x},${rect2.y},${rect2.width},${rect2.height}` : "null"}`;
|
|
29393
|
+
}
|
|
29394
|
+
function sameRect(a, b) {
|
|
29395
|
+
if (b === null) return false;
|
|
29396
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
28849
29397
|
}
|
|
28850
29398
|
const OVERLAY_BACKGROUND = "#1b1f24";
|
|
28851
|
-
function createHardenedView() {
|
|
29399
|
+
function createHardenedView(options) {
|
|
28852
29400
|
const partition = `address-overlay-${node_crypto.randomUUID()}`;
|
|
28853
29401
|
const childSession = electron.session.fromPartition(partition);
|
|
28854
29402
|
childSession.setPermissionRequestHandler(
|
|
@@ -28868,10 +29416,15 @@ function createHardenedView() {
|
|
|
28868
29416
|
nodeIntegration: false,
|
|
28869
29417
|
webSecurity: true,
|
|
28870
29418
|
allowRunningInsecureContent: false,
|
|
28871
|
-
// No `preload`, and no
|
|
28872
|
-
//
|
|
28873
|
-
|
|
28874
|
-
|
|
29419
|
+
// No `preload`, and no app imports in either document. The flag is the
|
|
29420
|
+
// S1/S2 split: false for the inert document, true for the mirror list.
|
|
29421
|
+
javascript: options.javascript,
|
|
29422
|
+
// The mirror is a tiny document that must stay frame-accurate while the
|
|
29423
|
+
// owner window is hidden, unfocused or occluded (headless probe, a
|
|
29424
|
+
// background app window). Chromium otherwise throttles its frame loop,
|
|
29425
|
+
// and a native resize can sit unapplied for ~1 s — the exact interval
|
|
29426
|
+
// `measure_viewport` is there to catch. S1 keeps the default.
|
|
29427
|
+
...options.javascript ? { backgroundThrottling: false } : {}
|
|
28875
29428
|
}
|
|
28876
29429
|
});
|
|
28877
29430
|
view.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
|
|
@@ -28882,6 +29435,8 @@ class AddressOverlayController {
|
|
|
28882
29435
|
window;
|
|
28883
29436
|
documentUrl;
|
|
28884
29437
|
createView;
|
|
29438
|
+
javascript;
|
|
29439
|
+
onEvent;
|
|
28885
29440
|
readinessTimeoutMs;
|
|
28886
29441
|
log;
|
|
28887
29442
|
now;
|
|
@@ -28909,10 +29464,42 @@ class AddressOverlayController {
|
|
|
28909
29464
|
disposed;
|
|
28910
29465
|
ownerListeners;
|
|
28911
29466
|
childListeners;
|
|
29467
|
+
// ---- S2 mirror state ----------------------------------------------------
|
|
29468
|
+
lastSeq;
|
|
29469
|
+
shownSeq;
|
|
29470
|
+
lastProjection;
|
|
29471
|
+
/** The rectangle last applied for a projection. `onLayout`'s unchanged-key
|
|
29472
|
+
* re-assert uses THIS, never the S1 top-strip rule: an S2 child sits at the
|
|
29473
|
+
* dock, and re-raising it with the S1 rectangle would move the list. */
|
|
29474
|
+
lastAppliedBounds;
|
|
29475
|
+
lastMeasurement;
|
|
29476
|
+
themeFact;
|
|
29477
|
+
applyCount;
|
|
29478
|
+
commitsEmitted;
|
|
29479
|
+
commitsDropped;
|
|
29480
|
+
hideCounts;
|
|
29481
|
+
keysBlocked;
|
|
29482
|
+
mouseDowns;
|
|
29483
|
+
mouseUps;
|
|
29484
|
+
mouseMoves;
|
|
29485
|
+
mouseDownRow;
|
|
29486
|
+
contextMenuBlocked;
|
|
29487
|
+
// ---- S3 focus facts -----------------------------------------------------
|
|
29488
|
+
/** Counted only for the scripted (S2/S3) child, so S1's listener set stays
|
|
29489
|
+
* exactly what it was; see `wireChild`'s S2 policy comment. */
|
|
29490
|
+
childFocusEvents;
|
|
29491
|
+
childBlurEvents;
|
|
29492
|
+
/** The launch gate's `visible` fact, copied in so `facts()` can report it
|
|
29493
|
+
* without re-deriving the gate (which it must never do: one pure decision). */
|
|
29494
|
+
visibleMode;
|
|
29495
|
+
lastRejected;
|
|
29496
|
+
projectQueue;
|
|
28912
29497
|
constructor(deps) {
|
|
28913
29498
|
this.window = deps.window;
|
|
28914
29499
|
this.documentUrl = deps.documentUrl;
|
|
28915
|
-
this.createView = deps.createView ?? createHardenedView;
|
|
29500
|
+
this.createView = deps.createView ?? (() => createHardenedView({ javascript: deps.javascript === true }));
|
|
29501
|
+
this.javascript = deps.javascript === true;
|
|
29502
|
+
this.onEvent = deps.onEvent ?? null;
|
|
28916
29503
|
this.readinessTimeoutMs = deps.readinessTimeoutMs ?? ADDRESS_OVERLAY_READY_TIMEOUT_MS;
|
|
28917
29504
|
this.log = deps.log ?? (() => {
|
|
28918
29505
|
});
|
|
@@ -28946,6 +29533,27 @@ class AddressOverlayController {
|
|
|
28946
29533
|
this.disposed = false;
|
|
28947
29534
|
this.ownerListeners = [];
|
|
28948
29535
|
this.childListeners = [];
|
|
29536
|
+
this.lastSeq = 0;
|
|
29537
|
+
this.shownSeq = -1;
|
|
29538
|
+
this.lastProjection = null;
|
|
29539
|
+
this.lastAppliedBounds = null;
|
|
29540
|
+
this.lastMeasurement = null;
|
|
29541
|
+
this.themeFact = "absent";
|
|
29542
|
+
this.applyCount = 0;
|
|
29543
|
+
this.commitsEmitted = 0;
|
|
29544
|
+
this.commitsDropped = /* @__PURE__ */ new Map();
|
|
29545
|
+
this.hideCounts = /* @__PURE__ */ new Map();
|
|
29546
|
+
this.keysBlocked = 0;
|
|
29547
|
+
this.mouseDowns = 0;
|
|
29548
|
+
this.mouseUps = 0;
|
|
29549
|
+
this.mouseMoves = 0;
|
|
29550
|
+
this.mouseDownRow = null;
|
|
29551
|
+
this.contextMenuBlocked = 0;
|
|
29552
|
+
this.childFocusEvents = 0;
|
|
29553
|
+
this.childBlurEvents = 0;
|
|
29554
|
+
this.visibleMode = deps.visible === true;
|
|
29555
|
+
this.lastRejected = null;
|
|
29556
|
+
this.projectQueue = Promise.resolve();
|
|
28949
29557
|
this.attachOwnerListeners();
|
|
28950
29558
|
}
|
|
28951
29559
|
// ---- observer hooks ------------------------------------------------------
|
|
@@ -28975,7 +29583,7 @@ class AddressOverlayController {
|
|
|
28975
29583
|
if (this.state !== "shown") return;
|
|
28976
29584
|
const view = this.view;
|
|
28977
29585
|
if (!view || this.viewUsable(view) === false) return;
|
|
28978
|
-
const bounds = overlayBoundsFor(next.contentRect, this.ownerSize());
|
|
29586
|
+
const bounds = this.lastProjection !== null ? this.lastAppliedBounds : overlayBoundsFor(next.contentRect, this.ownerSize());
|
|
28979
29587
|
if (bounds === null || !next.presented) {
|
|
28980
29588
|
this.hide("layout_invalid");
|
|
28981
29589
|
return;
|
|
@@ -29022,6 +29630,10 @@ class AddressOverlayController {
|
|
|
29022
29630
|
this.generation += 1;
|
|
29023
29631
|
this.settleReady(false);
|
|
29024
29632
|
this.hiddenReason = reason;
|
|
29633
|
+
this.hideCounts.set(reason, (this.hideCounts.get(reason) ?? 0) + 1);
|
|
29634
|
+
this.shownSeq = -1;
|
|
29635
|
+
this.mouseDownRow = null;
|
|
29636
|
+
this.lastMeasurement = null;
|
|
29025
29637
|
const view = this.view;
|
|
29026
29638
|
if (view && this.viewUsable(view)) {
|
|
29027
29639
|
try {
|
|
@@ -29034,6 +29646,14 @@ class AddressOverlayController {
|
|
|
29034
29646
|
}
|
|
29035
29647
|
this.log(`[browser] address overlay hidden: ${reason}`);
|
|
29036
29648
|
}
|
|
29649
|
+
/** `WebContents.isFocused()` behind a guard: a disposed view throws. */
|
|
29650
|
+
childIsFocused(view) {
|
|
29651
|
+
try {
|
|
29652
|
+
return view.webContents.isFocused();
|
|
29653
|
+
} catch {
|
|
29654
|
+
return false;
|
|
29655
|
+
}
|
|
29656
|
+
}
|
|
29037
29657
|
/** Facts only: identifiers, geometry and counters. No page content. */
|
|
29038
29658
|
facts() {
|
|
29039
29659
|
const view = this.view;
|
|
@@ -29056,7 +29676,10 @@ class AddressOverlayController {
|
|
|
29056
29676
|
visible,
|
|
29057
29677
|
bounds,
|
|
29058
29678
|
order,
|
|
29059
|
-
childCount
|
|
29679
|
+
childCount,
|
|
29680
|
+
focusEvents: this.childFocusEvents,
|
|
29681
|
+
blurEvents: this.childBlurEvents,
|
|
29682
|
+
isFocused: this.childIsFocused(view)
|
|
29060
29683
|
};
|
|
29061
29684
|
}
|
|
29062
29685
|
let ownerDestroyed = true;
|
|
@@ -29067,6 +29690,18 @@ class AddressOverlayController {
|
|
|
29067
29690
|
} catch {
|
|
29068
29691
|
ownerDestroyed = true;
|
|
29069
29692
|
}
|
|
29693
|
+
let ownerWindowFocused = false;
|
|
29694
|
+
try {
|
|
29695
|
+
ownerWindowFocused = !ownerDestroyed && this.window.isFocused();
|
|
29696
|
+
} catch {
|
|
29697
|
+
ownerWindowFocused = false;
|
|
29698
|
+
}
|
|
29699
|
+
let ownerFocusedContentsId = null;
|
|
29700
|
+
try {
|
|
29701
|
+
ownerFocusedContentsId = electron.webContents.getFocusedWebContents()?.id ?? null;
|
|
29702
|
+
} catch {
|
|
29703
|
+
ownerFocusedContentsId = null;
|
|
29704
|
+
}
|
|
29070
29705
|
return {
|
|
29071
29706
|
documentUrl: this.documentUrl,
|
|
29072
29707
|
state: this.state,
|
|
@@ -29094,10 +29729,36 @@ class AddressOverlayController {
|
|
|
29094
29729
|
},
|
|
29095
29730
|
pendingShow: this.showInFlight !== null,
|
|
29096
29731
|
readinessTimer: this.readinessTimer !== null,
|
|
29732
|
+
visibleMode: this.visibleMode,
|
|
29733
|
+
ownerFocusedContentsId,
|
|
29734
|
+
ownerWindowFocused,
|
|
29735
|
+
s2: {
|
|
29736
|
+
list: this.javascript,
|
|
29737
|
+
javascript: this.javascript,
|
|
29738
|
+
lastSeq: this.lastSeq,
|
|
29739
|
+
shownSeq: this.shownSeq,
|
|
29740
|
+
lastRejected: this.lastRejected,
|
|
29741
|
+
theme: this.themeFact,
|
|
29742
|
+
applyCount: this.applyCount,
|
|
29743
|
+
view: this.lastProjection === null ? { kind: "none", rowCount: 0, active: -1 } : {
|
|
29744
|
+
kind: this.lastProjection.view.kind,
|
|
29745
|
+
rowCount: this.lastProjection.view.kind === "rows" ? this.lastProjection.view.rows.length : 0,
|
|
29746
|
+
active: this.lastProjection.active
|
|
29747
|
+
},
|
|
29748
|
+
measured: this.lastMeasurement,
|
|
29749
|
+
commitsEmitted: this.commitsEmitted,
|
|
29750
|
+
commitsDropped: Object.fromEntries(this.commitsDropped),
|
|
29751
|
+
hideCounts: Object.fromEntries(this.hideCounts),
|
|
29752
|
+
keysBlocked: this.keysBlocked,
|
|
29753
|
+
mouseDowns: this.mouseDowns,
|
|
29754
|
+
mouseUps: this.mouseUps,
|
|
29755
|
+
mouseMoves: this.mouseMoves,
|
|
29756
|
+
contextMenuBlocked: this.contextMenuBlocked
|
|
29757
|
+
},
|
|
29097
29758
|
metrics: this.appMetrics ? this.appMetrics() : null
|
|
29098
29759
|
};
|
|
29099
29760
|
}
|
|
29100
|
-
async probe(action) {
|
|
29761
|
+
async probe(action, argument) {
|
|
29101
29762
|
if (action === "show") {
|
|
29102
29763
|
await this.showOverlay();
|
|
29103
29764
|
return this.facts();
|
|
@@ -29106,8 +29767,278 @@ class AddressOverlayController {
|
|
|
29106
29767
|
this.hide("probe");
|
|
29107
29768
|
return this.facts();
|
|
29108
29769
|
}
|
|
29770
|
+
if (action === "click") {
|
|
29771
|
+
this.probeClick(argument);
|
|
29772
|
+
await new Promise((done) => setTimeout(done, 40));
|
|
29773
|
+
return this.facts();
|
|
29774
|
+
}
|
|
29775
|
+
if (action === "measure") {
|
|
29776
|
+
await this.probeMeasure();
|
|
29777
|
+
return this.facts();
|
|
29778
|
+
}
|
|
29779
|
+
if (action === "key") {
|
|
29780
|
+
this.probeKey(argument);
|
|
29781
|
+
await new Promise((done) => setTimeout(done, 20));
|
|
29782
|
+
return this.facts();
|
|
29783
|
+
}
|
|
29109
29784
|
return this.facts();
|
|
29110
29785
|
}
|
|
29786
|
+
/**
|
|
29787
|
+
* Apply one renderer projection (S2). Everything arriving here was or will be
|
|
29788
|
+
* revalidated: JSON size, shape, every string, tab/layout freshness, zoom,
|
|
29789
|
+
* anchor geometry and — after the child paints — the child's own measurements.
|
|
29790
|
+
*
|
|
29791
|
+
* Serialized: a per-frame renderer can outpace `executeJavaScript`, and two
|
|
29792
|
+
* overlapping applies would let an older model win. The queue is per
|
|
29793
|
+
* controller, so a burst between frames costs one apply each in arrival order.
|
|
29794
|
+
*/
|
|
29795
|
+
project(raw) {
|
|
29796
|
+
const run = () => this.projectOnce(raw);
|
|
29797
|
+
const next = this.projectQueue.then(run, run);
|
|
29798
|
+
this.projectQueue = next.then(
|
|
29799
|
+
() => void 0,
|
|
29800
|
+
() => void 0
|
|
29801
|
+
);
|
|
29802
|
+
return next;
|
|
29803
|
+
}
|
|
29804
|
+
async projectOnce(raw) {
|
|
29805
|
+
const parsed = sanitizeProjection(raw);
|
|
29806
|
+
if (!parsed.ok) {
|
|
29807
|
+
this.drop(parsed.reason);
|
|
29808
|
+
return {
|
|
29809
|
+
accepted: false,
|
|
29810
|
+
shown: false,
|
|
29811
|
+
reason: parsed.reason,
|
|
29812
|
+
bounds: null
|
|
29813
|
+
};
|
|
29814
|
+
}
|
|
29815
|
+
const projection = parsed.projection;
|
|
29816
|
+
this.themeFact = projection.themeFact;
|
|
29817
|
+
if (!projection.visible) {
|
|
29818
|
+
if (projection.seq > this.lastSeq) this.lastSeq = projection.seq;
|
|
29819
|
+
this.hide("projection_hidden");
|
|
29820
|
+
return { accepted: true, shown: false, reason: null, bounds: null };
|
|
29821
|
+
}
|
|
29822
|
+
if (this.disposed) {
|
|
29823
|
+
return this.refuseProjection("disposed");
|
|
29824
|
+
}
|
|
29825
|
+
if (projection.seq < this.lastSeq) {
|
|
29826
|
+
this.drop("stale_seq");
|
|
29827
|
+
return {
|
|
29828
|
+
accepted: false,
|
|
29829
|
+
shown: this.state === "shown",
|
|
29830
|
+
reason: "stale_seq",
|
|
29831
|
+
bounds: null
|
|
29832
|
+
};
|
|
29833
|
+
}
|
|
29834
|
+
if (projection.seq === this.lastSeq && projection.seq === this.shownSeq) {
|
|
29835
|
+
return {
|
|
29836
|
+
accepted: true,
|
|
29837
|
+
shown: this.state === "shown",
|
|
29838
|
+
reason: null,
|
|
29839
|
+
bounds: this.view && this.viewUsable(this.view) ? this.view.getBounds() : null
|
|
29840
|
+
};
|
|
29841
|
+
}
|
|
29842
|
+
if (this.failedReason) return this.refuseProjection("overlay_failed");
|
|
29843
|
+
if (this.ownerUnavailable()) {
|
|
29844
|
+
this.releaseChild("owner_unavailable");
|
|
29845
|
+
return this.refuseProjection("owner_unavailable");
|
|
29846
|
+
}
|
|
29847
|
+
const zoom = this.ownerZoom();
|
|
29848
|
+
if (zoom === null || Math.abs(zoom - 1) > 1e-3) {
|
|
29849
|
+
return this.refuseProjection("zoomed");
|
|
29850
|
+
}
|
|
29851
|
+
const layout = this.layout;
|
|
29852
|
+
if (projection.tabId !== layout.activeTabId) {
|
|
29853
|
+
return this.refuseProjection("stale_tab");
|
|
29854
|
+
}
|
|
29855
|
+
if (!layout.presented || !sameRect(projection.pageRect, layout.contentRect)) {
|
|
29856
|
+
return this.refuseProjection("stale_layout");
|
|
29857
|
+
}
|
|
29858
|
+
const bounds = addressListBoundsFor(
|
|
29859
|
+
projection.field,
|
|
29860
|
+
projection.listTop,
|
|
29861
|
+
projection.view,
|
|
29862
|
+
this.ownerSize()
|
|
29863
|
+
);
|
|
29864
|
+
if (bounds === null) return this.refuseProjection("anchor_inconsistent");
|
|
29865
|
+
this.generation += 1;
|
|
29866
|
+
const generation = this.generation;
|
|
29867
|
+
this.boundTabId = projection.tabId;
|
|
29868
|
+
if (!this.view) {
|
|
29869
|
+
const created = this.createChild(bounds);
|
|
29870
|
+
if (!created) {
|
|
29871
|
+
return this.refuseProjection(
|
|
29872
|
+
this.failedReason ?? "child_create_failed"
|
|
29873
|
+
);
|
|
29874
|
+
}
|
|
29875
|
+
}
|
|
29876
|
+
if (!(this.domReady && this.finished)) {
|
|
29877
|
+
this.state = "loading";
|
|
29878
|
+
const ready = await this.waitForReady();
|
|
29879
|
+
if (!ready)
|
|
29880
|
+
return this.refuseProjection(this.failedReason ?? "invalidated");
|
|
29881
|
+
}
|
|
29882
|
+
if (this.disposed || generation !== this.generation) {
|
|
29883
|
+
return this.refuseProjection("invalidated");
|
|
29884
|
+
}
|
|
29885
|
+
const view = this.view;
|
|
29886
|
+
if (!view || !this.viewUsable(view)) {
|
|
29887
|
+
return this.refuseProjection("child_unavailable");
|
|
29888
|
+
}
|
|
29889
|
+
view.setBounds(bounds);
|
|
29890
|
+
this.raiseChild(view);
|
|
29891
|
+
view.setVisible(true);
|
|
29892
|
+
const model = addressListViewModel(projection);
|
|
29893
|
+
let measured = null;
|
|
29894
|
+
let problem = null;
|
|
29895
|
+
try {
|
|
29896
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
29897
|
+
if (attempt % 2 === 1) view.setBounds(bounds);
|
|
29898
|
+
measured = await view.webContents.executeJavaScript(
|
|
29899
|
+
`window.__loAddressOverlay && window.__loAddressOverlay.apply(${JSON.stringify(model)})`,
|
|
29900
|
+
false
|
|
29901
|
+
);
|
|
29902
|
+
problem = addressListMeasurementProblem(
|
|
29903
|
+
measured,
|
|
29904
|
+
projection,
|
|
29905
|
+
bounds.height
|
|
29906
|
+
);
|
|
29907
|
+
if (problem === null) break;
|
|
29908
|
+
if (problem !== "measure_scroll" && problem !== "measure_viewport")
|
|
29909
|
+
break;
|
|
29910
|
+
await new Promise((done) => setTimeout(done, 150));
|
|
29911
|
+
}
|
|
29912
|
+
} catch {
|
|
29913
|
+
return this.refuseProjection("apply_error");
|
|
29914
|
+
}
|
|
29915
|
+
if (problem !== null) return this.refuseProjection(problem, bounds);
|
|
29916
|
+
this.applyCount += 1;
|
|
29917
|
+
this.lastSeq = projection.seq;
|
|
29918
|
+
this.shownSeq = projection.seq;
|
|
29919
|
+
this.lastProjection = projection;
|
|
29920
|
+
this.lastAppliedBounds = bounds;
|
|
29921
|
+
this.lastMeasurement = measured;
|
|
29922
|
+
this.lastRejected = null;
|
|
29923
|
+
this.state = "shown";
|
|
29924
|
+
this.hiddenReason = null;
|
|
29925
|
+
return { accepted: true, shown: true, reason: null, bounds };
|
|
29926
|
+
}
|
|
29927
|
+
refuseProjection(reason, bounds = null) {
|
|
29928
|
+
this.lastRejected = reason;
|
|
29929
|
+
this.drop(reason);
|
|
29930
|
+
this.hide(reason);
|
|
29931
|
+
return { accepted: false, shown: false, reason, bounds };
|
|
29932
|
+
}
|
|
29933
|
+
drop(reason) {
|
|
29934
|
+
this.commitsDropped.set(reason, (this.commitsDropped.get(reason) ?? 0) + 1);
|
|
29935
|
+
}
|
|
29936
|
+
ownerZoom() {
|
|
29937
|
+
try {
|
|
29938
|
+
const zoom = this.window.webContents.getZoomFactor();
|
|
29939
|
+
return typeof zoom === "number" && Number.isFinite(zoom) ? zoom : null;
|
|
29940
|
+
} catch {
|
|
29941
|
+
return null;
|
|
29942
|
+
}
|
|
29943
|
+
}
|
|
29944
|
+
rowAt(y) {
|
|
29945
|
+
if (this.state !== "shown") return null;
|
|
29946
|
+
if (this.lastProjection === null) return null;
|
|
29947
|
+
if (this.shownSeq !== this.lastSeq || this.shownSeq < 1) return null;
|
|
29948
|
+
const projection = this.lastProjection;
|
|
29949
|
+
if (projection.view.kind !== "rows") return null;
|
|
29950
|
+
const index = addressListRowIndexAt(projection.view, y);
|
|
29951
|
+
if (index === null || index >= projection.view.rows.length) return null;
|
|
29952
|
+
return index;
|
|
29953
|
+
}
|
|
29954
|
+
emitCommit(index) {
|
|
29955
|
+
const projection = this.lastProjection;
|
|
29956
|
+
const sink = this.onEvent;
|
|
29957
|
+
if (projection === null || sink === null) {
|
|
29958
|
+
this.drop("no_sink");
|
|
29959
|
+
return;
|
|
29960
|
+
}
|
|
29961
|
+
if (projection.view.kind !== "rows" || index >= projection.view.rows.length) {
|
|
29962
|
+
this.drop("commit_index");
|
|
29963
|
+
return;
|
|
29964
|
+
}
|
|
29965
|
+
this.commitsEmitted += 1;
|
|
29966
|
+
sink({ type: "commit", seq: projection.seq, index });
|
|
29967
|
+
this.log(
|
|
29968
|
+
`[browser] address overlay commit emitted: seq=${projection.seq} index=${index}`
|
|
29969
|
+
);
|
|
29970
|
+
}
|
|
29971
|
+
/**
|
|
29972
|
+
* Probe-only mouse path: `sendInputEvent` into the child's own input pipeline.
|
|
29973
|
+
* This exercises the child and the before-mouse-event observer; it does NOT
|
|
29974
|
+
* exercise macOS NSView hit-testing between sibling views (matrix V3).
|
|
29975
|
+
*/
|
|
29976
|
+
probeClick(argument) {
|
|
29977
|
+
const view = this.view;
|
|
29978
|
+
const measurement = this.lastMeasurement;
|
|
29979
|
+
if (!view || !this.viewUsable(view) || !measurement) {
|
|
29980
|
+
throw new Error("address_overlay_click: no applied list to click");
|
|
29981
|
+
}
|
|
29982
|
+
const arg = argument ?? {};
|
|
29983
|
+
const row = arg.row;
|
|
29984
|
+
const upRow = arg.upRow === void 0 ? row : arg.upRow;
|
|
29985
|
+
const rows = measurement.rowRects;
|
|
29986
|
+
if (typeof row !== "number" || !Number.isInteger(row) || !rows[row]) {
|
|
29987
|
+
throw new Error(
|
|
29988
|
+
"address_overlay_click: row must be a rendered row index"
|
|
29989
|
+
);
|
|
29990
|
+
}
|
|
29991
|
+
if (typeof upRow !== "number" || !Number.isInteger(upRow)) {
|
|
29992
|
+
throw new Error("address_overlay_click: upRow must be an integer");
|
|
29993
|
+
}
|
|
29994
|
+
const point = (index) => {
|
|
29995
|
+
const rect2 = rows[index];
|
|
29996
|
+
const fallbackY = ADDRESS_LIST_HEADER_HEIGHT + 2 + (index + 0.5) * ADDRESS_LIST_ROW_HEIGHT;
|
|
29997
|
+
return rect2 ? {
|
|
29998
|
+
x: 40,
|
|
29999
|
+
y: ADDRESS_LIST_HEADER_HEIGHT + 2 + rect2.top + rect2.height / 2
|
|
30000
|
+
} : { x: 40, y: fallbackY };
|
|
30001
|
+
};
|
|
30002
|
+
const receive = (type2, index) => {
|
|
30003
|
+
const { x, y } = point(index);
|
|
30004
|
+
view.webContents.sendInputEvent({
|
|
30005
|
+
type: type2,
|
|
30006
|
+
x: Math.round(x),
|
|
30007
|
+
y: Math.round(y),
|
|
30008
|
+
button: "left",
|
|
30009
|
+
clickCount: 1
|
|
30010
|
+
});
|
|
30011
|
+
};
|
|
30012
|
+
receive("mouseMove", row);
|
|
30013
|
+
receive("mouseDown", row);
|
|
30014
|
+
receive("mouseUp", upRow);
|
|
30015
|
+
}
|
|
30016
|
+
/**
|
|
30017
|
+
* Probe-only key into the child's own input pipeline. It is how the gated
|
|
30018
|
+
* harness proves `before-input-event` swallows keys; CDP-injected keys go
|
|
30019
|
+
* through a different path and were measured NOT to fire it.
|
|
30020
|
+
*/
|
|
30021
|
+
probeKey(argument) {
|
|
30022
|
+
const view = this.view;
|
|
30023
|
+
if (!view || !this.viewUsable(view)) {
|
|
30024
|
+
throw new Error("address_overlay_key: no child");
|
|
30025
|
+
}
|
|
30026
|
+
const arg = argument ?? {};
|
|
30027
|
+
const keyCode = typeof arg.keyCode === "string" && arg.keyCode.length > 0 ? arg.keyCode.slice(0, 32) : "A";
|
|
30028
|
+
view.webContents.sendInputEvent({ type: "keyDown", keyCode });
|
|
30029
|
+
view.webContents.sendInputEvent({ type: "keyUp", keyCode });
|
|
30030
|
+
}
|
|
30031
|
+
async probeMeasure() {
|
|
30032
|
+
const view = this.view;
|
|
30033
|
+
if (!view || !this.viewUsable(view)) {
|
|
30034
|
+
throw new Error("address_overlay_measure: no child");
|
|
30035
|
+
}
|
|
30036
|
+
const measured = await view.webContents.executeJavaScript(
|
|
30037
|
+
"window.__loAddressOverlay && window.__loAddressOverlay.measure()",
|
|
30038
|
+
false
|
|
30039
|
+
);
|
|
30040
|
+
this.lastMeasurement = measured;
|
|
30041
|
+
}
|
|
29111
30042
|
/**
|
|
29112
30043
|
* Idempotent teardown for the host's stop/rollback/window-close paths.
|
|
29113
30044
|
* Closing the webContents before detaching is required: removing a
|
|
@@ -29319,6 +30250,54 @@ class AddressOverlayController {
|
|
|
29319
30250
|
event?.preventDefault?.();
|
|
29320
30251
|
this.log(`[browser] address overlay refused redirect to ${url}`);
|
|
29321
30252
|
});
|
|
30253
|
+
if (!this.javascript) return;
|
|
30254
|
+
on(contents, "before-input-event", (...args) => {
|
|
30255
|
+
const event = args[0];
|
|
30256
|
+
event?.preventDefault?.();
|
|
30257
|
+
this.keysBlocked += 1;
|
|
30258
|
+
});
|
|
30259
|
+
on(contents, "context-menu", (...args) => {
|
|
30260
|
+
const event = args[0];
|
|
30261
|
+
event?.preventDefault?.();
|
|
30262
|
+
this.contextMenuBlocked += 1;
|
|
30263
|
+
});
|
|
30264
|
+
on(contents, "focus", () => {
|
|
30265
|
+
this.childFocusEvents += 1;
|
|
30266
|
+
});
|
|
30267
|
+
on(contents, "blur", () => {
|
|
30268
|
+
this.childBlurEvents += 1;
|
|
30269
|
+
});
|
|
30270
|
+
on(contents, "before-mouse-event", (...args) => {
|
|
30271
|
+
const event = args[0];
|
|
30272
|
+
const mouse = args[1];
|
|
30273
|
+
if (!mouse || typeof mouse.x !== "number" || typeof mouse.y !== "number") {
|
|
30274
|
+
return;
|
|
30275
|
+
}
|
|
30276
|
+
if (mouse.type === "mouseMove") {
|
|
30277
|
+
this.mouseMoves += 1;
|
|
30278
|
+
return;
|
|
30279
|
+
}
|
|
30280
|
+
event?.preventDefault?.();
|
|
30281
|
+
if (mouse.type === "mouseDown") {
|
|
30282
|
+
this.mouseDowns += 1;
|
|
30283
|
+
this.mouseDownRow = this.rowAt(mouse.y);
|
|
30284
|
+
return;
|
|
30285
|
+
}
|
|
30286
|
+
if (mouse.type !== "mouseUp") return;
|
|
30287
|
+
this.mouseUps += 1;
|
|
30288
|
+
const row = this.rowAt(mouse.y);
|
|
30289
|
+
const downRow = this.mouseDownRow;
|
|
30290
|
+
this.mouseDownRow = null;
|
|
30291
|
+
if (downRow === null || row === null) {
|
|
30292
|
+
this.drop("mouse_no_row");
|
|
30293
|
+
return;
|
|
30294
|
+
}
|
|
30295
|
+
if (downRow !== row) {
|
|
30296
|
+
this.drop("mouse_mismatch");
|
|
30297
|
+
return;
|
|
30298
|
+
}
|
|
30299
|
+
this.emitCommit(row);
|
|
30300
|
+
});
|
|
29322
30301
|
}
|
|
29323
30302
|
/** Terminal for this host lifetime: a broken document must not be retried. */
|
|
29324
30303
|
failChild(reason) {
|
|
@@ -63799,6 +64778,13 @@ class TabRegistry {
|
|
|
63799
64778
|
nextTabId = 1;
|
|
63800
64779
|
activeTabId = null;
|
|
63801
64780
|
visible = true;
|
|
64781
|
+
/** WHY the view is hidden when it is: the renderer is painting one of the
|
|
64782
|
+
* browser's OWN state pages (start page, load failure), not an overlay that
|
|
64783
|
+
* covers the chrome band. Only shortcut ownership reads this. Presentation
|
|
64784
|
+
* reads `visible` alone, so a state page never makes a native view
|
|
64785
|
+
* "presented"; and every report overwrites it, so a reason cannot outlive the
|
|
64786
|
+
* hide it described. */
|
|
64787
|
+
ownStatePage = false;
|
|
63802
64788
|
contentRect = null;
|
|
63803
64789
|
/** Tabs with a command in flight, keyed by tabId. See `lane`. */
|
|
63804
64790
|
busy = /* @__PURE__ */ new Set();
|
|
@@ -63900,12 +64886,20 @@ class TabRegistry {
|
|
|
63900
64886
|
}
|
|
63901
64887
|
return null;
|
|
63902
64888
|
}
|
|
63903
|
-
/**
|
|
63904
|
-
*
|
|
63905
|
-
*
|
|
63906
|
-
*
|
|
64889
|
+
/** Whether the browser's chrome band is the presented UI and therefore owns
|
|
64890
|
+
* the native shortcut keys (Cmd+L/T/R and the rest).
|
|
64891
|
+
*
|
|
64892
|
+
* NOT the same fact as "the native view is visible". The browser's own state
|
|
64893
|
+
* pages - start page, load-failure panel - hide the view ON PURPOSE so their
|
|
64894
|
+
* DOM can paint (design 11.3), and they are still the browser the user is
|
|
64895
|
+
* looking at. Reading `visible` alone dropped every chord on those two states,
|
|
64896
|
+
* and on main's side the press was not even consumed, so the app menu's Reload
|
|
64897
|
+
* reloaded the whole window. Only an overlay hide, or an unmounted surface (no
|
|
64898
|
+
* rect), takes the chords away. The native window floor in
|
|
64899
|
+
* `browserChromeOwnsChord` is untouched: this says whose keys they are, never
|
|
64900
|
+
* that the window is foreground. */
|
|
63907
64901
|
get chromeVisible() {
|
|
63908
|
-
return this.
|
|
64902
|
+
return this.contentRect !== null && (this.visible || this.ownStatePage);
|
|
63909
64903
|
}
|
|
63910
64904
|
get activeTab() {
|
|
63911
64905
|
return this.activeTabId === null ? null : this.tabs.get(this.activeTabId) ?? null;
|
|
@@ -64082,14 +65076,22 @@ class TabRegistry {
|
|
|
64082
65076
|
* hijacked window. Visiting the route again reports a fresh rect and restores
|
|
64083
65077
|
* it. A null rect is also how the caller hides the view at teardown, so it must
|
|
64084
65078
|
* never be swallowed on the way here (see `use-browser-chrome`). */
|
|
64085
|
-
setContentRect(
|
|
64086
|
-
this.contentRect =
|
|
65079
|
+
setContentRect(rect2) {
|
|
65080
|
+
this.contentRect = rect2;
|
|
64087
65081
|
this.applyLayout();
|
|
64088
65082
|
}
|
|
64089
65083
|
/** Full-window overlays hide the view (design 11.3): a native view paints
|
|
64090
|
-
* above all DOM, so an overlay is invisible unless the view goes away.
|
|
64091
|
-
|
|
65084
|
+
* above all DOM, so an overlay is invisible unless the view goes away.
|
|
65085
|
+
*
|
|
65086
|
+
* `ownPage` separates the two reasons a hide can have, because they mean
|
|
65087
|
+
* opposite things for the KEYBOARD: an overlay (dialog, menu, picker) owns it,
|
|
65088
|
+
* while the browser's own start page / failure panel does not - that surface is
|
|
65089
|
+
* still the browser, and `chromeVisible` keeps its shortcuts. Normalised
|
|
65090
|
+
* against `visible` so the invariant held is "a reason only exists while the
|
|
65091
|
+
* view is hidden", never two facts that disagree. */
|
|
65092
|
+
setViewVisible(visible, ownPage = false) {
|
|
64092
65093
|
this.visible = visible;
|
|
65094
|
+
this.ownStatePage = !visible && ownPage;
|
|
64093
65095
|
this.applyLayout();
|
|
64094
65096
|
}
|
|
64095
65097
|
activate(tabId) {
|
|
@@ -64109,16 +65111,16 @@ class TabRegistry {
|
|
|
64109
65111
|
record.view.setVisible(record.presented);
|
|
64110
65112
|
}
|
|
64111
65113
|
this.layoutRevision += 1;
|
|
64112
|
-
const
|
|
65114
|
+
const rect2 = this.contentRect;
|
|
64113
65115
|
try {
|
|
64114
65116
|
this.onLayout({
|
|
64115
65117
|
activeTabId: this.activeTabId,
|
|
64116
65118
|
presented: active?.presented === true,
|
|
64117
|
-
contentRect:
|
|
64118
|
-
x:
|
|
64119
|
-
y:
|
|
64120
|
-
width:
|
|
64121
|
-
height:
|
|
65119
|
+
contentRect: rect2 ? {
|
|
65120
|
+
x: rect2.x,
|
|
65121
|
+
y: rect2.y,
|
|
65122
|
+
width: rect2.width,
|
|
65123
|
+
height: rect2.height
|
|
64122
65124
|
} : null,
|
|
64123
65125
|
revision: this.layoutRevision
|
|
64124
65126
|
});
|
|
@@ -66287,15 +67289,20 @@ class BrowserHost {
|
|
|
66287
67289
|
}
|
|
66288
67290
|
/** Layout authority is the renderer's (design 11.2): it measures the content
|
|
66289
67291
|
* area and reports it, and main applies it to the active tab only. */
|
|
66290
|
-
setContentRect(
|
|
66291
|
-
this.registry.setContentRect(
|
|
66292
|
-
if (
|
|
67292
|
+
setContentRect(rect2) {
|
|
67293
|
+
this.registry.setContentRect(rect2);
|
|
67294
|
+
if (rect2 === null) this.clearPageFocus();
|
|
66293
67295
|
return this.chromeState();
|
|
66294
67296
|
}
|
|
66295
67297
|
/** Full-window overlays hide the view: a native view paints above all DOM, so
|
|
66296
|
-
* an overlay is invisible unless the view goes away (design 11.3).
|
|
66297
|
-
|
|
66298
|
-
|
|
67298
|
+
* an overlay is invisible unless the view goes away (design 11.3).
|
|
67299
|
+
*
|
|
67300
|
+
* `ownPage` is the RENDERER'S REASON for the hide, and the only fact that lets
|
|
67301
|
+
* main tell the browser's own state pages (start page, load failure - hidden so
|
|
67302
|
+
* their DOM can paint) from an overlay covering them. Shortcut ownership keys
|
|
67303
|
+
* on it; presentation does not. See `TabRegistry.chromeVisible`. */
|
|
67304
|
+
setViewVisible(visible, ownPage = false) {
|
|
67305
|
+
this.registry.setViewVisible(visible, ownPage);
|
|
66299
67306
|
if (!visible) this.clearPageFocus();
|
|
66300
67307
|
return this.chromeState();
|
|
66301
67308
|
}
|
|
@@ -67219,58 +68226,6 @@ function restrictMode(path2, log2) {
|
|
|
67219
68226
|
);
|
|
67220
68227
|
}
|
|
67221
68228
|
}
|
|
67222
|
-
const DEV_DRIVER_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER";
|
|
67223
|
-
const DEV_DRIVER_OUT_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER_OUT";
|
|
67224
|
-
const DEV_DRIVER_ARG = "--lo-dev-driver";
|
|
67225
|
-
const DEV_DRIVER_CAPTURE = "dev-driver-capture";
|
|
67226
|
-
const DEV_DRIVER_FACTS = "dev-driver-facts";
|
|
67227
|
-
const DEV_DRIVER_ADDRESS_OVERLAY = "dev-driver-address-overlay";
|
|
67228
|
-
const TRUTHY = /* @__PURE__ */ new Set(["1", "true"]);
|
|
67229
|
-
function readOptIn(env) {
|
|
67230
|
-
const raw = env[DEV_DRIVER_ENV];
|
|
67231
|
-
if (raw === void 0 || raw.trim() === "")
|
|
67232
|
-
return { asked: false, value: null };
|
|
67233
|
-
return { asked: true, value: raw.trim() };
|
|
67234
|
-
}
|
|
67235
|
-
function resolveDevDriverArming(input) {
|
|
67236
|
-
const off = (problems = []) => ({
|
|
67237
|
-
armed: false,
|
|
67238
|
-
outDir: null,
|
|
67239
|
-
problems
|
|
67240
|
-
});
|
|
67241
|
-
const optIn = readOptIn(input.env);
|
|
67242
|
-
if (!optIn.asked) return off();
|
|
67243
|
-
if (!TRUTHY.has(optIn.value.toLowerCase())) {
|
|
67244
|
-
return off([
|
|
67245
|
-
`${DEV_DRIVER_ENV}=${optIn.value} is not an opt-in (accepted: 1, true); the renderer dev driver stayed off`
|
|
67246
|
-
]);
|
|
67247
|
-
}
|
|
67248
|
-
if (input.windowMode !== "headless" && input.windowMode !== "inactive") {
|
|
67249
|
-
return off([
|
|
67250
|
-
`the renderer dev driver refused to arm in a "${input.windowMode}" window; it never raises one, so use --window-mode=headless (or inactive)`
|
|
67251
|
-
]);
|
|
67252
|
-
}
|
|
67253
|
-
const outDir = input.env[DEV_DRIVER_OUT_ENV]?.trim() ?? "";
|
|
67254
|
-
if (outDir === "" || !outDir.startsWith("/")) {
|
|
67255
|
-
return off([
|
|
67256
|
-
`${DEV_DRIVER_ENV} was set but ${DEV_DRIVER_OUT_ENV} is ${outDir === "" ? "missing" : `"${outDir}"`}; an absolute frames directory is required, so the driver did not arm`
|
|
67257
|
-
]);
|
|
67258
|
-
}
|
|
67259
|
-
return { armed: true, outDir, problems: [] };
|
|
67260
|
-
}
|
|
67261
|
-
function headlessExerciserAllowed(input) {
|
|
67262
|
-
return input.arming.armed && input.windowMode === "headless";
|
|
67263
|
-
}
|
|
67264
|
-
function devDriverArgument(outDir) {
|
|
67265
|
-
return `${DEV_DRIVER_ARG}=${outDir}`;
|
|
67266
|
-
}
|
|
67267
|
-
function describeDevDriverArming(arming) {
|
|
67268
|
-
if (arming.armed) {
|
|
67269
|
-
return `[dev-driver] ARMED; frames are written to ${arming.outDir}`;
|
|
67270
|
-
}
|
|
67271
|
-
if (arming.problems.length === 0) return null;
|
|
67272
|
-
return `[dev-driver] not armed: ${arming.problems.join("; ")}`;
|
|
67273
|
-
}
|
|
67274
68229
|
class StartupRollback {
|
|
67275
68230
|
owners = [];
|
|
67276
68231
|
add(cleanup) {
|
|
@@ -67373,7 +68328,8 @@ const BROWSER_IPC_CHANNELS = [
|
|
|
67373
68328
|
* `unregisterBrowserIpc` remove it on a stop/retry like every other channel
|
|
67374
68329
|
* (removing an unregistered handler is a no-op).
|
|
67375
68330
|
*/
|
|
67376
|
-
DEV_DRIVER_ADDRESS_OVERLAY
|
|
68331
|
+
DEV_DRIVER_ADDRESS_OVERLAY,
|
|
68332
|
+
DEV_DRIVER_ADDRESS_OVERLAY_PROJECT
|
|
67377
68333
|
];
|
|
67378
68334
|
function registerBrowserIpc(options) {
|
|
67379
68335
|
function authorizeSender(event) {
|
|
@@ -67518,17 +68474,23 @@ function registerBrowserIpc(options) {
|
|
|
67518
68474
|
}
|
|
67519
68475
|
return host.historyActive(direction);
|
|
67520
68476
|
});
|
|
67521
|
-
electron.ipcMain.handle("browser-set-content-rect", (event,
|
|
68477
|
+
electron.ipcMain.handle("browser-set-content-rect", (event, rect2) => {
|
|
67522
68478
|
const host = authorize(event);
|
|
67523
|
-
return host.setContentRect(contentRectOrNull(
|
|
68479
|
+
return host.setContentRect(contentRectOrNull(rect2));
|
|
67524
68480
|
});
|
|
67525
|
-
electron.ipcMain.handle(
|
|
67526
|
-
|
|
67527
|
-
|
|
67528
|
-
|
|
68481
|
+
electron.ipcMain.handle(
|
|
68482
|
+
"browser-set-visible",
|
|
68483
|
+
(event, visible, ownPage) => {
|
|
68484
|
+
const host = authorize(event);
|
|
68485
|
+
if (typeof visible !== "boolean") {
|
|
68486
|
+
throw new Error("Visibility must be a boolean.");
|
|
68487
|
+
}
|
|
68488
|
+
if (ownPage !== void 0 && typeof ownPage !== "boolean") {
|
|
68489
|
+
throw new Error("The view-hidden reason must be a boolean.");
|
|
68490
|
+
}
|
|
68491
|
+
return host.setViewVisible(visible, ownPage === true);
|
|
67529
68492
|
}
|
|
67530
|
-
|
|
67531
|
-
});
|
|
68493
|
+
);
|
|
67532
68494
|
electron.ipcMain.handle(
|
|
67533
68495
|
"browser-hand-over",
|
|
67534
68496
|
(event, tabId, sessionId2) => {
|
|
@@ -67620,21 +68582,40 @@ function registerBrowserIpc(options) {
|
|
|
67620
68582
|
return { retried: true };
|
|
67621
68583
|
});
|
|
67622
68584
|
if (options.addressOverlayProbe) {
|
|
67623
|
-
electron.ipcMain.handle(
|
|
67624
|
-
|
|
67625
|
-
|
|
67626
|
-
|
|
67627
|
-
|
|
67628
|
-
|
|
68585
|
+
electron.ipcMain.handle(
|
|
68586
|
+
DEV_DRIVER_ADDRESS_OVERLAY,
|
|
68587
|
+
(event, action, argument) => {
|
|
68588
|
+
authorizeSender(event);
|
|
68589
|
+
if (action !== "show" && action !== "hide" && action !== "facts" && action !== "click" && action !== "measure" && action !== "key") {
|
|
68590
|
+
throw new Error(
|
|
68591
|
+
"address_overlay_invalid_action: expected 'show', 'hide', 'facts', 'click', 'measure' or 'key'."
|
|
68592
|
+
);
|
|
68593
|
+
}
|
|
68594
|
+
const probe = options.addressOverlayProbe;
|
|
68595
|
+
if (!probe) {
|
|
68596
|
+
throw new Error(
|
|
68597
|
+
"address_overlay_unavailable: the overlay probe is not running."
|
|
68598
|
+
);
|
|
68599
|
+
}
|
|
68600
|
+
const readsArgument = action === "click" || action === "measure" || action === "key";
|
|
68601
|
+
return probe(action, readsArgument ? argument : void 0);
|
|
67629
68602
|
}
|
|
67630
|
-
|
|
67631
|
-
|
|
67632
|
-
|
|
67633
|
-
|
|
67634
|
-
|
|
68603
|
+
);
|
|
68604
|
+
}
|
|
68605
|
+
if (options.addressOverlayProject) {
|
|
68606
|
+
electron.ipcMain.handle(
|
|
68607
|
+
DEV_DRIVER_ADDRESS_OVERLAY_PROJECT,
|
|
68608
|
+
(event, payload) => {
|
|
68609
|
+
authorizeSender(event);
|
|
68610
|
+
const project = options.addressOverlayProject;
|
|
68611
|
+
if (!project) {
|
|
68612
|
+
throw new Error(
|
|
68613
|
+
"address_overlay_unavailable: the overlay projection is not running."
|
|
68614
|
+
);
|
|
68615
|
+
}
|
|
68616
|
+
return project(payload);
|
|
67635
68617
|
}
|
|
67636
|
-
|
|
67637
|
-
});
|
|
68618
|
+
);
|
|
67638
68619
|
}
|
|
67639
68620
|
}
|
|
67640
68621
|
function unregisterBrowserIpc() {
|
|
@@ -67699,17 +68680,17 @@ function contentRectOrNull(value) {
|
|
|
67699
68680
|
if (value === null || value === void 0) return null;
|
|
67700
68681
|
if (typeof value !== "object")
|
|
67701
68682
|
throw new Error("Content rect must be an object.");
|
|
67702
|
-
const
|
|
68683
|
+
const rect2 = value;
|
|
67703
68684
|
for (const key of ["x", "y", "width", "height"]) {
|
|
67704
|
-
if (typeof
|
|
68685
|
+
if (typeof rect2[key] !== "number" || !Number.isFinite(rect2[key])) {
|
|
67705
68686
|
throw new Error("Content rect needs finite x, y, width and height.");
|
|
67706
68687
|
}
|
|
67707
68688
|
}
|
|
67708
68689
|
return {
|
|
67709
|
-
x:
|
|
67710
|
-
y:
|
|
67711
|
-
width:
|
|
67712
|
-
height:
|
|
68690
|
+
x: rect2.x,
|
|
68691
|
+
y: rect2.y,
|
|
68692
|
+
width: rect2.width,
|
|
68693
|
+
height: rect2.height
|
|
67713
68694
|
};
|
|
67714
68695
|
}
|
|
67715
68696
|
function paneNavigationDirection(input) {
|
|
@@ -69619,15 +70600,30 @@ function startBrowserHost(options) {
|
|
|
69619
70600
|
* replaces it) rather than capturing a controller here.
|
|
69620
70601
|
*/
|
|
69621
70602
|
...options.addressOverlay?.enabled ? {
|
|
69622
|
-
addressOverlayProbe: (action) => {
|
|
70603
|
+
addressOverlayProbe: (action, argument) => {
|
|
69623
70604
|
const probe = bindings.addressOverlayProbe;
|
|
69624
70605
|
if (!probe) {
|
|
69625
70606
|
throw new Error(
|
|
69626
70607
|
"address_overlay_unavailable: the overlay controller is not running."
|
|
69627
70608
|
);
|
|
69628
70609
|
}
|
|
69629
|
-
return probe(action);
|
|
69630
|
-
}
|
|
70610
|
+
return probe(action, argument);
|
|
70611
|
+
},
|
|
70612
|
+
/* S2's projection. Registered only with the S1 probe, because the
|
|
70613
|
+
* projection can only drive a child the probe's gate also armed. The
|
|
70614
|
+
* current attempt's handler is read through `bindings` per call, like
|
|
70615
|
+
* the probe above, so a retry cannot leave a stale controller. */
|
|
70616
|
+
...options.addressOverlay.list === true ? {
|
|
70617
|
+
addressOverlayProject: (payload) => {
|
|
70618
|
+
const project = bindings.addressOverlayProject;
|
|
70619
|
+
if (!project) {
|
|
70620
|
+
throw new Error(
|
|
70621
|
+
"address_overlay_unavailable: the overlay controller is not running."
|
|
70622
|
+
);
|
|
70623
|
+
}
|
|
70624
|
+
return project(payload);
|
|
70625
|
+
}
|
|
70626
|
+
} : {}
|
|
69631
70627
|
} : {},
|
|
69632
70628
|
log: options.log
|
|
69633
70629
|
});
|
|
@@ -70017,6 +71013,18 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
70017
71013
|
const addressOverlay = options.addressOverlay?.enabled === true ? new AddressOverlayController({
|
|
70018
71014
|
window: options.window,
|
|
70019
71015
|
documentUrl: options.addressOverlay.documentUrl,
|
|
71016
|
+
javascript: options.addressOverlay.list === true,
|
|
71017
|
+
visible: options.addressOverlay.visible === true,
|
|
71018
|
+
// The only consumer of a validated row click: the owner window's own
|
|
71019
|
+
// renderer. Guarded because a stop can race a queued mouse event.
|
|
71020
|
+
onEvent: (event) => {
|
|
71021
|
+
if (!options.window.isDestroyed()) {
|
|
71022
|
+
options.window.webContents.send(
|
|
71023
|
+
DEV_DRIVER_ADDRESS_OVERLAY_EVENT,
|
|
71024
|
+
event
|
|
71025
|
+
);
|
|
71026
|
+
}
|
|
71027
|
+
},
|
|
70020
71028
|
log: log2,
|
|
70021
71029
|
appMetrics: () => electron.app.getAppMetrics().map((metric) => ({
|
|
70022
71030
|
pid: metric.pid,
|
|
@@ -70325,13 +71333,13 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
70325
71333
|
);
|
|
70326
71334
|
rollback.add(() => stateWriter?.clear());
|
|
70327
71335
|
stateWriter.start(server.port, sessionKey);
|
|
70328
|
-
const probeAddressOverlay = async (action) => {
|
|
71336
|
+
const probeAddressOverlay = async (action, argument) => {
|
|
70329
71337
|
if (!addressOverlay) {
|
|
70330
71338
|
throw new Error(
|
|
70331
71339
|
"address_overlay_unavailable: this host owns no controller."
|
|
70332
71340
|
);
|
|
70333
71341
|
}
|
|
70334
|
-
const facts2 = await addressOverlay.probe(action);
|
|
71342
|
+
const facts2 = await addressOverlay.probe(action, argument);
|
|
70335
71343
|
const record = registry.activeTab;
|
|
70336
71344
|
const page = record ? {
|
|
70337
71345
|
tabId: record.tabId,
|
|
@@ -70341,8 +71349,19 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
70341
71349
|
} : null;
|
|
70342
71350
|
return { ...facts2, page };
|
|
70343
71351
|
};
|
|
71352
|
+
const projectAddressOverlay = (payload) => {
|
|
71353
|
+
if (!addressOverlay) {
|
|
71354
|
+
throw new Error(
|
|
71355
|
+
"address_overlay_unavailable: this host owns no controller."
|
|
71356
|
+
);
|
|
71357
|
+
}
|
|
71358
|
+
return addressOverlay.project(payload);
|
|
71359
|
+
};
|
|
70344
71360
|
if (addressOverlay) {
|
|
70345
71361
|
bindings.addressOverlayProbe = probeAddressOverlay;
|
|
71362
|
+
if (options.addressOverlay?.list === true) {
|
|
71363
|
+
bindings.addressOverlayProject = projectAddressOverlay;
|
|
71364
|
+
}
|
|
70346
71365
|
}
|
|
70347
71366
|
Object.assign(bindings, {
|
|
70348
71367
|
window: () => options.window,
|
|
@@ -81704,11 +82723,12 @@ if (devDriverLine) console.log(devDriverLine);
|
|
|
81704
82723
|
const addressOverlayGate = resolveAddressOverlayGate({
|
|
81705
82724
|
env: launchEnv,
|
|
81706
82725
|
devDriverArmed: devDriverArming.armed,
|
|
81707
|
-
windowMode: windowLaunch.mode
|
|
82726
|
+
windowMode: windowLaunch.mode,
|
|
82727
|
+
packaged: electron.app.isPackaged
|
|
81708
82728
|
});
|
|
81709
82729
|
if (addressOverlayGate.asked) {
|
|
81710
82730
|
console.log(
|
|
81711
|
-
addressOverlayGate.enabled ? "[address-overlay] ARMED (
|
|
82731
|
+
addressOverlayGate.enabled ? addressOverlayGate.visible ? "[address-overlay] ARMED (S3 VISIBLE probe, inactive window, 120 s cap, no product path)" : `[address-overlay] ARMED (S${addressOverlayGate.list ? "2 probe: mirror list" : "1 probe: native child"}, no product path)` : `[address-overlay] not armed: ${addressOverlayGate.reason}`
|
|
81712
82732
|
);
|
|
81713
82733
|
}
|
|
81714
82734
|
const devDriverWebPreferences = devDriverArming.armed && devDriverArming.outDir ? { additionalArguments: [devDriverArgument(devDriverArming.outDir)] } : {};
|
|
@@ -81982,7 +83002,10 @@ electron.app.whenReady().then(async () => {
|
|
|
81982
83002
|
const launcher = resolveLauncherWatchPlan({
|
|
81983
83003
|
mode: windowLaunch.mode,
|
|
81984
83004
|
launcherPid: process.ppid,
|
|
81985
|
-
env: launchEnv
|
|
83005
|
+
env: launchEnv,
|
|
83006
|
+
// S3 only: the one visible run nobody is watching still must not
|
|
83007
|
+
// outlive its launcher. False for every product launch.
|
|
83008
|
+
boundedVisibleProbe: addressOverlayGate.visible
|
|
81986
83009
|
});
|
|
81987
83010
|
logger.info(`[window-mode] ${launcher.reason}`, LogFileType.BACKEND);
|
|
81988
83011
|
if (windowLaunch.mode !== "normal") {
|
|
@@ -81997,12 +83020,23 @@ electron.app.whenReady().then(async () => {
|
|
|
81997
83020
|
onLauncherGone: ({ message, detail }) => endHeadlessRun(message, detail)
|
|
81998
83021
|
});
|
|
81999
83022
|
}
|
|
82000
|
-
if (
|
|
83023
|
+
if (addressOverlayGate.visible) {
|
|
83024
|
+
const cap = setTimeout(() => {
|
|
83025
|
+
endHeadlessRun(
|
|
83026
|
+
`the visible address-overlay probe reached its ${ADDRESS_OVERLAY_VISIBLE_MAX_MS / 1e3} s cap`
|
|
83027
|
+
);
|
|
83028
|
+
}, ADDRESS_OVERLAY_VISIBLE_MAX_MS);
|
|
83029
|
+
cap.unref();
|
|
83030
|
+
console.log(
|
|
83031
|
+
`[address-overlay] visible probe cap: ${ADDRESS_OVERLAY_VISIBLE_MAX_MS / 1e3} s from launch`
|
|
83032
|
+
);
|
|
83033
|
+
}
|
|
83034
|
+
if (windowLaunch.mode === "headless" || addressOverlayGate.visible) {
|
|
82001
83035
|
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
82002
83036
|
process.on(signal, () => endHeadlessRun(`received ${signal}`));
|
|
82003
83037
|
}
|
|
82004
83038
|
}
|
|
82005
|
-
if (windowLaunch.mode !== "headless") {
|
|
83039
|
+
if (windowLaunch.mode !== "headless" && !addressOverlayGate.visible) {
|
|
82006
83040
|
const onSigtermDuringQuit = () => {
|
|
82007
83041
|
if (!quitState.isQuitting()) {
|
|
82008
83042
|
process.removeListener("SIGTERM", onSigtermDuringQuit);
|
|
@@ -82114,7 +83148,7 @@ electron.app.whenReady().then(async () => {
|
|
|
82114
83148
|
});
|
|
82115
83149
|
const rendererUrl = rendererDocumentUrl();
|
|
82116
83150
|
const consoleCaptureUrl = consoleCaptureUrlFor(rendererUrl);
|
|
82117
|
-
const addressOverlayDocumentUrl = addressOverlayDocumentUrlFor(rendererUrl);
|
|
83151
|
+
const addressOverlayDocumentUrl = addressOverlayGate.list ? addressListDocumentUrlFor(rendererUrl) : addressOverlayDocumentUrlFor(rendererUrl);
|
|
82118
83152
|
backendService.observeDesktopFeed(
|
|
82119
83153
|
(frame) => {
|
|
82120
83154
|
if (frame.type === "notification") {
|
|
@@ -82928,6 +83962,8 @@ electron.app.whenReady().then(async () => {
|
|
|
82928
83962
|
...addressOverlayGate.enabled ? {
|
|
82929
83963
|
addressOverlay: {
|
|
82930
83964
|
enabled: true,
|
|
83965
|
+
list: addressOverlayGate.list,
|
|
83966
|
+
visible: addressOverlayGate.visible,
|
|
82931
83967
|
documentUrl: addressOverlayDocumentUrl
|
|
82932
83968
|
}
|
|
82933
83969
|
} : {},
|