OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "core/css/parser/CSSPropertyParser.h" | 6 #include "core/css/parser/CSSPropertyParser.h" |
7 | 7 |
8 #include "core/StylePropertyShorthand.h" | 8 #include "core/StylePropertyShorthand.h" |
9 #include "core/css/CSSCalculationValue.h" | 9 #include "core/css/CSSCalculationValue.h" |
| 10 #include "core/css/CSSCrossfadeValue.h" |
10 #include "core/css/CSSCursorImageValue.h" | 11 #include "core/css/CSSCursorImageValue.h" |
11 #include "core/css/CSSCustomIdentValue.h" | 12 #include "core/css/CSSCustomIdentValue.h" |
12 #include "core/css/CSSFontFaceSrcValue.h" | 13 #include "core/css/CSSFontFaceSrcValue.h" |
13 #include "core/css/CSSFontFeatureValue.h" | 14 #include "core/css/CSSFontFeatureValue.h" |
14 #include "core/css/CSSFunctionValue.h" | 15 #include "core/css/CSSFunctionValue.h" |
15 #include "core/css/CSSImageSetValue.h" | 16 #include "core/css/CSSImageSetValue.h" |
16 #include "core/css/CSSPathValue.h" | 17 #include "core/css/CSSPathValue.h" |
17 #include "core/css/CSSPrimitiveValueMappings.h" | 18 #include "core/css/CSSPrimitiveValueMappings.h" |
18 #include "core/css/CSSQuadValue.h" | 19 #include "core/css/CSSQuadValue.h" |
19 #include "core/css/CSSSVGDocumentValue.h" | 20 #include "core/css/CSSSVGDocumentValue.h" |
(...skipping 2378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2398 cursorType = consumeIdent(range); | 2399 cursorType = consumeIdent(range); |
2399 } | 2400 } |
2400 | 2401 |
2401 if (!list) | 2402 if (!list) |
2402 return cursorType.release(); | 2403 return cursorType.release(); |
2403 if (cursorType) | 2404 if (cursorType) |
2404 list->append(cursorType.release()); | 2405 list->append(cursorType.release()); |
2405 return list.release(); | 2406 return list.release(); |
2406 } | 2407 } |
2407 | 2408 |
| 2409 static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode
cssParserMode, CSSGradientValue* gradient) |
| 2410 { |
| 2411 bool supportsColorHints = gradient->gradientType() == CSSLinearGradient || g
radient->gradientType() == CSSRadialGradient; |
| 2412 |
| 2413 // The first color stop cannot be a color hint. |
| 2414 bool previousStopWasColorHint = true; |
| 2415 do { |
| 2416 CSSGradientColorStop stop; |
| 2417 stop.m_color = consumeColor(range, cssParserMode); |
| 2418 // Two hints in a row are not allowed. |
| 2419 if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint)) |
| 2420 return false; |
| 2421 previousStopWasColorHint = !stop.m_color; |
| 2422 stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRang
eAll); |
| 2423 if (!stop.m_color && !stop.m_position) |
| 2424 return false; |
| 2425 gradient->addStop(stop); |
| 2426 } while (consumeCommaIncludingWhitespace(range)); |
| 2427 |
| 2428 // The last color stop cannot be a color hint. |
| 2429 if (previousStopWasColorHint) |
| 2430 return false; |
| 2431 |
| 2432 // Must have 2 or more stops to be valid. |
| 2433 return gradient->stopCount() >= 2; |
| 2434 } |
| 2435 |
| 2436 static PassRefPtrWillBeRawPtr<CSSValue> consumeRadialGradient(CSSParserTokenRang
e& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating) |
| 2437 { |
| 2438 RefPtrWillBeRawPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::
create(repeating, CSSRadialGradient); |
| 2439 |
| 2440 RefPtrWillBeRawPtr<CSSPrimitiveValue> shape = nullptr; |
| 2441 RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeKeyword = nullptr; |
| 2442 RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontalSize = nullptr; |
| 2443 RefPtrWillBeRawPtr<CSSPrimitiveValue> verticalSize = nullptr; |
| 2444 |
| 2445 // First part of grammar, the size/shape clause: |
| 2446 // [ circle || <length> ] | |
| 2447 // [ ellipse || [ <length> | <percentage> ]{2} ] | |
| 2448 // [ [ circle | ellipse] || <size-keyword> ] |
| 2449 for (int i = 0; i < 3; ++i) { |
| 2450 if (args.peek().type() == IdentToken) { |
| 2451 CSSValueID id = args.peek().id(); |
| 2452 if (id == CSSValueCircle || id == CSSValueEllipse) { |
| 2453 if (shape) |
| 2454 return nullptr; |
| 2455 shape = consumeIdent(args); |
| 2456 } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner
|| id == CSSValueFarthestSide || id == CSSValueFarthestCorner) { |
| 2457 if (sizeKeyword) |
| 2458 return nullptr; |
| 2459 sizeKeyword = consumeIdent(args); |
| 2460 } else { |
| 2461 break; |
| 2462 } |
| 2463 } else { |
| 2464 RefPtrWillBeRawPtr<CSSPrimitiveValue> center = consumeLengthOrPercen
t(args, cssParserMode, ValueRangeAll); |
| 2465 if (!center) |
| 2466 break; |
| 2467 if (horizontalSize) |
| 2468 return nullptr; |
| 2469 horizontalSize = center; |
| 2470 if ((center = consumeLengthOrPercent(args, cssParserMode, ValueRange
All))) { |
| 2471 verticalSize = center.release(); |
| 2472 ++i; |
| 2473 } |
| 2474 } |
| 2475 } |
| 2476 |
| 2477 // You can specify size as a keyword or a length/percentage, not both. |
| 2478 if (sizeKeyword && horizontalSize) |
| 2479 return nullptr; |
| 2480 // Circles must have 0 or 1 lengths. |
| 2481 if (shape && shape->getValueID() == CSSValueCircle && verticalSize) |
| 2482 return nullptr; |
| 2483 // Ellipses must have 0 or 2 length/percentages. |
| 2484 if (shape && shape->getValueID() == CSSValueEllipse && horizontalSize && !ve
rticalSize) |
| 2485 return nullptr; |
| 2486 // If there's only one size, it must be a length. |
| 2487 // TODO(timloh): Calcs with both lengths and percentages should be rejected. |
| 2488 if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) |
| 2489 return nullptr; |
| 2490 |
| 2491 result->setShape(shape); |
| 2492 result->setSizingBehavior(sizeKeyword); |
| 2493 result->setEndHorizontalSize(horizontalSize); |
| 2494 result->setEndVerticalSize(verticalSize); |
| 2495 |
| 2496 RefPtrWillBeRawPtr<CSSValue> centerX = nullptr; |
| 2497 RefPtrWillBeRawPtr<CSSValue> centerY = nullptr; |
| 2498 if (args.peek().id() == CSSValueAt) { |
| 2499 args.consumeIncludingWhitespace(); |
| 2500 consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, cen
terY); |
| 2501 if (!(centerX && centerY)) |
| 2502 return nullptr; |
| 2503 result->setFirstX(centerX); |
| 2504 result->setFirstY(centerY); |
| 2505 // Right now, CSS radial gradients have the same start and end centers. |
| 2506 result->setSecondX(centerX); |
| 2507 result->setSecondY(centerY); |
| 2508 } |
| 2509 |
| 2510 if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consu
meCommaIncludingWhitespace(args)) |
| 2511 return nullptr; |
| 2512 if (!consumeGradientColorStops(args, cssParserMode, result.get())) |
| 2513 return nullptr; |
| 2514 return result.release(); |
| 2515 } |
| 2516 |
| 2517 static PassRefPtrWillBeRawPtr<CSSValue> consumeLinearGradient(CSSParserTokenRang
e& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientTy
pe gradientType) |
| 2518 { |
| 2519 RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::
create(repeating, gradientType); |
| 2520 |
| 2521 bool expectComma = true; |
| 2522 RefPtrWillBeRawPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMo
de); |
| 2523 if (angle) { |
| 2524 result->setAngle(angle.release()); |
| 2525 } else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValu
eTo>(args)) { |
| 2526 RefPtrWillBeRawPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft,
CSSValueRight>(args); |
| 2527 RefPtrWillBeRawPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom
, CSSValueTop>(args); |
| 2528 if (!endX && !endY) { |
| 2529 if (gradientType == CSSLinearGradient) |
| 2530 return nullptr; |
| 2531 endY = cssValuePool().createIdentifierValue(CSSValueTop); |
| 2532 expectComma = false; |
| 2533 } else if (!endX) { |
| 2534 endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); |
| 2535 } |
| 2536 |
| 2537 result->setFirstX(endX.release()); |
| 2538 result->setFirstY(endY.release()); |
| 2539 } else { |
| 2540 expectComma = false; |
| 2541 } |
| 2542 |
| 2543 if (expectComma && !consumeCommaIncludingWhitespace(args)) |
| 2544 return nullptr; |
| 2545 if (!consumeGradientColorStops(args, cssParserMode, result.get())) |
| 2546 return nullptr; |
| 2547 return result.release(); |
| 2548 } |
| 2549 |
| 2550 static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange&, CSSPa
rserContext); |
| 2551 |
| 2552 static PassRefPtrWillBeRawPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& ar
gs, CSSParserContext context) |
| 2553 { |
| 2554 RefPtrWillBeRawPtr<CSSValue> fromImageValue = consumeImage(args, context); |
| 2555 if (!fromImageValue || !consumeCommaIncludingWhitespace(args)) |
| 2556 return nullptr; |
| 2557 RefPtrWillBeRawPtr<CSSValue> toImageValue = consumeImage(args, context); |
| 2558 if (!toImageValue || !consumeCommaIncludingWhitespace(args)) |
| 2559 return nullptr; |
| 2560 |
| 2561 RefPtrWillBeRawPtr<CSSPrimitiveValue> percentage = nullptr; |
| 2562 const CSSParserToken& percentageArg = args.consumeIncludingWhitespace(); |
| 2563 if (percentageArg.type() == PercentageToken) |
| 2564 percentage = cssValuePool().createValue(clampTo<double>(percentageArg.nu
mericValue() / 100, 0, 1), CSSPrimitiveValue::UnitType::Number); |
| 2565 else if (percentageArg.type() == NumberToken) |
| 2566 percentage = cssValuePool().createValue(clampTo<double>(percentageArg.nu
mericValue(), 0, 1), CSSPrimitiveValue::UnitType::Number); |
| 2567 |
| 2568 if (!percentage) |
| 2569 return nullptr; |
| 2570 return CSSCrossfadeValue::create(fromImageValue, toImageValue, percentage); |
| 2571 } |
| 2572 |
| 2573 static PassRefPtrWillBeRawPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRang
e& range, CSSParserContext context) |
| 2574 { |
| 2575 CSSValueID id = range.peek().functionId(); |
| 2576 CSSParserTokenRange rangeCopy = range; |
| 2577 CSSParserTokenRange args = consumeFunction(rangeCopy); |
| 2578 RefPtrWillBeRawPtr<CSSValue> result = nullptr; |
| 2579 if (id == CSSValueRadialGradient) { |
| 2580 result = consumeRadialGradient(args, context.mode(), NonRepeating); |
| 2581 } else if (id == CSSValueWebkitLinearGradient) { |
| 2582 // FIXME: This should send a deprecation message. |
| 2583 if (context.useCounter()) |
| 2584 context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradie
nt); |
| 2585 result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSPr
efixedLinearGradient); |
| 2586 } else if (id == CSSValueWebkitRepeatingLinearGradient) { |
| 2587 // FIXME: This should send a deprecation message. |
| 2588 if (context.useCounter()) |
| 2589 context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLin
earGradient); |
| 2590 result = consumeLinearGradient(args, context.mode(), Repeating, CSSPrefi
xedLinearGradient); |
| 2591 } else if (id == CSSValueLinearGradient) { |
| 2592 result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSLi
nearGradient); |
| 2593 } else if (id == CSSValueWebkitCrossFade) { |
| 2594 result = consumeCrossFade(args, context); |
| 2595 } |
| 2596 if (!result || !args.atEnd()) |
| 2597 return nullptr; |
| 2598 range = rangeCopy; |
| 2599 return result; |
| 2600 } |
| 2601 |
| 2602 static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange& range,
CSSParserContext context) |
| 2603 { |
| 2604 if (range.peek().id() == CSSValueNone) |
| 2605 return consumeIdent(range); |
| 2606 |
| 2607 AtomicString uri(consumeUrl(range)); |
| 2608 if (!uri.isNull()) |
| 2609 return CSSPropertyParser::createCSSImageValueWithReferrer(uri, context); |
| 2610 if (range.peek().type() == FunctionToken) { |
| 2611 CSSValueID id = range.peek().functionId(); |
| 2612 if (id == CSSValueWebkitImageSet) |
| 2613 return consumeImageSet(range, context); |
| 2614 if (CSSPropertyParser::isGeneratedImage(id)) |
| 2615 return consumeGeneratedImage(range, context); |
| 2616 } |
| 2617 return nullptr; |
| 2618 } |
| 2619 |
2408 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSProperty
ID unresolvedProperty) | 2620 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSProperty
ID unresolvedProperty) |
2409 { | 2621 { |
2410 CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty); | 2622 CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty); |
2411 switch (property) { | 2623 switch (property) { |
2412 case CSSPropertyWillChange: | 2624 case CSSPropertyWillChange: |
2413 return consumeWillChange(m_range); | 2625 return consumeWillChange(m_range); |
2414 case CSSPropertyPage: | 2626 case CSSPropertyPage: |
2415 return consumePage(m_range); | 2627 return consumePage(m_range); |
2416 case CSSPropertyQuotes: | 2628 case CSSPropertyQuotes: |
2417 return consumeQuotes(m_range); | 2629 return consumeQuotes(m_range); |
(...skipping 816 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3234 return consumeShorthandGreedily(flexFlowShorthand(), important); | 3446 return consumeShorthandGreedily(flexFlowShorthand(), important); |
3235 case CSSPropertyWebkitColumnRule: | 3447 case CSSPropertyWebkitColumnRule: |
3236 return consumeShorthandGreedily(webkitColumnRuleShorthand(), important); | 3448 return consumeShorthandGreedily(webkitColumnRuleShorthand(), important); |
3237 default: | 3449 default: |
3238 m_currentShorthand = oldShorthand; | 3450 m_currentShorthand = oldShorthand; |
3239 return false; | 3451 return false; |
3240 } | 3452 } |
3241 } | 3453 } |
3242 | 3454 |
3243 } // namespace blink | 3455 } // namespace blink |
OLD | NEW |