refactor(ivy): remove ɵɵelementAttribute instruction (#30640)

PR Close #30640
This commit is contained in:
Ben Lesh 2019-05-31 10:39:14 -07:00 committed by Misko Hevery
parent 30efb6b8ea
commit 3859bcc70c
12 changed files with 151 additions and 115 deletions

View File

@ -37,8 +37,6 @@ export class Identifiers {
static componentHostSyntheticListener: static componentHostSyntheticListener:
o.ExternalReference = {name: 'ɵɵcomponentHostSyntheticListener', moduleName: CORE}; o.ExternalReference = {name: 'ɵɵcomponentHostSyntheticListener', moduleName: CORE};
static elementAttribute: o.ExternalReference = {name: 'ɵɵelementAttribute', moduleName: CORE};
static attribute: o.ExternalReference = {name: 'ɵɵattribute', moduleName: CORE}; static attribute: o.ExternalReference = {name: 'ɵɵattribute', moduleName: CORE};
static attributeInterpolate1: static attributeInterpolate1:

View File

@ -126,7 +126,6 @@ export {
ɵɵenableBindings, ɵɵenableBindings,
ɵɵdisableBindings, ɵɵdisableBindings,
ɵɵallocHostVars, ɵɵallocHostVars,
ɵɵelementAttribute,
ɵɵelementContainerStart, ɵɵelementContainerStart,
ɵɵelementContainerEnd, ɵɵelementContainerEnd,
ɵɵstyling, ɵɵstyling,

View File

@ -7,16 +7,18 @@
*/ */
import '../util/ng_i18n_closure_mode'; import '../util/ng_i18n_closure_mode';
import {getPluralCase} from '../i18n/localization'; import {getPluralCase} from '../i18n/localization';
import {SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, getTemplateContent} from '../sanitization/html_sanitizer'; import {SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, getTemplateContent} from '../sanitization/html_sanitizer';
import {InertBodyHelper} from '../sanitization/inert_body'; import {InertBodyHelper} from '../sanitization/inert_body';
import {_sanitizeUrl, sanitizeSrcset} from '../sanitization/url_sanitizer'; import {_sanitizeUrl, sanitizeSrcset} from '../sanitization/url_sanitizer';
import {addAllToArray} from '../util/array_utils'; import {addAllToArray} from '../util/array_utils';
import {assertDataInRange, assertDefined, assertEqual, assertGreaterThan} from '../util/assert'; import {assertDataInRange, assertDefined, assertEqual, assertGreaterThan} from '../util/assert';
import {attachPatchData} from './context_discovery'; import {attachPatchData} from './context_discovery';
import {elementAttributeInternal, setDelayProjection, ɵɵload, ɵɵtextBinding} from './instructions/all'; import {setDelayProjection, ɵɵload, ɵɵtextBinding} from './instructions/all';
import {attachI18nOpCodesDebug} from './instructions/lview_debug'; import {attachI18nOpCodesDebug} from './instructions/lview_debug';
import {allocExpando, elementPropertyInternal, getOrCreateTNode, setInputsForProperty} from './instructions/shared'; import {allocExpando, elementAttributeInternal, elementPropertyInternal, getOrCreateTNode, setInputsForProperty} from './instructions/shared';
import {LContainer, NATIVE} from './interfaces/container'; import {LContainer, NATIVE} from './interfaces/container';
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n'; import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node'; import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
@ -755,10 +757,9 @@ function readCreateOpCodes(
const elementNodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF; const elementNodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
const attrName = createOpCodes[++i] as string; const attrName = createOpCodes[++i] as string;
const attrValue = createOpCodes[++i] as string; const attrValue = createOpCodes[++i] as string;
const renderer = viewData[RENDERER];
// This code is used for ICU expressions only, since we don't support // This code is used for ICU expressions only, since we don't support
// directives/components in ICUs, we don't need to worry about inputs here // directives/components in ICUs, we don't need to worry about inputs here
elementAttributeInternal(elementNodeIndex, attrName, attrValue, viewData, renderer); elementAttributeInternal(elementNodeIndex, attrName, attrValue, viewData);
break; break;
default: default:
throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`); throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`);
@ -988,8 +989,7 @@ function i18nAttributesFirstPass(tView: TView, index: number, values: string[])
generateBindingUpdateOpCodes(value, previousElementIndex, attrName), updateOpCodes); generateBindingUpdateOpCodes(value, previousElementIndex, attrName), updateOpCodes);
} else { } else {
const lView = getLView(); const lView = getLView();
const renderer = lView[RENDERER]; elementAttributeInternal(previousElementIndex, attrName, value, lView);
elementAttributeInternal(previousElementIndex, attrName, value, lView, renderer);
// Check if that attribute is a directive input // Check if that attribute is a directive input
const tNode = getTNode(previousElementIndex, lView); const tNode = getTNode(previousElementIndex, lView);
const dataValue = tNode.inputs && tNode.inputs[attrName]; const dataValue = tNode.inputs && tNode.inputs[attrName];

View File

@ -47,7 +47,6 @@ export {
ɵɵdirectiveInject, ɵɵdirectiveInject,
ɵɵelement, ɵɵelement,
ɵɵelementAttribute,
ɵɵelementContainerEnd, ɵɵelementContainerEnd,
ɵɵelementContainerStart, ɵɵelementContainerStart,

View File

@ -6,10 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {SanitizerFn} from '../interfaces/sanitization'; import {SanitizerFn} from '../interfaces/sanitization';
import {getSelectedIndex} from '../state'; import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {ɵɵelementAttribute} from './element';
import {ɵɵbind} from './property'; import {ɵɵbind} from './property';
import {elementAttributeInternal} from './shared';
/** /**
* Updates the value of or removes a bound attribute on an Element. * Updates the value of or removes a bound attribute on an Element.
@ -27,6 +30,10 @@ import {ɵɵbind} from './property';
export function ɵɵattribute( export function ɵɵattribute(
name: string, value: any, sanitizer?: SanitizerFn | null, namespace?: string) { name: string, value: any, sanitizer?: SanitizerFn | null, namespace?: string) {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
return ɵɵelementAttribute(index, name, ɵɵbind(value), sanitizer, namespace); const bound = ɵɵbind(value);
if (bound !== NO_CHANGE) {
return elementAttributeInternal(index, name, bound, lView, sanitizer, namespace);
}
} }

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {SanitizerFn} from '../interfaces/sanitization'; import {SanitizerFn} from '../interfaces/sanitization';
import {getSelectedIndex} from '../state'; import {getLView, getSelectedIndex} from '../state';
import {ɵɵelementAttribute} from './element'; import {NO_CHANGE} from '../tokens';
import {ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV} from './interpolation'; import {ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV} from './interpolation';
import {TsickleIssue1009} from './shared'; import {TsickleIssue1009, elementAttributeInternal} from './shared';
@ -41,12 +42,13 @@ export function ɵɵattributeInterpolate1(
attrName: string, prefix: string, v0: any, suffix: string, sanitizer?: SanitizerFn, attrName: string, prefix: string, v0: any, suffix: string, sanitizer?: SanitizerFn,
namespace?: string): TsickleIssue1009 { namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation1(prefix, v0, suffix); const interpolatedValue = ɵɵinterpolation1(prefix, v0, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate1; return ɵɵattributeInterpolate1;
} }
@ -80,10 +82,13 @@ export function ɵɵattributeInterpolate2(
attrName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string, attrName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string,
sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 { sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation2(prefix, v0, i0, v1, suffix); const interpolatedValue = ɵɵinterpolation2(prefix, v0, i0, v1, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate2; return ɵɵattributeInterpolate2;
} }
@ -120,10 +125,13 @@ export function ɵɵattributeInterpolate3(
attrName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, attrName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 { suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix); const interpolatedValue = ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate3; return ɵɵattributeInterpolate3;
} }
@ -162,10 +170,13 @@ export function ɵɵattributeInterpolate4(
attrName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, attrName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 { v3: any, suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix); const interpolatedValue = ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate4; return ɵɵattributeInterpolate4;
} }
@ -207,10 +218,13 @@ export function ɵɵattributeInterpolate5(
v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn, v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn,
namespace?: string): TsickleIssue1009 { namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); const interpolatedValue = ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate5; return ɵɵattributeInterpolate5;
} }
@ -254,10 +268,13 @@ export function ɵɵattributeInterpolate6(
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, sanitizer?: SanitizerFn, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, sanitizer?: SanitizerFn,
namespace?: string): TsickleIssue1009 { namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = const interpolatedValue =
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate6; return ɵɵattributeInterpolate6;
} }
@ -303,10 +320,13 @@ export function ɵɵattributeInterpolate7(
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string,
sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 { sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = const interpolatedValue =
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate7; return ɵɵattributeInterpolate7;
} }
@ -354,10 +374,13 @@ export function ɵɵattributeInterpolate8(
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 { suffix: string, sanitizer?: SanitizerFn, namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = const interpolatedValue =
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
ɵɵelementAttribute(index, attrName, interpolatedValue, sanitizer, namespace); if (interpolatedValue !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolatedValue, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolate8; return ɵɵattributeInterpolate8;
} }
@ -391,7 +414,11 @@ export function ɵɵattributeInterpolateV(
attrName: string, values: any[], sanitizer?: SanitizerFn, attrName: string, values: any[], sanitizer?: SanitizerFn,
namespace?: string): TsickleIssue1009 { namespace?: string): TsickleIssue1009 {
const index = getSelectedIndex(); const index = getSelectedIndex();
const lView = getLView();
// TODO(FW-1340): Refactor to remove the use of other instructions here. // TODO(FW-1340): Refactor to remove the use of other instructions here.
ɵɵelementAttribute(index, attrName, ɵɵinterpolationV(values), sanitizer, namespace); const interpolated = ɵɵinterpolationV(values);
if (interpolated !== NO_CHANGE) {
elementAttributeInternal(index, attrName, interpolated, lView, sanitizer, namespace);
}
return ɵɵattributeInterpolateV; return ɵɵattributeInterpolateV;
} }

View File

@ -5,16 +5,14 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {validateAgainstEventAttributes} from '../../sanitization/sanitization';
import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert'; import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert'; import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery'; import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks'; import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node'; import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer'; import {RElement} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {StylingContext} from '../interfaces/styling'; import {StylingContext} from '../interfaces/styling';
import {BINDING_INDEX, HEADER_OFFSET, LView, QUERIES, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; import {BINDING_INDEX, HEADER_OFFSET, QUERIES, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert'; import {assertNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation'; import {appendChild} from '../node_manipulation';
import {applyOnCreateInstructions} from '../node_util'; import {applyOnCreateInstructions} from '../node_util';
@ -23,14 +21,14 @@ import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticCo
import {getStylingContextFromLView, hasClassInput, hasStyleInput} from '../styling/util'; import {getStylingContextFromLView, hasClassInput, hasStyleInput} from '../styling/util';
import {registerInitialStylingIntoContext} from '../styling_next/instructions'; import {registerInitialStylingIntoContext} from '../styling_next/instructions';
import {runtimeIsNewStylingInUse} from '../styling_next/state'; import {runtimeIsNewStylingInUse} from '../styling_next/state';
import {NO_CHANGE} from '../tokens';
import {attrsStylingIndexOf, setUpAttributes} from '../util/attrs_utils'; import {attrsStylingIndexOf, setUpAttributes} from '../util/attrs_utils';
import {renderStringify} from '../util/misc_utils'; import {getNativeByTNode, getTNode} from '../util/view_utils';
import {getNativeByIndex, getNativeByTNode, getTNode} from '../util/view_utils';
import {createDirectivesAndLocals, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, setInputsForProperty, setNodeStylingTemplate} from './shared'; import {createDirectivesAndLocals, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, setInputsForProperty, setNodeStylingTemplate} from './shared';
import {getActiveDirectiveStylingIndex} from './styling'; import {getActiveDirectiveStylingIndex} from './styling';
/** /**
* Create DOM element. The instruction must later be followed by `elementEnd()` call. * Create DOM element. The instruction must later be followed by `elementEnd()` call.
* *
@ -197,54 +195,6 @@ export function ɵɵelement(
ɵɵelementEnd(); ɵɵelementEnd();
} }
/**
* Updates the value or removes an attribute on an Element.
*
* @param index The index of the element in the data array
* @param name name The name of the attribute.
* @param value value The attribute is removed when value is `null` or `undefined`.
* Otherwise the attribute value is set to the stringified value.
* @param sanitizer An optional function used to sanitize the value.
* @param namespace Optional namespace to use when setting the attribute.
*
* @codeGenApi
*/
export function ɵɵelementAttribute(
index: number, name: string, value: any, sanitizer?: SanitizerFn | null,
namespace?: string): void {
if (value !== NO_CHANGE) {
const lView = getLView();
const renderer = lView[RENDERER];
elementAttributeInternal(index, name, value, lView, renderer, sanitizer, namespace);
}
}
export function elementAttributeInternal(
index: number, name: string, value: any, lView: LView, renderer: Renderer3,
sanitizer?: SanitizerFn | null, namespace?: string) {
ngDevMode && validateAgainstEventAttributes(name);
const element = getNativeByIndex(index, lView) as RElement;
if (value == null) {
ngDevMode && ngDevMode.rendererRemoveAttribute++;
isProceduralRenderer(renderer) ? renderer.removeAttribute(element, name, namespace) :
element.removeAttribute(name);
} else {
ngDevMode && ngDevMode.rendererSetAttribute++;
const tNode = getTNode(index, lView);
const strValue =
sanitizer == null ? renderStringify(value) : sanitizer(value, tNode.tagName || '', name);
if (isProceduralRenderer(renderer)) {
renderer.setAttribute(element, name, strValue, namespace);
} else {
namespace ? element.setAttributeNS(namespace, name, strValue) :
element.setAttribute(name, strValue);
}
}
}
/** /**
* Assign static attribute values to a host element. * Assign static attribute values to a host element.
* *

View File

@ -9,7 +9,7 @@ import {Injector} from '../../di';
import {ErrorHandler} from '../../error_handler'; import {ErrorHandler} from '../../error_handler';
import {Type} from '../../interface/type'; import {Type} from '../../interface/type';
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema'; import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema';
import {validateAgainstEventProperties} from '../../sanitization/sanitization'; import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization';
import {Sanitizer} from '../../sanitization/security'; import {Sanitizer} from '../../sanitization/security';
import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual, assertNotSame} from '../../util/assert'; import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual, assertNotSame} from '../../util/assert';
import {createNamedArrayType} from '../../util/named_array_type'; import {createNamedArrayType} from '../../util/named_array_type';
@ -35,7 +35,7 @@ import {initializeStaticContext as initializeStaticStylingContext} from '../styl
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util'; import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
import {NO_CHANGE} from '../tokens'; import {NO_CHANGE} from '../tokens';
import {attrsStylingIndexOf} from '../util/attrs_utils'; import {attrsStylingIndexOf} from '../util/attrs_utils';
import {INTERPOLATION_DELIMITER, stringifyForError} from '../util/misc_utils'; import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
import {getLViewParent, getRootContext} from '../util/view_traversal_utils'; import {getLViewParent, getRootContext} from '../util/view_traversal_utils';
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils'; import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
@ -1303,6 +1303,33 @@ function addComponentLogic<T>(
} }
} }
export function elementAttributeInternal(
index: number, name: string, value: any, lView: LView, sanitizer?: SanitizerFn | null,
namespace?: string) {
ngDevMode && assertNotSame(value, NO_CHANGE as any, 'Incoming value should never be NO_CHANGE.');
ngDevMode && validateAgainstEventAttributes(name);
const element = getNativeByIndex(index, lView) as RElement;
const renderer = lView[RENDERER];
if (value == null) {
ngDevMode && ngDevMode.rendererRemoveAttribute++;
isProceduralRenderer(renderer) ? renderer.removeAttribute(element, name, namespace) :
element.removeAttribute(name);
} else {
ngDevMode && ngDevMode.rendererSetAttribute++;
const tNode = getTNode(index, lView);
const strValue =
sanitizer == null ? renderStringify(value) : sanitizer(value, tNode.tagName || '', name);
if (isProceduralRenderer(renderer)) {
renderer.setAttribute(element, name, strValue, namespace);
} else {
namespace ? element.setAttributeNS(namespace, name, strValue) :
element.setAttribute(name, strValue);
}
}
}
/** /**
* Sets initial input properties on directive instances from attribute data * Sets initial input properties on directive instances from attribute data
* *

View File

@ -46,7 +46,6 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature, 'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature,
'ɵɵProvidersFeature': r3.ɵɵProvidersFeature, 'ɵɵProvidersFeature': r3.ɵɵProvidersFeature,
'ɵɵInheritDefinitionFeature': r3.ɵɵInheritDefinitionFeature, 'ɵɵInheritDefinitionFeature': r3.ɵɵInheritDefinitionFeature,
'ɵɵelementAttribute': r3.ɵɵelementAttribute,
'ɵɵbind': r3.ɵɵbind, 'ɵɵbind': r3.ɵɵbind,
'ɵɵcontainer': r3.ɵɵcontainer, 'ɵɵcontainer': r3.ɵɵcontainer,
'ɵɵnextContext': r3.ɵɵnextContext, 'ɵɵnextContext': r3.ɵɵnextContext,

View File

@ -9,7 +9,7 @@
import {NgForOfContext} from '@angular/common'; import {NgForOfContext} from '@angular/common';
import {ɵɵdefineComponent} from '../../src/render3/definition'; import {ɵɵdefineComponent} from '../../src/render3/definition';
import {RenderFlags, ɵɵbind, ɵɵclassMap, ɵɵelement, ɵɵelementAttribute, ɵɵelementEnd, ɵɵelementStart, ɵɵinterpolation1, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index'; import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵinterpolation1, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
import {AttributeMarker} from '../../src/render3/interfaces/node'; import {AttributeMarker} from '../../src/render3/interfaces/node';
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
@ -119,16 +119,20 @@ describe('instructions', () => {
}); });
}); });
describe('elementAttribute', () => { describe('attribute', () => {
it('should use sanitizer function', () => { it('should use sanitizer function', () => {
const t = new TemplateFixture(createDiv, () => {}, 1); const t = new TemplateFixture(createDiv, () => {}, 1, 1);
t.update(() => ɵɵelementAttribute(0, 'title', 'javascript:true', ɵɵsanitizeUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl);
});
expect(t.html).toEqual('<div title="unsafe:javascript:true"></div>'); expect(t.html).toEqual('<div title="unsafe:javascript:true"></div>');
t.update( t.update(() => {
() => ɵɵelementAttribute( ɵɵselect(0);
0, 'title', bypassSanitizationTrustUrl('javascript:true'), ɵɵsanitizeUrl)); ɵɵattribute('title', bypassSanitizationTrustUrl('javascript:true'), ɵɵsanitizeUrl);
});
expect(t.html).toEqual('<div title="javascript:true"></div>'); expect(t.html).toEqual('<div title="javascript:true"></div>');
expect(ngDevMode).toHaveProperties({ expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1, firstTemplatePass: 1,
@ -360,99 +364,126 @@ describe('instructions', () => {
describe('sanitization injection compatibility', () => { describe('sanitization injection compatibility', () => {
it('should work for url sanitization', () => { it('should work for url sanitization', () => {
const s = new LocalMockSanitizer(value => `${value}-sanitized`); const s = new LocalMockSanitizer(value => `${value}-sanitized`);
const t = new TemplateFixture(createAnchor, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s);
const inputValue = 'http://foo'; const inputValue = 'http://foo';
const outputValue = 'http://foo-sanitized'; const outputValue = 'http://foo-sanitized';
t.update(() => ɵɵelementAttribute(0, 'href', inputValue, ɵɵsanitizeUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('href', inputValue, ɵɵsanitizeUrl);
});
expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(t.html).toEqual(`<a href="${outputValue}"></a>`);
expect(s.lastSanitizedValue).toEqual(outputValue); expect(s.lastSanitizedValue).toEqual(outputValue);
}); });
it('should bypass url sanitization if marked by the service', () => { it('should bypass url sanitization if marked by the service', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createAnchor, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s);
const inputValue = s.bypassSecurityTrustUrl('http://foo'); const inputValue = s.bypassSecurityTrustUrl('http://foo');
const outputValue = 'http://foo'; const outputValue = 'http://foo';
t.update(() => ɵɵelementAttribute(0, 'href', inputValue, ɵɵsanitizeUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('href', inputValue, ɵɵsanitizeUrl);
});
expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(t.html).toEqual(`<a href="${outputValue}"></a>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });
it('should bypass ivy-level url sanitization if a custom sanitizer is used', () => { it('should bypass ivy-level url sanitization if a custom sanitizer is used', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createAnchor, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s);
const inputValue = bypassSanitizationTrustUrl('http://foo'); const inputValue = bypassSanitizationTrustUrl('http://foo');
const outputValue = 'http://foo-ivy'; const outputValue = 'http://foo-ivy';
t.update(() => ɵɵelementAttribute(0, 'href', inputValue, ɵɵsanitizeUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('href', inputValue, ɵɵsanitizeUrl);
});
expect(t.html).toEqual(`<a href="${outputValue}"></a>`); expect(t.html).toEqual(`<a href="${outputValue}"></a>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });
it('should work for style sanitization', () => { it('should work for style sanitization', () => {
const s = new LocalMockSanitizer(value => `color:blue`); const s = new LocalMockSanitizer(value => `color:blue`);
const t = new TemplateFixture(createDiv, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s);
const inputValue = 'color:red'; const inputValue = 'color:red';
const outputValue = 'color:blue'; const outputValue = 'color:blue';
t.update(() => ɵɵelementAttribute(0, 'style', inputValue, ɵɵsanitizeStyle)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('style', inputValue, ɵɵsanitizeStyle);
});
expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`);
expect(s.lastSanitizedValue).toEqual(outputValue); expect(s.lastSanitizedValue).toEqual(outputValue);
}); });
it('should bypass style sanitization if marked by the service', () => { it('should bypass style sanitization if marked by the service', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createDiv, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s);
const inputValue = s.bypassSecurityTrustStyle('color:maroon'); const inputValue = s.bypassSecurityTrustStyle('color:maroon');
const outputValue = 'color:maroon'; const outputValue = 'color:maroon';
t.update(() => ɵɵelementAttribute(0, 'style', inputValue, ɵɵsanitizeStyle)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('style', inputValue, ɵɵsanitizeStyle);
});
expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });
it('should bypass ivy-level style sanitization if a custom sanitizer is used', () => { it('should bypass ivy-level style sanitization if a custom sanitizer is used', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createDiv, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s);
const inputValue = bypassSanitizationTrustStyle('font-family:foo'); const inputValue = bypassSanitizationTrustStyle('font-family:foo');
const outputValue = 'font-family:foo-ivy'; const outputValue = 'font-family:foo-ivy';
t.update(() => ɵɵelementAttribute(0, 'style', inputValue, ɵɵsanitizeStyle)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('style', inputValue, ɵɵsanitizeStyle);
});
expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`); expect(stripStyleWsCharacters(t.html)).toEqual(`<div style="${outputValue}"></div>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });
it('should work for resourceUrl sanitization', () => { it('should work for resourceUrl sanitization', () => {
const s = new LocalMockSanitizer(value => `${value}-sanitized`); const s = new LocalMockSanitizer(value => `${value}-sanitized`);
const t = new TemplateFixture(createScript, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s);
const inputValue = 'http://resource'; const inputValue = 'http://resource';
const outputValue = 'http://resource-sanitized'; const outputValue = 'http://resource-sanitized';
t.update(() => ɵɵelementAttribute(0, 'src', inputValue, ɵɵsanitizeResourceUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl);
});
expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(t.html).toEqual(`<script src="${outputValue}"></script>`);
expect(s.lastSanitizedValue).toEqual(outputValue); expect(s.lastSanitizedValue).toEqual(outputValue);
}); });
it('should bypass resourceUrl sanitization if marked by the service', () => { it('should bypass resourceUrl sanitization if marked by the service', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createScript, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s);
const inputValue = s.bypassSecurityTrustResourceUrl('file://all-my-secrets.pdf'); const inputValue = s.bypassSecurityTrustResourceUrl('file://all-my-secrets.pdf');
const outputValue = 'file://all-my-secrets.pdf'; const outputValue = 'file://all-my-secrets.pdf';
t.update(() => ɵɵelementAttribute(0, 'src', inputValue, ɵɵsanitizeResourceUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl);
});
expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(t.html).toEqual(`<script src="${outputValue}"></script>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });
it('should bypass ivy-level resourceUrl sanitization if a custom sanitizer is used', () => { it('should bypass ivy-level resourceUrl sanitization if a custom sanitizer is used', () => {
const s = new LocalMockSanitizer(value => ''); const s = new LocalMockSanitizer(value => '');
const t = new TemplateFixture(createScript, undefined, 1, 0, null, null, s); const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s);
const inputValue = bypassSanitizationTrustResourceUrl('file://all-my-secrets.pdf'); const inputValue = bypassSanitizationTrustResourceUrl('file://all-my-secrets.pdf');
const outputValue = 'file://all-my-secrets.pdf-ivy'; const outputValue = 'file://all-my-secrets.pdf-ivy';
t.update(() => ɵɵelementAttribute(0, 'src', inputValue, ɵɵsanitizeResourceUrl)); t.update(() => {
ɵɵselect(0);
ɵɵattribute('src', inputValue, ɵɵsanitizeResourceUrl);
});
expect(t.html).toEqual(`<script src="${outputValue}"></script>`); expect(t.html).toEqual(`<script src="${outputValue}"></script>`);
expect(s.lastSanitizedValue).toBeFalsy(); expect(s.lastSanitizedValue).toBeFalsy();
}); });

View File

@ -8,8 +8,8 @@
import {RendererType2} from '../../src/render/api'; import {RendererType2} from '../../src/render/api';
import {getLContext} from '../../src/render3/context_discovery'; import {getLContext} from '../../src/render3/context_discovery';
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵproperty} from '../../src/render3/index'; import {AttributeMarker, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵproperty} from '../../src/render3/index';
import {ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementAttribute, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵselect, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all'; import {ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵselect, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context'; import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
@ -286,7 +286,8 @@ describe('component animations', () => {
ɵɵelement(0, 'div', [AttributeMarker.Bindings, '@fooAnimation']); ɵɵelement(0, 'div', [AttributeMarker.Bindings, '@fooAnimation']);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementAttribute(0, '@fooAnimation', ɵɵbind(ctx.animationValue)); ɵɵselect(0);
ɵɵattribute('@fooAnimation', ctx.animationValue);
} }
} }
}); });

View File

@ -818,8 +818,6 @@ export declare function ɵɵdisableBindings(): void;
export declare function ɵɵelement(index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void; export declare function ɵɵelement(index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void;
export declare function ɵɵelementAttribute(index: number, name: string, value: any, sanitizer?: SanitizerFn | null, namespace?: string): void;
export declare function ɵɵelementContainerEnd(): void; export declare function ɵɵelementContainerEnd(): void;
export declare function ɵɵelementContainerStart(index: number, attrs?: TAttributes | null, localRefs?: string[] | null): void; export declare function ɵɵelementContainerStart(index: number, attrs?: TAttributes | null, localRefs?: string[] | null): void;