commit 35a1f22e045d2162432aaeaee3e8bbc8abc882b8
parent e3649ec3becfec722636182bb9ef21e518057743
Author: agoloman <agoloman@mozilla.com>
Date: Thu, 8 Jan 2026 11:41:42 +0200
Revert "Bug 2001504 - Chat Assistant markdown rendering r=Mardak,ai-frontend-reviewers,Gijs" for causing bc failures @DOMSecurityMonitor.cpp.
This reverts commit be98a8d8835c6dd5dad781550c70f87f8b8920b7.
Diffstat:
3 files changed, 42 insertions(+), 156 deletions(-)
diff --git a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs
@@ -2,14 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-import { html, unsafeHTML } from "chrome://global/content/vendor/lit.all.mjs";
+import { html } from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
-import {
- defaultMarkdownParser,
- DOMSerializer,
-} from "chrome://browser/content/multilineeditor/prosemirror.bundle.mjs";
-
-const SERIALIZER = DOMSerializer.fromSchema(defaultMarkdownParser.schema);
/**
* A custom element for managing AI Chat Content
@@ -32,31 +26,6 @@ export class AIChatMessage extends MozLitElement {
super.connectedCallback();
}
- /**
- * Parse markdown content to HTML using ProseMirror
- *
- * @param {string} markdown
- * @returns {string} HTML string
- */
- parseMarkdown(markdown) {
- if (!markdown) {
- return "";
- }
-
- const node = defaultMarkdownParser.parse(markdown);
- const fragment = SERIALIZER.serializeFragment(node.content);
-
- // Convert DocumentFragment to HTML string
- const container = this.ownerDocument.createElement("div");
- container.appendChild(fragment);
- const containerString = container.innerHTML;
-
- // Sanitize the HTML string before returning useing "setHTML"
- const sanitizedContainer = this.ownerDocument.createElement("div");
- sanitizedContainer.setHTML(containerString);
- return sanitizedContainer.innerHTML;
- }
-
render() {
return html`
<link
@@ -64,18 +33,12 @@ export class AIChatMessage extends MozLitElement {
href="chrome://browser/content/aiwindow/components/ai-chat-message.css"
/>
- ${this.role === "user"
- ? html`<article>
- <div class=${"message-" + this.role}>
- <!-- TODO: Parse user prompt to add any mentions pills -->
- ${this.message}
- </div>
- </article>`
- : html`<article>
- <div class=${"message-" + this.role}>
- ${unsafeHTML(this.parseMarkdown(this.message))}
- </div>
- </article>`}
+ <article>
+ <div class=${"message-" + this.role}>
+ <!-- TODO: Add markdown parsing here -->
+ ${this.message}
+ </div>
+ </article>
`;
}
}
diff --git a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs
@@ -20,7 +20,7 @@ export default {
};
const Template = ({ role, content }) => html`
- <ai-chat-message .role=${role} .message=${content}></ai-chat-message>
+ <ai-chat-message .message=${{ role, content }}></ai-chat-message>
`;
export const UserMessage = Template.bind({});
@@ -35,10 +35,3 @@ AssistantMessage.args = {
content:
"Test: I don't have access to real-time weather data, but I can help you with other tasks!",
};
-
-export const AssistantMessageWithMarkdown = Template.bind({});
-AssistantMessageWithMarkdown.args = {
- role: "assistant",
- content:
- "Here's some **bold text** and *italic text*:\n\n- Item 1\n- Item 2\n\n```javascript\nconsole.log('code block');\n```",
-};
diff --git a/browser/components/aiwindow/ui/test/browser/browser_aichat_message.js b/browser/components/aiwindow/ui/test/browser/browser_aichat_message.js
@@ -4,127 +4,57 @@
"use strict";
/**
- * Basic rendering + markdown/sanitization test for <ai-chat-message>.
- *
- * Avoids Lit's updateComplete because MozLitElement variants may not expose it
- * or it may never resolve in this harness.
+ * Test ai-chat-message custom element basic rendering
*/
add_task(async function test_ai_chat_message_rendering() {
await SpecialPowers.pushPrefEnv({
set: [["browser.aiwindow.enabled", true]],
});
+ // Use about:aichatcontent which already loads the components properly
await BrowserTestUtils.withNewTab("about:aichatcontent", async browser => {
await SpecialPowers.spawn(browser, [], async () => {
await content.customElements.whenDefined("ai-chat-message");
- const doc = content.document;
+ // Create a test ai-chat-message element
+ const messageElement = content.document.createElement("ai-chat-message");
+ content.document.body.appendChild(messageElement);
- const el = doc.createElement("ai-chat-message");
- doc.body.appendChild(el);
+ Assert.ok(messageElement, "ai-chat-message element should be created");
- Assert.ok(el, "ai-chat-message element should be created");
+ // Test setting a user message
+ messageElement.message = { role: "user", content: "Test user message" };
+ await messageElement.updateComplete;
- function root() {
- return el.shadowRoot ?? el;
- }
-
- function setRoleAndMessage(role, message) {
- // Set both property + attribute to avoid any reflection differences.
- el.role = role;
- el.setAttribute("role", role);
-
- el.message = message;
- el.setAttribute("message", message);
- }
-
- async function waitFor(fn, msg) {
- for (let i = 0; i < 120; i++) {
- try {
- if (fn()) {
- return;
- }
- } catch (e) {
- // Keep looping; DOM may not be ready yet.
- }
- await new content.Promise(resolve =>
- content.requestAnimationFrame(resolve)
- );
- }
- Assert.ok(false, `Timed out: ${msg}`);
+ const messageDiv =
+ messageElement.renderRoot?.querySelector(".message-user");
+ if (messageDiv) {
+ Assert.ok(messageDiv, "User message div should be rendered");
+ Assert.ok(
+ messageDiv.textContent.includes("Test user message"),
+ "User message content should be present"
+ );
}
- // --- User message ---
- setRoleAndMessage("user", "Test user message");
-
- await waitFor(() => {
- const div = root().querySelector(".message-user");
- return div && div.textContent.includes("Test user message");
- }, "User message should render with expected text");
-
- const userDiv = root().querySelector(".message-user");
- Assert.ok(userDiv, "User message div should exist");
- Assert.ok(
- userDiv.textContent.includes("Test user message"),
- `User message content should be present (got: "${userDiv.textContent}")`
- );
-
- // --- Assistant message ---
- setRoleAndMessage("assistant", "Test AI response");
-
- await waitFor(() => {
- const div = root().querySelector(".message-assistant");
- return div && div.textContent.includes("Test AI response");
- }, "Assistant message should render with expected text");
-
- let assistantDiv = root().querySelector(".message-assistant");
- Assert.ok(assistantDiv, "Assistant message div should exist");
- Assert.ok(
- assistantDiv.textContent.includes("Test AI response"),
- `Assistant message content should be present (got: "${assistantDiv.textContent}")`
- );
-
- // --- Markdown parsing (positive) ---
- setRoleAndMessage("assistant", "**Bold** and *italic* text");
-
- await waitFor(() => {
- const div = root().querySelector(".message-assistant");
- return div && div.querySelector("strong") && div.querySelector("em");
- }, "Markdown should produce <strong> and <em>");
-
- assistantDiv = root().querySelector(".message-assistant");
- Assert.ok(
- assistantDiv.querySelector("strong"),
- `Expected <strong> in: ${assistantDiv.innerHTML}`
- );
- Assert.ok(
- assistantDiv.querySelector("em"),
- `Expected <em> in: ${assistantDiv.innerHTML}`
- );
-
- // --- Negative: raw HTML should not become markup ---
- setRoleAndMessage("assistant", "<b>not bolded</b>");
-
- await waitFor(() => {
- const div = root().querySelector(".message-assistant");
- return (
- div &&
- !div.querySelector("b") &&
- div.textContent.includes("not bolded")
+ // Test setting an assistant message
+ messageElement.message = {
+ role: "assistant",
+ content: "Test AI response",
+ };
+ await messageElement.updateComplete;
+
+ const assistantDiv =
+ messageElement.renderRoot?.querySelector(".message-assistant");
+ if (assistantDiv) {
+ Assert.ok(assistantDiv, "Assistant message div should be rendered");
+ Assert.ok(
+ assistantDiv.textContent.includes("Test AI response"),
+ "Assistant message content should be present"
);
- }, "Raw HTML should not become a <b> element, but text should remain");
-
- assistantDiv = root().querySelector(".message-assistant");
- Assert.ok(
- !assistantDiv.querySelector("b"),
- `Should not contain real <b>: ${assistantDiv.innerHTML}`
- );
- Assert.ok(
- assistantDiv.textContent.includes("not bolded"),
- `Raw HTML content should still be visible as text (got: "${assistantDiv.textContent}")`
- );
+ }
- el.remove();
+ // Clean up
+ messageElement.remove();
});
});