Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* Any copyright is dedicated to the Public Domain.
*/
"use strict";
/* Verifies that the canvas text recipes (canvasdata9/10) render with the
* bundled font rather than the platform fallback font. Regression test:
* the bundled FontFace used to be registered only in usercharacteristics.html
* while the recipes render in an arbitrary content window via the
* UserCharacteristicsCanvasRendering actor, so the text silently fell back
* to the platform default serif (and the bundled woff only covered 2 glyphs
* anyway). See the field analysis: every dominant canvas9/10 hash had
* default-serif text metrics.
*/
const ACTOR_NAME = "UserCharacteristicsCanvasRendering";
function registerActor() {
ChromeUtils.registerWindowActor(ACTOR_NAME, {
parent: {
esModuleURI: "resource://gre/actors/UserCharacteristicsParent.sys.mjs",
},
child: {
esModuleURI:
"resource://gre/actors/UserCharacteristicsCanvasRenderingChild.sys.mjs",
},
safeForUntrustedWebProcess: true,
});
}
add_task(async function test_canvas_text_uses_bundled_font() {
registerActor();
registerCleanupFunction(() => {
ChromeUtils.unregisterWindowActor(ACTOR_NAME);
});
const actor =
browser.browsingContext.currentWindowGlobal.getActor(ACTOR_NAME);
const data = await actor.sendQuery("CanvasRendering:Render", {
hwRenderingExpected: false,
});
const errors = data.get("errors");
Assert.ok(
!errors.some(e => e.name == "bundledFont"),
`bundled font loaded without error (errors: ${JSON.stringify(errors)})`
);
const renderings = data.get("renderings");
const actorHash = renderings.get("canvasdata9Software");
Assert.ok(actorHash, "canvasdata9Software was rendered");
// Render the same recipe in the same document with the platform fallback
// (serif) and with an unknown family (what the actor's render used to
// resolve to before the fix). Neither may match the actor's hash, and
// the bundled font must no longer be observable in the page.
//
// The rendering runs via content.eval so it executes in the content
// scope: reading the getImageData TypedArray to compute its SHA-1 is
// forbidden across the SpecialPowers sandbox's Xray wrappers.
const renderInContent = async () => {
// Keep in sync with the canvasdata9 recipe in
// toolkit/actors/UserCharacteristicsCanvasRenderingChild.sys.mjs
async function renderHash(family) {
const canvas = document.createElement("canvas");
canvas.width = 250;
canvas.height = 250;
const ctx = canvas.getContext("2d", {
forceSoftwareRendering: true,
});
ctx.fillStyle = "green";
ctx.font = `italic 30px ${family}`;
ctx.fillText("The quick brown", 15, 100);
ctx.fillText("fox jumps over", 15, 150);
ctx.fillText("the lazy dog", 15, 200);
const imageData = ctx.getImageData(0, 0, 250, 250);
const digest = await crypto.subtle.digest("SHA-1", imageData.data);
return Array.from(new Uint8Array(digest))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
return {
serifHash: await renderHash("serif"),
unknownFamilyHash: await renderHash("LocalFiraSans"),
fontVisibleToPage: [...document.fonts].some(f =>
f.family.includes("LocalFiraSans")
),
};
};
const content = await SpecialPowers.spawn(
browser,
[renderInContent.toString()],
fnStr => content.eval(`(${fnStr})()`)
);
Assert.notEqual(
actorHash,
content.serifHash,
"actor's canvasdata9Software is not the serif fallback rendering"
);
Assert.notEqual(
actorHash,
content.unknownFamilyHash,
"actor's canvasdata9Software is not an unknown-family fallback rendering"
);
Assert.ok(
!content.fontVisibleToPage,
"the bundled font is removed from the page's document.fonts afterwards"
);
});
});