closure 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +12 -12
- data/closure-compiler/README.md +503 -0
- data/closure-compiler/compiler.jar +0 -0
- data/closure-templates/SoyToJsSrcCompiler.jar +0 -0
- data/closure-templates/soyutils.js +1593 -469
- data/closure-templates/soyutils_usegoog.js +1105 -223
- data/docs/SCRIPT.md +5 -5
- data/docs/closure/Closure/BeanShell.html +63 -54
- data/docs/closure/Closure/Compiler/Compilation.html +124 -107
- data/docs/closure/Closure/Compiler/Error.html +28 -21
- data/docs/closure/Closure/Compiler.html +81 -76
- data/docs/closure/Closure/FileResponse.html +113 -98
- data/docs/closure/Closure/Goog.html +264 -253
- data/docs/closure/Closure/Middleware.html +66 -55
- data/docs/closure/Closure/Script/NotFound.html +28 -21
- data/docs/closure/Closure/Script/RenderStackOverflow.html +28 -21
- data/docs/closure/Closure/Script.html +212 -203
- data/docs/closure/Closure/Server.html +100 -90
- data/docs/closure/Closure/ShowExceptions.html +63 -52
- data/docs/closure/Closure/Sources.html +254 -246
- data/docs/closure/Closure/Templates/Error.html +28 -21
- data/docs/closure/Closure/Templates.html +88 -80
- data/docs/closure/Closure.html +181 -163
- data/docs/closure/_index.html +42 -38
- data/docs/closure/class_list.html +19 -8
- data/docs/closure/css/full_list.css +4 -2
- data/docs/closure/css/style.css +68 -51
- data/docs/closure/file.LICENSE.html +24 -217
- data/docs/closure/file.README.html +54 -47
- data/docs/closure/file_list.html +20 -9
- data/docs/closure/frames.html +18 -5
- data/docs/closure/index.html +54 -47
- data/docs/closure/js/app.js +60 -46
- data/docs/closure/js/full_list.js +24 -10
- data/docs/closure/js/jquery.js +4 -16
- data/docs/closure/method_list.html +74 -175
- data/docs/closure/top-level-namespace.html +29 -20
- data/lib/closure/compiler.rb +32 -42
- data/lib/closure/goog.rb +12 -12
- data/lib/closure/server.rb +6 -6
- data/lib/closure/show_exceptions.rb +15 -12
- data/lib/closure/version.rb +1 -1
- data/scripts/git.erb +183 -0
- data/scripts/hello/compiler.js.erb +2 -2
- data/scripts/hello/hello.js +1 -1
- data/scripts/hello/index.erb +6 -0
- data/scripts/hello/legume.js +12 -7
- data/scripts/index.erb +15 -13
- data/scripts/modules/compiler.js.erb +3 -3
- data/scripts/modules/compiler_build.js +3 -3
- data/scripts/modules/compiler_build.map +13 -12159
- data/scripts/modules/compiler_build_api.js +1 -1
- data/scripts/modules/compiler_build_app.js +74 -71
- data/scripts/modules/compiler_build_settings.js +2 -2
- data/scripts/modules/index.erb +5 -3
- data/scripts/modules/settings.js +1 -1
- data/scripts/svn.erb +11 -11
- data/scripts/welcome.erb +7 -6
- metadata +65 -81
- data/closure-compiler/README +0 -292
- data/scripts/hello/compiler_build.js +0 -5
- data/scripts/hello/compiler_build.map +0 -748
- data/scripts/hello/compiler_debug.js +0 -119
- data/scripts/modules/compiler_debug.js +0 -6
- data/scripts/modules/compiler_debug_api.js +0 -11
- data/scripts/modules/compiler_debug_app.js +0 -2414
- data/scripts/modules/compiler_debug_settings.js +0 -39
- data/scripts/rails/index.erb +0 -46
- data/scripts/rails/rails_ujs.js +0 -96
@@ -30,9 +30,10 @@
|
|
30
30
|
* by Soy-generated JS code. Please do not use these functions directly from
|
31
31
|
* your hand-writen code. Their names all start with '$$'.
|
32
32
|
*
|
33
|
+
* @author Garrett Boyer
|
33
34
|
* @author Mike Samuel
|
34
35
|
* @author Kai Huang
|
35
|
-
* @author Aharon
|
36
|
+
* @author Aharon Lanin
|
36
37
|
*/
|
37
38
|
|
38
39
|
goog.provide('soy');
|
@@ -41,8 +42,10 @@ goog.provide('soy.esc');
|
|
41
42
|
goog.provide('soydata');
|
42
43
|
goog.provide('soydata.SanitizedHtml');
|
43
44
|
goog.provide('soydata.SanitizedHtmlAttribute');
|
45
|
+
goog.provide('soydata.SanitizedJs');
|
44
46
|
goog.provide('soydata.SanitizedJsStrChars');
|
45
47
|
goog.provide('soydata.SanitizedUri');
|
48
|
+
goog.provide('soydata.VERY_UNSAFE');
|
46
49
|
|
47
50
|
goog.require('goog.asserts');
|
48
51
|
goog.require('goog.dom.DomHelper');
|
@@ -50,6 +53,7 @@ goog.require('goog.format');
|
|
50
53
|
goog.require('goog.i18n.BidiFormatter');
|
51
54
|
goog.require('goog.i18n.bidi');
|
52
55
|
goog.require('goog.soy');
|
56
|
+
goog.require('goog.soy.data.SanitizedContentKind');
|
53
57
|
goog.require('goog.string');
|
54
58
|
goog.require('goog.string.StringBuffer');
|
55
59
|
|
@@ -60,7 +64,7 @@ goog.require('goog.string.StringBuffer');
|
|
60
64
|
|
61
65
|
/**
|
62
66
|
* Utility class to facilitate much faster string concatenation in IE,
|
63
|
-
* using Array.join() rather than the '+' operator.
|
67
|
+
* using Array.join() rather than the '+' operator. For other browsers
|
64
68
|
* we simply use the '+' operator.
|
65
69
|
*
|
66
70
|
* @param {Object} var_args Initial items to append,
|
@@ -77,131 +81,441 @@ soy.StringBuilder = goog.string.StringBuffer;
|
|
77
81
|
|
78
82
|
/**
|
79
83
|
* A type of textual content.
|
80
|
-
*
|
84
|
+
*
|
85
|
+
* This is an enum of type Object so that these values are unforgeable.
|
86
|
+
*
|
87
|
+
* @enum {!Object}
|
81
88
|
*/
|
82
|
-
soydata.SanitizedContentKind =
|
89
|
+
soydata.SanitizedContentKind = goog.soy.data.SanitizedContentKind;
|
83
90
|
|
84
|
-
/**
|
85
|
-
* A snippet of HTML that does not start or end inside a tag, comment, entity,
|
86
|
-
* or DOCTYPE; and that does not contain any executable code
|
87
|
-
* (JS, {@code <object>}s, etc.) from a different trust domain.
|
88
|
-
*/
|
89
|
-
HTML: 0,
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
92
|
+
/**
|
93
|
+
* Checks whether a given value is of a given content kind.
|
94
|
+
*
|
95
|
+
* @param {*} value The value to be examined.
|
96
|
+
* @param {soydata.SanitizedContentKind} contentKind The desired content
|
97
|
+
* kind.
|
98
|
+
* @return {boolean} Whether the given value is of the given kind.
|
99
|
+
* @private
|
100
|
+
*/
|
101
|
+
soydata.isContentKind = function(value, contentKind) {
|
102
|
+
// TODO(user): This function should really include the assert on
|
103
|
+
// value.constructor that is currently sprinkled at most of the call sites.
|
104
|
+
// Unfortunately, that would require a (debug-mode-only) switch statement.
|
105
|
+
// TODO(user): Perhaps we should get rid of the contentKind property
|
106
|
+
// altogether and only at the constructor.
|
107
|
+
return value != null && value.contentKind === contentKind;
|
108
|
+
};
|
104
109
|
|
105
|
-
/** A properly encoded portion of a URI. */
|
106
|
-
URI: 2,
|
107
110
|
|
108
|
-
|
109
|
-
|
111
|
+
/**
|
112
|
+
* Returns a given value's contentDir property, constrained to a
|
113
|
+
* goog.i18n.bidi.Dir value or null. Returns null if the value is null,
|
114
|
+
* undefined, a primitive or does not have a contentDir property, or the
|
115
|
+
* property's value is not 1 (for LTR), -1 (for RTL), or 0 (for neutral).
|
116
|
+
*
|
117
|
+
* @param {*} value The value whose contentDir property, if any, is to
|
118
|
+
* be returned.
|
119
|
+
* @return {?goog.i18n.bidi.Dir} The contentDir property.
|
120
|
+
*/
|
121
|
+
soydata.getContentDir = function(value) {
|
122
|
+
if (value != null) {
|
123
|
+
switch (value.contentDir) {
|
124
|
+
case goog.i18n.bidi.Dir.LTR:
|
125
|
+
return goog.i18n.bidi.Dir.LTR;
|
126
|
+
case goog.i18n.bidi.Dir.RTL:
|
127
|
+
return goog.i18n.bidi.Dir.RTL;
|
128
|
+
case goog.i18n.bidi.Dir.NEUTRAL:
|
129
|
+
return goog.i18n.bidi.Dir.NEUTRAL;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
return null;
|
110
133
|
};
|
111
134
|
|
112
135
|
|
113
136
|
/**
|
114
|
-
*
|
115
|
-
*
|
137
|
+
* Content of type {@link soydata.SanitizedContentKind.HTML}.
|
138
|
+
*
|
139
|
+
* The content is a string of HTML that can safely be embedded in a PCDATA
|
140
|
+
* context in your app. If you would be surprised to find that an HTML
|
141
|
+
* sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and
|
142
|
+
* you wouldn't write a template that produces {@code s} on security or privacy
|
143
|
+
* grounds, then don't pass {@code s} here. The default content direction is
|
144
|
+
* unknown, i.e. to be estimated when necessary.
|
145
|
+
*
|
116
146
|
* @constructor
|
117
|
-
* @
|
147
|
+
* @extends {goog.soy.data.SanitizedContent}
|
118
148
|
*/
|
119
|
-
soydata.
|
120
|
-
|
121
|
-
* The textual content.
|
122
|
-
* @type {string}
|
123
|
-
*/
|
124
|
-
this.content = content;
|
149
|
+
soydata.SanitizedHtml = function() {
|
150
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
125
151
|
};
|
126
|
-
|
127
|
-
/** @type {soydata.SanitizedContentKind} */
|
128
|
-
soydata.SanitizedContent.prototype.contentKind;
|
152
|
+
goog.inherits(soydata.SanitizedHtml, goog.soy.data.SanitizedContent);
|
129
153
|
|
130
154
|
/** @override */
|
131
|
-
soydata.
|
132
|
-
|
155
|
+
soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML;
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Returns a SanitizedHtml object for a particular value. The content direction
|
159
|
+
* is preserved.
|
160
|
+
*
|
161
|
+
* This HTML-escapes the value unless it is already SanitizedHtml.
|
162
|
+
*
|
163
|
+
* @param {*} value The value to convert. If it is already a SanitizedHtml
|
164
|
+
* object, it is left alone.
|
165
|
+
* @return {!soydata.SanitizedHtml} A SanitizedHtml object derived from the
|
166
|
+
* stringified value. It is escaped unless the input is SanitizedHtml.
|
167
|
+
*/
|
168
|
+
soydata.SanitizedHtml.from = function(value) {
|
169
|
+
// The check is soydata.isContentKind() inlined for performance.
|
170
|
+
if (value != null &&
|
171
|
+
value.contentKind === soydata.SanitizedContentKind.HTML) {
|
172
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
173
|
+
return /** @type {!soydata.SanitizedHtml} */ (value);
|
174
|
+
}
|
175
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
176
|
+
soy.esc.$$escapeHtmlHelper(String(value)), soydata.getContentDir(value));
|
133
177
|
};
|
134
178
|
|
135
179
|
|
136
180
|
/**
|
137
|
-
* Content of type {@link soydata.SanitizedContentKind.
|
138
|
-
*
|
139
|
-
*
|
140
|
-
*
|
141
|
-
*
|
142
|
-
* privacy grounds, then don't pass {@code s} here.
|
181
|
+
* Content of type {@link soydata.SanitizedContentKind.JS}.
|
182
|
+
*
|
183
|
+
* The content is Javascript source that when evaluated does not execute any
|
184
|
+
* attacker-controlled scripts. The content direction is LTR.
|
185
|
+
*
|
143
186
|
* @constructor
|
144
|
-
* @extends {
|
187
|
+
* @extends {goog.soy.data.SanitizedContent}
|
145
188
|
*/
|
146
|
-
soydata.
|
147
|
-
|
189
|
+
soydata.SanitizedJs = function() {
|
190
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
148
191
|
};
|
149
|
-
goog.inherits(soydata.
|
192
|
+
goog.inherits(soydata.SanitizedJs, goog.soy.data.SanitizedContent);
|
150
193
|
|
151
194
|
/** @override */
|
152
|
-
soydata.
|
195
|
+
soydata.SanitizedJs.prototype.contentKind =
|
196
|
+
soydata.SanitizedContentKind.JS;
|
197
|
+
|
198
|
+
/** @override */
|
199
|
+
soydata.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
153
200
|
|
154
201
|
|
155
202
|
/**
|
156
203
|
* Content of type {@link soydata.SanitizedContentKind.JS_STR_CHARS}.
|
157
|
-
*
|
158
|
-
*
|
159
|
-
*
|
160
|
-
*
|
204
|
+
*
|
205
|
+
* The content can be safely inserted as part of a single- or double-quoted
|
206
|
+
* string without terminating the string. The default content direction is
|
207
|
+
* unknown, i.e. to be estimated when necessary.
|
208
|
+
*
|
161
209
|
* @constructor
|
162
|
-
* @extends {
|
210
|
+
* @extends {goog.soy.data.SanitizedContent}
|
163
211
|
*/
|
164
|
-
soydata.SanitizedJsStrChars = function(
|
165
|
-
|
212
|
+
soydata.SanitizedJsStrChars = function() {
|
213
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
166
214
|
};
|
167
|
-
goog.inherits(soydata.SanitizedJsStrChars,
|
215
|
+
goog.inherits(soydata.SanitizedJsStrChars, goog.soy.data.SanitizedContent);
|
168
216
|
|
169
217
|
/** @override */
|
170
218
|
soydata.SanitizedJsStrChars.prototype.contentKind =
|
171
219
|
soydata.SanitizedContentKind.JS_STR_CHARS;
|
172
220
|
|
173
|
-
|
174
221
|
/**
|
175
222
|
* Content of type {@link soydata.SanitizedContentKind.URI}.
|
176
|
-
*
|
177
|
-
*
|
223
|
+
*
|
224
|
+
* The content is a URI chunk that the caller knows is safe to emit in a
|
225
|
+
* template. The content direction is LTR.
|
226
|
+
*
|
178
227
|
* @constructor
|
179
|
-
* @extends {
|
228
|
+
* @extends {goog.soy.data.SanitizedContent}
|
180
229
|
*/
|
181
|
-
soydata.SanitizedUri = function(
|
182
|
-
|
230
|
+
soydata.SanitizedUri = function() {
|
231
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
183
232
|
};
|
184
|
-
goog.inherits(soydata.SanitizedUri,
|
233
|
+
goog.inherits(soydata.SanitizedUri, goog.soy.data.SanitizedContent);
|
185
234
|
|
186
235
|
/** @override */
|
187
236
|
soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI;
|
188
237
|
|
238
|
+
/** @override */
|
239
|
+
soydata.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
240
|
+
|
189
241
|
|
190
242
|
/**
|
191
|
-
* Content of type {@link soydata.SanitizedContentKind.
|
192
|
-
*
|
193
|
-
*
|
243
|
+
* Content of type {@link soydata.SanitizedContentKind.ATTRIBUTES}.
|
244
|
+
*
|
245
|
+
* The content should be safely embeddable within an open tag, such as a
|
246
|
+
* key="value" pair. The content direction is LTR.
|
247
|
+
*
|
194
248
|
* @constructor
|
195
|
-
* @extends {
|
249
|
+
* @extends {goog.soy.data.SanitizedContent}
|
196
250
|
*/
|
197
|
-
soydata.SanitizedHtmlAttribute = function(
|
198
|
-
|
251
|
+
soydata.SanitizedHtmlAttribute = function() {
|
252
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
199
253
|
};
|
200
|
-
goog.inherits(soydata.SanitizedHtmlAttribute,
|
254
|
+
goog.inherits(soydata.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
|
201
255
|
|
202
256
|
/** @override */
|
203
257
|
soydata.SanitizedHtmlAttribute.prototype.contentKind =
|
204
|
-
soydata.SanitizedContentKind.
|
258
|
+
soydata.SanitizedContentKind.ATTRIBUTES;
|
259
|
+
|
260
|
+
/** @override */
|
261
|
+
soydata.SanitizedHtmlAttribute.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
262
|
+
|
263
|
+
|
264
|
+
/**
|
265
|
+
* Content of type {@link soydata.SanitizedContentKind.CSS}.
|
266
|
+
*
|
267
|
+
* The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
|
268
|
+
* The content direction is LTR.
|
269
|
+
*
|
270
|
+
* @constructor
|
271
|
+
* @extends {goog.soy.data.SanitizedContent}
|
272
|
+
*/
|
273
|
+
soydata.SanitizedCss = function() {
|
274
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
275
|
+
};
|
276
|
+
goog.inherits(soydata.SanitizedCss, goog.soy.data.SanitizedContent);
|
277
|
+
|
278
|
+
/** @override */
|
279
|
+
soydata.SanitizedCss.prototype.contentKind =
|
280
|
+
soydata.SanitizedContentKind.CSS;
|
281
|
+
|
282
|
+
/** @override */
|
283
|
+
soydata.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
284
|
+
|
285
|
+
|
286
|
+
/**
|
287
|
+
* Unsanitized plain text string.
|
288
|
+
*
|
289
|
+
* While all strings are effectively safe to use as a plain text, there are no
|
290
|
+
* guarantees about safety in any other context such as HTML. This is
|
291
|
+
* sometimes used to mark that should never be used unescaped.
|
292
|
+
*
|
293
|
+
* @param {*} content Plain text with no guarantees.
|
294
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
295
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
296
|
+
* @constructor
|
297
|
+
* @extends {goog.soy.data.SanitizedContent}
|
298
|
+
*/
|
299
|
+
soydata.UnsanitizedText = function(content, opt_contentDir) {
|
300
|
+
/** @override */
|
301
|
+
this.content = String(content);
|
302
|
+
this.contentDir = opt_contentDir != null ? opt_contentDir : null;
|
303
|
+
};
|
304
|
+
goog.inherits(soydata.UnsanitizedText, goog.soy.data.SanitizedContent);
|
305
|
+
|
306
|
+
/** @override */
|
307
|
+
soydata.UnsanitizedText.prototype.contentKind =
|
308
|
+
soydata.SanitizedContentKind.TEXT;
|
309
|
+
|
310
|
+
|
311
|
+
/**
|
312
|
+
* Empty string, used as a type in Soy templates.
|
313
|
+
* @enum {string}
|
314
|
+
* @private
|
315
|
+
*/
|
316
|
+
soydata.$$EMPTY_STRING_ = {
|
317
|
+
VALUE: ''
|
318
|
+
};
|
319
|
+
|
320
|
+
|
321
|
+
/**
|
322
|
+
* Creates a factory for SanitizedContent types.
|
323
|
+
*
|
324
|
+
* This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
|
325
|
+
* instantiate Sanitized* classes, without making the Sanitized* constructors
|
326
|
+
* publicly usable. Requiring all construction to use the VERY_UNSAFE names
|
327
|
+
* helps callers and their reviewers easily tell that creating SanitizedContent
|
328
|
+
* is not always safe and calls for careful review.
|
329
|
+
*
|
330
|
+
* @param {function(new: T)} ctor A constructor.
|
331
|
+
* @return {!function(*, ?goog.i18n.bidi.Dir=): T} A factory that takes
|
332
|
+
* content and an optional content direction and returns a new instance. If
|
333
|
+
* the content direction is undefined, ctor.prototype.contentDir is used.
|
334
|
+
* @template T
|
335
|
+
* @private
|
336
|
+
*/
|
337
|
+
soydata.$$makeSanitizedContentFactory_ = function(ctor) {
|
338
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
339
|
+
function InstantiableCtor() {}
|
340
|
+
InstantiableCtor.prototype = ctor.prototype;
|
341
|
+
/**
|
342
|
+
* Creates a ctor-type SanitizedContent instance.
|
343
|
+
*
|
344
|
+
* @param {*} content The content to put in the instance.
|
345
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
|
346
|
+
* undefined, ctor.prototype.contentDir is used.
|
347
|
+
* @return {!goog.soy.data.SanitizedContent} The new instance. It is actually
|
348
|
+
* of type T above (ctor's type, a descendant of SanitizedContent), but
|
349
|
+
* there is no way to express that here.
|
350
|
+
*/
|
351
|
+
function sanitizedContentFactory(content, opt_contentDir) {
|
352
|
+
var result = new InstantiableCtor();
|
353
|
+
result.content = String(content);
|
354
|
+
if (opt_contentDir !== undefined) {
|
355
|
+
result.contentDir = opt_contentDir;
|
356
|
+
}
|
357
|
+
return result;
|
358
|
+
}
|
359
|
+
return sanitizedContentFactory;
|
360
|
+
};
|
361
|
+
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Creates a factory for SanitizedContent types that should always have their
|
365
|
+
* default directionality.
|
366
|
+
*
|
367
|
+
* This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
|
368
|
+
* instantiate Sanitized* classes, without making the Sanitized* constructors
|
369
|
+
* publicly usable. Requiring all construction to use the VERY_UNSAFE names
|
370
|
+
* helps callers and their reviewers easily tell that creating SanitizedContent
|
371
|
+
* is not always safe and calls for careful review.
|
372
|
+
*
|
373
|
+
* @param {function(new: T, string)} ctor A constructor.
|
374
|
+
* @return {!function(*): T} A factory that takes content and returns a new
|
375
|
+
* instance (with default directionality, i.e. ctor.prototype.contentDir).
|
376
|
+
* @template T
|
377
|
+
* @private
|
378
|
+
*/
|
379
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_ = function(ctor) {
|
380
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
381
|
+
function InstantiableCtor() {}
|
382
|
+
InstantiableCtor.prototype = ctor.prototype;
|
383
|
+
/**
|
384
|
+
* Creates a ctor-type SanitizedContent instance.
|
385
|
+
*
|
386
|
+
* @param {*} content The content to put in the instance.
|
387
|
+
* @return {!goog.soy.data.SanitizedContent} The new instance. It is actually
|
388
|
+
* of type T above (ctor's type, a descendant of SanitizedContent), but
|
389
|
+
* there is no way to express that here.
|
390
|
+
*/
|
391
|
+
function sanitizedContentFactory(content) {
|
392
|
+
var result = new InstantiableCtor();
|
393
|
+
result.content = String(content);
|
394
|
+
return result;
|
395
|
+
}
|
396
|
+
return sanitizedContentFactory;
|
397
|
+
};
|
398
|
+
|
399
|
+
|
400
|
+
// -----------------------------------------------------------------------------
|
401
|
+
// Sanitized content ordainers. Please use these with extreme caution (with the
|
402
|
+
// exception of markUnsanitizedText). A good recommendation is to limit usage
|
403
|
+
// of these to just a handful of files in your source tree where usages can be
|
404
|
+
// carefully audited.
|
405
|
+
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Protects a string from being used in an noAutoescaped context.
|
409
|
+
*
|
410
|
+
* This is useful for content where there is significant risk of accidental
|
411
|
+
* unescaped usage in a Soy template. A great case is for user-controlled
|
412
|
+
* data that has historically been a source of vulernabilities.
|
413
|
+
*
|
414
|
+
* @param {*} content Text to protect.
|
415
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
416
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
417
|
+
* @return {!soydata.UnsanitizedText} A wrapper that is rejected by the
|
418
|
+
* Soy noAutoescape print directive.
|
419
|
+
*/
|
420
|
+
soydata.markUnsanitizedText = function(content, opt_contentDir) {
|
421
|
+
return new soydata.UnsanitizedText(content, opt_contentDir);
|
422
|
+
};
|
423
|
+
|
424
|
+
|
425
|
+
/**
|
426
|
+
* Takes a leap of faith that the provided content is "safe" HTML.
|
427
|
+
*
|
428
|
+
* @param {*} content A string of HTML that can safely be embedded in
|
429
|
+
* a PCDATA context in your app. If you would be surprised to find that an
|
430
|
+
* HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs)
|
431
|
+
* and you wouldn't write a template that produces {@code s} on security or
|
432
|
+
* privacy grounds, then don't pass {@code s} here.
|
433
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
434
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
435
|
+
* @return {!soydata.SanitizedHtml} Sanitized content wrapper that
|
436
|
+
* indicates to Soy not to escape when printed as HTML.
|
437
|
+
*/
|
438
|
+
soydata.VERY_UNSAFE.ordainSanitizedHtml =
|
439
|
+
soydata.$$makeSanitizedContentFactory_(soydata.SanitizedHtml);
|
440
|
+
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Takes a leap of faith that the provided content is "safe" (non-attacker-
|
444
|
+
* controlled, XSS-free) Javascript.
|
445
|
+
*
|
446
|
+
* @param {*} content Javascript source that when evaluated does not
|
447
|
+
* execute any attacker-controlled scripts.
|
448
|
+
* @return {!soydata.SanitizedJs} Sanitized content wrapper that indicates to
|
449
|
+
* Soy not to escape when printed as Javascript source.
|
450
|
+
*/
|
451
|
+
soydata.VERY_UNSAFE.ordainSanitizedJs =
|
452
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
453
|
+
soydata.SanitizedJs);
|
454
|
+
|
455
|
+
|
456
|
+
// TODO: This function is probably necessary, either externally or internally
|
457
|
+
// as an implementation detail. Generally, plain text will always work here,
|
458
|
+
// as there's no harm to unescaping the string and then re-escaping when
|
459
|
+
// finally printed.
|
460
|
+
/**
|
461
|
+
* Takes a leap of faith that the provided content can be safely embedded in
|
462
|
+
* a Javascript string without re-esacping.
|
463
|
+
*
|
464
|
+
* @param {*} content Content that can be safely inserted as part of a
|
465
|
+
* single- or double-quoted string without terminating the string.
|
466
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
467
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
468
|
+
* @return {!soydata.SanitizedJsStrChars} Sanitized content wrapper that
|
469
|
+
* indicates to Soy not to escape when printed in a JS string.
|
470
|
+
*/
|
471
|
+
soydata.VERY_UNSAFE.ordainSanitizedJsStrChars =
|
472
|
+
soydata.$$makeSanitizedContentFactory_(soydata.SanitizedJsStrChars);
|
473
|
+
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Takes a leap of faith that the provided content is "safe" to use as a URI
|
477
|
+
* in a Soy template.
|
478
|
+
*
|
479
|
+
* This creates a Soy SanitizedContent object which indicates to Soy there is
|
480
|
+
* no need to escape it when printed as a URI (e.g. in an href or src
|
481
|
+
* attribute), such as if it's already been encoded or if it's a Javascript:
|
482
|
+
* URI.
|
483
|
+
*
|
484
|
+
* @param {*} content A chunk of URI that the caller knows is safe to
|
485
|
+
* emit in a template.
|
486
|
+
* @return {!soydata.SanitizedUri} Sanitized content wrapper that indicates to
|
487
|
+
* Soy not to escape or filter when printed in URI context.
|
488
|
+
*/
|
489
|
+
soydata.VERY_UNSAFE.ordainSanitizedUri =
|
490
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
491
|
+
soydata.SanitizedUri);
|
492
|
+
|
493
|
+
|
494
|
+
/**
|
495
|
+
* Takes a leap of faith that the provided content is "safe" to use as an
|
496
|
+
* HTML attribute.
|
497
|
+
*
|
498
|
+
* @param {*} content An attribute name and value, such as
|
499
|
+
* {@code dir="ltr"}.
|
500
|
+
* @return {!soydata.SanitizedHtmlAttribute} Sanitized content wrapper that
|
501
|
+
* indicates to Soy not to escape when printed as an HTML attribute.
|
502
|
+
*/
|
503
|
+
soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute =
|
504
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
505
|
+
soydata.SanitizedHtmlAttribute);
|
506
|
+
|
507
|
+
|
508
|
+
/**
|
509
|
+
* Takes a leap of faith that the provided content is "safe" to use as CSS
|
510
|
+
* in a style attribute or block.
|
511
|
+
*
|
512
|
+
* @param {*} content CSS, such as {@code color:#c3d9ff}.
|
513
|
+
* @return {!soydata.SanitizedCss} Sanitized CSS wrapper that indicates to
|
514
|
+
* Soy there is no need to escape or filter when printed in CSS context.
|
515
|
+
*/
|
516
|
+
soydata.VERY_UNSAFE.ordainSanitizedCss =
|
517
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
518
|
+
soydata.SanitizedCss);
|
205
519
|
|
206
520
|
|
207
521
|
// -----------------------------------------------------------------------------
|
@@ -217,9 +531,11 @@ soydata.SanitizedHtmlAttribute.prototype.contentKind =
|
|
217
531
|
* NOTE: New code should consider using goog.soy.renderElement instead.
|
218
532
|
*
|
219
533
|
* @param {Element} element The element whose content we are rendering.
|
220
|
-
* @param {
|
221
|
-
*
|
534
|
+
* @param {null|function(ARG_TYPES, null=, Object.<string, *>=):*} template
|
535
|
+
* The Soy template defining the element's content.
|
536
|
+
* @param {ARG_TYPES} opt_templateData The data for the template.
|
222
537
|
* @param {Object=} opt_injectedData The injected data for the template.
|
538
|
+
* @template ARG_TYPES
|
223
539
|
*/
|
224
540
|
soy.renderElement = goog.soy.renderElement;
|
225
541
|
|
@@ -234,12 +550,14 @@ soy.renderElement = goog.soy.renderElement;
|
|
234
550
|
* NOTE: New code should consider using goog.soy.renderAsFragment
|
235
551
|
* instead (note that the arguments are different).
|
236
552
|
*
|
237
|
-
* @param {
|
238
|
-
*
|
553
|
+
* @param {null|function(ARG_TYPES, null=, Object.<string, *>=):*} template
|
554
|
+
* The Soy template defining the element's content.
|
555
|
+
* @param {ARG_TYPES} opt_templateData The data for the template.
|
239
556
|
* @param {Document=} opt_document The document used to create DOM nodes. If not
|
240
557
|
* specified, global document object is used.
|
241
558
|
* @param {Object=} opt_injectedData The injected data for the template.
|
242
559
|
* @return {!Node} The resulting node or document fragment.
|
560
|
+
* @template ARG_TYPES
|
243
561
|
*/
|
244
562
|
soy.renderAsFragment = function(
|
245
563
|
template, opt_templateData, opt_document, opt_injectedData) {
|
@@ -257,13 +575,15 @@ soy.renderAsFragment = function(
|
|
257
575
|
* NOTE: New code should consider using goog.soy.renderAsElement
|
258
576
|
* instead (note that the arguments are different).
|
259
577
|
*
|
260
|
-
* @param {
|
261
|
-
*
|
578
|
+
* @param {null|function(ARG_TYPES, null=, Object.<string, *>=):*} template
|
579
|
+
* The Soy template defining the element's content.
|
580
|
+
* @param {ARG_TYPES} opt_templateData The data for the template.
|
262
581
|
* @param {Document=} opt_document The document used to create DOM nodes. If not
|
263
582
|
* specified, global document object is used.
|
264
583
|
* @param {Object=} opt_injectedData The injected data for the template.
|
265
584
|
* @return {!Element} Rendered template contents, wrapped in a parent DIV
|
266
585
|
* element if necessary.
|
586
|
+
* @template ARG_TYPES
|
267
587
|
*/
|
268
588
|
soy.renderAsElement = function(
|
269
589
|
template, opt_templateData, opt_document, opt_injectedData) {
|
@@ -278,39 +598,61 @@ soy.renderAsElement = function(
|
|
278
598
|
|
279
599
|
|
280
600
|
/**
|
281
|
-
*
|
282
|
-
*
|
283
|
-
*
|
284
|
-
|
285
|
-
|
286
|
-
|
601
|
+
* Whether the locale is right-to-left.
|
602
|
+
*
|
603
|
+
* @type {boolean}
|
604
|
+
*/
|
605
|
+
soy.$$IS_LOCALE_RTL = goog.i18n.bidi.IS_RTL;
|
606
|
+
|
607
|
+
|
608
|
+
/**
|
609
|
+
* Builds an augmented map. The returned map will contain mappings from both
|
610
|
+
* the base map and the additional map. If the same key appears in both, then
|
611
|
+
* the value from the additional map will be visible, while the value from the
|
612
|
+
* base map will be hidden. The base map will be used, but not modified.
|
287
613
|
*
|
288
|
-
* @param {!Object}
|
289
|
-
* @param {Object}
|
290
|
-
* @return {Object} An augmented
|
291
|
-
*
|
614
|
+
* @param {!Object} baseMap The original map to augment.
|
615
|
+
* @param {!Object} additionalMap A map containing the additional mappings.
|
616
|
+
* @return {!Object} An augmented map containing both the original and
|
617
|
+
* additional mappings.
|
292
618
|
*/
|
293
|
-
soy.$$
|
619
|
+
soy.$$augmentMap = function(baseMap, additionalMap) {
|
294
620
|
|
295
|
-
// Create a new
|
621
|
+
// Create a new map whose '__proto__' field is set to baseMap.
|
296
622
|
/** @constructor */
|
297
623
|
function TempCtor() {}
|
298
|
-
TempCtor.prototype =
|
299
|
-
var
|
624
|
+
TempCtor.prototype = baseMap;
|
625
|
+
var augmentedMap = new TempCtor();
|
300
626
|
|
301
|
-
// Add the additional
|
302
|
-
for (var key in
|
303
|
-
|
627
|
+
// Add the additional mappings to the new map.
|
628
|
+
for (var key in additionalMap) {
|
629
|
+
augmentedMap[key] = additionalMap[key];
|
304
630
|
}
|
305
631
|
|
306
|
-
return
|
632
|
+
return augmentedMap;
|
633
|
+
};
|
634
|
+
|
635
|
+
|
636
|
+
/**
|
637
|
+
* Checks that the given map key is a string.
|
638
|
+
* @param {*} key Key to check.
|
639
|
+
* @return {string} The given key.
|
640
|
+
*/
|
641
|
+
soy.$$checkMapKey = function(key) {
|
642
|
+
// TODO: Support map literal with nonstring key.
|
643
|
+
if ((typeof key) != 'string') {
|
644
|
+
throw Error(
|
645
|
+
'Map literal\'s key expression must evaluate to string' +
|
646
|
+
' (encountered type "' + (typeof key) + '").');
|
647
|
+
}
|
648
|
+
return key;
|
307
649
|
};
|
308
650
|
|
309
651
|
|
310
652
|
/**
|
311
653
|
* Gets the keys in a map as an array. There are no guarantees on the order.
|
312
654
|
* @param {Object} map The map to get the keys of.
|
313
|
-
* @return {Array.<string>} The array of keys in the given map.
|
655
|
+
* @return {!Array.<string>} The array of keys in the given map.
|
314
656
|
*/
|
315
657
|
soy.$$getMapKeys = function(map) {
|
316
658
|
var mapKeys = [];
|
@@ -339,13 +681,13 @@ soy.$$getMapKeys = function(map) {
|
|
339
681
|
*
|
340
682
|
* @consistentIdGenerator
|
341
683
|
*/
|
342
|
-
soy.$$
|
684
|
+
soy.$$getDelTemplateId = function(delTemplateName) {
|
343
685
|
return delTemplateName;
|
344
686
|
};
|
345
687
|
|
346
688
|
|
347
689
|
/**
|
348
|
-
* Map from registered delegate template
|
690
|
+
* Map from registered delegate template key to the priority of the
|
349
691
|
* implementation.
|
350
692
|
* @type {Object}
|
351
693
|
* @private
|
@@ -353,7 +695,7 @@ soy.$$getDelegateId = function(delTemplateName) {
|
|
353
695
|
soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {};
|
354
696
|
|
355
697
|
/**
|
356
|
-
* Map from registered delegate template
|
698
|
+
* Map from registered delegate template key to the implementation function.
|
357
699
|
* @type {Object}
|
358
700
|
* @private
|
359
701
|
*/
|
@@ -361,17 +703,21 @@ soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {};
|
|
361
703
|
|
362
704
|
|
363
705
|
/**
|
364
|
-
* Registers a delegate implementation. If the same delegate template id
|
365
|
-
* has been registered previously, then priority values are
|
366
|
-
* the higher priority implementation is stored (if
|
367
|
-
* error is thrown).
|
706
|
+
* Registers a delegate implementation. If the same delegate template key (id
|
707
|
+
* and variant) has been registered previously, then priority values are
|
708
|
+
* compared and only the higher priority implementation is stored (if
|
709
|
+
* priorities are equal, an error is thrown).
|
368
710
|
*
|
369
|
-
* @param {string} delTemplateId The delegate template id
|
711
|
+
* @param {string} delTemplateId The delegate template id.
|
712
|
+
* @param {string} delTemplateVariant The delegate template variant (can be
|
713
|
+
* empty string).
|
370
714
|
* @param {number} delPriority The implementation's priority value.
|
371
715
|
* @param {Function} delFn The implementation function.
|
372
716
|
*/
|
373
|
-
soy.$$registerDelegateFn = function(
|
374
|
-
|
717
|
+
soy.$$registerDelegateFn = function(
|
718
|
+
delTemplateId, delTemplateVariant, delPriority, delFn) {
|
719
|
+
|
720
|
+
var mapKey = 'key_' + delTemplateId + ':' + delTemplateVariant;
|
375
721
|
var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey];
|
376
722
|
if (currPriority === undefined || delPriority > currPriority) {
|
377
723
|
// Registering new or higher-priority function: replace registry entry.
|
@@ -380,8 +726,8 @@ soy.$$registerDelegateFn = function(delTemplateId, delPriority, delFn) {
|
|
380
726
|
} else if (delPriority == currPriority) {
|
381
727
|
// Registering same-priority function: error.
|
382
728
|
throw Error(
|
383
|
-
'Encountered two active delegates with same priority (
|
384
|
-
|
729
|
+
'Encountered two active delegates with the same priority ("' +
|
730
|
+
delTemplateId + ':' + delTemplateVariant + '").');
|
385
731
|
} else {
|
386
732
|
// Registering lower-priority function: do nothing.
|
387
733
|
}
|
@@ -390,16 +736,38 @@ soy.$$registerDelegateFn = function(delTemplateId, delPriority, delFn) {
|
|
390
736
|
|
391
737
|
/**
|
392
738
|
* Retrieves the (highest-priority) implementation that has been registered for
|
393
|
-
* a given delegate template id
|
394
|
-
* for the
|
395
|
-
*
|
739
|
+
* a given delegate template key (id and variant). If no implementation has
|
740
|
+
* been registered for the key, then the fallback is the same id with empty
|
741
|
+
* variant. If the fallback is also not registered, and allowsEmptyDefault is
|
742
|
+
* true, then returns an implementation that is equivalent to an empty template
|
743
|
+
* (i.e. rendered output would be empty string).
|
396
744
|
*
|
397
|
-
* @param {string} delTemplateId The delegate template id
|
745
|
+
* @param {string} delTemplateId The delegate template id.
|
746
|
+
* @param {string} delTemplateVariant The delegate template variant (can be
|
747
|
+
* empty string).
|
748
|
+
* @param {boolean} allowsEmptyDefault Whether to default to the empty template
|
749
|
+
* function if there's no active implementation.
|
398
750
|
* @return {Function} The retrieved implementation function.
|
399
751
|
*/
|
400
|
-
soy.$$getDelegateFn = function(
|
401
|
-
|
402
|
-
|
752
|
+
soy.$$getDelegateFn = function(
|
753
|
+
delTemplateId, delTemplateVariant, allowsEmptyDefault) {
|
754
|
+
|
755
|
+
var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_[
|
756
|
+
'key_' + delTemplateId + ':' + delTemplateVariant];
|
757
|
+
if (! delFn && delTemplateVariant != '') {
|
758
|
+
// Fallback to empty variant.
|
759
|
+
delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId + ':'];
|
760
|
+
}
|
761
|
+
|
762
|
+
if (delFn) {
|
763
|
+
return delFn;
|
764
|
+
} else if (allowsEmptyDefault) {
|
765
|
+
return soy.$$EMPTY_TEMPLATE_FN_;
|
766
|
+
} else {
|
767
|
+
throw Error(
|
768
|
+
'Found no active impl for delegate call to "' + delTemplateId + ':' +
|
769
|
+
delTemplateVariant + '" (and not allowemptydefault="true").');
|
770
|
+
}
|
403
771
|
};
|
404
772
|
|
405
773
|
|
@@ -418,26 +786,222 @@ soy.$$EMPTY_TEMPLATE_FN_ = function(opt_data, opt_sb, opt_ijData) {
|
|
418
786
|
};
|
419
787
|
|
420
788
|
|
789
|
+
// -----------------------------------------------------------------------------
|
790
|
+
// Internal sanitized content wrappers.
|
791
|
+
|
792
|
+
|
793
|
+
/**
|
794
|
+
* Creates a SanitizedContent factory for SanitizedContent types for internal
|
795
|
+
* Soy let and param blocks.
|
796
|
+
*
|
797
|
+
* This is a hack within Soy so that SanitizedContent objects created via let
|
798
|
+
* and param blocks will truth-test as false if they are empty string.
|
799
|
+
* Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
|
800
|
+
* not possible, and changing the Soy compiler to wrap every boolean statement
|
801
|
+
* for just this purpose is impractical. Instead, we just avoid wrapping empty
|
802
|
+
* string as SanitizedContent, since it's a no-op for empty strings anyways.
|
803
|
+
*
|
804
|
+
* @param {function(new: T)} ctor A constructor.
|
805
|
+
* @return {!function(*, ?goog.i18n.bidi.Dir=): (T|soydata.$$EMPTY_STRING_)}
|
806
|
+
* A factory that takes content and an optional content direction and
|
807
|
+
* returns a new instance, or an empty string. If the content direction is
|
808
|
+
* undefined, ctor.prototype.contentDir is used.
|
809
|
+
* @template T
|
810
|
+
* @private
|
811
|
+
*/
|
812
|
+
soydata.$$makeSanitizedContentFactoryForInternalBlocks_ = function(ctor) {
|
813
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
814
|
+
function InstantiableCtor() {}
|
815
|
+
InstantiableCtor.prototype = ctor.prototype;
|
816
|
+
/**
|
817
|
+
* Creates a ctor-type SanitizedContent instance.
|
818
|
+
*
|
819
|
+
* @param {*} content The content to put in the instance.
|
820
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
|
821
|
+
* undefined, ctor.prototype.contentDir is used.
|
822
|
+
* @return {!goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
|
823
|
+
* instance, or an empty string. A new instance is actually of type T
|
824
|
+
* above (ctor's type, a descendant of SanitizedContent), but there's no
|
825
|
+
* way to express that here.
|
826
|
+
* a descendant of SanitizedContent), but there's no way to express that here.
|
827
|
+
*/
|
828
|
+
function sanitizedContentFactory(content, opt_contentDir) {
|
829
|
+
var contentString = String(content);
|
830
|
+
if (!contentString) {
|
831
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
832
|
+
}
|
833
|
+
var result = new InstantiableCtor();
|
834
|
+
result.content = String(content);
|
835
|
+
if (opt_contentDir !== undefined) {
|
836
|
+
result.contentDir = opt_contentDir;
|
837
|
+
}
|
838
|
+
return result;
|
839
|
+
}
|
840
|
+
return sanitizedContentFactory;
|
841
|
+
};
|
842
|
+
|
843
|
+
|
844
|
+
/**
|
845
|
+
* Creates a SanitizedContent factory for SanitizedContent types that should
|
846
|
+
* always have their default directionality for internal Soy let and param
|
847
|
+
* blocks.
|
848
|
+
*
|
849
|
+
* This is a hack within Soy so that SanitizedContent objects created via let
|
850
|
+
* and param blocks will truth-test as false if they are empty string.
|
851
|
+
* Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
|
852
|
+
* not possible, and changing the Soy compiler to wrap every boolean statement
|
853
|
+
* for just this purpose is impractical. Instead, we just avoid wrapping empty
|
854
|
+
* string as SanitizedContent, since it's a no-op for empty strings anyways.
|
855
|
+
*
|
856
|
+
* @param {function(new: T)} ctor A constructor.
|
857
|
+
* @return {!function(*): (T|soydata.$$EMPTY_STRING_)} A
|
858
|
+
* factory that takes content and returns a
|
859
|
+
* new instance (with default directionality, i.e.
|
860
|
+
* ctor.prototype.contentDir), or an empty string.
|
861
|
+
* @template T
|
862
|
+
* @private
|
863
|
+
*/
|
864
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_ =
|
865
|
+
function(ctor) {
|
866
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
867
|
+
function InstantiableCtor() {}
|
868
|
+
InstantiableCtor.prototype = ctor.prototype;
|
869
|
+
/**
|
870
|
+
* Creates a ctor-type SanitizedContent instance.
|
871
|
+
*
|
872
|
+
* @param {*} content The content to put in the instance.
|
873
|
+
* @return {!goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
|
874
|
+
* instance, or an empty string. A new instance is actually of type T
|
875
|
+
* above (ctor's type, a descendant of SanitizedContent), but there's no
|
876
|
+
* way to express that here.
|
877
|
+
* a descendant of SanitizedContent), but there's no way to express that here.
|
878
|
+
*/
|
879
|
+
function sanitizedContentFactory(content) {
|
880
|
+
var contentString = String(content);
|
881
|
+
if (!contentString) {
|
882
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
883
|
+
}
|
884
|
+
var result = new InstantiableCtor();
|
885
|
+
result.content = String(content);
|
886
|
+
return result;
|
887
|
+
}
|
888
|
+
return sanitizedContentFactory;
|
889
|
+
};
|
890
|
+
|
891
|
+
|
892
|
+
/**
|
893
|
+
* Creates kind="text" block contents (internal use only).
|
894
|
+
*
|
895
|
+
* @param {*} content Text.
|
896
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
897
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
898
|
+
* @return {!soydata.UnsanitizedText|soydata.$$EMPTY_STRING_} Wrapped result.
|
899
|
+
*/
|
900
|
+
soydata.$$markUnsanitizedTextForInternalBlocks = function(
|
901
|
+
content, opt_contentDir) {
|
902
|
+
var contentString = String(content);
|
903
|
+
if (!contentString) {
|
904
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
905
|
+
}
|
906
|
+
return new soydata.UnsanitizedText(contentString, opt_contentDir);
|
907
|
+
};
|
908
|
+
|
909
|
+
|
910
|
+
/**
|
911
|
+
* Creates kind="html" block contents (internal use only).
|
912
|
+
*
|
913
|
+
* @param {*} content Text.
|
914
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
915
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
916
|
+
* @return {!soydata.SanitizedHtml|soydata.$$EMPTY_STRING_} Wrapped result.
|
917
|
+
*/
|
918
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedHtmlForInternalBlocks =
|
919
|
+
soydata.$$makeSanitizedContentFactoryForInternalBlocks_(
|
920
|
+
soydata.SanitizedHtml);
|
921
|
+
|
922
|
+
|
923
|
+
/**
|
924
|
+
* Creates kind="js" block contents (internal use only).
|
925
|
+
*
|
926
|
+
* @param {*} content Text.
|
927
|
+
* @return {!soydata.SanitizedJs|soydata.$$EMPTY_STRING_} Wrapped result.
|
928
|
+
*/
|
929
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedJsForInternalBlocks =
|
930
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
931
|
+
soydata.SanitizedJs);
|
932
|
+
|
933
|
+
|
934
|
+
/**
|
935
|
+
* Creates kind="uri" block contents (internal use only).
|
936
|
+
*
|
937
|
+
* @param {*} content Text.
|
938
|
+
* @return {soydata.SanitizedUri|soydata.$$EMPTY_STRING_} Wrapped result.
|
939
|
+
*/
|
940
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedUriForInternalBlocks =
|
941
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
942
|
+
soydata.SanitizedUri);
|
943
|
+
|
944
|
+
|
945
|
+
/**
|
946
|
+
* Creates kind="attributes" block contents (internal use only).
|
947
|
+
*
|
948
|
+
* @param {*} content Text.
|
949
|
+
* @return {soydata.SanitizedHtmlAttribute|soydata.$$EMPTY_STRING_} Wrapped
|
950
|
+
* result.
|
951
|
+
*/
|
952
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedAttributesForInternalBlocks =
|
953
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
954
|
+
soydata.SanitizedHtmlAttribute);
|
955
|
+
|
956
|
+
|
957
|
+
/**
|
958
|
+
* Creates kind="css" block contents (internal use only).
|
959
|
+
*
|
960
|
+
* @param {*} content Text.
|
961
|
+
* @return {soydata.SanitizedCss|soydata.$$EMPTY_STRING_} Wrapped result.
|
962
|
+
*/
|
963
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedCssForInternalBlocks =
|
964
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
965
|
+
soydata.SanitizedCss);
|
966
|
+
|
967
|
+
|
421
968
|
// -----------------------------------------------------------------------------
|
422
969
|
// Escape/filter/normalize.
|
423
970
|
|
424
971
|
|
425
972
|
/**
|
426
|
-
*
|
427
|
-
*
|
428
|
-
* tag attribute value within double quotes.
|
429
|
-
* Will emit known safe HTML as-is.
|
973
|
+
* Returns a SanitizedHtml object for a particular value. The content direction
|
974
|
+
* is preserved.
|
430
975
|
*
|
431
|
-
*
|
432
|
-
*
|
433
|
-
*
|
976
|
+
* This HTML-escapes the value unless it is already SanitizedHtml. Escapes
|
977
|
+
* double quote '"' in addition to '&', '<', and '>' so that a string can be
|
978
|
+
* included in an HTML tag attribute value within double quotes.
|
979
|
+
*
|
980
|
+
* @param {*} value The value to convert. If it is already a SanitizedHtml
|
981
|
+
* object, it is left alone.
|
982
|
+
* @return {!soydata.SanitizedHtml} An escaped version of value.
|
434
983
|
*/
|
435
984
|
soy.$$escapeHtml = function(value) {
|
436
|
-
|
437
|
-
|
438
|
-
|
985
|
+
return soydata.SanitizedHtml.from(value);
|
986
|
+
};
|
987
|
+
|
988
|
+
|
989
|
+
/**
|
990
|
+
* Strips unsafe tags to convert a string of untrusted HTML into HTML that
|
991
|
+
* is safe to embed. The content direction is preserved.
|
992
|
+
*
|
993
|
+
* @param {*} value The string-like value to be escaped. May not be a string,
|
994
|
+
* but the value will be coerced to a string.
|
995
|
+
* @return {!soydata.SanitizedHtml} A sanitized and normalized version of value.
|
996
|
+
*/
|
997
|
+
soy.$$cleanHtml = function(value) {
|
998
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
999
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
1000
|
+
return /** @type {!soydata.SanitizedHtml} */ (value);
|
439
1001
|
}
|
440
|
-
return
|
1002
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
1003
|
+
soy.$$stripHtmlTags(value, soy.esc.$$SAFE_TAG_WHITELIST_),
|
1004
|
+
soydata.getContentDir(value));
|
441
1005
|
};
|
442
1006
|
|
443
1007
|
|
@@ -446,7 +1010,7 @@ soy.$$escapeHtml = function(value) {
|
|
446
1010
|
* RCDATA.
|
447
1011
|
* <p>
|
448
1012
|
* Escapes HTML special characters so that the value will not prematurely end
|
449
|
-
* the body of a tag like {@code <textarea>} or {@code <title>}.
|
1013
|
+
* the body of a tag like {@code <textarea>} or {@code <title>}. RCDATA tags
|
450
1014
|
* cannot contain other HTML entities, so it is not strictly necessary to escape
|
451
1015
|
* HTML special characters except when part of that text looks like an HTML
|
452
1016
|
* entity or like a close tag : {@code </textarea>}.
|
@@ -455,13 +1019,13 @@ soy.$$escapeHtml = function(value) {
|
|
455
1019
|
* contain an innocuous {@code </textarea>} don't prematurely end an RCDATA
|
456
1020
|
* element.
|
457
1021
|
*
|
458
|
-
* @param {*} value The string-like value to be escaped.
|
1022
|
+
* @param {*} value The string-like value to be escaped. May not be a string,
|
459
1023
|
* but the value will be coerced to a string.
|
460
1024
|
* @return {string} An escaped version of value.
|
461
1025
|
*/
|
462
1026
|
soy.$$escapeHtmlRcdata = function(value) {
|
463
|
-
if (
|
464
|
-
|
1027
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
1028
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
465
1029
|
return soy.esc.$$normalizeHtmlHelper(value.content);
|
466
1030
|
}
|
467
1031
|
return soy.esc.$$escapeHtmlHelper(value);
|
@@ -469,29 +1033,133 @@ soy.$$escapeHtmlRcdata = function(value) {
|
|
469
1033
|
|
470
1034
|
|
471
1035
|
/**
|
472
|
-
*
|
473
|
-
*
|
1036
|
+
* Matches any/only HTML5 void elements' start tags.
|
1037
|
+
* See http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
|
1038
|
+
* @type {RegExp}
|
1039
|
+
* @private
|
1040
|
+
*/
|
1041
|
+
soy.$$HTML5_VOID_ELEMENTS_ = new RegExp(
|
1042
|
+
'^<(?:area|base|br|col|command|embed|hr|img|input' +
|
1043
|
+
'|keygen|link|meta|param|source|track|wbr)\\b');
|
1044
|
+
|
1045
|
+
|
1046
|
+
/**
|
1047
|
+
* Removes HTML tags from a string of known safe HTML.
|
1048
|
+
* If opt_tagWhitelist is not specified or is empty, then
|
1049
|
+
* the result can be used as an attribute value.
|
474
1050
|
*
|
475
|
-
* @param {*} value The HTML to be escaped.
|
1051
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
476
1052
|
* value will be coerced to a string.
|
477
|
-
* @
|
478
|
-
*
|
1053
|
+
* @param {Object.<string, number>=} opt_tagWhitelist Has an own property whose
|
1054
|
+
* name is a lower-case tag name and whose value is {@code 1} for
|
1055
|
+
* each element that is allowed in the output.
|
1056
|
+
* @return {string} A representation of value without disallowed tags,
|
1057
|
+
* HTML comments, or other non-text content.
|
1058
|
+
*/
|
1059
|
+
soy.$$stripHtmlTags = function(value, opt_tagWhitelist) {
|
1060
|
+
if (!opt_tagWhitelist) {
|
1061
|
+
// If we have no white-list, then use a fast track which elides all tags.
|
1062
|
+
return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '')
|
1063
|
+
// This is just paranoia since callers should normalize the result
|
1064
|
+
// anyway, but if they didn't, it would be necessary to ensure that
|
1065
|
+
// after the first replace non-tag uses of < do not recombine into
|
1066
|
+
// tags as in "<<foo>script>alert(1337)</<foo>script>".
|
1067
|
+
.replace(soy.esc.$$LT_REGEX_, '<');
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
// Escapes '[' so that we can use [123] below to mark places where tags
|
1071
|
+
// have been removed.
|
1072
|
+
var html = String(value).replace(/\[/g, '[');
|
1073
|
+
|
1074
|
+
// Consider all uses of '<' and replace whitelisted tags with markers like
|
1075
|
+
// [1] which are indices into a list of approved tag names.
|
1076
|
+
// Replace all other uses of < and > with entities.
|
1077
|
+
var tags = [];
|
1078
|
+
html = html.replace(
|
1079
|
+
soy.esc.$$HTML_TAG_REGEX_,
|
1080
|
+
function(tok, tagName) {
|
1081
|
+
if (tagName) {
|
1082
|
+
tagName = tagName.toLowerCase();
|
1083
|
+
if (opt_tagWhitelist.hasOwnProperty(tagName) &&
|
1084
|
+
opt_tagWhitelist[tagName]) {
|
1085
|
+
var start = tok.charAt(1) === '/' ? '</' : '<';
|
1086
|
+
var index = tags.length;
|
1087
|
+
tags[index] = start + tagName + '>';
|
1088
|
+
return '[' + index + ']';
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
return '';
|
1092
|
+
});
|
1093
|
+
|
1094
|
+
// Escape HTML special characters. Now there are no '<' in html that could
|
1095
|
+
// start a tag.
|
1096
|
+
html = soy.esc.$$normalizeHtmlHelper(html);
|
1097
|
+
|
1098
|
+
var finalCloseTags = soy.$$balanceTags_(tags);
|
1099
|
+
|
1100
|
+
// Now html contains no tags or less-than characters that could become
|
1101
|
+
// part of a tag via a replacement operation and tags only contains
|
1102
|
+
// approved tags.
|
1103
|
+
// Reinsert the white-listed tags.
|
1104
|
+
html = html.replace(
|
1105
|
+
/\[(\d+)\]/g, function(_, index) { return tags[index]; });
|
1106
|
+
|
1107
|
+
// Close any still open tags.
|
1108
|
+
// This prevents unclosed formatting elements like <ol> and <table> from
|
1109
|
+
// breaking the layout of containing HTML.
|
1110
|
+
return html + finalCloseTags;
|
1111
|
+
};
|
1112
|
+
|
1113
|
+
|
1114
|
+
/**
|
1115
|
+
* Throw out any close tags that don't correspond to start tags.
|
1116
|
+
* If {@code <table>} is used for formatting, embedded HTML shouldn't be able
|
1117
|
+
* to use a mismatched {@code </table>} to break page layout.
|
1118
|
+
*
|
1119
|
+
* @param {Array.<string>} tags an array of tags that will be modified in place
|
1120
|
+
* include tags, the empty string, or concatenations of empty tags.
|
1121
|
+
* @return {string} zero or more closed tags that close all elements that are
|
1122
|
+
* opened in tags but not closed.
|
1123
|
+
* @private
|
479
1124
|
*/
|
480
|
-
soy.$$
|
481
|
-
|
1125
|
+
soy.$$balanceTags_ = function(tags) {
|
1126
|
+
var open = [];
|
1127
|
+
for (var i = 0, n = tags.length; i < n; ++i) {
|
1128
|
+
var tag = tags[i];
|
1129
|
+
if (tag.charAt(1) === '/') {
|
1130
|
+
var openTagIndex = open.length - 1;
|
1131
|
+
// NOTE: This is essentially lastIndexOf, but it's not supported in IE.
|
1132
|
+
while (openTagIndex >= 0 && open[openTagIndex] != tag) {
|
1133
|
+
openTagIndex--;
|
1134
|
+
}
|
1135
|
+
if (openTagIndex < 0) {
|
1136
|
+
tags[i] = ''; // Drop close tag.
|
1137
|
+
} else {
|
1138
|
+
tags[i] = open.slice(openTagIndex).reverse().join('');
|
1139
|
+
open.length = openTagIndex;
|
1140
|
+
}
|
1141
|
+
} else if (!soy.$$HTML5_VOID_ELEMENTS_.test(tag)) {
|
1142
|
+
open.push('</' + tag.substring(1));
|
1143
|
+
}
|
1144
|
+
}
|
1145
|
+
return open.reverse().join('');
|
482
1146
|
};
|
483
1147
|
|
484
1148
|
|
485
1149
|
/**
|
486
1150
|
* Escapes HTML special characters in an HTML attribute value.
|
487
1151
|
*
|
488
|
-
* @param {*} value The HTML to be escaped.
|
1152
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
489
1153
|
* value will be coerced to a string.
|
490
1154
|
* @return {string} An escaped version of value.
|
491
1155
|
*/
|
492
1156
|
soy.$$escapeHtmlAttribute = function(value) {
|
493
|
-
|
494
|
-
|
1157
|
+
// NOTE: We don't accept ATTRIBUTES here because ATTRIBUTES is actually not
|
1158
|
+
// the attribute value context, but instead k/v pairs.
|
1159
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
1160
|
+
// NOTE: After removing tags, we also escape quotes ("normalize") so that
|
1161
|
+
// the HTML can be embedded in attribute context.
|
1162
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
495
1163
|
return soy.esc.$$normalizeHtmlHelper(soy.$$stripHtmlTags(value.content));
|
496
1164
|
}
|
497
1165
|
return soy.esc.$$escapeHtmlHelper(value);
|
@@ -502,13 +1170,13 @@ soy.$$escapeHtmlAttribute = function(value) {
|
|
502
1170
|
* Escapes HTML special characters in a string including space and other
|
503
1171
|
* characters that can end an unquoted HTML attribute value.
|
504
1172
|
*
|
505
|
-
* @param {*} value The HTML to be escaped.
|
1173
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
506
1174
|
* value will be coerced to a string.
|
507
1175
|
* @return {string} An escaped version of value.
|
508
1176
|
*/
|
509
1177
|
soy.$$escapeHtmlAttributeNospace = function(value) {
|
510
|
-
if (
|
511
|
-
|
1178
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
1179
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
512
1180
|
return soy.esc.$$normalizeHtmlNospaceHelper(
|
513
1181
|
soy.$$stripHtmlTags(value.content));
|
514
1182
|
}
|
@@ -519,29 +1187,46 @@ soy.$$escapeHtmlAttributeNospace = function(value) {
|
|
519
1187
|
/**
|
520
1188
|
* Filters out strings that cannot be a substring of a valid HTML attribute.
|
521
1189
|
*
|
522
|
-
*
|
1190
|
+
* Note the input is expected to be key=value pairs.
|
1191
|
+
*
|
1192
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
523
1193
|
* will be coerced to a string.
|
524
1194
|
* @return {string} A valid HTML attribute name part or name/value pair.
|
525
1195
|
* {@code "zSoyz"} if the input is invalid.
|
526
1196
|
*/
|
527
|
-
soy.$$
|
528
|
-
|
529
|
-
|
530
|
-
|
1197
|
+
soy.$$filterHtmlAttributes = function(value) {
|
1198
|
+
// NOTE: Explicitly no support for SanitizedContentKind.HTML, since that is
|
1199
|
+
// meaningless in this context, which is generally *between* html attributes.
|
1200
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.ATTRIBUTES)) {
|
1201
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtmlAttribute);
|
1202
|
+
// Add a space at the end to ensure this won't get merged into following
|
1203
|
+
// attributes, unless the interpretation is unambiguous (ending with quotes
|
1204
|
+
// or a space).
|
1205
|
+
return value.content.replace(/([^"'\s])$/, '$1 ');
|
531
1206
|
}
|
532
|
-
|
1207
|
+
// TODO: Dynamically inserting attributes that aren't marked as trusted is
|
1208
|
+
// probably unnecessary. Any filtering done here will either be inadequate
|
1209
|
+
// for security or not flexible enough. Having clients use kind="attributes"
|
1210
|
+
// in parameters seems like a wiser idea.
|
1211
|
+
return soy.esc.$$filterHtmlAttributesHelper(value);
|
533
1212
|
};
|
534
1213
|
|
535
1214
|
|
536
1215
|
/**
|
537
1216
|
* Filters out strings that cannot be a substring of a valid HTML element name.
|
538
1217
|
*
|
539
|
-
* @param {*} value The value to escape.
|
1218
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
540
1219
|
* will be coerced to a string.
|
541
1220
|
* @return {string} A valid HTML element name part.
|
542
1221
|
* {@code "zSoyz"} if the input is invalid.
|
543
1222
|
*/
|
544
1223
|
soy.$$filterHtmlElementName = function(value) {
|
1224
|
+
// NOTE: We don't accept any SanitizedContent here. HTML indicates valid
|
1225
|
+
// PCDATA, not tag names. A sloppy developer shouldn't be able to cause an
|
1226
|
+
// exploit:
|
1227
|
+
// ... {let userInput}script src=http://evil.com/evil.js{/let} ...
|
1228
|
+
// ... {param tagName kind="html"}{$userInput}{/param} ...
|
1229
|
+
// ... <{$tagName}>Hello World</{$tagName}>
|
545
1230
|
return soy.esc.$$filterHtmlElementNameHelper(value);
|
546
1231
|
};
|
547
1232
|
|
@@ -550,7 +1235,7 @@ soy.$$filterHtmlElementName = function(value) {
|
|
550
1235
|
* Escapes characters in the value to make it valid content for a JS string
|
551
1236
|
* literal.
|
552
1237
|
*
|
553
|
-
* @param {*} value The value to escape.
|
1238
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
554
1239
|
* will be coerced to a string.
|
555
1240
|
* @return {string} An escaped version of value.
|
556
1241
|
* @deprecated
|
@@ -564,13 +1249,15 @@ soy.$$escapeJs = function(value) {
|
|
564
1249
|
* Escapes characters in the value to make it valid content for a JS string
|
565
1250
|
* literal.
|
566
1251
|
*
|
567
|
-
* @param {*} value The value to escape.
|
1252
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
568
1253
|
* will be coerced to a string.
|
569
1254
|
* @return {string} An escaped version of value.
|
570
1255
|
*/
|
571
1256
|
soy.$$escapeJsString = function(value) {
|
572
|
-
if (
|
573
|
-
|
1257
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS_STR_CHARS)) {
|
1258
|
+
// TODO: It might still be worthwhile to normalize it to remove
|
1259
|
+
// unescaped quotes, null, etc: replace(/(?:^|[^\])['"]/g, '\\$
|
1260
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedJsStrChars);
|
574
1261
|
return value.content;
|
575
1262
|
}
|
576
1263
|
return soy.esc.$$escapeJsStringHelper(value);
|
@@ -580,7 +1267,7 @@ soy.$$escapeJsString = function(value) {
|
|
580
1267
|
/**
|
581
1268
|
* Encodes a value as a JavaScript literal.
|
582
1269
|
*
|
583
|
-
* @param {*} value The value to escape.
|
1270
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
584
1271
|
* will be coerced to a string.
|
585
1272
|
* @return {string} A JavaScript code representation of the input.
|
586
1273
|
*/
|
@@ -595,6 +1282,10 @@ soy.$$escapeJsValue = function(value) {
|
|
595
1282
|
// distinct undefined value.
|
596
1283
|
return ' null ';
|
597
1284
|
}
|
1285
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS)) {
|
1286
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedJs);
|
1287
|
+
return value.content;
|
1288
|
+
}
|
598
1289
|
switch (typeof value) {
|
599
1290
|
case 'boolean': case 'number':
|
600
1291
|
return ' ' + value + ' ';
|
@@ -608,7 +1299,7 @@ soy.$$escapeJsValue = function(value) {
|
|
608
1299
|
* Escapes characters in the string to make it valid content for a JS regular
|
609
1300
|
* expression literal.
|
610
1301
|
*
|
611
|
-
* @param {*} value The value to escape.
|
1302
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
612
1303
|
* will be coerced to a string.
|
613
1304
|
* @return {string} An escaped version of value.
|
614
1305
|
*/
|
@@ -642,13 +1333,13 @@ soy.$$pctEncode_ = function(ch) {
|
|
642
1333
|
/**
|
643
1334
|
* Escapes a string so that it can be safely included in a URI.
|
644
1335
|
*
|
645
|
-
* @param {*} value The value to escape.
|
1336
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
646
1337
|
* will be coerced to a string.
|
647
1338
|
* @return {string} An escaped version of value.
|
648
1339
|
*/
|
649
1340
|
soy.$$escapeUri = function(value) {
|
650
|
-
if (
|
651
|
-
|
1341
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) {
|
1342
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedUri);
|
652
1343
|
return soy.$$normalizeUri(value);
|
653
1344
|
}
|
654
1345
|
// Apostophes and parentheses are not matched by encodeURIComponent.
|
@@ -667,7 +1358,7 @@ soy.$$escapeUri = function(value) {
|
|
667
1358
|
/**
|
668
1359
|
* Removes rough edges from a URI by escaping any raw HTML/JS string delimiters.
|
669
1360
|
*
|
670
|
-
* @param {*} value The value to escape.
|
1361
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
671
1362
|
* will be coerced to a string.
|
672
1363
|
* @return {string} An escaped version of value.
|
673
1364
|
*/
|
@@ -680,19 +1371,37 @@ soy.$$normalizeUri = function(value) {
|
|
680
1371
|
* Vets a URI's protocol and removes rough edges from a URI by escaping
|
681
1372
|
* any raw HTML/JS string delimiters.
|
682
1373
|
*
|
683
|
-
* @param {*} value The value to escape.
|
1374
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
684
1375
|
* will be coerced to a string.
|
685
1376
|
* @return {string} An escaped version of value.
|
686
1377
|
*/
|
687
1378
|
soy.$$filterNormalizeUri = function(value) {
|
1379
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) {
|
1380
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedUri);
|
1381
|
+
return soy.$$normalizeUri(value);
|
1382
|
+
}
|
688
1383
|
return soy.esc.$$filterNormalizeUriHelper(value);
|
689
1384
|
};
|
690
1385
|
|
691
1386
|
|
1387
|
+
/**
|
1388
|
+
* Allows only data-protocol image URI's.
|
1389
|
+
*
|
1390
|
+
* @param {*} value The value to process. May not be a string, but the value
|
1391
|
+
* will be coerced to a string.
|
1392
|
+
* @return {!soydata.SanitizedUri} An escaped version of value.
|
1393
|
+
*/
|
1394
|
+
soy.$$filterImageDataUri = function(value) {
|
1395
|
+
// NOTE: Even if it's a SanitizedUri, we will still filter it.
|
1396
|
+
return soydata.VERY_UNSAFE.ordainSanitizedUri(
|
1397
|
+
soy.esc.$$filterImageDataUriHelper(value));
|
1398
|
+
};
|
1399
|
+
|
1400
|
+
|
692
1401
|
/**
|
693
1402
|
* Escapes a string so it can safely be included inside a quoted CSS string.
|
694
1403
|
*
|
695
|
-
* @param {*} value The value to escape.
|
1404
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
696
1405
|
* will be coerced to a string.
|
697
1406
|
* @return {string} An escaped version of value.
|
698
1407
|
*/
|
@@ -704,11 +1413,15 @@ soy.$$escapeCssString = function(value) {
|
|
704
1413
|
/**
|
705
1414
|
* Encodes a value as a CSS identifier part, keyword, or quantity.
|
706
1415
|
*
|
707
|
-
* @param {*} value The value to escape.
|
1416
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
708
1417
|
* will be coerced to a string.
|
709
1418
|
* @return {string} A safe CSS identifier part, keyword, or quanitity.
|
710
1419
|
*/
|
711
1420
|
soy.$$filterCssValue = function(value) {
|
1421
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.CSS)) {
|
1422
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedCss);
|
1423
|
+
return value.content;
|
1424
|
+
}
|
712
1425
|
// Uses == to intentionally match null and undefined for Java compatibility.
|
713
1426
|
if (value == null) {
|
714
1427
|
return '';
|
@@ -717,17 +1430,47 @@ soy.$$filterCssValue = function(value) {
|
|
717
1430
|
};
|
718
1431
|
|
719
1432
|
|
1433
|
+
/**
|
1434
|
+
* Sanity-checks noAutoescape input for explicitly tainted content.
|
1435
|
+
*
|
1436
|
+
* SanitizedContentKind.TEXT is used to explicitly mark input that was never
|
1437
|
+
* meant to be used unescaped.
|
1438
|
+
*
|
1439
|
+
* @param {*} value The value to filter.
|
1440
|
+
* @return {*} The value, that we dearly hope will not cause an attack.
|
1441
|
+
*/
|
1442
|
+
soy.$$filterNoAutoescape = function(value) {
|
1443
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.TEXT)) {
|
1444
|
+
// Fail in development mode.
|
1445
|
+
goog.asserts.fail(
|
1446
|
+
'Tainted SanitizedContentKind.TEXT for |noAutoescape: `%s`',
|
1447
|
+
[value.content]);
|
1448
|
+
// Return innocuous data in production.
|
1449
|
+
return 'zSoyz';
|
1450
|
+
}
|
1451
|
+
|
1452
|
+
return value;
|
1453
|
+
};
|
1454
|
+
|
1455
|
+
|
720
1456
|
// -----------------------------------------------------------------------------
|
721
1457
|
// Basic directives/functions.
|
722
1458
|
|
723
1459
|
|
724
1460
|
/**
|
725
1461
|
* Converts \r\n, \r, and \n to <br>s
|
726
|
-
* @param {*}
|
727
|
-
* @return {string} A copy of {@code
|
728
|
-
|
729
|
-
|
730
|
-
|
1462
|
+
* @param {*} value The string in which to convert newlines.
|
1463
|
+
* @return {string|!soydata.SanitizedHtml} A copy of {@code value} with
|
1464
|
+
* converted newlines. If {@code value} is SanitizedHtml, the return value
|
1465
|
+
* is also SanitizedHtml, of the same known directionality.
|
1466
|
+
*/
|
1467
|
+
soy.$$changeNewlineToBr = function(value) {
|
1468
|
+
var result = goog.string.newLineToBr(String(value), false);
|
1469
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
1470
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
1471
|
+
result, soydata.getContentDir(value));
|
1472
|
+
}
|
1473
|
+
return result;
|
731
1474
|
};
|
732
1475
|
|
733
1476
|
|
@@ -737,14 +1480,22 @@ soy.$$changeNewlineToBr = function(str) {
|
|
737
1480
|
* HTML tags or entities. Entites count towards the character count; HTML tags
|
738
1481
|
* do not.
|
739
1482
|
*
|
740
|
-
* @param {*}
|
1483
|
+
* @param {*} value The HTML string to insert word breaks into. Can be other
|
741
1484
|
* types, but the value will be coerced to a string.
|
742
1485
|
* @param {number} maxCharsBetweenWordBreaks Maximum number of non-space
|
743
1486
|
* characters to allow before adding a word break.
|
744
|
-
* @return {string} The string including word
|
745
|
-
|
746
|
-
|
747
|
-
|
1487
|
+
* @return {string|!soydata.SanitizedHtml} The string including word
|
1488
|
+
* breaks. If {@code value} is SanitizedHtml, the return value
|
1489
|
+
* is also SanitizedHtml, of the same known directionality.
|
1490
|
+
*/
|
1491
|
+
soy.$$insertWordBreaks = function(value, maxCharsBetweenWordBreaks) {
|
1492
|
+
var result = goog.format.insertWordBreaks(
|
1493
|
+
String(value), maxCharsBetweenWordBreaks);
|
1494
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
1495
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
1496
|
+
result, soydata.getContentDir(value));
|
1497
|
+
}
|
1498
|
+
return result;
|
748
1499
|
};
|
749
1500
|
|
750
1501
|
|
@@ -832,7 +1583,7 @@ soy.$$bidiFormatterCache_ = {};
|
|
832
1583
|
* Returns cached bidi formatter for bidiGlobalDir, or creates a new one.
|
833
1584
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
834
1585
|
* if rtl, 0 if unknown.
|
835
|
-
* @return {goog.i18n.BidiFormatter} A formatter for bidiGlobalDir.
|
1586
|
+
* @return {!goog.i18n.BidiFormatter} A formatter for bidiGlobalDir.
|
836
1587
|
* @private
|
837
1588
|
*/
|
838
1589
|
soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) {
|
@@ -846,37 +1597,53 @@ soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) {
|
|
846
1597
|
* Estimate the overall directionality of text. If opt_isHtml, makes sure to
|
847
1598
|
* ignore the LTR nature of the mark-up and escapes in text, making the logic
|
848
1599
|
* suitable for HTML and HTML-escaped text.
|
849
|
-
*
|
1600
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
1601
|
+
* estimating the directionality.
|
1602
|
+
*
|
1603
|
+
* @param {*} text The content whose directionality is to be estimated.
|
850
1604
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
851
1605
|
* Default: false.
|
852
1606
|
* @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral.
|
853
1607
|
*/
|
854
1608
|
soy.$$bidiTextDir = function(text, opt_isHtml) {
|
855
|
-
|
856
|
-
|
1609
|
+
var contentDir = soydata.getContentDir(text);
|
1610
|
+
if (contentDir != null) {
|
1611
|
+
return contentDir;
|
857
1612
|
}
|
858
|
-
|
1613
|
+
var isHtml = opt_isHtml ||
|
1614
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
1615
|
+
return goog.i18n.bidi.estimateDirection(text + '', isHtml);
|
859
1616
|
};
|
860
1617
|
|
861
1618
|
|
862
1619
|
/**
|
863
|
-
* Returns
|
1620
|
+
* Returns 'dir="ltr"' or 'dir="rtl"', depending on text's estimated
|
864
1621
|
* directionality, if it is not the same as bidiGlobalDir.
|
865
1622
|
* Otherwise, returns the empty string.
|
866
1623
|
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
867
1624
|
* in text, making the logic suitable for HTML and HTML-escaped text.
|
1625
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
1626
|
+
* estimating the directionality.
|
1627
|
+
*
|
868
1628
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
869
1629
|
* if rtl, 0 if unknown.
|
870
|
-
* @param {
|
1630
|
+
* @param {*} text The content whose directionality is to be estimated.
|
871
1631
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
872
1632
|
* Default: false.
|
873
|
-
* @return {soydata.SanitizedHtmlAttribute}
|
874
|
-
* context;
|
1633
|
+
* @return {!soydata.SanitizedHtmlAttribute} 'dir="rtl"' for RTL text in non-RTL
|
1634
|
+
* context; 'dir="ltr"' for LTR text in non-LTR context;
|
875
1635
|
* else, the empty string.
|
876
1636
|
*/
|
877
1637
|
soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
878
|
-
|
879
|
-
|
1638
|
+
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
1639
|
+
var contentDir = soydata.getContentDir(text);
|
1640
|
+
if (contentDir == null) {
|
1641
|
+
var isHtml = opt_isHtml ||
|
1642
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
1643
|
+
contentDir = goog.i18n.bidi.estimateDirection(text + '', isHtml);
|
1644
|
+
}
|
1645
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute(
|
1646
|
+
formatter.knownDirAttr(contentDir));
|
880
1647
|
};
|
881
1648
|
|
882
1649
|
|
@@ -886,9 +1653,12 @@ soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
|
886
1653
|
* bidiGlobalDir. Otherwise returns the empty string.
|
887
1654
|
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
888
1655
|
* in text, making the logic suitable for HTML and HTML-escaped text.
|
1656
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
1657
|
+
* estimating the directionality.
|
1658
|
+
*
|
889
1659
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
890
1660
|
* if rtl, 0 if unknown.
|
891
|
-
* @param {
|
1661
|
+
* @param {*} text The content whose directionality is to be estimated.
|
892
1662
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
893
1663
|
* Default: false.
|
894
1664
|
* @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
|
@@ -897,44 +1667,112 @@ soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
|
897
1667
|
*/
|
898
1668
|
soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
|
899
1669
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
900
|
-
|
1670
|
+
var isHtml = opt_isHtml ||
|
1671
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
1672
|
+
return formatter.markAfterKnownDir(soydata.getContentDir(text), text + '',
|
1673
|
+
isHtml);
|
901
1674
|
};
|
902
1675
|
|
903
1676
|
|
904
1677
|
/**
|
905
|
-
* Returns
|
906
|
-
* but only if that is neither neutral nor the same as the
|
907
|
-
* Otherwise, returns
|
908
|
-
* Always treats
|
909
|
-
* estimating
|
1678
|
+
* Returns text wrapped in a <span dir="ltr|rtl"> according to its
|
1679
|
+
* directionality - but only if that is neither neutral nor the same as the
|
1680
|
+
* global context. Otherwise, returns text unchanged.
|
1681
|
+
* Always treats text as HTML/HTML-escaped, i.e. ignores mark-up and escapes
|
1682
|
+
* when estimating text's directionality.
|
1683
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
1684
|
+
* estimating the directionality.
|
1685
|
+
*
|
910
1686
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
911
1687
|
* if rtl, 0 if unknown.
|
912
|
-
* @param {*}
|
1688
|
+
* @param {*} text The string to be wrapped. Can be other types, but the value
|
913
1689
|
* will be coerced to a string.
|
914
|
-
* @return {string} The wrapped
|
1690
|
+
* @return {!goog.soy.data.SanitizedContent|string} The wrapped text.
|
915
1691
|
*/
|
916
|
-
soy.$$bidiSpanWrap = function(bidiGlobalDir,
|
1692
|
+
soy.$$bidiSpanWrap = function(bidiGlobalDir, text) {
|
917
1693
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
918
|
-
|
1694
|
+
|
1695
|
+
// We always treat the value as HTML, because span-wrapping is only useful
|
1696
|
+
// when its output will be treated as HTML (without escaping), and because
|
1697
|
+
// |bidiSpanWrap is not itself specified to do HTML escaping in Soy. (Both
|
1698
|
+
// explicit and automatic HTML escaping, if any, is done before calling
|
1699
|
+
// |bidiSpanWrap because the BidiSpanWrapDirective Java class implements
|
1700
|
+
// SanitizedContentOperator, but this does not mean that the input has to be
|
1701
|
+
// HTML SanitizedContent. In legacy usage, a string that is not
|
1702
|
+
// SanitizedContent is often printed in an autoescape="false" template or by
|
1703
|
+
// a print with a |noAutoescape, in which case our input is just SoyData.) If
|
1704
|
+
// the output will be treated as HTML, the input had better be safe
|
1705
|
+
// HTML/HTML-escaped (even if it isn't HTML SanitizedData), or we have an XSS
|
1706
|
+
// opportunity and a much bigger problem than bidi garbling.
|
1707
|
+
var wrappedText = formatter.spanWrapWithKnownDir(
|
1708
|
+
soydata.getContentDir(text), text + '', true /* opt_isHtml */);
|
1709
|
+
|
1710
|
+
// Like other directives whose Java class implements SanitizedContentOperator,
|
1711
|
+
// |bidiSpanWrap is called after the escaping (if any) has already been done,
|
1712
|
+
// and thus there is no need for it to produce actual SanitizedContent.
|
1713
|
+
return wrappedText;
|
919
1714
|
};
|
920
1715
|
|
921
1716
|
|
922
1717
|
/**
|
923
|
-
* Returns
|
1718
|
+
* Returns text wrapped in Unicode BiDi formatting characters according to its
|
924
1719
|
* directionality, i.e. either LRE or RLE at the beginning and PDF at the end -
|
925
|
-
* but only if
|
926
|
-
* global context. Otherwise, returns
|
927
|
-
*
|
928
|
-
* estimating
|
1720
|
+
* but only if text's directionality is neither neutral nor the same as the
|
1721
|
+
* global context. Otherwise, returns text unchanged.
|
1722
|
+
* Only treats soydata.SanitizedHtml as HTML/HTML-escaped, i.e. ignores mark-up
|
1723
|
+
* and escapes when estimating text's directionality.
|
1724
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
1725
|
+
* estimating the directionality.
|
1726
|
+
*
|
929
1727
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
930
1728
|
* if rtl, 0 if unknown.
|
931
|
-
* @param {*}
|
1729
|
+
* @param {*} text The string to be wrapped. Can be other types, but the value
|
932
1730
|
* will be coerced to a string.
|
933
|
-
* @return {string} The wrapped string.
|
1731
|
+
* @return {!goog.soy.data.SanitizedContent|string} The wrapped string.
|
934
1732
|
*/
|
935
|
-
soy.$$bidiUnicodeWrap = function(bidiGlobalDir,
|
1733
|
+
soy.$$bidiUnicodeWrap = function(bidiGlobalDir, text) {
|
936
1734
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
937
|
-
|
1735
|
+
|
1736
|
+
// We treat the value as HTML if and only if it says it's HTML, even though in
|
1737
|
+
// legacy usage, we sometimes have an HTML string (not SanitizedContent) that
|
1738
|
+
// is passed to an autoescape="false" template or a {print $foo|noAutoescape},
|
1739
|
+
// with the output going into an HTML context without escaping. We simply have
|
1740
|
+
// no way of knowing if this is what is happening when we get
|
1741
|
+
// non-SanitizedContent input, and most of the time it isn't.
|
1742
|
+
var isHtml = soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
1743
|
+
var wrappedText = formatter.unicodeWrapWithKnownDir(
|
1744
|
+
soydata.getContentDir(text), text + '', isHtml);
|
1745
|
+
|
1746
|
+
// Bidi-wrapping a value converts it to the context directionality. Since it
|
1747
|
+
// does not cost us anything, we will indicate this known direction in the
|
1748
|
+
// output SanitizedContent, even though the intended consumer of that
|
1749
|
+
// information - a bidi wrapping directive - has already been run.
|
1750
|
+
var wrappedTextDir = formatter.getContextDir();
|
1751
|
+
|
1752
|
+
// Unicode-wrapping UnsanitizedText gives UnsanitizedText.
|
1753
|
+
// Unicode-wrapping safe HTML or JS string data gives valid, safe HTML or JS
|
1754
|
+
// string data.
|
1755
|
+
// ATTENTION: Do these need to be ...ForInternalBlocks()?
|
1756
|
+
if (soydata.isContentKind(text, soydata.SanitizedContentKind.TEXT)) {
|
1757
|
+
return new soydata.UnsanitizedText(wrappedText, wrappedTextDir);
|
1758
|
+
}
|
1759
|
+
if (isHtml) {
|
1760
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(wrappedText, wrappedTextDir);
|
1761
|
+
}
|
1762
|
+
if (soydata.isContentKind(text, soydata.SanitizedContentKind.JS_STR_CHARS)) {
|
1763
|
+
return soydata.VERY_UNSAFE.ordainSanitizedJsStrChars(
|
1764
|
+
wrappedText, wrappedTextDir);
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
// Unicode-wrapping does not conform to the syntax of the other types of
|
1768
|
+
// content. For lack of anything better to do, we we do not declare a content
|
1769
|
+
// kind at all by falling through to the non-SanitizedContent case below.
|
1770
|
+
// TODO(user): Consider throwing a runtime error on receipt of
|
1771
|
+
// SanitizedContent other than TEXT, HTML, or JS_STR_CHARS.
|
1772
|
+
|
1773
|
+
// The input was not SanitizedContent, so our output isn't SanitizedContent
|
1774
|
+
// either.
|
1775
|
+
return wrappedText;
|
938
1776
|
};
|
939
1777
|
|
940
1778
|
|
@@ -944,6 +1782,10 @@ soy.$$bidiUnicodeWrap = function(bidiGlobalDir, str) {
|
|
944
1782
|
|
945
1783
|
|
946
1784
|
|
1785
|
+
|
1786
|
+
|
1787
|
+
|
1788
|
+
|
947
1789
|
// START GENERATED CODE FOR ESCAPERS.
|
948
1790
|
|
949
1791
|
/**
|
@@ -954,7 +1796,7 @@ soy.esc.$$escapeUriHelper = function(v) {
|
|
954
1796
|
};
|
955
1797
|
|
956
1798
|
/**
|
957
|
-
* Maps
|
1799
|
+
* Maps characters to the escaped versions for the named escape directives.
|
958
1800
|
* @type {Object.<string, string>}
|
959
1801
|
* @private
|
960
1802
|
*/
|
@@ -982,7 +1824,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSP
|
|
982
1824
|
};
|
983
1825
|
|
984
1826
|
/**
|
985
|
-
* A function that can be used with String.replace
|
1827
|
+
* A function that can be used with String.replace.
|
986
1828
|
* @param {string} ch A single character matched by a compatible matcher.
|
987
1829
|
* @return {string} A token in the output language.
|
988
1830
|
* @private
|
@@ -992,7 +1834,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPAC
|
|
992
1834
|
};
|
993
1835
|
|
994
1836
|
/**
|
995
|
-
* Maps
|
1837
|
+
* Maps characters to the escaped versions for the named escape directives.
|
996
1838
|
* @type {Object.<string, string>}
|
997
1839
|
* @private
|
998
1840
|
*/
|
@@ -1034,7 +1876,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = {
|
|
1034
1876
|
};
|
1035
1877
|
|
1036
1878
|
/**
|
1037
|
-
* A function that can be used with String.replace
|
1879
|
+
* A function that can be used with String.replace.
|
1038
1880
|
* @param {string} ch A single character matched by a compatible matcher.
|
1039
1881
|
* @return {string} A token in the output language.
|
1040
1882
|
* @private
|
@@ -1044,7 +1886,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) {
|
|
1044
1886
|
};
|
1045
1887
|
|
1046
1888
|
/**
|
1047
|
-
* Maps
|
1889
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1048
1890
|
* @type {Object.<string, string>}
|
1049
1891
|
* @private
|
1050
1892
|
*/
|
@@ -1079,7 +1921,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = {
|
|
1079
1921
|
};
|
1080
1922
|
|
1081
1923
|
/**
|
1082
|
-
* A function that can be used with String.replace
|
1924
|
+
* A function that can be used with String.replace.
|
1083
1925
|
* @param {string} ch A single character matched by a compatible matcher.
|
1084
1926
|
* @return {string} A token in the output language.
|
1085
1927
|
* @private
|
@@ -1089,7 +1931,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) {
|
|
1089
1931
|
};
|
1090
1932
|
|
1091
1933
|
/**
|
1092
|
-
* Maps
|
1934
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1093
1935
|
* @type {Object.<string, string>}
|
1094
1936
|
* @private
|
1095
1937
|
*/
|
@@ -1162,7 +2004,7 @@ soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = {
|
|
1162
2004
|
};
|
1163
2005
|
|
1164
2006
|
/**
|
1165
|
-
* A function that can be used with String.replace
|
2007
|
+
* A function that can be used with String.replace.
|
1166
2008
|
* @param {string} ch A single character matched by a compatible matcher.
|
1167
2009
|
* @return {string} A token in the output language.
|
1168
2010
|
* @private
|
@@ -1246,7 +2088,14 @@ soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?:(?:https?|mailto):|[^&:\/?#]*(
|
|
1246
2088
|
* @type RegExp
|
1247
2089
|
* @private
|
1248
2090
|
*/
|
1249
|
-
soy.esc.$$
|
2091
|
+
soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_ = /^data:image\/(?:bmp|gif|jpe?g|png|tiff|webp);base64,[a-z0-9+\/]+=*$/i;
|
2092
|
+
|
2093
|
+
/**
|
2094
|
+
* A pattern that vets values produced by the named directives.
|
2095
|
+
* @type RegExp
|
2096
|
+
* @private
|
2097
|
+
*/
|
2098
|
+
soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_ = /^(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*)$/i;
|
1250
2099
|
|
1251
2100
|
/**
|
1252
2101
|
* A pattern that vets values produced by the named directives.
|
@@ -1374,7 +2223,7 @@ soy.esc.$$filterNormalizeUriHelper = function(value) {
|
|
1374
2223
|
var str = String(value);
|
1375
2224
|
if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) {
|
1376
2225
|
goog.asserts.fail('Bad value `%s` for |filterNormalizeUri', [str]);
|
1377
|
-
return 'zSoyz';
|
2226
|
+
return '#zSoyz';
|
1378
2227
|
}
|
1379
2228
|
return str.replace(
|
1380
2229
|
soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
|
@@ -1382,14 +2231,28 @@ soy.esc.$$filterNormalizeUriHelper = function(value) {
|
|
1382
2231
|
};
|
1383
2232
|
|
1384
2233
|
/**
|
1385
|
-
* A helper for the Soy directive |
|
2234
|
+
* A helper for the Soy directive |filterImageDataUri
|
1386
2235
|
* @param {*} value Can be of any type but will be coerced to a string.
|
1387
2236
|
* @return {string} The escaped text.
|
1388
2237
|
*/
|
1389
|
-
soy.esc.$$
|
2238
|
+
soy.esc.$$filterImageDataUriHelper = function(value) {
|
1390
2239
|
var str = String(value);
|
1391
|
-
if (!soy.esc.$$
|
1392
|
-
goog.asserts.fail('Bad value `%s` for |
|
2240
|
+
if (!soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_.test(str)) {
|
2241
|
+
goog.asserts.fail('Bad value `%s` for |filterImageDataUri', [str]);
|
2242
|
+
return 'data:image/gif;base64,zSoyz';
|
2243
|
+
}
|
2244
|
+
return str;
|
2245
|
+
};
|
2246
|
+
|
2247
|
+
/**
|
2248
|
+
* A helper for the Soy directive |filterHtmlAttributes
|
2249
|
+
* @param {*} value Can be of any type but will be coerced to a string.
|
2250
|
+
* @return {string} The escaped text.
|
2251
|
+
*/
|
2252
|
+
soy.esc.$$filterHtmlAttributesHelper = function(value) {
|
2253
|
+
var str = String(value);
|
2254
|
+
if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_.test(str)) {
|
2255
|
+
goog.asserts.fail('Bad value `%s` for |filterHtmlAttributes', [str]);
|
1393
2256
|
return 'zSoyz';
|
1394
2257
|
}
|
1395
2258
|
return str;
|
@@ -1411,10 +2274,29 @@ soy.esc.$$filterHtmlElementNameHelper = function(value) {
|
|
1411
2274
|
|
1412
2275
|
/**
|
1413
2276
|
* Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
|
2277
|
+
* By removing these, and replacing any '<' or '>' characters with
|
2278
|
+
* entities we guarantee that the result can be embedded into a
|
2279
|
+
* an attribute without introducing a tag boundary.
|
2280
|
+
*
|
2281
|
+
* @type {RegExp}
|
2282
|
+
* @private
|
2283
|
+
*/
|
2284
|
+
soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g;
|
2285
|
+
|
2286
|
+
/**
|
2287
|
+
* Matches all occurrences of '<'.
|
1414
2288
|
*
|
1415
2289
|
* @type {RegExp}
|
1416
2290
|
* @private
|
1417
2291
|
*/
|
1418
|
-
soy.esc.$$
|
2292
|
+
soy.esc.$$LT_REGEX_ = /</g;
|
2293
|
+
|
2294
|
+
/**
|
2295
|
+
* Maps lower-case names of innocuous tags to 1.
|
2296
|
+
*
|
2297
|
+
* @type {Object.<string,number>}
|
2298
|
+
* @private
|
2299
|
+
*/
|
2300
|
+
soy.esc.$$SAFE_TAG_WHITELIST_ = {'b': 1, 'br': 1, 'em': 1, 'i': 1, 's': 1, 'sub': 1, 'sup': 1, 'u': 1};
|
1419
2301
|
|
1420
2302
|
// END GENERATED CODE
|