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