diff --git a/pkgs/html/CHANGELOG.md b/pkgs/html/CHANGELOG.md index 9a881e9d7..eaa1f8c00 100644 --- a/pkgs/html/CHANGELOG.md +++ b/pkgs/html/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.15.5+1 + +- Support "ambiguous ampersand" in attribute values. + ## 0.15.5 - Require Dart `3.2`. diff --git a/pkgs/html/lib/src/tokenizer.dart b/pkgs/html/lib/src/tokenizer.dart index 3bed0b0b6..228087c3d 100644 --- a/pkgs/html/lib/src/tokenizer.dart +++ b/pkgs/html/lib/src/tokenizer.dart @@ -313,7 +313,6 @@ class HtmlTokenizer implements Iterator { // Try to find the longest entity the string will match to take care // of ¬i for instance. - int entityLen; for (entityLen = charStack.length - 1; entityLen > 1; entityLen--) { final possibleEntityName = charStack.sublist(0, entityLen).join(); @@ -340,7 +339,11 @@ class HtmlTokenizer implements Iterator { output = '$output${slice(charStack, entityLen).join()}'; } } else { - _addToken(ParseErrorToken('expected-named-entity')); + if (!fromAttribute) { + // Only emit this error token when we're consuming this NOT as part of an attribute. + // See: https://html.spec.whatwg.org/multipage/parsing.html#ambiguous-ampersand-state + _addToken(ParseErrorToken('expected-named-entity')); + } stream.unget(charStack.removeLast()); output = '&${charStack.join()}'; } diff --git a/pkgs/html/pubspec.yaml b/pkgs/html/pubspec.yaml index 447b98e18..7508588ad 100644 --- a/pkgs/html/pubspec.yaml +++ b/pkgs/html/pubspec.yaml @@ -1,5 +1,5 @@ name: html -version: 0.15.5 +version: 0.15.5+1 description: APIs for parsing and manipulating HTML content outside the browser. repository: https://github.com/dart-lang/tools/tree/main/pkgs/html issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Ahtml diff --git a/pkgs/html/test/data/tokenizer/test4.test b/pkgs/html/test/data/tokenizer/test4.test index c0f3b2b8c..6c0a77ce1 100644 --- a/pkgs/html/test/data/tokenizer/test4.test +++ b/pkgs/html/test/data/tokenizer/test4.test @@ -28,6 +28,10 @@ "input":"", "output":["ParseError", "ParseError", "ParseError", ["StartTag", "z", {"=": "=="}]]}, +{"description":"Ambiguous ampersand in attribute value", +"input":"", +"output":[["StartTag", "tag", {"attr": "foo?a=b&c=d"}]]}, + {"description":"Allowed \" after ampersand in attribute value", "input":"", "output":[["StartTag", "z", {"z": "&"}]]}, diff --git a/pkgs/html/test/parser_feature_test.dart b/pkgs/html/test/parser_feature_test.dart index 7156146e0..df9172ee7 100644 --- a/pkgs/html/test/parser_feature_test.dart +++ b/pkgs/html/test/parser_feature_test.dart @@ -148,6 +148,18 @@ On line 4, column 3 of ParseError: Unexpected DOCTYPE. Ignored. expect(elem.attributeSpans!['extends'], null); }); + test('attribute spans if value contains & (ambiguous ampersand)', () { + final expectedUrl = 'foo?key=value&key2=value2'; + final text = ''; + + final doc = parse(text, generateSpans: true); + final elem = doc.querySelector('script')!; + final span = elem.attributeValueSpans!['src']!; + + expect(span.start.offset, text.indexOf('foo')); + expect(span.text, expectedUrl); + }); + test('void element innerHTML', () { var doc = parse('
'); expect(doc.body!.innerHtml, '
');