Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / test / test_unicode_range_loading.html
blob43622e2ae5864aee8703d48a1955f85a23721c2c
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <meta charset=utf-8>
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">
13 </style>
14 </head>
15 <body>
16 <div id="log"></div>
17 <pre id="display"></pre>
18 <style id="testfonts"></style>
19 <style id="teststyle"></style>
20 <div id="testcontent"></div>
22 <script>
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}],
35 content: "AAA" },
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}],
38 content: "BBB" },
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}],
42 content: "AAA" },
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}],
46 content: "BBB" },
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}],
50 content: "CCC" },
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}],
54 content: "ABC" },
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}],
58 content: "納豆嫌い" },
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}],
62 content: "ABC" },
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}],
66 content: "ABC" },
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}],
71 content: "CCC" },
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}],
76 content: "CCC" },
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}],
81 content: "ABC" },
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}],
86 content: "AAA" },
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}],
91 content: "ABC" },
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}],
96 content: "CCC" },
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 = {
113 style: "font-style",
114 weight: "font-weight",
115 stretch: "font-stretch",
116 unicodeRange: "unicode-range",
117 variant: "font-variant",
118 featureSettings: "font-feature-settings"
121 var kBaseFontURL;
122 if ("SpecialPowers" in window) {
123 kBaseFontURL = "";
124 } else {
125 kBaseFontURL = "fonts/";
128 var mapFontURLs = {
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) {
152 var desc = [];
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) {
165 sheet.deleteRule(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) {
179 sheet.deleteRule(0);
181 var rule = [];
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]);
189 rule.push("}");
190 sheet.insertRule(rule.join("; "), 0);
192 var content = document.getElementById("testcontent");
193 content.innerHTML = testdata.content;
194 content.offsetHeight;
197 // work arounds
198 function getFonts() {
199 if ("forEach" in document.fonts) {
200 return document.fonts;
202 return Array.from(document.fonts);
205 function getSize() {
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);
232 var newfont;
233 var fonts = getFonts();
234 try {
235 fonts.forEach(function(font) { newfont = font; });
236 createdFonts.push({family: fn, data: f, font: newfont});
237 } catch (e) {
238 console.log(e);
241 return createdFonts;
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});
253 return createdFonts;
256 var q = Promise.resolve();
258 function runTests() {
259 function setupTests() {
260 setup({explicit_done: true});
263 function checkFontsBeforeLoad(name, testdata, fd) {
264 test(function() {
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");
269 var i = 0;
270 fonts = getFonts();
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) {
278 test(function() {
279 assert_equals(document.fonts.status, "loaded", "after ready promise resolved, no fonts should be loading");
280 var i = 0;
281 fd.forEach(function(f) {
282 assert_true(f.font instanceof FontFace, "font needs to be an instance of FontFace object");
283 if (f.data.loaded) {
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);
286 } else {
287 assert_equals(f.font.status, "unloaded", "font loaded - font " + i + " " + f.data.src + " "
288 + JSON.stringify(f.data.descriptors) + " for content " + testdata.content);
290 i++;
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);
302 }).then(function() {
303 return setTimeoutPromise(0).then(function() {
304 checkFontsAfterLoad(name, testdata, fd, true);
306 }).then(function() {
307 var ar = getReady();
308 return ar.then(function() {
309 test(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");
317 }).then(function() {
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() {
337 setupTests();
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);
346 }).then(function() {
347 return testUnicodeRangeDocumentFonts(testdata, i, ft);
352 q = q.then(function() {
353 done();
357 if ("fonts" in document) {
358 runTests();
359 } else {
360 test(function() {
361 assert_true(true, "CSS Font Loading API is not enabled.");
362 }, "CSS Font Loading API is not enabled");
364 </script>
365 </body>
366 </html>