summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxFontEntry.h
blob: a721f7f6b6832fed9ec57839f1b6bcb61c9657c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef GFX_FONTENTRY_H
#define GFX_FONTENTRY_H

#include <limits>
#include <math.h>
#include <new>
#include <utility>
#include "COLRFonts.h"
#include "ThebesRLBoxTypes.h"
#include "gfxFontUtils.h"
#include "gfxFontVariations.h"
#include "gfxRect.h"
#include "gfxTypes.h"
#include "harfbuzz/hb.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/FontPropertyTypes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/RWLock.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/intl/UnicodeScriptCodes.h"
#include "nsTHashMap.h"
#include "nsDebug.h"
#include "nsHashKeys.h"
#include "nsISupports.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
#include "nscore.h"

class FontInfoData;
class gfxContext;
class gfxFont;
class gfxFontFamily;
class gfxPlatformFontList;
class gfxSVGGlyphs;
class gfxUserFontData;
class nsAtom;
struct FontListSizes;
struct gfxFontFeature;
struct gfxFontStyle;
enum class eFontPresentation : uint8_t;

namespace IPC {
template <class P>
struct ParamTraits;
}

namespace mozilla {
class SVGContextPaint;
namespace fontlist {
struct Face;
struct Family;
}  // namespace fontlist
}  // namespace mozilla

typedef struct gr_face gr_face;
typedef struct FT_MM_Var_ FT_MM_Var;

#define NO_FONT_LANGUAGE_OVERRIDE 0

class gfxCharacterMap : public gfxSparseBitSet {
 public:
  // gfxCharacterMap instances may be shared across multiple threads via a
  // global table managed by gfxPlatformFontList. Once a gfxCharacterMap is
  // inserted in the global table, its mShared flag will be TRUE, and we
  // cannot safely delete it except from gfxPlatformFontList (which will
  // use a lock to ensure entries are removed from its table and deleted
  // safely).

  // AddRef() is pretty much standard. We don't return the refcount as our
  // users don't care about it.
  void AddRef() {
    MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(gfxCharacterMap);
    MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
    [[maybe_unused]] nsrefcnt count = ++mRefCnt;
    NS_LOG_ADDREF(this, count, "gfxCharacterMap", sizeof(*this));
  }

  // Custom Release(): if the object is referenced from the global shared
  // table, and we're releasing the last *other* reference to it, then we
  // notify the global table to consider also releasing its ref. (That may
  // not actually happen, if another thread is racing with us and takes a
  // new reference, or completes the release first!)
  void Release() {
    MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
    // We can't safely read this after we've decremented mRefCnt, so save it
    // in a local variable here. Note that the value is never reset to false
    // once it has been set to true (when recording the cmap in the shared
    // table), so there's no risk of this resulting in a "false positive" when
    // tested later. A "false negative" is possible but harmless; it would
    // just mean we miss an opportunity to release a reference from the shared
    // cmap table.
    bool isShared = mShared;

    // Ensure we only access mRefCnt once, for consistency if the object is
    // being used by multiple threads.
    nsrefcnt count = --mRefCnt;
    NS_LOG_RELEASE(this, count, "gfxCharacterMap");

    // If isShared was true, this object has been shared across threads. In
    // that case, if the refcount went to 1, we notify the shared table so
    // it can drop its reference and delete the object.
    if (isShared) {
      MOZ_ASSERT(count > 0);
      if (count == 1) {
        NotifyMaybeReleased(this);
      }
      return;
    }

    // Otherwise, this object hasn't been shared and we can safely delete it
    // as we must have been holding the only reference. (Note that if we were
    // holding the only reference, there's no other owner who can have set
    // mShared to true since we read it above.)
    if (count == 0) {
      delete this;
    }
  }

  gfxCharacterMap() = default;

  explicit gfxCharacterMap(const gfxSparseBitSet& aOther)
      : gfxSparseBitSet(aOther) {}

  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
    return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf);
  }

  // hash of the cmap bitvector
  uint32_t mHash = 0;

  // if cmap is built on the fly it's never shared
  bool mBuildOnTheFly = false;

  // Character map is shared globally. This can only be set by the thread that
  // originally created the map, as no other thread can get a reference until
  // it has been shared via the global table.
  bool mShared = false;

 protected:
  friend class gfxPlatformFontList;

  // Destructor should not be called except via Release().
  // (Note that our "friend" gfxPlatformFontList also accesses this from its
  // MaybeRemoveCmap method.)
  ~gfxCharacterMap() = default;

  nsrefcnt RefCount() const { return mRefCnt; }

  void CalcHash() { mHash = GetChecksum(); }

  static void NotifyMaybeReleased(gfxCharacterMap* aCmap);

  // Only used when clearing the shared-cmap hashtable during shutdown.
  void ClearSharedFlag() {
    MOZ_ASSERT(NS_IsMainThread());
    mShared = false;
  }

  mozilla::ThreadSafeAutoRefCnt mRefCnt;

 private:
  gfxCharacterMap(const gfxCharacterMap&) = delete;
  gfxCharacterMap& operator=(const gfxCharacterMap&) = delete;
};

// Info on an individual font feature, for reporting available features
// to DevTools via the GetFeatureInfo method.
struct gfxFontFeatureInfo {
  uint32_t mTag;
  uint32_t mScript;
  uint32_t mLangSys;
};

class gfxFontEntryCallbacks;

class gfxFontEntry {
 public:
  typedef mozilla::gfx::DrawTarget DrawTarget;
  typedef mozilla::intl::Script Script;
  typedef mozilla::FontWeight FontWeight;
  typedef mozilla::FontSlantStyle FontSlantStyle;
  typedef mozilla::FontStretch FontStretch;
  typedef mozilla::WeightRange WeightRange;
  typedef mozilla::SlantStyleRange SlantStyleRange;
  typedef mozilla::StretchRange StretchRange;

  // Used by stylo
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontEntry)

  explicit gfxFontEntry(const nsACString& aName, bool aIsStandardFace = false);

  gfxFontEntry() = delete;
  gfxFontEntry(const gfxFontEntry&) = delete;
  gfxFontEntry& operator=(const gfxFontEntry&) = delete;

  // Create a new entry that refers to the same font as this, but without
  // additional state that may have been set up (such as family name).
  // (This is only to be used for system fonts in the platform font list,
  // not user fonts.)
  virtual gfxFontEntry* Clone() const = 0;

  // unique name for the face, *not* the family; not necessarily the
  // "real" or user-friendly name, may be an internal identifier
  const nsCString& Name() const { return mName; }

  // family name
  const nsCString& FamilyName() const { return mFamilyName; }

  // The following two methods may be relatively expensive, as they
  // will (usually, except on Linux) load and parse the 'name' table;
  // they are intended only for the font-inspection API, not for
  // perf-critical layout/drawing work.

  // The "real" name of the face, if available from the font resource;
  // returns Name() if nothing better is available.
  virtual nsCString RealFaceName();

  WeightRange Weight() const { return mWeightRange; }
  StretchRange Stretch() const { return mStretchRange; }
  SlantStyleRange SlantStyle() const { return mStyleRange; }

  bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
  bool IsLocalUserFont() const { return mIsLocalUserFont; }
  bool IsFixedPitch() const { return mFixedPitch; }
  bool IsItalic() const { return SlantStyle().Min().IsItalic(); }
  bool IsOblique() const { return SlantStyle().Min().IsOblique(); }
  bool IsUpright() const { return SlantStyle().Min().IsNormal(); }
  inline bool SupportsItalic();  // defined below, because of RangeFlags use
  inline bool SupportsBold();
  inline bool MayUseSyntheticSlant();
  bool IgnoreGDEF() const { return mIgnoreGDEF; }
  bool IgnoreGSUB() const { return mIgnoreGSUB; }

  // Return whether the face corresponds to "normal" CSS style properties:
  //    font-style: normal;
  //    font-weight: normal;
  //    font-stretch: normal;
  // If this is false, we might want to fall back to a different face and
  // possibly apply synthetic styling.
  bool IsNormalStyle() const {
    return IsUpright() && Weight().Min() <= FontWeight::NORMAL &&
           Weight().Max() >= FontWeight::NORMAL &&
           Stretch().Min() <= FontStretch::NORMAL &&
           Stretch().Max() >= FontStretch::NORMAL;
  }

  // whether a feature is supported by the font (limited to a small set
  // of features for which some form of fallback needs to be implemented)
  virtual bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
  bool SupportsGraphiteFeature(uint32_t aFeatureTag);

  // returns a set containing all input glyph ids for a given feature
  const hb_set_t* InputsForOpenTypeFeature(Script aScript,
                                           uint32_t aFeatureTag);

  virtual bool HasFontTable(uint32_t aTableTag);

  inline bool HasGraphiteTables() {
    LazyFlag flag = mHasGraphiteTables;
    if (flag == LazyFlag::Uninitialized) {
      flag = CheckForGraphiteTables() ? LazyFlag::Yes : LazyFlag::No;
      mHasGraphiteTables = flag;
    }
    return flag == LazyFlag::Yes;
  }

  inline bool HasCmapTable() {
    if (!mCharacterMap && !mShmemCharacterMap) {
      ReadCMAP();
      NS_ASSERTION(mCharacterMap || mShmemCharacterMap,
                   "failed to initialize character map");
    }
    return mHasCmapTable;
  }

  inline bool HasCharacter(uint32_t ch) {
    if (mShmemCharacterMap) {
      return GetShmemCharacterMap()->test(ch);
    }
    if (mCharacterMap) {
      if (mShmemFace && TrySetShmemCharacterMap()) {
        // Forget our temporary local copy, now we can use the shared cmap
        auto* oldCmap = mCharacterMap.exchange(nullptr);
        NS_IF_RELEASE(oldCmap);
        return GetShmemCharacterMap()->test(ch);
      }
      if (GetCharacterMap()->test(ch)) {
        return true;
      }
    }
    return TestCharacterMap(ch);
  }

  virtual bool SkipDuringSystemFallback() { return false; }
  void EnsureUVSMapInitialized();
  uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);

  // All concrete gfxFontEntry subclasses (except gfxUserFontEntry) need
  // to override this, otherwise the font will never be used as it will
  // be considered to support no characters.
  // ReadCMAP() must *always* set the mCharacterMap pointer to a valid
  // gfxCharacterMap, even if empty, as other code assumes this pointer
  // can be safely dereferenced.
  virtual nsresult ReadCMAP(FontInfoData* aFontInfoData = nullptr);

  bool TryGetSVGData(const gfxFont* aFont);
  bool HasSVGGlyph(uint32_t aGlyphId);
  bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
                          gfxFloat aSize, gfxRect* aResult);
  void RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId,
                      mozilla::SVGContextPaint* aContextPaint);
  // Call this when glyph geometry or rendering has changed
  // (e.g. animated SVG glyphs)
  void NotifyGlyphsChanged();

  bool TryGetColorGlyphs();

  bool HasColorBitmapTable() {
    LazyFlag flag = mHasColorBitmapTable;
    if (flag == LazyFlag::Uninitialized) {
      flag = HasFontTable(TRUETYPE_TAG('C', 'B', 'D', 'T')) ||
                     HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))
                 ? LazyFlag::Yes
                 : LazyFlag::No;
      mHasColorBitmapTable = flag;
    }
    return flag == LazyFlag::Yes;
  }

  // Access to raw font table data (needed for Harfbuzz):
  // returns a pointer to data owned by the fontEntry or the OS,
  // which will remain valid until the blob is destroyed.
  // The data MUST be treated as read-only; we may be getting a
  // reference to a shared system font cache.
  //
  // The default implementation uses CopyFontTable to get the data
  // into a byte array, and maintains a cache of loaded tables.
  //
  // Subclasses should override this if they can provide more efficient
  // access than copying table data into our own buffers.
  //
  // Get blob that encapsulates a specific font table, or nullptr if
  // the table doesn't exist in the font.
  //
  // Caller is responsible to call hb_blob_destroy() on the returned blob
  // (if non-nullptr) when no longer required. For transient access to a
  // table, use of AutoTable (below) is generally preferred.
  virtual hb_blob_t* GetFontTable(uint32_t aTag);

  // Stack-based utility to return a specified table, automatically releasing
  // the blob when the AutoTable goes out of scope.
  class AutoTable {
   public:
    AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag) {
      mBlob = aFontEntry->GetFontTable(aTag);
    }
    ~AutoTable() { hb_blob_destroy(mBlob); }
    operator hb_blob_t*() const { return mBlob; }

   private:
    hb_blob_t* mBlob;
    // not implemented:
    AutoTable(const AutoTable&) = delete;
    AutoTable& operator=(const AutoTable&) = delete;
  };

  // Return a font instance for a particular style. This may be a newly-
  // created instance, or a font already in the global cache.
  // We can't return a UniquePtr here, because we may be returning a shared
  // cached instance; but we also don't return already_AddRefed, because
  // the caller may only need to use the font temporarily and doesn't need
  // a strong reference.
  already_AddRefed<gfxFont> FindOrMakeFont(
      const gfxFontStyle* aStyle, gfxCharacterMap* aUnicodeRangeMap = nullptr);

  // Get an existing font table cache entry in aBlob if it has been
  // registered, or return false if not.  Callers must call
  // hb_blob_destroy on aBlob if true is returned.
  //
  // Note that some gfxFont implementations may not call this at all,
  // if it is more efficient to get the table from the OS at that level.
  bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob);

  // Elements of aTable are transferred (not copied) to and returned in a
  // new hb_blob_t which is registered on the gfxFontEntry, but the initial
  // reference is owned by the caller.  Removing the last reference
  // unregisters the table from the font entry.
  //
  // Pass nullptr for aBuffer to indicate that the table is not present and
  // nullptr will be returned.  Also returns nullptr on OOM.
  hb_blob_t* ShareFontTableAndGetBlob(uint32_t aTag, nsTArray<uint8_t>* aTable);

  // Get the font's unitsPerEm from the 'head' table, in the case of an
  // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts,
  // if present on the platform.
  uint16_t UnitsPerEm();
  enum {
    kMinUPEM = 16,     // Limits on valid unitsPerEm range, from the
    kMaxUPEM = 16384,  // OpenType spec
    kInvalidUPEM = uint16_t(-1)
  };

  // Shaper face accessors:
  // NOTE that harfbuzz and graphite handle ownership/lifetime of the face
  // object in completely different ways.

  // Create a HarfBuzz face corresponding to this font file.
  // Our reference to the underlying hb_face_t will be released when the
  // returned AutoHBFace goes out of scope, but the hb_face_t itself may
  // be kept alive by other references (e.g. if an hb_font_t has been
  // instantiated for it).
  class MOZ_STACK_CLASS AutoHBFace {
   public:
    explicit AutoHBFace(hb_face_t* aFace) : mFace(aFace) {}
    ~AutoHBFace() { hb_face_destroy(mFace); }

    operator hb_face_t*() const { return mFace; }

    // Not default-constructible, not copyable.
    AutoHBFace() = delete;
    AutoHBFace(const AutoHBFace&) = delete;
    AutoHBFace& operator=(const AutoHBFace&) = delete;

   private:
    hb_face_t* mFace;
  };

  AutoHBFace GetHBFace() {
    return AutoHBFace(hb_face_create_for_tables(HBGetTable, this, nullptr));
  }

  // Get the sandbox instance that graphite is running in.
  rlbox_sandbox_gr* GetGrSandbox();

  // Register and get the callback handle for the glyph advance firefox callback
  // Since the sandbox instance is shared with multiple test shapers, callback
  // registration must be handled centrally to ensure multiple instances don't
  // register the same callback.
  sandbox_callback_gr<float (*)(const void*, uint16_t)>*
  GetGrSandboxAdvanceCallbackHandle();

  // Get Graphite face corresponding to this font file.
  // Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
  // Graphite is run in a sandbox
  tainted_opaque_gr<gr_face*> GetGrFace();
  void ReleaseGrFace(tainted_opaque_gr<gr_face*> aFace);

  // Does the font have graphite contextuals that involve the space glyph
  // (and therefore we should bypass the word cache)?
  // Since this function inspects data from libGraphite stored in sandbox memory
  // it can only return a "hint" to the correct return value. This is because
  // a compromised libGraphite could change the sandbox memory maliciously at
  // any moment. The caller must ensure the calling code performs safe actions
  // independent of the value returned, to unwrap this return.
  tainted_boolean_hint HasGraphiteSpaceContextuals();

  // Release any SVG-glyphs document this font may have loaded.
  void DisconnectSVG();

  // Called to notify that aFont is being destroyed. Needed when we're tracking
  // the fonts belonging to this font entry.
  void NotifyFontDestroyed(gfxFont* aFont);

  // For memory reporting of the platform font list.
  virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                      FontListSizes* aSizes) const;
  virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                      FontListSizes* aSizes) const;

  // Used for reporting on individual font entries in the user font cache,
  // which are not present in the platform font list.
  size_t ComputedSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

  // Used when checking for complex script support, to mask off cmap ranges
  struct ScriptRange {
    uint32_t rangeStart;
    uint32_t rangeEnd;
    uint32_t numTags;  // number of entries in the tags[] array
    hb_tag_t tags[3];  // up to three OpenType script tags to check
  };

  bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags, uint32_t aNumTags);

  /**
   * Font-variation query methods.
   *
   * Font backends that don't support variations should provide empty
   * implementations.
   */
  virtual bool HasVariations() = 0;

  virtual void GetVariationAxes(
      nsTArray<gfxFontVariationAxis>& aVariationAxes) = 0;

  virtual void GetVariationInstances(
      nsTArray<gfxFontVariationInstance>& aInstances) = 0;

  bool HasBoldVariableWeight();
  bool HasItalicVariation();
  bool HasSlantVariation();
  bool HasOpticalSize();

  void CheckForVariationAxes();

  // Set up the entry's weight/stretch/style ranges according to axes found
  // by GetVariationAxes (for installed fonts; do NOT call this for user
  // fonts, where the ranges are provided by @font-face descriptors).
  void SetupVariationRanges();

  // Get variation axis settings that should be used to implement a particular
  // font style using this resource.
  void GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
                             const gfxFontStyle& aStyle);

  // Get the font's list of features (if any) for DevTools support.
  void GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo);

  // This is only called on platforms where we use FreeType.
  virtual FT_MM_Var* GetMMVar() { return nullptr; }

  // Return true if the font has a 'trak' table (and we can successfully
  // interpret it), otherwise false. This will load and cache the table
  // the first time it is called.
  bool HasTrackingTable();

  // Return the tracking (in font units) to be applied for the given size.
  // (This is a floating-point number because of possible interpolation.)
  float TrackingForCSSPx(float aSize) const;

  mozilla::gfx::Rect GetFontExtents(float aFUnitScaleFactor) const {
    // Flip the y-axis here to match the orientation of Gecko's coordinates.
    return mozilla::gfx::Rect(float(mXMin) * aFUnitScaleFactor,
                              float(-mYMax) * aFUnitScaleFactor,
                              float(mXMax - mXMin) * aFUnitScaleFactor,
                              float(mYMax - mYMin) * aFUnitScaleFactor);
  }

  nsCString mName;
  nsCString mFamilyName;

  // These are mutable so that we can take a read lock within a const method.
  mutable mozilla::RWLock mLock;
  mutable mozilla::Mutex mFeatureInfoLock;

  mozilla::Atomic<gfxCharacterMap*> mCharacterMap;  // strong ref
  gfxCharacterMap* GetCharacterMap() const { return mCharacterMap; }

  mozilla::fontlist::Face* mShmemFace = nullptr;
  const mozilla::fontlist::Family* mShmemFamily = nullptr;

  mozilla::Atomic<const SharedBitSet*> mShmemCharacterMap;
  const SharedBitSet* GetShmemCharacterMap() const {
    return mShmemCharacterMap;
  }

  mozilla::Atomic<const uint8_t*> mUVSData;
  const uint8_t* GetUVSData() const { return mUVSData; }

  mozilla::UniquePtr<gfxUserFontData> mUserFontData;

  mozilla::Atomic<gfxSVGGlyphs*> mSVGGlyphs;
  gfxSVGGlyphs* GetSVGGlyphs() const { return mSVGGlyphs; }

  // list of gfxFonts that are using SVG glyphs
  nsTArray<const gfxFont*> mFontsUsingSVGGlyphs MOZ_GUARDED_BY(mLock);
  nsTArray<gfxFontFeature> mFeatureSettings;
  nsTArray<gfxFontVariation> mVariationSettings;

  mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, bool>> mSupportedFeatures
      MOZ_GUARDED_BY(mFeatureInfoLock);
  mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, hb_set_t*>> mFeatureInputs
      MOZ_GUARDED_BY(mFeatureInfoLock);

  // Color Layer font support. These tables are inert once loaded, so we don't
  // need to hold a lock when reading them.
  mozilla::Atomic<hb_blob_t*> mCOLR;
  mozilla::Atomic<hb_blob_t*> mCPAL;
  hb_blob_t* GetCOLR() const { return mCOLR; }
  hb_blob_t* GetCPAL() const { return mCPAL; }

  // bitvector of substitution space features per script, one each
  // for default and non-default features
  uint32_t mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
  uint32_t
      mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];

  mozilla::Atomic<uint32_t> mUVSOffset;

  uint32_t mLanguageOverride = NO_FONT_LANGUAGE_OVERRIDE;

  WeightRange mWeightRange = WeightRange(FontWeight::FromInt(500));
  StretchRange mStretchRange = StretchRange(FontStretch::NORMAL);
  SlantStyleRange mStyleRange = SlantStyleRange(FontSlantStyle::NORMAL);

  // Font metrics overrides (as multiples of used font size); negative values
  // indicate no override to be applied.
  float mAscentOverride = -1.0;
  float mDescentOverride = -1.0;
  float mLineGapOverride = -1.0;

  // Scaling factor to be applied to the font size.
  float mSizeAdjust = 1.0;

  // For user fonts (only), we need to record whether or not weight/stretch/
  // slant variations should be clamped to the range specified in the entry
  // properties. When the @font-face rule omitted one or more of these
  // descriptors, it is treated as the initial value for font-matching (and
  // so that is what we record in the font entry), but when rendering the
  // range is NOT clamped.
  enum class RangeFlags : uint16_t {
    eNoFlags = 0,
    eAutoWeight = (1 << 0),
    eAutoStretch = (1 << 1),
    eAutoSlantStyle = (1 << 2),

    // Flag to record whether the face has a variable "wght" axis
    // that supports "bold" values, used to disable the application
    // of synthetic-bold effects.
    eBoldVariableWeight = (1 << 3),
    // Whether the face has an 'ital' axis.
    eItalicVariation = (1 << 4),
    // Whether the face has a 'slnt' axis.
    eSlantVariation = (1 << 5),

    // Flags to record if the face uses a non-CSS-compatible scale
    // for weight and/or stretch, in which case we won't map the
    // properties to the variation axes (though they can still be
    // explicitly set using font-variation-settings).
    eNonCSSWeight = (1 << 6),
    eNonCSSStretch = (1 << 7),

    // Whether the font has an 'opsz' axis.
    eOpticalSize = (1 << 8)
  };
  RangeFlags mRangeFlags = RangeFlags::eNoFlags;

  bool mFixedPitch : 1;
  bool mIsBadUnderlineFont : 1;
  bool mIsUserFontContainer : 1;  // userfont entry
  bool mIsDataUserFont : 1;       // platform font entry (data)
  bool mIsLocalUserFont : 1;      // platform font entry (local)
  bool mStandardFace : 1;
  bool mIgnoreGDEF : 1;
  bool mIgnoreGSUB : 1;
  bool mSkipDefaultFeatureSpaceCheck : 1;

  mozilla::Atomic<bool> mSVGInitialized;
  mozilla::Atomic<bool> mHasCmapTable;
  mozilla::Atomic<bool> mGrFaceInitialized;
  mozilla::Atomic<bool> mCheckedForColorGlyph;
  mozilla::Atomic<bool> mCheckedForVariationAxes;

  // Atomic flags that are lazily evaluated - initially set to UNINITIALIZED,
  // changed to NO or YES once we determine the actual value.
  enum class LazyFlag : uint8_t { Uninitialized = 0xff, No = 0, Yes = 1 };

  std::atomic<LazyFlag> mSpaceGlyphIsInvisible;
  std::atomic<LazyFlag> mHasGraphiteTables;
  std::atomic<LazyFlag> mHasGraphiteSpaceContextuals;
  std::atomic<LazyFlag> mHasColorBitmapTable;

  enum class SpaceFeatures : uint8_t {
    Uninitialized = 0xff,
    None = 0,
    HasFeatures = 1 << 0,
    Kerning = 1 << 1,
    NonKerning = 1 << 2
  };

  std::atomic<SpaceFeatures> mHasSpaceFeatures;

 protected:
  friend class gfxPlatformFontList;
  friend class gfxFontFamily;
  friend class gfxUserFontEntry;

  // Protected destructor, to discourage deletion outside of Release():
  virtual ~gfxFontEntry();

  virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle) = 0;

  inline bool CheckForGraphiteTables() {
    return HasFontTable(TRUETYPE_TAG('S', 'i', 'l', 'f'));
  }

  // Copy a font table into aBuffer.
  // The caller will be responsible for ownership of the data.
  virtual nsresult CopyFontTable(uint32_t aTableTag,
                                 nsTArray<uint8_t>& aBuffer) {
    MOZ_ASSERT_UNREACHABLE(
        "forgot to override either GetFontTable or "
        "CopyFontTable?");
    return NS_ERROR_FAILURE;
  }

  // Helper for HasTrackingTable; check/parse the table and cache pointers
  // to the subtables we need. Returns false on failure, in which case the
  // table is unusable.
  bool ParseTrakTable() MOZ_REQUIRES(mLock);

  // lookup the cmap in cached font data
  virtual already_AddRefed<gfxCharacterMap> GetCMAPFromFontInfo(
      FontInfoData* aFontInfoData, uint32_t& aUVSOffset);

  // helper for HasCharacter(), which is what client code should call
  virtual bool TestCharacterMap(uint32_t aCh);

  // Try to set mShmemCharacterMap, based on the char map in mShmemFace;
  // return true if successful, false if it remains null (maybe the parent
  // hasn't handled our SetCharacterMap message yet).
  bool TrySetShmemCharacterMap();

  // Helper for gfxPlatformFontList::CreateFontEntry methods: set properties
  // of the gfxFontEntry based on shared Face and Family records.
  void InitializeFrom(mozilla::fontlist::Face* aFace,
                      const mozilla::fontlist::Family* aFamily);

  // Shaper-specific face objects, shared by all instantiations of the same
  // physical font, regardless of size.
  // Usually, only one of these will actually be created for any given font
  // entry, depending on the font tables that are present.

  // hb_face_t is refcounted internally, so each shaper that's using it will
  // bump the ref count when it acquires the face, and "destroy" (release) it
  // in its destructor. The font entry has only this non-owning reference to
  // the face; when the face is deleted, it will tell the font entry to forget
  // it, so that a new face will be created next time it is needed.
  mozilla::Atomic<hb_face_t*> mHBFace;

  static hb_blob_t* HBGetTable(hb_face_t* face, uint32_t aTag, void* aUserData);

  // Callback that the hb_face will use to tell us when it is being deleted.
  static void HBFaceDeletedCallback(void* aUserData);

  // All libGraphite functionality is sandboxed in an rlbox sandbox. This
  // contains data for the sandbox instance.
  // Currently graphite shaping is only supported on the main thread.
  struct GrSandboxData;
  GrSandboxData* mSandboxData = nullptr;

  // gr_face is -not- refcounted, so it will be owned directly by the font
  // entry, and we'll keep a count of how many references we've handed out;
  // each shaper is responsible to call ReleaseGrFace on its entry when
  // finished with it, so that we know when it can be deleted.
  tainted_opaque_gr<gr_face*> mGrFace;

  // For AAT font, a strong reference to the 'trak' table (if present).
  hb_blob_t* const kTrakTableUninitialized = (hb_blob_t*)(intptr_t(-1));
  mozilla::Atomic<hb_blob_t*> mTrakTable;
  hb_blob_t* GetTrakTable() const { return mTrakTable; }
  bool TrakTableInitialized() const {
    return mTrakTable != kTrakTableUninitialized;
  }

  // Cached pointers to tables within 'trak', initialized by ParseTrakTable.
  // This data is inert once loaded, so locking is not required to read it.
  const mozilla::AutoSwap_PRInt16* mTrakValues = nullptr;
  const mozilla::AutoSwap_PRInt32* mTrakSizeTable = nullptr;

  // number of current users of this entry's mGrFace
  nsrefcnt mGrFaceRefCnt = 0;

  friend class gfxFontEntryCallbacks;

  // For memory reporting: size of user-font data belonging to this entry.
  // We record this in the font entry because the actual data block may be
  // handed over to platform APIs, so that it would become difficult (and
  // platform-specific) to measure it directly at report-gathering time.
  uint32_t mComputedSizeOfUserFont = 0;

  // Font's unitsPerEm from the 'head' table, if available (will be set to
  // kInvalidUPEM for non-sfnt font formats)
  uint16_t mUnitsPerEm = 0;

  uint16_t mNumTrakSizes = 0;

  // Font extents in FUnits. (To be set from the 'head' table; default to
  // "huge" to avoid any clipping if real extents not available.)
  int16_t mXMin = std::numeric_limits<int16_t>::min();
  int16_t mYMin = std::numeric_limits<int16_t>::min();
  int16_t mXMax = std::numeric_limits<int16_t>::max();
  int16_t mYMax = std::numeric_limits<int16_t>::max();

 private:
  /**
   * Font table hashtable, to support GetFontTable for harfbuzz.
   *
   * The harfbuzz shaper (and potentially other clients) needs access to raw
   * font table data. This needs to be cached so that it can be used
   * repeatedly (each time we construct a text run; in some cases, for
   * each character/glyph within the run) without re-fetching large tables
   * every time.
   *
   * Because we may instantiate many gfxFonts for the same physical font
   * file (at different sizes), we should ensure that they can share a
   * single cached copy of the font tables. To do this, we implement table
   * access and sharing on the fontEntry rather than the font itself.
   *
   * The default implementation uses GetFontTable() to read font table
   * data into byte arrays, and wraps them in blobs which are registered in
   * a hashtable.  The hashtable can then return pre-existing blobs to
   * harfbuzz.
   *
   * Harfbuzz will "destroy" the blobs when it is finished with them.  When
   * the last blob reference is removed, the FontTableBlobData user data
   * will remove the blob from the hashtable if still registered.
   */

  class FontTableBlobData;

  /**
   * FontTableHashEntry manages the entries of hb_blob_t's containing font
   * table data.
   *
   * This is used to share font tables across fonts with the same
   * font entry (but different sizes) for use by HarfBuzz.  The hashtable
   * does not own a strong reference to the blob, but keeps a weak pointer,
   * managed by FontTableBlobData.  Similarly FontTableBlobData keeps only a
   * weak pointer to the hashtable, managed by FontTableHashEntry.
   */

  class FontTableHashEntry : public nsUint32HashKey {
   public:
    // Declarations for nsTHashtable

    typedef nsUint32HashKey KeyClass;
    typedef KeyClass::KeyType KeyType;
    typedef KeyClass::KeyTypePointer KeyTypePointer;

    explicit FontTableHashEntry(KeyTypePointer aTag)
        : KeyClass(aTag), mSharedBlobData(nullptr), mBlob(nullptr) {}

    // NOTE: This assumes the new entry belongs to the same hashtable as
    // the old, because the mHashtable pointer in mSharedBlobData (if
    // present) will not be updated.
    FontTableHashEntry(FontTableHashEntry&& toMove)
        : KeyClass(std::move(toMove)),
          mSharedBlobData(std::move(toMove.mSharedBlobData)),
          mBlob(std::move(toMove.mBlob)) {
      toMove.mSharedBlobData = nullptr;
      toMove.mBlob = nullptr;
    }

    ~FontTableHashEntry() { Clear(); }

    // FontTable/Blob API

    // Transfer (not copy) elements of aTable to a new hb_blob_t and
    // return ownership to the caller.  A weak reference to the blob is
    // recorded in the hashtable entry so that others may use the same
    // table.
    hb_blob_t* ShareTableAndGetBlob(
        nsTArray<uint8_t>&& aTable,
        nsTHashtable<FontTableHashEntry>* aHashtable);

    // Return a strong reference to the blob.
    // Callers must hb_blob_destroy the returned blob.
    hb_blob_t* GetBlob() const;

    void Clear();

    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;

   private:
    static void DeleteFontTableBlobData(void* aBlobData);
    // not implemented
    FontTableHashEntry& operator=(FontTableHashEntry& toCopy);

    FontTableBlobData* mSharedBlobData;
    hb_blob_t* mBlob;
  };

  using FontTableCache = nsTHashtable<FontTableHashEntry>;
  mozilla::Atomic<FontTableCache*> mFontTableCache;
  FontTableCache* GetFontTableCache() const { return mFontTableCache; }
};

MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxFontEntry::RangeFlags)
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxFontEntry::SpaceFeatures)

inline bool gfxFontEntry::SupportsItalic() {
  return SlantStyle().Max().IsItalic() ||
         ((mRangeFlags & RangeFlags::eAutoSlantStyle) ==
              RangeFlags::eAutoSlantStyle &&
          HasItalicVariation());
}

inline bool gfxFontEntry::SupportsBold() {
  // bold == weights 600 and above
  // We return true if the face has a max weight descriptor >= 600,
  // OR if it's a user font with auto-weight (no descriptor) and has
  // a weight axis that supports values >= 600
  return Weight().Max().IsBold() ||
         ((mRangeFlags & RangeFlags::eAutoWeight) == RangeFlags::eAutoWeight &&
          HasBoldVariableWeight());
}

inline bool gfxFontEntry::MayUseSyntheticSlant() {
  if (!IsUpright()) {
    return false;  // The resource is already non-upright.
  }
  if (HasSlantVariation()) {
    if (mRangeFlags & RangeFlags::eAutoSlantStyle) {
      return false;
    }
    if (!SlantStyle().IsSingle()) {
      return false;  // The resource has a 'slnt' axis, and has not been
                     // clamped to just its upright setting.
    }
  }
  return true;
}

// used when iterating over all fonts looking for a match for a given character
struct GlobalFontMatch {
  GlobalFontMatch(uint32_t aCharacter, uint32_t aNextCh,
                  const gfxFontStyle& aStyle, eFontPresentation aPresentation)
      : mStyle(aStyle),
        mCh(aCharacter),
        mNextCh(aNextCh),
        mPresentation(aPresentation) {}

  RefPtr<gfxFontEntry> mBestMatch;       // current best match
  RefPtr<gfxFontFamily> mMatchedFamily;  // the family it belongs to
  mozilla::fontlist::Family* mMatchedSharedFamily = nullptr;
  const gfxFontStyle& mStyle;  // style to match
  const uint32_t mCh;          // codepoint to be matched
  const uint32_t mNextCh;      // following codepoint (or zero)
  eFontPresentation mPresentation;
  uint32_t mCount = 0;               // number of fonts matched
  uint32_t mCmapsTested = 0;         // number of cmaps tested
  double mMatchDistance = INFINITY;  // metric indicating closest match
};

class gfxFontFamily {
 public:
  // Used by stylo
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontFamily)

  gfxFontFamily(const nsACString& aName, FontVisibility aVisibility)
      : mName(aName),
        mLock("gfxFontFamily lock"),
        mVisibility(aVisibility),
        mIsSimpleFamily(false),
        mIsBadUnderlineFamily(false),
        mSkipDefaultFeatureSpaceCheck(false),
        mCheckForFallbackFaces(false) {}

  const nsCString& Name() const { return mName; }

  virtual void LocalizedName(nsACString& aLocalizedName);
  virtual bool HasOtherFamilyNames();

  // See https://bugzilla.mozilla.org/show_bug.cgi?id=835204:
  // check the font's 'name' table to see if it has a legacy family name
  // that would have been used by GDI (e.g. to split extra-bold or light
  // faces in a large family into separate "styled families" because of
  // GDI's 4-faces-per-family limitation). If found, the styled family
  // name will be added to the font list's "other family names" table.
  // Note that the caller must already hold the gfxPlatformFontList lock.
  bool CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList);

  // Callers must hold a read-lock for as long as they're using the list.
  const nsTArray<RefPtr<gfxFontEntry>>& GetFontList()
      MOZ_REQUIRES_SHARED(mLock) {
    return mAvailableFonts;
  }
  void ReadLock() MOZ_ACQUIRE_SHARED(mLock) { mLock.ReadLock(); }
  void ReadUnlock() MOZ_RELEASE_SHARED(mLock) { mLock.ReadUnlock(); }

  uint32_t FontListLength() const {
    mozilla::AutoReadLock lock(mLock);
    return mAvailableFonts.Length();
  }

  void AddFontEntry(RefPtr<gfxFontEntry> aFontEntry) {
    mozilla::AutoWriteLock lock(mLock);
    AddFontEntryLocked(aFontEntry);
  }

  void AddFontEntryLocked(RefPtr<gfxFontEntry> aFontEntry) MOZ_REQUIRES(mLock) {
    // Avoid potentially duplicating entries.
    if (mAvailableFonts.Contains(aFontEntry)) {
      return;
    }
    // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces
    // of Times New Roman, because of buggy table in those fonts
    if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() &&
        Name().EqualsLiteral("Times New Roman")) {
      aFontEntry->mIgnoreGDEF = true;
    }
    if (aFontEntry->mFamilyName.IsEmpty()) {
      aFontEntry->mFamilyName = Name();
    } else {
      MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name()));
    }
    aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck;
    mAvailableFonts.AppendElement(aFontEntry);

    // If we're adding a face to a family that has been marked as "simple",
    // we need to ensure any null entries are removed, as well as clearing
    // the flag (which may be set again later).
    if (mIsSimpleFamily) {
      mAvailableFonts.RemoveElementsBy([](const auto& font) { return !font; });
      mIsSimpleFamily = false;
    }
  }

  // note that the styles for this family have been added
  bool HasStyles() const { return mHasStyles; }
  void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }

  void SetCheckedForLegacyFamilyNames(bool aChecked) {
    mCheckedForLegacyFamilyNames = aChecked;
  }

  // choose a specific face to match a style using CSS font matching
  // rules (weight matching occurs here).  may return a face that doesn't
  // precisely match (e.g. normal face when no italic face exists).
  gfxFontEntry* FindFontForStyle(const gfxFontStyle& aFontStyle,
                                 bool aIgnoreSizeTolerance = false);

  virtual void FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
                                    nsTArray<gfxFontEntry*>& aFontEntryList,
                                    bool aIgnoreSizeTolerance = false);

  // Checks for a matching font within the family; used as part of the font
  // fallback process.
  // Note that when this is called, the caller must already be holding the
  // gfxPlatformFontList lock.
  void FindFontForChar(GlobalFontMatch* aMatchData);

  // checks all fonts for a matching font within the family
  void SearchAllFontsForChar(GlobalFontMatch* aMatchData);

  // read in other family names, if any, and use functor to add each into cache
  virtual void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList);

  // set when other family names have been read in
  void SetOtherFamilyNamesInitialized() { mOtherFamilyNamesInitialized = true; }

  // Read in other localized family names, fullnames and Postscript names
  // for all faces and append to lookup tables.
  // Note that when this is called, the caller must already be holding the
  // gfxPlatformFontList lock.
  virtual void ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
                             bool aNeedFullnamePostscriptNames,
                             FontInfoData* aFontInfoData = nullptr);

  // Find faces belonging to this family (platform implementations override).
  // This is a no-op in cases where the family is explicitly populated by other
  // means, rather than being asked to find its faces via system API.
  virtual void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
      MOZ_REQUIRES(mLock){};
  void FindStyleVariations(FontInfoData* aFontInfoData = nullptr) {
    if (mHasStyles) {
      return;
    }
    mozilla::AutoWriteLock lock(mLock);
    FindStyleVariationsLocked(aFontInfoData);
  }

  // search for a specific face using the Postscript name
  gfxFontEntry* FindFont(const nsACString& aFontName,
                         const nsCStringComparator& aCmp) const;

  // Read in cmaps for all the faces.
  // Note that when this is called, the caller must already be holding the
  // gfxPlatformFontList lock.
  void ReadAllCMAPs(FontInfoData* aFontInfoData = nullptr);

  bool TestCharacterMap(uint32_t aCh) {
    if (!mFamilyCharacterMapInitialized) {
      ReadAllCMAPs();
    }
    mozilla::AutoReadLock lock(mLock);
    return mFamilyCharacterMap.test(aCh);
  }

  void ResetCharacterMap() MOZ_REQUIRES(mLock) {
    mFamilyCharacterMap.reset();
    mFamilyCharacterMapInitialized = false;
  }

  // mark this family as being in the "bad" underline offset blocklist
  void SetBadUnderlineFamily() {
    mozilla::AutoWriteLock lock(mLock);
    mIsBadUnderlineFamily = true;
    if (mHasStyles) {
      SetBadUnderlineFonts();
    }
  }

  virtual bool IsSingleFaceFamily() const { return false; }

  bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
  bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }

  // sort available fonts to put preferred (standard) faces towards the end
  void SortAvailableFonts() MOZ_REQUIRES(mLock);

  // check whether the family fits into the simple 4-face model,
  // so we can use simplified style-matching;
  // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
  void CheckForSimpleFamily() MOZ_REQUIRES(mLock);

  // For memory reporter
  virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                      FontListSizes* aSizes) const;
  virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                      FontListSizes* aSizes) const;

#ifdef DEBUG
  // Only used for debugging checks - does a linear search
  bool ContainsFace(gfxFontEntry* aFontEntry);
#endif

  void SetSkipSpaceFeatureCheck(bool aSkipCheck) {
    mSkipDefaultFeatureSpaceCheck = aSkipCheck;
  }

  // Check whether this family is appropriate to include in the Preferences
  // font list for the given langGroup and CSS generic, if the platform lets
  // us determine this.
  // Return true if the family should be included in the list, false to omit.
  // Default implementation returns true for everything, so no filtering
  // will occur; individual platforms may override.
  virtual bool FilterForFontList(nsAtom* aLangGroup,
                                 const nsACString& aGeneric) const {
    return true;
  }

  FontVisibility Visibility() const { return mVisibility; }
  bool IsHidden() const { return Visibility() == FontVisibility::Hidden; }
  bool IsWebFontFamily() const {
    return Visibility() == FontVisibility::Webfont;
  }

 protected:
  // Protected destructor, to discourage deletion outside of Release():
  virtual ~gfxFontFamily();

  bool ReadOtherFamilyNamesForFace(gfxPlatformFontList* aPlatformFontList,
                                   hb_blob_t* aNameTable,
                                   bool useFullName = false);

  // set whether this font family is in "bad" underline offset blocklist.
  void SetBadUnderlineFonts() MOZ_REQUIRES(mLock) {
    for (auto& f : mAvailableFonts) {
      if (f) {
        f->mIsBadUnderlineFont = true;
      }
    }
  }

  nsCString mName;
  nsTArray<RefPtr<gfxFontEntry>> mAvailableFonts MOZ_GUARDED_BY(mLock);
  gfxSparseBitSet mFamilyCharacterMap MOZ_GUARDED_BY(mLock);

  mutable mozilla::RWLock mLock;

  FontVisibility mVisibility;

  mozilla::Atomic<bool> mOtherFamilyNamesInitialized;
  mozilla::Atomic<bool> mFaceNamesInitialized;
  mozilla::Atomic<bool> mHasStyles;
  mozilla::Atomic<bool> mFamilyCharacterMapInitialized;
  mozilla::Atomic<bool> mCheckedForLegacyFamilyNames;
  mozilla::Atomic<bool> mHasOtherFamilyNames;

  bool mIsSimpleFamily : 1 MOZ_GUARDED_BY(mLock);
  bool mIsBadUnderlineFamily : 1;
  bool mSkipDefaultFeatureSpaceCheck : 1;
  bool mCheckForFallbackFaces : 1;  // check other faces for character

  enum {
    // for "simple" families, the faces are stored in mAvailableFonts
    // with fixed positions:
    kRegularFaceIndex = 0,
    kBoldFaceIndex = 1,
    kItalicFaceIndex = 2,
    kBoldItalicFaceIndex = 3,
    // mask values for selecting face with bold and/or italic attributes
    kBoldMask = 0x01,
    kItalicMask = 0x02
  };
};

// Wrapper for either a raw pointer to a mozilla::fontlist::Family in the shared
// font list or a strong pointer to an unshared gfxFontFamily that belongs just
// to the current process.
struct FontFamily {
  FontFamily() = default;
  FontFamily(const FontFamily& aOther) = default;

  explicit FontFamily(RefPtr<gfxFontFamily>&& aFamily)
      : mUnshared(std::move(aFamily)) {}

  explicit FontFamily(gfxFontFamily* aFamily) : mUnshared(aFamily) {}

  explicit FontFamily(mozilla::fontlist::Family* aFamily) : mShared(aFamily) {}

  bool operator==(const FontFamily& aOther) const {
    return mShared == aOther.mShared && mUnshared == aOther.mUnshared;
  }

  bool IsNull() const { return !mShared && !mUnshared; }

  RefPtr<gfxFontFamily> mUnshared;
  mozilla::fontlist::Family* mShared = nullptr;
};

// Struct used in the gfxFontGroup font list to keep track of a font family
// together with the CSS generic (if any) that was mapped to it in this
// particular case (so it can be reported to the DevTools font inspector).
struct FamilyAndGeneric final {
  FamilyAndGeneric()
      : mFamily(), mGeneric(mozilla::StyleGenericFontFamily(0)) {}
  FamilyAndGeneric(const FamilyAndGeneric& aOther) = default;
  explicit FamilyAndGeneric(gfxFontFamily* aFamily,
                            mozilla::StyleGenericFontFamily aGeneric =
                                mozilla::StyleGenericFontFamily(0))
      : mFamily(aFamily), mGeneric(aGeneric) {}
  explicit FamilyAndGeneric(RefPtr<gfxFontFamily>&& aFamily,
                            mozilla::StyleGenericFontFamily aGeneric =
                                mozilla::StyleGenericFontFamily(0))
      : mFamily(std::move(aFamily)), mGeneric(aGeneric) {}
  explicit FamilyAndGeneric(mozilla::fontlist::Family* aFamily,
                            mozilla::StyleGenericFontFamily aGeneric =
                                mozilla::StyleGenericFontFamily(0))
      : mFamily(aFamily), mGeneric(aGeneric) {}
  explicit FamilyAndGeneric(const FontFamily& aFamily,
                            mozilla::StyleGenericFontFamily aGeneric =
                                mozilla::StyleGenericFontFamily(0))
      : mFamily(aFamily), mGeneric(aGeneric) {}

  bool operator==(const FamilyAndGeneric& aOther) const {
    return mFamily == aOther.mFamily && mGeneric == aOther.mGeneric;
  }

  FontFamily mFamily;
  mozilla::StyleGenericFontFamily mGeneric;
};

#endif