blob: 0b18cb75dd13d8ee4aef88a93f79cd0a30a9af12 [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 "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/frame/Deprecation.h"
#include "core/style/BorderImageLengthBox.h"
#include "core/style/ComputedStyle.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->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
switch (identifier_value.GetValueID()) {
case CSSValueFixed:
layer->SetAttachment(EFillAttachment::kFixed);
break;
case CSSValueScroll:
layer->SetAttachment(EFillAttachment::kScroll);
break;
case CSSValueLocal:
layer->SetAttachment(EFillAttachment::kLocal);
break;
default:
return;
}
}
void CSSToStyleMap::MapFillClip(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetClip(FillLayer::InitialFillClip(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetClip(identifier_value.ConvertTo<EFillBox>());
}
void CSSToStyleMap::MapFillComposite(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetComposite(FillLayer::InitialFillComposite(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetComposite(identifier_value.ConvertTo<CompositeOperator>());
}
void CSSToStyleMap::MapFillBlendMode(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetBlendMode(FillLayer::InitialFillBlendMode(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetBlendMode(identifier_value.ConvertTo<WebBlendMode>());
}
void CSSToStyleMap::MapFillOrigin(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetOrigin(FillLayer::InitialFillOrigin(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetOrigin(identifier_value.ConvertTo<EFillBox>());
}
void CSSToStyleMap::MapFillImage(StyleResolverState& state,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetImage(FillLayer::InitialFillImage(layer->GetType()));
return;
}
CSSPropertyID property = layer->GetType() == EFillLayerType::kBackground
? CSSPropertyBackgroundImage
: CSSPropertyWebkitMaskImage;
layer->SetImage(state.GetStyleImage(property, value));
}
void CSSToStyleMap::MapFillRepeatX(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetRepeatX(FillLayer::InitialFillRepeatX(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetRepeatX(identifier_value.ConvertTo<EFillRepeat>());
}
void CSSToStyleMap::MapFillRepeatY(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetRepeatY(FillLayer::InitialFillRepeatY(layer->GetType()));
return;
}
if (!value.IsIdentifierValue())
return;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
layer->SetRepeatY(identifier_value.ConvertTo<EFillRepeat>());
}
void CSSToStyleMap::MapFillSize(StyleResolverState& state,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetSizeType(FillLayer::InitialFillSizeType(layer->GetType()));
layer->SetSizeLength(FillLayer::InitialFillSizeLength(layer->GetType()));
return;
}
if (!value.IsIdentifierValue() && !value.IsPrimitiveValue() &&
!value.IsValuePair())
return;
if (value.IsIdentifierValue() &&
ToCSSIdentifierValue(value).GetValueID() == CSSValueContain)
layer->SetSizeType(EFillSizeType::kContain);
else if (value.IsIdentifierValue() &&
ToCSSIdentifierValue(value).GetValueID() == CSSValueCover)
layer->SetSizeType(EFillSizeType::kCover);
else
layer->SetSizeType(EFillSizeType::kSizeLength);
LengthSize b = FillLayer::InitialFillSizeLength(layer->GetType());
if (value.IsIdentifierValue() &&
(ToCSSIdentifierValue(value).GetValueID() == CSSValueContain ||
ToCSSIdentifierValue(value).GetValueID() == CSSValueCover)) {
layer->SetSizeLength(b);
return;
}
Length first_length;
Length second_length;
if (value.IsValuePair()) {
const CSSValuePair& pair = ToCSSValuePair(value);
first_length =
StyleBuilderConverter::ConvertLengthOrAuto(state, pair.First());
second_length =
StyleBuilderConverter::ConvertLengthOrAuto(state, pair.Second());
} else {
DCHECK(value.IsPrimitiveValue() || value.IsIdentifierValue());
first_length = StyleBuilderConverter::ConvertLengthOrAuto(state, value);
second_length = Length();
}
b.SetWidth(first_length);
b.SetHeight(second_length);
layer->SetSizeLength(b);
}
void CSSToStyleMap::MapFillPositionX(StyleResolverState& state,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetPositionX(FillLayer::InitialFillPositionX(layer->GetType()));
return;
}
if (!value.IsIdentifierValue() && !value.IsPrimitiveValue() &&
!value.IsValuePair())
return;
Length length;
if (value.IsValuePair())
length = ToCSSPrimitiveValue(ToCSSValuePair(value).Second())
.ConvertToLength(state.CssToLengthConversionData());
else
length = StyleBuilderConverter::ConvertPositionLength<CSSValueLeft,
CSSValueRight>(state,
value);
layer->SetPositionX(length);
if (value.IsValuePair())
layer->SetBackgroundXOrigin(
ToCSSIdentifierValue(ToCSSValuePair(value).First())
.ConvertTo<BackgroundEdgeOrigin>());
}
void CSSToStyleMap::MapFillPositionY(StyleResolverState& state,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetPositionY(FillLayer::InitialFillPositionY(layer->GetType()));
return;
}
if (!value.IsIdentifierValue() && !value.IsPrimitiveValue() &&
!value.IsValuePair())
return;
Length length;
if (value.IsValuePair())
length = ToCSSPrimitiveValue(ToCSSValuePair(value).Second())
.ConvertToLength(state.CssToLengthConversionData());
else
length = StyleBuilderConverter::ConvertPositionLength<CSSValueTop,
CSSValueBottom>(
state, value);
layer->SetPositionY(length);
if (value.IsValuePair())
layer->SetBackgroundYOrigin(
ToCSSIdentifierValue(ToCSSValuePair(value).First())
.ConvertTo<BackgroundEdgeOrigin>());
}
void CSSToStyleMap::MapFillMaskSourceType(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
EMaskSourceType type = FillLayer::InitialFillMaskSourceType(layer->GetType());
if (value.IsInitialValue()) {
layer->SetMaskSourceType(type);
return;
}
if (!value.IsIdentifierValue())
return;
switch (ToCSSIdentifierValue(value).GetValueID()) {
case CSSValueAlpha:
type = EMaskSourceType::kAlpha;
break;
case CSSValueLuminance:
type = EMaskSourceType::kLuminance;
break;
case CSSValueAuto:
break;
default:
NOTREACHED();
}
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 (ToCSSIdentifierValue(value).GetValueID()) {
case CSSValueNormal:
return Timing::PlaybackDirection::NORMAL;
case CSSValueAlternate:
return Timing::PlaybackDirection::ALTERNATE_NORMAL;
case CSSValueReverse:
return Timing::PlaybackDirection::REVERSE;
case CSSValueAlternateReverse:
return Timing::PlaybackDirection::ALTERNATE_REVERSE;
default:
NOTREACHED();
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 (ToCSSIdentifierValue(value).GetValueID()) {
case CSSValueNone:
return Timing::FillMode::NONE;
case CSSValueForwards:
return Timing::FillMode::FORWARDS;
case CSSValueBackwards:
return Timing::FillMode::BACKWARDS;
case CSSValueBoth:
return Timing::FillMode::BOTH;
default:
NOTREACHED();
return CSSAnimationData::InitialFillMode();
}
}
double CSSToStyleMap::MapAnimationIterationCount(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialIterationCount();
if (value.IsIdentifierValue() &&
ToCSSIdentifierValue(value).GetValueID() == CSSValueInfinite)
return std::numeric_limits<double>::infinity();
return ToCSSPrimitiveValue(value).GetFloatValue();
}
AtomicString CSSToStyleMap::MapAnimationName(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialName();
if (value.IsCustomIdentValue())
return AtomicString(ToCSSCustomIdentValue(value).Value());
DCHECK_EQ(ToCSSIdentifierValue(value).GetValueID(), CSSValueNone);
return CSSAnimationData::InitialName();
}
EAnimPlayState CSSToStyleMap::MapAnimationPlayState(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialPlayState();
if (ToCSSIdentifierValue(value).GetValueID() == CSSValuePaused)
return EAnimPlayState::kPaused;
DCHECK_EQ(ToCSSIdentifierValue(value).GetValueID(), CSSValueRunning);
return EAnimPlayState::kPlaying;
}
CSSTransitionData::TransitionProperty CSSToStyleMap::MapAnimationProperty(
const CSSValue& value) {
if (value.IsInitialValue())
return CSSTransitionData::InitialProperty();
if (value.IsCustomIdentValue()) {
const CSSCustomIdentValue& custom_ident_value =
ToCSSCustomIdentValue(value);
if (custom_ident_value.IsKnownPropertyID())
return CSSTransitionData::TransitionProperty(
custom_ident_value.ValueAsPropertyID());
return CSSTransitionData::TransitionProperty(custom_ident_value.Value());
}
DCHECK_EQ(ToCSSIdentifierValue(value).GetValueID(), CSSValueNone);
return CSSTransitionData::TransitionProperty(
CSSTransitionData::kTransitionNone);
}
scoped_refptr<TimingFunction> CSSToStyleMap::MapAnimationTimingFunction(
const CSSValue& value,
bool allow_step_middle,
Document* document) {
// 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.IsIdentifierValue()) {
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
switch (identifier_value.GetValueID()) {
case CSSValueLinear:
return LinearTimingFunction::Shared();
case CSSValueEase:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE);
case CSSValueEaseIn:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_IN);
case CSSValueEaseOut:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_OUT);
case CSSValueEaseInOut:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
case CSSValueStepStart:
return StepsTimingFunction::Preset(
StepsTimingFunction::StepPosition::START);
case CSSValueStepMiddle:
if (allow_step_middle) {
DCHECK(document);
if (document) {
Deprecation::CountDeprecation(
*document, WebFeature::kDeprecatedTimingFunctionStepMiddle);
}
return StepsTimingFunction::Preset(
StepsTimingFunction::StepPosition::MIDDLE);
}
return CSSTimingData::InitialTimingFunction();
case CSSValueStepEnd:
return StepsTimingFunction::Preset(
StepsTimingFunction::StepPosition::END);
default:
NOTREACHED();
return CSSTimingData::InitialTimingFunction();
}
}
if (value.IsCubicBezierTimingFunctionValue()) {
const CSSCubicBezierTimingFunctionValue& cubic_timing_function =
ToCSSCubicBezierTimingFunctionValue(value);
return CubicBezierTimingFunction::Create(
cubic_timing_function.X1(), cubic_timing_function.Y1(),
cubic_timing_function.X2(), cubic_timing_function.Y2());
}
if (value.IsInitialValue())
return CSSTimingData::InitialTimingFunction();
if (value.IsFramesTimingFunctionValue()) {
const CSSFramesTimingFunctionValue& frames_timing_function =
ToCSSFramesTimingFunctionValue(value);
return FramesTimingFunction::Create(
frames_timing_function.NumberOfFrames());
}
const CSSStepsTimingFunctionValue& steps_timing_function =
ToCSSStepsTimingFunctionValue(value);
if (steps_timing_function.GetStepPosition() ==
StepsTimingFunction::StepPosition::MIDDLE) {
if (!allow_step_middle) {
return CSSTimingData::InitialTimingFunction();
}
DCHECK(document);
if (document) {
Deprecation::CountDeprecation(
*document, WebFeature::kDeprecatedTimingFunctionStepMiddle);
}
}
return StepsTimingFunction::Create(steps_timing_function.NumberOfSteps(),
steps_timing_function.GetStepPosition());
}
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& border_image = ToCSSValueList(value);
// Set the image (this kicks off the load).
CSSPropertyID image_property;
if (property == CSSPropertyWebkitBorderImage)
image_property = CSSPropertyBorderImageSource;
else if (property == CSSPropertyWebkitMaskBoxImage)
image_property = CSSPropertyWebkitMaskBoxImageSource;
else
image_property = property;
for (unsigned i = 0; i < border_image.length(); ++i) {
const CSSValue& current = border_image.Item(i);
if (current.IsImageValue() || current.IsImageGeneratorValue() ||
current.IsImageSetValue()) {
image.SetImage(state.GetStyleImage(image_property, current));
} else if (current.IsBorderImageSliceValue()) {
MapNinePieceImageSlice(state, current, image);
} else if (current.IsValueList()) {
const CSSValueList& slash_list = ToCSSValueList(current);
size_t length = slash_list.length();
// Map in the image slices.
if (length && slash_list.Item(0).IsBorderImageSliceValue())
MapNinePieceImageSlice(state, slash_list.Item(0), image);
// Map in the border slices.
if (length > 1)
image.SetBorderSlices(MapNinePieceImageQuad(state, slash_list.Item(1)));
// Map in the outset.
if (length > 2)
image.SetOutset(MapNinePieceImageQuad(state, slash_list.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());
}
}
static Length ConvertBorderImageSliceSide(const CSSPrimitiveValue& value) {
if (value.IsPercentage())
return Length(value.GetDoubleValue(), kPercent);
return Length(round(value.GetDoubleValue()), kFixed);
}
void CSSToStyleMap::MapNinePieceImageSlice(StyleResolverState&,
const CSSValue& value,
NinePieceImage& image) {
if (!value.IsBorderImageSliceValue())
return;
// Retrieve the border image value.
const cssvalue::CSSBorderImageSliceValue& border_image_slice =
cssvalue::ToCSSBorderImageSliceValue(value);
// Set up a length box to represent our image slices.
LengthBox box;
const CSSQuadValue& slices = border_image_slice.Slices();
box.top_ = ConvertBorderImageSliceSide(ToCSSPrimitiveValue(*slices.Top()));
box.bottom_ =
ConvertBorderImageSliceSide(ToCSSPrimitiveValue(*slices.Bottom()));
box.left_ = ConvertBorderImageSliceSide(ToCSSPrimitiveValue(*slices.Left()));
box.right_ =
ConvertBorderImageSliceSide(ToCSSPrimitiveValue(*slices.Right()));
image.SetImageSlices(box);
// Set our fill mode.
image.SetFill(border_image_slice.Fill());
}
static BorderImageLength ToBorderImageLength(
CSSValue& value,
const CSSToLengthConversionData& conversion_data) {
if (value.IsPrimitiveValue()) {
const CSSPrimitiveValue& primitive_value = ToCSSPrimitiveValue(value);
if (primitive_value.IsNumber())
return primitive_value.GetDoubleValue();
if (primitive_value.IsPercentage())
return Length(primitive_value.GetDoubleValue(), kPercent);
return primitive_value.ComputeLength<Length>(conversion_data);
}
DCHECK_EQ(ToCSSIdentifierValue(value).GetValueID(), CSSValueAuto);
return Length(kAuto);
}
BorderImageLengthBox CSSToStyleMap::MapNinePieceImageQuad(
StyleResolverState& state,
const CSSValue& value) {
if (!value.IsQuadValue())
return BorderImageLengthBox(Length(kAuto));
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 first_identifier = ToCSSIdentifierValue(pair.First()).GetValueID();
CSSValueID second_identifier =
ToCSSIdentifierValue(pair.Second()).GetValueID();
ENinePieceImageRule horizontal_rule;
switch (first_identifier) {
case CSSValueStretch:
horizontal_rule = kStretchImageRule;
break;
case CSSValueRound:
horizontal_rule = kRoundImageRule;
break;
case CSSValueSpace:
horizontal_rule = kSpaceImageRule;
break;
default: // CSSValueRepeat
horizontal_rule = kRepeatImageRule;
break;
}
image.SetHorizontalRule(horizontal_rule);
ENinePieceImageRule vertical_rule;
switch (second_identifier) {
case CSSValueStretch:
vertical_rule = kStretchImageRule;
break;
case CSSValueRound:
vertical_rule = kRoundImageRule;
break;
case CSSValueSpace:
vertical_rule = kSpaceImageRule;
break;
default: // CSSValueRepeat
vertical_rule = kRepeatImageRule;
break;
}
image.SetVerticalRule(vertical_rule);
}
} // namespace blink