feat(ivy): added new namespace and element instructions to JIT environment (#23899)
PR Close #23899
This commit is contained in:
		
							parent
							
								
									1007d1ad27
								
							
						
					
					
						commit
						acf270d724
					
				| @ -136,7 +136,8 @@ export function compileComponentFromMetadata( | |||||||
|   const templateFunctionExpression = |   const templateFunctionExpression = | ||||||
|       new TemplateDefinitionBuilder( |       new TemplateDefinitionBuilder( | ||||||
|           constantPool, CONTEXT_NAME, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName, |           constantPool, CONTEXT_NAME, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName, | ||||||
|           meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed) |           meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, | ||||||
|  |           R3.namespaceHTML) | ||||||
|           .buildTemplateFunction( |           .buildTemplateFunction( | ||||||
|               template.nodes, [], template.hasNgContent, template.ngContentSelectors); |               template.nodes, [], template.hasNgContent, template.ngContentSelectors); | ||||||
| 
 | 
 | ||||||
| @ -443,4 +444,4 @@ function typeMapToExpressionMap( | |||||||
|   const entries = Array.from(map).map( |   const entries = Array.from(map).map( | ||||||
|       ([key, type]): [string, o.Expression] => [key, outputCtx.importExpr(type)]); |       ([key, type]): [string, o.Expression] => [key, outputCtx.importExpr(type)]); | ||||||
|   return new Map(entries); |   return new Map(entries); | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,8 +18,8 @@ import * as html from '../../ml_parser/ast'; | |||||||
| import {HtmlParser} from '../../ml_parser/html_parser'; | import {HtmlParser} from '../../ml_parser/html_parser'; | ||||||
| import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces'; | import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces'; | ||||||
| import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; | import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; | ||||||
|  | import {splitNsName} from '../../ml_parser/tags'; | ||||||
| import * as o from '../../output/output_ast'; | import * as o from '../../output/output_ast'; | ||||||
| import {ExternalReference} from '../../output/output_ast'; |  | ||||||
| import {ParseError, ParseSourceSpan} from '../../parse_util'; | import {ParseError, ParseSourceSpan} from '../../parse_util'; | ||||||
| import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; | import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; | ||||||
| import {CssSelector, SelectorMatcher} from '../../selector'; | import {CssSelector, SelectorMatcher} from '../../selector'; | ||||||
| @ -52,7 +52,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|   private _valueConverter: ValueConverter; |   private _valueConverter: ValueConverter; | ||||||
|   private _unsupported = unsupported; |   private _unsupported = unsupported; | ||||||
|   private _bindingScope: BindingScope; |   private _bindingScope: BindingScope; | ||||||
|   private _namespace = R3.namespaceHTML; |  | ||||||
| 
 | 
 | ||||||
|   // Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
 |   // Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
 | ||||||
|   private _inI18nSection: boolean = false; |   private _inI18nSection: boolean = false; | ||||||
| @ -68,7 +67,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|       parentBindingScope: BindingScope, private level = 0, private contextName: string|null, |       parentBindingScope: BindingScope, private level = 0, private contextName: string|null, | ||||||
|       private templateName: string|null, private viewQueries: R3QueryMetadata[], |       private templateName: string|null, private viewQueries: R3QueryMetadata[], | ||||||
|       private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>, |       private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>, | ||||||
|       private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>) { |       private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>, | ||||||
|  |       private _namespace: o.ExternalReference) { | ||||||
|     this._bindingScope = |     this._bindingScope = | ||||||
|         parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => { |         parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => { | ||||||
|           this._bindingCode.push( |           this._bindingCode.push( | ||||||
| @ -91,6 +91,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|   buildTemplateFunction( |   buildTemplateFunction( | ||||||
|       nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false, |       nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false, | ||||||
|       ngContentSelectors: string[] = []): o.FunctionExpr { |       ngContentSelectors: string[] = []): o.FunctionExpr { | ||||||
|  |     if (this._namespace !== R3.namespaceHTML) { | ||||||
|  |       this.instruction(this._creationCode, null, this._namespace); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Create variable bindings
 |     // Create variable bindings
 | ||||||
|     for (const variable of variables) { |     for (const variable of variables) { | ||||||
|       const variableName = variable.name; |       const variableName = variable.name; | ||||||
| @ -224,20 +228,20 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Gets the namespace instruction function based on the current element |    * Gets the namespace instruction function based on the current element | ||||||
|    * @param ivyElementName An system element name, can include colons like :svg:svg |    * @param namespaceKey A system key for a namespace (e.g. 'svg' or 'math') | ||||||
|    */ |    */ | ||||||
|   getNamespaceInstruction(ivyElementName: string) { |   getNamespaceInstruction(namespaceKey: string|null) { | ||||||
|     switch (ivyElementName) { |     switch (namespaceKey) { | ||||||
|       case ':svg:svg': |       case 'svg': | ||||||
|         return R3.namespaceSVG; |         return R3.namespaceSVG; | ||||||
|       case ':math:math': |       case 'math': | ||||||
|         return R3.namespaceMathML; |         return R3.namespaceMathML; | ||||||
|       default: |       default: | ||||||
|         return this._namespace; |         return R3.namespaceHTML; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   addNamespaceInstruction(nsInstruction: ExternalReference, element: t.Element) { |   addNamespaceInstruction(nsInstruction: o.ExternalReference, element: t.Element) { | ||||||
|     this._namespace = nsInstruction; |     this._namespace = nsInstruction; | ||||||
|     this.instruction(this._creationCode, element.sourceSpan, nsInstruction); |     this.instruction(this._creationCode, element.sourceSpan, nsInstruction); | ||||||
|   } |   } | ||||||
| @ -249,7 +253,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
| 
 | 
 | ||||||
|     const outputAttrs: {[name: string]: string} = {}; |     const outputAttrs: {[name: string]: string} = {}; | ||||||
|     const attrI18nMetas: {[name: string]: string} = {}; |     const attrI18nMetas: {[name: string]: string} = {}; | ||||||
|     let i18nMeta: string = ''; |     let i18nMeta = ''; | ||||||
|  | 
 | ||||||
|  |     const [namespaceKey, elementName] = splitNsName(element.name); | ||||||
| 
 | 
 | ||||||
|     // Elements inside i18n sections are replaced with placeholders
 |     // Elements inside i18n sections are replaced with placeholders
 | ||||||
|     // TODO(vicb): nested elements are a WIP in this phase
 |     // TODO(vicb): nested elements are a WIP in this phase
 | ||||||
| @ -291,7 +297,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     // Element creation mode
 |     // Element creation mode
 | ||||||
|     const parameters: o.Expression[] = [ |     const parameters: o.Expression[] = [ | ||||||
|       o.literal(elementIndex), |       o.literal(elementIndex), | ||||||
|       o.literal(element.name), |       o.literal(elementName), | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     // Add the attributes
 |     // Add the attributes
 | ||||||
| @ -343,7 +349,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     const implicit = o.variable(CONTEXT_NAME); |     const implicit = o.variable(CONTEXT_NAME); | ||||||
| 
 | 
 | ||||||
|     const wasInNamespace = this._namespace; |     const wasInNamespace = this._namespace; | ||||||
|     const currentNamespace = this.getNamespaceInstruction(element.name); |     const currentNamespace = this.getNamespaceInstruction(namespaceKey); | ||||||
|  | 
 | ||||||
|     // If the namespace is changing now, include an instruction to change it
 |     // If the namespace is changing now, include an instruction to change it
 | ||||||
|     // during element creation.
 |     // during element creation.
 | ||||||
|     if (currentNamespace !== wasInNamespace) { |     if (currentNamespace !== wasInNamespace) { | ||||||
| @ -359,14 +366,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|           this._creationCode, element.sourceSpan, R3.elementStart, |           this._creationCode, element.sourceSpan, R3.elementStart, | ||||||
|           ...trimTrailingNulls(parameters)); |           ...trimTrailingNulls(parameters)); | ||||||
| 
 | 
 | ||||||
|       // If the element happens to be an SVG <foreignObject>, we need to switch
 |  | ||||||
|       // to the HTML namespace inside of it
 |  | ||||||
|       if (element.name === ':svg:foreignObject') { |  | ||||||
|         // NOTE(benlesh): this may cause extremem corner-case bugs if someone was to do something
 |  | ||||||
|         //   like <math>...<foreignObject></foreignObject>...</math>.
 |  | ||||||
|         this.addNamespaceInstruction(R3.namespaceHTML, element); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // Generate Listeners (outputs)
 |       // Generate Listeners (outputs)
 | ||||||
|       element.outputs.forEach((outputAst: t.BoundEvent) => { |       element.outputs.forEach((outputAst: t.BoundEvent) => { | ||||||
|         const elName = sanitizeIdentifier(element.name); |         const elName = sanitizeIdentifier(element.name); | ||||||
| @ -423,7 +422,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     } |     } | ||||||
|     // Restore the state before exiting this node
 |     // Restore the state before exiting this node
 | ||||||
|     this._inI18nSection = wasInI18nSection; |     this._inI18nSection = wasInI18nSection; | ||||||
|     this._namespace = wasInNamespace; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   visitTemplate(template: t.Template) { |   visitTemplate(template: t.Template) { | ||||||
| @ -484,7 +482,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     // Create the template function
 |     // Create the template function
 | ||||||
|     const templateVisitor = new TemplateDefinitionBuilder( |     const templateVisitor = new TemplateDefinitionBuilder( | ||||||
|         this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName, |         this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName, | ||||||
|         templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes); |         templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, | ||||||
|  |         this._namespace); | ||||||
|     const templateFunctionExpr = |     const templateFunctionExpr = | ||||||
|         templateVisitor.buildTemplateFunction(template.children, template.variables); |         templateVisitor.buildTemplateFunction(template.children, template.variables); | ||||||
|     this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null)); |     this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null)); | ||||||
|  | |||||||
| @ -95,8 +95,8 @@ describe('compiler compliance', () => { | |||||||
|           if (rf & 1) { |           if (rf & 1) { | ||||||
|             $r3$.ɵE(0, 'div', $e0_attrs$); |             $r3$.ɵE(0, 'div', $e0_attrs$); | ||||||
|             $r3$.ɵNS(); |             $r3$.ɵNS(); | ||||||
|             $r3$.ɵE(1, ':svg:svg'); |             $r3$.ɵE(1, 'svg'); | ||||||
|             $r3$.ɵEe(2, ':svg:circle', $e2_attrs$); |             $r3$.ɵEe(2, 'circle', $e2_attrs$); | ||||||
|             $r3$.ɵe(); |             $r3$.ɵe(); | ||||||
|             $r3$.ɵe(); |             $r3$.ɵe(); | ||||||
|           } |           } | ||||||
| @ -110,6 +110,54 @@ describe('compiler compliance', () => { | |||||||
|       expectEmit(result.source, template, 'Incorrect template'); |       expectEmit(result.source, template, 'Incorrect template'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('should enter and leave the SVG namespace appropriately', () => { | ||||||
|  |       const files = { | ||||||
|  |         app: { | ||||||
|  |           'spec.ts': ` | ||||||
|  |               import {Component, NgModule} from '@angular/core'; | ||||||
|  | 
 | ||||||
|  |               @Component({ | ||||||
|  |                 selector: 'my-component', | ||||||
|  |                 template: \`<div class="my-app" title="Hello"><svg><circle cx="50" cy="100" r="25"/></svg><p>TEST 2</p></div>\` | ||||||
|  |               }) | ||||||
|  |               export class MyComponent {} | ||||||
|  | 
 | ||||||
|  |               @NgModule({declarations: [MyComponent]}) | ||||||
|  |               export class MyModule {} | ||||||
|  |           ` | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       // The factory should look like this:
 | ||||||
|  |       const factory = 'factory: function MyComponent_Factory() { return new MyComponent(); }'; | ||||||
|  | 
 | ||||||
|  |       // The template should look like this (where IDENT is a wild card for an identifier):
 | ||||||
|  |       const template = ` | ||||||
|  |         const $c1$ = ['class', 'my-app', 'title', 'Hello']; | ||||||
|  |         … | ||||||
|  |         template: function MyComponent_Template(rf: IDENT, ctx: IDENT) { | ||||||
|  |           if (rf & 1) { | ||||||
|  |             $r3$.ɵE(0, 'div', $e0_attrs$); | ||||||
|  |               $r3$.ɵNS(); | ||||||
|  |               $r3$.ɵE(1, 'svg'); | ||||||
|  |                 $r3$.ɵEe(2, 'circle', $e2_attrs$); | ||||||
|  |               $r3$.ɵe(); | ||||||
|  |               $r3$.ɵNH(); | ||||||
|  |               $r3$.ɵE(3, 'p'); | ||||||
|  |                 $r3$.ɵT(4, 'TEST 2'); | ||||||
|  |               $r3$.ɵe(); | ||||||
|  |             $r3$.ɵe(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       `;
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |       const result = compile(files, angularFiles); | ||||||
|  | 
 | ||||||
|  |       expectEmit(result.source, factory, 'Incorrect factory'); | ||||||
|  |       expectEmit(result.source, template, 'Incorrect template'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it('should bind to element properties', () => { |     it('should bind to element properties', () => { | ||||||
|       const files = { |       const files = { | ||||||
|         app: { |         app: { | ||||||
| @ -1274,7 +1322,7 @@ describe('compiler compliance', () => { | |||||||
|         expectEmit(source, MyComponentDefinition, 'Invalid component definition'); |         expectEmit(source, MyComponentDefinition, 'Invalid component definition'); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should support a let variable and reference for SVG', () => { |       it('should support embedded views in the SVG namespace', () => { | ||||||
|         const files = { |         const files = { | ||||||
|           app: { |           app: { | ||||||
|             ...shared, |             ...shared, | ||||||
| @ -1321,15 +1369,16 @@ describe('compiler compliance', () => { | |||||||
|             template: function MyComponent_Template(rf:IDENT,ctx:IDENT){ |             template: function MyComponent_Template(rf:IDENT,ctx:IDENT){ | ||||||
|               if (rf & 1) { |               if (rf & 1) { | ||||||
|                 $r3$.ɵNS(); |                 $r3$.ɵNS(); | ||||||
|                 $r3$.ɵE(0,':svg:svg'); |                 $r3$.ɵE(0,'svg'); | ||||||
|                 $r3$.ɵC(1,MyComponent__svg_g_Template_1,null,$_c0$); |                 $r3$.ɵC(1,MyComponent__svg_g_Template_1,null,$_c0$); | ||||||
|                 $r3$.ɵe(); |                 $r3$.ɵe(); | ||||||
|               } |               } | ||||||
|               if (rf & 2) { $r3$.ɵp(1,'forOf',$r3$.ɵb(ctx.items)); } |               if (rf & 2) { $r3$.ɵp(1,'forOf',$r3$.ɵb(ctx.items)); } | ||||||
|               function MyComponent__svg_g_Template_1(rf:IDENT,ctx0:IDENT) { |               function MyComponent__svg_g_Template_1(rf:IDENT,ctx0:IDENT) { | ||||||
|                 if (rf & 1) { |                 if (rf & 1) { | ||||||
|                   $r3$.ɵE(0,':svg:g'); |                   $r3$.ɵNS(); | ||||||
|                   $r3$.ɵEe(1,':svg:circle'); |                   $r3$.ɵE(0,'g'); | ||||||
|  |                   $r3$.ɵEe(1,'circle'); | ||||||
|                   $r3$.ɵe(); |                   $r3$.ɵe(); | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
|  | |||||||
| @ -45,7 +45,6 @@ export { | |||||||
|   i7 as ɵi7, |   i7 as ɵi7, | ||||||
|   i8 as ɵi8, |   i8 as ɵi8, | ||||||
|   iV as ɵiV, |   iV as ɵiV, | ||||||
|   N as ɵN, |  | ||||||
|   NH as ɵNH, |   NH as ɵNH, | ||||||
|   NM as ɵNM, |   NM as ɵNM, | ||||||
|   NS as ɵNS, |   NS as ɵNS, | ||||||
|  | |||||||
| @ -150,7 +150,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | |||||||
| | `{{ ['literal', exp ] }}`                   |  ✅     |  ✅      |  ✅      | | | `{{ ['literal', exp ] }}`                   |  ✅     |  ✅      |  ✅      | | ||||||
| | `{{ { a: 'literal', b: exp } }}`            |  ✅     |  ✅      |  ✅      | | | `{{ { a: 'literal', b: exp } }}`            |  ✅     |  ✅      |  ✅      | | ||||||
| | `{{ exp \| pipe: arg }}`                    |  ✅     |  ✅      |  ✅      | | | `{{ exp \| pipe: arg }}`                    |  ✅     |  ✅      |  ✅      | | ||||||
| | `<svg:g svg:p>`                             |  ✅     |  ✅      |  ❌      | | | `<svg:g svg:p>`                             |  ✅     |  ✅      |  ✅      | | ||||||
| | `<img src=[userData]>` sanitization         |  ❌     |  ❌      |  ❌      | | | `<img src=[userData]>` sanitization         |  ❌     |  ❌      |  ❌      | | ||||||
| | `<div (nocd.click)>`                        |  ❌     |  ❌      |  ❌      | | | `<div (nocd.click)>`                        |  ❌     |  ❌      |  ❌      | | ||||||
| | `<div (bubble.click)>`                      |  ❌     |  ❌      |  ❌      | | | `<div (bubble.click)>`                      |  ❌     |  ❌      |  ❌      | | ||||||
|  | |||||||
| @ -261,8 +261,8 @@ export function injectAttribute(attrNameToInject: string): string|undefined { | |||||||
|   if (attrs) { |   if (attrs) { | ||||||
|     for (let i = 0; i < attrs.length; i = i + 2) { |     for (let i = 0; i < attrs.length; i = i + 2) { | ||||||
|       let attrName = attrs[i]; |       let attrName = attrs[i]; | ||||||
|       if (attrName === AttributeMarker.SELECT_ONLY) break; |       if (attrName === AttributeMarker.SelectOnly) break; | ||||||
|       if (attrName === AttributeMarker.NAMESPACE_URI) { |       if (attrName === AttributeMarker.NamespaceUri) { | ||||||
|         attrName = attrs[i += 2]; |         attrName = attrs[i += 2]; | ||||||
|       } |       } | ||||||
|       if (attrName == attrNameToInject) { |       if (attrName == attrNameToInject) { | ||||||
|  | |||||||
| @ -54,7 +54,6 @@ export { | |||||||
|   elementStyle as s, |   elementStyle as s, | ||||||
|   elementStyleNamed as sn, |   elementStyleNamed as sn, | ||||||
| 
 | 
 | ||||||
|   namespace as N, |  | ||||||
|   namespaceHTML as NH, |   namespaceHTML as NH, | ||||||
|   namespaceMathML as NM, |   namespaceMathML as NM, | ||||||
|   namespaceSVG as NS, |   namespaceSVG as NS, | ||||||
|  | |||||||
| @ -560,15 +560,6 @@ function getRenderFlags(view: LView): RenderFlags { | |||||||
| //////////////////////////
 | //////////////////////////
 | ||||||
| let _currentNamespace: string|null = null; | let _currentNamespace: string|null = null; | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * Sets the namespace URI that will be used to create elements in {@link element} |  | ||||||
|  * and {@link elementStart} |  | ||||||
|  * @param uri the full namespaceUri |  | ||||||
|  */ |  | ||||||
| export function namespace(uri: string | null) { |  | ||||||
|   _currentNamespace = uri; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Sets the current namespace URI to null, meaning createElement (not createElementNS) |  * Sets the current namespace URI to null, meaning createElement (not createElementNS) | ||||||
|  * will be used to create elements in {@link element} and {@link elementStart} |  * will be used to create elements in {@link element} and {@link elementStart} | ||||||
| @ -617,9 +608,17 @@ export function elementStart( | |||||||
| 
 | 
 | ||||||
|   ngDevMode && ngDevMode.rendererCreateElement++; |   ngDevMode && ngDevMode.rendererCreateElement++; | ||||||
| 
 | 
 | ||||||
|   const native: RElement = _currentNamespace === null || isProceduralRenderer(renderer) ? |   let native: RElement; | ||||||
|       renderer.createElement(name) : | 
 | ||||||
|       (renderer as ObjectOrientedRenderer3).createElementNS(_currentNamespace, name); |   if (isProceduralRenderer(renderer)) { | ||||||
|  |     native = renderer.createElement(name, _currentNamespace); | ||||||
|  |   } else { | ||||||
|  |     if (_currentNamespace === null) { | ||||||
|  |       native = renderer.createElement(name); | ||||||
|  |     } else { | ||||||
|  |       native = renderer.createElementNS(_currentNamespace, name); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   ngDevMode && assertDataInRange(index - 1); |   ngDevMode && assertDataInRange(index - 1); | ||||||
| 
 | 
 | ||||||
| @ -861,7 +860,7 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void { | |||||||
|   const isProc = isProceduralRenderer(renderer); |   const isProc = isProceduralRenderer(renderer); | ||||||
|   for (let i = 0; i < attrs.length; i += 2) { |   for (let i = 0; i < attrs.length; i += 2) { | ||||||
|     let attrName = attrs[i]; |     let attrName = attrs[i]; | ||||||
|     if (attrName === AttributeMarker.NAMESPACE_URI) { |     if (attrName === AttributeMarker.NamespaceUri) { | ||||||
|       const attrNS = attrs[i + 1] as string; |       const attrNS = attrs[i + 1] as string; | ||||||
|       attrName = attrs[i + 2] as string; |       attrName = attrs[i + 2] as string; | ||||||
|       const attrVal = attrs[i + 3] as string; |       const attrVal = attrs[i + 3] as string; | ||||||
| @ -872,7 +871,7 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void { | |||||||
|         native.setAttributeNS(attrNS, attrName, attrVal); |         native.setAttributeNS(attrNS, attrName, attrVal); | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       if (attrName === AttributeMarker.SELECT_ONLY) break; |       if (attrName === AttributeMarker.SelectOnly) break; | ||||||
|       if (attrName !== NG_PROJECT_AS_ATTR_NAME) { |       if (attrName !== NG_PROJECT_AS_ATTR_NAME) { | ||||||
|         const attrVal = attrs[i + 1]; |         const attrVal = attrs[i + 1]; | ||||||
|         ngDevMode && ngDevMode.rendererSetAttribute++; |         ngDevMode && ngDevMode.rendererSetAttribute++; | ||||||
| @ -1533,11 +1532,11 @@ function generateInitialInputs( | |||||||
|   const attrs = tNode.attrs !; |   const attrs = tNode.attrs !; | ||||||
|   for (let i = 0; i < attrs.length; i += 2) { |   for (let i = 0; i < attrs.length; i += 2) { | ||||||
|     const first = attrs[i]; |     const first = attrs[i]; | ||||||
|     const attrName = first === AttributeMarker.NAMESPACE_URI ? attrs[i += 2] : first; |     const attrName = first === AttributeMarker.NamespaceUri ? attrs[i += 2] : first; | ||||||
|     const minifiedInputName = inputs[attrName]; |     const minifiedInputName = inputs[attrName]; | ||||||
|     const attrValue = attrs[i + 1]; |     const attrValue = attrs[i + 1]; | ||||||
| 
 | 
 | ||||||
|     if (attrName === AttributeMarker.SELECT_ONLY) break; |     if (attrName === AttributeMarker.SelectOnly) break; | ||||||
|     if (minifiedInputName !== undefined) { |     if (minifiedInputName !== undefined) { | ||||||
|       const inputsToStore: InitialInputs = |       const inputsToStore: InitialInputs = | ||||||
|           initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []); |           initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []); | ||||||
|  | |||||||
| @ -164,7 +164,7 @@ export const enum AttributeMarker { | |||||||
|    * Use the next value as the full namespaces URI, the values after that |    * Use the next value as the full namespaces URI, the values after that | ||||||
|    * are then the name and the value, respectively. |    * are then the name and the value, respectively. | ||||||
|    */ |    */ | ||||||
|   NAMESPACE_URI = 0,  // namespace. Has to be repeated.
 |   NamespaceUri = 0,  // namespace. Has to be repeated.
 | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * This marker indicates that the following attribute names were extracted from bindings (ex.: |    * This marker indicates that the following attribute names were extracted from bindings (ex.: | ||||||
| @ -172,7 +172,7 @@ export const enum AttributeMarker { | |||||||
|    * Taking the above bindings and outputs as an example an attributes array could look as follows: |    * Taking the above bindings and outputs as an example an attributes array could look as follows: | ||||||
|    * ['class', 'fade in', AttributeMarker.SELECT_ONLY, 'foo', 'bar'] |    * ['class', 'fade in', AttributeMarker.SELECT_ONLY, 'foo', 'bar'] | ||||||
|    */ |    */ | ||||||
|   SELECT_ONLY = 1 |   SelectOnly = 1 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -37,6 +37,7 @@ export const angularCoreEnv: {[name: string]: Function} = { | |||||||
|   'ɵcr': r3.cr, |   'ɵcr': r3.cr, | ||||||
|   'ɵd': r3.d, |   'ɵd': r3.d, | ||||||
|   'ɵE': r3.E, |   'ɵE': r3.E, | ||||||
|  |   'ɵEe': r3.Ee, | ||||||
|   'ɵe': r3.e, |   'ɵe': r3.e, | ||||||
|   'ɵf0': r3.f0, |   'ɵf0': r3.f0, | ||||||
|   'ɵf1': r3.f1, |   'ɵf1': r3.f1, | ||||||
| @ -60,6 +61,9 @@ export const angularCoreEnv: {[name: string]: Function} = { | |||||||
|   'ɵkn': r3.kn, |   'ɵkn': r3.kn, | ||||||
|   'ɵL': r3.L, |   'ɵL': r3.L, | ||||||
|   'ɵld': r3.ld, |   'ɵld': r3.ld, | ||||||
|  |   'ɵNH': r3.NH, | ||||||
|  |   'ɵNM': r3.NM, | ||||||
|  |   'ɵNS': r3.NS, | ||||||
|   'ɵp': r3.p, |   'ɵp': r3.p, | ||||||
|   'ɵpb1': r3.pb1, |   'ɵpb1': r3.pb1, | ||||||
|   'ɵpb2': r3.pb2, |   'ɵpb2': r3.pb2, | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ export function isNodeMatchingSelector(tNode: TNode, selector: CssSelector): boo | |||||||
| 
 | 
 | ||||||
|   let mode: SelectorFlags = SelectorFlags.ELEMENT; |   let mode: SelectorFlags = SelectorFlags.ELEMENT; | ||||||
|   const nodeAttrs = tNode.attrs !; |   const nodeAttrs = tNode.attrs !; | ||||||
|   const selectOnlyMarkerIdx = nodeAttrs ? nodeAttrs.indexOf(AttributeMarker.SELECT_ONLY) : -1; |   const selectOnlyMarkerIdx = nodeAttrs ? nodeAttrs.indexOf(AttributeMarker.SelectOnly) : -1; | ||||||
| 
 | 
 | ||||||
|   // When processing ":not" selectors, we skip to the next ":not" if the
 |   // When processing ":not" selectors, we skip to the next ":not" if the
 | ||||||
|   // current one doesn't match
 |   // current one doesn't match
 | ||||||
| @ -107,11 +107,11 @@ function findAttrIndexInNode(name: string, attrs: TAttributes | null): number { | |||||||
|   if (attrs === null) return -1; |   if (attrs === null) return -1; | ||||||
|   for (let i = 0; i < attrs.length; i += step) { |   for (let i = 0; i < attrs.length; i += step) { | ||||||
|     const attrName = attrs[i]; |     const attrName = attrs[i]; | ||||||
|     if (attrName === AttributeMarker.NAMESPACE_URI) { |     if (attrName === AttributeMarker.NamespaceUri) { | ||||||
|       step = 2; |       step = 2; | ||||||
|     } else if (attrName === name) { |     } else if (attrName === name) { | ||||||
|       return i; |       return i; | ||||||
|     } else if (attrName === AttributeMarker.SELECT_ONLY) { |     } else if (attrName === AttributeMarker.SelectOnly) { | ||||||
|       step = 1; |       step = 1; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -165,6 +165,7 @@ describe('template variables', () => { | |||||||
|           } |           } | ||||||
|           function MyComponent__svg_g_Template_1(rf: $RenderFlags$, ctx0: $MyComponent$) { |           function MyComponent__svg_g_Template_1(rf: $RenderFlags$, ctx0: $MyComponent$) { | ||||||
|             if (rf & 1) { |             if (rf & 1) { | ||||||
|  |               $r3$.ɵNS(); | ||||||
|               $r3$.ɵE(0, 'g'); |               $r3$.ɵE(0, 'g'); | ||||||
|               $r3$.ɵEe(1, 'circle'); |               $r3$.ɵEe(1, 'circle'); | ||||||
|               $r3$.ɵe(); |               $r3$.ɵe(); | ||||||
| @ -180,7 +181,7 @@ describe('template variables', () => { | |||||||
|         [ForOfDirective.ngDirectiveDef]; |         [ForOfDirective.ngDirectiveDef]; | ||||||
|     // /NON-NORMATIVE
 |     // /NON-NORMATIVE
 | ||||||
| 
 | 
 | ||||||
|     // TODO(chuckj): update when the changes to enable ngForOf lands.
 |     // TODO(benlesh): update when the changes to enable ngForOf lands.
 | ||||||
|     expect(toHtml(renderComponent(MyComponent))).toEqual('<svg></svg>'); |     expect(toHtml(renderComponent(MyComponent))).toEqual('<svg></svg>'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -605,7 +605,7 @@ describe('content projection', () => { | |||||||
|         if (rf & RenderFlags.Create) { |         if (rf & RenderFlags.Create) { | ||||||
|           elementStart(0, 'child'); |           elementStart(0, 'child'); | ||||||
|           { |           { | ||||||
|             elementStart(1, 'span', [AttributeMarker.SELECT_ONLY, 'title']); |             elementStart(1, 'span', [AttributeMarker.SelectOnly, 'title']); | ||||||
|             { text(2, 'Has title'); } |             { text(2, 'Has title'); } | ||||||
|             elementEnd(); |             elementEnd(); | ||||||
|           } |           } | ||||||
|  | |||||||
| @ -1225,7 +1225,7 @@ describe('di', () => { | |||||||
| 
 | 
 | ||||||
|       const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { |       const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { | ||||||
|         if (rf & RenderFlags.Create) { |         if (rf & RenderFlags.Create) { | ||||||
|           elementStart(0, 'div', ['exist', 'existValue', AttributeMarker.SELECT_ONLY, 'nonExist']); |           elementStart(0, 'div', ['exist', 'existValue', AttributeMarker.SelectOnly, 'nonExist']); | ||||||
|           exist = injectAttribute('exist'); |           exist = injectAttribute('exist'); | ||||||
|           nonExist = injectAttribute('nonExist'); |           nonExist = injectAttribute('nonExist'); | ||||||
|         } |         } | ||||||
| @ -1243,7 +1243,7 @@ describe('di', () => { | |||||||
|       const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { |       const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { | ||||||
|         if (rf & RenderFlags.Create) { |         if (rf & RenderFlags.Create) { | ||||||
|           elementStart(0, 'div', [ |           elementStart(0, 'div', [ | ||||||
|             'exist', 'existValue', AttributeMarker.SELECT_ONLY, 'binding1', 'nonExist', 'binding2' |             'exist', 'existValue', AttributeMarker.SelectOnly, 'binding1', 'nonExist', 'binding2' | ||||||
|           ]); |           ]); | ||||||
|           exist = injectAttribute('exist'); |           exist = injectAttribute('exist'); | ||||||
|           nonExist = injectAttribute('nonExist'); |           nonExist = injectAttribute('nonExist'); | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ describe('directive', () => { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function Template() { |       function Template() { | ||||||
|         elementStart(0, 'span', [AttributeMarker.SELECT_ONLY, 'dir']); |         elementStart(0, 'span', [AttributeMarker.SelectOnly, 'dir']); | ||||||
|         elementEnd(); |         elementEnd(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -82,7 +82,7 @@ describe('directive', () => { | |||||||
|        */ |        */ | ||||||
|       function createTemplate() { |       function createTemplate() { | ||||||
|         // using 2 bindings to show example shape of attributes array
 |         // using 2 bindings to show example shape of attributes array
 | ||||||
|         elementStart(0, 'span', ['class', 'fade', AttributeMarker.SELECT_ONLY, 'test', 'other']); |         elementStart(0, 'span', ['class', 'fade', AttributeMarker.SelectOnly, 'test', 'other']); | ||||||
|         elementEnd(); |         elementEnd(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -132,7 +132,7 @@ describe('directive', () => { | |||||||
|          function createTemplate() { |          function createTemplate() { | ||||||
|            // putting name (test) in the "usual" value position
 |            // putting name (test) in the "usual" value position
 | ||||||
|            elementStart( |            elementStart( | ||||||
|                0, 'span', ['class', 'fade', AttributeMarker.SELECT_ONLY, 'prop1', 'test', 'prop2']); |                0, 'span', ['class', 'fade', AttributeMarker.SelectOnly, 'prop1', 'test', 'prop2']); | ||||||
|            elementEnd(); |            elementEnd(); | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
| @ -168,7 +168,7 @@ describe('directive', () => { | |||||||
|        * <span (out)="someVar = true"></span> |        * <span (out)="someVar = true"></span> | ||||||
|        */ |        */ | ||||||
|       function createTemplate() { |       function createTemplate() { | ||||||
|         elementStart(0, 'span', [AttributeMarker.SELECT_ONLY, 'out']); |         elementStart(0, 'span', [AttributeMarker.SelectOnly, 'out']); | ||||||
|         { listener('out', () => {}); } |         { listener('out', () => {}); } | ||||||
|         elementEnd(); |         elementEnd(); | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -95,7 +95,7 @@ describe('instructions', () => { | |||||||
|     it('should use sanitizer function even on elements with namespaced attributes', () => { |     it('should use sanitizer function even on elements with namespaced attributes', () => { | ||||||
|       const t = new TemplateFixture(() => { |       const t = new TemplateFixture(() => { | ||||||
|         element(0, 'div', [ |         element(0, 'div', [ | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.example.com/2004/test', |           'http://www.example.com/2004/test', | ||||||
|           'whatever', |           'whatever', | ||||||
|           'abc', |           'abc', | ||||||
| @ -445,7 +445,7 @@ describe('instructions', () => { | |||||||
|           'height', |           'height', | ||||||
|           '300', |           '300', | ||||||
|           // test:title="abc"
 |           // test:title="abc"
 | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.example.com/2014/test', |           'http://www.example.com/2014/test', | ||||||
|           'title', |           'title', | ||||||
|           'abc', |           'abc', | ||||||
| @ -472,7 +472,7 @@ describe('instructions', () => { | |||||||
|           'id', |           'id', | ||||||
|           'container', |           'container', | ||||||
|           // test:title="abc"
 |           // test:title="abc"
 | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.example.com/2014/test', |           'http://www.example.com/2014/test', | ||||||
|           'title', |           'title', | ||||||
|           'abc', |           'abc', | ||||||
| @ -492,19 +492,19 @@ describe('instructions', () => { | |||||||
|           'container', |           'container', | ||||||
| 
 | 
 | ||||||
|           // NS1:title="abc"
 |           // NS1:title="abc"
 | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.example.com/2014/test', |           'http://www.example.com/2014/test', | ||||||
|           'title', |           'title', | ||||||
|           'abc', |           'abc', | ||||||
| 
 | 
 | ||||||
|           // NS1:whatever="wee"
 |           // NS1:whatever="wee"
 | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.example.com/2014/test', |           'http://www.example.com/2014/test', | ||||||
|           'whatever', |           'whatever', | ||||||
|           'wee', |           'wee', | ||||||
| 
 | 
 | ||||||
|           // NS2:shazbot="wocka wocka"
 |           // NS2:shazbot="wocka wocka"
 | ||||||
|           AttributeMarker.NAMESPACE_URI, |           AttributeMarker.NamespaceUri, | ||||||
|           'http://www.whatever.com/2016/blah', |           'http://www.whatever.com/2016/blah', | ||||||
|           'shazbot', |           'shazbot', | ||||||
|           'wocka wocka', |           'wocka wocka', | ||||||
|  | |||||||
| @ -179,14 +179,14 @@ describe('css selector matching', () => { | |||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should take optional binding attribute names into account', () => { |       it('should take optional binding attribute names into account', () => { | ||||||
|         expect(isMatching('span', [AttributeMarker.SELECT_ONLY, 'directive'], [ |         expect(isMatching('span', [AttributeMarker.SelectOnly, 'directive'], [ | ||||||
|           '', 'directive', '' |           '', 'directive', '' | ||||||
|         ])).toBeTruthy(`Selector '[directive]' should match <span [directive]="exp">`); |         ])).toBeTruthy(`Selector '[directive]' should match <span [directive]="exp">`); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       it('should not match optional binding attribute names if attribute selector has value', |       it('should not match optional binding attribute names if attribute selector has value', | ||||||
|          () => { |          () => { | ||||||
|            expect(isMatching('span', [AttributeMarker.SELECT_ONLY, 'directive'], [ |            expect(isMatching('span', [AttributeMarker.SelectOnly, 'directive'], [ | ||||||
|              '', 'directive', 'value' |              '', 'directive', 'value' | ||||||
|            ])).toBeFalsy(`Selector '[directive=value]' should not match <span [directive]="exp">`); |            ])).toBeFalsy(`Selector '[directive=value]' should not match <span [directive]="exp">`); | ||||||
|          }); |          }); | ||||||
| @ -194,7 +194,7 @@ describe('css selector matching', () => { | |||||||
|       it('should not match optional binding attribute names if attribute selector has value and next name equals to value', |       it('should not match optional binding attribute names if attribute selector has value and next name equals to value', | ||||||
|          () => { |          () => { | ||||||
|            expect(isMatching( |            expect(isMatching( | ||||||
|                       'span', [AttributeMarker.SELECT_ONLY, 'directive', 'value'], |                       'span', [AttributeMarker.SelectOnly, 'directive', 'value'], | ||||||
|                       ['', 'directive', 'value'])) |                       ['', 'directive', 'value'])) | ||||||
|                .toBeFalsy( |                .toBeFalsy( | ||||||
|                    `Selector '[directive=value]' should not match <span [directive]="exp" [value]="otherExp">`); |                    `Selector '[directive=value]' should not match <span [directive]="exp" [value]="otherExp">`); | ||||||
|  | |||||||
| @ -856,7 +856,7 @@ describe('query', () => { | |||||||
|                  } |                  } | ||||||
|                }, null, []); |                }, null, []); | ||||||
| 
 | 
 | ||||||
|                container(5, undefined, null, [AttributeMarker.SELECT_ONLY, 'vc']); |                container(5, undefined, null, [AttributeMarker.SelectOnly, 'vc']); | ||||||
|              } |              } | ||||||
| 
 | 
 | ||||||
|              if (rf & RenderFlags.Update) { |              if (rf & RenderFlags.Update) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user