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 PassRefPtr<TimingFunction> CSSToStyleMap::animationTimingFunction(CSSValue* value, bool allowInitial)
453 if (allowInitial && value->isInitialValue()) {
454 return CSSAnimationData::initialAnimationTimingFunction();
457 if (value->isPrimitiveValue()) {
458 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
459 switch (primitiveValue->getValueID()) {
461 return LinearTimingFunction::create();
464 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease);
467 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
469 case CSSValueEaseOut:
470 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
472 case CSSValueEaseInOut:
473 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut);
475 case CSSValueStepStart:
476 return StepsTimingFunction::preset(StepsTimingFunction::Start);
478 case CSSValueStepEnd:
479 return StepsTimingFunction::preset(StepsTimingFunction::End);
487 if (value->isCubicBezierTimingFunctionValue()) {
488 CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value);
489 return CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2());
490 } else if (value->isStepsTimingFunctionValue()) {
491 CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value);
492 return StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart());
498 void CSSToStyleMap::mapAnimationTimingFunction(CSSAnimationData* animation, CSSValue* value) const
500 RefPtr<TimingFunction> timingFunction = animationTimingFunction(value, true);
502 animation->setTimingFunction(timingFunction);
505 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image)
507 // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
508 if (!value || !value->isValueList())
511 // Retrieve the border image value.
512 CSSValueList* borderImage = toCSSValueList(value);
514 // Set the image (this kicks off the load).
515 CSSPropertyID imageProperty;
516 if (property == CSSPropertyWebkitBorderImage)
517 imageProperty = CSSPropertyBorderImageSource;
518 else if (property == CSSPropertyWebkitMaskBoxImage)
519 imageProperty = CSSPropertyWebkitMaskBoxImageSource;
521 imageProperty = property;
523 for (unsigned i = 0 ; i < borderImage->length() ; ++i) {
524 CSSValue* current = borderImage->item(i);
526 if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue())
527 image.setImage(styleImage(imageProperty, current));
528 else if (current->isBorderImageSliceValue())
529 mapNinePieceImageSlice(current, image);
530 else if (current->isValueList()) {
531 CSSValueList* slashList = toCSSValueList(current);
532 // Map in the image slices.
533 if (slashList->item(0) && slashList->item(0)->isBorderImageSliceValue())
534 mapNinePieceImageSlice(slashList->item(0), image);
536 // Map in the border slices.
537 if (slashList->item(1))
538 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1)));
540 // Map in the outset.
541 if (slashList->item(2))
542 image.setOutset(mapNinePieceImageQuad(slashList->item(2)));
543 } else if (current->isPrimitiveValue()) {
544 // Set the appropriate rules for stretch/round/repeat of the slices.
545 mapNinePieceImageRepeat(current, image);
549 if (property == CSSPropertyWebkitBorderImage) {
550 // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
551 // also set the border widths. We don't need to worry about percentages, since we don't even support
552 // those on real borders yet.
553 if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed())
554 mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value());
555 if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed())
556 mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value());
557 if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed())
558 mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value());
559 if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed())
560 mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value());
564 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const
566 if (!value || !value->isBorderImageSliceValue())
569 // Retrieve the border image value.
570 CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value);
572 // Set up a length box to represent our image slices.
574 Quad* slices = borderImageSlice->slices();
575 if (slices->top()->isPercentage())
576 box.m_top = Length(slices->top()->getDoubleValue(), Percent);
578 box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
579 if (slices->bottom()->isPercentage())
580 box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent);
582 box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
583 if (slices->left()->isPercentage())
584 box.m_left = Length(slices->left()->getDoubleValue(), Percent);
586 box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
587 if (slices->right()->isPercentage())
588 box.m_right = Length(slices->right()->getDoubleValue(), Percent);
590 box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
591 image.setImageSlices(box);
593 // Set our fill mode.
594 image.setFill(borderImageSlice->m_fill);
597 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData)
599 if (value.isNumber())
600 return value.getDoubleValue();
601 if (value.isPercentage())
602 return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
603 if (value.getValueID() != CSSValueAuto)
604 return value.computeLength<Length>(conversionData);
608 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const
610 if (!value || !value->isPrimitiveValue())
611 return BorderImageLengthBox(Length(Auto));
613 float zoom = useSVGZoomRules() ? 1.0f : cssToLengthConversionData().zoom();
614 Quad* slices = toCSSPrimitiveValue(value)->getQuadValue();
616 // Set up a border image length box to represent our image slices.
617 const CSSToLengthConversionData& conversionData = cssToLengthConversionData().copyWithAdjustedZoom(zoom);
618 return BorderImageLengthBox(
619 toBorderImageLength(*slices->top(), conversionData),
620 toBorderImageLength(*slices->right(), conversionData),
621 toBorderImageLength(*slices->bottom(), conversionData),
622 toBorderImageLength(*slices->left(), conversionData));
625 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const
627 if (!value || !value->isPrimitiveValue())
630 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
631 Pair* pair = primitiveValue->getPairValue();
632 if (!pair || !pair->first() || !pair->second())
635 CSSValueID firstIdentifier = pair->first()->getValueID();
636 CSSValueID secondIdentifier = pair->second()->getValueID();
638 ENinePieceImageRule horizontalRule;
639 switch (firstIdentifier) {
640 case CSSValueStretch:
641 horizontalRule = StretchImageRule;
644 horizontalRule = RoundImageRule;
647 horizontalRule = SpaceImageRule;
649 default: // CSSValueRepeat
650 horizontalRule = RepeatImageRule;
653 image.setHorizontalRule(horizontalRule);
655 ENinePieceImageRule verticalRule;
656 switch (secondIdentifier) {
657 case CSSValueStretch:
658 verticalRule = StretchImageRule;
661 verticalRule = RoundImageRule;
664 verticalRule = SpaceImageRule;
666 default: // CSSValueRepeat
667 verticalRule = RepeatImageRule;
670 image.setVerticalRule(verticalRule);