blob: f838a9a53db2d25cf94083bea39702a8e0b199db [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
* Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "core/css/resolver/CSSToStyleMap.h"
#include "core/CSSValueKeywords.h"
#include "core/animation/css/CSSAnimationData.h"
#include "core/css/CSSBorderImageSliceValue.h"
#include "core/css/CSSCustomIdentValue.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/CSSPrimitiveValueMappings.h"
#include "core/css/CSSQuadValue.h"
#include "core/css/CSSTimingFunctionValue.h"
#include "core/css/CSSValuePair.h"
#include "core/css/resolver/StyleBuilderConverter.h"
#include "core/css/resolver/StyleResolverState.h"
#include "core/style/BorderImageLengthBox.h"
#include "core/style/FillLayer.h"
namespace blink {
void CSSToStyleMap::mapFillAttachment(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setAttachment(FillLayer::initialFillAttachment(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
switch (primitiveValue.getValueID()) {
case CSSValueFixed:
layer->setAttachment(FixedBackgroundAttachment);
break;
case CSSValueScroll:
layer->setAttachment(ScrollBackgroundAttachment);
break;
case CSSValueLocal:
layer->setAttachment(LocalBackgroundAttachment);
break;
default:
return;
}
}
void CSSToStyleMap::mapFillClip(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setClip(FillLayer::initialFillClip(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setClip(primitiveValue.convertTo<EFillBox>());
}
void CSSToStyleMap::mapFillComposite(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setComposite(FillLayer::initialFillComposite(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setComposite(primitiveValue.convertTo<CompositeOperator>());
}
void CSSToStyleMap::mapFillBlendMode(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setBlendMode(FillLayer::initialFillBlendMode(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setBlendMode(primitiveValue.convertTo<WebBlendMode>());
}
void CSSToStyleMap::mapFillOrigin(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setOrigin(FillLayer::initialFillOrigin(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setOrigin(primitiveValue.convertTo<EFillBox>());
}
void CSSToStyleMap::mapFillImage(StyleResolverState& state, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setImage(FillLayer::initialFillImage(layer->type()));
return;
}
CSSPropertyID property = layer->type() == BackgroundFillLayer ? CSSPropertyBackgroundImage : CSSPropertyWebkitMaskImage;
layer->setImage(state.styleImage(property, value));
}
void CSSToStyleMap::mapFillRepeatX(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setRepeatX(FillLayer::initialFillRepeatX(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setRepeatX(primitiveValue.convertTo<EFillRepeat>());
}
void CSSToStyleMap::mapFillRepeatY(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setRepeatY(FillLayer::initialFillRepeatY(layer->type()));
return;
}
if (!value.isPrimitiveValue())
return;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
layer->setRepeatY(primitiveValue.convertTo<EFillRepeat>());
}
void CSSToStyleMap::mapFillSize(StyleResolverState& state, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setSizeType(FillLayer::initialFillSizeType(layer->type()));
layer->setSizeLength(FillLayer::initialFillSizeLength(layer->type()));
return;
}
if (!value.isPrimitiveValue() && !value.isValuePair())
return;
if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).getValueID() == CSSValueContain)
layer->setSizeType(Contain);
else if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).getValueID() == CSSValueCover)
layer->setSizeType(Cover);
else
layer->setSizeType(SizeLength);
LengthSize b = FillLayer::initialFillSizeLength(layer->type());
if (value.isPrimitiveValue() && (toCSSPrimitiveValue(value).getValueID() == CSSValueContain || toCSSPrimitiveValue(value).getValueID() == CSSValueCover)) {
layer->setSizeLength(b);
return;
}
Length firstLength;
Length secondLength;
if (value.isValuePair()) {
const CSSValuePair& pair = toCSSValuePair(value);
firstLength = StyleBuilderConverter::convertLengthOrAuto(state, pair.first());
secondLength = StyleBuilderConverter::convertLengthOrAuto(state, pair.second());
} else {
ASSERT(value.isPrimitiveValue());
firstLength = StyleBuilderConverter::convertLengthOrAuto(state, value);
secondLength = Length();
}
b.setWidth(firstLength);
b.setHeight(secondLength);
layer->setSizeLength(b);
}
void CSSToStyleMap::mapFillXPosition(StyleResolverState& state, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setXPosition(FillLayer::initialFillXPosition(layer->type()));
return;
}
if (!value.isPrimitiveValue() && !value.isValuePair())
return;
Length length;
if (value.isValuePair())
length = toCSSPrimitiveValue(toCSSValuePair(value).second()).convertToLength(state.cssToLengthConversionData());
else
length = toCSSPrimitiveValue(value).convertToLength(state.cssToLengthConversionData());
layer->setXPosition(length);
if (value.isValuePair())
layer->setBackgroundXOrigin(toCSSPrimitiveValue(toCSSValuePair(value).first()).convertTo<BackgroundEdgeOrigin>());
}
void CSSToStyleMap::mapFillYPosition(StyleResolverState& state, FillLayer* layer, const CSSValue& value)
{
if (value.isInitialValue()) {
layer->setYPosition(FillLayer::initialFillYPosition(layer->type()));
return;
}
if (!value.isPrimitiveValue() && !value.isValuePair())
return;
Length length;
if (value.isValuePair())
length = toCSSPrimitiveValue(toCSSValuePair(value).second()).convertToLength(state.cssToLengthConversionData());
else
length = toCSSPrimitiveValue(value).convertToLength(state.cssToLengthConversionData());
layer->setYPosition(length);
if (value.isValuePair())
layer->setBackgroundYOrigin(toCSSPrimitiveValue(toCSSValuePair(value).first()).convertTo<BackgroundEdgeOrigin>());
}
void CSSToStyleMap::mapFillMaskSourceType(StyleResolverState&, FillLayer* layer, const CSSValue& value)
{
EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type());
if (value.isInitialValue()) {
layer->setMaskSourceType(type);
return;
}
if (!value.isPrimitiveValue())
return;
switch (toCSSPrimitiveValue(value).getValueID()) {
case CSSValueAlpha:
type = MaskAlpha;
break;
case CSSValueLuminance:
type = MaskLuminance;
break;
case CSSValueAuto:
break;
default:
ASSERT_NOT_REACHED();
}
layer->setMaskSourceType(type);
}
double CSSToStyleMap::mapAnimationDelay(const CSSValue& value)
{
if (value.isInitialValue())
return CSSTimingData::initialDelay();
return toCSSPrimitiveValue(value).computeSeconds();
}
Timing::PlaybackDirection CSSToStyleMap::mapAnimationDirection(const CSSValue& value)
{
if (value.isInitialValue())
return CSSAnimationData::initialDirection();
switch (toCSSPrimitiveValue(value).getValueID()) {
case CSSValueNormal:
return Timing::PlaybackDirectionNormal;
case CSSValueAlternate:
return Timing::PlaybackDirectionAlternate;
case CSSValueReverse:
return Timing::PlaybackDirectionReverse;
case CSSValueAlternateReverse:
return Timing::PlaybackDirectionAlternateReverse;
default:
ASSERT_NOT_REACHED();
return CSSAnimationData::initialDirection();
}
}
double CSSToStyleMap::mapAnimationDuration(const CSSValue& value)
{
if (value.isInitialValue())
return CSSTimingData::initialDuration();
return toCSSPrimitiveValue(value).computeSeconds();
}
Timing::FillMode CSSToStyleMap::mapAnimationFillMode(const CSSValue& value)
{
if (value.isInitialValue())
return CSSAnimationData::initialFillMode();
switch (toCSSPrimitiveValue(value).getValueID()) {
case CSSValueNone:
return Timing::FillModeNone;
case CSSValueForwards:
return Timing::FillModeForwards;
case CSSValueBackwards:
return Timing::FillModeBackwards;
case CSSValueBoth:
return Timing::FillModeBoth;
default:
ASSERT_NOT_REACHED();
return CSSAnimationData::initialFillMode();
}
}
double CSSToStyleMap::mapAnimationIterationCount(const CSSValue& value)
{
if (value.isInitialValue())
return CSSAnimationData::initialIterationCount();
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
if (primitiveValue.getValueID() == CSSValueInfinite)
return std::numeric_limits<double>::infinity();
return primitiveValue.getFloatValue();
}
AtomicString CSSToStyleMap::mapAnimationName(const CSSValue& value)
{
if (value.isInitialValue())
return CSSAnimationData::initialName();
if (value.isCustomIdentValue())
return AtomicString(toCSSCustomIdentValue(value).value());
ASSERT(toCSSPrimitiveValue(value).getValueID() == CSSValueNone);
return CSSAnimationData::initialName();
}
EAnimPlayState CSSToStyleMap::mapAnimationPlayState(const CSSValue& value)
{
if (value.isInitialValue())
return CSSAnimationData::initialPlayState();
if (toCSSPrimitiveValue(value).getValueID() == CSSValuePaused)
return AnimPlayStatePaused;
ASSERT(toCSSPrimitiveValue(value).getValueID() == CSSValueRunning);
return AnimPlayStatePlaying;
}
CSSTransitionData::TransitionProperty CSSToStyleMap::mapAnimationProperty(const CSSValue& value)
{
if (value.isInitialValue())
return CSSTransitionData::initialProperty();
if (value.isCustomIdentValue()) {
const CSSCustomIdentValue& customIdentValue = toCSSCustomIdentValue(value);
if (customIdentValue.isKnownPropertyID())
return CSSTransitionData::TransitionProperty(customIdentValue.valueAsPropertyID());
return CSSTransitionData::TransitionProperty(customIdentValue.value());
}
ASSERT(toCSSPrimitiveValue(value).getValueID() == CSSValueNone);
return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionNone);
}
PassRefPtr<TimingFunction> CSSToStyleMap::mapAnimationTimingFunction(const CSSValue& value, bool allowStepMiddle)
{
// FIXME: We should probably only call into this function with a valid
// single timing function value which isn't initial or inherit. We can
// currently get into here with initial since the parser expands unset
// properties in shorthands to initial.
if (value.isPrimitiveValue()) {
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
switch (primitiveValue.getValueID()) {
case CSSValueLinear:
return LinearTimingFunction::shared();
case CSSValueEase:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease);
case CSSValueEaseIn:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
case CSSValueEaseOut:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
case CSSValueEaseInOut:
return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut);
case CSSValueStepStart:
return StepsTimingFunction::preset(StepsTimingFunction::Start);
case CSSValueStepMiddle:
if (allowStepMiddle)
return StepsTimingFunction::preset(StepsTimingFunction::Middle);
return CSSTimingData::initialTimingFunction();
case CSSValueStepEnd:
return StepsTimingFunction::preset(StepsTimingFunction::End);
default:
ASSERT_NOT_REACHED();
return CSSTimingData::initialTimingFunction();
}
}
if (value.isCubicBezierTimingFunctionValue()) {
const CSSCubicBezierTimingFunctionValue& cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value);
return CubicBezierTimingFunction::create(cubicTimingFunction.x1(), cubicTimingFunction.y1(), cubicTimingFunction.x2(), cubicTimingFunction.y2());
}
if (value.isInitialValue())
return CSSTimingData::initialTimingFunction();
const CSSStepsTimingFunctionValue& stepsTimingFunction = toCSSStepsTimingFunctionValue(value);
if (stepsTimingFunction.stepAtPosition() == StepsTimingFunction::Middle && !allowStepMiddle)
return CSSTimingData::initialTimingFunction();
return StepsTimingFunction::create(stepsTimingFunction.numberOfSteps(), stepsTimingFunction.stepAtPosition());
}
void CSSToStyleMap::mapNinePieceImage(StyleResolverState& state, CSSPropertyID property, const CSSValue& value, NinePieceImage& image)
{
// If we're not a value list, then we are "none" and don't need to alter the empty image at all.
if (!value.isValueList())
return;
// Retrieve the border image value.
const CSSValueList& borderImage = toCSSValueList(value);
// Set the image (this kicks off the load).
CSSPropertyID imageProperty;
if (property == CSSPropertyWebkitBorderImage)
imageProperty = CSSPropertyBorderImageSource;
else if (property == CSSPropertyWebkitMaskBoxImage)
imageProperty = CSSPropertyWebkitMaskBoxImageSource;
else
imageProperty = property;
for (unsigned i = 0 ; i < borderImage.length() ; ++i) {
const CSSValue& current = *borderImage.item(i);
if (current.isImageValue() || current.isImageGeneratorValue() || current.isImageSetValue()) {
image.setImage(state.styleImage(imageProperty, current));
} else if (current.isBorderImageSliceValue()) {
mapNinePieceImageSlice(state, current, image);
} else if (current.isValueList()) {
const CSSValueList& slashList = toCSSValueList(current);
size_t length = slashList.length();
// Map in the image slices.
if (length && slashList.item(0)->isBorderImageSliceValue())
mapNinePieceImageSlice(state, *slashList.item(0), image);
// Map in the border slices.
if (length > 1)
image.setBorderSlices(mapNinePieceImageQuad(state, *slashList.item(1)));
// Map in the outset.
if (length > 2)
image.setOutset(mapNinePieceImageQuad(state, *slashList.item(2)));
} else if (current.isPrimitiveValue() || current.isValuePair()) {
// Set the appropriate rules for stretch/round/repeat of the slices.
mapNinePieceImageRepeat(state, current, image);
}
}
if (property == CSSPropertyWebkitBorderImage) {
// We have to preserve the legacy behavior of -webkit-border-image and make the border slices
// also set the border widths. We don't need to worry about percentages, since we don't even support
// those on real borders yet.
if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed())
state.style()->setBorderTopWidth(image.borderSlices().top().length().value());
if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed())
state.style()->setBorderRightWidth(image.borderSlices().right().length().value());
if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed())
state.style()->setBorderBottomWidth(image.borderSlices().bottom().length().value());
if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed())
state.style()->setBorderLeftWidth(image.borderSlices().left().length().value());
}
}
void CSSToStyleMap::mapNinePieceImageSlice(StyleResolverState&, const CSSValue& value, NinePieceImage& image)
{
if (!value.isBorderImageSliceValue())
return;
// Retrieve the border image value.
const CSSBorderImageSliceValue& borderImageSlice = toCSSBorderImageSliceValue(value);
// Set up a length box to represent our image slices.
LengthBox box;
CSSQuadValue* slices = borderImageSlice.slices();
if (slices->top()->isPercentage())
box.m_top = Length(slices->top()->getDoubleValue(), Percent);
else
box.m_top = Length(slices->top()->getIntValue(), Fixed);
if (slices->bottom()->isPercentage())
box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent);
else
box.m_bottom = Length(slices->bottom()->getIntValue(), Fixed);
if (slices->left()->isPercentage())
box.m_left = Length(slices->left()->getDoubleValue(), Percent);
else
box.m_left = Length(slices->left()->getIntValue(), Fixed);
if (slices->right()->isPercentage())
box.m_right = Length(slices->right()->getDoubleValue(), Percent);
else
box.m_right = Length(slices->right()->getIntValue(), Fixed);
image.setImageSlices(box);
// Set our fill mode.
image.setFill(borderImageSlice.m_fill);
}
static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData)
{
if (value.isNumber())
return value.getDoubleValue();
if (value.isPercentage())
return Length(value.getDoubleValue(), Percent);
if (value.getValueID() != CSSValueAuto)
return value.computeLength<Length>(conversionData);
return Length(Auto);
}
BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(StyleResolverState& state, const CSSValue& value)
{
if (!value.isQuadValue())
return BorderImageLengthBox(Length(Auto));
const CSSQuadValue& slices = toCSSQuadValue(value);
// Set up a border image length box to represent our image slices.
return BorderImageLengthBox(
toBorderImageLength(*slices.top(), state.cssToLengthConversionData()),
toBorderImageLength(*slices.right(), state.cssToLengthConversionData()),
toBorderImageLength(*slices.bottom(), state.cssToLengthConversionData()),
toBorderImageLength(*slices.left(), state.cssToLengthConversionData()));
}
void CSSToStyleMap::mapNinePieceImageRepeat(StyleResolverState&, const CSSValue& value, NinePieceImage& image)
{
if (!value.isValuePair())
return;
const CSSValuePair& pair = toCSSValuePair(value);
CSSValueID firstIdentifier = toCSSPrimitiveValue(pair.first()).getValueID();
CSSValueID secondIdentifier = toCSSPrimitiveValue(pair.second()).getValueID();
ENinePieceImageRule horizontalRule;
switch (firstIdentifier) {
case CSSValueStretch:
horizontalRule = StretchImageRule;
break;
case CSSValueRound:
horizontalRule = RoundImageRule;
break;
case CSSValueSpace:
horizontalRule = SpaceImageRule;
break;
default: // CSSValueRepeat
horizontalRule = RepeatImageRule;
break;
}
image.setHorizontalRule(horizontalRule);
ENinePieceImageRule verticalRule;
switch (secondIdentifier) {
case CSSValueStretch:
verticalRule = StretchImageRule;
break;
case CSSValueRound:
verticalRule = RoundImageRule;
break;
case CSSValueSpace:
verticalRule = SpaceImageRule;
break;
default: // CSSValueRepeat
verticalRule = RepeatImageRule;
break;
}
image.setVerticalRule(verticalRule);
}
};