tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>