From eff11e88bbca30302fc1f0504229a613f7a77be2 Mon Sep 17 00:00:00 2001 From: calixteman Date: Wed, 25 Mar 2026 10:16:42 +0100 Subject: [PATCH] Don't walk the children of a node having some attached MathML (bug 2025674) And now Chrome has a MathML Sanitizer implementation (146), the tests related to MathML are updated to reflect that. --- test/integration/accessibility_spec.mjs | 164 ++++++++++++------------ test/pdfs/.gitignore | 1 + test/pdfs/bug2025674.pdf | Bin 0 -> 25998 bytes web/struct_tree_layer_builder.js | 23 +++- 4 files changed, 97 insertions(+), 91 deletions(-) create mode 100644 test/pdfs/bug2025674.pdf diff --git a/test/integration/accessibility_spec.mjs b/test/integration/accessibility_spec.mjs index 8d78d7fb6..330b1d434 100644 --- a/test/integration/accessibility_spec.mjs +++ b/test/integration/accessibility_spec.mjs @@ -320,37 +320,22 @@ describe("accessibility", () => { it("must check that the MathML is correctly inserted", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - const isSanitizerSupported = await page.evaluate(() => { - try { - // eslint-disable-next-line no-undef - return typeof Sanitizer !== "undefined"; - } catch { - return false; - } - }); - if (isSanitizerSupported) { - const mathML = await page.$eval( - "span.structTree span[aria-owns='p58R_mc13'] > math", - el => el?.innerHTML ?? "" + const mathML = await page.$eval( + "span.structTree span[aria-owns='p58R_mc13'] > math", + el => el?.innerHTML ?? "" + ); + expect(mathML) + .withContext(`In ${browserName}`) + .toEqual( + ` x2 = |x| ` ); - expect(mathML) - .withContext(`In ${browserName}`) - .toEqual( - ` x2 = |x| ` - ); - // Check that the math corresponding element is hidden in the text - // layer. - const ariaHidden = await page.$eval("span#p58R_mc13", el => - el.getAttribute("aria-hidden") - ); - expect(ariaHidden).withContext(`In ${browserName}`).toEqual("true"); - } else { - // eslint-disable-next-line no-console - console.log( - `Pending in Chrome: Sanitizer API (in ${browserName}) is not supported` - ); - } + // Check that the math corresponding element is hidden in the text + // layer. + const ariaHidden = await page.$eval("span#p58R_mc13", el => + el.getAttribute("aria-hidden") + ); + expect(ariaHidden).withContext(`In ${browserName}`).toEqual("true"); }) ); }); @@ -370,30 +355,15 @@ describe("accessibility", () => { it("must check that the MathML is correctly inserted", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - const isSanitizerSupported = await page.evaluate(() => { - try { - // eslint-disable-next-line no-undef - return typeof Sanitizer !== "undefined"; - } catch { - return false; - } - }); - if (isSanitizerSupported) { - const mathML = await page.$eval( - "span.structTree span[aria-owns='p21R_mc64']", - el => el?.innerHTML ?? "" + const mathML = await page.$eval( + "span.structTree span[aria-owns='p21R_mc64']", + el => el?.innerHTML ?? "" + ); + expect(mathML) + .withContext(`In ${browserName}`) + .toEqual( + ' 𝑛 𝑝 = 𝑛 mod 𝑝 ' ); - expect(mathML) - .withContext(`In ${browserName}`) - .toEqual( - ' 𝑛 𝑝 = 𝑛 mod 𝑝 ' - ); - } else { - // eslint-disable-next-line no-console - console.log( - `Pending in Chrome: Sanitizer API (in ${browserName}) is not supported` - ); - } }) ); }); @@ -475,25 +445,11 @@ describe("accessibility", () => { it("must check that there's no alt-text on the MathML node", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - const isSanitizerSupported = await page.evaluate(() => { - try { - // eslint-disable-next-line no-undef - return typeof Sanitizer !== "undefined"; - } catch { - return false; - } - }); const ariaLabel = await page.$eval( "span[aria-owns='p3R_mc2']", el => el.getAttribute("aria-label") || "" ); - if (isSanitizerSupported) { - expect(ariaLabel).withContext(`In ${browserName}`).toEqual(""); - } else { - expect(ariaLabel) - .withContext(`In ${browserName}`) - .toEqual("cube root of , x plus y end cube root "); - } + expect(ariaLabel).withContext(`In ${browserName}`).toEqual(""); }) ); }); @@ -513,14 +469,6 @@ describe("accessibility", () => { it("must check that the text in text layer is aria-hidden", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - const isSanitizerSupported = await page.evaluate(() => { - try { - // eslint-disable-next-line no-undef - return typeof Sanitizer !== "undefined"; - } catch { - return false; - } - }); const ariaHidden = await page.evaluate(() => Array.from( document.querySelectorAll(".structTree :has(> math)") @@ -530,16 +478,64 @@ describe("accessibility", () => { .getAttribute("aria-hidden") ) ); - if (isSanitizerSupported) { - expect(ariaHidden) - .withContext(`In ${browserName}`) - .toEqual(["true", "true", "true"]); - } else { - // eslint-disable-next-line no-console - console.log( - `Pending in Chrome: Sanitizer API (in ${browserName}) is not supported` + expect(ariaHidden) + .withContext(`In ${browserName}`) + .toEqual(["true", "true", "true"]); + }) + ); + }); + }); + + describe("MathML in AF entry with struct tree children must not be duplicated", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("bug2025674.pdf", ".textLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the MathML is not duplicated in the struct tree", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + // The Formula node has both AF MathML and struct tree children. + // When AF MathML is present, children must not be walked to avoid + // rendering the math content twice in the accessibility tree. + const mathCount = await page.evaluate( + () => document.querySelectorAll(".structTree math").length + ); + expect(mathCount).withContext(`In ${browserName}`).toBe(1); + + // All text layer elements referenced by the formula subtree must + // be aria-hidden so screen readers don't read both the MathML and + // the underlying text content. + const allHidden = await page.evaluate(() => { + const ids = []; + for (const el of document.querySelectorAll( + ".structTree [aria-owns]" + )) { + if (el.closest("math")) { + ids.push(el.getAttribute("aria-owns")); + } + } + // Also collect ids from the formula span itself. + for (const el of document.querySelectorAll( + ".structTree span:has(> math)" + )) { + const owned = el.getAttribute("aria-owns"); + if (owned) { + ids.push(owned); + } + } + return ids.every( + id => + document.getElementById(id)?.getAttribute("aria-hidden") === + "true" ); - } + }); + expect(allHidden).withContext(`In ${browserName}`).toBeTrue(); }) ); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index e6936d0f7..7d628caf2 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -892,3 +892,4 @@ !issue20930.pdf !text_rise_eol_bug.pdf !hello_world_rotated.pdf +!bug2025674.pdf diff --git a/test/pdfs/bug2025674.pdf b/test/pdfs/bug2025674.pdf new file mode 100644 index 0000000000000000000000000000000000000000..24a07b32c9c4d911756af5e7f27b0a9fc14028bd GIT binary patch literal 25998 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