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