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 "mozilla/dom/FontFace.h"
8 
9 #include <algorithm>
10 #include "mozilla/dom/FontFaceBinding.h"
11 #include "mozilla/dom/FontFaceSet.h"
12 #include "mozilla/dom/Promise.h"
13 #include "mozilla/dom/TypedArray.h"
14 #include "mozilla/dom/UnionTypes.h"
15 #include "mozilla/CycleCollectedJSContext.h"
16 #include "mozilla/ServoCSSParser.h"
17 #include "mozilla/ServoStyleSet.h"
18 #include "mozilla/ServoUtils.h"
19 #include "nsCSSFontFaceRule.h"
20 #include "nsCSSParser.h"
21 #include "nsIDocument.h"
22 #include "nsStyleUtil.h"
23 #include "StylePrefs.h"
24 
25 namespace mozilla {
26 namespace dom {
27 
28 // -- FontFaceBufferSource ---------------------------------------------------
29 
30 /**
31  * An object that wraps a FontFace object and exposes its ArrayBuffer
32  * or ArrayBufferView data in a form the user font set can consume.
33  */
34 class FontFaceBufferSource : public gfxFontFaceBufferSource {
35  public:
FontFaceBufferSource(FontFace * aFontFace)36   explicit FontFaceBufferSource(FontFace* aFontFace) : mFontFace(aFontFace) {}
37   virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) override;
38 
39  private:
40   RefPtr<FontFace> mFontFace;
41 };
42 
TakeBuffer(uint8_t * & aBuffer,uint32_t & aLength)43 void FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) {
44   MOZ_ASSERT(mFontFace,
45              "only call TakeBuffer once on a given "
46              "FontFaceBufferSource object");
47   mFontFace->TakeBuffer(aBuffer, aLength);
48   mFontFace = nullptr;
49 }
50 
51 // -- Utility functions ------------------------------------------------------
52 
53 template <typename T>
GetDataFrom(const T & aObject,uint8_t * & aBuffer,uint32_t & aLength)54 static void GetDataFrom(const T& aObject, uint8_t*& aBuffer,
55                         uint32_t& aLength) {
56   MOZ_ASSERT(!aBuffer);
57   aObject.ComputeLengthAndData();
58   // We use malloc here rather than a FallibleTArray or fallible
59   // operator new[] since the gfxUserFontEntry will be calling free
60   // on it.
61   aBuffer = (uint8_t*)malloc(aObject.Length());
62   if (!aBuffer) {
63     return;
64   }
65   memcpy((void*)aBuffer, aObject.Data(), aObject.Length());
66   aLength = aObject.Length();
67 }
68 
69 // -- FontFace ---------------------------------------------------------------
70 
71 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace)
72 
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)
74   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
75   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded)
76   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRule)
77   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
78   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets)
79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
80 
81 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace)
82   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
83   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded)
84   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule)
85   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
86   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets)
87   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
89 
90 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace)
91   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
92 NS_IMPL_CYCLE_COLLECTION_TRACE_END
93 
94 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFace)
95   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
96   NS_INTERFACE_MAP_ENTRY(nsISupports)
97 NS_INTERFACE_MAP_END
98 
99 NS_IMPL_CYCLE_COLLECTING_ADDREF(FontFace)
100 NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace)
101 
102 FontFace::FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet)
103     : mParent(aParent),
104       mLoadedRejection(NS_OK),
105       mStatus(FontFaceLoadStatus::Unloaded),
106       mSourceType(SourceType(0)),
107       mSourceBuffer(nullptr),
108       mSourceBufferLength(0),
109       mFontFaceSet(aFontFaceSet),
110       mUnicodeRangeDirty(true),
111       mInFontFaceSet(false) {}
112 
~FontFace()113 FontFace::~FontFace() {
114   // Assert that we don't drop any FontFace objects during a Servo traversal,
115   // since PostTraversalTask objects can hold raw pointers to FontFaces.
116   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
117 
118   SetUserFontEntry(nullptr);
119 
120   if (mSourceBuffer) {
121     free(mSourceBuffer);
122   }
123 }
124 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)125 JSObject* FontFace::WrapObject(JSContext* aCx,
126                                JS::Handle<JSObject*> aGivenProto) {
127   return FontFaceBinding::Wrap(aCx, this, aGivenProto);
128 }
129 
LoadStateToStatus(gfxUserFontEntry::UserFontLoadState aLoadState)130 static FontFaceLoadStatus LoadStateToStatus(
131     gfxUserFontEntry::UserFontLoadState aLoadState) {
132   switch (aLoadState) {
133     case gfxUserFontEntry::UserFontLoadState::STATUS_NOT_LOADED:
134       return FontFaceLoadStatus::Unloaded;
135     case gfxUserFontEntry::UserFontLoadState::STATUS_LOAD_PENDING:
136     case gfxUserFontEntry::UserFontLoadState::STATUS_LOADING:
137       return FontFaceLoadStatus::Loading;
138     case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED:
139       return FontFaceLoadStatus::Loaded;
140     case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED:
141       return FontFaceLoadStatus::Error;
142   }
143   NS_NOTREACHED("invalid aLoadState value");
144   return FontFaceLoadStatus::Error;
145 }
146 
CreateForRule(nsISupports * aGlobal,FontFaceSet * aFontFaceSet,nsCSSFontFaceRule * aRule)147 already_AddRefed<FontFace> FontFace::CreateForRule(nsISupports* aGlobal,
148                                                    FontFaceSet* aFontFaceSet,
149                                                    nsCSSFontFaceRule* aRule) {
150   RefPtr<FontFace> obj = new FontFace(aGlobal, aFontFaceSet);
151   obj->mRule = aRule;
152   obj->mSourceType = eSourceType_FontFaceRule;
153   obj->mInFontFaceSet = true;
154   return obj.forget();
155 }
156 
Constructor(const GlobalObject & aGlobal,const nsAString & aFamily,const StringOrArrayBufferOrArrayBufferView & aSource,const FontFaceDescriptors & aDescriptors,ErrorResult & aRv)157 already_AddRefed<FontFace> FontFace::Constructor(
158     const GlobalObject& aGlobal, const nsAString& aFamily,
159     const StringOrArrayBufferOrArrayBufferView& aSource,
160     const FontFaceDescriptors& aDescriptors, ErrorResult& aRv) {
161   nsISupports* global = aGlobal.GetAsSupports();
162   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
163   nsIDocument* doc = window->GetDoc();
164   if (!doc) {
165     aRv.Throw(NS_ERROR_FAILURE);
166     return nullptr;
167   }
168 
169   RefPtr<FontFace> obj = new FontFace(global, doc->Fonts());
170   if (!obj->SetDescriptors(aFamily, aDescriptors)) {
171     return obj.forget();
172   }
173 
174   obj->InitializeSource(aSource);
175   return obj.forget();
176 }
177 
InitializeSource(const StringOrArrayBufferOrArrayBufferView & aSource)178 void FontFace::InitializeSource(
179     const StringOrArrayBufferOrArrayBufferView& aSource) {
180   if (aSource.IsString()) {
181     if (!ParseDescriptor(eCSSFontDesc_Src, aSource.GetAsString(),
182                          mDescriptors->mSrc)) {
183       Reject(NS_ERROR_DOM_SYNTAX_ERR);
184 
185       SetStatus(FontFaceLoadStatus::Error);
186       return;
187     }
188 
189     mSourceType = eSourceType_URLs;
190     return;
191   }
192 
193   mSourceType = FontFace::eSourceType_Buffer;
194 
195   if (aSource.IsArrayBuffer()) {
196     GetDataFrom(aSource.GetAsArrayBuffer(), mSourceBuffer, mSourceBufferLength);
197   } else {
198     MOZ_ASSERT(aSource.IsArrayBufferView());
199     GetDataFrom(aSource.GetAsArrayBufferView(), mSourceBuffer,
200                 mSourceBufferLength);
201   }
202 
203   SetStatus(FontFaceLoadStatus::Loading);
204   DoLoad();
205 }
206 
GetFamily(nsString & aResult)207 void FontFace::GetFamily(nsString& aResult) {
208   mFontFaceSet->FlushUserFontSet();
209 
210   // Serialize the same way as in nsCSSFontFaceStyleDecl::GetPropertyValue.
211   nsCSSValue value;
212   GetDesc(eCSSFontDesc_Family, value);
213 
214   aResult.Truncate();
215 
216   if (value.GetUnit() == eCSSUnit_Null) {
217     return;
218   }
219 
220   nsDependentString family(value.GetStringBufferValue());
221   if (!family.IsEmpty()) {
222     // The string length can be zero when the author passed an invalid
223     // family name or an invalid descriptor to the JS FontFace constructor.
224     nsStyleUtil::AppendEscapedCSSString(family, aResult);
225   }
226 }
227 
SetFamily(const nsAString & aValue,ErrorResult & aRv)228 void FontFace::SetFamily(const nsAString& aValue, ErrorResult& aRv) {
229   mFontFaceSet->FlushUserFontSet();
230   SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
231 }
232 
GetStyle(nsString & aResult)233 void FontFace::GetStyle(nsString& aResult) {
234   mFontFaceSet->FlushUserFontSet();
235   GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult);
236 }
237 
SetStyle(const nsAString & aValue,ErrorResult & aRv)238 void FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv) {
239   mFontFaceSet->FlushUserFontSet();
240   SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
241 }
242 
GetWeight(nsString & aResult)243 void FontFace::GetWeight(nsString& aResult) {
244   mFontFaceSet->FlushUserFontSet();
245   GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult);
246 }
247 
SetWeight(const nsAString & aValue,ErrorResult & aRv)248 void FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv) {
249   mFontFaceSet->FlushUserFontSet();
250   SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
251 }
252 
GetStretch(nsString & aResult)253 void FontFace::GetStretch(nsString& aResult) {
254   mFontFaceSet->FlushUserFontSet();
255   GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult);
256 }
257 
SetStretch(const nsAString & aValue,ErrorResult & aRv)258 void FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv) {
259   mFontFaceSet->FlushUserFontSet();
260   SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
261 }
262 
GetUnicodeRange(nsString & aResult)263 void FontFace::GetUnicodeRange(nsString& aResult) {
264   mFontFaceSet->FlushUserFontSet();
265 
266   // There is no eCSSProperty_unicode_range for us to pass in to GetDesc
267   // to get a serialized (possibly defaulted) value, but that function
268   // doesn't use the property ID for this descriptor anyway.
269   GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult);
270 }
271 
SetUnicodeRange(const nsAString & aValue,ErrorResult & aRv)272 void FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv) {
273   mFontFaceSet->FlushUserFontSet();
274   SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
275 }
276 
GetVariant(nsString & aResult)277 void FontFace::GetVariant(nsString& aResult) {
278   mFontFaceSet->FlushUserFontSet();
279 
280   // XXX Just expose the font-variant descriptor as "normal" until we
281   // support it properly (bug 1055385).
282   aResult.AssignLiteral("normal");
283 }
284 
SetVariant(const nsAString & aValue,ErrorResult & aRv)285 void FontFace::SetVariant(const nsAString& aValue, ErrorResult& aRv) {
286   mFontFaceSet->FlushUserFontSet();
287 
288   // XXX Ignore assignments to variant until we support font-variant
289   // descriptors (bug 1055385).
290 }
291 
GetFeatureSettings(nsString & aResult)292 void FontFace::GetFeatureSettings(nsString& aResult) {
293   mFontFaceSet->FlushUserFontSet();
294   GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings,
295           aResult);
296 }
297 
SetFeatureSettings(const nsAString & aValue,ErrorResult & aRv)298 void FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv) {
299   mFontFaceSet->FlushUserFontSet();
300   SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
301 }
302 
GetVariationSettings(nsString & aResult)303 void FontFace::GetVariationSettings(nsString& aResult) {
304   mFontFaceSet->FlushUserFontSet();
305   GetDesc(eCSSFontDesc_FontVariationSettings,
306           eCSSProperty_font_variation_settings, aResult);
307 }
308 
SetVariationSettings(const nsAString & aValue,ErrorResult & aRv)309 void FontFace::SetVariationSettings(const nsAString& aValue, ErrorResult& aRv) {
310   mFontFaceSet->FlushUserFontSet();
311   SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv);
312 }
313 
GetDisplay(nsString & aResult)314 void FontFace::GetDisplay(nsString& aResult) {
315   mFontFaceSet->FlushUserFontSet();
316   GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult);
317 }
318 
SetDisplay(const nsAString & aValue,ErrorResult & aRv)319 void FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv) {
320   mFontFaceSet->FlushUserFontSet();
321   SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
322 }
323 
Status()324 FontFaceLoadStatus FontFace::Status() { return mStatus; }
325 
Load(ErrorResult & aRv)326 Promise* FontFace::Load(ErrorResult& aRv) {
327   MOZ_ASSERT(NS_IsMainThread());
328 
329   mFontFaceSet->FlushUserFontSet();
330 
331   EnsurePromise();
332 
333   if (!mLoaded) {
334     aRv.Throw(NS_ERROR_FAILURE);
335     return nullptr;
336   }
337 
338   // Calling Load on a FontFace constructed with an ArrayBuffer data source,
339   // or on one that is already loading (or has finished loading), has no
340   // effect.
341   if (mSourceType == eSourceType_Buffer ||
342       mStatus != FontFaceLoadStatus::Unloaded) {
343     return mLoaded;
344   }
345 
346   // Calling the user font entry's Load method will end up setting our
347   // status to Loading, but the spec requires us to set it to Loading
348   // here.
349   SetStatus(FontFaceLoadStatus::Loading);
350 
351   DoLoad();
352 
353   return mLoaded;
354 }
355 
CreateUserFontEntry()356 gfxUserFontEntry* FontFace::CreateUserFontEntry() {
357   if (!mUserFontEntry) {
358     MOZ_ASSERT(!HasRule(),
359                "Rule backed FontFace objects should already have a user font "
360                "entry by the time Load() can be called on them");
361 
362     RefPtr<gfxUserFontEntry> newEntry =
363         mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this);
364     if (newEntry) {
365       SetUserFontEntry(newEntry);
366     }
367   }
368 
369   return mUserFontEntry;
370 }
371 
DoLoad()372 void FontFace::DoLoad() {
373   if (!CreateUserFontEntry()) {
374     return;
375   }
376   mUserFontEntry->Load();
377 }
378 
GetLoaded(ErrorResult & aRv)379 Promise* FontFace::GetLoaded(ErrorResult& aRv) {
380   MOZ_ASSERT(NS_IsMainThread());
381 
382   mFontFaceSet->FlushUserFontSet();
383 
384   EnsurePromise();
385 
386   if (!mLoaded) {
387     aRv.Throw(NS_ERROR_FAILURE);
388     return nullptr;
389   }
390 
391   return mLoaded;
392 }
393 
SetStatus(FontFaceLoadStatus aStatus)394 void FontFace::SetStatus(FontFaceLoadStatus aStatus) {
395   AssertIsMainThreadOrServoFontMetricsLocked();
396 
397   if (mStatus == aStatus) {
398     return;
399   }
400 
401   if (aStatus < mStatus) {
402     // We're being asked to go backwards in status!  Normally, this shouldn't
403     // happen.  But it can if the FontFace had a user font entry that had
404     // loaded, but then was given a new one by FontFaceSet::InsertRuleFontFace
405     // if we used a local() rule.  For now, just ignore the request to
406     // go backwards in status.
407     return;
408   }
409 
410   mStatus = aStatus;
411 
412   if (mInFontFaceSet) {
413     mFontFaceSet->OnFontFaceStatusChanged(this);
414   }
415 
416   for (FontFaceSet* otherSet : mOtherFontFaceSets) {
417     otherSet->OnFontFaceStatusChanged(this);
418   }
419 
420   if (mStatus == FontFaceLoadStatus::Loaded) {
421     if (mLoaded) {
422       DoResolve();
423     }
424   } else if (mStatus == FontFaceLoadStatus::Error) {
425     if (mSourceType == eSourceType_Buffer) {
426       Reject(NS_ERROR_DOM_SYNTAX_ERR);
427     } else {
428       Reject(NS_ERROR_DOM_NETWORK_ERR);
429     }
430   }
431 }
432 
DoResolve()433 void FontFace::DoResolve() {
434   AssertIsMainThreadOrServoFontMetricsLocked();
435 
436   if (ServoStyleSet* ss = ServoStyleSet::Current()) {
437     // See comments in Gecko_GetFontMetrics.
438     ss->AppendTask(PostTraversalTask::ResolveFontFaceLoadedPromise(this));
439     return;
440   }
441 
442   mLoaded->MaybeResolve(this);
443 }
444 
DoReject(nsresult aResult)445 void FontFace::DoReject(nsresult aResult) {
446   AssertIsMainThreadOrServoFontMetricsLocked();
447 
448   if (ServoStyleSet* ss = ServoStyleSet::Current()) {
449     // See comments in Gecko_GetFontMetrics.
450     ss->AppendTask(
451         PostTraversalTask::RejectFontFaceLoadedPromise(this, aResult));
452     return;
453   }
454 
455   mLoaded->MaybeReject(aResult);
456 }
457 
ParseDescriptor(nsCSSFontDesc aDescID,const nsAString & aString,nsCSSValue & aResult)458 bool FontFace::ParseDescriptor(nsCSSFontDesc aDescID, const nsAString& aString,
459                                nsCSSValue& aResult) {
460   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
461   nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
462 
463   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
464   nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
465   nsCOMPtr<nsIURI> base = window->GetDocBaseURI();
466 
467   if (mFontFaceSet->Document()->IsStyledByServo()) {
468     RefPtr<URLExtraData> url = new URLExtraData(base, docURI, principal);
469     return ServoCSSParser::ParseFontDescriptor(aDescID, aString, url, aResult);
470   }
471 
472 #ifdef MOZ_OLD_STYLE
473   nsCSSParser parser;
474   if (!parser.ParseFontFaceDescriptor(aDescID, aString,
475                                       docURI,  // aSheetURL
476                                       base, principal, aResult)) {
477     aResult.Reset();
478     return false;
479   }
480 
481   return true;
482 #else
483   MOZ_CRASH("old style system disabled");
484 #endif
485 }
486 
SetDescriptor(nsCSSFontDesc aFontDesc,const nsAString & aValue,ErrorResult & aRv)487 void FontFace::SetDescriptor(nsCSSFontDesc aFontDesc, const nsAString& aValue,
488                              ErrorResult& aRv) {
489   NS_ASSERTION(!HasRule(), "we don't handle rule backed FontFace objects yet");
490   if (HasRule()) {
491     return;
492   }
493 
494   nsCSSValue parsedValue;
495   if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) {
496     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
497     return;
498   }
499 
500   mDescriptors->Get(aFontDesc) = parsedValue;
501 
502   if (aFontDesc == eCSSFontDesc_UnicodeRange) {
503     mUnicodeRangeDirty = true;
504   }
505 
506   // XXX Setting descriptors doesn't actually have any effect on FontFace
507   // objects that have started loading or have already been loaded.
508 }
509 
SetDescriptors(const nsAString & aFamily,const FontFaceDescriptors & aDescriptors)510 bool FontFace::SetDescriptors(const nsAString& aFamily,
511                               const FontFaceDescriptors& aDescriptors) {
512   MOZ_ASSERT(!HasRule());
513   MOZ_ASSERT(!mDescriptors);
514 
515   mDescriptors = new CSSFontFaceDescriptors;
516 
517   // Parse all of the mDescriptors in aInitializer, which are the values
518   // we got from the JS constructor.
519   if (!ParseDescriptor(eCSSFontDesc_Family, aFamily, mDescriptors->mFamily) ||
520       *mDescriptors->mFamily.GetStringBufferValue() == 0 ||
521       !ParseDescriptor(eCSSFontDesc_Style, aDescriptors.mStyle,
522                        mDescriptors->mStyle) ||
523       !ParseDescriptor(eCSSFontDesc_Weight, aDescriptors.mWeight,
524                        mDescriptors->mWeight) ||
525       !ParseDescriptor(eCSSFontDesc_Stretch, aDescriptors.mStretch,
526                        mDescriptors->mStretch) ||
527       !ParseDescriptor(eCSSFontDesc_UnicodeRange, aDescriptors.mUnicodeRange,
528                        mDescriptors->mUnicodeRange) ||
529       !ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
530                        aDescriptors.mFeatureSettings,
531                        mDescriptors->mFontFeatureSettings) ||
532       (StylePrefs::sFontVariationsEnabled &&
533        !ParseDescriptor(eCSSFontDesc_FontVariationSettings,
534                         aDescriptors.mVariationSettings,
535                         mDescriptors->mFontVariationSettings)) ||
536       !ParseDescriptor(eCSSFontDesc_Display, aDescriptors.mDisplay,
537                        mDescriptors->mDisplay)) {
538     // XXX Handle font-variant once we support it (bug 1055385).
539 
540     // If any of the descriptors failed to parse, none of them should be set
541     // on the FontFace.
542     mDescriptors = new CSSFontFaceDescriptors;
543 
544     Reject(NS_ERROR_DOM_SYNTAX_ERR);
545 
546     SetStatus(FontFaceLoadStatus::Error);
547     return false;
548   }
549 
550   return true;
551 }
552 
GetDesc(nsCSSFontDesc aDescID,nsCSSValue & aResult) const553 void FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const {
554   if (HasRule()) {
555     MOZ_ASSERT(mRule);
556     MOZ_ASSERT(!mDescriptors);
557     mRule->GetDesc(aDescID, aResult);
558   } else {
559     aResult = mDescriptors->Get(aDescID);
560   }
561 }
562 
GetDesc(nsCSSFontDesc aDescID,nsCSSPropertyID aPropID,nsString & aResult) const563 void FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSPropertyID aPropID,
564                        nsString& aResult) const {
565   MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange ||
566                  aDescID == eCSSFontDesc_Display ||
567                  aPropID != eCSSProperty_UNKNOWN,
568              "only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange");
569 
570   nsCSSValue value;
571   GetDesc(aDescID, value);
572 
573   aResult.Truncate();
574 
575   // Fill in a default value for missing descriptors.
576   if (value.GetUnit() == eCSSUnit_Null) {
577     if (aDescID == eCSSFontDesc_UnicodeRange) {
578       aResult.AssignLiteral("U+0-10FFFF");
579     } else if (aDescID == eCSSFontDesc_Display) {
580       aResult.AssignLiteral("auto");
581     } else if (aDescID != eCSSFontDesc_Family && aDescID != eCSSFontDesc_Src) {
582       aResult.AssignLiteral("normal");
583     }
584     return;
585   }
586 
587   if (aDescID == eCSSFontDesc_UnicodeRange) {
588     // Since there's no unicode-range property, we can't use
589     // nsCSSValue::AppendToString to serialize this descriptor.
590     nsStyleUtil::AppendUnicodeRange(value, aResult);
591   } else if (aDescID == eCSSFontDesc_Display) {
592     AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(
593                            value.GetIntValue(), nsCSSProps::kFontDisplayKTable),
594                        aResult);
595   } else {
596     value.AppendToString(aPropID, aResult);
597   }
598 }
599 
SetUserFontEntry(gfxUserFontEntry * aEntry)600 void FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry) {
601   if (mUserFontEntry) {
602     mUserFontEntry->mFontFaces.RemoveElement(this);
603   }
604 
605   mUserFontEntry = static_cast<Entry*>(aEntry);
606   if (mUserFontEntry) {
607     mUserFontEntry->mFontFaces.AppendElement(this);
608 
609     MOZ_ASSERT(
610         mUserFontEntry->GetUserFontSet() == mFontFaceSet->GetUserFontSet(),
611         "user font entry must be associated with the same user font set "
612         "as the FontFace");
613 
614     // Our newly assigned user font entry might be in the process of or
615     // finished loading, so set our status accordingly.  But only do so
616     // if we're not going "backwards" in status, which could otherwise
617     // happen in this case:
618     //
619     //   new FontFace("ABC", "url(x)").load();
620     //
621     // where the SetUserFontEntry call (from the after-initialization
622     // DoLoad call) comes after the author's call to load(), which set mStatus
623     // to Loading.
624     FontFaceLoadStatus newStatus =
625         LoadStateToStatus(mUserFontEntry->LoadState());
626     if (newStatus > mStatus) {
627       SetStatus(newStatus);
628     }
629   }
630 }
631 
GetFamilyName(nsString & aResult)632 bool FontFace::GetFamilyName(nsString& aResult) {
633   nsCSSValue value;
634   GetDesc(eCSSFontDesc_Family, value);
635 
636   if (value.GetUnit() == eCSSUnit_String) {
637     nsString familyname;
638     value.GetStringValue(familyname);
639     aResult.Append(familyname);
640   }
641 
642   return !aResult.IsEmpty();
643 }
644 
DisconnectFromRule()645 void FontFace::DisconnectFromRule() {
646   MOZ_ASSERT(HasRule());
647 
648   // Make a copy of the descriptors.
649   mDescriptors = new CSSFontFaceDescriptors;
650   mRule->GetDescriptors(*mDescriptors);
651   mRule = nullptr;
652   mInFontFaceSet = false;
653 }
654 
HasFontData() const655 bool FontFace::HasFontData() const {
656   return mSourceType == eSourceType_Buffer && mSourceBuffer;
657 }
658 
TakeBuffer(uint8_t * & aBuffer,uint32_t & aLength)659 void FontFace::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) {
660   MOZ_ASSERT(HasFontData());
661 
662   aBuffer = mSourceBuffer;
663   aLength = mSourceBufferLength;
664 
665   mSourceBuffer = nullptr;
666   mSourceBufferLength = 0;
667 }
668 
CreateBufferSource()669 already_AddRefed<gfxFontFaceBufferSource> FontFace::CreateBufferSource() {
670   RefPtr<FontFaceBufferSource> bufferSource = new FontFaceBufferSource(this);
671   return bufferSource.forget();
672 }
673 
IsInFontFaceSet(FontFaceSet * aFontFaceSet) const674 bool FontFace::IsInFontFaceSet(FontFaceSet* aFontFaceSet) const {
675   if (mFontFaceSet == aFontFaceSet) {
676     return mInFontFaceSet;
677   }
678   return mOtherFontFaceSets.Contains(aFontFaceSet);
679 }
680 
AddFontFaceSet(FontFaceSet * aFontFaceSet)681 void FontFace::AddFontFaceSet(FontFaceSet* aFontFaceSet) {
682   MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet));
683 
684   if (mFontFaceSet == aFontFaceSet) {
685     mInFontFaceSet = true;
686   } else {
687     mOtherFontFaceSets.AppendElement(aFontFaceSet);
688   }
689 }
690 
RemoveFontFaceSet(FontFaceSet * aFontFaceSet)691 void FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet) {
692   MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet));
693 
694   if (mFontFaceSet == aFontFaceSet) {
695     mInFontFaceSet = false;
696   } else {
697     mOtherFontFaceSets.RemoveElement(aFontFaceSet);
698   }
699 }
700 
Reject(nsresult aResult)701 void FontFace::Reject(nsresult aResult) {
702   AssertIsMainThreadOrServoFontMetricsLocked();
703 
704   if (mLoaded) {
705     DoReject(aResult);
706   } else if (mLoadedRejection == NS_OK) {
707     mLoadedRejection = aResult;
708   }
709 }
710 
EnsurePromise()711 void FontFace::EnsurePromise() {
712   MOZ_ASSERT(NS_IsMainThread());
713 
714   if (mLoaded) {
715     return;
716   }
717 
718   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
719 
720   // If the pref is not set, don't create the Promise (which the page wouldn't
721   // be able to get to anyway) as it causes the window.FontFace constructor
722   // to be created.
723   if (global && FontFaceSet::PrefEnabled()) {
724     ErrorResult rv;
725     mLoaded = Promise::Create(global, rv);
726 
727     if (mStatus == FontFaceLoadStatus::Loaded) {
728       mLoaded->MaybeResolve(this);
729     } else if (mLoadedRejection != NS_OK) {
730       mLoaded->MaybeReject(mLoadedRejection);
731     }
732   }
733 }
734 
GetUnicodeRangeAsCharacterMap()735 gfxCharacterMap* FontFace::GetUnicodeRangeAsCharacterMap() {
736   if (!mUnicodeRangeDirty) {
737     return mUnicodeRange;
738   }
739 
740   nsCSSValue val;
741   GetDesc(eCSSFontDesc_UnicodeRange, val);
742 
743   if (val.GetUnit() == eCSSUnit_Array) {
744     mUnicodeRange = new gfxCharacterMap();
745     const nsCSSValue::Array& sources = *val.GetArrayValue();
746     MOZ_ASSERT(sources.Count() % 2 == 0,
747                "odd number of entries in a unicode-range: array");
748 
749     for (uint32_t i = 0; i < sources.Count(); i += 2) {
750       uint32_t min = sources[i].GetIntValue();
751       uint32_t max = sources[i + 1].GetIntValue();
752       mUnicodeRange->SetRange(min, max);
753     }
754   } else {
755     mUnicodeRange = nullptr;
756   }
757 
758   mUnicodeRangeDirty = false;
759   return mUnicodeRange;
760 }
761 
762 // -- FontFace::Entry --------------------------------------------------------
763 
SetLoadState(UserFontLoadState aLoadState)764 /* virtual */ void FontFace::Entry::SetLoadState(UserFontLoadState aLoadState) {
765   gfxUserFontEntry::SetLoadState(aLoadState);
766 
767   for (size_t i = 0; i < mFontFaces.Length(); i++) {
768     mFontFaces[i]->SetStatus(LoadStateToStatus(aLoadState));
769   }
770 }
771 
GetUserFontSets(nsTArray<gfxUserFontSet * > & aResult)772 /* virtual */ void FontFace::Entry::GetUserFontSets(
773     nsTArray<gfxUserFontSet*>& aResult) {
774   aResult.Clear();
775 
776   for (FontFace* f : mFontFaces) {
777     if (f->mInFontFaceSet) {
778       aResult.AppendElement(f->mFontFaceSet->GetUserFontSet());
779     }
780     for (FontFaceSet* s : f->mOtherFontFaceSets) {
781       aResult.AppendElement(s->GetUserFontSet());
782     }
783   }
784 
785   // Remove duplicates.
786   aResult.Sort();
787   auto it = std::unique(aResult.begin(), aResult.end());
788   aResult.TruncateLength(it - aResult.begin());
789 }
790 
791 }  // namespace dom
792 }  // namespace mozilla
793