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

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: address comments. 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.
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 */
10 class HtmlEscapeMode { 23 class HtmlEscapeMode {
11 final String _name; 24 final String _name;
25 /** Whether to escape '<' and '>'. */
12 final bool escapeLtGt; 26 final bool escapeLtGt;
27 /** Whether to escape '"' (quote). */
13 final bool escapeQuot; 28 final bool escapeQuot;
29 /** Whether to escape "'" (apostrophe). */
14 final bool escapeApos; 30 final bool escapeApos;
15 final bool escapeSlash;
16 31
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 */
18 static const HtmlEscapeMode UNKNOWN = 42 static const HtmlEscapeMode UNKNOWN =
19 const HtmlEscapeMode._('unknown', true, true, true, true); 43 const HtmlEscapeMode._('unknown', true, true, true);
20 44
21 // TODO(floitsch) - Document - Issue 13097 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 */
22 static const HtmlEscapeMode ATTRIBUTE = 53 static const HtmlEscapeMode ATTRIBUTE =
23 const HtmlEscapeMode._('attribute', false, true, false, false); 54 const HtmlEscapeMode._('attribute', false, true, false);
24 55
25 // TODO(floitsch) - Document - Issue 13097 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 */
26 static const HtmlEscapeMode ELEMENT = 76 static const HtmlEscapeMode ELEMENT =
27 const HtmlEscapeMode._('element', true, false, false, true); 77 const HtmlEscapeMode._('element', true, false, false);
28 78
29 // TODO(floitsch) - Document - Issue 13097 79 const HtmlEscapeMode._(
30 const HtmlEscapeMode._(this._name, this.escapeLtGt, this.escapeQuot, 80 this._name, this.escapeLtGt, this.escapeQuot, this.escapeApos);
31 this.escapeApos, this.escapeSlash); 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;
32 93
33 String toString() => _name; 94 String toString() => _name;
34 } 95 }
35 96
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 `&#39;` instead of `&apos;` since
110 * not all browsers understand `&apos;`.
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 */
37 class HtmlEscape extends Converter<String, String> { 116 class HtmlEscape extends Converter<String, String> {
38 117
39 // TODO(floitsch) - Document - Issue 13097 118 /** The [HtmlEscapeMode] used by the converter. */
40 final HtmlEscapeMode mode; 119 final HtmlEscapeMode mode;
41 120
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 */
43 const HtmlEscape([this.mode = HtmlEscapeMode.UNKNOWN]); 129 const HtmlEscape([this.mode = HtmlEscapeMode.UNKNOWN]);
44 130
45 String convert(String text) { 131 String convert(String text) {
46 var val = _convert(text, 0, text.length); 132 var val = _convert(text, 0, text.length);
47 return val == null ? text : val; 133 return val == null ? text : val;
48 } 134 }
49 135
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 */
50 String _convert(String text, int start, int end) { 142 String _convert(String text, int start, int end) {
51 StringBuffer result = null; 143 StringBuffer result = null;
52 for (int i = start; i < end; i++) { 144 for (int i = start; i < end; i++) {
53 var ch = text[i]; 145 var ch = text[i];
54 String replace = null; 146 String replacement = null;
55 switch (ch) { 147 switch (ch) {
56 case '&': replace = '&amp;'; break; 148 case '&': replacement = '&amp;'; break;
57 case '\u00A0'/*NO-BREAK SPACE*/: replace = '&nbsp;'; break; 149 case '"': if (mode.escapeQuot) replacement = '&quot;'; break;
58 case '"': if (mode.escapeQuot) replace = '&quot;'; break; 150 case "'": if (mode.escapeApos) replacement = '&#39;'; break;
59 case "'": if (mode.escapeApos) replace = '&#x27;'; break; 151 case '<': if (mode.escapeLtGt) replacement = '&lt;'; break;
60 case '<': if (mode.escapeLtGt) replace = '&lt;'; break; 152 case '>': if (mode.escapeLtGt) replacement = '&gt;'; break;
61 case '>': if (mode.escapeLtGt) replace = '&gt;'; break;
62 case '/': if (mode.escapeSlash) replace = '&#x2F;'; break;
63 } 153 }
64 if (replace != null) { 154 if (replacement != null) {
65 if (result == null) result = new StringBuffer(text.substring(start, i)); 155 if (result == null) result = new StringBuffer();
66 result.write(replace); 156 if (i > start) result.write(text.substring(start, i));
67 } else if (result != null) { 157 result.write(replacement);
68 result.write(ch); 158 start = i + 1;
69 } 159 }
70 } 160 }
71 161 if (result == null) return null;
72 return result != null ? result.toString() : null; 162 if (end > start) result.write(text.substring(start, end));
163 return result.toString();
73 } 164 }
74 165
75 StringConversionSink startChunkedConversion(Sink<String> sink) { 166 StringConversionSink startChunkedConversion(Sink<String> sink) {
76 if (sink is! StringConversionSink) { 167 if (sink is! StringConversionSink) {
77 sink = new StringConversionSink.from(sink); 168 sink = new StringConversionSink.from(sink);
78 } 169 }
79 return new _HtmlEscapeSink(this, sink); 170 return new _HtmlEscapeSink(this, sink);
80 } 171 }
81 } 172 }
82 173
83 class _HtmlEscapeSink extends StringConversionSinkBase { 174 class _HtmlEscapeSink extends StringConversionSinkBase {
84 final HtmlEscape _escape; 175 final HtmlEscape _escape;
85 final StringConversionSink _sink; 176 final StringConversionSink _sink;
86 177
87 _HtmlEscapeSink(this._escape, this._sink); 178 _HtmlEscapeSink(this._escape, this._sink);
88 179
89 void addSlice(String chunk, int start, int end, bool isLast) { 180 void addSlice(String chunk, int start, int end, bool isLast) {
90 var val = _escape._convert(chunk, start, end); 181 var val = _escape._convert(chunk, start, end);
91 if(val == null) { 182 if(val == null) {
92 _sink.addSlice(chunk, start, end, isLast); 183 _sink.addSlice(chunk, start, end, isLast);
93 } else { 184 } else {
94 _sink.add(val); 185 _sink.add(val);
95 if (isLast) _sink.close(); 186 if (isLast) _sink.close();
96 } 187 }
97 } 188 }
98 189
99 void close() => _sink.close(); 190 void close() => _sink.close();
100 } 191 }
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