Bug 1032671 - Part 2. Use font.name-list.emoji preference for emoji presenration. r?jfkthame draft
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Sun, 12 Nov 2017 16:24:58 +0900
changeset 706547 196e3c68dc3b810196c655cfd1e952f9ec2efa9a
parent 706546 23d082ec9c61487201442a3ce01184334f6b13de
child 706548 941e679a68c83000f7a1787d8043fd347bcc976a
push id91829
push userbmo:m_kato@ga2.so-net.ne.jp
push dateSat, 02 Dec 2017 09:30:34 +0000
reviewersjfkthame
bugs1032671
milestone59.0a1
Bug 1032671 - Part 2. Use font.name-list.emoji preference for emoji presenration. r?jfkthame MozReview-Commit-ID: DbrGeXzCpNT
gfx/thebes/gfxFontConstants.h
gfx/thebes/gfxFontFamilyList.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/gfxTextRun.cpp
gfx/thebes/gfxTextRun.h
--- a/gfx/thebes/gfxFontConstants.h
+++ b/gfx/thebes/gfxFontConstants.h
@@ -179,14 +179,15 @@
 
 // pref lang id's for font prefs
 enum eFontPrefLang {
     #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_
     #include "gfxFontPrefLangList.h"
     #undef FONT_PREF_LANG
 
     , eFontPrefLang_CJKSet  // special code for CJK set
+    , eFontPrefLang_Emoji   // special code for emoji presentation
     , eFontPrefLang_First = eFontPrefLang_Western
     , eFontPrefLang_Last = eFontPrefLang_Others
     , eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1)
 };
 
 #endif
--- a/gfx/thebes/gfxFontFamilyList.h
+++ b/gfx/thebes/gfxFontFamilyList.h
@@ -35,16 +35,17 @@ enum FontFamilyType : uint32_t {
   eFamily_sans_serif,
   eFamily_monospace,
   eFamily_cursive,
   eFamily_fantasy,
 
   // special
   eFamily_moz_variable,
   eFamily_moz_fixed,
+  eFamily_moz_emoji,
 
   eFamily_generic_first = eFamily_serif,
   eFamily_generic_last = eFamily_fantasy,
   eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1)
 };
 
 enum QuotedName { eQuotedName, eUnquotedName };
 
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -955,50 +955,87 @@ gfxPlatformFontList::ResolveGenericFontN
 
     // load fonts for "font.name-list.generic.lang"
     gfxFontUtils::AppendPrefsFontList(
         NameListPref(generic, langGroupStr).get(), genericFamilies);
 
     nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
     NS_ASSERTION(langGroup, "null lang group for pref lang");
 
-    // lookup and add platform fonts uniquely
-    for (const nsString& genericFamily : genericFamilies) {
-        gfxFontStyle style;
-        style.language = langGroup;
-        style.systemFont = false;
-        AutoTArray<gfxFontFamily*,10> families;
-        FindAndAddFamilies(genericFamily, &families, FindFamiliesFlags(0),
-                           &style);
-        for (gfxFontFamily* f : families) {
-            if (!aGenericFamilies->Contains(f)) {
-                aGenericFamilies->AppendElement(f);
-            }
-        }
-    }
+    gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
+                                                           langGroup,
+                                                           aGenericFamilies);
 
 #if 0  // dump out generic mappings
     printf("%s ===> ", prefFontName.get());
     for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
         if (k > 0) printf(", ");
         printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get());
     }
     printf("\n");
 #endif
 }
 
+void
+gfxPlatformFontList::ResolveEmojiFontNames(
+    nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+    // emoji preference has no lang name
+    AutoTArray<nsString,4> genericFamilies;
+
+    nsAutoCString prefFontListName("font.name-list.emoji");
+    gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
+
+    gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies,
+                                                            nullptr,
+                                                            aGenericFamilies);
+}
+
+void
+gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(
+    nsTArray<nsString>& aGenericNameFamilies,
+    nsAtom* aLangGroup,
+    nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies)
+{
+    // lookup and add platform fonts uniquely
+    for (const nsString& genericFamily : aGenericNameFamilies) {
+        gfxFontStyle style;
+        style.language = aLangGroup;
+        style.systemFont = false;
+        AutoTArray<gfxFontFamily*,10> families;
+        FindAndAddFamilies(genericFamily, &families, FindFamiliesFlags(0),
+                           &style);
+        for (gfxFontFamily* f : families) {
+            if (!aGenericFamilies->Contains(f)) {
+                aGenericFamilies->AppendElement(f);
+            }
+        }
+    }
+}
+
 nsTArray<RefPtr<gfxFontFamily>>*
 gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
                                            eFontPrefLang aPrefLang)
 {
     // treat -moz-fixed as monospace
     if (aGenericType == eFamily_moz_fixed) {
         aGenericType = eFamily_monospace;
     }
 
+    if (aGenericType == eFamily_moz_emoji) {
+        // Emoji font has no lang
+        PrefFontList* prefFonts = mEmojiPrefFont.get();
+        if (MOZ_UNLIKELY(!prefFonts)) {
+            prefFonts = new PrefFontList;
+            ResolveEmojiFontNames(prefFonts);
+            mEmojiPrefFont.reset(prefFonts);
+        }
+        return prefFonts;
+    }
+
     PrefFontList* prefFonts =
         mLangGroupPrefFonts[aPrefLang][aGenericType].get();
     if (MOZ_UNLIKELY(!prefFonts)) {
         prefFonts = new PrefFontList;
         ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
         mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
     }
     return prefFonts;
@@ -1293,16 +1330,20 @@ gfxPlatformFontList::AppendPrefLang(eFon
         aPrefLangs[aLen] = aAddLang;
         aLen++;
     }
 }
 
 mozilla::FontFamilyType
 gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
 {
+    if (aLang == eFontPrefLang_Emoji) {
+      return eFamily_moz_emoji;
+    }
+
     // initialize lang group pref font defaults (i.e. serif/sans-serif)
     if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
         mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
         for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
             nsAutoCString prefDefaultFontType("font.default.");
             prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
             nsAutoCString serifOrSans;
             Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -492,16 +492,25 @@ protected:
 
     void RebuildLocalFonts();
 
     void
     ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
                             eFontPrefLang aPrefLang,
                             nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
 
+    void
+    ResolveEmojiFontNames(nsTArray<RefPtr<gfxFontFamily>>* aGenericFamilies);
+
+    void
+    GetFontFamiliesFromGenericFamilies(
+        nsTArray<nsString>& aGenericFamilies,
+        nsAtom* aLangGroup,
+        nsTArray<RefPtr<gfxFontFamily>>* aFontFamilies);
+
     virtual nsresult InitFontListForPlatform() = 0;
 
     void ApplyWhitelist();
 
     // Create a new gfxFontFamily of the appropriate subclass for the platform,
     // used when AddWithLegacyFamilyName needs to create a new family.
     virtual gfxFontFamily* CreateFontFamily(const nsAString& aName) const = 0;
 
@@ -557,16 +566,17 @@ protected:
 
     typedef nsTArray<RefPtr<gfxFontFamily>> PrefFontList;
     typedef mozilla::RangedArray<mozilla::UniquePtr<PrefFontList>,
                                  mozilla::eFamily_generic_first,
                                  mozilla::eFamily_generic_count> PrefFontsForLangGroup;
     mozilla::RangedArray<PrefFontsForLangGroup,
                          eFontPrefLang_First,
                          eFontPrefLang_Count> mLangGroupPrefFonts;
+    mozilla::UniquePtr<PrefFontList> mEmojiPrefFont;
 
     // when system-wide font lookup fails for a character, cache it to skip future searches
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time
     // on pages with lots of problems
     RefPtr<gfxFontFamily> mReplacementCharFallbackFamily;
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -32,16 +32,19 @@
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 using mozilla::services::GetObserverService;
 
 static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
 static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
 
+const uint32_t kVariationSelector15 = 0xFE0E; // text presentation
+const uint32_t kVariationSelector16 = 0xFE0F; // emoji presentation
+
 #ifdef DEBUG_roc
 #define DEBUG_TEXT_RUN_STORAGE_METRICS
 #endif
 
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
 extern uint32_t gTextRunStorageHighWaterMark;
 extern uint32_t gTextRunStorage;
 extern uint32_t gFontCount;
@@ -3051,17 +3054,17 @@ gfxFontGroup::FindFontForChar(uint32_t a
         }
     }
 
     // if character is in Private Use Area, don't do matching against pref or system fonts
     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
         return nullptr;
 
     // 2. search pref fonts
-    gfxFont* font = WhichPrefFontSupportsChar(aCh);
+    gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
     if (font) {
         *aMatchType = gfxTextRange::kPrefsFallback;
         return font;
     }
 
     // 3. use fallback fonts
     // -- before searching for something else check the font used for the previous character
     if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
@@ -3332,22 +3335,32 @@ gfxFontGroup::ContainsUserFont(const gfx
         if (ff.EqualsUserFont(aUserFont)) {
             return true;
         }
     }
     return false;
 }
 
 gfxFont*
-gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
+gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh)
 {
-    // get the pref font list if it hasn't been set up already
-    uint32_t unicodeRange = FindCharUnicodeRange(aCh);
+    eFontPrefLang charLang;
     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
-    eFontPrefLang charLang = pfl->GetFontPrefLangFor(unicodeRange);
+
+    EmojiPresentation emoji = GetEmojiPresentation(aCh);
+    if ((emoji != EmojiPresentation::TextOnly &&
+         (aNextCh == kVariationSelector16 ||
+          (emoji == EmojiPresentation::EmojiDefault &&
+           aNextCh != kVariationSelector15)))) {
+        charLang = eFontPrefLang_Emoji;
+    } else {
+        // get the pref font list if it hasn't been set up already
+        uint32_t unicodeRange = FindCharUnicodeRange(aCh);
+        charLang = pfl->GetFontPrefLangFor(unicodeRange);
+    }
 
     // if the last pref font was the first family in the pref list, no need to recheck through a list of families
     if (mLastPrefFont && charLang == mLastPrefLang &&
         mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
         return mLastPrefFont;
     }
 
     // based on char lang and page lang, set up list of pref lang fonts to check
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -996,17 +996,18 @@ public:
     // group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
     // Get it/use it/forget it :) - don't keep a reference that might go stale.
     gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
                                    mozilla::gfx::ShapedTextFlags aFlags,
                                    LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
 
 protected:
     // search through pref fonts for a character, return nullptr if no matching pref font
-    gfxFont* WhichPrefFontSupportsChar(uint32_t aCh);
+    gfxFont* WhichPrefFontSupportsChar(uint32_t aCh,
+                                       uint32_t aNextCh);
 
     gfxFont* WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
                                          Script aRunScript);
 
     template<typename T>
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
                        const T *aString, uint32_t aLength,
                        Script aRunScript,