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