88
99import { Injector } from '../di' ;
1010import { getViewComponent } from '../render3/global_utils_api' ;
11- import { TNode } from '../render3/interfaces/node' ;
11+ import { LContainer , NATIVE , VIEWS } from '../render3/interfaces/container' ;
12+ import { TElementNode , TNode , TNodeFlags , TNodeType } from '../render3/interfaces/node' ;
1213import { StylingIndex } from '../render3/interfaces/styling' ;
13- import { LView , TData , TVIEW } from '../render3/interfaces/view' ;
14+ import { LView , NEXT , PARENT , TData , TVIEW , T_HOST } from '../render3/interfaces/view' ;
1415import { getProp , getValue , isClassBasedValue } from '../render3/styling/class_and_style_bindings' ;
1516import { getStylingContext } from '../render3/styling/util' ;
1617import { getComponent , getContext , getInjectionTokens , getInjector , getListeners , getLocalRefs , isBrowserEvents , loadLContext , loadLContextFromNode } from '../render3/util/discovery_utils' ;
1718import { INTERPOLATION_DELIMITER , isPropMetadataString , renderStringify } from '../render3/util/misc_utils' ;
19+ import { findComponentView } from '../render3/util/view_traversal_utils' ;
20+ import { getComponentViewByIndex , getNativeByTNode , isComponent , isLContainer } from '../render3/util/view_utils' ;
1821import { assertDomNode } from '../util/assert' ;
1922import { DebugContext } from '../view/index' ;
2023
@@ -368,13 +371,13 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
368371
369372 queryAll ( predicate : Predicate < DebugElement > ) : DebugElement [ ] {
370373 const matches : DebugElement [ ] = [ ] ;
371- _queryNodeChildrenR3 ( this , predicate , matches , true ) ;
374+ _queryAllR3 ( this , predicate , matches , true ) ;
372375 return matches ;
373376 }
374377
375378 queryAllNodes ( predicate : Predicate < DebugNode > ) : DebugNode [ ] {
376379 const matches : DebugNode [ ] = [ ] ;
377- _queryNodeChildrenR3 ( this , predicate , matches , false ) ;
380+ _queryAllR3 ( this , predicate , matches , false ) ;
378381 return matches ;
379382 }
380383
@@ -387,20 +390,130 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
387390 }
388391}
389392
393+ /**
394+ * Walk the TNode tree to find matches for the predicate, skipping the parent element.
395+ *
396+ * @param parentElement the element from which the walk is started
397+ * @param predicate the predicate to match
398+ * @param matches the list of positive matches
399+ * @param elementsOnly whether only elements should be searched
400+ */
401+ function _queryAllR3 (
402+ parentElement : DebugElement , predicate : Predicate < DebugNode > , matches : DebugNode [ ] ,
403+ elementsOnly : boolean ) {
404+ const context = loadLContext ( parentElement . nativeNode ) ! ;
405+ const parentTNode = context . lView [ TVIEW ] . data [ context . nodeIndex ] as TNode ;
406+ // This the fixture's debug element, so this is always a component view.
407+ const lView = context . lView [ parentTNode . index ] ;
408+ const tNode = lView [ TVIEW ] . firstChild ;
409+ _queryNodeChildrenR3 ( tNode , lView , predicate , matches , elementsOnly ) ;
410+ }
411+
412+ /**
413+ * Recursively match the current TNode against the predicate, and goes on with the next ones.
414+ *
415+ * @param tNode the current TNode
416+ * @param lView the LView of this TNode
417+ * @param predicate the predicate to match
418+ * @param matches the list of positive matches
419+ * @param elementsOnly whether only elements should be searched
420+ */
390421function _queryNodeChildrenR3 (
391- parentNode : DebugNode , predicate : Predicate < DebugNode > , matches : DebugNode [ ] ,
422+ tNode : TNode , lView : LView , predicate : Predicate < DebugNode > , matches : DebugNode [ ] ,
392423 elementsOnly : boolean ) {
393- if ( parentNode instanceof DebugElement__POST_R3__ ) {
394- parentNode . childNodes . forEach ( node => {
395- if ( predicate ( node ) ) {
396- matches . push ( node ) ;
424+ // For each type of TNode, specific logic is executed.
425+ if ( tNode . type === TNodeType . Element || tNode . type === TNodeType . ElementContainer ) {
426+ // Case 1: the TNode is an element
427+ // The native node has to be checked.
428+ _addQueryMatchR3 ( getNativeByTNode ( tNode , lView ) , predicate , matches , elementsOnly ) ;
429+ if ( isComponent ( tNode ) ) {
430+ // If the element is the host of a component, then all nodes in its view have to be processed.
431+ // Note: the component's content (tNode.child) will be processed from the insertion points.
432+ const componentView = getComponentViewByIndex ( tNode . index , lView ) ;
433+ if ( componentView && componentView [ TVIEW ] . firstChild )
434+ _queryNodeChildrenR3 (
435+ componentView [ TVIEW ] . firstChild ! , componentView , predicate , matches , elementsOnly ) ;
436+ } else {
437+ // Otherwise, its children have to be processed.
438+ if ( tNode . child ) _queryNodeChildrenR3 ( tNode . child , lView , predicate , matches , elementsOnly ) ;
439+ }
440+ // In all cases, if a dynamic container exists for this node, each view inside it has to be
441+ // processed.
442+ const nodeOrContainer = lView [ tNode . index ] ;
443+ if ( isLContainer ( nodeOrContainer ) ) {
444+ _queryNodeChildrenInContainerR3 ( nodeOrContainer , predicate , matches , elementsOnly ) ;
445+ }
446+ } else if ( tNode . type === TNodeType . Container ) {
447+ // Case 2: the TNode is a container
448+ // The native node has to be checked.
449+ const lContainer = lView [ tNode . index ] ;
450+ _addQueryMatchR3 ( lContainer [ NATIVE ] , predicate , matches , elementsOnly ) ;
451+ // Each view inside the container has to be processed.
452+ _queryNodeChildrenInContainerR3 ( lContainer , predicate , matches , elementsOnly ) ;
453+ } else if ( tNode . type === TNodeType . Projection ) {
454+ // Case 3: the TNode is a projection insertion point (i.e. a <ng-content>).
455+ // The nodes projected at this location all need to be processed.
456+ const componentView = findComponentView ( lView ! ) ;
457+ const componentHost = componentView [ T_HOST ] as TElementNode ;
458+ const head : TNode | null =
459+ ( componentHost . projection as ( TNode | null ) [ ] ) [ tNode . projection as number ] ;
460+
461+ if ( Array . isArray ( head ) ) {
462+ for ( let nativeNode of head ) {
463+ _addQueryMatchR3 ( nativeNode , predicate , matches , elementsOnly ) ;
397464 }
398- if ( node instanceof DebugElement__POST_R3__ ) {
399- if ( elementsOnly ? node . nativeElement : true ) {
400- _queryNodeChildrenR3 ( node , predicate , matches , elementsOnly ) ;
401- }
465+ } else {
466+ if ( head ) {
467+ const nextLView = componentView [ PARENT ] ! as LView ;
468+ const nextTNode = nextLView [ TVIEW ] . data [ head . index ] as TNode ;
469+ _queryNodeChildrenR3 ( nextTNode , nextLView , predicate , matches , elementsOnly ) ;
402470 }
403- } ) ;
471+ }
472+ } else {
473+ // Case 4: the TNode is a view.
474+ if ( tNode . child ) {
475+ _queryNodeChildrenR3 ( tNode . child , lView , predicate , matches , elementsOnly ) ;
476+ }
477+ }
478+ // To determine the next node to be processed, we need to use the next or the projectionNext link,
479+ // depending on whether the current node has been projected.
480+ const nextTNode = ( tNode . flags & TNodeFlags . isProjected ) ? tNode . projectionNext : tNode . next ;
481+ if ( nextTNode ) {
482+ _queryNodeChildrenR3 ( nextTNode , lView , predicate , matches , elementsOnly ) ;
483+ }
484+ }
485+
486+ /**
487+ * Process all TNodes in a given container.
488+ *
489+ * @param lContainer the container to be processed
490+ * @param predicate the predicate to match
491+ * @param matches the list of positive matches
492+ * @param elementsOnly whether only elements should be searched
493+ */
494+ function _queryNodeChildrenInContainerR3 (
495+ lContainer : LContainer , predicate : Predicate < DebugNode > , matches : DebugNode [ ] ,
496+ elementsOnly : boolean ) {
497+ for ( let i = 0 ; i < lContainer [ VIEWS ] . length ; i ++ ) {
498+ const childView = lContainer [ VIEWS ] [ i ] ;
499+ _queryNodeChildrenR3 ( childView [ TVIEW ] . node ! , childView , predicate , matches , elementsOnly ) ;
500+ }
501+ }
502+
503+ /**
504+ * Match the current native node against the predicate.
505+ *
506+ * @param nativeNode the current native node
507+ * @param predicate the predicate to match
508+ * @param matches the list of positive matches
509+ * @param elementsOnly whether only elements should be searched
510+ */
511+ function _addQueryMatchR3 (
512+ nativeNode : any , predicate : Predicate < DebugNode > , matches : DebugNode [ ] , elementsOnly : boolean ) {
513+ const debugNode = getDebugNode ( nativeNode ) ;
514+ if ( debugNode && ( elementsOnly ? debugNode instanceof DebugElement__POST_R3__ : true ) &&
515+ predicate ( debugNode ) ) {
516+ matches . push ( debugNode ) ;
404517 }
405518}
406519
0 commit comments