5 <title>unicode-range load tests using font loading api
</title>
6 <link rel=
"author" title=
"John Daggett" href=
"mailto:jdaggett@mozilla.com">
7 <link rel=
"help" href=
"http://www.w3.org/TR/css-fonts-3/#unicode-range-desc" />
8 <link rel=
"help" href=
"http://dev.w3.org/csswg/css-font-loading/" />
9 <meta name=
"assert" content=
"unicode-range descriptor defines precisely which fonts should be loaded" />
10 <script type=
"text/javascript" src=
"/resources/testharness.js"></script>
11 <script type=
"text/javascript" src=
"/resources/testharnessreport.js"></script>
12 <style type=
"text/css">
17 <pre id=
"display"></pre>
18 <style id=
"testfonts"></style>
19 <style id=
"teststyle"></style>
20 <div id=
"testcontent"></div>
24 const kSheetFonts
= 1;
25 const kSheetStyles
= 2;
27 const redSquDataURL
= "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' width='100%' height='100%'><rect fill='red' x='0' y='0' width='10' height='10'/></svg>";
29 var unicodeRangeTests
= [
30 { test
: "simple load sanity check, unused fonts not loaded",
31 fonts
: [{ family
: "a", src
: "markA", descriptors
: { }, loaded
: false}],
32 content
: "AAA", style
: { "font-family": "unused" } },
33 { test
: "simple load sanity check, font for a used character loaded",
34 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true}],
36 { test
: "simple load sanity check, font for an unused character not loaded",
37 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: false}],
39 { test
: "simple load sanity check, with two fonts only font for used character loaded A",
40 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
41 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: false}],
43 { test
: "simple load sanity check, with two fonts only font for used character loaded B",
44 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: false},
45 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: true}],
47 { test
: "simple load sanity check, two fonts but neither supports characters used",
48 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: false},
49 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: false}],
51 { test
: "simple load sanity check, two fonts and both are used",
52 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
53 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: true}],
55 { test
: "simple load sanity check, one with Han ranges",
56 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+3???,u+5???,u+7???,u+8???" }, loaded
: true},
57 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: false}],
59 { test
: "simple load sanity check, two fonts with different styles A",
60 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
61 { family
: "a", src
: "markB", descriptors
: { weight
: "bold", unicodeRange
: "u+42" }, loaded
: false}],
63 { test
: "simple load sanity check, two fonts with different styles B",
64 fonts
: [{ family
: "a", src
: "markA", descriptors
: { weight
: "bold", unicodeRange
: "u+41" }, loaded
: false},
65 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: true}],
67 { test
: "multiple fonts with overlapping ranges, all with default ranges, only last one supports character used",
68 fonts
: [{ family
: "a", src
: "markC", descriptors
: { }, loaded
: true},
69 { family
: "a", src
: "markA", descriptors
: { }, loaded
: true},
70 { family
: "a", src
: "markB", descriptors
: { }, loaded
: true}],
72 { test
: "multiple fonts with overlapping ranges, all with default ranges, first one supports character used",
73 fonts
: [{ family
: "a", src
: "markB", descriptors
: { }, loaded
: false},
74 { family
: "a", src
: "markA", descriptors
: { }, loaded
: false},
75 { family
: "a", src
: "markC", descriptors
: { }, loaded
: true}],
77 { test
: "multiple fonts with overlapping ranges, one with default value in the fallback position",
78 fonts
: [{ family
: "a", src
: "markC", descriptors
: { }, loaded
: true},
79 { family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
80 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: true}],
82 { test
: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to one",
83 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
84 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: false},
85 { family
: "a", src
: "markC", descriptors
: { }, loaded
: true}],
87 { test
: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to two",
88 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: true},
89 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: true},
90 { family
: "a", src
: "markC", descriptors
: { }, loaded
: true}],
92 { test
: "multiple fonts with overlapping ranges, one with default value in the primary use position, no fallback",
93 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+41" }, loaded
: false},
94 { family
: "a", src
: "markB", descriptors
: { unicodeRange
: "u+42" }, loaded
: false},
95 { family
: "a", src
: "markC", descriptors
: { }, loaded
: true}],
97 { test
: "metrics only case, ex-sized image, single font with space in range",
98 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+0??" }, loaded
: true}],
99 content
: "<img style='width: 2ex' src=\"" + redSquDataURL
+ "\">" },
100 { test
: "metrics only case, ex-sized image, single font with space outside range",
101 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+1??" }, loaded
: false}],
102 content
: "<img style='width: 2ex' src=\"" + redSquDataURL
+ "\">" },
103 { test
: "metrics only case, ch-sized image, single font with space in range",
104 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+0??" }, loaded
: true}],
105 content
: "<img style='width: 2ch' src=\"" + redSquDataURL
+ "\">" },
106 { test
: "metrics only case, ch-sized image, single font with space outside range",
107 fonts
: [{ family
: "a", src
: "markA", descriptors
: { unicodeRange
: "u+1??" }, loaded
: false}],
108 content
: "<img style='width: 2ch' src=\"" + redSquDataURL
+ "\">" },
111 // map font loading descriptor names to @font-face rule descriptor names
112 var mapDescriptorNames
= {
114 weight
: "font-weight",
115 stretch
: "font-stretch",
116 unicodeRange
: "unicode-range",
117 variant
: "font-variant",
118 featureSettings
: "font-feature-settings"
122 if ("SpecialPowers" in window
) {
125 kBaseFontURL
= "fonts/";
129 markA
: "url(" + kBaseFontURL
+ "markA.woff" + ")",
130 markB
: "url(" + kBaseFontURL
+ "markB.woff" + ")",
131 markC
: "url(" + kBaseFontURL
+ "markC.woff" + ")",
132 markD
: "url(" + kBaseFontURL
+ "markD.woff" + ")",
134 /* twourl versions include a bogus url followed by a valid url */
135 markAtwourl
: "url(" + kBaseFontURL
+ "bogus-markA.woff" + "), url(" + kBaseFontURL
+ "markA.woff" + ")",
136 markBtwourl
: "url(" + kBaseFontURL
+ "bogus-markB.woff" + "), url(" + kBaseFontURL
+ "markB.woff" + ")",
137 markCtwourl
: "url(" + kBaseFontURL
+ "bogus-markC.woff" + "), url(" + kBaseFontURL
+ "markC.woff" + ")",
138 markDtwourl
: "url(" + kBaseFontURL
+ "bogus-markD.woff" + "), url(" + kBaseFontURL
+ "markD.woff" + ")",
140 /* localfont versions include a bogus local ref followed by a valid url */
141 markAlocalfirst
: "local(bogus-markA), url(" + kBaseFontURL
+ "markA.woff" + ")",
142 markBlocalfirst
: "local(bogus-markB), url(" + kBaseFontURL
+ "markB.woff" + ")",
143 markClocalfirst
: "local(bogus-markC), url(" + kBaseFontURL
+ "markC.woff" + ")",
144 markDlocalfirst
: "local(bogus-markD), url(" + kBaseFontURL
+ "markD.woff" + ")",
147 function familyName(name
, i
) {
148 return "test" + i
+ "-" + name
;
151 function fontFaceRule(name
, fontdata
, ft
) {
153 desc
.push("font-family: " + name
);
154 var srckey
= fontdata
.src
+ ft
;
155 desc
.push("src: " + mapFontURLs
[srckey
]);
156 for (var d
in fontdata
.descriptors
) {
157 desc
.push(mapDescriptorNames
[d
] + ": " + fontdata
.descriptors
[d
]);
159 return "@font-face { " + desc
.join(";") + " }";
162 function clearRules(sheetIndex
) {
163 var sheet
= document
.styleSheets
[sheetIndex
];
164 while(sheet
.cssRules
.length
> 0) {
169 function clearAllRulesAndFonts() {
170 clearRules(kSheetFonts
);
171 clearRules(kSheetStyles
);
172 document
.fonts
.clear();
175 function addStyleRulesAndText(testdata
, i
) {
176 // add style rules for testcontent
177 var sheet
= document
.styleSheets
[kSheetStyles
];
178 while(sheet
.cssRules
.length
> 0) {
182 var family
= familyName(testdata
.fonts
[0].family
, i
);
183 rule
.push("#testcontent { font-family: " + family
);
184 if ("style" in testdata
) {
185 for (s
in testdata
.style
) {
186 rule
.push(s
+ ": " + testdata
.style
[s
]);
190 sheet
.insertRule(rule
.join("; "), 0);
192 var content
= document
.getElementById("testcontent");
193 content
.innerHTML
= testdata
.content
;
194 content
.offsetHeight
;
198 function getFonts() {
199 if ("forEach" in document
.fonts
) {
200 return document
.fonts
;
202 return Array
.from(document
.fonts
);
206 if ("size" in document
.fonts
) {
207 return document
.fonts
.size
;
209 return getFonts().length
;
212 function getReady() {
213 if (typeof(document
.fonts
.ready
) == "function") {
214 return document
.fonts
.ready();
216 return document
.fonts
.ready
;
219 function setTimeoutPromise(aDelay
) {
220 return new Promise(function(aResolve
, aReject
) {
221 setTimeout(aResolve
, aDelay
);
225 function addFontFaceRules(testdata
, i
, ft
) {
226 var sheet
= document
.styleSheets
[kSheetFonts
];
227 var createdFonts
= [];
228 testdata
.fonts
.forEach(function(f
) {
229 var n
= sheet
.cssRules
.length
;
230 var fn
= familyName(f
.family
, i
);
231 sheet
.insertRule(fontFaceRule(fn
, f
, ft
), n
);
233 var fonts
= getFonts();
235 fonts
.forEach(function(font
) { newfont
= font
; });
236 createdFonts
.push({family
: fn
, data
: f
, font
: newfont
});
244 function addDocumentFonts(testdata
, i
, ft
) {
245 var createdFonts
= [];
246 testdata
.fonts
.forEach(function(fd
) {
247 var fn
= familyName(fd
.family
, i
);
248 var srckey
= fd
.src
+ ft
;
249 var f
= new FontFace(fn
, mapFontURLs
[srckey
], fd
.descriptors
);
250 document
.fonts
.add(f
);
251 createdFonts
.push({family
: fn
, data
: fd
, font
: f
});
256 var q
= Promise
.resolve();
258 function runTests() {
259 function setupTests() {
260 setup({explicit_done
: true});
263 function checkFontsBeforeLoad(name
, testdata
, fd
) {
265 assert_equals(document
.fonts
.status
, "loaded", "before initializing test, no fonts should be loading - found: " + document
.fonts
.status
);
266 var size
= getSize();
267 assert_equals(size
, testdata
.fonts
.length
,
268 "fonts where not added to the font set object");
271 fonts
.forEach(function(ff
) {
272 assert_equals(ff
.status
, "unloaded", "added fonts should be in unloaded state");
274 }, name
+ " before load");
277 function checkFontsAfterLoad(name
, testdata
, fd
, afterTimeout
) {
279 assert_equals(document
.fonts
.status
, "loaded", "after ready promise resolved, no fonts should be loading");
281 fd
.forEach(function(f
) {
282 assert_true(f
.font
instanceof FontFace
, "font needs to be an instance of FontFace object");
284 assert_equals(f
.font
.status
, "loaded", "font not loaded - font " + i
+ " " + f
.data
.src
+ " "
285 + JSON
.stringify(f
.data
.descriptors
) + " for content " + testdata
.content
);
287 assert_equals(f
.font
.status
, "unloaded", "font loaded - font " + i
+ " " + f
.data
.src
+ " "
288 + JSON
.stringify(f
.data
.descriptors
) + " for content " + testdata
.content
);
292 }, name
+ " after load" + (afterTimeout
? " and timeout" : ""));
295 function testFontLoads(testdata
, i
, name
, fd
) {
296 checkFontsBeforeLoad(name
, testdata
, fd
);
297 addStyleRulesAndText(testdata
, i
);
299 var ready
= getReady();
300 return ready
.then(function() {
301 checkFontsAfterLoad(name
, testdata
, fd
, false);
303 return setTimeoutPromise(0).then(function() {
304 checkFontsAfterLoad(name
, testdata
, fd
, true);
308 return ar
.then(function() {
310 assert_equals(document
.fonts
.status
, "loaded", "after ready promise fulfilled once, fontset should not be loading");
311 var fonts
= getFonts();
312 fonts
.forEach(function(f
) {
313 assert_not_equals(f
.status
, "loading", "after ready promise fulfilled once, no font should be loading");
315 }, name
+ " test done check");
318 clearAllRulesAndFonts();
322 function testUnicodeRangeFontFace(testdata
, i
, ft
) {
323 var name
= "TEST " + i
+ " " + testdata
.test
+ " (@font-face rules)" + (ft
!= "" ? " " + ft
: ft
);
325 var fd
= addFontFaceRules(testdata
, i
, ft
);
326 return testFontLoads(testdata
, i
, name
, fd
);
329 function testUnicodeRangeDocumentFonts(testdata
, i
, ft
) {
330 var name
= "TEST " + i
+ " " + testdata
.test
+ " (document.fonts)" + (ft
!= "" ? " " + ft
: ft
);
332 var fd
= addDocumentFonts(testdata
, i
, ft
);
333 return testFontLoads(testdata
, i
, name
, fd
);
336 q
= q
.then(function() {
340 var fontTypes
= ["", "twourl", "localfirst"];
342 unicodeRangeTests
.forEach(function(testdata
, i
) {
343 fontTypes
.forEach(function(ft
) {
344 q
= q
.then(function() {
345 return testUnicodeRangeFontFace(testdata
, i
, ft
);
347 return testUnicodeRangeDocumentFonts(testdata
, i
, ft
);
352 q
= q
.then(function() {
357 if ("fonts" in document
) {
361 assert_true(true, "CSS Font Loading API is not enabled.");
362 }, "CSS Font Loading API is not enabled");