refactor(core): remove style sanitization code for `[style]`/`[style.prop]` bindings (#36965)

In 420b9be1c1 all style-based sanitization code was
disabled because modern browsers no longer allow for javascript expressions within
CSS. This patch is a follow-up patch which removes all traces of style sanitization
code (both instructions and runtime logic) for the `[style]` and `[style.prop]` bindings.

PR Close #36965
This commit is contained in:
Matias Niemelä 2020-05-06 16:14:37 -07:00 committed by Misko Hevery
parent 141fcb95a4
commit 45f4a47286
17 changed files with 54 additions and 409 deletions

View File

@ -723,8 +723,6 @@ export declare function ɵɵcontentQuery<T>(directiveIndex: number, predicate: T
export declare function ɵɵCopyDefinitionFeature(definition: ɵDirectiveDef<any> | ɵComponentDef<any>): void;
export declare const ɵɵdefaultStyleSanitizer: StyleSanitizeFn;
export declare function ɵɵdefineComponent<T>(componentDefinition: {
type: Type<T>;
selectors?: ɵCssSelectorList;
@ -1050,8 +1048,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<any> | null, decls: number, vars: number, tagName?: string | null, attrsIndex?: number | null, localRefsIndex?: number | null, localRefExtractor?: LocalRefExtractor): void;
export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: ɵangular_packages_core_core_bo): TemplateRef<unknown> | null;

View File

@ -12,8 +12,8 @@
"master": {
"uncompressed": {
"runtime-es2015": 2987,
"main-es2015": 453518,
"polyfills-es2015": 52195
"main-es2015": 452717,
"polyfills-es2015": 52628
}
}
},

View File

@ -555,7 +555,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelement(0, "div");
}
if (rf & 2) {
$r3$.ɵɵstyleProp("background-image", ctx.myImage, $r3$.ɵɵdefaultStyleSanitizer);
$r3$.ɵɵstyleProp("background-image", ctx.myImage);
}
},
encapsulation: 2
@ -1151,15 +1151,15 @@ describe('compiler compliance: styling', () => {
app: {
'spec.ts': `
import {Component, NgModule, HostBinding} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
<div class="A{{p1}}B"></div>
<div class="A{{p1}}B{{p2}}C"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E{{p5}}F"></div>
<div class="A{{p1}}B{{p2}}C"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E{{p5}}F"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E{{p5}}F{{p6}}G"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E{{p5}}F{{p6}}G{{p7}}H"></div>
<div class="A{{p1}}B{{p2}}C{{p3}}D{{p4}}E{{p5}}F{{p6}}G{{p7}}H{{p8}}I"></div>
@ -1178,7 +1178,7 @@ describe('compiler compliance: styling', () => {
p8 = 100;
p9 = 100;
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
@ -1221,7 +1221,7 @@ describe('compiler compliance: styling', () => {
app: {
'spec.ts': `
import {Component, NgModule, HostBinding} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
@ -1248,7 +1248,7 @@ describe('compiler compliance: styling', () => {
p8 = 100;
p9 = 100;
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
@ -1554,8 +1554,8 @@ describe('compiler compliance: styling', () => {
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$.ɵɵstylePropInterpolate1("background", "url(", ctx.myUrl1, ")");
$r3$.ɵɵstylePropInterpolate2("border-image", "url(", ctx.myUrl2, ") ", ctx.myRepeat, " auto");
$r3$.ɵɵstylePropInterpolate3("box-shadow", "", ctx.myBoxX, " ", ctx.myBoxY, " ", ctx.myBoxWidth, " black");
}
@ -2036,110 +2036,6 @@ describe('compiler compliance: styling', () => {
});
describe('new styling refactor', () => {
it('should generate a `styleSanitizer` instruction when one or more sanitizable style properties are statically detected',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-app',
template: \`
<div [style.background-image]="bgExp"></div>
\`
})
export class MyAppComp {
bgExp = '';
}
`
}
};
const template = `
template: function MyAppComp_Template(rf, ctx) {
if (rf & 2) {
$r3$.ɵɵstyleProp("background-image", ctx.bgExp, $r3$.ɵɵdefaultStyleSanitizer);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should generate a `styleSanitizer` instruction when a `styleMap` instruction is used',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-app',
template: \`
<div [style]="mapExp"></div>
\`
})
export class MyAppComp {
mapExp = {};
}
`
}
};
const template = `
template: function MyAppComp_Template(rf, ctx) {
if (rf & 2) {
$r3$.ɵɵstyleMap(ctx.mapExp);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('shouldn\'t generate a `styleSanitizer` instruction when class-based instructions are used',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-app',
template: \`
<div [class]="mapExp" [class.name]="nameExp"></div>
\`
})
export class MyAppComp {
mapExp = {};
nameExp = true;
}
`
}
};
const template = `
template: function MyAppComp_Template(rf, ctx) {
if (rf & 2) {
$r3$.ɵɵclassMap(ctx.mapExp);
$r3$.ɵɵclassProp("name", ctx.nameExp);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should generate the correct amount of host bindings when styling is present', () => {
const files = {
app: {

View File

@ -315,8 +315,6 @@ export class Identifiers {
// sanitization-related functions
static sanitizeHtml: o.ExternalReference = {name: 'ɵɵsanitizeHtml', moduleName: CORE};
static sanitizeStyle: o.ExternalReference = {name: 'ɵɵsanitizeStyle', moduleName: CORE};
static defaultStyleSanitizer:
o.ExternalReference = {name: 'ɵɵdefaultStyleSanitizer', moduleName: CORE};
static sanitizeResourceUrl:
o.ExternalReference = {name: 'ɵɵsanitizeResourceUrl', moduleName: CORE};
static sanitizeScript: o.ExternalReference = {name: 'ɵɵsanitizeScript', moduleName: CORE};

View File

@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool} from '../../constant_pool';
import {AttributeMarker} from '../../core';
import {AST, ASTWithSource, BindingPipe, BindingType, Interpolation} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
@ -92,9 +91,8 @@ export interface StylingInstructionCall {
interface BoundStylingEntry {
hasOverrideFlag: boolean;
name: string|null;
unit: string|null;
suffix: string|null;
sourceSpan: ParseSourceSpan;
sanitize: boolean;
value: AST;
}
@ -217,20 +215,15 @@ export class StylingBuilder {
registerStyleInput(
name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan,
unit?: string|null): BoundStylingEntry|null {
suffix?: string|null): BoundStylingEntry|null {
if (isEmptyExpression(value)) {
return null;
}
name = normalizePropName(name);
const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
const entry: BoundStylingEntry = {
name: property,
sanitize: property ? isStyleSanitizable(property) : true,
unit: unit || bindingUnit,
value,
sourceSpan,
hasOverrideFlag
};
const {property, hasOverrideFlag, suffix: bindingSuffix} = parseProperty(name);
suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
const entry:
BoundStylingEntry = {name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag};
if (isMapBased) {
this._styleMapInput = entry;
} else {
@ -250,8 +243,8 @@ export class StylingBuilder {
return null;
}
const {property, hasOverrideFlag} = parseProperty(name);
const entry: BoundStylingEntry =
{name: property, value, sourceSpan, sanitize: false, hasOverrideFlag, unit: null};
const entry:
BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, suffix: null};
if (isMapBased) {
if (this._classMapInput) {
throw new Error(
@ -430,7 +423,7 @@ export class StylingBuilder {
allocateBindingSlots: totalBindingSlotsRequired,
supportsInterpolation: !!getInterpolationExpressionFn,
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
// params => stylingProp(propName, value, suffix|sanitizer)
// params => stylingProp(propName, value, suffix)
const params: o.Expression[] = [];
params.push(o.literal(input.name));
@ -441,16 +434,10 @@ export class StylingBuilder {
params.push(convertResult);
}
// [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));
}
// [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
// if that is detected then we need to pass that in as an optional param.
if (!isClassBased && input.suffix !== null) {
params.push(o.literal(input.suffix));
}
return params;
@ -517,27 +504,8 @@ function registerIntoMap(map: Map<string, number>, key: string) {
}
}
function isStyleSanitizable(prop: string): boolean {
// Note that browsers support both the dash case and
// camel case property names when setting through JS.
return prop === 'background-image' || prop === 'backgroundImage' || prop === 'background' ||
prop === 'border-image' || prop === 'borderImage' || prop === 'border-image-source' ||
prop === 'borderImageSource' || prop === 'filter' || prop === 'list-style' ||
prop === 'listStyle' || prop === 'list-style-image' || prop === 'listStyleImage' ||
prop === 'clip-path' || prop === 'clipPath';
}
/**
* Simple helper function to either provide the constant literal that will house the value
* here or a null value if the provided values are empty.
*/
function getConstantLiteralFromArray(
constantPool: ConstantPool, values: o.Expression[]): o.Expression {
return values.length ? constantPool.getConstLiteral(o.literalArr(values), true) : o.NULL_EXPR;
}
export function parseProperty(name: string):
{property: string, unit: string, hasOverrideFlag: boolean} {
{property: string, suffix: string|null, hasOverrideFlag: boolean} {
let hasOverrideFlag = false;
const overrideIndex = name.indexOf(IMPORTANT_FLAG);
if (overrideIndex !== -1) {
@ -545,15 +513,15 @@ export function parseProperty(name: string):
hasOverrideFlag = true;
}
let unit = '';
let suffix: string|null = null;
let property = name;
const unitIndex = name.lastIndexOf('.');
if (unitIndex > 0) {
unit = name.substr(unitIndex + 1);
suffix = name.substr(unitIndex + 1);
property = name.substring(0, unitIndex);
}
return {property, unit, hasOverrideFlag};
return {property, suffix, hasOverrideFlag};
}
/**

View File

@ -227,7 +227,6 @@ export {
ɵɵstylePropInterpolate7,
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstyleSanitizer,
ɵɵtemplate,
ɵɵtemplateRefExtractor,
ɵɵtext,
@ -286,7 +285,6 @@ export {
bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl,
} from './sanitization/bypass';
export {
ɵɵdefaultStyleSanitizer,
ɵɵsanitizeHtml,
ɵɵsanitizeResourceUrl,
ɵɵsanitizeScript,

View File

@ -113,7 +113,6 @@ export {
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstyleSanitizer,
ɵɵtemplate,
ɵɵtext,

View File

@ -7,7 +7,7 @@
*/
import {bindingUpdated} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {getLView, getSelectedIndex, getSelectedTNode, getTView, nextBindingIndex} from '../state';
import {getLView, getSelectedTNode, getTView, nextBindingIndex} from '../state';
import {elementAttributeInternal, storePropertyBindingMetadata} from './shared';

View File

@ -7,8 +7,6 @@
*/
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
import {stylePropNeedsSanitization, ɵɵsanitizeStyle} from '../../sanitization/sanitization';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {KeyValueArray, keyValueArrayGet, keyValueArraySet} from '../../util/array_utils';
import {assertDefined, assertEqual, assertLessThan, assertNotEqual, throwError} from '../../util/assert';
import {EMPTY_ARRAY} from '../../util/empty';
@ -18,11 +16,10 @@ import {bindingUpdated} from '../bindings';
import {DirectiveDef} from '../interfaces/definition';
import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement, Renderer3} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling';
import {HEADER_OFFSET, LView, RENDERER, TData, TView} from '../interfaces/view';
import {applyStyling} from '../node_manipulation';
import {getCurrentDirectiveDef, getCurrentStyleSanitizer, getLView, getSelectedIndex, getTView, incrementBindingIndex, setCurrentStyleSanitizer} from '../state';
import {getCurrentDirectiveDef, getLView, getSelectedIndex, getTView, incrementBindingIndex} from '../state';
import {insertTStylingBinding} from '../styling/style_binding_list';
import {getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext} from '../styling/styling_parser';
import {NO_CHANGE} from '../tokens';
@ -31,26 +28,6 @@ import {getNativeByIndex} from '../util/view_utils';
import {setDirectiveInputsWhichShadowsStyling} from './property';
/**
* 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.
*
@ -64,8 +41,6 @@ export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn|null): void {
* @param prop A valid CSS property.
* @param value New value to write (`null` or an empty string to remove).
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
*
* Note that this will apply the provided style value to the host element if this function is called
* within a host binding function.
@ -183,11 +158,11 @@ export function classStringParser(keyValueArray: KeyValueArray<any>, text: strin
*
* @param prop property name.
* @param value binding value.
* @param suffixOrSanitizer suffix or sanitization function
* @param suffix suffix for the property (e.g. `em` or `px`)
* @param isClassBased `true` if `class` change (`false` if `style`)
*/
export function checkStylingProperty(
prop: string, value: any|NO_CHANGE, suffixOrSanitizer: SanitizerFn|string|undefined|null,
prop: string, value: any|NO_CHANGE, suffix: string|undefined|null,
isClassBased: boolean): void {
const lView = getLView();
const tView = getTView();
@ -199,19 +174,10 @@ export function checkStylingProperty(
stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
}
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
// This is a work around. Once PR#34480 lands the sanitizer is passed explicitly and this line
// can be removed.
let styleSanitizer: StyleSanitizeFn|null;
if (suffixOrSanitizer == null) {
if (styleSanitizer = getCurrentStyleSanitizer()) {
suffixOrSanitizer = styleSanitizer as any;
}
}
const tNode = tView.data[getSelectedIndex() + HEADER_OFFSET] as TNode;
updateStyling(
tView, tNode, lView, lView[RENDERER], prop,
lView[bindingIndex + 1] = normalizeAndApplySuffixOrSanitizer(value, suffixOrSanitizer),
isClassBased, bindingIndex);
lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
}
}
@ -219,9 +185,7 @@ export function checkStylingProperty(
* Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* function so that `style` can be processed. This is done for tree shaking purposes.
* @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
* have different parsers.)
* @param value bound value from application
@ -605,9 +569,8 @@ function collectStylingFromTAttrs(
* keep additional `Map` to keep track of duplicates or items which have not yet been visited.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* function so that `style` can be processed. This is done
* for tree shaking purposes.
* @param stringParser The parser is passed in so that it will be tree shakable. See
* `styleStringParser` and `classStringParser`
* @param value The value to parse/convert to `KeyValueArray`
@ -639,19 +602,16 @@ export function toStylingKeyValueArray(
}
/**
* Set a `value` for a `key` taking style sanitization into account.
* Set a `value` for a `key`.
*
* See: `keyValueArraySet` for details
*
* @param keyValueArray KeyValueArray to add to.
* @param key Style key to add. (This key will be checked if it needs sanitization)
* @param value The value to set (If key needs sanitization it will be sanitized)
* @param key Style key to add.
* @param value The value to set.
*/
export function styleKeyValueArraySet(keyValueArray: KeyValueArray<any>, key: string, value: any) {
if (stylePropNeedsSanitization(key)) {
value = ɵɵsanitizeStyle(value);
}
keyValueArraySet(keyValueArray, key, value);
keyValueArraySet(keyValueArray, key, unwrapSafeValue(value));
}
/**
@ -784,10 +744,7 @@ function updateStyling(
* NOTE: The styling stores two values.
* 1. The raw value which came from the application is stored at `index + 0` location. (This value
* is used for dirty checking).
* 2. The normalized value (converted to `KeyValueArray` if map and sanitized) is stored at `index +
* 1`.
* The advantage of storing the sanitized value is that once the value is written we don't need
* to worry about sanitizing it later or keeping track of the sanitizer.
* 2. The normalized value is stored at `index + 1`.
*
* @param tData `TData` used for traversing the priority.
* @param tNode `TNode` to use for resolving static styling. Also controls search direction.
@ -867,22 +824,17 @@ function isStylingValuePresent(value: any): boolean {
}
/**
* Sanitizes or adds suffix to the value.
* Normalizes and/or adds a suffix to the value.
*
* If value is `null`/`undefined` no suffix is added
* @param value
* @param suffixOrSanitizer
* @param suffix
*/
function normalizeAndApplySuffixOrSanitizer(
value: any, suffixOrSanitizer: SanitizerFn|string|undefined|null): string|null|undefined|
boolean {
function normalizeSuffix(value: any, suffix: string|undefined|null): string|null|undefined|boolean {
if (value == null /** || value === undefined */) {
// do nothing
} else if (typeof suffixOrSanitizer === 'function') {
// sanitize the value.
value = suffixOrSanitizer(value);
} else if (typeof suffixOrSanitizer === 'string') {
value = value + suffixOrSanitizer;
} else if (typeof suffix === 'string') {
value = value + suffix;
} else if (typeof value === 'object') {
value = stringify(unwrapSafeValue(value));
}

View File

@ -133,7 +133,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,
@ -164,7 +163,6 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵsanitizeHtml': sanitization.ɵɵsanitizeHtml,
'ɵɵsanitizeStyle': sanitization.ɵɵsanitizeStyle,
'ɵɵdefaultStyleSanitizer': sanitization.ɵɵdefaultStyleSanitizer,
'ɵɵsanitizeResourceUrl': sanitization.ɵɵsanitizeResourceUrl,
'ɵɵsanitizeScript': sanitization.ɵɵsanitizeScript,
'ɵɵsanitizeUrl': sanitization.ɵɵsanitizeUrl,

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {assertDefined, assertEqual} from '../util/assert';
import {assertLViewOrUndefined} from './assert';
import {DirectiveDef} from './interfaces/definition';
@ -97,11 +96,6 @@ interface LFrame {
*/
currentNamespace: string|null;
/**
* Current sanitizer
*/
currentSanitizer: StyleSanitizeFn|null;
/**
* The root index from which pure function instructions should calculate their binding
@ -421,7 +415,6 @@ export function enterView(newView: LView, tNode: TNode|null): void {
assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
assertEqual(newLFrame.currentSanitizer, null, 'Expected clean LFrame');
assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
}
@ -454,7 +447,6 @@ function createLFrame(parent: LFrame|null): LFrame {
contextLView: null!, //
elementDepthCount: 0, //
currentNamespace: null, //
currentSanitizer: null, //
currentDirectiveIndex: -1, //
bindingRootIndex: -1, //
bindingIndex: -1, //
@ -508,7 +500,6 @@ export function leaveView() {
oldLFrame.elementDepthCount = 0;
oldLFrame.currentDirectiveIndex = -1;
oldLFrame.currentNamespace = null;
oldLFrame.currentSanitizer = null;
oldLFrame.bindingRootIndex = -1;
oldLFrame.bindingIndex = -1;
oldLFrame.currentQueryIndex = 0;
@ -602,18 +593,3 @@ export function namespaceHTMLInternal() {
export function getNamespace(): string|null {
return instructionState.lFrame.currentNamespace;
}
export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn|null) {
instructionState.lFrame.currentSanitizer = sanitizer;
}
export function resetCurrentStyleSanitizer() {
setCurrentStyleSanitizer(null);
}
export function getCurrentStyleSanitizer() {
// TODO(misko): This should throw when there is no LView, but it turns out we can get here from
// `NodeStyleDebug` hence we return `null`. This should be fixed
const lFrame = instructionState.lFrame;
return lFrame === null ? null : lFrame.currentSanitizer;
}

View File

@ -15,7 +15,6 @@ import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './by
import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer';
import {Sanitizer} from './sanitizer';
import {SecurityContext} from './security';
import {StyleSanitizeFn, StyleSanitizeMode} from './style_sanitizer';
import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
@ -176,47 +175,6 @@ export function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl: any, tag: string, prop:
return getUrlSanitizer(tag, prop)(unsafeUrl);
}
/**
* The default style sanitizer will handle sanitization for style properties.
*
* Style sanitization is no longer apart of Angular because modern browsers no
* longer support javascript expressions. Therefore, the reason why this API
* exists is exclusively for unwrapping any style value expressions that were
* marked as `SafeValue` values.
*
* This API will be removed in a future release of Angular.
*
* @publicApi
*/
export const ɵɵdefaultStyleSanitizer =
(function(prop: string, value: string|null, mode?: StyleSanitizeMode): string|boolean|null {
if (value === undefined && mode === undefined) {
// This is a workaround for the fact that `StyleSanitizeFn` should not exist once PR#34480
// lands. For now the `StyleSanitizeFn` and should act like `(value: any) => string` as a
// work around.
return ɵɵsanitizeStyle(prop);
}
mode = mode || StyleSanitizeMode.ValidateAndSanitize;
let doSanitizeValue = true;
if (mode & StyleSanitizeMode.ValidateProperty) {
doSanitizeValue = stylePropNeedsSanitization(prop);
}
if (mode & StyleSanitizeMode.SanitizeOnly) {
return doSanitizeValue ? ɵɵsanitizeStyle(value) : unwrapSafeValue(value);
} else {
return doSanitizeValue;
}
} as StyleSanitizeFn);
export function stylePropNeedsSanitization(prop: string): boolean {
return prop === 'background-image' || prop === 'backgroundImage' || prop === 'background' ||
prop === 'border-image' || prop === 'borderImage' || prop === 'border-image-source' ||
prop === 'borderImageSource' || prop === 'filter' || prop === 'list-style' ||
prop === 'listStyle' || prop === 'list-style-image' || prop === 'listStyleImage' ||
prop === 'clip-path' || prop === 'clipPath';
}
export function validateAgainstEventProperties(name: string) {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +

View File

@ -1,58 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SafeValue} from './bypass';
/*
* ========== WARNING ==========
*
* Style sanitization in Angular (for `[style.prop]` and `[style]` bindings)
* is no longer required and has been removed. The reason why this feature
* has been removed is because style-based sanitization is no longer
* required with modern browsers.
*
* The contents of this file are still in flux. Various APIs and symbols will
* be removed in a future version of Angular. Please hold off from modifying this
* file for the time being.
*
* =============================
*/
/**
* A series of flags to instruct a style sanitizer to either validate
* or sanitize a value.
*
* Because sanitization is dependent on the style property (i.e. style
* sanitization for `width` is much different than for `background-image`)
* the sanitization function (e.g. `StyleSanitizerFn`) needs to check a
* property value first before it actually sanitizes any values.
*
* This enum exist to allow a style sanitization function to either only
* do validation (check the property to see whether a value will be
* sanitized or not) or to sanitize the value (or both).
*
* @publicApi
*/
export const enum StyleSanitizeMode {
/** Just check to see if the property is required to be sanitized or not */
ValidateProperty = 0b01,
/** Skip checking the property; just sanitize the value */
SanitizeOnly = 0b10,
/** Check the property and (if true) then sanitize the value */
ValidateAndSanitize = 0b11,
}
/**
* Used to intercept and sanitize style values before they are written to the renderer.
*
* This function is designed to be called in two modes. When a value is not provided
* then the function will return a boolean whether a property will be sanitized later.
* If a value is provided then the sanitized version of that will be returned.
*/
export interface StyleSanitizeFn {
(prop: string, value: string|SafeValue|null, mode?: StyleSanitizeMode): any;
}

View File

@ -1880,7 +1880,7 @@ describe('styling', () => {
});
onlyInIvy('only ivy has [style.prop] support')
.it('should sanitize style values before writing them', () => {
.it('should not sanitize style values before writing them', () => {
@Component({
template: `
<div [style.width]="widthExp"
@ -1914,7 +1914,7 @@ describe('styling', () => {
});
onlyInIvy('only ivy has [style] support')
.it('should sanitize style values before writing them', () => {
.it('should not sanitize style values before writing them', () => {
@Component({
template: `
<div [style.width]="widthExp"

View File

@ -596,9 +596,6 @@
{
"name": "getCurrentDirectiveIndex"
},
{
"name": "getCurrentStyleSanitizer"
},
{
"name": "getDebugContext"
},
@ -1017,7 +1014,7 @@
"name": "noSideEffects"
},
{
"name": "normalizeAndApplySuffixOrSanitizer"
"name": "normalizeSuffix"
},
{
"name": "readPatchedData"

View File

@ -8,7 +8,7 @@
import {DirectiveDef} from '@angular/core/src/render3';
import {ɵɵdefineDirective} from '@angular/core/src/render3/definition';
import {classStringParser, styleStringParser, toStylingKeyValueArray, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer} from '@angular/core/src/render3/instructions/styling';
import {classStringParser, styleStringParser, toStylingKeyValueArray, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp} from '@angular/core/src/render3/instructions/styling';
import {AttributeMarker, TAttributes} from '@angular/core/src/render3/interfaces/node';
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, StylingRange, toTStylingRange, TStylingKey, TStylingRange} from '@angular/core/src/render3/interfaces/styling';
import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view';

View File

@ -10,10 +10,10 @@ import {NgForOfContext} from '@angular/common';
import {getSortedClassName} from '@angular/core/testing/src/styling';
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, SafeValue, unwrapSafeValue} from '../../src/sanitization/bypass';
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
import {ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer} from '../../src/sanitization/sanitizer';
import {SecurityContext} from '../../src/sanitization/security';
@ -170,7 +170,6 @@ describe('instructions', () => {
return createDiv();
},
() => {
ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer);
ɵɵstyleProp('background-image', backgroundImage);
},
2, 2);
@ -199,35 +198,6 @@ describe('instructions', () => {
fixture.update();
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
});
it('should sanitize new styles that may contain `url` properties', () => {
const detectedValues: string[] = [];
const sanitizerInterceptor = new MockSanitizerInterceptor(value => {
detectedValues.push(value);
});
const fixture = new TemplateFixture(
() => {
return createDiv();
}, //
() => {
ɵɵstyleSanitizer(sanitizerInterceptor.getStyleSanitizer());
ɵɵstyleMap({
'background-image': 'background-image',
'background': 'background',
'border-image': 'border-image',
'list-style': 'list-style',
'list-style-image': 'list-style-image',
'filter': 'filter',
'width': 'width'
});
},
1, 2, null, null, sanitizerInterceptor);
const props = detectedValues.sort();
expect(props).toEqual([
'background', 'background-image', 'border-image', 'filter', 'list-style', 'list-style-image'
]);
});
});
describe('elementClass', () => {
@ -559,9 +529,6 @@ class LocalMockSanitizer implements Sanitizer {
class MockSanitizerInterceptor {
public lastValue: string|null = null;
constructor(private _interceptorFn?: ((value: any) => any)|null) {}
getStyleSanitizer() {
return ɵɵdefaultStyleSanitizer;
}
sanitize(context: SecurityContext, value: LocalSanitizedValue|string|null|any): string|null {
if (this._interceptorFn) {
this._interceptorFn(value);