Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/core/inlines.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ function inlineXrefMatches(matched, text) {
// slices "{{" at the beginning and "}}" at the end
const ref = norm(matched.slice(2, -2));
if (ref.startsWith("\\")) {
return matched.replace("\\", "");
// Remove the escape backslash that immediately follows "{{" (with optional
// whitespace), using an anchored regex to avoid removing unintended backslashes.
return matched.replace(/^(\{\{\s*)\\/, "$1");
}

const node = idlStringToHtml(ref);
Expand Down
13 changes: 12 additions & 1 deletion tests/spec/core/highlight.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@
const langURL = new URL("../../support-files/hljs-testlang.js", window.location).href;
const propName = "testLang";
const lang = "testlang";
worker.postMessage({ action, langURL, propName, lang });
// Fetch the language script in the main thread so it can be sent as
// content to the worker. importScripts() from blob workers is blocked
// by browsers for cross-origin URLs (blob worker origin is "null").
let langScript;
try {
const response = await fetch(langURL);
if (response.ok) langScript = await response.text();
} catch {
// If the fetch fails, the language will not be registered and
// highlighting will be skipped (see respec-worker.js).
}
worker.postMessage({ action, langScript, propName, lang });
return new Promise(resolve => {
worker.addEventListener("message", function listener({ data }) {
const { action: responseAction, lang: responseLang } = data;
Expand Down
16 changes: 16 additions & 0 deletions tests/spec/core/inlines-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,20 @@ describe("Core - Inlines", () => {
expect(event.textContent).toBe("event");
expect(noRef.textContent).toBe(`"no-referrer"`);
});

it("renders escaped {{ \\IDL }} references as plain text without a link", async () => {
const body = `
<section>
<p id="no-space">{{\\Window}}</p>
<p id="with-space">{{ \\Window }}</p>
</section>
`;
const doc = await makeRSDoc(makeStandardOps(null, body));
const noSpace = doc.getElementById("no-space");
expect(noSpace.querySelector("a")).toBeNull();
expect(noSpace.textContent.trim()).toBe("{{Window}}");
const withSpace = doc.getElementById("with-space");
expect(withSpace.querySelector("a")).toBeNull();
expect(withSpace.textContent.trim()).toBe("{{ Window }}");
});
});
22 changes: 19 additions & 3 deletions worker/respec-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,25 @@ self.addEventListener("message", ({ data: originalData }) => {
const data = Object.assign({}, originalData);
switch (data.action) {
case "highlight-load-lang": {
const { langURL, propName, lang } = data;
importScripts(langURL);
self.hljs.registerLanguage(lang, self[propName]);
const { langScript, propName, lang } = data;
try {
if (!langScript) {
throw new Error(`No script content provided for language "${lang}"`);
}
// importScripts() from blob workers is blocked for cross-origin URLs
// (blob worker origin is "null"). Create a same-origin blob URL from
// the pre-fetched script content to load the language safely.
const blob = new Blob([langScript], { type: "application/javascript" });
const objectURL = URL.createObjectURL(blob);
try {
importScripts(objectURL);
} finally {
URL.revokeObjectURL(objectURL);
}
self.hljs.registerLanguage(lang, self[propName]);
} catch (err) {
console.error("Failed to load or register language", lang, err);
}
break;
}
case "highlight": {
Expand Down
Loading