commit b373b47fa8047820f91243734699c68929246b87
parent 6e1cfb8bbf744c2d804bd54d078d0381fcdccf37
Author: Jonathan Kew <jkew@mozilla.com>
Date: Sat, 6 Dec 2025 10:57:34 +0000
Bug 1962172 - Store CJK font-selection fallback order in the cached FontPrefs. r=gfx-reviewers,lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D246894
Diffstat:
5 files changed, 164 insertions(+), 135 deletions(-)
diff --git a/gfx/tests/crashtests/1962172-1.html b/gfx/tests/crashtests/1962172-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<body>
+</body>
+<script>
+const blob = new Blob([`
+ self.onmessage = (evt) => {
+ let ctx = evt.data.canvas.getContext("2d");
+ ctx.font = "24px serif";
+ // Text that will cause CJK font fallback, which looks up
+ // the accept-language pref.
+ ctx.measureText("你好");
+ postMessage("Done");
+ }
+`], {
+ type: "application/typescript",
+});
+
+const url = URL.createObjectURL(blob);
+const worker = new Worker(url);
+
+worker.onmessage = (msg) => {
+ document.documentElement.classList.remove("reftest-wait");
+};
+
+let offscreen = new OffscreenCanvas(100, 100);
+worker.postMessage({ canvas: offscreen }, [offscreen]);
+</script>
+</html>
diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list
@@ -228,5 +228,6 @@ load 1843622.html
skip-if(Android&&debug) pref(gfx.missing_fonts.notify,true) load 1871755-1.html
load 1898569.html
load 1938548.html
+load 1962172-1.html
load 1983053.html
load 1999841-1.html
diff --git a/gfx/thebes/gfxFontConstants.h b/gfx/thebes/gfxFontConstants.h
@@ -101,7 +101,7 @@ enum class StyleFontVariantEmoji : uint8_t { Normal, Text, Emoji, Unicode };
#define NS_FONT_SUB_SUPER_LARGE_SIZE (45.0)
// pref lang id's for font prefs
-enum eFontPrefLang {
+enum eFontPrefLang : uint8_t {
#define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_##enum_id_
#include "gfxFontPrefLangList.h"
#undef FONT_PREF_LANG
diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp
@@ -2491,139 +2491,8 @@ void gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[],
AppendPrefLang(aPrefLangs, aLen, aPageLang);
}
- // if not set up, set up the default CJK order, based on accept lang
- // settings and locale
- if (mCJKPrefLangs.Length() == 0) {
- // temp array
- eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
- uint32_t tempLen = 0;
- auto* localeService = LocaleService::GetInstance();
-
- // Add the CJK pref fonts from accept languages, the order should be same
- // order.
- nsAutoCString acceptLang;
- nsresult rv = localeService->GetAcceptLanguages(acceptLang);
-
- // We use gfxFontUtils::ParseFontList to read the list even
- // though it's not actually a list of fonts but of locale codes;
- // the format is the same.
- AutoTArray<nsCString, 5> list;
- if (NS_SUCCEEDED(rv)) {
- gfxFontUtils::ParseFontList(acceptLang, list);
- }
-
- for (const auto& lang : list) {
- eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
- switch (fpl) {
- case eFontPrefLang_Japanese:
- case eFontPrefLang_Korean:
- case eFontPrefLang_ChineseCN:
- case eFontPrefLang_ChineseHK:
- case eFontPrefLang_ChineseTW:
- AppendPrefLang(tempPrefLangs, tempLen, fpl);
- break;
- default:
- break;
- }
- }
-
- // Try using app's locale
- nsAutoCString localeStr;
- localeService->GetAppLocaleAsBCP47(localeStr);
-
- {
- Locale locale;
- if (LocaleParser::TryParse(localeStr, locale).isOk() &&
- locale.Canonicalize().isOk()) {
- if (locale.Language().EqualTo("ja")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
- } else if (locale.Language().EqualTo("zh")) {
- if (locale.Region().EqualTo("CN")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
- } else if (locale.Region().EqualTo("TW")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
- } else if (locale.Region().EqualTo("HK")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
- }
- } else if (locale.Language().EqualTo("ko")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
- }
- }
- }
-
- // Then add the known CJK prefs in order of system preferred locales
- AutoTArray<nsCString, 5> prefLocales;
- prefLocales.AppendElement("ja"_ns);
- prefLocales.AppendElement("zh-CN"_ns);
- prefLocales.AppendElement("zh-TW"_ns);
- prefLocales.AppendElement("zh-HK"_ns);
- prefLocales.AppendElement("ko"_ns);
-
- AutoTArray<nsCString, 16> sysLocales;
- AutoTArray<nsCString, 16> negLocales;
- if (NS_SUCCEEDED(
- OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
- LocaleService::GetInstance()->NegotiateLanguages(
- sysLocales, prefLocales, ""_ns,
- LocaleService::kLangNegStrategyFiltering, negLocales);
- for (const auto& localeStr : negLocales) {
- Locale locale;
- if (LocaleParser::TryParse(localeStr, locale).isOk() &&
- locale.Canonicalize().isOk()) {
- if (locale.Language().EqualTo("ja")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
- } else if (locale.Language().EqualTo("zh")) {
- if (locale.Region().EqualTo("CN")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
- } else if (locale.Region().EqualTo("TW")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
- } else if (locale.Region().EqualTo("HK")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
- }
- } else if (locale.Language().EqualTo("ko")) {
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
- }
- }
- }
- }
-
- // Last resort... set up CJK font prefs in the order listed by the user-
- // configurable ordering pref.
- gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
- for (const auto& item : list) {
- eFontPrefLang fpl = GetFontPrefLangFor(item.get());
- switch (fpl) {
- case eFontPrefLang_Japanese:
- case eFontPrefLang_Korean:
- case eFontPrefLang_ChineseCN:
- case eFontPrefLang_ChineseHK:
- case eFontPrefLang_ChineseTW:
- AppendPrefLang(tempPrefLangs, tempLen, fpl);
- break;
- default:
- break;
- }
- }
-
- // Truly-last resort... try Chinese font prefs before Japanese because
- // they tend to have more complete character coverage, and therefore less
- // risk of "ransom-note" effects.
- // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
- // this will do nothing as all these values are already present.)
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
- AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
-
- // copy into the cached array
- for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
- mCJKPrefLangs.AppendElement(lang);
- }
- }
-
// append in cached CJK langs
- for (const auto lang : mCJKPrefLangs) {
+ for (const auto lang : GetFontPrefs()->CJKPrefLangs()) {
AppendPrefLang(aPrefLangs, aLen, eFontPrefLang(lang));
}
}
@@ -2968,7 +2837,6 @@ void gfxPlatformFontList::ClearLangGroupPrefFontsLocked() {
pref = nullptr;
}
}
- mCJKPrefLangs.Clear();
mEmojiPrefFont = nullptr;
// Create a new FontPrefs and replace the existing one.
@@ -3372,6 +3240,132 @@ void gfxPlatformFontList::FontPrefs::Init() {
}
}
mEmojiHasUserValue = Preferences::HasUserValue("font.name-list.emoji");
+
+ // Record the default CJK order, based on accept-lang settings and locale.
+ eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
+ uint32_t tempLen = 0;
+
+ // Add the CJK pref fonts from accept languages, the order should be same
+ // order.
+ nsAutoCString acceptLang;
+ nsresult rv = LocaleService::GetInstance()->GetAcceptLanguages(acceptLang);
+
+ // We use gfxFontUtils::ParseFontList to read the list even
+ // though it's not actually a list of fonts but of locale codes;
+ // the format is the same.
+ AutoTArray<nsCString, 5> list;
+ if (NS_SUCCEEDED(rv)) {
+ gfxFontUtils::ParseFontList(acceptLang, list);
+ }
+
+ for (const auto& lang : list) {
+ eFontPrefLang fpl = GetFontPrefLangFor(lang.get());
+ switch (fpl) {
+ case eFontPrefLang_Japanese:
+ case eFontPrefLang_Korean:
+ case eFontPrefLang_ChineseCN:
+ case eFontPrefLang_ChineseHK:
+ case eFontPrefLang_ChineseTW:
+ AppendPrefLang(tempPrefLangs, tempLen, fpl);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Try using app's locale
+ nsAutoCString localeStr;
+ LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr);
+
+ {
+ Locale locale;
+ if (LocaleParser::TryParse(localeStr, locale).isOk() &&
+ locale.Canonicalize().isOk()) {
+ if (locale.Language().EqualTo("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (locale.Language().EqualTo("zh")) {
+ if (locale.Region().EqualTo("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (locale.Region().EqualTo("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (locale.Region().EqualTo("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (locale.Language().EqualTo("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ }
+ }
+
+ // Then add the known CJK prefs in order of system preferred locales
+ AutoTArray<nsCString, 5> prefLocales;
+ prefLocales.AppendElement("ja"_ns);
+ prefLocales.AppendElement("zh-CN"_ns);
+ prefLocales.AppendElement("zh-TW"_ns);
+ prefLocales.AppendElement("zh-HK"_ns);
+ prefLocales.AppendElement("ko"_ns);
+
+ AutoTArray<nsCString, 16> sysLocales;
+ AutoTArray<nsCString, 16> negLocales;
+ if (NS_SUCCEEDED(
+ OSPreferences::GetInstance()->GetSystemLocales(sysLocales))) {
+ LocaleService::GetInstance()->NegotiateLanguages(
+ sysLocales, prefLocales, ""_ns,
+ LocaleService::kLangNegStrategyFiltering, negLocales);
+ for (const auto& localeStr : negLocales) {
+ Locale locale;
+ if (LocaleParser::TryParse(localeStr, locale).isOk() &&
+ locale.Canonicalize().isOk()) {
+ if (locale.Language().EqualTo("ja")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ } else if (locale.Language().EqualTo("zh")) {
+ if (locale.Region().EqualTo("CN")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ } else if (locale.Region().EqualTo("TW")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ } else if (locale.Region().EqualTo("HK")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ }
+ } else if (locale.Language().EqualTo("ko")) {
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+ }
+ }
+ }
+ }
+
+ // Last resort... set up CJK font prefs in the order listed by the user-
+ // configurable ordering pref.
+ gfxFontUtils::GetPrefsFontList(kCJKFallbackOrderPref, list);
+ for (const auto& item : list) {
+ eFontPrefLang fpl = GetFontPrefLangFor(item.get());
+ switch (fpl) {
+ case eFontPrefLang_Japanese:
+ case eFontPrefLang_Korean:
+ case eFontPrefLang_ChineseCN:
+ case eFontPrefLang_ChineseHK:
+ case eFontPrefLang_ChineseTW:
+ AppendPrefLang(tempPrefLangs, tempLen, fpl);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Truly-last resort... try Chinese font prefs before Japanese because
+ // they tend to have more complete character coverage, and therefore less
+ // risk of "ransom-note" effects.
+ // (If the kCJKFallbackOrderPref was fully populated, as it is by default,
+ // this will do nothing as all these values are already present.)
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+ AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+
+ // copy into the cached array
+ for (const auto lang : Span<eFontPrefLang>(tempPrefLangs, tempLen)) {
+ mCJKPrefLangs.AppendElement(lang);
+ }
}
bool gfxPlatformFontList::FontPrefs::LookupName(const nsACString& aPref,
diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h
@@ -198,6 +198,10 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
return mFontNameList.ConstIter();
}
+ const nsTArray<eFontPrefLang>& CJKPrefLangs() const {
+ return mCJKPrefLangs;
+ }
+
private:
static constexpr char kNamePrefix[] = "font.name.";
static constexpr char kNameListPrefix[] = "font.name-list.";
@@ -206,6 +210,7 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
HashMap mFontName;
HashMap mFontNameList;
+ nsTArray<eFontPrefLang> mCJKPrefLangs;
bool mEmojiHasUserValue = false;
};
@@ -1063,7 +1068,6 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
nsLanguageAtomService* mLangService = nullptr;
- nsTArray<uint32_t> mCJKPrefLangs MOZ_GUARDED_BY(mLock);
nsTArray<mozilla::StyleGenericFontFamily> mDefaultGenericsLangGroup
MOZ_GUARDED_BY(mLock);