2018-10-18 09:23:18 +02:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
|
2019-05-28 10:31:01 -07:00
|
|
|
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
2020-01-26 12:29:03 -08:00
|
|
|
import {assertDefined} from '../util/assert';
|
2019-01-28 14:45:31 -08:00
|
|
|
import {assertLViewOrUndefined} from './assert';
|
2019-10-14 13:59:17 -07:00
|
|
|
import {TNode} from './interfaces/node';
|
2019-10-11 12:43:32 -07:00
|
|
|
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
|
2019-10-14 13:59:17 -07:00
|
|
|
|
2018-10-18 09:23:18 +02:00
|
|
|
|
|
|
|
/**
|
2019-10-11 12:43:32 -07:00
|
|
|
*
|
2018-10-18 09:23:18 +02:00
|
|
|
*/
|
2019-10-14 13:59:17 -07:00
|
|
|
interface LFrame {
|
|
|
|
/**
|
|
|
|
* Parent LFrame.
|
|
|
|
*
|
|
|
|
* This is needed when `leaveView` is called to restore the previous state.
|
|
|
|
*/
|
|
|
|
parent: LFrame;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Child LFrame.
|
|
|
|
*
|
|
|
|
* This is used to cache existing LFrames to relieve the memory pressure.
|
|
|
|
*/
|
|
|
|
child: LFrame|null;
|
|
|
|
|
2019-10-11 12:43:32 -07:00
|
|
|
/**
|
|
|
|
* State of the current view being processed.
|
|
|
|
*
|
|
|
|
* An array of nodes (text, element, container, etc), pipes, their bindings, and
|
|
|
|
* any local variables that need to be stored between invocations.
|
|
|
|
*/
|
|
|
|
lView: LView;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to set the parent property when nodes are created and track query results.
|
|
|
|
*
|
2019-12-17 15:40:37 -08:00
|
|
|
* This is used in conjunction with `isParent`.
|
2019-10-11 12:43:32 -07:00
|
|
|
*/
|
|
|
|
previousOrParentTNode: TNode;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If `isParent` is:
|
|
|
|
* - `true`: then `previousOrParentTNode` points to a parent node.
|
|
|
|
* - `false`: then `previousOrParentTNode` points to previous node (sibling).
|
|
|
|
*/
|
|
|
|
isParent: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Index of currently selected element in LView.
|
|
|
|
*
|
|
|
|
* Used by binding instructions. Updated as part of advance instruction.
|
|
|
|
*/
|
|
|
|
selectedIndex: number;
|
|
|
|
|
2019-10-11 12:43:32 -07:00
|
|
|
/**
|
|
|
|
* Current pointer to the binding index.
|
|
|
|
*/
|
|
|
|
bindingIndex: number;
|
|
|
|
|
2019-10-11 12:43:32 -07:00
|
|
|
/**
|
|
|
|
* The last viewData retrieved by nextContext().
|
|
|
|
* Allows building nextContext() and reference() calls.
|
|
|
|
*
|
|
|
|
* e.g. const inner = x().$implicit; const outer = x().$implicit;
|
|
|
|
*/
|
|
|
|
contextLView: LView;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store the element depth count. This is used to identify the root elements of the template
|
2019-10-14 13:59:17 -07:00
|
|
|
* so that we can then attach patch data `LView` to only those elements. We know that those
|
|
|
|
* are the only places where the patch data could change, this way we will save on number
|
|
|
|
* of places where tha patching occurs.
|
2019-10-11 12:43:32 -07:00
|
|
|
*/
|
|
|
|
elementDepthCount: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current namespace to be used when creating elements
|
|
|
|
*/
|
|
|
|
currentNamespace: string|null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current sanitizer
|
|
|
|
*/
|
|
|
|
currentSanitizer: StyleSanitizeFn|null;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The root index from which pure function instructions should calculate their binding
|
|
|
|
* indices. In component views, this is TView.bindingStartIndex. In a host binding
|
|
|
|
* context, this is the TView.expandoStartIndex + any dirs/hostVars before the given dir.
|
|
|
|
*/
|
|
|
|
bindingRootIndex: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current index of a View or Content Query which needs to be processed next.
|
|
|
|
* We iterate over the list of Queries and increment current query index at every step.
|
|
|
|
*/
|
|
|
|
currentQueryIndex: number;
|
2020-01-26 12:29:03 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* When host binding is executing this points to the directive index.
|
|
|
|
* `TView.data[currentDirectiveIndex]` is `DirectiveDef`
|
|
|
|
* `LView[currentDirectiveIndex]` is directive instance.
|
|
|
|
*/
|
|
|
|
currentDirectiveIndex: number;
|
2019-10-14 13:59:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All implicit instruction state is stored here.
|
|
|
|
*
|
|
|
|
* It is useful to have a single object where all of the state is stored as a mental model
|
|
|
|
* (rather it being spread across many different variables.)
|
|
|
|
*
|
|
|
|
* PERF NOTE: Turns out that writing to a true global variable is slower than
|
|
|
|
* having an intermediate object with properties.
|
|
|
|
*/
|
|
|
|
interface InstructionState {
|
|
|
|
/**
|
|
|
|
* Current `LFrame`
|
|
|
|
*
|
|
|
|
* `null` if we have not called `enterView`
|
|
|
|
*/
|
|
|
|
lFrame: LFrame;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores whether directives should be matched to elements.
|
|
|
|
*
|
|
|
|
* When template contains `ngNonBindable` then we need to prevent the runtime from matching
|
|
|
|
* directives on children of that element.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* ```
|
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should match component / directive.
|
|
|
|
* </my-comp>
|
|
|
|
* <div ngNonBindable>
|
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should not match component / directive because we are in ngNonBindable.
|
|
|
|
* </my-comp>
|
|
|
|
* </div>
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
bindingsEnabled: boolean;
|
2019-10-11 12:43:32 -07:00
|
|
|
|
2019-10-14 13:59:17 -07:00
|
|
|
/**
|
|
|
|
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
|
|
|
|
*
|
|
|
|
* Necessary to support ChangeDetectorRef.checkNoChanges().
|
|
|
|
*/
|
|
|
|
checkNoChangesMode: boolean;
|
2019-10-11 12:43:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export const instructionState: InstructionState = {
|
2019-10-14 13:59:17 -07:00
|
|
|
lFrame: createLFrame(null),
|
2019-10-11 12:43:32 -07:00
|
|
|
bindingsEnabled: true,
|
2019-10-14 13:59:17 -07:00
|
|
|
checkNoChangesMode: false,
|
2019-10-11 12:43:32 -07:00
|
|
|
};
|
|
|
|
|
2018-10-18 09:23:18 +02:00
|
|
|
|
|
|
|
export function getElementDepthCount() {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.elementDepthCount;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function increaseElementDepthCount() {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.elementDepthCount++;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function decreaseElementDepthCount() {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.elementDepthCount--;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getBindingsEnabled(): boolean {
|
2019-10-11 12:43:32 -07:00
|
|
|
return instructionState.bindingsEnabled;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enables directive matching on elements.
|
|
|
|
*
|
|
|
|
* * Example:
|
|
|
|
* ```
|
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should match component / directive.
|
|
|
|
* </my-comp>
|
|
|
|
* <div ngNonBindable>
|
2019-05-17 18:49:21 -07:00
|
|
|
* <!-- ɵɵdisableBindings() -->
|
2018-10-18 09:23:18 +02:00
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should not match component / directive because we are in ngNonBindable.
|
|
|
|
* </my-comp>
|
2019-05-17 18:49:21 -07:00
|
|
|
* <!-- ɵɵenableBindings() -->
|
2018-10-18 09:23:18 +02:00
|
|
|
* </div>
|
|
|
|
* ```
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2018-10-18 09:23:18 +02:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵenableBindings(): void {
|
2019-10-11 12:43:32 -07:00
|
|
|
instructionState.bindingsEnabled = true;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disables directive matching on element.
|
|
|
|
*
|
|
|
|
* * Example:
|
|
|
|
* ```
|
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should match component / directive.
|
|
|
|
* </my-comp>
|
|
|
|
* <div ngNonBindable>
|
2019-05-17 18:49:21 -07:00
|
|
|
* <!-- ɵɵdisableBindings() -->
|
2018-10-18 09:23:18 +02:00
|
|
|
* <my-comp my-directive>
|
|
|
|
* Should not match component / directive because we are in ngNonBindable.
|
|
|
|
* </my-comp>
|
2019-05-17 18:49:21 -07:00
|
|
|
* <!-- ɵɵenableBindings() -->
|
2018-10-18 09:23:18 +02:00
|
|
|
* </div>
|
|
|
|
* ```
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2018-10-18 09:23:18 +02:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵdisableBindings(): void {
|
2019-10-11 12:43:32 -07:00
|
|
|
instructionState.bindingsEnabled = false;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-10-14 13:59:17 -07:00
|
|
|
/**
|
|
|
|
* Return the current LView.
|
|
|
|
*
|
|
|
|
* The return value can be `null` if the method is called outside of template. This can happen if
|
|
|
|
* directive is instantiated by module injector (rather than by node injector.)
|
|
|
|
*/
|
2018-11-21 21:14:06 -08:00
|
|
|
export function getLView(): LView {
|
2019-10-14 13:59:17 -07:00
|
|
|
// TODO(misko): the return value should be `LView|null` but doing so breaks a lot of code.
|
|
|
|
const lFrame = instructionState.lFrame;
|
|
|
|
return lFrame === null ? null ! : lFrame.lView;
|
2018-11-13 09:36:30 +01:00
|
|
|
}
|
|
|
|
|
2019-04-02 16:16:00 -07:00
|
|
|
/**
|
|
|
|
* Sets the active directive host element and resets the directive id value
|
|
|
|
* (when the provided elementIndex value has changed).
|
|
|
|
*
|
|
|
|
* @param elementIndex the element index value for the host element where
|
|
|
|
* the directive/component instance lives
|
|
|
|
*/
|
2019-12-17 15:40:37 -08:00
|
|
|
export function setActiveHostElement(elementIndex: number) {
|
|
|
|
setSelectedIndex(elementIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function clearActiveHostElement() {
|
|
|
|
setSelectedIndex(-1);
|
2019-04-02 16:16:00 -07:00
|
|
|
}
|
|
|
|
|
2018-10-18 09:23:18 +02:00
|
|
|
/**
|
|
|
|
* Restores `contextViewData` to the given OpaqueViewState instance.
|
|
|
|
*
|
|
|
|
* Used in conjunction with the getCurrentView() instruction to save a snapshot
|
|
|
|
* of the current view and restore it when listeners are invoked. This allows
|
|
|
|
* walking the declaration view tree in listeners to get vars from parent views.
|
|
|
|
*
|
|
|
|
* @param viewToRestore The OpaqueViewState instance to restore.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2018-10-18 09:23:18 +02:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵrestoreView(viewToRestore: OpaqueViewState) {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.contextLView = viewToRestore as any as LView;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getPreviousOrParentTNode(): TNode {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.previousOrParentTNode;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-14 21:47:11 -07:00
|
|
|
export function setPreviousOrParentTNode(tNode: TNode, _isParent: boolean) {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.previousOrParentTNode = tNode;
|
|
|
|
instructionState.lFrame.isParent = _isParent;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getIsParent(): boolean {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.isParent;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-05-14 21:47:11 -07:00
|
|
|
export function setIsNotParent(): void {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.isParent = false;
|
2019-05-14 21:47:11 -07:00
|
|
|
}
|
|
|
|
export function setIsParent(): void {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.isParent = true;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
export function getContextLView(): LView {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.contextLView;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getCheckNoChangesMode(): boolean {
|
2019-12-17 15:40:37 -08:00
|
|
|
// TODO(misko): remove this from the LView since it is ngDevMode=true mode only.
|
2019-10-11 12:43:32 -07:00
|
|
|
return instructionState.checkNoChangesMode;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function setCheckNoChangesMode(mode: boolean): void {
|
2019-10-11 12:43:32 -07:00
|
|
|
instructionState.checkNoChangesMode = mode;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
|
|
|
export function getBindingRoot() {
|
2019-10-14 13:59:17 -07:00
|
|
|
const lFrame = instructionState.lFrame;
|
|
|
|
let index = lFrame.bindingRootIndex;
|
|
|
|
if (index === -1) {
|
|
|
|
const lView = lFrame.lView;
|
2019-10-11 12:43:32 -07:00
|
|
|
index = lFrame.bindingRootIndex = lView[TVIEW].bindingStartIndex;
|
2019-10-14 13:59:17 -07:00
|
|
|
}
|
|
|
|
return index;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 12:43:32 -07:00
|
|
|
export function getBindingIndex(): number {
|
|
|
|
return instructionState.lFrame.bindingIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setBindingIndex(value: number): number {
|
|
|
|
return instructionState.lFrame.bindingIndex = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function nextBindingIndex(): number {
|
|
|
|
return instructionState.lFrame.bindingIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function incrementBindingIndex(count: number): number {
|
|
|
|
const lFrame = instructionState.lFrame;
|
|
|
|
const index = lFrame.bindingIndex;
|
|
|
|
lFrame.bindingIndex = lFrame.bindingIndex + count;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a new binding root index so that host template functions can execute.
|
|
|
|
*
|
|
|
|
* Bindings inside the host template are 0 index. But because we don't know ahead of time
|
|
|
|
* how many host bindings we have we can't pre-compute them. For this reason they are all
|
|
|
|
* 0 index and we just shift the root so that they match next available location in the LView.
|
2020-01-26 12:29:03 -08:00
|
|
|
*
|
|
|
|
* @param bindingRootIndex Root index for `hostBindings`
|
|
|
|
* @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
|
|
|
|
* whose `hostBindings` are being processed.
|
|
|
|
*/
|
|
|
|
export function setBindingRootForHostBindings(
|
|
|
|
bindingRootIndex: number, currentDirectiveIndex: number) {
|
|
|
|
const lFrame = instructionState.lFrame;
|
|
|
|
lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
|
|
|
|
lFrame.currentDirectiveIndex = currentDirectiveIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When host binding is executing this points to the directive index.
|
|
|
|
* `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef`
|
|
|
|
* `LView[getCurrentDirectiveIndex()]` is directive instance.
|
2019-10-11 12:43:32 -07:00
|
|
|
*/
|
2020-01-26 12:29:03 -08:00
|
|
|
export function getCurrentDirectiveIndex(): number {
|
|
|
|
return instructionState.lFrame.currentDirectiveIndex;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:54:43 -08:00
|
|
|
export function getCurrentQueryIndex(): number {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.currentQueryIndex;
|
2019-01-18 18:02:32 -08:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:54:43 -08:00
|
|
|
export function setCurrentQueryIndex(value: number): void {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.currentQueryIndex = value;
|
2019-01-18 18:02:32 -08:00
|
|
|
}
|
|
|
|
|
2019-10-14 13:59:17 -07:00
|
|
|
/**
|
|
|
|
* This is a light weight version of the `enterView` which is needed by the DI system.
|
|
|
|
* @param newView
|
|
|
|
* @param tNode
|
|
|
|
*/
|
|
|
|
export function enterDI(newView: LView, tNode: TNode) {
|
|
|
|
ngDevMode && assertLViewOrUndefined(newView);
|
|
|
|
const newLFrame = allocLFrame();
|
|
|
|
instructionState.lFrame = newLFrame;
|
|
|
|
newLFrame.previousOrParentTNode = tNode !;
|
|
|
|
newLFrame.lView = newView;
|
|
|
|
if (ngDevMode) {
|
|
|
|
// resetting for safety in dev mode only.
|
|
|
|
newLFrame.isParent = DEV_MODE_VALUE;
|
|
|
|
newLFrame.selectedIndex = DEV_MODE_VALUE;
|
|
|
|
newLFrame.contextLView = DEV_MODE_VALUE;
|
|
|
|
newLFrame.elementDepthCount = DEV_MODE_VALUE;
|
|
|
|
newLFrame.currentNamespace = DEV_MODE_VALUE;
|
|
|
|
newLFrame.currentSanitizer = DEV_MODE_VALUE;
|
|
|
|
newLFrame.bindingRootIndex = DEV_MODE_VALUE;
|
|
|
|
newLFrame.currentQueryIndex = DEV_MODE_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEV_MODE_VALUE: any =
|
|
|
|
'Value indicating that DI is trying to read value which it should not need to know about.';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a light weight version of the `leaveView` which is needed by the DI system.
|
|
|
|
*
|
|
|
|
* Because the implementation is same it is only an alias
|
|
|
|
*/
|
|
|
|
export const leaveDI = leaveView;
|
|
|
|
|
2018-10-18 09:23:18 +02:00
|
|
|
/**
|
2019-08-22 11:43:24 +02:00
|
|
|
* Swap the current lView with a new lView.
|
2018-10-18 09:23:18 +02:00
|
|
|
*
|
2019-08-22 11:43:24 +02:00
|
|
|
* For performance reasons we store the lView in the top level of the module.
|
2018-10-18 09:23:18 +02:00
|
|
|
* This way we minimize the number of properties to read. Whenever a new view
|
2019-08-22 11:43:24 +02:00
|
|
|
* is entered we have to store the lView for later, and when the view is
|
2018-10-18 09:23:18 +02:00
|
|
|
* exited the state has to be restored
|
|
|
|
*
|
2019-08-22 11:43:24 +02:00
|
|
|
* @param newView New lView to become active
|
2019-10-14 13:59:17 -07:00
|
|
|
* @param tNode Element to which the View is a child of
|
2019-08-22 11:43:24 +02:00
|
|
|
* @returns the previously active lView;
|
2018-10-18 09:23:18 +02:00
|
|
|
*/
|
2019-10-14 13:59:17 -07:00
|
|
|
export function enterView(newView: LView, tNode: TNode | null): void {
|
|
|
|
ngDevMode && assertLViewOrUndefined(newView);
|
|
|
|
const newLFrame = allocLFrame();
|
|
|
|
instructionState.lFrame = newLFrame;
|
|
|
|
newLFrame.previousOrParentTNode = tNode !;
|
|
|
|
newLFrame.isParent = true;
|
|
|
|
newLFrame.lView = newView;
|
|
|
|
newLFrame.selectedIndex = 0;
|
|
|
|
newLFrame.contextLView = newView !;
|
|
|
|
newLFrame.elementDepthCount = 0;
|
2020-01-26 12:29:03 -08:00
|
|
|
newLFrame.currentDirectiveIndex = -1;
|
2019-10-14 13:59:17 -07:00
|
|
|
newLFrame.currentNamespace = null;
|
|
|
|
newLFrame.currentSanitizer = null;
|
|
|
|
newLFrame.bindingRootIndex = -1;
|
2019-10-11 12:43:32 -07:00
|
|
|
newLFrame.bindingIndex = newView === null ? -1 : newView[TVIEW].bindingStartIndex;
|
2019-10-14 13:59:17 -07:00
|
|
|
newLFrame.currentQueryIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
|
|
|
|
*/
|
|
|
|
function allocLFrame() {
|
|
|
|
const currentLFrame = instructionState.lFrame;
|
|
|
|
const childLFrame = currentLFrame === null ? null : currentLFrame.child;
|
|
|
|
const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
|
|
|
|
return newLFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createLFrame(parent: LFrame | null): LFrame {
|
|
|
|
const lFrame: LFrame = {
|
|
|
|
previousOrParentTNode: null !, //
|
|
|
|
isParent: true, //
|
|
|
|
lView: null !, //
|
|
|
|
selectedIndex: 0, //
|
|
|
|
contextLView: null !, //
|
|
|
|
elementDepthCount: 0, //
|
|
|
|
currentNamespace: null, //
|
|
|
|
currentSanitizer: null, //
|
2020-01-26 12:29:03 -08:00
|
|
|
currentDirectiveIndex: -1, //
|
2019-10-14 13:59:17 -07:00
|
|
|
bindingRootIndex: -1, //
|
2019-10-11 12:43:32 -07:00
|
|
|
bindingIndex: -1, //
|
2019-10-14 13:59:17 -07:00
|
|
|
currentQueryIndex: 0, //
|
|
|
|
parent: parent !, //
|
|
|
|
child: null, //
|
|
|
|
};
|
|
|
|
parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
|
|
|
|
return lFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function leaveView() {
|
|
|
|
instructionState.lFrame = instructionState.lFrame.parent;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2019-12-02 15:10:22 +01:00
|
|
|
export function nextContextImpl<T = any>(level: number): T {
|
|
|
|
const contextLView = instructionState.lFrame.contextLView =
|
|
|
|
walkUpViews(level, instructionState.lFrame.contextLView !);
|
|
|
|
return contextLView[CONTEXT] as T;
|
2018-10-18 09:23:18 +02:00
|
|
|
}
|
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
function walkUpViews(nestingLevel: number, currentView: LView): LView {
|
2018-10-18 09:23:18 +02:00
|
|
|
while (nestingLevel > 0) {
|
|
|
|
ngDevMode && assertDefined(
|
|
|
|
currentView[DECLARATION_VIEW],
|
|
|
|
'Declaration view should be defined if nesting level is greater than 0.');
|
|
|
|
currentView = currentView[DECLARATION_VIEW] !;
|
|
|
|
nestingLevel--;
|
|
|
|
}
|
|
|
|
return currentView;
|
|
|
|
}
|
|
|
|
|
2019-03-26 14:57:36 -07:00
|
|
|
/**
|
2019-12-17 15:40:37 -08:00
|
|
|
* Gets the currently selected element index.
|
2019-03-26 14:57:36 -07:00
|
|
|
*
|
|
|
|
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
|
|
|
* current `LView` to act on.
|
|
|
|
*/
|
|
|
|
export function getSelectedIndex() {
|
2020-01-15 16:52:54 -08:00
|
|
|
return instructionState.lFrame.selectedIndex;
|
2019-03-26 14:57:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the most recent index passed to {@link select}
|
|
|
|
*
|
|
|
|
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
|
|
|
* current `LView` to act on.
|
2019-09-09 13:14:26 -07:00
|
|
|
*
|
|
|
|
* (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
|
|
|
|
* run if and when the provided `index` value is different from the current selected index value.)
|
2019-03-26 14:57:36 -07:00
|
|
|
*/
|
|
|
|
export function setSelectedIndex(index: number) {
|
2020-01-15 16:52:54 -08:00
|
|
|
instructionState.lFrame.selectedIndex = index;
|
2019-03-26 14:57:36 -07:00
|
|
|
}
|
2019-04-01 15:36:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-04-01 15:36:43 -07:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵnamespaceSVG() {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.currentNamespace = 'http://www.w3.org/2000/svg';
|
2019-04-01 15:36:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-04-01 15:36:43 -07:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵnamespaceMathML() {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.currentNamespace = 'http://www.w3.org/1998/MathML/';
|
2019-04-01 15:36:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-26 09:21:57 +02:00
|
|
|
* Sets the namespace used to create elements to `null`, which forces element creation to use
|
2019-04-01 15:36:43 -07:00
|
|
|
* `createElement` rather than `createElementNS`.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-04-01 15:36:43 -07:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵnamespaceHTML() {
|
2019-06-26 09:21:57 +02:00
|
|
|
namespaceHTMLInternal();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the namespace used to create elements to `null`, which forces element creation to use
|
|
|
|
* `createElement` rather than `createElementNS`.
|
|
|
|
*/
|
|
|
|
export function namespaceHTMLInternal() {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.currentNamespace = null;
|
2019-04-01 15:36:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getNamespace(): string|null {
|
2019-10-14 13:59:17 -07:00
|
|
|
return instructionState.lFrame.currentNamespace;
|
2019-04-01 15:36:43 -07:00
|
|
|
}
|
2019-05-28 10:31:01 -07:00
|
|
|
|
|
|
|
export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn | null) {
|
2019-10-14 13:59:17 -07:00
|
|
|
instructionState.lFrame.currentSanitizer = sanitizer;
|
2019-05-28 10:31:01 -07:00
|
|
|
}
|
|
|
|
|
2019-10-14 13:45:51 -07:00
|
|
|
export function resetCurrentStyleSanitizer() {
|
|
|
|
setCurrentStyleSanitizer(null);
|
|
|
|
}
|
|
|
|
|
2019-05-28 10:31:01 -07:00
|
|
|
export function getCurrentStyleSanitizer() {
|
2019-10-14 13:59:17 -07:00
|
|
|
// TODO(misko): This should throw when there is no LView, but it turns out we can get here from
|
|
|
|
// `NodeStyleDebug` hence we return `null`. This should be fixed
|
|
|
|
const lFrame = instructionState.lFrame;
|
|
|
|
return lFrame === null ? null : lFrame.currentSanitizer;
|
2019-05-28 10:31:01 -07:00
|
|
|
}
|
2019-12-17 15:40:37 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used for encoding both Class and Style index into `LFrame.stylingBindingChanged`.
|
|
|
|
*/
|
|
|
|
const enum BindingChanged {
|
|
|
|
CLASS_SHIFT = 16,
|
|
|
|
STYLE_MASK = 0xFFFF,
|
|
|
|
}
|