test_value_storage.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <!DOCTYPE HTML>
  2. <html>
  3. <!--
  4. -->
  5. <head>
  6. <title>Test for parsing, storage, and serialization of CSS values</title>
  7. <script type="text/javascript" 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. <div id="testnode"></div>
  18. </div>
  19. <pre id="test">
  20. <script class="testbody" type="text/javascript">
  21. /** Test for parsing, storage, and serialization of CSS values **/
  22. /*
  23. * The idempotence tests here deserve a little bit of explanation. What
  24. * we're testing here are the following operations:
  25. * parse: string -> CSS rule
  26. * serialize: CSS rule -> string (normalization 1)
  27. * (this actually has two variants that go through partly different
  28. * codepaths, which we exercise with getPropertyValue and cssText)
  29. * compute: CSS rule -> computed style
  30. * cserialize: computed style -> string (normalization 2)
  31. *
  32. * Both serialize and cserialize do some normalization, so we can't test
  33. * for pure round-tripping, and we also can't compare their output since
  34. * they could normalize differently. (We might at some point in the
  35. * future want to guarantee that any output of cserialize is
  36. * untouched by going through parse+serialize, though.)
  37. *
  38. * So we test idempotence of parse + serialize by running the whole
  39. * operation twice. Likewise for parse + compute + cserialize.
  40. *
  41. * Slightly more interestingly, we test that serialize + parse is the
  42. * identity transform by comparing the output of parse + compute +
  43. * cserialize to the output of parse + serialize + parse + compute +
  44. * cserialize.
  45. */
  46. var gSystemFont = {
  47. "caption": true,
  48. "icon": true,
  49. "menu": true,
  50. "message-box": true,
  51. "small-caption": true,
  52. "status-bar": true,
  53. "-moz-window": true,
  54. "-moz-document": true,
  55. "-moz-desktop": true,
  56. "-moz-info": true,
  57. "-moz-dialog": true,
  58. "-moz-button": true,
  59. "-moz-pull-down-menu": true,
  60. "-moz-list": true,
  61. "-moz-field": true,
  62. "-moz-workspace": true,
  63. };
  64. var gBadCompute = {
  65. // output wrapped around to positive, in exponential notation
  66. "-moz-box-ordinal-group": [ "-1", "-1000" ],
  67. };
  68. function xfail_compute(property, value)
  69. {
  70. if (property in gBadCompute &&
  71. gBadCompute[property].indexOf(value) != -1)
  72. return true;
  73. return false;
  74. }
  75. // constructed to map longhands ==> list of containing shorthands
  76. var gPropertyShorthands = {};
  77. var gElement = document.getElementById("testnode");
  78. var gDeclaration = gElement.style;
  79. var gComputedStyle = window.getComputedStyle(gElement, "");
  80. var gPrereqDeclaration =
  81. document.getElementById("prereqsheet").sheet.cssRules[0].style;
  82. // Returns true if propA and propB are equivalent, considering aliasing.
  83. // (i.e. if one is an alias of the other, or if they're both aliases of
  84. // the same 3rd property)
  85. function are_properties_aliased(propA, propB)
  86. {
  87. // If either property is an alias, replace it with the property it aliases.
  88. if ("alias_for" in gCSSProperties[propA]) {
  89. propA = gCSSProperties[propA].alias_for;
  90. }
  91. if ("alias_for" in gCSSProperties[propB]) {
  92. propB = gCSSProperties[propB].alias_for;
  93. }
  94. return propA == propB;
  95. }
  96. function test_property(property)
  97. {
  98. var info = gCSSProperties[property];
  99. // can all properties be removed from the style?
  100. function test_remove_all_properties(property, value) {
  101. var i, p = [];
  102. for (i = 0; i < gDeclaration.length; i++) p.push(gDeclaration[i]);
  103. for (i = 0; i < p.length; i++) gDeclaration.removeProperty(p[i]);
  104. var errstr = "when setting property " + property + " to " + value;
  105. is(gDeclaration.length, 0, "unremovable properties " + errstr);
  106. is(gDeclaration.cssText, "", "non-empty serialization after removing all properties " + errstr);
  107. }
  108. function test_other_shorthands_empty(value, subprop) {
  109. if (!(subprop in gPropertyShorthands)) return;
  110. var shorthands = gPropertyShorthands[subprop];
  111. for (idx in shorthands) {
  112. var sh = shorthands[idx];
  113. if (are_properties_aliased(sh, property)) {
  114. continue;
  115. }
  116. is(gDeclaration.getPropertyValue(sh), "",
  117. "setting '" + value + "' on '" + property + "' (for shorthand '" + sh + "')");
  118. }
  119. }
  120. function test_value(value, resolved_value) {
  121. var value_has_variable_reference = resolved_value != null;
  122. gDeclaration.setProperty(property, value, "");
  123. var idx;
  124. var step1val = gDeclaration.getPropertyValue(property);
  125. var step1vals = [];
  126. var step1ser = gDeclaration.cssText;
  127. if ("subproperties" in info)
  128. for (idx in info.subproperties)
  129. step1vals.push(gDeclaration.getPropertyValue(info.subproperties[idx]));
  130. var step1comp;
  131. var step1comps = [];
  132. if (info.type != CSS_TYPE_TRUE_SHORTHAND)
  133. step1comp = gComputedStyle.getPropertyValue(property);
  134. if ("subproperties" in info)
  135. for (idx in info.subproperties)
  136. step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx]));
  137. SimpleTest.isnot(step1val, "", "setting '" + value + "' on '" + property + "'");
  138. if ("subproperties" in info)
  139. for (idx in info.subproperties) {
  140. var subprop = info.subproperties[idx];
  141. if (value_has_variable_reference &&
  142. (!info.alias_for || info.type == CSS_TYPE_TRUE_SHORTHAND)) {
  143. is(gDeclaration.getPropertyValue(subprop), "",
  144. "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
  145. test_other_shorthands_empty(value, subprop);
  146. } else {
  147. isnot(gDeclaration.getPropertyValue(subprop), "",
  148. "setting '" + value + "' on '" + property + "' (for '" + subprop + "')");
  149. }
  150. }
  151. // We don't care particularly about the whitespace or the placement of
  152. // semicolons, but for simplicity we'll test the current behavior.
  153. var expected_serialization = "";
  154. if (step1val != "") {
  155. if ("alias_for" in info) {
  156. expected_serialization = info.alias_for + ": " + step1val + ";";
  157. } else {
  158. expected_serialization = property + ": " + step1val + ";";
  159. }
  160. }
  161. is(step1ser, expected_serialization,
  162. "serialization should match property value");
  163. gDeclaration.removeProperty(property);
  164. gDeclaration.setProperty(property, step1val, "");
  165. is(gDeclaration.getPropertyValue(property), step1val,
  166. "parse+serialize should be idempotent for '" +
  167. property + ": " + value + "'");
  168. if (info.type != CSS_TYPE_TRUE_SHORTHAND) {
  169. is(gComputedStyle.getPropertyValue(property), step1comp,
  170. "serialize+parse should be identity transform for '" +
  171. property + ": " + value + "'");
  172. }
  173. if ("subproperties" in info &&
  174. // Using setProperty over subproperties is not sufficient for
  175. // system fonts, since the shorthand does more than its parts.
  176. (property != "font" || !(value in gSystemFont)) &&
  177. // Likewise for special compatibility values of transform
  178. (property != "-moz-transform" || !value.match(/^matrix.*(px|em|%)/)) &&
  179. !value_has_variable_reference) {
  180. gDeclaration.removeProperty(property);
  181. for (idx in info.subproperties) {
  182. var subprop = info.subproperties[idx];
  183. gDeclaration.setProperty(subprop, step1vals[idx], "");
  184. }
  185. // Now that all the subprops are set, check their values. Note that we
  186. // need this in a separate loop, in case parts of the shorthand affect
  187. // the computed values of other parts.
  188. for (idx in info.subproperties) {
  189. var subprop = info.subproperties[idx];
  190. is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
  191. "serialize(" + subprop + ")+parse should be the identity " +
  192. "transform for '" + property + ": " + value + "'");
  193. }
  194. is(gDeclaration.getPropertyValue(property), step1val,
  195. "parse+split+serialize should be idempotent for '" +
  196. property + ": " + value + "'");
  197. }
  198. if (info.type != CSS_TYPE_TRUE_SHORTHAND &&
  199. property != "mask") {
  200. gDeclaration.removeProperty(property);
  201. gDeclaration.setProperty(property, step1comp, "");
  202. var func = xfail_compute(property, value) ? todo_is : is;
  203. func(gComputedStyle.getPropertyValue(property), step1comp,
  204. "parse+compute+serialize should be idempotent for '" +
  205. property + ": " + value + "'");
  206. }
  207. if ("subproperties" in info) {
  208. gDeclaration.removeProperty(property);
  209. for (idx in info.subproperties) {
  210. var subprop = info.subproperties[idx];
  211. gDeclaration.setProperty(subprop, step1comps[idx], "");
  212. }
  213. // Now that all the subprops are set, check their values. Note that we
  214. // need this in a separate loop, in case parts of the shorthand affect
  215. // the computed values of other parts.
  216. for (idx in info.subproperties) {
  217. var subprop = info.subproperties[idx];
  218. is(gComputedStyle.getPropertyValue(subprop), step1comps[idx],
  219. "parse+compute+serialize(" + subprop + ") should be idempotent for '" +
  220. property + ": " + value + "'");
  221. }
  222. }
  223. // sanity check shorthands to make sure disabled props aren't exposed
  224. if (info.type != CSS_TYPE_LONGHAND) {
  225. gDeclaration.setProperty(property, value, "");
  226. test_remove_all_properties(property, value);
  227. }
  228. gDeclaration.removeProperty(property);
  229. }
  230. function test_value_without_variable(value) {
  231. test_value(value, null);
  232. }
  233. function test_value_with_variable(value) {
  234. gPrereqDeclaration.setProperty("--a", value, "");
  235. test_value("var(--a)", value);
  236. gPrereqDeclaration.removeProperty("--a");
  237. }
  238. if ("prerequisites" in info) {
  239. var prereqs = info.prerequisites;
  240. for (var prereq in prereqs) {
  241. gPrereqDeclaration.setProperty(prereq, prereqs[prereq], "");
  242. }
  243. }
  244. var idx;
  245. for (idx in info.initial_values) {
  246. test_value_without_variable(info.initial_values[idx]);
  247. test_value_with_variable(info.initial_values[idx]);
  248. }
  249. for (idx in info.other_values) {
  250. test_value_without_variable(info.other_values[idx]);
  251. test_value_with_variable(info.other_values[idx]);
  252. }
  253. if ("prerequisites" in info) {
  254. for (var prereq in info.prerequisites) {
  255. gPrereqDeclaration.removeProperty(prereq);
  256. }
  257. }
  258. }
  259. function runTest() {
  260. // To avoid triggering the slow script dialog, we have to test one
  261. // property at a time.
  262. ok(SpecialPowers.getBoolPref("layout.css.variables.enabled"), "pref not set #1");
  263. var props = [];
  264. for (var prop in gCSSProperties) {
  265. var info = gCSSProperties[prop];
  266. if ("subproperties" in info) {
  267. for (var idx in info.subproperties) {
  268. var subprop = info.subproperties[idx];
  269. if (!(subprop in gPropertyShorthands)) {
  270. gPropertyShorthands[subprop] = [];
  271. }
  272. gPropertyShorthands[subprop].push(prop);
  273. }
  274. }
  275. props.push(prop);
  276. }
  277. props = props.reverse();
  278. function do_one() {
  279. if (props.length == 0) {
  280. SimpleTest.finish();
  281. return;
  282. }
  283. test_property(props.pop());
  284. SimpleTest.executeSoon(do_one);
  285. }
  286. SimpleTest.executeSoon(do_one);
  287. }
  288. SimpleTest.waitForExplicitFinish();
  289. SimpleTest.requestLongerTimeout(7);
  290. SpecialPowers.pushPrefEnv({ set: [["layout.css.variables.enabled", true]] },
  291. runTest);
  292. </script>
  293. </pre>
  294. </body>
  295. </html>