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(), 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->isInitialValue()) {
184 layer->setSizeType(FillLayer::initialFillSizeType(layer->type()));
185 layer->setSizeLength(FillLayer::initialFillSizeLength(layer->type()));
189 if (!value->isPrimitiveValue())
192 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
193 if (primitiveValue->getValueID() == CSSValueContain)
194 layer->setSizeType(Contain);
195 else if (primitiveValue->getValueID() == CSSValueCover)
196 layer->setSizeType(Cover);
198 layer->setSizeType(SizeLength);
200 LengthSize b = FillLayer::initialFillSizeLength(layer->type());
202 if (primitiveValue->getValueID() == CSSValueContain || primitiveValue->getValueID() == CSSValueCover) {
203 layer->setSizeLength(b);
210 if (Pair* pair = primitiveValue->getPairValue()) {
211 firstLength = pair->first()->convertToLength<AnyConversion>(cssToLengthConversionData());
212 secondLength = pair->second()->convertToLength<AnyConversion>(cssToLengthConversionData());
214 firstLength = primitiveValue->convertToLength<AnyConversion>(cssToLengthConversionData());
215 secondLength = Length();
218 b.setWidth(firstLength);
219 b.setHeight(secondLength);
220 layer->setSizeLength(b);
223 void CSSToStyleMap::mapFillXPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const
225 if (value->isInitialValue()) {
226 layer->setXPosition(FillLayer::initialFillXPosition(layer->type()));
230 if (!value->isPrimitiveValue())
233 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
234 Pair* pair = primitiveValue->getPairValue();
236 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionX || propertyID == CSSPropertyWebkitMaskPositionX);
237 primitiveValue = pair->second();
240 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
242 layer->setXPosition(length);
244 layer->setBackgroundXOrigin(*(pair->first()));
247 void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const
249 if (value->isInitialValue()) {
250 layer->setYPosition(FillLayer::initialFillYPosition(layer->type()));
254 if (!value->isPrimitiveValue())
257 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
258 Pair* pair = primitiveValue->getPairValue();
260 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY);
261 primitiveValue = pair->second();
264 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
266 layer->setYPosition(length);
268 layer->setBackgroundYOrigin(*(pair->first()));
271 void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID, FillLayer* layer, CSSValue* value)
273 EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type());
274 if (value->isInitialValue()) {
275 layer->setMaskSourceType(type);
279 if (!value->isPrimitiveValue())
282 switch (toCSSPrimitiveValue(value)->getValueID()) {
286 case CSSValueLuminance:
287 type = MaskLuminance;
292 ASSERT_NOT_REACHED();
295 layer->setMaskSourceType(type);
298 void CSSToStyleMap::mapAnimationDelay(CSSAnimationData* animation, CSSValue* value) const
300 if (value->isInitialValue()) {
301 animation->setDelay(CSSAnimationData::initialAnimationDelay());
305 if (!value->isPrimitiveValue())
308 animation->setDelay(toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>());
311 void CSSToStyleMap::mapAnimationDirection(CSSAnimationData* layer, CSSValue* value) const
313 if (value->isInitialValue()) {
314 layer->setDirection(CSSAnimationData::initialAnimationDirection());
318 if (!value->isPrimitiveValue())
321 switch (toCSSPrimitiveValue(value)->getValueID()) {
323 layer->setDirection(CSSAnimationData::AnimationDirectionNormal);
325 case CSSValueAlternate:
326 layer->setDirection(CSSAnimationData::AnimationDirectionAlternate);
328 case CSSValueReverse:
329 layer->setDirection(CSSAnimationData::AnimationDirectionReverse);
331 case CSSValueAlternateReverse:
332 layer->setDirection(CSSAnimationData::AnimationDirectionAlternateReverse);
339 void CSSToStyleMap::mapAnimationDuration(CSSAnimationData* animation, CSSValue* value) const
341 if (value->isInitialValue()) {
342 animation->setDuration(CSSAnimationData::initialAnimationDuration());
346 if (!value->isPrimitiveValue())
349 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
350 animation->setDuration(primitiveValue->computeTime<double, CSSPrimitiveValue::Seconds>());
353 void CSSToStyleMap::mapAnimationFillMode(CSSAnimationData* layer, CSSValue* value) const
355 if (value->isInitialValue()) {
356 layer->setFillMode(CSSAnimationData::initialAnimationFillMode());
360 if (!value->isPrimitiveValue())
363 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
364 switch (primitiveValue->getValueID()) {
366 layer->setFillMode(AnimationFillModeNone);
368 case CSSValueForwards:
369 layer->setFillMode(AnimationFillModeForwards);
371 case CSSValueBackwards:
372 layer->setFillMode(AnimationFillModeBackwards);
375 layer->setFillMode(AnimationFillModeBoth);
382 void CSSToStyleMap::mapAnimationIterationCount(CSSAnimationData* animation, CSSValue* value) const
384 if (value->isInitialValue()) {
385 animation->setIterationCount(CSSAnimationData::initialAnimationIterationCount());
389 if (!value->isPrimitiveValue())
392 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
393 if (primitiveValue->getValueID() == CSSValueInfinite)
394 animation->setIterationCount(CSSAnimationData::IterationCountInfinite);
396 animation->setIterationCount(primitiveValue->getFloatValue());
399 void CSSToStyleMap::mapAnimationName(CSSAnimationData* layer, CSSValue* value) const
401 if (value->isInitialValue()) {
402 layer->setName(CSSAnimationData::initialAnimationName());
406 if (!value->isPrimitiveValue())
409 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
410 if (primitiveValue->getValueID() == CSSValueNone)
411 layer->setIsNoneAnimation(true);
413 layer->setName(AtomicString(primitiveValue->getStringValue()));
416 void CSSToStyleMap::mapAnimationPlayState(CSSAnimationData* layer, CSSValue* value) const
418 if (value->isInitialValue()) {
419 layer->setPlayState(CSSAnimationData::initialAnimationPlayState());
423 if (!value->isPrimitiveValue())
426 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
427 EAnimPlayState playState = (primitiveValue->getValueID() == CSSValuePaused) ? AnimPlayStatePaused : AnimPlayStatePlaying;
428 layer->setPlayState(playState);
431 void CSSToStyleMap::mapAnimationProperty(CSSAnimationData* animation, CSSValue* value) const
433 if (value->isInitialValue()) {
434 animation->setAnimationMode(CSSAnimationData::AnimateAll);
435 animation->setProperty(CSSPropertyInvalid);
439 if (!value->isPrimitiveValue())
442 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
443 if (primitiveValue->getValueID() == CSSValueAll) {
444 animation->setAnimationMode(CSSAnimationData::AnimateAll);
445 animation->setProperty(CSSPropertyInvalid);
446 } else if (primitiveValue->getValueID() == CSSValueNone) {
447 animation->setAnimationMode(CSSAnimationData::AnimateNone);
448 animation->setProperty(CSSPropertyInvalid);
450 animation->setAnimationMode(CSSAnimationData::AnimateSingleProperty);
451 animation->setProperty(primitiveValue->getPropertyID());
455 PassRefPtr<TimingFunction> CSSToStyleMap::animationTimingFunction(CSSValue* value, bool allowInitial)
457 if (allowInitial && value->isInitialValue()) {
458 return CSSAnimationData::initialAnimationTimingFunction();
461 if (value->isPrimitiveValue()) {
462 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
463 switch (primitiveValue->getValueID()) {
465 return LinearTimingFunction::shared();
468 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease);
471 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
473 case CSSValueEaseOut:
474 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
476 case CSSValueEaseInOut:
477 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut);
479 case CSSValueStepStart:
480 return StepsTimingFunction::preset(StepsTimingFunction::Start);
482 case CSSValueStepMiddle:
483 return StepsTimingFunction::preset(StepsTimingFunction::Middle);
485 case CSSValueStepEnd:
486 return StepsTimingFunction::preset(StepsTimingFunction::End);
494 if (value->isCubicBezierTimingFunctionValue()) {
495 CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value);
496 return CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2());
497 } else if (value->isStepsTimingFunctionValue()) {
498 CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value);
499 return StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtPosition());
505 void CSSToStyleMap::mapAnimationTimingFunction(CSSAnimationData* animation, CSSValue* value) const
507 RefPtr<TimingFunction> timingFunction = animationTimingFunction(value, true);
508 if (timingFunction) {
509 // Step middle timing functions are supported up to this point for use in the Web Animations API,
510 // but should not be supported for CSS Animations and Transitions.
511 bool isStepMiddleFunction = (timingFunction->type() == TimingFunction::StepsFunction) && (toStepsTimingFunction(*timingFunction).stepAtPosition() == StepsTimingFunction::StepAtMiddle);
512 if (isStepMiddleFunction)
513 animation->setTimingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease));
515 animation->setTimingFunction(timingFunction);
519 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image)
521 // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
522 if (!value || !value->isValueList())
525 // Retrieve the border image value.
526 CSSValueList* borderImage = toCSSValueList(value);
528 // Set the image (this kicks off the load).
529 CSSPropertyID imageProperty;
530 if (property == CSSPropertyWebkitBorderImage)
531 imageProperty = CSSPropertyBorderImageSource;
532 else if (property == CSSPropertyWebkitMaskBoxImage)
533 imageProperty = CSSPropertyWebkitMaskBoxImageSource;
535 imageProperty = property;
537 for (unsigned i = 0 ; i < borderImage->length() ; ++i) {
538 CSSValue* current = borderImage->item(i);
540 if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue())
541 image.setImage(styleImage(imageProperty, current));
542 else if (current->isBorderImageSliceValue())
543 mapNinePieceImageSlice(current, image);
544 else if (current->isValueList()) {
545 CSSValueList* slashList = toCSSValueList(current);
546 // Map in the image slices.
547 if (slashList->item(0) && slashList->item(0)->isBorderImageSliceValue())
548 mapNinePieceImageSlice(slashList->item(0), image);
550 // Map in the border slices.
551 if (slashList->item(1))
552 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1)));
554 // Map in the outset.
555 if (slashList->item(2))
556 image.setOutset(mapNinePieceImageQuad(slashList->item(2)));
557 } else if (current->isPrimitiveValue()) {
558 // Set the appropriate rules for stretch/round/repeat of the slices.
559 mapNinePieceImageRepeat(current, image);
563 if (property == CSSPropertyWebkitBorderImage) {
564 // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
565 // also set the border widths. We don't need to worry about percentages, since we don't even support
566 // those on real borders yet.
567 if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed())
568 mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value());
569 if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed())
570 mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value());
571 if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed())
572 mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value());
573 if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed())
574 mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value());
578 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const
580 if (!value || !value->isBorderImageSliceValue())
583 // Retrieve the border image value.
584 CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value);
586 // Set up a length box to represent our image slices.
588 Quad* slices = borderImageSlice->slices();
589 if (slices->top()->isPercentage())
590 box.m_top = Length(slices->top()->getDoubleValue(), Percent);
592 box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
593 if (slices->bottom()->isPercentage())
594 box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent);
596 box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
597 if (slices->left()->isPercentage())
598 box.m_left = Length(slices->left()->getDoubleValue(), Percent);
600 box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
601 if (slices->right()->isPercentage())
602 box.m_right = Length(slices->right()->getDoubleValue(), Percent);
604 box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
605 image.setImageSlices(box);
607 // Set our fill mode.
608 image.setFill(borderImageSlice->m_fill);
611 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData)
613 if (value.isNumber())
614 return value.getDoubleValue();
615 if (value.isPercentage())
616 return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
617 if (value.getValueID() != CSSValueAuto)
618 return value.computeLength<Length>(conversionData);
622 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const
624 if (!value || !value->isPrimitiveValue())
625 return BorderImageLengthBox(Length(Auto));
627 float zoom = useSVGZoomRules() ? 1.0f : cssToLengthConversionData().zoom();
628 Quad* slices = toCSSPrimitiveValue(value)->getQuadValue();
630 // Set up a border image length box to represent our image slices.
631 const CSSToLengthConversionData& conversionData = cssToLengthConversionData().copyWithAdjustedZoom(zoom);
632 return BorderImageLengthBox(
633 toBorderImageLength(*slices->top(), conversionData),
634 toBorderImageLength(*slices->right(), conversionData),
635 toBorderImageLength(*slices->bottom(), conversionData),
636 toBorderImageLength(*slices->left(), conversionData));
639 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const
641 if (!value || !value->isPrimitiveValue())
644 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
645 Pair* pair = primitiveValue->getPairValue();
646 if (!pair || !pair->first() || !pair->second())
649 CSSValueID firstIdentifier = pair->first()->getValueID();
650 CSSValueID secondIdentifier = pair->second()->getValueID();
652 ENinePieceImageRule horizontalRule;
653 switch (firstIdentifier) {
654 case CSSValueStretch:
655 horizontalRule = StretchImageRule;
658 horizontalRule = RoundImageRule;
661 horizontalRule = SpaceImageRule;
663 default: // CSSValueRepeat
664 horizontalRule = RepeatImageRule;
667 image.setHorizontalRule(horizontalRule);
669 ENinePieceImageRule verticalRule;
670 switch (secondIdentifier) {
671 case CSSValueStretch:
672 verticalRule = StretchImageRule;
675 verticalRule = RoundImageRule;
678 verticalRule = SpaceImageRule;
680 default: // CSSValueRepeat
681 verticalRule = RepeatImageRule;
684 image.setVerticalRule(verticalRule);