refactor(ivy): LContainer now stored in LView[PARENT] (#28382)

- Removes CONTAINER_INDEX
- LView[PARENT] now contains LContainer when necessary
- Removes now unused arguments to methods after refactor

PR Close #28382
This commit is contained in:
Ben Lesh 2019-01-28 14:45:31 -08:00
parent f0f81f482e
commit ba6aa93aa3
19 changed files with 235 additions and 159 deletions

View File

@ -11,6 +11,7 @@ import {assertDefined, assertEqual, throwError} from '../util/assert';
import {getComponentDef, getNgModuleDef} from './definition';
import {TNode} from './interfaces/node';
import {LView} from './interfaces/view';
import {isLContainer, isLView} from './util';
export function assertComponentType(
@ -44,3 +45,21 @@ export function assertDataNext(lView: LView, index: number, arr?: any[]) {
assertEqual(
arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`);
}
export function assertLContainerOrUndefined(value: any): void {
value && assertEqual(isLContainer(value), true, 'Expecting LContainer or undefined or null');
}
export function assertLContainer(value: any): void {
assertDefined(value, 'LContainer must be defined');
assertEqual(isLContainer(value), true, 'Expecting LContainer');
}
export function assertLViewOrUndefined(value: any): void {
value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
}
export function assertLView(value: any) {
assertDefined(value, 'LView must be defined');
assertEqual(isLView(value), true, 'Expecting LView');
}

View File

@ -134,7 +134,7 @@ export function renderComponent<T>(
component = createRootComponent(
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
addToViewTree(rootView, HEADER_OFFSET, componentView);
addToViewTree(rootView, componentView);
refreshDescendantViews(rootView); // creation mode pass
rootView[FLAGS] &= ~LViewFlags.CreationMode;

View File

@ -8,13 +8,14 @@
import {Injector} from '../di/injector';
import {assertLView} from './assert';
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from './context_discovery';
import {NodeInjector} from './di';
import {LContext} from './interfaces/context';
import {DirectiveDef} from './interfaces/definition';
import {TElementNode, TNode, TNodeProviderIndexes} from './interfaces/node';
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
import {getRootView, readElementValue, renderStringify} from './util';
import {CLEANUP, CONTEXT, FLAGS, HOST, LView, LViewFlags, TVIEW} from './interfaces/view';
import {getLViewParent, getRootContext, readElementValue, renderStringify} from './util';
@ -95,28 +96,18 @@ export function getContext<T = {}>(element: Element): T|null {
*/
export function getViewComponent<T = {}>(element: Element | {}): T|null {
const context = loadLContext(element) !;
let lView: LView = context.lView;
while (lView[PARENT] && lView[HOST] === null) {
let lView = context.lView;
let parent: LView|null;
ngDevMode && assertLView(lView);
while (lView[HOST] === null && (parent = getLViewParent(lView) !)) {
// As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf`
lView = lView[PARENT] !;
if (parent) {
lView = parent;
}
}
return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T;
}
/**
* Returns the `RootContext` instance that is associated with
* the application where the target is situated.
*
*/
export function getRootContext(target: LView | {}): RootContext {
const lViewData = Array.isArray(target) ? target : loadLContext(target) !.lView;
const rootLView = getRootView(lViewData);
return rootLView[CONTEXT] as RootContext;
}
/**
* Retrieve all root components.
*

View File

@ -14,11 +14,11 @@ import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../metad
import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../sanitization/sanitization';
import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {assertDataInRange, assertDefined, assertEqual, assertLessThan, assertNotEqual} from '../util/assert';
import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual} from '../util/assert';
import {isObservable} from '../util/lang';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../util/ng_reflect';
import {assertHasParent, assertPreviousIsParent} from './assert';
import {assertHasParent, assertLContainerOrUndefined, assertLView, assertPreviousIsParent} from './assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from './bindings';
import {attachPatchData, getComponentViewByInstance} from './context_discovery';
import {diPublicInInjector, getNodeInjectable, getOrCreateInjectable, getOrCreateNodeInjectorForNode, injectAttributeImpl} from './di';
@ -33,7 +33,8 @@ import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'
import {LQueries} from './interfaces/query';
import {GlobalTargetResolver, ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {SanitizerFn} from './interfaces/sanitization';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TAIL, TData, TVIEW, TView, T_HOST} from './interfaces/view';
import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
@ -42,7 +43,7 @@ import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticCo
import {BoundPlayerFactory} from './styling/player_factory';
import {ANIMATION_PROP_PREFIX, allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput, hasStyling, isAnimationProp} from './styling/util';
import {NO_CHANGE} from './tokens';
import {INTERPOLATION_DELIMITER, applyOnCreateInstructions, findComponentView, getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readElementValue, readPatchedLView, renderStringify} from './util';
import {INTERPOLATION_DELIMITER, applyOnCreateInstructions, findComponentView, getComponentViewByIndex, getLViewParent, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readElementValue, readPatchedLView, renderStringify} from './util';
@ -816,7 +817,6 @@ function createViewBlueprint(bindingStartIndex: number, initialViewLength: numbe
const blueprint = new Array(initialViewLength)
.fill(null, 0, bindingStartIndex)
.fill(NO_CHANGE, bindingStartIndex) as LView;
blueprint[CONTAINER_INDEX] = -1;
blueprint[BINDING_INDEX] = bindingStartIndex;
return blueprint;
}
@ -2098,11 +2098,10 @@ function addComponentLogic<T>(
// accessed through their containers because they may be removed / re-added later.
const rendererFactory = lView[RENDERER_FACTORY];
const componentView = addToViewTree(
lView, previousOrParentTNode.index as number,
createLView(
lView, tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways,
lView[previousOrParentTNode.index], previousOrParentTNode as TElementNode,
rendererFactory, lView[RENDERER_FACTORY].createRenderer(native as RElement, def)));
lView, createLView(
lView, tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways,
lView[previousOrParentTNode.index], previousOrParentTNode as TElementNode,
rendererFactory, lView[RENDERER_FACTORY].createRenderer(native as RElement, def)));
componentView[T_HOST] = previousOrParentTNode as TElementNode;
@ -2208,8 +2207,10 @@ function generateInitialInputs(
* @returns LContainer
*/
export function createLContainer(
hostNative: RElement | RComment, currentView: LView, native: RComment,
hostNative: RElement | RComment | StylingContext | LView, currentView: LView, native: RComment,
isForViewContainerRef?: boolean): LContainer {
ngDevMode && assertDomNode(native);
ngDevMode && assertLView(currentView);
return [
isForViewContainerRef ? -1 : 0, // active index
[], // views
@ -2295,7 +2296,7 @@ function containerInternal(
// Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted.
addToViewTree(lView, index + HEADER_OFFSET, lContainer);
addToViewTree(lView, lContainer);
ngDevMode && assertNodeType(getPreviousOrParentTNode(), TNodeType.Container);
return tNode;
@ -2457,7 +2458,7 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
if (lContainer) {
if (isCreationMode(viewToRender)) {
// it is a new view, insert it into collection of views for a given container
insertView(viewToRender, lContainer, lView, lContainer[ACTIVE_INDEX] !, -1);
insertView(viewToRender, lContainer, lContainer[ACTIVE_INDEX] !);
}
lContainer[ACTIVE_INDEX] !++;
}
@ -2502,7 +2503,9 @@ export function embeddedViewEnd(): void {
lView[FLAGS] &= ~LViewFlags.CreationMode;
}
refreshDescendantViews(lView); // update mode pass
leaveView(lView[PARENT] !);
const lContainer = lView[PARENT] as LContainer;
ngDevMode && assertLContainerOrUndefined(lContainer);
leaveView(lContainer[PARENT] !);
setPreviousOrParentTNode(viewHost !);
setIsParent(false);
}
@ -2648,7 +2651,8 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
const componentView = findComponentView(lView);
const componentNode = componentView[T_HOST] as TElementNode;
let nodeToProject = (componentNode.projection as(TNode | null)[])[selectorIndex];
let projectedView = componentView[PARENT] !;
let projectedView = componentView[PARENT] !as LView;
ngDevMode && assertLView(projectedView);
let projectionNodeIndex = -1;
if (Array.isArray(nodeToProject)) {
@ -2670,7 +2674,7 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
projectionNodeStack[++projectionNodeIndex] = projectedView;
nodeToProject = firstProjectedNode;
projectedView = currentComponentView[PARENT] !;
projectedView = getLViewParent(currentComponentView) !;
continue;
}
}
@ -2703,16 +2707,17 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
* @param state The LView or LContainer to add to the view tree
* @returns The state passed in
*/
export function addToViewTree<T extends LView|LContainer>(
lView: LView, adjustedHostIndex: number, state: T): T {
const tView = lView[TVIEW];
if (lView[TAIL]) {
lView[TAIL] ![NEXT] = state;
} else if (tView.firstTemplatePass) {
tView.childIndex = adjustedHostIndex;
export function addToViewTree<T extends LView|LContainer>(lView: LView, lViewOrLContainer: T): T {
// TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer to
// the end of the queue, which means if the developer asks for the LContainers out of order, the
// change detection will run out of order.
if (lView[CHILD_HEAD]) {
lView[CHILD_TAIL] ![NEXT] = lViewOrLContainer;
} else {
lView[CHILD_HEAD] = lViewOrLContainer;
}
lView[TAIL] = state;
return state;
lView[CHILD_TAIL] = lViewOrLContainer;
return lViewOrLContainer;
}
///////////////////////////////
@ -2721,6 +2726,7 @@ export function addToViewTree<T extends LView|LContainer>(
/** If node is an OnPush component, marks its LView dirty. */
function markDirtyIfOnPush(lView: LView, viewIndex: number): void {
ngDevMode && assertLView(lView);
const childComponentLView = getComponentViewByIndex(viewIndex, lView);
if (!(childComponentLView[FLAGS] & LViewFlags.CheckAlways)) {
childComponentLView[FLAGS] |= LViewFlags.Dirty;
@ -2780,12 +2786,13 @@ function wrapListener(
export function markViewDirty(lView: LView): LView|null {
while (lView) {
lView[FLAGS] |= LViewFlags.Dirty;
const parent = getLViewParent(lView);
// Stop traversing up as soon as you find a root view that wasn't attached to any container
if (isRootView(lView) && lView[CONTAINER_INDEX] === -1) {
if (isRootView(lView) && !parent) {
return lView;
}
// continue otherwise
lView = lView[PARENT] !;
lView = parent !;
}
return null;
}

View File

@ -62,7 +62,7 @@ export interface LContainer extends Array<any> {
* Access to the parent view is necessary so we can propagate back
* up from inside a container to parent[NEXT].
*/
[PARENT]: LView|null;
[PARENT]: LView;
/**
* This allows us to jump from a container to a sibling container or component
@ -85,10 +85,10 @@ export interface LContainer extends Array<any> {
* It could also be a styling context if this is a node with a style/class
* binding.
*/
[HOST]: RElement|RComment|StylingContext|LView;
readonly[HOST]: RElement|RComment|StylingContext|LView;
/** The comment element that serves as an anchor for this LContainer. */
[NATIVE]: RComment;
readonly[NATIVE]: RComment;
}
// Note: This hack is necessary so we don't erroneously get a circular dependency

View File

@ -110,11 +110,27 @@ export const domRendererFactory3: RendererFactory3 = {
/** Subset of API needed for appending elements and text nodes. */
export interface RNode {
/**
* Returns the parent Element, Document, or DocumentFragment
*/
parentNode: RNode|null;
/**
* Returns the parent Element if there is one
*/
parentElement: RElement|null;
/**
* Gets the Node immediately following this one in the parent's childNodes
*/
nextSibling: RNode|null;
removeChild(oldChild: RNode): void;
/**
* Removes a child from the current node and returns the removed node
* @param oldChild the child node to remove
*/
removeChild(oldChild: RNode): RNode;
/**
* Insert a child node.

View File

@ -41,8 +41,8 @@ export const INJECTOR = 10;
export const RENDERER_FACTORY = 11;
export const RENDERER = 12;
export const SANITIZER = 13;
export const TAIL = 14;
export const CONTAINER_INDEX = 15;
export const CHILD_HEAD = 14;
export const CHILD_TAIL = 15;
export const CONTENT_QUERIES = 16;
export const DECLARATION_VIEW = 17;
/** Size of LView's header. Necessary to adjust for it when setting slots. */
@ -86,7 +86,7 @@ export interface LView extends Array<any> {
* This is the "insertion" view for embedded views. This allows us to properly
* destroy embedded views.
*/
[PARENT]: LView|null;
[PARENT]: LView|LContainer|null;
/**
*
@ -168,17 +168,7 @@ export interface LView extends Array<any> {
* The tail allows us to quickly add a new state to the end of the view list
* without having to propagate starting from the first child.
*/
[TAIL]: LView|LContainer|null;
/**
* The index of the parent container's host node. Applicable only to embedded views that
* have been inserted dynamically. Will be -1 for component views and inline views.
*
* This is necessary to jump from dynamically created embedded views to their parent
* containers because their parent cannot be stored on the TViewNode (views may be inserted
* in multiple containers, so the parent cannot be shared between view instances).
*/
[CONTAINER_INDEX]: number;
[CHILD_TAIL]: LView|LContainer|null;
/**
* Stores QueryLists associated with content queries of a directive. This data structure is

View File

@ -7,29 +7,33 @@
*/
import {ViewEncapsulation} from '../metadata/view';
import {assertDefined} from '../util/assert';
import {assertLContainer, assertLView} from './assert';
import {attachPatchData} from './context_discovery';
import {LContainer, NATIVE, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {ComponentDef} from './interfaces/definition';
import {NodeInjectorFactory} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {CHILD_HEAD, CLEANUP, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeType} from './node_assert';
import {findComponentView, getNativeByTNode, isComponent, isLContainer, isRootView, readElementValue, renderStringify} from './util';
import {findComponentView, getLViewParent, getNativeByTNode, isComponent, isLContainer, isLView, isRootView, readElementValue, renderStringify} from './util';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
export function getLContainer(tNode: TViewNode, embeddedView: LView): LContainer|null {
ngDevMode && assertLView(embeddedView);
const container = embeddedView[PARENT] as LContainer;
if (tNode.index === -1) {
// This is a dynamically created view inside a dynamic container.
// If the host index is -1, the view has not yet been inserted, so it has no parent.
const containerHostIndex = embeddedView[CONTAINER_INDEX];
return containerHostIndex > -1 ? embeddedView[PARENT] ![containerHostIndex] : null;
// The parent isn't an LContainer if the embedded view hasn't been attached yet.
return isLContainer(container) ? container : null;
} else {
ngDevMode && assertLContainer(container);
// This is a inline view node (e.g. embeddedViewStart)
return embeddedView[PARENT] ![tNode.parent !.index] as LContainer;
return container;
}
}
@ -123,7 +127,7 @@ function walkTNodeTree(
projectionNodeStack[++projectionNodeIndex] = tNode;
projectionNodeStack[++projectionNodeIndex] = currentView !;
if (head) {
currentView = componentView[PARENT] !;
currentView = componentView[PARENT] !as LView;
nextTNode = currentView[TVIEW].data[head.index] as TNode;
}
}
@ -156,7 +160,7 @@ function walkTNodeTree(
// When exiting a container, the beforeNode must be restored to the previous value
if (tNode.type === TNodeType.Container) {
currentView = currentView[PARENT] !;
currentView = getLViewParent(currentView) !;
beforeNode = currentView[tNode.index][NATIVE];
}
@ -252,35 +256,35 @@ export function addRemoveViewFromContainer(
*/
export function destroyViewTree(rootView: LView): void {
// If the view has no children, we can clean it up and return early.
if (rootView[TVIEW].childIndex === -1) {
let lViewOrLContainer = rootView[CHILD_HEAD];
if (!lViewOrLContainer) {
return cleanUpView(rootView);
}
let viewOrContainer: LView|LContainer|null = getLViewChild(rootView);
while (viewOrContainer) {
while (lViewOrLContainer) {
let next: LView|LContainer|null = null;
if (viewOrContainer.length >= HEADER_OFFSET) {
if (isLView(lViewOrLContainer)) {
// If LView, traverse down to child.
const view = viewOrContainer as LView;
if (view[TVIEW].childIndex > -1) next = getLViewChild(view);
next = lViewOrLContainer[CHILD_HEAD];
} else {
ngDevMode && assertLContainer(lViewOrLContainer);
// If container, traverse down to its first LView.
const container = viewOrContainer as LContainer;
if (container[VIEWS].length) next = container[VIEWS][0];
const views = lViewOrLContainer[VIEWS] as LView[];
if (views.length > 0) next = views[0];
}
if (next == null) {
if (!next) {
// Only clean up view when moving to the side or up, as destroy hooks
// should be called in order from the bottom up.
while (viewOrContainer && !viewOrContainer ![NEXT] && viewOrContainer !== rootView) {
cleanUpView(viewOrContainer);
viewOrContainer = getParentState(viewOrContainer, rootView);
while (lViewOrLContainer && !lViewOrLContainer ![NEXT] && lViewOrLContainer !== rootView) {
cleanUpView(lViewOrLContainer);
lViewOrLContainer = getParentState(lViewOrLContainer, rootView);
}
cleanUpView(viewOrContainer || rootView);
next = viewOrContainer && viewOrContainer ![NEXT];
cleanUpView(lViewOrLContainer || rootView);
next = lViewOrLContainer && lViewOrLContainer ![NEXT];
}
viewOrContainer = next;
lViewOrLContainer = next;
}
}
@ -294,15 +298,13 @@ export function destroyViewTree(rootView: LView): void {
*
* @param lView The view to insert
* @param lContainer The container into which the view should be inserted
* @param parentView The new parent of the inserted view
* @param index The index at which to insert the view
* @param containerIndex The index of the container node, if dynamic
* @param index Which index in the container to insert the child view into
*/
export function insertView(
lView: LView, lContainer: LContainer, parentView: LView, index: number,
containerIndex: number) {
export function insertView(lView: LView, lContainer: LContainer, index: number) {
ngDevMode && assertLView(lView);
ngDevMode && assertLContainer(lContainer);
const views = lContainer[VIEWS];
ngDevMode && assertDefined(views, 'Container must have views');
if (index > 0) {
// This is a new view, we need to add it to the children.
views[index - 1][NEXT] = lView;
@ -316,12 +318,7 @@ export function insertView(
lView[NEXT] = null;
}
// Dynamically inserted views need a reference to their parent container's host so it's
// possible to jump from a view to its container's next when walking the node tree.
if (containerIndex > -1) {
lView[CONTAINER_INDEX] = containerIndex;
lView[PARENT] = parentView;
}
lView[PARENT] = lContainer;
// Notify query that a new view has been added
if (lView[QUERIES]) {
@ -354,7 +351,6 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView {
if (viewToDetach[QUERIES]) {
viewToDetach[QUERIES] !.removeView();
}
viewToDetach[CONTAINER_INDEX] = -1;
viewToDetach[PARENT] = null;
// Unsets the attached flag
viewToDetach[FLAGS] &= ~LViewFlags.Attached;
@ -404,20 +400,21 @@ export function destroyLView(view: LView) {
* embedded views, the container (which is the view node's parent, but not the
* LView's parent) needs to be checked for a possible next property.
*
* @param state The LViewOrLContainer for which we need a parent state
* @param lViewOrLContainer The LViewOrLContainer for which we need a parent state
* @param rootView The rootView, so we don't propagate too far up the view tree
* @returns The correct parent LViewOrLContainer
*/
export function getParentState(state: LView | LContainer, rootView: LView): LView|LContainer|null {
export function getParentState(lViewOrLContainer: LView | LContainer, rootView: LView): LView|
LContainer|null {
let tNode;
if (state.length >= HEADER_OFFSET && (tNode = (state as LView) ![T_HOST]) &&
if (isLView(lViewOrLContainer) && (tNode = lViewOrLContainer[T_HOST]) &&
tNode.type === TNodeType.View) {
// if it's an embedded view, the state needs to go up to the container, in case the
// container has a next
return getLContainer(tNode as TViewNode, state as LView) as LContainer;
return getLContainer(tNode as TViewNode, lViewOrLContainer);
} else {
// otherwise, use parent view for containers or component views
return state[PARENT] === rootView ? null : state[PARENT];
return lViewOrLContainer[PARENT] === rootView ? null : lViewOrLContainer[PARENT];
}
}
@ -576,9 +573,10 @@ function getRenderParent(tNode: TNode, currentView: LView): RElement|null {
* a host element.
*/
function getHostNative(currentView: LView): RElement|null {
ngDevMode && assertLView(currentView);
const hostTNode = currentView[T_HOST];
return hostTNode && hostTNode.type === TNodeType.Element ?
(getNativeByTNode(hostTNode, currentView[PARENT] !) as RElement) :
(getNativeByTNode(hostTNode, getLViewParent(currentView) !) as RElement) :
null;
}

View File

@ -8,11 +8,11 @@
import '../util/ng_dev_mode';
import {getLContext} from './context_discovery';
import {getRootContext} from './discovery_utils';
import {scheduleTick} from './instructions';
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
import {HEADER_OFFSET, RootContextFlags} from './interfaces/view';
import {RootContextFlags} from './interfaces/view';
import {addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayersInternal, getStylingContext, throwInvalidRefError} from './styling/util';
import {getRootContext} from './util';
/**
* Adds a player to an element, directive or component instance that will later be

View File

@ -8,12 +8,14 @@
import {assertDefined} from '../util/assert';
import {assertLViewOrUndefined} from './assert';
import {executeHooks} from './hooks';
import {ComponentDef, DirectiveDef} from './interfaces/definition';
import {TElementNode, TNode, TViewNode} from './interfaces/node';
import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, FLAGS, InitPhaseState, LView, LViewFlags, OpaqueViewState, TVIEW} from './interfaces/view';
/**
* Store the element depth count. This is used to identify the root elements of the template
* so that we can than attach `LView` to only those elements.
@ -142,6 +144,7 @@ export function setPreviousOrParentTNode(tNode: TNode) {
}
export function setTNodeAndViewData(tNode: TNode, view: LView) {
ngDevMode && assertLViewOrUndefined(view);
previousOrParentTNode = tNode;
lView = view;
}
@ -249,6 +252,7 @@ export function setCurrentQueryIndex(value: number): void {
* @returns the previous state;
*/
export function enterView(newView: LView, hostTNode: TElementNode | TViewNode | null): LView {
ngDevMode && assertLViewOrUndefined(newView);
const oldView = lView;
if (newView) {
const tView = newView[TVIEW];

View File

@ -9,17 +9,36 @@
import {assertDataInRange, assertDefined, assertGreaterThan, assertLessThan} from '../util/assert';
import {global} from '../util/global';
import {assertLView} from './assert';
import {LCONTAINER_LENGTH, LContainer} from './interfaces/container';
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {ComponentDef, DirectiveDef} from './interfaces/definition';
import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector';
import {TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {RComment, RElement, RText} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, RootContext, TData, TVIEW, T_HOST} from './interfaces/view';
/**
* Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
* that LContainer, which is an LView
* @param lView the lView whose parent to get
*/
export function getLViewParent(lView: LView): LView|null {
ngDevMode && assertLView(lView);
const parent = lView[PARENT];
return isLContainer(parent) ? parent[PARENT] ! : parent;
}
/**
* Returns true if the value is an {@link LView}
* @param value the value to check
*/
export function isLView(value: any): value is LView {
return Array.isArray(value) && value.length >= HEADER_OFFSET;
}
/**
* Returns whether the values are different from a change detection stand point.
*
@ -85,7 +104,7 @@ export function loadInternal<T>(view: LView | TData, index: number): T {
*
* @param value The initial value in `LView`
*/
export function readElementValue(value: RElement | StylingContext | LContainer | LView): RElement {
export function readElementValue(value: any): RElement {
while (Array.isArray(value)) {
value = value[HOST] as any;
}
@ -113,7 +132,9 @@ export function getTNode(index: number, view: LView): TNode {
export function getComponentViewByIndex(nodeIndex: number, hostView: LView): LView {
// Could be an LView or an LContainer. If LContainer, unwrap to find LView.
const slotValue = hostView[nodeIndex];
return slotValue.length >= HEADER_OFFSET ? slotValue : slotValue[HOST];
const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
ngDevMode && assertLView(lView);
return lView;
}
export function isContentQueryHost(tNode: TNode): boolean {
@ -128,7 +149,7 @@ export function isComponentDef<T>(def: DirectiveDef<T>): def is ComponentDef<T>
return (def as ComponentDef<T>).template !== null;
}
export function isLContainer(value: RElement | RComment | LContainer | StylingContext): boolean {
export function isLContainer(value: any): value is LContainer {
// Styling contexts are also arrays, but their first index contains an element node
return Array.isArray(value) && value.length === LCONTAINER_LENGTH;
}
@ -138,20 +159,27 @@ export function isRootView(target: LView): boolean {
}
/**
* Retrieve the root view from any component by walking the parent `LView` until
* Retrieve the root view from any component or `LView` by walking the parent `LView` until
* reaching the root `LView`.
*
* @param component any component
* @param componentOrLView any component or `LView`
*/
export function getRootView(target: LView | {}): LView {
ngDevMode && assertDefined(target, 'component');
let lView = Array.isArray(target) ? (target as LView) : readPatchedLView(target) !;
export function getRootView(componentOrLView: LView | {}): LView {
ngDevMode && assertDefined(componentOrLView, 'component');
let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView) !;
while (lView && !(lView[FLAGS] & LViewFlags.IsRoot)) {
lView = lView[PARENT] !;
lView = getLViewParent(lView) !;
}
ngDevMode && assertLView(lView);
return lView;
}
/**
* Returns the `RootContext` instance that is associated with
* the application where the target is situated. It does this by walking the parent views until it
* gets to the root view, then getting the context off of that.
*
* @param viewOrComponent the `LView` or component to get the root context for.
*/
export function getRootContext(viewOrComponent: LView | {}): RootContext {
const rootView = getRootView(viewOrComponent);
ngDevMode &&
@ -279,6 +307,7 @@ export function findComponentView(lView: LView): LView {
rootTNode = lView[T_HOST];
}
ngDevMode && assertLView(lView);
return lView;
}

View File

@ -22,7 +22,7 @@ import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbedd
import {ACTIVE_INDEX, LContainer, NATIVE, VIEWS} from './interfaces/container';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
import {CONTAINER_INDEX, CONTEXT, LView, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
import {CONTEXT, LView, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation';
import {getLView, getPreviousOrParentTNode} from './state';
@ -101,15 +101,13 @@ export function createTemplateRef<T>(
super();
}
createEmbeddedView(
context: T, container?: LContainer,
hostTNode?: TElementNode|TContainerNode|TElementContainerNode, hostView?: LView,
index?: number): viewEngine_EmbeddedViewRef<T> {
createEmbeddedView(context: T, container?: LContainer, index?: number):
viewEngine_EmbeddedViewRef<T> {
const lView = createEmbeddedViewAndNode(
this._tView, context, this._declarationParentView, this._hostLContainer[QUERIES],
this._injectorIndex);
if (container) {
insertView(lView, container, hostView !, index !, hostTNode !.index);
insertView(lView, container, index !);
}
renderEmbeddedTemplate(lView, this._tView, context);
const viewRef = new ViewRef(lView, context, -1);
@ -207,9 +205,7 @@ export function createContainerRef(
viewEngine_EmbeddedViewRef<C> {
const adjustedIdx = this._adjustIndex(index);
const viewRef = (templateRef as any)
.createEmbeddedView(
context || <any>{}, this._lContainer, this._hostTNode,
this._hostView, adjustedIdx);
.createEmbeddedView(context || <any>{}, this._lContainer, adjustedIdx);
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
this._viewRefs.splice(adjustedIdx, 0, viewRef);
return viewRef;
@ -237,7 +233,7 @@ export function createContainerRef(
const lView = (viewRef as ViewRef<any>)._lView !;
const adjustedIdx = this._adjustIndex(index);
insertView(lView, this._lContainer, this._hostView, adjustedIdx, this._hostTNode.index);
insertView(lView, this._lContainer, adjustedIdx);
const beforeNode =
getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], this._lContainer[NATIVE]);
@ -271,7 +267,7 @@ export function createContainerRef(
const adjustedIdx = this._adjustIndex(index, -1);
const view = detachView(this._lContainer, adjustedIdx);
const wasDetached = this._viewRefs.splice(adjustedIdx, 1)[0] != null;
return wasDetached ? new ViewRef(view, view[CONTEXT], view[CONTAINER_INDEX]) : null;
return wasDetached ? new ViewRef(view, view[CONTEXT], -1) : null;
}
private _adjustIndex(index?: number, shift: number = 0) {

View File

@ -15,7 +15,7 @@ import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootVie
import {TNode, TNodeType, TViewNode} from './interfaces/node';
import {FLAGS, HOST, LView, LViewFlags, PARENT, T_HOST} from './interfaces/view';
import {destroyLView} from './node_manipulation';
import {getNativeByTNode} from './util';
import {getLViewParent, getNativeByTNode} from './util';
@ -271,7 +271,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
}
private _lookUpContext(): T {
return this._context = this._lView[PARENT] ![this._componentIndex] as T;
return this._context = getLViewParent(this._lView) ![this._componentIndex] as T;
}
}

View File

@ -65,7 +65,10 @@ export function throwError(msg: string): never {
}
export function assertDomNode(node: any) {
assertEqual(node instanceof Node, true, 'The provided value must be an instance of a DOM Node');
assertEqual(
node instanceof Node ||
(typeof node === 'object' && node.constructor.name === 'WebWorkerRenderNode'),
true, 'The provided value must be an instance of a DOM Node');
}

View File

@ -12,10 +12,13 @@
"name": "BLOOM_MASK"
},
{
"name": "CLEAN_PROMISE"
"name": "CHILD_HEAD"
},
{
"name": "CONTAINER_INDEX"
"name": "CHILD_TAIL"
},
{
"name": "CLEAN_PROMISE"
},
{
"name": "CONTEXT"
@ -119,9 +122,6 @@
{
"name": "SANITIZER"
},
{
"name": "TAIL"
},
{
"name": "TVIEW"
},
@ -351,7 +351,7 @@
"name": "getLView"
},
{
"name": "getLViewChild"
"name": "getLViewParent"
},
{
"name": "getNativeAnchorNode"
@ -470,6 +470,12 @@
{
"name": "isFactory"
},
{
"name": "isLContainer"
},
{
"name": "isLView"
},
{
"name": "isNodeMatchingSelector"
},

View File

@ -9,10 +9,13 @@
"name": "BLOOM_MASK"
},
{
"name": "CLEAN_PROMISE"
"name": "CHILD_HEAD"
},
{
"name": "CONTAINER_INDEX"
"name": "CHILD_TAIL"
},
{
"name": "CLEAN_PROMISE"
},
{
"name": "CONTEXT"
@ -50,6 +53,9 @@
{
"name": "INJECTOR_BLOOM_PARENT_SIZE"
},
{
"name": "LCONTAINER_LENGTH"
},
{
"name": "MONKEY_PATCH_KEY_NAME"
},
@ -98,9 +104,6 @@
{
"name": "SANITIZER"
},
{
"name": "TAIL"
},
{
"name": "TVIEW"
},
@ -258,7 +261,7 @@
"name": "getLView"
},
{
"name": "getLViewChild"
"name": "getLViewParent"
},
{
"name": "getNativeAnchorNode"
@ -335,6 +338,12 @@
{
"name": "isFactory"
},
{
"name": "isLContainer"
},
{
"name": "isLView"
},
{
"name": "isProceduralRenderer"
},

View File

@ -17,15 +17,18 @@
{
"name": "BoundPlayerFactory"
},
{
"name": "CHILD_HEAD"
},
{
"name": "CHILD_TAIL"
},
{
"name": "CLEANUP"
},
{
"name": "CLEAN_PROMISE"
},
{
"name": "CONTAINER_INDEX"
},
{
"name": "CONTEXT"
},
@ -209,9 +212,6 @@
{
"name": "SkipSelf"
},
{
"name": "TAIL"
},
{
"name": "TNODE"
},
@ -720,7 +720,7 @@
"name": "getLView"
},
{
"name": "getLViewChild"
"name": "getLViewParent"
},
{
"name": "getMatchingBindingIndex"
@ -965,6 +965,9 @@
{
"name": "isLContainer"
},
{
"name": "isLView"
},
{
"name": "isListLikeIterable"
},

View File

@ -319,7 +319,7 @@ describe('Query API', () => {
const template = `<needs-content-children-shallow>
<ng-template [ngIf]="true">
<div #q></div>
</ng-template>
</ng-template>
</needs-content-children-shallow>`;
const view = createTestCmpAndDetectChanges(MyComp0, template);
@ -333,7 +333,7 @@ describe('Query API', () => {
const template = `<needs-content-children-shallow>
<ng-template [ngIf]="true">
<div #q directive-needs-content-child></div>
</ng-template>
</ng-template>
</needs-content-children-shallow>`;
const view = createTestCmpAndDetectChanges(MyComp0, template);
@ -358,7 +358,7 @@ describe('Query API', () => {
const template = `<needs-content-children-shallow>
<ng-container>
<div #q></div>
</ng-container>
</ng-container>
</needs-content-children-shallow>`;
const view = createTestCmpAndDetectChanges(MyComp0, template);

View File

@ -8,14 +8,19 @@
*/
import {SECURITY_SCHEMA} from '@angular/compiler/src/schema/dom_security_schema';
import {HEADER_OFFSET, LView} from '@angular/core/src/render3/interfaces/view';
import {setTNodeAndViewData} from '@angular/core/src/render3/state';
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {getUrlSanitizer, sanitizeHtml, sanitizeResourceUrl, sanitizeScript, sanitizeStyle, sanitizeUrl, sanitizeUrlOrResourceUrl} from '../../src/sanitization/sanitization';
import {SecurityContext} from '../../src/sanitization/security';
function fakeLView(): LView {
return Array.from({length: HEADER_OFFSET}) as LView;
}
describe('sanitization', () => {
beforeEach(() => setTNodeAndViewData(null !, [] as any));
beforeEach(() => setTNodeAndViewData(null !, fakeLView()));
afterEach(() => setTNodeAndViewData(null !, null !));
class Wrap {
constructor(private value: string) {}