@@ -7,69 +7,160 @@ part of dart.convert;
77// TODO(floitsch) - Document - Issue 13097
88const HtmlEscape HTML_ESCAPE = const HtmlEscape ();
99
10+ /**
11+ * HTML escape modes.
12+ *
13+ * Allows specifying a mode for HTML escaping that depend on the context
14+ * where the escaped result is going to be used.
15+ * The relevant contexts are:
16+ *
17+ * * as text content of an HTML element.
18+ * * as value of a (single- or double-) quoted attribute value.
19+ *
20+ * All modes require escaping of `&` (ampersand) characters, and may
21+ * enable escaping of more characters.
22+ */
1023class HtmlEscapeMode {
1124 final String _name;
25+ /** Whether to escape '<' and '>'. */
1226 final bool escapeLtGt;
27+ /** Whether to escape '"' (quote). */
1328 final bool escapeQuot;
29+ /** Whether to escape "'" (apostrophe). */
1430 final bool escapeApos;
15- final bool escapeSlash;
1631
17- // TODO(floitsch) - Document - Issue 13097
32+ /**
33+ * Default escaping mode which escape all characters.
34+ *
35+ * The result of such an escaping is usable both in element content and
36+ * in any attribute value.
37+ *
38+ * The escaping only works for elements with normal HTML content,
39+ * and not for, for example, script or style element content,
40+ * which require escapes matching their particular content syntax.
41+ */
1842 static const HtmlEscapeMode UNKNOWN =
19- const HtmlEscapeMode ._('unknown' , true , true , true , true );
20-
21- // TODO(floitsch) - Document - Issue 13097
43+ const HtmlEscapeMode ._('unknown' , true , true , true );
44+
45+ /**
46+ * Escaping mode for text going into double-quoted HTML attribute values.
47+ *
48+ * The result should not be used as the content of an unquoted
49+ * or single-quoted attribute value.
50+ *
51+ * Escapes only double quotes (`"` ) but not single quotes (`'` ).
52+ */
2253 static const HtmlEscapeMode ATTRIBUTE =
23- const HtmlEscapeMode ._('attribute' , false , true , false , false );
24-
25- // TODO(floitsch) - Document - Issue 13097
54+ const HtmlEscapeMode ._('attribute' , false , true , false );
55+
56+ /**
57+ * Escaping mode for text going into single-quoted HTML attribute values.
58+ *
59+ * The result should not be used as the content of an unquoted
60+ * or double-quoted attribute value.
61+ *
62+ * Escapes only single quotes (`'` ) but not double quotes (`"` ).
63+ */
64+ static const HtmlEscapeMode SQ_ATTRIBUTE =
65+ const HtmlEscapeMode ._('attribute' , false , false , true );
66+
67+ /**
68+ * Escaping mode for text going into HTML element content.
69+ *
70+ * The escaping only works for elements with normal HTML content,
71+ * and not for, for example, script or style element content,
72+ * which require escapes matching their particular content syntax.
73+ *
74+ * Escapes `<` and `>` characters.
75+ */
2676 static const HtmlEscapeMode ELEMENT =
27- const HtmlEscapeMode ._('element' , true , false , false , true );
28-
29- // TODO(floitsch) - Document - Issue 13097
30- const HtmlEscapeMode ._(this ._name, this .escapeLtGt, this .escapeQuot,
31- this .escapeApos, this .escapeSlash);
77+ const HtmlEscapeMode ._('element' , true , false , false );
78+
79+ const HtmlEscapeMode ._(
80+ this ._name, this .escapeLtGt, this .escapeQuot, this .escapeApos);
81+
82+ /**
83+ * Create a custom escaping mode.
84+ *
85+ * All modes escape `&` .
86+ * The mode can further be set to escape `<` and `>` ([escapeLtGt] ),
87+ * `"` ([escapeQuot] ) and/or `'` ([escapeApos] ).
88+ */
89+ const HtmlEscapeMode ({String name: "custom" ,
90+ this .escapeLtGt: false ,
91+ this .escapeQuot: false ,
92+ this .escapeApos: false }) : _name = name;
3293
3394 String toString () => _name;
3495}
3596
36- // TODO(floitsch) - Document - Issue 13097
97+ /**
98+ * Converter which escapes characters with special meaning in HTML.
99+ *
100+ * The converter finds characters that are siginificant in HTML source and
101+ * replaces them with corresponding HTML entities.
102+ *
103+ * The characters that need escaping in HTML are:
104+ *
105+ * * `&` (ampersand) always need to be escaped.
106+ * * `<` (less than) and '>' (greater than) when inside an element.
107+ * * `"` (quote) when inside a double-quoted attribute value.
108+ * * `'` (apostrophe) when inside a single-quoted attribute value.
109+ * Apostrophe is escaped as `'` instead of `'` since
110+ * not all browsers understand `'`.
111+ *
112+ * Escaping `>` (greater than) isn't necessary, but the result is often
113+ * found to be easier to read if greater-than is also escaped whenever
114+ * less-than is.
115+ */
37116class HtmlEscape extends Converter <String , String > {
38117
39- // TODO(floitsch) - Document - Issue 13097
118+ /** The [HtmlEscapeMode] used by the converter. */
40119 final HtmlEscapeMode mode;
41120
42- // TODO(floitsch) - Document - Issue 13097
121+ /**
122+ * Create converter that escapes HTML characters.
123+ *
124+ * If [mode] is provided as either [HtmlEscapeMode.ATTRIBUTE] or
125+ * [HtmlEscapeMode.ELEMENT] , only the corresponding subset of HTML
126+ * characters are escaped.
127+ * The default is to escape all HTML characters.
128+ */
43129 const HtmlEscape ([this .mode = HtmlEscapeMode .UNKNOWN ]);
44130
45131 String convert (String text) {
46132 var val = _convert (text, 0 , text.length);
47133 return val == null ? text : val;
48134 }
49135
136+ /**
137+ * Converts the substring of text from start to end.
138+ *
139+ * Returns `null` if no changes were necessary, otherwise returns
140+ * the converted string.
141+ */
50142 String _convert (String text, int start, int end) {
51143 StringBuffer result = null ;
52144 for (int i = start; i < end; i++ ) {
53145 var ch = text[i];
54- String replace = null ;
146+ String replacement = null ;
55147 switch (ch) {
56- case '&' : replace = '&' ; break ;
57- case '\u 00A0' /*NO-BREAK SPACE*/ : replace = ' ' ; break ;
58- case '"' : if (mode.escapeQuot) replace = '"' ; break ;
59- case "'" : if (mode.escapeApos) replace = ''' ; break ;
60- case '<' : if (mode.escapeLtGt) replace = '<' ; break ;
61- case '>' : if (mode.escapeLtGt) replace = '>' ; break ;
62- case '/' : if (mode.escapeSlash) replace = '/' ; break ;
148+ case '&' : replacement = '&' ; break ;
149+ case '"' : if (mode.escapeQuot) replacement = '"' ; break ;
150+ case "'" : if (mode.escapeApos) replacement = ''' ; break ;
151+ case '<' : if (mode.escapeLtGt) replacement = '<' ; break ;
152+ case '>' : if (mode.escapeLtGt) replacement = '>' ; break ;
63153 }
64- if (replace != null ) {
65- if (result == null ) result = new StringBuffer (text. substring (start, i) );
66- result.write (replace );
67- } else if ( result != null ) {
68- result. write (ch) ;
154+ if (replacement != null ) {
155+ if (result == null ) result = new StringBuffer ();
156+ if (i > start) result.write (text. substring (start, i) );
157+ result. write (replacement);
158+ start = i + 1 ;
69159 }
70160 }
71-
72- return result != null ? result.toString () : null ;
161+ if (result == null ) return null ;
162+ if (end > start) result.write (text.substring (start, end));
163+ return result.toString ();
73164 }
74165
75166 StringConversionSink startChunkedConversion (Sink <String > sink) {
0 commit comments