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