Skip to content

Commit b2d6f43

Browse files
mheveryIgorMinar
authored andcommitted
fix(ivy): Implement remaining methods for DebugNode (angular#27387)
PR Close angular#27387
1 parent f0b0d64 commit b2d6f43

34 files changed

+605
-406
lines changed

packages/core/src/core_render3_private_export.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export {
154154
} from './sanitization/bypass';
155155

156156
export {
157-
getContext as ɵgetContext
157+
getLContext as ɵgetLContext
158158
} from './render3/context_discovery';
159159

160160
export {

packages/core/src/debug/debug_node.ts

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
*/
88

99
import {Injector} from '../di';
10-
import {DirectiveDef} from '../render3';
1110
import {assertDomNode} from '../render3/assert';
12-
import {getComponent, getInjector, getLocalRefs, loadContext} from '../render3/discovery_utils';
13-
import {TNode, TNodeFlags} from '../render3/interfaces/node';
11+
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext, loadLContextFromNode} from '../render3/discovery_utils';
12+
import {TNode} from '../render3/interfaces/node';
13+
import {StylingIndex} from '../render3/interfaces/styling';
1414
import {TVIEW} from '../render3/interfaces/view';
15+
import {getProp, getValue, isClassBased} from '../render3/styling/class_and_style_bindings';
16+
import {getStylingContext} from '../render3/styling/util';
1517
import {DebugContext} from '../view/index';
1618

1719
export class EventListener {
@@ -204,54 +206,25 @@ class DebugNode__POST_R3__ implements DebugNode {
204206
constructor(nativeNode: Node) { this.nativeNode = nativeNode; }
205207

206208
get parent(): DebugElement|null {
207-
const parent = this.nativeNode.parentNode as HTMLElement;
209+
const parent = this.nativeNode.parentNode as Element;
208210
return parent ? new DebugElement__POST_R3__(parent) : null;
209211
}
210212

211213
get injector(): Injector { return getInjector(this.nativeNode); }
212214

213215
get componentInstance(): any {
214216
const nativeElement = this.nativeNode;
215-
return nativeElement && getComponent(nativeElement as HTMLElement);
216-
}
217-
get context(): any {
218-
// https://angular-team.atlassian.net/browse/FW-719
219-
throw notImplemented();
217+
return nativeElement && getComponent(nativeElement as Element);
220218
}
219+
get context(): any { return getContext(this.nativeNode as Element); }
221220

222221
get listeners(): EventListener[] {
223-
// TODO: add real implementation;
224-
// https://angular-team.atlassian.net/browse/FW-719
225-
return [];
222+
return getListeners(this.nativeNode as Element).filter(isBrowserEvents);
226223
}
227224

228225
get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); }
229226

230-
get providerTokens(): any[] {
231-
// TODO move to discoverable utils
232-
const context = loadContext(this.nativeNode as HTMLElement, false) !;
233-
if (!context) return [];
234-
const lView = context.lView;
235-
const tView = lView[TVIEW];
236-
const tNode = tView.data[context.nodeIndex] as TNode;
237-
const providerTokens: any[] = [];
238-
const nodeFlags = tNode.flags;
239-
const startIndex = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
240-
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
241-
const endIndex = startIndex + directiveCount;
242-
for (let i = startIndex; i < endIndex; i++) {
243-
let value = tView.data[i];
244-
if (isDirectiveDefHack(value)) {
245-
// The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
246-
// design flaw. We should always store same type so that we can be monomorphic. The issue
247-
// is that for Components/Directives we store the def instead the type. The correct behavior
248-
// is that we should always be storing injectable type in this location.
249-
value = value.type;
250-
}
251-
providerTokens.push(value);
252-
}
253-
return providerTokens;
254-
}
227+
get providerTokens(): any[] { return getInjectionTokens(this.nativeNode as Element); }
255228
}
256229

257230
class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugElement {
@@ -264,10 +237,10 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
264237
return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode as Element : null;
265238
}
266239

267-
get name(): string { return (this.nativeElement as HTMLElement).nodeName; }
240+
get name(): string { return this.nativeElement !.nodeName; }
268241

269242
get properties(): {[key: string]: any;} {
270-
const context = loadContext(this.nativeNode) !;
243+
const context = loadLContext(this.nativeNode) !;
271244
const lView = context.lView;
272245
const tView = lView[TVIEW];
273246
const tNode = tView.data[context.nodeIndex] as TNode;
@@ -278,18 +251,77 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
278251
}
279252

280253
get attributes(): {[key: string]: string | null;} {
281-
// https://angular-team.atlassian.net/browse/FW-719
282-
throw notImplemented();
254+
const attributes: {[key: string]: string | null;} = {};
255+
const element = this.nativeElement;
256+
if (element) {
257+
const eAttrs = element.attributes;
258+
for (let i = 0; i < eAttrs.length; i++) {
259+
const attr = eAttrs[i];
260+
attributes[attr.name] = attr.value;
261+
}
262+
}
263+
return attributes;
283264
}
284265

285266
get classes(): {[key: string]: boolean;} {
286-
// https://angular-team.atlassian.net/browse/FW-719
287-
throw notImplemented();
267+
const classes: {[key: string]: boolean;} = {};
268+
const element = this.nativeElement;
269+
if (element) {
270+
const lContext = loadLContextFromNode(element);
271+
const lNode = lContext.lView[lContext.nodeIndex];
272+
const stylingContext = getStylingContext(lContext.nodeIndex, lContext.lView);
273+
if (stylingContext) {
274+
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
275+
i += StylingIndex.Size) {
276+
if (isClassBased(lNode, i)) {
277+
const className = getProp(lNode, i);
278+
const value = getValue(lNode, i);
279+
if (typeof value == 'boolean') {
280+
// we want to ignore `null` since those don't overwrite the values.
281+
classes[className] = value;
282+
}
283+
}
284+
}
285+
} else {
286+
// Fallback, just read DOM.
287+
const eClasses = element.classList;
288+
for (let i = 0; i < eClasses.length; i++) {
289+
classes[eClasses[i]] = true;
290+
}
291+
}
292+
}
293+
return classes;
288294
}
289295

290296
get styles(): {[key: string]: string | null;} {
291-
// https://angular-team.atlassian.net/browse/FW-719
292-
throw notImplemented();
297+
const styles: {[key: string]: string | null;} = {};
298+
const element = this.nativeElement;
299+
if (element) {
300+
const lContext = loadLContextFromNode(element);
301+
const lNode = lContext.lView[lContext.nodeIndex];
302+
const stylingContext = getStylingContext(lContext.nodeIndex, lContext.lView);
303+
if (stylingContext) {
304+
for (let i = StylingIndex.SingleStylesStartPosition; i < lNode.length;
305+
i += StylingIndex.Size) {
306+
if (!isClassBased(lNode, i)) {
307+
const styleName = getProp(lNode, i);
308+
const value = getValue(lNode, i) as string | null;
309+
if (value !== null) {
310+
// we want to ignore `null` since those don't overwrite the values.
311+
styles[styleName] = value;
312+
}
313+
}
314+
}
315+
} else {
316+
// Fallback, just read DOM.
317+
const eStyles = (element as HTMLElement).style;
318+
for (let i = 0; i < eStyles.length; i++) {
319+
const name = eStyles.item(i);
320+
styles[name] = eStyles.getPropertyValue(name);
321+
}
322+
}
323+
}
324+
return styles;
293325
}
294326

295327
get childNodes(): DebugNode[] {
@@ -332,24 +364,14 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
332364
}
333365

334366
triggerEventHandler(eventName: string, eventObj: any): void {
335-
// This is a hack implementation. The correct implementation would bypass the DOM and `TNode`
336-
// information to invoke the listeners directly.
337-
// https://angular-team.atlassian.net/browse/FW-719
338-
const event = document.createEvent('MouseEvent');
339-
event.initEvent(eventName, true, true);
340-
(this.nativeElement as HTMLElement).dispatchEvent(event);
367+
this.listeners.forEach((listener) => {
368+
if (listener.name === eventName) {
369+
listener.callback(eventObj);
370+
}
371+
});
341372
}
342373
}
343374

344-
/**
345-
* This function should not exist because it is megamorphic and only mostly correct.
346-
*
347-
* See call site for more info.
348-
*/
349-
function isDirectiveDefHack(obj: any): obj is DirectiveDef<any> {
350-
return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
351-
}
352-
353375
function _queryNodeChildrenR3(
354376
parentNode: DebugNode, predicate: Predicate<DebugNode>, matches: DebugNode[],
355377
elementsOnly: boolean) {

packages/core/src/render3/component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ import {getComponentDef} from './definition';
1717
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
1818
import {publishDefaultGlobalUtils} from './global_utils';
1919
import {queueInitHooks, queueLifecycleHooks} from './hooks';
20-
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
20+
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTNode, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
2121
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
22-
import {TElementNode, TNodeFlags, TNodeType} from './interfaces/node';
22+
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
2323
import {PlayerHandler} from './interfaces/player';
2424
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
25-
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
25+
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
2626
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
2727
import {defaultScheduler, getRootView, readPatchedLView, stringify} from './util';
2828

@@ -235,7 +235,9 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
235235
const dirIndex = rootTView.data.length - 1;
236236

237237
queueInitHooks(dirIndex, def.onInit, def.doCheck, rootTView);
238-
queueLifecycleHooks(dirIndex << TNodeFlags.DirectiveStartingIndexShift | 1, rootTView);
238+
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
239+
// LNode).
240+
queueLifecycleHooks(rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
239241
}
240242

241243
/**

packages/core/src/render3/context_discovery.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatched
3636
*
3737
* @param target Component, Directive or DOM Node.
3838
*/
39-
export function getContext(target: any): LContext|null {
39+
export function getLContext(target: any): LContext|null {
4040
let mpValue = readPatchedData(target);
4141
if (mpValue) {
4242
// only when it's an array is it considered an LView instance
@@ -250,8 +250,8 @@ function findViaDirective(lView: LView, directiveInstance: {}): number {
250250
// list of directives for the instance.
251251
let tNode = lView[TVIEW].firstChild;
252252
while (tNode) {
253-
const directiveIndexStart = getDirectiveStartIndex(tNode);
254-
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
253+
const directiveIndexStart = tNode.directiveStart;
254+
const directiveIndexEnd = tNode.directiveEnd;
255255
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
256256
if (lView[i] === directiveInstance) {
257257
return tNode.index;
@@ -273,16 +273,16 @@ function findViaDirective(lView: LView, directiveInstance: {}): number {
273273
export function getDirectivesAtNodeIndex(
274274
nodeIndex: number, lView: LView, includeComponents: boolean): any[]|null {
275275
const tNode = lView[TVIEW].data[nodeIndex] as TNode;
276-
let directiveStartIndex = getDirectiveStartIndex(tNode);
276+
let directiveStartIndex = tNode.directiveStart;
277277
if (directiveStartIndex == 0) return EMPTY_ARRAY;
278-
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
278+
const directiveEndIndex = tNode.directiveEnd;
279279
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
280280
return lView.slice(directiveStartIndex, directiveEndIndex);
281281
}
282282

283283
export function getComponentAtNodeIndex(nodeIndex: number, lView: LView): {}|null {
284284
const tNode = lView[TVIEW].data[nodeIndex] as TNode;
285-
let directiveStartIndex = getDirectiveStartIndex(tNode);
285+
let directiveStartIndex = tNode.directiveStart;
286286
return tNode.flags & TNodeFlags.isComponent ? lView[directiveStartIndex] : null;
287287
}
288288

@@ -305,18 +305,3 @@ export function discoverLocalRefs(lView: LView, nodeIndex: number): {[key: strin
305305

306306
return null;
307307
}
308-
309-
function getDirectiveStartIndex(tNode: TNode): number {
310-
// the tNode instances store a flag value which then has a
311-
// pointer which tells the starting index of where all the
312-
// active directives are in the master directive array
313-
return tNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
314-
}
315-
316-
function getDirectiveEndIndex(tNode: TNode, startIndex: number): number {
317-
// The end value is also a part of the same flag
318-
// (see `TNodeFlags` to see how the flag bit shifting
319-
// values are used).
320-
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
321-
return count ? (startIndex + count) : -1;
322-
}

packages/core/src/render3/di.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -393,30 +393,32 @@ export function getOrCreateInjectable<T>(
393393
const NOT_FOUND = {};
394394

395395
function searchTokensOnInjector<T>(
396-
injectorIndex: number, injectorView: LView, token: Type<T>| InjectionToken<T>,
396+
injectorIndex: number, lView: LView, token: Type<T>| InjectionToken<T>,
397397
previousTView: TView | null) {
398-
const currentTView = injectorView[TVIEW];
398+
const currentTView = lView[TVIEW];
399399
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
400-
// First, we step through providers
401-
let canAccessViewProviders = false;
402-
// We need to determine if view providers can be accessed by the starting element.
403-
// It happens in 2 cases:
404-
// 1) On the initial element injector , if we are instantiating a token which can see the
405-
// viewProviders of the component of that element. Such token are:
406-
// - the component itself (but not other directives)
407-
// - viewProviders tokens of the component (but not providers tokens)
408-
// 2) Upper in the element injector tree, if the starting element is actually in the view of
409-
// the current element. To determine this, we track the transition of view during the climb,
410-
// and check the host node of the current view to identify component views.
411-
if (previousTView == null && isComponent(tNode) && includeViewProviders ||
412-
previousTView != null && previousTView != currentTView &&
413-
(currentTView.node == null || currentTView.node.type === TNodeType.Element)) {
414-
canAccessViewProviders = true;
415-
}
416-
const injectableIdx =
417-
locateDirectiveOrProvider(tNode, injectorView, token, canAccessViewProviders);
400+
// First, we need to determine if view providers can be accessed by the starting element.
401+
// There are two possibities
402+
const canAccessViewProviders = previousTView == null ?
403+
// 1) This is the first invocation `previousTView == null` which means that we are at the
404+
// `TNode` of where injector is starting to look. In such a case the only time we are allowed
405+
// to look into the ViewProviders is if:
406+
// - we are on a component
407+
// - AND the injector set `includeViewProviders` to true (implying that the token can see
408+
// ViewProviders because it is the Component or a Service which itself was declared in
409+
// ViewProviders)
410+
(isComponent(tNode) && includeViewProviders) :
411+
// 2) `previousTView != null` which means that we are now walking across the parent nodes.
412+
// In such a case we are only allowed to look into the ViewProviders if:
413+
// - We just crossed from child View to Parent View `previousTView != currentTView`
414+
// - AND the parent TNode is an Element.
415+
// This means that we just came from the Component's View and therefore are allowed to see
416+
// into the ViewProviders.
417+
(previousTView != currentTView && (tNode.type === TNodeType.Element));
418+
419+
const injectableIdx = locateDirectiveOrProvider(tNode, lView, token, canAccessViewProviders);
418420
if (injectableIdx !== null) {
419-
return getNodeInjectable(currentTView.data, injectorView, injectableIdx, tNode as TElementNode);
421+
return getNodeInjectable(currentTView.data, lView, injectableIdx, tNode as TElementNode);
420422
} else {
421423
return NOT_FOUND;
422424
}
@@ -439,17 +441,17 @@ export function locateDirectiveOrProvider<T>(
439441
const nodeProviderIndexes = tNode.providerIndexes;
440442
const tInjectables = tView.data;
441443

442-
const startInjectables = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
443-
const startDirectives = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
444+
const injectablesStart = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
445+
const directivesStart = tNode.directiveStart;
446+
const directiveEnd = tNode.directiveEnd;
444447
const cptViewProvidersCount =
445448
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
446449
const startingIndex =
447-
canAccessViewProviders ? startInjectables : startInjectables + cptViewProvidersCount;
448-
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
449-
for (let i = startingIndex; i < startDirectives + directiveCount; i++) {
450+
canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
451+
for (let i = startingIndex; i < directiveEnd; i++) {
450452
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
451-
if (i < startDirectives && token === providerTokenOrDef ||
452-
i >= startDirectives && (providerTokenOrDef as DirectiveDef<any>).type === token) {
453+
if (i < directivesStart && token === providerTokenOrDef ||
454+
i >= directivesStart && (providerTokenOrDef as DirectiveDef<any>).type === token) {
453455
return i;
454456
}
455457
}

0 commit comments

Comments
 (0)