From 4b0748ecf9eca4a412ece7efb149bf6653804302 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Thu, 29 Aug 2024 21:22:55 -0400 Subject: [PATCH 1/2] content [nfc]: Use single and last instead to access child. This improves readability, especially in the .last case. Signed-off-by: Zixuan James Li --- lib/model/content.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model/content.dart b/lib/model/content.dart index e9ee7875ef..3228fe9406 100644 --- a/lib/model/content.dart +++ b/lib/model/content.dart @@ -986,7 +986,7 @@ class _ZulipContentParser { && divElement.className == "codehilite"); if (divElement.nodes.length != 1) return null; - final child = divElement.nodes[0]; + final child = divElement.nodes.single; if (child is! dom.Element) return null; if (child.localName != 'pre') return null; @@ -997,7 +997,7 @@ class _ZulipContentParser { || first.localName != 'span' || first.nodes.isNotEmpty) return null; } - final grandchild = child.nodes[child.nodes.length - 1]; + final grandchild = child.nodes.last; if (grandchild is! dom.Element) return null; if (grandchild.localName != 'code') return null; From 5d369770eb7506f6e6f87a2bd15c55ae28e67915 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Wed, 28 Aug 2024 02:58:43 -0400 Subject: [PATCH 2/2] content: Handle code block elements without grandchildren. A code block containing no grandchildren (i.e. a
element containing an empty
 block)
appears in some very old messages.  The parser was crashing on them.

The affect messages in public https://chat.zulip.org history ranged
from message ID 5020 to 29855, all in 2016.

Signed-off-by: Zixuan James Li 
---
 lib/model/content.dart       |  2 +-
 test/model/content_test.dart | 12 ++++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/model/content.dart b/lib/model/content.dart
index 3228fe9406..e99d4d391a 100644
--- a/lib/model/content.dart
+++ b/lib/model/content.dart
@@ -990,7 +990,7 @@ class _ZulipContentParser {
       if (child is! dom.Element) return null;
       if (child.localName != 'pre') return null;
 
-      if (child.nodes.length > 2) return null;
+      if (child.nodes.length > 2 || child.nodes.isEmpty) return null;
       if (child.nodes.length == 2) {
         final first = child.nodes[0];
         if (first is! dom.Element
diff --git a/test/model/content_test.dart b/test/model/content_test.dart
index 0846273795..4aef38d5cc 100644
--- a/test/model/content_test.dart
+++ b/test/model/content_test.dart
@@ -344,6 +344,17 @@ class ContentExample {
       ]),
     ]);
 
+  // Current servers no longer produce this, but it can be found in ancient
+  // messages.  For example:
+  //   https://chat.zulip.org/#narrow/stream/2-general/topic/Error.20in.20dev.20server/near/18765
+  static final codeBlockWithEmptyBody = ContentExample(
+      'code block, with an empty body',
+      '```',
+      '
', [ + blockUnimplemented( + '
'), + ]); + static final codeBlockWithHighlightedLines = ContentExample( 'code block, with syntax highlighting and highlighted lines', '```\n::markdown hl_lines="2 4"\n# he\n## llo\n### world\n```', @@ -1149,6 +1160,7 @@ void main() { testParseExample(ContentExample.codeBlockPlain); testParseExample(ContentExample.codeBlockHighlightedShort); testParseExample(ContentExample.codeBlockHighlightedMultiline); + testParseExample(ContentExample.codeBlockWithEmptyBody); testParseExample(ContentExample.codeBlockWithHighlightedLines); testParseExample(ContentExample.codeBlockWithUnknownSpanType); testParseExample(ContentExample.codeBlockFollowedByMultipleLineBreaks);