Bug 1811840 [wpt PR 38105] - Don't lose the spanner path when relaying out., a=testonly
[gecko.git] / widget / tests / test_textScaleFactor_system_font.html
blobbd2b55fbb6242321e7db0f84b7aaa5e846aafd79
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <meta charset="utf-8">
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"/>
8 <style>
9 p { width: max-content }
10 #menu { font: menu }
11 </style>
12 </head>
13 <body>
14 <p id="menu">"menu" text.</p>
15 <p id="default">Default text.</p>
16 </body>
17 <script>
18 "use strict";
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) {
40 resolve(size);
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");
49 });
50 });
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");
63 });
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.
71 !headless);
73 async function setScaleAndPromiseFontSize(scale, element) {
74 const prefPromise = SpecialPowers.pushPrefEnv({
75 set: [["ui.textScaleFactor", scale]],
76 });
77 if (!expectSuccess) {
78 // The size is not expected to change but get it afresh to check our
79 // assumption.
80 await prefPromise;
81 return elementFontSize(element);
83 const [size] = await Promise.all([
84 promiseNewFontSizeOnThemeChange(element),
85 prefPromise,
86 ]);
87 return size;
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
106 // property value.
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);
138 </script>
139 </html>