diff --git a/packages/compiler/src/ml_parser/html_tags.ts b/packages/compiler/src/ml_parser/html_tags.ts
index 9d3da64f4e..0b49ab978d 100644
--- a/packages/compiler/src/ml_parser/html_tags.ts
+++ b/packages/compiler/src/ml_parser/html_tags.ts
@@ -69,63 +69,72 @@ export class HtmlTagDefinition implements TagDefinition {
}
}
+let _DEFAULT_TAG_DEFINITION !: HtmlTagDefinition;
+
// see http://www.w3.org/TR/html51/syntax.html#optional-tags
// This implementation does not fully conform to the HTML5 spec.
-const TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
- 'base': new HtmlTagDefinition({isVoid: true}),
- 'meta': new HtmlTagDefinition({isVoid: true}),
- 'area': new HtmlTagDefinition({isVoid: true}),
- 'embed': new HtmlTagDefinition({isVoid: true}),
- 'link': new HtmlTagDefinition({isVoid: true}),
- 'img': new HtmlTagDefinition({isVoid: true}),
- 'input': new HtmlTagDefinition({isVoid: true}),
- 'param': new HtmlTagDefinition({isVoid: true}),
- 'hr': new HtmlTagDefinition({isVoid: true}),
- 'br': new HtmlTagDefinition({isVoid: true}),
- 'source': new HtmlTagDefinition({isVoid: true}),
- 'track': new HtmlTagDefinition({isVoid: true}),
- 'wbr': new HtmlTagDefinition({isVoid: true}),
- 'p': new HtmlTagDefinition({
- closedByChildren: [
- 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form',
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr',
- 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'
- ],
- closedByParent: true
- }),
- 'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}),
- 'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}),
- 'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}),
- 'tr': new HtmlTagDefinition({
- closedByChildren: ['tr'],
- requiredParents: ['tbody', 'tfoot', 'thead'],
- closedByParent: true
- }),
- 'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
- 'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
- 'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
- 'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
- 'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
- 'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
- 'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
- 'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
- 'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
- 'rt': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
- 'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}),
- 'rp': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
- 'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}),
- 'option': new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}),
- 'pre': new HtmlTagDefinition({ignoreFirstLf: true}),
- 'listing': new HtmlTagDefinition({ignoreFirstLf: true}),
- 'style': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}),
- 'script': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}),
- 'title': new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT}),
- 'textarea':
- new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}),
-};
-
-const _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
+let TAG_DEFINITIONS !: {[key: string]: HtmlTagDefinition};
export function getHtmlTagDefinition(tagName: string): HtmlTagDefinition {
+ if (!TAG_DEFINITIONS) {
+ _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
+ TAG_DEFINITIONS = {
+ 'base': new HtmlTagDefinition({isVoid: true}),
+ 'meta': new HtmlTagDefinition({isVoid: true}),
+ 'area': new HtmlTagDefinition({isVoid: true}),
+ 'embed': new HtmlTagDefinition({isVoid: true}),
+ 'link': new HtmlTagDefinition({isVoid: true}),
+ 'img': new HtmlTagDefinition({isVoid: true}),
+ 'input': new HtmlTagDefinition({isVoid: true}),
+ 'param': new HtmlTagDefinition({isVoid: true}),
+ 'hr': new HtmlTagDefinition({isVoid: true}),
+ 'br': new HtmlTagDefinition({isVoid: true}),
+ 'source': new HtmlTagDefinition({isVoid: true}),
+ 'track': new HtmlTagDefinition({isVoid: true}),
+ 'wbr': new HtmlTagDefinition({isVoid: true}),
+ 'p': new HtmlTagDefinition({
+ closedByChildren: [
+ 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
+ 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
+ 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
+ 'p', 'pre', 'section', 'table', 'ul'
+ ],
+ closedByParent: true
+ }),
+ 'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}),
+ 'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}),
+ 'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}),
+ 'tr': new HtmlTagDefinition({
+ closedByChildren: ['tr'],
+ requiredParents: ['tbody', 'tfoot', 'thead'],
+ closedByParent: true
+ }),
+ 'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
+ 'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
+ 'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
+ 'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
+ 'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
+ 'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
+ 'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
+ 'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
+ 'rb': new HtmlTagDefinition(
+ {closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
+ 'rt': new HtmlTagDefinition(
+ {closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
+ 'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}),
+ 'rp': new HtmlTagDefinition(
+ {closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}),
+ 'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}),
+ 'option':
+ new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}),
+ 'pre': new HtmlTagDefinition({ignoreFirstLf: true}),
+ 'listing': new HtmlTagDefinition({ignoreFirstLf: true}),
+ 'style': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}),
+ 'script': new HtmlTagDefinition({contentType: TagContentType.RAW_TEXT}),
+ 'title': new HtmlTagDefinition({contentType: TagContentType.ESCAPABLE_RAW_TEXT}),
+ 'textarea': new HtmlTagDefinition(
+ {contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true}),
+ };
+ }
return TAG_DEFINITIONS[tagName.toLowerCase()] || _DEFAULT_TAG_DEFINITION;
}
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index 28968f4c88..09827c1cbc 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -1078,8 +1078,14 @@ export class BindingScope implements LocalResolver {
private map = new Map();
private referenceNameIndex = 0;
private restoreViewVariable: o.ReadVarExpr|null = null;
+ private static _ROOT_SCOPE: BindingScope;
- static ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event'));
+ static get ROOT_SCOPE(): BindingScope {
+ if (!BindingScope._ROOT_SCOPE) {
+ BindingScope._ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event'));
+ }
+ return BindingScope._ROOT_SCOPE;
+ }
private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {}
diff --git a/packages/compiler/src/schema/dom_element_schema_registry.ts b/packages/compiler/src/schema/dom_element_schema_registry.ts
index 7382670a94..9562a3c0d2 100644
--- a/packages/compiler/src/schema/dom_element_schema_registry.ts
+++ b/packages/compiler/src/schema/dom_element_schema_registry.ts
@@ -342,11 +342,11 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
// property names do not have a security impact.
tagName = tagName.toLowerCase();
propName = propName.toLowerCase();
- let ctx = SECURITY_SCHEMA[tagName + '|' + propName];
+ let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
if (ctx) {
return ctx;
}
- ctx = SECURITY_SCHEMA['*|' + propName];
+ ctx = SECURITY_SCHEMA()['*|' + propName];
return ctx ? ctx : SecurityContext.NONE;
}
diff --git a/packages/compiler/src/schema/dom_security_schema.ts b/packages/compiler/src/schema/dom_security_schema.ts
index 0843a95840..73bff2233e 100644
--- a/packages/compiler/src/schema/dom_security_schema.ts
+++ b/packages/compiler/src/schema/dom_security_schema.ts
@@ -20,39 +20,45 @@ import {SecurityContext} from '../core';
// =================================================================================================
/** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */
-export const SECURITY_SCHEMA: {[k: string]: SecurityContext} = {};
+let _SECURITY_SCHEMA !: {[k: string]: SecurityContext};
-function registerContext(ctx: SecurityContext, specs: string[]) {
- for (const spec of specs) SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
+export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} {
+ if (!_SECURITY_SCHEMA) {
+ _SECURITY_SCHEMA = {};
+ // Case is insignificant below, all element and attribute names are lower-cased for lookup.
+
+ registerContext(SecurityContext.HTML, [
+ 'iframe|srcdoc',
+ '*|innerHTML',
+ '*|outerHTML',
+ ]);
+ registerContext(SecurityContext.STYLE, ['*|style']);
+ // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
+ registerContext(SecurityContext.URL, [
+ '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
+ 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
+ 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
+ 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
+ ]);
+ registerContext(SecurityContext.RESOURCE_URL, [
+ 'applet|code',
+ 'applet|codebase',
+ 'base|href',
+ 'embed|src',
+ 'frame|src',
+ 'head|profile',
+ 'html|manifest',
+ 'iframe|src',
+ 'link|href',
+ 'media|src',
+ 'object|codebase',
+ 'object|data',
+ 'script|src',
+ ]);
+ }
+ return _SECURITY_SCHEMA;
}
-// Case is insignificant below, all element and attribute names are lower-cased for lookup.
-
-registerContext(SecurityContext.HTML, [
- 'iframe|srcdoc',
- '*|innerHTML',
- '*|outerHTML',
-]);
-registerContext(SecurityContext.STYLE, ['*|style']);
-// NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
-registerContext(SecurityContext.URL, [
- '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
- 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
- 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
- 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
-]);
-registerContext(SecurityContext.RESOURCE_URL, [
- 'applet|code',
- 'applet|codebase',
- 'base|href',
- 'embed|src',
- 'frame|src',
- 'head|profile',
- 'html|manifest',
- 'iframe|src',
- 'link|href',
- 'media|src',
- 'object|codebase',
- 'object|data',
- 'script|src',
-]);
+function registerContext(ctx: SecurityContext, specs: string[]) {
+ for (const spec of specs) _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
+}
diff --git a/packages/compiler/src/template_parser/template_parser.ts b/packages/compiler/src/template_parser/template_parser.ts
index e7a26c865f..a646856cc8 100644
--- a/packages/compiler/src/template_parser/template_parser.ts
+++ b/packages/compiler/src/template_parser/template_parser.ts
@@ -57,7 +57,13 @@ const IDENT_EVENT_IDX = 10;
const TEMPLATE_ATTR_PREFIX = '*';
const CLASS_ATTR = 'class';
-const TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
+let _TEXT_CSS_SELECTOR !: CssSelector;
+function TEXT_CSS_SELECTOR(): CssSelector {
+ if (!_TEXT_CSS_SELECTOR) {
+ _TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
+ }
+ return _TEXT_CSS_SELECTOR;
+}
export class TemplateParseError extends ParseError {
constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) {
@@ -227,7 +233,7 @@ class TemplateParseVisitor implements html.Visitor {
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return null; }
visitText(text: html.Text, parent: ElementContext): any {
- const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
+ const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !;
const valueNoNgsp = replaceNgsp(text.value);
const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !);
return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan !) :
@@ -775,7 +781,7 @@ class NonBindableVisitor implements html.Visitor {
}
visitText(text: html.Text, parent: ElementContext): t.TextAst {
- const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
+ const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR()) !;
return new t.TextAst(text.value, ngContentIndex, text.sourceSpan !);
}
diff --git a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json
index 73854eced0..8fa753be8a 100644
--- a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json
@@ -1439,9 +1439,6 @@
{
"name": "SyncAsync"
},
- {
- "name": "TAG_DEFINITIONS"
- },
{
"name": "TAG_TO_PLACEHOLDER_NAMES"
},
@@ -1730,9 +1727,6 @@
{
"name": "_DEFAULT_SOURCE_LANG$1"
},
- {
- "name": "_DEFAULT_TAG_DEFINITION"
- },
{
"name": "_DOCTYPE"
},
@@ -3509,6 +3503,9 @@
{
"name": "refCount"
},
+ {
+ "name": "registerContext"
+ },
{
"name": "registerModuleFactory"
},