element-hq / element-hq/element-android

[SDK] Reply event 'body' contains HTML instead of plain text

Open
#9,164 0 comments 0 reactions 0 assignees View on GitHub
matrix-sdk
Dominant language
Kotlin
Stars
3.7k
Forks
917
PR merge metrics
No merged PRs in 30d

Description

### Description

The `body` field of reply events can contain HTML markup, even though it should contain the plain-text representation of the message.

I think the bug come from that :

When creating a reply event, `createNewRepliedEvent()` calls `createReplyTextContent()`.

```
val replyText = localEchoEventFactory
.bodyForReply(currentTimelineEvent.getLastMessageContent(), true).takeFormatted()

val newContent = localEchoEventFactory.createReplyTextContent(
timelineEventMapper.map(timelineEventEntity),
replyText,
null,
false,
showInThread = false,
isRedactedEvent = isRedactedEvent
).toContent()
```

```
fun createReplyTextContent(
eventReplied: TimelineEvent,
replyText: CharSequence,
replyTextFormatted: CharSequence?,
autoMarkdown: Boolean,
rootThreadEventId: String? = null,
showInThread: Boolean,
isRedactedEvent: Boolean = false
): MessageContent? {
// Fallbacks and event representation
// TODO Add error/warning logs when any of this is null
val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null
val userId = eventReplied.root.senderId ?: return null
val userLink = permalinkFactory.createPermalink(userId, false) ?: return null

val body = bodyForReply(timelineEvent = eventReplied, isRedactedEvent = isRedactedEvent)

// As we always supply formatted body for replies we should force the MarkdownParser to produce html.
val finalReplyTextFormatted = replyTextFormatted?.toString() ?: markdownParser.parse(replyText, force = true, advanced = autoMarkdown).takeFormatted()
// Body of the original message may not have formatted version, so may also have to convert to html.
val bodyFormatted = body.formattedText ?: markdownParser.parse(body.text, force = true, advanced = autoMarkdown).takeFormatted()
val replyFormatted = buildFormattedReply(
permalink,
userLink,
userId,
bodyFormatted,
finalReplyTextFormatted
)
//
// > <@alice:example.org> This is the original body
//
val replyFallback = buildReplyFallback(body, userId, replyText.toString())

val eventId = eventReplied.root.eventId ?: return null
return MessageTextContent(
msgType = MessageType.MSGTYPE_TEXT,
format = MessageFormat.FORMAT_MATRIX_HTML,
body = replyFallback,
formattedBody = replyFormatted,
relatesTo = generateReplyRelationContent(
eventId = eventId,
rootThreadEventId = rootThreadEventId,
showInThread = showInThread
)
)
}
```

The issue appears to be caused by `createNewRepliedEvent()` passing a formatted version of the reply text as `replyText`, while passing `null` as `replyTextFormatted`.

As a result, `createReplyTextContent()` receives the already-formatted HTML content as `replyText` and `null` as `replyTextFormatted`. It then uses `replyText` to build both the formatted reply and the reply fallback.

Since `replyText` already contains formatted content, `replyFallback` also contain HTML markup. `replyFallback` is then assigned directly to the body field.

This results in HTML markup being present in `body`, whereas `body` should contain the plain-text representation and the formatted representation should only be present in `formatted_body`.

Contributor guide

Open the contributing guide

Research direction

Start at createNewRepliedEvent() and follow its call to createReplyTextContent(). Check how replyText and replyTextFormatted are passed and how buildReplyFallback() produces the body. Done means reply events use plain text in body while formatted HTML remains in formatted_body.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.