/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {isDevMode} from '../application_ref'; import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node'; import {Injector} from '../di'; import {ErrorHandler} from '../error_handler'; import {NgModuleRef} from '../linker/ng_module_factory'; import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api'; import {Sanitizer} from '../security'; import {Type} from '../type'; import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors'; import {resolveDep} from './provider'; import {dirtyParentQueries, getQueryValue} from './query'; import {createInjector, createNgModuleRef} from './refs'; import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types'; import {NOOP, isComponentView, renderNode, splitDepsDsl, viewParentEl} from './util'; import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view'; let initialized = false; export function initServicesIfNeeded() { if (initialized) { return; } initialized = true; const services = isDevMode() ? createDebugServices() : createProdServices(); Services.setCurrentNode = services.setCurrentNode; Services.createRootView = services.createRootView; Services.createEmbeddedView = services.createEmbeddedView; Services.createComponentView = services.createComponentView; Services.createNgModuleRef = services.createNgModuleRef; Services.overrideProvider = services.overrideProvider; Services.clearProviderOverrides = services.clearProviderOverrides; Services.checkAndUpdateView = services.checkAndUpdateView; Services.checkNoChangesView = services.checkNoChangesView; Services.destroyView = services.destroyView; Services.resolveDep = resolveDep; Services.createDebugContext = services.createDebugContext; Services.handleEvent = services.handleEvent; Services.updateDirectives = services.updateDirectives; Services.updateRenderer = services.updateRenderer; Services.dirtyParentQueries = dirtyParentQueries; } function createProdServices() { return { setCurrentNode: () => {}, createRootView: createProdRootView, createEmbeddedView: createEmbeddedView, createComponentView: createComponentView, createNgModuleRef: createNgModuleRef, overrideProvider: NOOP, clearProviderOverrides: NOOP, checkAndUpdateView: checkAndUpdateView, checkNoChangesView: checkNoChangesView, destroyView: destroyView, createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) => view.def.handleEvent(view, nodeIndex, eventName, event), updateDirectives: (view: ViewData, checkType: CheckType) => view.def.updateDirectives( checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : prodCheckNoChangesNode, view), updateRenderer: (view: ViewData, checkType: CheckType) => view.def.updateRenderer( checkType === CheckType.CheckAndUpdate ? prodCheckAndUpdateNode : prodCheckNoChangesNode, view), }; } function createDebugServices() { return { setCurrentNode: debugSetCurrentNode, createRootView: debugCreateRootView, createEmbeddedView: debugCreateEmbeddedView, createComponentView: debugCreateComponentView, createNgModuleRef: debugCreateNgModuleRef, overrideProvider: debugOverrideProvider, clearProviderOverrides: debugClearProviderOverrides, checkAndUpdateView: debugCheckAndUpdateView, checkNoChangesView: debugCheckNoChangesView, destroyView: debugDestroyView, createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex), handleEvent: debugHandleEvent, updateDirectives: debugUpdateDirectives, updateRenderer: debugUpdateRenderer, }; } function createProdRootView( elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any, def: ViewDefinition, ngModule: NgModuleRef, context?: any): ViewData { const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2); return createRootView( createRootData(elInjector, ngModule, rendererFactory, projectableNodes, rootSelectorOrNode), def, context); } function debugCreateRootView( elInjector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any, def: ViewDefinition, ngModule: NgModuleRef, context?: any): ViewData { const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2); const root = createRootData( elInjector, ngModule, new DebugRendererFactory2(rendererFactory), projectableNodes, rootSelectorOrNode); const defWithOverride = applyProviderOverridesToView(def); return callWithDebugContext( DebugAction.create, createRootView, null, [root, defWithOverride, context]); } function createRootData( elInjector: Injector, ngModule: NgModuleRef, rendererFactory: RendererFactory2, projectableNodes: any[][], rootSelectorOrNode: any): RootData { const sanitizer = ngModule.injector.get(Sanitizer); const errorHandler = ngModule.injector.get(ErrorHandler); const renderer = rendererFactory.createRenderer(null, null); return { ngModule, injector: elInjector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler }; } function debugCreateEmbeddedView( parentView: ViewData, anchorDef: NodeDef, viewDef: ViewDefinition, context?: any): ViewData { const defWithOverride = applyProviderOverridesToView(viewDef); return callWithDebugContext( DebugAction.create, createEmbeddedView, null, [parentView, anchorDef, defWithOverride, context]); } function debugCreateComponentView( parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData { const defWithOverride = applyProviderOverridesToView(viewDef); return callWithDebugContext( DebugAction.create, createComponentView, null, [parentView, nodeDef, defWithOverride, hostElement]); } function debugCreateNgModuleRef( moduleType: Type, parentInjector: Injector, bootstrapComponents: Type[], def: NgModuleDefinition): NgModuleRef { const defWithOverride = applyProviderOverridesToNgModule(def); return createNgModuleRef(moduleType, parentInjector, bootstrapComponents, defWithOverride); } const providerOverrides = new Map(); function debugOverrideProvider(override: ProviderOverride) { providerOverrides.set(override.token, override); } function debugClearProviderOverrides() { providerOverrides.clear(); } // Notes about the algorithm: // 1) Locate the providers of an element and check if one of them was overwritten // 2) Change the providers of that element // // We only create new datastructures if we need to, to keep perf impact // reasonable. function applyProviderOverridesToView(def: ViewDefinition): ViewDefinition { if (providerOverrides.size === 0) { return def; } const elementIndicesWithOverwrittenProviders = findElementIndicesWithOverwrittenProviders(def); if (elementIndicesWithOverwrittenProviders.length === 0) { return def; } // clone the whole view definition, // as it maintains references between the nodes that are hard to update. def = def.factory !(() => NOOP); for (let i = 0; i < elementIndicesWithOverwrittenProviders.length; i++) { applyProviderOverridesToElement(def, elementIndicesWithOverwrittenProviders[i]); } return def; function findElementIndicesWithOverwrittenProviders(def: ViewDefinition): number[] { const elIndicesWithOverwrittenProviders: number[] = []; let lastElementDef: NodeDef|null = null; for (let i = 0; i < def.nodes.length; i++) { const nodeDef = def.nodes[i]; if (nodeDef.flags & NodeFlags.TypeElement) { lastElementDef = nodeDef; } if (lastElementDef && nodeDef.flags & NodeFlags.CatProviderNoDirective && providerOverrides.has(nodeDef.provider !.token)) { elIndicesWithOverwrittenProviders.push(lastElementDef !.index); lastElementDef = null; } } return elIndicesWithOverwrittenProviders; } function applyProviderOverridesToElement(viewDef: ViewDefinition, elIndex: number) { for (let i = elIndex + 1; i < viewDef.nodes.length; i++) { const nodeDef = viewDef.nodes[i]; if (nodeDef.flags & NodeFlags.TypeElement) { // stop at the next element return; } if (nodeDef.flags & NodeFlags.CatProviderNoDirective) { // Make all providers lazy, so that we don't get into trouble // with ordering problems of providers on the same element nodeDef.flags |= NodeFlags.LazyProvider; const provider = nodeDef.provider !; const override = providerOverrides.get(provider.token); if (override) { nodeDef.flags = (nodeDef.flags & ~NodeFlags.CatProviderNoDirective) | override.flags; provider.deps = splitDepsDsl(override.deps); provider.value = override.value; } } } } } // Notes about the algorithm: // We only create new datastructures if we need to, to keep perf impact // reasonable. function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefinition { if (providerOverrides.size === 0 || !hasOverrrides(def)) { return def; } // clone the whole view definition, // as it maintains references between the nodes that are hard to update. def = def.factory !(() => NOOP); applyProviderOverrides(def); return def; function hasOverrrides(def: NgModuleDefinition): boolean { return def.providers.some( node => !!(node.flags & NodeFlags.CatProviderNoDirective) && providerOverrides.has(node.token)); } function applyProviderOverrides(def: NgModuleDefinition) { for (let i = 0; i < def.providers.length; i++) { const provider = def.providers[i]; // Make all providers lazy, so that we don't get into trouble // with ordering problems of providers on the same element provider.flags |= NodeFlags.LazyProvider; const override = providerOverrides.get(provider.token); if (override) { provider.flags = (provider.flags & ~NodeFlags.CatProviderNoDirective) | override.flags; provider.deps = splitDepsDsl(override.deps); provider.value = override.value; } } } } function prodCheckAndUpdateNode( view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { const nodeDef = view.def.nodes[nodeIndex]; checkAndUpdateNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); return (nodeDef.flags & NodeFlags.CatPureExpression) ? asPureExpressionData(view, nodeIndex).value : undefined; } function prodCheckNoChangesNode( view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any { const nodeDef = view.def.nodes[nodeIndex]; checkNoChangesNode(view, nodeDef, argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); return (nodeDef.flags & NodeFlags.CatPureExpression) ? asPureExpressionData(view, nodeIndex).value : undefined; } function debugCheckAndUpdateView(view: ViewData) { return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]); } function debugCheckNoChangesView(view: ViewData) { return callWithDebugContext(DebugAction.checkNoChanges, checkNoChangesView, null, [view]); } function debugDestroyView(view: ViewData) { return callWithDebugContext(DebugAction.destroy, destroyView, null, [view]); } enum DebugAction { create, detectChanges, checkNoChanges, destroy, handleEvent } let _currentAction: DebugAction; let _currentView: ViewData; let _currentNodeIndex: number|null; function debugSetCurrentNode(view: ViewData, nodeIndex: number | null) { _currentView = view; _currentNodeIndex = nodeIndex; } function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, event: any) { debugSetCurrentNode(view, nodeIndex); return callWithDebugContext( DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]); } function debugUpdateDirectives(view: ViewData, checkType: CheckType) { if (view.state & ViewState.Destroyed) { throw viewDestroyedError(DebugAction[_currentAction]); } debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0)); return view.def.updateDirectives(debugCheckDirectivesFn, view); function debugCheckDirectivesFn( view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { const nodeDef = view.def.nodes[nodeIndex]; if (checkType === CheckType.CheckAndUpdate) { debugCheckAndUpdateNode(view, nodeDef, argStyle, values); } else { debugCheckNoChangesNode(view, nodeDef, argStyle, values); } if (nodeDef.flags & NodeFlags.TypeDirective) { debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex)); } return (nodeDef.flags & NodeFlags.CatPureExpression) ? asPureExpressionData(view, nodeDef.index).value : undefined; } } function debugUpdateRenderer(view: ViewData, checkType: CheckType) { if (view.state & ViewState.Destroyed) { throw viewDestroyedError(DebugAction[_currentAction]); } debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0)); return view.def.updateRenderer(debugCheckRenderNodeFn, view); function debugCheckRenderNodeFn( view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) { const nodeDef = view.def.nodes[nodeIndex]; if (checkType === CheckType.CheckAndUpdate) { debugCheckAndUpdateNode(view, nodeDef, argStyle, values); } else { debugCheckNoChangesNode(view, nodeDef, argStyle, values); } if (nodeDef.flags & NodeFlags.CatRenderNode) { debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex)); } return (nodeDef.flags & NodeFlags.CatPureExpression) ? asPureExpressionData(view, nodeDef.index).value : undefined; } } function debugCheckAndUpdateNode( view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, givenValues: any[]): void { const changed = (checkAndUpdateNode)(view, nodeDef, argStyle, ...givenValues); if (changed) { const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues; if (nodeDef.flags & NodeFlags.TypeDirective) { const bindingValues: {[key: string]: string} = {}; for (let i = 0; i < nodeDef.bindings.length; i++) { const binding = nodeDef.bindings[i]; const value = values[i]; if (binding.flags & BindingFlags.TypeProperty) { bindingValues[normalizeDebugBindingName(binding.nonMinifiedName !)] = normalizeDebugBindingValue(value); } } const elDef = nodeDef.parent !; const el = asElementData(view, elDef.index).renderElement; if (!elDef.element !.name) { // a comment. view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`); } else { // a regular element. for (let attr in bindingValues) { const value = bindingValues[attr]; if (value != null) { view.renderer.setAttribute(el, attr, value); } else { view.renderer.removeAttribute(el, attr); } } } } } } function debugCheckNoChangesNode( view: ViewData, nodeDef: NodeDef, argStyle: ArgumentType, values: any[]): void { (checkNoChangesNode)(view, nodeDef, argStyle, ...values); } function normalizeDebugBindingName(name: string) { // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers name = camelCaseToDashCase(name.replace(/[$@]/g, '_')); return `ng-reflect-${name}`; } const CAMEL_CASE_REGEXP = /([A-Z])/g; function camelCaseToDashCase(input: string): string { return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase()); } function normalizeDebugBindingValue(value: any): string { try { // Limit the size of the value as otherwise the DOM just gets polluted. return value != null ? value.toString().slice(0, 30) : value; } catch (e) { return '[ERROR] Exception while trying to serialize the value'; } } function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number|null { for (let i = nodeIndex; i < view.def.nodes.length; i++) { const nodeDef = view.def.nodes[i]; if (nodeDef.flags & NodeFlags.TypeDirective && nodeDef.bindings && nodeDef.bindings.length) { return i; } } return null; } function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number|null { for (let i = nodeIndex; i < view.def.nodes.length; i++) { const nodeDef = view.def.nodes[i]; if ((nodeDef.flags & NodeFlags.CatRenderNode) && nodeDef.bindings && nodeDef.bindings.length) { return i; } } return null; } class DebugContext_ implements DebugContext { private nodeDef: NodeDef; private elView: ViewData; private elDef: NodeDef; constructor(public view: ViewData, public nodeIndex: number|null) { if (nodeIndex == null) { this.nodeIndex = nodeIndex = 0; } this.nodeDef = view.def.nodes[nodeIndex]; let elDef = this.nodeDef; let elView = view; while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) { elDef = elDef.parent !; } if (!elDef) { while (!elDef && elView) { elDef = viewParentEl(elView) !; elView = elView.parent !; } } this.elDef = elDef; this.elView = elView; } private get elOrCompView() { // Has to be done lazily as we use the DebugContext also during creation of elements... return asElementData(this.elView, this.elDef.index).componentView || this.view; } get injector(): Injector { return createInjector(this.elView, this.elDef); } get component(): any { return this.elOrCompView.component; } get context(): any { return this.elOrCompView.context; } get providerTokens(): any[] { const tokens: any[] = []; if (this.elDef) { for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) { const childDef = this.elView.def.nodes[i]; if (childDef.flags & NodeFlags.CatProvider) { tokens.push(childDef.provider !.token); } i += childDef.childCount; } } return tokens; } get references(): {[key: string]: any} { const references: {[key: string]: any} = {}; if (this.elDef) { collectReferences(this.elView, this.elDef, references); for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) { const childDef = this.elView.def.nodes[i]; if (childDef.flags & NodeFlags.CatProvider) { collectReferences(this.elView, childDef, references); } i += childDef.childCount; } } return references; } get componentRenderElement() { const elData = findHostElement(this.elOrCompView); return elData ? elData.renderElement : undefined; } get renderNode(): any { return this.nodeDef.flags & NodeFlags.TypeText ? renderNode(this.view, this.nodeDef) : renderNode(this.elView, this.elDef); } logError(console: Console, ...values: any[]) { let logViewDef: ViewDefinition; let logNodeIndex: number; if (this.nodeDef.flags & NodeFlags.TypeText) { logViewDef = this.view.def; logNodeIndex = this.nodeDef.index; } else { logViewDef = this.elView.def; logNodeIndex = this.elDef.index; } // Note: we only generate a log function for text and element nodes // to make the generated code as small as possible. const renderNodeIndex = getRenderNodeIndex(logViewDef, logNodeIndex); let currRenderNodeIndex = -1; let nodeLogger: NodeLogger = () => { currRenderNodeIndex++; if (currRenderNodeIndex === renderNodeIndex) { return console.error.bind(console, ...values); } else { return NOOP; } }; logViewDef.factory !(nodeLogger); if (currRenderNodeIndex < renderNodeIndex) { console.error('Illegal state: the ViewDefinitionFactory did not call the logger!'); (console.error)(...values); } } } function getRenderNodeIndex(viewDef: ViewDefinition, nodeIndex: number): number { let renderNodeIndex = -1; for (let i = 0; i <= nodeIndex; i++) { const nodeDef = viewDef.nodes[i]; if (nodeDef.flags & NodeFlags.CatRenderNode) { renderNodeIndex++; } } return renderNodeIndex; } function findHostElement(view: ViewData): ElementData|null { while (view && !isComponentView(view)) { view = view.parent !; } if (view.parent) { return asElementData(view.parent, viewParentEl(view) !.index); } return null; } function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) { for (let refName in nodeDef.references) { references[refName] = getQueryValue(view, nodeDef, nodeDef.references[refName]); } } function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any[]) { const oldAction = _currentAction; const oldView = _currentView; const oldNodeIndex = _currentNodeIndex; try { _currentAction = action; const result = fn.apply(self, args); _currentView = oldView; _currentNodeIndex = oldNodeIndex; _currentAction = oldAction; return result; } catch (e) { if (isViewDebugError(e) || !_currentView) { throw e; } throw viewWrappedDebugError(e, getCurrentDebugContext() !); } } export function getCurrentDebugContext(): DebugContext|null { return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null; } class DebugRendererFactory2 implements RendererFactory2 { constructor(private delegate: RendererFactory2) {} createRenderer(element: any, renderData: RendererType2|null): Renderer2 { return new DebugRenderer2(this.delegate.createRenderer(element, renderData)); } begin() { if (this.delegate.begin) { this.delegate.begin(); } } end() { if (this.delegate.end) { this.delegate.end(); } } whenRenderingDone(): Promise { if (this.delegate.whenRenderingDone) { return this.delegate.whenRenderingDone(); } return Promise.resolve(null); } } class DebugRenderer2 implements Renderer2 { constructor(private delegate: Renderer2) {} get data() { return this.delegate.data; } destroyNode(node: any) { removeDebugNodeFromIndex(getDebugNode(node) !); if (this.delegate.destroyNode) { this.delegate.destroyNode(node); } } destroy() { this.delegate.destroy(); } createElement(name: string, namespace?: string): any { const el = this.delegate.createElement(name, namespace); const debugCtx = getCurrentDebugContext(); if (debugCtx) { const debugEl = new DebugElement(el, null, debugCtx); debugEl.name = name; indexDebugNode(debugEl); } return el; } createComment(value: string): any { const comment = this.delegate.createComment(value); const debugCtx = getCurrentDebugContext(); if (debugCtx) { indexDebugNode(new DebugNode(comment, null, debugCtx)); } return comment; } createText(value: string): any { const text = this.delegate.createText(value); const debugCtx = getCurrentDebugContext(); if (debugCtx) { indexDebugNode(new DebugNode(text, null, debugCtx)); } return text; } appendChild(parent: any, newChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(newChild); if (debugEl && debugChildEl && debugEl instanceof DebugElement) { debugEl.addChild(debugChildEl); } this.delegate.appendChild(parent, newChild); } insertBefore(parent: any, newChild: any, refChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(newChild); const debugRefEl = getDebugNode(refChild) !; if (debugEl && debugChildEl && debugEl instanceof DebugElement) { debugEl.insertBefore(debugRefEl, debugChildEl); } this.delegate.insertBefore(parent, newChild, refChild); } removeChild(parent: any, oldChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(oldChild); if (debugEl && debugChildEl && debugEl instanceof DebugElement) { debugEl.removeChild(debugChildEl); } this.delegate.removeChild(parent, oldChild); } selectRootElement(selectorOrNode: string|any): any { const el = this.delegate.selectRootElement(selectorOrNode); const debugCtx = getCurrentDebugContext(); if (debugCtx) { indexDebugNode(new DebugElement(el, null, debugCtx)); } return el; } setAttribute(el: any, name: string, value: string, namespace?: string): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { const fullName = namespace ? namespace + ':' + name : name; debugEl.attributes[fullName] = value; } this.delegate.setAttribute(el, name, value, namespace); } removeAttribute(el: any, name: string, namespace?: string): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { const fullName = namespace ? namespace + ':' + name : name; debugEl.attributes[fullName] = null; } this.delegate.removeAttribute(el, name, namespace); } addClass(el: any, name: string): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { debugEl.classes[name] = true; } this.delegate.addClass(el, name); } removeClass(el: any, name: string): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { debugEl.classes[name] = false; } this.delegate.removeClass(el, name); } setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { debugEl.styles[style] = value; } this.delegate.setStyle(el, style, value, flags); } removeStyle(el: any, style: string, flags: RendererStyleFlags2): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { debugEl.styles[style] = null; } this.delegate.removeStyle(el, style, flags); } setProperty(el: any, name: string, value: any): void { const debugEl = getDebugNode(el); if (debugEl && debugEl instanceof DebugElement) { debugEl.properties[name] = value; } this.delegate.setProperty(el, name, value); } listen( target: 'document'|'windows'|'body'|any, eventName: string, callback: (event: any) => boolean): () => void { if (typeof target !== 'string') { const debugEl = getDebugNode(target); if (debugEl) { debugEl.listeners.push(new EventListener(eventName, callback)); } } return this.delegate.listen(target, eventName, callback); } parentNode(node: any): any { return this.delegate.parentNode(node); } nextSibling(node: any): any { return this.delegate.nextSibling(node); } setValue(node: any, value: string): void { return this.delegate.setValue(node, value); } }