2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
29 #include "core/css/resolver/CSSToStyleMap.h"
31 #include "CSSValueKeywords.h"
32 #include "core/animation/css/CSSAnimationData.h"
33 #include "core/css/CSSBorderImageSliceValue.h"
34 #include "core/css/CSSPrimitiveValue.h"
35 #include "core/css/CSSPrimitiveValueMappings.h"
36 #include "core/css/CSSTimingFunctionValue.h"
37 #include "core/css/Pair.h"
38 #include "core/css/Rect.h"
39 #include "core/css/resolver/StyleResolverState.h"
40 #include "core/rendering/style/BorderImageLengthBox.h"
41 #include "core/rendering/style/FillLayer.h"
45 const CSSToLengthConversionData& CSSToStyleMap::cssToLengthConversionData() const
47 return m_state.cssToLengthConversionData();
50 bool CSSToStyleMap::useSVGZoomRules() const
52 return m_state.useSVGZoomRules();
55 PassRefPtr<StyleImage> CSSToStyleMap::styleImage(CSSPropertyID propertyId, CSSValue* value)
57 return m_elementStyleResources.styleImage(m_state.document().textLinkColors(), m_state.style()->color(), propertyId, value);
60 void CSSToStyleMap::mapFillAttachment(CSSPropertyID, FillLayer* layer, CSSValue* value) const
62 if (value->isInitialValue()) {
63 layer->setAttachment(FillLayer::initialFillAttachment(layer->type()));
67 if (!value->isPrimitiveValue())
70 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
71 switch (primitiveValue->getValueID()) {
73 layer->setAttachment(FixedBackgroundAttachment);
76 layer->setAttachment(ScrollBackgroundAttachment);
79 layer->setAttachment(LocalBackgroundAttachment);
86 void CSSToStyleMap::mapFillClip(CSSPropertyID, FillLayer* layer, CSSValue* value) const
88 if (value->isInitialValue()) {
89 layer->setClip(FillLayer::initialFillClip(layer->type()));
93 if (!value->isPrimitiveValue())
96 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
97 layer->setClip(*primitiveValue);
100 void CSSToStyleMap::mapFillComposite(CSSPropertyID, FillLayer* layer, CSSValue* value) const
102 if (value->isInitialValue()) {
103 layer->setComposite(FillLayer::initialFillComposite(layer->type()));
107 if (!value->isPrimitiveValue())
110 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
111 layer->setComposite(*primitiveValue);
114 void CSSToStyleMap::mapFillBlendMode(CSSPropertyID, FillLayer* layer, CSSValue* value) const
116 if (value->isInitialValue()) {
117 layer->setBlendMode(FillLayer::initialFillBlendMode(layer->type()));
121 if (!value->isPrimitiveValue())
124 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
125 layer->setBlendMode(*primitiveValue);
128 void CSSToStyleMap::mapFillOrigin(CSSPropertyID, FillLayer* layer, CSSValue* value) const
130 if (value->isInitialValue()) {
131 layer->setOrigin(FillLayer::initialFillOrigin(layer->type()));
135 if (!value->isPrimitiveValue())
138 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
139 layer->setOrigin(*primitiveValue);
143 void CSSToStyleMap::mapFillImage(CSSPropertyID property, FillLayer* layer, CSSValue* value)
145 if (value->isInitialValue()) {
146 layer->setImage(FillLayer::initialFillImage(layer->type()));
150 layer->setImage(styleImage(property, value));
153 void CSSToStyleMap::mapFillRepeatX(CSSPropertyID, FillLayer* layer, CSSValue* value) const
155 if (value->isInitialValue()) {
156 layer->setRepeatX(FillLayer::initialFillRepeatX(layer->type()));
160 if (!value->isPrimitiveValue())
163 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
164 layer->setRepeatX(*primitiveValue);
167 void CSSToStyleMap::mapFillRepeatY(CSSPropertyID, FillLayer* layer, CSSValue* value) const
169 if (value->isInitialValue()) {
170 layer->setRepeatY(FillLayer::initialFillRepeatY(layer->type()));
174 if (!value->isPrimitiveValue())
177 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
178 layer->setRepeatY(*primitiveValue);
181 void CSSToStyleMap::mapFillSize(CSSPropertyID, FillLayer* layer, CSSValue* value) const
183 if (!value->isPrimitiveValue()) {
184 layer->setSizeType(SizeNone);
188 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
189 if (primitiveValue->getValueID() == CSSValueContain)
190 layer->setSizeType(Contain);
191 else if (primitiveValue->getValueID() == CSSValueCover)
192 layer->setSizeType(Cover);
194 layer->setSizeType(SizeLength);
196 LengthSize b = FillLayer::initialFillSizeLength(layer->type());
198 if (value->isInitialValue() || primitiveValue->getValueID() == CSSValueContain || primitiveValue->getValueID() == CSSValueCover) {
199 layer->setSizeLength(b);
206 if (Pair* pair = primitiveValue->getPairValue()) {
207 firstLength = pair->first()->convertToLength<AnyConversion>(cssToLengthConversionData());
208 secondLength = pair->second()->convertToLength<AnyConversion>(cssToLengthConversionData());
210 firstLength = primitiveValue->convertToLength<AnyConversion>(cssToLengthConversionData());
211 secondLength = Length();
214 b.setWidth(firstLength);
215 b.setHeight(secondLength);
216 layer->setSizeLength(b);
219 void CSSToStyleMap::mapFillXPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const
221 if (value->isInitialValue()) {
222 layer->setXPosition(FillLayer::initialFillXPosition(layer->type()));
226 if (!value->isPrimitiveValue())
229 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
230 Pair* pair = primitiveValue->getPairValue();
232 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionX || propertyID == CSSPropertyWebkitMaskPositionX);
233 primitiveValue = pair->second();
236 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
238 layer->setXPosition(length);
240 layer->setBackgroundXOrigin(*(pair->first()));
243 void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const
245 if (value->isInitialValue()) {
246 layer->setYPosition(FillLayer::initialFillYPosition(layer->type()));
250 if (!value->isPrimitiveValue())
253 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
254 Pair* pair = primitiveValue->getPairValue();
256 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY);
257 primitiveValue = pair->second();
260 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
262 layer->setYPosition(length);
264 layer->setBackgroundYOrigin(*(pair->first()));
267 void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID, FillLayer* layer, CSSValue* value)
269 EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type());
270 if (value->isInitialValue()) {
271 layer->setMaskSourceType(type);
275 if (!value->isPrimitiveValue())
278 switch (toCSSPrimitiveValue(value)->getValueID()) {
282 case CSSValueLuminance:
283 type = MaskLuminance;
288 ASSERT_NOT_REACHED();
291 layer->setMaskSourceType(type);
294 void CSSToStyleMap::mapAnimationDelay(CSSAnimationData* animation, CSSValue* value) const
296 if (value->isInitialValue()) {
297 animation->setDelay(CSSAnimationData::initialAnimationDelay());
301 if (!value->isPrimitiveValue())
304 animation->setDelay(toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>());
307 void CSSToStyleMap::mapAnimationDirection(CSSAnimationData* layer, CSSValue* value) const
309 if (value->isInitialValue()) {
310 layer->setDirection(CSSAnimationData::initialAnimationDirection());
314 if (!value->isPrimitiveValue())
317 switch (toCSSPrimitiveValue(value)->getValueID()) {
319 layer->setDirection(CSSAnimationData::AnimationDirectionNormal);
321 case CSSValueAlternate:
322 layer->setDirection(CSSAnimationData::AnimationDirectionAlternate);
324 case CSSValueReverse:
325 layer->setDirection(CSSAnimationData::AnimationDirectionReverse);
327 case CSSValueAlternateReverse:
328 layer->setDirection(CSSAnimationData::AnimationDirectionAlternateReverse);
335 void CSSToStyleMap::mapAnimationDuration(CSSAnimationData* animation, CSSValue* value) const
337 if (value->isInitialValue()) {
338 animation->setDuration(CSSAnimationData::initialAnimationDuration());
342 if (!value->isPrimitiveValue())
345 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
346 animation->setDuration(primitiveValue->computeTime<double, CSSPrimitiveValue::Seconds>());
349 void CSSToStyleMap::mapAnimationFillMode(CSSAnimationData* layer, CSSValue* value) const
351 if (value->isInitialValue()) {
352 layer->setFillMode(CSSAnimationData::initialAnimationFillMode());
356 if (!value->isPrimitiveValue())
359 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
360 switch (primitiveValue->getValueID()) {
362 layer->setFillMode(AnimationFillModeNone);
364 case CSSValueForwards:
365 layer->setFillMode(AnimationFillModeForwards);
367 case CSSValueBackwards:
368 layer->setFillMode(AnimationFillModeBackwards);
371 layer->setFillMode(AnimationFillModeBoth);
378 void CSSToStyleMap::mapAnimationIterationCount(CSSAnimationData* animation, CSSValue* value) const
380 if (value->isInitialValue()) {
381 animation->setIterationCount(CSSAnimationData::initialAnimationIterationCount());
385 if (!value->isPrimitiveValue())
388 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
389 if (primitiveValue->getValueID() == CSSValueInfinite)
390 animation->setIterationCount(CSSAnimationData::IterationCountInfinite);
392 animation->setIterationCount(primitiveValue->getFloatValue());
395 void CSSToStyleMap::mapAnimationName(CSSAnimationData* layer, CSSValue* value) const
397 if (value->isInitialValue()) {
398 layer->setName(CSSAnimationData::initialAnimationName());
402 if (!value->isPrimitiveValue())
405 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
406 if (primitiveValue->getValueID() == CSSValueNone)
407 layer->setIsNoneAnimation(true);
409 layer->setName(AtomicString(primitiveValue->getStringValue()));
412 void CSSToStyleMap::mapAnimationPlayState(CSSAnimationData* layer, CSSValue* value) const
414 if (value->isInitialValue()) {
415 layer->setPlayState(CSSAnimationData::initialAnimationPlayState());
419 if (!value->isPrimitiveValue())
422 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
423 EAnimPlayState playState = (primitiveValue->getValueID() == CSSValuePaused) ? AnimPlayStatePaused : AnimPlayStatePlaying;
424 layer->setPlayState(playState);
427 void CSSToStyleMap::mapAnimationProperty(CSSAnimationData* animation, CSSValue* value) const
429 if (value->isInitialValue()) {
430 animation->setAnimationMode(CSSAnimationData::AnimateAll);
431 animation->setProperty(CSSPropertyInvalid);
435 if (!value->isPrimitiveValue())
438 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
439 if (primitiveValue->getValueID() == CSSValueAll) {
440 animation->setAnimationMode(CSSAnimationData::AnimateAll);
441 animation->setProperty(CSSPropertyInvalid);
442 } else if (primitiveValue->getValueID() == CSSValueNone) {
443 animation->setAnimationMode(CSSAnimationData::AnimateNone);
444 animation->setProperty(CSSPropertyInvalid);
446 animation->setAnimationMode(CSSAnimationData::AnimateSingleProperty);
447 animation->setProperty(primitiveValue->getPropertyID());
451 void CSSToStyleMap::mapAnimationTimingFunction(CSSAnimationData* animation, CSSValue* value) const
453 if (value->isInitialValue()) {
454 animation->setTimingFunction(CSSAnimationData::initialAnimationTimingFunction());
458 if (value->isPrimitiveValue()) {
459 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
460 switch (primitiveValue->getValueID()) {
462 animation->setTimingFunction(LinearTimingFunction::create());
465 animation->setTimingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease));
468 animation->setTimingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn));
470 case CSSValueEaseOut:
471 animation->setTimingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut));
473 case CSSValueEaseInOut:
474 animation->setTimingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut));
476 case CSSValueStepStart:
477 animation->setTimingFunction(StepsTimingFunction::preset(StepsTimingFunction::Start));
479 case CSSValueStepEnd:
480 animation->setTimingFunction(StepsTimingFunction::preset(StepsTimingFunction::End));
488 if (value->isCubicBezierTimingFunctionValue()) {
489 CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value);
490 animation->setTimingFunction(CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2()));
491 } else if (value->isStepsTimingFunctionValue()) {
492 CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value);
493 animation->setTimingFunction(StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart()));
497 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image)
499 // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
500 if (!value || !value->isValueList())
503 // Retrieve the border image value.
504 CSSValueList* borderImage = toCSSValueList(value);
506 // Set the image (this kicks off the load).
507 CSSPropertyID imageProperty;
508 if (property == CSSPropertyWebkitBorderImage)
509 imageProperty = CSSPropertyBorderImageSource;
510 else if (property == CSSPropertyWebkitMaskBoxImage)
511 imageProperty = CSSPropertyWebkitMaskBoxImageSource;
513 imageProperty = property;
515 for (unsigned i = 0 ; i < borderImage->length() ; ++i) {
516 CSSValue* current = borderImage->item(i);
518 if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue())
519 image.setImage(styleImage(imageProperty, current));
520 else if (current->isBorderImageSliceValue())
521 mapNinePieceImageSlice(current, image);
522 else if (current->isValueList()) {
523 CSSValueList* slashList = toCSSValueList(current);
524 // Map in the image slices.
525 if (slashList->item(0) && slashList->item(0)->isBorderImageSliceValue())
526 mapNinePieceImageSlice(slashList->item(0), image);
528 // Map in the border slices.
529 if (slashList->item(1))
530 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1)));
532 // Map in the outset.
533 if (slashList->item(2))
534 image.setOutset(mapNinePieceImageQuad(slashList->item(2)));
535 } else if (current->isPrimitiveValue()) {
536 // Set the appropriate rules for stretch/round/repeat of the slices.
537 mapNinePieceImageRepeat(current, image);
541 if (property == CSSPropertyWebkitBorderImage) {
542 // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
543 // also set the border widths. We don't need to worry about percentages, since we don't even support
544 // those on real borders yet.
545 if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed())
546 mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value());
547 if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed())
548 mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value());
549 if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed())
550 mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value());
551 if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed())
552 mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value());
556 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const
558 if (!value || !value->isBorderImageSliceValue())
561 // Retrieve the border image value.
562 CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value);
564 // Set up a length box to represent our image slices.
566 Quad* slices = borderImageSlice->slices();
567 if (slices->top()->isPercentage())
568 box.m_top = Length(slices->top()->getDoubleValue(), Percent);
570 box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
571 if (slices->bottom()->isPercentage())
572 box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent);
574 box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
575 if (slices->left()->isPercentage())
576 box.m_left = Length(slices->left()->getDoubleValue(), Percent);
578 box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
579 if (slices->right()->isPercentage())
580 box.m_right = Length(slices->right()->getDoubleValue(), Percent);
582 box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
583 image.setImageSlices(box);
585 // Set our fill mode.
586 image.setFill(borderImageSlice->m_fill);
589 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData)
591 if (value.isNumber())
592 return value.getDoubleValue();
593 if (value.isPercentage())
594 return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
595 if (value.getValueID() != CSSValueAuto)
596 return value.computeLength<Length>(conversionData);
600 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const
602 if (!value || !value->isPrimitiveValue())
603 return BorderImageLengthBox(Length(Auto));
605 float zoom = useSVGZoomRules() ? 1.0f : cssToLengthConversionData().zoom();
606 Quad* slices = toCSSPrimitiveValue(value)->getQuadValue();
608 // Set up a border image length box to represent our image slices.
609 const CSSToLengthConversionData& conversionData = cssToLengthConversionData().copyWithAdjustedZoom(zoom);
610 return BorderImageLengthBox(
611 toBorderImageLength(*slices->top(), conversionData),
612 toBorderImageLength(*slices->right(), conversionData),
613 toBorderImageLength(*slices->bottom(), conversionData),
614 toBorderImageLength(*slices->left(), conversionData));
617 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const
619 if (!value || !value->isPrimitiveValue())
622 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
623 Pair* pair = primitiveValue->getPairValue();
624 if (!pair || !pair->first() || !pair->second())
627 CSSValueID firstIdentifier = pair->first()->getValueID();
628 CSSValueID secondIdentifier = pair->second()->getValueID();
630 ENinePieceImageRule horizontalRule;
631 switch (firstIdentifier) {
632 case CSSValueStretch:
633 horizontalRule = StretchImageRule;
636 horizontalRule = RoundImageRule;
639 horizontalRule = SpaceImageRule;
641 default: // CSSValueRepeat
642 horizontalRule = RepeatImageRule;
645 image.setHorizontalRule(horizontalRule);
647 ENinePieceImageRule verticalRule;
648 switch (secondIdentifier) {
649 case CSSValueStretch:
650 verticalRule = StretchImageRule;
653 verticalRule = RoundImageRule;
656 verticalRule = SpaceImageRule;
658 default: // CSSValueRepeat
659 verticalRule = RepeatImageRule;
662 image.setVerticalRule(verticalRule);