Fix HTML escape issues (#484)
diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c6672f..38d261b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ character references. * Add a new syntax `SoftLineBreakSyntax` to remove the single space before the line ending. +* Add a new syntax `EscapeHtmlSyntax` to encode (`"`), (`<`), (`>`) and (`&`). * Add an option `caseSensitive` to `TextSyntax`. ## 6.0.1
diff --git a/benchmark/output.html b/benchmark/output.html index 16689fd..e3831bd 100644 --- a/benchmark/output.html +++ b/benchmark/output.html
@@ -7,58 +7,58 @@ sample of real-world markdown:</p> <p>Tests are specified using the top-level <a href="https://pub.dev/documentation/test_core/latest/test_core/test.html"><code>test()</code></a> function, and test assertions are made using <a href="https://pub.dev/documentation/test_api/latest/test_api/expect.html"><code>expect()</code></a>:</p> -<pre><code class="language-dart">import "package:test/test.dart"; +<pre><code class="language-dart">import "package:test/test.dart"; void main() { - test("String.split() splits the string on the delimiter", () { - var string = "foo,bar,baz"; - expect(string.split(","), equals(["foo", "bar", "baz"])); + test("String.split() splits the string on the delimiter", () { + var string = "foo,bar,baz"; + expect(string.split(","), equals(["foo", "bar", "baz"])); }); - test("String.trim() removes surrounding whitespace", () { - var string = " foo "; - expect(string.trim(), equals("foo")); + test("String.trim() removes surrounding whitespace", () { + var string = " foo "; + expect(string.trim(), equals("foo")); }); } </code></pre> <p>Tests can be grouped together using the [<code>group()</code>] function. Each group's description is added to the beginning of its test's descriptions.</p> -<pre><code class="language-dart">import "package:test/test.dart"; +<pre><code class="language-dart">import "package:test/test.dart"; void main() { - group("String", () { - test(".split() splits the string on the delimiter", () { - var string = "foo,bar,baz"; - expect(string.split(","), equals(["foo", "bar", "baz"])); + group("String", () { + test(".split() splits the string on the delimiter", () { + var string = "foo,bar,baz"; + expect(string.split(","), equals(["foo", "bar", "baz"])); }); - test(".trim() removes surrounding whitespace", () { - var string = " foo "; - expect(string.trim(), equals("foo")); + test(".trim() removes surrounding whitespace", () { + var string = " foo "; + expect(string.trim(), equals("foo")); }); }); - group("int", () { - test(".remainder() returns the remainder of division", () { + group("int", () { + test(".remainder() returns the remainder of division", () { expect(11.remainder(3), equals(2)); }); - test(".toRadixString() returns a hex string", () { - expect(11.toRadixString(16), equals("b")); + test(".toRadixString() returns a hex string", () { + expect(11.toRadixString(16), equals("b")); }); }); } </code></pre> <p>Any matchers from the <a href="https://pub.dev/documentation/matcher/latest/matcher/matcher-library.html"><code>matcher</code></a> package can be used with <code>expect()</code> to do complex validations:</p> -<pre><code class="language-dart">import "package:test/test.dart"; +<pre><code class="language-dart">import "package:test/test.dart"; void main() { - test(".split() splits the string on the delimiter", () { - expect("foo,bar,baz", allOf([ - contains("foo"), - isNot(startsWith("bar")), - endsWith("baz") + test(".split() splits the string on the delimiter", () { + expect("foo,bar,baz", allOf([ + contains("foo"), + isNot(startsWith("bar")), + endsWith("baz") ])); }); } @@ -66,9 +66,9 @@ <h2>Running Tests</h2> <p>A single test file can be run just using <code>pub run test:test path/to/test.dart</code> (on Dart 1.10, this can be shortened to <code>pub run test path/to/test.dart</code>).</p> -<p><img src="https://raw.githubusercontent.com/dart-lang/test/master/image/test1.gif" alt="Single file being run via pub run"" /></p> +<p><img src="https://raw.githubusercontent.com/dart-lang/test/master/image/test1.gif" alt="Single file being run via pub run"" /></p> <p>Many tests can be run at a time using <code>pub run test:test path/to/dir</code>.</p> -<p><img src="https://raw.githubusercontent.com/dart-lang/test/master/image/test2.gif" alt="Directory being run via "pub run"." /></p> +<p><img src="https://raw.githubusercontent.com/dart-lang/test/master/image/test2.gif" alt="Directory being run via "pub run"." /></p> <p>It's also possible to run a test on the Dart VM only by invoking it using <code>dart path/to/test.dart</code>, but this doesn't load the full test runner and will be missing some features.</p> <p>The test runner considers any file that ends with <code>_test.dart</code> to be a test @@ -78,7 +78,7 @@ well by passing <code>pub run test:test -p chrome path/to/test.dart</code>. <code>test</code> will take care of starting the browser and loading the tests, and all the results will be reported on the command line just like for VM tests. In -fact, you can even run tests on both platforms with a single command: <code>pub run test:test -p "chrome,vm" path/to/test.dart</code>.</p> +fact, you can even run tests on both platforms with a single command: <code>pub run test:test -p "chrome,vm" path/to/test.dart</code>.</p> <h3>Restricting Tests to Certain Platforms</h3> <p>Some test files only make sense to run on particular platforms. They may use <code>dart:html</code> or <code>dart:io</code>, they might test Windows' particular filesystem @@ -86,17 +86,17 @@ <a href="https://pub.dev/documentation/test_api/latest/test_api/TestOn-class.html"><code>@TestOn</code></a> annotation makes it easy to declare exactly which platforms a test file should run on. Just put it at the top of your file, before any <code>library</code> or <code>import</code> declarations:</p> -<pre><code class="language-dart">@TestOn("vm") +<pre><code class="language-dart">@TestOn("vm") -import "dart:io"; +import "dart:io"; -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { // ... } </code></pre> -<p>The string you pass to <code>@TestOn</code> is what's called a "platform selector", and it +<p>The string you pass to <code>@TestOn</code> is what's called a "platform selector", and it specifies exactly which platforms a test can run on. It can be as simple as the name of a platform, or a more complex Dart-like boolean expression involving these platform names.</p> @@ -171,16 +171,16 @@ </li> </ul> <p>For example, if you wanted to run a test on every browser but Chrome, you would -write <code>@TestOn("browser && !chrome")</code>.</p> +write <code>@TestOn("browser && !chrome")</code>.</p> <h2>Asynchronous Tests</h2> <p>Tests written with <code>async</code>/<code>await</code> will work automatically. The test runner won't consider the test finished until the returned <code>Future</code> completes.</p> -<pre><code class="language-dart">import "dart:async"; +<pre><code class="language-dart">import "dart:async"; -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { - test("new Future.value() returns the value", () async { + test("new Future.value() returns the value", () async { var value = await new Future.value(10); expect(value, equals(10)); }); @@ -190,12 +190,12 @@ asynchrony. The <a href="https://pub.dev/documentation/test_api/latest/test_api/completion.html"><code>completion()</code></a> matcher can be used to test <code>Futures</code>; it ensures that the test doesn't finish until the <code>Future</code> completes, and runs a matcher against that <code>Future</code>'s value.</p> -<pre><code class="language-dart">import "dart:async"; +<pre><code class="language-dart">import "dart:async"; -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { - test("new Future.value() returns the value", () { + test("new Future.value() returns the value", () { expect(new Future.value(10), completion(equals(10))); }); } @@ -203,14 +203,14 @@ <p>The <a href="https://pub.dev/documentation/test_api/latest/test_api/throwsA.html"><code>throwsA()</code></a> matcher and the various <code>throwsExceptionType</code> matchers work with both synchronous callbacks and asynchronous <code>Future</code>s. They ensure that a particular type of exception is thrown:</p> -<pre><code class="language-dart">import "dart:async"; +<pre><code class="language-dart">import "dart:async"; -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { - test("new Future.error() throws the error", () { - expect(new Future.error("oh no"), throwsA(equals("oh no"))); - expect(new Future.error(new StateError("bad state")), throwsStateError); + test("new Future.error() throws the error", () { + expect(new Future.error("oh no"), throwsA(equals("oh no"))); + expect(new Future.error(new StateError("bad state")), throwsStateError); }); } </code></pre> @@ -219,12 +219,12 @@ times, and will cause the test to fail if it's called too often; second, it keeps the test from finishing until the function is called the requisite number of times.</p> -<pre><code class="language-dart">import "dart:async"; +<pre><code class="language-dart">import "dart:async"; -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { - test("Stream.fromIterable() emits the values in the iterable", () { + test("Stream.fromIterable() emits the values in the iterable", () { var stream = new Stream.fromIterable([1, 2, 3]); stream.listen(expectAsync((number) { @@ -242,11 +242,11 @@ <p>They must have the same name as the test, with <code>.dart</code> replaced by <code>.html</code>.</p> </li> <li> -<p>They must contain a <code>link</code> tag with <code>rel="x-dart-test"</code> and an <code>href</code> +<p>They must contain a <code>link</code> tag with <code>rel="x-dart-test"</code> and an <code>href</code> attribute pointing to the test script.</p> </li> <li> -<p>They must contain <code><script src="packages/test/dart.js"></script></code>.</p> +<p>They must contain <code><script src="packages/test/dart.js"></script></code>.</p> </li> </ul> <p>For example, if you had a test called <code>custom_html_test.dart</code>, you might write @@ -256,8 +256,8 @@ <html> <head> <title>Custom HTML Test</title> - <link rel="x-dart-test" href="custom_html_test.dart"> - <script src="packages/test/dart.js"></script> + <link rel="x-dart-test" href="custom_html_test.dart"> + <script src="packages/test/dart.js"></script> </head> <body> // ... @@ -267,15 +267,15 @@ <h2>Configuring Tests</h2> <h3>Skipping Tests</h3> <p>If a test, group, or entire suite isn't working yet and you just want it to stop -complaining, you can mark it as "skipped". The test or tests won't be run, and, +complaining, you can mark it as "skipped". The test or tests won't be run, and, if you supply a reason why, that reason will be printed. In general, skipping tests indicates that they should run but is temporarily not working. If they're is fundamentally incompatible with a platform, <a href="https://pub.dev/documentation/test_api/latest/test_api/TestOn-class.html"><code>@TestOn</code>/<code>testOn</code></a> should be used instead.</p> <p>To skip a test suite, put a <code>@Skip</code> annotation at the top of the file:</p> -<pre><code class="language-dart">@Skip("currently failing (see issue 1234)") +<pre><code class="language-dart">@Skip("currently failing (see issue 1234)") -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { // ... @@ -285,16 +285,16 @@ include it, but it's a good idea to document why the test isn't running.</p> <p>Groups and individual tests can be skipped by passing the <code>skip</code> parameter. This can be either <code>true</code> or a String describing why the test is skipped. For example:</p> -<pre><code class="language-dart">import "package:test/test.dart"; +<pre><code class="language-dart">import "package:test/test.dart"; void main() { - group("complicated algorithm tests", () { + group("complicated algorithm tests", () { // ... - }, skip: "the algorithm isn't quite right"); + }, skip: "the algorithm isn't quite right"); - test("error-checking test", () { + test("error-checking test", () { // ... - }, skip: "TODO: add error-checking."); + }, skip: "TODO: add error-checking."); } </code></pre> <h3>Timeouts</h3> @@ -303,7 +303,7 @@ for a test suite, put a <code>@Timeout</code> annotation at the top of the file:</p> <pre><code class="language-dart">@Timeout(const Duration(seconds: 45)) -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { // ... @@ -314,20 +314,20 @@ set the timeout to one and a half times as long as the default—45 seconds.</p> <p>Timeouts can be set for tests and groups using the <code>timeout</code> parameter. This parameter takes a <code>Timeout</code> object just like the annotation. For example:</p> -<pre><code class="language-dart">import "package:test/test.dart"; +<pre><code class="language-dart">import "package:test/test.dart"; void main() { - group("slow tests", () { + group("slow tests", () { // ... - test("even slower test", () { + test("even slower test", () { // ... }, timeout: new Timeout.factor(2)) }, timeout: new Timeout(new Duration(minutes: 1))); } </code></pre> <p>Nested timeouts apply in order from outermost to innermost. That means that -"even slower test" will take two minutes to time out, since it multiplies the +"even slower test" will take two minutes to time out, since it multiplies the group's timeout by 2.</p> <h3>Platform-Specific Configuration</h3> <p>Sometimes a test may need to be configured differently for different platforms. @@ -337,16 +337,16 @@ and <code>group()</code>. For example:</p> <pre><code class="language-dart">@OnPlatform(const { // Give Windows some extra wiggle-room before timing out. - "windows": const Timeout.factor(2) + "windows": const Timeout.factor(2) }) -import "package:test/test.dart"; +import "package:test/test.dart"; void main() { - test("do a thing", () { + test("do a thing", () { // ... }, onPlatform: { - "safari": new Skip("Safari is currently broken (see #1234)") + "safari": new Skip("Safari is currently broken (see #1234)") }); } </code></pre>
diff --git a/lib/markdown.dart b/lib/markdown.dart index 62d7576..3e435af 100644 --- a/lib/markdown.dart +++ b/lib/markdown.dart
@@ -72,6 +72,7 @@ export 'src/inline_syntaxes/email_autolink_syntax.dart'; export 'src/inline_syntaxes/emoji_syntax.dart'; export 'src/inline_syntaxes/emphasis_syntax.dart'; +export 'src/inline_syntaxes/escape_html_syntax.dart'; export 'src/inline_syntaxes/escape_syntax.dart'; export 'src/inline_syntaxes/image_syntax.dart'; export 'src/inline_syntaxes/inline_html_syntax.dart';
diff --git a/lib/src/inline_parser.dart b/lib/src/inline_parser.dart index c9ebbb6..d69af2b 100644 --- a/lib/src/inline_parser.dart +++ b/lib/src/inline_parser.dart
@@ -11,6 +11,7 @@ import 'inline_syntaxes/delimiter_syntax.dart'; import 'inline_syntaxes/email_autolink_syntax.dart'; import 'inline_syntaxes/emphasis_syntax.dart'; +import 'inline_syntaxes/escape_html_syntax.dart'; import 'inline_syntaxes/escape_syntax.dart'; import 'inline_syntaxes/image_syntax.dart'; import 'inline_syntaxes/inline_syntax.dart'; @@ -27,8 +28,6 @@ EmailAutolinkSyntax(), AutolinkSyntax(), LineBreakSyntax(), - // Allow any punctuation to be escaped. - EscapeSyntax(), // "*" surrounded by spaces is left alone. TextSyntax(r' \* ', startCharacter: $space), // "_" surrounded by spaces is left alone. @@ -47,12 +46,6 @@ // Leave already-encoded HTML entities alone. Ensures we don't turn // "&" into "&amp;" TextSyntax('&[#a-zA-Z0-9]*;', startCharacter: $ampersand), - // Encode "&". - TextSyntax('&', sub: '&', startCharacter: $ampersand), - // Encode "<". - TextSyntax('<', sub: '<', startCharacter: $lt), - // Encode ">". - TextSyntax('>', sub: '>', startCharacter: $gt), ]); /// The string of Markdown being parsed. @@ -95,6 +88,7 @@ if (document.withDefaultInlineSyntaxes) { // Custom link resolvers go after the generic text syntax. syntaxes.addAll([ + EscapeSyntax(), DecodeHtmlSyntax(), LinkSyntax(linkResolver: document.linkResolver), ImageSyntax(linkResolver: document.imageLinkResolver) @@ -104,6 +98,10 @@ } if (encodeHtml) { + syntaxes.add(EscapeHtmlSyntax()); + } + + if (encodeHtml) { syntaxes.addAll(_htmlSyntaxes); } }
diff --git a/lib/src/inline_syntaxes/autolink_syntax.dart b/lib/src/inline_syntaxes/autolink_syntax.dart index 05eb41c..073c1cc 100644 --- a/lib/src/inline_syntaxes/autolink_syntax.dart +++ b/lib/src/inline_syntaxes/autolink_syntax.dart
@@ -16,7 +16,10 @@ final url = match[1]!; final text = parser.encodeHtml ? escapeHtml(url) : url; final anchor = Element.text('a', text); - anchor.attributes['href'] = Uri.encodeFull(url); + + final destination = normalizeLinkDestination(url); + anchor.attributes['href'] = + parser.encodeHtml ? escapeHtml(destination) : destination; parser.addNode(anchor); return true;
diff --git a/lib/src/inline_syntaxes/decode_html_syntax.dart b/lib/src/inline_syntaxes/decode_html_syntax.dart index 348a6b2..1af15d5 100644 --- a/lib/src/inline_syntaxes/decode_html_syntax.dart +++ b/lib/src/inline_syntaxes/decode_html_syntax.dart
@@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import '../assets/html_entities.dart'; import '../ast.dart'; import '../charcode.dart'; import '../inline_parser.dart'; @@ -27,9 +28,7 @@ return false; } - // TODO(Zhiguang): Enable HTML entity decoding when working on HTML escape - // issues. - if (match[1] != null) { + if (match[1] != null && htmlEntitiesMap[match.match] == null) { return false; } @@ -40,7 +39,11 @@ @override bool onMatch(InlineParser parser, Match match) { - final decodedText = decodeHtmlCharacterFromMatch(match); + var decodedText = decodeHtmlCharacterFromMatch(match); + + if (parser.encodeHtml) { + decodedText = escapeHtml(decodedText); + } parser.addNode(Text(decodedText)); return true;
diff --git a/lib/src/inline_syntaxes/escape_html_syntax.dart b/lib/src/inline_syntaxes/escape_html_syntax.dart new file mode 100644 index 0000000..565f870 --- /dev/null +++ b/lib/src/inline_syntaxes/escape_html_syntax.dart
@@ -0,0 +1,20 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../ast.dart'; +import '../inline_parser.dart'; +import '../util.dart'; +import 'inline_syntax.dart'; + +/// Encodes (`"`), (`<`), (`>`) and (`&`). +class EscapeHtmlSyntax extends InlineSyntax { + EscapeHtmlSyntax() : super('["<>&]'); + @override + bool onMatch(InlineParser parser, Match match) { + final text = escapeHtml(match[0]!); + parser.addNode(Text(text)); + + return true; + } +}
diff --git a/lib/src/inline_syntaxes/escape_syntax.dart b/lib/src/inline_syntaxes/escape_syntax.dart index 48a6426..30e6ea4 100644 --- a/lib/src/inline_syntaxes/escape_syntax.dart +++ b/lib/src/inline_syntaxes/escape_syntax.dart
@@ -5,34 +5,29 @@ import '../ast.dart'; import '../charcode.dart'; import '../inline_parser.dart'; +import '../patterns.dart'; import '../util.dart'; import 'inline_syntax.dart'; -/// Escape punctuation preceded by a backslash. +/// Escape ASCII punctuation preceded by a backslash. +/// +/// Backslashes before other characters are treated as literal backslashes. +// See https://spec.commonmark.org/0.30/#backslash-escapes. class EscapeSyntax extends InlineSyntax { - EscapeSyntax() : super(r'''\\[!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]'''); + EscapeSyntax() + : super('\\\\([$asciiPunctuationEscaped])', startCharacter: $backslash); @override bool onMatch(InlineParser parser, Match match) { final chars = match.match; - final char = chars.codeUnitAt(1); - // Insert the substitution. Why these three charactes are replaced with - // their equivalent HTML entity referenced appears to be missing from the - // CommonMark spec, but is very present in all of the examples. - // https://talk.commonmark.org/t/entity-ification-of-quotes-and-brackets-missing-from-spec/3207 - if (parser.encodeHtml) { - if (char == $double_quote) { - parser.addNode(Text('"')); - } else if (char == $lt) { - parser.addNode(Text('<')); - } else if (char == $gt) { - parser.addNode(Text('>')); - } else { - parser.addNode(Text(chars[1])); - } + + if ('&"<>'.contains(match[1]!) && parser.encodeHtml) { + final text = escapeHtml(match[1]!); + parser.addNode(Text(text)); + return true; } else { parser.addNode(Text(chars[1])); + return true; } - return true; } }
diff --git a/lib/src/inline_syntaxes/image_syntax.dart b/lib/src/inline_syntaxes/image_syntax.dart index 3c13679..cb47ae0 100644 --- a/lib/src/inline_syntaxes/image_syntax.dart +++ b/lib/src/inline_syntaxes/image_syntax.dart
@@ -27,8 +27,7 @@ element.attributes['src'] = destination; element.attributes['alt'] = children.map((node) => node.textContent).join(); if (title != null && title.isNotEmpty) { - element.attributes['title'] = - escapeAttribute(title.replaceAll('&', '&')); + element.attributes['title'] = normalizeLinkTitle(title); } return element; }
diff --git a/lib/src/inline_syntaxes/link_syntax.dart b/lib/src/inline_syntaxes/link_syntax.dart index 0546b93..bc47b49 100644 --- a/lib/src/inline_syntaxes/link_syntax.dart +++ b/lib/src/inline_syntaxes/link_syntax.dart
@@ -152,9 +152,13 @@ }) { final children = getChildren(); final element = Element('a', children); - element.attributes['href'] = escapeAttribute(destination); + element.attributes['href'] = normalizeLinkDestination( + escapePunctuation(destination), + ); if (title != null && title.isNotEmpty) { - element.attributes['title'] = escapeAttribute(title); + element.attributes['title'] = normalizeLinkTitle( + escapePunctuation(title), + ); } return element; }
diff --git a/lib/src/patterns.dart b/lib/src/patterns.dart index a99cc5e..269689f 100644 --- a/lib/src/patterns.dart +++ b/lib/src/patterns.dart
@@ -202,9 +202,13 @@ caseSensitive: false); /// ASCII punctuation characters. -// see https://spec.commonmark.org/0.30/#unicode-whitespace-character. +// See https://spec.commonmark.org/0.30/#unicode-whitespace-character. const asciiPunctuationCharacters = r'''!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~'''; +/// ASCII punctuation characters with some characters escaped, in order to be +// used in the RegExp character set. +const asciiPunctuationEscaped = r'''!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~'''; + /// A pattern to match HTML entity references and numeric character references. // https://spec.commonmark.org/0.30/#entity-and-numeric-character-references final htmlCharactersPattern = RegExp(
diff --git a/lib/src/util.dart b/lib/src/util.dart index a4861e2..02e26ab 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart
@@ -9,81 +9,20 @@ import 'charcode.dart'; import 'patterns.dart'; -String escapeHtml(String html) => - const HtmlEscape(HtmlEscapeMode.element).convert(html); - -String escapeHtmlAttribute(String text) => - const HtmlEscape(HtmlEscapeMode.attribute).convert(text); - -/// Escapes the contents of [value], so that it may be used as an HTML -/// attribute. -/// -/// Based on http://spec.commonmark.org/0.28/#backslash-escapes. -String escapeAttribute(String value) { - final result = StringBuffer(); - int ch; - for (var i = 0; i < value.codeUnits.length; i++) { - ch = value.codeUnitAt(i); - if (ch == $backslash) { - i++; - if (i == value.codeUnits.length) { - result.writeCharCode(ch); - break; - } - ch = value.codeUnitAt(i); - switch (ch) { - case $quote: - result.write('"'); - break; - case $exclamation: - case $hash: - case $dollar: - case $percent: - case $ampersand: - case $apostrophe: - case $lparen: - case $rparen: - case $asterisk: - case $plus: - case $comma: - case $dash: - case $dot: - case $slash: - case $colon: - case $semicolon: - case $lt: - case $equal: - case $gt: - case $question: - case $at: - case $lbracket: - case $backslash: - case $rbracket: - case $caret: - case $underscore: - case $backquote: - case $lbrace: - case $bar: - case $rbrace: - case $tilde: - result.writeCharCode(ch); - break; - default: - result.write('%5C'); - result.writeCharCode(ch); - } - } else if (ch == $quote) { - result.write('%22'); - } else { - result.writeCharCode(ch); - } - } - return result.toString(); -} - /// One or more whitespace, for compressing. final _oneOrMoreWhitespacePattern = RegExp('[ \n\r\t]+'); +/// Escapes (`'`), (`"`), (`<`), (`>`) and (`&`) characters. +String escapeHtml(String html) => const HtmlEscape(HtmlEscapeMode( + escapeApos: true, + escapeLtGt: true, + escapeQuot: true, + )).convert(html); + +/// Escapes (`"`), (`<`) and (`>`) characters. +String escapeHtmlAttribute(String text) => + const HtmlEscape(HtmlEscapeMode.attribute).convert(text); + /// "Normalizes" a link label, according to the [CommonMark spec]. /// /// [CommonMark spec] https://spec.commonmark.org/0.30/#link-label @@ -98,6 +37,34 @@ return text; } +/// Normalizes a link destination, including the process of HTML characters +/// decoding and percent encoding. +// See the description of these examples: +// https://spec.commonmark.org/0.30/#example-501 +// https://spec.commonmark.org/0.30/#example-502 +String normalizeLinkDestination(String destination) { + // Decode first, because the destination might have been partly encoded. + // For example https://spec.commonmark.org/0.30/#example-502. + // With this function, `foo%20bä` will be parsed in the following steps: + // 1. foo bä + // 2. foo bä + // 3. foo%20b%C3%A4 + try { + destination = Uri.decodeFull(destination); + } catch (_) {} + return Uri.encodeFull(decodeHtmlCharacters(destination)); +} + +/// Normalizes a link title, including the process of HTML characters decoding +/// and HTML characters escaping. +// See the description of these examples: +// https://spec.commonmark.org/0.30/#example-505 +// https://spec.commonmark.org/0.30/#example-506 +// https://spec.commonmark.org/0.30/#example-507 +// https://spec.commonmark.org/0.30/#example-508 +String normalizeLinkTitle(String title) => + escapeHtmlAttribute(decodeHtmlCharacters(title)); + /// Decodes HTML entity and numeric character references, for example decode /// `#` to `#`. String decodeHtmlCharacters(String input) =>
diff --git a/test/common_mark/autolinks.unit b/test/common_mark/autolinks.unit index 5547760..8b5b887 100644 --- a/test/common_mark/autolinks.unit +++ b/test/common_mark/autolinks.unit
@@ -5,7 +5,7 @@ >>> Autolinks - 594 <http://foo.bar.baz/test?q=hello&id=22&boolean> <<< -<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p> +<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p> >>> Autolinks - 595 <irc://foo.bar:2233/baz> <<<
diff --git a/test/common_mark/backslash_escapes.unit b/test/common_mark/backslash_escapes.unit index 7042aa8..2c499ed 100644 --- a/test/common_mark/backslash_escapes.unit +++ b/test/common_mark/backslash_escapes.unit
@@ -1,7 +1,7 @@ >>> Backslash escapes - 12 \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~ <<< -<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p> +<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p> >>> Backslash escapes - 13 \ \A\a\ \3\φ\« <<< @@ -18,14 +18,14 @@ \ö not a character entity <<< <p>*not emphasized* -<br/> not a tag +<br/> not a tag [not a link](/foo) `not code` 1. not a list * not a list # not a heading -[foo]: /url "not a reference" -ö not a character entity</p> +[foo]: /url "not a reference" +&ouml; not a character entity</p> >>> Backslash escapes - 15 \\*emphasis* <<<
diff --git a/test/common_mark/code_spans.unit b/test/common_mark/code_spans.unit index 6864bf7..4c664ab 100644 --- a/test/common_mark/code_spans.unit +++ b/test/common_mark/code_spans.unit
@@ -70,7 +70,7 @@ >>> Code spans - 343 `<a href="`">` <<< -<p><code><a href="</code>">`</p> +<p><code><a href="</code>">`</p> >>> Code spans - 344 <a href="`">` <<<
diff --git a/test/common_mark/emphasis_and_strong_emphasis.unit b/test/common_mark/emphasis_and_strong_emphasis.unit index 67155bf..514b651 100644 --- a/test/common_mark/emphasis_and_strong_emphasis.unit +++ b/test/common_mark/emphasis_and_strong_emphasis.unit
@@ -9,7 +9,7 @@ >>> Emphasis and strong emphasis - 352 a*"foo"* <<< -<p>a*"foo"*</p> +<p>a*"foo"*</p> >>> Emphasis and strong emphasis - 353 * a * <<< @@ -33,7 +33,7 @@ >>> Emphasis and strong emphasis - 358 a_"foo"_ <<< -<p>a_"foo"_</p> +<p>a_"foo"_</p> >>> Emphasis and strong emphasis - 359 foo_bar_ <<< @@ -49,7 +49,7 @@ >>> Emphasis and strong emphasis - 362 aa_"bb"_cc <<< -<p>aa_"bb"_cc</p> +<p>aa_"bb"_cc</p> >>> Emphasis and strong emphasis - 363 foo-_(bar)_ <<< @@ -119,7 +119,7 @@ >>> Emphasis and strong emphasis - 379 a**"foo"** <<< -<p>a**"foo"**</p> +<p>a**"foo"**</p> >>> Emphasis and strong emphasis - 380 foo**bar** <<< @@ -141,7 +141,7 @@ >>> Emphasis and strong emphasis - 384 a__"foo"__ <<< -<p>a__"foo"__</p> +<p>a__"foo"__</p> >>> Emphasis and strong emphasis - 385 foo__bar__ <<< @@ -183,7 +183,7 @@ >>> Emphasis and strong emphasis - 394 **foo "*bar*" foo** <<< -<p><strong>foo "<em>bar</em>" foo</strong></p> +<p><strong>foo "<em>bar</em>" foo</strong></p> >>> Emphasis and strong emphasis - 395 **foo**bar <<<
diff --git a/test/common_mark/entity_and_numeric_character_references.unit b/test/common_mark/entity_and_numeric_character_references.unit index da96678..2859435 100644 --- a/test/common_mark/entity_and_numeric_character_references.unit +++ b/test/common_mark/entity_and_numeric_character_references.unit
@@ -3,9 +3,9 @@ ¾ ℋ ⅆ ∲ ≧̸ <<< -<p> & © Æ Ď -¾ ℋ ⅆ -∲ ≧̸</p> +<p>& © Æ Ď +¾ ℋ ⅆ +∲ ≧̸</p> >>> Entity and numeric character references - 26 # Ӓ Ϡ � <<< @@ -13,17 +13,17 @@ >>> Entity and numeric character references - 27 " ആ ಫ <<< -<p>" ആ ಫ</p> +<p>" ആ ಫ</p> >>> Entity and numeric character references - 28   &x; &#; &#x; � &#abcdef0; &ThisIsNotDefined; &hi?; <<< -<p>&nbsp &x; &#; &#x; -� -&#abcdef0; -&ThisIsNotDefined; &hi?;</p> +<p>&nbsp &x; &#; &#x; +&#87654321; +&#abcdef0; +&ThisIsNotDefined; &hi?;</p> >>> Entity and numeric character references - 29 © <<< @@ -31,7 +31,7 @@ >>> Entity and numeric character references - 30 &MadeUpEntity; <<< -<p>&MadeUpEntity;</p> +<p>&MadeUpEntity;</p> >>> Entity and numeric character references - 31 <a href="öö.html"> <<< @@ -39,13 +39,13 @@ >>> Entity and numeric character references - 32 [foo](/föö "föö") <<< -<p><a href="/föö" title="föö">foo</a></p> +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> >>> Entity and numeric character references - 33 [foo] [foo]: /föö "föö" <<< -<p><a href="/föö" title="föö">foo</a></p> +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> >>> Entity and numeric character references - 34 ``` föö foo
diff --git a/test/common_mark/images.unit b/test/common_mark/images.unit index f1cf602..158ca40 100644 --- a/test/common_mark/images.unit +++ b/test/common_mark/images.unit
@@ -100,7 +100,7 @@ [[foo]]: /url "title" <<< <p>![[foo]]</p> -<p>[[foo]]: /url "title"</p> +<p>[[foo]]: /url "title"</p> >>> Images - 590 ![Foo]
diff --git a/test/common_mark/link_reference_definitions.unit b/test/common_mark/link_reference_definitions.unit index 46bbcf1..8d9ae8f 100644 --- a/test/common_mark/link_reference_definitions.unit +++ b/test/common_mark/link_reference_definitions.unit
@@ -72,7 +72,7 @@ [foo] <<< -<p><a href="<>">foo</a></p> +<p><a href="%3C%3E">foo</a></p> >>> Link reference definitions - 201 [foo]: <bar>(baz) @@ -84,7 +84,7 @@ [foo] <<< -<p>[foo]: /url\bar*baz "foo"bar\baz"</p> +<p>[foo]: /url\bar*baz "foo"bar\baz"</p> <p>[foo]</p> >>> Link reference definitions - 203 [foo] @@ -110,7 +110,7 @@ [αγω] <<< -<p><a href="/φου">αγω</a></p> +<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p> >>> Link reference definitions - 207 [foo]: /url <<< @@ -125,18 +125,18 @@ >>> Link reference definitions - 209 [foo]: /url "title" ok <<< -<p>[foo]: /url "title" ok</p> +<p>[foo]: /url "title" ok</p> >>> Link reference definitions - 210 [foo]: /url "title" ok <<< -<p>"title" ok</p> +<p>"title" ok</p> >>> Link reference definitions - 211 [foo]: /url "title" [foo] <<< -<pre><code>[foo]: /url "title" +<pre><code>[foo]: /url "title" </code></pre> <p>[foo]</p> >>> Link reference definitions - 212
diff --git a/test/common_mark/links.unit b/test/common_mark/links.unit index 5e99f65..4015350 100644 --- a/test/common_mark/links.unit +++ b/test/common_mark/links.unit
@@ -99,7 +99,7 @@ >>> Links - 502 [link](foo%20bä) <<< -<p><a href="foo%20bä">link</a></p> +<p><a href="foo%20b%C3%A4">link</a></p> >>> Links - 503 [link]("title") <<< @@ -115,19 +115,19 @@ >>> Links - 505 [link](/url "title \""") <<< -<p><a href="/url" title="title %22"">link</a></p> +<p><a href="/url" title="title """>link</a></p> >>> Links - 506 [link](/url "title") <<< -<p><a href="/url %22title%22">link</a></p> +<p><a href="/url%C2%A0%22title%22">link</a></p> >>> Links - 507 [link](/url "title "and" title") <<< -<p>[link](/url "title "and" title")</p> +<p>[link](/url "title "and" title")</p> >>> Links - 508 [link](/url 'title "and" title') <<< -<p><a href="/url" title="title %22and%22 title">link</a></p> +<p><a href="/url" title="title "and" title">link</a></p> >>> Links - 509 [link]( /uri "title" )
diff --git a/test/common_mark/raw_html.unit b/test/common_mark/raw_html.unit index cde09c4..64315b0 100644 --- a/test/common_mark/raw_html.unit +++ b/test/common_mark/raw_html.unit
@@ -29,11 +29,11 @@ >>> Raw HTML - 618 <a h*#ref="hi"> <<< -<p><a h*#ref="hi"></p> +<p><a h*#ref="hi"></p> >>> Raw HTML - 619 <a href="hi'> <a href=hi'> <<< -<p><a href="hi'> <a href=hi'></p> +<p><a href="hi'> <a href=hi'></p> >>> Raw HTML - 620 < a>< foo><bar/ > @@ -43,7 +43,7 @@ <p>< a>< foo><bar/ > <foo bar=baz -bim!bop /></p> +bim!bop /></p> >>> Raw HTML - 621 <a href='bar'title=title> <<< @@ -55,7 +55,7 @@ >>> Raw HTML - 623 </a href="foo"> <<< -<p></a href="foo"></p> +<p></a href="foo"></p> >>> Raw HTML - 624 foo <!-- this is a comment - with hyphen --> @@ -96,4 +96,4 @@ >>> Raw HTML - 632 <a href="\""> <<< -<p><a href="""></p> +<p><a href="""></p>
diff --git a/test/common_mark/setext_headings.unit b/test/common_mark/setext_headings.unit index 8fae410..dca5f93 100644 --- a/test/common_mark/setext_headings.unit +++ b/test/common_mark/setext_headings.unit
@@ -99,8 +99,8 @@ <<< <h2>`Foo</h2> <p>`</p> -<h2><a title="a lot</h2> -<p>of dashes"/></p> +<h2><a title="a lot</h2> +<p>of dashes"/></p> >>> Setext headings - 92 > Foo ---
diff --git a/test/extensions/inline_html.unit b/test/extensions/inline_html.unit index d02d1fd..1540118 100644 --- a/test/extensions/inline_html.unit +++ b/test/extensions/inline_html.unit
@@ -14,4 +14,4 @@ Text <a href="_foo_">And "_foo_"</a>. <<< -<p>Text <a href="_foo_">And "<em>foo</em>"</a>.</p> +<p>Text <a href="_foo_">And "<em>foo</em>"</a>.</p>
diff --git a/test/gfm/autolinks.unit b/test/gfm/autolinks.unit index 4de9122..6df5296 100644 --- a/test/gfm/autolinks.unit +++ b/test/gfm/autolinks.unit
@@ -5,7 +5,7 @@ >>> Autolinks - 603 <http://foo.bar.baz/test?q=hello&id=22&boolean> <<< -<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p> +<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p> >>> Autolinks - 604 <irc://foo.bar:2233/baz> <<<
diff --git a/test/gfm/autolinks_extension.unit b/test/gfm/autolinks_extension.unit index 024fde1..ff69a53 100644 --- a/test/gfm/autolinks_extension.unit +++ b/test/gfm/autolinks_extension.unit
@@ -36,7 +36,7 @@ www.google.com/search?q=commonmark&hl; <<< <p><a href="http://www.google.com/search?q=commonmark&hl=en">www.google.com/search?q=commonmark&hl=en</a></p> -<p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&hl;</p> +<p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&hl;</p> >>> Autolinks (extension) - 627 www.commonmark.org/he<lp <<<
diff --git a/test/gfm/backslash_escapes.unit b/test/gfm/backslash_escapes.unit index f5cce5c..0e9074a 100644 --- a/test/gfm/backslash_escapes.unit +++ b/test/gfm/backslash_escapes.unit
@@ -1,7 +1,7 @@ >>> Backslash escapes - 308 \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~ <<< -<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p> +<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p> >>> Backslash escapes - 309 \ \A\a\ \3\φ\« <<< @@ -18,14 +18,14 @@ \ö not a character entity <<< <p>*not emphasized* -<br/> not a tag +<br/> not a tag [not a link](/foo) `not code` 1. not a list * not a list # not a heading -[foo]: /url "not a reference" -ö not a character entity</p> +[foo]: /url "not a reference" +&ouml; not a character entity</p> >>> Backslash escapes - 311 \\*emphasis* <<<
diff --git a/test/gfm/code_spans.unit b/test/gfm/code_spans.unit index f620371..ebf1f64 100644 --- a/test/gfm/code_spans.unit +++ b/test/gfm/code_spans.unit
@@ -70,7 +70,7 @@ >>> Code spans - 353 `<a href="`">` <<< -<p><code><a href="</code>">`</p> +<p><code><a href="</code>">`</p> >>> Code spans - 354 <a href="`">` <<<
diff --git a/test/gfm/emphasis_and_strong_emphasis.unit b/test/gfm/emphasis_and_strong_emphasis.unit index b4222a6..3cde638 100644 --- a/test/gfm/emphasis_and_strong_emphasis.unit +++ b/test/gfm/emphasis_and_strong_emphasis.unit
@@ -9,7 +9,7 @@ >>> Emphasis and strong emphasis - 362 a*"foo"* <<< -<p>a*"foo"*</p> +<p>a*"foo"*</p> >>> Emphasis and strong emphasis - 363 * a * <<< @@ -33,7 +33,7 @@ >>> Emphasis and strong emphasis - 368 a_"foo"_ <<< -<p>a_"foo"_</p> +<p>a_"foo"_</p> >>> Emphasis and strong emphasis - 369 foo_bar_ <<< @@ -49,7 +49,7 @@ >>> Emphasis and strong emphasis - 372 aa_"bb"_cc <<< -<p>aa_"bb"_cc</p> +<p>aa_"bb"_cc</p> >>> Emphasis and strong emphasis - 373 foo-_(bar)_ <<< @@ -119,7 +119,7 @@ >>> Emphasis and strong emphasis - 389 a**"foo"** <<< -<p>a**"foo"**</p> +<p>a**"foo"**</p> >>> Emphasis and strong emphasis - 390 foo**bar** <<< @@ -141,7 +141,7 @@ >>> Emphasis and strong emphasis - 394 a__"foo"__ <<< -<p>a__"foo"__</p> +<p>a__"foo"__</p> >>> Emphasis and strong emphasis - 395 foo__bar__ <<< @@ -183,7 +183,7 @@ >>> Emphasis and strong emphasis - 404 **foo "*bar*" foo** <<< -<p><strong>foo "<em>bar</em>" foo</strong></p> +<p><strong>foo "<em>bar</em>" foo</strong></p> >>> Emphasis and strong emphasis - 405 **foo**bar <<<
diff --git a/test/gfm/entity_and_numeric_character_references.unit b/test/gfm/entity_and_numeric_character_references.unit index be80572..2a6248e 100644 --- a/test/gfm/entity_and_numeric_character_references.unit +++ b/test/gfm/entity_and_numeric_character_references.unit
@@ -3,9 +3,9 @@ ¾ ℋ ⅆ ∲ ≧̸ <<< -<p> & © Æ Ď -¾ ℋ ⅆ -∲ ≧̸</p> +<p>& © Æ Ď +¾ ℋ ⅆ +∲ ≧̸</p> >>> Entity and numeric character references - 322 # Ӓ Ϡ � <<< @@ -13,17 +13,17 @@ >>> Entity and numeric character references - 323 " ആ ಫ <<< -<p>" ആ ಫ</p> +<p>" ആ ಫ</p> >>> Entity and numeric character references - 324   &x; &#; &#x; � &#abcdef0; &ThisIsNotDefined; &hi?; <<< -<p>&nbsp &x; &#; &#x; -� -&#abcdef0; -&ThisIsNotDefined; &hi?;</p> +<p>&nbsp &x; &#; &#x; +&#987654321; +&#abcdef0; +&ThisIsNotDefined; &hi?;</p> >>> Entity and numeric character references - 325 © <<< @@ -31,7 +31,7 @@ >>> Entity and numeric character references - 326 &MadeUpEntity; <<< -<p>&MadeUpEntity;</p> +<p>&MadeUpEntity;</p> >>> Entity and numeric character references - 327 <a href="öö.html"> <<< @@ -39,13 +39,13 @@ >>> Entity and numeric character references - 328 [foo](/föö "föö") <<< -<p><a href="/föö" title="föö">foo</a></p> +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> >>> Entity and numeric character references - 329 [foo] [foo]: /föö "föö" <<< -<p><a href="/föö" title="föö">foo</a></p> +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> >>> Entity and numeric character references - 330 ``` föö foo
diff --git a/test/gfm/images.unit b/test/gfm/images.unit index 838a07c..e0bfb90 100644 --- a/test/gfm/images.unit +++ b/test/gfm/images.unit
@@ -100,7 +100,7 @@ [[foo]]: /url "title" <<< <p>![[foo]]</p> -<p>[[foo]]: /url "title"</p> +<p>[[foo]]: /url "title"</p> >>> Images - 599 ![Foo]
diff --git a/test/gfm/link_reference_definitions.unit b/test/gfm/link_reference_definitions.unit index 16f86fb..442c431 100644 --- a/test/gfm/link_reference_definitions.unit +++ b/test/gfm/link_reference_definitions.unit
@@ -72,7 +72,7 @@ [foo] <<< -<p><a href="<>">foo</a></p> +<p><a href="%3C%3E">foo</a></p> >>> Link reference definitions - 170 [foo]: <bar>(baz) @@ -84,7 +84,7 @@ [foo] <<< -<p>[foo]: /url\bar*baz "foo"bar\baz"</p> +<p>[foo]: /url\bar*baz "foo"bar\baz"</p> <p>[foo]</p> >>> Link reference definitions - 172 [foo] @@ -110,7 +110,7 @@ [αγω] <<< -<p><a href="/φου">αγω</a></p> +<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p> >>> Link reference definitions - 176 [foo]: /url <<< @@ -125,18 +125,18 @@ >>> Link reference definitions - 178 [foo]: /url "title" ok <<< -<p>[foo]: /url "title" ok</p> +<p>[foo]: /url "title" ok</p> >>> Link reference definitions - 179 [foo]: /url "title" ok <<< -<p>"title" ok</p> +<p>"title" ok</p> >>> Link reference definitions - 180 [foo]: /url "title" [foo] <<< -<pre><code>[foo]: /url "title" +<pre><code>[foo]: /url "title" </code></pre> <p>[foo]</p> >>> Link reference definitions - 181
diff --git a/test/gfm/links.unit b/test/gfm/links.unit index b2d8e7f..38a861e 100644 --- a/test/gfm/links.unit +++ b/test/gfm/links.unit
@@ -87,7 +87,7 @@ >>> Links - 511 [link](foo%20bä) <<< -<p><a href="foo%20bä">link</a></p> +<p><a href="foo%20b%C3%A4">link</a></p> >>> Links - 512 [link]("title") <<< @@ -103,19 +103,19 @@ >>> Links - 514 [link](/url "title \""") <<< -<p><a href="/url" title="title %22"">link</a></p> +<p><a href="/url" title="title """>link</a></p> >>> Links - 515 [link](/url "title") <<< -<p><a href="/url %22title%22">link</a></p> +<p><a href="/url%C2%A0%22title%22">link</a></p> >>> Links - 516 [link](/url "title "and" title") <<< -<p>[link](/url "title "and" title")</p> +<p>[link](/url "title "and" title")</p> >>> Links - 517 [link](/url 'title "and" title') <<< -<p><a href="/url" title="title %22and%22 title">link</a></p> +<p><a href="/url" title="title "and" title">link</a></p> >>> Links - 518 [link]( /uri "title" )
diff --git a/test/gfm/raw_html.unit b/test/gfm/raw_html.unit index 1802dba..bc3a388 100644 --- a/test/gfm/raw_html.unit +++ b/test/gfm/raw_html.unit
@@ -29,11 +29,11 @@ >>> Raw HTML - 638 <a h*#ref="hi"> <<< -<p><a h*#ref="hi"></p> +<p><a h*#ref="hi"></p> >>> Raw HTML - 639 <a href="hi'> <a href=hi'> <<< -<p><a href="hi'> <a href=hi'></p> +<p><a href="hi'> <a href=hi'></p> >>> Raw HTML - 640 < a>< foo><bar/ > @@ -43,7 +43,7 @@ <p>< a>< foo><bar/ > <foo bar=baz -bim!bop /></p> +bim!bop /></p> >>> Raw HTML - 641 <a href='bar'title=title> <<< @@ -55,7 +55,7 @@ >>> Raw HTML - 643 </a href="foo"> <<< -<p></a href="foo"></p> +<p></a href="foo"></p> >>> Raw HTML - 644 foo <!-- this is a comment - with hyphen --> @@ -96,4 +96,4 @@ >>> Raw HTML - 652 <a href="\""> <<< -<p><a href="""></p> +<p><a href="""></p>
diff --git a/test/gfm/setext_headings.unit b/test/gfm/setext_headings.unit index 5909731..4306bf4 100644 --- a/test/gfm/setext_headings.unit +++ b/test/gfm/setext_headings.unit
@@ -99,8 +99,8 @@ <<< <h2>`Foo</h2> <p>`</p> -<h2><a title="a lot</h2> -<p>of dashes"/></p> +<h2><a title="a lot</h2> +<p>of dashes"/></p> >>> Setext headings - 62 > Foo ---
diff --git a/test/markdown_test.dart b/test/markdown_test.dart index 35fde7e..58b42c6 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart
@@ -74,7 +74,7 @@ validateCore('Unicode ellipsis as punctuation', ''' "Connecting dot **A** to **B.**…" ''', ''' -<p>"Connecting dot <strong>A</strong> to <strong>B.</strong>…"</p> +<p>"Connecting dot <strong>A</strong> to <strong>B.</strong>…"</p> '''); });
diff --git a/test/original/autolinks.unit b/test/original/autolinks.unit index 804b65c..d3658e8 100644 --- a/test/original/autolinks.unit +++ b/test/original/autolinks.unit
@@ -7,4 +7,4 @@ <http://foo.com/?a=1&b=2> <<< -<p><a href="http://foo.com/?a=1&b=2">http://foo.com/?a=1&b=2</a></p> +<p><a href="http://foo.com/?a=1&b=2">http://foo.com/?a=1&b=2</a></p>
diff --git a/test/original/backslash_escapes.unit b/test/original/backslash_escapes.unit index 05a8c99..e3446f3 100644 --- a/test/original/backslash_escapes.unit +++ b/test/original/backslash_escapes.unit
@@ -5,7 +5,7 @@ and \~. <<< -<p>Punctuations like ! and " and # and $ and % and & and ' and ( and ) +<p>Punctuations like ! and " and # and $ and % and & and ' and ( and ) and * and + and , and - and . and / and : and ; and < and = and > and ? and @ and [ and \ and ] and ^ and _ and ` and { and | and } and ~.</p>
diff --git a/test/original/inline_links.unit b/test/original/inline_links.unit index 4d910c8..0a40407 100644 --- a/test/original/inline_links.unit +++ b/test/original/inline_links.unit
@@ -84,4 +84,4 @@ links [are](<http://example.com> "title\") awesome <<< -<p>links [are](<a href="http://example.com">http://example.com</a> "title") awesome</p> \ No newline at end of file +<p>links [are](<a href="http://example.com">http://example.com</a> "title") awesome</p> \ No newline at end of file
diff --git a/tool/common_mark_stats.json b/tool/common_mark_stats.json index 4e814d3..65181ec 100644 --- a/tool/common_mark_stats.json +++ b/tool/common_mark_stats.json
@@ -21,7 +21,7 @@ }, "Autolinks": { "593": "strict", - "594": "loose", + "594": "strict", "595": "strict", "596": "strict", "597": "strict", @@ -41,9 +41,9 @@ "611": "strict" }, "Backslash escapes": { - "12": "loose", + "12": "strict", "13": "strict", - "14": "loose", + "14": "strict", "15": "strict", "16": "strict", "17": "strict", @@ -101,7 +101,7 @@ "340": "strict", "341": "strict", "342": "strict", - "343": "loose", + "343": "strict", "344": "strict", "345": "strict", "346": "strict", @@ -112,17 +112,17 @@ "Emphasis and strong emphasis": { "350": "strict", "351": "strict", - "352": "loose", + "352": "strict", "353": "fail", "354": "strict", "355": "strict", "356": "strict", "357": "strict", - "358": "loose", + "358": "strict", "359": "strict", "360": "strict", "361": "strict", - "362": "loose", + "362": "strict", "363": "strict", "364": "strict", "365": "strict", @@ -139,12 +139,12 @@ "376": "strict", "377": "strict", "378": "strict", - "379": "loose", + "379": "strict", "380": "strict", "381": "strict", "382": "strict", "383": "strict", - "384": "loose", + "384": "strict", "385": "strict", "386": "strict", "387": "strict", @@ -154,7 +154,7 @@ "391": "strict", "392": "strict", "393": "strict", - "394": "loose", + "394": "strict", "395": "strict", "396": "strict", "397": "strict", @@ -245,13 +245,13 @@ "Entity and numeric character references": { "25": "loose", "26": "strict", - "27": "loose", - "28": "loose", + "27": "strict", + "28": "strict", "29": "strict", - "30": "loose", + "30": "strict", "31": "strict", - "32": "fail", - "33": "fail", + "32": "strict", + "33": "strict", "34": "strict", "35": "strict", "36": "strict", @@ -374,7 +374,7 @@ "586": "strict", "587": "strict", "588": "strict", - "589": "loose", + "589": "strict", "590": "strict", "591": "strict", "592": "strict" @@ -411,12 +411,12 @@ "203": "strict", "204": "strict", "205": "strict", - "206": "fail", + "206": "strict", "207": "loose", "208": "strict", - "209": "loose", - "210": "loose", - "211": "loose", + "209": "strict", + "210": "strict", + "211": "strict", "212": "strict", "213": "strict", "214": "strict", @@ -447,13 +447,13 @@ "499": "strict", "500": "strict", "501": "strict", - "502": "fail", + "502": "strict", "503": "strict", "504": "strict", - "505": "fail", - "506": "fail", - "507": "loose", - "508": "fail", + "505": "strict", + "506": "strict", + "507": "strict", + "508": "strict", "509": "strict", "510": "strict", "511": "strict", @@ -615,12 +615,12 @@ "615": "strict", "616": "strict", "617": "strict", - "618": "loose", - "619": "loose", - "620": "loose", + "618": "strict", + "619": "strict", + "620": "strict", "621": "strict", "622": "strict", - "623": "loose", + "623": "strict", "624": "strict", "625": "strict", "626": "strict", @@ -629,7 +629,7 @@ "629": "strict", "630": "strict", "631": "strict", - "632": "loose" + "632": "strict" }, "Setext headings": { "80": "strict", @@ -643,7 +643,7 @@ "88": "strict", "89": "strict", "90": "strict", - "91": "loose", + "91": "strict", "92": "strict", "93": "fail", "94": "strict",
diff --git a/tool/common_mark_stats.txt b/tool/common_mark_stats.txt index 4b6f751..72b98de 100644 --- a/tool/common_mark_stats.txt +++ b/tool/common_mark_stats.txt
@@ -5,15 +5,15 @@ 25 of 25 – 100.0% Block quotes 22 of 22 – 100.0% Code spans 130 of 131 – 99.2% Emphasis and strong emphasis - 15 of 17 – 88.2% Entity and numeric character references + 17 of 17 – 100.0% Entity and numeric character references 29 of 29 – 100.0% Fenced code blocks 15 of 15 – 100.0% Hard line breaks 44 of 44 – 100.0% HTML blocks 21 of 22 – 95.5% Images 11 of 12 – 91.7% Indented code blocks 1 of 1 – 100.0% Inlines - 20 of 27 – 74.1% Link reference definitions - 85 of 90 – 94.4% Links + 21 of 27 – 77.8% Link reference definitions + 89 of 90 – 98.9% Links 45 of 48 – 93.8% List items 22 of 26 – 84.6% Lists 8 of 8 – 100.0% Paragraphs @@ -24,5 +24,5 @@ 11 of 11 – 100.0% Tabs 3 of 3 – 100.0% Textual content 19 of 19 – 100.0% Thematic breaks - 626 of 652 – 96.0% TOTAL - 573 of 626 – 91.5% TOTAL Strict + 633 of 652 – 97.1% TOTAL + 604 of 633 – 95.4% TOTAL Strict
diff --git a/tool/gfm_stats.json b/tool/gfm_stats.json index ff4d34a..657aaff 100644 --- a/tool/gfm_stats.json +++ b/tool/gfm_stats.json
@@ -21,7 +21,7 @@ }, "Autolinks": { "602": "strict", - "603": "loose", + "603": "strict", "604": "strict", "605": "strict", "606": "strict", @@ -46,7 +46,7 @@ "623": "strict", "624": "strict", "625": "strict", - "626": "loose", + "626": "strict", "627": "strict", "628": "strict", "629": "strict", @@ -54,9 +54,9 @@ "631": "strict" }, "Backslash escapes": { - "308": "loose", + "308": "strict", "309": "strict", - "310": "loose", + "310": "strict", "311": "strict", "312": "strict", "313": "strict", @@ -114,7 +114,7 @@ "350": "strict", "351": "strict", "352": "strict", - "353": "loose", + "353": "strict", "354": "strict", "355": "strict", "356": "strict", @@ -128,17 +128,17 @@ "Emphasis and strong emphasis": { "360": "strict", "361": "strict", - "362": "loose", + "362": "strict", "363": "fail", "364": "strict", "365": "strict", "366": "strict", "367": "strict", - "368": "loose", + "368": "strict", "369": "strict", "370": "strict", "371": "strict", - "372": "loose", + "372": "strict", "373": "strict", "374": "strict", "375": "strict", @@ -155,12 +155,12 @@ "386": "strict", "387": "strict", "388": "strict", - "389": "loose", + "389": "strict", "390": "strict", "391": "strict", "392": "strict", "393": "strict", - "394": "loose", + "394": "strict", "395": "strict", "396": "strict", "397": "strict", @@ -170,7 +170,7 @@ "401": "strict", "402": "strict", "403": "strict", - "404": "loose", + "404": "strict", "405": "strict", "406": "strict", "407": "strict", @@ -261,13 +261,13 @@ "Entity and numeric character references": { "321": "loose", "322": "strict", - "323": "loose", - "324": "loose", + "323": "strict", + "324": "strict", "325": "strict", - "326": "loose", + "326": "strict", "327": "strict", - "328": "fail", - "329": "fail", + "328": "strict", + "329": "strict", "330": "strict", "331": "strict", "332": "strict", @@ -389,7 +389,7 @@ "595": "strict", "596": "strict", "597": "strict", - "598": "loose", + "598": "strict", "599": "strict", "600": "strict", "601": "strict" @@ -426,12 +426,12 @@ "172": "strict", "173": "strict", "174": "strict", - "175": "fail", + "175": "strict", "176": "loose", "177": "strict", - "178": "loose", - "179": "loose", - "180": "loose", + "178": "strict", + "179": "strict", + "180": "strict", "181": "strict", "182": "strict", "183": "strict", @@ -460,13 +460,13 @@ "508": "strict", "509": "strict", "510": "strict", - "511": "fail", + "511": "strict", "512": "strict", "513": "strict", - "514": "fail", - "515": "fail", - "516": "loose", - "517": "fail", + "514": "strict", + "515": "strict", + "516": "strict", + "517": "strict", "518": "strict", "519": "strict", "520": "strict", @@ -628,12 +628,12 @@ "635": "strict", "636": "strict", "637": "strict", - "638": "loose", - "639": "loose", - "640": "loose", + "638": "strict", + "639": "strict", + "640": "strict", "641": "strict", "642": "strict", - "643": "loose", + "643": "strict", "644": "strict", "645": "strict", "646": "strict", @@ -642,7 +642,7 @@ "649": "strict", "650": "strict", "651": "strict", - "652": "loose" + "652": "strict" }, "Setext headings": { "50": "strict", @@ -656,7 +656,7 @@ "58": "strict", "59": "strict", "60": "strict", - "61": "loose", + "61": "strict", "62": "strict", "63": "fail", "64": "strict",
diff --git a/tool/gfm_stats.txt b/tool/gfm_stats.txt index 5cb3981..aea55ba 100644 --- a/tool/gfm_stats.txt +++ b/tool/gfm_stats.txt
@@ -7,15 +7,15 @@ 22 of 22 – 100.0% Code spans 0 of 1 – 0.0% Disallowed Raw HTML (extension) 130 of 131 – 99.2% Emphasis and strong emphasis - 15 of 17 – 88.2% Entity and numeric character references + 17 of 17 – 100.0% Entity and numeric character references 29 of 29 – 100.0% Fenced code blocks 15 of 15 – 100.0% Hard line breaks 43 of 43 – 100.0% HTML blocks 21 of 22 – 95.5% Images 11 of 12 – 91.7% Indented code blocks 1 of 1 – 100.0% Inlines - 21 of 28 – 75.0% Link reference definitions - 82 of 87 – 94.3% Links + 22 of 28 – 78.6% Link reference definitions + 86 of 87 – 98.9% Links 45 of 48 – 93.8% List items 22 of 26 – 84.6% Lists 8 of 8 – 100.0% Paragraphs @@ -28,5 +28,5 @@ 11 of 11 – 100.0% Tabs 3 of 3 – 100.0% Textual content 19 of 19 – 100.0% Thematic breaks - 644 of 671 – 96.0% TOTAL - 589 of 644 – 91.5% TOTAL Strict + 651 of 671 – 97.0% TOTAL + 621 of 651 – 95.4% TOTAL Strict