Skip to content

Commit 27659ec

Browse files
authored
fix: normalizer does not strip br nodes inside blockquote (#720)
# Summary The `<br>` was getting stripped when inside of a `<blockquote>` The example HTML that was reproducing the issue: ```html <html><blockquote><p>this is a pretty short blockquote.</p><br><p>This is a line after an empty line.</p></blockquote></html> ``` ## Screenshots When setting the input's value with that HTML I mentioned in the summary. Before: <img width="335" height="82" alt="image" src="https://github.com/user-attachments/assets/3d76e4f1-b2ea-406b-bcbc-3c58ddf853a5" /> After: <img width="375" height="106" alt="image" src="https://github.com/user-attachments/assets/d83dc9d7-93d0-44e4-9f44-a9f559d52d67" /> ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | | Web | ✅ | ## Checklist - [ ] E2E tests are passing - [ ] Required E2E tests have been added (if applicable)
1 parent ec1c03f commit 27659ec

4 files changed

Lines changed: 33 additions & 7 deletions

File tree

cpp/parser/GumboNormalizer.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,11 @@ static void flatten_bq_node(GumboNode *node, buffer_t *ib, buffer_t *out) {
598598
return;
599599
}
600600
if (is_br_node(node)) {
601-
flush_inline_p(ib, out, NULL);
601+
// Emit the canonical <br> so it is not silently dropped.
602+
// With buffered inline content it just terminates the current paragraph.
603+
if (!flush_inline_p(ib, out, NULL)) {
604+
buffer_append_str(out, "<br>");
605+
}
602606
return;
603607
}
604608
if (is_block_producing(node) || is_blockquote_node(node)) {

cpp/tests/GumboParserTest.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ TEST(GumboParserTest, DivRemappings) {
401401
"</b>hello<div><br></div><div>hi</div></li></ul></div></div></"
402402
"blockquote></span>"),
403403
"<p>what do you think of this craziness</p><blockquote><p><b>another one "
404-
"</b>hello</p><p>hi</p></blockquote>");
404+
"</b>hello</p><br><p>hi</p></blockquote>");
405405
}
406406

407407
TEST(GumboParserTest, ListFlattening) {
@@ -508,6 +508,13 @@ TEST(GumboParserTest, BrRemappings) {
508508
"href='https://google.com'>Net</a></p>"),
509509
"<p><b>Asdasdasd</b></p><br><br><p>Sent with <a "
510510
"href=\"https://google.com\">Net</a></p>");
511+
// A <br> between blockquote paragraphs is preserved.
512+
EXPECT_EQ(
513+
GumboParser::normalizeHtml(
514+
"<blockquote><p>this is a pretty short blockquote.</p><br><p>This is "
515+
"a line after an empty line.</p></blockquote>"),
516+
"<blockquote><p>this is a pretty short blockquote.</p><br><p>This is a "
517+
"line after an empty line.</p></blockquote>");
511518
}
512519

513520
// Preserve text alignment
@@ -581,9 +588,10 @@ TEST(GumboParserTest, InterBlockWhitespace) {
581588
EXPECT_EQ(GumboParser::normalizeHtml(
582589
"<p>Asdasd</p>\n\n<p>Asdasd</p>\n\n<p>Asdasda</p>"),
583590
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
584-
EXPECT_EQ(GumboParser::normalizeHtml(
585-
"<html>\n<p>Asdasd</p>\n<p>Asdasd</p>\n<p>Asdasda</p>\n</html>"),
586-
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
591+
EXPECT_EQ(
592+
GumboParser::normalizeHtml(
593+
"<html>\n<p>Asdasd</p>\n<p>Asdasd</p>\n<p>Asdasda</p>\n</html>"),
594+
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
587595
EXPECT_EQ(GumboParser::normalizeHtml("<p>Asdasd</p> <p>Asdasd</p>"),
588596
"<p>Asdasd</p><p>Asdasd</p>");
589597

src/web/__tests__/htmlNormalizer.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ describe('htmlNormalizer', () => {
359359
],
360360
[
361361
'<div>what do you think of this craziness</div><span><blockquote><div><div><ul><li><b>another one </b>hello<div><br></div><div>hi</div></li></ul></div></div></blockquote></span>',
362-
'<p>what do you think of this craziness</p><blockquote><p><b>another one </b>hello</p><p>hi</p></blockquote>',
362+
'<p>what do you think of this craziness</p><blockquote><p><b>another one </b>hello</p><br><p>hi</p></blockquote>',
363363
],
364364
])('%s → %s', (input, expected) => {
365365
expect(normalizeHtml(input)).toBe(expected);
@@ -477,6 +477,16 @@ describe('htmlNormalizer', () => {
477477
'<p><b>Asdasdasd</b></p><br><br><p>Sent with <a href="https://google.com">Net</a></p>'
478478
);
479479
});
480+
481+
test('<br> between blockquote paragraphs is preserved', () => {
482+
expect(
483+
normalizeHtml(
484+
'<blockquote><p>this is a pretty short blockquote.</p><br><p>This is a line after an empty line.</p></blockquote>'
485+
)
486+
).toBe(
487+
'<blockquote><p>this is a pretty short blockquote.</p><br><p>This is a line after an empty line.</p></blockquote>'
488+
);
489+
});
480490
});
481491

482492
describe('character escaping', () => {

src/web/normalization/htmlNormalizer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,11 @@ function flattenBqNode(
401401
}
402402
if (!isElement(node)) return;
403403
if (isBrNode(node)) {
404-
flushInlineP(ib, out);
404+
// Emit the canonical <br> so it is not silently dropped.
405+
// With buffered inline content it just terminates the current paragraph.
406+
if (!flushInlineP(ib, out)) {
407+
out.buf += '<br>';
408+
}
405409
return;
406410
}
407411
if (isBlockProducing(node) || isBlockquoteNode(node)) {

0 commit comments

Comments
 (0)