Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: sdk/lib/convert/html_escape.dart

Issue 1019853002: Document and fix HTML-escape. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Make constructor public. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/lib/convert/html_escape_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.convert; 5 part of dart.convert;
6 6
7 // TODO(floitsch) - Document - Issue 13097 7 // TODO(floitsch) - Document - Issue 13097
8 const HtmlEscape HTML_ESCAPE = const HtmlEscape(); 8 const HtmlEscape HTML_ESCAPE = const HtmlEscape();
9 9
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.
Søren Gjesse 2015/03/19 14:09:33 Maybe mention what the different contexts are.
Lasse Reichstein Nielsen 2015/03/20 08:47:04 Done.
15 *
16 * All modes require escaping of `&` (ampersand) characters, and may
17 * enable escaping of more characters.
18 */
10 class HtmlEscapeMode { 19 class HtmlEscapeMode {
11 final String _name; 20 final String _name;
21 /** Whether to escape '<' and '>'. */
12 final bool escapeLtGt; 22 final bool escapeLtGt;
23 /** Whether to escape '"' (quote). */
13 final bool escapeQuot; 24 final bool escapeQuot;
25 /** Whether to escape "'" (apostrophe). */
14 final bool escapeApos; 26 final bool escapeApos;
15 final bool escapeSlash;
16 27
17 // TODO(floitsch) - Document - Issue 13097 28 /**
29 * Default escaping mode which escape all characters.
30 *
31 * The result of such an escaping is usable both in element content and
32 * in any attribute value.
33 *
34 * The escaping only works for elements with normal HTML content,
35 * and not for, for example, script or style element content,
36 * which require escapes matching their particular content syntax.
37 */
18 static const HtmlEscapeMode UNKNOWN = 38 static const HtmlEscapeMode UNKNOWN =
19 const HtmlEscapeMode._('unknown', true, true, true, true); 39 const HtmlEscapeMode._('unknown', true, true, true);
20 40
21 // TODO(floitsch) - Document - Issue 13097 41 /**
42 * Escaping mode for text going into double-quoted HTML attribute values.
43 *
44 * The result should not be used as the content of an unquoted
45 * or single-quoted attribute value.
46 *
47 * Escapes only double quotes (`"`) but not single quotes (`'`).
48 */
22 static const HtmlEscapeMode ATTRIBUTE = 49 static const HtmlEscapeMode ATTRIBUTE =
23 const HtmlEscapeMode._('attribute', false, true, false, false); 50 const HtmlEscapeMode._('attribute', false, true, false);
24 51
25 // TODO(floitsch) - Document - Issue 13097 52 /**
53 * Escaping mode for text going into single-quoted HTML attribute values.
54 *
55 * The result should not be used as the content of an unquoted
56 * or double-quoted attribute value.
57 *
58 * Escapes only single quotes (`'`) but not double quotes (`"`).
59 */
60 static const HtmlEscapeMode SQ_ATTRIBUTE =
61 const HtmlEscapeMode._('attribute', false, false, true);
62
63 /**
64 * Escaping mode for text going into HTML element content.
65 *
66 * The escaping only works for elements with normal HTML content,
67 * and not for, for example, script or style element content,
68 * which require escapes matching their particular content syntax.
69 *
70 * Escapes `<` and `>` characters.
71 */
26 static const HtmlEscapeMode ELEMENT = 72 static const HtmlEscapeMode ELEMENT =
27 const HtmlEscapeMode._('element', true, false, false, true); 73 const HtmlEscapeMode._('element', true, false, false);
28 74
29 // TODO(floitsch) - Document - Issue 13097 75 const HtmlEscapeMode._(
30 const HtmlEscapeMode._(this._name, this.escapeLtGt, this.escapeQuot, 76 this._name, this.escapeLtGt, this.escapeQuot, this.escapeApos);
31 this.escapeApos, this.escapeSlash); 77
78 /**
79 * Create a custom escaping mode.
80 *
81 * All modes escape `&`.
82 * The mode can further be set to escape `<` and `>` ([escapeLtGt]),
83 * `"` ([escapeQuot]) and/or `'` ([escapeApos]).
84 */
85 const HtmlEscapeMode({String name: "custom",
86 this.escapeLtGt: false,
87 this.escapeQuot: false,
88 this.escapeApos: false}) : _name = name;
32 89
33 String toString() => _name; 90 String toString() => _name;
34 } 91 }
35 92
36 // TODO(floitsch) - Document - Issue 13097 93 /**
94 * Converter which escapes characters that means something when parsed as HTML.
floitsch 2015/03/19 14:10:39 that have a meaning in HTML? otherwise: "mean" (n
Lasse Reichstein Nielsen 2015/03/20 08:47:04 Rewritten.
95 *
96 * The converter finds characters that have meaning in HTML and replaces
97 * them with HTML corresponding entities.
98 *
99 * The characters that need escaping in HTML are:
100 *
101 * * `&` (ampersand) always need to be escaped.
102 * * `<` (less than) and '>' (greater than) when inside an element.
103 * * `"` (quote) when inside a double-quoted attribute value.
104 * * `'` (apostrophe) when inside a single-quoted attribute value.
105 * Apostrophe is escaped as `&#39;` instead of `&apos;` since
106 * not all browsers understand `&apos;`.
107 *
108 * Escaping `>` (greater than) isn't necessary, but the result is often
109 * found to be easier to read if greater-than is also escaped whenever
110 * less-than is.
111 */
37 class HtmlEscape extends Converter<String, String> { 112 class HtmlEscape extends Converter<String, String> {
38 113
39 // TODO(floitsch) - Document - Issue 13097 114 /** The [HtmlEscapeMode] used by the converter. */
40 final HtmlEscapeMode mode; 115 final HtmlEscapeMode mode;
41 116
42 // TODO(floitsch) - Document - Issue 13097 117 /**
118 * Create converter that escapes HTML characters.
119 *
120 * If [mode] is provided as either [HtmlEscapeMode.ATTRIBUTE] or
121 * [HtmlEscapeMode.ELEMENT], only the corresponding subset of HTML
122 * characters are escaped.
123 * The default is to escape all HTML characters.
124 */
43 const HtmlEscape([this.mode = HtmlEscapeMode.UNKNOWN]); 125 const HtmlEscape([this.mode = HtmlEscapeMode.UNKNOWN]);
44 126
45 String convert(String text) { 127 String convert(String text) {
46 var val = _convert(text, 0, text.length); 128 var val = _convert(text, 0, text.length);
47 return val == null ? text : val; 129 return val == null ? text : val;
48 } 130 }
49 131
132 /**
133 * Converts the substring of text from start to end.
134 *
135 * Returns `null` if no changes were necessary, otherwise returns
136 * the converted string.
137 */
50 String _convert(String text, int start, int end) { 138 String _convert(String text, int start, int end) {
51 StringBuffer result = null; 139 StringBuffer result = null;
52 for (int i = start; i < end; i++) { 140 for (int i = start; i < end; i++) {
53 var ch = text[i]; 141 var ch = text[i];
54 String replace = null; 142 String replacement = null;
55 switch (ch) { 143 switch (ch) {
56 case '&': replace = '&amp;'; break; 144 case '&': replacement = '&amp;'; break;
57 case '\u00A0'/*NO-BREAK SPACE*/: replace = '&nbsp;'; break; 145 case '"': if (mode.escapeQuot) replacement = '&quot;'; break;
58 case '"': if (mode.escapeQuot) replace = '&quot;'; break; 146 case "'": if (mode.escapeApos) replacement = '&#39;'; break;
Søren Gjesse 2015/03/19 14:09:33 Maybe add a comment why &apos; is not used.
Lasse Reichstein Nielsen 2015/03/20 08:47:05 It's in the documentation for the class.
59 case "'": if (mode.escapeApos) replace = '&#x27;'; break; 147 case '<': if (mode.escapeLtGt) replacement = '&lt;'; break;
60 case '<': if (mode.escapeLtGt) replace = '&lt;'; break; 148 case '>': if (mode.escapeLtGt) replacement = '&gt;'; break;
61 case '>': if (mode.escapeLtGt) replace = '&gt;'; break;
62 case '/': if (mode.escapeSlash) replace = '&#x2F;'; break;
63 } 149 }
64 if (replace != null) { 150 if (replacement != null) {
65 if (result == null) result = new StringBuffer(text.substring(start, i)); 151 if (result == null) result = new StringBuffer();
66 result.write(replace); 152 if (i > start) result.write(text.substring(start, i));
67 } else if (result != null) { 153 result.write(replacement);
68 result.write(ch); 154 start = i + 1;
69 } 155 }
70 } 156 }
71 157 if (result == null) return null;
72 return result != null ? result.toString() : null; 158 if (end > start) result.write(text.substring(start, end));
159 return result.toString();
73 } 160 }
74 161
75 StringConversionSink startChunkedConversion(Sink<String> sink) { 162 StringConversionSink startChunkedConversion(Sink<String> sink) {
76 if (sink is! StringConversionSink) { 163 if (sink is! StringConversionSink) {
77 sink = new StringConversionSink.from(sink); 164 sink = new StringConversionSink.from(sink);
78 } 165 }
79 return new _HtmlEscapeSink(this, sink); 166 return new _HtmlEscapeSink(this, sink);
80 } 167 }
81 } 168 }
82 169
83 class _HtmlEscapeSink extends StringConversionSinkBase { 170 class _HtmlEscapeSink extends StringConversionSinkBase {
84 final HtmlEscape _escape; 171 final HtmlEscape _escape;
85 final StringConversionSink _sink; 172 final StringConversionSink _sink;
86 173
87 _HtmlEscapeSink(this._escape, this._sink); 174 _HtmlEscapeSink(this._escape, this._sink);
88 175
89 void addSlice(String chunk, int start, int end, bool isLast) { 176 void addSlice(String chunk, int start, int end, bool isLast) {
90 var val = _escape._convert(chunk, start, end); 177 var val = _escape._convert(chunk, start, end);
91 if(val == null) { 178 if(val == null) {
92 _sink.addSlice(chunk, start, end, isLast); 179 _sink.addSlice(chunk, start, end, isLast);
93 } else { 180 } else {
94 _sink.add(val); 181 _sink.add(val);
95 if (isLast) _sink.close(); 182 if (isLast) _sink.close();
96 } 183 }
97 } 184 }
98 185
99 void close() => _sink.close(); 186 void close() => _sink.close();
100 } 187 }
OLDNEW
« no previous file with comments | « no previous file | tests/lib/convert/html_escape_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698