1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "InspectorFontFace.h"
8 
9 #include "gfxPlatformFontList.h"
10 #include "gfxTextRun.h"
11 #include "gfxUserFontSet.h"
12 #include "nsFontFaceLoader.h"
13 #include "mozilla/gfx/2D.h"
14 #include "brotli/decode.h"
15 #include "zlib.h"
16 #include "mozilla/dom/CSSFontFaceRule.h"
17 #include "mozilla/dom/FontFaceSet.h"
18 #include "mozilla/ServoBindings.h"
19 #include "mozilla/Unused.h"
20 
21 namespace mozilla {
22 namespace dom {
23 
InspectorFontFace(gfxFontEntry * aFontEntry,gfxFontGroup * aFontGroup,FontMatchType aMatchType)24 InspectorFontFace::InspectorFontFace(gfxFontEntry* aFontEntry,
25                                      gfxFontGroup* aFontGroup,
26                                      FontMatchType aMatchType)
27     : mFontEntry(aFontEntry), mFontGroup(aFontGroup), mMatchType(aMatchType) {
28   MOZ_COUNT_CTOR(InspectorFontFace);
29 }
30 
~InspectorFontFace()31 InspectorFontFace::~InspectorFontFace() { MOZ_COUNT_DTOR(InspectorFontFace); }
32 
FromFontGroup()33 bool InspectorFontFace::FromFontGroup() {
34   return bool(mMatchType.kind & FontMatchType::Kind::kFontGroup);
35 }
36 
FromLanguagePrefs()37 bool InspectorFontFace::FromLanguagePrefs() {
38   return bool(mMatchType.kind & FontMatchType::Kind::kPrefsFallback);
39 }
40 
FromSystemFallback()41 bool InspectorFontFace::FromSystemFallback() {
42   return bool(mMatchType.kind & FontMatchType::Kind::kSystemFallback);
43 }
44 
GetName(nsAString & aName)45 void InspectorFontFace::GetName(nsAString& aName) {
46   if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
47     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
48     aName.Append(NS_ConvertUTF8toUTF16(mFontEntry->mUserFontData->mRealName));
49   } else {
50     aName.Append(NS_ConvertUTF8toUTF16(mFontEntry->RealFaceName()));
51   }
52 }
53 
GetCSSFamilyName(nsAString & aCSSFamilyName)54 void InspectorFontFace::GetCSSFamilyName(nsAString& aCSSFamilyName) {
55   aCSSFamilyName.Append(NS_ConvertUTF8toUTF16(mFontEntry->FamilyName()));
56 }
57 
GetCSSGeneric(nsAString & aName)58 void InspectorFontFace::GetCSSGeneric(nsAString& aName) {
59   if (mMatchType.generic != StyleGenericFontFamily::None) {
60     aName.AssignASCII(gfxPlatformFontList::GetGenericName(mMatchType.generic));
61   } else {
62     aName.Truncate(0);
63   }
64 }
65 
GetRule()66 CSSFontFaceRule* InspectorFontFace::GetRule() {
67   if (!mRule) {
68     // check whether this font entry is associated with an @font-face rule
69     // in the relevant font group's user font set
70     RawServoFontFaceRule* rule = nullptr;
71     if (mFontEntry->IsUserFont()) {
72       FontFaceSet::UserFontSet* fontSet =
73           static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
74       if (fontSet) {
75         FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
76         if (fontFaceSet) {
77           rule = fontFaceSet->FindRuleForEntry(mFontEntry);
78         }
79       }
80     }
81     if (rule) {
82       // XXX It would be better if we can share this with CSSOM tree,
83       // but that may require us to create another map, which is not
84       // great either. As far as they would use the same backend, and
85       // we don't really support mutating @font-face rule via CSSOM,
86       // it's probably fine for now.
87       uint32_t line, column;
88       Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
89       mRule =
90           new CSSFontFaceRule(do_AddRef(rule), nullptr, nullptr, line, column);
91     }
92   }
93   return mRule;
94 }
95 
SrcIndex()96 int32_t InspectorFontFace::SrcIndex() {
97   if (mFontEntry->IsUserFont()) {
98     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
99     return mFontEntry->mUserFontData->mSrcIndex;
100   }
101 
102   return -1;
103 }
104 
GetURI(nsAString & aURI)105 void InspectorFontFace::GetURI(nsAString& aURI) {
106   aURI.Truncate();
107   if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
108     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
109     if (mFontEntry->mUserFontData->mURI) {
110       nsAutoCString spec;
111       mFontEntry->mUserFontData->mURI->GetSpec(spec);
112       AppendUTF8toUTF16(spec, aURI);
113     }
114   }
115 }
116 
GetLocalName(nsAString & aLocalName)117 void InspectorFontFace::GetLocalName(nsAString& aLocalName) {
118   aLocalName.Truncate();
119   if (mFontEntry->IsLocalUserFont()) {
120     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
121     aLocalName.Append(
122         NS_ConvertUTF8toUTF16(mFontEntry->mUserFontData->mLocalName));
123   }
124 }
125 
AppendToFormat(nsAString & aResult,const char * aFormat)126 static void AppendToFormat(nsAString& aResult, const char* aFormat) {
127   if (!aResult.IsEmpty()) {
128     aResult.Append(',');
129   }
130   aResult.AppendASCII(aFormat);
131 }
132 
GetFormat(nsAString & aFormat)133 void InspectorFontFace::GetFormat(nsAString& aFormat) {
134   aFormat.Truncate();
135   if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
136     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
137     uint32_t formatFlags = mFontEntry->mUserFontData->mFormat;
138     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_OPENTYPE) {
139       AppendToFormat(aFormat, "opentype");
140     }
141     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE) {
142       AppendToFormat(aFormat, "truetype");
143     }
144     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT) {
145       AppendToFormat(aFormat, "truetype-aat");
146     }
147     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_EOT) {
148       AppendToFormat(aFormat, "embedded-opentype");
149     }
150     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_SVG) {
151       AppendToFormat(aFormat, "svg");
152     }
153     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF) {
154       AppendToFormat(aFormat, "woff");
155     }
156     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF2) {
157       AppendToFormat(aFormat, "woff2");
158     }
159     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_OPENTYPE_VARIATIONS) {
160       AppendToFormat(aFormat, "opentype-variations");
161     }
162     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE_VARIATIONS) {
163       AppendToFormat(aFormat, "truetype-variations");
164     }
165     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF_VARIATIONS) {
166       AppendToFormat(aFormat, "woff-variations");
167     }
168     if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF2_VARIATIONS) {
169       AppendToFormat(aFormat, "woff2-variations");
170     }
171   }
172 }
173 
GetMetadata(nsAString & aMetadata)174 void InspectorFontFace::GetMetadata(nsAString& aMetadata) {
175   aMetadata.Truncate();
176   if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
177     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
178     const gfxUserFontData* userFontData = mFontEntry->mUserFontData.get();
179     if (userFontData->mMetadata.Length() && userFontData->mMetaOrigLen) {
180       nsAutoCString str;
181       str.SetLength(userFontData->mMetaOrigLen);
182       if (str.Length() == userFontData->mMetaOrigLen) {
183         switch (userFontData->mCompression) {
184           case gfxUserFontData::kZlibCompression: {
185             uLongf destLen = userFontData->mMetaOrigLen;
186             if (uncompress((Bytef*)(str.BeginWriting()), &destLen,
187                            (const Bytef*)(userFontData->mMetadata.Elements()),
188                            userFontData->mMetadata.Length()) == Z_OK &&
189                 destLen == userFontData->mMetaOrigLen) {
190               AppendUTF8toUTF16(str, aMetadata);
191             }
192           } break;
193           case gfxUserFontData::kBrotliCompression: {
194             size_t decodedSize = userFontData->mMetaOrigLen;
195             if (BrotliDecoderDecompress(userFontData->mMetadata.Length(),
196                                         userFontData->mMetadata.Elements(),
197                                         &decodedSize,
198                                         (uint8_t*)str.BeginWriting()) == 1 &&
199                 decodedSize == userFontData->mMetaOrigLen) {
200               AppendUTF8toUTF16(str, aMetadata);
201             }
202           } break;
203         }
204       }
205     }
206   }
207 }
208 
209 // Append an OpenType tag to a string as a 4-ASCII-character code.
AppendTagAsASCII(nsAString & aString,uint32_t aTag)210 static void AppendTagAsASCII(nsAString& aString, uint32_t aTag) {
211   aString.AppendPrintf("%c%c%c%c", (aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
212                        (aTag >> 8) & 0xff, aTag & 0xff);
213 }
214 
GetVariationAxes(nsTArray<InspectorVariationAxis> & aResult,ErrorResult & aRV)215 void InspectorFontFace::GetVariationAxes(
216     nsTArray<InspectorVariationAxis>& aResult, ErrorResult& aRV) {
217   if (!mFontEntry->HasVariations()) {
218     return;
219   }
220   AutoTArray<gfxFontVariationAxis, 4> axes;
221   mFontEntry->GetVariationAxes(axes);
222   MOZ_ASSERT(!axes.IsEmpty());
223   if (!aResult.SetCapacity(axes.Length(), mozilla::fallible)) {
224     aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
225     return;
226   }
227   for (auto a : axes) {
228     InspectorVariationAxis& axis = *aResult.AppendElement();
229     AppendTagAsASCII(axis.mTag, a.mTag);
230     axis.mName.Append(NS_ConvertUTF8toUTF16(a.mName));
231     axis.mMinValue = a.mMinValue;
232     axis.mMaxValue = a.mMaxValue;
233     axis.mDefaultValue = a.mDefaultValue;
234   }
235 }
236 
GetVariationInstances(nsTArray<InspectorVariationInstance> & aResult,ErrorResult & aRV)237 void InspectorFontFace::GetVariationInstances(
238     nsTArray<InspectorVariationInstance>& aResult, ErrorResult& aRV) {
239   if (!mFontEntry->HasVariations()) {
240     return;
241   }
242   AutoTArray<gfxFontVariationInstance, 16> instances;
243   mFontEntry->GetVariationInstances(instances);
244   if (!aResult.SetCapacity(instances.Length(), mozilla::fallible)) {
245     aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
246     return;
247   }
248   for (const auto& i : instances) {
249     InspectorVariationInstance& inst = *aResult.AppendElement();
250     inst.mName.Append(NS_ConvertUTF8toUTF16(i.mName));
251     // inst.mValues is a webidl sequence<>, which is a fallible array,
252     // so we are required to use fallible SetCapacity and AppendElement calls,
253     // and check the result. In practice we don't expect failure here; the
254     // list of values cannot get huge because of limits in the font format.
255     if (!inst.mValues.SetCapacity(i.mValues.Length(), mozilla::fallible)) {
256       aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
257       return;
258     }
259     for (const auto& v : i.mValues) {
260       InspectorVariationValue value;
261       AppendTagAsASCII(value.mAxis, v.mAxis);
262       value.mValue = v.mValue;
263       // This won't fail, because of SetCapacity above.
264       Unused << inst.mValues.AppendElement(value, mozilla::fallible);
265     }
266   }
267 }
268 
GetFeatures(nsTArray<InspectorFontFeature> & aResult,ErrorResult & aRV)269 void InspectorFontFace::GetFeatures(nsTArray<InspectorFontFeature>& aResult,
270                                     ErrorResult& aRV) {
271   AutoTArray<gfxFontFeatureInfo, 64> features;
272   mFontEntry->GetFeatureInfo(features);
273   if (features.IsEmpty()) {
274     return;
275   }
276   if (!aResult.SetCapacity(features.Length(), mozilla::fallible)) {
277     aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
278     return;
279   }
280   for (auto& f : features) {
281     InspectorFontFeature& feat = *aResult.AppendElement();
282     AppendTagAsASCII(feat.mTag, f.mTag);
283     AppendTagAsASCII(feat.mScript, f.mScript);
284     AppendTagAsASCII(feat.mLanguageSystem, f.mLangSys);
285   }
286 }
287 
GetRanges(nsTArray<RefPtr<nsRange>> & aResult)288 void InspectorFontFace::GetRanges(nsTArray<RefPtr<nsRange>>& aResult) {
289   aResult = mRanges.Clone();
290 }
291 
AddRange(nsRange * aRange)292 void InspectorFontFace::AddRange(nsRange* aRange) {
293   mRanges.AppendElement(aRange);
294 }
295 
296 }  // namespace dom
297 }  // namespace mozilla
298