feat(ivy): added new namespace and element instructions to JIT environment (#23899)

PR Close #23899
This commit is contained in:
Ben Lesh 2018-06-01 16:00:51 -07:00 committed by Victor Berchet
parent 1007d1ad27
commit acf270d724
18 changed files with 126 additions and 75 deletions

View File

@ -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);

View File

@ -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));

View File

@ -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();
} }
} }

View File

@ -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,

View File

@ -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)>` | ❌ | ❌ | ❌ |

View File

@ -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) {

View File

@ -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,

View File

@ -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] = []);

View File

@ -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
} }
/** /**

View File

@ -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,

View File

@ -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;
} }
} }

View File

@ -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>');
}); });

View File

@ -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();
} }

View File

@ -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');

View File

@ -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();
} }

View File

@ -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',

View File

@ -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">`);

View File

@ -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) {