OLD | NEW |
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 /** | 7 /** |
8 * A `String` converter that converts characters to HTML entities. | 8 * A `String` converter that converts characters to HTML entities. |
9 * | 9 * |
10 * This is intended to sanitize text before inserting the text into an HTML | 10 * This is intended to sanitize text before inserting the text into an HTML |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 * | 102 * |
103 * The escaping only works for elements with normal HTML content, | 103 * The escaping only works for elements with normal HTML content, |
104 * and not for, for example, script or style element content, | 104 * and not for, for example, script or style element content, |
105 * which require escapes matching their particular content syntax. | 105 * which require escapes matching their particular content syntax. |
106 * | 106 * |
107 * Escapes `<` and `>` characters. | 107 * Escapes `<` and `>` characters. |
108 */ | 108 */ |
109 static const HtmlEscapeMode ELEMENT = | 109 static const HtmlEscapeMode ELEMENT = |
110 const HtmlEscapeMode._('element', true, false, false, false); | 110 const HtmlEscapeMode._('element', true, false, false, false); |
111 | 111 |
112 const HtmlEscapeMode._(this._name, | 112 const HtmlEscapeMode._(this._name, this.escapeLtGt, this.escapeQuot, |
113 this.escapeLtGt, | 113 this.escapeApos, this.escapeSlash); |
114 this.escapeQuot, | |
115 this.escapeApos, | |
116 this.escapeSlash); | |
117 | 114 |
118 /** | 115 /** |
119 * Create a custom escaping mode. | 116 * Create a custom escaping mode. |
120 * | 117 * |
121 * All modes escape `&`. | 118 * All modes escape `&`. |
122 * The mode can further be set to escape `<` and `>` ([escapeLtGt]), | 119 * The mode can further be set to escape `<` and `>` ([escapeLtGt]), |
123 * `"` ([escapeQuot]), `'` ([escapeApos]), and/or `/` ([escapeSlash]). | 120 * `"` ([escapeQuot]), `'` ([escapeApos]), and/or `/` ([escapeSlash]). |
124 */ | 121 */ |
125 const HtmlEscapeMode({String name: "custom", | 122 const HtmlEscapeMode( |
126 this.escapeLtGt: false, | 123 {String name: "custom", |
127 this.escapeQuot: false, | 124 this.escapeLtGt: false, |
128 this.escapeApos: false, | 125 this.escapeQuot: false, |
129 this.escapeSlash: false}) : _name = name; | 126 this.escapeApos: false, |
| 127 this.escapeSlash: false}) |
| 128 : _name = name; |
130 | 129 |
131 String toString() => _name; | 130 String toString() => _name; |
132 } | 131 } |
133 | 132 |
134 /** | 133 /** |
135 * Converter which escapes characters with special meaning in HTML. | 134 * Converter which escapes characters with special meaning in HTML. |
136 * | 135 * |
137 * The converter finds characters that are significant in HTML source and | 136 * The converter finds characters that are significant in HTML source and |
138 * replaces them with corresponding HTML entities. | 137 * replaces them with corresponding HTML entities. |
139 * | 138 * |
140 * The characters that need escaping in HTML are: | 139 * The characters that need escaping in HTML are: |
141 * | 140 * |
142 * * `&` (ampersand) always need to be escaped. | 141 * * `&` (ampersand) always need to be escaped. |
143 * * `<` (less than) and '>' (greater than) when inside an element. | 142 * * `<` (less than) and '>' (greater than) when inside an element. |
144 * * `"` (quote) when inside a double-quoted attribute value. | 143 * * `"` (quote) when inside a double-quoted attribute value. |
145 * * `'` (apostrophe) when inside a single-quoted attribute value. | 144 * * `'` (apostrophe) when inside a single-quoted attribute value. |
146 * Apostrophe is escaped as `'` instead of `'` since | 145 * Apostrophe is escaped as `'` instead of `'` since |
147 * not all browsers understand `'`. | 146 * not all browsers understand `'`. |
148 * * `/` (slash) is recommended to be escaped because it may be used | 147 * * `/` (slash) is recommended to be escaped because it may be used |
149 * to terminate an element in some HTML dialects. | 148 * to terminate an element in some HTML dialects. |
150 * | 149 * |
151 * Escaping `>` (greater than) isn't necessary, but the result is often | 150 * Escaping `>` (greater than) isn't necessary, but the result is often |
152 * found to be easier to read if greater-than is also escaped whenever | 151 * found to be easier to read if greater-than is also escaped whenever |
153 * less-than is. | 152 * less-than is. |
154 */ | 153 */ |
155 class HtmlEscape extends Converter<String, String> | 154 class HtmlEscape extends Converter<String, String> |
156 implements ChunkedConverter<String, String, String, String> { | 155 implements ChunkedConverter<String, String, String, String> { |
157 | |
158 /** The [HtmlEscapeMode] used by the converter. */ | 156 /** The [HtmlEscapeMode] used by the converter. */ |
159 final HtmlEscapeMode mode; | 157 final HtmlEscapeMode mode; |
160 | 158 |
161 /** | 159 /** |
162 * Create converter that escapes HTML characters. | 160 * Create converter that escapes HTML characters. |
163 * | 161 * |
164 * If [mode] is provided as either [HtmlEscapeMode.ATTRIBUTE] or | 162 * If [mode] is provided as either [HtmlEscapeMode.ATTRIBUTE] or |
165 * [HtmlEscapeMode.ELEMENT], only the corresponding subset of HTML | 163 * [HtmlEscapeMode.ELEMENT], only the corresponding subset of HTML |
166 * characters are escaped. | 164 * characters are escaped. |
167 * The default is to escape all HTML characters. | 165 * The default is to escape all HTML characters. |
(...skipping 10 matching lines...) Expand all Loading... |
178 * | 176 * |
179 * Returns `null` if no changes were necessary, otherwise returns | 177 * Returns `null` if no changes were necessary, otherwise returns |
180 * the converted string. | 178 * the converted string. |
181 */ | 179 */ |
182 String _convert(String text, int start, int end) { | 180 String _convert(String text, int start, int end) { |
183 StringBuffer result = null; | 181 StringBuffer result = null; |
184 for (int i = start; i < end; i++) { | 182 for (int i = start; i < end; i++) { |
185 var ch = text[i]; | 183 var ch = text[i]; |
186 String replacement = null; | 184 String replacement = null; |
187 switch (ch) { | 185 switch (ch) { |
188 case '&': replacement = '&'; break; | 186 case '&': |
189 case '"': if (mode.escapeQuot) replacement = '"'; break; | 187 replacement = '&'; |
190 case "'": if (mode.escapeApos) replacement = '''; break; | 188 break; |
191 case '<': if (mode.escapeLtGt) replacement = '<'; break; | 189 case '"': |
192 case '>': if (mode.escapeLtGt) replacement = '>'; break; | 190 if (mode.escapeQuot) replacement = '"'; |
193 case '/': if (mode.escapeSlash) replacement = '/'; break; | 191 break; |
| 192 case "'": |
| 193 if (mode.escapeApos) replacement = '''; |
| 194 break; |
| 195 case '<': |
| 196 if (mode.escapeLtGt) replacement = '<'; |
| 197 break; |
| 198 case '>': |
| 199 if (mode.escapeLtGt) replacement = '>'; |
| 200 break; |
| 201 case '/': |
| 202 if (mode.escapeSlash) replacement = '/'; |
| 203 break; |
194 } | 204 } |
195 if (replacement != null) { | 205 if (replacement != null) { |
196 if (result == null) result = new StringBuffer(); | 206 if (result == null) result = new StringBuffer(); |
197 if (i > start) result.write(text.substring(start, i)); | 207 if (i > start) result.write(text.substring(start, i)); |
198 result.write(replacement); | 208 result.write(replacement); |
199 start = i + 1; | 209 start = i + 1; |
200 } | 210 } |
201 } | 211 } |
202 if (result == null) return null; | 212 if (result == null) return null; |
203 if (end > start) result.write(text.substring(start, end)); | 213 if (end > start) result.write(text.substring(start, end)); |
204 return result.toString(); | 214 return result.toString(); |
205 } | 215 } |
206 | 216 |
207 StringConversionSink startChunkedConversion(Sink<String> sink) { | 217 StringConversionSink startChunkedConversion(Sink<String> sink) { |
208 if (sink is! StringConversionSink) { | 218 if (sink is! StringConversionSink) { |
209 sink = new StringConversionSink.from(sink); | 219 sink = new StringConversionSink.from(sink); |
210 } | 220 } |
211 return new _HtmlEscapeSink(this, sink); | 221 return new _HtmlEscapeSink(this, sink); |
212 } | 222 } |
213 } | 223 } |
214 | 224 |
215 class _HtmlEscapeSink extends StringConversionSinkBase { | 225 class _HtmlEscapeSink extends StringConversionSinkBase { |
216 final HtmlEscape _escape; | 226 final HtmlEscape _escape; |
217 final StringConversionSink _sink; | 227 final StringConversionSink _sink; |
218 | 228 |
219 _HtmlEscapeSink(this._escape, this._sink); | 229 _HtmlEscapeSink(this._escape, this._sink); |
220 | 230 |
221 void addSlice(String chunk, int start, int end, bool isLast) { | 231 void addSlice(String chunk, int start, int end, bool isLast) { |
222 var val = _escape._convert(chunk, start, end); | 232 var val = _escape._convert(chunk, start, end); |
223 if(val == null) { | 233 if (val == null) { |
224 _sink.addSlice(chunk, start, end, isLast); | 234 _sink.addSlice(chunk, start, end, isLast); |
225 } else { | 235 } else { |
226 _sink.add(val); | 236 _sink.add(val); |
227 if (isLast) _sink.close(); | 237 if (isLast) _sink.close(); |
228 } | 238 } |
229 } | 239 } |
230 | 240 |
231 void close() { _sink.close(); } | 241 void close() { |
| 242 _sink.close(); |
| 243 } |
232 } | 244 } |
OLD | NEW |