Fall back to the message body when the formatted body has nothing to render

The HTML sanitiser in toHtmlDocument() drops every tag outside its safelist,
which does not include img. A formatted body whose only content is an image —
a custom emoji, or a bot posting an image inline — therefore cleans down to an
empty document, and because that document is not null the ?: body fallbacks in
the timeline item factory, the room list preview, the notification resolver and
plainText are all dead. The message renders as an empty bubble.

Return null from toHtmlDocument() when no text survives the sanitisation, so the
existing fallbacks take the plain text body, which for these messages carries the
alt text the sender provided.
This commit is contained in:
hayaksi1
2026-08-25 11:03:42 +03:00
parent 6ab3d7393b
commit 6b2e83cb63
4 changed files with 58 additions and 2 deletions
@@ -19,7 +19,8 @@ import org.jsoup.safety.Safelist
/**
* Converts the HTML string [FormattedBody.body] to a [Document] by parsing it.
* If the message is not formatted or the format is not [MessageFormat.HTML] we return `null`.
* If the message is not formatted, the format is not [MessageFormat.HTML], or nothing renderable is left once the
* unsupported tags have been removed, we return `null` so that the plain text body is used instead.
*
* This will also make sure mentions are prefixed with `@`.
*
@@ -44,7 +45,7 @@ fun FormattedBody.toHtmlDocument(
// Prepend `@` to mentions
fixMentions(dom, permalinkParser)
dom
dom.takeIf { it.text().isNotBlank() }
}
}
@@ -103,4 +103,28 @@ class ToHtmlDocumentTest : RobolectricTest() {
})
assertThat(document?.text()).isEqualTo("Hey Alice!")
}
@Test
fun `toHtmlDocument - returns null if the body only contains unsupported tags`() {
val body = FormattedBody(
format = MessageFormat.HTML,
body = "<img data-mx-emoticon src=\"mxc://matrix.org/anImage\" height=\"32\" width=\"32\" alt=\"\uD83D\uDE1C\" />"
)
val document = body.toHtmlDocument(permalinkParser = FakePermalinkParser())
assertThat(document).isNull()
}
@Test
fun `toHtmlDocument - returns a Document if some text survives next to unsupported tags`() {
val body = FormattedBody(
format = MessageFormat.HTML,
body = "Look at this <img src=\"mxc://matrix.org/anImage\" alt=\"cat\" />!"
)
val document = body.toHtmlDocument(permalinkParser = FakePermalinkParser())
assertThat(document?.text()).isEqualTo("Look at this !")
}
}
@@ -149,4 +149,16 @@ class ToPlainTextTest : RobolectricTest() {
)
assertThat(messageType.toPlainText(permalinkParser = FakePermalinkParser())).isEqualTo("This is the message content.")
}
@Test
fun `TextMessageType toPlainText - returns the body if the formatted one only contains an image`() {
val messageType = TextMessageType(
body = "\uD83D\uDE1C",
formatted = FormattedBody(
format = MessageFormat.HTML,
body = "<img data-mx-emoticon src=\"mxc://matrix.org/anImage\" height=\"32\" width=\"32\" alt=\"\uD83D\uDE1C\" />"
)
)
assertThat(messageType.toPlainText(permalinkParser = FakePermalinkParser())).isEqualTo("\uD83D\uDE1C")
}
}