refactor(ivy): move all styling configurations into TNodeFlags (#33540)
				
					
				
			This patch gets rid of the configuration settings present in the `TStylingContext` array that is used within the styling algorithm for `[style]`, `[style.prop]`, `[class]` and `[class.name]` bindings. These configurations now all live inside of the `TNodeFlags`. PR Close #33540
This commit is contained in:
		
							parent
							
								
									e89c2dd8d0
								
							
						
					
					
						commit
						41560b47c4
					
				| @ -39,7 +39,7 @@ | ||||
|     "master": { | ||||
|       "uncompressed": { | ||||
|         "runtime-es2015": 2289, | ||||
|         "main-es2015": 265613, | ||||
|         "main-es2015": 268331, | ||||
|         "polyfills-es2015": 36808, | ||||
|         "5-es2015": 751 | ||||
|       } | ||||
|  | ||||
| @ -375,10 +375,10 @@ export function buildDebugNode(tNode: TNode, lView: LView, nodeIndex: number): D | ||||
|   const native = unwrapRNode(rawValue); | ||||
|   const componentLViewDebug = toDebug(readLViewValue(rawValue)); | ||||
|   const styles = isStylingContext(tNode.styles) ? | ||||
|       new NodeStylingDebug(tNode.styles as any as TStylingContext, lView, false) : | ||||
|       new NodeStylingDebug(tNode.styles as any as TStylingContext, tNode, lView, false) : | ||||
|       null; | ||||
|   const classes = isStylingContext(tNode.classes) ? | ||||
|       new NodeStylingDebug(tNode.classes as any as TStylingContext, lView, true) : | ||||
|       new NodeStylingDebug(tNode.classes as any as TStylingContext, tNode, lView, true) : | ||||
|       null; | ||||
|   return { | ||||
|     html: toHtml(native), | ||||
|  | ||||
| @ -11,7 +11,7 @@ import {throwErrorIfNoChangesMode} from '../errors'; | ||||
| import {setInputsForProperty} from '../instructions/shared'; | ||||
| import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node'; | ||||
| import {RElement} from '../interfaces/renderer'; | ||||
| import {StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext} from '../interfaces/styling'; | ||||
| import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from '../interfaces/styling'; | ||||
| import {isDirectiveHost} from '../interfaces/type_checks'; | ||||
| import {LView, RENDERER, TVIEW, TView} from '../interfaces/view'; | ||||
| import {getActiveDirectiveId, getCheckNoChangesMode, getCurrentStyleSanitizer, getLView, getSelectedIndex, incrementBindingIndex, nextBindingIndex, resetCurrentStyleSanitizer, setCurrentStyleSanitizer, setElementExitFn} from '../state'; | ||||
| @ -95,9 +95,21 @@ export function stylePropInternal( | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
|   const lView = getLView(); | ||||
|   const tNode = getTNode(elementIndex, lView); | ||||
|   const firstUpdatePass = lView[TVIEW].firstUpdatePass; | ||||
| 
 | ||||
|   const updated = | ||||
|       stylingProp(elementIndex, bindingIndex, prop, resolveStylePropValue(value, suffix), false); | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(tNode, TNodeFlags.hasStylePropBindings); | ||||
|     patchHostStylingFlag(tNode, isHostStyling(), false); | ||||
|   } | ||||
| 
 | ||||
|   const updated = stylingProp( | ||||
|       tNode, firstUpdatePass, lView, bindingIndex, prop, resolveStylePropValue(value, suffix), | ||||
|       false); | ||||
|   if (ngDevMode) { | ||||
|     ngDevMode.styleProp++; | ||||
|     if (updated) { | ||||
| @ -127,8 +139,20 @@ export function ɵɵclassProp(className: string, value: boolean | null): void { | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
|   const lView = getLView(); | ||||
|   const elementIndex = getSelectedIndex(); | ||||
|   const tNode = getTNode(elementIndex, lView); | ||||
|   const firstUpdatePass = lView[TVIEW].firstUpdatePass; | ||||
| 
 | ||||
|   const updated = stylingProp(getSelectedIndex(), bindingIndex, className, value, true); | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(tNode, TNodeFlags.hasClassPropBindings); | ||||
|     patchHostStylingFlag(tNode, isHostStyling(), true); | ||||
|   } | ||||
| 
 | ||||
|   const updated = stylingProp(tNode, firstUpdatePass, lView, bindingIndex, className, value, true); | ||||
|   if (ngDevMode) { | ||||
|     ngDevMode.classProp++; | ||||
|     if (updated) { | ||||
| @ -148,25 +172,15 @@ export function ɵɵclassProp(className: string, value: boolean | null): void { | ||||
|  * present together). | ||||
|  */ | ||||
| function stylingProp( | ||||
|     elementIndex: number, bindingIndex: number, prop: string, | ||||
|     tNode: TNode, firstUpdatePass: boolean, lView: LView, bindingIndex: number, prop: string, | ||||
|     value: boolean | number | SafeValue | string | null | undefined | NO_CHANGE, | ||||
|     isClassBased: boolean): boolean { | ||||
|   let updated = false; | ||||
| 
 | ||||
|   const lView = getLView(); | ||||
|   const firstUpdatePass = lView[TVIEW].firstUpdatePass; | ||||
|   const tNode = getTNode(elementIndex, lView); | ||||
|   const native = getNativeByTNode(tNode, lView) as RElement; | ||||
|   const context = isClassBased ? getClassesContext(tNode) : getStylesContext(tNode); | ||||
|   const sanitizer = isClassBased ? null : getCurrentStyleSanitizer(); | ||||
| 
 | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(context, TStylingConfig.HasPropBindings); | ||||
|   } | ||||
| 
 | ||||
|   // [style.prop] and [class.name] bindings do not use `bind()` and will
 | ||||
|   // therefore manage accessing and updating the new value in the lView directly.
 | ||||
|   // For this reason, the checkNoChanges situation must also be handled here
 | ||||
| @ -180,11 +194,12 @@ function stylingProp( | ||||
| 
 | ||||
|   // Direct Apply Case: bypass context resolution and apply the
 | ||||
|   // style/class value directly to the element
 | ||||
|   if (allowDirectStyling(context, firstUpdatePass)) { | ||||
|   if (allowDirectStyling(tNode, isClassBased, firstUpdatePass)) { | ||||
|     const sanitizerToUse = isClassBased ? null : sanitizer; | ||||
|     const renderer = getRenderer(tNode, lView); | ||||
|     updated = applyStylingValueDirectly( | ||||
|         renderer, context, native, lView, bindingIndex, prop, value, isClassBased, sanitizerToUse); | ||||
|         renderer, context, tNode, native, lView, bindingIndex, prop, value, isClassBased, | ||||
|         sanitizerToUse); | ||||
| 
 | ||||
|     if (sanitizerToUse) { | ||||
|       // it's important we remove the current style sanitizer once the
 | ||||
| @ -199,11 +214,11 @@ function stylingProp( | ||||
|     const directiveIndex = getActiveDirectiveId(); | ||||
|     if (isClassBased) { | ||||
|       updated = updateClassViaContext( | ||||
|           context, lView, native, directiveIndex, prop, bindingIndex, | ||||
|           context, tNode, lView, native, directiveIndex, prop, bindingIndex, | ||||
|           value as string | boolean | null, false, firstUpdatePass); | ||||
|     } else { | ||||
|       updated = updateStyleViaContext( | ||||
|           context, lView, native, directiveIndex, prop, bindingIndex, | ||||
|           context, tNode, lView, native, directiveIndex, prop, bindingIndex, | ||||
|           value as string | SafeValue | null, sanitizer, false, firstUpdatePass); | ||||
|     } | ||||
| 
 | ||||
| @ -245,15 +260,24 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = incrementBindingIndex(2); | ||||
|   const hostBindingsMode = isHostStyling(); | ||||
| 
 | ||||
|   // inputs are only evaluated from a template binding into a directive, therefore,
 | ||||
|   // there should not be a situation where a directive host bindings function
 | ||||
|   // evaluates the inputs (this should only happen in the template function)
 | ||||
|   if (!isHostStyling() && hasDirectiveInput && styles !== NO_CHANGE) { | ||||
|   if (!hostBindingsMode && hasDirectiveInput && styles !== NO_CHANGE) { | ||||
|     updateDirectiveInputValue(context, lView, tNode, bindingIndex, styles, false, firstUpdatePass); | ||||
|     styles = NO_CHANGE; | ||||
|   } | ||||
| 
 | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(tNode, TNodeFlags.hasStyleMapBindings); | ||||
|     patchHostStylingFlag(tNode, isHostStyling(), false); | ||||
|   } | ||||
| 
 | ||||
|   stylingMap( | ||||
|       context, tNode, firstUpdatePass, lView, bindingIndex, styles, false, hasDirectiveInput); | ||||
| } | ||||
| @ -299,15 +323,24 @@ export function classMapInternal( | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = incrementBindingIndex(2); | ||||
|   const hostBindingsMode = isHostStyling(); | ||||
| 
 | ||||
|   // inputs are only evaluated from a template binding into a directive, therefore,
 | ||||
|   // there should not be a situation where a directive host bindings function
 | ||||
|   // evaluates the inputs (this should only happen in the template function)
 | ||||
|   if (!isHostStyling() && hasDirectiveInput && classes !== NO_CHANGE) { | ||||
|   if (!hostBindingsMode && hasDirectiveInput && classes !== NO_CHANGE) { | ||||
|     updateDirectiveInputValue(context, lView, tNode, bindingIndex, classes, true, firstUpdatePass); | ||||
|     classes = NO_CHANGE; | ||||
|   } | ||||
| 
 | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(tNode, TNodeFlags.hasClassMapBindings); | ||||
|     patchHostStylingFlag(tNode, isHostStyling(), true); | ||||
|   } | ||||
| 
 | ||||
|   stylingMap( | ||||
|       context, tNode, firstUpdatePass, lView, bindingIndex, classes, true, hasDirectiveInput); | ||||
| } | ||||
| @ -336,20 +369,13 @@ function stylingMap( | ||||
|     throwErrorIfNoChangesMode(false, oldValue, value); | ||||
|   } | ||||
| 
 | ||||
|   // we check for this in the instruction code so that the context can be notified
 | ||||
|   // about prop or map bindings so that the direct apply check can decide earlier
 | ||||
|   // if it allows for context resolution to be bypassed.
 | ||||
|   if (firstUpdatePass) { | ||||
|     patchConfig(context, TStylingConfig.HasMapBindings); | ||||
|   } | ||||
| 
 | ||||
|   // Direct Apply Case: bypass context resolution and apply the
 | ||||
|   // style/class map values directly to the element
 | ||||
|   if (allowDirectStyling(context, firstUpdatePass)) { | ||||
|   if (allowDirectStyling(tNode, isClassBased, firstUpdatePass)) { | ||||
|     const sanitizerToUse = isClassBased ? null : sanitizer; | ||||
|     const renderer = getRenderer(tNode, lView); | ||||
|     applyStylingMapDirectly( | ||||
|         renderer, context, native, lView, bindingIndex, value, isClassBased, sanitizerToUse, | ||||
|         renderer, context, tNode, native, lView, bindingIndex, value, isClassBased, sanitizerToUse, | ||||
|         valueHasChanged, hasDirectiveInput); | ||||
|     if (sanitizerToUse) { | ||||
|       // it's important we remove the current style sanitizer once the
 | ||||
| @ -368,12 +394,12 @@ function stylingMap( | ||||
|     // value to the element.
 | ||||
|     if (isClassBased) { | ||||
|       updateClassViaContext( | ||||
|           context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, | ||||
|           context, tNode, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, | ||||
|           valueHasChanged, firstUpdatePass); | ||||
|     } else { | ||||
|       updateStyleViaContext( | ||||
|           context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, sanitizer, | ||||
|           valueHasChanged, firstUpdatePass); | ||||
|           context, tNode, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, | ||||
|           sanitizer, valueHasChanged, firstUpdatePass); | ||||
|     } | ||||
| 
 | ||||
|     setElementExitFn(stylingApply); | ||||
| @ -463,7 +489,7 @@ function stylingApply(): void { | ||||
|   const classesContext = isStylingContext(tNode.classes) ? tNode.classes as TStylingContext : null; | ||||
|   const stylesContext = isStylingContext(tNode.styles) ? tNode.styles as TStylingContext : null; | ||||
|   flushStyling( | ||||
|       renderer, lView, classesContext, stylesContext, native, directiveIndex, sanitizer, | ||||
|       renderer, lView, tNode, classesContext, stylesContext, native, directiveIndex, sanitizer, | ||||
|       tView.firstUpdatePass); | ||||
|   resetCurrentStyleSanitizer(); | ||||
| } | ||||
| @ -541,7 +567,7 @@ function getContext(tNode: TNode, isClassBased: boolean): TStylingContext { | ||||
|     const hasDirectives = isDirectiveHost(tNode); | ||||
|     context = allocTStylingContext(context as StylingMapArray | null, hasDirectives); | ||||
|     if (ngDevMode) { | ||||
|       attachStylingDebugObject(context as TStylingContext, isClassBased); | ||||
|       attachStylingDebugObject(context as TStylingContext, tNode, isClassBased); | ||||
|     } | ||||
| 
 | ||||
|     if (isClassBased) { | ||||
| @ -582,3 +608,10 @@ function resolveStylePropValue( | ||||
| function isHostStyling(): boolean { | ||||
|   return isHostStylingActive(getActiveDirectiveId()); | ||||
| } | ||||
| 
 | ||||
| function patchHostStylingFlag(tNode: TNode, hostBindingsMode: boolean, isClassBased: boolean) { | ||||
|   const flag = hostBindingsMode ? | ||||
|       isClassBased ? TNodeFlags.hasHostClassBindings : TNodeFlags.hasHostStyleBindings : | ||||
|       isClassBased ? TNodeFlags.hasTemplateClassBindings : TNodeFlags.hasTemplateStyleBindings; | ||||
|   patchConfig(tNode, flag); | ||||
| } | ||||
|  | ||||
| @ -67,19 +67,121 @@ export const enum TNodeFlags { | ||||
|   /** Bit #6 - This bit is set if the node has any "style" inputs */ | ||||
|   hasStyleInput = 0x20, | ||||
| 
 | ||||
|   /** Bit #7 - This bit is set if the node has initial styling */ | ||||
|   hasInitialStyling = 0x40, | ||||
| 
 | ||||
|   /** Bit #8 - This bit is set if the node has been detached by i18n */ | ||||
|   isDetached = 0x80, | ||||
|   /** Bit #7 This bit is set if the node has been detached by i18n */ | ||||
|   isDetached = 0x40, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #9 - This bit is set if the node has directives with host bindings. | ||||
|    * Bit #8 - This bit is set if the node has directives with host bindings. | ||||
|    * | ||||
|    * This flags allows us to guard host-binding logic and invoke it only on nodes | ||||
|    * that actually have directives with host bindings. | ||||
|    */ | ||||
|   hasHostBindings = 0x100, | ||||
|   hasHostBindings = 0x80, | ||||
| 
 | ||||
|   /** Bit #9 - This bit is set if the node has initial styling */ | ||||
|   hasInitialStyling = 0x100, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #10 - Whether or not there are class-based map bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [class]="x">` | ||||
|    * 2. `@HostBinding('class') x` | ||||
|    */ | ||||
|   hasClassMapBindings = 0x200, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #11 - Whether or not there are any class-based prop bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [class.name]="x">` | ||||
|    * 2. `@HostBinding('class.name') x` | ||||
|    */ | ||||
|   hasClassPropBindings = 0x400, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #12 - whether or not there are any active [class] and [class.name] bindings | ||||
|    */ | ||||
|   hasClassPropAndMapBindings = hasClassMapBindings | hasClassPropBindings, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #13 - Whether or not the context contains one or more class-based template bindings. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [class]="x">` | ||||
|    * 2. `<div [class.name]="x">` | ||||
|    */ | ||||
|   hasTemplateClassBindings = 0x800, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #14 - Whether or not the context contains one or more class-based host bindings. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `@HostBinding('class') x` | ||||
|    * 2. `@HostBinding('class.name') x` | ||||
|    */ | ||||
|   hasHostClassBindings = 0x1000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #15 - Whether or not there are two or more sources for a class property in the context. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. prop + prop: `<div [class.active]="x" dir-that-sets-active-class>` | ||||
|    * 2. map + prop: `<div [class]="x" [class.foo]>` | ||||
|    * 3. map + map: `<div [class]="x" dir-that-sets-class>` | ||||
|    */ | ||||
|   hasDuplicateClassBindings = 0x2000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #16 - Whether or not there are style-based map bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style]="x">` | ||||
|    * 2. `@HostBinding('style') x` | ||||
|    */ | ||||
|   hasStyleMapBindings = 0x4000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #17 - Whether or not there are any style-based prop bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style.prop]="x">` | ||||
|    * 2. `@HostBinding('style.prop') x` | ||||
|    */ | ||||
|   hasStylePropBindings = 0x8000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #18 - whether or not there are any active [style] and [style.prop] bindings | ||||
|    */ | ||||
|   hasStylePropAndMapBindings = hasStyleMapBindings | hasStylePropBindings, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #19 - Whether or not the context contains one or more style-based template bindings. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style]="x">` | ||||
|    * 2. `<div [style.prop]="x">` | ||||
|    */ | ||||
|   hasTemplateStyleBindings = 0x10000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #20 - Whether or not the context contains one or more style-based host bindings. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `@HostBinding('style') x` | ||||
|    * 2. `@HostBinding('style.prop') x` | ||||
|    */ | ||||
|   hasHostStyleBindings = 0x20000, | ||||
| 
 | ||||
|   /** | ||||
|    * Bit #21 - Whether or not there are two or more sources for a style property in the context. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. prop + prop: `<div [style.width]="x" dir-that-sets-width>` | ||||
|    * 2. map + prop: `<div [style]="x" [style.prop]>` | ||||
|    * 3. map + map: `<div [style]="x" dir-that-sets-style>` | ||||
|    */ | ||||
|   hasDuplicateStyleBindings = 0x40000, | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -6,9 +6,12 @@ | ||||
| * found in the LICENSE file at https://angular.io/license
 | ||||
| */ | ||||
| import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; | ||||
| 
 | ||||
| import {TNodeFlags} from './node'; | ||||
| import {ProceduralRenderer3, RElement, Renderer3} from './renderer'; | ||||
| import {LView} from './view'; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * -------- | ||||
|  * | ||||
| @ -75,7 +78,6 @@ import {LView} from './view'; | ||||
|  * //  ...
 | ||||
|  * // </div>
 | ||||
|  * tNode.styles = [ | ||||
|  *   0,         // the context config value (see `TStylingContextConfig`)
 | ||||
|  *   1,         // the total amount of sources present (only `1` b/c there are only template
 | ||||
|  * bindings) | ||||
|  *   [null],    // initial values array (an instance of `StylingMapArray`)
 | ||||
| @ -323,9 +325,6 @@ import {LView} from './view'; | ||||
|  */ | ||||
| export interface TStylingContext extends | ||||
|     Array<number|string|number|boolean|null|StylingMapArray|{}> { | ||||
|   /** Configuration data for the context */ | ||||
|   [TStylingContextIndex.ConfigPosition]: TStylingConfig; | ||||
| 
 | ||||
|   /** The total amount of sources present in the context */ | ||||
|   [TStylingContextIndex.TotalSourcesPosition]: number; | ||||
| 
 | ||||
| @ -333,125 +332,13 @@ export interface TStylingContext extends | ||||
|   [TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A series of flags used to configure the config value present within an instance of | ||||
|  * `TStylingContext`. | ||||
|  */ | ||||
| export const enum TStylingConfig { | ||||
|   /** | ||||
|    * The initial state of the styling context config. | ||||
|    */ | ||||
|   Initial = 0b000000, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not there are any directives on this element. | ||||
|    * | ||||
|    * This is used so that certain performance optimizations can | ||||
|    * take place (e.g. direct style/class binding application). | ||||
|    * | ||||
|    * Note that the presence of this flag doesn't guarantee the | ||||
|    * presence of host-level style or class bindings within any | ||||
|    * of the active directives on the element. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div dir-one>` | ||||
|    * 2. `<div dir-one [dir-two]="x">` | ||||
|    * 3. `<comp>` | ||||
|    * 4. `<comp dir-one>` | ||||
|    */ | ||||
|   HasDirectives = 0b000001, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not there are prop-based bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style.prop]="x">` | ||||
|    * 2. `<div [class.prop]="x">` | ||||
|    * 3. `@HostBinding('style.prop') x` | ||||
|    * 4. `@HostBinding('class.prop') x` | ||||
|    */ | ||||
|   HasPropBindings = 0b000010, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not there are map-based bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style]="x">` | ||||
|    * 2. `<div [class]="x">` | ||||
|    * 3. `@HostBinding('style') x` | ||||
|    * 4. `@HostBinding('class') x` | ||||
|    */ | ||||
|   HasMapBindings = 0b000100, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not there are map-based and prop-based bindings present. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style]="x" [style.prop]="y">` | ||||
|    * 2. `<div [class]="x" [style.prop]="y">` | ||||
|    * 3. `<div [style]="x" dir-that-sets-some-prop>` | ||||
|    * 4. `<div [class]="x" dir-that-sets-some-class>` | ||||
|    */ | ||||
|   HasPropAndMapBindings = HasPropBindings | HasMapBindings, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not there are two or more sources for a single property in the context. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. prop + prop: `<div [style.width]="x" dir-that-sets-width>` | ||||
|    * 2. map + prop: `<div [style]="x" [style.prop]>` | ||||
|    * 3. map + map: `<div [style]="x" dir-that-sets-style>` | ||||
|    */ | ||||
|   HasCollisions = 0b001000, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not the context contains initial styling values. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div style="width:200px">` | ||||
|    * 2. `<div class="one two three">` | ||||
|    * 3. `@Directive({ host: { 'style': 'width:200px' } })` | ||||
|    * 4. `@Directive({ host: { 'class': 'one two three' } })` | ||||
|    */ | ||||
|   HasInitialStyling = 0b0010000, | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not the context contains one or more template bindings. | ||||
|    * | ||||
|    * Examples include: | ||||
|    * 1. `<div [style]="x">` | ||||
|    * 2. `<div [style.width]="x">` | ||||
|    * 3. `<div [class]="x">` | ||||
|    * 4. `<div [class.name]="x">` | ||||
|    */ | ||||
|   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; } | ||||
|  | ||||
| @ -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. `<div [style.width]="w">`) 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; | ||||
|  | ||||
| @ -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,      //
 | ||||
|  | ||||
| @ -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<T = any>(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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
|   }, | ||||
|  | ||||
| @ -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[]) { | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user