// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Blazor.Components; using System; using System.Runtime.InteropServices; namespace Microsoft.AspNetCore.Blazor.RenderTree { /// /// Represents an entry in a tree of user interface (UI) items. /// [StructLayout(LayoutKind.Explicit)] public readonly struct RenderTreeFrame { // Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms, // which means that all reference-type fields need to take up 8 bytes (except for the last // one, which will be sized as either 4 or 8 bytes depending on the runtime platform). // This is not optimal for the Mono-WebAssembly case because that's always 32-bit so the // reference-type fields could be reduced to 4 bytes each. We could use ifdefs to have // different fields offsets for the 32 and 64 bit compile targets, but then we'd have the // complexity of needing different binaries when loaded into Mono-WASM vs desktop. // Eventually we might stop using this shared memory interop altogether (and would have to // if running as a web worker) so for now to keep things simple, treat reference types as // 8 bytes here. // -------------------------------------------------------------------------------- // Common // -------------------------------------------------------------------------------- /// /// Gets the sequence number of the frame. Sequence numbers indicate the relative source /// positions of the instructions that inserted the frames. Sequence numbers are only /// comparable within the same sequence (typically, the same source method). /// [FieldOffset(0)] public readonly int Sequence; /// /// Describes the type of this frame. /// [FieldOffset(4)] public readonly RenderTreeFrameType FrameType; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Element // -------------------------------------------------------------------------------- /// /// If the property equals , /// gets the index of the final descendant frame in the tree. The value is /// zero if the frame has not yet been closed. /// [FieldOffset(8)] public readonly int ElementDescendantsEndIndex; /// /// If the property equals , /// gets a name representing the type of the element. Otherwise, the value is . /// [FieldOffset(16)] public readonly string ElementName; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Text // -------------------------------------------------------------------------------- /// /// If the property equals , /// gets the content of the text frame. Otherwise, the value is . /// [FieldOffset(16)] public readonly string TextContent; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Attribute // -------------------------------------------------------------------------------- /// /// If the property equals , /// gets the attribute name. Otherwise, the value is . /// [FieldOffset(16)] public readonly string AttributeName; /// /// If the property equals , /// gets the attribute value. Otherwise, the value is . /// [FieldOffset(24)] public readonly object AttributeValue; // -------------------------------------------------------------------------------- // RenderTreeFrameType.Component // -------------------------------------------------------------------------------- /// /// If the property equals , /// gets the index of the final descendant frame in the tree. The value is /// zero if the frame has not yet been closed. /// [FieldOffset(8)] public readonly int ComponentDescendantsEndIndex; /// /// If the property equals , /// gets the child component instance identifier. /// [FieldOffset(12)] public readonly int ComponentId; /// /// If the property equals , /// gets the type of the child component. /// [FieldOffset(16)] public readonly Type ComponentType; /// /// If the property equals , /// gets the child component instance. Otherwise, the value is . /// [FieldOffset(24)] public readonly IComponent Component; private RenderTreeFrame(int sequence, string elementName, int descendantsEndIndex) : this() { FrameType = RenderTreeFrameType.Element; Sequence = sequence; ElementName = elementName; ElementDescendantsEndIndex = descendantsEndIndex; } private RenderTreeFrame(int sequence, Type componentType, int descendantsEndIndex) : this() { FrameType = RenderTreeFrameType.Component; Sequence = sequence; ComponentType = componentType; ComponentDescendantsEndIndex = descendantsEndIndex; } private RenderTreeFrame(int sequence, Type componentType, int descendantsEndIndex, int componentId, IComponent component) : this(sequence, componentType, descendantsEndIndex) { ComponentId = componentId; Component = component; } private RenderTreeFrame(int sequence, string textContent) : this() { FrameType = RenderTreeFrameType.Text; Sequence = sequence; TextContent = textContent; } private RenderTreeFrame(int sequence, string attributeName, object attributeValue) : this() { FrameType = RenderTreeFrameType.Attribute; Sequence = sequence; AttributeName = attributeName; AttributeValue = attributeValue; } internal static RenderTreeFrame Element(int sequence, string elementName) => new RenderTreeFrame(sequence, elementName: elementName, descendantsEndIndex: 0); internal static RenderTreeFrame Text(int sequence, string textContent) => new RenderTreeFrame(sequence, textContent: textContent); internal static RenderTreeFrame Attribute(int sequence, string name, UIEventHandler value) => new RenderTreeFrame(sequence, attributeName: name, attributeValue: value); internal static RenderTreeFrame Attribute(int sequence, string name, object value) => new RenderTreeFrame(sequence, attributeName: name, attributeValue: value); internal static RenderTreeFrame ChildComponent(int sequence) where T : IComponent => new RenderTreeFrame(sequence, typeof(T), 0); internal RenderTreeFrame WithElementDescendantsEndIndex(int descendantsEndIndex) => new RenderTreeFrame(Sequence, elementName: ElementName, descendantsEndIndex: descendantsEndIndex); internal RenderTreeFrame WithComponentDescendantsEndIndex(int descendantsEndIndex) => new RenderTreeFrame(Sequence, componentType: ComponentType, descendantsEndIndex: descendantsEndIndex); internal RenderTreeFrame WithAttributeSequence(int sequence) => new RenderTreeFrame(sequence, attributeName: AttributeName, attributeValue: AttributeValue); internal RenderTreeFrame WithComponentInstance(int componentId, IComponent component) => new RenderTreeFrame(Sequence, ComponentType, ComponentDescendantsEndIndex, componentId, component); } }