<!
DOCTYPE HTML>
<
html>
<
head>
<
meta charset=
"utf-8">
<
title>Test that system
font sizing is independent from ui.textScaleFactor</
title>
<
script src=
"/tests/SimpleTest/SimpleTest.js"></
script>
<
link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css"/>
<
style>
p { width: max-content }
#
menu {
font:
menu }
</
style>
</
head>
<
body>
<p id=
"menu">
"menu" text.</p>
<p id=
"default">Default text.</p>
</
body>
<
script>
"use strict";
const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
// Returns a Number for the
font size in CSS pixels.
function elementFontSize(element) {
return parseFloat(getComputedStyle(element).getPropertyValue(
"font-size"));
}
//
"look-and-feel-changed" may be dispatched twice: once for the pref
// change and once after receiving the new values from
// ContentChild::RecvThemeChanged().
// pushPrefEnv() resolves after the former. This resolves after the latter.
function promiseNewFontSizeOnThemeChange(element) {
return new Promise(resolve => {
const lastSize = elementFontSize(element);
function ThemeChanged() {
const size = elementFontSize(element);
if (size != lastSize) {
resolve(size);
}
}
//
"look-and-feel-changed" is dispatched before the
style system is flushed,
//
https://searchfox.org/mozilla-central/rev/380fc5571b039fd453b45bbb64ed13146fe9b066/layout/base/nsPresContext.cpp#1684,1703-1705
// so use an async observer to get a notification after
style changes.
SpecialPowers.addAsyncObserver(ThemeChanged,
"look-and-feel-changed");
SimpleTest.registerCleanupFunction(function() {
SpecialPowers.removeAsyncObserver(ThemeChanged,
"look-and-feel-changed");
});
});
}
function fuzzyCompareLength(actual, expected, message, tolerance, expectFn) {
expectFn(Math.abs(actual-expected) <= tolerance,
`${message} - got ${actual}, expected ${expected} +/- ${tolerance}`);
}
add_task(async () => {
// MOZ_HEADLESS is set in content processes with GTK regardless of the
// headless state of the parent process. Check the parent state.
const headless = await SpecialPowers.spawnChrome([], function get_headless() {
return Services.env.get(
"MOZ_HEADLESS");
});
// LookAndFeel::TextScaleFactor::FloatID is implemented only for WINNT and
// GTK. ui.textScaleFactor happens to scale CSS pixels on other platforms
// but system
font integration has not been implemented.
const expectSuccess = AppConstants.MOZ_WIDGET_TOOLKIT ==
"windows" ||
(AppConstants.MOZ_WIDGET_TOOLKIT ==
"gtk" &&
// Headless GTK doesn
't get system font sizes from the system, but
// uses sizes fixed in CSS pixels.
!headless);
async function setScaleAndPromiseFontSize(scale, element) {
const prefPromise = SpecialPowers.pushPrefEnv({
set: [[
"ui.textScaleFactor", scale]],
});
if (!expectSuccess) {
// The size is not expected to change but get it afresh to check our
// assumption.
await prefPromise;
return elementFontSize(element);
}
const [size] = await Promise.all([
promiseNewFontSizeOnThemeChange(element),
prefPromise,
]);
return size;
}
const
menu = document.getElementById(
"menu");
const def = document.getElementById(
"default");
// Choose a scaleFactor value different enough from possible default values
// that app unit rounding does not prevent a change in devicePixelRatio.
// A scaleFactor of 120 also has no rounding of app units per dev pixel.
const referenceScale = 120;
const menuSize1 = await setScaleAndPromiseFontSize(referenceScale,
menu);
const menuRect1 =
menu.getBoundingClientRect();
const defSize1 = elementFontSize(def);
const defRect1 = def.getBoundingClientRect();
const expectFn = expectSuccess ? ok : todo;
const menuSize2 = await setScaleAndPromiseFontSize(2*referenceScale,
menu);
{
const singlePrecisionULP = Math.pow(2, -23);
// Precision seems to be lost in the conversion to decimal string for the
// property value.
const reltolerance = 30 * singlePrecisionULP;
fuzzyCompareLength(menuSize2, menuSize1/2,
"size of menu font",
reltolerance*menuSize1/2, expectFn);
}
{
const menuRect2 =
menu.getBoundingClientRect();
// The
menu font text renders exactly the same and app-unit rects are
// equal, but the DOMRect conversion is rounded to 1/65536 CSS pixels.
//
https://searchfox.org/mozilla-central/rev/380fc5571b039fd453b45bbb64ed13146fe9b066/dom/base/DOMRect.cpp#151-159
// See also
https://bugzilla.mozilla.org/show_bug.cgi?id=1640441#c28
//
// Increased tolerance in
https://bugzilla.mozilla.org/show_bug.cgi?id=1920733
// as the change to rendering mode seems to have perturbed results a bit, though
// it
's still "basically the same".
const absTolerance = 0.05;
fuzzyCompareLength(menuRect2.width, menuRect1.width/2,
"width of menu font in px"
, absTolerance, expectFn);
fuzzyCompareLength(menuRect2.height, menuRect1.height/2,
"height of menu font in px"
, absTolerance, expectFn);
}
const defSize2 = elementFontSize(def);
is(defSize2, defSize1,
"size of default font");
{
const defRect2 = def.getBoundingClientRect();
// Wider tolerance for hinting and snapping
const relTolerance = 1/12;
fuzzyCompareLength(defRect2.width, defRect1.width,
"width of default font in px"
,
relTolerance*defRect1.width, ok);
fuzzyCompareLength(defRect2.height, defRect1.height,
"height of default font in px"
,
relTolerance*defRect1.height, ok);
}
});
</
script>
</
html>