5 <title>Test that system font sizing is independent from ui.textScaleFactor
</title>
6 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
7 <link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css"/>
9 p { width: max-content }
14 <p id=
"menu">"menu" text.
</p>
15 <p id=
"default">Default text.
</p>
20 const { AppConstants
} = SpecialPowers
.ChromeUtils
.import(
21 "resource://gre/modules/AppConstants.jsm"
24 // Returns a Number for the font size in CSS pixels.
25 function elementFontSize(element
) {
26 return parseFloat(getComputedStyle(element
).getPropertyValue("font-size"));
29 // "look-and-feel-changed" may be dispatched twice: once for the pref
30 // change and once after receiving the new values from
31 // ContentChild::RecvThemeChanged().
32 // pushPrefEnv() resolves after the former. This resolves after the latter.
33 function promiseNewFontSizeOnThemeChange(element
) {
34 return new Promise(resolve
=> {
35 const lastSize
= elementFontSize(element
);
37 function ThemeChanged() {
38 const size
= elementFontSize(element
);
39 if (size
!= lastSize
) {
43 // "look-and-feel-changed" is dispatched before the style system is flushed,
44 // https://searchfox.org/mozilla-central/rev/380fc5571b039fd453b45bbb64ed13146fe9b066/layout/base/nsPresContext.cpp#1684,1703-1705
45 // so use an async observer to get a notification after style changes.
46 SpecialPowers
.addAsyncObserver(ThemeChanged
, "look-and-feel-changed");
47 SimpleTest
.registerCleanupFunction(function() {
48 SpecialPowers
.removeAsyncObserver(ThemeChanged
, "look-and-feel-changed");
53 function fuzzyCompareLength(actual
, expected
, message
, tolerance
, expectFn
) {
54 expectFn(Math
.abs(actual
-expected
) <= tolerance
,
55 `${message} - got ${actual}, expected ${expected} +/- ${tolerance}`);
58 add_task(async () => {
59 // MOZ_HEADLESS is set in content processes with GTK regardless of the
60 // headless state of the parent process. Check the parent state.
61 const headless
= await SpecialPowers
.spawnChrome([], function get_headless() {
62 return Services
.env
.get("MOZ_HEADLESS");
64 // LookAndFeel::TextScaleFactor::FloatID is implemented only for WINNT and
65 // GTK. ui.textScaleFactor happens to scale CSS pixels on other platforms
66 // but system font integration has not been implemented.
67 const expectSuccess
= AppConstants
.MOZ_WIDGET_TOOLKIT
== "windows" ||
68 (AppConstants
.MOZ_WIDGET_TOOLKIT
== "gtk" &&
69 // Headless GTK doesn't get system font sizes from the system, but
70 // uses sizes fixed in CSS pixels.
73 async
function setScaleAndPromiseFontSize(scale
, element
) {
74 const prefPromise
= SpecialPowers
.pushPrefEnv({
75 set: [["ui.textScaleFactor", scale
]],
78 // The size is not expected to change but get it afresh to check our
81 return elementFontSize(element
);
83 const [size
] = await Promise
.all([
84 promiseNewFontSizeOnThemeChange(element
),
90 const menu
= document
.getElementById("menu");
91 const def
= document
.getElementById("default");
92 // Choose a scaleFactor value different enough from possible default values
93 // that app unit rounding does not prevent a change in devicePixelRatio.
94 // A scaleFactor of 120 also has no rounding of app units per dev pixel.
95 const referenceScale
= 120;
96 const menuSize1
= await
setScaleAndPromiseFontSize(referenceScale
, menu
);
97 const menuRect1
= menu
.getBoundingClientRect();
98 const defSize1
= elementFontSize(def
);
99 const defRect1
= def
.getBoundingClientRect();
101 const expectFn
= expectSuccess
? ok
: todo
;
102 const menuSize2
= await
setScaleAndPromiseFontSize(2*referenceScale
, menu
);
104 const singlePrecisionULP
= Math
.pow(2, -23);
105 // Precision seems to be lost in the conversion to decimal string for the
107 const reltolerance
= 30 * singlePrecisionULP
;
108 fuzzyCompareLength(menuSize2
, menuSize1
/2, "size of menu font",
109 reltolerance
*menuSize1
/2, expectFn
);
112 const menuRect2
= menu
.getBoundingClientRect();
113 // The menu font text renders exactly the same and app-unit rects are
114 // equal, but the DOMRect conversion is rounded to 1/65536 CSS pixels.
115 // https://searchfox.org/mozilla-central/rev/380fc5571b039fd453b45bbb64ed13146fe9b066/dom/base/DOMRect.cpp#151-159
116 // See also https://bugzilla.mozilla.org/show_bug.cgi?id=1640441#c28
117 const absTolerance
= 1/65536
118 fuzzyCompareLength(menuRect2
.width
, menuRect1
.width
/2,
119 "width of menu font <p> in px", absTolerance
, expectFn
);
120 fuzzyCompareLength(menuRect2
.height
, menuRect1
.height
/2,
121 "height of menu font <p> in px", absTolerance
, expectFn
);
124 const defSize2
= elementFontSize(def
);
125 is(defSize2
, defSize1
, "size of default font");
127 const defRect2
= def
.getBoundingClientRect();
128 // Wider tolerance for hinting and snapping
129 const relTolerance
= 1/12;
130 fuzzyCompareLength(defRect2
.width
, defRect1
.width
,
131 "width of default font <p> in px",
132 relTolerance
*defRect1
.width
, ok
);
133 fuzzyCompareLength(defRect2
.height
, defRect1
.height
,
134 "height of default font <p> in px",
135 relTolerance
*defRect1
.height
, ok
);