tor-browser

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

test_font_feature_values_parsing.html (17262B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <meta charset=utf-8>
      5  <title>@font-feature-values rule parsing tests</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-4/#font-feature-values" />
      8  <meta name="assert" content="tests that valid @font-feature-values rules parse and invalid ones don't" />
      9  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=549861 -->
     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 type="text/css" id="testbox"></style>
     19 
     20 <script type="text/javascript">
     21 var gPrefix = "";
     22 var kFontFeatureValuesRuleType = 14;
     23 
     24 function ruleName() { return "@" + gPrefix + "font-feature-values"; }
     25 function makeRule(f, v) {
     26  return ruleName() + " " + f + " { " + v + " }";
     27 }
     28 
     29 function _()
     30 {
     31  var i, decl = [];
     32  for (i = 0; i < arguments.length; i++) {
     33    decl.push(arguments[i]);
     34  }
     35  return makeRule("bongo", decl.join(" "));
     36 }
     37 
     38 // note: because of bugs in the way family names are serialized,
     39 // 'serializationSame' only implies that the value definition block
     40 // is the same (i.e. not including the family name list)
     41 
     42 var testrules = [
     43 
     44  /* basic syntax */
     45  { rule: ruleName() + ";", invalid: true },
     46  { rule: ruleName() + " bongo;", invalid: true },
     47  { rule: ruleName().replace("values", "value") + " {;}", invalid: true },
     48  { rule: ruleName().replace("feature", "features") + " {;}", invalid: true },
     49  { rule: makeRule("bongo", ""), serializationNoValueDefn: true },
     50  { rule: makeRule("bongo", ";"), serializationNoValueDefn: true },
     51  { rule: makeRule("bongo", ",;"), serializationNoValueDefn: true },
     52  { rule: makeRule("bongo", ";,"), serializationNoValueDefn: true },
     53  { rule: makeRule("bongo", ",;,"), serializationNoValueDefn: true },
     54  { rule: makeRule("bongo", "@styleset;"), serializationNoValueDefn: true },
     55  { rule: makeRule("bongo", "@styleset,;"), serializationNoValueDefn: true },
     56  { rule: makeRule("bongo", "@styleset abc;"), serializationNoValueDefn: true },
     57  { rule: makeRule("bongo", "@styleset { abc }"), serializationNoValueDefn: true },
     58  { rule: makeRule("bongo", "@styleset { ;;abc }"), serializationNoValueDefn: true },
     59  { rule: makeRule("bongo", "@styleset { abc;; }"), serializationNoValueDefn: true },
     60  { rule: makeRule("bongo", "@styleset { abc: }"), serializationNoValueDefn: true },
     61  { rule: makeRule("bongo", "@styleset { abc,: }"), serializationNoValueDefn: true },
     62  { rule: makeRule("bongo", "@styleset { abc:, }"), serializationNoValueDefn: true },
     63  { rule: makeRule("bongo", "@styleset { abc:,; }"), serializationNoValueDefn: true },
     64  { rule: makeRule("bongo", "@styleset { a,b }"), serializationNoValueDefn: true },
     65  { rule: makeRule("bongo", "@styleset { a;b }"), serializationNoValueDefn: true },
     66  { rule: makeRule("bongo", "@styleset { a:;b: }"), serializationNoValueDefn: true },
     67  { rule: makeRule("bongo", "@styleset { a:,;b: }"), serializationNoValueDefn: true },
     68  { rule: makeRule("bongo", "@styleset { a:1,;b: }"), serializationNoValueDefn: true },
     69  { rule: makeRule("bongo", "@styleset { abc 1 2 3 }"), serializationNoValueDefn: true },
     70  { rule: makeRule("bongo", "@styleset { abc:, 1 2 3 }"), serializationNoValueDefn: true },
     71  { rule: makeRule("bongo", "@styleset { abc:; 1 2 3 }"), serializationNoValueDefn: true },
     72  { rule: makeRule("bongo", "@styleset { abc: 1 2 3a }"), serializationNoValueDefn: true },
     73  { rule: makeRule("bongo", "@styleset { abc: 1 2 3, def: 1; }"), serializationNoValueDefn: true },
     74  { rule: makeRule("bongo", "@blah @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
     75  { rule: makeRule("bongo", "@blah } @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
     76  { rule: makeRule("bongo", "@blah , @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
     77  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3; }", serialization: _("@styleset { abc: 1 2 3; }") },
     78  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3 }", serialization: _("@styleset { abc: 1 2 3; }") },
     79  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3;", serialization: _("@styleset { abc: 1 2 3; }") },
     80  { rule: ruleName() + " bongo { @styleset { abc: 1 2 3", serialization: _("@styleset { abc: 1 2 3; }") },
     81  { rule: _("@styleset { ok-1: 1; }"), serializationSame: true },
     82  { rule: _("@annotation { ok-1: 3; }"), serializationSame: true },
     83  { rule: _("@stylistic { blah: 3; }"), serializationSame: true },
     84  { rule: _("@stylistic { blah: sibling-index(); }"), serializationNoValueDefn: true },
     85  { rule: makeRule("bongo", "\n@styleset\n  { blah: 3; super-blah: 4 5;\n  more-blah: 5 6 7;\n }"), serializationSame: true },
     86  { rule: makeRule("bongo", "\n@styleset\n  {\n blah:\n 3\n;\n super-blah:\n 4\n 5\n;\n  more-blah:\n 5 6\n 7;\n }"), serializationSame: true },
     87 
     88  /* limits on number of values */
     89  { rule: _("@stylistic { blah: 1; }"), serializationSame: true },
     90  { rule: _("@styleset { blah: 1 2 3 4; }"), serializationSame: true },
     91  { rule: _("@character-variant { blah: 1 2; }"), serializationSame: true },
     92  { rule: _("@swash { blah: 1; }"), serializationSame: true },
     93  { rule: _("@ornaments { blah: 1; }"), serializationSame: true },
     94  { rule: _("@annotation { blah: 1; }"), serializationSame: true },
     95 
     96  /* values ignored when used */
     97  { rule: _("@styleset { blah: 0; }"), serializationSame: true },
     98  { rule: _("@styleset { blah: 120 124; }"), serializationSame: true },
     99  { rule: _("@character-variant { blah: 0; }"), serializationSame: true },
    100  { rule: _("@character-variant { blah: 111; }"), serializationSame: true },
    101  { rule: _("@character-variant { blah: 111 13; }"), serializationSame: true },
    102 
    103  /* invalid value name */
    104  { rulesrc: ["styleset { blah: 1 }"], serializationNoValueDefn: true },
    105  { rulesrc: ["stylistic { blah: 1 }"], serializationNoValueDefn: true },
    106  { rulesrc: ["character-variant { blah: 1 }"], serializationNoValueDefn: true },
    107  { rulesrc: ["swash { blah: 1 }"], serializationNoValueDefn: true },
    108  { rulesrc: ["ornaments { blah: 1 }"], serializationNoValueDefn: true },
    109  { rulesrc: ["annotation { blah: 1 }"], serializationNoValueDefn: true },
    110  { rulesrc: ["@bongo { blah: 1 }"], serializationNoValueDefn: true },
    111  { rulesrc: ["@bongo { blah: 1 2 3 }"], serializationNoValueDefn: true },
    112  { rulesrc: ["@bongo { blah: 1 2 3; burp: 1;;; }"], serializationNoValueDefn: true },
    113 
    114  /* values */
    115  { rulesrc: ["@styleset { blah: -1 }"], serializationNoValueDefn: true },
    116  { rulesrc: ["@styleset { blah: 1 -1 }"], serializationNoValueDefn: true },
    117  { rulesrc: ["@styleset { blah: 1.5 }"], serializationNoValueDefn: true },
    118  { rulesrc: ["@styleset { blah: 15px }"], serializationNoValueDefn: true },
    119  { rulesrc: ["@styleset { blah: red }"], serializationNoValueDefn: true },
    120  { rulesrc: ["@styleset { blah: (1) }"], serializationNoValueDefn: true },
    121  { rulesrc: ["@styleset { blah:(1) }"], serializationNoValueDefn: true },
    122  { rulesrc: ["@styleset { blah:, 1 }"], serializationNoValueDefn: true },
    123  { rulesrc: ["@styleset { blah: <1> }"], serializationNoValueDefn: true },
    124  { rulesrc: ["@styleset { blah: 1! }"], serializationNoValueDefn: true },
    125  { rulesrc: ["@styleset { blah: 1,, }"], serializationNoValueDefn: true },
    126  { rulesrc: ["@styleset { blah: 1 1 1 1; }"], serializationSame: true },
    127 
    128  /* limits on number of values */
    129  { rulesrc: ["@stylistic { blah: 1 2 }"], serializationNoValueDefn: true },
    130  { rulesrc: ["@character-variant { blah: 1 2 3 }"], serializationNoValueDefn: true },
    131  { rulesrc: ["@swash { blah: 1 2 }"], serializationNoValueDefn: true },
    132  { rulesrc: ["@ornaments { blah: 1 2 }"], serializationNoValueDefn: true },
    133  { rulesrc: ["@annotation { blah: 1 2 }"], serializationNoValueDefn: true },
    134  { rulesrc: ["@styleset { blah: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; }"], serializationSame: true },
    135 
    136  /* family names */
    137  { rule: makeRule("bongo", "@styleset { blah: 1; }"), serializationSame: true },
    138  { rule: makeRule("\"bongo\"", "@styleset { blah: 1; }"), serializationSame: true },
    139  { rule: makeRule("'bongo'", "@styleset { blah: 1; }"), serializationSame: true },
    140  { rule: makeRule("\\62 ongo", "@styleset { blah: 1; }"), serializationSame: true },
    141  { rule: makeRule("bongo, super bongo, bongo the supreme", "@styleset { blah: 1; }"), serializationSame: true },
    142  { rule: makeRule("bongo,, super bongo", "@styleset { blah: 1; }"), invalid: true },
    143  { rule: makeRule("bongo,*", "@styleset { blah: 1; }"), invalid: true },
    144  { rule: makeRule("bongo, sans-serif", "@styleset { blah: 1; }"), invalid: true },
    145  { rule: makeRule("serif, sans-serif", "@styleset { blah: 1; }"), invalid: true },
    146  { rule: makeRule("'serif', 'sans-serif'", "@styleset { blah: 1; }"), serializationSame: true },
    147  { rule: makeRule("bongo, \"super bongo\", 'bongo the supreme'", "@styleset { blah: 1; }"), serializationSame: true },
    148  { rule: makeRule("毎日カレーを食べたい!", "@styleset { blah: 1; }"), serializationSame: true },
    149  { rule: makeRule("毎日カレーを食べたい!, 納豆嫌い", "@styleset { blah: 1; }"), serializationSame: true },
    150 
    151  { rule: makeRule("bongo, \"super\" bongo, bongo the supreme", "@styleset { blah: 1; }"), invalid: true },
    152  { rule: makeRule("--bongo", "@styleset { blah: 1; }"), invalid: true },
    153 
    154  /* ident tests */
    155  { rule: _("@styleset { blah: 1; blah: 1; }"), serializationSame: true },
    156  { rule: _("@styleset { blah: 1; de-blah: 1; blah: 2; }"), serializationSame: true },
    157  { rule: _("@styleset { \\tra-la: 1; }"), serialization: _("@styleset { tra-la: 1; }") },
    158  { rule: _("@styleset { b\\lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
    159  { rule: _("@styleset { \\62 lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
    160  { rule: _("@styleset { \\:blah: 1; }"), serialization: _("@styleset { \\:blah: 1; }") },
    161  { rule: _("@styleset { \\;blah: 1; }"), serialization: _("@styleset { \\;blah: 1; }") },
    162  { rule: _("@styleset { complex\\20 blah: 1; }"), serialization: _("@styleset { complex\\ blah: 1; }") },
    163  { rule: _("@styleset { complex\\ blah: 1; }"), serializationSame: true },
    164  { rule: _("@styleset { Håkon: 1; }"), serializationSame: true },
    165  { rule: _("@styleset { Åквариум: 1; }"), serializationSame: true },
    166  { rule: _("@styleset { \\1f449\\1f4a9\\1f448: 1; }"), serialization: _("@styleset { 👉💩👈: 1; }") },
    167  { rule: _("@styleset { 魅力: 1; }"), serializationSame: true },
    168  { rule: _("@styleset { 毎日カレーを食べたい!: 1; }"), serializationSame: true },
    169  /* from http://en.wikipedia.org/wiki/Metal_umlaut */
    170  { rule: _("@styleset { TECHNICIÄNS\\ ÖF\\ SPÅCE\\ SHIP\\ EÅRTH\\ THIS\\ IS\\ YÖÜR\\ CÄPTÅIN\\ SPEÄKING\\ YÖÜR\\ ØÅPTÅIN\\ IS\\ DEA̋D: 1; }"), serializationSame: true },
    171 
    172  { rulesrc: ["@styleset { 123blah: 1; }"], serializationNoValueDefn: true },
    173  { rulesrc: ["@styleset { :123blah 1; }"], serializationNoValueDefn: true },
    174  { rulesrc: ["@styleset { :123blah: 1; }"], serializationNoValueDefn: true },
    175  { rulesrc: ["@styleset { ?123blah: 1; }"], serializationNoValueDefn: true },
    176  { rulesrc: ["@styleset { \"blah\": 1; }"], serializationNoValueDefn: true },
    177  { rulesrc: ["@styleset { complex blah: 1; }"], serializationNoValueDefn: true },
    178  { rulesrc: ["@styleset { complex\\  blah: 1; }"], serializationNoValueDefn: true }
    179 
    180 ];
    181 
    182 // test that invalid value declarations don't affect the parsing of surrounding
    183 // declarations.  So before + invalid + after should match the serialization
    184 // given in s.
    185 
    186 var gSurroundingTests = [
    187  // -- invalid, valid ==> valid
    188  { before: "", after: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }") },
    189 
    190  // -- valid, invalid ==> valid
    191  { before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }", after: "", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }") },
    192 
    193  // -- valid, invalid, valid ==> valid, valid
    194  { before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", after: "@character-variant { whatchamacallit-2: 23 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; } @character-variant { whatchamacallit-2: 23 4; }") },
    195 
    196  // -- invalid, valid, invalid ==> valid
    197  { between: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }") }
    198 ];
    199 
    200 /* strip out just values, along with empty value blocks (e.g. @swash { })*/
    201 function valuesText(ruletext)
    202 {
    203  var t = ruletext.replace(/@[a-zA-Z0-9\-]+[ \n]*{[ \n]*}/g, "");
    204  t = t.replace(/[ \n]+/g, " ");
    205  t = t.replace(/^[^{]+{[ \n]*/, "");
    206  t = t.replace(/[ \n]*}[^}]*$/, "");
    207  t = t.replace(/[ \n]*;/g, ";");
    208  return t;
    209 }
    210 
    211 function testParse(rulesrc)
    212 {
    213  var sheet = document.styleSheets[1];
    214  var rule = _.apply(this, rulesrc);
    215 
    216  while(sheet.cssRules.length > 0)
    217    sheet.deleteRule(0);
    218  try {
    219    sheet.insertRule(rule, 0);
    220  } catch (e) {
    221    return e.toString();
    222  }
    223 
    224  if (sheet.cssRules.length == 1 && sheet.cssRules[0].type == kFontFeatureValuesRuleType) {
    225    return sheet.cssRules[0].cssText.replace(/[ \n]+/g, " ");
    226  }
    227 
    228  return "";
    229 }
    230 
    231 function testOneRule(testrule) {
    232  var sheet = document.styleSheets[1];
    233  var rule;
    234 
    235  if ("rulesrc" in testrule) {
    236    rule = _.apply(this, testrule.rulesrc);
    237  } else {
    238    rule = testrule.rule;
    239  }
    240 
    241  var parseErr = false;
    242  var expectedErr = false;
    243  var invalid = false;
    244  if ("invalid" in testrule && testrule.invalid) invalid = true;
    245 
    246  while(sheet.cssRules.length > 0)
    247    sheet.deleteRule(0);
    248  try {
    249    sheet.insertRule(rule, 0);
    250  } catch (e) {
    251    expectedErr = (e.name == "SyntaxError"
    252      && e instanceof DOMException
    253      && e.code == DOMException.SYNTAX_ERR
    254      && invalid);
    255    parseErr = true;
    256  }
    257 
    258  test(function() {
    259    assert_true(!parseErr || expectedErr, "unexpected syntax error");
    260    if (!parseErr) {
    261      assert_equals(sheet.cssRules.length, 1, "bad rule count");
    262      assert_equals(sheet.cssRules[0].type, kFontFeatureValuesRuleType, "bad rule type");
    263    }
    264  }, "basic parse tests - " + rule);
    265 
    266  var sanitizedRule = rule.replace(/[ \n]+/g, " ");
    267  if (parseErr) {
    268    return;
    269  }
    270 
    271  // should result in one @font-feature-values rule constructed
    272 
    273  // serialization matches expectation
    274  // -- note: due to inconsistent font family serialization problems,
    275  //    only the serialization of the values is tested currently
    276 
    277  var ruleValues = valuesText(rule);
    278  var serialized = sheet.cssRules[0].cssText;
    279  var serializedValues = valuesText(serialized);
    280  var haveSerialization = true;
    281 
    282  if (testrule.serializationSame) {
    283    test(function() {
    284      assert_equals(serializedValues, ruleValues, "canonical cssText serialization doesn't match");
    285    }, "serialization check - " + rule);
    286  } else if ("serialization" in testrule) {
    287    var s = valuesText(testrule.serialization);
    288    test(function() {
    289      assert_equals(serializedValues, s, "non-canonical cssText serialization doesn't match - ");
    290    }, "serialization check - " + rule);
    291  } else if (testrule.serializationNoValueDefn) {
    292    test(function() {
    293      assert_equals(serializedValues, "", "cssText serialization should have no value defintions - ");
    294    }, "no value definitions in serialization - " + rule);
    295 
    296    haveSerialization = false;
    297 
    298    if ("rulesrc" in testrule) {
    299      test(function() {
    300        var j, rulesrc = testrule.rulesrc;
    301 
    302        // invalid value definitions shouldn't affect the parsing of valid
    303        // definitions before or after an invalid one
    304        for (var j = 0; j < gSurroundingTests.length; j++) {
    305          var t = gSurroundingTests[j];
    306          var srulesrc = [];
    307 
    308          if ("between" in t) {
    309            srulesrc = srulesrc.concat(rulesrc);
    310            srulesrc = srulesrc.concat(t.between);
    311            srulesrc = srulesrc.concat(rulesrc);
    312          } else {
    313            if (t.before != "")
    314              srulesrc = srulesrc.concat(t.before);
    315            srulesrc = srulesrc.concat(rulesrc);
    316            if (t.after != "")
    317              srulesrc = srulesrc.concat(t.after);
    318          }
    319 
    320          var result = testParse(srulesrc);
    321          assert_equals(valuesText(result), valuesText(t.s), "invalid declarations should not affect valid ones - ");
    322        }
    323      }, "invalid declarations don't affect valid ones - " + rule);
    324    }
    325  }
    326 
    327  // if serialization non-empty, serialization should round-trip to itself
    328  if (haveSerialization) {
    329    var roundTripText = testParse([serializedValues]);
    330    test(function() {
    331      assert_equals(valuesText(roundTripText), serializedValues,
    332         "serialization should round-trip to itself - ");
    333    }, "serialization round-trip - " + rule);
    334  }
    335 }
    336 
    337 function testFontFeatureValuesRuleParsing() {
    338  var i;
    339  for (i = 0; i < testrules.length; i++) {
    340    var testrule = testrules[i];
    341    var rule;
    342 
    343    if ("rulesrc" in testrule) {
    344      rule = _.apply(this, testrule.rulesrc);
    345    } else {
    346      rule = testrule.rule;
    347    }
    348 
    349    testOneRule(testrule);
    350    //test(function() { testOneRule(testrule); }, "parsing " + rule);
    351  }
    352 }
    353 
    354 testFontFeatureValuesRuleParsing();
    355 </script>
    356 </body></html>