blob: a8af005e0ef5624efb63d52f20836cabb3b7b65d [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 "third_party/blink/renderer/core/css/resolver/css_to_style_map.h"
#include "third_party/blink/renderer/core/animation/css/css_animation_data.h"
#include "third_party/blink/renderer/core/css/css_border_image_slice_value.h"
#include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
#include "third_party/blink/renderer/core/css/css_quad_value.h"
#include "third_party/blink/renderer/core/css/css_timing_function_value.h"
#include "third_party/blink/renderer/core/css/css_value_pair.h"
#include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/frame/deprecation.h"
#include "third_party/blink/renderer/core/style/border_image_length_box.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/fill_layer.h"
namespace blink {
void CSSToStyleMap::MapFillAttachment(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetAttachment(FillLayer::InitialFillAttachment(layer->GetType()));
return;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
switch (identifier_value->GetValueID()) {
case CSSValueID::kFixed:
layer->SetAttachment(EFillAttachment::kFixed);
break;
case CSSValueID::kScroll:
layer->SetAttachment(EFillAttachment::kScroll);
break;
case CSSValueID::kLocal:
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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
layer->SetBlendMode(identifier_value->ConvertTo<BlendMode>());
}
void CSSToStyleMap::MapFillOrigin(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetOrigin(FillLayer::InitialFillOrigin(layer->GetType()));
return;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
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
? CSSPropertyID::kBackgroundImage
: CSSPropertyID::kWebkitMaskImage;
layer->SetImage(state.GetStyleImage(
property, state.ResolveLightDarkPair(CSSProperty::Get(property), value)));
}
void CSSToStyleMap::MapFillRepeatX(StyleResolverState&,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetRepeatX(FillLayer::InitialFillRepeatX(layer->GetType()));
return;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value)
return;
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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value && !value.IsPrimitiveValue() && !value.IsValuePair())
return;
if (identifier_value &&
identifier_value->GetValueID() == CSSValueID::kContain)
layer->SetSizeType(EFillSizeType::kContain);
else if (identifier_value &&
identifier_value->GetValueID() == CSSValueID::kCover)
layer->SetSizeType(EFillSizeType::kCover);
else
layer->SetSizeType(EFillSizeType::kSizeLength);
LengthSize b = FillLayer::InitialFillSizeLength(layer->GetType());
if (identifier_value &&
(identifier_value->GetValueID() == CSSValueID::kContain ||
identifier_value->GetValueID() == CSSValueID::kCover)) {
layer->SetSizeLength(b);
return;
}
Length first_length;
Length second_length;
if (const auto* pair = DynamicTo<CSSValuePair>(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;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value && !value.IsPrimitiveValue() && !value.IsValuePair())
return;
Length length;
auto* pair = DynamicTo<CSSValuePair>(value);
if (pair)
length = To<CSSPrimitiveValue>(pair->Second())
.ConvertToLength(state.CssToLengthConversionData());
else
length = StyleBuilderConverter::ConvertPositionLength<CSSValueID::kLeft,
CSSValueID::kRight>(
state, value);
layer->SetPositionX(length);
if (pair) {
layer->SetBackgroundXOrigin(To<CSSIdentifierValue>(pair->First())
.ConvertTo<BackgroundEdgeOrigin>());
}
}
void CSSToStyleMap::MapFillPositionY(StyleResolverState& state,
FillLayer* layer,
const CSSValue& value) {
if (value.IsInitialValue()) {
layer->SetPositionY(FillLayer::InitialFillPositionY(layer->GetType()));
return;
}
const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (!identifier_value && !value.IsPrimitiveValue() && !value.IsValuePair())
return;
Length length;
auto* pair = DynamicTo<CSSValuePair>(value);
if (pair)
length = To<CSSPrimitiveValue>(pair->Second())
.ConvertToLength(state.CssToLengthConversionData());
else
length = StyleBuilderConverter::ConvertPositionLength<CSSValueID::kTop,
CSSValueID::kBottom>(
state, value);
layer->SetPositionY(length);
if (pair) {
layer->SetBackgroundYOrigin(To<CSSIdentifierValue>(pair->First())
.ConvertTo<BackgroundEdgeOrigin>());
}
}
double CSSToStyleMap::MapAnimationDelay(const CSSValue& value) {
if (value.IsInitialValue())
return CSSTimingData::InitialDelay();
return To<CSSPrimitiveValue>(value).ComputeSeconds();
}
Timing::PlaybackDirection CSSToStyleMap::MapAnimationDirection(
const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialDirection();
switch (To<CSSIdentifierValue>(value).GetValueID()) {
case CSSValueID::kNormal:
return Timing::PlaybackDirection::NORMAL;
case CSSValueID::kAlternate:
return Timing::PlaybackDirection::ALTERNATE_NORMAL;
case CSSValueID::kReverse:
return Timing::PlaybackDirection::REVERSE;
case CSSValueID::kAlternateReverse:
return Timing::PlaybackDirection::ALTERNATE_REVERSE;
default:
NOTREACHED();
return CSSAnimationData::InitialDirection();
}
}
double CSSToStyleMap::MapAnimationDuration(const CSSValue& value) {
if (value.IsInitialValue())
return CSSTimingData::InitialDuration();
return To<CSSPrimitiveValue>(value).ComputeSeconds();
}
Timing::FillMode CSSToStyleMap::MapAnimationFillMode(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialFillMode();
switch (To<CSSIdentifierValue>(value).GetValueID()) {
case CSSValueID::kNone:
return Timing::FillMode::NONE;
case CSSValueID::kForwards:
return Timing::FillMode::FORWARDS;
case CSSValueID::kBackwards:
return Timing::FillMode::BACKWARDS;
case CSSValueID::kBoth:
return Timing::FillMode::BOTH;
default:
NOTREACHED();
return CSSAnimationData::InitialFillMode();
}
}
double CSSToStyleMap::MapAnimationIterationCount(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialIterationCount();
auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
if (identifier_value &&
identifier_value->GetValueID() == CSSValueID::kInfinite)
return std::numeric_limits<double>::infinity();
return To<CSSPrimitiveValue>(value).GetFloatValue();
}
AtomicString CSSToStyleMap::MapAnimationName(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialName();
if (auto* custom_ident_value = DynamicTo<CSSCustomIdentValue>(value))
return AtomicString(custom_ident_value->Value());
DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNone);
return CSSAnimationData::InitialName();
}
StyleNameOrKeyword CSSToStyleMap::MapAnimationTimeline(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialTimeline();
if (auto* ident = DynamicTo<CSSIdentifierValue>(value)) {
DCHECK(ident->GetValueID() == CSSValueID::kAuto ||
ident->GetValueID() == CSSValueID::kNone);
return StyleNameOrKeyword(ident->GetValueID());
}
if (auto* custom_ident = DynamicTo<CSSCustomIdentValue>(value)) {
return StyleNameOrKeyword(
StyleName(custom_ident->Value(), StyleName::Type::kCustomIdent));
}
return StyleNameOrKeyword(
StyleName(AtomicString(To<CSSStringValue>(value).Value()),
StyleName::Type::kString));
}
EAnimPlayState CSSToStyleMap::MapAnimationPlayState(const CSSValue& value) {
if (value.IsInitialValue())
return CSSAnimationData::InitialPlayState();
if (To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kPaused)
return EAnimPlayState::kPaused;
DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kRunning);
return EAnimPlayState::kPlaying;
}
CSSTransitionData::TransitionProperty CSSToStyleMap::MapAnimationProperty(
const CSSValue& value) {
if (value.IsInitialValue())
return CSSTransitionData::InitialProperty();
if (const auto* custom_ident_value = DynamicTo<CSSCustomIdentValue>(value)) {
if (custom_ident_value->IsKnownPropertyID()) {
return CSSTransitionData::TransitionProperty(
custom_ident_value->ValueAsPropertyID());
}
return CSSTransitionData::TransitionProperty(custom_ident_value->Value());
}
if (To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kAll)
return CSSTransitionData::InitialProperty();
DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNone);
return CSSTransitionData::TransitionProperty(
CSSTransitionData::kTransitionNone);
}
scoped_refptr<TimingFunction> CSSToStyleMap::MapAnimationTimingFunction(
const CSSValue& value) {
// 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 (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
switch (identifier_value->GetValueID()) {
case CSSValueID::kLinear:
return LinearTimingFunction::Shared();
case CSSValueID::kEase:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE);
case CSSValueID::kEaseIn:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_IN);
case CSSValueID::kEaseOut:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_OUT);
case CSSValueID::kEaseInOut:
return CubicBezierTimingFunction::Preset(
CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
case CSSValueID::kStepStart:
return StepsTimingFunction::Preset(
StepsTimingFunction::StepPosition::START);
case CSSValueID::kStepEnd:
return StepsTimingFunction::Preset(
StepsTimingFunction::StepPosition::END);
default:
NOTREACHED();
return CSSTimingData::InitialTimingFunction();
}
}
if (const auto* cubic_timing_function =
DynamicTo<cssvalue::CSSCubicBezierTimingFunctionValue>(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();
const auto& steps_timing_function =
To<cssvalue::CSSStepsTimingFunctionValue>(value);
return StepsTimingFunction::Create(steps_timing_function.NumberOfSteps(),
steps_timing_function.GetStepPosition());
}
void CSSToStyleMap::MapNinePieceImage(StyleResolverState& state,
CSSPropertyID property,
const CSSValue& value,
NinePieceImage& image) {
// Retrieve the border image value.
const auto* border_image = DynamicTo<CSSValueList>(value);
// If we're not a value list, then we are "none" and don't need to alter the
// empty image at all.
if (!border_image)
return;
// Set the image (this kicks off the load).
CSSPropertyID image_property;
if (property == CSSPropertyID::kWebkitBorderImage)
image_property = CSSPropertyID::kBorderImageSource;
else if (property == CSSPropertyID::kWebkitMaskBoxImage)
image_property = CSSPropertyID::kWebkitMaskBoxImageSource;
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 (const auto* slash_list = DynamicTo<CSSValueList>(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 == CSSPropertyID::kWebkitBorderImage) {
// 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::Percent(value.GetDoubleValue());
return Length::Fixed(round(value.GetDoubleValue()));
}
void CSSToStyleMap::MapNinePieceImageSlice(StyleResolverState&,
const CSSValue& value,
NinePieceImage& image) {
if (!IsA<cssvalue::CSSBorderImageSliceValue>(value))
return;
// Retrieve the border image value.
const auto& border_image_slice =
To<cssvalue::CSSBorderImageSliceValue>(value);
// Set up a length box to represent our image slices.
LengthBox box;
const CSSQuadValue& slices = border_image_slice.Slices();
box.top_ = ConvertBorderImageSliceSide(To<CSSPrimitiveValue>(*slices.Top()));
box.bottom_ =
ConvertBorderImageSliceSide(To<CSSPrimitiveValue>(*slices.Bottom()));
box.left_ =
ConvertBorderImageSliceSide(To<CSSPrimitiveValue>(*slices.Left()));
box.right_ =
ConvertBorderImageSliceSide(To<CSSPrimitiveValue>(*slices.Right()));
image.SetImageSlices(box);
// Set our fill mode.
image.SetFill(border_image_slice.Fill());
}
static BorderImageLength ToBorderImageLength(const StyleResolverState& state,
const CSSValue& value) {
if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) {
if (primitive_value->IsNumber())
return primitive_value->GetDoubleValue();
}
return StyleBuilderConverter::ConvertLengthOrAuto(state, value);
}
BorderImageLengthBox CSSToStyleMap::MapNinePieceImageQuad(
StyleResolverState& state,
const CSSValue& value) {
const auto* slices = DynamicTo<CSSQuadValue>(value);
if (!slices)
return BorderImageLengthBox(Length::Auto());
// Set up a border image length box to represent our image slices.
return BorderImageLengthBox(ToBorderImageLength(state, *slices->Top()),
ToBorderImageLength(state, *slices->Right()),
ToBorderImageLength(state, *slices->Bottom()),
ToBorderImageLength(state, *slices->Left()));
}
void CSSToStyleMap::MapNinePieceImageRepeat(StyleResolverState&,
const CSSValue& value,
NinePieceImage& image) {
const auto* pair = DynamicTo<CSSValuePair>(value);
if (!pair)
return;
CSSValueID first_identifier =
To<CSSIdentifierValue>(pair->First()).GetValueID();
CSSValueID second_identifier =
To<CSSIdentifierValue>(pair->Second()).GetValueID();
ENinePieceImageRule horizontal_rule;
switch (first_identifier) {
case CSSValueID::kStretch:
horizontal_rule = kStretchImageRule;
break;
case CSSValueID::kRound:
horizontal_rule = kRoundImageRule;
break;
case CSSValueID::kSpace:
horizontal_rule = kSpaceImageRule;
break;
default: // CSSValueID::kRepeat
horizontal_rule = kRepeatImageRule;
break;
}
image.SetHorizontalRule(horizontal_rule);
ENinePieceImageRule vertical_rule;
switch (second_identifier) {
case CSSValueID::kStretch:
vertical_rule = kStretchImageRule;
break;
case CSSValueID::kRound:
vertical_rule = kRoundImageRule;
break;
case CSSValueID::kSpace:
vertical_rule = kSpaceImageRule;
break;
default: // CSSValueID::kRepeat
vertical_rule = kRepeatImageRule;
break;
}
image.SetVerticalRule(vertical_rule);
}
} // namespace blink