`
- * 4. `
`
- */
- HasTemplateBindings = 0b0100000,
-
- /**
- * Whether or not the context contains one or more host bindings.
- *
- * Examples include:
- * 1. `@HostBinding('style') x`
- * 2. `@HostBinding('style.width') x`
- * 3. `@HostBinding('class') x`
- * 4. `@HostBinding('class.name') x`
- */
- HasHostBindings = 0b1000000,
-
- /** A Mask of all the configurations */
- Mask = 0b1111111,
-
- /** Total amount of configuration bits used */
- TotalBits = 7,
-}
-
/**
* An index of position and offset values used to navigate the `TStylingContext`.
*/
export const enum TStylingContextIndex {
- ConfigPosition = 0,
- TotalSourcesPosition = 1,
- InitialStylingValuePosition = 2,
- ValuesStartPosition = 3,
+ TotalSourcesPosition = 0,
+ InitialStylingValuePosition = 1,
+ ValuesStartPosition = 2,
// each tuple entry in the context
// (config, templateBitGuard, hostBindingBitGuard, prop, ...bindings||default-value)
@@ -577,3 +464,10 @@ export const enum StylingMapsSyncMode {
/** Only check to see if a value was set somewhere in each map */
CheckValuesOnly = 0b10000,
}
+
+/**
+ * Simplified `TNode` interface for styling-related code.
+ *
+ * The styling algorithm code only needs access to `flags`.
+ */
+export interface TStylingNode { flags: TNodeFlags; }
diff --git a/packages/core/src/render3/styling/bindings.ts b/packages/core/src/render3/styling/bindings.ts
index 7a01d81b56..66dc7ade51 100644
--- a/packages/core/src/render3/styling/bindings.ts
+++ b/packages/core/src/render3/styling/bindings.ts
@@ -8,10 +8,11 @@
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
import {global} from '../../util/global';
+import {TNodeFlags} from '../interfaces/node';
import {ProceduralRenderer3, RElement, Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
-import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from '../interfaces/styling';
+import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags, TStylingNode} from '../interfaces/styling';
import {NO_CHANGE} from '../tokens';
-import {DEFAULT_BINDING_INDEX, DEFAULT_BINDING_VALUE, DEFAULT_GUARD_MASK_VALUE, MAP_BASED_ENTRY_PROP_NAME, TEMPLATE_DIRECTIVE_INDEX, concatString, forceStylesAsString, getBindingValue, getConfig, getDefaultValue, getGuardMask, getInitialStylingValue, getMapProp, getMapValue, getProp, getPropValuesStartPosition, getStylingMapArray, getTotalSources, getValue, getValuesCount, hasConfig, hasValueChanged, isHostStylingActive, isSanitizationRequired, isStylingMapArray, isStylingValueDefined, normalizeIntoStylingMap, patchConfig, setDefaultValue, setGuardMask, setMapAsDirty, setValue} from '../util/styling_utils';
+import {DEFAULT_BINDING_INDEX, DEFAULT_BINDING_VALUE, DEFAULT_GUARD_MASK_VALUE, MAP_BASED_ENTRY_PROP_NAME, TEMPLATE_DIRECTIVE_INDEX, concatString, forceStylesAsString, getBindingValue, getDefaultValue, getGuardMask, getInitialStylingValue, getMapProp, getMapValue, getProp, getPropValuesStartPosition, getStylingMapArray, getTotalSources, getValue, getValuesCount, hasConfig, hasValueChanged, isHostStylingActive, isSanitizationRequired, isStylingMapArray, isStylingValueDefined, normalizeIntoStylingMap, patchConfig, setDefaultValue, setGuardMask, setMapAsDirty, setValue} from '../util/styling_utils';
import {getStylingState, resetStylingState} from './state';
@@ -27,7 +28,7 @@ const VALUE_IS_EXTERNALLY_MODIFIED = {};
*
* When a binding is encountered (e.g. `
`) then
* the binding data will be populated into a `TStylingContext` data-structure.
- * There is only one `TStylingContext` per `TNode` and each element instance
+ * There is only one `TStylingContext` per `TStylingNode` and each element instance
* will update its style/class binding values in concert with the styling
* context.
*
@@ -54,8 +55,8 @@ const STYLING_INDEX_FOR_MAP_BINDING = 0;
* and the bit mask values to be in sync).
*/
export function updateClassViaContext(
- context: TStylingContext, data: LStylingData, element: RElement, directiveIndex: number,
- prop: string | null, bindingIndex: number,
+ context: TStylingContext, tNode: TStylingNode, data: LStylingData, element: RElement,
+ directiveIndex: number, prop: string | null, bindingIndex: number,
value: boolean | string | null | undefined | StylingMapArray | NO_CHANGE, forceUpdate: boolean,
firstUpdatePass: boolean): boolean {
const isMapBased = !prop;
@@ -67,8 +68,8 @@ export function updateClassViaContext(
// is aware of the binding even if things change after the first update pass.
if (firstUpdatePass || value !== NO_CHANGE) {
const updated = updateBindingData(
- context, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate, false,
- firstUpdatePass);
+ context, tNode, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate,
+ false, firstUpdatePass, true);
if (updated || forceUpdate) {
// We flip the bit in the bitMask to reflect that the binding
// at the `index` slot has changed. This identifies to the flushing
@@ -93,8 +94,8 @@ export function updateClassViaContext(
* and the bit mask values to be in sync).
*/
export function updateStyleViaContext(
- context: TStylingContext, data: LStylingData, element: RElement, directiveIndex: number,
- prop: string | null, bindingIndex: number,
+ 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 {
const isMapBased = !prop;
@@ -109,8 +110,8 @@ export function updateStyleViaContext(
true :
(sanitizer ? sanitizer(prop !, null, StyleSanitizeMode.ValidateProperty) : false);
const updated = updateBindingData(
- context, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate,
- sanitizationRequired, firstUpdatePass);
+ context, tNode, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate,
+ sanitizationRequired, firstUpdatePass, false);
if (updated || forceUpdate) {
// We flip the bit in the bitMask to reflect that the binding
// at the `index` slot has changed. This identifies to the flushing
@@ -136,11 +137,14 @@ export function updateStyleViaContext(
* @returns whether or not the binding value was updated in the `LStylingData`.
*/
function updateBindingData(
- context: TStylingContext, data: LStylingData, counterIndex: number, sourceIndex: number,
- prop: string | null, bindingIndex: number,
+ context: TStylingContext, tNode: TStylingNode, data: LStylingData, counterIndex: number,
+ sourceIndex: number, prop: string | null, bindingIndex: number,
value: string | SafeValue | number | boolean | null | undefined | StylingMapArray,
- forceUpdate: boolean, sanitizationRequired: boolean, firstUpdatePass: boolean): boolean {
+ forceUpdate: boolean, sanitizationRequired: boolean, firstUpdatePass: boolean,
+ isClassBased: boolean): boolean {
const hostBindingsMode = isHostStylingActive(sourceIndex);
+ const hostBindingsFlag =
+ isClassBased ? TNodeFlags.hasHostClassBindings : TNodeFlags.hasHostStyleBindings;
if (firstUpdatePass) {
// this will only happen during the first update pass of the
// context. The reason why we can't use `tView.firstCreatePass`
@@ -148,19 +152,18 @@ function updateBindingData(
// update pass is executed (remember that all styling instructions
// are run in the update phase, and, as a result, are no more
// styling instructions that are run in the creation phase).
- registerBinding(context, counterIndex, sourceIndex, prop, bindingIndex, sanitizationRequired);
- patchConfig(
- context,
- hostBindingsMode ? TStylingConfig.HasHostBindings : TStylingConfig.HasTemplateBindings);
+ registerBinding(
+ context, tNode, counterIndex, sourceIndex, prop, bindingIndex, sanitizationRequired,
+ isClassBased);
}
const changed = forceUpdate || hasValueChanged(data[bindingIndex], value);
if (changed) {
setValue(data, bindingIndex, value);
- const doSetValuesAsStale = (getConfig(context) & TStylingConfig.HasHostBindings) &&
- !hostBindingsMode && (prop ? !value : true);
+ const doSetValuesAsStale =
+ hasConfig(tNode, hostBindingsFlag) && !hostBindingsMode && (prop ? !value : true);
if (doSetValuesAsStale) {
- renderHostBindingsAsStale(context, data, prop);
+ renderHostBindingsAsStale(context, tNode, data, prop, isClassBased);
}
}
return changed;
@@ -178,10 +181,13 @@ function updateBindingData(
* binding changes.
*/
function renderHostBindingsAsStale(
- context: TStylingContext, data: LStylingData, prop: string | null): void {
+ context: TStylingContext, tNode: TStylingNode, data: LStylingData, prop: string | null,
+ isClassBased: boolean): void {
const valuesCount = getValuesCount(context);
- if (prop !== null && hasConfig(context, TStylingConfig.HasPropBindings)) {
+ const hostBindingsFlag =
+ isClassBased ? TNodeFlags.hasHostClassBindings : TNodeFlags.hasHostStyleBindings;
+ if (prop !== null && hasConfig(tNode, hostBindingsFlag)) {
const itemsPerRow = TStylingContextIndex.BindingsStartOffset + valuesCount;
let i = TStylingContextIndex.ValuesStartPosition;
@@ -208,7 +214,9 @@ function renderHostBindingsAsStale(
}
}
- if (hasConfig(context, TStylingConfig.HasMapBindings)) {
+ const mapBindingsFlag =
+ isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings;
+ if (hasConfig(tNode, mapBindingsFlag)) {
const bindingsStart =
TStylingContextIndex.ValuesStartPosition + TStylingContextIndex.BindingsStartOffset;
const valuesStart = bindingsStart + 1; // the first column is template bindings
@@ -253,8 +261,9 @@ function renderHostBindingsAsStale(
* much the same as prop-based bindings, but, their property name value is set as `[MAP]`.
*/
export function registerBinding(
- context: TStylingContext, countId: number, sourceIndex: number, prop: string | null,
- bindingValue: number | null | string | boolean, sanitizationRequired?: boolean): void {
+ context: TStylingContext, tNode: TStylingNode, countId: number, sourceIndex: number,
+ prop: string | null, bindingValue: number | null | string | boolean,
+ sanitizationRequired: boolean, isClassBased: boolean): void {
let found = false;
prop = prop || MAP_BASED_ENTRY_PROP_NAME;
@@ -268,6 +277,8 @@ export function registerBinding(
totalSources++;
}
+ const collisionFlag =
+ isClassBased ? TNodeFlags.hasDuplicateClassBindings : TNodeFlags.hasDuplicateStyleBindings;
const isBindingIndexValue = typeof bindingValue === 'number';
const entriesPerRow = TStylingContextIndex.BindingsStartOffset + getValuesCount(context);
let i = TStylingContextIndex.ValuesStartPosition;
@@ -279,7 +290,7 @@ export function registerBinding(
if (prop < p) {
allocateNewContextEntry(context, i, prop, sanitizationRequired);
} else if (isBindingIndexValue) {
- patchConfig(context, TStylingConfig.HasCollisions);
+ patchConfig(tNode, collisionFlag);
}
addBindingIntoContext(context, i, bindingValue, countId, sourceIndex);
found = true;
@@ -405,7 +416,7 @@ function addNewSourceColumn(context: TStylingContext): void {
* (i.e. the `bitMask` and `counter` values for styles and classes will be cleared).
*/
export function flushStyling(
- renderer: Renderer3 | ProceduralRenderer3 | null, data: LStylingData,
+ renderer: Renderer3 | ProceduralRenderer3 | null, data: LStylingData, tNode: TStylingNode,
classesContext: TStylingContext | null, stylesContext: TStylingContext | null,
element: RElement, directiveIndex: number, styleSanitizer: StyleSanitizeFn | null,
firstUpdatePass: boolean): void {
@@ -415,22 +426,22 @@ export function flushStyling(
const hostBindingsMode = isHostStylingActive(state.sourceIndex);
if (stylesContext) {
- firstUpdatePass && syncContextInitialStyling(stylesContext);
+ firstUpdatePass && syncContextInitialStyling(stylesContext, tNode, false);
if (state.stylesBitMask !== 0) {
applyStylingViaContext(
- stylesContext, renderer, element, data, state.stylesBitMask, setStyle, styleSanitizer,
- hostBindingsMode);
+ stylesContext, tNode, renderer, element, data, state.stylesBitMask, setStyle,
+ styleSanitizer, hostBindingsMode, false);
}
}
if (classesContext) {
- firstUpdatePass && syncContextInitialStyling(classesContext);
+ firstUpdatePass && syncContextInitialStyling(classesContext, tNode, true);
if (state.classesBitMask !== 0) {
applyStylingViaContext(
- classesContext, renderer, element, data, state.classesBitMask, setClass, null,
- hostBindingsMode);
+ classesContext, tNode, renderer, element, data, state.classesBitMask, setClass, null,
+ hostBindingsMode, true);
}
}
@@ -492,10 +503,11 @@ export function flushStyling(
* ]
* ```
*/
-function syncContextInitialStyling(context: TStylingContext): void {
+function syncContextInitialStyling(
+ context: TStylingContext, tNode: TStylingNode, isClassBased: boolean): void {
// the TStylingContext always has initial style/class values which are
// stored in styling array format.
- updateInitialStylingOnContext(context, getStylingMapArray(context) !);
+ updateInitialStylingOnContext(context, tNode, getStylingMapArray(context) !, isClassBased);
}
/**
@@ -513,7 +525,8 @@ function syncContextInitialStyling(context: TStylingContext): void {
* update itself with the complete initial styling for the element.
*/
function updateInitialStylingOnContext(
- context: TStylingContext, initialStyling: StylingMapArray): void {
+ context: TStylingContext, tNode: TStylingNode, initialStyling: StylingMapArray,
+ isClassBased: boolean): void {
// `-1` is used here because all initial styling data is not a apart
// of a binding (since it's static)
const COUNT_ID_FOR_STYLING = -1;
@@ -524,13 +537,13 @@ function updateInitialStylingOnContext(
const value = getMapValue(initialStyling, i);
if (value) {
const prop = getMapProp(initialStyling, i);
- registerBinding(context, COUNT_ID_FOR_STYLING, 0, prop, value, false);
+ registerBinding(context, tNode, COUNT_ID_FOR_STYLING, 0, prop, value, false, isClassBased);
hasInitialStyling = true;
}
}
if (hasInitialStyling) {
- patchConfig(context, TStylingConfig.HasInitialStyling);
+ patchConfig(tNode, TNodeFlags.hasInitialStyling);
}
}
@@ -562,14 +575,17 @@ function updateInitialStylingOnContext(
* the styles and classes contexts).
*/
export function applyStylingViaContext(
- context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
- bindingData: LStylingData, bitMaskValue: number | boolean, applyStylingFn: ApplyStylingFn,
- sanitizer: StyleSanitizeFn | null, hostBindingsMode: boolean): void {
+ context: TStylingContext, tNode: TStylingNode, renderer: Renderer3 | ProceduralRenderer3 | null,
+ element: RElement, bindingData: LStylingData, bitMaskValue: number | boolean,
+ applyStylingFn: ApplyStylingFn, sanitizer: StyleSanitizeFn | null, hostBindingsMode: boolean,
+ isClassBased: boolean): void {
const bitMask = normalizeBitMaskValue(bitMaskValue);
let stylingMapsSyncFn: SyncStylingMapsFn|null = null;
let applyAllValues = false;
- if (hasConfig(context, TStylingConfig.HasMapBindings)) {
+ const mapBindingsFlag =
+ isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings;
+ if (hasConfig(tNode, mapBindingsFlag)) {
stylingMapsSyncFn = getStylingMapsSyncFn();
const mapsGuardMask =
getGuardMask(context, TStylingContextIndex.ValuesStartPosition, hostBindingsMode);
@@ -585,7 +601,7 @@ export function applyStylingViaContext(
totalBindingsToVisit = valuesCount - 1;
}
- let i = getPropValuesStartPosition(context);
+ let i = getPropValuesStartPosition(context, tNode, isClassBased);
while (i < context.length) {
const guardMask = getGuardMask(context, i, hostBindingsMode);
if (bitMask & guardMask) {
@@ -681,14 +697,13 @@ export function applyStylingViaContext(
* @returns whether or not the styling map was applied to the element.
*/
export function applyStylingMapDirectly(
- renderer: any, context: TStylingContext, element: RElement, data: LStylingData,
- bindingIndex: number, value: {[key: string]: any} | string | null, isClassBased: boolean,
- sanitizer?: StyleSanitizeFn | null, forceUpdate?: boolean,
- bindingValueContainsInitial?: boolean): void {
+ renderer: any, context: TStylingContext, tNode: TStylingNode, element: RElement,
+ data: LStylingData, bindingIndex: number, value: {[key: string]: any} | string | null,
+ isClassBased: boolean, sanitizer: StyleSanitizeFn | null, forceUpdate: boolean,
+ bindingValueContainsInitial: boolean): void {
const oldValue = getValue(data, bindingIndex);
if (forceUpdate || hasValueChanged(oldValue, value)) {
- const config = getConfig(context);
- const hasInitial = config & TStylingConfig.HasInitialStyling;
+ const hasInitial = hasConfig(tNode, TNodeFlags.hasInitialStyling);
const initialValue =
hasInitial && !bindingValueContainsInitial ? getInitialStylingValue(context) : null;
setValue(data, bindingIndex, value);
@@ -707,7 +722,9 @@ export function applyStylingMapDirectly(
// fast pass cannot guarantee that the external values are retained.
// When this happens, the algorithm will bail out and not write to
// the style or className attribute directly.
- let writeToAttrDirectly = !(config & TStylingConfig.HasPropBindings);
+ const propBindingsFlag =
+ isClassBased ? TNodeFlags.hasClassPropBindings : TNodeFlags.hasStylePropBindings;
+ let writeToAttrDirectly = !hasConfig(tNode, propBindingsFlag);
if (writeToAttrDirectly &&
checkIfExternallyModified(element as HTMLElement, cachedValue, isClassBased)) {
writeToAttrDirectly = false;
@@ -818,8 +835,8 @@ export function writeStylingValueDirectly(
* @returns whether or not the prop/value styling was applied to the element.
*/
export function applyStylingValueDirectly(
- renderer: any, context: TStylingContext, element: RElement, data: LStylingData,
- bindingIndex: number, prop: string, value: any, isClassBased: boolean,
+ renderer: any, context: TStylingContext, tNode: TStylingNode, element: RElement,
+ data: LStylingData, bindingIndex: number, prop: string, value: any, isClassBased: boolean,
sanitizer?: StyleSanitizeFn | null): boolean {
let applied = false;
if (hasValueChanged(data[bindingIndex], value)) {
@@ -830,7 +847,9 @@ export function applyStylingValueDirectly(
applied = applyStylingValue(renderer, element, prop, value, applyFn, bindingIndex, sanitizer);
// case 2: find the matching property in a styling map and apply the detected value
- if (!applied && hasConfig(context, TStylingConfig.HasMapBindings)) {
+ const mapBindingsFlag =
+ isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings;
+ if (!applied && hasConfig(tNode, mapBindingsFlag)) {
const state = getStylingState(element, TEMPLATE_DIRECTIVE_INDEX);
const map = isClassBased ? state.lastDirectClassMap : state.lastDirectStyleMap;
applied = map ?
@@ -839,7 +858,7 @@ export function applyStylingValueDirectly(
}
// case 3: apply the initial value (if it exists)
- if (!applied && hasConfig(context, TStylingConfig.HasInitialStyling)) {
+ if (!applied && hasConfig(tNode, TNodeFlags.hasInitialStyling)) {
const map = getStylingMapArray(context);
applied =
map ? findAndApplyMapValue(renderer, element, applyFn, map, prop, bindingIndex) : false;
diff --git a/packages/core/src/render3/styling/styling_debug.ts b/packages/core/src/render3/styling/styling_debug.ts
index 13e97065d1..3969e7731b 100644
--- a/packages/core/src/render3/styling/styling_debug.ts
+++ b/packages/core/src/render3/styling/styling_debug.ts
@@ -7,8 +7,9 @@
*/
import {createProxy} from '../../debug/proxy';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
+import {TNodeFlags} from '../interfaces/node';
import {RElement} from '../interfaces/renderer';
-import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from '../interfaces/styling';
+import {ApplyStylingFn, LStylingData, TStylingContext, TStylingContextIndex, TStylingNode} from '../interfaces/styling';
import {getCurrentStyleSanitizer} from '../state';
import {attachDebugObject} from '../util/debug_utils';
import {MAP_BASED_ENTRY_PROP_NAME, TEMPLATE_DIRECTIVE_INDEX, allowDirectStyling as _allowDirectStyling, getBindingValue, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValue, getValuesCount, hasConfig, isSanitizationRequired, isStylingContext, normalizeIntoStylingMap, setValue} from '../util/styling_utils';
@@ -52,7 +53,7 @@ export interface DebugStylingContext {
/**
- * A debug/testing-oriented summary of `TStylingConfig`.
+ * A debug/testing-oriented summary of all styling information in `TNode.flags`.
*/
export interface DebugStylingConfig {
hasMapBindings: boolean; //
@@ -150,8 +151,9 @@ export interface DebugNodeStylingEntry {
/**
* Instantiates and attaches an instance of `TStylingContextDebug` to the provided context
*/
-export function attachStylingDebugObject(context: TStylingContext, isClassBased: boolean) {
- const debug = new TStylingContextDebug(context, isClassBased);
+export function attachStylingDebugObject(
+ context: TStylingContext, tNode: TStylingNode, isClassBased: boolean) {
+ const debug = new TStylingContextDebug(context, tNode, isClassBased);
attachDebugObject(context, debug);
return debug;
}
@@ -163,9 +165,11 @@ export function attachStylingDebugObject(context: TStylingContext, isClassBased:
* application has `ngDevMode` activated.
*/
class TStylingContextDebug implements DebugStylingContext {
- constructor(public readonly context: TStylingContext, private _isClassBased: boolean) {}
+ constructor(
+ public readonly context: TStylingContext, private _tNode: TStylingNode,
+ private _isClassBased: boolean) {}
- get config(): DebugStylingConfig { return buildConfig(this.context); }
+ get config(): DebugStylingConfig { return buildConfig(this._tNode, this._isClassBased); }
/**
* Returns a detailed summary of each styling entry in the context.
@@ -176,7 +180,7 @@ class TStylingContextDebug implements DebugStylingContext {
const context = this.context;
const totalColumns = getValuesCount(context);
const entries: {[prop: string]: DebugStylingContextEntry} = {};
- const start = getPropValuesStartPosition(context);
+ const start = getPropValuesStartPosition(context, this._tNode, this._isClassBased);
let i = start;
while (i < context.length) {
const prop = getProp(context, i);
@@ -351,10 +355,10 @@ export class NodeStylingDebug implements DebugNodeStyling {
private _debugContext: DebugStylingContext;
constructor(
- context: TStylingContext|DebugStylingContext, private _data: LStylingData,
- private _isClassBased: boolean) {
+ context: TStylingContext|DebugStylingContext, private _tNode: TStylingNode,
+ private _data: LStylingData, private _isClassBased: boolean) {
this._debugContext = isStylingContext(context) ?
- new TStylingContextDebug(context as TStylingContext, _isClassBased) :
+ new TStylingContextDebug(context as TStylingContext, _tNode, _isClassBased) :
(context as DebugStylingContext);
}
@@ -421,7 +425,7 @@ export class NodeStylingDebug implements DebugNodeStyling {
});
}
- get config() { return buildConfig(this.context.context); }
+ get config() { return buildConfig(this._tNode, this._isClassBased); }
/**
* Returns a key/value map of all the styles/classes that were last applied to the element.
@@ -447,7 +451,7 @@ export class NodeStylingDebug implements DebugNodeStyling {
private _convertMapBindingsToStylingMapArrays(data: LStylingData) {
const context = this.context.context;
- const limit = getPropValuesStartPosition(context);
+ const limit = getPropValuesStartPosition(context, this._tNode, this._isClassBased);
for (let i =
TStylingContextIndex.ValuesStartPosition + TStylingContextIndex.BindingsStartOffset;
i < limit; i++) {
@@ -467,7 +471,9 @@ export class NodeStylingDebug implements DebugNodeStyling {
// element is only used when the styling algorithm attempts to
// style the value (and we mock out the stylingApplyFn anyway).
const mockElement = {} as any;
- const hasMaps = hasConfig(this.context.context, TStylingConfig.HasMapBindings);
+ const mapBindingsFlag =
+ this._isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings;
+ const hasMaps = hasConfig(this._tNode, mapBindingsFlag);
if (hasMaps) {
activateStylingMapFeature();
}
@@ -480,25 +486,34 @@ export class NodeStylingDebug implements DebugNodeStyling {
// run the template bindings
applyStylingViaContext(
- this.context.context, null, mockElement, data, true, mapFn, sanitizer, false);
+ this.context.context, this._tNode, null, mockElement, data, true, mapFn, sanitizer, false,
+ this._isClassBased);
// and also the host bindings
applyStylingViaContext(
- this.context.context, null, mockElement, data, true, mapFn, sanitizer, true);
+ this.context.context, this._tNode, null, mockElement, data, true, mapFn, sanitizer, true,
+ this._isClassBased);
}
}
-function buildConfig(context: TStylingContext) {
- const hasMapBindings = hasConfig(context, TStylingConfig.HasMapBindings);
- const hasPropBindings = hasConfig(context, TStylingConfig.HasPropBindings);
- const hasCollisions = hasConfig(context, TStylingConfig.HasCollisions);
- const hasTemplateBindings = hasConfig(context, TStylingConfig.HasTemplateBindings);
- const hasHostBindings = hasConfig(context, TStylingConfig.HasHostBindings);
+function buildConfig(tNode: TStylingNode, isClassBased: boolean): DebugStylingConfig {
+ const hasMapBindings = hasConfig(
+ tNode, isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings);
+ const hasPropBindings = hasConfig(
+ tNode, isClassBased ? TNodeFlags.hasClassPropBindings : TNodeFlags.hasStylePropBindings);
+ const hasCollisions = hasConfig(
+ tNode,
+ isClassBased ? TNodeFlags.hasDuplicateClassBindings : TNodeFlags.hasDuplicateStyleBindings);
+ const hasTemplateBindings = hasConfig(
+ tNode,
+ isClassBased ? TNodeFlags.hasTemplateClassBindings : TNodeFlags.hasTemplateStyleBindings);
+ const hasHostBindings = hasConfig(
+ tNode, isClassBased ? TNodeFlags.hasHostClassBindings : TNodeFlags.hasHostStyleBindings);
// `firstTemplatePass` here is false because the context has already been constructed
// directly within the behavior of the debugging tools (outside of style/class debugging,
// the context is constructed during the first template pass).
- const allowDirectStyling = _allowDirectStyling(context, false);
+ const allowDirectStyling = _allowDirectStyling(tNode, isClassBased, false);
return {
hasMapBindings, //
hasPropBindings, //
diff --git a/packages/core/src/render3/util/styling_utils.ts b/packages/core/src/render3/util/styling_utils.ts
index fd62e9f5c8..bc35b63967 100644
--- a/packages/core/src/render3/util/styling_utils.ts
+++ b/packages/core/src/render3/util/styling_utils.ts
@@ -5,8 +5,8 @@
* 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 {PropertyAliases, TNode, TNodeFlags} from '../interfaces/node';
-import {LStylingData, StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from '../interfaces/styling';
+import {PropertyAliases, TNodeFlags} from '../interfaces/node';
+import {LStylingData, StylingMapArray, StylingMapArrayIndex, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags, TStylingNode} from '../interfaces/styling';
import {NO_CHANGE} from '../tokens';
export const MAP_BASED_ENTRY_PROP_NAME = '[MAP]';
@@ -44,17 +44,9 @@ export const DEFAULT_GUARD_MASK_VALUE = 0b1;
export function allocTStylingContext(
initialStyling: StylingMapArray | null, hasDirectives: boolean): TStylingContext {
initialStyling = initialStyling || allocStylingMapArray(null);
- let config = TStylingConfig.Initial;
- if (hasDirectives) {
- config |= TStylingConfig.HasDirectives;
- }
- if (initialStyling.length > StylingMapArrayIndex.ValuesStartPosition) {
- config |= TStylingConfig.HasInitialStyling;
- }
return [
- config, // 1) config for the styling context
- DEFAULT_TOTAL_SOURCES, // 2) total amount of styling sources (template, directives, etc...)
- initialStyling, // 3) initial styling values
+ DEFAULT_TOTAL_SOURCES, // 1) total amount of styling sources (template, directives, etc...)
+ initialStyling, // 2) initial styling values
];
}
@@ -62,12 +54,8 @@ export function allocStylingMapArray(value: {} | string | null): StylingMapArray
return [value];
}
-export function getConfig(context: TStylingContext) {
- return context[TStylingContextIndex.ConfigPosition];
-}
-
-export function hasConfig(context: TStylingContext, flag: TStylingConfig) {
- return (getConfig(context) & flag) !== 0;
+export function hasConfig(tNode: TStylingNode, flag: TNodeFlags) {
+ return (tNode.flags & flag) !== 0;
}
/**
@@ -81,36 +69,35 @@ export function hasConfig(context: TStylingContext, flag: TStylingConfig) {
* 3. There are no collisions (i.e. properties with more than one binding) across multiple
* sources (i.e. template + directive, directive + directive, directive + component)
*/
-export function allowDirectStyling(context: TStylingContext, firstUpdatePass: boolean): boolean {
+export function allowDirectStyling(
+ tNode: TStylingNode, isClassBased: boolean, firstUpdatePass: boolean): boolean {
let allow = false;
- const config = getConfig(context);
- const hasNoDirectives = (config & TStylingConfig.HasDirectives) === 0;
// if no directives are present then we do not need populate a context at all. This
// is because duplicate prop bindings cannot be registered through the template. If
// and when this happens we can safely apply the value directly without context
// resolution...
- if (hasNoDirectives) {
+ const hasDirectives = hasConfig(tNode, TNodeFlags.hasHostBindings);
+ if (!hasDirectives) {
// `ngDevMode` is required to be checked here because tests/debugging rely on the context being
// populated. If things are in production mode then there is no need to build a context
// therefore the direct apply can be allowed (even on the first update).
allow = ngDevMode ? !firstUpdatePass : true;
} else if (!firstUpdatePass) {
- const hasNoCollisions = (config & TStylingConfig.HasCollisions) === 0;
- const hasOnlyMapsOrOnlyProps =
- (config & TStylingConfig.HasPropAndMapBindings) !== TStylingConfig.HasPropAndMapBindings;
- allow = hasNoCollisions && hasOnlyMapsOrOnlyProps;
+ const duplicateStylingFlag =
+ isClassBased ? TNodeFlags.hasDuplicateClassBindings : TNodeFlags.hasDuplicateStyleBindings;
+ const hasDuplicates = hasConfig(tNode, duplicateStylingFlag);
+ const hasOnlyMapOrPropsFlag = isClassBased ? TNodeFlags.hasClassPropAndMapBindings :
+ TNodeFlags.hasStylePropAndMapBindings;
+ const hasOnlyMapsOrOnlyProps = (tNode.flags & hasOnlyMapOrPropsFlag) !== hasOnlyMapOrPropsFlag;
+ allow = !hasDuplicates && hasOnlyMapsOrOnlyProps;
}
return allow;
}
-export function setConfig(context: TStylingContext, value: TStylingConfig): void {
- context[TStylingContextIndex.ConfigPosition] = value;
-}
-
-export function patchConfig(context: TStylingContext, flag: TStylingConfig): void {
- context[TStylingContextIndex.ConfigPosition] |= flag;
+export function patchConfig(tNode: TStylingNode, flag: TNodeFlags): void {
+ tNode.flags |= flag;
}
export function getProp(context: TStylingContext, index: number): string {
@@ -173,9 +160,11 @@ export function getValue(data: LStylingData, bindingIndex: number): T|n
return bindingIndex !== 0 ? data[bindingIndex] as T : null;
}
-export function getPropValuesStartPosition(context: TStylingContext) {
+export function getPropValuesStartPosition(
+ context: TStylingContext, tNode: TStylingNode, isClassBased: boolean) {
let startPosition = TStylingContextIndex.ValuesStartPosition;
- if (hasConfig(context, TStylingConfig.HasMapBindings)) {
+ const flag = isClassBased ? TNodeFlags.hasClassMapBindings : TNodeFlags.hasStyleMapBindings;
+ if (hasConfig(tNode, flag)) {
startPosition += TStylingContextIndex.BindingsStartOffset + getValuesCount(context);
}
return startPosition;
@@ -247,11 +236,11 @@ export function getInitialStylingValue(context: TStylingContext | StylingMapArra
return map && (map[StylingMapArrayIndex.RawValuePosition] as string | null) || '';
}
-export function hasClassInput(tNode: TNode) {
+export function hasClassInput(tNode: TStylingNode) {
return (tNode.flags & TNodeFlags.hasClassInput) !== 0;
}
-export function hasStyleInput(tNode: TNode) {
+export function hasStyleInput(tNode: TStylingNode) {
return (tNode.flags & TNodeFlags.hasStyleInput) !== 0;
}
diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json
index 6cd8042102..60337420fc 100644
--- a/packages/core/test/bundling/todo/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json
@@ -632,9 +632,6 @@
{
"name": "getComponentViewByInstance"
},
- {
- "name": "getConfig"
- },
{
"name": "getConstant"
},
@@ -932,6 +929,9 @@
{
"name": "isForwardRef"
},
+ {
+ "name": "isHostStyling"
+ },
{
"name": "isHostStylingActive"
},
@@ -1067,6 +1067,9 @@
{
"name": "patchConfig"
},
+ {
+ "name": "patchHostStylingFlag"
+ },
{
"name": "readPatchedData"
},
diff --git a/packages/core/test/render3/styling_next/styling_context_spec.ts b/packages/core/test/render3/styling_next/styling_context_spec.ts
index a2e4cd4d1b..89e914cbca 100644
--- a/packages/core/test/render3/styling_next/styling_context_spec.ts
+++ b/packages/core/test/render3/styling_next/styling_context_spec.ts
@@ -5,11 +5,22 @@
* 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 {registerBinding} from '@angular/core/src/render3/styling/bindings';
+import {TStylingContext, TStylingNode} from '@angular/core/src/render3/interfaces/styling';
+import {registerBinding as _registerBinding} from '@angular/core/src/render3/styling/bindings';
import {attachStylingDebugObject} from '@angular/core/src/render3/styling/styling_debug';
import {DEFAULT_GUARD_MASK_VALUE, allocTStylingContext} from '../../../src/render3/util/styling_utils';
+function registerBinding(
+ context: TStylingContext, countId: number, sourceIndex: number, prop: string | null,
+ value: any) {
+ let tNode: TStylingNode = (context as any).tNode;
+ if (!tNode) {
+ tNode = (context as any).tNode = {flags: 0};
+ }
+ _registerBinding(context, tNode, countId, sourceIndex, prop, value, false, false);
+}
+
describe('styling context', () => {
it('should register a series of entries into the context', () => {
const debug = makeContextWithDebug(false);
@@ -111,7 +122,9 @@ describe('styling context', () => {
function makeContextWithDebug(isClassBased: boolean) {
const ctx = allocTStylingContext(null, false);
- return attachStylingDebugObject(ctx, isClassBased);
+ const tNode: TStylingNode = {flags: 0};
+ (ctx as any).tNode = ctx;
+ return attachStylingDebugObject(ctx, tNode, isClassBased);
}
function buildGuardMask(...bindingIndices: number[]) {
diff --git a/packages/core/test/render3/styling_next/styling_debug_spec.ts b/packages/core/test/render3/styling_next/styling_debug_spec.ts
index 5c1497750a..75cdf5decf 100644
--- a/packages/core/test/render3/styling_next/styling_debug_spec.ts
+++ b/packages/core/test/render3/styling_next/styling_debug_spec.ts
@@ -5,6 +5,7 @@
* 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 {TStylingNode} from '@angular/core/src/render3/interfaces/styling';
import {registerBinding} from '@angular/core/src/render3/styling/bindings';
import {NodeStylingDebug, attachStylingDebugObject} from '@angular/core/src/render3/styling/styling_debug';
import {allocTStylingContext} from '@angular/core/src/render3/util/styling_utils';
@@ -15,12 +16,14 @@ describe('styling debugging tools', () => {
() => {
if (isIE()) return;
- const debug = makeContextWithDebug(false);
- const context = debug.context;
- const data: any[] = [];
- const d = new NodeStylingDebug(context, data, false);
+ const values = makeContextWithDebug(false);
+ const context = values.context;
+ const tNode = values.tNode;
- registerBinding(context, 0, 0, 'width', null);
+ const data: any[] = [];
+ const d = new NodeStylingDebug(context, tNode, data, false);
+
+ registerBinding(context, tNode, 0, 0, 'width', null, false, false);
expect(d.summary).toEqual({
width: {
prop: 'width',
@@ -29,7 +32,7 @@ describe('styling debugging tools', () => {
},
});
- registerBinding(context, 0, 0, 'width', '100px');
+ registerBinding(context, tNode, 0, 0, 'width', '100px', false, false);
expect(d.summary).toEqual({
width: {
prop: 'width',
@@ -41,7 +44,7 @@ describe('styling debugging tools', () => {
const someBindingIndex1 = 1;
data[someBindingIndex1] = '200px';
- registerBinding(context, 0, 0, 'width', someBindingIndex1);
+ registerBinding(context, tNode, 0, 0, 'width', someBindingIndex1, false, false);
expect(d.summary).toEqual({
width: {
prop: 'width',
@@ -53,7 +56,7 @@ describe('styling debugging tools', () => {
const someBindingIndex2 = 2;
data[someBindingIndex2] = '500px';
- registerBinding(context, 0, 1, 'width', someBindingIndex2);
+ registerBinding(context, tNode, 0, 1, 'width', someBindingIndex2, false, false);
expect(d.summary).toEqual({
width: {
prop: 'width',
@@ -66,8 +69,14 @@ describe('styling debugging tools', () => {
});
function makeContextWithDebug(isClassBased: boolean) {
- const ctx = allocTStylingContext(null, false);
- return attachStylingDebugObject(ctx, isClassBased);
+ const context = allocTStylingContext(null, false);
+ const tNode = createTStylingNode();
+ attachStylingDebugObject(context, tNode, isClassBased);
+ return {context, tNode};
+}
+
+function createTStylingNode(): TStylingNode {
+ return {flags: 0};
}
function isIE() {