diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index 46083c897c..9143b0a86b 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -377,7 +377,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelement(0, "div"); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap($ctx$.myStyleExp); } } @@ -511,7 +510,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelement(0, "div", 0); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap($ctx$.myStyleExp); $r3$.ɵɵstyleProp("width", $ctx$.myWidth)("height", $ctx$.myHeight); $r3$.ɵɵattribute("style", "border-width: 10px", $r3$.ɵɵsanitizeStyle); @@ -557,8 +555,7 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelement(0, "div"); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); - $r3$.ɵɵstyleProp("background-image", ctx.myImage); + $r3$.ɵɵstyleProp("background-image", ctx.myImage, $r3$.ɵɵdefaultStyleSanitizer); } }, encapsulation: 2 @@ -816,7 +813,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelement(0, "div"); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap($ctx$.myStyleExp); $r3$.ɵɵclassMap($ctx$.myClassExp); } @@ -858,7 +854,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelementEnd(); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap($r3$.ɵɵpipeBind1(1, 4, $ctx$.myStyleExp)); $r3$.ɵɵclassMap($r3$.ɵɵpipeBind1(2, 6, $ctx$.myClassExp)); } @@ -911,7 +906,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelementEnd(); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap($r3$.ɵɵpipeBind2(1, 8, $ctx$.myStyleExp, 1000)); $r3$.ɵɵclassMap($r3$.ɵɵpureFunction0(20, _c0)); $r3$.ɵɵstyleProp("bar", $r3$.ɵɵpipeBind2(2, 11, $ctx$.barExp, 3000))("baz", $r3$.ɵɵpipeBind2(3, 14, $ctx$.bazExp, 4000)); @@ -1017,7 +1011,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelementHostAttrs($e0_attrs$); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.myStyle); $r3$.ɵɵclassMap(ctx.myClass); $r3$.ɵɵstyleProp("color", ctx.myColorProp); @@ -1075,7 +1068,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵallocHostVars(8); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.myStyle); $r3$.ɵɵclassMap(ctx.myClasses); $r3$.ɵɵstyleProp("height", ctx.myHeightProp, "pt")("width", ctx.myWidthProp); @@ -1133,7 +1125,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵelement(0, "div"); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.myStyleExp); $r3$.ɵɵclassMap(ctx.myClassExp); $r3$.ɵɵstyleProp("height", ctx.myHeightExp); @@ -1148,7 +1139,6 @@ describe('compiler compliance: styling', () => { $r3$.ɵɵallocHostVars(6); } if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.myStyleExp); $r3$.ɵɵclassMap(ctx.myClassExp); $r3$.ɵɵstyleProp("width", ctx.myWidthExp); @@ -1424,6 +1414,46 @@ describe('compiler compliance: styling', () => { 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: \` +
+ \` + }) + 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', () => { const files = { @@ -1849,7 +1879,6 @@ describe('compiler compliance: styling', () => { } if (rf & 2) { $r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title); - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.myStyle); $r3$.ɵɵclassMap(ctx.myClass); } @@ -1902,7 +1931,7 @@ describe('compiler compliance: styling', () => { }); describe('new styling refactor', () => { - it('should generate a `styleSanitizer` instruction when one or more sanitizable style properties are statically detected', + it('should generate a sanitizer value into the instruction when one or more sanitizable style properties are statically detected', () => { const files = { app: { @@ -1926,8 +1955,7 @@ describe('compiler compliance: styling', () => { template: function MyAppComp_Template(rf, ctx) { … if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); - $r3$.ɵɵstyleProp("background-image", ctx.bgExp); + $r3$.ɵɵstyleProp("background-image", ctx.bgExp, $r3$.ɵɵdefaultStyleSanitizer); } … } @@ -1937,11 +1965,10 @@ describe('compiler compliance: styling', () => { expectEmit(result.source, template, 'Incorrect template'); }); - it('should generate a `styleSanitizer` instruction when a `styleMap` instruction is used', - () => { - const files = { - app: { - 'spec.ts': ` + it('should not add a sanitizer param when a `styleMap` instruction is used', () => { + const files = { + app: { + 'spec.ts': ` import {Component, NgModule} from '@angular/core'; @Component({ @@ -1954,25 +1981,24 @@ describe('compiler compliance: styling', () => { mapExp = {}; } ` - } - }; + } + }; - const template = ` + const template = ` template: function MyAppComp_Template(rf, ctx) { … if (rf & 2) { - $r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵstyleMap(ctx.mapExp); } … } `; - const result = compile(files, angularFiles); - expectEmit(result.source, template, 'Incorrect template'); - }); + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); - it('shouldn\'t generate a `styleSanitizer` instruction when class-based instructions are used', + it('shouldn\'t generate a sanitizer param into the styling instruction when class-based instructions are used', () => { const files = { app: { diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index dc43f681a2..06b76c07d0 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -113,8 +113,6 @@ export class Identifiers { static stylePropInterpolateV: o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE}; - static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE}; - static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE}; static containerCreate: o.ExternalReference = {name: 'ɵɵcontainer', moduleName: CORE}; diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts index 67d546ecab..a85e903335 100644 --- a/packages/compiler/src/render3/view/styling_builder.ts +++ b/packages/compiler/src/render3/view/styling_builder.ts @@ -44,6 +44,7 @@ interface BoundStylingEntry { name: string|null; unit: string|null; sourceSpan: ParseSourceSpan; + sanitize: boolean; value: AST; } @@ -116,10 +117,6 @@ export class StylingBuilder { private _initialStyleValues: 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) {} /** @@ -179,14 +176,13 @@ export class StylingBuilder { const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name); const entry: BoundStylingEntry = { name: property, + sanitize: property ? isStyleSanitizable(property) : true, unit: unit || bindingUnit, value, sourceSpan, hasOverrideFlag }; if (isMapBased) { - this._useDefaultSanitizer = true; this._styleMapInput = entry; } else { (this._singleStyleInputs = this._singleStyleInputs || []).push(entry); - this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(name); registerIntoMap(this._stylesIndex, property); } this._lastStylingInput = entry; @@ -202,8 +198,8 @@ export class StylingBuilder { return null; } const {property, hasOverrideFlag} = parseProperty(name); - const entry: - BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, unit: null}; + const entry: BoundStylingEntry = + {name: property, value, sourceSpan, sanitize: false, hasOverrideFlag, unit: null}; if (isMapBased) { if (this._classMapInput) { throw new Error( @@ -364,10 +360,9 @@ export class StylingBuilder { } private _buildSingleInputs( - reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map, - allowUnits: boolean, valueConverter: ValueConverter, - getInterpolationExpressionFn?: (value: Interpolation) => o.ExternalReference): - StylingInstruction[] { + reference: o.ExternalReference, inputs: BoundStylingEntry[], valueConverter: ValueConverter, + getInterpolationExpressionFn: ((value: Interpolation) => o.ExternalReference)|null, + isClassBased: boolean): StylingInstruction[] { const instructions: StylingInstruction[] = []; inputs.forEach(input => { @@ -390,7 +385,7 @@ export class StylingBuilder { allocateBindingSlots: totalBindingSlotsRequired, supportsInterpolation: !!getInterpolationExpressionFn, params: (convertFn: (value: any) => o.Expression | o.Expression[]) => { - // params => stylingProp(propName, value) + // params => stylingProp(propName, value, suffix|sanitizer) const params: o.Expression[] = []; params.push(o.literal(input.name)); @@ -401,8 +396,16 @@ export class StylingBuilder { params.push(convertResult); } - if (allowUnits && input.unit) { - params.push(o.literal(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)); + } else if (input.sanitize) { + params.push(o.importExpr(R3.defaultStyleSanitizer)); + } } return params; @@ -427,7 +430,7 @@ export class StylingBuilder { private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] { if (this._singleClassInputs) { return this._buildSingleInputs( - R3.classProp, this._singleClassInputs, this._classesIndex, false, valueConverter); + R3.classProp, this._singleClassInputs, valueConverter, null, true); } return []; } @@ -435,23 +438,12 @@ export class StylingBuilder { private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] { if (this._singleStyleInputs) { return this._buildSingleInputs( - R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter, - getStylePropInterpolationExpression); + R3.styleProp, this._singleStyleInputs, valueConverter, + getStylePropInterpolationExpression, false); } 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 * into the update block of a template function or a directive hostBindings function. @@ -459,9 +451,6 @@ export class StylingBuilder { buildUpdateLevelInstructions(valueConverter: ValueConverter) { const instructions: StylingInstruction[] = []; if (this.hasBindings) { - if (this._useDefaultSanitizer) { - instructions.push(this._buildSanitizerFn()); - } const styleMapInstruction = this.buildStyleMapInstruction(valueConverter); if (styleMapInstruction) { instructions.push(styleMapInstruction); diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 49beffaba4..bd1fc6186d 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -122,7 +122,6 @@ export { ɵɵelementContainerEnd, ɵɵelementContainer, ɵɵstyleMap, - ɵɵstyleSanitizer, ɵɵclassMap, ɵɵclassMapInterpolate1, ɵɵclassMapInterpolate2, diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 7baacc216e..aed5411850 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -115,7 +115,6 @@ export { ɵɵstylePropInterpolate8, ɵɵstylePropInterpolateV, - ɵɵstyleSanitizer, ɵɵtemplate, ɵɵtext, diff --git a/packages/core/src/render3/instructions/styling.ts b/packages/core/src/render3/instructions/styling.ts index 68c108bdcf..2f3f556325 100644 --- a/packages/core/src/render3/instructions/styling.ts +++ b/packages/core/src/render3/instructions/styling.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {SafeValue} from '../../sanitization/bypass'; +import {ɵɵdefaultStyleSanitizer} from '../../sanitization/sanitization'; import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; import {throwErrorIfNoChangesMode} from '../errors'; import {setInputsForProperty} from '../instructions/shared'; @@ -15,7 +16,7 @@ import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from '../interfa import {isDirectiveHost} from '../interfaces/type_checks'; import {LView, RENDERER, TVIEW} from '../interfaces/view'; import {getActiveDirectiveId, getCheckNoChangesMode, getCurrentStyleSanitizer, getLView, getSelectedIndex, incrementBindingIndex, nextBindingIndex, resetCurrentStyleSanitizer, setCurrentStyleSanitizer, setElementExitFn} from '../state'; -import {applyStylingMapDirectly, applyStylingValueDirectly, flushStyling, setClass, setStyle, updateClassViaContext, updateStyleViaContext} from '../styling/bindings'; +import {applyStylingMapDirectly, applyStylingValueDirectly, flushStyling, updateClassViaContext, updateStyleViaContext} from '../styling/bindings'; import {activateStylingMapFeature} from '../styling/map_based_bindings'; import {attachStylingDebugObject} from '../styling/styling_debug'; import {NO_CHANGE} from '../tokens'; @@ -35,26 +36,6 @@ import {getNativeByTNode, getTNode} from '../util/view_utils'; * -------- */ -/** - * Sets the current style sanitizer function which will then be used - * within all follow-up prop and map-based style binding instructions - * for the given element. - * - * Note that once styling has been applied to the element (i.e. once - * `advance(n)` is executed or the hostBindings/template function exits) - * then the active `sanitizerFn` will be set to `null`. This means that - * once styling is applied to another element then a another call to - * `styleSanitizer` will need to be made. - * - * @param sanitizerFn The sanitization function that will be used to - * process style prop/value entries. - * - * @codeGenApi - */ -export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void { - setCurrentStyleSanitizer(sanitizer); -} - /** * Update a style binding on an element with the provided value. * @@ -78,8 +59,8 @@ export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void { */ export function ɵɵstyleProp( prop: string, value: string | number | SafeValue | null, - suffix?: string | null): typeof ɵɵstyleProp { - stylePropInternal(getSelectedIndex(), prop, value, suffix); + suffixOrSanitizer?: StyleSanitizeFn | string | null): typeof ɵɵstyleProp { + stylePropInternal(getSelectedIndex(), prop, value, suffixOrSanitizer); return ɵɵstyleProp; } @@ -91,7 +72,7 @@ export function ɵɵstyleProp( */ export function stylePropInternal( elementIndex: number, prop: string, value: string | number | SafeValue | null, - suffix?: string | null | undefined): void { + suffixOrSanitizer?: StyleSanitizeFn | string | null): void { // if a value is interpolated then it may render a `NO_CHANGE` value. // in this case we do not need to do anything, but the binding index // still needs to be incremented because all styling binding values @@ -109,9 +90,12 @@ export function stylePropInternal( patchHostStylingFlag(tNode, isHostStyling(), false); } + const isString = typeof suffixOrSanitizer === 'string'; + const suffix = isString ? (suffixOrSanitizer as string) : null; + const sanitizer = isString ? null : (suffixOrSanitizer as StyleSanitizeFn | null | undefined); const updated = stylingProp( tNode, firstUpdatePass, lView, bindingIndex, prop, resolveStylePropValue(value, suffix), - false); + false, sanitizer); if (ngDevMode) { ngDevMode.styleProp++; if (updated) { @@ -154,7 +138,8 @@ export function ɵɵclassProp(className: string, value: boolean | null): typeof patchHostStylingFlag(tNode, isHostStyling(), true); } - const updated = stylingProp(tNode, firstUpdatePass, lView, bindingIndex, className, value, true); + const updated = + stylingProp(tNode, firstUpdatePass, lView, bindingIndex, className, value, true, null); if (ngDevMode) { ngDevMode.classProp++; if (updated) { @@ -177,12 +162,15 @@ export function ɵɵclassProp(className: string, value: boolean | null): typeof function stylingProp( tNode: TNode, firstUpdatePass: boolean, lView: LView, bindingIndex: number, prop: string, value: boolean | number | SafeValue | string | null | undefined | NO_CHANGE, - isClassBased: boolean): boolean { + isClassBased: boolean, sanitizer: StyleSanitizeFn | null | undefined): boolean { let updated = false; + if (sanitizer) { + setCurrentStyleSanitizer(sanitizer); + } + const native = getNativeByTNode(tNode, lView) as RElement; const context = isClassBased ? getClassesContext(tNode) : getStylesContext(tNode); - const sanitizer = isClassBased ? null : getCurrentStyleSanitizer(); // [style.prop] and [class.name] bindings do not use `bind()` and will // therefore manage accessing and updating the new value in the lView directly. @@ -283,7 +271,8 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu } stylingMap( - context, tNode, firstUpdatePass, lView, bindingIndex, styles, false, hasDirectiveInput); + context, tNode, firstUpdatePass, lView, bindingIndex, styles, false, hasDirectiveInput, + ɵɵdefaultStyleSanitizer); } /** @@ -346,7 +335,7 @@ export function classMapInternal( } stylingMap( - context, tNode, firstUpdatePass, lView, bindingIndex, classes, true, hasDirectiveInput); + context, tNode, firstUpdatePass, lView, bindingIndex, classes, true, hasDirectiveInput, null); } /** @@ -358,11 +347,11 @@ export function classMapInternal( function stylingMap( context: TStylingContext, tNode: TNode, firstUpdatePass: boolean, lView: LView, bindingIndex: number, value: {[key: string]: any} | string | null, isClassBased: boolean, - hasDirectiveInput: boolean): void { + hasDirectiveInput: boolean, sanitizer: StyleSanitizeFn | null): void { const directiveIndex = getActiveDirectiveId(); const native = getNativeByTNode(tNode, lView) as RElement; const oldValue = getValue(lView, bindingIndex); - const sanitizer = getCurrentStyleSanitizer(); + setCurrentStyleSanitizer(ɵɵdefaultStyleSanitizer); const valueHasChanged = hasValueChanged(oldValue, value); // [style] and [class] bindings do not use `bind()` and will therefore diff --git a/packages/core/src/render3/interfaces/styling.ts b/packages/core/src/render3/interfaces/styling.ts index 04a88399ed..e83f557d81 100644 --- a/packages/core/src/render3/interfaces/styling.ts +++ b/packages/core/src/render3/interfaces/styling.ts @@ -297,12 +297,14 @@ import {LView} from './view'; * * It is enabled in two cases: * - * 1. The `styleSanitizer(sanitizerFn)` instruction was called (just before any other - * styling instructions are run). + * 1. One or more styleProp instructions are generated (a sanitizer is passed in to each one). * - * 2. The component/directive `LView` instance has a sanitizer object attached to it - * (this happens when `renderComponent` is executed with a `sanitizer` value or - * if the ngModule contains a sanitizer provider attached to it). + * 2. the `styleMap` instruction runs (it uses it by default internally). + * + * Sanitization can be enabled in the cases above, however, if a sanitizer is attached + * in the `LView` then that sanitizer can be used to override whatever sanitizer is + * passed in the examples above (this happens when `renderComponent` is executed with a + * `sanitizer` value or if the ngModule contains a sanitizer provider attached to it). * * If and when sanitization is active then all property/value entries will be evaluated * through the active sanitizer before they are applied to the element (or the styling @@ -310,18 +312,6 @@ import {LView} from './view'; * * If a `Sanitizer` object is used (via the `LView[SANITIZER]` value) then that object * will be used for every property. - * - * If a `StyleSanitizerFn` function is used (via the `styleSanitizer`) then it will be - * called in two ways: - * - * 1. property validation mode: this will be called early to mark whether a property - * should be sanitized or not at during the flushing stage. - * - * 2. value sanitization mode: this will be called during the flushing stage and will - * run the sanitizer function against the value before applying it to the element. - * - * If sanitization returns an empty value then that empty value will be applied - * to the element. */ export interface TStylingContext extends Array { diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 5e4612edb2..a2ca0d40d0 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -129,7 +129,6 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵstylePropInterpolate7': r3.ɵɵstylePropInterpolate7, 'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8, 'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV, - 'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer, 'ɵɵclassProp': r3.ɵɵclassProp, 'ɵɵselect': r3.ɵɵselect, 'ɵɵadvance': r3.ɵɵadvance, diff --git a/packages/core/src/render3/styling/bindings.ts b/packages/core/src/render3/styling/bindings.ts index 176f03f050..7c0326dcee 100644 --- a/packages/core/src/render3/styling/bindings.ts +++ b/packages/core/src/render3/styling/bindings.ts @@ -97,7 +97,8 @@ export function updateStyleViaContext( context: TStylingContext, tNode: TStylingNode, data: LStylingData, element: RElement, directiveIndex: number, prop: string | null, bindingIndex: number, value: string | number | SafeValue | null | undefined | StylingMapArray | NO_CHANGE, - sanitizer: StyleSanitizeFn | null, forceUpdate: boolean, firstUpdatePass: boolean): boolean { + sanitizer: StyleSanitizeFn | null | undefined, forceUpdate: boolean, + firstUpdatePass: boolean): boolean { const isMapBased = !prop; const state = getStylingState(element, directiveIndex); const countIndex = isMapBased ? STYLING_INDEX_FOR_MAP_BINDING : state.stylesIndex++; diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index be6f6b5fa2..9e06835880 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -9,7 +9,7 @@ import {NgForOfContext} from '@angular/common'; import {ɵɵdefineComponent} from '../../src/render3/definition'; -import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index'; +import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index'; import {AttributeMarker} from '../../src/render3/interfaces/node'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, getSanitizationBypassType, unwrapSafeValue} from '../../src/sanitization/bypass'; import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; @@ -137,20 +137,22 @@ describe('instructions', () => { describe('styleProp', () => { it('should automatically sanitize unless a bypass operation is applied', () => { - const t = new TemplateFixture(() => { return createDiv(); }, () => {}, 1); - t.update(() => { - ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer); - ɵɵstyleProp('background-image', 'url("http://server")'); - }); - // nothing is set because sanitizer suppresses it. - expect(t.html).toEqual('
'); + const t = new TemplateFixture( + () => { return createDiv(); }, + () => { + ɵɵstyleProp('background-image', 'url("http://server")', ɵɵdefaultStyleSanitizer); + }, + 1); + + const element = t.hostElement.firstChild as HTMLElement; + expect(element.style.getPropertyValue('background-image')).toEqual(''); t.update(() => { - ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer); - ɵɵstyleProp('background-image', bypassSanitizationTrustStyle('url("http://server2")')); + ɵɵstyleProp( + 'background-image', bypassSanitizationTrustStyle('url("http://server2")'), + ɵɵdefaultStyleSanitizer); }); - expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image')) - .toEqual('url("http://server2")'); + expect(element.style.getPropertyValue('background-image')).toEqual('url("http://server2")'); }); }); @@ -160,10 +162,14 @@ describe('instructions', () => { function createDivWithStyle() { ɵɵelement(0, 'div', 0); } it('should add style', () => { - const fixture = new TemplateFixture( - createDivWithStyle, () => {}, 1, 0, null, null, null, undefined, attrs); - fixture.update(() => { ɵɵstyleMap({'background-color': 'red'}); }); - expect(fixture.html).toEqual('
'); + const fixture = new TemplateFixture(createDivWithStyle, () => { + ɵɵstyleMap({'background-color': 'red'}); + }, 1, 0, null, null, null, undefined, attrs); + fixture.update(); + const targetDiv = fixture.hostElement.querySelector('div') !; + const style = targetDiv.style as{[key: string]: any}; + expect(style['background-color']).toEqual('red'); + expect(style['height']).toEqual('10px'); }); it('should sanitize new styles that may contain `url` properties', () => { @@ -173,7 +179,6 @@ describe('instructions', () => { const fixture = new TemplateFixture( () => { return createDiv(); }, // () => { - ɵɵstyleSanitizer(sanitizerInterceptor.getStyleSanitizer()); ɵɵstyleMap({ 'background-image': 'background-image', 'background': 'background', diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index e2da2d0137..e76b3bd1f5 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -1037,7 +1037,7 @@ export declare function ɵɵstyleMap(styles: { [styleName: string]: any; } | NO_CHANGE | null): void; -export declare function ɵɵstyleProp(prop: string, value: string | number | SafeValue | null, suffix?: string | null): typeof ɵɵstyleProp; +export declare function ɵɵstyleProp(prop: string, value: string | number | SafeValue | null, suffixOrSanitizer?: StyleSanitizeFn | string | null): typeof ɵɵstyleProp; export declare function ɵɵstylePropInterpolate1(prop: string, prefix: string, v0: any, suffix: string, valueSuffix?: string | null): typeof ɵɵstylePropInterpolate1; @@ -1057,8 +1057,6 @@ export declare function ɵɵstylePropInterpolate8(prop: string, prefix: string, export declare function ɵɵstylePropInterpolateV(prop: string, values: any[], valueSuffix?: string | null): typeof ɵɵstylePropInterpolateV; -export declare function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void; - export declare function ɵɵtemplate(index: number, templateFn: ComponentTemplate | null, decls: number, vars: number, tagName?: string | null, attrsIndex?: number | null, localRefsIndex?: number | null, localRefExtractor?: LocalRefExtractor): void; export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: LView): ViewEngine_TemplateRef | null;