parsing-utils.js (31989B)
1 var ParsingUtils = (function() { 2 function testInlineStyle(value, expected) { 3 var div = document.createElement('div'); 4 div.style.setProperty('shape-outside', value); 5 var actual = div.style.getPropertyValue('shape-outside'); 6 assert_equals(actual, expected); 7 } 8 9 function testComputedStyle(value, expected) { 10 var div = document.createElement('div'); 11 div.style.setProperty('shape-outside', value); 12 document.body.appendChild(div); 13 var style = getComputedStyle(div); 14 var actual = style.getPropertyValue('shape-outside'); 15 actual = roundResultStr(actual); 16 document.body.removeChild(div); 17 18 // Some of the tests in this suite have either/or expected results 19 // so this check allows for testing that at least one of them passes. 20 // Description of the 2 expecteds is below near calcTestValues. 21 if(Object.prototype.toString.call( expected ) === '[object Array]' && expected.length == 2) { 22 assert_in_array(actual, expected); 23 } else { 24 assert_equals(actual, typeof expected !== 'undefined' ? expected : value); 25 } 26 } 27 28 function testShapeMarginInlineStyle(value, expected) { 29 var div = document.createElement('div'); 30 div.style.setProperty('shape-outside', "border-box inset(10px)"); 31 div.style.setProperty('shape-margin', value); 32 var actual = div.style.getPropertyValue('shape-margin'); 33 actual = roundResultStr(actual); 34 expected = roundResultStr(expected); 35 assert_equals(actual, expected); 36 } 37 38 function testShapeMarginComputedStyle(value, expected) { 39 40 var outerDiv = document.createElement('div'); 41 outerDiv.style.setProperty('width', '100px'); 42 43 var innerDiv = document.createElement('div'); 44 innerDiv.style.setProperty('shape-outside', "border-box inset(10px)"); 45 innerDiv.style.setProperty('shape-margin', value); 46 47 outerDiv.appendChild(innerDiv); 48 document.body.appendChild(outerDiv); 49 50 var style = getComputedStyle(innerDiv); 51 var actual = style.getPropertyValue('shape-margin'); 52 53 assert_not_equals(actual, null); 54 if(actual.indexOf('calc') == -1 ) 55 actual = roundResultStr(actual); 56 document.body.removeChild(outerDiv); 57 58 // See comment above about multiple expected results 59 if(Object.prototype.toString.call( expected ) === '[object Array]' && expected.length == 2) { 60 assert_in_array(actual, expected); 61 } else { 62 assert_equals(actual, !expected ? '0px' : expected); 63 } 64 } 65 66 function testShapeThresholdInlineStyle(value, expected) { 67 var div = document.createElement('div'); 68 div.style.setProperty('shape-outside', 'url(someimage.png)'); 69 div.style.setProperty('shape-image-threshold', value); 70 var actual = div.style.getPropertyValue('shape-image-threshold'); 71 assert_equals(actual, expected); 72 } 73 74 function testShapeThresholdComputedStyle(value, expected) { 75 76 var div = document.createElement('div'); 77 div.style.setProperty('shape-outside', 'url(someimage.png)'); 78 div.style.setProperty('shape-image-threshold', value); 79 document.body.appendChild(div); 80 81 var style = getComputedStyle(div); 82 var actual = style.getPropertyValue('shape-image-threshold'); 83 84 assert_not_equals(actual, null); 85 if(actual.indexOf('calc') == -1 ) 86 actual = roundResultStr(actual); 87 document.body.removeChild(div); 88 89 // See comment above about multiple expected results 90 if(Object.prototype.toString.call( expected ) === '[object Array]' && expected.length == 2) { 91 assert_in_array(actual, expected); 92 } else { 93 assert_equals(actual, !expected ? '0' : expected); 94 } 95 } 96 97 // Builds an array of test cases to send to testharness.js where one test case is: [name, actual, expected] 98 // These test cases will verify results from testInlineStyle() or testComputedStyle() 99 function buildTestCases(testCases, testType) { 100 var results = []; 101 102 // If test_type isn't specified, test inline style 103 var type = typeof testType == 'undefined' ? 'invalid': testType; 104 105 testCases.forEach(function(test) { 106 oneTestCase = []; 107 108 // name - annotated by type (inline vs. computed) 109 if ( test.hasOwnProperty('name') ) { 110 oneTestCase.push(test['name'] +' - '+ type); 111 } else { 112 // If test_name isn't specified, use the actual 113 oneTestCase.push(test['actual'] +' - '+ type); 114 } 115 116 // actual 117 oneTestCase.push(test['actual']) 118 119 // expected 120 if( type.indexOf('invalid') != -1 ){ 121 oneTestCase.push("") 122 } else if( type == 'inline' ) { 123 oneTestCase.push(test['expected_inline']); 124 } else if( type == 'computed' ){ 125 oneTestCase.push( convertToPx(test['expected_computed']) ); 126 } 127 results.push(oneTestCase); 128 }); 129 return results; 130 } 131 132 function isAbsoluteLength(unit) { 133 return unit == "cm" || unit == "mm" || unit == "Q" || unit == "in" || 134 unit == "pt" || unit == "pc" || unit == "px"; 135 } 136 137 function buildPositionTests(shape, valid, type, units) { 138 var results = new Array(); 139 var is_computed = type.indexOf('computed') != -1 ? true : false; 140 141 if(Object.prototype.toString.call( units ) === '[object Array]') { 142 units.forEach(function(unit) { 143 positionTests = buildPositionTests(shape, valid, type, unit); 144 results = results.concat(positionTests); 145 }); 146 } else { 147 if (valid) { 148 validPositions.forEach(function(test) { 149 var testCase = [], testName, actual, expected; 150 // skip if this isn't explicitly testing length units 151 if (!(type.indexOf('lengthUnit') != -1 && test[0].indexOf("u1") == -1)) { 152 // actual 153 actual = shape + '(at ' + setUnit(test[0], false, units) +')'; 154 155 let position = test[1]; 156 let convert = is_computed; 157 if (!is_computed) { 158 // For specified values. 159 // Note: "[convert]" tag is used only for the specified 160 // value. 161 if (position.includes('[convert]')) { 162 // We should convert the absolute length into the 163 // canonical unit in calc(), for specified values. 164 // e.g. 165 // 1. "circle(at 1pt 50%)" serializes as 166 // "circle(at 1pt 50%)". 167 // 2. "circle(at calc(1pt) 50%)" serializes as 168 // "circle(at calc(1.33px) 50%)". 169 convert = isAbsoluteLength(units); 170 } 171 } else if (test.length == 3) { 172 // Use the 3rd element as the expected computed value. 173 position = test[2]; 174 } 175 176 // Remove the tag if there is. 177 position = position.replace('[convert] ', ''); 178 179 // expected 180 // if(convert && shape == 'circle') 181 // expected = shape + '(at ' + setUnit(test[1], convert, units) +')'; 182 // else if(convert && shape == 'ellipse') 183 // expected = shape + '(at ' + setUnit(test[1], convert, units) +')'; 184 // else 185 expected = shape + '(at ' + setUnit(position, convert, units) +')'; 186 187 // name 188 if (type == 'lengthUnit + inline') 189 testName = 'test unit (inline): ' + units +' - '+ actual; 190 else if (type == 'lengthUnit + computed') 191 testName = 'test unit (computed): ' + units +' - '+ actual; 192 else 193 testName = (actual + ' serializes as ' + expected +' - '+ type); 194 195 testCase.push(testName) 196 testCase.push(actual); 197 testCase.push(expected); 198 results.push(testCase); 199 } 200 }); 201 } else { 202 invalidPositions.forEach(function(test) { 203 var testValue = shape + '(at ' + setUnit(test, false, units) +')'; 204 testCase = new Array(); 205 testCase.push(testValue + ' is invalid'); 206 testCase.push(testValue); 207 testCase.push(""); 208 results.push(testCase); 209 }); 210 } 211 } 212 return unique(results); 213 } 214 215 function buildRadiiTests(shape, type, units) { 216 var results = new Array(); 217 var testUnits = typeof units == 'undefined' ? 'px': units; 218 var convert = type.indexOf('computed') != -1 ? true : false; 219 220 if(Object.prototype.toString.call( testUnits ) === '[object Array]') { 221 testUnits.forEach(function(unit) { 222 radiiTests = buildRadiiTests(shape, type, unit); 223 results = results.concat(radiiTests); 224 }); 225 } else { 226 var validRadii = shape == 'circle' ? validCircleRadii : validEllipseRadii; 227 validRadii.forEach(function(test) { 228 var testCase = [], name, actual, expected; 229 230 // skip if this isn't explicitly testing length units 231 if( !(type.indexOf('lengthUnit') != -1 && test[0].indexOf("u1") == -1) ) { 232 actual = shape + '(' + setUnit(test[0], false, testUnits) +')'; 233 // name 234 if (type.indexOf('lengthUnit') != -1) { 235 name = 'test unit: ' + units +' - '+ actual; 236 if(type.indexOf('computed') != -1) 237 name = name + ' - computed'; 238 else 239 name = name + ' - inline'; 240 } 241 else 242 name = actual +' - '+ type; 243 244 testCase.push(name); 245 246 // actual 247 testCase.push(actual); 248 249 // expected 250 if(type.indexOf('computed') != -1 && test.length == 3) { 251 expected = shape + '(' + setUnit(test[2], convert, testUnits) +')'; 252 } else { 253 expected = shape + '(' + setUnit(test[1], convert, testUnits) +')'; 254 } 255 testCase.push(expected); 256 results.push(testCase); 257 } 258 }); 259 } 260 return unique(results); 261 } 262 263 function buildInsetTests(unit1, unit2, type) { 264 var results = new Array(); 265 var convert = type == 'computed' ? true : false; 266 267 if(Object.prototype.toString.call( unit1 ) === '[object Array]') { 268 unit1.forEach(function(unit) { 269 insetTests = buildInsetTests(unit, unit2, type); 270 results = results.concat(insetTests); 271 }); 272 } else { 273 validInsets.forEach(function(test) { 274 var testCase = [], name, actual, expected; 275 276 name = setUnit(test[0], false, unit1, unit2) +' - '+ type; 277 actual = 'inset(' + setUnit(test[1], convert, unit1, unit2) +')'; 278 expected = actual; 279 280 testCase.push(name); 281 testCase.push(actual); 282 testCase.push(expected); 283 284 results.push(testCase); 285 }); 286 } 287 return unique(results); 288 } 289 290 function buildPolygonTests(unitSet, type) { 291 var results = new Array(); 292 var convert = type == 'computed' ? true : false; 293 294 unitSet.forEach(function(set) { 295 validPolygons.forEach(function(test) { 296 var testCase = []; 297 // name 298 testCase.push(setUnit(test[0], false, set[0], set[1], set[2]) +' - '+ type); 299 // actual 300 testCase.push('polygon(' + setUnit(test[1], false, set[0], set[1], set[2]) +')'); 301 // expected 302 testCase.push('polygon(' + setUnit(test[1], convert, set[0], set[1], set[2]) +')'); 303 results.push(testCase); 304 }); 305 }); 306 return unique(results); 307 } 308 309 function buildCalcTests(testCases, type) { 310 var results = new Array(); 311 testCases.forEach(function(test){ 312 var testCase = []; 313 if(type == 'computed') { 314 testCase.push(test[0] + ' - computed style'); 315 testCase.push(test[0]); 316 testCase.push(test[2]); 317 } 318 else { 319 testCase.push(test[0] + ' - inline style'); 320 testCase.push(test[0]); 321 testCase.push(test[1]); 322 } 323 testCase.push(type); 324 results.push(testCase) 325 }); 326 return unique(results); 327 } 328 329 function unique(tests) { 330 var list = tests.concat(); 331 for(var i = 0; i< list.length; ++i) { 332 for(var j = i+1; j < list.length; ++j) { 333 if(list[i][0] === list[j][0]) 334 list.splice(j--, 1); 335 } 336 } 337 return list; 338 } 339 340 function setUnit(str, convert, unit1, unit2, unit3) { 341 var retStr = str; 342 if(typeof unit1 !== 'undefined') { 343 retStr = retStr.replace(new RegExp('u1', 'g'), unit1); 344 } 345 if(typeof unit2 !== 'undefined') { 346 retStr = retStr.replace(new RegExp("u2", 'g'), unit2); 347 } 348 if(typeof unit3 !== 'undefined') { 349 retStr = retStr.replace(new RegExp("u3", 'g'), unit3); 350 } 351 retStr = convert ? convertToPx(retStr) : retStr; 352 return retStr; 353 } 354 355 function roundCssNumber(n) { 356 // See https://drafts.csswg.org/cssom/#serializing-css-values for numbers. 357 return parseFloat(n.toPrecision(6)); 358 } 359 360 function convertToPx(origValue) { 361 362 var valuesToConvert = origValue.match(/[0-9]+(\.[0-9]+)?([a-z]{2,4}|%|)/g); 363 if(!valuesToConvert) 364 return origValue; 365 366 var retStr = origValue; 367 for(var i = 0; i < valuesToConvert.length; i++) { 368 var unit = (valuesToConvert[i].match(/[a-z]{2,4}|%/) || '').toString(); 369 var numberStr = valuesToConvert[i].match(/[0-9]+(\.[0-9]+)?/)[0]; 370 371 var number = parseFloat(numberStr); 372 var convertedUnit = 'px'; 373 if( typeof number !== 'NaN' ) 374 { 375 if (unit == 'in') { 376 number = (96 * number); 377 } else if (unit == 'cm') { 378 number = (37.795275591 * number); 379 } else if (unit == 'mm') { 380 number = (3.779527559 * number); 381 } else if (unit == 'pt') { 382 number = (1.333333333333 * number); 383 } else if (unit == 'pc') { 384 number = (16 * number); 385 } else if (unit == 'em') { 386 number = (16 * number); 387 } else if (unit == 'ex') { 388 number = (12.8 * number); 389 } else if (unit == 'ch') { 390 number = (16 * number); 391 } else if (unit == 'rem') { 392 number = (16 * number); 393 } else if (unit == 'vw') { 394 number = ((.01 * window.innerWidth) * number); 395 } else if (unit == 'vh') { 396 number = ((.01 * window.innerHeight) * number); 397 } else if (unit == 'vmin') { 398 number = Math.min( (.01 * window.innerWidth), (.01 * window.innerHeight) ) * number; 399 } else if (unit == 'vmax') { 400 number = Math.max( (.01 * window.innerWidth), (.01 * window.innerHeight) ) * number; 401 } 402 else { 403 convertedUnit = unit; 404 } 405 number = roundCssNumber(number); 406 var find = valuesToConvert[i]; 407 var replace = number.toString() + convertedUnit; 408 retStr = retStr.replace(valuesToConvert[i], number.toString() + convertedUnit); 409 } 410 } 411 return retStr.replace(',,', ','); 412 } 413 414 function roundResultStr(str) { 415 if(Object.prototype.toString.call( str ) !== '[object String]') 416 return str; 417 418 var numbersToRound = str.match(/[0-9]+\.[0-9]+/g); 419 if(!numbersToRound) 420 return str; 421 422 var retStr = str; 423 for(var i = 0; i < numbersToRound.length; i++) { 424 num = parseFloat(numbersToRound[i]); 425 if( !isNaN(num) ) { 426 roundedNum = roundCssNumber(num); 427 retStr = retStr.replace(numbersToRound[i].toString(), roundedNum.toString()); 428 } 429 } 430 431 return retStr; 432 } 433 434 function generateInsetRoundCases(units, testType) { 435 var convert = testType.indexOf('computed') != -1 ? true : false; 436 var testUnit = units; 437 var sizes = [ 438 '10' + units, 439 '20' + units, 440 '30' + units, 441 '40' + units 442 ]; 443 444 function insetRound(value) { 445 return 'inset(10' +testUnit+ ' round ' + value + ')'; 446 } 447 448 function serializedInsetRound(lhsValues, rhsValues, convert) { 449 var retStr = ''; 450 if(!rhsValues) 451 retStr = 'inset(10' +testUnit+ ' round ' + lhsValues +')'; 452 else 453 retStr = 'inset(10' +testUnit+ ' round ' + lhsValues +' / '+ rhsValues +')'; 454 455 if(convert) 456 return convertToPx(retStr); 457 458 return retStr; 459 } 460 461 var results = [], left, lhs, right, rhs; 462 for (left = 1; left <= 4; left++) { 463 lhs = sizes.slice(0, left).join(' '); 464 results.push([insetRound(lhs) +' - '+ testType, insetRound(lhs), serializedInsetRound(lhs, null, convert)]); 465 for (right = 1; right <= 4; right++) { 466 rhs = sizes.slice(0, right).join(' '); 467 if(lhs == rhs) 468 results.push([insetRound(lhs + ' / ' + rhs) +' - '+ testType, insetRound(lhs + ' / ' + rhs), serializedInsetRound(lhs, null, convert)]); 469 else 470 results.push([insetRound(lhs + ' / ' + rhs) +' - '+ testType, insetRound(lhs + ' / ' + rhs), serializedInsetRound(lhs, rhs, convert)]); 471 } 472 } 473 return results; 474 } 475 476 function each(object, func) { 477 for (var prop in object) { 478 if (object.hasOwnProperty(prop)) { 479 func(prop, object[prop]); 480 } 481 } 482 } 483 484 /// For saving and restoring font properties 485 var savedFontValues = { }; 486 487 function setupFonts() { 488 var fontProperties = { 489 'font-family': 'Ahem', 490 'font-size': '16px', 491 'line-height': '1' 492 }; 493 savedFontValues = { }; 494 each(fontProperties, function (key, value) { 495 savedFontValues[key] = document.body.style.getPropertyValue(key); 496 document.body.style.setProperty(key, value); 497 }); 498 } 499 500 function restoreFonts() { 501 each(savedFontValues, function (key, value) { 502 if (value) { 503 document.body.style.setProperty(key, value); 504 } 505 else { 506 document.body.style.removeProperty(key); 507 } 508 }); 509 savedFontValues = { }; 510 } 511 512 var validUnits = [ 513 "cm","mm","in","pt","pc", // Absolute length units (omitting px b/c we default to that in all tests) 514 "em","ex","ch","rem", // Font relative length units 515 "vw","vh","vmin","vmax" // Viewport percentage units 516 ] 517 518 /// [actual, expected] 519 var validPositions = [ 520 521 /// [ percent ], [ length ], [ percent | percent ], [ percent | length ], [ length | percent ], [ length | length ] 522 ["50%", "50% 50%"], 523 ["50u1", "50u1 50%"], 524 ["50% 50%", "50% 50%"], 525 ["50% 50u1", "50% 50u1"], 526 ["50u1 50%", "50u1 50%"], 527 ["50u1 50u1", "50u1 50u1"], 528 529 ///// [ keyword ], [ keyword keyword ] x 5 keywords 530 ["left", "0% 50%"], 531 ["top", "50% 0%"], 532 ["right", "100% 50%"], 533 ["bottom", "50% 100%"], 534 ["center", "50% 50%"], 535 536 ["left top", "0% 0%"], 537 ["left bottom", "0% 100%"], 538 ["left center", "0% 50%"], 539 540 ["top left", "0% 0%"], 541 ["top right", "100% 0%"], 542 ["top center", "50% 0%"], 543 544 ["right top", "100% 0%"], 545 ["right bottom", "100% 100%"], 546 ["right center", "100% 50%"], 547 548 ["bottom left", "0% 100%"], 549 ["bottom right", "100% 100%"], 550 ["bottom center", "50% 100%"], 551 552 ["center top", "50% 0%"], 553 ["center left", "0% 50%"], 554 ["center right", "100% 50%"], 555 ["center bottom", "50% 100%"], 556 ["center center", "50% 50%"], 557 558 ////// [ keyword | percent ], [ keyword | length ], [ percent | keyword ], [ length | keyword ] x 5 keywords 559 ["left 50%", "0% 50%"], 560 ["left 50u1", "0% 50u1"], 561 562 ["50% top", "50% 0%"], 563 ["50u1 top", "50u1 0%"], 564 565 ["right 80%", "100% 80%"], 566 ["right 80u1", "100% 80u1"], 567 568 ["70% bottom", "70% 100%"], 569 ["70u1 bottom", "70u1 100%"], 570 571 ["center 60%", "50% 60%"], 572 ["center 60u1", "50% 60u1"], 573 ["60% center", "60% 50%"], 574 ["60u1 center", "60u1 50%"], 575 576 ////// [ keyword percent | keyword percent], [ keyword percent | keyword length], 577 ////// [ keyword length | keyword length], [ keyword length | keyword percent] x 5 keywords 578 ["left 50% top 50%", "50% 50%"], 579 ["left 50% top 50u1", "50% 50u1"], 580 ["left 50% bottom 70%", "50% calc(30%)", "50% 30%"], 581 ["left 50% bottom 70u1", "[convert] 50% calc(100% - 70u1)"], 582 ["left 50u1 top 50%", "50u1 50%"], 583 ["left 50u1 top 50u1", "50u1 50u1"], 584 ["left 50u1 bottom 70%", "50u1 calc(30%)", "50u1 30%"], 585 586 ["top 50% left 50%", "50% 50%"], 587 ["top 50% left 50u1", "50u1 50%"], 588 ["top 50% right 80%", "calc(20%) 50%", "20% 50%"], 589 ["top 50% right 80u1", "[convert] calc(100% - 80u1) 50%"], 590 ["top 50u1 left 50%", "50% 50u1"], 591 ["top 50u1 left 50u1", "50u1 50u1"], 592 ["top 50u1 right 80%", "calc(20%) 50u1", "20% 50u1"], 593 594 ["bottom 70% left 50%", "50% calc(30%)", "50% 30%"], 595 ["bottom 70% left 50u1", "50u1 calc(30%)", "50u1 30%"], 596 ["bottom 70% right 80%", "calc(20%) calc(30%)", "20% 30%"], 597 ["bottom 70% right 80u1", "[convert] calc(100% - 80u1) calc(30%)", "calc(100% - 80u1) 30%"], 598 ["bottom 70u1 left 50%", "[convert] 50% calc(100% - 70u1)"], 599 ["bottom 70u1 right 50%", "[convert] calc(50%) calc(100% - 70u1)", "50% calc(100% - 70u1)"], 600 ["bottom 70u1 right 80u1", "[convert] calc(100% - 80u1) calc(100% - 70u1)"], 601 602 ["right 80% top 50%", "calc(20%) 50%", "20% 50%"], 603 ["right 80% top 50u1", "calc(20%) 50u1", "20% 50u1"], 604 ["right 80% bottom 70%", "calc(20%) calc(30%)", "20% 30%"], 605 ["right 80% bottom 70u1", "[convert] calc(20%) calc(100% - 70u1)", "20% calc(100% - 70u1)"], 606 ["right 80u1 top 50%", "[convert] calc(100% - 80u1) 50%"], 607 ["right 80u1 bottom 70%", "[convert] calc(100% - 80u1) calc(30%)", "calc(100% - 80u1) 30%"], 608 ["right 80u1 bottom 70u1", "[convert] calc(100% - 80u1) calc(100% - 70u1)"], 609 ]; 610 611 var invalidPositions = [ 612 ////// [ keyword | percent ], [ keyword | length ], [ percent | keyword ], [ length | keyword ] x 5 keywords 613 "50% left", 614 "50px left", 615 "top 50%", 616 "80% right", 617 "80px right", 618 "bottom 70%", 619 "bottom 70px", 620 621 ////// [ keyword | keyword percent ], [ keyword | keyword length ] x 5 keywords 622 "center center 60%", 623 "center center 60px", 624 625 "left center 60%", 626 "left center 60px", 627 "left right 80%", 628 "left right 80px", 629 "left left 50%", 630 "left left 50px", 631 632 "top center 60%", 633 "top center 60px", 634 "top bottom 80%", 635 "top bottom 80px", 636 "top top 50%", 637 "top top 50px", 638 639 "bottom center 60%", 640 "bottom center 60px", 641 "bottom top 50%", 642 "bottom top 50px", 643 "bottom bottom 50%", 644 "bottom bottom 50px", 645 646 "right center 60%", 647 "right center 60px", 648 "right left 50%", 649 "right left 50px", 650 "right right 70%", 651 "right right 70px", 652 653 ////// [ keyword percent | keyword], [ keyword length | keyword ] x 5 keywords 654 "center 60% top", 655 "center 60px top", 656 "center 60% bottom", 657 "center 60px bottom", 658 "center 60% left", 659 "center 60px left", 660 "center 60% right", 661 "center 60px right", 662 "center 60% center", 663 "center 60px center", 664 665 "left 50% right", 666 "left 50px right", 667 "left 50% left", 668 "left 50px left", 669 670 "top 50% bottom", 671 "top 50px bottom", 672 "top 50% top", 673 "top 50px top", 674 675 "bottom 70% top", 676 "bottom 70px top", 677 "bottom 70% bottom", 678 "bottom 70px bottom", 679 680 "right 80% left", 681 "right 80px left", 682 683 ////// [ keyword percent | keyword percent], [ keyword percent | keyword length], 684 ////// [ keyword length | keyword length], [ keyword length | keyword percent] x 5 keywords 685 "center 60% top 50%", 686 "center 60% top 50px", 687 "center 60% bottom 70%", 688 "center 60% bottom 70px", 689 "center 60% left 50%", 690 "center 60% left 50px", 691 "center 60% right 70%", 692 "center 60% right 70px", 693 "center 60% center 65%", 694 "center 60% center 65px", 695 "center 60px top 50%", 696 "center 60px top 50px", 697 "center 60px bottom 70%", 698 "center 60px bottom 70px", 699 "center 60px left 50%", 700 "center 60px left 50px", 701 "center 60px right 70%", 702 "center 60px right 70px", 703 "center 60px center 65%", 704 "center 60px center 65px", 705 706 "left 50% center 60%", 707 "left 50% center 60px", 708 "left 50% right 80%", 709 "left 50% right 80px", 710 "left 50% left 50%", 711 "left 50% left 50px", 712 "left 50px center 60%", 713 "left 50px center 60px", 714 "left 50px right 80%", 715 "left 50px right 80px", 716 "left 50px left 50%", 717 "left 50px left 50px", 718 719 "top 50% center 60%", 720 "top 50% center 60px", 721 "top 50% bottom 50%", 722 "top 50% bottom 50px", 723 "top 50% top 50%", 724 "top 50% top 50px", 725 "top 50px center 60%", 726 "top 50px center 60px", 727 "top 50px bottom 70%", 728 "top 50px bottom 70px", 729 "top 50px top 50%", 730 "top 50px top 50px", 731 732 "bottom 70% center 60%", 733 "bottom 70% center 60px", 734 "bottom 70% top 50%", 735 "bottom 70% top 50px", 736 "bottom 70% bottom 50%", 737 "bottom 70% bottom 50px", 738 "bottom 70px center 60%", 739 "bottom 70px center 60px", 740 "bottom 70px top 50%", 741 "bottom 70px top 50px", 742 "bottom 70px bottom 50%", 743 "bottom 70px bottom 50px", 744 745 "right 80% center 60%", 746 "right 80% center 60px", 747 "right 80% left 50%", 748 "right 80% left 50px", 749 "right 80% right 85%", 750 "right 80% right 85px", 751 "right 80px center 60%", 752 "right 80px center 60px", 753 "right 80px left 50%", 754 "right 80px left 50px", 755 "right 80px right 85%", 756 "right 80px right 85px" 757 ]; 758 759 // valid radii values for circle + ellipse 760 // [value, expected_inline, [expected_computed?]] 761 var validCircleRadii = [ 762 ['at 50% 50%', 'at 50% 50%'], 763 ['50u1 at 50% 50%', '50u1 at 50% 50%'], 764 ['50% at 50% 50%', '50% at 50% 50%'], 765 ['closest-side at 50% 50%', 'at 50% 50%'], 766 ['farthest-side at 50% 50%', 'farthest-side at 50% 50%'], 767 ['', ''], 768 ['50u1', '50u1'], 769 ['50%', '50%'], 770 ['closest-side', ''], 771 ['farthest-side', 'farthest-side'] 772 ] 773 var validEllipseRadii = [ 774 ['at 50% 50%', 'at 50% 50%', 'at 50% 50%'], 775 ['50u1 100u1 at 50% 50%', '50u1 100u1 at 50% 50%'], 776 ['100u1 100px at 50% 50%', '100u1 100px at 50% 50%'], 777 ['25% 50% at 50% 50%', '25% 50% at 50% 50%'], 778 ['50u1 25% at 50% 50%', '50u1 25% at 50% 50%'], 779 ['25% 50u1 at 50% 50%', '25% 50u1 at 50% 50%'], 780 ['25% closest-side at 50% 50%', '25% closest-side at 50% 50%'], 781 ['25u1 closest-side at 50% 50%', '25u1 closest-side at 50% 50%'], 782 ['closest-side 75% at 50% 50%', 'closest-side 75% at 50% 50%'], 783 ['closest-side 75u1 at 50% 50%', 'closest-side 75u1 at 50% 50%'], 784 ['25% farthest-side at 50% 50%', '25% farthest-side at 50% 50%'], 785 ['25u1 farthest-side at 50% 50%', '25u1 farthest-side at 50% 50%'], 786 ['farthest-side 75% at 50% 50%', 'farthest-side 75% at 50% 50%'], 787 ['farthest-side 75u1 at 50% 50%', 'farthest-side 75u1 at 50% 50%'], 788 ['closest-side closest-side at 50% 50%', 'at 50% 50%'], 789 ['farthest-side farthest-side at 50% 50%', 'farthest-side farthest-side at 50% 50%'], 790 ['closest-side farthest-side at 50% 50%', 'closest-side farthest-side at 50% 50%'], 791 ['farthest-side closest-side at 50% 50%', 'farthest-side closest-side at 50% 50%'], 792 ['', ''], 793 ['50u1 100u1', '50u1 100u1'], 794 ['100u1 100px', '100u1 100px'], 795 ['25% 50%', '25% 50%'], 796 ['50u1 25%', '50u1 25%'], 797 ['25% 50u1', '25% 50u1'], 798 ['25% closest-side', '25% closest-side'], 799 ['25u1 closest-side', '25u1 closest-side'], 800 ['closest-side 75%', 'closest-side 75%'], 801 ['closest-side 75u1', 'closest-side 75u1'], 802 ['25% farthest-side', '25% farthest-side'], 803 ['25u1 farthest-side', '25u1 farthest-side'], 804 ['farthest-side 75%', 'farthest-side 75%'], 805 ['farthest-side 75u1', 'farthest-side 75u1'], 806 ['closest-side closest-side', ''], 807 ['farthest-side farthest-side', 'farthest-side farthest-side'], 808 ['closest-side farthest-side', 'closest-side farthest-side'], 809 ['farthest-side closest-side', 'farthest-side closest-side'] 810 ] 811 812 var validInsets = [ 813 ["One arg - u1", "10u1"], 814 ["One arg - u2", "10u2"], 815 ["Two args - u1 u1", "10u1 20u1"], 816 ["Two args - u1 u2", "10u1 20u2"], 817 ["Two args - u2 u1", "10u2 20u1"], 818 ["Two args - u2 u2", "10u2 20u2"], 819 ["Three args - u1 u1 u1", "10u1 20u1 30u1"], 820 ["Three args - u1 u1 u2", "10u1 20u1 30u2"], 821 ["Three args - u1 u2 u1", "10u1 20u2 30u1"], 822 ["Three args - u1 u2 u2 ", "10u1 20u2 30u2"], 823 ["Three args - u2 u1 u1", "10u2 20u1 30u1"], 824 ["Three args - u2 u1 u2 ", "10u2 20u1 30u2"], 825 ["Three args - u2 u2 u1 ", "10u2 20u2 30u1"], 826 ["Three args - u2 u2 u2 ","10u2 20u2 30u2"], 827 ["Four args - u1 u1 u1 u1", "10u1 20u1 30u1 40u1"], 828 ["Four args - u1 u1 u1 u2", "10u1 20u1 30u1 40u2"], 829 ["Four args - u1 u1 u2 u1", "10u1 20u1 30u2 40u1"], 830 ["Four args - u1 u1 u2 u2", "10u1 20u1 30u2 40u2"], 831 ["Four args - u1 u2 u1 u1", "10u1 20u2 30u1 40u1"], 832 ["Four args - u1 u2 u1 u2", "10u1 20u2 30u1 40u2"], 833 ["Four args - u1 u2 u2 u1", "10u1 20u2 30u2 40u1"], 834 ["Four args - u1 u2 u2 u2", "10u1 20u2 30u2 40u2"], 835 ["Four args - u2 u1 u1 u1", "10u2 20u1 30u1 40u1"], 836 ["Four args - u2 u1 u1 u2", "10u2 20u1 30u1 40u2"], 837 ["Four args - u2 u1 u2 u1", "10u2 20u1 30u2 40u1"], 838 ["Four args - u2 u1 u2 u2", "10u2 20u1 30u2 40u2"], 839 ["Four args - u2 u2 u1 u1", "10u2 20u2 30u1 40u1"], 840 ["Four args - u2 u2 u1 u2", "10u2 20u2 30u1 40u2"], 841 ["Four args - u2 u2 u2 u1", "10u2 20u2 30u2 40u1"], 842 ["Four args - u2 u2 u2 u2", "10u2 20u2 30u2 40u2"] 843 ] 844 845 var validPolygons = [ 846 ["One vertex - u1 u1", "10u1 20u1"], 847 ["One vertex - u1 u2", "10u1 20u2"], 848 ["Two vertices - u1 u1, u1 u1", "10u1 20u1, 30u1 40u1"], 849 ["Two vertices - u1 u1, u2 u2", "10u1 20u1, 30u2 40u2"], 850 ["Two vertices - u2 u2, u1 u1", "10u2 20u2, 30u1 40u1"], 851 ["Two vertices - u1 u2, u2 u1", "10u1 20u2, 30u2 40u1"], 852 ["Three vertices - u1 u1, u1 u1, u1 u1", "10u1 20u1, 30u1 40u1, 50u1 60u1"], 853 ["Three vertices - u2 u2, u2 u2, u2 u2", "10u2 20u2, 30u2 40u2, 50u2 60u2"], 854 ["Three vertices - u3 u3, u3 u3, u3 u3", "10u3 20u3, 30u3 40u3, 50u3 60u3"], 855 ["Three vertices - u1 u1, u2 u2, u3 u3", "10u1 20u1, 30u2 40u2, 50u3 60u3"], 856 ["Three vertices - u3 u3, u1, u1, u2 u2", "10u3 20u3, 30u1 40u1, 50u2 60u2"], 857 ] 858 859 // [test value, expected property value, expected computed style] 860 // See https://github.com/w3c/csswg-drafts/issues/4399#issuecomment-556160413 861 // for the latest resolution to this respect. 862 var calcTestValues = [ 863 ["calc(10in)", "calc(960px)", "960px"], 864 ["calc(10in + 20px)", "calc(980px)", "980px"], 865 ["calc(30%)", "calc(30%)", "30%"], 866 ["calc(100%/4)", "calc(25%)", "25%"], 867 ["calc(25%*3)", "calc(75%)", "75%"], 868 ["calc(25%*3 - 10in)", "calc(75% - 960px)", "calc(75% - 960px)"], 869 ["calc((12.5%*6 + 10in) / 4)", "calc(18.75% + 240px)", "calc(18.75% + 240px)"] 870 ] 871 872 return { 873 testInlineStyle: testInlineStyle, 874 testComputedStyle: testComputedStyle, 875 testShapeMarginInlineStyle: testShapeMarginInlineStyle, 876 testShapeMarginComputedStyle: testShapeMarginComputedStyle, 877 testShapeThresholdInlineStyle: testShapeThresholdInlineStyle, 878 testShapeThresholdComputedStyle: testShapeThresholdComputedStyle, 879 buildTestCases: buildTestCases, 880 buildRadiiTests: buildRadiiTests, 881 buildPositionTests: buildPositionTests, 882 buildInsetTests: buildInsetTests, 883 buildPolygonTests: buildPolygonTests, 884 generateInsetRoundCases: generateInsetRoundCases, 885 buildCalcTests: buildCalcTests, 886 validUnits: validUnits, 887 calcTestValues: calcTestValues, 888 roundResultStr: roundResultStr, 889 setupFonts: setupFonts, 890 restoreFonts: restoreFonts, 891 } 892 })();