tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 4e1d5ad706a238ce6e47afc444edef1f83955662
parent fdc8f4dba119903c206a6115035a8f4a95debfd9
Author: Yubin Jamora <yjamora@mozilla.com>
Date:   Mon,  1 Dec 2025 23:26:17 +0000

Bug 2000910 - remove prompt after auto submit in chatbot r=Mardak,ai-frontend-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D273082

Diffstat:
Mbrowser/components/genai/GenAIChild.sys.mjs | 29++++++++++++++++++++++++-----
Mbrowser/components/genai/tests/browser/browser_chat_request.js | 21+++++++++++++++------
Mbrowser/components/genai/tests/browser/file_chat-autosubmit.html | 33++++++++++++++++++++++++++++-----
3 files changed, 67 insertions(+), 16 deletions(-)

diff --git a/browser/components/genai/GenAIChild.sys.mjs b/browser/components/genai/GenAIChild.sys.mjs @@ -230,14 +230,33 @@ export class GenAIChild extends JSWindowActorChild { (/chatgpt\.com/i.test(win.location.host) || win.location.pathname.includes("file_chat-autosubmit.html")) ) { - win.setTimeout(() => { - if (editable.textContent) { - editable.textContent = ""; - editable.dispatchEvent( + const container = editable.parentElement; + if (!container) { + return; + } + + const observer = new win.MutationObserver(() => { + // Always refetch because ChatGPT replaces editable div + const currentEditable = container.querySelector( + '[contenteditable="true"]' + ); + if (!currentEditable) { + return; + } + + let hasText = currentEditable.textContent?.trim().length > 0; + if (hasText) { + currentEditable.textContent = ""; + currentEditable.dispatchEvent( new win.InputEvent("input", { bubbles: true }) ); } - }, 500); + }); + + observer.observe(container, { childList: true, subtree: true }); + + // Disconnect once things stabilize + win.setTimeout(() => observer.disconnect(), 2000); } } diff --git a/browser/components/genai/tests/browser/browser_chat_request.js b/browser/components/genai/tests/browser/browser_chat_request.js @@ -136,15 +136,24 @@ add_task(async function test_chat_auto_submit() { 1, "Form is triggered by AutoSubmitClick" ); + }); - await ContentTaskUtils.waitForCondition( - () => content.document.getElementById("ta").textContent === "", - "Prompt was cleared" - ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + await ContentTaskUtils.waitForCondition(() => { + const editable = content.document.querySelector( + '[contenteditable="true"]' + ); + + return editable && editable.textContent.trim() === ""; + }, "Prompt text was cleared by MutationObserver"); + }); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + const editable = content.document.querySelector('[contenteditable="true"]'); Assert.equal( - content.document.getElementById("ta").textContent, + editable.textContent.trim(), "", - "Prompt text is cleared after auto submission" + "Prompt text was cleared after auto submission" ); }); diff --git a/browser/components/genai/tests/browser/file_chat-autosubmit.html b/browser/components/genai/tests/browser/file_chat-autosubmit.html @@ -9,14 +9,37 @@ <script> const url = new URL(location.href); const prompt = url.searchParams.get("q") || ""; - const ta = document.getElementById("ta"); const form = document.getElementById("f"); + window.submitCount = 0; - setTimeout(() => { - ta.textContent = prompt; - ta.dispatchEvent(new InputEvent("input", { bubbles: true })); - }, 50); + // Simulate ChatGPT's behavior - replace editable div mulitiple time + function simulateChatGPTReplacement() { + let editable = document.querySelector('[contenteditable="true"]'); + editable.textContent = prompt; + + editable.dispatchEvent(new InputEvent("input"), { bubbles: true }); + + setTimeout(() => { + const newDiv = document.createElement("div"); + newDiv.setAttribute("contenteditable", "true"); + newDiv.textContent = prompt; + editable.replaceWith(newDiv); + + newDiv.dispatchEvent(new InputEvent("input", { bubbles: true })); + + setTimeout(() => { + const finalDiv = document.createElement("div"); + finalDiv.setAttribute("contenteditable", "true"); + finalDiv.textContent = prompt; + newDiv.replaceWith(finalDiv); + + finalDiv.dispatchEvent(new InputEvent("input", { bubbles: true })); + }, 50); + }, 50); + } + + setTimeout(simulateChatGPTReplacement, 50); form.addEventListener("submit", e => { e.preventDefault();