From cfa1892e17e3696da004c71a4942d5b12186b12c Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Tue, 28 Jul 2026 19:55:53 +0530 Subject: fix(domains): one fill engine, so browser_fill stops lying about checkboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit browser_fill and browser_fill_form carried separate in-page implementations that disagreed on the same element. The worst case was silent: browser_fill on a checkbox fired the HTMLInputElement value setter, which writes the submit value and leaves `checked` alone, then reported status ok — a no-op the agent had no reason to doubt. Both tools now share one engine, with browser_fill's documented and points at + * browser_select_option; browser_fill_form fills it. + * focusFirst — browser_fill_form focuses the field first; browser_fill + * documents that it does not move focus. + * + * Returns a PageResult (see form.ts): { ok, reason?, kind?, value?, text?, + * checked?, changed?, options?, tag? }. + */ + +export type FillOptions = { + readonly rejectSelect: boolean; + readonly focusFirst: boolean; +}; + +const ENGINE = ` + const el = this; + if (!el || el.nodeType !== 1) return { ok: false, reason: "ref does not point to an element" }; + const tagName = el.tagName || ""; + const tag = tagName.toLowerCase(); + const type = (el.type || "").toLowerCase(); + + if (__rejectSelect && tag === "select") { + return { ok: false, reason: "element is a is not fillable (not an input, textarea, or contenteditable): ${target}. For use browser_select_option.`, - details: { ref: args.ref, selector: args.selector, tag: res.tag }, + message: `Could not fill ${target}: ${res.reason ?? "unknown reason"}`, + details, }); } + const shown = res.value !== undefined ? JSON.stringify(res.value) : `checked=${res.checked === true}`; const diff = await interactiveDiff(client); return ok({ - text: `Filled ${target} = ${JSON.stringify(res.value)}${diff}`, - details: { ref: args.ref, selector: args.selector, value: args.value, verified: res.value, tag: res.tag, kind: res.kind }, + text: `Filled ${target} = ${shown}${diff}`, + details: { + ref: args.ref, + selector: args.selector, + value: args.value, + verified: res.value ?? res.checked, + ...(res.tag !== undefined ? { tag: res.tag } : {}), + ...(res.kind !== undefined ? { kind: res.kind } : {}), + }, }); }, }); @@ -320,6 +295,7 @@ export type PageResult = { readonly text?: string; readonly checked?: boolean; readonly changed?: boolean; + readonly tag?: string; readonly options?: ReadonlyArray<{ value: string; text: string }>; }; @@ -336,6 +312,7 @@ const isPageResult = (v: unknown): v is PageResult => { if ("text" in v && typeof v.text !== "string") return false; if ("checked" in v && typeof v.checked !== "boolean") return false; if ("changed" in v && typeof v.changed !== "boolean") return false; + if ("tag" in v && typeof v.tag !== "string") return false; if ("options" in v && !(Array.isArray(v.options) && v.options.every(isOption))) return false; return true; }; -- cgit v1.2.3