Make it possible to use CSS Variables inside Calc expressions.

https://bugs.webkit.org/show_bug.cgi?id=95284

Reviewed by Tony Chang.

Allows calc expressions to contain unevaluated variables, which are then resolved in StyleResolver.cpp when building the RenderStyle tree.

Tests:
fast/css/variables/calc.html

* css/CSSCalculationValue.cpp:
(WebCore::unitCategory):
(WebCore):
(WebCore::CSSCalcValue::customSerializeResolvingVariables):
Generates a CSS expression with variables resolved into their corresponding values.
(WebCore::CSSCalcValue::hasVariableReference):
Returns true if the calculation's expression tree refers to a variable (that needs to be resolved).
(CSSCalcPrimitiveValue):
(WebCore::CSSCalcPrimitiveValue::serializeResolvingVariables):
Resolves the variable using the underlying CSSPrimitiveValue's serializeResolvingVariables function.
(WebCore::CSSCalcPrimitiveValue::hasVariableReference):
(WebCore::CSSCalcPrimitiveValue::toCalcValue):
(WebCore::CSSCalcPrimitiveValue::doubleValue):
(WebCore::CSSCalcPrimitiveValue::computeLengthPx):
(WebCore::CSSCalcBinaryOperation::create):
(CSSCalcBinaryOperation):
(WebCore::CSSCalcBinaryOperation::serializeResolvingVariables):
Builds a CSS expression for contained subtrees.
(WebCore::CSSCalcBinaryOperation::hasVariableReference):
Returns true if either subtree contains a variable.
* css/CSSCalculationValue.h:
(CSSCalcExpressionNode):
(CSSCalcValue):
* css/CSSGrammar.y:
* css/CSSParser.cpp:
(WebCore::CSSParser::validCalculationUnit):
* css/CSSPrimitiveValue.cpp:
(WebCore::CSSPrimitiveValue::primitiveType):
(WebCore::CSSPrimitiveValue::customSerializeResolvingVariables):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::collectMatchingRulesForList):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@127220 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3218f14e
<!DOCTYPE html>
<html>
<style>
div {
border-color: green;
border-style: solid;
border-width: 3px 10px;
}
</style>
<div>This text should have top and bottom borders of 3px and left and right borders of 10px</div>
</html>
<!DOCTYPE html>
<html>
<style>
div {
border-color: green;
border-style: solid;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<script>
internals.settings.setCSSVariablesEnabled(true);
</script>
<style>
div {
border-color: green;
border-style: solid;
border-width: -webkit-calc(-webkit-var(a) + 1px);
-webkit-var-a: -webkit-calc(1px + 3px);
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<style>
div {
border-color: green;
border-style: solid;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<script>
internals.settings.setCSSVariablesEnabled(true);
</script>
<style>
div {
border-color: green;
border-style: solid;
border-width: -webkit-calc(-webkit-var(a) + 1px);
-webkit-var-a: black;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<style>
div {
border-color: green;
border-style: solid;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<script>
internals.settings.setCSSVariablesEnabled(true);
</script>
<style>
div {
border-color: green;
border-style: solid;
border-width: -webkit-calc(-webkit-var(a) + 1px);
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<style>
div {
border-color: green;
border-style: solid;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<script>
internals.settings.setCSSVariablesEnabled(true);
</script>
<style>
div {
border-color: green;
border-style: solid;
border-width: -webkit-calc(--webkit-var(a) + 1px);
-webkit-var-a: 5px;
}
</style>
<div>This text should have the default border width</div>
</html>
<!DOCTYPE html>
<html>
<script>
internals.settings.setCSSVariablesEnabled(true);
</script>
<style>
div {
border-color: green;
border-style: solid;
border-width: -webkit-calc(-webkit-var(a) + -webkit-var(b)) -webkit-calc(-webkit-var(c) * -webkit-var(d));
-webkit-var-a: 2px;
-webkit-var-b: 1px;
-webkit-var-c: 5;
-webkit-var-d: 2px;
}
</style>
<div>This text should have top and bottom borders of 3px and left and right borders of 10px</div>
</html>
2012-08-30 Luke Macpherson <macpherson@chromium.org>
Make it possible to use CSS Variables inside Calc expressions.
https://bugs.webkit.org/show_bug.cgi?id=95284
Reviewed by Tony Chang.
Allows calc expressions to contain unevaluated variables, which are then resolved in StyleResolver.cpp when building the RenderStyle tree.
Tests:
fast/css/variables/calc.html
* css/CSSCalculationValue.cpp:
(WebCore::unitCategory):
(WebCore):
(WebCore::CSSCalcValue::customSerializeResolvingVariables):
Generates a CSS expression with variables resolved into their corresponding values.
(WebCore::CSSCalcValue::hasVariableReference):
Returns true if the calculation's expression tree refers to a variable (that needs to be resolved).
(CSSCalcPrimitiveValue):
(WebCore::CSSCalcPrimitiveValue::serializeResolvingVariables):
Resolves the variable using the underlying CSSPrimitiveValue's serializeResolvingVariables function.
(WebCore::CSSCalcPrimitiveValue::hasVariableReference):
(WebCore::CSSCalcPrimitiveValue::toCalcValue):
(WebCore::CSSCalcPrimitiveValue::doubleValue):
(WebCore::CSSCalcPrimitiveValue::computeLengthPx):
(WebCore::CSSCalcBinaryOperation::create):
(CSSCalcBinaryOperation):
(WebCore::CSSCalcBinaryOperation::serializeResolvingVariables):
Builds a CSS expression for contained subtrees.
(WebCore::CSSCalcBinaryOperation::hasVariableReference):
Returns true if either subtree contains a variable.
* css/CSSCalculationValue.h:
(CSSCalcExpressionNode):
(CSSCalcValue):
* css/CSSGrammar.y:
* css/CSSParser.cpp:
(WebCore::CSSParser::validCalculationUnit):
* css/CSSPrimitiveValue.cpp:
(WebCore::CSSPrimitiveValue::primitiveType):
(WebCore::CSSPrimitiveValue::customSerializeResolvingVariables):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::collectMatchingRulesForList):
2012-08-30 Max Vujovic <mvujovic@adobe.com>
[CSS Shaders] Implement normal blend mode and source-atop compositing mode
......@@ -68,17 +68,19 @@ static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
case CSSPrimitiveValue::CSS_PC:
case CSSPrimitiveValue::CSS_REMS:
return CalcLength;
#if ENABLE(CSS_VARIABLES)
case CSSPrimitiveValue::CSS_VARIABLE_NAME:
return CalcVariable;
#endif
default:
return CalcOther;
}
}
String CSSCalcValue::customCssText() const
static String buildCssText(const String& expression)
{
StringBuilder result;
result.append("-webkit-calc");
String expression = m_expression->customCssText();
bool expressionHasSingleTerm = expression[0] != '(';
if (expressionHasSingleTerm)
result.append('(');
......@@ -88,6 +90,23 @@ String CSSCalcValue::customCssText() const
return result.toString();
}
String CSSCalcValue::customCssText() const
{
return buildCssText(m_expression->customCssText());
}
#if ENABLE(CSS_VARIABLES)
String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
{
return buildCssText(m_expression->serializeResolvingVariables(variables));
}
bool CSSCalcValue::hasVariableReference() const
{
return m_expression->hasVariableReference();
}
#endif
void CSSCalcValue::reportDescendantMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
{
MemoryClassInfo info(memoryObjectInfo, this, MemoryInstrumentation::CSS);
......@@ -130,6 +149,18 @@ public:
return m_value->cssText();
}
#if ENABLE(CSS_VARIABLES)
virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
{
return m_value->customSerializeResolvingVariables(variables);
}
virtual bool hasVariableReference() const
{
return m_value->isVariableName();
}
#endif
virtual PassOwnPtr<CalcExpressionNode> toCalcValue(RenderStyle* style, RenderStyle* rootStyle, double zoom) const
{
switch (m_category) {
......@@ -143,6 +174,9 @@ public:
// Only types that could be part of a Length expression can be converted
// to a CalcExpressionNode. CalcPercentNumber makes no sense as a Length.
case CalcPercentNumber:
#if ENABLE(CSS_VARIABLES)
case CalcVariable:
#endif
case CalcOther:
ASSERT_NOT_REACHED();
}
......@@ -158,6 +192,9 @@ public:
case CalcLength:
case CalcPercentLength:
case CalcPercentNumber:
#if ENABLE(CSS_VARIABLES)
case CalcVariable:
#endif
case CalcOther:
ASSERT_NOT_REACHED();
break;
......@@ -175,6 +212,9 @@ public:
return m_value->getDoubleValue();
case CalcPercentLength:
case CalcPercentNumber:
#if ENABLE(CSS_VARIABLES)
case CalcVariable:
#endif
case CalcOther:
ASSERT_NOT_REACHED();
break;
......@@ -191,7 +231,7 @@ public:
private:
explicit CSSCalcPrimitiveValue(CSSPrimitiveValue* value, bool isInteger)
: CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
, m_value(value)
, m_value(value)
{
}
......@@ -205,42 +245,50 @@ static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
{ CalcPercentNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther },
{ CalcOther, CalcPercentLength, CalcPercentLength, CalcOther, CalcPercentLength },
};
static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
{
CalculationCategory leftCategory = leftSide.category();
CalculationCategory rightCategory = rightSide.category();
if (leftCategory == CalcOther || rightCategory == CalcOther)
return CalcOther;
#if ENABLE(CSS_VARIABLES)
if (leftCategory == CalcVariable || rightCategory == CalcVariable)
return CalcVariable;
#endif
switch (op) {
case CalcAdd:
case CalcSubtract:
return addSubtractResult[leftCategory][rightCategory];
case CalcMultiply:
if (leftCategory != CalcNumber && rightCategory != CalcNumber)
return CalcOther;
return leftCategory == CalcNumber ? rightCategory : leftCategory;
case CalcDivide:
if (rightCategory != CalcNumber || rightSide.isZero())
return CalcOther;
return leftCategory;
}
ASSERT_NOT_REACHED();
return CalcOther;
}
class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
public:
static PassRefPtr<CSSCalcBinaryOperation> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
{
CalculationCategory leftCategory = leftSide->category();
CalculationCategory rightCategory = rightSide->category();
CalculationCategory newCategory = CalcOther;
ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
switch (op) {
case CalcAdd:
case CalcSubtract:
if (leftCategory == CalcOther || rightCategory == CalcOther)
return 0;
newCategory = addSubtractResult[leftCategory][rightCategory];
break;
case CalcMultiply:
if (leftCategory != CalcNumber && rightCategory != CalcNumber)
return 0;
newCategory = leftCategory == CalcNumber ? rightCategory : leftCategory;
break;
case CalcDivide:
if (rightCategory != CalcNumber || rightSide->isZero())
return 0;
newCategory = leftCategory;
break;
}
ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
if (newCategory == CalcOther)
return 0;
return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
}
......@@ -279,19 +327,36 @@ public:
info.addInstrumentedMember(m_rightSide);
}
virtual String customCssText() const
static String buildCssText(const String& leftExpression, const String& rightExpression, CalcOperator op)
{
StringBuilder result;
result.append('(');
result.append(m_leftSide->customCssText());
result.append(leftExpression);
result.append(' ');
result.append(static_cast<char>(m_operator));
result.append(static_cast<char>(op));
result.append(' ');
result.append(m_rightSide->customCssText());
result.append(rightExpression);
result.append(')');
return result.toString();
return result.toString();
}
virtual String customCssText() const
{
return buildCssText(m_leftSide->customCssText(), m_rightSide->customCssText(), m_operator);
}
#if ENABLE(CSS_VARIABLES)
virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
{
return buildCssText(m_leftSide->serializeResolvingVariables(variables), m_rightSide->serializeResolvingVariables(variables), m_operator);
}
virtual bool hasVariableReference() const
{
return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference();
}
#endif
private:
CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
......
......@@ -52,6 +52,9 @@ enum CalculationCategory {
CalcPercent,
CalcPercentNumber,
CalcPercentLength,
#if ENABLE(CSS_VARIABLES)
CalcVariable,
#endif
CalcOther
};
......@@ -64,6 +67,10 @@ public:
virtual double doubleValue() const = 0;
virtual double computeLengthPx(RenderStyle* currentStyle, RenderStyle* rootStyle, double multiplier = 1.0, bool computingFontSize = false) const = 0;
virtual String customCssText() const = 0;
#if ENABLE(CSS_VARIABLES)
virtual String serializeResolvingVariables(const HashMap<AtomicString, String>&) const = 0;
virtual bool hasVariableReference() const = 0;
#endif
virtual void reportMemoryUsage(MemoryObjectInfo*) const = 0;
......@@ -97,6 +104,10 @@ public:
double computeLengthPx(RenderStyle* currentStyle, RenderStyle* rootStyle, double multiplier = 1.0, bool computingFontSize = false) const;
String customCssText() const;
#if ENABLE(CSS_VARIABLES)
String customSerializeResolvingVariables(const HashMap<AtomicString, String>&) const;
bool hasVariableReference() const;
#endif
void reportDescendantMemoryUsage(MemoryObjectInfo*) const;
......
......@@ -100,7 +100,7 @@ static int cssyylex(YYSTYPE* yylval, CSSParser* parser)
%}
%expect 62
%expect 63
%nonassoc LOWEST_PREC
......@@ -1550,6 +1550,13 @@ function:
calc_func_term:
unary_term { $$ = $1; }
| VARFUNCTION maybe_space IDENT ')' maybe_space {
#if ENABLE(CSS_VARIABLES)
$$.id = 0;
$$.string = $3;
$$.unit = CSSPrimitiveValue::CSS_VARIABLE_NAME;
#endif
}
| unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
;
......
......@@ -1443,6 +1443,11 @@ bool CSSParser::validCalculationUnit(CSSParserValue* value, Units unitflags)
case CalcPercentNumber:
b = (unitflags & FPercent) && (unitflags & FNumber);
break;
#if ENABLE(CSS_VARIABLES)
case CalcVariable:
b = true;
break;
#endif
case CalcOther:
break;
}
......
......@@ -186,6 +186,10 @@ unsigned short CSSPrimitiveValue::primitiveType() const
return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER;
case CalcPercentLength:
return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH;
#if ENABLE(CSS_VARIABLES)
case CalcVariable:
return CSSPrimitiveValue::CSS_UNKNOWN; // The type of a calculation containing a variable cannot be known until the value of the variable is determined.
#endif
case CalcOther:
return CSSPrimitiveValue::CSS_UNKNOWN;
}
......@@ -1119,8 +1123,10 @@ String CSSPrimitiveValue::customCssText() const
#if ENABLE(CSS_VARIABLES)
String CSSPrimitiveValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
{
if (m_primitiveUnitType == CSS_VARIABLE_NAME && variables.contains(m_value.string))
if (isVariableName() && variables.contains(m_value.string))
return variables.get(m_value.string);
if (isCalculated())
return cssCalcValue()->customSerializeResolvingVariables(variables);
return customCssText();
}
#endif
......
......@@ -3361,8 +3361,15 @@ static bool createGridPosition(CSSValue* value, Length& position)
#if ENABLE(CSS_VARIABLES)
static bool hasVariableReference(CSSValue* value)
{
if (value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value)->isVariableName())
return true;
if (value->isPrimitiveValue()) {
CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
if (CSSCalcValue* calcValue = primitiveValue->cssCalcValue())
return calcValue->hasVariableReference();
return primitiveValue->isVariableName();
}
if (value->isCalculationValue())
return static_cast<CSSCalcValue*>(value)->hasVariableReference();
for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
if (hasVariableReference(i.value()))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment