Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/soy/soy.js
2868 views
1
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Provides utility methods to render soy template.
17
* @author [email protected] (Chris Henry)
18
*/
19
20
goog.provide('goog.soy');
21
22
goog.require('goog.asserts');
23
goog.require('goog.dom');
24
goog.require('goog.dom.NodeType');
25
goog.require('goog.dom.TagName');
26
goog.require('goog.html.legacyconversions');
27
goog.require('goog.soy.data.SanitizedContent');
28
goog.require('goog.soy.data.SanitizedContentKind');
29
goog.require('goog.string');
30
31
32
/**
33
* @define {boolean} Whether to require all Soy templates to be "strict html".
34
* Soy templates that use strict autoescaping forbid noAutoescape along with
35
* many dangerous directives, and return a runtime type SanitizedContent that
36
* marks them as safe.
37
*
38
* If this flag is enabled, Soy templates will fail to render if a template
39
* returns plain text -- indicating it is a non-strict template.
40
*/
41
goog.define('goog.soy.REQUIRE_STRICT_AUTOESCAPE', false);
42
43
44
/**
45
* Type definition for strict Soy templates. Very useful when passing a template
46
* as an argument.
47
* @typedef {function(?, null=, ?Object<string, *>=):
48
* !goog.soy.data.SanitizedContent}
49
*/
50
goog.soy.StrictTemplate;
51
52
53
/**
54
* Type definition for strict Soy HTML templates. Very useful when passing
55
* a template as an argument.
56
* @typedef {function(?, null=, ?Object<string, *>=):
57
* !goog.soy.data.SanitizedHtml}
58
*/
59
goog.soy.StrictHtmlTemplate;
60
61
62
/**
63
* Sets the processed template as the innerHTML of an element. It is recommended
64
* to use this helper function instead of directly setting innerHTML in your
65
* hand-written code, so that it will be easier to audit the code for cross-site
66
* scripting vulnerabilities.
67
*
68
* @param {?Element} element The element whose content we are rendering into.
69
* @param {!goog.soy.data.SanitizedContent} templateResult The processed
70
* template of kind HTML or TEXT (which will be escaped).
71
* @template ARG_TYPES
72
*/
73
goog.soy.renderHtml = function(element, templateResult) {
74
element.innerHTML = goog.soy.ensureTemplateOutputHtml_(templateResult);
75
};
76
77
78
/**
79
* Renders a Soy template and then set the output string as
80
* the innerHTML of an element. It is recommended to use this helper function
81
* instead of directly setting innerHTML in your hand-written code, so that it
82
* will be easier to audit the code for cross-site scripting vulnerabilities.
83
*
84
* @param {Element} element The element whose content we are rendering into.
85
* @param {?function(ARG_TYPES, Object<string, *>=):*|
86
* ?function(ARG_TYPES, null=, Object<string, *>=):*} template
87
* The Soy template defining the element's content.
88
* @param {ARG_TYPES=} opt_templateData The data for the template.
89
* @param {Object=} opt_injectedData The injected data for the template.
90
* @template ARG_TYPES
91
*/
92
goog.soy.renderElement = function(
93
element, template, opt_templateData, opt_injectedData) {
94
// Soy template parameter is only nullable for historical reasons.
95
goog.asserts.assert(template, 'Soy template may not be null.');
96
element.innerHTML = goog.soy.ensureTemplateOutputHtml_(
97
template(
98
opt_templateData || goog.soy.defaultTemplateData_, undefined,
99
opt_injectedData));
100
};
101
102
103
/**
104
* Renders a Soy template into a single node or a document
105
* fragment. If the rendered HTML string represents a single node, then that
106
* node is returned (note that this is *not* a fragment, despite them name of
107
* the method). Otherwise a document fragment is returned containing the
108
* rendered nodes.
109
*
110
* @param {?function(ARG_TYPES, Object<string, *>=):*|
111
* ?function(ARG_TYPES, null=, Object<string, *>=):*} template
112
* The Soy template defining the element's content.
113
* @param {ARG_TYPES=} opt_templateData The data for the template.
114
* @param {Object=} opt_injectedData The injected data for the template.
115
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
116
* create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
117
* @return {!Node} The resulting node or document fragment.
118
* @template ARG_TYPES
119
*/
120
goog.soy.renderAsFragment = function(
121
template, opt_templateData, opt_injectedData, opt_domHelper) {
122
// Soy template parameter is only nullable for historical reasons.
123
goog.asserts.assert(template, 'Soy template may not be null.');
124
var dom = opt_domHelper || goog.dom.getDomHelper();
125
var output = template(
126
opt_templateData || goog.soy.defaultTemplateData_, undefined,
127
opt_injectedData);
128
var html = goog.soy.ensureTemplateOutputHtml_(output);
129
goog.soy.assertFirstTagValid_(html);
130
var safeHtml = output instanceof goog.soy.data.SanitizedContent ?
131
output.toSafeHtml() :
132
goog.html.legacyconversions.safeHtmlFromString(html);
133
return dom.safeHtmlToNode(safeHtml);
134
};
135
136
137
/**
138
* Renders a Soy template into a single node. If the rendered
139
* HTML string represents a single node, then that node is returned. Otherwise,
140
* a DIV element is returned containing the rendered nodes.
141
*
142
* @param {?function(ARG_TYPES, Object<string, *>=):*|
143
* ?function(ARG_TYPES, null=, Object<string, *>=):*} template
144
* The Soy template defining the element's content.
145
* @param {ARG_TYPES=} opt_templateData The data for the template.
146
* @param {Object=} opt_injectedData The injected data for the template.
147
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
148
* create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
149
* @return {!Element} Rendered template contents, wrapped in a parent DIV
150
* element if necessary.
151
* @template ARG_TYPES
152
*/
153
goog.soy.renderAsElement = function(
154
template, opt_templateData, opt_injectedData, opt_domHelper) {
155
// Soy template parameter is only nullable for historical reasons.
156
goog.asserts.assert(template, 'Soy template may not be null.');
157
return goog.soy.convertToElement_(
158
template(
159
opt_templateData || goog.soy.defaultTemplateData_, undefined,
160
opt_injectedData),
161
opt_domHelper);
162
};
163
164
165
/**
166
* Converts a processed Soy template into a single node. If the rendered
167
* HTML string represents a single node, then that node is returned. Otherwise,
168
* a DIV element is returned containing the rendered nodes.
169
*
170
* @param {!goog.soy.data.SanitizedContent} templateResult The processed
171
* template of kind HTML or TEXT (which will be escaped).
172
* @param {?goog.dom.DomHelper=} opt_domHelper The DOM helper used to
173
* create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
174
* @return {!Element} Rendered template contents, wrapped in a parent DIV
175
* element if necessary.
176
*/
177
goog.soy.convertToElement = function(templateResult, opt_domHelper) {
178
return goog.soy.convertToElement_(templateResult, opt_domHelper);
179
};
180
181
182
/**
183
* Non-strict version of {@code goog.soy.convertToElement}.
184
*
185
* @param {*} templateResult The processed template.
186
* @param {?goog.dom.DomHelper=} opt_domHelper The DOM helper used to
187
* create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
188
* @return {!Element} Rendered template contents, wrapped in a parent DIV
189
* element if necessary.
190
* @private
191
*/
192
goog.soy.convertToElement_ = function(templateResult, opt_domHelper) {
193
var dom = opt_domHelper || goog.dom.getDomHelper();
194
var wrapper = dom.createElement(goog.dom.TagName.DIV);
195
var html = goog.soy.ensureTemplateOutputHtml_(templateResult);
196
goog.soy.assertFirstTagValid_(html);
197
wrapper.innerHTML = html;
198
199
// If the template renders as a single element, return it.
200
if (wrapper.childNodes.length == 1) {
201
var firstChild = wrapper.firstChild;
202
if (firstChild.nodeType == goog.dom.NodeType.ELEMENT) {
203
return /** @type {!Element} */ (firstChild);
204
}
205
}
206
207
// Otherwise, return the wrapper DIV.
208
return wrapper;
209
};
210
211
212
/**
213
* Ensures the result is "safe" to insert as HTML.
214
*
215
* Note if the template has non-strict autoescape, the guarantees here are very
216
* weak. It is recommended applications switch to requiring strict
217
* autoescaping over time by tweaking goog.soy.REQUIRE_STRICT_AUTOESCAPE.
218
*
219
* In the case the argument is a SanitizedContent object, it either must
220
* already be of kind HTML, or if it is kind="text", the output will be HTML
221
* escaped.
222
*
223
* @param {*} templateResult The template result.
224
* @return {string} The assumed-safe HTML output string.
225
* @private
226
*/
227
goog.soy.ensureTemplateOutputHtml_ = function(templateResult) {
228
// Allow strings as long as strict autoescaping is not mandated. Note we
229
// allow everything that isn't an object, because some non-escaping templates
230
// end up returning non-strings if their only print statement is a
231
// non-escaped argument, plus some unit tests spoof templates.
232
// TODO(gboyer): Track down and fix these cases.
233
if (!goog.soy.REQUIRE_STRICT_AUTOESCAPE && !goog.isObject(templateResult)) {
234
return String(templateResult);
235
}
236
237
// Allow SanitizedContent of kind HTML.
238
if (templateResult instanceof goog.soy.data.SanitizedContent) {
239
templateResult =
240
/** @type {!goog.soy.data.SanitizedContent} */ (templateResult);
241
var ContentKind = goog.soy.data.SanitizedContentKind;
242
if (templateResult.contentKind === ContentKind.HTML) {
243
return goog.asserts.assertString(templateResult.getContent());
244
}
245
if (templateResult.contentKind === ContentKind.TEXT) {
246
// Allow text to be rendered, as long as we escape it. Other content
247
// kinds will fail, since we don't know what to do with them.
248
// TODO(gboyer): Perhaps also include URI in this case.
249
return goog.string.htmlEscape(templateResult.getContent());
250
}
251
}
252
253
goog.asserts.fail(
254
'Soy template output is unsafe for use as HTML: ' + templateResult);
255
256
// In production, return a safe string, rather than failing hard.
257
return 'zSoyz';
258
};
259
260
261
/**
262
* Checks that the rendered HTML does not start with an invalid tag that would
263
* likely cause unexpected output from renderAsElement or renderAsFragment.
264
* See {@link http://www.w3.org/TR/html5/semantics.html#semantics} for reference
265
* as to which HTML elements can be parents of each other.
266
* @param {string} html The output of a template.
267
* @private
268
*/
269
goog.soy.assertFirstTagValid_ = function(html) {
270
if (goog.asserts.ENABLE_ASSERTS) {
271
var matches = html.match(goog.soy.INVALID_TAG_TO_RENDER_);
272
goog.asserts.assert(
273
!matches, 'This template starts with a %s, which ' +
274
'cannot be a child of a <div>, as required by soy internals. ' +
275
'Consider using goog.soy.renderElement instead.\nTemplate output: %s',
276
matches && matches[0], html);
277
}
278
};
279
280
281
/**
282
* A pattern to find templates that cannot be rendered by renderAsElement or
283
* renderAsFragment, as these elements cannot exist as the child of a <div>.
284
* @type {!RegExp}
285
* @private
286
*/
287
goog.soy.INVALID_TAG_TO_RENDER_ =
288
/^<(body|caption|col|colgroup|head|html|tr|td|th|tbody|thead|tfoot)>/i;
289
290
291
/**
292
* Immutable object that is passed into templates that are rendered
293
* without any data.
294
* @private @const
295
*/
296
goog.soy.defaultTemplateData_ = {};
297
298