3 <title>Test for the CSS Font Loading API
</title>
4 <script src=/tests/SimpleTest/SimpleTest.js
></script>
5 <link rel=stylesheet type=text/css href=/tests/SimpleTest/test.css
>
7 <script src=descriptor_database.js
></script>
9 <body onload=
"runTest()">
10 <iframe id=v
src=
"file_font_loading_api_vframe.html"></iframe>
11 <iframe id=n
style=
"display: none"></iframe>
14 // Map of FontFace descriptor attribute names to @font-face rule descriptor
16 var descriptorNames
= {
18 weight
: "font-weight",
19 stretch
: "font-stretch",
20 unicodeRange
: "unicode-range",
21 variant
: "font-variant",
22 featureSettings
: "font-feature-settings",
23 display
: "font-display"
26 // Default values for the FontFace descriptor attributes other than family, as
27 // Gecko currently serializes them.
32 unicodeRange
: "U+0-10FFFF",
34 featureSettings
: "normal",
38 // Non-default values for the FontFace descriptor attributes other than family
39 // along with how Gecko currently serializes them. Each value is chosen to be
40 // different from the default value and also has a different serialized form.
41 var nonDefaultValues
= {
42 style
: ["Italic", "italic"],
43 weight
: ["Bold", "bold"],
44 stretch
: ["Ultra-Condensed", "ultra-condensed"],
45 unicodeRange
: ["U+3??", "U+300-3FF"],
46 variant
: ["Small-Caps", "small-caps"],
47 featureSettings
: ["'dlig' on", "\"dlig\""],
48 display
: ["Block", "block"]
51 // Invalid values for the FontFace descriptor attributes other than family.
56 unicodeRange
: "U+1????-2????",
58 featureSettings
: "dlig",
62 // Invalid font family names.
63 var invalidFontFamilyNames
= [
64 "", "sans-serif", "A, B", "inherit", "a 1"
67 // Font family list where at least one is likely to be available on
68 // platforms we care about.
69 var likelyPlatformFonts
= "Helvetica Neue, Bitstream Vera Sans, Bitstream Vera Sans Roman, FreeSans, Free Sans, SwissA, DejaVu Sans, Arial";
71 // Will hold an ArrayBuffer containing a valid font.
74 var queue
= Promise
.resolve();
76 function is_resolved_with(aPromise
, aExpectedValue
, aDescription
, aTestID
) {
77 // This assumes that all Promise tasks come from the task source.
79 return new Promise(function(aResolve
, aReject
) {
80 aPromise
.then(function(aValue
) {
83 is(aValue
, aExpectedValue
, aDescription
+ " should be resolved with the expected value " + aTestID
);
89 ok(false, aDescription
+ " should be resolved; instead it was rejected with " + aError
+ " " + aTestID
);
93 Promise
.resolve().then(function() {
96 ok(false, aDescription
+ " should be resolved; instead it is pending " + aTestID
);
103 function is_pending(aPromise
, aDescription
, aTestID
) {
104 // This assumes that all Promise tasks come from the task source.
106 return new Promise(function(aResolve
, aReject
) {
107 aPromise
.then(function(aValue
) {
110 ok(false, aDescription
+ " should be pending; instead it was resolved with " + aValue
+ " " + aTestID
);
113 }, function(aError
) {
116 ok(false, aDescription
+ " should be pending; instead it was rejected with " + aError
+ " " + aTestID
);
120 Promise
.resolve().then(function() {
123 ok(true, aDescription
+ " should be pending " + aTestID
);
130 function fetchAsArrayBuffer(aURL
) {
131 return new Promise(function(aResolve
, aReject
) {
132 var xhr
= new XMLHttpRequest();
133 xhr
.open("GET", aURL
);
134 xhr
.responseType
= "arraybuffer";
135 xhr
.onreadystatechange = function(evt
) {
136 if (xhr
.readyState
== 4) {
137 if (xhr
.status
>= 200 && xhr
.status
<= 299) {
138 aResolve(xhr
.response
);
140 aReject(new Error("Error fetching file " + aURL
+ ", status " + xhr
.status
));
148 function setTimeoutZero() {
149 return new Promise(function(aResolve
, aReject
) {
150 setTimeout(aResolve
, 0);
154 function awaitRefresh() {
155 function awaitOneRefresh() {
156 return new Promise(function(aResolve
, aReject
) {
157 requestAnimationFrame(aResolve
);
161 return awaitOneRefresh().then(awaitOneRefresh
);
164 function flushStyles() {
165 getComputedStyle(document
.body
).width
;
169 // Document and window from inside the display:none iframe.
170 var nframe
= document
.getElementById("n");
171 var ndocument
= nframe
.contentDocument
;
172 var nwindow
= nframe
.contentWindow
;
174 // Document and window from inside the visible iframe.
175 var vframe
= document
.getElementById("v");
176 var vdocument
= vframe
.contentDocument
;
177 var vwindow
= vframe
.contentWindow
;
179 // For iterating over different combinations of documents and windows
182 { win
: window
, doc
: document
, what
: "window/document" },
183 { win
: vwindow
, doc
: vdocument
, what
: "vwindow/vdocument" },
184 { win
: nwindow
, doc
: ndocument
, what
: "nwindow/ndocument" },
185 { win
: window
, doc
: vdocument
, what
: "window/vdocument" },
186 { win
: window
, doc
: ndocument
, what
: "window/ndocument" },
187 { win
: vwindow
, doc
: document
, what
: "vwindow/document" },
188 { win
: vwindow
, doc
: ndocument
, what
: "vwindow/ndocument" },
189 { win
: nwindow
, doc
: document
, what
: "nwindow/document" },
190 { win
: nwindow
, doc
: vdocument
, what
: "nwindow/vdocument" },
193 var sourceDocuments
= [
194 { doc
: document
, what
: "document" },
195 { doc
: vdocument
, what
: "vdocument" },
196 { doc
: ndocument
, what
: "ndocument" },
199 var sourceWindows
= [
200 { win
: window
, what
: "window" },
201 { win
: vwindow
, what
: "vwindow" },
202 { win
: nwindow
, what
: "nwindow" },
205 queue
= queue
.then(function() {
207 // First, initialize fontData.
208 return fetchAsArrayBuffer("BitPattern.woff")
209 .then(function(aResult
) { fontData
= aResult
; });
213 // (TEST 1) Some miscellaneous tests for FontFaceSet and FontFace.
214 ok(window
.FontFaceSet
, "FontFaceSet interface object should be present (TEST 1)");
215 is(Object
.getPrototypeOf(FontFaceSet
.prototype), EventTarget
.prototype, "FontFaceSet should inherit from EventTarget (TEST 1)");
216 ok(document
.fonts
instanceof FontFaceSet
, "document.fonts should be a a FontFaceSet (TEST 1)");
217 ok(window
.FontFace
, "FontFace interface object should be present (TEST 1)");
218 is(Object
.getPrototypeOf(FontFace
.prototype), Object
.prototype, "FontFace should inherit from Object (TEST 1)");
220 // (TEST 2) Some miscellaneous tests for FontFaceSetLoadEvent.
221 ok(window
.FontFaceSetLoadEvent
, "FontFaceSetLoadEvent interface object should be present (TEST 2)");
222 is(Object
.getPrototypeOf(FontFaceSetLoadEvent
.prototype), Event
.prototype, "FontFaceSetLoadEvent should inherit from Event (TEST 2)");
226 // (TEST 3) Test that document.fonts.ready is resolved with the
227 // document.fonts FontFaceSet.
228 var p
= Promise
.resolve();
229 sourceDocuments
.forEach(function({ doc
, what
}) {
230 p
= p
.then(_
=> { return doc
.fonts
.ready
}).then(function() {
231 return is_resolved_with(doc
.fonts
.ready
, doc
.fonts
, "document.fonts.ready resolves with document.fonts.", "(TEST 3) (" + what
+ ")");
238 // (TEST 4) Test that document.fonts in this test document starts out with no
239 // FontFace objects in it.
240 sourceDocuments
.forEach(function({ doc
, what
}) {
241 is(Array
.from(doc
.fonts
).length
, 0, "initial number of FontFace objects in document.fonts (TEST 4) (" + what
+ ")");
244 // (TEST 5) Test that document.fonts.status starts off as loaded.
245 sourceDocuments
.forEach(function({ doc
, what
}) {
246 is(doc
.fonts
.status
, "loaded", "initial value of document.fonts.status (TEST 5) (" + what
+ ")");
249 // (TEST 6) Test initial value of FontFace.status when a url() source is
251 sourceWindows
.forEach(function({ win
, what
}) {
252 is(new win
.FontFace("test", "url(x)").status
, "unloaded", "initial value of FontFace.status when a url() source is used (TEST 6) (" + what
+ ")");
255 // (TEST 7) Test initial value of FontFace.status when an invalid
256 // ArrayBuffer source is used. Because it has an implicit initial
257 // load() call, it should either be "loading" if the browser is
258 // asynchronously parsing the font data, or "error" if it parsed
260 sourceWindows
.forEach(function({ win
, what
}) {
261 var status
= new win
.FontFace("test", new ArrayBuffer(0)).status
;
262 ok(status
== "loading" || status
== "error", "initial value of FontFace.status when an invalid ArrayBuffer source is used (TEST 7) (" + what
+ ")");
265 // (TEST 8) Test initial value of FontFace.status when a valid ArrayBuffer
266 // source is used. Because it has an implicit initial load() call, it
267 // should either be "loading" if the browser is asynchronously parsing the
268 // font data, or "loaded" if it parsed it immediately.
269 sourceWindows
.forEach(function({ win
, what
}) {
270 status
= new win
.FontFace("test", fontData
).status
;
271 ok(status
== "loading" || status
== "loaded", "initial value of FontFace.status when a valid ArrayBuffer source is used (TEST 8) (" + what
+ ")");
274 // (TEST 9) (old test became redundant with TEST 19)
278 // (TEST 10) Test initial value of FontFace.loaded when a valid url()
280 var p
= Promise
.resolve();
281 sourceWindows
.forEach(function({ win
, what
}) {
282 p
= p
.then(function() {
283 return is_pending(new win
.FontFace("test", "url(x)").loaded
, "initial value of FontFace.loaded when a valid url() source is used", "(TEST 10) (" + what
+ ")");
290 // (TEST 11) (old test became redundant with TEST 21)
294 // (TEST 12) (old test became redundant with TEST 20)
298 // (TEST 13) Test initial values of the descriptor attributes on FontFace
300 sourceWindows
.forEach(function({ win
, what
}) {
301 var face
= new win
.FontFace("test", fontData
);
302 // XXX Spec issue: what values do the descriptor attributes have before the
303 // constructor's dictionary argument is parsed?
304 for (var desc
in defaultValues
) {
305 is(face
[desc
], defaultValues
[desc
], "initial value of FontFace." + desc
+ " (TEST 13) (" + what
+ ")");
309 // (TEST 14) Test default values of the FontFaceDescriptors dictionary.
310 var p
= Promise
.resolve();
311 sourceWindows
.forEach(function({ win
, what
}) {
312 p
= p
.then(function() {
313 var face
= new win
.FontFace("test", fontData
);
314 return face
.loaded
.then(function() {
315 for (var desc
in defaultValues
) {
316 is(face
[desc
], defaultValues
[desc
], "default value of FontFace." + desc
+ " (TEST 14) (" + what
+ ")");
318 }, function(aError
) {
319 ok(false, "FontFace should have loaded succesfully (TEST 14) (" + what
+ ")");
327 // (TEST 15) Test passing non-default descriptor values to the FontFace
329 var p
= Promise
.resolve();
330 sourceWindows
.forEach(function({ win
, what
}) {
331 p
= p
.then(function() {
332 var descriptorTests
= Promise
.resolve();
333 Object
.keys(nonDefaultValues
).forEach(function(aDesc
) {
334 descriptorTests
= descriptorTests
.then(function() {
336 init
[aDesc
] = nonDefaultValues
[aDesc
][0];
337 var face
= new win
.FontFace("test", fontData
, init
);
338 var ok_todo
= aDesc
== "variant" ? todo
: ok
;
339 ok_todo(face
[aDesc
] == nonDefaultValues
[aDesc
][1], "specified valid non-default value of FontFace." + aDesc
+ " immediately after construction (TEST 15) (" + what
+ ")");
340 return face
.loaded
.then(function() {
341 ok_todo(face
[aDesc
] == nonDefaultValues
[aDesc
][1], "specified valid non-default value of FontFace." + aDesc
+ " (TEST 15) (" + what
+ ")");
342 }, function(aError
) {
343 ok(false, "FontFace should have loaded succesfully (TEST 15) (" + what
+ ")");
347 return descriptorTests
;
354 // (TEST 16) Test passing invalid descriptor values to the FontFace
356 var p
= Promise
.resolve();
357 sourceWindows
.forEach(function({ win
, what
}) {
358 p
= p
.then(function() {
359 var descriptorTests
= Promise
.resolve();
360 Object
.keys(invalidValues
).forEach(function(aDesc
) {
361 descriptorTests
= descriptorTests
.then(function() {
363 init
[aDesc
] = invalidValues
[aDesc
];
364 var face
= new win
.FontFace("test", fontData
, init
);
365 var ok_todo
= aDesc
== "variant" ? todo
: ok
;
366 ok_todo(face
.status
== "error", "FontFace should be error immediately after construction with invalid value of FontFace." + aDesc
+ " (TEST 16) (" + what
+ ")");
367 return face
.loaded
.then(function() {
368 ok_todo(false, "FontFace should not load after invalid value of FontFace." + aDesc
+ " specified (TEST 16) (" + what
+ ")");
369 }, function(aError
) {
370 ok(true, "FontFace should not load after invalid value of FontFace." + aDesc
+ " specified (TEST 16) (" + what
+ ")");
371 is(aError
.name
, "SyntaxError", "FontFace.loaded with invalid value of FontFace." + aDesc
+ " should be rejected with a SyntaxError (TEST 16) (" + what
+ ")");
375 return descriptorTests
;
382 // (TEST 17) Test passing an invalid font family name to the FontFace
384 var p
= Promise
.resolve();
385 sourceWindows
.forEach(function({ win
, what
}) {
386 p
= p
.then(function() {
387 var familyTests
= Promise
.resolve();
388 invalidFontFamilyNames
.forEach(function(aFamilyName
) {
389 familyTests
= familyTests
.then(function() {
390 var face
= new win
.FontFace(aFamilyName
, fontData
);
391 is(face
.status
, "error", "FontFace should be error immediately after construction with invalid family name " + aFamilyName
+ " (TEST 17) (" + what
+ ")");
392 is(face
.family
, "", "FontFace.family should be the empty string after construction with invalid family name " + aFamilyName
+ " (TEST 17) (" + what
+ ")");
393 return face
.loaded
.then(function() {
394 ok(false, "FontFace should not load after invalid family name " + aFamilyName
+ " specified (TEST 17) (" + what
+ ")");
395 }, function(aError
) {
396 ok(true, "FontFace should not load after invalid family name " + aFamilyName
+ " specified (TEST 17) (" + what
+ ")");
397 is(aError
.name
, "SyntaxError", "FontFace.loaded with invalid family name " + aFamilyName
+ " should be rejected with a SyntaxError (TEST 17) (" + what
+ ")");
408 // (TEST 18) Test passing valid url() source strings to the FontFace
410 var p
= Promise
.resolve();
412 // The sub-test is very fragile on Android platform, see Bug 1455824,
413 // especially Comment 34.
414 if (navigator
.appVersion
.includes("Android")) {
418 sourceWindows
.forEach(function({ win
, what
}) {
419 p
= p
.then(function() {
420 var srcTests
= Promise
.resolve();
421 gCSSFontFaceDescriptors
.src
.values
.forEach(function(aSrc
) {
422 srcTests
= srcTests
.then(function() {
423 var face
= new win
.FontFace("test", aSrc
);
424 return face
.load().then(function() {
425 ok(true, "FontFace should load with valid url() src " + aSrc
+ " (TEST 18) (" + what
+ ")");
426 }, function(aError
) {
427 is(aError
.name
, "NetworkError", "FontFace had NetworkError when loading with valid url() src " + aSrc
+ " (TEST 18) (" + what
+ ")");
438 // (TEST 19) Test passing invalid url() source strings to the FontFace
440 var p
= Promise
.resolve();
441 sourceWindows
.forEach(function({ win
, what
}) {
442 p
= p
.then(function() {
443 var srcTests
= Promise
.resolve();
444 gCSSFontFaceDescriptors
.src
.invalid_values
.forEach(function(aSrc
) {
445 srcTests
= srcTests
.then(function() {
446 var face
= new win
.FontFace("test", aSrc
);
447 is(face
.status
, "error", "FontFace.status should be \"error\" when constructed with an invalid url() src " + aSrc
+ " (TEST 19) (" + what
+ ")");
448 return face
.loaded
.then(function() {
449 ok(false, "FontFace should not load with invalid url() src " + aSrc
+ " (TEST 19) (" + what
+ ")");
450 }, function(aError
) {
451 is(aError
.name
, "SyntaxError", "FontFace.ready should have been rejected with a SyntaxError when constructed with an invalid url() src " + aSrc
+ " (TEST 19) (" + what
+ ")");
462 // (TEST 20) Test that the status of a FontFace constructed with a valid
463 // ArrayBuffer source eventually becomes "loaded".
464 var p
= Promise
.resolve();
465 sourceWindows
.forEach(function({ win
, what
}) {
466 p
= p
.then(function() {
467 var face
= new win
.FontFace("test", fontData
);
468 return face
.loaded
.then(function(aFace
) {
469 is(face
.status
, "loaded", "status of FontFace constructed with a valid ArrayBuffer source should eventually be \"loaded\" (TEST 20) (" + what
+ ")");
470 is(face
, aFace
, "FontFace.loaded was resolved with the FontFace object once loaded (TEST 20) (" + what
+ ")");
471 }, function(aError
) {
472 ok(false, "FontFace constructed with a valid ArrayBuffer should eventually load (TEST 20) (" + what
+ ")");
480 // (TEST 21) Test that the status of a FontFace constructed with an invalid
481 // ArrayBuffer source eventually becomes "error".
482 var p
= Promise
.resolve();
483 sourceWindows
.forEach(function({ win
, what
}) {
484 p
= p
.then(function() {
485 var face
= new win
.FontFace("test", new ArrayBuffer(0));
486 return face
.loaded
.then(function() {
487 ok(false, "FontFace constructed with an invalid ArrayBuffer should not load (TEST 21) (" + what
+ ")");
488 }, function(aError
) {
489 is(aError
.name
, "SyntaxError", "loaded of FontFace constructed with an invalid ArrayBuffer source should be rejected with TypeError (TEST 21) (" + what
+ ")");
490 is(face
.status
, "error", "status of FontFace constructed with an invalid ArrayBuffer source should eventually be \"error\" (TEST 21) (" + what
+ ")");
498 // (TEST 22) Test assigning non-default descriptor values on the FontFace.
499 var p
= Promise
.resolve();
500 sourceWindows
.forEach(function({ win
, what
}) {
501 p
= p
.then(function() {
502 var descriptorTests
= Promise
.resolve();
503 Object
.keys(nonDefaultValues
).forEach(function(aDesc
) {
504 descriptorTests
= descriptorTests
.then(function() {
505 var face
= new win
.FontFace("test", fontData
);
506 return face
.loaded
.then(function() {
507 var ok_todo
= aDesc
== "variant" ? todo
: ok
;
508 face
[aDesc
] = nonDefaultValues
[aDesc
][0];
509 ok_todo(face
[aDesc
] == nonDefaultValues
[aDesc
][1], "assigned valid non-default value to FontFace." + aDesc
+ " (TEST 22) (" + what
+ ")");
510 }, function(aError
) {
511 ok(false, "FontFace should have loaded succesfully (TEST 22) (" + what
+ ")");
515 return descriptorTests
;
522 // (TEST 23) Test assigning invalid descriptor values on the FontFace.
523 var p
= Promise
.resolve();
524 sourceWindows
.forEach(function({ win
, what
}) {
525 p
= p
.then(function() {
526 var descriptorTests
= Promise
.resolve();
527 Object
.keys(invalidValues
).forEach(function(aDesc
) {
528 descriptorTests
= descriptorTests
.then(function() {
529 var face
= new win
.FontFace("test", fontData
);
530 return face
.loaded
.then(function() {
531 var ok_todo
= aDesc
== "variant" ? todo
: ok
;
532 var exceptionName
= "";
534 face
[aDesc
] = invalidValues
[aDesc
];
536 exceptionName
= ex
.name
;
538 ok_todo(exceptionName
== "SyntaxError", "assigning invalid value to FontFace." + aDesc
+ " should throw a SyntaxError (TEST 23) (" + what
+ ")");
539 }, function(aError
) {
540 ok(false, "FontFace should have loaded succesfully (TEST 23) (" + what
+ ")");
544 return descriptorTests
;
551 // (TEST 24) Test that the status of a FontFace with a non-existing url()
552 // source is set to "loading" right after load() is called, that its .loaded
553 // Promise is returned, and that the Promise is eventually rejected with a
554 // NetworkError and its status is set to "error".
555 var p
= Promise
.resolve();
556 sourceWindows
.forEach(function({ win
, what
}) {
557 p
= p
.then(function() {
558 var face
= new win
.FontFace("test", "url(x)");
559 var result
= face
.load();
560 is(face
.status
, "loading", "FontFace.status should be \"loading\" right after load() is called (TEST 24) (" + what
+ ")");
561 is(result
, face
.loaded
, "FontFace.load() should return the .loaded Promise (TEST 24) (" + what
+ ")");
563 return result
.then(function() {
564 ok(false, "FontFace with a non-existing url() source should not load (TEST 24) (" + what
+ ")");
565 }, function(aError
) {
566 is(aError
.name
, "NetworkError", "FontFace with a non-existing url() source should result in its .loaded Promise being rejected with a NetworkError (TEST 24) (" + what
+ ")");
567 is(face
.status
, "error", "FontFace with a non-existing url() source should result in its .status being set to \"error\" (TEST 24) (" + what
+ ")");
575 // (TEST 25) Test simple manipulation of the FontFaceSet.
576 var p
= Promise
.resolve();
577 sources
.forEach(function({ win
, doc
, what
}) {
578 p
= p
.then(function() {
579 var face
, face2
, all
;
580 face
= new win
.FontFace("test", "url(x)");
581 face2
= new win
.FontFace("test2", "url(x)");
582 ok(!doc
.fonts
.has(face
), "newly created FontFace should not be in document.fonts (TEST 25) (" + what
+ ")");
584 ok(doc
.fonts
.has(face
), "should be able to add a FontFace to document.fonts (TEST 25) (" + what
+ ")");
586 ok(doc
.fonts
.has(face
), "should be able to repeatedly add a FontFace to document.fonts (TEST 25) (" + what
+ ")");
587 ok(doc
.fonts
.delete(face
), "FontFaceSet.delete should return true when it succeeds (TEST 25) (" + what
+ ")");
588 ok(!doc
.fonts
.has(face
), "FontFace should be gone from document.fonts after delete is called (TEST 25) (" + what
+ ")");
589 ok(!doc
.fonts
.delete(face
), "FontFaceSet.delete should return false when it fails (TEST 25) (" + what
+ ")");
591 doc
.fonts
.add(face2
);
592 ok(doc
.fonts
.has(face2
), "should be able to add a second FontFace to document.fonts (TEST 25) (" + what
+ ")");
594 ok(!doc
.fonts
.has(face
) && !doc
.fonts
.has(face2
), "FontFaces should be gone from document.fonts after clear is called (TEST 25) (" + what
+ ")");
596 doc
.fonts
.add(face2
);
597 all
= Array
.from(doc
.fonts
);
598 is(all
[0], face
, "FontFaces should be returned in the same order as insertion (TEST 25) (" + what
+ ")");
599 is(all
[1], face2
, "FontFaces should be returned in the same order as insertion (TEST 25) (" + what
+ ")");
601 all
= Array
.from(doc
.fonts
);
602 is(all
[0], face
, "FontFaces should be not be reordered when a duplicate entry is added (TEST 25) (" + what
+ ")");
603 is(all
[1], face2
, "FontFaces should be not be reordered when a duplicate entry is added (TEST 25) (" + what
+ ")");
605 return doc
.fonts
.ready
;
612 // (TEST 26) Test that FontFaceSet.ready is replaced, .status is set to
613 // "loading", and a loading event is dispatched when a loading FontFace is
615 var p
= Promise
.resolve();
616 sources
.forEach(function({ win
, doc
, what
}) {
617 p
= p
.then(function() {
618 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
620 var onloadingTriggered
= false, loadingDispatched
= false;
623 if (onloadingTriggered
&& loadingDispatched
) {
624 doc
.fonts
.onloading
= null;
625 doc
.fonts
.removeEventListener("loading", listener
);
630 var listener = function(aEvent
) {
631 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.Event
.prototype, "loading event should be a plain Event object (TEST 26) (" + what
+ ")");
632 loadingDispatched
= true;
635 doc
.fonts
.addEventListener("loading", listener
);
636 doc
.fonts
.onloading = function(aEvent
) {
637 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.Event
.prototype, "loading event should be a plain Event object (TEST 26) (" + what
+ ")");
638 onloadingTriggered
= true;
643 is(doc
.fonts
.status
, "loaded", "FontFaceSet.status initially (TEST 26) (" + what
+ ")");
645 var oldReady
= doc
.fonts
.ready
;
646 var face
= new win
.FontFace("test", "url(neverending_font_load.sjs)");
650 var newReady
= doc
.fonts
.ready
;
651 isnot(newReady
, oldReady
, "FontFaceSet.ready should be replaced when a loading FontFace is added to it (TEST 26) (" + what
+ ")");
652 is(doc
.fonts
.status
, "loading", "FontFaceSet.status should be set to \"loading\" when a loading FontFace is added to it (TEST 26) (" + what
+ ")");
656 return is_pending(newReady
, "FontFaceSet.ready should be replaced with a fresh pending Promise when a loading FontFace is added to it", "(TEST 26) (" + what
+ ")");
660 return doc
.fonts
.ready
;
668 // (TEST 27) Test that FontFaceSet.ready is resolved, .status is set to
669 // "loaded", and a loadingdone event (but no loadingerror event) is
670 // dispatched when the only loading FontFace in it is removed.
671 var p
= Promise
.resolve();
672 sources
.forEach(function({ win
, doc
, what
}) {
673 p
= p
.then(function() {
674 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
676 var onloadingdoneTriggered
= false, loadingdoneDispatched
= false;
677 var onloadingerrorTriggered
= false, loadingerrorDispatched
= false;
680 doc
.fonts
.onloadingdone
= null;
681 doc
.fonts
.onloadingerror
= null;
682 doc
.fonts
.removeEventListener("loadingdone", doneListener
);
683 doc
.fonts
.removeEventListener("loadingerror", errorListener
);
687 var doneListener = function(aEvent
) {
688 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.FontFaceSetLoadEvent
.prototype, "loadingdone event should be a FontFaceSetLoadEvent object (TEST 27) (" + what
+ ")");
689 is(aEvent
.fontfaces
.length
, 0, "the FontFaceSetLoadEvent should have an empty fontfaces array (TEST 27) (" + what
+ ")");
690 loadingdoneDispatched
= true;
693 doc
.fonts
.addEventListener("loadingdone", doneListener
);
694 doc
.fonts
.onloadingdone = function(aEvent
) {
695 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.FontFaceSetLoadEvent
.prototype, "loadingdone event should be a FontFaceSetLoadEvent object (TEST 27) (" + what
+ ")");
696 is(aEvent
.fontfaces
.length
, 0, "the FontFaceSetLoadEvent should have an empty fontfaces array (TEST 27) (" + what
+ ")");
697 onloadingdoneTriggered
= true;
700 var errorListener = function(aEvent
) {
701 loadingerrorDispatched
= true;
704 doc
.fonts
.addEventListener("loadingerror", errorListener
);
705 doc
.fonts
.onloadingerror = function(aEvent
) {
706 onloadingdoneTriggered
= true;
711 is(doc
.fonts
.status
, "loaded", "FontFaceSet.status should be \"loaded\" initially (TEST 27) (" + what
+ ")");
713 var f
= new win
.FontFace("test", "url(neverending_font_load.sjs)");
717 is(doc
.fonts
.status
, "loading", "FontFaceSet.status should be \"loading\" when a loading FontFace is in it (TEST 27) (" + what
+ ")");
723 return is_resolved_with(doc
.fonts
.ready
, doc
.fonts
, "FontFaceSet.ready when the FontFaceSet is cleared", "(TEST 27) (" + what
+ ")");
726 is(doc
.fonts
.status
, "loaded", "FontFaceSet.status should be set to \"loaded\" when it is cleared (TEST 27) (" + what
+ ")");
727 return doc
.fonts
.ready
;
735 // (TEST 28) Test that FontFaceSet.ready is replaced, .status is set to
736 // "loading", and a loading event is dispatched when a FontFace in it
738 var p
= Promise
.resolve();
739 sources
.forEach(function({ win
, doc
, what
}) {
740 p
= p
.then(function() {
741 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
743 var onloadingTriggered
= false, loadingDispatched
= false;
746 if (onloadingTriggered
&& loadingDispatched
) {
747 doc
.fonts
.onloading
= null;
748 doc
.fonts
.removeEventListener("loading", listener
);
753 var listener = function(aEvent
) {
754 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.Event
.prototype, "loading event should be a plain Event object (TEST 28) (" + what
+ ")");
755 loadingDispatched
= true;
758 doc
.fonts
.addEventListener("loading", listener
);
759 doc
.fonts
.onloading = function(aEvent
) {
760 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.Event
.prototype, "loading event should be a plain Event object (TEST 28) (" + what
+ ")");
761 onloadingTriggered
= true;
766 var oldReady
= doc
.fonts
.ready
;
767 var face
= new win
.FontFace("test", "url(neverending_font_load.sjs)");
771 var newReady
= doc
.fonts
.ready
;
772 isnot(newReady
, oldReady
, "FontFaceSet.ready should be replaced when its only FontFace starts loading (TEST 28) (" + what
+ ")");
773 is(doc
.fonts
.status
, "loading", "FontFaceSet.status should be set to \"loading\" when its only FontFace starts loading (TEST 28) (" + what
+ ")");
777 return is_pending(newReady
, "FontFaceSet.ready when the FontFaceSet's only FontFace starts loading", "(TEST 28) (" + what
+ ")");
781 return doc
.fonts
.ready
;
789 // (TEST 29) Test that a loadingdone and a loadingerror event is dispatched
790 // when a FontFace that eventually becomes status "error" is added to the
792 var p
= Promise
.resolve();
793 sources
.forEach(function({ win
, doc
, what
}) {
794 p
= p
.then(function() {
796 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
798 var onloadingdoneTriggered
= false, loadingdoneDispatched
= false;
799 var onloadingerrorTriggered
= false, loadingerrorDispatched
= false;
802 if (onloadingdoneTriggered
&& loadingdoneDispatched
&&
803 onloadingerrorTriggered
&& loadingerrorDispatched
) {
804 doc
.fonts
.onloadingdone
= null;
805 doc
.fonts
.onloadingerror
= null;
806 doc
.fonts
.removeEventListener("loadingdone", doneListener
);
807 doc
.fonts
.removeEventListener("loadingerror", errorListener
);
812 var doneListener = function(aEvent
) {
813 loadingdoneDispatched
= true;
816 doc
.fonts
.addEventListener("loadingdone", doneListener
);
817 doc
.fonts
.onloadingdone = function(aEvent
) {
818 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.FontFaceSetLoadEvent
.prototype, "loadingdone event should be a FontFaceSetLoadEvent object (TEST 29) (" + what
+ ")");
819 is(aEvent
.fontfaces
.length
, 0, "the FontFaceSetLoadEvent should have an empty fontfaces array (TEST 29) (" + what
+ ")");
820 onloadingdoneTriggered
= true;
823 var errorListener = function(aEvent
) {
824 is(Object
.getPrototypeOf(aEvent
), doc
.defaultView
.FontFaceSetLoadEvent
.prototype, "loadingerror event should be a FontFaceSetLoadEvent object (TEST 29) (" + what
+ ")");
825 is(aEvent
.fontfaces
[0], face
, "the FontFaceSetLoadEvent should have a fontfaces array with the FontFace in it (TEST 29) (" + what
+ ")");
826 loadingerrorDispatched
= true;
829 doc
.fonts
.addEventListener("loadingerror", errorListener
);
830 doc
.fonts
.onloadingerror = function(aEvent
) {
831 onloadingerrorTriggered
= true;
836 face
= new win
.FontFace("test", "url(x)");
838 is(face
.status
, "loading", "FontFace should have status \"loading\" (TEST 29) (" + what
+ ")");
843 ok(false, "the FontFace should not load (TEST 29) (" + what
+ ")");
844 }, function(aError
) {
845 is(face
.status
, "error", "FontFace should have status \"error\" (TEST 29) (" + what
+ ")");
850 return doc
.fonts
.ready
;
858 // (TEST 30) Test that a loadingdone event is dispatched when a FontFace
859 // that eventually becomes status "loaded" is added to the FontFaceSet.
860 var p
= Promise
.resolve();
861 sources
.forEach(function({ win
, doc
, what
}, i
) {
862 p
= p
.then(function() {
864 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
866 var onloadingdoneTriggered
= false, loadingdoneDispatched
= false;
869 if (onloadingdoneTriggered
&& loadingdoneDispatched
) {
870 doc
.fonts
.onloadingdone
= null;
871 doc
.fonts
.removeEventListener("loadingdone", doneListener
);
876 var doneListener = function(aEvent
) {
877 loadingdoneDispatched
= true;
880 doc
.fonts
.addEventListener("loadingdone", doneListener
);
881 doc
.fonts
.onloadingdone = function(aEvent
) {
882 is(aEvent
.fontfaces
[0], face
, "the FontFaceSetLoadEvent should have a fontfaces array with the FontFace in it (TEST 30) (" + what
+ ")");
883 onloadingdoneTriggered
= true;
888 face
= new win
.FontFace("test", "url(BitPattern.woff?test30." + i
+ ")");
890 is(face
.status
, "loading", "FontFace should have status \"loading\" (TEST 30) (" + what
+ ")");
895 is(face
.status
, "loaded", "FontFace should have status \"loaded\" (TEST 30) (" + what
+ ")");
907 // (TEST 31) Test that a loadingdone event is dispatched when a FontFace
908 // with status "unloaded" is added to the FontFaceSet and load() is called
910 var p
= Promise
.resolve();
911 sources
.forEach(function({ win
, doc
, what
}, i
) {
912 p
= p
.then(function() {
914 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
916 var onloadingdoneTriggered
= false, loadingdoneDispatched
= false;
919 if (onloadingdoneTriggered
&& loadingdoneDispatched
) {
920 doc
.fonts
.onloadingdone
= null;
921 doc
.fonts
.removeEventListener("loadingdone", doneListener
);
926 var doneListener = function(aEvent
) {
927 loadingdoneDispatched
= true;
930 doc
.fonts
.addEventListener("loadingdone", doneListener
);
931 doc
.fonts
.onloadingdone = function(aEvent
) {
932 is(aEvent
.fontfaces
[0], face
, "the FontFaceSetLoadEvent should have a fontfaces array with the FontFace in it (TEST 31) (" + what
+ ")");
933 onloadingdoneTriggered
= true;
938 face
= new win
.FontFace("test", "url(BitPattern.woff?test31." + i
+ ")");
939 is(face
.status
, "unloaded", "FontFace should have status \"unloaded\" (TEST 31) (" + what
+ ")");
947 is(face
.status
, "loaded", "FontFace should have status \"loaded\" (TEST 31) (" + what
+ ")");
949 return doc
.fonts
.ready
;
957 // (TEST 32) Test that pending restyles prevent document.fonts.status
958 // from becoming loaded.
959 var face
= new FontFace("test", "url(neverending_font_load.sjs)");
961 document
.fonts
.add(face
);
963 is(document
.fonts
.status
, "loading", "FontFaceSet.status after adding a loading FontFace (TEST 32)");
965 document
.fonts
.clear();
968 is(document
.fonts
.status
, "loaded", "FontFaceSet.status after clearing (TEST 32)");
970 document
.fonts
.add(face
);
972 is(document
.fonts
.status
, "loading", "FontFaceSet.status after adding a loading FontFace again (TEST 32)");
974 var div
= document
.querySelector("div");
975 div
.style
.color
= "blue";
977 document
.fonts
.clear();
978 is(document
.fonts
.status
, "loading", "FontFaceSet.status after clearing but when there is a pending restyle (TEST 32)");
980 return awaitRefresh() // wait for a refresh driver tick
982 is(document
.fonts
.status
, "loaded", "FontFaceSet.status after clearing and the restyle has been flushed (TEST 32)");
983 return document
.fonts
.ready
;
988 // (TEST 33) Test that CSS-connected FontFace objects are created
989 // for @font-face rules in the document.
991 is(document
.fonts
.status
, "loaded", "document.fonts.status should initially be loaded (TEST 33)");
993 var style
= document
.querySelector("style");
994 var ruleText
= "@font-face { font-family: something; src: url(x); ";
995 Object
.keys(nonDefaultValues
).forEach(function(aDesc
) {
996 ruleText
+= descriptorNames
[aDesc
] + ": " + nonDefaultValues
[aDesc
][0] + "; ";
1000 style
.textContent
= ruleText
;
1002 var rule
= style
.sheet
.cssRules
[0];
1004 var all
= Array
.from(document
.fonts
);
1005 is(all
.length
, 1, "document.fonts should contain one FontFace (TEST 33)");
1008 is(face
.family
, "something", "FontFace should have correct family value (TEST 33)");
1009 Object
.keys(nonDefaultValues
).forEach(function(aDesc
) {
1010 var ok_todo
= aDesc
== "variant" ? todo
: ok
;
1011 ok_todo(face
[aDesc
] == nonDefaultValues
[aDesc
][1], "FontFace should have correct " + aDesc
+ " value (TEST 33)");
1014 is(document
.fonts
.status
, "loaded", "document.fonts.status should still be loaded (TEST 33)");
1015 is(face
.status
, "unloaded", "FontFace.status should be unloaded (TEST 33)");
1017 document
.fonts
.clear();
1018 ok(document
.fonts
.has(face
), "CSS-connected FontFace should not be removed from document.fonts when clear is called (TEST 33)");
1020 is(document
.fonts
.delete(face
), false, "attempting to remove CSS-connected FontFace from document.fonts should return false (TEST 33)");
1021 ok(document
.fonts
.has(face
), "CSS-connected FontFace should not be removed from document.fonts when delete is called (TEST 33)");
1023 style
.textContent
= "";
1025 ok(!document
.fonts
.has(face
), "CSS-connected FontFace should be removed from document.fonts once the rule has been removed (TEST 33)");
1027 is(document
.fonts
.status
, "loaded", "document.fonts.status should still be loaded after rule is removed (TEST 33)");
1028 is(face
.status
, "unloaded", "FontFace.status should still be unloaded after rule is removed (TEST 33)");
1030 document
.fonts
.add(face
);
1031 ok(document
.fonts
.has(face
), "previously CSS-connected FontFace should be able to be added to document.fonts (TEST 33)");
1033 is(document
.fonts
.status
, "loaded", "document.fonts.status should still be loaded after now disconnected FontFace is added (TEST 33)");
1034 is(face
.status
, "unloaded", "FontFace.status should still be unloaded after now disconnected FontFace is added (TEST 33)");
1036 document
.fonts
.delete(face
);
1037 ok(!document
.fonts
.has(face
), "previously CSS-connected FontFace should be able to be removed from document.fonts (TEST 33)");
1039 }).then(function() {
1041 // (TEST 34) Test that descriptor getters for unspecified descriptors on
1042 // CSS-connected FontFace objects return their default values.
1043 var style
= document
.querySelector("style");
1044 var ruleText
= "@font-face { font-family: something; src: url(x); }";
1046 style
.textContent
= ruleText
;
1048 var all
= Array
.from(document
.fonts
);
1051 Object
.keys(defaultValues
).forEach(function(aDesc
) {
1052 is(face
[aDesc
], defaultValues
[aDesc
], "FontFace should return default value for " + aDesc
+ " (TEST 34)");
1055 style
.textContent
= "";
1057 }).then(function() {
1059 // (TEST 35) Test that no loadingdone event is dispatched when a FontFace
1060 // with "loaded" status is added to a "loaded" FontFaceSet.
1061 var p
= Promise
.resolve();
1062 sources
.forEach(function({ win
, doc
, what
}) {
1063 p
= p
.then(function() {
1064 var gotLoadingDone
= false;
1065 doc
.fonts
.onloadingdone = function(aEvent
) {
1066 gotLoadingDone
= true;
1069 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" (TEST 35) (" + what
+ ")");
1070 var face
= new win
.FontFace("test", fontData
);
1074 is(face
.status
, "loaded", "FontFace should have status \"loaded\" (TEST 35) (" + what
+ ")");
1075 doc
.fonts
.add(face
);
1076 is(doc
.fonts
.status
, "loaded", "document.fonts.status should still have status \"loaded\" (TEST 35) (" + what
+ ")");
1077 return doc
.fonts
.ready
;
1080 ok(!gotLoadingDone
, "loadingdone event should not be dispatched (TEST 35) (" + what
+ ")");
1081 doc
.fonts
.onloadingdone
= null;
1088 }).then(function() {
1090 // (TEST 36) Test that no loadingdone or loadingerror event is dispatched
1091 // when a FontFace with "error" status is added to a "loaded" FontFaceSet.
1092 var p
= Promise
.resolve();
1093 sources
.forEach(function({ win
, doc
, what
}) {
1094 var doc
= win
.document
;
1095 p
= p
.then(function() {
1096 var gotLoadingDone
= false, gotLoadingError
= false;
1097 doc
.fonts
.onloadingdone = function(aEvent
) {
1098 gotLoadingDone
= true;
1100 doc
.fonts
.onloadingerror = function(aEvent
) {
1101 gotLoadingError
= true;
1104 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" (TEST 36) (" + what
+ ")");
1105 var face
= new win
.FontFace("test", new ArrayBuffer(0));
1109 ok(false, "FontFace should not have loaded (TEST 36) (" + what
+ ")");
1111 is(face
.status
, "error", "FontFace should have status \"error\" (TEST 36) (" + what
+ ")");
1112 doc
.fonts
.add(face
);
1113 is(doc
.fonts
.status
, "loaded", "document.fonts.status should still have status \"loaded\" (TEST 36) (" + what
+ ")");
1114 return doc
.fonts
.ready
;
1117 ok(!gotLoadingDone
, "loadingdone event should not be dispatched (TEST 36) (" + what
+ ")");
1118 ok(!gotLoadingError
, "loadingerror event should not be dispatched (TEST 36) (" + what
+ ")");
1119 doc
.fonts
.onloadingdone
= null;
1120 doc
.fonts
.onloadingerror
= null;
1127 }).then(function() {
1129 // (TEST 37) Test that a FontFace only has one loadingdone event dispatched
1130 // at the FontFaceSet containing it.
1132 var p
= Promise
.resolve();
1133 sources
.forEach(function({ win
, doc
, what
}, i
) {
1134 p
= p
.then(function() {
1135 return setTimeoutZero(); // wait for any previous events to be dispatched
1136 }).then(function() {
1137 var events
= [], face
, face2
;
1139 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
1140 doc
.fonts
.onloadingdone
= doc
.fonts
.onloadingerror = function(e
) {
1142 if (events
.length
== 2) {
1148 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" (TEST 37) (" + what
+ ")");
1150 face
= new win
.FontFace("test", "url(BitPattern.woff?test37." + i
+ "a)");
1152 doc
.fonts
.add(face
);
1153 is(doc
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after first font added (TEST 37) (" + what
+ ")");
1155 return doc
.fonts
.ready
1157 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after first font loaded (TEST 37) (" + what
+ ")");
1158 is(face
.status
, "loaded", "first FontFace should have status \"loaded\" (TEST 37) (" + what
+ ")");
1160 face2
= new win
.FontFace("test2", "url(BitPattern.woff?test37." + i
+ "b)");
1162 doc
.fonts
.add(face2
);
1163 is(doc
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after second font added (TEST 37) (" + what
+ ")");
1165 return doc
.fonts
.ready
;
1166 }).then(function() {
1168 }).then(function() {
1169 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after second font loaded (TEST 37) (" + what
+ ")");
1170 is(face2
.status
, "loaded", "second FontFace should have status \"loaded\" (TEST 37) (" + what
+ ")");
1172 is(events
.length
, 2, "should receive two events (TEST 37) (" + what
+ ")");
1174 is(events
[0].type
, "loadingdone", "first event should be \"loadingdone\" (TEST 37) (" + what
+ ")");
1175 is(events
[0].fontfaces
.length
, 1, "first event should have 1 FontFace (TEST 37) (" + what
+ ")");
1176 is(events
[0].fontfaces
[0], face
, "first event should have the first FontFace");
1178 is(events
[1].type
, "loadingdone", "second event should be \"loadingdone\" (TEST 37) (" + what
+ ")");
1179 is(events
[1].fontfaces
.length
, 1, "second event should only have 1 FontFace (TEST 37) (" + what
+ ")");
1180 is(events
[1].fontfaces
[0], face2
, "second event should have the second FontFace (TEST 37) (" + what
+ ")");
1182 doc
.fonts
.onloadingdone
= null;
1183 doc
.fonts
.onloadingerror
= null;
1185 return doc
.fonts
.ready
;
1191 }).then(function() {
1193 // (TEST 38) Test that a FontFace only has one loadingerror event dispatched
1194 // at the FontFaceSet containing it.
1196 var p
= Promise
.resolve();
1197 sources
.forEach(function({ win
, doc
, what
}) {
1198 p
= p
.then(function() {
1199 return setTimeoutZero(); // wait for any previous events to be dispatched
1200 }).then(function() {
1201 var events
= [], face
, face2
;
1203 var awaitEvents
= new Promise(function(aResolve
, aReject
) {
1204 doc
.fonts
.onloadingdone
= doc
.fonts
.onloadingerror = function(e
) {
1206 if (events
.length
== 4) {
1212 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" (TEST 38) (" + what
+ ")");
1214 face
= new win
.FontFace("test", "url(x)");
1216 doc
.fonts
.add(face
);
1217 is(doc
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after first font added (TEST 38) (" + what
+ ")");
1219 return doc
.fonts
.ready
1221 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after first font failed to load (TEST 38) (" + what
+ ")");
1222 is(face
.status
, "error", "first FontFace should have status \"error\" (TEST 38) (" + what
+ ")");
1224 face2
= new win
.FontFace("test2", "url(x)");
1226 doc
.fonts
.add(face2
);
1227 is(doc
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after second font added (TEST 38) (" + what
+ ")");
1229 return doc
.fonts
.ready
;
1230 }).then(function() {
1232 }).then(function() {
1233 is(doc
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after second font failed to load (TEST 38) (" + what
+ ")");
1234 is(face2
.status
, "error", "second FontFace should have status \"error\" (TEST 38) (" + what
+ ")");
1236 is(events
.length
, 4, "should receive four events (TEST 38) (" + what
+ ")");
1238 is(events
[0].type
, "loadingdone", "first event should be \"loadingdone\" (TEST 38) (" + what
+ ")");
1239 is(events
[0].fontfaces
.length
, 0, "first event should have no FontFaces (TEST 38) (" + what
+ ")");
1241 is(events
[1].type
, "loadingerror", "second event should be \"loadingerror\" (TEST 38) (" + what
+ ")");
1242 is(events
[1].fontfaces
.length
, 1, "second event should have 1 FontFace (TEST 38) (" + what
+ ")");
1243 is(events
[1].fontfaces
[0], face
, "second event should have the first FontFace");
1245 is(events
[2].type
, "loadingdone", "third event should be \"loadingdone\" (TEST 38) (" + what
+ ")");
1246 is(events
[2].fontfaces
.length
, 0, "third event should have no FontFaces (TEST 38) (" + what
+ ")");
1248 is(events
[3].type
, "loadingerror", "third event should be \"loadingerror\" (TEST 38) (" + what
+ ")");
1249 is(events
[3].fontfaces
.length
, 1, "third event should only have 1 FontFace (TEST 38) (" + what
+ ")");
1250 is(events
[3].fontfaces
[0], face2
, "third event should have the second FontFace");
1252 doc
.fonts
.onloadingdone
= null;
1253 doc
.fonts
.onloadingerror
= null;
1255 return doc
.fonts
.ready
;
1261 }).then(function() {
1263 // (TEST 39) Test that a FontFace for an @font-face rule only has one
1264 // loadingdone event dispatched at the FontFaceSet containing it.
1266 var style
, all
, events
, awaitEvents
;
1268 return setTimeoutZero() // wait for any previous events to be dispatched
1270 style
= document
.querySelector("style");
1271 var ruleText
= "@font-face { font-family: test; src: url(BitPattern.woff?test39a); } " +
1272 "@font-face { font-family: test2; src: url(BitPattern.woff?test39b); }";
1274 style
.textContent
= ruleText
;
1276 all
= Array
.from(document
.fonts
);
1279 awaitEvents
= new Promise(function(aResolve
, aReject
) {
1280 document
.fonts
.onloadingdone
= document
.fonts
.onloadingerror = function(e
) {
1282 if (events
.length
== 2) {
1288 is(document
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" (TEST 39)");
1291 is(document
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after first font loading (TEST 39)");
1293 return document
.fonts
.ready
1294 }).then(function() {
1295 is(document
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after first font loaded (TEST 39)");
1296 is(all
[0].status
, "loaded", "first FontFace should have status \"loaded\" (TEST 39)");
1297 is(all
[1].status
, "unloaded", "second FontFace should have status \"unloaded\" (TEST 39)");
1300 is(document
.fonts
.status
, "loading", "document.fonts.status should have status \"loading\" after second font loading (TEST 39)");
1302 return document
.fonts
.ready
;
1303 }).then(function() {
1305 }).then(function() {
1306 is(document
.fonts
.status
, "loaded", "document.fonts.status should have status \"loaded\" after second font loaded (TEST 39)");
1307 is(all
[1].status
, "loaded", "second FontFace should have status \"loaded\" (TEST 39)");
1309 is(events
.length
, 2, "should receive two events (TEST 39)");
1311 is(events
[0].type
, "loadingdone", "first event should be \"loadingdone\" (TEST 39)");
1312 is(events
[0].fontfaces
.length
, 1, "first event should have 1 FontFace (TEST 39)");
1313 is(events
[0].fontfaces
[0], all
[0], "first event should have the first FontFace");
1315 is(events
[1].type
, "loadingdone", "second event should be \"loadingdone\" (TEST 39)");
1316 is(events
[1].fontfaces
.length
, 1, "second event should only have 1 FontFace (TEST 39)");
1317 is(events
[1].fontfaces
[0], all
[1], "second event should have the second FontFace (TEST 39)");
1319 style
.textContent
= "";
1321 document
.fonts
.onloadingdone
= null;
1322 document
.fonts
.onloadingerror
= null;
1323 document
.fonts
.clear();
1324 return document
.fonts
.ready
;
1327 }).then(function() {
1329 // (TEST 40) Test that an attempt to add the same FontFace object a second
1330 // time to a FontFaceSet (where one of the FontFace objects is reflecting
1331 // an @font-face rule) will be ignored.
1333 // First set up a @font-face rule.
1334 var style
= document
.querySelector("style");
1335 style
.textContent
= "@font-face { font-family: something; src: url(x); }";
1337 // Then add a couple of non-connected FontFace objects.
1338 var f1
= new FontFace("test1", "url(x)");
1339 var f2
= new FontFace("test2", "url(x)");
1341 document
.fonts
.add(f1
);
1342 document
.fonts
.add(f2
);
1344 var all
= Array
.from(document
.fonts
);
1345 var ruleFontFace
= all
[0];
1347 is(all
.length
, 3, "number of FontFace objects in the FontFaceSet before duplicate add (TEST 40)");
1348 is(all
[1], f1
, "first non-connected FontFace object in the FontFaceSet before duplicate add (TEST 40)");
1349 is(all
[2], f2
, "second non-connected FontFace object in the FontFaceSet before duplicate add (TEST 40)");
1351 document
.fonts
.add(f1
);
1353 all
= Array
.from(document
.fonts
);
1354 is(all
.length
, 3, "number of FontFace objects in the FontFaceSet after duplicate add #1 (TEST 40)");
1355 is(all
[0], ruleFontFace
, "rule-based FontFace object in the FontFaceSEt after duplicate add #1 (TEST 40)");
1356 is(all
[1], f1
, "first non-connected FontFace object in the FontFaceSet after duplicate add #1 (TEST 40)");
1357 is(all
[2], f2
, "second non-connected FontFace object in the FontFaceSet after duplicate add #1 (TEST 40)");
1359 document
.fonts
.add(ruleFontFace
);
1361 all
= Array
.from(document
.fonts
);
1362 is(all
.length
, 3, "number of FontFace objects in the FontFaceSet after duplicate add #2 (TEST 40)");
1363 is(all
[0], ruleFontFace
, "rule-based FontFace object in the FontFaceSEt after duplicate add #2 (TEST 40)");
1364 is(all
[1], f1
, "first non-connected FontFace object in the FontFaceSet after duplicate add #2 (TEST 40)");
1365 is(all
[2], f2
, "second non-connected FontFace object in the FontFaceSet after duplicate add #2 (TEST 40)");
1367 style
.textContent
= "";
1369 document
.fonts
.clear();
1371 }).then(function() {
1373 // (TEST 41) Test that an attempt to add the same FontFace object a second
1374 // time to a FontFaceSet (where none of the FontFace objects are reflecting
1375 // an @font-face rule) will be ignored.
1377 sources
.forEach(function({ win
, doc
, what
}) {
1378 // Add a couple of non-connected FontFace objects.
1379 var f1
= new win
.FontFace("test1", "url(x)");
1380 var f2
= new win
.FontFace("test2", "url(x)");
1385 var all
= Array
.from(doc
.fonts
);
1387 is(all
.length
, 2, "number of FontFace objects in the FontFaceSet before duplicate add (TEST 41) (" + what
+ ")");
1388 is(all
[0], f1
, "first non-connected FontFace object in the FontFaceSet before duplicate add (TEST 41) (" + what
+ ")");
1389 is(all
[1], f2
, "second non-connected FontFace object in the FontFaceSet before duplicate add (TEST 41) (" + what
+ ")");
1393 all
= Array
.from(doc
.fonts
);
1394 is(all
.length
, 2, "number of FontFace objects in the FontFaceSet after duplicate add (TEST 41) (" + what
+ ")");
1395 is(all
[0], f1
, "first non-connected FontFace object in the FontFaceSet after duplicate add (TEST 41) (" + what
+ ")");
1396 is(all
[1], f2
, "second non-connected FontFace object in the FontFaceSet after duplicate add (TEST 41) (" + what
+ ")");
1401 }).then(function() {
1403 // (TEST 42) Test that adding a FontFace to multiple FontFaceSets and then
1404 // loading it updates the status of all FontFaceSets.
1406 var face
= new FontFace("test", "url(x)");
1408 sourceDocuments
.forEach(function({ doc
, what
}) {
1409 doc
.fonts
.add(face
);
1412 sourceDocuments
.forEach(function({ doc
, what
}) {
1413 is(doc
.fonts
.status
, "loaded", what
+ ".fonts.status before loading (TEST 42)");
1418 sourceDocuments
.forEach(function({ doc
, what
}) {
1419 is(doc
.fonts
.status
, "loading", what
+ ".fonts.status after loading started (TEST 42)");
1422 return Promise
.all(sourceDocuments
.map(function({ doc
}) { return doc
.fonts
.ready
; }))
1424 is(face
.status
, "error", "FontFace.status after loading finished (TEST 42)");
1425 sourceDocuments
.forEach(function({ doc
, what
}) {
1426 is(doc
.fonts
.status
, "loaded", what
+ ".fonts.status after loading finished (TEST 42)");
1429 sourceDocuments
.forEach(function({ doc
, what
}) {
1434 }).then(function() {
1436 // (TEST 43) Test the check method with platform fonts and some
1437 // degenerate cases.
1439 sourceDocuments
.forEach(function({ doc
, what
}) {
1440 // Invalid font shorthands should throw a SyntaxError.
1442 doc
.fonts
.check("Helvetica");
1443 ok(false, "check should throw when a syntactically invalid font shorthand is given (TEST 43) (" + what
+ ")");
1445 is(ex
.name
, "SyntaxError", "exception name when check is called with a syntactically invalid font shorthand (TEST 43) (" + what
+ ")");
1448 // System fonts should throw a SyntaxError.
1450 doc
.fonts
.check("caption");
1451 ok(false, "check should throw when a system font value is given (TEST 43) (" + what
+ ")");
1453 is(ex
.name
, "SyntaxError", "exception name when check is called with a system font value (TEST 43) (" + what
+ ")");
1456 // CSS-wide keywords should throw a SyntaxError.
1458 doc
.fonts
.check("inherit");
1459 ok(false, "check should throw when a CSS-wide keyword is given (TEST 43) (" + what
+ ")");
1461 is(ex
.name
, "SyntaxError", "exception name when check is called with a CSS-wide keyword (TEST 43) (" + what
+ ")");
1464 // CSS variables should throw a SyntaxError.
1466 doc
.fonts
.check("16px var(--family)");
1467 ok(false, "check should throw when CSS variables are used (TEST 43) (" + what
+ ")");
1469 is(ex
.name
, "SyntaxError", "exception name when check is called with CSS variables (TEST 43) (" + what
+ ")");
1472 // No matching font family names => return true.
1473 is(doc
.fonts
.check("16px NonExistentFont1, NonExistentFont2"), true, "check return value when no matching font family names are used (TEST 43) (" + what
+ ")");
1475 // Matching platform font family name => return true.
1476 is(doc
.fonts
.check("16px NonExistentFont, " + likelyPlatformFonts
), true, "check return value when a matching platform font family name is used (TEST 43) (" + what
+ ")");
1478 // Matching platform font family name, but using a different test
1479 // strings. (Platform fonts always return true from check, regardless
1480 // of the actual glyphs present.)
1482 { test
: "\0", desc
: "a single non-matching glyph" },
1483 { test
: "A\0", desc
: "a matching and a non-matching glyph" },
1484 { test
: "A", desc
: "a matching glyph" },
1485 { test
: "AB", desc
: "multiple matching glyphs" }
1486 ].forEach(function({ test
, desc
}) {
1487 is(doc
.fonts
.check("16px " + likelyPlatformFonts
, test
), true, "check return value when a matching platform font family name is used but with " + desc
+ " (TEST 43) (" + what
+ ")");
1490 // No matching font family name, but an empty test string.
1491 is(doc
.fonts
.check("16px NonExistentFont", ""), true, "check return value with a non-matching font family name and an empty test string (TEST 43) (" + what
+ ")");
1493 // Matching platform font family name, but empty test string.
1494 is(doc
.fonts
.check("16px " + likelyPlatformFonts
, ""), true, "check return value with an empty test string (TEST 43) (" + what
+ ")");
1497 }).then(function() {
1499 // (TEST 44) Test the check method with script-created FontFaces.
1502 // at least one matching FontFace is not loaded ==> false
1503 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded" }] },
1504 { result
: false, font
: "16px Test", faces
: [{ family
: "SecondTest", status
: "loaded" }, { family
: "Test", status
: "unloaded" }] },
1505 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded" }, { family
: "Test", status
: "loaded" }] },
1506 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "loading" }] },
1507 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "error" }] },
1508 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", style
: "italic" }] },
1509 { result
: false, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "600" }, { family
: "Test", status
: "unloaded", weight
: "bold" }] },
1510 { result
: false, font
: "16px Test, SecondTest", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "SecondTest", status
: "unloaded" }] },
1511 { result
: false, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "SecondTest", status
: "unloaded" }] },
1513 // all matching FontFaces are loaded ==> true
1514 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded" }] },
1515 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "Test", status
: "loaded" }] },
1516 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "Test", status
: "unloaded", unicodeRange
: "U+4E0A" }] },
1517 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "Test", status
: "error", unicodeRange
: "U+4E0A" }] },
1518 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "Irrelevant", status
: "unloaded" }] },
1519 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", style
: "italic" }] },
1520 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "bold" }] },
1521 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", stretch
: "condensed" }] },
1522 { result
: true, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "bold" }, { family
: "Test", status
: "unloaded", weight
: "600" }] },
1523 { result
: true, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "Test", status
: "loaded" }, { family
: "SecondTest", status
: "loaded" }] },
1525 // no matching FontFaces at all ==> true
1526 { result
: true, font
: "16px Test", faces
: [] },
1527 { result
: true, font
: "16px Test", faces
: [{ family
: "Irrelevant", status
: "unloaded" }] },
1528 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", unicodeRange
: "U+4E0A" }] },
1529 { result
: true, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "ThirdTest", status
: "loaded" }] },
1531 // matching FontFace for one sample text character is loaded but
1532 // not the other ==> false
1533 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", unicodeRange
: "U+61" }, { family
: "Test", status
: "unloaded", unicodeRange
: "U+62" }] },
1535 // matching FontFaces for separate sample text characters are all
1537 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", unicodeRange
: "U+61" }, { family
: "Test", status
: "loaded", unicodeRange
: "U+62" }] },
1540 sources
.forEach(function({ win
, doc
, what
}, i
) {
1541 tests
.forEach(function({ result
, font
, faces
}, j
) {
1542 faces
.forEach(function(f
, k
) {
1544 if (f
.status
== "loaded") {
1545 fontFace
= new win
.FontFace(f
.family
, fontData
, f
);
1546 } else if (f
.status
== "error") {
1547 fontFace
= new win
.FontFace(f
.family
, new ArrayBuffer(0), f
);
1549 fontFace
= new win
.FontFace(f
.family
, "url(BitPattern.woff?test44." + [i
, j
, k
] + ")", f
);
1550 if (f
.status
== "loading") {
1554 is(fontFace
.status
, f
.status
, "status of newly created FontFace " + [j
, k
] + " (TEST 44) (" + what
+ ")");
1555 doc
.fonts
.add(fontFace
);
1557 is(doc
.fonts
.check(font
, "ab"), result
, "check return value for subtest " + j
+ " (TEST 44) (" + what
+ ")");
1562 }).then(function() {
1564 // (TEST 45) Test the load method with platform fonts and some
1565 // degenerate cases.
1567 var p
= Promise
.resolve();
1568 sources
.forEach(function({ win
, doc
, what
}) {
1569 p
= p
.then(function() {
1570 // Invalid font shorthands should reject the promise with a SyntaxError.
1571 return doc
.fonts
.load("Helvetica").then(function() {
1572 ok(false, "load should reject when a syntactically invalid font shorthand is given (TEST 45) (" + what
+ ")");
1574 is(ex
.name
, "SyntaxError", "exception name when load is called with a syntactically invalid font shorthand (TEST 45) (" + what
+ ")");
1578 p
= p
.then(function() {
1579 // System fonts should reject with a SyntaxError.
1580 return doc
.fonts
.load("caption").then(function() {
1581 ok(false, "load should throw when a system font value is given (TEST 45) (" + what
+ ")");
1583 is(ex
.name
, "SyntaxError", "exception name when load is called with a system font value (TEST 45) (" + what
+ ")");
1587 p
= p
.then(function() {
1588 // CSS-wide keywords should reject with a SyntaxError.
1589 return doc
.fonts
.load("inherit").then(function() {
1590 ok(false, "load should throw when a CSS-wide keyword is given (TEST 45) (" + what
+ ")");
1592 is(ex
.name
, "SyntaxError", "exception name when load is called with a CSS-wide keyword (TEST 45) (" + what
+ ")");
1596 p
= p
.then(function() {
1597 // CSS variables should throw a SyntaxError.
1598 return doc
.fonts
.load("16px var(--family)").then(function() {
1599 ok(false, "load should throw when CSS variables are used (TEST 45) (" + what
+ ")");
1601 is(ex
.name
, "SyntaxError", "exception name when load is called with CSS variables (TEST 45) (" + what
+ ")");
1605 p
= p
.then(function() {
1606 // No matching font family names => return true.
1607 return doc
.fonts
.load("16px NonExistentFont1, NonExistentFont2").then(function(result
) {
1608 is(result
.length
, 0, "load resolves with an emtpy array when no matching font family names are used (TEST 45) (" + what
+ ")");
1612 p
= p
.then(function() {
1613 // Matching platform font family name => return true.
1614 return doc
.fonts
.load("16px NonExistentFont1, " + likelyPlatformFonts
).then(function(result
) {
1615 is(result
.length
, 0, "load resolves with an emtpy array when no matching font family names are used (TEST 45) (" + what
+ ")");
1619 // Matching platform font family name, but using a different test
1620 // strings. (Platform fonts always return true from load, regardless
1621 // of the actual glyphs present.)
1623 { sample
: "\0", desc
: "a single non-matching glyph" },
1624 { sample
: "A\0", desc
: "a matching and a non-matching glyph" },
1625 { sample
: "A", desc
: "a matching glyph" },
1626 { sample
: "AB", desc
: "multiple matching glyphs" }
1627 ].forEach(function({ sample
, desc
}) {
1628 p
= p
.then(function() {
1629 return doc
.fonts
.load("16px " + likelyPlatformFonts
, sample
).then(function(result
) {
1630 is(result
.length
, 0, "load resolves with an empty array when a matching platform font family name is used but with " + desc
+ " (TEST 45) (" + what
+ ")");
1635 p
= p
.then(function() {
1636 // No matching font family name, but an empty test string.
1637 return doc
.fonts
.load("16px NonExistentFont", "").then(function(result
) {
1638 is(result
.length
, 0, "load resolves with an empty array when a non-matching platform font family name and an empty test string is used (TEST 45) (" + what
+ ")");
1642 p
= p
.then(function() {
1643 // Matching font family name, but an empty test string.
1644 return doc
.fonts
.load("16px " + likelyPlatformFonts
, "").then(function(result
) {
1645 is(result
.length
, 0, "load resolves with an empty array when a matching platform font family name and an empty test string is used (TEST 45) (" + what
+ ")");
1651 }).then(function() {
1653 // (TEST 46) Test the load method with script-created FontFaces.
1656 // at least one matching FontFace is not yet loaded, but will load ==> resolve
1657 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", included
: true }] },
1658 { result
: true, font
: "16px Test", faces
: [{ family
: "SecondTest", status
: "loaded" }, { family
: "Test", status
: "unloaded", included
: true }] },
1659 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", included
: true }, { family
: "Test", status
: "loaded", included
: true }] },
1660 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loading", included
: true }] },
1661 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", style
: "italic", included
: true }] },
1662 { result
: true, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "unloaded", weight
: "600", included
: true }] },
1663 { result
: true, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "unloaded", weight
: "600" }, { family
: "Test", status
: "unloaded", weight
: "bold", included
: true }] },
1664 { result
: true, font
: "16px Test, SecondTest", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "SecondTest", status
: "unloaded", included
: true }] },
1665 { result
: true, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "SecondTest", status
: "unloaded", included
: true }] },
1667 // at least one matching FontFace is in an error state ==> reject
1668 { result
: false, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded" }, { family
: "Test", status
: "error" }] },
1670 // all matching FontFaces are already loaded ==> resolve
1671 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", included
: true }] },
1672 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "Test", status
: "loaded", included
: true }] },
1673 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "Test", status
: "unloaded", unicodeRange
: "U+4E0A" }] },
1674 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "Test", status
: "error", unicodeRange
: "U+4E0A" }] },
1675 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "Irrelevant", status
: "unloaded" }] },
1676 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", style
: "italic", included
: true }] },
1677 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "bold", included
: true }] },
1678 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", stretch
: "condensed", included
: true }] },
1679 { result
: true, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "bold", included
: true }, { family
: "Test", status
: "loaded", weight
: "600" }] },
1680 { result
: true, font
: "bold 16px Test", faces
: [{ family
: "Test", status
: "loaded", weight
: "600" }, { family
: "Test", status
: "loaded", weight
: "bold", included
: true }] },
1681 { result
: true, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "Test", status
: "loaded", included
: true }, { family
: "SecondTest", status
: "loaded", included
: true }] },
1683 // no matching FontFaces at all ==> resolve
1684 { result
: true, font
: "16px Test", faces
: [] },
1685 { result
: true, font
: "16px Test", faces
: [{ family
: "Irrelevant", status
: "unloaded" }] },
1686 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "unloaded", unicodeRange
: "U+4E0A" }] },
1687 { result
: true, font
: "16px Test, " + likelyPlatformFonts
+ ", SecondTest, sans-serif", faces
: [{ family
: "ThirdTest", status
: "loaded" }] },
1689 // matching FontFace for one sample text character is already loaded but
1690 // the other is not (but will) ==> resolve
1691 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", unicodeRange
: "U+61", included
: true }, { family
: "Test", status
: "unloaded", unicodeRange
: "U+62", included
: true }] },
1693 // matching FontFaces for separate sample text characters are all
1694 // loaded ==> resolve
1695 { result
: true, font
: "16px Test", faces
: [{ family
: "Test", status
: "loaded", unicodeRange
: "U+61", included
: true }, { family
: "Test", status
: "loaded", unicodeRange
: "U+62", included
: true }] },
1698 var p
= Promise
.resolve();
1699 sources
.forEach(function({ win
, doc
, what
}, i
) {
1700 tests
.forEach(function({ result
, font
, faces
}, j
) {
1701 p
= p
.then(function() {
1703 faces
.forEach(function(f
, k
) {
1705 if (f
.status
== "loaded") {
1706 fontFace
= new win
.FontFace(f
.family
, fontData
, f
);
1707 } else if (f
.status
== "error") {
1708 fontFace
= new win
.FontFace(f
.family
, new ArrayBuffer(0), f
);
1710 fontFace
= new win
.FontFace(f
.family
, "url(BitPattern.woff?test46." + [i
, j
, k
] + ")", f
);
1711 if (f
.status
== "loading") {
1715 is(fontFace
.status
, f
.status
, "status of newly created FontFace " + [j
, k
] + " (TEST 46) (" + what
+ ")");
1716 doc
.fonts
.add(fontFace
);
1717 fontFaces
.push(fontFace
);
1719 return doc
.fonts
.load(font
, "ab").then(function(array
) {
1720 ok(result
, "load should resolve for subtest " + j
+ " (TEST 46) (" + what
+ ")");
1722 for (var k
= 0; k
< faces
.length
; k
++) {
1723 if (faces
[k
].included
) {
1724 expected
.push(fontFaces
[k
]);
1727 is(array
.length
, expected
.length
, "length of array load resolves with for subtest " + j
+ " (TEST 46) (" + what
+ ")");
1728 for (var k
= 0; k
< array
.length
; k
++) {
1729 is(array
[k
], expected
[k
], "value in array[" + k
+ "] load resolves with for subtest " + j
+ " (TEST 46) (" + what
+ ")");
1732 ok(!result
, "load should not resolve for subtest " + j
+ " (TEST 46) (" + what
+ ")");
1733 is(ex
.name
, "SyntaxError", "exception load's return value is rejected with for subtest " + j
+ " (TEST 46) (" + what
+ ")");
1734 }).then(function() {
1742 }).then(function() {
1744 // (TEST 47) Test that CSS-connected FontFaces can't be added to other
1747 var style
= document
.querySelector("style");
1748 style
.textContent
= "@font-face { font-family: something; src: url(x); }";
1750 var rule
= style
.sheet
.cssRules
[0];
1752 var all
= Array
.from(document
.fonts
);
1753 is(all
.length
, 1, "document.fonts should contain one FontFace (TEST 47)");
1757 sourceDocuments
.forEach(function({ doc
, what
}) {
1758 if (doc
== document
) {
1764 doc
.fonts
.add(face
);
1765 ok(false, "add should throw when attempting to add a CSS-connected FontFace to another FontFaceSet (TEST 47) (" + what
+ ")");
1767 is(ex
.name
, "InvalidModificationError", "exception name when add is called with a CSS-connected FontFace from another FontFaceSet (TEST 47) (" + what
+ ")");
1771 style
.textContent
= "";
1772 document
.body
.offsetTop
;
1774 sourceDocuments
.forEach(function({ doc
, what
}) {
1775 if (doc
== document
) {
1779 ok(!doc
.fonts
.has(face
), "FontFaceSet initially doesn't have the FontFace (TEST 47) (" + what
+ ")");
1780 doc
.fonts
.add(face
);
1781 ok(doc
.fonts
.has(face
), "add should allow a previously CSS-connected FontFace to be added to another FontFaceSet (TEST 47) (" + what
+ ")");
1785 document
.fonts
.clear();
1787 }).then(function() {
1789 // (TEST 48) Test that FontFaceSets that hold a combination of FontFaces
1790 // from different documents expose the right set of FontFaces.
1792 // Expected FontFaceSet contents.
1799 // Create a CSS-connected FontFace in the top-level document.
1800 var style
= document
.querySelector("style");
1801 style
.textContent
= "@font-face { font-family: something; src: url(x); }";
1803 var all
= Array
.from(document
.fonts
);
1804 is(all
.length
, 1, "document.fonts should contain one FontFace (TEST 48)");
1806 all
[0]._description
= "CSS-connected in document";
1807 expected
.document
.push(all
[0]);
1809 // Create a CSS-connected FontFace in the visible iframe.
1810 var vstyle
= vdocument
.querySelector("style");
1811 vstyle
.textContent
= "@font-face { font-family: somethingelse; src: url(x); }";
1813 all
= Array
.from(vdocument
.fonts
);
1814 all
[0]._description
= "CSS-connected in vdocument";
1815 is(all
.length
, 1, "vdocument.fonts should contain one FontFace (TEST 48)");
1817 expected
.vdocument
.push(all
[0]);
1819 // Create a FontFace in each window and add it to each document's FontFaceSet.
1821 sourceWindows
.forEach(function({ win
, what
: whatWin
}, index
) {
1822 var f
= new win
.FontFace("test" + index
, "url(x)");
1823 sourceDocuments
.forEach(function({ doc
, what
: whatDoc
}) {
1825 expected
[whatDoc
].push(f
);
1826 f
._description
= whatWin
+ "/" + whatDoc
;
1830 sourceDocuments
.forEach(function({ doc
, what
}) {
1831 let allFonts
= Array
.from(doc
.fonts
);
1832 is(expected
[what
].length
, allFonts
.length
, "expected FontFaceSet size (TEST 48) (" + what
+ ")");
1833 for (let i
= 0; i
< expected
[what
].length
; i
++) {
1834 is(expected
[what
][i
], allFonts
[i
], "expected FontFace (" + expected
[what
][i
]._description
+ ") at index " + i
+ " (TEST 48) (" + what
+ ")");
1838 vstyle
.textContent
= "";
1839 style
.textContent
= "";
1841 sourceDocuments
.forEach(function({ doc
}) { doc
.fonts
.clear(); });
1843 }).then(function() {
1845 // (TEST LAST) Test that a pending style sheet load prevents
1846 // document.fonts.status from being set to "loaded".
1848 // First, add a FontFace to document.fonts that will load soon.
1849 var face
= new FontFace("test", "url(BitPattern.woff?testlast)");
1851 document
.fonts
.add(face
);
1853 // Next, add a style sheet reference.
1854 var link
= document
.createElement("link");
1855 link
.rel
= "stylesheet";
1856 link
.href
= "neverending_stylesheet_load.sjs";
1857 link
.type
= "text/css";
1858 document
.head
.appendChild(link
);
1860 return setTimeoutZero() // wait for the style sheet to start loading
1862 document
.fonts
.clear();
1863 is(document
.fonts
.status
, "loading", "FontFaceSet.status when the FontFaceSet has been cleared of loading FontFaces but there is a pending style sheet load (TEST LAST)");
1864 document
.head
.removeChild(link
);
1865 // XXX Removing the <link> element won't cancel the load of the
1866 // style sheet, so we can't do that to test that
1867 // document.fonts.ready is resolved once there are no more
1868 // loading style sheets.
1871 // NOTE: It is important that this style sheet test comes last in the file,
1872 // as the neverending style sheet load will interfere with subsequent
1875 }).then(function() {
1877 // End of the tests.
1878 SimpleTest
.finish();
1880 }, function(aError
) {
1882 // Something failed.
1883 ok(false, "Something failed: " + aError
);
1884 SimpleTest
.finish();
1889 SimpleTest
.waitForExplicitFinish();
1890 SimpleTest
.requestLongerTimeout(5);