refactor(ivy): generate 2 slots per styling instruction (#34616)
Compiler keeps track of number of slots (`vars`) which are needed for binding instructions. Normally each binding instructions allocates a single slot in the `LView` but styling instructions need to allocate two slots. PR Close #34616
This commit is contained in:
parent
b7ff38b1ef
commit
4005815114
|
@ -477,7 +477,7 @@ describe('compiler compliance', () => {
|
||||||
const template = `
|
const template = `
|
||||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
|
MyComponent.ɵcmp = i0.ɵɵdefineComponent({type:MyComponent,selectors:[["my-component"]],
|
||||||
decls: 1,
|
decls: 1,
|
||||||
vars: 2,
|
vars: 4,
|
||||||
template: function MyComponent_Template(rf,ctx){
|
template: function MyComponent_Template(rf,ctx){
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelement(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
|
|
|
@ -377,8 +377,7 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵelement(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -504,15 +503,14 @@ describe('compiler compliance: styling', () => {
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
selectors:[["my-component"]],
|
selectors:[["my-component"]],
|
||||||
decls: 1,
|
decls: 1,
|
||||||
vars: 5,
|
vars: 7,
|
||||||
consts: [[${AttributeMarker.Styles}, "opacity", "1"]],
|
consts: [[${AttributeMarker.Styles}, "opacity", "1"]],
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelement(0, "div", 0);
|
$r3$.ɵɵelement(0, "div", 0);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
|
||||||
$r3$.ɵɵstyleProp("width", $ctx$.myWidth)("height", $ctx$.myHeight);
|
$r3$.ɵɵstyleProp("width", $ctx$.myWidth)("height", $ctx$.myHeight);
|
||||||
$r3$.ɵɵattribute("style", "border-width: 10px", $r3$.ɵɵsanitizeStyle);
|
$r3$.ɵɵattribute("style", "border-width: 10px", $r3$.ɵɵsanitizeStyle);
|
||||||
}
|
}
|
||||||
|
@ -551,14 +549,13 @@ describe('compiler compliance: styling', () => {
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
selectors: [["my-component"]],
|
selectors: [["my-component"]],
|
||||||
decls: 1,
|
decls: 1,
|
||||||
vars: 1,
|
vars: 2,
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelement(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleProp("background-image", ctx.myImage, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleProp("background-image", ctx.myImage);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
encapsulation: 2
|
encapsulation: 2
|
||||||
|
@ -697,7 +694,7 @@ describe('compiler compliance: styling', () => {
|
||||||
type: MyComponent,
|
type: MyComponent,
|
||||||
selectors:[["my-component"]],
|
selectors:[["my-component"]],
|
||||||
decls: 1,
|
decls: 1,
|
||||||
vars: 5,
|
vars: 7,
|
||||||
consts: [[${AttributeMarker.Classes}, "grape"]],
|
consts: [[${AttributeMarker.Classes}, "grape"]],
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
|
@ -816,8 +813,7 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵelement(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
|
||||||
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,8 +854,7 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵelementEnd();
|
$r3$.ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind1(1, 4, $ctx$.myStyleExp), $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind1(1, 4, $ctx$.myStyleExp));
|
|
||||||
$r3$.ɵɵclassMap($r3$.ɵɵpipeBind1(2, 6, $ctx$.myClassExp));
|
$r3$.ɵɵclassMap($r3$.ɵɵpipeBind1(2, 6, $ctx$.myClassExp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -911,11 +906,10 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵelementEnd();
|
$r3$.ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind2(1, 11, $ctx$.myStyleExp, 1000), $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind2(1, 8, $ctx$.myStyleExp, 1000));
|
$r3$.ɵɵclassMap($r3$.ɵɵpureFunction0(23, _c0));
|
||||||
$r3$.ɵɵclassMap($r3$.ɵɵpureFunction0(20, _c0));
|
$r3$.ɵɵstyleProp("bar", $r3$.ɵɵpipeBind2(2, 14, $ctx$.barExp, 3000))("baz", $r3$.ɵɵpipeBind2(3, 17, $ctx$.bazExp, 4000));
|
||||||
$r3$.ɵɵstyleProp("bar", $r3$.ɵɵpipeBind2(2, 11, $ctx$.barExp, 3000))("baz", $r3$.ɵɵpipeBind2(3, 14, $ctx$.bazExp, 4000));
|
$r3$.ɵɵclassProp("foo", $r3$.ɵɵpipeBind2(4, 20, $ctx$.fooExp, 2000));
|
||||||
$r3$.ɵɵclassProp("foo", $r3$.ɵɵpipeBind2(4, 17, $ctx$.fooExp, 2000));
|
|
||||||
$r3$.ɵɵadvance(5);
|
$r3$.ɵɵadvance(5);
|
||||||
$r3$.ɵɵtextInterpolate1(" ", $ctx$.item, "");
|
$r3$.ɵɵtextInterpolate1(" ", $ctx$.item, "");
|
||||||
}
|
}
|
||||||
|
@ -1014,9 +1008,15 @@ describe('compiler compliance: styling', () => {
|
||||||
hostAttrs: [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"],
|
hostAttrs: [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"],
|
||||||
hostVars: 6,
|
hostVars: 6,
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵallocHostVars(8);
|
||||||
|
$r3$.ɵɵelementHostAttrs($e0_attrs$);
|
||||||
|
}
|
||||||
|
>>>>>>> 3a14b06a3b... refactor(ivy): generate 2 slots per styling instruction
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.myStyle, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyle);
|
|
||||||
$r3$.ɵɵclassMap(ctx.myClass);
|
$r3$.ɵɵclassMap(ctx.myClass);
|
||||||
$r3$.ɵɵstyleProp("color", ctx.myColorProp);
|
$r3$.ɵɵstyleProp("color", ctx.myColorProp);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
||||||
|
@ -1070,9 +1070,14 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
hostVars: 8,
|
hostVars: 8,
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵallocHostVars(12);
|
||||||
|
}
|
||||||
|
>>>>>>> 3a14b06a3b... refactor(ivy): generate 2 slots per styling instruction
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.myStyle, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyle);
|
|
||||||
$r3$.ɵɵclassMap(ctx.myClasses);
|
$r3$.ɵɵclassMap(ctx.myClasses);
|
||||||
$r3$.ɵɵstyleProp("height", ctx.myHeightProp, "pt")("width", ctx.myWidthProp);
|
$r3$.ɵɵstyleProp("height", ctx.myHeightProp, "pt")("width", ctx.myWidthProp);
|
||||||
$r3$.ɵɵclassProp("bar", ctx.myBarClass)("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("bar", ctx.myBarClass)("foo", ctx.myFooClass);
|
||||||
|
@ -1129,8 +1134,7 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵelement(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.myStyleExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyleExp);
|
|
||||||
$r3$.ɵɵclassMap(ctx.myClassExp);
|
$r3$.ɵɵclassMap(ctx.myClassExp);
|
||||||
$r3$.ɵɵstyleProp("height", ctx.myHeightExp);
|
$r3$.ɵɵstyleProp("height", ctx.myHeightExp);
|
||||||
$r3$.ɵɵclassProp("bar", ctx.myBarClassExp);
|
$r3$.ɵɵclassProp("bar", ctx.myBarClassExp);
|
||||||
|
@ -1141,9 +1145,14 @@ describe('compiler compliance: styling', () => {
|
||||||
const hostBindings = `
|
const hostBindings = `
|
||||||
hostVars: 6,
|
hostVars: 6,
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵɵallocHostVars(8);
|
||||||
|
}
|
||||||
|
>>>>>>> 3a14b06a3b... refactor(ivy): generate 2 slots per styling instruction
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.myStyleExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyleExp);
|
|
||||||
$r3$.ɵɵclassMap(ctx.myClassExp);
|
$r3$.ɵɵclassMap(ctx.myClassExp);
|
||||||
$r3$.ɵɵstyleProp("width", ctx.myWidthExp);
|
$r3$.ɵɵstyleProp("width", ctx.myWidthExp);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClassExp);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClassExp);
|
||||||
|
@ -1412,6 +1421,46 @@ describe('compiler compliance: styling', () => {
|
||||||
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
|
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate update instructions for interpolated style properties with a sanitizer',
|
||||||
|
() => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: \`
|
||||||
|
<div style.background="url({{ myUrl1 }})"
|
||||||
|
style.borderImage="url({{ myUrl2 }}) {{ myRepeat }} auto"
|
||||||
|
style.boxShadow="{{ myBoxX }} {{ myBoxY }} {{ myBoxWidth }} black"></div>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
myUrl1 = '...';
|
||||||
|
myUrl2 = '...';
|
||||||
|
myBoxX = '0px';
|
||||||
|
myBoxY = '0px';
|
||||||
|
myBoxWidth = '100px';
|
||||||
|
myRepeat = 'no-repeat';
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
…
|
||||||
|
if (rf & 2) {
|
||||||
|
$r3$.ɵɵstylePropInterpolate1("background", "url(", ctx.myUrl1, ")", $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
$r3$.ɵɵstylePropInterpolate2("border-image", "url(", ctx.myUrl2, ") ", ctx.myRepeat, " auto", $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
$r3$.ɵɵstylePropInterpolate3("box-shadow", "", ctx.myBoxX, " ", ctx.myBoxY, " ", ctx.myBoxWidth, " black");
|
||||||
|
}
|
||||||
|
…
|
||||||
|
`;
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
|
||||||
|
});
|
||||||
|
|
||||||
it('should generate update instructions for interpolated style properties with !important',
|
it('should generate update instructions for interpolated style properties with !important',
|
||||||
() => {
|
() => {
|
||||||
const files = {
|
const files = {
|
||||||
|
@ -1833,8 +1882,7 @@ describe('compiler compliance: styling', () => {
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.myStyle, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyle);
|
|
||||||
$r3$.ɵɵclassMap(ctx.myClass);
|
$r3$.ɵɵclassMap(ctx.myClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1908,8 +1956,7 @@ describe('compiler compliance: styling', () => {
|
||||||
template: function MyAppComp_Template(rf, ctx) {
|
template: function MyAppComp_Template(rf, ctx) {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleProp("background-image", ctx.bgExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleProp("background-image", ctx.bgExp);
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
|
@ -1943,8 +1990,7 @@ describe('compiler compliance: styling', () => {
|
||||||
template: function MyAppComp_Template(rf, ctx) {
|
template: function MyAppComp_Template(rf, ctx) {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleMap(ctx.mapExp, $r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.mapExp);
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
|
@ -2035,11 +2081,17 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
hostVars: 9,
|
hostVars: 9,
|
||||||
hostBindings: function MyDir_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyDir_HostBindings(rf, ctx, elIndex) {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
…
|
||||||
|
$r3$.ɵɵallocHostVars(10);
|
||||||
|
…
|
||||||
|
>>>>>>> 3a14b06a3b... refactor(ivy): generate 2 slots per styling instruction
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵhostProperty("title", ctx.title);
|
$r3$.ɵɵhostProperty("title", ctx.title);
|
||||||
$r3$.ɵɵupdateSyntheticHostBinding("@anim",
|
$r3$.ɵɵupdateSyntheticHostBinding("@anim",
|
||||||
$r3$.ɵɵpureFunction2(6, _c1, ctx._animValue,
|
$r3$.ɵɵpureFunction2(7, _c1, ctx._animValue,
|
||||||
$r3$.ɵɵpureFunction2(3, _c0, ctx._animParam1, ctx._animParam2)));
|
$r3$.ɵɵpureFunction2(4, _c0, ctx._animParam1, ctx._animParam2)));
|
||||||
$r3$.ɵɵclassProp("foo", ctx.foo);
|
$r3$.ɵɵclassProp("foo", ctx.foo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,8 +113,6 @@ export class Identifiers {
|
||||||
static stylePropInterpolateV:
|
static stylePropInterpolateV:
|
||||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
||||||
|
|
||||||
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
|
|
||||||
|
|
||||||
static containerCreate: o.ExternalReference = {name: 'ɵɵcontainer', moduleName: CORE};
|
static containerCreate: o.ExternalReference = {name: 'ɵɵcontainer', moduleName: CORE};
|
||||||
|
|
||||||
static nextContext: o.ExternalReference = {name: 'ɵɵnextContext', moduleName: CORE};
|
static nextContext: o.ExternalReference = {name: 'ɵɵnextContext', moduleName: CORE};
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {Render3ParseResult} from '../r3_template_transform';
|
||||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
||||||
|
|
||||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api';
|
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api';
|
||||||
import {StylingBuilder, StylingInstructionCall} from './styling_builder';
|
import {MIN_STYLING_BINDING_SLOTS_REQUIRED, StylingBuilder, StylingInstructionCall} from './styling_builder';
|
||||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
|
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
|
||||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
||||||
|
|
||||||
|
@ -530,8 +530,6 @@ function createHostBindingsFunction(
|
||||||
hostBindingsMetadata: R3HostMetadata, typeSourceSpan: ParseSourceSpan,
|
hostBindingsMetadata: R3HostMetadata, typeSourceSpan: ParseSourceSpan,
|
||||||
bindingParser: BindingParser, constantPool: ConstantPool, selector: string, name: string,
|
bindingParser: BindingParser, constantPool: ConstantPool, selector: string, name: string,
|
||||||
definitionMap: DefinitionMap): o.Expression|null {
|
definitionMap: DefinitionMap): o.Expression|null {
|
||||||
// Initialize hostVarsCount to number of bound host properties (interpolations illegal)
|
|
||||||
const hostVarsCount = Object.keys(hostBindingsMetadata.properties).length;
|
|
||||||
const elVarExp = o.variable('elIndex');
|
const elVarExp = o.variable('elIndex');
|
||||||
const bindingContext = o.variable(CONTEXT_NAME);
|
const bindingContext = o.variable(CONTEXT_NAME);
|
||||||
const styleBuilder = new StylingBuilder(elVarExp, bindingContext);
|
const styleBuilder = new StylingBuilder(elVarExp, bindingContext);
|
||||||
|
@ -547,10 +545,38 @@ function createHostBindingsFunction(
|
||||||
const createStatements: o.Statement[] = [];
|
const createStatements: o.Statement[] = [];
|
||||||
const updateStatements: o.Statement[] = [];
|
const updateStatements: o.Statement[] = [];
|
||||||
|
|
||||||
let totalHostVarsCount = hostVarsCount;
|
|
||||||
const hostBindingSourceSpan = typeSourceSpan;
|
const hostBindingSourceSpan = typeSourceSpan;
|
||||||
const directiveSummary = metadataAsSummary(hostBindingsMetadata);
|
const directiveSummary = metadataAsSummary(hostBindingsMetadata);
|
||||||
|
|
||||||
|
// Calculate host event bindings
|
||||||
|
const eventBindings =
|
||||||
|
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
||||||
|
if (eventBindings && eventBindings.length) {
|
||||||
|
const listeners = createHostListeners(eventBindings, name);
|
||||||
|
createStatements.push(...listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the host property bindings
|
||||||
|
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
||||||
|
const allOtherBindings: ParsedProperty[] = [];
|
||||||
|
|
||||||
|
// We need to calculate the total amount of binding slots required by
|
||||||
|
// all the instructions together before any value conversions happen.
|
||||||
|
// Value conversions may require additional slots for interpolation and
|
||||||
|
// bindings with pipes. These calculates happen after this block.
|
||||||
|
let totalHostVarsCount = 0;
|
||||||
|
bindings && bindings.forEach((binding: ParsedProperty) => {
|
||||||
|
const name = binding.name;
|
||||||
|
const stylingInputWasSet =
|
||||||
|
styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan);
|
||||||
|
if (stylingInputWasSet) {
|
||||||
|
totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
||||||
|
} else {
|
||||||
|
allOtherBindings.push(binding);
|
||||||
|
totalHostVarsCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let valueConverter: ValueConverter;
|
let valueConverter: ValueConverter;
|
||||||
const getValueConverter = () => {
|
const getValueConverter = () => {
|
||||||
if (!valueConverter) {
|
if (!valueConverter) {
|
||||||
|
@ -568,25 +594,10 @@ function createHostBindingsFunction(
|
||||||
return valueConverter;
|
return valueConverter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate host event bindings
|
|
||||||
const eventBindings =
|
|
||||||
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
|
||||||
if (eventBindings && eventBindings.length) {
|
|
||||||
const listeners = createHostListeners(eventBindings, name);
|
|
||||||
createStatements.push(...listeners);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the host property bindings
|
|
||||||
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
|
||||||
const propertyBindings: o.Expression[][] = [];
|
const propertyBindings: o.Expression[][] = [];
|
||||||
const attributeBindings: o.Expression[][] = [];
|
const attributeBindings: o.Expression[][] = [];
|
||||||
const syntheticHostBindings: o.Expression[][] = [];
|
const syntheticHostBindings: o.Expression[][] = [];
|
||||||
|
allOtherBindings.forEach((binding: ParsedProperty) => {
|
||||||
bindings && bindings.forEach((binding: ParsedProperty) => {
|
|
||||||
const name = binding.name;
|
|
||||||
const stylingInputWasSet =
|
|
||||||
styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan);
|
|
||||||
if (!stylingInputWasSet) {
|
|
||||||
// resolve literal arrays and literal objects
|
// resolve literal arrays and literal objects
|
||||||
const value = binding.expression.visit(getValueConverter());
|
const value = binding.expression.visit(getValueConverter());
|
||||||
const bindingExpr = bindingFn(bindingContext, value);
|
const bindingExpr = bindingFn(bindingContext, value);
|
||||||
|
@ -627,7 +638,6 @@ function createHostBindingsFunction(
|
||||||
} else {
|
} else {
|
||||||
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
|
updateStatements.push(o.importExpr(instruction).callFn(instructionParams).toStmt());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (propertyBindings.length > 0) {
|
if (propertyBindings.length > 0) {
|
||||||
|
@ -664,7 +674,8 @@ function createHostBindingsFunction(
|
||||||
instruction.calls.forEach(call => {
|
instruction.calls.forEach(call => {
|
||||||
// we subtract a value of `1` here because the binding slot was already allocated
|
// we subtract a value of `1` here because the binding slot was already allocated
|
||||||
// at the top of this method when all the input bindings were counted.
|
// at the top of this method when all the input bindings were counted.
|
||||||
totalHostVarsCount += Math.max(call.allocateBindingSlots - 1, 0);
|
totalHostVarsCount +=
|
||||||
|
Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
|
||||||
calls.push(convertStylingCall(call, bindingContext, bindingFn));
|
calls.push(convertStylingCall(call, bindingContext, bindingFn));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,56 @@ import {DefinitionMap, getInterpolationArgsLength} from './util';
|
||||||
|
|
||||||
const IMPORTANT_FLAG = '!important';
|
const IMPORTANT_FLAG = '!important';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum amount of binding slots required in the runtime for style/class bindings.
|
||||||
|
*
|
||||||
|
* Styling in Angular uses up two slots in the runtime LView/TData data structures to
|
||||||
|
* record binding data, property information and metadata.
|
||||||
|
*
|
||||||
|
* When a binding is registered it will place the following information in the `LView`:
|
||||||
|
*
|
||||||
|
* slot 1) binding value
|
||||||
|
* slot 2) cached value (all other values collected before it in string form)
|
||||||
|
*
|
||||||
|
* When a binding is registered it will place the following information in the `TData`:
|
||||||
|
*
|
||||||
|
* slot 1) prop name
|
||||||
|
* slot 2) binding index that points to the previous style/class binding (and some extra config
|
||||||
|
* values)
|
||||||
|
*
|
||||||
|
* Let's imagine we have a binding that looks like so:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <div [style.width]="x" [style.height]="y">
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Our `LView` and `TData` data-structures look like so:
|
||||||
|
*
|
||||||
|
* ```typescript
|
||||||
|
* LView = [
|
||||||
|
* // ...
|
||||||
|
* x, // value of x
|
||||||
|
* "width: x",
|
||||||
|
*
|
||||||
|
* y, // value of y
|
||||||
|
* "width: x; height: y",
|
||||||
|
* // ...
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* TData = [
|
||||||
|
* // ...
|
||||||
|
* "width", // binding slot 20
|
||||||
|
* 0,
|
||||||
|
*
|
||||||
|
* "height",
|
||||||
|
* 20,
|
||||||
|
* // ...
|
||||||
|
* ];
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
export const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A styling expression summary that is to be processed by the compiler
|
* A styling expression summary that is to be processed by the compiler
|
||||||
*/
|
*/
|
||||||
|
@ -44,6 +94,7 @@ interface BoundStylingEntry {
|
||||||
name: string|null;
|
name: string|null;
|
||||||
unit: string|null;
|
unit: string|null;
|
||||||
sourceSpan: ParseSourceSpan;
|
sourceSpan: ParseSourceSpan;
|
||||||
|
sanitize: boolean;
|
||||||
value: AST;
|
value: AST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,10 +167,6 @@ export class StylingBuilder {
|
||||||
private _initialStyleValues: string[] = [];
|
private _initialStyleValues: string[] = [];
|
||||||
private _initialClassValues: string[] = [];
|
private _initialClassValues: string[] = [];
|
||||||
|
|
||||||
// certain style properties ALWAYS need sanitization
|
|
||||||
// this is checked each time new styles are encountered
|
|
||||||
private _useDefaultSanitizer = false;
|
|
||||||
|
|
||||||
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,14 +226,13 @@ export class StylingBuilder {
|
||||||
const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
|
const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
|
||||||
const entry: BoundStylingEntry = {
|
const entry: BoundStylingEntry = {
|
||||||
name: property,
|
name: property,
|
||||||
|
sanitize: property ? isStyleSanitizable(property) : true,
|
||||||
unit: unit || bindingUnit, value, sourceSpan, hasOverrideFlag
|
unit: unit || bindingUnit, value, sourceSpan, hasOverrideFlag
|
||||||
};
|
};
|
||||||
if (isMapBased) {
|
if (isMapBased) {
|
||||||
this._useDefaultSanitizer = true;
|
|
||||||
this._styleMapInput = entry;
|
this._styleMapInput = entry;
|
||||||
} else {
|
} else {
|
||||||
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
||||||
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(name);
|
|
||||||
registerIntoMap(this._stylesIndex, property);
|
registerIntoMap(this._stylesIndex, property);
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
this._lastStylingInput = entry;
|
||||||
|
@ -202,8 +248,8 @@ export class StylingBuilder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const {property, hasOverrideFlag} = parseProperty(name);
|
const {property, hasOverrideFlag} = parseProperty(name);
|
||||||
const entry:
|
const entry: BoundStylingEntry =
|
||||||
BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, unit: null};
|
{name: property, value, sourceSpan, sanitize: false, hasOverrideFlag, unit: null};
|
||||||
if (isMapBased) {
|
if (isMapBased) {
|
||||||
if (this._classMapInput) {
|
if (this._classMapInput) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -319,7 +365,7 @@ export class StylingBuilder {
|
||||||
// map-based bindings allocate two slots: one for the
|
// map-based bindings allocate two slots: one for the
|
||||||
// previous binding value and another for the previous
|
// previous binding value and another for the previous
|
||||||
// className or style attribute value.
|
// className or style attribute value.
|
||||||
let totalBindingSlotsRequired = 2;
|
let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
||||||
|
|
||||||
// these values must be outside of the update block so that they can
|
// these values must be outside of the update block so that they can
|
||||||
// be evaluated (the AST visit call) during creation time so that any
|
// be evaluated (the AST visit call) during creation time so that any
|
||||||
|
@ -341,17 +387,23 @@ export class StylingBuilder {
|
||||||
allocateBindingSlots: totalBindingSlotsRequired,
|
allocateBindingSlots: totalBindingSlotsRequired,
|
||||||
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
||||||
const convertResult = convertFn(mapValue);
|
const convertResult = convertFn(mapValue);
|
||||||
return Array.isArray(convertResult) ? convertResult : [convertResult];
|
const params = Array.isArray(convertResult) ? convertResult : [convertResult];
|
||||||
|
|
||||||
|
// [style] instructions will sanitize all their values. For this reason we
|
||||||
|
// need to include the sanitizer as a param.
|
||||||
|
if (!isClassBased) {
|
||||||
|
params.push(o.importExpr(R3.defaultStyleSanitizer));
|
||||||
|
}
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildSingleInputs(
|
private _buildSingleInputs(
|
||||||
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
|
reference: o.ExternalReference, inputs: BoundStylingEntry[], valueConverter: ValueConverter,
|
||||||
allowUnits: boolean, valueConverter: ValueConverter,
|
getInterpolationExpressionFn: ((value: Interpolation) => o.ExternalReference)|null,
|
||||||
getInterpolationExpressionFn?: (value: Interpolation) => o.ExternalReference):
|
isClassBased: boolean): StylingInstruction[] {
|
||||||
StylingInstruction[] {
|
|
||||||
const instructions: StylingInstruction[] = [];
|
const instructions: StylingInstruction[] = [];
|
||||||
|
|
||||||
inputs.forEach(input => {
|
inputs.forEach(input => {
|
||||||
|
@ -359,7 +411,14 @@ export class StylingBuilder {
|
||||||
instructions[instructions.length - 1];
|
instructions[instructions.length - 1];
|
||||||
const value = input.value.visit(valueConverter);
|
const value = input.value.visit(valueConverter);
|
||||||
let referenceForCall = reference;
|
let referenceForCall = reference;
|
||||||
let totalBindingSlotsRequired = 1; // each styling binding value is stored in the LView
|
|
||||||
|
// each styling binding value is stored in the LView
|
||||||
|
// but there are two values stored for each binding:
|
||||||
|
// 1) the value itself
|
||||||
|
// 2) an intermediate value (concatenation of style up to this point).
|
||||||
|
// We need to store the intermediate value so that we don't allocate
|
||||||
|
// the strings on each CD.
|
||||||
|
let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
||||||
|
|
||||||
if (value instanceof Interpolation) {
|
if (value instanceof Interpolation) {
|
||||||
totalBindingSlotsRequired += value.expressions.length;
|
totalBindingSlotsRequired += value.expressions.length;
|
||||||
|
@ -374,7 +433,7 @@ export class StylingBuilder {
|
||||||
allocateBindingSlots: totalBindingSlotsRequired,
|
allocateBindingSlots: totalBindingSlotsRequired,
|
||||||
supportsInterpolation: !!getInterpolationExpressionFn,
|
supportsInterpolation: !!getInterpolationExpressionFn,
|
||||||
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
|
||||||
// params => stylingProp(propName, value)
|
// params => stylingProp(propName, value, suffix|sanitizer)
|
||||||
const params: o.Expression[] = [];
|
const params: o.Expression[] = [];
|
||||||
params.push(o.literal(input.name));
|
params.push(o.literal(input.name));
|
||||||
|
|
||||||
|
@ -385,8 +444,16 @@ export class StylingBuilder {
|
||||||
params.push(convertResult);
|
params.push(convertResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowUnits && input.unit) {
|
// [style.prop] bindings may use suffix values (e.g. px, em, etc...) and they
|
||||||
|
// can also use a sanitizer. Sanitization occurs for url-based entries. Having
|
||||||
|
// the suffix value and a sanitizer together into the instruction doesn't make
|
||||||
|
// any sense (url-based entries cannot be sanitized).
|
||||||
|
if (!isClassBased) {
|
||||||
|
if (input.unit) {
|
||||||
params.push(o.literal(input.unit));
|
params.push(o.literal(input.unit));
|
||||||
|
} else if (input.sanitize) {
|
||||||
|
params.push(o.importExpr(R3.defaultStyleSanitizer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
|
@ -411,7 +478,7 @@ export class StylingBuilder {
|
||||||
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||||
if (this._singleClassInputs) {
|
if (this._singleClassInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.classProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
|
R3.classProp, this._singleClassInputs, valueConverter, null, true);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -419,23 +486,12 @@ export class StylingBuilder {
|
||||||
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||||
if (this._singleStyleInputs) {
|
if (this._singleStyleInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter,
|
R3.styleProp, this._singleStyleInputs, valueConverter,
|
||||||
getStylePropInterpolationExpression);
|
getStylePropInterpolationExpression, false);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildSanitizerFn(): StylingInstruction {
|
|
||||||
return {
|
|
||||||
reference: R3.styleSanitizer,
|
|
||||||
calls: [{
|
|
||||||
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
|
|
||||||
allocateBindingSlots: 0,
|
|
||||||
params: () => [o.importExpr(R3.defaultStyleSanitizer)]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs all instructions which contain the expressions that will be placed
|
* Constructs all instructions which contain the expressions that will be placed
|
||||||
* into the update block of a template function or a directive hostBindings function.
|
* into the update block of a template function or a directive hostBindings function.
|
||||||
|
@ -443,9 +499,6 @@ export class StylingBuilder {
|
||||||
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
||||||
const instructions: StylingInstruction[] = [];
|
const instructions: StylingInstruction[] = [];
|
||||||
if (this.hasBindings) {
|
if (this.hasBindings) {
|
||||||
if (this._useDefaultSanitizer) {
|
|
||||||
instructions.push(this._buildSanitizerFn());
|
|
||||||
}
|
|
||||||
const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
|
const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
|
||||||
if (styleMapInstruction) {
|
if (styleMapInstruction) {
|
||||||
instructions.push(styleMapInstruction);
|
instructions.push(styleMapInstruction);
|
||||||
|
|
|
@ -684,7 +684,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
|
|
||||||
// the code here will collect all update-level styling instructions and add them to the
|
// the code here will collect all update-level styling instructions and add them to the
|
||||||
// update block of the template function AOT code. Instructions like `styleProp`,
|
// update block of the template function AOT code. Instructions like `styleProp`,
|
||||||
// `styleMap`, `classMap`, `classProp` and `stylingApply`
|
// `styleMap`, `classMap`, `classProp` and `flushStyling`
|
||||||
// are all generated and assigned in the code below.
|
// are all generated and assigned in the code below.
|
||||||
const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
|
const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
|
||||||
const limit = stylingInstructions.length - 1;
|
const limit = stylingInstructions.length - 1;
|
||||||
|
|
Loading…
Reference in New Issue