test_logical_properties.html (16434B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <title>Test for handling of logical and physical properties</title> 4 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 7 <style id="sheet"></style> 8 9 <!-- specify size for <body> to avoid unconstrained-isize warnings 10 when writing-mode of the test <div> is vertical-* --> 11 <body style="width:100px; height: 100px;"> 12 <div id="test" class="test"></div> 13 </body> 14 15 <script> 16 var gSheet = document.getElementById("sheet"); 17 var gTest = document.getElementById("test"); 18 19 // list of groups of physical and logical box properties, such as 20 // 21 // { left: "margin-left", right: "margin-right", 22 // top: "margin-top", bottom: "margin-bottom", 23 // inlineStart: "margin-inline-start", inlineEnd: "margin-inline-end", 24 // blockStart: "margin-block-start", blockEnd: "margin-block-end", 25 // type: "length", prerequisites: "..." } 26 // 27 // where the type is a key from the gValues object and the prerequisites 28 // is a declaration including gCSSProperties' listed prerequisites for 29 // all four physical properties. 30 var gBoxPropertyGroups; 31 32 // list of groups of physical and logical axis properties, such as 33 // 34 // { horizontal: "width", vertical: "height", 35 // inline: "inline-size", block: "block-size", 36 // type: "length", prerequisites: "..." } 37 var gAxisPropertyGroups; 38 39 // values to use while testing 40 var gValues = { 41 "length": ["1px", "2px", "3px", "4px", "5px"], 42 "color": ["rgb(1, 1, 1)", "rgb(2, 2, 2)", "rgb(3, 3, 3)", "rgb(4, 4, 4)", "rgb(5, 5, 5)"], 43 "border-style": ["solid", "dashed", "dotted", "double", "groove"], 44 }; 45 46 // Six unique overall writing modes for property-mapping purposes. 47 // Note that text-orientation does not affect these mappings, now that 48 // the proposed sideways-left value no longer exists (superseded in CSS 49 // Writing Modes by writing-mode: sideways-lr). 50 var gWritingModes = [ 51 { style: [ 52 "writing-mode: horizontal-tb; direction: ltr; ", 53 ], 54 blockStart: "top", blockEnd: "bottom", inlineStart: "left", inlineEnd: "right", 55 block: "vertical", inline: "horizontal" }, 56 { style: [ 57 "writing-mode: horizontal-tb; direction: rtl; ", 58 ], 59 blockStart: "top", blockEnd: "bottom", inlineStart: "right", inlineEnd: "left", 60 block: "vertical", inline: "horizontal" }, 61 { style: [ 62 "writing-mode: vertical-rl; direction: rtl; ", 63 "writing-mode: sideways-rl; direction: rtl; ", 64 ], 65 blockStart: "right", blockEnd: "left", inlineStart: "bottom", inlineEnd: "top", 66 block: "horizontal", inline: "vertical" }, 67 { style: [ 68 "writing-mode: vertical-rl; direction: ltr; ", 69 "writing-mode: sideways-rl; direction: ltr; ", 70 ], 71 blockStart: "right", blockEnd: "left", inlineStart: "top", inlineEnd: "bottom", 72 block: "horizontal", inline: "vertical" }, 73 { style: [ 74 "writing-mode: vertical-lr; direction: rtl; ", 75 "writing-mode: sideways-lr; direction: ltr; ", 76 ], 77 blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top", 78 block: "horizontal", inline: "vertical" }, 79 { style: [ 80 "writing-mode: vertical-lr; direction: ltr; ", 81 "writing-mode: sideways-lr; direction: rtl; ", 82 ], 83 blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom", 84 block: "horizontal", inline: "vertical" }, 85 ]; 86 87 function init() { 88 gBoxPropertyGroups = []; 89 90 for (var p in gCSSProperties) { 91 var type = gCSSProperties[p].type; 92 93 if ((type == CSS_TYPE_SHORTHAND_AND_LONGHAND || 94 type == CSS_TYPE_LONGHAND && gCSSProperties[p].logical) && 95 /-inline-end/.test(p)) { 96 var valueType; 97 if (/margin|padding|width|inset|offset/.test(p)) { 98 valueType = "length"; 99 } else if (/color/.test(p)) { 100 valueType = "color"; 101 } else if (/border.*style/.test(p)) { 102 valueType = "border-style"; 103 } else { 104 throw `unexpected property ${p}`; 105 } 106 var group = { 107 inlineStart: p.replace("-inline-end", "-inline-start"), 108 inlineEnd: p, 109 blockStart: p.replace("-inline-end", "-block-start"), 110 blockEnd: p.replace("-inline-end", "-block-end"), 111 type: valueType 112 }; 113 if (/^(offset|inset)/.test(p)) { 114 group.left = "left"; 115 group.right = "right"; 116 group.top = "top"; 117 group.bottom = "bottom"; 118 } else { 119 group.left = p.replace("-inline-end", "-left"); 120 group.right = p.replace("-inline-end", "-right"); 121 group.top = p.replace("-inline-end", "-top"); 122 group.bottom = p.replace("-inline-end", "-bottom"); 123 } 124 group.prerequisites = 125 make_declaration(gCSSProperties[group.top].prerequisites) + 126 make_declaration(gCSSProperties[group.right].prerequisites) + 127 make_declaration(gCSSProperties[group.bottom].prerequisites) + 128 make_declaration(gCSSProperties[group.left].prerequisites); 129 gBoxPropertyGroups.push(group); 130 } 131 } 132 133 // We don't populate this automatically since the only entries we have, for 134 // inline-size etc., don't lend themselves to automatically determining 135 // the names "width", "height", "min-width", etc. 136 gAxisPropertyGroups = []; 137 ["", "max-", "min-"].forEach(function(aPrefix) { 138 gAxisPropertyGroups.push({ 139 horizontal: `${aPrefix}width`, vertical: `${aPrefix}height`, 140 inline: `${aPrefix}inline-size`, block: `${aPrefix}block-size`, 141 type: "length", 142 prerequisites: 143 make_declaration(gCSSProperties[`${aPrefix}height`].prerequisites) 144 }); 145 }); 146 } 147 148 function test_computed_values(aTestName, aRules, aExpectedValues) { 149 gSheet.textContent = aRules; 150 var cs = getComputedStyle(gTest); 151 aExpectedValues.forEach(function(aPair) { 152 is(cs.getPropertyValue(aPair[0]), aPair[1], `${aTestName}, ${aPair[0]}`); 153 }); 154 gSheet.textContent = ""; 155 } 156 157 function make_declaration(aObject) { 158 var decl = ""; 159 if (aObject) { 160 for (var p in aObject) { 161 decl += `${p}: ${aObject[p]}; `; 162 } 163 } 164 return decl; 165 } 166 167 function start() { 168 var script = document.createElement("script"); 169 script.src = "property_database.js"; 170 script.onload = function() { 171 init(); 172 run_tests(); 173 }; 174 document.body.appendChild(script); 175 } 176 177 function run_axis_test_for_writing_mode(aGroup, aWritingMode, aWritingModeDecl) { 178 var values = gValues[aGroup.type]; 179 var decl; 180 181 // Test that logical axis properties are converted to their physical 182 // equivalent correctly when all four are present on a single 183 // declaration, with no overwriting of previous properties and 184 // no physical properties present. We put the writing mode properties 185 // on a separate declaration to test that the computed values of these 186 // properties are used, rather than those on the same declaration. 187 188 decl = aGroup.prerequisites + 189 `${aGroup.inline}: ${values[0]}; ` + 190 `${aGroup.block}: ${values[1]}; `; 191 test_computed_values('logical properties on one declaration, writing ' + 192 'mode properties on another, ' + 193 `'${aWritingModeDecl}'`, 194 `.test { ${aWritingModeDecl} } ` + 195 `.test { ${decl} }`, 196 [[aGroup[aWritingMode.inline], values[0]], 197 [aGroup[aWritingMode.block], values[1]]]); 198 199 200 // Test that logical and physical axis properties are cascaded together, 201 // honoring their relative order on a single declaration. 202 203 // (a) with a single logical property after the physical ones 204 205 ["inline", "block"].forEach(function(aLogicalAxis) { 206 decl = aWritingModeDecl + aGroup.prerequisites + 207 `${aGroup.horizontal}: ${values[0]}; ` + 208 `${aGroup.vertical}: ${values[1]}; ` + 209 `${aGroup[aLogicalAxis]}: ${values[2]}; `; 210 var expected = ["horizontal", "vertical"].map( 211 (axis, i) => [aGroup[axis], 212 values[axis == aWritingMode[aLogicalAxis] ? 2 : i]] 213 ); 214 test_computed_values(`${aLogicalAxis} last on single declaration, ` + 215 `'${aWritingModeDecl}'`, 216 `.test { ${decl} }`, 217 expected); 218 }); 219 220 // (b) with a single physical property after the logical ones 221 222 ["horizontal", "vertical"].forEach(function(aPhysicalAxis) { 223 decl = aWritingModeDecl + aGroup.prerequisites + 224 `${aGroup.inline}: ${values[0]}; ` + 225 `${aGroup.block}: ${values[1]}; ` + 226 `${aGroup[aPhysicalAxis]}: ${values[2]}; `; 227 var expected = ["inline", "block"].map( 228 (axis, i) => [aGroup[aWritingMode[axis]], 229 values[aWritingMode[axis] == aPhysicalAxis ? 2 : i]] 230 ); 231 test_computed_values(`${aPhysicalAxis} last on single declaration, ` + 232 `'${aWritingModeDecl}'`, 233 `.test { ${decl} }`, 234 expected); 235 }); 236 237 238 // Test that logical and physical axis properties are cascaded properly when 239 // on different declarations. 240 241 var loDecl; // lower specifity 242 var hiDecl; // higher specificity 243 244 // (a) with a logical property in the high specificity rule 245 246 loDecl = aWritingModeDecl + aGroup.prerequisites + 247 `${aGroup.horizontal}: ${values[0]}; ` + 248 `${aGroup.vertical}: ${values[1]}; `; 249 250 ["inline", "block"].forEach(function(aLogicalAxis) { 251 hiDecl = `${aGroup[aLogicalAxis]}: ${values[2]}; `; 252 var expected = ["horizontal", "vertical"].map( 253 (axis, i) => [aGroup[axis], 254 values[axis == aWritingMode[aLogicalAxis] ? 2 : i]] 255 ); 256 test_computed_values(`${aLogicalAxis}, two declarations, ` + 257 `'${aWritingModeDecl}'`, 258 `#test { ${hiDecl} } ` + 259 `.test { ${loDecl} }`, 260 expected); 261 }); 262 263 // (b) with a physical property in the high specificity rule 264 265 loDecl = aWritingModeDecl + aGroup.prerequisites + 266 `${aGroup.inline}: ${values[0]}; ` + 267 `${aGroup.block}: ${values[1]}; `; 268 269 ["horizontal", "vertical"].forEach(function(aPhysicalAxis) { 270 hiDecl = `${aGroup[aPhysicalAxis]}: ${values[2]}; `; 271 var expected = ["inline", "block"].map( 272 (axis, i) => [aGroup[aWritingMode[axis]], 273 values[aWritingMode[axis] == aPhysicalAxis ? 2 : i]] 274 ); 275 test_computed_values(`${aPhysicalAxis}, two declarations, ` + 276 `'${aWritingModeDecl}'`, 277 `#test { ${hiDecl} } ` + 278 `.test { ${loDecl} }`, 279 expected); 280 }); 281 } 282 283 function run_box_test_for_writing_mode(aGroup, aWritingMode, aWritingModeDecl) { 284 var values = gValues[aGroup.type]; 285 var decl; 286 287 // Test that logical box properties are converted to their physical 288 // equivalent correctly when all four are present on a single 289 // declaration, with no overwriting of previous properties and 290 // no physical properties present. We put the writing mode properties 291 // on a separate declaration to test that the computed values of these 292 // properties are used, rather than those on the same declaration. 293 294 decl = aGroup.prerequisites + 295 `${aGroup.inlineStart}: ${values[0]}; ` + 296 `${aGroup.inlineEnd}: ${values[1]}; ` + 297 `${aGroup.blockStart}: ${values[2]}; ` + 298 `${aGroup.blockEnd}: ${values[3]}; `; 299 test_computed_values('logical properties on one declaration, writing ' + 300 'mode properties on another, ' + 301 `'${aWritingModeDecl}'`, 302 `.test { ${aWritingModeDecl} } ` + 303 `.test { ${decl} }`, 304 [[aGroup[aWritingMode.inlineStart], values[0]], 305 [aGroup[aWritingMode.inlineEnd], values[1]], 306 [aGroup[aWritingMode.blockStart], values[2]], 307 [aGroup[aWritingMode.blockEnd], values[3]]]); 308 309 // Test that logical and physical box properties are cascaded together, 310 // honoring their relative order on a single declaration. 311 312 // (a) with a single logical property after the physical ones 313 314 ["inlineStart", "inlineEnd", "blockStart", "blockEnd"].forEach(function(aLogicalSide) { 315 decl = aWritingModeDecl + aGroup.prerequisites + 316 `${aGroup.left}: ${values[0]}; ` + 317 `${aGroup.right}: ${values[1]}; ` + 318 `${aGroup.top}: ${values[2]}; ` + 319 `${aGroup.bottom}: ${values[3]}; ` + 320 `${aGroup[aLogicalSide]}: ${values[4]}; `; 321 var expected = ["left", "right", "top", "bottom"].map( 322 (side, i) => [aGroup[side], 323 values[side == aWritingMode[aLogicalSide] ? 4 : i]] 324 ); 325 test_computed_values(`${aLogicalSide} last on single declaration, ` + 326 `'${aWritingModeDecl}'`, 327 `.test { ${decl} }`, 328 expected); 329 }); 330 331 // (b) with a single physical property after the logical ones 332 333 ["left", "right", "top", "bottom"].forEach(function(aPhysicalSide) { 334 decl = aWritingModeDecl + aGroup.prerequisites + 335 `${aGroup.inlineStart}: ${values[0]}; ` + 336 `${aGroup.inlineEnd}: ${values[1]}; ` + 337 `${aGroup.blockStart}: ${values[2]}; ` + 338 `${aGroup.blockEnd}: ${values[3]}; ` + 339 `${aGroup[aPhysicalSide]}: ${values[4]}; `; 340 var expected = ["inlineStart", "inlineEnd", "blockStart", "blockEnd"].map( 341 (side, i) => [aGroup[aWritingMode[side]], 342 values[aWritingMode[side] == aPhysicalSide ? 4 : i]] 343 ); 344 test_computed_values(`${aPhysicalSide} last on single declaration, ` + 345 `'${aWritingModeDecl}'`, 346 `.test { ${decl} }`, 347 expected); 348 }); 349 350 351 // Test that logical and physical box properties are cascaded properly when 352 // on different declarations. 353 354 var loDecl; // lower specifity 355 var hiDecl; // higher specificity 356 357 // (a) with a logical property in the high specificity rule 358 359 loDecl = aWritingModeDecl + aGroup.prerequisites + 360 `${aGroup.left}: ${values[0]}; ` + 361 `${aGroup.right}: ${values[1]}; ` + 362 `${aGroup.top}: ${values[2]}; ` + 363 `${aGroup.bottom}: ${values[3]}; `; 364 365 ["inlineStart", "inlineEnd", "blockStart", "blockEnd"].forEach(function(aLogicalSide) { 366 hiDecl = `${aGroup[aLogicalSide]}: ${values[4]}; `; 367 var expected = ["left", "right", "top", "bottom"].map( 368 (side, i) => [aGroup[side], 369 values[side == aWritingMode[aLogicalSide] ? 4 : i]] 370 ); 371 test_computed_values(`${aLogicalSide}, two declarations, ` + 372 `'${aWritingModeDecl}'`, 373 `#test { ${hiDecl} } ` + 374 `.test { ${loDecl} }`, 375 expected); 376 }); 377 378 // (b) with a physical property in the high specificity rule 379 380 loDecl = aWritingModeDecl + aGroup.prerequisites + 381 `${aGroup.inlineStart}: ${values[0]}; ` + 382 `${aGroup.inlineEnd}: ${values[1]}; ` + 383 `${aGroup.blockStart}: ${values[2]}; ` + 384 `${aGroup.blockEnd}: ${values[3]}; `; 385 386 ["left", "right", "top", "bottom"].forEach(function(aPhysicalSide) { 387 hiDecl = `${aGroup[aPhysicalSide]}: ${values[4]}; `; 388 var expected = ["inlineStart", "inlineEnd", "blockStart", "blockEnd"].map( 389 (side, i) => [aGroup[aWritingMode[side]], 390 values[aWritingMode[side] == aPhysicalSide ? 4 : i]] 391 ); 392 test_computed_values(`${aPhysicalSide}, two declarations, ` + 393 `'${aWritingModeDecl}'`, 394 `#test { ${hiDecl} } ` + 395 `.test { ${loDecl} }`, 396 expected); 397 }); 398 } 399 400 function run_tests() { 401 gBoxPropertyGroups.forEach(function(aGroup) { 402 gWritingModes.forEach(function(aWritingMode) { 403 aWritingMode.style.forEach(function(aWritingModeDecl) { 404 run_box_test_for_writing_mode(aGroup, aWritingMode, aWritingModeDecl); 405 }); 406 }); 407 }); 408 409 gAxisPropertyGroups.forEach(function(aGroup) { 410 gWritingModes.forEach(function(aWritingMode) { 411 aWritingMode.style.forEach(function(aWritingModeDecl) { 412 run_axis_test_for_writing_mode(aGroup, aWritingMode, aWritingModeDecl); 413 }); 414 }); 415 }); 416 417 SimpleTest.finish(); 418 } 419 420 SimpleTest.waitForExplicitFinish(); 421 start(); 422 </script>