Table of Contents

Class UITKHtmlContentRenderer

Namespace
FishMMO.Client
Assembly
FishMMO.Client.dll

Renders a parsed launcher news fragment into a tree of UnityEngine.UIElements.VisualElements.

public static class UITKHtmlContentRenderer
Inheritance
UITKHtmlContentRenderer
Inherited Members

Remarks

Builds elements rather than a rich-text string. UI Toolkit's rich text has no equivalent of a link tag, so a news link cannot be a span of styled text — it has to be an element that can receive a click, which means the tree has to be walked rather than flattened.

Correction to a previous remark. This class used to claim that "text assigned to a UnityEngine.UIElements.Label is never parsed as markup", and used that as the reason building elements removed the escaping problem. That is false, and it was load-bearing. UnityEngine.UIElements.TextElement.enableRichText defaults to true on every UnityEngine.UIElements.Label, so assigning remote text to one hands it straight to Unity's rich-text parser. Worse, HtmlDecode(string) below turns an escaped &lt;color=#0f0&gt; — which a feed author reasonably believes is inert — back into a live tag. A compromised or merely sloppy news feed could therefore forge authoritative-looking launcher chrome ("Verified by FishMMO — enter your key at…") or <size=2000> the pane into uselessness.

Every label produced here now goes through RemoteText, which turns rich text off and strips control characters. Building elements is still the right shape — it is what makes links possible — but it is not what makes the content safe.

Bounded and incremental. The document is fetched from a URL this client does not control, so its size, breadth and depth are all attacker-chosen. Rendering used to be one synchronous recursive pass over the whole thing on the main thread: a large feed froze the launcher, and a hostile one could keep it frozen. Traversal is now an explicit work stack driven by RenderIncremental(HtmlNode, VisualElement, Action<string>), which yields every FishMMO.Client.UITKHtmlContentRenderer.NodesPerFrame nodes, and it stops dead on every budget rather than merely declining to add more elements.

Methods

Render(HtmlNode, VisualElement, Action<string>)

Clears container and rebuilds it from root, synchronously.

public static void Render(HtmlNode root, VisualElement container, Action<string> onLinkActivated)

Parameters

root HtmlNode

The extracted news fragment. Null renders an empty pane.

container VisualElement

The element to populate.

onLinkActivated Action<string>

Invoked with the raw href when a link is clicked. Callers must route this through LauncherLinkPolicy — this renderer deliberately does not open URLs itself, so the allowlist stays in one place.

Remarks

Retained for callers with no coroutine host and for small, locally-authored trees. Anything rendering a fetched document should prefer RenderIncremental(HtmlNode, VisualElement, Action<string>) — the budgets are identical, but this form spends the whole of them inside one frame.

RenderIncremental(HtmlNode, VisualElement, Action<string>)

Clears container and rebuilds it from root, spreading the work across frames.

public static IEnumerator RenderIncremental(HtmlNode root, VisualElement container, Action<string> onLinkActivated)

Parameters

root HtmlNode

The extracted news fragment. Null renders an empty pane.

container VisualElement

The element to populate.

onLinkActivated Action<string>

Invoked with the raw href when a link is clicked.

Returns

IEnumerator

Remarks

Drive this with StartCoroutine. It yields null (one frame) every FishMMO.Client.UITKHtmlContentRenderer.NodesPerFrame nodes, so a document large enough to be a problem becomes a pane that fills in progressively instead of a launcher that stops responding.