Add RenderBatch reader abstraction (#1052)
* In .JS project, make RenderBatch into an interface with SharedMemoryRenderBatch as implementation * In SharedMemoryRenderBatch, use prototype functions instead of assigning references on each instantiation
This commit is contained in:
parent
389ff14a82
commit
b869ced648
|
|
@ -2,6 +2,8 @@ import { platform } from './Environment';
|
||||||
import { navigateTo, internalFunctions as uriHelperInternalFunctions } from './Services/UriHelper';
|
import { navigateTo, internalFunctions as uriHelperInternalFunctions } from './Services/UriHelper';
|
||||||
import { internalFunctions as httpInternalFunctions } from './Services/Http';
|
import { internalFunctions as httpInternalFunctions } from './Services/Http';
|
||||||
import { attachRootComponentToElement, renderBatch } from './Rendering/Renderer';
|
import { attachRootComponentToElement, renderBatch } from './Rendering/Renderer';
|
||||||
|
import { Pointer } from './Platform/Platform';
|
||||||
|
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
// When the library is loaded in a browser via a <script> element, make the
|
// When the library is loaded in a browser via a <script> element, make the
|
||||||
|
|
@ -12,7 +14,7 @@ if (typeof window !== 'undefined') {
|
||||||
|
|
||||||
_internal: {
|
_internal: {
|
||||||
attachRootComponentToElement,
|
attachRootComponentToElement,
|
||||||
renderBatch,
|
renderBatch: (browserRendererId: number, batchAddress: Pointer) => renderBatch(browserRendererId, new SharedMemoryRenderBatch(batchAddress)),
|
||||||
http: httpInternalFunctions,
|
http: httpInternalFunctions,
|
||||||
uriHelper: uriHelperInternalFunctions
|
uriHelper: uriHelperInternalFunctions
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { System_Array, MethodHandle } from '../Platform/Platform';
|
import { System_Array, MethodHandle } from '../Platform/Platform';
|
||||||
import { getRenderTreeEditPtr, renderTreeEdit, RenderTreeEditPointer, EditType } from './RenderTreeEdit';
|
import { RenderBatch, ArraySegment, ArrayRange, RenderTreeEdit, RenderTreeFrame, EditType, FrameType, ArrayValues } from './RenderBatch/RenderBatch';
|
||||||
import { getTreeFramePtr, renderTreeFrame, FrameType, RenderTreeFramePointer } from './RenderTreeFrame';
|
|
||||||
import { platform } from '../Environment';
|
import { platform } from '../Environment';
|
||||||
import { EventDelegator } from './EventDelegator';
|
import { EventDelegator } from './EventDelegator';
|
||||||
import { EventForDotNet, UIEventArgs } from './EventForDotNet';
|
import { EventForDotNet, UIEventArgs } from './EventForDotNet';
|
||||||
|
|
@ -24,13 +23,13 @@ export class BrowserRenderer {
|
||||||
this.attachComponentToElement(componentId, toLogicalElement(element));
|
this.attachComponentToElement(componentId, toLogicalElement(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateComponent(componentId: number, edits: System_Array<RenderTreeEditPointer>, editsOffset: number, editsLength: number, referenceFrames: System_Array<RenderTreeFramePointer>) {
|
public updateComponent(batch: RenderBatch, componentId: number, edits: ArraySegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>) {
|
||||||
const element = this.childComponentLocations[componentId];
|
const element = this.childComponentLocations[componentId];
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error(`No element is currently associated with component ${componentId}`);
|
throw new Error(`No element is currently associated with component ${componentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.applyEdits(componentId, element, 0, edits, editsOffset, editsLength, referenceFrames);
|
this.applyEdits(batch, componentId, element, 0, edits, referenceFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public disposeComponent(componentId: number) {
|
public disposeComponent(componentId: number) {
|
||||||
|
|
@ -45,33 +44,41 @@ export class BrowserRenderer {
|
||||||
this.childComponentLocations[componentId] = element;
|
this.childComponentLocations[componentId] = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyEdits(componentId: number, parent: LogicalElement, childIndex: number, edits: System_Array<RenderTreeEditPointer>, editsOffset: number, editsLength: number, referenceFrames: System_Array<RenderTreeFramePointer>) {
|
private applyEdits(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, edits: ArraySegment<RenderTreeEdit>, referenceFrames: ArrayValues<RenderTreeFrame>) {
|
||||||
let currentDepth = 0;
|
let currentDepth = 0;
|
||||||
let childIndexAtCurrentDepth = childIndex;
|
let childIndexAtCurrentDepth = childIndex;
|
||||||
|
|
||||||
|
const arraySegmentReader = batch.arraySegmentReader;
|
||||||
|
const editReader = batch.editReader;
|
||||||
|
const frameReader = batch.frameReader;
|
||||||
|
const editsValues = arraySegmentReader.values(edits);
|
||||||
|
const editsOffset = arraySegmentReader.offset(edits);
|
||||||
|
const editsLength = arraySegmentReader.count(edits);
|
||||||
const maxEditIndexExcl = editsOffset + editsLength;
|
const maxEditIndexExcl = editsOffset + editsLength;
|
||||||
|
|
||||||
for (let editIndex = editsOffset; editIndex < maxEditIndexExcl; editIndex++) {
|
for (let editIndex = editsOffset; editIndex < maxEditIndexExcl; editIndex++) {
|
||||||
const edit = getRenderTreeEditPtr(edits, editIndex);
|
const edit = batch.diffReader.editsEntry(editsValues, editIndex);
|
||||||
const editType = renderTreeEdit.type(edit);
|
const editType = editReader.editType(edit);
|
||||||
switch (editType) {
|
switch (editType) {
|
||||||
case EditType.prependFrame: {
|
case EditType.prependFrame: {
|
||||||
const frameIndex = renderTreeEdit.newTreeIndex(edit);
|
const frameIndex = editReader.newTreeIndex(edit);
|
||||||
const frame = getTreeFramePtr(referenceFrames, frameIndex);
|
const frame = batch.referenceFramesEntry(referenceFrames, frameIndex);
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
this.insertFrame(componentId, parent, childIndexAtCurrentDepth + siblingIndex, referenceFrames, frame, frameIndex);
|
this.insertFrame(batch, componentId, parent, childIndexAtCurrentDepth + siblingIndex, referenceFrames, frame, frameIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.removeFrame: {
|
case EditType.removeFrame: {
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
removeLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
removeLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.setAttribute: {
|
case EditType.setAttribute: {
|
||||||
const frameIndex = renderTreeEdit.newTreeIndex(edit);
|
const frameIndex = editReader.newTreeIndex(edit);
|
||||||
const frame = getTreeFramePtr(referenceFrames, frameIndex);
|
const frame = batch.referenceFramesEntry(referenceFrames, frameIndex);
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
const element = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
const element = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
||||||
if (element instanceof HTMLElement) {
|
if (element instanceof HTMLElement) {
|
||||||
this.applyAttribute(componentId, element, frame);
|
this.applyAttribute(batch, componentId, element, frame);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Cannot set attribute on non-element child`);
|
throw new Error(`Cannot set attribute on non-element child`);
|
||||||
}
|
}
|
||||||
|
|
@ -80,12 +87,12 @@ export class BrowserRenderer {
|
||||||
case EditType.removeAttribute: {
|
case EditType.removeAttribute: {
|
||||||
// Note that we don't have to dispose the info we track about event handlers here, because the
|
// Note that we don't have to dispose the info we track about event handlers here, because the
|
||||||
// disposed event handler IDs are delivered separately (in the 'disposedEventHandlerIds' array)
|
// disposed event handler IDs are delivered separately (in the 'disposedEventHandlerIds' array)
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
const element = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
const element = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
||||||
if (element instanceof HTMLElement) {
|
if (element instanceof HTMLElement) {
|
||||||
const attributeName = renderTreeEdit.removedAttributeName(edit)!;
|
const attributeName = editReader.removedAttributeName(edit)!;
|
||||||
// First try to remove any special property we use for this attribute
|
// First try to remove any special property we use for this attribute
|
||||||
if (!this.tryApplySpecialProperty(element, attributeName, null)) {
|
if (!this.tryApplySpecialProperty(batch, element, attributeName, null)) {
|
||||||
// If that's not applicable, it's a regular DOM attribute so remove that
|
// If that's not applicable, it's a regular DOM attribute so remove that
|
||||||
element.removeAttribute(attributeName);
|
element.removeAttribute(attributeName);
|
||||||
}
|
}
|
||||||
|
|
@ -95,19 +102,19 @@ export class BrowserRenderer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.updateText: {
|
case EditType.updateText: {
|
||||||
const frameIndex = renderTreeEdit.newTreeIndex(edit);
|
const frameIndex = editReader.newTreeIndex(edit);
|
||||||
const frame = getTreeFramePtr(referenceFrames, frameIndex);
|
const frame = batch.referenceFramesEntry(referenceFrames, frameIndex);
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
const textNode = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
const textNode = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
||||||
if (textNode instanceof Text) {
|
if (textNode instanceof Text) {
|
||||||
textNode.textContent = renderTreeFrame.textContent(frame);
|
textNode.textContent = frameReader.textContent(frame);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Cannot set text content on non-text child`);
|
throw new Error(`Cannot set text content on non-text child`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.stepIn: {
|
case EditType.stepIn: {
|
||||||
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
const siblingIndex = editReader.siblingIndex(edit);
|
||||||
parent = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
parent = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex);
|
||||||
currentDepth++;
|
currentDepth++;
|
||||||
childIndexAtCurrentDepth = 0;
|
childIndexAtCurrentDepth = 0;
|
||||||
|
|
@ -127,25 +134,26 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertFrame(componentId: number, parent: LogicalElement, childIndex: number, frames: System_Array<RenderTreeFramePointer>, frame: RenderTreeFramePointer, frameIndex: number): number {
|
private insertFrame(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, frame: RenderTreeFrame, frameIndex: number): number {
|
||||||
const frameType = renderTreeFrame.frameType(frame);
|
const frameReader = batch.frameReader;
|
||||||
|
const frameType = frameReader.frameType(frame);
|
||||||
switch (frameType) {
|
switch (frameType) {
|
||||||
case FrameType.element:
|
case FrameType.element:
|
||||||
this.insertElement(componentId, parent, childIndex, frames, frame, frameIndex);
|
this.insertElement(batch, componentId, parent, childIndex, frames, frame, frameIndex);
|
||||||
return 1;
|
return 1;
|
||||||
case FrameType.text:
|
case FrameType.text:
|
||||||
this.insertText(parent, childIndex, frame);
|
this.insertText(batch, parent, childIndex, frame);
|
||||||
return 1;
|
return 1;
|
||||||
case FrameType.attribute:
|
case FrameType.attribute:
|
||||||
throw new Error('Attribute frames should only be present as leading children of element frames.');
|
throw new Error('Attribute frames should only be present as leading children of element frames.');
|
||||||
case FrameType.component:
|
case FrameType.component:
|
||||||
this.insertComponent(parent, childIndex, frame);
|
this.insertComponent(batch, parent, childIndex, frame);
|
||||||
return 1;
|
return 1;
|
||||||
case FrameType.region:
|
case FrameType.region:
|
||||||
return this.insertFrameRange(componentId, parent, childIndex, frames, frameIndex + 1, frameIndex + renderTreeFrame.subtreeLength(frame));
|
return this.insertFrameRange(batch, componentId, parent, childIndex, frames, frameIndex + 1, frameIndex + frameReader.subtreeLength(frame));
|
||||||
case FrameType.elementReferenceCapture:
|
case FrameType.elementReferenceCapture:
|
||||||
if (parent instanceof Element) {
|
if (parent instanceof Element) {
|
||||||
applyCaptureIdToElement(parent, renderTreeFrame.elementReferenceCaptureId(frame));
|
applyCaptureIdToElement(parent, frameReader.elementReferenceCaptureId(frame));
|
||||||
return 0; // A "capture" is a child in the diff, but has no node in the DOM
|
return 0; // A "capture" is a child in the diff, but has no node in the DOM
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Reference capture frames can only be children of element frames.');
|
throw new Error('Reference capture frames can only be children of element frames.');
|
||||||
|
|
@ -156,8 +164,9 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertElement(componentId: number, parent: LogicalElement, childIndex: number, frames: System_Array<RenderTreeFramePointer>, frame: RenderTreeFramePointer, frameIndex: number) {
|
private insertElement(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, frame: RenderTreeFrame, frameIndex: number) {
|
||||||
const tagName = renderTreeFrame.elementName(frame)!;
|
const frameReader = batch.frameReader;
|
||||||
|
const tagName = frameReader.elementName(frame)!;
|
||||||
const newDomElementRaw = tagName === 'svg' || isSvgElement(parent) ?
|
const newDomElementRaw = tagName === 'svg' || isSvgElement(parent) ?
|
||||||
document.createElementNS('http://www.w3.org/2000/svg', tagName) :
|
document.createElementNS('http://www.w3.org/2000/svg', tagName) :
|
||||||
document.createElement(tagName);
|
document.createElement(tagName);
|
||||||
|
|
@ -165,39 +174,40 @@ export class BrowserRenderer {
|
||||||
insertLogicalChild(newDomElementRaw, parent, childIndex);
|
insertLogicalChild(newDomElementRaw, parent, childIndex);
|
||||||
|
|
||||||
// Apply attributes
|
// Apply attributes
|
||||||
const descendantsEndIndexExcl = frameIndex + renderTreeFrame.subtreeLength(frame);
|
const descendantsEndIndexExcl = frameIndex + frameReader.subtreeLength(frame);
|
||||||
for (let descendantIndex = frameIndex + 1; descendantIndex < descendantsEndIndexExcl; descendantIndex++) {
|
for (let descendantIndex = frameIndex + 1; descendantIndex < descendantsEndIndexExcl; descendantIndex++) {
|
||||||
const descendantFrame = getTreeFramePtr(frames, descendantIndex);
|
const descendantFrame = batch.referenceFramesEntry(frames, descendantIndex);
|
||||||
if (renderTreeFrame.frameType(descendantFrame) === FrameType.attribute) {
|
if (frameReader.frameType(descendantFrame) === FrameType.attribute) {
|
||||||
this.applyAttribute(componentId, newDomElementRaw, descendantFrame);
|
this.applyAttribute(batch, componentId, newDomElementRaw, descendantFrame);
|
||||||
} else {
|
} else {
|
||||||
// As soon as we see a non-attribute child, all the subsequent child frames are
|
// As soon as we see a non-attribute child, all the subsequent child frames are
|
||||||
// not attributes, so bail out and insert the remnants recursively
|
// not attributes, so bail out and insert the remnants recursively
|
||||||
this.insertFrameRange(componentId, newElement, 0, frames, descendantIndex, descendantsEndIndexExcl);
|
this.insertFrameRange(batch, componentId, newElement, 0, frames, descendantIndex, descendantsEndIndexExcl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertComponent(parent: LogicalElement, childIndex: number, frame: RenderTreeFramePointer) {
|
private insertComponent(batch: RenderBatch, parent: LogicalElement, childIndex: number, frame: RenderTreeFrame) {
|
||||||
const containerElement = createAndInsertLogicalContainer(parent, childIndex);
|
const containerElement = createAndInsertLogicalContainer(parent, childIndex);
|
||||||
|
|
||||||
// All we have to do is associate the child component ID with its location. We don't actually
|
// All we have to do is associate the child component ID with its location. We don't actually
|
||||||
// do any rendering here, because the diff for the child will appear later in the render batch.
|
// do any rendering here, because the diff for the child will appear later in the render batch.
|
||||||
const childComponentId = renderTreeFrame.componentId(frame);
|
const childComponentId = batch.frameReader.componentId(frame);
|
||||||
this.attachComponentToElement(childComponentId, containerElement);
|
this.attachComponentToElement(childComponentId, containerElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertText(parent: LogicalElement, childIndex: number, textFrame: RenderTreeFramePointer) {
|
private insertText(batch: RenderBatch, parent: LogicalElement, childIndex: number, textFrame: RenderTreeFrame) {
|
||||||
const textContent = renderTreeFrame.textContent(textFrame)!;
|
const textContent = batch.frameReader.textContent(textFrame)!;
|
||||||
const newTextNode = document.createTextNode(textContent);
|
const newTextNode = document.createTextNode(textContent);
|
||||||
insertLogicalChild(newTextNode, parent, childIndex);
|
insertLogicalChild(newTextNode, parent, childIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyAttribute(componentId: number, toDomElement: Element, attributeFrame: RenderTreeFramePointer) {
|
private applyAttribute(batch: RenderBatch, componentId: number, toDomElement: Element, attributeFrame: RenderTreeFrame) {
|
||||||
const attributeName = renderTreeFrame.attributeName(attributeFrame)!;
|
const frameReader = batch.frameReader;
|
||||||
|
const attributeName = frameReader.attributeName(attributeFrame)!;
|
||||||
const browserRendererId = this.browserRendererId;
|
const browserRendererId = this.browserRendererId;
|
||||||
const eventHandlerId = renderTreeFrame.attributeEventHandlerId(attributeFrame);
|
const eventHandlerId = frameReader.attributeEventHandlerId(attributeFrame);
|
||||||
|
|
||||||
if (eventHandlerId) {
|
if (eventHandlerId) {
|
||||||
const firstTwoChars = attributeName.substring(0, 2);
|
const firstTwoChars = attributeName.substring(0, 2);
|
||||||
|
|
@ -210,33 +220,34 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// First see if we have special handling for this attribute
|
// First see if we have special handling for this attribute
|
||||||
if (!this.tryApplySpecialProperty(toDomElement, attributeName, attributeFrame)) {
|
if (!this.tryApplySpecialProperty(batch, toDomElement, attributeName, attributeFrame)) {
|
||||||
// If not, treat it as a regular string-valued attribute
|
// If not, treat it as a regular string-valued attribute
|
||||||
toDomElement.setAttribute(
|
toDomElement.setAttribute(
|
||||||
attributeName,
|
attributeName,
|
||||||
renderTreeFrame.attributeValue(attributeFrame)!
|
frameReader.attributeValue(attributeFrame)!
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryApplySpecialProperty(element: Element, attributeName: string, attributeFrame: RenderTreeFramePointer | null) {
|
private tryApplySpecialProperty(batch: RenderBatch, element: Element, attributeName: string, attributeFrame: RenderTreeFrame | null) {
|
||||||
switch (attributeName) {
|
switch (attributeName) {
|
||||||
case 'value':
|
case 'value':
|
||||||
return this.tryApplyValueProperty(element, attributeFrame);
|
return this.tryApplyValueProperty(batch, element, attributeFrame);
|
||||||
case 'checked':
|
case 'checked':
|
||||||
return this.tryApplyCheckedProperty(element, attributeFrame);
|
return this.tryApplyCheckedProperty(batch, element, attributeFrame);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryApplyValueProperty(element: Element, attributeFrame: RenderTreeFramePointer | null) {
|
private tryApplyValueProperty(batch: RenderBatch, element: Element, attributeFrame: RenderTreeFrame | null) {
|
||||||
// Certain elements have built-in behaviour for their 'value' property
|
// Certain elements have built-in behaviour for their 'value' property
|
||||||
|
const frameReader = batch.frameReader;
|
||||||
switch (element.tagName) {
|
switch (element.tagName) {
|
||||||
case 'INPUT':
|
case 'INPUT':
|
||||||
case 'SELECT':
|
case 'SELECT':
|
||||||
case 'TEXTAREA': {
|
case 'TEXTAREA': {
|
||||||
const value = attributeFrame ? renderTreeFrame.attributeValue(attributeFrame) : null;
|
const value = attributeFrame ? frameReader.attributeValue(attributeFrame) : null;
|
||||||
(element as any).value = value;
|
(element as any).value = value;
|
||||||
|
|
||||||
if (element.tagName === 'SELECT') {
|
if (element.tagName === 'SELECT') {
|
||||||
|
|
@ -249,7 +260,7 @@ export class BrowserRenderer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 'OPTION': {
|
case 'OPTION': {
|
||||||
const value = attributeFrame ? renderTreeFrame.attributeValue(attributeFrame) : null;
|
const value = attributeFrame ? frameReader.attributeValue(attributeFrame) : null;
|
||||||
if (value) {
|
if (value) {
|
||||||
element.setAttribute('value', value);
|
element.setAttribute('value', value);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -258,7 +269,7 @@ export class BrowserRenderer {
|
||||||
// See above for why we have this special handling for <select>/<option>
|
// See above for why we have this special handling for <select>/<option>
|
||||||
const parentElement = element.parentElement;
|
const parentElement = element.parentElement;
|
||||||
if (parentElement && (selectValuePropname in parentElement) && parentElement[selectValuePropname] === value) {
|
if (parentElement && (selectValuePropname in parentElement) && parentElement[selectValuePropname] === value) {
|
||||||
this.tryApplyValueProperty(parentElement, attributeFrame);
|
this.tryApplyValueProperty(batch, parentElement, attributeFrame);
|
||||||
delete parentElement[selectValuePropname];
|
delete parentElement[selectValuePropname];
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -268,10 +279,10 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryApplyCheckedProperty(element: Element, attributeFrame: RenderTreeFramePointer | null) {
|
private tryApplyCheckedProperty(batch: RenderBatch, element: Element, attributeFrame: RenderTreeFrame | null) {
|
||||||
// Certain elements have built-in behaviour for their 'checked' property
|
// Certain elements have built-in behaviour for their 'checked' property
|
||||||
if (element.tagName === 'INPUT') {
|
if (element.tagName === 'INPUT') {
|
||||||
const value = attributeFrame ? renderTreeFrame.attributeValue(attributeFrame) : null;
|
const value = attributeFrame ? batch.frameReader.attributeValue(attributeFrame) : null;
|
||||||
(element as any).checked = value !== null;
|
(element as any).checked = value !== null;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -279,30 +290,31 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertFrameRange(componentId: number, parent: LogicalElement, childIndex: number, frames: System_Array<RenderTreeFramePointer>, startIndex: number, endIndexExcl: number): number {
|
private insertFrameRange(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, startIndex: number, endIndexExcl: number): number {
|
||||||
const origChildIndex = childIndex;
|
const origChildIndex = childIndex;
|
||||||
for (let index = startIndex; index < endIndexExcl; index++) {
|
for (let index = startIndex; index < endIndexExcl; index++) {
|
||||||
const frame = getTreeFramePtr(frames, index);
|
const frame = batch.referenceFramesEntry(frames, index);
|
||||||
const numChildrenInserted = this.insertFrame(componentId, parent, childIndex, frames, frame, index);
|
const numChildrenInserted = this.insertFrame(batch, componentId, parent, childIndex, frames, frame, index);
|
||||||
childIndex += numChildrenInserted;
|
childIndex += numChildrenInserted;
|
||||||
|
|
||||||
// Skip over any descendants, since they are already dealt with recursively
|
// Skip over any descendants, since they are already dealt with recursively
|
||||||
index += countDescendantFrames(frame);
|
index += countDescendantFrames(batch, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (childIndex - origChildIndex); // Total number of children inserted
|
return (childIndex - origChildIndex); // Total number of children inserted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function countDescendantFrames(frame: RenderTreeFramePointer): number {
|
function countDescendantFrames(batch: RenderBatch, frame: RenderTreeFrame): number {
|
||||||
switch (renderTreeFrame.frameType(frame)) {
|
const frameReader = batch.frameReader;
|
||||||
|
switch (frameReader.frameType(frame)) {
|
||||||
// The following frame types have a subtree length. Other frames may use that memory slot
|
// The following frame types have a subtree length. Other frames may use that memory slot
|
||||||
// to mean something else, so we must not read it. We should consider having nominal subtypes
|
// to mean something else, so we must not read it. We should consider having nominal subtypes
|
||||||
// of RenderTreeFramePointer that prevent access to non-applicable fields.
|
// of RenderTreeFramePointer that prevent access to non-applicable fields.
|
||||||
case FrameType.component:
|
case FrameType.component:
|
||||||
case FrameType.element:
|
case FrameType.element:
|
||||||
case FrameType.region:
|
case FrameType.region:
|
||||||
return renderTreeFrame.subtreeLength(frame) - 1;
|
return frameReader.subtreeLength(frame) - 1;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import { Pointer, System_Array } from '../Platform/Platform';
|
|
||||||
import { platform } from '../Environment';
|
|
||||||
import { RenderTreeFramePointer } from './RenderTreeFrame';
|
|
||||||
import { RenderTreeEditPointer } from './RenderTreeEdit';
|
|
||||||
|
|
||||||
// Keep in sync with the structs in .NET code
|
|
||||||
|
|
||||||
export const renderBatch = {
|
|
||||||
updatedComponents: (obj: RenderBatchPointer) => platform.readStructField<ArrayRangePointer<RenderTreeDiffPointer>>(obj, 0),
|
|
||||||
referenceFrames: (obj: RenderBatchPointer) => platform.readStructField<ArrayRangePointer<RenderTreeFramePointer>>(obj, arrayRangeStructLength),
|
|
||||||
disposedComponentIds: (obj: RenderBatchPointer) => platform.readStructField<ArrayRangePointer<number>>(obj, arrayRangeStructLength + arrayRangeStructLength),
|
|
||||||
disposedEventHandlerIds: (obj: RenderBatchPointer) => platform.readStructField<ArrayRangePointer<number>>(obj, arrayRangeStructLength + arrayRangeStructLength + arrayRangeStructLength),
|
|
||||||
};
|
|
||||||
|
|
||||||
const arrayRangeStructLength = 8;
|
|
||||||
export const arrayRange = {
|
|
||||||
array: <T>(obj: ArrayRangePointer<T>) => platform.readObjectField<System_Array<T>>(obj, 0),
|
|
||||||
count: <T>(obj: ArrayRangePointer<T>) => platform.readInt32Field(obj, 4),
|
|
||||||
};
|
|
||||||
|
|
||||||
const arraySegmentStructLength = 12;
|
|
||||||
export const arraySegment = {
|
|
||||||
array: <T>(obj: ArraySegmentPointer<T>) => platform.readObjectField<System_Array<T>>(obj, 0),
|
|
||||||
offset: <T>(obj: ArraySegmentPointer<T>) => platform.readInt32Field(obj, 4),
|
|
||||||
count: <T>(obj: ArraySegmentPointer<T>) => platform.readInt32Field(obj, 8),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const renderTreeDiffStructLength = 4 + arraySegmentStructLength;
|
|
||||||
export const renderTreeDiff = {
|
|
||||||
componentId: (obj: RenderTreeDiffPointer) => platform.readInt32Field(obj, 0),
|
|
||||||
edits: (obj: RenderTreeDiffPointer) => platform.readStructField<ArraySegmentPointer<RenderTreeEditPointer>>(obj, 4),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nominal types to ensure only valid pointers are passed to the functions above.
|
|
||||||
// At runtime the values are just numbers.
|
|
||||||
export interface RenderBatchPointer extends Pointer { RenderBatchPointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
export interface ArrayRangePointer<T> extends Pointer { ArrayRangePointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
export interface ArraySegmentPointer<T> extends Pointer { ArraySegmentPointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
export interface RenderTreeDiffPointer extends Pointer { RenderTreeDiffPointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
export interface RenderBatch {
|
||||||
|
updatedComponents(): ArrayRange<RenderTreeDiff>;
|
||||||
|
referenceFrames(): ArrayRange<RenderTreeFrame>;
|
||||||
|
disposedComponentIds(): ArrayRange<number>;
|
||||||
|
disposedEventHandlerIds(): ArrayRange<number>;
|
||||||
|
|
||||||
|
updatedComponentsEntry(values: ArrayValues<RenderTreeDiff>, index: number): RenderTreeDiff;
|
||||||
|
referenceFramesEntry(values: ArrayValues<RenderTreeFrame>, index: number): RenderTreeFrame;
|
||||||
|
disposedComponentIdsEntry(values: ArrayValues<number>, index: number): number;
|
||||||
|
disposedEventHandlerIdsEntry(values: ArrayValues<number>, index: number): number;
|
||||||
|
|
||||||
|
diffReader: RenderTreeDiffReader;
|
||||||
|
editReader: RenderTreeEditReader;
|
||||||
|
frameReader: RenderTreeFrameReader;
|
||||||
|
arrayRangeReader: ArrayRangeReader;
|
||||||
|
arraySegmentReader: ArraySegmentReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArrayRangeReader {
|
||||||
|
count<T>(arrayRange: ArrayRange<T>): number;
|
||||||
|
values<T>(arrayRange: ArrayRange<T>): ArrayValues<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArraySegmentReader {
|
||||||
|
offset<T>(arraySegment: ArraySegment<T>): number;
|
||||||
|
count<T>(arraySegment: ArraySegment<T>): number;
|
||||||
|
values<T>(arraySegment: ArraySegment<T>): ArrayValues<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RenderTreeDiffReader {
|
||||||
|
componentId(diff: RenderTreeDiff): number;
|
||||||
|
edits(diff: RenderTreeDiff): ArraySegment<RenderTreeEdit>;
|
||||||
|
editsEntry(values: ArrayValues<RenderTreeEdit>, index: number): RenderTreeEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RenderTreeEditReader {
|
||||||
|
editType(edit: RenderTreeEdit): EditType;
|
||||||
|
siblingIndex(edit: RenderTreeEdit): number;
|
||||||
|
newTreeIndex(edit: RenderTreeEdit): number;
|
||||||
|
removedAttributeName(edit: RenderTreeEdit): string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RenderTreeFrameReader {
|
||||||
|
frameType(frame: RenderTreeFrame): FrameType;
|
||||||
|
subtreeLength(frame: RenderTreeFrame): number;
|
||||||
|
elementReferenceCaptureId(frame: RenderTreeFrame): number;
|
||||||
|
componentId(frame: RenderTreeFrame): number;
|
||||||
|
elementName(frame: RenderTreeFrame): string | null;
|
||||||
|
textContent(frame: RenderTreeFrame): string | null;
|
||||||
|
attributeName(frame: RenderTreeFrame): string | null;
|
||||||
|
attributeValue(frame: RenderTreeFrame): string | null;
|
||||||
|
attributeEventHandlerId(frame: RenderTreeFrame): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArrayRange<T> { ArrayRange__DO_NOT_IMPLEMENT: any }
|
||||||
|
export interface ArraySegment<T> { ArraySegment__DO_NOT_IMPLEMENT: any }
|
||||||
|
export interface ArrayValues<T> { ArrayValues__DO_NOT_IMPLEMENT: any }
|
||||||
|
|
||||||
|
export interface RenderTreeDiff { RenderTreeDiff__DO_NOT_IMPLEMENT: any }
|
||||||
|
export interface RenderTreeFrame { RenderTreeFrame__DO_NOT_IMPLEMENT: any }
|
||||||
|
export interface RenderTreeEdit { RenderTreeEdit__DO_NOT_IMPLEMENT: any }
|
||||||
|
|
||||||
|
export enum EditType {
|
||||||
|
// The values must be kept in sync with the .NET equivalent in RenderTreeEditType.cs
|
||||||
|
prependFrame = 1,
|
||||||
|
removeFrame = 2,
|
||||||
|
setAttribute = 3,
|
||||||
|
removeAttribute = 4,
|
||||||
|
updateText = 5,
|
||||||
|
stepIn = 6,
|
||||||
|
stepOut = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrameType {
|
||||||
|
// The values must be kept in sync with the .NET equivalent in RenderTreeFrameType.cs
|
||||||
|
element = 1,
|
||||||
|
text = 2,
|
||||||
|
attribute = 3,
|
||||||
|
component = 4,
|
||||||
|
region = 5,
|
||||||
|
elementReferenceCapture = 6,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { platform } from '../../Environment';
|
||||||
|
import { RenderBatch, ArrayRange, ArrayRangeReader, ArraySegment, RenderTreeDiff, RenderTreeEdit, RenderTreeFrame, ArrayValues, EditType, FrameType, RenderTreeFrameReader } from './RenderBatch';
|
||||||
|
import { Pointer, System_Array } from '../../Platform/Platform';
|
||||||
|
|
||||||
|
// Used when running on Mono WebAssembly for shared-memory interop. The code here encapsulates
|
||||||
|
// our knowledge of the memory layout of RenderBatch and all referenced types.
|
||||||
|
//
|
||||||
|
// In this implementation, all the DTO types are really heap pointers at runtime, hence all
|
||||||
|
// the casts to 'any' whenever we pass them to platform.read.
|
||||||
|
|
||||||
|
export class SharedMemoryRenderBatch implements RenderBatch {
|
||||||
|
constructor(private batchAddress: Pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in RenderBatch.cs
|
||||||
|
updatedComponents() { return platform.readStructField<Pointer>(this.batchAddress, 0) as any as ArrayRange<RenderTreeDiff>; }
|
||||||
|
referenceFrames() { return platform.readStructField<Pointer>(this.batchAddress, arrayRangeReader.structLength) as any as ArrayRange<RenderTreeDiff>; }
|
||||||
|
disposedComponentIds() { return platform.readStructField<Pointer>(this.batchAddress, arrayRangeReader.structLength * 2) as any as ArrayRange<number>; }
|
||||||
|
disposedEventHandlerIds() { return platform.readStructField<Pointer>(this.batchAddress, arrayRangeReader.structLength * 3) as any as ArrayRange<number>; }
|
||||||
|
|
||||||
|
updatedComponentsEntry(values: ArrayValues<RenderTreeDiff>, index: number) {
|
||||||
|
return arrayValuesEntry(values, index, diffReader.structLength);
|
||||||
|
}
|
||||||
|
referenceFramesEntry(values: ArrayValues<RenderTreeFrame>, index: number) {
|
||||||
|
return arrayValuesEntry(values, index, frameReader.structLength);
|
||||||
|
}
|
||||||
|
disposedComponentIdsEntry(values: ArrayValues<number>, index: number) {
|
||||||
|
return arrayValuesEntry(values, index, /* int length */ 4);
|
||||||
|
}
|
||||||
|
disposedEventHandlerIdsEntry(values: ArrayValues<number>, index: number) {
|
||||||
|
return arrayValuesEntry(values, index, /* int length */ 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayRangeReader = arrayRangeReader;
|
||||||
|
arraySegmentReader = arraySegmentReader;
|
||||||
|
diffReader = diffReader;
|
||||||
|
editReader = editReader;
|
||||||
|
frameReader = frameReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in ArrayRange.cs
|
||||||
|
const arrayRangeReader = {
|
||||||
|
structLength: 8,
|
||||||
|
values: <T>(arrayRange: ArrayRange<T>) => platform.readObjectField<System_Array<T>>(arrayRange as any, 0) as any as ArrayValues<T>,
|
||||||
|
count: <T>(arrayRange: ArrayRange<T>) => platform.readInt32Field(arrayRange as any, 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in ArraySegment
|
||||||
|
const arraySegmentReader = {
|
||||||
|
structLength: 12,
|
||||||
|
values: <T>(arraySegment: ArraySegment<T>) => platform.readObjectField<System_Array<T>>(arraySegment as any, 0) as any as ArrayValues<T>,
|
||||||
|
offset: <T>(arraySegment: ArraySegment<T>) => platform.readInt32Field(arraySegment as any, 4),
|
||||||
|
count: <T>(arraySegment: ArraySegment<T>) => platform.readInt32Field(arraySegment as any, 8),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in RenderTreeDiff.cs
|
||||||
|
const diffReader = {
|
||||||
|
structLength: 4 + arraySegmentReader.structLength,
|
||||||
|
componentId: (diff: RenderTreeDiff) => platform.readInt32Field(diff as any, 0),
|
||||||
|
edits: (diff: RenderTreeDiff) => platform.readStructField<Pointer>(diff as any, 4) as any as ArraySegment<RenderTreeEdit>,
|
||||||
|
editsEntry: (values: ArrayValues<RenderTreeEdit>, index: number) => arrayValuesEntry(values, index, editReader.structLength),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in RenderTreeEdit.cs
|
||||||
|
const editReader = {
|
||||||
|
structLength: 16,
|
||||||
|
editType: (edit: RenderTreeEdit) => platform.readInt32Field(edit as any, 0) as EditType,
|
||||||
|
siblingIndex: (edit: RenderTreeEdit) => platform.readInt32Field(edit as any, 4),
|
||||||
|
newTreeIndex: (edit: RenderTreeEdit) => platform.readInt32Field(edit as any, 8),
|
||||||
|
removedAttributeName: (edit: RenderTreeEdit) => platform.readStringField(edit as any, 12),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep in sync with memory layout in RenderTreeFrame.cs
|
||||||
|
const frameReader = {
|
||||||
|
structLength: 28,
|
||||||
|
frameType: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 4) as FrameType,
|
||||||
|
subtreeLength: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8),
|
||||||
|
elementReferenceCaptureId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8),
|
||||||
|
componentId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 12),
|
||||||
|
elementName: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16),
|
||||||
|
textContent: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16),
|
||||||
|
attributeName: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16),
|
||||||
|
attributeValue: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 24),
|
||||||
|
attributeEventHandlerId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8),
|
||||||
|
};
|
||||||
|
|
||||||
|
function arrayValuesEntry<T>(arrayValues: ArrayValues<T>, index: number, itemSize: number): T {
|
||||||
|
return platform.getArrayEntryPtr(arrayValues as any as System_Array<T>, index, itemSize) as any as T;
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { System_Array, Pointer } from '../Platform/Platform';
|
|
||||||
import { platform } from '../Environment';
|
|
||||||
const renderTreeEditStructLength = 16;
|
|
||||||
|
|
||||||
export function getRenderTreeEditPtr(renderTreeEdits: System_Array<RenderTreeEditPointer>, index: number) {
|
|
||||||
return platform.getArrayEntryPtr(renderTreeEdits, index, renderTreeEditStructLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const renderTreeEdit = {
|
|
||||||
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeEdit.cs
|
|
||||||
type: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 0) as EditType,
|
|
||||||
siblingIndex: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 4),
|
|
||||||
newTreeIndex: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 8),
|
|
||||||
removedAttributeName: (edit: RenderTreeEditPointer) => platform.readStringField(edit, 12),
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum EditType {
|
|
||||||
prependFrame = 1,
|
|
||||||
removeFrame = 2,
|
|
||||||
setAttribute = 3,
|
|
||||||
removeAttribute = 4,
|
|
||||||
updateText = 5,
|
|
||||||
stepIn = 6,
|
|
||||||
stepOut = 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nominal type to ensure only valid pointers are passed to the renderTreeEdit functions.
|
|
||||||
// At runtime the values are just numbers.
|
|
||||||
export interface RenderTreeEditPointer extends Pointer { RenderTreeEditPointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import { System_String, System_Array, Pointer } from '../Platform/Platform';
|
|
||||||
import { platform } from '../Environment';
|
|
||||||
const renderTreeFrameStructLength = 28;
|
|
||||||
|
|
||||||
// To minimise GC pressure, instead of instantiating a JS object to represent each tree frame,
|
|
||||||
// we work in terms of pointers to the structs on the .NET heap, and use static functions that
|
|
||||||
// know how to read property values from those structs.
|
|
||||||
|
|
||||||
export function getTreeFramePtr(renderTreeEntries: System_Array<RenderTreeFramePointer>, index: number) {
|
|
||||||
return platform.getArrayEntryPtr(renderTreeEntries, index, renderTreeFrameStructLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const renderTreeFrame = {
|
|
||||||
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeFrame.cs
|
|
||||||
frameType: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 4) as FrameType,
|
|
||||||
subtreeLength: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8) as FrameType,
|
|
||||||
elementReferenceCaptureId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8),
|
|
||||||
componentId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 12),
|
|
||||||
elementName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
|
||||||
textContent: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
|
||||||
attributeName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
|
||||||
attributeValue: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 24),
|
|
||||||
attributeEventHandlerId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8),
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum FrameType {
|
|
||||||
// The values must be kept in sync with the .NET equivalent in RenderTreeFrameType.cs
|
|
||||||
element = 1,
|
|
||||||
text = 2,
|
|
||||||
attribute = 3,
|
|
||||||
component = 4,
|
|
||||||
region = 5,
|
|
||||||
elementReferenceCapture = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nominal type to ensure only valid pointers are passed to the renderTreeFrame functions.
|
|
||||||
// At runtime the values are just numbers.
|
|
||||||
export interface RenderTreeFramePointer extends Pointer { RenderTreeFramePointer__DO_NOT_IMPLEMENT: any }
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { System_Object, System_String, System_Array, MethodHandle, Pointer } from '../Platform/Platform';
|
import { System_Object, System_String, System_Array, MethodHandle, Pointer } from '../Platform/Platform';
|
||||||
import { platform } from '../Environment';
|
import { platform } from '../Environment';
|
||||||
import { renderBatch as renderBatchStruct, arrayRange, arraySegment, renderTreeDiffStructLength, renderTreeDiff, RenderBatchPointer, RenderTreeDiffPointer } from './RenderBatch';
|
import { RenderBatch } from './RenderBatch/RenderBatch';
|
||||||
import { BrowserRenderer } from './BrowserRenderer';
|
import { BrowserRenderer } from './BrowserRenderer';
|
||||||
|
|
||||||
type BrowserRendererRegistry = { [browserRendererId: number]: BrowserRenderer };
|
type BrowserRendererRegistry = { [browserRendererId: number]: BrowserRenderer };
|
||||||
|
|
@ -20,45 +20,40 @@ export function attachRootComponentToElement(browserRendererId: number, elementS
|
||||||
browserRenderer.attachRootComponentToElement(componentId, element);
|
browserRenderer.attachRootComponentToElement(componentId, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderBatch(browserRendererId: number, batch: RenderBatchPointer) {
|
export function renderBatch(browserRendererId: number, batch: RenderBatch) {
|
||||||
const browserRenderer = browserRenderers[browserRendererId];
|
const browserRenderer = browserRenderers[browserRendererId];
|
||||||
if (!browserRenderer) {
|
if (!browserRenderer) {
|
||||||
throw new Error(`There is no browser renderer with ID ${browserRendererId}.`);
|
throw new Error(`There is no browser renderer with ID ${browserRendererId}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedComponents = renderBatchStruct.updatedComponents(batch);
|
const arrayRangeReader = batch.arrayRangeReader;
|
||||||
const updatedComponentsLength = arrayRange.count(updatedComponents);
|
const updatedComponentsRange = batch.updatedComponents();
|
||||||
const updatedComponentsArray = arrayRange.array(updatedComponents);
|
const updatedComponentsValues = arrayRangeReader.values(updatedComponentsRange);
|
||||||
const referenceFramesStruct = renderBatchStruct.referenceFrames(batch);
|
const updatedComponentsLength = arrayRangeReader.count(updatedComponentsRange);
|
||||||
const referenceFrames = arrayRange.array(referenceFramesStruct);
|
const referenceFrames = batch.referenceFrames();
|
||||||
|
const referenceFramesValues = arrayRangeReader.values(referenceFrames);
|
||||||
|
const diffReader = batch.diffReader;
|
||||||
|
|
||||||
for (let i = 0; i < updatedComponentsLength; i++) {
|
for (let i = 0; i < updatedComponentsLength; i++) {
|
||||||
const diff = platform.getArrayEntryPtr(updatedComponentsArray, i, renderTreeDiffStructLength);
|
const diff = batch.updatedComponentsEntry(updatedComponentsValues, i);
|
||||||
const componentId = renderTreeDiff.componentId(diff);
|
const componentId = diffReader.componentId(diff);
|
||||||
|
const edits = diffReader.edits(diff);
|
||||||
const editsArraySegment = renderTreeDiff.edits(diff);
|
browserRenderer.updateComponent(batch, componentId, edits, referenceFramesValues);
|
||||||
const edits = arraySegment.array(editsArraySegment);
|
|
||||||
const editsOffset = arraySegment.offset(editsArraySegment);
|
|
||||||
const editsLength = arraySegment.count(editsArraySegment);
|
|
||||||
|
|
||||||
browserRenderer.updateComponent(componentId, edits, editsOffset, editsLength, referenceFrames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const disposedComponentIds = renderBatchStruct.disposedComponentIds(batch);
|
const disposedComponentIdsRange = batch.disposedComponentIds();
|
||||||
const disposedComponentIdsLength = arrayRange.count(disposedComponentIds);
|
const disposedComponentIdsValues = arrayRangeReader.values(disposedComponentIdsRange);
|
||||||
const disposedComponentIdsArray = arrayRange.array(disposedComponentIds);
|
const disposedComponentIdsLength = arrayRangeReader.count(disposedComponentIdsRange);
|
||||||
for (let i = 0; i < disposedComponentIdsLength; i++) {
|
for (let i = 0; i < disposedComponentIdsLength; i++) {
|
||||||
const componentIdPtr = platform.getArrayEntryPtr(disposedComponentIdsArray, i, 4);
|
const componentId = batch.disposedComponentIdsEntry(disposedComponentIdsValues, i);
|
||||||
const componentId = platform.readInt32Field(componentIdPtr);
|
|
||||||
browserRenderer.disposeComponent(componentId);
|
browserRenderer.disposeComponent(componentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const disposedEventHandlerIds = renderBatchStruct.disposedEventHandlerIds(batch);
|
const disposedEventHandlerIdsRange = batch.disposedEventHandlerIds();
|
||||||
const disposedEventHandlerIdsLength = arrayRange.count(disposedEventHandlerIds);
|
const disposedEventHandlerIdsValues = arrayRangeReader.values(disposedComponentIdsRange);
|
||||||
const disposedEventHandlerIdsArray = arrayRange.array(disposedEventHandlerIds);
|
const disposedEventHandlerIdsLength = arrayRangeReader.count(disposedEventHandlerIdsRange);
|
||||||
for (let i = 0; i < disposedEventHandlerIdsLength; i++) {
|
for (let i = 0; i < disposedEventHandlerIdsLength; i++) {
|
||||||
const eventHandlerIdPtr = platform.getArrayEntryPtr(disposedEventHandlerIdsArray, i, 4);
|
const eventHandlerId = batch.disposedEventHandlerIdsEntry(disposedEventHandlerIdsValues, i);
|
||||||
const eventHandlerId = platform.readInt32Field(eventHandlerIdPtr);
|
|
||||||
browserRenderer.disposeEventHandler(eventHandlerId);
|
browserRenderer.disposeEventHandler(eventHandlerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue