summaryrefslogtreecommitdiffstats
path: root/widget/tests/test_textScaleFactor_system_font.html
diff options
context:
space:
mode:
Diffstat (limited to 'widget/tests/test_textScaleFactor_system_font.html')
-rw-r--r--widget/tests/test_textScaleFactor_system_font.html139
1 files changed, 139 insertions, 0 deletions
diff --git a/widget/tests/test_textScaleFactor_system_font.html b/widget/tests/test_textScaleFactor_system_font.html
new file mode 100644
index 0000000000..bd2b55fbb6
--- /dev/null
+++ b/widget/tests/test_textScaleFactor_system_font.html
@@ -0,0 +1,139 @@
+<!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.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+// 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
+ const absTolerance = 1/65536
+ 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>