Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/labs/html/scrubber.js
2868 views
1
// Copyright 2013 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
/**
17
* @fileoverview
18
* HTML tag filtering, and balancing.
19
* A more user-friendly API is exposed via {@code goog.labs.html.sanitizer}.
20
* @visibility {//visibility:private}
21
*/
22
23
24
goog.provide('goog.labs.html.scrubber');
25
26
goog.require('goog.array');
27
goog.require('goog.dom.tags');
28
goog.require('goog.labs.html.attributeRewriterPresubmitWorkaround');
29
goog.require('goog.string');
30
31
32
/**
33
* Replaces tags not on the white-list with empty text nodes, dropping all
34
* attributes, and drops other non-text nodes such as comments.
35
*
36
* @param {!Object<string, boolean>} tagWhitelist a set of lower-case tag names
37
* following the convention established by {@link goog.object.createSet}.
38
* @param {!Object<string, Object<string, goog.labs.html.AttributeRewriter>>}
39
* attrWhitelist
40
* maps lower-case tag names and the special string {@code "*"} to functions
41
* from decoded attribute values to sanitized values or {@code null} to
42
* indicate that the attribute is not allowed with that value.
43
*
44
* For example, if {@code attrWhitelist['a']['href']} is defined then it
45
* is used to sanitize the value of the link's URL.
46
*
47
* If {@code attrWhitelist['*']['id']} is defined, and
48
* {@code attrWhitelist['div']['id']} is not, then the former is used to
49
* sanitize any {@code id} attribute on a {@code <div>} element.
50
* @param {string} html a string of HTML
51
* @return {string} the input but with potentially dangerous tokens removed.
52
*/
53
goog.labs.html.scrubber.scrub = function(tagWhitelist, attrWhitelist, html) {
54
return goog.labs.html.scrubber.render_(
55
goog.labs.html.scrubber.balance_(
56
goog.labs.html.scrubber.filter_(
57
tagWhitelist, attrWhitelist,
58
goog.labs.html.scrubber.lex_(html))));
59
};
60
61
62
/**
63
* Balances tags in trusted HTML.
64
* @param {string} html a string of HTML
65
* @return {string} the input but with an end-tag for each non-void start tag
66
* and only for non-void start tags, and with start and end tags nesting
67
* properly.
68
*/
69
goog.labs.html.scrubber.balance = function(html) {
70
return goog.labs.html.scrubber.render_(
71
goog.labs.html.scrubber.balance_(goog.labs.html.scrubber.lex_(html)));
72
};
73
74
75
/** Character code constant for {@code '<'}. @private */
76
goog.labs.html.scrubber.CC_LT_ = '<'.charCodeAt(0);
77
78
79
/** Character code constant for {@code '!'}. @private */
80
goog.labs.html.scrubber.CC_BANG_ = '!'.charCodeAt(0);
81
82
83
/** Character code constant for {@code '/'}. @private */
84
goog.labs.html.scrubber.CC_SLASH_ = '/'.charCodeAt(0);
85
86
87
/** Character code constant for {@code '?'}. @private */
88
goog.labs.html.scrubber.CC_QMARK_ = '?'.charCodeAt(0);
89
90
91
/**
92
* Matches content following a tag name or attribute value, and before the
93
* beginning of the next attribute value.
94
* @private
95
*/
96
goog.labs.html.scrubber.ATTR_VALUE_PRECEDER_ = '[^=>]+';
97
98
99
/** @private */
100
goog.labs.html.scrubber.UNQUOTED_ATTR_VALUE_ = '(?:[^"\'\\s>][^\\s>]*)';
101
102
103
/** @private */
104
goog.labs.html.scrubber.DOUBLE_QUOTED_ATTR_VALUE_ = '(?:"[^"]*"?)';
105
106
107
/** @private */
108
goog.labs.html.scrubber.SINGLE_QUOTED_ATTR_VALUE_ = "(?:'[^']*'?)";
109
110
111
/**
112
* Matches the equals-sign and any attribute value following it, but does not
113
* capture any {@code >} that would close the tag.
114
* @private
115
*/
116
goog.labs.html.scrubber.ATTR_VALUE_ = '=\\s*(?:' +
117
goog.labs.html.scrubber.UNQUOTED_ATTR_VALUE_ + '|' +
118
goog.labs.html.scrubber.DOUBLE_QUOTED_ATTR_VALUE_ + '|' +
119
goog.labs.html.scrubber.SINGLE_QUOTED_ATTR_VALUE_ + ')?';
120
121
122
/**
123
* The body of a tag between the end of the name and the closing {@code >}
124
* if any.
125
* @private
126
*/
127
goog.labs.html.scrubber.ATTRS_ = '(?:' +
128
goog.labs.html.scrubber.ATTR_VALUE_PRECEDER_ + '|' +
129
goog.labs.html.scrubber.ATTR_VALUE_ + ')*';
130
131
132
/**
133
* A character that continues a tag name as defined at
134
* http://www.w3.org/html/wg/drafts/html/master/syntax.html#tag-name-state
135
* @private
136
*/
137
goog.labs.html.scrubber.TAG_NAME_CHAR_ = '[^\t\f\n />]';
138
139
140
/**
141
* Matches when the next character cannot continue a tag name.
142
* @private
143
*/
144
goog.labs.html.scrubber.BREAK_ =
145
'(?!' + goog.labs.html.scrubber.TAG_NAME_CHAR_ + ')';
146
147
148
/**
149
* Matches the open tag and body of a special element :
150
* one whose body cannot contain nested elements so uses special parsing rules.
151
* It does not include the end tag.
152
* @private
153
*/
154
goog.labs.html.scrubber.SPECIAL_ELEMENT_ = '<(?:' +
155
// Special tag name.
156
'(iframe|script|style|textarea|title|xmp)' +
157
// End of tag name
158
goog.labs.html.scrubber.BREAK_ +
159
// Attributes
160
goog.labs.html.scrubber.ATTRS_ + '>' +
161
// Element content includes non '<' characters, and
162
// '<' that don't start a matching end tag.
163
// This uses a back-reference to the tag name to determine whether
164
// the tag names match.
165
// Since matching is case-insensitive, this can only be used in
166
// a case-insensitive regular expression.
167
// JavaScript does not treat Turkish dotted I's as equivalent to their
168
// ASCII equivalents.
169
'(?:[^<]|<(?!/\\1' + goog.labs.html.scrubber.BREAK_ + '))*' +
170
')';
171
172
173
/**
174
* Regexp pattern for an HTML tag.
175
* @private
176
*/
177
goog.labs.html.scrubber.TAG_ = '<[/]?[a-z]' +
178
goog.labs.html.scrubber.TAG_NAME_CHAR_ + '*' +
179
goog.labs.html.scrubber.ATTRS_ + '>?';
180
181
182
/**
183
* Regexp pattern for an HTML text node.
184
* @private
185
*/
186
goog.labs.html.scrubber.TEXT_NODE_ = '(?:[^<]|<(?![a-z]|[?!/]))+';
187
188
189
/**
190
* Matches HTML comments including HTML 5 "bogus comments" of the form
191
* {@code <!...>} or {@code <?...>} or {@code </...>}.
192
* @private
193
*/
194
goog.labs.html.scrubber.COMMENT_ =
195
'<!--(?:[^\\-]|-+(?![\\->]))*(?:-(?:->?)?)?' +
196
'|<[!?/][^>]*>?';
197
198
199
/**
200
* Regexp pattern for an HTML token after a doctype.
201
* Special elements introduces a capturing group for use with a
202
* back-reference.
203
* @private
204
*/
205
goog.labs.html.scrubber.HTML_TOKENS_RE_ = new RegExp(
206
'(?:' + goog.labs.html.scrubber.TEXT_NODE_ + '|' +
207
goog.labs.html.scrubber.SPECIAL_ELEMENT_ + '|' +
208
goog.labs.html.scrubber.TAG_ + '|' + goog.labs.html.scrubber.COMMENT_ +
209
')',
210
'ig');
211
212
213
/**
214
* An HTML tag which captures the name in group 1,
215
* and any attributes in group 2.
216
* @private
217
*/
218
goog.labs.html.scrubber.TAG_RE_ = new RegExp(
219
'<[/]?([a-z]' + goog.labs.html.scrubber.TAG_NAME_CHAR_ + '*)' +
220
'(' + goog.labs.html.scrubber.ATTRS_ + ')>?',
221
'i');
222
223
224
/**
225
* A global matcher that separates attributes out of the tag body cruft.
226
* @private
227
*/
228
goog.labs.html.scrubber.ATTRS_RE_ = new RegExp(
229
'[^=\\s]+\\s*(?:' + goog.labs.html.scrubber.ATTR_VALUE_ + ')?', 'ig');
230
231
232
/**
233
* Returns an array of HTML tokens including tags, text nodes and comments.
234
* "Special" elements, like {@code <script>..</script>} whose bodies cannot
235
* include nested elements, are returned as single tokens.
236
*
237
* @param {string} html a string of HTML
238
* @return {!Array<string>}
239
* @private
240
*/
241
goog.labs.html.scrubber.lex_ = function(html) {
242
return ('' + html).match(goog.labs.html.scrubber.HTML_TOKENS_RE_) || [];
243
};
244
245
246
/**
247
* Replaces tags not on the white-list with empty text nodes, dropping all
248
* attributes, and drops other non-text nodes such as comments.
249
*
250
* @param {!Object<string, boolean>} tagWhitelist a set of lower-case tag names
251
* following the convention established by {@link goog.object.createSet}.
252
* @param {!Object<string, Object<string, goog.labs.html.AttributeRewriter>>
253
* } attrWhitelist
254
* maps lower-case tag names and the special string {@code "*"} to functions
255
* from decoded attribute values to sanitized values or {@code null} to
256
* indicate that the attribute is not allowed with that value.
257
*
258
* For example, if {@code attrWhitelist['a']['href']} is defined then it is
259
* used to sanitize the value of the link's URL.
260
*
261
* If {@code attrWhitelist['*']['id']} is defined, and
262
* {@code attrWhitelist['div']['id']} is not, then the former is used to
263
* sanitize any {@code id} attribute on a {@code <div>} element.
264
* @param {!Array<string>} htmlTokens an array of HTML tokens as returned by
265
* {@link goog.labs.html.scrubber.lex_}.
266
* @return {!Array<string>} the input array modified in place to have some
267
* tokens removed.
268
* @private
269
*/
270
goog.labs.html.scrubber.filter_ = function(
271
tagWhitelist, attrWhitelist, htmlTokens) {
272
var genericAttrWhitelist = attrWhitelist['*'];
273
for (var i = 0, n = htmlTokens.length; i < n; ++i) {
274
var htmlToken = htmlTokens[i];
275
if (htmlToken.charCodeAt(0) !== goog.labs.html.scrubber.CC_LT_) {
276
// Definitely not a tag
277
continue;
278
}
279
280
var tag = htmlToken.match(goog.labs.html.scrubber.TAG_RE_);
281
if (tag) {
282
var lowerCaseTagName = tag[1].toLowerCase();
283
var isCloseTag =
284
htmlToken.charCodeAt(1) === goog.labs.html.scrubber.CC_SLASH_;
285
var attrs = '';
286
if (!isCloseTag && tag[2]) {
287
var tagSpecificAttrWhitelist =
288
/** @type {Object<string, goog.labs.html.AttributeRewriter>} */ (
289
goog.labs.html.scrubber.readOwnProperty_(
290
attrWhitelist, lowerCaseTagName));
291
if (genericAttrWhitelist || tagSpecificAttrWhitelist) {
292
attrs = goog.labs.html.scrubber.filterAttrs_(
293
tag[2], genericAttrWhitelist, tagSpecificAttrWhitelist);
294
}
295
}
296
var specialContent = htmlToken.substring(tag[0].length);
297
htmlTokens[i] = (tagWhitelist[lowerCaseTagName] === true) ?
298
((isCloseTag ? '</' : '<') + lowerCaseTagName + attrs + '>' +
299
specialContent) :
300
'';
301
} else if (htmlToken.length > 1) {
302
switch (htmlToken.charCodeAt(1)) {
303
case goog.labs.html.scrubber.CC_BANG_:
304
case goog.labs.html.scrubber.CC_SLASH_:
305
case goog.labs.html.scrubber.CC_QMARK_:
306
htmlTokens[i] = ''; // Elide comments.
307
break;
308
default:
309
// Otherwise, token is just a text node that starts with '<'.
310
// Speed up later passes by normalizing the text node.
311
htmlTokens[i] = htmlTokens[i].replace(/</g, '&lt;');
312
}
313
}
314
}
315
return htmlTokens;
316
};
317
318
319
/**
320
* Parses attribute names and values out of a tag body and applies the attribute
321
* white-list to produce a tag body containing only safe attributes.
322
*
323
* @param {string} attrsText the text of a tag between the end of the tag name
324
* and the beginning of the tag end marker, so {@code " foo bar='baz'"} for
325
* the tag {@code <tag foo bar='baz'/>}.
326
* @param {Object<string, goog.labs.html.AttributeRewriter>}
327
* genericAttrWhitelist
328
* a whitelist of attribute transformations for attributes that are allowed
329
* on any element.
330
* @param {Object<string, goog.labs.html.AttributeRewriter>}
331
* tagSpecificAttrWhitelist
332
* a whitelist of attribute transformations for attributes that are allowed
333
* on the element started by the tag whose body is {@code tagBody}.
334
* @return {string} a tag-body that consists only of safe attributes.
335
* @private
336
*/
337
goog.labs.html.scrubber.filterAttrs_ = function(
338
attrsText, genericAttrWhitelist, tagSpecificAttrWhitelist) {
339
var attrs = attrsText.match(goog.labs.html.scrubber.ATTRS_RE_);
340
var nAttrs = attrs ? attrs.length : 0;
341
var safeAttrs = '';
342
for (var i = 0; i < nAttrs; ++i) {
343
var attr = attrs[i];
344
var eq = attr.indexOf('=');
345
var name, value;
346
if (eq >= 0) {
347
name = goog.string.trim(attr.substr(0, eq));
348
value =
349
goog.string.stripQuotes(goog.string.trim(attr.substr(eq + 1)), '"\'');
350
} else {
351
name = value = attr;
352
}
353
name = name.toLowerCase();
354
var rewriter = /** @type {?goog.labs.html.AttributeRewriter} */ (
355
tagSpecificAttrWhitelist &&
356
goog.labs.html.scrubber.readOwnProperty_(
357
tagSpecificAttrWhitelist, name) ||
358
genericAttrWhitelist &&
359
goog.labs.html.scrubber.readOwnProperty_(
360
genericAttrWhitelist, name));
361
if (rewriter) {
362
var safeValue = rewriter(goog.string.unescapeEntities(value));
363
if (safeValue != null) {
364
if (safeValue.implementsGoogStringTypedString) {
365
safeValue = /** @type {goog.string.TypedString} */
366
(safeValue).getTypedStringValue();
367
}
368
safeValue = String(safeValue);
369
if (safeValue.indexOf('`') >= 0) {
370
safeValue += ' ';
371
}
372
safeAttrs +=
373
' ' + name + '="' + goog.string.htmlEscape(safeValue, false) + '"';
374
}
375
}
376
}
377
return safeAttrs;
378
};
379
380
381
/**
382
* @param {!Object} o the object
383
* @param {!string} k a key into o
384
* @return {*}
385
* @private
386
*/
387
goog.labs.html.scrubber.readOwnProperty_ = function(o, k) {
388
return Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined;
389
};
390
391
392
/**
393
* We limit the nesting limit of balanced HTML to a large but manageable number
394
* so that built-in browser limits aren't likely to kick in and undo all our
395
* matching of start and end tags.
396
* <br>
397
* This mitigates the HTML parsing equivalent of stack smashing attacks.
398
* <br>
399
* Otherwise, crafted inputs like
400
* {@code <p><p><p><p>...Ad nauseam..</p></p></p></p>} could exploit
401
* browser bugs, and/or undocumented nesting limit recovery code to misnest
402
* tags.
403
* @private
404
* @const
405
*/
406
goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_ = 256;
407
408
409
/**
410
* Ensures that there are end-tags for all and only for non-void start tags.
411
* @param {Array<string>} htmlTokens an array of HTML tokens as returned by
412
* {@link goog.labs.html.scrubber.lex}.
413
* @return {!Array<string>} the input array modified in place to have some
414
* tokens removed.
415
* @private
416
*/
417
goog.labs.html.scrubber.balance_ = function(htmlTokens) {
418
var openElementStack = [];
419
for (var i = 0, n = htmlTokens.length; i < n; ++i) {
420
var htmlToken = htmlTokens[i];
421
if (htmlToken.charCodeAt(0) !== goog.labs.html.scrubber.CC_LT_) {
422
// Definitely not a tag
423
continue;
424
}
425
var tag = htmlToken.match(goog.labs.html.scrubber.TAG_RE_);
426
if (tag) {
427
var lowerCaseTagName = tag[1].toLowerCase();
428
var isCloseTag =
429
htmlToken.charCodeAt(1) === goog.labs.html.scrubber.CC_SLASH_;
430
// Special case: HTML5 mandates that </br> be treated as <br>.
431
if (isCloseTag && lowerCaseTagName == 'br') {
432
isCloseTag = false;
433
htmlToken = '<br>';
434
}
435
var isVoidTag = goog.dom.tags.isVoidTag(lowerCaseTagName);
436
if (isVoidTag && isCloseTag) {
437
htmlTokens[i] = '';
438
continue;
439
}
440
441
var prefix = '';
442
443
// Insert implied open tags.
444
var nOpenElements = openElementStack.length;
445
if (nOpenElements && !isCloseTag) {
446
var top = openElementStack[nOpenElements - 1];
447
var groups = goog.labs.html.scrubber.ELEMENT_GROUPS_[lowerCaseTagName];
448
if (groups === undefined) {
449
groups = goog.labs.html.scrubber.Group_.INLINE_;
450
}
451
var canContain = goog.labs.html.scrubber.ELEMENT_CONTENTS_[top];
452
if (!(groups & canContain)) {
453
var blockContainer = goog.labs.html.scrubber.BLOCK_CONTAINERS_[top];
454
if ('string' === typeof blockContainer) {
455
var containerCanContain =
456
goog.labs.html.scrubber.ELEMENT_CONTENTS_[blockContainer];
457
if (containerCanContain & groups) {
458
if (nOpenElements <
459
goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
460
prefix = '<' + blockContainer + '>';
461
}
462
openElementStack[nOpenElements] = blockContainer;
463
++nOpenElements;
464
}
465
}
466
}
467
}
468
469
// Insert any missing close tags we need.
470
var newStackLen = goog.labs.html.scrubber.pickElementsToClose_(
471
lowerCaseTagName, isCloseTag, openElementStack);
472
473
var nClosed = nOpenElements - newStackLen;
474
if (nClosed) { // ["p", "a", "b"] -> "</b></a></p>"
475
// First, dump anything past the nesting limit.
476
if (nOpenElements > goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
477
nClosed -=
478
nOpenElements - goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
479
nOpenElements = goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
480
}
481
// Truncate to the new limit, and produce end tags.
482
var closeTags = openElementStack.splice(newStackLen, nClosed);
483
if (closeTags.length) {
484
closeTags.reverse();
485
prefix += '</' + closeTags.join('></') + '>';
486
}
487
}
488
489
// We could do resumption here to handle misnested tags like
490
// <b><i class="c">Foo</b>Bar</i>
491
// which is equivalent to
492
// <b><i class="c">Foo</i></b><i class="c">Bar</i>
493
// but that requires storing attributes on the open element stack
494
// which complicates all the code using it for marginal added value.
495
496
if (isCloseTag) {
497
// If the close tag matched an open tag, then the closed section
498
// included that tag name.
499
htmlTokens[i] = prefix;
500
} else {
501
if (!isVoidTag) {
502
openElementStack[openElementStack.length] = lowerCaseTagName;
503
}
504
if (openElementStack.length >
505
goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
506
htmlToken = '';
507
}
508
htmlTokens[i] = prefix + htmlToken;
509
}
510
}
511
}
512
if (openElementStack.length) {
513
if (openElementStack.length >
514
goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
515
openElementStack.length = goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
516
}
517
if (openElementStack.length) {
518
openElementStack.reverse();
519
htmlTokens[htmlTokens.length] = '</' + openElementStack.join('></') + '>';
520
}
521
}
522
return htmlTokens;
523
};
524
525
526
/**
527
* Normalizes HTML tokens and concatenates them into a string.
528
* @param {Array<string>} htmlTokens an array of HTML tokens as returned by
529
* {@link goog.labs.html.scrubber.lex}.
530
* @return {string} a string of HTML.
531
* @private
532
*/
533
goog.labs.html.scrubber.render_ = function(htmlTokens) {
534
for (var i = 0, n = htmlTokens.length; i < n; ++i) {
535
var htmlToken = htmlTokens[i];
536
if (htmlToken.charCodeAt(0) === goog.labs.html.scrubber.CC_LT_ &&
537
goog.labs.html.scrubber.TAG_RE_.test(htmlToken)) {
538
// The well-formedness and quotedness of attributes must be ensured by
539
// earlier passes. filter does this.
540
} else {
541
if (htmlToken.indexOf('<') >= 0) {
542
htmlToken = htmlToken.replace(/</g, '&lt;');
543
}
544
if (htmlToken.indexOf('>') >= 0) {
545
htmlToken = htmlToken.replace(/>/g, '&gt;');
546
}
547
htmlTokens[i] = htmlToken;
548
}
549
}
550
return htmlTokens.join('');
551
};
552
553
554
/**
555
* Groups of elements used to specify containment relationships.
556
* @enum {number}
557
* @private
558
*/
559
goog.labs.html.scrubber.Group_ = {
560
BLOCK_: (1 << 0),
561
INLINE_: (1 << 1),
562
INLINE_MINUS_A_: (1 << 2),
563
MIXED_: (1 << 3),
564
TABLE_CONTENT_: (1 << 4),
565
HEAD_CONTENT_: (1 << 5),
566
TOP_CONTENT_: (1 << 6),
567
AREA_ELEMENT_: (1 << 7),
568
FORM_ELEMENT_: (1 << 8),
569
LEGEND_ELEMENT_: (1 << 9),
570
LI_ELEMENT_: (1 << 10),
571
DL_PART_: (1 << 11),
572
P_ELEMENT_: (1 << 12),
573
OPTIONS_ELEMENT_: (1 << 13),
574
OPTION_ELEMENT_: (1 << 14),
575
PARAM_ELEMENT_: (1 << 15),
576
TABLE_ELEMENT_: (1 << 16),
577
TR_ELEMENT_: (1 << 17),
578
TD_ELEMENT_: (1 << 18),
579
COL_ELEMENT_: (1 << 19),
580
CHARACTER_DATA_: (1 << 20)
581
};
582
583
584
/**
585
* Element scopes limit where close tags can have effects.
586
* For example, a table cannot be implicitly closed by a {@code </p>} even if
587
* the table appears inside a {@code <p>} because the {@code <table>} element
588
* introduces a scope.
589
*
590
* @enum {number}
591
* @private
592
*/
593
goog.labs.html.scrubber.Scope_ = {
594
COMMON_: (1 << 0),
595
BUTTON_: (1 << 1),
596
LIST_ITEM_: (1 << 2),
597
TABLE_: (1 << 3)
598
};
599
600
601
/** @const @private */
602
goog.labs.html.scrubber.ALL_SCOPES_ = goog.labs.html.scrubber.Scope_.COMMON_ |
603
goog.labs.html.scrubber.Scope_.BUTTON_ |
604
goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
605
goog.labs.html.scrubber.Scope_.TABLE_;
606
607
608
/**
609
* Picks which open HTML elements to close.
610
*
611
* @param {string} lowerCaseTagName The name of the tag.
612
* @param {boolean} isCloseTag True for a {@code </tagname>} tag.
613
* @param {Array<string>} openElementStack The names of elements that have been
614
* opened and not subsequently closed.
615
* @return {number} the length of openElementStack after closing any tags that
616
* need to be closed.
617
* @private
618
*/
619
goog.labs.html.scrubber.pickElementsToClose_ = function(
620
lowerCaseTagName, isCloseTag, openElementStack) {
621
var nOpenElements = openElementStack.length;
622
if (isCloseTag) {
623
// Look for a matching close tag inside blocking scopes.
624
var topMost;
625
if (/^h[1-6]$/.test(lowerCaseTagName)) {
626
// </h1> will close any header.
627
topMost = -1;
628
for (var i = nOpenElements; --i >= 0;) {
629
if (/^h[1-6]$/.test(openElementStack[i])) {
630
topMost = i;
631
}
632
}
633
} else {
634
topMost = goog.array.lastIndexOf(openElementStack, lowerCaseTagName);
635
}
636
if (topMost >= 0) {
637
var blockers = goog.labs.html.scrubber.ALL_SCOPES_ &
638
~(goog.labs.html.scrubber.ELEMENT_SCOPES_[lowerCaseTagName] | 0);
639
for (var i = nOpenElements; --i > topMost;) {
640
var blocks =
641
goog.labs.html.scrubber.ELEMENT_SCOPES_[openElementStack[i]] | 0;
642
if (blockers & blocks) {
643
return nOpenElements;
644
}
645
}
646
return topMost;
647
}
648
return nOpenElements;
649
} else {
650
// Close anything that cannot contain the tag name.
651
var groups = goog.labs.html.scrubber.ELEMENT_GROUPS_[lowerCaseTagName];
652
if (groups === undefined) {
653
groups = goog.labs.html.scrubber.Group_.INLINE_;
654
}
655
for (var i = nOpenElements; --i >= 0;) {
656
var canContain =
657
goog.labs.html.scrubber.ELEMENT_CONTENTS_[openElementStack[i]];
658
if (canContain === undefined) {
659
canContain = goog.labs.html.scrubber.Group_.INLINE_;
660
}
661
if (groups & canContain) {
662
return i + 1;
663
}
664
}
665
return 0;
666
}
667
};
668
669
670
/**
671
* The groups into which the element falls.
672
* The default is an inline element.
673
* @private
674
*/
675
goog.labs.html.scrubber.ELEMENT_GROUPS_ = {
676
'a': goog.labs.html.scrubber.Group_.INLINE_,
677
'abbr': goog.labs.html.scrubber.Group_.INLINE_ |
678
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
679
'acronym': goog.labs.html.scrubber.Group_.INLINE_ |
680
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
681
'address': goog.labs.html.scrubber.Group_.BLOCK_,
682
'applet': goog.labs.html.scrubber.Group_.INLINE_ |
683
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
684
'area': goog.labs.html.scrubber.Group_.AREA_ELEMENT_,
685
'audio': goog.labs.html.scrubber.Group_.INLINE_ |
686
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
687
'b': goog.labs.html.scrubber.Group_.INLINE_ |
688
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
689
'base': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
690
'basefont': goog.labs.html.scrubber.Group_.INLINE_ |
691
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
692
'bdi': goog.labs.html.scrubber.Group_.INLINE_ |
693
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
694
'bdo': goog.labs.html.scrubber.Group_.INLINE_ |
695
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
696
'big': goog.labs.html.scrubber.Group_.INLINE_ |
697
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
698
'blink': goog.labs.html.scrubber.Group_.INLINE_ |
699
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
700
'blockquote': goog.labs.html.scrubber.Group_.BLOCK_,
701
'body': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
702
'br': goog.labs.html.scrubber.Group_.INLINE_ |
703
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
704
'button': goog.labs.html.scrubber.Group_.INLINE_ |
705
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
706
'canvas': goog.labs.html.scrubber.Group_.INLINE_ |
707
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
708
'caption': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
709
'center': goog.labs.html.scrubber.Group_.BLOCK_,
710
'cite': goog.labs.html.scrubber.Group_.INLINE_ |
711
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
712
'code': goog.labs.html.scrubber.Group_.INLINE_ |
713
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
714
'col': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
715
goog.labs.html.scrubber.Group_.COL_ELEMENT_,
716
'colgroup': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
717
'dd': goog.labs.html.scrubber.Group_.DL_PART_,
718
'del': goog.labs.html.scrubber.Group_.BLOCK_ |
719
goog.labs.html.scrubber.Group_.INLINE_ |
720
goog.labs.html.scrubber.Group_.MIXED_,
721
'dfn': goog.labs.html.scrubber.Group_.INLINE_ |
722
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
723
'dir': goog.labs.html.scrubber.Group_.BLOCK_,
724
'div': goog.labs.html.scrubber.Group_.BLOCK_,
725
'dl': goog.labs.html.scrubber.Group_.BLOCK_,
726
'dt': goog.labs.html.scrubber.Group_.DL_PART_,
727
'em': goog.labs.html.scrubber.Group_.INLINE_ |
728
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
729
'fieldset': goog.labs.html.scrubber.Group_.BLOCK_,
730
'font': goog.labs.html.scrubber.Group_.INLINE_ |
731
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
732
'form': goog.labs.html.scrubber.Group_.BLOCK_ |
733
goog.labs.html.scrubber.Group_.FORM_ELEMENT_,
734
'h1': goog.labs.html.scrubber.Group_.BLOCK_,
735
'h2': goog.labs.html.scrubber.Group_.BLOCK_,
736
'h3': goog.labs.html.scrubber.Group_.BLOCK_,
737
'h4': goog.labs.html.scrubber.Group_.BLOCK_,
738
'h5': goog.labs.html.scrubber.Group_.BLOCK_,
739
'h6': goog.labs.html.scrubber.Group_.BLOCK_,
740
'head': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
741
'hr': goog.labs.html.scrubber.Group_.BLOCK_,
742
'html': 0,
743
'i': goog.labs.html.scrubber.Group_.INLINE_ |
744
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
745
'iframe': goog.labs.html.scrubber.Group_.INLINE_ |
746
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
747
'img': goog.labs.html.scrubber.Group_.INLINE_ |
748
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
749
'input': goog.labs.html.scrubber.Group_.INLINE_ |
750
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
751
'ins': goog.labs.html.scrubber.Group_.BLOCK_ |
752
goog.labs.html.scrubber.Group_.INLINE_,
753
'isindex': goog.labs.html.scrubber.Group_.INLINE_,
754
'kbd': goog.labs.html.scrubber.Group_.INLINE_ |
755
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
756
'label': goog.labs.html.scrubber.Group_.INLINE_ |
757
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
758
'legend': goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_,
759
'li': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
760
'link': goog.labs.html.scrubber.Group_.INLINE_ |
761
goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
762
'listing': goog.labs.html.scrubber.Group_.BLOCK_,
763
'map': goog.labs.html.scrubber.Group_.INLINE_,
764
'meta': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
765
'nobr': goog.labs.html.scrubber.Group_.INLINE_ |
766
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
767
'noframes': goog.labs.html.scrubber.Group_.BLOCK_ |
768
goog.labs.html.scrubber.Group_.TOP_CONTENT_,
769
'noscript': goog.labs.html.scrubber.Group_.BLOCK_,
770
'object': goog.labs.html.scrubber.Group_.INLINE_ |
771
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
772
goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
773
'ol': goog.labs.html.scrubber.Group_.BLOCK_,
774
'optgroup': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
775
'option': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_ |
776
goog.labs.html.scrubber.Group_.OPTION_ELEMENT_,
777
'p': goog.labs.html.scrubber.Group_.BLOCK_ |
778
goog.labs.html.scrubber.Group_.P_ELEMENT_,
779
'param': goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
780
'pre': goog.labs.html.scrubber.Group_.BLOCK_,
781
'q': goog.labs.html.scrubber.Group_.INLINE_ |
782
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
783
's': goog.labs.html.scrubber.Group_.INLINE_ |
784
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
785
'samp': goog.labs.html.scrubber.Group_.INLINE_ |
786
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
787
'script':
788
(goog.labs.html.scrubber.Group_.BLOCK_ |
789
goog.labs.html.scrubber.Group_.INLINE_ |
790
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
791
goog.labs.html.scrubber.Group_.MIXED_ |
792
goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
793
goog.labs.html.scrubber.Group_.HEAD_CONTENT_ |
794
goog.labs.html.scrubber.Group_.TOP_CONTENT_ |
795
goog.labs.html.scrubber.Group_.AREA_ELEMENT_ |
796
goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
797
goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_ |
798
goog.labs.html.scrubber.Group_.LI_ELEMENT_ |
799
goog.labs.html.scrubber.Group_.DL_PART_ |
800
goog.labs.html.scrubber.Group_.P_ELEMENT_ |
801
goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_ |
802
goog.labs.html.scrubber.Group_.OPTION_ELEMENT_ |
803
goog.labs.html.scrubber.Group_.PARAM_ELEMENT_ |
804
goog.labs.html.scrubber.Group_.TABLE_ELEMENT_ |
805
goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
806
goog.labs.html.scrubber.Group_.TD_ELEMENT_ |
807
goog.labs.html.scrubber.Group_.COL_ELEMENT_),
808
'select': goog.labs.html.scrubber.Group_.INLINE_,
809
'small': goog.labs.html.scrubber.Group_.INLINE_ |
810
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
811
'span': goog.labs.html.scrubber.Group_.INLINE_ |
812
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
813
'strike': goog.labs.html.scrubber.Group_.INLINE_ |
814
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
815
'strong': goog.labs.html.scrubber.Group_.INLINE_ |
816
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
817
'style': goog.labs.html.scrubber.Group_.INLINE_ |
818
goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
819
'sub': goog.labs.html.scrubber.Group_.INLINE_ |
820
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
821
'sup': goog.labs.html.scrubber.Group_.INLINE_ |
822
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
823
'table': goog.labs.html.scrubber.Group_.BLOCK_ |
824
goog.labs.html.scrubber.Group_.TABLE_ELEMENT_,
825
'tbody': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
826
'td': goog.labs.html.scrubber.Group_.TD_ELEMENT_,
827
'textarea': goog.labs.html.scrubber.Group_.INLINE_,
828
'tfoot': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
829
'th': goog.labs.html.scrubber.Group_.TD_ELEMENT_,
830
'thead': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
831
'title': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
832
'tr': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
833
goog.labs.html.scrubber.Group_.TR_ELEMENT_,
834
'tt': goog.labs.html.scrubber.Group_.INLINE_ |
835
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
836
'u': goog.labs.html.scrubber.Group_.INLINE_ |
837
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
838
'ul': goog.labs.html.scrubber.Group_.BLOCK_,
839
'var': goog.labs.html.scrubber.Group_.INLINE_ |
840
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
841
'video': goog.labs.html.scrubber.Group_.INLINE_ |
842
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
843
'wbr': goog.labs.html.scrubber.Group_.INLINE_ |
844
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
845
'xmp': goog.labs.html.scrubber.Group_.BLOCK_
846
};
847
848
849
/**
850
* The groups which the element can contain.
851
* Defaults to 0.
852
* @private
853
*/
854
goog.labs.html.scrubber.ELEMENT_CONTENTS_ = {
855
'a': goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
856
'abbr': goog.labs.html.scrubber.Group_.INLINE_,
857
'acronym': goog.labs.html.scrubber.Group_.INLINE_,
858
'address': goog.labs.html.scrubber.Group_.INLINE_ |
859
goog.labs.html.scrubber.Group_.P_ELEMENT_,
860
'applet': goog.labs.html.scrubber.Group_.BLOCK_ |
861
goog.labs.html.scrubber.Group_.INLINE_ |
862
goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
863
'b': goog.labs.html.scrubber.Group_.INLINE_,
864
'bdi': goog.labs.html.scrubber.Group_.INLINE_,
865
'bdo': goog.labs.html.scrubber.Group_.INLINE_,
866
'big': goog.labs.html.scrubber.Group_.INLINE_,
867
'blink': goog.labs.html.scrubber.Group_.INLINE_,
868
'blockquote': goog.labs.html.scrubber.Group_.BLOCK_ |
869
goog.labs.html.scrubber.Group_.INLINE_,
870
'body': goog.labs.html.scrubber.Group_.BLOCK_ |
871
goog.labs.html.scrubber.Group_.INLINE_,
872
'button': goog.labs.html.scrubber.Group_.BLOCK_ |
873
goog.labs.html.scrubber.Group_.INLINE_,
874
'canvas': goog.labs.html.scrubber.Group_.INLINE_,
875
'caption': goog.labs.html.scrubber.Group_.INLINE_,
876
'center': goog.labs.html.scrubber.Group_.BLOCK_ |
877
goog.labs.html.scrubber.Group_.INLINE_,
878
'cite': goog.labs.html.scrubber.Group_.INLINE_,
879
'code': goog.labs.html.scrubber.Group_.INLINE_,
880
'colgroup': goog.labs.html.scrubber.Group_.COL_ELEMENT_,
881
'dd': goog.labs.html.scrubber.Group_.BLOCK_ |
882
goog.labs.html.scrubber.Group_.INLINE_,
883
'del': goog.labs.html.scrubber.Group_.BLOCK_ |
884
goog.labs.html.scrubber.Group_.INLINE_,
885
'dfn': goog.labs.html.scrubber.Group_.INLINE_,
886
'dir': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
887
'div': goog.labs.html.scrubber.Group_.BLOCK_ |
888
goog.labs.html.scrubber.Group_.INLINE_,
889
'dl': goog.labs.html.scrubber.Group_.DL_PART_,
890
'dt': goog.labs.html.scrubber.Group_.INLINE_,
891
'em': goog.labs.html.scrubber.Group_.INLINE_,
892
'fieldset': goog.labs.html.scrubber.Group_.BLOCK_ |
893
goog.labs.html.scrubber.Group_.INLINE_ |
894
goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_,
895
'font': goog.labs.html.scrubber.Group_.INLINE_,
896
'form':
897
(goog.labs.html.scrubber.Group_.BLOCK_ |
898
goog.labs.html.scrubber.Group_.INLINE_ |
899
goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
900
goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
901
goog.labs.html.scrubber.Group_.TD_ELEMENT_),
902
'h1': goog.labs.html.scrubber.Group_.INLINE_,
903
'h2': goog.labs.html.scrubber.Group_.INLINE_,
904
'h3': goog.labs.html.scrubber.Group_.INLINE_,
905
'h4': goog.labs.html.scrubber.Group_.INLINE_,
906
'h5': goog.labs.html.scrubber.Group_.INLINE_,
907
'h6': goog.labs.html.scrubber.Group_.INLINE_,
908
'head': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
909
'html': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
910
'i': goog.labs.html.scrubber.Group_.INLINE_,
911
'iframe': goog.labs.html.scrubber.Group_.BLOCK_ |
912
goog.labs.html.scrubber.Group_.INLINE_,
913
'ins': goog.labs.html.scrubber.Group_.BLOCK_ |
914
goog.labs.html.scrubber.Group_.INLINE_,
915
'kbd': goog.labs.html.scrubber.Group_.INLINE_,
916
'label': goog.labs.html.scrubber.Group_.INLINE_,
917
'legend': goog.labs.html.scrubber.Group_.INLINE_,
918
'li': goog.labs.html.scrubber.Group_.BLOCK_ |
919
goog.labs.html.scrubber.Group_.INLINE_,
920
'listing': goog.labs.html.scrubber.Group_.INLINE_,
921
'map': goog.labs.html.scrubber.Group_.BLOCK_ |
922
goog.labs.html.scrubber.Group_.AREA_ELEMENT_,
923
'nobr': goog.labs.html.scrubber.Group_.INLINE_,
924
'noframes': goog.labs.html.scrubber.Group_.BLOCK_ |
925
goog.labs.html.scrubber.Group_.INLINE_ |
926
goog.labs.html.scrubber.Group_.TOP_CONTENT_,
927
'noscript': goog.labs.html.scrubber.Group_.BLOCK_ |
928
goog.labs.html.scrubber.Group_.INLINE_,
929
'object': goog.labs.html.scrubber.Group_.BLOCK_ |
930
goog.labs.html.scrubber.Group_.INLINE_ |
931
goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
932
'ol': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
933
'optgroup': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
934
'option': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
935
'p': goog.labs.html.scrubber.Group_.INLINE_ |
936
goog.labs.html.scrubber.Group_.TABLE_ELEMENT_,
937
'pre': goog.labs.html.scrubber.Group_.INLINE_,
938
'q': goog.labs.html.scrubber.Group_.INLINE_,
939
's': goog.labs.html.scrubber.Group_.INLINE_,
940
'samp': goog.labs.html.scrubber.Group_.INLINE_,
941
'script': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
942
'select': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
943
'small': goog.labs.html.scrubber.Group_.INLINE_,
944
'span': goog.labs.html.scrubber.Group_.INLINE_,
945
'strike': goog.labs.html.scrubber.Group_.INLINE_,
946
'strong': goog.labs.html.scrubber.Group_.INLINE_,
947
'style': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
948
'sub': goog.labs.html.scrubber.Group_.INLINE_,
949
'sup': goog.labs.html.scrubber.Group_.INLINE_,
950
'table': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
951
goog.labs.html.scrubber.Group_.FORM_ELEMENT_,
952
'tbody': goog.labs.html.scrubber.Group_.TR_ELEMENT_,
953
'td': goog.labs.html.scrubber.Group_.BLOCK_ |
954
goog.labs.html.scrubber.Group_.INLINE_,
955
'textarea': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
956
'tfoot': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
957
goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
958
goog.labs.html.scrubber.Group_.TD_ELEMENT_,
959
'th': goog.labs.html.scrubber.Group_.BLOCK_ |
960
goog.labs.html.scrubber.Group_.INLINE_,
961
'thead': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
962
goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
963
goog.labs.html.scrubber.Group_.TD_ELEMENT_,
964
'title': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
965
'tr': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
966
goog.labs.html.scrubber.Group_.TD_ELEMENT_,
967
'tt': goog.labs.html.scrubber.Group_.INLINE_,
968
'u': goog.labs.html.scrubber.Group_.INLINE_,
969
'ul': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
970
'var': goog.labs.html.scrubber.Group_.INLINE_,
971
'xmp': goog.labs.html.scrubber.Group_.INLINE_
972
};
973
974
975
/**
976
* The scopes in which an element falls.
977
* No property defaults to 0.
978
* @private
979
*/
980
goog.labs.html.scrubber.ELEMENT_SCOPES_ = {
981
'applet': goog.labs.html.scrubber.Scope_.COMMON_ |
982
goog.labs.html.scrubber.Scope_.BUTTON_ |
983
goog.labs.html.scrubber.Scope_.LIST_ITEM_,
984
'button': goog.labs.html.scrubber.Scope_.BUTTON_,
985
'caption': goog.labs.html.scrubber.Scope_.COMMON_ |
986
goog.labs.html.scrubber.Scope_.BUTTON_ |
987
goog.labs.html.scrubber.Scope_.LIST_ITEM_,
988
'html': goog.labs.html.scrubber.Scope_.COMMON_ |
989
goog.labs.html.scrubber.Scope_.BUTTON_ |
990
goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
991
goog.labs.html.scrubber.Scope_.TABLE_,
992
'object': goog.labs.html.scrubber.Scope_.COMMON_ |
993
goog.labs.html.scrubber.Scope_.BUTTON_ |
994
goog.labs.html.scrubber.Scope_.LIST_ITEM_,
995
'ol': goog.labs.html.scrubber.Scope_.LIST_ITEM_,
996
'table': goog.labs.html.scrubber.Scope_.COMMON_ |
997
goog.labs.html.scrubber.Scope_.BUTTON_ |
998
goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
999
goog.labs.html.scrubber.Scope_.TABLE_,
1000
'td': goog.labs.html.scrubber.Scope_.COMMON_ |
1001
goog.labs.html.scrubber.Scope_.BUTTON_ |
1002
goog.labs.html.scrubber.Scope_.LIST_ITEM_,
1003
'th': goog.labs.html.scrubber.Scope_.COMMON_ |
1004
goog.labs.html.scrubber.Scope_.BUTTON_ |
1005
goog.labs.html.scrubber.Scope_.LIST_ITEM_,
1006
'ul': goog.labs.html.scrubber.Scope_.LIST_ITEM_
1007
};
1008
1009
1010
/**
1011
* Per-element, a child that can contain block content.
1012
* @private
1013
*/
1014
goog.labs.html.scrubber.BLOCK_CONTAINERS_ = {
1015
'dl': 'dd',
1016
'ol': 'li',
1017
'table': 'tr',
1018
'tr': 'td',
1019
'ul': 'li'
1020
};
1021
1022
1023
goog.labs.html.attributeRewriterPresubmitWorkaround();
1024
1025