tor-browser

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

test_value_storage.html (12767B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 -->
      5 <head>
      6  <title>Test for parsing, storage, and serialization of CSS values</title>
      7  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      8  <script type="text/javascript" src="property_database.js"></script>
      9  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     10  <style type="text/css" id="prereqsheet">
     11  #testnode {}
     12  </style>
     13 </head>
     14 <body>
     15 <p id="display"></p>
     16 <div id="content" style="display: none">
     17 
     18 <div id="testnode"></div>
     19 
     20 </div>
     21 <pre id="test">
     22 <script class="testbody" type="text/javascript">
     23 
     24 /** Test for parsing, storage, and serialization of CSS values */
     25 
     26 /*
     27 * The idempotence tests here deserve a little bit of explanation.  What
     28 * we're testing here are the following operations:
     29 *   parse: string -> CSS rule
     30 *   serialize: CSS rule -> string (normalization 1)
     31 *     (this actually has two variants that go through partly different
     32 *     codepaths, which we exercise with getPropertyValue and cssText)
     33 *   compute: CSS rule -> computed style
     34 *   cserialize: computed style -> string (normalization 2)
     35 *
     36 * Both serialize and cserialize do some normalization, so we can't test
     37 * for pure round-tripping, and we also can't compare their output since
     38 * they could normalize differently.  (We might at some point in the
     39 * future want to guarantee that any output of cserialize is
     40 * untouched by going through parse+serialize, though.)
     41 *
     42 * So we test idempotence of parse + serialize by running the whole
     43 * operation twice.  Likewise for parse + compute + cserialize.
     44 *
     45 * Slightly more interestingly, we test that serialize + parse is the
     46 * identity transform by comparing the output of parse + compute +
     47 * cserialize to the output of parse + serialize + parse + compute +
     48 * cserialize.
     49 */
     50 
     51 var gSystemFont = [
     52  "caption",
     53  "icon",
     54  "menu",
     55  "message-box",
     56  "small-caption",
     57  "status-bar",
     58  "-moz-button",
     59  "-moz-pull-down-menu",
     60  "-moz-list",
     61  "-moz-field",
     62 ];
     63 
     64 var gBadCompute = {
     65  // output wrapped around to positive, in exponential notation
     66  "-moz-box-ordinal-group": [ "-1", "-1000" ],
     67 };
     68 
     69 function xfail_compute(property, value)
     70 {
     71  if (property in gBadCompute &&
     72      gBadCompute[property].includes(value))
     73    return true;
     74 
     75  return false;
     76 }
     77 
     78 // constructed to map longhands ==> list of containing shorthands
     79 var gPropertyShorthands = {};
     80 
     81 var gElement = document.getElementById("testnode");
     82 var gDeclaration = gElement.style;
     83 var gComputedStyle = window.getComputedStyle(gElement);
     84 
     85 var gPrereqDeclaration =
     86  document.getElementById("prereqsheet").sheet.cssRules[0].style;
     87 
     88 // On Android, avoid most 'TEST-PASS' logging by overriding
     89 // SimpleTest.is/isnot, to improve performance
     90 if (navigator.appVersion.includes("Android")) {
     91  is = function is(a, b, name)
     92  {
     93    var pass = Object.is(a, b);
     94    if (!pass)
     95      SimpleTest.is(a, b, name);
     96  }
     97 
     98  isnot = function isnot(a, b, name)
     99  {
    100    var pass = !Object.is(a, b);
    101    if (!pass)
    102      SimpleTest.isnot(a, b, name);
    103  }
    104 }
    105 
    106 // Returns true if propA and propB are equivalent, considering aliasing.
    107 // (i.e. if one is an alias of the other, or if they're both aliases of
    108 // the same 3rd property)
    109 function are_properties_aliased(propA, propB)
    110 {
    111  // If either property is an alias, replace it with the property it aliases.
    112  if ("alias_for" in gCSSProperties[propA]) {
    113    propA = gCSSProperties[propA].alias_for;
    114  }
    115  if ("alias_for" in gCSSProperties[propB]) {
    116    propB = gCSSProperties[propB].alias_for;
    117  }
    118 
    119  return propA == propB;
    120 }
    121 
    122 function test_property(property)
    123 {
    124  var info = gCSSProperties[property];
    125 
    126  // can all properties be removed from the style?
    127  function test_remove_all_properties(propName, value) {
    128    var i, p = [];
    129    for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
    130    for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
    131    var errstr = "when setting property " + propName + " to " + value;
    132    is(gDeclaration.length, 0, "unremovable properties " + errstr);
    133    is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
    134  }
    135 
    136  function test_other_shorthands_empty(value, subprop) {
    137    if (!(subprop in gPropertyShorthands)) return;
    138    var shorthands = gPropertyShorthands[subprop];
    139    for (idx in shorthands) {
    140      var sh = shorthands[idx];
    141      if (are_properties_aliased(sh, property)) {
    142        continue;
    143      }
    144      is(gDeclaration.getPropertyValue(sh), "",
    145         "setting '" + value + "' on '" + property + "' (for shorthand '" + sh + "')");
    146    }
    147  }
    148 
    149  function test_value(value, resolved_value) {
    150    var value_has_variable_reference = resolved_value != null;
    151    var is_system_font = property == "font" && gSystemFont.includes(value);
    152 
    153    var colon = ": ";
    154    gDeclaration.setProperty(property, value, "");
    155 
    156    var idx;
    157 
    158    var step1val = gDeclaration.getPropertyValue(property);
    159    var step1vals = [];
    160    var step1ser = gDeclaration.cssText;
    161    if ("subproperties" in info)
    162      for (idx in info.subproperties)
    163        step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
    164    var step1comp;
    165    var step1comps = [];
    166    if (info.type != CSS_TYPE_TRUE_SHORTHAND)
    167      step1comp = gComputedStyle.getPropertyValue(property);
    168    if ("subproperties" in info)
    169      for (idx in info.subproperties)
    170        step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
    171 
    172    SimpleTest.isnot(step1val, "", "setting '" + value + "' on '" + property + "'");
    173    if ("subproperties" in info &&
    174        // System font doesn't produce meaningful value for subproperties.
    175        !is_system_font)
    176      for (idx in info.subproperties) {
    177        var subprop = info.subproperties[idx];
    178        if (value_has_variable_reference &&
    179            (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND ||
    180            info.type == CSS_TYPE_LEGACY_SHORTHAND)) {
    181          is(gDeclaration.getPropertyValue(subprop), "",
    182             "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
    183          test_other_shorthands_empty(value, subprop);
    184        } else {
    185          isnot(gDeclaration.getPropertyValue(subprop), "",
    186                "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
    187        }
    188      }
    189 
    190    // We don't care particularly about the whitespace or the placement of
    191    // semicolons, but for simplicity we'll test the current behavior.
    192    var expected_serialization = "";
    193    if (step1val != "") {
    194      if ("alias_for" in info) {
    195        let newValue = info.legacy_mapping && info.legacy_mapping[step1val]
    196          ? info.legacy_mapping[step1val] : step1val;
    197        // FIXME(emilio): This is a bit unfortunate:
    198        // https://github.com/w3c/csswg-drafts/issues/3332
    199        if (info.type == CSS_TYPE_LEGACY_SHORTHAND && value_has_variable_reference)
    200          newValue = "";
    201        expected_serialization = info.alias_for + colon + newValue + ";";
    202      } else if (info.type == CSS_TYPE_LEGACY_SHORTHAND) {
    203        is(property, "zoom", "Zoom is a bit special because it never " +
    204                             "serializes as-is, we always serialize the longhands, " +
    205                             "but it doesn't just map to a single property " +
    206                             "(and thus we can't use the 'alias_for' mechanism)");
    207        let transform = step1val == "1" ? "none" : "scale(" + step1val + ")";
    208        let origin = step1val == "1" ? "50% 50% 0px" : "0px 0px 0px";
    209        if (value_has_variable_reference) { // See above.
    210          transform = "";
    211          origin = "";
    212        }
    213        expected_serialization = "transform" + colon + transform + "; transform-origin" + colon + origin + ";";
    214      } else {
    215        expected_serialization = property + colon + step1val + ";";
    216      }
    217    }
    218    is(step1ser, expected_serialization,
    219       "serialization should match property value");
    220 
    221    gDeclaration.removeProperty(property);
    222    gDeclaration.setProperty(property, step1val, "");
    223 
    224    is(gDeclaration.getPropertyValue(property), step1val,
    225       "parse+serialize should be idempotent for '" +
    226         property + colon + value + "'");
    227    if (info.type != CSS_TYPE_TRUE_SHORTHAND) {
    228      is(gComputedStyle.getPropertyValue(property), step1comp,
    229         "serialize+parse should be identity transform for '" +
    230         property + ": " + value + "'");
    231    }
    232 
    233    if ("subproperties" in info &&
    234        // Using setProperty over subproperties is not sufficient for
    235        // system fonts, since the shorthand does more than its parts.
    236        !is_system_font &&
    237        !value_has_variable_reference) {
    238      gDeclaration.removeProperty(property);
    239      for (idx in info.subproperties) {
    240        var subprop = info.subproperties[idx];
    241        gDeclaration.setProperty(subprop, step1vals[idx], "");
    242      }
    243 
    244      // Now that all the subprops are set, check their values.  Note that we
    245      // need this in a separate loop, in case parts of the shorthand affect
    246      // the computed values of other parts.
    247      for (idx in info.subproperties) {
    248        var subprop = info.subproperties[idx];
    249        is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
    250           "serialize(" + subprop + ")+parse should be the identity " +
    251           "transform for '" + property + ": " + value + "'");
    252      }
    253      is(gDeclaration.getPropertyValue(property), step1val,
    254         "parse+split+serialize should be idempotent for '" +
    255         property + colon + value + "'");
    256    }
    257 
    258    // FIXME(emilio): Why is mask special?
    259    if (info.type != CSS_TYPE_TRUE_SHORTHAND &&
    260        property != "mask") {
    261      gDeclaration.removeProperty(property);
    262      gDeclaration.setProperty(property, step1comp, "");
    263      var func = xfail_compute(property, value) ? todo_is : is;
    264      func(gComputedStyle.getPropertyValue(property), step1comp,
    265           "parse+compute+serialize should be idempotent for '" +
    266           property + ": " + value + "'");
    267    }
    268    if ("subproperties" in info && !is_system_font) {
    269      gDeclaration.removeProperty(property);
    270      for (idx in info.subproperties) {
    271        var subprop = info.subproperties[idx];
    272        gDeclaration.setProperty(subprop, step1comps[idx], "");
    273      }
    274 
    275      // Now that all the subprops are set, check their values.  Note that we
    276      // need this in a separate loop, in case parts of the shorthand affect
    277      // the computed values of other parts.
    278      for (idx in info.subproperties) {
    279        var subprop = info.subproperties[idx];
    280        is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
    281           "parse+compute+serialize(" + subprop + ") should be idempotent for '" +
    282           property + ": " + value + "'");
    283      }
    284    }
    285 
    286    // sanity check shorthands to make sure disabled props aren't exposed
    287    if (info.type != CSS_TYPE_LONGHAND) {
    288      gDeclaration.setProperty(property, value, "");
    289      test_remove_all_properties(property, value);
    290    }
    291 
    292    gDeclaration.removeProperty(property);
    293  }
    294 
    295  function test_value_without_variable(value) {
    296    test_value(value, null);
    297  }
    298 
    299  function test_value_with_variable(value) {
    300    gPrereqDeclaration.setProperty("--a", value, "");
    301    test_value("var(--a)", value);
    302    gPrereqDeclaration.removeProperty("--a");
    303  }
    304 
    305  if ("prerequisites" in info) {
    306    var prereqs = info.prerequisites;
    307    for (var prereq in prereqs) {
    308      gPrereqDeclaration.setProperty(prereq, prereqs[prereq], "");
    309    }
    310  }
    311 
    312  var idx;
    313  for (idx in info.initial_values) {
    314    test_value_without_variable(info.initial_values[idx]);
    315    test_value_with_variable(info.initial_values[idx]);
    316  }
    317  for (idx in info.other_values) {
    318    test_value_without_variable(info.other_values[idx]);
    319    test_value_with_variable(info.other_values[idx]);
    320  }
    321 
    322  if ("prerequisites" in info) {
    323    for (var prereq in info.prerequisites) {
    324      gPrereqDeclaration.removeProperty(prereq);
    325    }
    326  }
    327 
    328 }
    329 
    330 function runTest() {
    331  // To avoid triggering the slow script dialog, we have to test one
    332  // property at a time.
    333  var props = [];
    334  for (var prop in gCSSProperties) {
    335    var info = gCSSProperties[prop];
    336    if ("subproperties" in info) {
    337      for (var idx in info.subproperties) {
    338        var subprop = info.subproperties[idx];
    339        if (!(subprop in gPropertyShorthands)) {
    340          gPropertyShorthands[subprop] = [];
    341        }
    342        gPropertyShorthands[subprop].push(prop);
    343      }
    344    }
    345    props.push(prop);
    346  }
    347  props = props.reverse();
    348  function do_one() {
    349    if (props.length == 0) {
    350      SimpleTest.finish();
    351      return;
    352    }
    353    test_property(props.pop());
    354    SimpleTest.executeSoon(do_one);
    355  }
    356  SimpleTest.executeSoon(do_one);
    357 }
    358 
    359 SimpleTest.waitForExplicitFinish();
    360 SimpleTest.requestLongerTimeout(7);
    361 runTest();
    362 </script>
    363 </pre>
    364 </body>
    365 </html>