| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 double CSSCalcValue::doubleValue() const | 172 double CSSCalcValue::doubleValue() const |
| 173 { | 173 { |
| 174 return clampToPermittedRange(m_expression->doubleValue()); | 174 return clampToPermittedRange(m_expression->doubleValue()); |
| 175 } | 175 } |
| 176 | 176 |
| 177 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversion
Data) const | 177 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversion
Data) const |
| 178 { | 178 { |
| 179 return clampToPermittedRange(m_expression->computeLengthPx(conversionData)); | 179 return clampToPermittedRange(m_expression->computeLengthPx(conversionData)); |
| 180 } | 180 } |
| 181 | 181 |
| 182 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSCalcExpressionNode) | |
| 183 | |
| 184 class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode { | 182 class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode { |
| 185 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(CSSCalcPrimitiveValue); | 183 WTF_MAKE_FAST_ALLOCATED(CSSCalcPrimitiveValue); |
| 186 public: | 184 public: |
| 187 | 185 |
| 188 static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(PassRefPtrWillBe
RawPtr<CSSPrimitiveValue> value, bool isInteger) | 186 static PassRefPtr<CSSCalcPrimitiveValue> create(PassRefPtr<CSSPrimitiveValue
> value, bool isInteger) |
| 189 { | 187 { |
| 190 return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(value, isInteger)); | 188 return adoptRef(new CSSCalcPrimitiveValue(value, isInteger)); |
| 191 } | 189 } |
| 192 | 190 |
| 193 static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(double value, CS
SPrimitiveValue::UnitType type, bool isInteger) | 191 static PassRefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveVa
lue::UnitType type, bool isInteger) |
| 194 { | 192 { |
| 195 if (std::isnan(value) || std::isinf(value)) | 193 if (std::isnan(value) || std::isinf(value)) |
| 196 return nullptr; | 194 return nullptr; |
| 197 return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(CSSPrimitiveValue::c
reate(value, type).get(), isInteger)); | 195 return adoptRef(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(valu
e, type).get(), isInteger)); |
| 198 } | 196 } |
| 199 | 197 |
| 200 bool isZero() const override | 198 bool isZero() const override |
| 201 { | 199 { |
| 202 return !m_value->getDoubleValue(); | 200 return !m_value->getDoubleValue(); |
| 203 } | 201 } |
| 204 | 202 |
| 205 String customCSSText() const override | 203 String customCSSText() const override |
| 206 { | 204 { |
| 207 return m_value->cssText(); | 205 return m_value->cssText(); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveVal
ue&>(other).m_value); | 263 return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveVal
ue&>(other).m_value); |
| 266 } | 264 } |
| 267 | 265 |
| 268 Type type() const override { return CssCalcPrimitiveValue; } | 266 Type type() const override { return CssCalcPrimitiveValue; } |
| 269 CSSPrimitiveValue::UnitType typeWithCalcResolved() const override | 267 CSSPrimitiveValue::UnitType typeWithCalcResolved() const override |
| 270 { | 268 { |
| 271 return m_value->typeWithCalcResolved(); | 269 return m_value->typeWithCalcResolved(); |
| 272 } | 270 } |
| 273 | 271 |
| 274 | 272 |
| 275 DEFINE_INLINE_VIRTUAL_TRACE() | |
| 276 { | |
| 277 visitor->trace(m_value); | |
| 278 CSSCalcExpressionNode::trace(visitor); | |
| 279 } | |
| 280 | |
| 281 private: | 273 private: |
| 282 CSSCalcPrimitiveValue(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool
isInteger) | 274 CSSCalcPrimitiveValue(PassRefPtr<CSSPrimitiveValue> value, bool isInteger) |
| 283 : CSSCalcExpressionNode(unitCategory(value->typeWithCalcResolved()), isI
nteger) | 275 : CSSCalcExpressionNode(unitCategory(value->typeWithCalcResolved()), isI
nteger) |
| 284 , m_value(value) | 276 , m_value(value) |
| 285 { | 277 { |
| 286 } | 278 } |
| 287 | 279 |
| 288 RefPtrWillBeMember<CSSPrimitiveValue> m_value; | 280 RefPtr<CSSPrimitiveValue> m_value; |
| 289 }; | 281 }; |
| 290 | 282 |
| 291 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = { | 283 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = { |
| 292 // CalcNumber CalcLength CalcPercent
CalcPercentNumber CalcPercentLength CalcAngle CalcTime CalcFrequency | 284 // CalcNumber CalcLength CalcPercent
CalcPercentNumber CalcPercentLength CalcAngle CalcTime CalcFrequency |
| 293 /* CalcNumber */ { CalcNumber, CalcOther, CalcPercentNumbe
r, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther }, | 285 /* CalcNumber */ { CalcNumber, CalcOther, CalcPercentNumbe
r, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther }, |
| 294 /* CalcLength */ { CalcOther, CalcLength, CalcPercentLengt
h, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther }, | 286 /* CalcLength */ { CalcOther, CalcLength, CalcPercentLengt
h, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther }, |
| 295 /* CalcPercent */ { CalcPercentNumber, CalcPercentLength, CalcPercent,
CalcPercentNumber, CalcPercentLength, CalcOther, CalcOther, CalcOther }, | 287 /* CalcPercent */ { CalcPercentNumber, CalcPercentLength, CalcPercent,
CalcPercentNumber, CalcPercentLength, CalcOther, CalcOther, CalcOther }, |
| 296 /* CalcPercentNumber */ { CalcPercentNumber, CalcOther, CalcPercentNumbe
r, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther }, | 288 /* CalcPercentNumber */ { CalcPercentNumber, CalcOther, CalcPercentNumbe
r, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther }, |
| 297 /* CalcPercentLength */ { CalcOther, CalcPercentLength, CalcPercentLengt
h, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther }, | 289 /* CalcPercentLength */ { CalcOther, CalcPercentLength, CalcPercentLengt
h, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther }, |
| 298 /* CalcAngle */ { CalcOther, CalcOther, CalcOther,
CalcOther, CalcOther, CalcAngle, CalcOther, CalcOther }, | 290 /* CalcAngle */ { CalcOther, CalcOther, CalcOther,
CalcOther, CalcOther, CalcAngle, CalcOther, CalcOther }, |
| (...skipping 30 matching lines...) Expand all Loading... |
| 329 static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalc
ExpressionNode* rightSide, CalcOperator op) | 321 static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalc
ExpressionNode* rightSide, CalcOperator op) |
| 330 { | 322 { |
| 331 // Not testing for actual integer values. | 323 // Not testing for actual integer values. |
| 332 // Performs W3C spec's type checking for calc integers. | 324 // Performs W3C spec's type checking for calc integers. |
| 333 // http://www.w3.org/TR/css3-values/#calc-type-checking | 325 // http://www.w3.org/TR/css3-values/#calc-type-checking |
| 334 return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger(); | 326 return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger(); |
| 335 } | 327 } |
| 336 | 328 |
| 337 class CSSCalcBinaryOperation final : public CSSCalcExpressionNode { | 329 class CSSCalcBinaryOperation final : public CSSCalcExpressionNode { |
| 338 public: | 330 public: |
| 339 static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> create(PassRefPtrWillBe
RawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpression
Node> rightSide, CalcOperator op) | 331 static PassRefPtr<CSSCalcExpressionNode> create(PassRefPtr<CSSCalcExpression
Node> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op) |
| 340 { | 332 { |
| 341 ASSERT(leftSide->category() != CalcOther && rightSide->category() != Cal
cOther); | 333 ASSERT(leftSide->category() != CalcOther && rightSide->category() != Cal
cOther); |
| 342 | 334 |
| 343 CalculationCategory newCategory = determineCategory(*leftSide, *rightSid
e, op); | 335 CalculationCategory newCategory = determineCategory(*leftSide, *rightSid
e, op); |
| 344 if (newCategory == CalcOther) | 336 if (newCategory == CalcOther) |
| 345 return nullptr; | 337 return nullptr; |
| 346 | 338 |
| 347 return adoptRefWillBeNoop(new CSSCalcBinaryOperation(leftSide, rightSide
, op, newCategory)); | 339 return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newC
ategory)); |
| 348 } | 340 } |
| 349 | 341 |
| 350 static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> createSimplified(PassRe
fPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalc
ExpressionNode> rightSide, CalcOperator op) | 342 static PassRefPtr<CSSCalcExpressionNode> createSimplified(PassRefPtr<CSSCalc
ExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOpera
tor op) |
| 351 { | 343 { |
| 352 CalculationCategory leftCategory = leftSide->category(); | 344 CalculationCategory leftCategory = leftSide->category(); |
| 353 CalculationCategory rightCategory = rightSide->category(); | 345 CalculationCategory rightCategory = rightSide->category(); |
| 354 ASSERT(leftCategory != CalcOther && rightCategory != CalcOther); | 346 ASSERT(leftCategory != CalcOther && rightCategory != CalcOther); |
| 355 | 347 |
| 356 bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op); | 348 bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op); |
| 357 | 349 |
| 358 // Simplify numbers. | 350 // Simplify numbers. |
| 359 if (leftCategory == CalcNumber && rightCategory == CalcNumber) { | 351 if (leftCategory == CalcNumber && rightCategory == CalcNumber) { |
| 360 return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doub
leValue(), rightSide->doubleValue(), op), CSSPrimitiveValue::UnitType::Number, i
sInteger); | 352 return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doub
leValue(), rightSide->doubleValue(), op), CSSPrimitiveValue::UnitType::Number, i
sInteger); |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 531 return CSSPrimitiveValue::UnitType::Hertz; | 523 return CSSPrimitiveValue::UnitType::Hertz; |
| 532 case CalcPercentLength: | 524 case CalcPercentLength: |
| 533 case CalcPercentNumber: | 525 case CalcPercentNumber: |
| 534 case CalcOther: | 526 case CalcOther: |
| 535 return CSSPrimitiveValue::UnitType::Unknown; | 527 return CSSPrimitiveValue::UnitType::Unknown; |
| 536 } | 528 } |
| 537 ASSERT_NOT_REACHED(); | 529 ASSERT_NOT_REACHED(); |
| 538 return CSSPrimitiveValue::UnitType::Unknown; | 530 return CSSPrimitiveValue::UnitType::Unknown; |
| 539 } | 531 } |
| 540 | 532 |
| 541 DEFINE_INLINE_VIRTUAL_TRACE() | |
| 542 { | |
| 543 visitor->trace(m_leftSide); | |
| 544 visitor->trace(m_rightSide); | |
| 545 CSSCalcExpressionNode::trace(visitor); | |
| 546 } | |
| 547 | |
| 548 private: | 533 private: |
| 549 CSSCalcBinaryOperation(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSid
e, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, Cal
culationCategory category) | 534 CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPt
r<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory categor
y) |
| 550 : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightS
ide.get(), op)) | 535 : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightS
ide.get(), op)) |
| 551 , m_leftSide(leftSide) | 536 , m_leftSide(leftSide) |
| 552 , m_rightSide(rightSide) | 537 , m_rightSide(rightSide) |
| 553 , m_operator(op) | 538 , m_operator(op) |
| 554 { | 539 { |
| 555 } | 540 } |
| 556 | 541 |
| 557 static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide,
CSSCalcExpressionNode* rightSide) | 542 static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide,
CSSCalcExpressionNode* rightSide) |
| 558 { | 543 { |
| 559 if (leftSide->category() == CalcNumber) | 544 if (leftSide->category() == CalcNumber) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 578 case CalcMultiply: | 563 case CalcMultiply: |
| 579 return leftValue * rightValue; | 564 return leftValue * rightValue; |
| 580 case CalcDivide: | 565 case CalcDivide: |
| 581 if (rightValue) | 566 if (rightValue) |
| 582 return leftValue / rightValue; | 567 return leftValue / rightValue; |
| 583 return std::numeric_limits<double>::quiet_NaN(); | 568 return std::numeric_limits<double>::quiet_NaN(); |
| 584 } | 569 } |
| 585 return 0; | 570 return 0; |
| 586 } | 571 } |
| 587 | 572 |
| 588 const RefPtrWillBeMember<CSSCalcExpressionNode> m_leftSide; | 573 const RefPtr<CSSCalcExpressionNode> m_leftSide; |
| 589 const RefPtrWillBeMember<CSSCalcExpressionNode> m_rightSide; | 574 const RefPtr<CSSCalcExpressionNode> m_rightSide; |
| 590 const CalcOperator m_operator; | 575 const CalcOperator m_operator; |
| 591 }; | 576 }; |
| 592 | 577 |
| 593 static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens) | 578 static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens) |
| 594 { | 579 { |
| 595 (*depth)++; | 580 (*depth)++; |
| 596 if (tokens.atEnd()) | 581 if (tokens.atEnd()) |
| 597 return NoMoreTokens; | 582 return NoMoreTokens; |
| 598 if (*depth > maxExpressionDepth) | 583 if (*depth > maxExpressionDepth) |
| 599 return TooDeep; | 584 return TooDeep; |
| 600 return OK; | 585 return OK; |
| 601 } | 586 } |
| 602 | 587 |
| 603 class CSSCalcExpressionNodeParser { | 588 class CSSCalcExpressionNodeParser { |
| 604 STACK_ALLOCATED(); | 589 STACK_ALLOCATED(); |
| 605 public: | 590 public: |
| 606 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange
tokens) | 591 PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens) |
| 607 { | 592 { |
| 608 Value result; | 593 Value result; |
| 609 tokens.consumeWhitespace(); | 594 tokens.consumeWhitespace(); |
| 610 bool ok = parseValueExpression(tokens, 0, &result); | 595 bool ok = parseValueExpression(tokens, 0, &result); |
| 611 if (!ok || !tokens.atEnd()) | 596 if (!ok || !tokens.atEnd()) |
| 612 return nullptr; | 597 return nullptr; |
| 613 return result.value; | 598 return result.value; |
| 614 } | 599 } |
| 615 | 600 |
| 616 private: | 601 private: |
| 617 struct Value { | 602 struct Value { |
| 618 STACK_ALLOCATED(); | 603 STACK_ALLOCATED(); |
| 619 public: | 604 public: |
| 620 RefPtrWillBeMember<CSSCalcExpressionNode> value; | 605 RefPtr<CSSCalcExpressionNode> value; |
| 621 }; | 606 }; |
| 622 | 607 |
| 623 char operatorValue(const CSSParserToken& token) | 608 char operatorValue(const CSSParserToken& token) |
| 624 { | 609 { |
| 625 if (token.type() == DelimiterToken) | 610 if (token.type() == DelimiterToken) |
| 626 return token.delimiter(); | 611 return token.delimiter(); |
| 627 return 0; | 612 return 0; |
| 628 } | 613 } |
| 629 | 614 |
| 630 bool parseValue(CSSParserTokenRange& tokens, Value* result) | 615 bool parseValue(CSSParserTokenRange& tokens, Value* result) |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 713 | 698 |
| 714 return true; | 699 return true; |
| 715 } | 700 } |
| 716 | 701 |
| 717 bool parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* res
ult) | 702 bool parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* res
ult) |
| 718 { | 703 { |
| 719 return parseAdditiveValueExpression(tokens, depth, result); | 704 return parseAdditiveValueExpression(tokens, depth, result); |
| 720 } | 705 } |
| 721 }; | 706 }; |
| 722 | 707 |
| 723 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode
(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger) | 708 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<
CSSPrimitiveValue> value, bool isInteger) |
| 724 { | 709 { |
| 725 return CSSCalcPrimitiveValue::create(value, isInteger); | 710 return CSSCalcPrimitiveValue::create(value, isInteger); |
| 726 } | 711 } |
| 727 | 712 |
| 728 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode
(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<
CSSCalcExpressionNode> rightSide, CalcOperator op) | 713 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<
CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, Ca
lcOperator op) |
| 729 { | 714 { |
| 730 return CSSCalcBinaryOperation::create(leftSide, rightSide, op); | 715 return CSSCalcBinaryOperation::create(leftSide, rightSide, op); |
| 731 } | 716 } |
| 732 | 717 |
| 733 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode
(double pixels, double percent) | 718 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(double pixe
ls, double percent) |
| 734 { | 719 { |
| 735 return createExpressionNode( | 720 return createExpressionNode( |
| 736 createExpressionNode(CSSPrimitiveValue::create(pixels, CSSPrimitiveValue
::UnitType::Pixels), pixels == trunc(pixels)), | 721 createExpressionNode(CSSPrimitiveValue::create(pixels, CSSPrimitiveValue
::UnitType::Pixels), pixels == trunc(pixels)), |
| 737 createExpressionNode(CSSPrimitiveValue::create(percent, CSSPrimitiveValu
e::UnitType::Percentage), percent == trunc(percent)), | 722 createExpressionNode(CSSPrimitiveValue::create(percent, CSSPrimitiveValu
e::UnitType::Percentage), percent == trunc(percent)), |
| 738 CalcAdd); | 723 CalcAdd); |
| 739 } | 724 } |
| 740 | 725 |
| 741 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(const CSSParserTokenRa
nge& tokens, ValueRange range) | 726 PassRefPtr<CSSCalcValue> CSSCalcValue::create(const CSSParserTokenRange& tokens,
ValueRange range) |
| 742 { | 727 { |
| 743 CSSCalcExpressionNodeParser parser; | 728 CSSCalcExpressionNodeParser parser; |
| 744 RefPtrWillBeRawPtr<CSSCalcExpressionNode> expression = parser.parseCalc(toke
ns); | 729 RefPtr<CSSCalcExpressionNode> expression = parser.parseCalc(tokens); |
| 745 | 730 |
| 746 return expression ? adoptRefWillBeNoop(new CSSCalcValue(expression, range))
: nullptr; | 731 return expression ? adoptRef(new CSSCalcValue(expression, range)) : nullptr; |
| 747 } | 732 } |
| 748 | 733 |
| 749 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtrWillBeRawPtr
<CSSCalcExpressionNode> expression, ValueRange range) | 734 PassRefPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtr<CSSCalcExpressionNode>
expression, ValueRange range) |
| 750 { | 735 { |
| 751 return adoptRefWillBeNoop(new CSSCalcValue(expression, range)); | 736 return adoptRef(new CSSCalcValue(expression, range)); |
| 752 } | 737 } |
| 753 | 738 |
| 754 } // namespace blink | 739 } // namespace blink |
| OLD | NEW |