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,27 +30,41 @@
|
|
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
|
|
39
40
|
// COPIED FROM nogoog_shim.js
|
40
41
|
|
41
42
|
// Create closure namespaces.
|
42
|
-
var goog;
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
var goog = goog || {};
|
44
|
+
|
45
|
+
|
46
|
+
goog.DEBUG = false;
|
47
|
+
|
46
48
|
|
47
49
|
goog.inherits = function(childCtor, parentCtor) {
|
48
50
|
/** @constructor */
|
49
|
-
function tempCtor() {}
|
51
|
+
function tempCtor() {};
|
50
52
|
tempCtor.prototype = parentCtor.prototype;
|
51
53
|
childCtor.superClass_ = parentCtor.prototype;
|
52
54
|
childCtor.prototype = new tempCtor();
|
53
55
|
childCtor.prototype.constructor = childCtor;
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Calls superclass constructor/method.
|
59
|
+
* @param {!Object} me Should always be "this".
|
60
|
+
* @param {string} methodName
|
61
|
+
* @param {...*} var_args
|
62
|
+
* @return {?} The return value of the superclass method/constructor.
|
63
|
+
*/
|
64
|
+
childCtor.base = function(me, methodName, var_args) {
|
65
|
+
var args = Array.prototype.slice.call(arguments, 2);
|
66
|
+
return parentCtor.prototype[methodName].apply(me, args);
|
67
|
+
};
|
54
68
|
};
|
55
69
|
|
56
70
|
|
@@ -64,45 +78,76 @@ if (!goog.userAgent) {
|
|
64
78
|
}
|
65
79
|
var isOpera = userAgent.indexOf('Opera') == 0;
|
66
80
|
return {
|
81
|
+
jscript: {
|
82
|
+
/**
|
83
|
+
* @type {boolean}
|
84
|
+
*/
|
85
|
+
HAS_JSCRIPT: 'ScriptEngine' in this
|
86
|
+
},
|
67
87
|
/**
|
68
88
|
* @type {boolean}
|
69
89
|
*/
|
70
|
-
|
71
|
-
/**
|
72
|
-
* @type {boolean}
|
73
|
-
*/
|
74
|
-
IS_OPERA: isOpera,
|
90
|
+
OPERA: isOpera,
|
75
91
|
/**
|
76
92
|
* @type {boolean}
|
77
93
|
*/
|
78
|
-
|
94
|
+
IE: !isOpera && userAgent.indexOf('MSIE') != -1,
|
79
95
|
/**
|
80
96
|
* @type {boolean}
|
81
97
|
*/
|
82
|
-
|
98
|
+
WEBKIT: !isOpera && userAgent.indexOf('WebKit') != -1
|
83
99
|
};
|
84
100
|
})();
|
85
101
|
}
|
86
102
|
|
87
103
|
if (!goog.asserts) {
|
88
104
|
goog.asserts = {
|
89
|
-
|
105
|
+
/**
|
106
|
+
* @param {*} condition Condition to check.
|
107
|
+
*/
|
108
|
+
assert: function (condition) {
|
109
|
+
if (!condition) {
|
110
|
+
throw Error('Assertion error');
|
111
|
+
}
|
112
|
+
},
|
113
|
+
/**
|
114
|
+
* @param {...*} var_args
|
115
|
+
*/
|
116
|
+
fail: function (var_args) {}
|
90
117
|
};
|
91
118
|
}
|
92
119
|
|
93
120
|
|
94
121
|
// Stub out the document wrapper used by renderAs*.
|
95
122
|
if (!goog.dom) {
|
96
|
-
goog.dom = {
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
123
|
+
goog.dom = {};
|
124
|
+
/**
|
125
|
+
* @param {Document=} d
|
126
|
+
* @constructor
|
127
|
+
*/
|
128
|
+
goog.dom.DomHelper = function(d) {
|
129
|
+
this.document_ = d || document;
|
130
|
+
};
|
131
|
+
/**
|
132
|
+
* @return {!Document}
|
133
|
+
*/
|
134
|
+
goog.dom.DomHelper.prototype.getDocument = function() {
|
135
|
+
return this.document_;
|
136
|
+
};
|
137
|
+
/**
|
138
|
+
* Creates a new element.
|
139
|
+
* @param {string} name Tag name.
|
140
|
+
* @return {!Element}
|
141
|
+
*/
|
142
|
+
goog.dom.DomHelper.prototype.createElement = function(name) {
|
143
|
+
return this.document_.createElement(name);
|
144
|
+
};
|
145
|
+
/**
|
146
|
+
* Creates a new document fragment.
|
147
|
+
* @return {!DocumentFragment}
|
148
|
+
*/
|
149
|
+
goog.dom.DomHelper.prototype.createDocumentFragment = function() {
|
150
|
+
return this.document_.createDocumentFragment();
|
106
151
|
};
|
107
152
|
}
|
108
153
|
|
@@ -195,66 +240,177 @@ if (!goog.format) {
|
|
195
240
|
},
|
196
241
|
/**
|
197
242
|
* String inserted as a word break by insertWordBreaks(). Safari requires
|
198
|
-
* <wbr></wbr>, Opera needs the
|
199
|
-
* visible hyphen at breaks.
|
243
|
+
* <wbr></wbr>, Opera needs the ­ entity, though this will give a
|
244
|
+
* visible hyphen at breaks. IE8+ use a zero width space. Other browsers
|
245
|
+
* just use <wbr>.
|
200
246
|
* @type {string}
|
201
247
|
* @private
|
202
248
|
*/
|
203
|
-
WORD_BREAK:
|
204
|
-
|
249
|
+
WORD_BREAK:
|
250
|
+
goog.userAgent.WEBKIT ? '<wbr></wbr>' :
|
251
|
+
goog.userAgent.OPERA ? '­' :
|
252
|
+
goog.userAgent.IE ? '​' :
|
253
|
+
'<wbr>'
|
205
254
|
};
|
206
255
|
}
|
207
256
|
|
208
257
|
|
209
258
|
if (!goog.i18n) {
|
210
259
|
goog.i18n = {
|
211
|
-
|
212
|
-
* Utility class for formatting text for display in a potentially
|
213
|
-
* opposite-directionality context without garbling. Provides the following
|
214
|
-
* functionality:
|
215
|
-
*
|
216
|
-
* @param {goog.i18n.bidi.Dir|number|boolean} contextDir The context
|
217
|
-
* directionality as a number
|
218
|
-
* (positive = LRT, negative = RTL, 0 = unknown).
|
219
|
-
* @constructor
|
220
|
-
*/
|
221
|
-
BidiFormatter: function (dir) {
|
222
|
-
this.dir_ = dir;
|
223
|
-
},
|
224
|
-
bidi: {
|
225
|
-
/**
|
226
|
-
* Check the directionality of a piece of text, return true if the piece
|
227
|
-
* of text should be laid out in RTL direction.
|
228
|
-
* @param {string} text The piece of text that need to be detected.
|
229
|
-
* @param {boolean=} opt_isHtml Whether {@code text} is HTML/HTML-escaped.
|
230
|
-
* Default: false.
|
231
|
-
* @return {boolean}
|
232
|
-
* @private
|
233
|
-
*/
|
234
|
-
detectRtlDirectionality: function(text, opt_isHtml) {
|
235
|
-
text = soyshim.$$bidiStripHtmlIfNecessary_(text, opt_isHtml);
|
236
|
-
return soyshim.$$bidiRtlWordRatio_(text)
|
237
|
-
> soyshim.$$bidiRtlDetectionThreshold_;
|
238
|
-
}
|
239
|
-
}
|
260
|
+
bidi: {}
|
240
261
|
};
|
241
262
|
}
|
242
263
|
|
243
264
|
|
244
265
|
/**
|
245
|
-
*
|
246
|
-
* directionality, if it is not the same as the context directionality.
|
247
|
-
* Otherwise, returns the empty string.
|
266
|
+
* Constant that defines whether or not the current locale is an RTL locale.
|
248
267
|
*
|
249
|
-
* @
|
250
|
-
|
268
|
+
* @type {boolean}
|
269
|
+
*/
|
270
|
+
goog.i18n.bidi.IS_RTL = false;
|
271
|
+
|
272
|
+
|
273
|
+
/**
|
274
|
+
* Directionality enum.
|
275
|
+
* @enum {number}
|
276
|
+
*/
|
277
|
+
goog.i18n.bidi.Dir = {
|
278
|
+
/**
|
279
|
+
* Left-to-right.
|
280
|
+
*/
|
281
|
+
LTR: 1,
|
282
|
+
|
283
|
+
/**
|
284
|
+
* Right-to-left.
|
285
|
+
*/
|
286
|
+
RTL: -1,
|
287
|
+
|
288
|
+
/**
|
289
|
+
* Neither left-to-right nor right-to-left.
|
290
|
+
*/
|
291
|
+
NEUTRAL: 0,
|
292
|
+
|
293
|
+
/**
|
294
|
+
* A historical misnomer for NEUTRAL.
|
295
|
+
* @deprecated For "neutral", use NEUTRAL; for "unknown", use null.
|
296
|
+
*/
|
297
|
+
UNKNOWN: 0
|
298
|
+
};
|
299
|
+
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Convert a directionality given in various formats to a goog.i18n.bidi.Dir
|
303
|
+
* constant. Useful for interaction with different standards of directionality
|
304
|
+
* representation.
|
305
|
+
*
|
306
|
+
* @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
|
307
|
+
* in one of the following formats:
|
308
|
+
* 1. A goog.i18n.bidi.Dir constant.
|
309
|
+
* 2. A number (positive = LTR, negative = RTL, 0 = neutral).
|
310
|
+
* 3. A boolean (true = RTL, false = LTR).
|
311
|
+
* 4. A null for unknown directionality.
|
312
|
+
* @param {boolean=} opt_noNeutral Whether a givenDir of zero or
|
313
|
+
* goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
|
314
|
+
* order to preserve legacy behavior.
|
315
|
+
* @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
|
316
|
+
* given directionality. If given null, returns null (i.e. unknown).
|
317
|
+
*/
|
318
|
+
goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
|
319
|
+
if (typeof givenDir == 'number') {
|
320
|
+
// This includes the non-null goog.i18n.bidi.Dir case.
|
321
|
+
return givenDir > 0 ? goog.i18n.bidi.Dir.LTR :
|
322
|
+
givenDir < 0 ? goog.i18n.bidi.Dir.RTL :
|
323
|
+
opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
|
324
|
+
} else if (givenDir == null) {
|
325
|
+
return null;
|
326
|
+
} else {
|
327
|
+
// Must be typeof givenDir == 'boolean'.
|
328
|
+
return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
|
329
|
+
}
|
330
|
+
};
|
331
|
+
|
332
|
+
|
333
|
+
/**
|
334
|
+
* Estimates the directionality of a string based on relative word counts.
|
335
|
+
* If the number of RTL words is above a certain percentage of the total number
|
336
|
+
* of strongly directional words, returns RTL.
|
337
|
+
* Otherwise, if any words are strongly or weakly LTR, returns LTR.
|
338
|
+
* Otherwise, returns NEUTRAL.
|
339
|
+
* Numbers are counted as weakly LTR.
|
340
|
+
* @param {string} str The string to be checked.
|
341
|
+
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
|
251
342
|
* Default: false.
|
252
|
-
* @return {
|
253
|
-
* text in non-LTR context; else, the empty string.
|
343
|
+
* @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
|
254
344
|
*/
|
255
|
-
goog.i18n.
|
256
|
-
var
|
257
|
-
|
345
|
+
goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
|
346
|
+
var rtlCount = 0;
|
347
|
+
var totalCount = 0;
|
348
|
+
var hasWeaklyLtr = false;
|
349
|
+
var tokens = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml).
|
350
|
+
split(soyshim.$$bidiWordSeparatorRe_);
|
351
|
+
for (var i = 0; i < tokens.length; i++) {
|
352
|
+
var token = tokens[i];
|
353
|
+
if (soyshim.$$bidiRtlDirCheckRe_.test(token)) {
|
354
|
+
rtlCount++;
|
355
|
+
totalCount++;
|
356
|
+
} else if (soyshim.$$bidiIsRequiredLtrRe_.test(token)) {
|
357
|
+
hasWeaklyLtr = true;
|
358
|
+
} else if (soyshim.$$bidiLtrCharRe_.test(token)) {
|
359
|
+
totalCount++;
|
360
|
+
} else if (soyshim.$$bidiHasNumeralsRe_.test(token)) {
|
361
|
+
hasWeaklyLtr = true;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
return totalCount == 0 ?
|
366
|
+
(hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
|
367
|
+
(rtlCount / totalCount > soyshim.$$bidiRtlDetectionThreshold_ ?
|
368
|
+
goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR);
|
369
|
+
};
|
370
|
+
|
371
|
+
|
372
|
+
/**
|
373
|
+
* Utility class for formatting text for display in a potentially
|
374
|
+
* opposite-directionality context without garbling. Provides the following
|
375
|
+
* functionality:
|
376
|
+
*
|
377
|
+
* @param {goog.i18n.bidi.Dir|number|boolean|null} dir The context
|
378
|
+
* directionality, in one of the following formats:
|
379
|
+
* 1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null,
|
380
|
+
* i.e. unknown, for backward compatibility with legacy calls.
|
381
|
+
* 2. A number (positive = LTR, negative = RTL, 0 = unknown).
|
382
|
+
* 3. A boolean (true = RTL, false = LTR).
|
383
|
+
* 4. A null for unknown directionality.
|
384
|
+
* @constructor
|
385
|
+
*/
|
386
|
+
goog.i18n.BidiFormatter = function(dir) {
|
387
|
+
/**
|
388
|
+
* The overall directionality of the context in which the formatter is being
|
389
|
+
* used.
|
390
|
+
* @type {?goog.i18n.bidi.Dir}
|
391
|
+
* @private
|
392
|
+
*/
|
393
|
+
this.dir_ = goog.i18n.bidi.toDir(dir, true /* opt_noNeutral */);
|
394
|
+
};
|
395
|
+
|
396
|
+
/**
|
397
|
+
* @return {?goog.i18n.bidi.Dir} The context directionality.
|
398
|
+
*/
|
399
|
+
goog.i18n.BidiFormatter.prototype.getContextDir = function() {
|
400
|
+
return this.dir_;
|
401
|
+
};
|
402
|
+
|
403
|
+
/**
|
404
|
+
* Returns 'dir="ltr"' or 'dir="rtl"', depending on the given directionality, if
|
405
|
+
* it is not the same as the context directionality. Otherwise, returns the
|
406
|
+
* empty string.
|
407
|
+
*
|
408
|
+
* @param {goog.i18n.bidi.Dir} dir A directionality.
|
409
|
+
* @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
|
410
|
+
* LTR text in non-LTR context; else, the empty string.
|
411
|
+
*/
|
412
|
+
goog.i18n.BidiFormatter.prototype.knownDirAttr = function(dir) {
|
413
|
+
return !dir || dir == this.dir_ ? '' : dir < 0 ? 'dir="rtl"' : 'dir="ltr"';
|
258
414
|
};
|
259
415
|
|
260
416
|
/**
|
@@ -269,7 +425,7 @@ goog.i18n.BidiFormatter.prototype.endEdge = function () {
|
|
269
425
|
/**
|
270
426
|
* Returns the Unicode BiDi mark matching the context directionality (LRM for
|
271
427
|
* LTR context directionality, RLM for RTL context directionality), or the
|
272
|
-
* empty string for
|
428
|
+
* empty string for unknown context directionality.
|
273
429
|
*
|
274
430
|
* @return {string} LRM for LTR context directionality and RLM for RTL context
|
275
431
|
* directionality.
|
@@ -282,37 +438,55 @@ goog.i18n.BidiFormatter.prototype.mark = function () {
|
|
282
438
|
};
|
283
439
|
|
284
440
|
/**
|
285
|
-
* Returns a Unicode
|
441
|
+
* Returns a Unicode bidi mark matching the context directionality (LRM or RLM)
|
286
442
|
* if the directionality or the exit directionality of {@code text} are opposite
|
287
443
|
* to the context directionality. Otherwise returns the empty string.
|
288
|
-
*
|
289
|
-
*
|
290
|
-
* @param {
|
444
|
+
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
445
|
+
* in text, making the logic suitable for HTML and HTML-escaped text.
|
446
|
+
* @param {?goog.i18n.bidi.Dir} textDir {@code text}'s overall directionality,
|
447
|
+
* or null if unknown and needs to be estimated.
|
448
|
+
* @param {string} text The text whose directionality is to be estimated.
|
449
|
+
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
291
450
|
* Default: false.
|
292
|
-
* @return {string} A Unicode bidi mark matching the
|
293
|
-
* the empty string
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
451
|
+
* @return {string} A Unicode bidi mark matching the context directionality, or
|
452
|
+
* the empty string when either the context directionality is unknown or
|
453
|
+
* neither the text's overall nor its exit directionality is opposite to it.
|
454
|
+
*/
|
455
|
+
goog.i18n.BidiFormatter.prototype.markAfterKnownDir = function (
|
456
|
+
textDir, text, opt_isHtml) {
|
457
|
+
if (textDir == null) {
|
458
|
+
textDir = goog.i18n.bidi.estimateDirection(text, opt_isHtml);
|
459
|
+
}
|
460
|
+
return (
|
461
|
+
this.dir_ > 0 && (textDir < 0 ||
|
462
|
+
soyshim.$$bidiIsRtlExitText_(text, opt_isHtml)) ? '\u200E' : // LRM
|
463
|
+
this.dir_ < 0 && (textDir > 0 ||
|
464
|
+
soyshim.$$bidiIsLtrExitText_(text, opt_isHtml)) ? '\u200F' : // RLM
|
465
|
+
'');
|
298
466
|
};
|
299
467
|
|
300
468
|
/**
|
301
|
-
* Formats
|
302
|
-
*
|
303
|
-
*
|
469
|
+
* Formats an HTML string for use in HTML output of the context directionality,
|
470
|
+
* so an opposite-directionality string is neither garbled nor garbles what
|
471
|
+
* follows it.
|
304
472
|
*
|
305
|
-
* @param {
|
306
|
-
*
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
473
|
+
* @param {?goog.i18n.bidi.Dir} textDir {@code str}'s overall directionality, or
|
474
|
+
* null if unknown and needs to be estimated.
|
475
|
+
* @param {string} str The input text (HTML or HTML-escaped).
|
476
|
+
* @param {boolean=} placeholder This argument exists for consistency with the
|
477
|
+
* Closure Library. Specifying it has no effect.
|
478
|
+
* @return {string} The input text after applying the above processing.
|
479
|
+
*/
|
480
|
+
goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir = function(
|
481
|
+
textDir, str, placeholder) {
|
482
|
+
if (textDir == null) {
|
483
|
+
textDir = goog.i18n.bidi.estimateDirection(str, true);
|
484
|
+
}
|
485
|
+
var reset = this.markAfterKnownDir(textDir, str, true);
|
312
486
|
if (textDir > 0 && this.dir_ <= 0) {
|
313
|
-
str = '<span dir=ltr>' + str + '</span>';
|
487
|
+
str = '<span dir="ltr">' + str + '</span>';
|
314
488
|
} else if (textDir < 0 && this.dir_ >= 0) {
|
315
|
-
str = '<span dir=rtl>' + str + '</span>';
|
489
|
+
str = '<span dir="rtl">' + str + '</span>';
|
316
490
|
}
|
317
491
|
return str + reset;
|
318
492
|
};
|
@@ -327,20 +501,26 @@ goog.i18n.BidiFormatter.prototype.startEdge = function () {
|
|
327
501
|
};
|
328
502
|
|
329
503
|
/**
|
330
|
-
* Formats
|
331
|
-
*
|
332
|
-
*
|
333
|
-
* As opposed to {@link #
|
334
|
-
* characters. In HTML,
|
335
|
-
* allow
|
504
|
+
* Formats an HTML-escaped string for use in HTML output of the context
|
505
|
+
* directionality, so an opposite-directionality string is neither garbled nor
|
506
|
+
* garbles what follows it.
|
507
|
+
* As opposed to {@link #spanWrapWithKnownDir}, this makes use of unicode bidi
|
508
|
+
* formatting characters. In HTML, it should only be used inside attribute
|
509
|
+
* values and elements that do not allow markup, e.g. an 'option' tag.
|
336
510
|
*
|
337
|
-
* @param {
|
338
|
-
*
|
511
|
+
* @param {?goog.i18n.bidi.Dir} textDir {@code str}'s overall directionality, or
|
512
|
+
* null if unknown and needs to be estimated.
|
513
|
+
* @param {string} str The input text (HTML-escaped).
|
514
|
+
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
|
515
|
+
* Default: false.
|
516
|
+
* @return {string} The input text after applying the above processing.
|
339
517
|
*/
|
340
|
-
goog.i18n.BidiFormatter.prototype.
|
341
|
-
|
342
|
-
|
343
|
-
|
518
|
+
goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir = function(
|
519
|
+
textDir, str, opt_isHtml) {
|
520
|
+
if (textDir == null) {
|
521
|
+
textDir = goog.i18n.bidi.estimateDirection(str, opt_isHtml);
|
522
|
+
}
|
523
|
+
var reset = this.markAfterKnownDir(textDir, str, opt_isHtml);
|
344
524
|
if (textDir > 0 && this.dir_ <= 0) {
|
345
525
|
str = '\u202A' + str + '\u202C';
|
346
526
|
} else if (textDir < 0 && this.dir_ >= 0) {
|
@@ -350,55 +530,58 @@ goog.i18n.BidiFormatter.prototype.unicodeWrap = function(str) {
|
|
350
530
|
};
|
351
531
|
|
352
532
|
|
353
|
-
goog.string
|
354
|
-
|
355
|
-
* Utility class to facilitate much faster string concatenation in IE,
|
356
|
-
* using Array.join() rather than the '+' operator. For other browsers
|
357
|
-
* we simply use the '+' operator.
|
358
|
-
*
|
359
|
-
* @param {Object|number|string|boolean=} opt_a1 Optional first initial item
|
360
|
-
* to append.
|
361
|
-
* @param {...Object|number|string|boolean} var_args Other initial items to
|
362
|
-
* append, e.g., new goog.string.StringBuffer('foo', 'bar').
|
363
|
-
* @constructor
|
364
|
-
*/
|
365
|
-
StringBuffer: function(opt_a1, var_args) {
|
366
|
-
|
533
|
+
if (!goog.string) {
|
534
|
+
goog.string = {
|
367
535
|
/**
|
368
|
-
*
|
369
|
-
* @
|
370
|
-
* @
|
536
|
+
* Converts \r\n, \r, and \n to <br>s
|
537
|
+
* @param {*} str The string in which to convert newlines.
|
538
|
+
* @param {boolean=} opt_xml Whether to use XML compatible tags.
|
539
|
+
* @return {string} A copy of {@code str} with converted newlines.
|
371
540
|
*/
|
372
|
-
|
541
|
+
newLineToBr: function(str, opt_xml) {
|
373
542
|
|
374
|
-
|
375
|
-
this.append.apply(this, arguments);
|
376
|
-
}
|
377
|
-
},
|
378
|
-
/**
|
379
|
-
* Converts \r\n, \r, and \n to <br>s
|
380
|
-
* @param {*} str The string in which to convert newlines.
|
381
|
-
* @return {string} A copy of {@code str} with converted newlines.
|
382
|
-
*/
|
383
|
-
newlineToBr: function(str) {
|
543
|
+
str = String(str);
|
384
544
|
|
385
|
-
|
545
|
+
// This quick test helps in the case when there are no chars to replace,
|
546
|
+
// in the worst case this makes barely a difference to the time taken.
|
547
|
+
if (!goog.string.NEWLINE_TO_BR_RE_.test(str)) {
|
548
|
+
return str;
|
549
|
+
}
|
386
550
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
551
|
+
return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
|
552
|
+
},
|
553
|
+
urlEncode: encodeURIComponent,
|
554
|
+
/**
|
555
|
+
* Regular expression used within newlineToBr().
|
556
|
+
* @type {RegExp}
|
557
|
+
* @private
|
558
|
+
*/
|
559
|
+
NEWLINE_TO_BR_RE_: /[\r\n]/
|
560
|
+
};
|
561
|
+
}
|
392
562
|
|
393
|
-
|
394
|
-
|
395
|
-
|
563
|
+
/**
|
564
|
+
* Utility class to facilitate much faster string concatenation in IE,
|
565
|
+
* using Array.join() rather than the '+' operator. For other browsers
|
566
|
+
* we simply use the '+' operator.
|
567
|
+
*
|
568
|
+
* @param {Object|number|string|boolean=} opt_a1 Optional first initial item
|
569
|
+
* to append.
|
570
|
+
* @param {...Object|number|string|boolean} var_args Other initial items to
|
571
|
+
* append, e.g., new goog.string.StringBuffer('foo', 'bar').
|
572
|
+
* @constructor
|
573
|
+
*/
|
574
|
+
goog.string.StringBuffer = function(opt_a1, var_args) {
|
396
575
|
/**
|
397
|
-
*
|
398
|
-
* @type {
|
576
|
+
* Internal buffer for the string to be concatenated.
|
577
|
+
* @type {string|Array}
|
399
578
|
* @private
|
400
579
|
*/
|
401
|
-
|
580
|
+
this.buffer_ = goog.userAgent.jscript.HAS_JSCRIPT ? [] : '';
|
581
|
+
|
582
|
+
if (opt_a1 != null) {
|
583
|
+
this.append.apply(this, arguments);
|
584
|
+
}
|
402
585
|
};
|
403
586
|
|
404
587
|
|
@@ -423,13 +606,13 @@ goog.string.StringBuffer.prototype.bufferLength_ = 0;
|
|
423
606
|
*/
|
424
607
|
goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
|
425
608
|
|
426
|
-
if (goog.userAgent.HAS_JSCRIPT) {
|
609
|
+
if (goog.userAgent.jscript.HAS_JSCRIPT) {
|
427
610
|
if (opt_a2 == null) { // no second argument (note: undefined == null)
|
428
|
-
// Array assignment is 2x faster than Array push.
|
611
|
+
// Array assignment is 2x faster than Array push. Also, use a1
|
429
612
|
// directly to avoid arguments instantiation, another 2x improvement.
|
430
613
|
this.buffer_[this.bufferLength_++] = a1;
|
431
614
|
} else {
|
432
|
-
var arr = /**@type {Array.<number|string|boolean>}*/this.buffer_;
|
615
|
+
var arr = /**@type {Array.<number|string|boolean>}*/(this.buffer_);
|
433
616
|
arr.push.apply(arr, arguments);
|
434
617
|
this.bufferLength_ = this.buffer_.length;
|
435
618
|
}
|
@@ -454,7 +637,7 @@ goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
|
|
454
637
|
*/
|
455
638
|
goog.string.StringBuffer.prototype.clear = function() {
|
456
639
|
|
457
|
-
if (goog.userAgent.HAS_JSCRIPT) {
|
640
|
+
if (goog.userAgent.jscript.HAS_JSCRIPT) {
|
458
641
|
this.buffer_.length = 0; // reuse array to avoid creating new object
|
459
642
|
this.bufferLength_ = 0;
|
460
643
|
|
@@ -471,7 +654,7 @@ goog.string.StringBuffer.prototype.clear = function() {
|
|
471
654
|
*/
|
472
655
|
goog.string.StringBuffer.prototype.toString = function() {
|
473
656
|
|
474
|
-
if (goog.userAgent.HAS_JSCRIPT) {
|
657
|
+
if (goog.userAgent.jscript.HAS_JSCRIPT) {
|
475
658
|
var str = this.buffer_.join('');
|
476
659
|
// Given a string with the entire contents, simplify the StringBuilder by
|
477
660
|
// setting its contents to only be this string, rather than many fragments.
|
@@ -495,15 +678,16 @@ if (!goog.soy) goog.soy = {
|
|
495
678
|
* innerHTML in your hand-written code, so that it will be easier
|
496
679
|
* to audit the code for cross-site scripting vulnerabilities.
|
497
680
|
*
|
498
|
-
* @param {Element} element The element whose content we are rendering.
|
499
681
|
* @param {Function} template The Soy template defining element's content.
|
500
682
|
* @param {Object=} opt_templateData The data for the template.
|
501
683
|
* @param {Object=} opt_injectedData The injected data for the template.
|
684
|
+
* @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
|
685
|
+
* nodes will be created.
|
502
686
|
*/
|
503
687
|
renderAsElement: function(
|
504
|
-
template, opt_templateData, opt_injectedData,
|
688
|
+
template, opt_templateData, opt_injectedData, opt_dom) {
|
505
689
|
return /** @type {!Element} */ (soyshim.$$renderWithWrapper_(
|
506
|
-
template, opt_templateData,
|
690
|
+
template, opt_templateData, opt_dom, true /* asElement */,
|
507
691
|
opt_injectedData));
|
508
692
|
},
|
509
693
|
/**
|
@@ -515,15 +699,15 @@ if (!goog.soy) goog.soy = {
|
|
515
699
|
*
|
516
700
|
* @param {Function} template The Soy template defining element's content.
|
517
701
|
* @param {Object=} opt_templateData The data for the template.
|
518
|
-
* @param {Document=} opt_document The document used to create DOM nodes.
|
519
|
-
* If not specified, global document object is used.
|
520
702
|
* @param {Object=} opt_injectedData The injected data for the template.
|
703
|
+
* @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
|
704
|
+
* nodes will be created.
|
521
705
|
* @return {!Node} The resulting node or document fragment.
|
522
706
|
*/
|
523
707
|
renderAsFragment: function(
|
524
|
-
template, opt_templateData, opt_injectedData,
|
708
|
+
template, opt_templateData, opt_injectedData, opt_dom) {
|
525
709
|
return soyshim.$$renderWithWrapper_(
|
526
|
-
template, opt_templateData,
|
710
|
+
template, opt_templateData, opt_dom, false /* asElement */,
|
527
711
|
opt_injectedData);
|
528
712
|
},
|
529
713
|
/**
|
@@ -542,13 +726,129 @@ if (!goog.soy) goog.soy = {
|
|
542
726
|
renderElement: function(
|
543
727
|
element, template, opt_templateData, opt_injectedData) {
|
544
728
|
element.innerHTML = template(opt_templateData, null, opt_injectedData);
|
545
|
-
}
|
729
|
+
},
|
730
|
+
data: {}
|
731
|
+
};
|
732
|
+
|
733
|
+
|
734
|
+
/**
|
735
|
+
* A type of textual content.
|
736
|
+
*
|
737
|
+
* This is an enum of type Object so that these values are unforgeable.
|
738
|
+
*
|
739
|
+
* @enum {!Object}
|
740
|
+
*/
|
741
|
+
goog.soy.data.SanitizedContentKind = {
|
742
|
+
|
743
|
+
/**
|
744
|
+
* A snippet of HTML that does not start or end inside a tag, comment, entity,
|
745
|
+
* or DOCTYPE; and that does not contain any executable code
|
746
|
+
* (JS, {@code <object>}s, etc.) from a different trust domain.
|
747
|
+
*/
|
748
|
+
HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},
|
749
|
+
|
750
|
+
/**
|
751
|
+
* Executable Javascript code or expression, safe for insertion in a
|
752
|
+
* script-tag or event handler context, known to be free of any
|
753
|
+
* attacker-controlled scripts. This can either be side-effect-free
|
754
|
+
* Javascript (such as JSON) or Javascript that's entirely under Google's
|
755
|
+
* control.
|
756
|
+
*/
|
757
|
+
JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},
|
758
|
+
|
759
|
+
/**
|
760
|
+
* A sequence of code units that can appear between quotes (either kind) in a
|
761
|
+
* JS program without causing a parse error, and without causing any side
|
762
|
+
* effects.
|
763
|
+
* <p>
|
764
|
+
* The content should not contain unescaped quotes, newlines, or anything else
|
765
|
+
* that would cause parsing to fail or to cause a JS parser to finish the
|
766
|
+
* string its parsing inside the content.
|
767
|
+
* <p>
|
768
|
+
* The content must also not end inside an escape sequence ; no partial octal
|
769
|
+
* escape sequences or odd number of '{@code \}'s at the end.
|
770
|
+
*/
|
771
|
+
JS_STR_CHARS: goog.DEBUG ? {sanitizedContentJsStrChars: true} : {},
|
772
|
+
|
773
|
+
/** A properly encoded portion of a URI. */
|
774
|
+
URI: goog.DEBUG ? {sanitizedContentUri: true} : {},
|
775
|
+
|
776
|
+
/**
|
777
|
+
* Repeated attribute names and values. For example,
|
778
|
+
* {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
|
779
|
+
*/
|
780
|
+
ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
|
781
|
+
|
782
|
+
// TODO: Consider separating rules, declarations, and values into
|
783
|
+
// separate types, but for simplicity, we'll treat explicitly blessed
|
784
|
+
// SanitizedContent as allowed in all of these contexts.
|
785
|
+
/**
|
786
|
+
* A CSS3 declaration, property, value or group of semicolon separated
|
787
|
+
* declarations.
|
788
|
+
*/
|
789
|
+
CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},
|
790
|
+
|
791
|
+
/**
|
792
|
+
* Unsanitized plain-text content.
|
793
|
+
*
|
794
|
+
* This is effectively the "null" entry of this enum, and is sometimes used
|
795
|
+
* to explicitly mark content that should never be used unescaped. Since any
|
796
|
+
* string is safe to use as text, being of ContentKind.TEXT makes no
|
797
|
+
* guarantees about its safety in any other context such as HTML.
|
798
|
+
*/
|
799
|
+
TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}
|
800
|
+
};
|
801
|
+
|
802
|
+
|
803
|
+
|
804
|
+
/**
|
805
|
+
* A string-like object that carries a content-type and a content direction.
|
806
|
+
*
|
807
|
+
* IMPORTANT! Do not create these directly, nor instantiate the subclasses.
|
808
|
+
* Instead, use a trusted, centrally reviewed library as endorsed by your team
|
809
|
+
* to generate these objects. Otherwise, you risk accidentally creating
|
810
|
+
* SanitizedContent that is attacker-controlled and gets evaluated unescaped in
|
811
|
+
* templates.
|
812
|
+
*
|
813
|
+
* @constructor
|
814
|
+
*/
|
815
|
+
goog.soy.data.SanitizedContent = function() {
|
816
|
+
throw Error('Do not instantiate directly');
|
817
|
+
};
|
818
|
+
|
819
|
+
|
820
|
+
/**
|
821
|
+
* The context in which this content is safe from XSS attacks.
|
822
|
+
* @type {goog.soy.data.SanitizedContentKind}
|
823
|
+
*/
|
824
|
+
goog.soy.data.SanitizedContent.prototype.contentKind;
|
825
|
+
|
826
|
+
|
827
|
+
/**
|
828
|
+
* The content's direction; null if unknown and thus to be estimated when
|
829
|
+
* necessary.
|
830
|
+
* @type {?goog.i18n.bidi.Dir}
|
831
|
+
*/
|
832
|
+
goog.soy.data.SanitizedContent.prototype.contentDir = null;
|
833
|
+
|
834
|
+
|
835
|
+
/**
|
836
|
+
* The already-safe content.
|
837
|
+
* @type {string}
|
838
|
+
*/
|
839
|
+
goog.soy.data.SanitizedContent.prototype.content;
|
840
|
+
|
841
|
+
|
842
|
+
/** @override */
|
843
|
+
goog.soy.data.SanitizedContent.prototype.toString = function() {
|
844
|
+
return this.content;
|
546
845
|
};
|
547
846
|
|
548
847
|
|
549
848
|
var soy = { esc: {} };
|
550
849
|
var soydata = {};
|
551
|
-
|
850
|
+
soydata.VERY_UNSAFE = {};
|
851
|
+
var soyshim = { $$DEFAULT_TEMPLATE_DATA_: {} };
|
552
852
|
/**
|
553
853
|
* Helper function to render a Soy template into a single node or a document
|
554
854
|
* fragment. If the rendered HTML string represents a single node, then that
|
@@ -557,8 +857,8 @@ var soyshim = {};
|
|
557
857
|
*
|
558
858
|
* @param {Function} template The Soy template defining the element's content.
|
559
859
|
* @param {Object=} opt_templateData The data for the template.
|
560
|
-
* @param {Document=}
|
561
|
-
*
|
860
|
+
* @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM
|
861
|
+
* nodes will be created.
|
562
862
|
* @param {boolean=} opt_asElement Whether to wrap the fragment in an
|
563
863
|
* element if the template does not render a single element. If true,
|
564
864
|
* result is always an Element.
|
@@ -567,10 +867,10 @@ var soyshim = {};
|
|
567
867
|
* @private
|
568
868
|
*/
|
569
869
|
soyshim.$$renderWithWrapper_ = function(
|
570
|
-
template, opt_templateData,
|
870
|
+
template, opt_templateData, opt_dom, opt_asElement, opt_injectedData) {
|
571
871
|
|
572
|
-
var
|
573
|
-
var wrapper =
|
872
|
+
var dom = opt_dom || document;
|
873
|
+
var wrapper = dom.createElement('div');
|
574
874
|
wrapper.innerHTML = template(
|
575
875
|
opt_templateData || soyshim.$$DEFAULT_TEMPLATE_DATA_, undefined,
|
576
876
|
opt_injectedData);
|
@@ -589,7 +889,7 @@ soyshim.$$renderWithWrapper_ = function(
|
|
589
889
|
}
|
590
890
|
|
591
891
|
// Otherwise, create and return a fragment.
|
592
|
-
var fragment =
|
892
|
+
var fragment = dom.createDocumentFragment();
|
593
893
|
while (wrapper.firstChild) {
|
594
894
|
fragment.appendChild(wrapper.firstChild);
|
595
895
|
}
|
@@ -597,38 +897,11 @@ soyshim.$$renderWithWrapper_ = function(
|
|
597
897
|
};
|
598
898
|
|
599
899
|
|
600
|
-
/**
|
601
|
-
* Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the
|
602
|
-
* directionality or the exit directionality of text are opposite to
|
603
|
-
* bidiGlobalDir. Otherwise returns the empty string.
|
604
|
-
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
605
|
-
* in text, making the logic suitable for HTML and HTML-escaped text.
|
606
|
-
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
607
|
-
* if rtl, 0 if unknown.
|
608
|
-
* @param {number} dir text's directionality: 1 if ltr, -1 if rtl, 0 if unknown.
|
609
|
-
* @param {string} text The text whose directionality is to be estimated.
|
610
|
-
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
611
|
-
* Default: false.
|
612
|
-
* @return {string} A Unicode bidi mark matching bidiGlobalDir, or
|
613
|
-
* the empty string when text's overall and exit directionalities both match
|
614
|
-
* bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
|
615
|
-
* @private
|
616
|
-
*/
|
617
|
-
soyshim.$$bidiMarkAfterKnownDir_ = function(
|
618
|
-
bidiGlobalDir, dir, text, opt_isHtml) {
|
619
|
-
return (
|
620
|
-
bidiGlobalDir > 0 && (dir < 0 ||
|
621
|
-
soyshim.$$bidiIsRtlExitText_(text, opt_isHtml)) ? '\u200E' : // LRM
|
622
|
-
bidiGlobalDir < 0 && (dir > 0 ||
|
623
|
-
soyshim.$$bidiIsLtrExitText_(text, opt_isHtml)) ? '\u200F' : // RLM
|
624
|
-
'');
|
625
|
-
};
|
626
|
-
|
627
|
-
|
628
900
|
/**
|
629
901
|
* Strips str of any HTML mark-up and escapes. Imprecise in several ways, but
|
630
902
|
* precision is not very important, since the result is only meant to be used
|
631
903
|
* for directionality detection.
|
904
|
+
* Based on goog.i18n.bidi.stripHtmlIfNeeded_().
|
632
905
|
* @param {string} str The string to be stripped.
|
633
906
|
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
|
634
907
|
* Default: false.
|
@@ -636,7 +909,7 @@ soyshim.$$bidiMarkAfterKnownDir_ = function(
|
|
636
909
|
* @private
|
637
910
|
*/
|
638
911
|
soyshim.$$bidiStripHtmlIfNecessary_ = function(str, opt_isHtml) {
|
639
|
-
return opt_isHtml ? str.replace(soyshim.$$BIDI_HTML_SKIP_RE_, '
|
912
|
+
return opt_isHtml ? str.replace(soyshim.$$BIDI_HTML_SKIP_RE_, '') : str;
|
640
913
|
};
|
641
914
|
|
642
915
|
|
@@ -644,6 +917,7 @@ soyshim.$$bidiStripHtmlIfNecessary_ = function(str, opt_isHtml) {
|
|
644
917
|
* Simplified regular expression for am HTML tag (opening or closing) or an HTML
|
645
918
|
* escape - the things we want to skip over in order to ignore their ltr
|
646
919
|
* characters.
|
920
|
+
* Copied from goog.i18n.bidi.htmlSkipReg_.
|
647
921
|
* @type {RegExp}
|
648
922
|
* @private
|
649
923
|
*/
|
@@ -654,38 +928,30 @@ soyshim.$$BIDI_HTML_SKIP_RE_ = /<[^>]*>|&[^;]+;/g;
|
|
654
928
|
* A practical pattern to identify strong LTR character. This pattern is not
|
655
929
|
* theoretically correct according to unicode standard. It is simplified for
|
656
930
|
* performance and small code size.
|
931
|
+
* Copied from goog.i18n.bidi.ltrChars_.
|
657
932
|
* @type {string}
|
658
933
|
* @private
|
659
934
|
*/
|
660
935
|
soyshim.$$bidiLtrChars_ =
|
661
936
|
'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
|
662
|
-
'\u2C00-\uFB1C\
|
663
|
-
|
664
|
-
|
665
|
-
/**
|
666
|
-
* A practical pattern to identify strong neutral and weak character. This
|
667
|
-
* pattern is not theoretically correct according to unicode standard. It is
|
668
|
-
* simplified for performance and small code size.
|
669
|
-
* @type {string}
|
670
|
-
* @private
|
671
|
-
*/
|
672
|
-
soyshim.$$bidiNeutralChars_ =
|
673
|
-
'\u0000-\u0020!-@[-`{-\u00BF\u00D7\u00F7\u02B9-\u02FF\u2000-\u2BFF';
|
937
|
+
'\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
|
674
938
|
|
675
939
|
|
676
940
|
/**
|
677
941
|
* A practical pattern to identify strong RTL character. This pattern is not
|
678
942
|
* theoretically correct according to unicode standard. It is simplified for
|
679
943
|
* performance and small code size.
|
944
|
+
* Copied from goog.i18n.bidi.rtlChars_.
|
680
945
|
* @type {string}
|
681
946
|
* @private
|
682
947
|
*/
|
683
|
-
soyshim.$$bidiRtlChars_ = '\u0591-\u07FF\uFB1D-\
|
948
|
+
soyshim.$$bidiRtlChars_ = '\u0591-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
|
684
949
|
|
685
950
|
|
686
951
|
/**
|
687
952
|
* Regular expressions to check if a piece of text is of RTL directionality
|
688
953
|
* on first character with strong directionality.
|
954
|
+
* Based on goog.i18n.bidi.rtlDirCheckRe_.
|
689
955
|
* @type {RegExp}
|
690
956
|
* @private
|
691
957
|
*/
|
@@ -694,73 +960,59 @@ soyshim.$$bidiRtlDirCheckRe_ = new RegExp(
|
|
694
960
|
|
695
961
|
|
696
962
|
/**
|
697
|
-
* Regular
|
698
|
-
*
|
963
|
+
* Regular expression to check for LTR characters.
|
964
|
+
* Based on goog.i18n.bidi.ltrCharReg_.
|
699
965
|
* @type {RegExp}
|
700
966
|
* @private
|
701
967
|
*/
|
702
|
-
soyshim.$$
|
703
|
-
'^[' + soyshim.$$bidiNeutralChars_ + ']*$|^http://');
|
968
|
+
soyshim.$$bidiLtrCharRe_ = new RegExp('[' + soyshim.$$bidiLtrChars_ + ']');
|
704
969
|
|
705
970
|
|
706
971
|
/**
|
707
|
-
*
|
708
|
-
*
|
709
|
-
*
|
710
|
-
*
|
972
|
+
* Regular expression to check if a string looks like something that must
|
973
|
+
* always be LTR even in RTL text, e.g. a URL. When estimating the
|
974
|
+
* directionality of text containing these, we treat these as weakly LTR,
|
975
|
+
* like numbers.
|
976
|
+
* Copied from goog.i18n.bidi.isRequiredLtrRe_.
|
977
|
+
* @type {RegExp}
|
711
978
|
* @private
|
712
979
|
*/
|
713
|
-
soyshim.$$
|
714
|
-
return soyshim.$$bidiRtlDirCheckRe_.test(str);
|
715
|
-
};
|
980
|
+
soyshim.$$bidiIsRequiredLtrRe_ = /^http:\/\/.*/;
|
716
981
|
|
717
982
|
|
718
983
|
/**
|
719
|
-
*
|
720
|
-
*
|
721
|
-
*
|
722
|
-
*
|
984
|
+
* Regular expression to check if a string contains any numerals. Used to
|
985
|
+
* differentiate between completely neutral strings and those containing
|
986
|
+
* numbers, which are weakly LTR.
|
987
|
+
* Copied from goog.i18n.bidi.hasNumeralsRe_.
|
988
|
+
* @type {RegExp}
|
723
989
|
* @private
|
724
990
|
*/
|
725
|
-
soyshim.$$
|
726
|
-
return soyshim.$$bidiNeutralDirCheckRe_.test(str);
|
727
|
-
};
|
991
|
+
soyshim.$$bidiHasNumeralsRe_ = /\d/;
|
728
992
|
|
729
993
|
|
730
994
|
/**
|
731
|
-
*
|
732
|
-
*
|
995
|
+
* Regular expression to split a string into "words" for directionality
|
996
|
+
* estimation based on relative word counts.
|
997
|
+
* Copied from goog.i18n.bidi.wordSeparatorRe_.
|
998
|
+
* @type {RegExp}
|
733
999
|
* @private
|
734
1000
|
*/
|
735
|
-
soyshim.$$
|
1001
|
+
soyshim.$$bidiWordSeparatorRe_ = /\s+/;
|
736
1002
|
|
737
1003
|
|
738
1004
|
/**
|
739
|
-
*
|
740
|
-
*
|
741
|
-
* @
|
1005
|
+
* This constant controls threshold of rtl directionality.
|
1006
|
+
* Copied from goog.i18n.bidi.rtlDetectionThreshold_.
|
1007
|
+
* @type {number}
|
742
1008
|
* @private
|
743
1009
|
*/
|
744
|
-
soyshim.$$
|
745
|
-
var rtlCount = 0;
|
746
|
-
var totalCount = 0;
|
747
|
-
var tokens = str.split(' ');
|
748
|
-
for (var i = 0; i < tokens.length; i++) {
|
749
|
-
if (soyshim.$$bidiIsRtlText_(tokens[i])) {
|
750
|
-
rtlCount++;
|
751
|
-
totalCount++;
|
752
|
-
} else if (!soyshim.$$bidiIsNeutralText_(tokens[i])) {
|
753
|
-
totalCount++;
|
754
|
-
}
|
755
|
-
}
|
756
|
-
|
757
|
-
return totalCount == 0 ? 0 : rtlCount / totalCount;
|
758
|
-
};
|
759
|
-
|
1010
|
+
soyshim.$$bidiRtlDetectionThreshold_ = 0.40;
|
760
1011
|
|
761
1012
|
/**
|
762
1013
|
* Regular expressions to check if the last strongly-directional character in a
|
763
1014
|
* piece of text is LTR.
|
1015
|
+
* Based on goog.i18n.bidi.ltrExitDirCheckRe_.
|
764
1016
|
* @type {RegExp}
|
765
1017
|
* @private
|
766
1018
|
*/
|
@@ -771,6 +1023,7 @@ soyshim.$$bidiLtrExitDirCheckRe_ = new RegExp(
|
|
771
1023
|
/**
|
772
1024
|
* Regular expressions to check if the last strongly-directional character in a
|
773
1025
|
* piece of text is RTL.
|
1026
|
+
* Based on goog.i18n.bidi.rtlExitDirCheckRe_.
|
774
1027
|
* @type {RegExp}
|
775
1028
|
* @private
|
776
1029
|
*/
|
@@ -781,6 +1034,7 @@ soyshim.$$bidiRtlExitDirCheckRe_ = new RegExp(
|
|
781
1034
|
/**
|
782
1035
|
* Check if the exit directionality a piece of text is LTR, i.e. if the last
|
783
1036
|
* strongly-directional character in the string is LTR.
|
1037
|
+
* Based on goog.i18n.bidi.endsWithLtr().
|
784
1038
|
* @param {string} str string being checked.
|
785
1039
|
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
|
786
1040
|
* Default: false.
|
@@ -796,6 +1050,7 @@ soyshim.$$bidiIsLtrExitText_ = function(str, opt_isHtml) {
|
|
796
1050
|
/**
|
797
1051
|
* Check if the exit directionality a piece of text is RTL, i.e. if the last
|
798
1052
|
* strongly-directional character in the string is RTL.
|
1053
|
+
* Based on goog.i18n.bidi.endsWithRtl().
|
799
1054
|
* @param {string} str string being checked.
|
800
1055
|
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
|
801
1056
|
* Default: false.
|
@@ -818,7 +1073,7 @@ soyshim.$$bidiIsRtlExitText_ = function(str, opt_isHtml) {
|
|
818
1073
|
|
819
1074
|
/**
|
820
1075
|
* Utility class to facilitate much faster string concatenation in IE,
|
821
|
-
* using Array.join() rather than the '+' operator.
|
1076
|
+
* using Array.join() rather than the '+' operator. For other browsers
|
822
1077
|
* we simply use the '+' operator.
|
823
1078
|
*
|
824
1079
|
* @param {Object} var_args Initial items to append,
|
@@ -835,131 +1090,441 @@ soy.StringBuilder = goog.string.StringBuffer;
|
|
835
1090
|
|
836
1091
|
/**
|
837
1092
|
* A type of textual content.
|
838
|
-
*
|
1093
|
+
*
|
1094
|
+
* This is an enum of type Object so that these values are unforgeable.
|
1095
|
+
*
|
1096
|
+
* @enum {!Object}
|
839
1097
|
*/
|
840
|
-
soydata.SanitizedContentKind =
|
1098
|
+
soydata.SanitizedContentKind = goog.soy.data.SanitizedContentKind;
|
841
1099
|
|
842
|
-
/**
|
843
|
-
* A snippet of HTML that does not start or end inside a tag, comment, entity,
|
844
|
-
* or DOCTYPE; and that does not contain any executable code
|
845
|
-
* (JS, {@code <object>}s, etc.) from a different trust domain.
|
846
|
-
*/
|
847
|
-
HTML: 0,
|
848
1100
|
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
1101
|
+
/**
|
1102
|
+
* Checks whether a given value is of a given content kind.
|
1103
|
+
*
|
1104
|
+
* @param {*} value The value to be examined.
|
1105
|
+
* @param {soydata.SanitizedContentKind} contentKind The desired content
|
1106
|
+
* kind.
|
1107
|
+
* @return {boolean} Whether the given value is of the given kind.
|
1108
|
+
* @private
|
1109
|
+
*/
|
1110
|
+
soydata.isContentKind = function(value, contentKind) {
|
1111
|
+
// TODO(user): This function should really include the assert on
|
1112
|
+
// value.constructor that is currently sprinkled at most of the call sites.
|
1113
|
+
// Unfortunately, that would require a (debug-mode-only) switch statement.
|
1114
|
+
// TODO(user): Perhaps we should get rid of the contentKind property
|
1115
|
+
// altogether and only at the constructor.
|
1116
|
+
return value != null && value.contentKind === contentKind;
|
1117
|
+
};
|
1118
|
+
|
1119
|
+
|
1120
|
+
/**
|
1121
|
+
* Returns a given value's contentDir property, constrained to a
|
1122
|
+
* goog.i18n.bidi.Dir value or null. Returns null if the value is null,
|
1123
|
+
* undefined, a primitive or does not have a contentDir property, or the
|
1124
|
+
* property's value is not 1 (for LTR), -1 (for RTL), or 0 (for neutral).
|
1125
|
+
*
|
1126
|
+
* @param {*} value The value whose contentDir property, if any, is to
|
1127
|
+
* be returned.
|
1128
|
+
* @return {?goog.i18n.bidi.Dir} The contentDir property.
|
1129
|
+
*/
|
1130
|
+
soydata.getContentDir = function(value) {
|
1131
|
+
if (value != null) {
|
1132
|
+
switch (value.contentDir) {
|
1133
|
+
case goog.i18n.bidi.Dir.LTR:
|
1134
|
+
return goog.i18n.bidi.Dir.LTR;
|
1135
|
+
case goog.i18n.bidi.Dir.RTL:
|
1136
|
+
return goog.i18n.bidi.Dir.RTL;
|
1137
|
+
case goog.i18n.bidi.Dir.NEUTRAL:
|
1138
|
+
return goog.i18n.bidi.Dir.NEUTRAL;
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
return null;
|
1142
|
+
};
|
1143
|
+
|
1144
|
+
|
1145
|
+
/**
|
1146
|
+
* Content of type {@link soydata.SanitizedContentKind.HTML}.
|
1147
|
+
*
|
1148
|
+
* The content is a string of HTML that can safely be embedded in a PCDATA
|
1149
|
+
* context in your app. If you would be surprised to find that an HTML
|
1150
|
+
* sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and
|
1151
|
+
* you wouldn't write a template that produces {@code s} on security or privacy
|
1152
|
+
* grounds, then don't pass {@code s} here. The default content direction is
|
1153
|
+
* unknown, i.e. to be estimated when necessary.
|
1154
|
+
*
|
1155
|
+
* @constructor
|
1156
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1157
|
+
*/
|
1158
|
+
soydata.SanitizedHtml = function() {
|
1159
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1160
|
+
};
|
1161
|
+
goog.inherits(soydata.SanitizedHtml, goog.soy.data.SanitizedContent);
|
1162
|
+
|
1163
|
+
/** @override */
|
1164
|
+
soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML;
|
1165
|
+
|
1166
|
+
/**
|
1167
|
+
* Returns a SanitizedHtml object for a particular value. The content direction
|
1168
|
+
* is preserved.
|
1169
|
+
*
|
1170
|
+
* This HTML-escapes the value unless it is already SanitizedHtml.
|
1171
|
+
*
|
1172
|
+
* @param {*} value The value to convert. If it is already a SanitizedHtml
|
1173
|
+
* object, it is left alone.
|
1174
|
+
* @return {!soydata.SanitizedHtml} A SanitizedHtml object derived from the
|
1175
|
+
* stringified value. It is escaped unless the input is SanitizedHtml.
|
1176
|
+
*/
|
1177
|
+
soydata.SanitizedHtml.from = function(value) {
|
1178
|
+
// The check is soydata.isContentKind() inlined for performance.
|
1179
|
+
if (value != null &&
|
1180
|
+
value.contentKind === soydata.SanitizedContentKind.HTML) {
|
1181
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
1182
|
+
return /** @type {!soydata.SanitizedHtml} */ (value);
|
1183
|
+
}
|
1184
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
1185
|
+
soy.esc.$$escapeHtmlHelper(String(value)), soydata.getContentDir(value));
|
1186
|
+
};
|
1187
|
+
|
1188
|
+
|
1189
|
+
/**
|
1190
|
+
* Content of type {@link soydata.SanitizedContentKind.JS}.
|
1191
|
+
*
|
1192
|
+
* The content is Javascript source that when evaluated does not execute any
|
1193
|
+
* attacker-controlled scripts. The content direction is LTR.
|
1194
|
+
*
|
1195
|
+
* @constructor
|
1196
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1197
|
+
*/
|
1198
|
+
soydata.SanitizedJs = function() {
|
1199
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1200
|
+
};
|
1201
|
+
goog.inherits(soydata.SanitizedJs, goog.soy.data.SanitizedContent);
|
1202
|
+
|
1203
|
+
/** @override */
|
1204
|
+
soydata.SanitizedJs.prototype.contentKind =
|
1205
|
+
soydata.SanitizedContentKind.JS;
|
1206
|
+
|
1207
|
+
/** @override */
|
1208
|
+
soydata.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
1209
|
+
|
1210
|
+
|
1211
|
+
/**
|
1212
|
+
* Content of type {@link soydata.SanitizedContentKind.JS_STR_CHARS}.
|
1213
|
+
*
|
1214
|
+
* The content can be safely inserted as part of a single- or double-quoted
|
1215
|
+
* string without terminating the string. The default content direction is
|
1216
|
+
* unknown, i.e. to be estimated when necessary.
|
1217
|
+
*
|
1218
|
+
* @constructor
|
1219
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1220
|
+
*/
|
1221
|
+
soydata.SanitizedJsStrChars = function() {
|
1222
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1223
|
+
};
|
1224
|
+
goog.inherits(soydata.SanitizedJsStrChars, goog.soy.data.SanitizedContent);
|
1225
|
+
|
1226
|
+
/** @override */
|
1227
|
+
soydata.SanitizedJsStrChars.prototype.contentKind =
|
1228
|
+
soydata.SanitizedContentKind.JS_STR_CHARS;
|
1229
|
+
|
1230
|
+
/**
|
1231
|
+
* Content of type {@link soydata.SanitizedContentKind.URI}.
|
1232
|
+
*
|
1233
|
+
* The content is a URI chunk that the caller knows is safe to emit in a
|
1234
|
+
* template. The content direction is LTR.
|
1235
|
+
*
|
1236
|
+
* @constructor
|
1237
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1238
|
+
*/
|
1239
|
+
soydata.SanitizedUri = function() {
|
1240
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1241
|
+
};
|
1242
|
+
goog.inherits(soydata.SanitizedUri, goog.soy.data.SanitizedContent);
|
1243
|
+
|
1244
|
+
/** @override */
|
1245
|
+
soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI;
|
1246
|
+
|
1247
|
+
/** @override */
|
1248
|
+
soydata.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
1249
|
+
|
1250
|
+
|
1251
|
+
/**
|
1252
|
+
* Content of type {@link soydata.SanitizedContentKind.ATTRIBUTES}.
|
1253
|
+
*
|
1254
|
+
* The content should be safely embeddable within an open tag, such as a
|
1255
|
+
* key="value" pair. The content direction is LTR.
|
1256
|
+
*
|
1257
|
+
* @constructor
|
1258
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1259
|
+
*/
|
1260
|
+
soydata.SanitizedHtmlAttribute = function() {
|
1261
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1262
|
+
};
|
1263
|
+
goog.inherits(soydata.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
|
1264
|
+
|
1265
|
+
/** @override */
|
1266
|
+
soydata.SanitizedHtmlAttribute.prototype.contentKind =
|
1267
|
+
soydata.SanitizedContentKind.ATTRIBUTES;
|
1268
|
+
|
1269
|
+
/** @override */
|
1270
|
+
soydata.SanitizedHtmlAttribute.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
1271
|
+
|
1272
|
+
|
1273
|
+
/**
|
1274
|
+
* Content of type {@link soydata.SanitizedContentKind.CSS}.
|
1275
|
+
*
|
1276
|
+
* The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
|
1277
|
+
* The content direction is LTR.
|
1278
|
+
*
|
1279
|
+
* @constructor
|
1280
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1281
|
+
*/
|
1282
|
+
soydata.SanitizedCss = function() {
|
1283
|
+
goog.soy.data.SanitizedContent.call(this); // Throws an exception.
|
1284
|
+
};
|
1285
|
+
goog.inherits(soydata.SanitizedCss, goog.soy.data.SanitizedContent);
|
1286
|
+
|
1287
|
+
/** @override */
|
1288
|
+
soydata.SanitizedCss.prototype.contentKind =
|
1289
|
+
soydata.SanitizedContentKind.CSS;
|
1290
|
+
|
1291
|
+
/** @override */
|
1292
|
+
soydata.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
|
1293
|
+
|
1294
|
+
|
1295
|
+
/**
|
1296
|
+
* Unsanitized plain text string.
|
1297
|
+
*
|
1298
|
+
* While all strings are effectively safe to use as a plain text, there are no
|
1299
|
+
* guarantees about safety in any other context such as HTML. This is
|
1300
|
+
* sometimes used to mark that should never be used unescaped.
|
1301
|
+
*
|
1302
|
+
* @param {*} content Plain text with no guarantees.
|
1303
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1304
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1305
|
+
* @constructor
|
1306
|
+
* @extends {goog.soy.data.SanitizedContent}
|
1307
|
+
*/
|
1308
|
+
soydata.UnsanitizedText = function(content, opt_contentDir) {
|
1309
|
+
/** @override */
|
1310
|
+
this.content = String(content);
|
1311
|
+
this.contentDir = opt_contentDir != null ? opt_contentDir : null;
|
1312
|
+
};
|
1313
|
+
goog.inherits(soydata.UnsanitizedText, goog.soy.data.SanitizedContent);
|
1314
|
+
|
1315
|
+
/** @override */
|
1316
|
+
soydata.UnsanitizedText.prototype.contentKind =
|
1317
|
+
soydata.SanitizedContentKind.TEXT;
|
1318
|
+
|
1319
|
+
|
1320
|
+
/**
|
1321
|
+
* Empty string, used as a type in Soy templates.
|
1322
|
+
* @enum {string}
|
1323
|
+
* @private
|
1324
|
+
*/
|
1325
|
+
soydata.$$EMPTY_STRING_ = {
|
1326
|
+
VALUE: ''
|
1327
|
+
};
|
862
1328
|
|
863
|
-
/** A properly encoded portion of a URI. */
|
864
|
-
URI: 2,
|
865
1329
|
|
866
|
-
|
867
|
-
|
1330
|
+
/**
|
1331
|
+
* Creates a factory for SanitizedContent types.
|
1332
|
+
*
|
1333
|
+
* This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
|
1334
|
+
* instantiate Sanitized* classes, without making the Sanitized* constructors
|
1335
|
+
* publicly usable. Requiring all construction to use the VERY_UNSAFE names
|
1336
|
+
* helps callers and their reviewers easily tell that creating SanitizedContent
|
1337
|
+
* is not always safe and calls for careful review.
|
1338
|
+
*
|
1339
|
+
* @param {function(new: T)} ctor A constructor.
|
1340
|
+
* @return {!function(*, ?goog.i18n.bidi.Dir=): T} A factory that takes
|
1341
|
+
* content and an optional content direction and returns a new instance. If
|
1342
|
+
* the content direction is undefined, ctor.prototype.contentDir is used.
|
1343
|
+
* @template T
|
1344
|
+
* @private
|
1345
|
+
*/
|
1346
|
+
soydata.$$makeSanitizedContentFactory_ = function(ctor) {
|
1347
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
1348
|
+
function InstantiableCtor() {}
|
1349
|
+
InstantiableCtor.prototype = ctor.prototype;
|
1350
|
+
/**
|
1351
|
+
* Creates a ctor-type SanitizedContent instance.
|
1352
|
+
*
|
1353
|
+
* @param {*} content The content to put in the instance.
|
1354
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
|
1355
|
+
* undefined, ctor.prototype.contentDir is used.
|
1356
|
+
* @return {goog.soy.data.SanitizedContent} The new instance. It is actually
|
1357
|
+
* of type T above (ctor's type, a descendant of SanitizedContent), but
|
1358
|
+
* there is no way to express that here.
|
1359
|
+
*/
|
1360
|
+
function sanitizedContentFactory(content, opt_contentDir) {
|
1361
|
+
var result = new InstantiableCtor();
|
1362
|
+
result.content = String(content);
|
1363
|
+
if (opt_contentDir !== undefined) {
|
1364
|
+
result.contentDir = opt_contentDir;
|
1365
|
+
}
|
1366
|
+
return result;
|
1367
|
+
}
|
1368
|
+
return sanitizedContentFactory;
|
868
1369
|
};
|
869
1370
|
|
870
1371
|
|
871
1372
|
/**
|
872
|
-
*
|
873
|
-
*
|
874
|
-
*
|
1373
|
+
* Creates a factory for SanitizedContent types that should always have their
|
1374
|
+
* default directionality.
|
1375
|
+
*
|
1376
|
+
* This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
|
1377
|
+
* instantiate Sanitized* classes, without making the Sanitized* constructors
|
1378
|
+
* publicly usable. Requiring all construction to use the VERY_UNSAFE names
|
1379
|
+
* helps callers and their reviewers easily tell that creating SanitizedContent
|
1380
|
+
* is not always safe and calls for careful review.
|
1381
|
+
*
|
1382
|
+
* @param {function(new: T, string)} ctor A constructor.
|
1383
|
+
* @return {!function(*): T} A factory that takes content and returns a new
|
1384
|
+
* instance (with default directionality, i.e. ctor.prototype.contentDir).
|
1385
|
+
* @template T
|
875
1386
|
* @private
|
876
1387
|
*/
|
877
|
-
soydata
|
1388
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_ = function(ctor) {
|
1389
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
1390
|
+
function InstantiableCtor() {}
|
1391
|
+
InstantiableCtor.prototype = ctor.prototype;
|
878
1392
|
/**
|
879
|
-
*
|
880
|
-
*
|
1393
|
+
* Creates a ctor-type SanitizedContent instance.
|
1394
|
+
*
|
1395
|
+
* @param {*} content The content to put in the instance.
|
1396
|
+
* @return {goog.soy.data.SanitizedContent} The new instance. It is actually
|
1397
|
+
* of type T above (ctor's type, a descendant of SanitizedContent), but
|
1398
|
+
* there is no way to express that here.
|
881
1399
|
*/
|
882
|
-
|
1400
|
+
function sanitizedContentFactory(content) {
|
1401
|
+
var result = new InstantiableCtor();
|
1402
|
+
result.content = String(content);
|
1403
|
+
return result;
|
1404
|
+
}
|
1405
|
+
return sanitizedContentFactory;
|
883
1406
|
};
|
884
1407
|
|
885
|
-
/** @type {soydata.SanitizedContentKind} */
|
886
|
-
soydata.SanitizedContent.prototype.contentKind;
|
887
1408
|
|
888
|
-
|
889
|
-
|
890
|
-
|
1409
|
+
// -----------------------------------------------------------------------------
|
1410
|
+
// Sanitized content ordainers. Please use these with extreme caution (with the
|
1411
|
+
// exception of markUnsanitizedText). A good recommendation is to limit usage
|
1412
|
+
// of these to just a handful of files in your source tree where usages can be
|
1413
|
+
// carefully audited.
|
1414
|
+
|
1415
|
+
|
1416
|
+
/**
|
1417
|
+
* Protects a string from being used in an noAutoescaped context.
|
1418
|
+
*
|
1419
|
+
* This is useful for content where there is significant risk of accidental
|
1420
|
+
* unescaped usage in a Soy template. A great case is for user-controlled
|
1421
|
+
* data that has historically been a source of vulernabilities.
|
1422
|
+
*
|
1423
|
+
* @param {*} content Text to protect.
|
1424
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1425
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1426
|
+
* @return {!soydata.UnsanitizedText} A wrapper that is rejected by the
|
1427
|
+
* Soy noAutoescape print directive.
|
1428
|
+
*/
|
1429
|
+
soydata.markUnsanitizedText = function(content, opt_contentDir) {
|
1430
|
+
return new soydata.UnsanitizedText(content, opt_contentDir);
|
891
1431
|
};
|
892
1432
|
|
893
1433
|
|
894
1434
|
/**
|
895
|
-
*
|
896
|
-
*
|
897
|
-
*
|
1435
|
+
* Takes a leap of faith that the provided content is "safe" HTML.
|
1436
|
+
*
|
1437
|
+
* @param {*} content A string of HTML that can safely be embedded in
|
1438
|
+
* a PCDATA context in your app. If you would be surprised to find that an
|
898
1439
|
* HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs)
|
899
1440
|
* and you wouldn't write a template that produces {@code s} on security or
|
900
1441
|
* privacy grounds, then don't pass {@code s} here.
|
901
|
-
* @
|
902
|
-
*
|
1442
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1443
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1444
|
+
* @return {!soydata.SanitizedHtml} Sanitized content wrapper that
|
1445
|
+
* indicates to Soy not to escape when printed as HTML.
|
903
1446
|
*/
|
904
|
-
soydata.
|
905
|
-
|
906
|
-
};
|
907
|
-
goog.inherits(soydata.SanitizedHtml, soydata.SanitizedContent);
|
908
|
-
|
909
|
-
/** @override */
|
910
|
-
soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML;
|
1447
|
+
soydata.VERY_UNSAFE.ordainSanitizedHtml =
|
1448
|
+
soydata.$$makeSanitizedContentFactory_(soydata.SanitizedHtml);
|
911
1449
|
|
912
1450
|
|
913
1451
|
/**
|
914
|
-
*
|
915
|
-
*
|
916
|
-
*
|
917
|
-
*
|
918
|
-
* any
|
919
|
-
* @
|
920
|
-
*
|
1452
|
+
* Takes a leap of faith that the provided content is "safe" (non-attacker-
|
1453
|
+
* controlled, XSS-free) Javascript.
|
1454
|
+
*
|
1455
|
+
* @param {*} content Javascript source that when evaluated does not
|
1456
|
+
* execute any attacker-controlled scripts.
|
1457
|
+
* @return {!soydata.SanitizedJs} Sanitized content wrapper that indicates to
|
1458
|
+
* Soy not to escape when printed as Javascript source.
|
921
1459
|
*/
|
922
|
-
soydata.
|
923
|
-
|
924
|
-
|
925
|
-
goog.inherits(soydata.SanitizedJsStrChars, soydata.SanitizedContent);
|
1460
|
+
soydata.VERY_UNSAFE.ordainSanitizedJs =
|
1461
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
1462
|
+
soydata.SanitizedJs);
|
926
1463
|
|
927
|
-
|
928
|
-
|
929
|
-
|
1464
|
+
|
1465
|
+
// TODO: This function is probably necessary, either externally or internally
|
1466
|
+
// as an implementation detail. Generally, plain text will always work here,
|
1467
|
+
// as there's no harm to unescaping the string and then re-escaping when
|
1468
|
+
// finally printed.
|
1469
|
+
/**
|
1470
|
+
* Takes a leap of faith that the provided content can be safely embedded in
|
1471
|
+
* a Javascript string without re-esacping.
|
1472
|
+
*
|
1473
|
+
* @param {*} content Content that can be safely inserted as part of a
|
1474
|
+
* single- or double-quoted string without terminating the string.
|
1475
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1476
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1477
|
+
* @return {!soydata.SanitizedJsStrChars} Sanitized content wrapper that
|
1478
|
+
* indicates to Soy not to escape when printed in a JS string.
|
1479
|
+
*/
|
1480
|
+
soydata.VERY_UNSAFE.ordainSanitizedJsStrChars =
|
1481
|
+
soydata.$$makeSanitizedContentFactory_(soydata.SanitizedJsStrChars);
|
930
1482
|
|
931
1483
|
|
932
1484
|
/**
|
933
|
-
*
|
934
|
-
*
|
1485
|
+
* Takes a leap of faith that the provided content is "safe" to use as a URI
|
1486
|
+
* in a Soy template.
|
1487
|
+
*
|
1488
|
+
* This creates a Soy SanitizedContent object which indicates to Soy there is
|
1489
|
+
* no need to escape it when printed as a URI (e.g. in an href or src
|
1490
|
+
* attribute), such as if it's already been encoded or if it's a Javascript:
|
1491
|
+
* URI.
|
1492
|
+
*
|
1493
|
+
* @param {*} content A chunk of URI that the caller knows is safe to
|
935
1494
|
* emit in a template.
|
936
|
-
* @
|
937
|
-
*
|
1495
|
+
* @return {!soydata.SanitizedUri} Sanitized content wrapper that indicates to
|
1496
|
+
* Soy not to escape or filter when printed in URI context.
|
938
1497
|
*/
|
939
|
-
soydata.
|
940
|
-
|
941
|
-
|
942
|
-
goog.inherits(soydata.SanitizedUri, soydata.SanitizedContent);
|
943
|
-
|
944
|
-
/** @override */
|
945
|
-
soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI;
|
1498
|
+
soydata.VERY_UNSAFE.ordainSanitizedUri =
|
1499
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
1500
|
+
soydata.SanitizedUri);
|
946
1501
|
|
947
1502
|
|
948
1503
|
/**
|
949
|
-
*
|
950
|
-
*
|
1504
|
+
* Takes a leap of faith that the provided content is "safe" to use as an
|
1505
|
+
* HTML attribute.
|
1506
|
+
*
|
1507
|
+
* @param {*} content An attribute name and value, such as
|
951
1508
|
* {@code dir="ltr"}.
|
952
|
-
* @
|
953
|
-
*
|
1509
|
+
* @return {!soydata.SanitizedHtmlAttribute} Sanitized content wrapper that
|
1510
|
+
* indicates to Soy not to escape when printed as an HTML attribute.
|
954
1511
|
*/
|
955
|
-
soydata.
|
956
|
-
|
957
|
-
|
958
|
-
goog.inherits(soydata.SanitizedHtmlAttribute, soydata.SanitizedContent);
|
1512
|
+
soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute =
|
1513
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
1514
|
+
soydata.SanitizedHtmlAttribute);
|
959
1515
|
|
960
|
-
|
961
|
-
|
962
|
-
|
1516
|
+
|
1517
|
+
/**
|
1518
|
+
* Takes a leap of faith that the provided content is "safe" to use as CSS
|
1519
|
+
* in a style attribute or block.
|
1520
|
+
*
|
1521
|
+
* @param {*} content CSS, such as {@code color:#c3d9ff}.
|
1522
|
+
* @return {!soydata.SanitizedCss} Sanitized CSS wrapper that indicates to
|
1523
|
+
* Soy there is no need to escape or filter when printed in CSS context.
|
1524
|
+
*/
|
1525
|
+
soydata.VERY_UNSAFE.ordainSanitizedCss =
|
1526
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
|
1527
|
+
soydata.SanitizedCss);
|
963
1528
|
|
964
1529
|
|
965
1530
|
// -----------------------------------------------------------------------------
|
@@ -1036,32 +1601,54 @@ soy.renderAsElement = function(
|
|
1036
1601
|
|
1037
1602
|
|
1038
1603
|
/**
|
1039
|
-
*
|
1040
|
-
*
|
1041
|
-
*
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1604
|
+
* Whether the locale is right-to-left.
|
1605
|
+
*
|
1606
|
+
* @type {boolean}
|
1607
|
+
*/
|
1608
|
+
soy.$$IS_LOCALE_RTL = goog.i18n.bidi.IS_RTL;
|
1609
|
+
|
1610
|
+
|
1611
|
+
/**
|
1612
|
+
* Builds an augmented map. The returned map will contain mappings from both
|
1613
|
+
* the base map and the additional map. If the same key appears in both, then
|
1614
|
+
* the value from the additional map will be visible, while the value from the
|
1615
|
+
* base map will be hidden. The base map will be used, but not modified.
|
1045
1616
|
*
|
1046
|
-
* @param {!Object}
|
1047
|
-
* @param {Object}
|
1048
|
-
* @return {Object} An augmented
|
1049
|
-
*
|
1617
|
+
* @param {!Object} baseMap The original map to augment.
|
1618
|
+
* @param {!Object} additionalMap A map containing the additional mappings.
|
1619
|
+
* @return {!Object} An augmented map containing both the original and
|
1620
|
+
* additional mappings.
|
1050
1621
|
*/
|
1051
|
-
soy.$$
|
1622
|
+
soy.$$augmentMap = function(baseMap, additionalMap) {
|
1052
1623
|
|
1053
|
-
// Create a new
|
1624
|
+
// Create a new map whose '__proto__' field is set to baseMap.
|
1054
1625
|
/** @constructor */
|
1055
1626
|
function TempCtor() {}
|
1056
|
-
TempCtor.prototype =
|
1057
|
-
var
|
1627
|
+
TempCtor.prototype = baseMap;
|
1628
|
+
var augmentedMap = new TempCtor();
|
1058
1629
|
|
1059
|
-
// Add the additional
|
1060
|
-
for (var key in
|
1061
|
-
|
1630
|
+
// Add the additional mappings to the new map.
|
1631
|
+
for (var key in additionalMap) {
|
1632
|
+
augmentedMap[key] = additionalMap[key];
|
1062
1633
|
}
|
1063
1634
|
|
1064
|
-
return
|
1635
|
+
return augmentedMap;
|
1636
|
+
};
|
1637
|
+
|
1638
|
+
|
1639
|
+
/**
|
1640
|
+
* Checks that the given map key is a string.
|
1641
|
+
* @param {*} key Key to check.
|
1642
|
+
* @return {string} The given key.
|
1643
|
+
*/
|
1644
|
+
soy.$$checkMapKey = function(key) {
|
1645
|
+
// TODO: Support map literal with nonstring key.
|
1646
|
+
if ((typeof key) != 'string') {
|
1647
|
+
throw Error(
|
1648
|
+
'Map literal\'s key expression must evaluate to string' +
|
1649
|
+
' (encountered type "' + (typeof key) + '").');
|
1650
|
+
}
|
1651
|
+
return key;
|
1065
1652
|
};
|
1066
1653
|
|
1067
1654
|
|
@@ -1097,13 +1684,13 @@ soy.$$getMapKeys = function(map) {
|
|
1097
1684
|
*
|
1098
1685
|
* @consistentIdGenerator
|
1099
1686
|
*/
|
1100
|
-
soy.$$
|
1687
|
+
soy.$$getDelTemplateId = function(delTemplateName) {
|
1101
1688
|
return delTemplateName;
|
1102
1689
|
};
|
1103
1690
|
|
1104
1691
|
|
1105
1692
|
/**
|
1106
|
-
* Map from registered delegate template
|
1693
|
+
* Map from registered delegate template key to the priority of the
|
1107
1694
|
* implementation.
|
1108
1695
|
* @type {Object}
|
1109
1696
|
* @private
|
@@ -1111,7 +1698,7 @@ soy.$$getDelegateId = function(delTemplateName) {
|
|
1111
1698
|
soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {};
|
1112
1699
|
|
1113
1700
|
/**
|
1114
|
-
* Map from registered delegate template
|
1701
|
+
* Map from registered delegate template key to the implementation function.
|
1115
1702
|
* @type {Object}
|
1116
1703
|
* @private
|
1117
1704
|
*/
|
@@ -1119,17 +1706,21 @@ soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {};
|
|
1119
1706
|
|
1120
1707
|
|
1121
1708
|
/**
|
1122
|
-
* Registers a delegate implementation. If the same delegate template id
|
1123
|
-
* has been registered previously, then priority values are
|
1124
|
-
* the higher priority implementation is stored (if
|
1125
|
-
* error is thrown).
|
1709
|
+
* Registers a delegate implementation. If the same delegate template key (id
|
1710
|
+
* and variant) has been registered previously, then priority values are
|
1711
|
+
* compared and only the higher priority implementation is stored (if
|
1712
|
+
* priorities are equal, an error is thrown).
|
1126
1713
|
*
|
1127
|
-
* @param {string} delTemplateId The delegate template id
|
1714
|
+
* @param {string} delTemplateId The delegate template id.
|
1715
|
+
* @param {string} delTemplateVariant The delegate template variant (can be
|
1716
|
+
* empty string).
|
1128
1717
|
* @param {number} delPriority The implementation's priority value.
|
1129
1718
|
* @param {Function} delFn The implementation function.
|
1130
1719
|
*/
|
1131
|
-
soy.$$registerDelegateFn = function(
|
1132
|
-
|
1720
|
+
soy.$$registerDelegateFn = function(
|
1721
|
+
delTemplateId, delTemplateVariant, delPriority, delFn) {
|
1722
|
+
|
1723
|
+
var mapKey = 'key_' + delTemplateId + ':' + delTemplateVariant;
|
1133
1724
|
var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey];
|
1134
1725
|
if (currPriority === undefined || delPriority > currPriority) {
|
1135
1726
|
// Registering new or higher-priority function: replace registry entry.
|
@@ -1138,8 +1729,8 @@ soy.$$registerDelegateFn = function(delTemplateId, delPriority, delFn) {
|
|
1138
1729
|
} else if (delPriority == currPriority) {
|
1139
1730
|
// Registering same-priority function: error.
|
1140
1731
|
throw Error(
|
1141
|
-
'Encountered two active delegates with same priority (
|
1142
|
-
|
1732
|
+
'Encountered two active delegates with the same priority ("' +
|
1733
|
+
delTemplateId + ':' + delTemplateVariant + '").');
|
1143
1734
|
} else {
|
1144
1735
|
// Registering lower-priority function: do nothing.
|
1145
1736
|
}
|
@@ -1148,16 +1739,38 @@ soy.$$registerDelegateFn = function(delTemplateId, delPriority, delFn) {
|
|
1148
1739
|
|
1149
1740
|
/**
|
1150
1741
|
* Retrieves the (highest-priority) implementation that has been registered for
|
1151
|
-
* a given delegate template id
|
1152
|
-
* for the
|
1153
|
-
*
|
1742
|
+
* a given delegate template key (id and variant). If no implementation has
|
1743
|
+
* been registered for the key, then the fallback is the same id with empty
|
1744
|
+
* variant. If the fallback is also not registered, and allowsEmptyDefault is
|
1745
|
+
* true, then returns an implementation that is equivalent to an empty template
|
1746
|
+
* (i.e. rendered output would be empty string).
|
1154
1747
|
*
|
1155
|
-
* @param {string} delTemplateId The delegate template id
|
1748
|
+
* @param {string} delTemplateId The delegate template id.
|
1749
|
+
* @param {string|number} delTemplateVariant The delegate template variant (can
|
1750
|
+
* be an empty string, or a number when a global is used).
|
1751
|
+
* @param {boolean} allowsEmptyDefault Whether to default to the empty template
|
1752
|
+
* function if there's no active implementation.
|
1156
1753
|
* @return {Function} The retrieved implementation function.
|
1157
1754
|
*/
|
1158
|
-
soy.$$getDelegateFn = function(
|
1159
|
-
|
1160
|
-
|
1755
|
+
soy.$$getDelegateFn = function(
|
1756
|
+
delTemplateId, delTemplateVariant, allowsEmptyDefault) {
|
1757
|
+
|
1758
|
+
var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_[
|
1759
|
+
'key_' + delTemplateId + ':' + delTemplateVariant];
|
1760
|
+
if (! delFn && delTemplateVariant != '') {
|
1761
|
+
// Fallback to empty variant.
|
1762
|
+
delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId + ':'];
|
1763
|
+
}
|
1764
|
+
|
1765
|
+
if (delFn) {
|
1766
|
+
return delFn;
|
1767
|
+
} else if (allowsEmptyDefault) {
|
1768
|
+
return soy.$$EMPTY_TEMPLATE_FN_;
|
1769
|
+
} else {
|
1770
|
+
throw Error(
|
1771
|
+
'Found no active impl for delegate call to "' + delTemplateId + ':' +
|
1772
|
+
delTemplateVariant + '" (and not allowemptydefault="true").');
|
1773
|
+
}
|
1161
1774
|
};
|
1162
1775
|
|
1163
1776
|
|
@@ -1176,26 +1789,220 @@ soy.$$EMPTY_TEMPLATE_FN_ = function(opt_data, opt_sb, opt_ijData) {
|
|
1176
1789
|
};
|
1177
1790
|
|
1178
1791
|
|
1792
|
+
// -----------------------------------------------------------------------------
|
1793
|
+
// Internal sanitized content wrappers.
|
1794
|
+
|
1795
|
+
|
1796
|
+
/**
|
1797
|
+
* Creates a SanitizedContent factory for SanitizedContent types for internal
|
1798
|
+
* Soy let and param blocks.
|
1799
|
+
*
|
1800
|
+
* This is a hack within Soy so that SanitizedContent objects created via let
|
1801
|
+
* and param blocks will truth-test as false if they are empty string.
|
1802
|
+
* Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
|
1803
|
+
* not possible, and changing the Soy compiler to wrap every boolean statement
|
1804
|
+
* for just this purpose is impractical. Instead, we just avoid wrapping empty
|
1805
|
+
* string as SanitizedContent, since it's a no-op for empty strings anyways.
|
1806
|
+
*
|
1807
|
+
* @param {function(new: T)} ctor A constructor.
|
1808
|
+
* @return {!function(*, ?goog.i18n.bidi.Dir=): (T|soydata.$$EMPTY_STRING_)}
|
1809
|
+
* A factory that takes content and an optional content direction and
|
1810
|
+
* returns a new instance, or an empty string. If the content direction is
|
1811
|
+
* undefined, ctor.prototype.contentDir is used.
|
1812
|
+
* @template T
|
1813
|
+
* @private
|
1814
|
+
*/
|
1815
|
+
soydata.$$makeSanitizedContentFactoryForInternalBlocks_ = function(ctor) {
|
1816
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
1817
|
+
function InstantiableCtor() {}
|
1818
|
+
InstantiableCtor.prototype = ctor.prototype;
|
1819
|
+
/**
|
1820
|
+
* Creates a ctor-type SanitizedContent instance.
|
1821
|
+
*
|
1822
|
+
* @param {*} content The content to put in the instance.
|
1823
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
|
1824
|
+
* undefined, ctor.prototype.contentDir is used.
|
1825
|
+
* @return {goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
|
1826
|
+
* instance, or an empty string. A new instance is actually of type T
|
1827
|
+
* above (ctor's type, a descendant of SanitizedContent), but there's no
|
1828
|
+
* way to express that here.
|
1829
|
+
*/
|
1830
|
+
function sanitizedContentFactory(content, opt_contentDir) {
|
1831
|
+
var contentString = String(content);
|
1832
|
+
if (!contentString) {
|
1833
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
1834
|
+
}
|
1835
|
+
var result = new InstantiableCtor();
|
1836
|
+
result.content = String(content);
|
1837
|
+
if (opt_contentDir !== undefined) {
|
1838
|
+
result.contentDir = opt_contentDir;
|
1839
|
+
}
|
1840
|
+
return result;
|
1841
|
+
}
|
1842
|
+
return sanitizedContentFactory;
|
1843
|
+
};
|
1844
|
+
|
1845
|
+
|
1846
|
+
/**
|
1847
|
+
* Creates a SanitizedContent factory for SanitizedContent types that should
|
1848
|
+
* always have their default directionality for internal Soy let and param
|
1849
|
+
* blocks.
|
1850
|
+
*
|
1851
|
+
* This is a hack within Soy so that SanitizedContent objects created via let
|
1852
|
+
* and param blocks will truth-test as false if they are empty string.
|
1853
|
+
* Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
|
1854
|
+
* not possible, and changing the Soy compiler to wrap every boolean statement
|
1855
|
+
* for just this purpose is impractical. Instead, we just avoid wrapping empty
|
1856
|
+
* string as SanitizedContent, since it's a no-op for empty strings anyways.
|
1857
|
+
*
|
1858
|
+
* @param {function(new: T)} ctor A constructor.
|
1859
|
+
* @return {!function(*): (T|soydata.$$EMPTY_STRING_)} A
|
1860
|
+
* factory that takes content and returns a
|
1861
|
+
* new instance (with default directionality, i.e.
|
1862
|
+
* ctor.prototype.contentDir), or an empty string.
|
1863
|
+
* @template T
|
1864
|
+
* @private
|
1865
|
+
*/
|
1866
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_ =
|
1867
|
+
function(ctor) {
|
1868
|
+
/** @type {function(new: goog.soy.data.SanitizedContent)} */
|
1869
|
+
function InstantiableCtor() {}
|
1870
|
+
InstantiableCtor.prototype = ctor.prototype;
|
1871
|
+
/**
|
1872
|
+
* Creates a ctor-type SanitizedContent instance.
|
1873
|
+
*
|
1874
|
+
* @param {*} content The content to put in the instance.
|
1875
|
+
* @return {goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
|
1876
|
+
* instance, or an empty string. A new instance is actually of type T
|
1877
|
+
* above (ctor's type, a descendant of SanitizedContent), but there's no
|
1878
|
+
* way to express that here.
|
1879
|
+
*/
|
1880
|
+
function sanitizedContentFactory(content) {
|
1881
|
+
var contentString = String(content);
|
1882
|
+
if (!contentString) {
|
1883
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
1884
|
+
}
|
1885
|
+
var result = new InstantiableCtor();
|
1886
|
+
result.content = String(content);
|
1887
|
+
return result;
|
1888
|
+
}
|
1889
|
+
return sanitizedContentFactory;
|
1890
|
+
};
|
1891
|
+
|
1892
|
+
|
1893
|
+
/**
|
1894
|
+
* Creates kind="text" block contents (internal use only).
|
1895
|
+
*
|
1896
|
+
* @param {*} content Text.
|
1897
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1898
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1899
|
+
* @return {!soydata.UnsanitizedText|soydata.$$EMPTY_STRING_} Wrapped result.
|
1900
|
+
*/
|
1901
|
+
soydata.$$markUnsanitizedTextForInternalBlocks = function(
|
1902
|
+
content, opt_contentDir) {
|
1903
|
+
var contentString = String(content);
|
1904
|
+
if (!contentString) {
|
1905
|
+
return soydata.$$EMPTY_STRING_.VALUE;
|
1906
|
+
}
|
1907
|
+
return new soydata.UnsanitizedText(contentString, opt_contentDir);
|
1908
|
+
};
|
1909
|
+
|
1910
|
+
|
1911
|
+
/**
|
1912
|
+
* Creates kind="html" block contents (internal use only).
|
1913
|
+
*
|
1914
|
+
* @param {*} content Text.
|
1915
|
+
* @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
|
1916
|
+
* unknown and thus to be estimated when necessary. Default: null.
|
1917
|
+
* @return {soydata.SanitizedHtml|soydata.$$EMPTY_STRING_} Wrapped result.
|
1918
|
+
*/
|
1919
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedHtmlForInternalBlocks =
|
1920
|
+
soydata.$$makeSanitizedContentFactoryForInternalBlocks_(
|
1921
|
+
soydata.SanitizedHtml);
|
1922
|
+
|
1923
|
+
|
1924
|
+
/**
|
1925
|
+
* Creates kind="js" block contents (internal use only).
|
1926
|
+
*
|
1927
|
+
* @param {*} content Text.
|
1928
|
+
* @return {soydata.SanitizedJs|soydata.$$EMPTY_STRING_} Wrapped result.
|
1929
|
+
*/
|
1930
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedJsForInternalBlocks =
|
1931
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
1932
|
+
soydata.SanitizedJs);
|
1933
|
+
|
1934
|
+
|
1935
|
+
/**
|
1936
|
+
* Creates kind="uri" block contents (internal use only).
|
1937
|
+
*
|
1938
|
+
* @param {*} content Text.
|
1939
|
+
* @return {soydata.SanitizedUri|soydata.$$EMPTY_STRING_} Wrapped result.
|
1940
|
+
*/
|
1941
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedUriForInternalBlocks =
|
1942
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
1943
|
+
soydata.SanitizedUri);
|
1944
|
+
|
1945
|
+
|
1946
|
+
/**
|
1947
|
+
* Creates kind="attributes" block contents (internal use only).
|
1948
|
+
*
|
1949
|
+
* @param {*} content Text.
|
1950
|
+
* @return {soydata.SanitizedHtmlAttribute|soydata.$$EMPTY_STRING_} Wrapped
|
1951
|
+
* result.
|
1952
|
+
*/
|
1953
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedAttributesForInternalBlocks =
|
1954
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
1955
|
+
soydata.SanitizedHtmlAttribute);
|
1956
|
+
|
1957
|
+
|
1958
|
+
/**
|
1959
|
+
* Creates kind="css" block contents (internal use only).
|
1960
|
+
*
|
1961
|
+
* @param {*} content Text.
|
1962
|
+
* @return {soydata.SanitizedCss|soydata.$$EMPTY_STRING_} Wrapped result.
|
1963
|
+
*/
|
1964
|
+
soydata.VERY_UNSAFE.$$ordainSanitizedCssForInternalBlocks =
|
1965
|
+
soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
|
1966
|
+
soydata.SanitizedCss);
|
1967
|
+
|
1968
|
+
|
1179
1969
|
// -----------------------------------------------------------------------------
|
1180
1970
|
// Escape/filter/normalize.
|
1181
1971
|
|
1182
1972
|
|
1183
1973
|
/**
|
1184
|
-
*
|
1185
|
-
*
|
1186
|
-
* tag attribute value within double quotes.
|
1187
|
-
* Will emit known safe HTML as-is.
|
1974
|
+
* Returns a SanitizedHtml object for a particular value. The content direction
|
1975
|
+
* is preserved.
|
1188
1976
|
*
|
1189
|
-
*
|
1190
|
-
*
|
1191
|
-
*
|
1977
|
+
* This HTML-escapes the value unless it is already SanitizedHtml. Escapes
|
1978
|
+
* double quote '"' in addition to '&', '<', and '>' so that a string can be
|
1979
|
+
* included in an HTML tag attribute value within double quotes.
|
1980
|
+
*
|
1981
|
+
* @param {*} value The value to convert. If it is already a SanitizedHtml
|
1982
|
+
* object, it is left alone.
|
1983
|
+
* @return {!soydata.SanitizedHtml} An escaped version of value.
|
1192
1984
|
*/
|
1193
1985
|
soy.$$escapeHtml = function(value) {
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1986
|
+
return soydata.SanitizedHtml.from(value);
|
1987
|
+
};
|
1988
|
+
|
1989
|
+
|
1990
|
+
/**
|
1991
|
+
* Strips unsafe tags to convert a string of untrusted HTML into HTML that
|
1992
|
+
* is safe to embed. The content direction is preserved.
|
1993
|
+
*
|
1994
|
+
* @param {*} value The string-like value to be escaped. May not be a string,
|
1995
|
+
* but the value will be coerced to a string.
|
1996
|
+
* @return {!soydata.SanitizedHtml} A sanitized and normalized version of value.
|
1997
|
+
*/
|
1998
|
+
soy.$$cleanHtml = function(value) {
|
1999
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2000
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
2001
|
+
return /** @type {!soydata.SanitizedHtml} */ (value);
|
1197
2002
|
}
|
1198
|
-
return
|
2003
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
2004
|
+
soy.$$stripHtmlTags(value, soy.esc.$$SAFE_TAG_WHITELIST_),
|
2005
|
+
soydata.getContentDir(value));
|
1199
2006
|
};
|
1200
2007
|
|
1201
2008
|
|
@@ -1204,7 +2011,7 @@ soy.$$escapeHtml = function(value) {
|
|
1204
2011
|
* RCDATA.
|
1205
2012
|
* <p>
|
1206
2013
|
* Escapes HTML special characters so that the value will not prematurely end
|
1207
|
-
* the body of a tag like {@code <textarea>} or {@code <title>}.
|
2014
|
+
* the body of a tag like {@code <textarea>} or {@code <title>}. RCDATA tags
|
1208
2015
|
* cannot contain other HTML entities, so it is not strictly necessary to escape
|
1209
2016
|
* HTML special characters except when part of that text looks like an HTML
|
1210
2017
|
* entity or like a close tag : {@code </textarea>}.
|
@@ -1213,13 +2020,13 @@ soy.$$escapeHtml = function(value) {
|
|
1213
2020
|
* contain an innocuous {@code </textarea>} don't prematurely end an RCDATA
|
1214
2021
|
* element.
|
1215
2022
|
*
|
1216
|
-
* @param {*} value The string-like value to be escaped.
|
2023
|
+
* @param {*} value The string-like value to be escaped. May not be a string,
|
1217
2024
|
* but the value will be coerced to a string.
|
1218
2025
|
* @return {string} An escaped version of value.
|
1219
2026
|
*/
|
1220
2027
|
soy.$$escapeHtmlRcdata = function(value) {
|
1221
|
-
if (
|
1222
|
-
|
2028
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2029
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
1223
2030
|
return soy.esc.$$normalizeHtmlHelper(value.content);
|
1224
2031
|
}
|
1225
2032
|
return soy.esc.$$escapeHtmlHelper(value);
|
@@ -1227,29 +2034,133 @@ soy.$$escapeHtmlRcdata = function(value) {
|
|
1227
2034
|
|
1228
2035
|
|
1229
2036
|
/**
|
1230
|
-
*
|
1231
|
-
*
|
2037
|
+
* Matches any/only HTML5 void elements' start tags.
|
2038
|
+
* See http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
|
2039
|
+
* @type {RegExp}
|
2040
|
+
* @private
|
2041
|
+
*/
|
2042
|
+
soy.$$HTML5_VOID_ELEMENTS_ = new RegExp(
|
2043
|
+
'^<(?:area|base|br|col|command|embed|hr|img|input' +
|
2044
|
+
'|keygen|link|meta|param|source|track|wbr)\\b');
|
2045
|
+
|
2046
|
+
|
2047
|
+
/**
|
2048
|
+
* Removes HTML tags from a string of known safe HTML.
|
2049
|
+
* If opt_tagWhitelist is not specified or is empty, then
|
2050
|
+
* the result can be used as an attribute value.
|
1232
2051
|
*
|
1233
|
-
* @param {*} value The HTML to be escaped.
|
2052
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
1234
2053
|
* value will be coerced to a string.
|
1235
|
-
* @
|
1236
|
-
*
|
2054
|
+
* @param {Object.<string, number>=} opt_tagWhitelist Has an own property whose
|
2055
|
+
* name is a lower-case tag name and whose value is {@code 1} for
|
2056
|
+
* each element that is allowed in the output.
|
2057
|
+
* @return {string} A representation of value without disallowed tags,
|
2058
|
+
* HTML comments, or other non-text content.
|
2059
|
+
*/
|
2060
|
+
soy.$$stripHtmlTags = function(value, opt_tagWhitelist) {
|
2061
|
+
if (!opt_tagWhitelist) {
|
2062
|
+
// If we have no white-list, then use a fast track which elides all tags.
|
2063
|
+
return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '')
|
2064
|
+
// This is just paranoia since callers should normalize the result
|
2065
|
+
// anyway, but if they didn't, it would be necessary to ensure that
|
2066
|
+
// after the first replace non-tag uses of < do not recombine into
|
2067
|
+
// tags as in "<<foo>script>alert(1337)</<foo>script>".
|
2068
|
+
.replace(soy.esc.$$LT_REGEX_, '<');
|
2069
|
+
}
|
2070
|
+
|
2071
|
+
// Escapes '[' so that we can use [123] below to mark places where tags
|
2072
|
+
// have been removed.
|
2073
|
+
var html = String(value).replace(/\[/g, '[');
|
2074
|
+
|
2075
|
+
// Consider all uses of '<' and replace whitelisted tags with markers like
|
2076
|
+
// [1] which are indices into a list of approved tag names.
|
2077
|
+
// Replace all other uses of < and > with entities.
|
2078
|
+
var tags = [];
|
2079
|
+
html = html.replace(
|
2080
|
+
soy.esc.$$HTML_TAG_REGEX_,
|
2081
|
+
function(tok, tagName) {
|
2082
|
+
if (tagName) {
|
2083
|
+
tagName = tagName.toLowerCase();
|
2084
|
+
if (opt_tagWhitelist.hasOwnProperty(tagName) &&
|
2085
|
+
opt_tagWhitelist[tagName]) {
|
2086
|
+
var start = tok.charAt(1) === '/' ? '</' : '<';
|
2087
|
+
var index = tags.length;
|
2088
|
+
tags[index] = start + tagName + '>';
|
2089
|
+
return '[' + index + ']';
|
2090
|
+
}
|
2091
|
+
}
|
2092
|
+
return '';
|
2093
|
+
});
|
2094
|
+
|
2095
|
+
// Escape HTML special characters. Now there are no '<' in html that could
|
2096
|
+
// start a tag.
|
2097
|
+
html = soy.esc.$$normalizeHtmlHelper(html);
|
2098
|
+
|
2099
|
+
var finalCloseTags = soy.$$balanceTags_(tags);
|
2100
|
+
|
2101
|
+
// Now html contains no tags or less-than characters that could become
|
2102
|
+
// part of a tag via a replacement operation and tags only contains
|
2103
|
+
// approved tags.
|
2104
|
+
// Reinsert the white-listed tags.
|
2105
|
+
html = html.replace(
|
2106
|
+
/\[(\d+)\]/g, function(_, index) { return tags[index]; });
|
2107
|
+
|
2108
|
+
// Close any still open tags.
|
2109
|
+
// This prevents unclosed formatting elements like <ol> and <table> from
|
2110
|
+
// breaking the layout of containing HTML.
|
2111
|
+
return html + finalCloseTags;
|
2112
|
+
};
|
2113
|
+
|
2114
|
+
|
2115
|
+
/**
|
2116
|
+
* Throw out any close tags that don't correspond to start tags.
|
2117
|
+
* If {@code <table>} is used for formatting, embedded HTML shouldn't be able
|
2118
|
+
* to use a mismatched {@code </table>} to break page layout.
|
2119
|
+
*
|
2120
|
+
* @param {Array.<string>} tags an array of tags that will be modified in place
|
2121
|
+
* include tags, the empty string, or concatenations of empty tags.
|
2122
|
+
* @return {string} zero or more closed tags that close all elements that are
|
2123
|
+
* opened in tags but not closed.
|
2124
|
+
* @private
|
1237
2125
|
*/
|
1238
|
-
soy.$$
|
1239
|
-
|
2126
|
+
soy.$$balanceTags_ = function(tags) {
|
2127
|
+
var open = [];
|
2128
|
+
for (var i = 0, n = tags.length; i < n; ++i) {
|
2129
|
+
var tag = tags[i];
|
2130
|
+
if (tag.charAt(1) === '/') {
|
2131
|
+
var openTagIndex = open.length - 1;
|
2132
|
+
// NOTE: This is essentially lastIndexOf, but it's not supported in IE.
|
2133
|
+
while (openTagIndex >= 0 && open[openTagIndex] != tag) {
|
2134
|
+
openTagIndex--;
|
2135
|
+
}
|
2136
|
+
if (openTagIndex < 0) {
|
2137
|
+
tags[i] = ''; // Drop close tag.
|
2138
|
+
} else {
|
2139
|
+
tags[i] = open.slice(openTagIndex).reverse().join('');
|
2140
|
+
open.length = openTagIndex;
|
2141
|
+
}
|
2142
|
+
} else if (!soy.$$HTML5_VOID_ELEMENTS_.test(tag)) {
|
2143
|
+
open.push('</' + tag.substring(1));
|
2144
|
+
}
|
2145
|
+
}
|
2146
|
+
return open.reverse().join('');
|
1240
2147
|
};
|
1241
2148
|
|
1242
2149
|
|
1243
2150
|
/**
|
1244
2151
|
* Escapes HTML special characters in an HTML attribute value.
|
1245
2152
|
*
|
1246
|
-
* @param {*} value The HTML to be escaped.
|
2153
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
1247
2154
|
* value will be coerced to a string.
|
1248
2155
|
* @return {string} An escaped version of value.
|
1249
2156
|
*/
|
1250
2157
|
soy.$$escapeHtmlAttribute = function(value) {
|
1251
|
-
|
1252
|
-
|
2158
|
+
// NOTE: We don't accept ATTRIBUTES here because ATTRIBUTES is actually not
|
2159
|
+
// the attribute value context, but instead k/v pairs.
|
2160
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2161
|
+
// NOTE: After removing tags, we also escape quotes ("normalize") so that
|
2162
|
+
// the HTML can be embedded in attribute context.
|
2163
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
1253
2164
|
return soy.esc.$$normalizeHtmlHelper(soy.$$stripHtmlTags(value.content));
|
1254
2165
|
}
|
1255
2166
|
return soy.esc.$$escapeHtmlHelper(value);
|
@@ -1260,13 +2171,13 @@ soy.$$escapeHtmlAttribute = function(value) {
|
|
1260
2171
|
* Escapes HTML special characters in a string including space and other
|
1261
2172
|
* characters that can end an unquoted HTML attribute value.
|
1262
2173
|
*
|
1263
|
-
* @param {*} value The HTML to be escaped.
|
2174
|
+
* @param {*} value The HTML to be escaped. May not be a string, but the
|
1264
2175
|
* value will be coerced to a string.
|
1265
2176
|
* @return {string} An escaped version of value.
|
1266
2177
|
*/
|
1267
2178
|
soy.$$escapeHtmlAttributeNospace = function(value) {
|
1268
|
-
if (
|
1269
|
-
|
2179
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2180
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtml);
|
1270
2181
|
return soy.esc.$$normalizeHtmlNospaceHelper(
|
1271
2182
|
soy.$$stripHtmlTags(value.content));
|
1272
2183
|
}
|
@@ -1277,29 +2188,46 @@ soy.$$escapeHtmlAttributeNospace = function(value) {
|
|
1277
2188
|
/**
|
1278
2189
|
* Filters out strings that cannot be a substring of a valid HTML attribute.
|
1279
2190
|
*
|
1280
|
-
*
|
2191
|
+
* Note the input is expected to be key=value pairs.
|
2192
|
+
*
|
2193
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1281
2194
|
* will be coerced to a string.
|
1282
2195
|
* @return {string} A valid HTML attribute name part or name/value pair.
|
1283
2196
|
* {@code "zSoyz"} if the input is invalid.
|
1284
2197
|
*/
|
1285
|
-
soy.$$
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
2198
|
+
soy.$$filterHtmlAttributes = function(value) {
|
2199
|
+
// NOTE: Explicitly no support for SanitizedContentKind.HTML, since that is
|
2200
|
+
// meaningless in this context, which is generally *between* html attributes.
|
2201
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.ATTRIBUTES)) {
|
2202
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedHtmlAttribute);
|
2203
|
+
// Add a space at the end to ensure this won't get merged into following
|
2204
|
+
// attributes, unless the interpretation is unambiguous (ending with quotes
|
2205
|
+
// or a space).
|
2206
|
+
return value.content.replace(/([^"'\s])$/, '$1 ');
|
1289
2207
|
}
|
1290
|
-
|
2208
|
+
// TODO: Dynamically inserting attributes that aren't marked as trusted is
|
2209
|
+
// probably unnecessary. Any filtering done here will either be inadequate
|
2210
|
+
// for security or not flexible enough. Having clients use kind="attributes"
|
2211
|
+
// in parameters seems like a wiser idea.
|
2212
|
+
return soy.esc.$$filterHtmlAttributesHelper(value);
|
1291
2213
|
};
|
1292
2214
|
|
1293
2215
|
|
1294
2216
|
/**
|
1295
2217
|
* Filters out strings that cannot be a substring of a valid HTML element name.
|
1296
2218
|
*
|
1297
|
-
* @param {*} value The value to escape.
|
2219
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1298
2220
|
* will be coerced to a string.
|
1299
2221
|
* @return {string} A valid HTML element name part.
|
1300
2222
|
* {@code "zSoyz"} if the input is invalid.
|
1301
2223
|
*/
|
1302
2224
|
soy.$$filterHtmlElementName = function(value) {
|
2225
|
+
// NOTE: We don't accept any SanitizedContent here. HTML indicates valid
|
2226
|
+
// PCDATA, not tag names. A sloppy developer shouldn't be able to cause an
|
2227
|
+
// exploit:
|
2228
|
+
// ... {let userInput}script src=http://evil.com/evil.js{/let} ...
|
2229
|
+
// ... {param tagName kind="html"}{$userInput}{/param} ...
|
2230
|
+
// ... <{$tagName}>Hello World</{$tagName}>
|
1303
2231
|
return soy.esc.$$filterHtmlElementNameHelper(value);
|
1304
2232
|
};
|
1305
2233
|
|
@@ -1308,7 +2236,7 @@ soy.$$filterHtmlElementName = function(value) {
|
|
1308
2236
|
* Escapes characters in the value to make it valid content for a JS string
|
1309
2237
|
* literal.
|
1310
2238
|
*
|
1311
|
-
* @param {*} value The value to escape.
|
2239
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1312
2240
|
* will be coerced to a string.
|
1313
2241
|
* @return {string} An escaped version of value.
|
1314
2242
|
* @deprecated
|
@@ -1322,13 +2250,15 @@ soy.$$escapeJs = function(value) {
|
|
1322
2250
|
* Escapes characters in the value to make it valid content for a JS string
|
1323
2251
|
* literal.
|
1324
2252
|
*
|
1325
|
-
* @param {*} value The value to escape.
|
2253
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1326
2254
|
* will be coerced to a string.
|
1327
2255
|
* @return {string} An escaped version of value.
|
1328
2256
|
*/
|
1329
2257
|
soy.$$escapeJsString = function(value) {
|
1330
|
-
if (
|
1331
|
-
|
2258
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS_STR_CHARS)) {
|
2259
|
+
// TODO: It might still be worthwhile to normalize it to remove
|
2260
|
+
// unescaped quotes, null, etc: replace(/(?:^|[^\])['"]/g, '\\$
|
2261
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedJsStrChars);
|
1332
2262
|
return value.content;
|
1333
2263
|
}
|
1334
2264
|
return soy.esc.$$escapeJsStringHelper(value);
|
@@ -1338,7 +2268,7 @@ soy.$$escapeJsString = function(value) {
|
|
1338
2268
|
/**
|
1339
2269
|
* Encodes a value as a JavaScript literal.
|
1340
2270
|
*
|
1341
|
-
* @param {*} value The value to escape.
|
2271
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1342
2272
|
* will be coerced to a string.
|
1343
2273
|
* @return {string} A JavaScript code representation of the input.
|
1344
2274
|
*/
|
@@ -1353,6 +2283,10 @@ soy.$$escapeJsValue = function(value) {
|
|
1353
2283
|
// distinct undefined value.
|
1354
2284
|
return ' null ';
|
1355
2285
|
}
|
2286
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS)) {
|
2287
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedJs);
|
2288
|
+
return value.content;
|
2289
|
+
}
|
1356
2290
|
switch (typeof value) {
|
1357
2291
|
case 'boolean': case 'number':
|
1358
2292
|
return ' ' + value + ' ';
|
@@ -1366,7 +2300,7 @@ soy.$$escapeJsValue = function(value) {
|
|
1366
2300
|
* Escapes characters in the string to make it valid content for a JS regular
|
1367
2301
|
* expression literal.
|
1368
2302
|
*
|
1369
|
-
* @param {*} value The value to escape.
|
2303
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1370
2304
|
* will be coerced to a string.
|
1371
2305
|
* @return {string} An escaped version of value.
|
1372
2306
|
*/
|
@@ -1400,13 +2334,13 @@ soy.$$pctEncode_ = function(ch) {
|
|
1400
2334
|
/**
|
1401
2335
|
* Escapes a string so that it can be safely included in a URI.
|
1402
2336
|
*
|
1403
|
-
* @param {*} value The value to escape.
|
2337
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1404
2338
|
* will be coerced to a string.
|
1405
2339
|
* @return {string} An escaped version of value.
|
1406
2340
|
*/
|
1407
2341
|
soy.$$escapeUri = function(value) {
|
1408
|
-
if (
|
1409
|
-
|
2342
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) {
|
2343
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedUri);
|
1410
2344
|
return soy.$$normalizeUri(value);
|
1411
2345
|
}
|
1412
2346
|
// Apostophes and parentheses are not matched by encodeURIComponent.
|
@@ -1425,7 +2359,7 @@ soy.$$escapeUri = function(value) {
|
|
1425
2359
|
/**
|
1426
2360
|
* Removes rough edges from a URI by escaping any raw HTML/JS string delimiters.
|
1427
2361
|
*
|
1428
|
-
* @param {*} value The value to escape.
|
2362
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1429
2363
|
* will be coerced to a string.
|
1430
2364
|
* @return {string} An escaped version of value.
|
1431
2365
|
*/
|
@@ -1438,19 +2372,37 @@ soy.$$normalizeUri = function(value) {
|
|
1438
2372
|
* Vets a URI's protocol and removes rough edges from a URI by escaping
|
1439
2373
|
* any raw HTML/JS string delimiters.
|
1440
2374
|
*
|
1441
|
-
* @param {*} value The value to escape.
|
2375
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1442
2376
|
* will be coerced to a string.
|
1443
2377
|
* @return {string} An escaped version of value.
|
1444
2378
|
*/
|
1445
2379
|
soy.$$filterNormalizeUri = function(value) {
|
2380
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) {
|
2381
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedUri);
|
2382
|
+
return soy.$$normalizeUri(value);
|
2383
|
+
}
|
1446
2384
|
return soy.esc.$$filterNormalizeUriHelper(value);
|
1447
2385
|
};
|
1448
2386
|
|
1449
2387
|
|
2388
|
+
/**
|
2389
|
+
* Allows only data-protocol image URI's.
|
2390
|
+
*
|
2391
|
+
* @param {*} value The value to process. May not be a string, but the value
|
2392
|
+
* will be coerced to a string.
|
2393
|
+
* @return {!soydata.SanitizedUri} An escaped version of value.
|
2394
|
+
*/
|
2395
|
+
soy.$$filterImageDataUri = function(value) {
|
2396
|
+
// NOTE: Even if it's a SanitizedUri, we will still filter it.
|
2397
|
+
return soydata.VERY_UNSAFE.ordainSanitizedUri(
|
2398
|
+
soy.esc.$$filterImageDataUriHelper(value));
|
2399
|
+
};
|
2400
|
+
|
2401
|
+
|
1450
2402
|
/**
|
1451
2403
|
* Escapes a string so it can safely be included inside a quoted CSS string.
|
1452
2404
|
*
|
1453
|
-
* @param {*} value The value to escape.
|
2405
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1454
2406
|
* will be coerced to a string.
|
1455
2407
|
* @return {string} An escaped version of value.
|
1456
2408
|
*/
|
@@ -1462,11 +2414,15 @@ soy.$$escapeCssString = function(value) {
|
|
1462
2414
|
/**
|
1463
2415
|
* Encodes a value as a CSS identifier part, keyword, or quantity.
|
1464
2416
|
*
|
1465
|
-
* @param {*} value The value to escape.
|
2417
|
+
* @param {*} value The value to escape. May not be a string, but the value
|
1466
2418
|
* will be coerced to a string.
|
1467
2419
|
* @return {string} A safe CSS identifier part, keyword, or quanitity.
|
1468
2420
|
*/
|
1469
2421
|
soy.$$filterCssValue = function(value) {
|
2422
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.CSS)) {
|
2423
|
+
goog.asserts.assert(value.constructor === soydata.SanitizedCss);
|
2424
|
+
return value.content;
|
2425
|
+
}
|
1470
2426
|
// Uses == to intentionally match null and undefined for Java compatibility.
|
1471
2427
|
if (value == null) {
|
1472
2428
|
return '';
|
@@ -1475,17 +2431,47 @@ soy.$$filterCssValue = function(value) {
|
|
1475
2431
|
};
|
1476
2432
|
|
1477
2433
|
|
2434
|
+
/**
|
2435
|
+
* Sanity-checks noAutoescape input for explicitly tainted content.
|
2436
|
+
*
|
2437
|
+
* SanitizedContentKind.TEXT is used to explicitly mark input that was never
|
2438
|
+
* meant to be used unescaped.
|
2439
|
+
*
|
2440
|
+
* @param {*} value The value to filter.
|
2441
|
+
* @return {*} The value, that we dearly hope will not cause an attack.
|
2442
|
+
*/
|
2443
|
+
soy.$$filterNoAutoescape = function(value) {
|
2444
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.TEXT)) {
|
2445
|
+
// Fail in development mode.
|
2446
|
+
goog.asserts.fail(
|
2447
|
+
'Tainted SanitizedContentKind.TEXT for |noAutoescape: `%s`',
|
2448
|
+
[value.content]);
|
2449
|
+
// Return innocuous data in production.
|
2450
|
+
return 'zSoyz';
|
2451
|
+
}
|
2452
|
+
|
2453
|
+
return value;
|
2454
|
+
};
|
2455
|
+
|
2456
|
+
|
1478
2457
|
// -----------------------------------------------------------------------------
|
1479
2458
|
// Basic directives/functions.
|
1480
2459
|
|
1481
2460
|
|
1482
2461
|
/**
|
1483
2462
|
* Converts \r\n, \r, and \n to <br>s
|
1484
|
-
* @param {*}
|
1485
|
-
* @return {string} A copy of {@code
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
2463
|
+
* @param {*} value The string in which to convert newlines.
|
2464
|
+
* @return {string|!soydata.SanitizedHtml} A copy of {@code value} with
|
2465
|
+
* converted newlines. If {@code value} is SanitizedHtml, the return value
|
2466
|
+
* is also SanitizedHtml, of the same known directionality.
|
2467
|
+
*/
|
2468
|
+
soy.$$changeNewlineToBr = function(value) {
|
2469
|
+
var result = goog.string.newLineToBr(String(value), false);
|
2470
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2471
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
2472
|
+
result, soydata.getContentDir(value));
|
2473
|
+
}
|
2474
|
+
return result;
|
1489
2475
|
};
|
1490
2476
|
|
1491
2477
|
|
@@ -1495,14 +2481,22 @@ soy.$$changeNewlineToBr = function(str) {
|
|
1495
2481
|
* HTML tags or entities. Entites count towards the character count; HTML tags
|
1496
2482
|
* do not.
|
1497
2483
|
*
|
1498
|
-
* @param {*}
|
2484
|
+
* @param {*} value The HTML string to insert word breaks into. Can be other
|
1499
2485
|
* types, but the value will be coerced to a string.
|
1500
2486
|
* @param {number} maxCharsBetweenWordBreaks Maximum number of non-space
|
1501
2487
|
* characters to allow before adding a word break.
|
1502
|
-
* @return {string} The string including word
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
2488
|
+
* @return {string|!soydata.SanitizedHtml} The string including word
|
2489
|
+
* breaks. If {@code value} is SanitizedHtml, the return value
|
2490
|
+
* is also SanitizedHtml, of the same known directionality.
|
2491
|
+
*/
|
2492
|
+
soy.$$insertWordBreaks = function(value, maxCharsBetweenWordBreaks) {
|
2493
|
+
var result = goog.format.insertWordBreaks(
|
2494
|
+
String(value), maxCharsBetweenWordBreaks);
|
2495
|
+
if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) {
|
2496
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(
|
2497
|
+
result, soydata.getContentDir(value));
|
2498
|
+
}
|
2499
|
+
return result;
|
1506
2500
|
};
|
1507
2501
|
|
1508
2502
|
|
@@ -1604,37 +2598,53 @@ soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) {
|
|
1604
2598
|
* Estimate the overall directionality of text. If opt_isHtml, makes sure to
|
1605
2599
|
* ignore the LTR nature of the mark-up and escapes in text, making the logic
|
1606
2600
|
* suitable for HTML and HTML-escaped text.
|
1607
|
-
*
|
2601
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
2602
|
+
* estimating the directionality.
|
2603
|
+
*
|
2604
|
+
* @param {*} text The content whose directionality is to be estimated.
|
1608
2605
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
1609
2606
|
* Default: false.
|
1610
2607
|
* @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral.
|
1611
2608
|
*/
|
1612
2609
|
soy.$$bidiTextDir = function(text, opt_isHtml) {
|
1613
|
-
|
1614
|
-
|
2610
|
+
var contentDir = soydata.getContentDir(text);
|
2611
|
+
if (contentDir != null) {
|
2612
|
+
return contentDir;
|
1615
2613
|
}
|
1616
|
-
|
2614
|
+
var isHtml = opt_isHtml ||
|
2615
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
2616
|
+
return goog.i18n.bidi.estimateDirection(text + '', isHtml);
|
1617
2617
|
};
|
1618
2618
|
|
1619
2619
|
|
1620
2620
|
/**
|
1621
|
-
* Returns
|
2621
|
+
* Returns 'dir="ltr"' or 'dir="rtl"', depending on text's estimated
|
1622
2622
|
* directionality, if it is not the same as bidiGlobalDir.
|
1623
2623
|
* Otherwise, returns the empty string.
|
1624
2624
|
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
1625
2625
|
* in text, making the logic suitable for HTML and HTML-escaped text.
|
2626
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
2627
|
+
* estimating the directionality.
|
2628
|
+
*
|
1626
2629
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
1627
2630
|
* if rtl, 0 if unknown.
|
1628
|
-
* @param {
|
2631
|
+
* @param {*} text The content whose directionality is to be estimated.
|
1629
2632
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
1630
2633
|
* Default: false.
|
1631
|
-
* @return {soydata.SanitizedHtmlAttribute}
|
1632
|
-
* context;
|
2634
|
+
* @return {soydata.SanitizedHtmlAttribute} 'dir="rtl"' for RTL text in non-RTL
|
2635
|
+
* context; 'dir="ltr"' for LTR text in non-LTR context;
|
1633
2636
|
* else, the empty string.
|
1634
2637
|
*/
|
1635
2638
|
soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
1636
|
-
|
1637
|
-
|
2639
|
+
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
2640
|
+
var contentDir = soydata.getContentDir(text);
|
2641
|
+
if (contentDir == null) {
|
2642
|
+
var isHtml = opt_isHtml ||
|
2643
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
2644
|
+
contentDir = goog.i18n.bidi.estimateDirection(text + '', isHtml);
|
2645
|
+
}
|
2646
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute(
|
2647
|
+
formatter.knownDirAttr(contentDir));
|
1638
2648
|
};
|
1639
2649
|
|
1640
2650
|
|
@@ -1644,9 +2654,12 @@ soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
|
1644
2654
|
* bidiGlobalDir. Otherwise returns the empty string.
|
1645
2655
|
* If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
|
1646
2656
|
* in text, making the logic suitable for HTML and HTML-escaped text.
|
2657
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
2658
|
+
* estimating the directionality.
|
2659
|
+
*
|
1647
2660
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
1648
2661
|
* if rtl, 0 if unknown.
|
1649
|
-
* @param {
|
2662
|
+
* @param {*} text The content whose directionality is to be estimated.
|
1650
2663
|
* @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
|
1651
2664
|
* Default: false.
|
1652
2665
|
* @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
|
@@ -1655,44 +2668,112 @@ soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
|
|
1655
2668
|
*/
|
1656
2669
|
soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
|
1657
2670
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
1658
|
-
|
2671
|
+
var isHtml = opt_isHtml ||
|
2672
|
+
soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
2673
|
+
return formatter.markAfterKnownDir(soydata.getContentDir(text), text + '',
|
2674
|
+
isHtml);
|
1659
2675
|
};
|
1660
2676
|
|
1661
2677
|
|
1662
2678
|
/**
|
1663
|
-
* Returns
|
1664
|
-
* but only if that is neither neutral nor the same as the
|
1665
|
-
* Otherwise, returns
|
1666
|
-
* Always treats
|
1667
|
-
* estimating
|
2679
|
+
* Returns text wrapped in a <span dir="ltr|rtl"> according to its
|
2680
|
+
* directionality - but only if that is neither neutral nor the same as the
|
2681
|
+
* global context. Otherwise, returns text unchanged.
|
2682
|
+
* Always treats text as HTML/HTML-escaped, i.e. ignores mark-up and escapes
|
2683
|
+
* when estimating text's directionality.
|
2684
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
2685
|
+
* estimating the directionality.
|
2686
|
+
*
|
1668
2687
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
1669
2688
|
* if rtl, 0 if unknown.
|
1670
|
-
* @param {*}
|
2689
|
+
* @param {*} text The string to be wrapped. Can be other types, but the value
|
1671
2690
|
* will be coerced to a string.
|
1672
|
-
* @return {string} The wrapped
|
2691
|
+
* @return {!goog.soy.data.SanitizedContent|string} The wrapped text.
|
1673
2692
|
*/
|
1674
|
-
soy.$$bidiSpanWrap = function(bidiGlobalDir,
|
2693
|
+
soy.$$bidiSpanWrap = function(bidiGlobalDir, text) {
|
1675
2694
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
1676
|
-
|
2695
|
+
|
2696
|
+
// We always treat the value as HTML, because span-wrapping is only useful
|
2697
|
+
// when its output will be treated as HTML (without escaping), and because
|
2698
|
+
// |bidiSpanWrap is not itself specified to do HTML escaping in Soy. (Both
|
2699
|
+
// explicit and automatic HTML escaping, if any, is done before calling
|
2700
|
+
// |bidiSpanWrap because the BidiSpanWrapDirective Java class implements
|
2701
|
+
// SanitizedContentOperator, but this does not mean that the input has to be
|
2702
|
+
// HTML SanitizedContent. In legacy usage, a string that is not
|
2703
|
+
// SanitizedContent is often printed in an autoescape="false" template or by
|
2704
|
+
// a print with a |noAutoescape, in which case our input is just SoyData.) If
|
2705
|
+
// the output will be treated as HTML, the input had better be safe
|
2706
|
+
// HTML/HTML-escaped (even if it isn't HTML SanitizedData), or we have an XSS
|
2707
|
+
// opportunity and a much bigger problem than bidi garbling.
|
2708
|
+
var wrappedText = formatter.spanWrapWithKnownDir(
|
2709
|
+
soydata.getContentDir(text), text + '', true /* opt_isHtml */);
|
2710
|
+
|
2711
|
+
// Like other directives whose Java class implements SanitizedContentOperator,
|
2712
|
+
// |bidiSpanWrap is called after the escaping (if any) has already been done,
|
2713
|
+
// and thus there is no need for it to produce actual SanitizedContent.
|
2714
|
+
return wrappedText;
|
1677
2715
|
};
|
1678
2716
|
|
1679
2717
|
|
1680
2718
|
/**
|
1681
|
-
* Returns
|
2719
|
+
* Returns text wrapped in Unicode BiDi formatting characters according to its
|
1682
2720
|
* directionality, i.e. either LRE or RLE at the beginning and PDF at the end -
|
1683
|
-
* but only if
|
1684
|
-
* global context. Otherwise, returns
|
1685
|
-
*
|
1686
|
-
* estimating
|
2721
|
+
* but only if text's directionality is neither neutral nor the same as the
|
2722
|
+
* global context. Otherwise, returns text unchanged.
|
2723
|
+
* Only treats soydata.SanitizedHtml as HTML/HTML-escaped, i.e. ignores mark-up
|
2724
|
+
* and escapes when estimating text's directionality.
|
2725
|
+
* If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
|
2726
|
+
* estimating the directionality.
|
2727
|
+
*
|
1687
2728
|
* @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
|
1688
2729
|
* if rtl, 0 if unknown.
|
1689
|
-
* @param {*}
|
2730
|
+
* @param {*} text The string to be wrapped. Can be other types, but the value
|
1690
2731
|
* will be coerced to a string.
|
1691
|
-
* @return {string} The wrapped string.
|
2732
|
+
* @return {!goog.soy.data.SanitizedContent|string} The wrapped string.
|
1692
2733
|
*/
|
1693
|
-
soy.$$bidiUnicodeWrap = function(bidiGlobalDir,
|
2734
|
+
soy.$$bidiUnicodeWrap = function(bidiGlobalDir, text) {
|
1694
2735
|
var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
|
1695
|
-
|
2736
|
+
|
2737
|
+
// We treat the value as HTML if and only if it says it's HTML, even though in
|
2738
|
+
// legacy usage, we sometimes have an HTML string (not SanitizedContent) that
|
2739
|
+
// is passed to an autoescape="false" template or a {print $foo|noAutoescape},
|
2740
|
+
// with the output going into an HTML context without escaping. We simply have
|
2741
|
+
// no way of knowing if this is what is happening when we get
|
2742
|
+
// non-SanitizedContent input, and most of the time it isn't.
|
2743
|
+
var isHtml = soydata.isContentKind(text, soydata.SanitizedContentKind.HTML);
|
2744
|
+
var wrappedText = formatter.unicodeWrapWithKnownDir(
|
2745
|
+
soydata.getContentDir(text), text + '', isHtml);
|
2746
|
+
|
2747
|
+
// Bidi-wrapping a value converts it to the context directionality. Since it
|
2748
|
+
// does not cost us anything, we will indicate this known direction in the
|
2749
|
+
// output SanitizedContent, even though the intended consumer of that
|
2750
|
+
// information - a bidi wrapping directive - has already been run.
|
2751
|
+
var wrappedTextDir = formatter.getContextDir();
|
2752
|
+
|
2753
|
+
// Unicode-wrapping UnsanitizedText gives UnsanitizedText.
|
2754
|
+
// Unicode-wrapping safe HTML or JS string data gives valid, safe HTML or JS
|
2755
|
+
// string data.
|
2756
|
+
// ATTENTION: Do these need to be ...ForInternalBlocks()?
|
2757
|
+
if (soydata.isContentKind(text, soydata.SanitizedContentKind.TEXT)) {
|
2758
|
+
return new soydata.UnsanitizedText(wrappedText, wrappedTextDir);
|
2759
|
+
}
|
2760
|
+
if (isHtml) {
|
2761
|
+
return soydata.VERY_UNSAFE.ordainSanitizedHtml(wrappedText, wrappedTextDir);
|
2762
|
+
}
|
2763
|
+
if (soydata.isContentKind(text, soydata.SanitizedContentKind.JS_STR_CHARS)) {
|
2764
|
+
return soydata.VERY_UNSAFE.ordainSanitizedJsStrChars(
|
2765
|
+
wrappedText, wrappedTextDir);
|
2766
|
+
}
|
2767
|
+
|
2768
|
+
// Unicode-wrapping does not conform to the syntax of the other types of
|
2769
|
+
// content. For lack of anything better to do, we we do not declare a content
|
2770
|
+
// kind at all by falling through to the non-SanitizedContent case below.
|
2771
|
+
// TODO(user): Consider throwing a runtime error on receipt of
|
2772
|
+
// SanitizedContent other than TEXT, HTML, or JS_STR_CHARS.
|
2773
|
+
|
2774
|
+
// The input was not SanitizedContent, so our output isn't SanitizedContent
|
2775
|
+
// either.
|
2776
|
+
return wrappedText;
|
1696
2777
|
};
|
1697
2778
|
|
1698
2779
|
|
@@ -1702,6 +2783,10 @@ soy.$$bidiUnicodeWrap = function(bidiGlobalDir, str) {
|
|
1702
2783
|
|
1703
2784
|
|
1704
2785
|
|
2786
|
+
|
2787
|
+
|
2788
|
+
|
2789
|
+
|
1705
2790
|
// START GENERATED CODE FOR ESCAPERS.
|
1706
2791
|
|
1707
2792
|
/**
|
@@ -1712,7 +2797,7 @@ soy.esc.$$escapeUriHelper = function(v) {
|
|
1712
2797
|
};
|
1713
2798
|
|
1714
2799
|
/**
|
1715
|
-
* Maps
|
2800
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1716
2801
|
* @type {Object.<string, string>}
|
1717
2802
|
* @private
|
1718
2803
|
*/
|
@@ -1740,7 +2825,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSP
|
|
1740
2825
|
};
|
1741
2826
|
|
1742
2827
|
/**
|
1743
|
-
* A function that can be used with String.replace
|
2828
|
+
* A function that can be used with String.replace.
|
1744
2829
|
* @param {string} ch A single character matched by a compatible matcher.
|
1745
2830
|
* @return {string} A token in the output language.
|
1746
2831
|
* @private
|
@@ -1750,7 +2835,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPAC
|
|
1750
2835
|
};
|
1751
2836
|
|
1752
2837
|
/**
|
1753
|
-
* Maps
|
2838
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1754
2839
|
* @type {Object.<string, string>}
|
1755
2840
|
* @private
|
1756
2841
|
*/
|
@@ -1792,7 +2877,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = {
|
|
1792
2877
|
};
|
1793
2878
|
|
1794
2879
|
/**
|
1795
|
-
* A function that can be used with String.replace
|
2880
|
+
* A function that can be used with String.replace.
|
1796
2881
|
* @param {string} ch A single character matched by a compatible matcher.
|
1797
2882
|
* @return {string} A token in the output language.
|
1798
2883
|
* @private
|
@@ -1802,7 +2887,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) {
|
|
1802
2887
|
};
|
1803
2888
|
|
1804
2889
|
/**
|
1805
|
-
* Maps
|
2890
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1806
2891
|
* @type {Object.<string, string>}
|
1807
2892
|
* @private
|
1808
2893
|
*/
|
@@ -1837,7 +2922,7 @@ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = {
|
|
1837
2922
|
};
|
1838
2923
|
|
1839
2924
|
/**
|
1840
|
-
* A function that can be used with String.replace
|
2925
|
+
* A function that can be used with String.replace.
|
1841
2926
|
* @param {string} ch A single character matched by a compatible matcher.
|
1842
2927
|
* @return {string} A token in the output language.
|
1843
2928
|
* @private
|
@@ -1847,7 +2932,7 @@ soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) {
|
|
1847
2932
|
};
|
1848
2933
|
|
1849
2934
|
/**
|
1850
|
-
* Maps
|
2935
|
+
* Maps characters to the escaped versions for the named escape directives.
|
1851
2936
|
* @type {Object.<string, string>}
|
1852
2937
|
* @private
|
1853
2938
|
*/
|
@@ -1920,7 +3005,7 @@ soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = {
|
|
1920
3005
|
};
|
1921
3006
|
|
1922
3007
|
/**
|
1923
|
-
* A function that can be used with String.replace
|
3008
|
+
* A function that can be used with String.replace.
|
1924
3009
|
* @param {string} ch A single character matched by a compatible matcher.
|
1925
3010
|
* @return {string} A token in the output language.
|
1926
3011
|
* @private
|
@@ -2004,7 +3089,14 @@ soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?:(?:https?|mailto):|[^&:\/?#]*(
|
|
2004
3089
|
* @type RegExp
|
2005
3090
|
* @private
|
2006
3091
|
*/
|
2007
|
-
soy.esc.$$
|
3092
|
+
soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_ = /^data:image\/(?:bmp|gif|jpe?g|png|tiff|webp);base64,[a-z0-9+\/]+=*$/i;
|
3093
|
+
|
3094
|
+
/**
|
3095
|
+
* A pattern that vets values produced by the named directives.
|
3096
|
+
* @type RegExp
|
3097
|
+
* @private
|
3098
|
+
*/
|
3099
|
+
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;
|
2008
3100
|
|
2009
3101
|
/**
|
2010
3102
|
* A pattern that vets values produced by the named directives.
|
@@ -2130,7 +3222,7 @@ soy.esc.$$normalizeUriHelper = function(value) {
|
|
2130
3222
|
soy.esc.$$filterNormalizeUriHelper = function(value) {
|
2131
3223
|
var str = String(value);
|
2132
3224
|
if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) {
|
2133
|
-
return 'zSoyz';
|
3225
|
+
return '#zSoyz';
|
2134
3226
|
}
|
2135
3227
|
return str.replace(
|
2136
3228
|
soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
|
@@ -2138,13 +3230,26 @@ soy.esc.$$filterNormalizeUriHelper = function(value) {
|
|
2138
3230
|
};
|
2139
3231
|
|
2140
3232
|
/**
|
2141
|
-
* A helper for the Soy directive |
|
3233
|
+
* A helper for the Soy directive |filterImageDataUri
|
3234
|
+
* @param {*} value Can be of any type but will be coerced to a string.
|
3235
|
+
* @return {string} The escaped text.
|
3236
|
+
*/
|
3237
|
+
soy.esc.$$filterImageDataUriHelper = function(value) {
|
3238
|
+
var str = String(value);
|
3239
|
+
if (!soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_.test(str)) {
|
3240
|
+
return 'data:image/gif;base64,zSoyz';
|
3241
|
+
}
|
3242
|
+
return str;
|
3243
|
+
};
|
3244
|
+
|
3245
|
+
/**
|
3246
|
+
* A helper for the Soy directive |filterHtmlAttributes
|
2142
3247
|
* @param {*} value Can be of any type but will be coerced to a string.
|
2143
3248
|
* @return {string} The escaped text.
|
2144
3249
|
*/
|
2145
|
-
soy.esc.$$
|
3250
|
+
soy.esc.$$filterHtmlAttributesHelper = function(value) {
|
2146
3251
|
var str = String(value);
|
2147
|
-
if (!soy.esc.$$
|
3252
|
+
if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_.test(str)) {
|
2148
3253
|
return 'zSoyz';
|
2149
3254
|
}
|
2150
3255
|
return str;
|
@@ -2165,10 +3270,29 @@ soy.esc.$$filterHtmlElementNameHelper = function(value) {
|
|
2165
3270
|
|
2166
3271
|
/**
|
2167
3272
|
* Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
|
3273
|
+
* By removing these, and replacing any '<' or '>' characters with
|
3274
|
+
* entities we guarantee that the result can be embedded into a
|
3275
|
+
* an attribute without introducing a tag boundary.
|
3276
|
+
*
|
3277
|
+
* @type {RegExp}
|
3278
|
+
* @private
|
3279
|
+
*/
|
3280
|
+
soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g;
|
3281
|
+
|
3282
|
+
/**
|
3283
|
+
* Matches all occurrences of '<'.
|
2168
3284
|
*
|
2169
3285
|
* @type {RegExp}
|
2170
3286
|
* @private
|
2171
3287
|
*/
|
2172
|
-
soy.esc.$$
|
3288
|
+
soy.esc.$$LT_REGEX_ = /</g;
|
3289
|
+
|
3290
|
+
/**
|
3291
|
+
* Maps lower-case names of innocuous tags to 1.
|
3292
|
+
*
|
3293
|
+
* @type {Object.<string,number>}
|
3294
|
+
* @private
|
3295
|
+
*/
|
3296
|
+
soy.esc.$$SAFE_TAG_WHITELIST_ = {'b': 1, 'br': 1, 'em': 1, 'i': 1, 's': 1, 'sub': 1, 'sup': 1, 'u': 1};
|
2173
3297
|
|
2174
3298
|
// END GENERATED CODE
|