From 35a95a8a7e9c62107039738eeb606d372fc47fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Wed, 25 Sep 2019 11:05:07 -0700 Subject: [PATCH] refactor(ivy): ensure `StylingDebug` instances provide context debug info (#32856) This patch enables a styling debug instance (which is apart of the `debugNode.styles` or `debugNode.classes` data structures) to expose its context value so that it can be easily debugged. PR Close #32856 --- .../src/render3/instructions/lview_debug.ts | 6 +- .../core/src/render3/styling/styling_debug.ts | 196 ++++++++++-------- .../core/src/render3/util/styling_utils.ts | 2 +- packages/core/test/acceptance/styling_spec.ts | 2 +- 4 files changed, 118 insertions(+), 88 deletions(-) diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index a6d647f79d..362e678c98 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -20,7 +20,7 @@ import {TQueries} from '../interfaces/query'; import {RComment, RElement, RNode} from '../interfaces/renderer'; import {TStylingContext} from '../interfaces/styling'; import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, T_HOST} from '../interfaces/view'; -import {DebugStyling as DebugNewStyling, NodeStylingDebug} from '../styling/styling_debug'; +import {DebugNodeStyling, NodeStylingDebug} from '../styling/styling_debug'; import {attachDebugObject} from '../util/debug_utils'; import {isStylingContext} from '../util/styling_utils'; import {getTNode, unwrapRNode} from '../util/view_utils'; @@ -343,8 +343,8 @@ export class LViewDebug { export interface DebugNode { html: string|null; native: Node; - styles: DebugNewStyling|null; - classes: DebugNewStyling|null; + styles: DebugNodeStyling|null; + classes: DebugNodeStyling|null; nodes: DebugNode[]|null; component: LViewDebug|null; } diff --git a/packages/core/src/render3/styling/styling_debug.ts b/packages/core/src/render3/styling/styling_debug.ts index f255056df3..4f29687fd8 100644 --- a/packages/core/src/render3/styling/styling_debug.ts +++ b/packages/core/src/render3/styling/styling_debug.ts @@ -10,7 +10,7 @@ import {RElement} from '../interfaces/renderer'; import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from '../interfaces/styling'; import {getCurrentStyleSanitizer} from '../state'; import {attachDebugObject} from '../util/debug_utils'; -import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isSanitizationRequired} from '../util/styling_utils'; +import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isSanitizationRequired, isStylingContext} from '../util/styling_utils'; import {applyStylingViaContext} from './bindings'; import {activateStylingMapFeature} from './map_based_bindings'; @@ -27,65 +27,42 @@ import {activateStylingMapFeature} from './map_based_bindings'; * -------- */ - /** - * A debug/testing-oriented summary of a styling entry. + * A debug-friendly version of `TStylingContext`. * - * A value such as this is generated as an artifact of the `DebugStyling` - * summary. + * An instance of this is attached to `tStylingContext.debug` when `ngDevMode` is active. */ -export interface LStylingSummary { - /** The style/class property that the summary is attached to */ - prop: string; +export interface DebugStylingContext { + /** The configuration settings of the associated `TStylingContext` */ + config: DebugStylingConfig; - /** The last applied value for the style/class property */ - value: string|boolean|null; - - /** The binding index of the last applied style/class property */ - bindingIndex: number|null; -} - -/** - * A debug/testing-oriented summary of all styling entries for a `DebugNode` instance. - */ -export interface DebugStyling { /** The associated TStylingContext instance */ context: TStylingContext; - /** Which configuration flags are active (see `TStylingContextConfig`) */ - config: { - hasMapBindings: boolean; // - hasPropBindings: boolean; // - hasCollisions: boolean; // - hasTemplateBindings: boolean; // - hasHostBindings: boolean; // - templateBindingsLocked: boolean; // - hostBindingsLocked: boolean; // - allowDirectStyling: boolean; // - }; - - /** - * A summarization of each style/class property - * present in the context - */ - summary: {[propertyName: string]: LStylingSummary}; - - /** - * A key/value map of all styling properties and their - * runtime values - */ - values: {[propertyName: string]: string | number | null | boolean}; - - /** - * Overrides the sanitizer used to process styles - */ - overrideSanitizer(sanitizer: StyleSanitizeFn|null): void; + /** The associated TStylingContext instance */ + entries: {[prop: string]: DebugStylingContextEntry}; } + +/** + * A debug/testing-oriented summary of `TStylingConfig`. + */ +export interface DebugStylingConfig { + hasMapBindings: boolean; // + hasPropBindings: boolean; // + hasCollisions: boolean; // + hasTemplateBindings: boolean; // + hasHostBindings: boolean; // + templateBindingsLocked: boolean; // + hostBindingsLocked: boolean; // + allowDirectStyling: boolean; // +} + + /** * A debug/testing-oriented summary of all styling entries within a `TStylingContext`. */ -export interface TStylingTupleSummary { +export interface DebugStylingContextEntry { /** The property (style or class property) that this tuple represents */ prop: string; @@ -120,6 +97,51 @@ export interface TStylingTupleSummary { sources: (number|null|string)[]; } + +/** + * A debug/testing-oriented summary of all styling entries for a `DebugNode` instance. + */ +export interface DebugNodeStyling { + /** The associated debug context of the TStylingContext instance */ + context: DebugStylingContext; + + /** + * A summarization of each style/class property + * present in the context + */ + summary: {[propertyName: string]: DebugNodeStylingEntry}; + + /** + * A key/value map of all styling properties and their + * runtime values + */ + values: {[propertyName: string]: string | number | null | boolean}; + + /** + * Overrides the sanitizer used to process styles + */ + overrideSanitizer(sanitizer: StyleSanitizeFn|null): void; +} + + +/** + * A debug/testing-oriented summary of a styling entry. + * + * A value such as this is generated as an artifact of the `DebugStyling` + * summary. + */ +export interface DebugNodeStylingEntry { + /** The style/class property that the summary is attached to */ + prop: string; + + /** The last applied value for the style/class property */ + value: string|boolean|null; + + /** The binding index of the last applied style/class property */ + bindingIndex: number|null; +} + + /** * Instantiates and attaches an instance of `TStylingContextDebug` to the provided context */ @@ -135,21 +157,20 @@ export function attachStylingDebugObject(context: TStylingContext) { * This class is designed to be used within testing code or when an * application has `ngDevMode` activated. */ -class TStylingContextDebug { +class TStylingContextDebug implements DebugStylingContext { constructor(public readonly context: TStylingContext) {} - get isTemplateLocked() { return isContextLocked(this.context, true); } - get isHostBindingsLocked() { return isContextLocked(this.context, false); } + get config(): DebugStylingConfig { return buildConfig(this.context); } /** * Returns a detailed summary of each styling entry in the context. * * See `TStylingTupleSummary`. */ - get entries(): {[prop: string]: TStylingTupleSummary} { + get entries(): {[prop: string]: DebugStylingContextEntry} { const context = this.context; const totalColumns = getValuesCount(context); - const entries: {[prop: string]: TStylingTupleSummary} = {}; + const entries: {[prop: string]: DebugStylingContextEntry} = {}; const start = getPropValuesStartPosition(context); let i = start; while (i < context.length) { @@ -189,12 +210,19 @@ class TStylingContextDebug { * This class is designed to be used within testing code or when an * application has `ngDevMode` activated. */ -export class NodeStylingDebug implements DebugStyling { +export class NodeStylingDebug implements DebugNodeStyling { private _sanitizer: StyleSanitizeFn|null = null; + private _debugContext: DebugStylingContext; constructor( - public context: TStylingContext, private _data: LStylingData, - private _isClassBased?: boolean) {} + context: TStylingContext|DebugStylingContext, private _data: LStylingData, + private _isClassBased?: boolean) { + this._debugContext = isStylingContext(context) ? + new TStylingContextDebug(context as TStylingContext) : + (context as DebugStylingContext); + } + + get context() { return this._debugContext; } /** * Overrides the sanitizer used to process styles. @@ -207,36 +235,15 @@ export class NodeStylingDebug implements DebugStyling { * * See `LStylingSummary`. */ - get summary(): {[key: string]: LStylingSummary} { - const entries: {[key: string]: LStylingSummary} = {}; + get summary(): {[key: string]: DebugNodeStylingEntry} { + const entries: {[key: string]: DebugNodeStylingEntry} = {}; this._mapValues((prop: string, value: any, bindingIndex: number | null) => { entries[prop] = {prop, value, bindingIndex}; }); return entries; } - get config() { - const hasMapBindings = hasConfig(this.context, TStylingConfig.HasMapBindings); - const hasPropBindings = hasConfig(this.context, TStylingConfig.HasPropBindings); - const hasCollisions = hasConfig(this.context, TStylingConfig.HasCollisions); - const hasTemplateBindings = hasConfig(this.context, TStylingConfig.HasTemplateBindings); - const hasHostBindings = hasConfig(this.context, TStylingConfig.HasHostBindings); - const templateBindingsLocked = hasConfig(this.context, TStylingConfig.TemplateBindingsLocked); - const hostBindingsLocked = hasConfig(this.context, TStylingConfig.HostBindingsLocked); - const allowDirectStyling = - _allowDirectStyling(this.context, false) || _allowDirectStyling(this.context, true); - - return { - hasMapBindings, // - hasPropBindings, // - hasCollisions, // - hasTemplateBindings, // - hasHostBindings, // - templateBindingsLocked, // - hostBindingsLocked, // - allowDirectStyling, // - }; - } + get config() { return buildConfig(this.context.context); } /** * Returns a key/value map of all the styles/classes that were last applied to the element. @@ -252,7 +259,7 @@ export class NodeStylingDebug implements DebugStyling { // 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, TStylingConfig.HasMapBindings); + const hasMaps = hasConfig(this.context.context, TStylingConfig.HasMapBindings); if (hasMaps) { activateStylingMapFeature(); } @@ -265,10 +272,33 @@ export class NodeStylingDebug implements DebugStyling { // run the template bindings applyStylingViaContext( - this.context, null, mockElement, this._data, true, mapFn, sanitizer, false); + this.context.context, null, mockElement, this._data, true, mapFn, sanitizer, false); // and also the host bindings applyStylingViaContext( - this.context, null, mockElement, this._data, true, mapFn, sanitizer, true); + this.context.context, null, mockElement, this._data, true, mapFn, sanitizer, true); } } + +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); + const templateBindingsLocked = hasConfig(context, TStylingConfig.TemplateBindingsLocked); + const hostBindingsLocked = hasConfig(context, TStylingConfig.HostBindingsLocked); + const allowDirectStyling = + _allowDirectStyling(context, false) || _allowDirectStyling(context, true); + + return { + hasMapBindings, // + hasPropBindings, // + hasCollisions, // + hasTemplateBindings, // + hasHostBindings, // + templateBindingsLocked, // + hostBindingsLocked, // + allowDirectStyling, // + }; +} diff --git a/packages/core/src/render3/util/styling_utils.ts b/packages/core/src/render3/util/styling_utils.ts index c3f30930d1..dfae838c0a 100644 --- a/packages/core/src/render3/util/styling_utils.ts +++ b/packages/core/src/render3/util/styling_utils.ts @@ -240,7 +240,7 @@ export function getStylingMapArray(value: TStylingContext | StylingMapArray | nu value as StylingMapArray; } -export function isStylingContext(value: TStylingContext | StylingMapArray | null): boolean { +export function isStylingContext(value: any): boolean { // the StylingMapArray is in the format of [initial, prop, string, prop, string] // and this is the defining value to distinguish between arrays return Array.isArray(value) && value.length >= TStylingContextIndex.ValuesStartPosition && diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index 543b4758b0..072c44a7d7 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -863,7 +863,7 @@ describe('styling', () => { const node = getDebugNode(element) !; const styles = node.styles !; - const config = styles.config; + const config = styles.context.config; expect(config.hasCollisions).toBeFalsy(); expect(config.hasMapBindings).toBeFalsy(); expect(config.hasPropBindings).toBeTruthy();