test_unicode_range_loading.html (15132B)
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> 21 22 <script> 23 24 const kSheetFonts = 1; 25 const kSheetStyles = 2; 26 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>"; 28 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 + "\">" }, 109 ]; 110 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" 119 }; 120 121 var kBaseFontURL; 122 if ("SpecialPowers" in window) { 123 kBaseFontURL = ""; 124 } else { 125 kBaseFontURL = "fonts/"; 126 } 127 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" + ")", 133 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" + ")", 139 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" + ")", 145 }; 146 147 function familyName(name, i) { 148 return "test" + i + "-" + name; 149 } 150 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]); 158 } 159 return "@font-face { " + desc.join(";") + " }"; 160 } 161 162 function clearRules(sheetIndex) { 163 var sheet = document.styleSheets[sheetIndex]; 164 while(sheet.cssRules.length > 0) { 165 sheet.deleteRule(0); 166 } 167 } 168 169 function clearAllRulesAndFonts() { 170 clearRules(kSheetFonts); 171 clearRules(kSheetStyles); 172 document.fonts.clear(); 173 } 174 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); 180 } 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]); 187 } 188 } 189 rule.push("}"); 190 sheet.insertRule(rule.join("; "), 0); 191 192 var content = document.getElementById("testcontent"); 193 content.innerHTML = testdata.content; 194 content.offsetHeight; 195 } 196 197 // work arounds 198 function getFonts() { 199 if ("forEach" in document.fonts) { 200 return document.fonts; 201 } 202 return Array.from(document.fonts); 203 } 204 205 function getSize() { 206 if ("size" in document.fonts) { 207 return document.fonts.size; 208 } 209 return getFonts().length; 210 } 211 212 function getReady() { 213 if (typeof(document.fonts.ready) == "function") { 214 return document.fonts.ready(); 215 } 216 return document.fonts.ready; 217 } 218 219 function setTimeoutPromise(aDelay) { 220 return new Promise(function(aResolve, aReject) { 221 setTimeout(aResolve, aDelay); 222 }); 223 } 224 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); 239 } 240 }); 241 return createdFonts; 242 } 243 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}); 252 }); 253 return createdFonts; 254 } 255 256 var q = Promise.resolve(); 257 258 function runTests() { 259 function setupTests() { 260 setup({explicit_done: true}); 261 } 262 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"); 273 }); 274 }, name + " before load"); 275 } 276 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); 289 } 290 i++; 291 }); 292 }, name + " after load" + (afterTimeout ? " and timeout" : "")); 293 } 294 295 function testFontLoads(testdata, i, name, fd) { 296 checkFontsBeforeLoad(name, testdata, fd); 297 addStyleRulesAndText(testdata, i); 298 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); 305 }); 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"); 314 }); 315 }, name + " test done check"); 316 }); 317 }).then(function() { 318 clearAllRulesAndFonts(); 319 }); 320 } 321 322 function testUnicodeRangeFontFace(testdata, i, ft) { 323 var name = "TEST " + i + " " + testdata.test + " (@font-face rules)" + (ft != "" ? " " + ft : ft); 324 325 var fd = addFontFaceRules(testdata, i, ft); 326 return testFontLoads(testdata, i, name, fd); 327 } 328 329 function testUnicodeRangeDocumentFonts(testdata, i, ft) { 330 var name = "TEST " + i + " " + testdata.test + " (document.fonts)" + (ft != "" ? " " + ft : ft); 331 332 var fd = addDocumentFonts(testdata, i, ft); 333 return testFontLoads(testdata, i, name, fd); 334 } 335 336 q = q.then(function() { 337 setupTests(); 338 }); 339 340 var fontTypes = ["", "twourl", "localfirst"]; 341 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); 348 }); 349 }); 350 }); 351 352 q = q.then(function() { 353 done(); 354 }); 355 } 356 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"); 363 } 364 </script> 365 </body> 366 </html>