bool SizesAttributeParser::calculateLengthInPixels(CSSParserTokenRange range, float& result)
{
    const CSSParserToken& startToken = range.peek();
    CSSParserTokenType type = startToken.type();
    if (type == DimensionToken) {
        double length;
        if (!CSSPrimitiveValue::isLength(startToken.unitType()))
            return false;
        if ((m_mediaValues->computeLength(startToken.numericValue(), startToken.unitType(), length)) && (length >= 0)) {
            result = clampTo<float>(length);
            return true;
        }
    } else if (type == FunctionToken) {
        SizesCalcParser calcParser(range, m_mediaValues);
        if (!calcParser.isValid())
            return false;
        result = calcParser.result();
        return true;
    } else if (type == NumberToken && !startToken.numericValue()) {
        result = 0;
        return true;
    }

    return false;
}
Beispiel #2
0
CSSPrimitiveValue* consumeAngle(CSSParserTokenRange& range)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == DimensionToken) {
        switch (token.unitType()) {
        case CSSPrimitiveValue::UnitType::Degrees:
        case CSSPrimitiveValue::UnitType::Radians:
        case CSSPrimitiveValue::UnitType::Gradians:
        case CSSPrimitiveValue::UnitType::Turns:
            return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
        default:
            return nullptr;
        }
    }
    if (token.type() == NumberToken && token.numericValue() == 0) {
        range.consumeIncludingWhitespace();
        return cssValuePool().createValue(0, CSSPrimitiveValue::UnitType::Degrees);
    }
    CalcParser calcParser(range, ValueRangeAll);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        if (calculation->category() == CalcAngle)
            return calcParser.consumeValue();
    }
    return nullptr;
}
Beispiel #3
0
bool SizesAttributeParser::calculateLengthInPixels(CSSParserTokenIterator startToken, CSSParserTokenIterator endToken, float& result)
{
    if (startToken == endToken)
        return false;
    CSSParserTokenType type = startToken->type();
    if (type == DimensionToken) {
        double length;
        if (!CSSPrimitiveValue::isLength(startToken->unitType()))
            return false;
        if ((m_mediaValues->computeLength(startToken->numericValue(), startToken->unitType(), length)) && (length >= 0)) {
            result = clampTo<float>(length);
            return true;
        }
    } else if (type == FunctionToken) {
        SizesCalcParser calcParser(startToken, endToken, m_mediaValues);
        if (!calcParser.isValid())
            return false;
        result = calcParser.result();
        return true;
    } else if (type == NumberToken && !startToken->numericValue()) {
        result = 0;
        return true;
    }

    return false;
}
Beispiel #4
0
bool consumeNumberRaw(CSSParserTokenRange& range, double& result)
{
    if (range.peek().type() == NumberToken) {
        result = range.consumeIncludingWhitespace().numericValue();
        return true;
    }
    CalcParser calcParser(range, ValueRangeAll);
    return calcParser.consumeNumberRaw(result);
}
Beispiel #5
0
CSSPrimitiveValue* consumeLengthOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == DimensionToken || token.type() == NumberToken)
        return consumeLength(range, cssParserMode, valueRange, unitless);
    if (token.type() == PercentageToken)
        return consumePercent(range, valueRange);
    CalcParser calcParser(range, valueRange);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        if (calculation->category() == CalcLength || calculation->category() == CalcPercent || calculation->category() == CalcPercentLength)
            return calcParser.consumeValue();
    }
    return nullptr;
}
Beispiel #6
0
CSSPrimitiveValue* consumePercent(CSSParserTokenRange& range, ValueRange valueRange)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == PercentageToken) {
        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
            return nullptr;
        return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::Percentage);
    }
    CalcParser calcParser(range, valueRange);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        if (calculation->category() == CalcPercent)
            return calcParser.consumeValue();
    }
    return nullptr;
}
Beispiel #7
0
CSSPrimitiveValue* consumeLength(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == DimensionToken) {
        switch (token.unitType()) {
        case CSSPrimitiveValue::UnitType::QuirkyEms:
            if (cssParserMode != UASheetMode)
                return nullptr;
        /* fallthrough intentional */
        case CSSPrimitiveValue::UnitType::Ems:
        case CSSPrimitiveValue::UnitType::Rems:
        case CSSPrimitiveValue::UnitType::Chs:
        case CSSPrimitiveValue::UnitType::Exs:
        case CSSPrimitiveValue::UnitType::Pixels:
        case CSSPrimitiveValue::UnitType::Centimeters:
        case CSSPrimitiveValue::UnitType::Millimeters:
        case CSSPrimitiveValue::UnitType::Inches:
        case CSSPrimitiveValue::UnitType::Points:
        case CSSPrimitiveValue::UnitType::Picas:
        case CSSPrimitiveValue::UnitType::UserUnits:
        case CSSPrimitiveValue::UnitType::ViewportWidth:
        case CSSPrimitiveValue::UnitType::ViewportHeight:
        case CSSPrimitiveValue::UnitType::ViewportMin:
        case CSSPrimitiveValue::UnitType::ViewportMax:
            break;
        default:
            return nullptr;
        }
        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
            return nullptr;
        return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
    }
    if (token.type() == NumberToken) {
        if (!shouldAcceptUnitlessLength(token.numericValue(), cssParserMode, unitless)
            || (valueRange == ValueRangeNonNegative && token.numericValue() < 0))
            return nullptr;
        CSSPrimitiveValue::UnitType unitType = CSSPrimitiveValue::UnitType::Pixels;
        if (cssParserMode == SVGAttributeMode)
            unitType = CSSPrimitiveValue::UnitType::UserUnits;
        return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), unitType);
    }
    if (cssParserMode == SVGAttributeMode)
        return nullptr;
    CalcParser calcParser(range, valueRange);
    if (calcParser.value() && calcParser.value()->category() == CalcLength)
        return calcParser.consumeValue();
    return nullptr;
}
Beispiel #8
0
CSSPrimitiveValue* consumeTime(CSSParserTokenRange& range, ValueRange valueRange)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == DimensionToken) {
        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
            return nullptr;
        CSSPrimitiveValue::UnitType unit = token.unitType();
        if (unit == CSSPrimitiveValue::UnitType::Milliseconds || unit == CSSPrimitiveValue::UnitType::Seconds)
            return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
        return nullptr;
    }
    CalcParser calcParser(range, valueRange);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        if (calculation->category() == CalcTime)
            return calcParser.consumeValue();
    }
    return nullptr;
}
Beispiel #9
0
// TODO(timloh): Work out if this can just call consumeNumberRaw
CSSPrimitiveValue* consumeNumber(CSSParserTokenRange& range, ValueRange valueRange)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == NumberToken) {
        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
            return nullptr;
        return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
    }
    CalcParser calcParser(range, ValueRangeAll);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        // TODO(rwlbuis) Calcs should not be subject to parse time range checks.
        // spec: https://drafts.csswg.org/css-values-3/#calc-range
        if (calculation->category() != CalcNumber || (valueRange == ValueRangeNonNegative && calculation->isNegative()))
            return nullptr;
        return calcParser.consumeNumber();
    }
    return nullptr;
}
Beispiel #10
0
CSSPrimitiveValue* consumeInteger(CSSParserTokenRange& range, double minimumValue)
{
    const CSSParserToken& token = range.peek();
    if (token.type() == NumberToken) {
        if (token.numericValueType() == NumberValueType || token.numericValue() < minimumValue)
            return nullptr;
        return cssValuePool().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::Integer);
    }
    CalcParser calcParser(range);
    if (const CSSCalcValue* calculation = calcParser.value()) {
        if (calculation->category() != CalcNumber || !calculation->isInt())
            return nullptr;
        double value = calculation->doubleValue();
        if (value < minimumValue)
            return nullptr;
        return calcParser.consumeNumber();
    }
    return nullptr;
}
TEST(SizesCalcParserTest, Basic)
{
    TestCase testCases[] = {
        {"calc(500px + 10em)", 660, true, false},
        {"calc(500px / 8)", 62.5, true, false},
        {"calc(500px + 2 * 10em)", 820, true, false},
        {"calc(500px + 2*10em)", 820, true, false},
        {"calc(500px + 0.5*10em)", 580, true, false},
        {"calc(500px + (0.5*10em + 13px))", 593, true, false},
        {"calc(100vw + (0.5*10em + 13px))", 593, true, false},
        {"calc(100vh + (0.5*10em + 13px))", 736, true, false},
        {"calc(100vh + calc(0.5*10em + 13px))", 736, true, true}, // CSSCalculationValue does not parse internal "calc(".
        {"calc(100vh + (50%*10em + 13px))", 0, false, false},
        {"calc(50em+13px)", 0, false, false},
        {"calc(50em-13px)", 0, false, false},
        {"calc(500px + 10)", 0, false, false},
        {"calc(500 + 10)", 0, false, false},
        {"calc(500px + 10s)", 0, false, true}, // This test ASSERTs in CSSCalculationValue.
        {"calc(500px + 1cm)", 537.795276, true, false},
        {"calc(500px - 10s)", 0, false, true}, // This test ASSERTs in CSSCalculationValue.
        {"calc(500px - 1cm)", 462.204724, true, false},
        {"calc(500px - 1vw)", 495, true, false},
        {"calc(50px*10)", 500, true, false},
        {"calc(50px*10px)", 0, false, false},
        {"calc(50px/10px)", 0, false, false},
        {"calc(500px/10)", 50, true, false},
        {"calc(500/10)", 0, false, false},
        {"calc(500px/0.5)", 1000, true, false},
        {"calc(500px/.5)", 1000, true, false},
        {"calc(500/0)", 0, false, false},
        {"calc(500px/0)", 0, false, false},
        {"calc(-500px/10)", 0, true, true}, // CSSCalculationValue does not clamp negative values to 0.
        {"calc(((4) * ((10px))))", 40, true, false},
        {"calc(50px / 0)", 0, false, false},
        {"calc(50px / (10 + 10))", 2.5, true, false},
        {"calc(50px / (10 - 10))", 0, false, false},
        {"calc(50px / (10 * 10))", 0.5, true, false},
        {"calc(50px / (10 / 10))", 50, true, false},
        {"calc(200px*)", 0, false, false},
        {"calc(+ +200px)", 0, false, false},
        {"calc()", 0, false, false},
        {"calc(100px + + +100px)", 0, false, false},
        {"calc(200px 200px)", 0, false, false},
        {"calc(100px * * 2)", 0, false, false},
        {"calc(100px @ 2)", 0, false, false},
        {"calc(1 flim 2)", 0, false, false},
        {"calc(100px @ 2)", 0, false, false},
        {"calc(1 flim 2)", 0, false, false},
        {"calc(1 flim (2))", 0, false, false},
        {0, 0, true, false} // Do not remove the terminator line.
    };


    MediaValuesCached::MediaValuesCachedData data;
    data.viewportWidth = 500;
    data.viewportHeight = 643;
    data.deviceWidth = 500;
    data.deviceHeight = 643;
    data.devicePixelRatio = 2.0;
    data.colorBitsPerComponent = 24;
    data.monochromeBitsPerComponent = 0;
    data.primaryPointerType = PointerTypeFine;
    data.defaultFontSize = 16;
    data.threeDEnabled = true;
    data.mediaType = MediaTypeNames::screen;
    data.strictMode = true;
    data.displayMode = WebDisplayModeBrowser;
    RawPtr<MediaValues> mediaValues = MediaValuesCached::create(data);

    for (unsigned i = 0; testCases[i].input; ++i) {
        SizesCalcParser calcParser(CSSTokenizer::Scope(testCases[i].input).tokenRange(), mediaValues);
        ASSERT_EQ(testCases[i].valid, calcParser.isValid());
        if (calcParser.isValid())
            ASSERT_EQ(testCases[i].output, calcParser.result());
    }

    for (unsigned i = 0; testCases[i].input; ++i) {
        if (testCases[i].dontRunInCSSCalc)
            continue;
        verifyCSSCalc(testCases[i].input, testCases[i].output, testCases[i].valid, data.defaultFontSize, data.viewportWidth, data.viewportHeight);
    }
}