tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Agfx/tests/crashtests/1962172-1.html | 30++++++++++++++++++++++++++++++
Mgfx/tests/crashtests/crashtests.list | 1+
Mgfx/thebes/gfxFontConstants.h | 2+-
Mgfx/thebes/gfxPlatformFontList.cpp | 260+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mgfx/thebes/gfxPlatformFontList.h | 6+++++-
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);