1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
<!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 <p> in px", absTolerance, expectFn);
fuzzyCompareLength(menuRect2.height, menuRect1.height/2,
"height of menu font <p> 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 <p> in px",
relTolerance*defRect1.width, ok);
fuzzyCompareLength(defRect2.height, defRect1.height,
"height of default font <p> in px",
relTolerance*defRect1.height, ok);
}
});
</script>
</html>
|