tor-browser

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

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 })();