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);
|
||||||
|
|
||||||
|
|
|
@ -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…
Reference in New Issue