[html] Fix unquoted content charset parsing and deduplicate _insertText
diff --git a/pkgs/html/CHANGELOG.md b/pkgs/html/CHANGELOG.md index baf0d1f..fd7c63a 100644 --- a/pkgs/html/CHANGELOG.md +++ b/pkgs/html/CHANGELOG.md
@@ -3,10 +3,10 @@ - Fix `Node.attributeSpans` and `Node.attributeValueSpans` throwing a `TypeError` when a start tag emits tokenizer parse errors, and preserve the first occurrence's span on duplicate attributes. - Fix `HtmlInputStream` throwing a `FormatException` on malformed UTF-8 or invalid ASCII byte sequences, and ignore `<meta charset>` declarations that resolve to unsupported Dart codecs. - Fix `HtmlTokenizer.consumeNumberEntity` throwing a `FormatException` when a numeric character reference overflows a 64-bit integer. -- Fix `EncodingParser` failing to parse `<meta charset>` attributes with whitespace before `=` and `EncodingBytes` throwing a `RangeError` at offset 0. +- Fix `EncodingParser` failing to parse `<meta charset>` attributes with whitespace before `=`, unquoted `charset` values at the end of `content` attributes, and `EncodingBytes` throwing a `RangeError` at offset 0. - Fix `querySelector` and `querySelectorAll` throwing a null-check `TypeError` instead of `FormatException` on empty `:not()` selectors. - Fix `TreeBuilder` not updating `Text.sourceSpan` when merging consecutive foster-parented text nodes before a `<table>`. -- Fix `ParseError.message` throwing a null-check `TypeError` on `self-closing-flag-on-end-tag`, `expected-end-of-tag-name-but-got-eof`, and `unexpected-EOF-after-attribute-value` tokenizer errors. +- Fix `ParseError.message` throwing a null-check `TypeError` on `unexpected-EOF-after-attribute-value` tokenizer errors. ## 0.15.7
diff --git a/pkgs/html/lib/parser.dart b/pkgs/html/lib/parser.dart index 5a6fd50..7425965 100644 --- a/pkgs/html/lib/parser.dart +++ b/pkgs/html/lib/parser.dart
@@ -3974,8 +3974,7 @@ /// Use [SourceSpan.message] or the [toString] from the [span] field to get a /// message including span information @override - String get message => formatStr( - errorMessages[errorCode] ?? errorMessages['undefined-error']!, data); + String get message => formatStr(errorMessages[errorCode]!, data); @override String toString({dynamic color}) {
diff --git a/pkgs/html/lib/src/encoding_parser.dart b/pkgs/html/lib/src/encoding_parser.dart index d92fe27..ac220d3 100644 --- a/pkgs/html/lib/src/encoding_parser.dart +++ b/pkgs/html/lib/src/encoding_parser.dart
@@ -354,7 +354,9 @@ } else { // Unquoted value final oldPosition = data._position; - data._skipUntil(isWhitespace); + if (data._skipUntil(isWhitespace) == null) { + return data._slice(oldPosition); + } return data._slice(oldPosition, data._position); } } on _EncodingRangeException catch (_) {
diff --git a/pkgs/html/lib/src/treebuilder.dart b/pkgs/html/lib/src/treebuilder.dart index 521f9d5..0c0ee1c 100644 --- a/pkgs/html/lib/src/treebuilder.dart +++ b/pkgs/html/lib/src/treebuilder.dart
@@ -278,31 +278,17 @@ static void _insertText(Node parent, String data, FileSpan? span, [Element? refNode]) { final nodes = parent.nodes; - if (refNode == null) { - if (nodes.isNotEmpty && nodes.last is Text) { - final last = nodes.last as Text; - last.appendData(data); + final index = refNode == null ? nodes.length : nodes.indexOf(refNode); + if (index > 0 && nodes[index - 1] is Text) { + final last = nodes[index - 1] as Text; + last.appendData(data); - if (span != null) { - last.sourceSpan = - span.file.span(last.sourceSpan!.start.offset, span.end.offset); - } - } else { - nodes.add(Text(data)..sourceSpan = span); + if (span != null) { + last.sourceSpan = + span.file.span(last.sourceSpan!.start.offset, span.end.offset); } } else { - final index = nodes.indexOf(refNode); - if (index > 0 && nodes[index - 1] is Text) { - final last = nodes[index - 1] as Text; - last.appendData(data); - - if (span != null) { - last.sourceSpan = - span.file.span(last.sourceSpan!.start.offset, span.end.offset); - } - } else { - nodes.insert(index, Text(data)..sourceSpan = span); - } + nodes.insert(index, Text(data)..sourceSpan = span); } }
diff --git a/pkgs/html/test/parser_feature_test.dart b/pkgs/html/test/parser_feature_test.dart index dc16b19..f27298e 100644 --- a/pkgs/html/test/parser_feature_test.dart +++ b/pkgs/html/test/parser_feature_test.dart
@@ -246,6 +246,12 @@ expect(error.toString(), isNotEmpty); } } + final eofAfterValue = HtmlParser('<div a="1"', generateSpans: true) + ..parse(); + expect( + eofAfterValue.errors.map((e) => e.message), + contains('Unexpected end of file after attribute value.'), + ); }); test('void element innerHTML', () { @@ -451,7 +457,15 @@ expect( getEncoding( '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'), - null); + 'utf-8'); + expect( + getEncoding( + '<meta http-equiv="content-type" content="text/html; charset=UTF-8 ">'), + 'utf-8'); + expect( + getEncoding( + '<meta http-equiv="content-type" content="text/html; charset=\'UTF-8">'), + isNull); }); }); }