1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef GFX_FONT_FAMILY_LIST_H
7 #define GFX_FONT_FAMILY_LIST_H
8 
9 #include "nsDebug.h"
10 #include "nsISupportsImpl.h"
11 #include "nsString.h"
12 #include "nsUnicharUtils.h"
13 #include "nsTArray.h"
14 #include "mozilla/MemoryReporting.h"
15 
16 namespace mozilla {
17 
18 /**
19  * type of font family name, either a name (e.g. Helvetica) or a
20  * generic (e.g. serif, sans-serif), with the ability to distinguish
21  * between unquoted and quoted names for serializaiton
22  */
23 
24 enum FontFamilyType : uint32_t {
25   eFamily_none = 0,  // used when finding generics
26 
27   // explicitly named font family (e.g. Helvetica)
28   eFamily_named,
29   eFamily_named_quoted,
30 
31   // generics
32   eFamily_serif,         // pref font code relies on this ordering!!!
33   eFamily_sans_serif,
34   eFamily_monospace,
35   eFamily_cursive,
36   eFamily_fantasy,
37 
38   // special
39   eFamily_moz_variable,
40   eFamily_moz_fixed,
41 
42   eFamily_generic_first = eFamily_serif,
43   eFamily_generic_last = eFamily_fantasy,
44   eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1)
45 };
46 
47 enum QuotedName { eQuotedName, eUnquotedName };
48 
49 /**
50  * font family name, a string for the name if not a generic and
51  * a font type indicated named family or which generic family
52  */
53 
54 struct FontFamilyName final {
FontFamilyNamefinal55     FontFamilyName()
56         : mType(eFamily_named)
57     {}
58 
59     // named font family - e.g. Helvetica
60     explicit FontFamilyName(const nsAString& aFamilyName,
61                             QuotedName aQuoted = eUnquotedName) {
62         mType = (aQuoted == eQuotedName) ? eFamily_named_quoted : eFamily_named;
63         mName = aFamilyName;
64     }
65 
66     // generic font family - e.g. sans-serif
FontFamilyNamefinal67     explicit FontFamilyName(FontFamilyType aType) {
68         NS_ASSERTION(aType != eFamily_named &&
69                      aType != eFamily_named_quoted &&
70                      aType != eFamily_none,
71                      "expected a generic font type");
72         mName.Truncate();
73         mType = aType;
74     }
75 
FontFamilyNamefinal76     FontFamilyName(const FontFamilyName& aCopy) {
77         mType = aCopy.mType;
78         mName = aCopy.mName;
79     }
80 
IsNamedfinal81     bool IsNamed() const {
82         return mType == eFamily_named || mType == eFamily_named_quoted;
83     }
84 
IsGenericfinal85     bool IsGeneric() const {
86         return !IsNamed();
87     }
88 
89     void AppendToString(nsAString& aFamilyList, bool aQuotes = true) const {
90         switch (mType) {
91             case eFamily_named:
92                 aFamilyList.Append(mName);
93                 break;
94             case eFamily_named_quoted:
95                 if (aQuotes) {
96                     aFamilyList.Append('"');
97                 }
98                 aFamilyList.Append(mName);
99                 if (aQuotes) {
100                     aFamilyList.Append('"');
101                 }
102                 break;
103             case eFamily_serif:
104                 aFamilyList.AppendLiteral("serif");
105                 break;
106             case eFamily_sans_serif:
107                 aFamilyList.AppendLiteral("sans-serif");
108                 break;
109             case eFamily_monospace:
110                 aFamilyList.AppendLiteral("monospace");
111                 break;
112             case eFamily_cursive:
113                 aFamilyList.AppendLiteral("cursive");
114                 break;
115             case eFamily_fantasy:
116                 aFamilyList.AppendLiteral("fantasy");
117                 break;
118             case eFamily_moz_fixed:
119                 aFamilyList.AppendLiteral("-moz-fixed");
120                 break;
121             default:
122                 break;
123         }
124     }
125 
126     // helper method that converts generic names to the right enum value
127     static FontFamilyName
Convertfinal128     Convert(const nsAString& aFamilyOrGenericName) {
129         // should only be passed a single font - not entirely correct, a family
130         // *could* have a comma in it but in practice never does so
131         // for debug purposes this is fine
132         NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1,
133                      "Convert method should only be passed a single family name");
134 
135         FontFamilyType genericType = eFamily_none;
136         if (aFamilyOrGenericName.LowerCaseEqualsLiteral("serif")) {
137             genericType = eFamily_serif;
138         } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("sans-serif")) {
139             genericType = eFamily_sans_serif;
140         } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("monospace")) {
141             genericType = eFamily_monospace;
142         } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("cursive")) {
143             genericType = eFamily_cursive;
144         } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("fantasy")) {
145             genericType = eFamily_fantasy;
146         } else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("-moz-fixed")) {
147             genericType = eFamily_moz_fixed;
148         } else {
149             return FontFamilyName(aFamilyOrGenericName, eUnquotedName);
150         }
151 
152         return FontFamilyName(genericType);
153     }
154 
155     // memory reporting
SizeOfExcludingThisfinal156     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
157         return mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
158     }
159 
160     FontFamilyType mType;
161     nsString       mName; // empty if mType != eFamily_named
162 };
163 
164 inline bool
165 operator==(const FontFamilyName& a, const FontFamilyName& b) {
166     return a.mType == b.mType && a.mName == b.mName;
167 }
168 
169 /**
170  * font family list, array of font families and a default font type.
171  * font family names are either named strings or generics. the default
172  * font type is used to preserve the variable font fallback behavior
173  */
174 
175 class FontFamilyList {
176 public:
FontFamilyList()177     FontFamilyList()
178         : mDefaultFontType(eFamily_none)
179     {
180     }
181 
FontFamilyList(FontFamilyType aGenericType)182     explicit FontFamilyList(FontFamilyType aGenericType)
183         : mDefaultFontType(eFamily_none)
184     {
185         Append(FontFamilyName(aGenericType));
186     }
187 
FontFamilyList(const nsAString & aFamilyName,QuotedName aQuoted)188     FontFamilyList(const nsAString& aFamilyName,
189                    QuotedName aQuoted)
190         : mDefaultFontType(eFamily_none)
191     {
192         Append(FontFamilyName(aFamilyName, aQuoted));
193     }
194 
FontFamilyList(const FontFamilyList & aOther)195     FontFamilyList(const FontFamilyList& aOther)
196         : mFontlist(aOther.mFontlist)
197         , mDefaultFontType(aOther.mDefaultFontType)
198     {
199     }
200 
Append(const FontFamilyName & aFamilyName)201     void Append(const FontFamilyName& aFamilyName) {
202         mFontlist.AppendElement(aFamilyName);
203     }
204 
Append(const nsTArray<nsString> & aFamilyNameList)205     void Append(const nsTArray<nsString>& aFamilyNameList) {
206         uint32_t len = aFamilyNameList.Length();
207         for (uint32_t i = 0; i < len; i++) {
208             mFontlist.AppendElement(FontFamilyName(aFamilyNameList[i],
209                                                    eUnquotedName));
210         }
211     }
212 
Clear()213     void Clear() {
214         mFontlist.Clear();
215     }
216 
Length()217     uint32_t Length() const {
218         return mFontlist.Length();
219     }
220 
IsEmpty()221     bool IsEmpty() const {
222       return mFontlist.IsEmpty();
223     }
224 
GetFontlist()225     const nsTArray<FontFamilyName>& GetFontlist() const {
226         return mFontlist;
227     }
228 
Equals(const FontFamilyList & aFontlist)229     bool Equals(const FontFamilyList& aFontlist) const {
230         return mFontlist == aFontlist.mFontlist &&
231                mDefaultFontType == aFontlist.mDefaultFontType;
232     }
233 
FirstGeneric()234     FontFamilyType FirstGeneric() const {
235         uint32_t len = mFontlist.Length();
236         for (uint32_t i = 0; i < len; i++) {
237             const FontFamilyName& name = mFontlist[i];
238             if (name.IsGeneric()) {
239                 return name.mType;
240             }
241         }
242         return eFamily_none;
243     }
244 
HasGeneric()245     bool HasGeneric() const {
246         return FirstGeneric() != eFamily_none;
247     }
248 
HasDefaultGeneric()249     bool HasDefaultGeneric() const {
250         uint32_t len = mFontlist.Length();
251         for (uint32_t i = 0; i < len; i++) {
252             const FontFamilyName& name = mFontlist[i];
253             if (name.mType == mDefaultFontType) {
254                 return true;
255             }
256         }
257         return false;
258     }
259 
260     // Find the first generic (but ignoring cursive and fantasy, as they are
261     // rarely configured in any useful way) in the list.
262     // If found, move it to the start and return true; else return false.
PrioritizeFirstGeneric()263     bool PrioritizeFirstGeneric() {
264         uint32_t len = mFontlist.Length();
265         for (uint32_t i = 0; i < len; i++) {
266             const FontFamilyName name = mFontlist[i];
267             if (name.IsGeneric()) {
268                 if (name.mType == eFamily_cursive ||
269                     name.mType == eFamily_fantasy) {
270                     continue;
271                 }
272                 if (i > 0) {
273                     mFontlist.RemoveElementAt(i);
274                     mFontlist.InsertElementAt(0, name);
275                 }
276                 return true;
277             }
278         }
279         return false;
280     }
281 
PrependGeneric(FontFamilyType aType)282     void PrependGeneric(FontFamilyType aType) {
283         mFontlist.InsertElementAt(0, FontFamilyName(aType));
284     }
285 
286     void ToString(nsAString& aFamilyList,
287                   bool aQuotes = true,
288                   bool aIncludeDefault = false) const {
289         aFamilyList.Truncate();
290         uint32_t len = mFontlist.Length();
291         for (uint32_t i = 0; i < len; i++) {
292             if (i != 0) {
293                 aFamilyList.Append(',');
294             }
295             const FontFamilyName& name = mFontlist[i];
296             name.AppendToString(aFamilyList, aQuotes);
297         }
298         if (aIncludeDefault && mDefaultFontType != eFamily_none) {
299             if (!aFamilyList.IsEmpty()) {
300                 aFamilyList.Append(',');
301             }
302             if (mDefaultFontType == eFamily_serif) {
303                 aFamilyList.AppendLiteral("serif");
304             } else {
305                 aFamilyList.AppendLiteral("sans-serif");
306             }
307         }
308     }
309 
310     // searches for a specific non-generic name, lowercase comparison
Contains(const nsAString & aFamilyName)311     bool Contains(const nsAString& aFamilyName) const {
312         uint32_t len = mFontlist.Length();
313         nsAutoString fam(aFamilyName);
314         ToLowerCase(fam);
315         for (uint32_t i = 0; i < len; i++) {
316             const FontFamilyName& name = mFontlist[i];
317             if (name.mType != eFamily_named &&
318                 name.mType != eFamily_named_quoted) {
319                 continue;
320             }
321             nsAutoString listname(name.mName);
322             ToLowerCase(listname);
323             if (listname.Equals(fam)) {
324                 return true;
325             }
326         }
327         return false;
328     }
329 
GetDefaultFontType()330     FontFamilyType GetDefaultFontType() const { return mDefaultFontType; }
SetDefaultFontType(FontFamilyType aType)331     void SetDefaultFontType(FontFamilyType aType) {
332         NS_ASSERTION(aType == eFamily_none || aType == eFamily_serif ||
333                      aType == eFamily_sans_serif,
334                      "default font type must be either serif or sans-serif");
335         mDefaultFontType = aType;
336     }
337 
338     // memory reporting
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)339     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
340         size_t n = 0;
341         n += mFontlist.ShallowSizeOfExcludingThis(aMallocSizeOf);
342         for (size_t i = 0; i < mFontlist.Length(); i++) {
343             n += mFontlist[i].SizeOfExcludingThis(aMallocSizeOf);
344         }
345         return n;
346     }
347 
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)348     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
349         return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
350     }
351 
352 private:
353     nsTArray<FontFamilyName>   mFontlist;
354     FontFamilyType             mDefaultFontType; // none, serif or sans-serif
355 };
356 
357 inline bool
358 operator==(const FontFamilyList& a, const FontFamilyList& b) {
359     return a.Equals(b);
360 }
361 
362 } // namespace mozilla
363 
364 #endif /* GFX_FONT_FAMILY_LIST_H */
365