Class UITKHtmlContentRenderer
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
<color=#0f0> — 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
rootHtmlNodeThe extracted news fragment. Null renders an empty pane.
containerVisualElementThe element to populate.
onLinkActivatedAction<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
rootHtmlNodeThe extracted news fragment. Null renders an empty pane.
containerVisualElementThe element to populate.
onLinkActivatedAction<string>Invoked with the raw href when a link is clicked.
Returns
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.