layout/style/test/test_font_feature_values_parsing.html

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/css3-fonts/#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>
    20 <script type="text/javascript">
    21 var gPrefix = "";
    22 var kFontFeatureValuesRuleType = 14;
    24 function ruleName() { return "@" + gPrefix + "font-feature-values"; }
    25 function makeRule(f, v) {
    26   return ruleName() + " " + f + " { " + v + " }";
    27 }
    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 }
    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)
    42 var testrules = [
    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 3 }"), serializationNoValueDefn: true },
    73   { rule: makeRule("bongo", "@styleset { abc: 1 2 3a }"), serializationNoValueDefn: true },
    74   { rule: makeRule("bongo", "@styleset { abc: 1 2 3, def: 1; }"), 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: makeRule("bongo", "@blah , @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
    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: ruleName() + " bongo { @styleset { abc: 1 2 3", serialization: _("@styleset { abc: 1 2 3; }") },
    82   { rule: _("@styleset { ok-1: 1; }"), serializationSame: true },
    83   { rule: _("@annotation { ok-1: 3; }"), serializationSame: true },
    84   { rule: _("@stylistic { blah: 3; }"), serializationSame: 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 },
    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 },
    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 },
   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 },
   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 },
   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 },
   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 },
   151   { rule: makeRule("bongo, \"super\" bongo, bongo the supreme", "@styleset { blah: 1; }"), invalid: true },
   152   { rule: makeRule("--bongo", "@styleset { blah: 1; }"), invalid: true },
   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 },
   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 }
   180 ];
   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.
   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; }") },
   190   // -- valid, invalid ==> valid
   191   { before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }", after: "", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }") },
   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; }") },
   196   // -- invalid, valid, invalid ==> valid
   197   { between: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }") }
   198 ];
   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 }
   211 function testParse(rulesrc)
   212 {
   213   var sheet = document.styleSheets[1];
   214   var rule = _.apply(this, rulesrc);
   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   }
   224   if (sheet.cssRules.length == 1 && sheet.cssRules[0].type == kFontFeatureValuesRuleType) {
   225     return sheet.cssRules[0].cssText.replace(/[ \n]+/g, " ");
   226   }
   228   return "";
   229 }
   231 function testOneRule(testrule) {
   232   var sheet = document.styleSheets[1];
   233   var rule;
   235   if ("rulesrc" in testrule) {
   236     rule = _.apply(this, testrule.rulesrc);
   237   } else {
   238     rule = testrule.rule;
   239   }
   241   var parseErr = false;
   242   var expectedErr = false;
   243   var invalid = false;
   244   if ("invalid" in testrule && testrule.invalid) invalid = true;
   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   }
   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);
   266   var sanitizedRule = rule.replace(/[ \n]+/g, " ");
   267   if (parseErr) {
   268     return;
   269   }
   271   // should result in one @font-feature-values rule constructed
   273   // serialization matches expectation
   274   // -- note: due to inconsistent font family serialization problems,
   275   //    only the serialization of the values is tested currently
   277   var ruleValues = valuesText(rule);
   278   var serialized = sheet.cssRules[0].cssText;
   279   var serializedValues = valuesText(serialized);
   280   var haveSerialization = true;
   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);
   296     haveSerialization = false;
   298     if ("rulesrc" in testrule) {
   299       test(function() {
   300         var j, rulesrc = testrule.rulesrc;
   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 = [];
   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           }
   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   }
   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 }
   337 function testFontFeatureValuesRuleParsing() {
   338   // Gecko-specific check - if pref not set, skip these tests
   339   if (window.SpecialPowers && !window.SpecialPowers.getBoolPref("layout.css.font-features.enabled")) {
   340     return;
   341   }
   342   var i;
   343   for (i = 0; i < testrules.length; i++) {
   344     var testrule = testrules[i];
   345     var rule;
   347     if ("rulesrc" in testrule) {
   348       rule = _.apply(this, testrule.rulesrc);
   349     } else {
   350       rule = testrule.rule;
   351     }
   353     testOneRule(testrule);
   354     //test(function() { testOneRule(testrule); }, "parsing " + rule);
   355   }
   356 }
   358 testFontFeatureValuesRuleParsing();
   359 </script>
   360 </body></html>

mercurial