diff --git a/crosswalk/docs/index.html b/crosswalk/docs/index.html index 937958b..74cadcf 100644 --- a/crosswalk/docs/index.html +++ b/crosswalk/docs/index.html @@ -1497,6 +1497,14 @@ return div.innerHTML; } + // Only a real DOM node may be appended. Anything else — most importantly a + // string that reached here from the URL — becomes a text node, so it is + // rendered as text and can never be parsed as markup. + function toNode(value) { + if (value && typeof value.nodeType === 'number') return value; + return document.createTextNode(String(value)); + } + function el(tag, attrs, children) { var node = document.createElement(tag); if (attrs) { @@ -1509,8 +1517,8 @@ } if (children) { if (typeof children === 'string') node.textContent = children; - else if (Array.isArray(children)) children.forEach(function(c) { if (c) node.appendChild(c); }); - else node.appendChild(children); + else if (Array.isArray(children)) children.forEach(function(c) { if (c) node.appendChild(toNode(c)); }); + else node.appendChild(toNode(children)); } return node; } @@ -1634,6 +1642,30 @@ '/review': renderReview }; + // Resolve a control id supplied in the URL to the canonical string held in + // our own data, searching the same three places renderControlDetail reads: + // the framework registry, the backlink index, then the mappings in DATA. + // Returns null when nothing matches. Callers render the returned value + // rather than the URL, so nothing user-supplied is ever put on the page — + // a control id that matches nothing could only have produced an empty page + // echoing the URL back anyway. + function resolveControlId(fwName, controlId) { + var regFw = FW_REGISTRY_MAP[fwName]; + if (regFw) { + var regControl = (regFw.controls || []).find(function(c) { return c.control_id === controlId; }); + if (regControl) return regControl.control_id; + } + var bl = BACKLINK_MAP[fwName + '::' + controlId]; + if (bl) return bl.control_id; + var found = null; + DATA.forEach(function(e) { + (e.mappings || []).forEach(function(m) { + if (found === null && m.framework === fwName && m.control_id === controlId) found = m.control_id; + }); + }); + return found; + } + function getRoute() { var hash = window.location.hash || '#/'; // Extract the path part (before any query params) @@ -1669,9 +1701,20 @@ if (fwControlMatch) { var fwNameCtrl = decodeURIComponent(fwControlMatch[1]); var ctrlId = decodeURIComponent(fwControlMatch[2]); - renderControlDetail(app, fwNameCtrl, ctrlId); - window.scrollTo(0, 0); - return; + // Resolve both halves of the deep link against our own data and render + // the resolved values, never the URL text. The framework name gets the + // same allow-list the plain framework route uses below; an unknown + // framework or control falls through to the frameworks index. + var fwIdx = FRAMEWORKS.indexOf(fwNameCtrl); + if (fwIdx !== -1) { + var knownFw = FRAMEWORKS[fwIdx]; + var knownCtrl = resolveControlId(knownFw, ctrlId); + if (knownCtrl !== null) { + renderControlDetail(app, knownFw, knownCtrl); + window.scrollTo(0, 0); + return; + } + } } // Framework detail is a full page, not a modal if (frameworkMatch) {