blob: 4bb143788403c602283a0869ebdc56fc99cd415d [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
* 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, 2013 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.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/CSSPropertyNames.h"
#include "core/CSSValueKeywords.h"
#include "core/StyleBuilderFunctions.h"
#include "core/StylePropertyShorthand.h"
#include "core/animation/css/CSSAnimations.h"
#include "core/css/CSSCounterValue.h"
#include "core/css/CSSCursorImageValue.h"
#include "core/css/CSSCustomPropertyDeclaration.h"
#include "core/css/CSSFunctionValue.h"
#include "core/css/CSSGridTemplateAreasValue.h"
#include "core/css/CSSHelper.h"
#include "core/css/CSSImageSetValue.h"
#include "core/css/CSSPendingSubstitutionValue.h"
#include "core/css/CSSPrimitiveValueMappings.h"
#include "core/css/CSSPropertyMetadata.h"
#include "core/css/CSSVariableReferenceValue.h"
#include "core/css/PropertyRegistration.h"
#include "core/css/PropertyRegistry.h"
#include "core/css/StylePropertySet.h"
#include "core/css/StyleRule.h"
#include "core/css/resolver/CSSVariableResolver.h"
#include "core/css/resolver/ElementStyleResources.h"
#include "core/css/resolver/FilterOperationResolver.h"
#include "core/css/resolver/FontBuilder.h"
#include "core/css/resolver/StyleBuilder.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/style/ComputedStyle.h"
#include "core/style/ComputedStyleConstants.h"
#include "core/style/ContentData.h"
#include "core/style/CounterContent.h"
#include "core/style/QuotesData.h"
#include "core/style/SVGComputedStyle.h"
#include "core/style/StyleGeneratedImage.h"
#include "core/style/StyleInheritedVariables.h"
#include "core/style/StyleNonInheritedVariables.h"
#include "platform/fonts/FontDescription.h"
#include "wtf/MathExtras.h"
#include "wtf/PtrUtil.h"
#include "wtf/StdLibExtras.h"
#include "wtf/Vector.h"
#include <memory>
namespace blink {
namespace {
static inline bool isValidVisitedLinkProperty(CSSPropertyID id) {
switch (id) {
case CSSPropertyBackgroundColor:
case CSSPropertyBorderLeftColor:
case CSSPropertyBorderRightColor:
case CSSPropertyBorderTopColor:
case CSSPropertyBorderBottomColor:
case CSSPropertyCaretColor:
case CSSPropertyColor:
case CSSPropertyFill:
case CSSPropertyOutlineColor:
case CSSPropertyStroke:
case CSSPropertyTextDecorationColor:
case CSSPropertyColumnRuleColor:
case CSSPropertyWebkitTextEmphasisColor:
case CSSPropertyWebkitTextFillColor:
case CSSPropertyWebkitTextStrokeColor:
return true;
default:
return false;
}
}
} // namespace
void StyleBuilder::applyProperty(CSSPropertyID id,
StyleResolverState& state,
const CSSValue& value) {
if (id != CSSPropertyVariable && (value.isVariableReferenceValue() ||
value.isPendingSubstitutionValue())) {
bool omitAnimationTainted = CSSAnimations::isAnimationAffectingProperty(id);
const CSSValue* resolvedValue =
CSSVariableResolver::resolveVariableReferences(state, id, value,
omitAnimationTainted);
applyProperty(id, state, *resolvedValue);
if (!state.style()->hasVariableReferenceFromNonInheritedProperty() &&
!CSSPropertyMetadata::isInheritedProperty(id))
state.style()->setHasVariableReferenceFromNonInheritedProperty();
return;
}
DCHECK(!isShorthandProperty(id)) << "Shorthand property id = " << id
<< " wasn't expanded at parsing time";
bool isInherit = state.parentNode() && value.isInheritedValue();
bool isInitial = value.isInitialValue() ||
(!state.parentNode() && value.isInheritedValue());
// isInherit => !isInitial && isInitial => !isInherit
DCHECK(!isInherit || !isInitial);
// isInherit => (state.parentNode() && state.parentStyle())
DCHECK(!isInherit || (state.parentNode() && state.parentStyle()));
if (!state.applyPropertyToRegularStyle() &&
(!state.applyPropertyToVisitedLinkStyle() ||
!isValidVisitedLinkProperty(id))) {
// Limit the properties that can be applied to only the ones honored by
// :visited.
return;
}
if (isInherit && !state.parentStyle()->hasExplicitlyInheritedProperties() &&
!CSSPropertyMetadata::isInheritedProperty(id)) {
state.parentStyle()->setHasExplicitlyInheritedProperties();
} else if (value.isUnsetValue()) {
DCHECK(!isInherit && !isInitial);
if (CSSPropertyMetadata::isInheritedProperty(id))
isInherit = true;
else
isInitial = true;
}
StyleBuilder::applyProperty(id, state, value, isInitial, isInherit);
}
void StyleBuilderFunctions::applyInitialCSSPropertyColor(
StyleResolverState& state) {
Color color = ComputedStyle::initialColor();
if (state.applyPropertyToRegularStyle())
state.style()->setColor(color);
if (state.applyPropertyToVisitedLinkStyle())
state.style()->setVisitedLinkColor(color);
}
void StyleBuilderFunctions::applyInheritCSSPropertyColor(
StyleResolverState& state) {
Color color = state.parentStyle()->color();
if (state.applyPropertyToRegularStyle())
state.style()->setColor(color);
if (state.applyPropertyToVisitedLinkStyle())
state.style()->setVisitedLinkColor(color);
}
void StyleBuilderFunctions::applyValueCSSPropertyColor(
StyleResolverState& state,
const CSSValue& value) {
// As per the spec, 'color: currentColor' is treated as 'color: inherit'
if (value.isIdentifierValue() &&
toCSSIdentifierValue(value).getValueID() == CSSValueCurrentcolor) {
applyInheritCSSPropertyColor(state);
return;
}
if (state.applyPropertyToRegularStyle())
state.style()->setColor(StyleBuilderConverter::convertColor(state, value));
if (state.applyPropertyToVisitedLinkStyle())
state.style()->setVisitedLinkColor(
StyleBuilderConverter::convertColor(state, value, true));
}
void StyleBuilderFunctions::applyInitialCSSPropertyCursor(
StyleResolverState& state) {
state.style()->clearCursorList();
state.style()->setCursor(ComputedStyle::initialCursor());
}
void StyleBuilderFunctions::applyInheritCSSPropertyCursor(
StyleResolverState& state) {
state.style()->setCursor(state.parentStyle()->cursor());
state.style()->setCursorList(state.parentStyle()->cursors());
}
void StyleBuilderFunctions::applyValueCSSPropertyCursor(
StyleResolverState& state,
const CSSValue& value) {
state.style()->clearCursorList();
if (value.isValueList()) {
state.style()->setCursor(ECursor::kAuto);
for (const auto& item : toCSSValueList(value)) {
if (item->isCursorImageValue()) {
const CSSCursorImageValue& cursor = toCSSCursorImageValue(*item);
const CSSValue& image = cursor.imageValue();
state.style()->addCursor(state.styleImage(CSSPropertyCursor, image),
cursor.hotSpotSpecified(), cursor.hotSpot());
} else {
state.style()->setCursor(
toCSSIdentifierValue(*item).convertTo<ECursor>());
}
}
} else {
state.style()->setCursor(toCSSIdentifierValue(value).convertTo<ECursor>());
}
}
void StyleBuilderFunctions::applyValueCSSPropertyDirection(
StyleResolverState& state,
const CSSValue& value) {
state.style()->setDirection(
toCSSIdentifierValue(value).convertTo<TextDirection>());
}
void StyleBuilderFunctions::applyInitialCSSPropertyGridTemplateAreas(
StyleResolverState& state) {
state.style()->setNamedGridArea(ComputedStyle::initialNamedGridArea());
state.style()->setNamedGridAreaRowCount(
ComputedStyle::initialNamedGridAreaCount());
state.style()->setNamedGridAreaColumnCount(
ComputedStyle::initialNamedGridAreaCount());
}
void StyleBuilderFunctions::applyInheritCSSPropertyGridTemplateAreas(
StyleResolverState& state) {
state.style()->setNamedGridArea(state.parentStyle()->namedGridArea());
state.style()->setNamedGridAreaRowCount(
state.parentStyle()->namedGridAreaRowCount());
state.style()->setNamedGridAreaColumnCount(
state.parentStyle()->namedGridAreaColumnCount());
}
void StyleBuilderFunctions::applyValueCSSPropertyGridTemplateAreas(
StyleResolverState& state,
const CSSValue& value) {
if (value.isIdentifierValue()) {
// FIXME: Shouldn't we clear the grid-area values
DCHECK_EQ(toCSSIdentifierValue(value).getValueID(), CSSValueNone);
return;
}
const CSSGridTemplateAreasValue& gridTemplateAreasValue =
toCSSGridTemplateAreasValue(value);
const NamedGridAreaMap& newNamedGridAreas =
gridTemplateAreasValue.gridAreaMap();
NamedGridLinesMap namedGridColumnLines;
NamedGridLinesMap namedGridRowLines;
StyleBuilderConverter::convertOrderedNamedGridLinesMapToNamedGridLinesMap(
state.style()->orderedNamedGridColumnLines(), namedGridColumnLines);
StyleBuilderConverter::convertOrderedNamedGridLinesMapToNamedGridLinesMap(
state.style()->orderedNamedGridRowLines(), namedGridRowLines);
StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(
newNamedGridAreas, namedGridColumnLines, ForColumns);
StyleBuilderConverter::createImplicitNamedGridLinesFromGridArea(
newNamedGridAreas, namedGridRowLines, ForRows);
state.style()->setNamedGridColumnLines(namedGridColumnLines);
state.style()->setNamedGridRowLines(namedGridRowLines);
state.style()->setNamedGridArea(newNamedGridAreas);
state.style()->setNamedGridAreaRowCount(gridTemplateAreasValue.rowCount());
state.style()->setNamedGridAreaColumnCount(
gridTemplateAreasValue.columnCount());
}
void StyleBuilderFunctions::applyValueCSSPropertyListStyleImage(
StyleResolverState& state,
const CSSValue& value) {
state.style()->setListStyleImage(
state.styleImage(CSSPropertyListStyleImage, value));
}
void StyleBuilderFunctions::applyInitialCSSPropertyOutlineStyle(
StyleResolverState& state) {
state.style()->setOutlineStyleIsAuto(
ComputedStyle::initialOutlineStyleIsAuto());
state.style()->setOutlineStyle(ComputedStyle::initialBorderStyle());
}
void StyleBuilderFunctions::applyInheritCSSPropertyOutlineStyle(
StyleResolverState& state) {
state.style()->setOutlineStyleIsAuto(
state.parentStyle()->outlineStyleIsAuto());
state.style()->setOutlineStyle(state.parentStyle()->outlineStyle());
}
void StyleBuilderFunctions::applyValueCSSPropertyOutlineStyle(
StyleResolverState& state,
const CSSValue& value) {
const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(value);
state.style()->setOutlineStyleIsAuto(
identifierValue.convertTo<OutlineIsAuto>());
state.style()->setOutlineStyle(identifierValue.convertTo<EBorderStyle>());
}
void StyleBuilderFunctions::applyValueCSSPropertyResize(
StyleResolverState& state,
const CSSValue& value) {
const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(value);
EResize r = RESIZE_NONE;
if (identifierValue.getValueID() == CSSValueAuto) {
if (Settings* settings = state.document().settings())
r = settings->getTextAreasAreResizable() ? RESIZE_BOTH : RESIZE_NONE;
} else {
r = identifierValue.convertTo<EResize>();
}
state.style()->setResize(r);
}
static float mmToPx(float mm) {
return mm * cssPixelsPerMillimeter;
}
static float inchToPx(float inch) {
return inch * cssPixelsPerInch;
}
static FloatSize getPageSizeFromName(const CSSIdentifierValue& pageSizeName) {
switch (pageSizeName.getValueID()) {
case CSSValueA5:
return FloatSize(mmToPx(148), mmToPx(210));
case CSSValueA4:
return FloatSize(mmToPx(210), mmToPx(297));
case CSSValueA3:
return FloatSize(mmToPx(297), mmToPx(420));
case CSSValueB5:
return FloatSize(mmToPx(176), mmToPx(250));
case CSSValueB4:
return FloatSize(mmToPx(250), mmToPx(353));
case CSSValueLetter:
return FloatSize(inchToPx(8.5), inchToPx(11));
case CSSValueLegal:
return FloatSize(inchToPx(8.5), inchToPx(14));
case CSSValueLedger:
return FloatSize(inchToPx(11), inchToPx(17));
default:
NOTREACHED();
return FloatSize(0, 0);
}
}
void StyleBuilderFunctions::applyInitialCSSPropertySize(StyleResolverState&) {}
void StyleBuilderFunctions::applyInheritCSSPropertySize(StyleResolverState&) {}
void StyleBuilderFunctions::applyValueCSSPropertySize(StyleResolverState& state,
const CSSValue& value) {
state.style()->resetPageSizeType();
FloatSize size;
PageSizeType pageSizeType = PAGE_SIZE_AUTO;
const CSSValueList& list = toCSSValueList(value);
if (list.length() == 2) {
// <length>{2} | <page-size> <orientation>
const CSSValue& first = list.item(0);
const CSSValue& second = list.item(1);
if (first.isPrimitiveValue() && toCSSPrimitiveValue(first).isLength()) {
// <length>{2}
size = FloatSize(
toCSSPrimitiveValue(first).computeLength<float>(
state.cssToLengthConversionData().copyWithAdjustedZoom(1.0)),
toCSSPrimitiveValue(second).computeLength<float>(
state.cssToLengthConversionData().copyWithAdjustedZoom(1.0)));
} else {
// <page-size> <orientation>
size = getPageSizeFromName(toCSSIdentifierValue(first));
DCHECK(toCSSIdentifierValue(second).getValueID() == CSSValueLandscape ||
toCSSIdentifierValue(second).getValueID() == CSSValuePortrait);
if (toCSSIdentifierValue(second).getValueID() == CSSValueLandscape)
size = size.transposedSize();
}
pageSizeType = PAGE_SIZE_RESOLVED;
} else {
DCHECK_EQ(list.length(), 1U);
// <length> | auto | <page-size> | [ portrait | landscape]
const CSSValue& first = list.item(0);
if (first.isPrimitiveValue() && toCSSPrimitiveValue(first).isLength()) {
// <length>
pageSizeType = PAGE_SIZE_RESOLVED;
float width = toCSSPrimitiveValue(first).computeLength<float>(
state.cssToLengthConversionData().copyWithAdjustedZoom(1.0));
size = FloatSize(width, width);
} else {
const CSSIdentifierValue& ident = toCSSIdentifierValue(first);
switch (ident.getValueID()) {
case CSSValueAuto:
pageSizeType = PAGE_SIZE_AUTO;
break;
case CSSValuePortrait:
pageSizeType = PAGE_SIZE_AUTO_PORTRAIT;
break;
case CSSValueLandscape:
pageSizeType = PAGE_SIZE_AUTO_LANDSCAPE;
break;
default:
// <page-size>
pageSizeType = PAGE_SIZE_RESOLVED;
size = getPageSizeFromName(ident);
}
}
}
state.style()->setPageSizeType(pageSizeType);
state.style()->setPageSize(size);
}
void StyleBuilderFunctions::applyInitialCSSPropertySnapHeight(
StyleResolverState& state) {
state.style()->setSnapHeightUnit(0);
state.style()->setSnapHeightPosition(0);
}
void StyleBuilderFunctions::applyInheritCSSPropertySnapHeight(
StyleResolverState& state) {
state.style()->setSnapHeightUnit(state.parentStyle()->snapHeightUnit());
state.style()->setSnapHeightPosition(
state.parentStyle()->snapHeightPosition());
}
void StyleBuilderFunctions::applyValueCSSPropertySnapHeight(
StyleResolverState& state,
const CSSValue& value) {
const CSSValueList& list = toCSSValueList(value);
const CSSPrimitiveValue& first = toCSSPrimitiveValue(list.item(0));
DCHECK(first.isLength());
int unit = first.computeLength<int>(state.cssToLengthConversionData());
DCHECK_GE(unit, 0);
state.style()->setSnapHeightUnit(clampTo<uint8_t>(unit));
if (list.length() == 1) {
state.style()->setSnapHeightPosition(0);
return;
}
DCHECK_EQ(list.length(), 2U);
const CSSPrimitiveValue& second = toCSSPrimitiveValue(list.item(1));
DCHECK(second.isNumber());
int position = second.getIntValue();
DCHECK(position > 0 && position <= 100);
state.style()->setSnapHeightPosition(position);
}
void StyleBuilderFunctions::applyValueCSSPropertyTextAlign(
StyleResolverState& state,
const CSSValue& value) {
if (value.isIdentifierValue() &&
toCSSIdentifierValue(value).getValueID() != CSSValueWebkitMatchParent) {
// Special case for th elements - UA stylesheet text-align does not apply if
// parent's computed value for text-align is not its initial value
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2
const CSSIdentifierValue& identValue = toCSSIdentifierValue(value);
if (identValue.getValueID() == CSSValueInternalCenter &&
state.parentStyle()->textAlign() != ComputedStyle::initialTextAlign())
state.style()->setTextAlign(state.parentStyle()->textAlign());
else
state.style()->setTextAlign(identValue.convertTo<ETextAlign>());
} else if (state.parentStyle()->textAlign() == ETextAlign::kStart) {
state.style()->setTextAlign(state.parentStyle()->isLeftToRightDirection()
? ETextAlign::kLeft
: ETextAlign::kRight);
} else if (state.parentStyle()->textAlign() == ETextAlign::kEnd) {
state.style()->setTextAlign(state.parentStyle()->isLeftToRightDirection()
? ETextAlign::kRight
: ETextAlign::kLeft);
} else {
state.style()->setTextAlign(state.parentStyle()->textAlign());
}
state.style()->setTextAlignIsInherited(false);
}
void StyleBuilderFunctions::applyInheritCSSPropertyTextIndent(
StyleResolverState& state) {
state.style()->setTextIndent(state.parentStyle()->textIndent());
state.style()->setTextIndentLine(state.parentStyle()->getTextIndentLine());
state.style()->setTextIndentType(state.parentStyle()->getTextIndentType());
}
void StyleBuilderFunctions::applyInitialCSSPropertyTextIndent(
StyleResolverState& state) {
state.style()->setTextIndent(ComputedStyle::initialTextIndent());
state.style()->setTextIndentLine(ComputedStyle::initialTextIndentLine());
state.style()->setTextIndentType(ComputedStyle::initialTextIndentType());
}
void StyleBuilderFunctions::applyValueCSSPropertyTextIndent(
StyleResolverState& state,
const CSSValue& value) {
Length lengthOrPercentageValue;
TextIndentLine textIndentLineValue = ComputedStyle::initialTextIndentLine();
TextIndentType textIndentTypeValue = ComputedStyle::initialTextIndentType();
for (auto& listValue : toCSSValueList(value)) {
if (listValue->isPrimitiveValue()) {
lengthOrPercentageValue =
toCSSPrimitiveValue(*listValue)
.convertToLength(state.cssToLengthConversionData());
} else if (toCSSIdentifierValue(*listValue).getValueID() ==
CSSValueEachLine) {
textIndentLineValue = TextIndentEachLine;
} else if (toCSSIdentifierValue(*listValue).getValueID() ==
CSSValueHanging) {
textIndentTypeValue = TextIndentHanging;
} else {
NOTREACHED();
}
}
state.style()->setTextIndent(lengthOrPercentageValue);
state.style()->setTextIndentLine(textIndentLineValue);
state.style()->setTextIndentType(textIndentTypeValue);
}
void StyleBuilderFunctions::applyInheritCSSPropertyVerticalAlign(
StyleResolverState& state) {
EVerticalAlign verticalAlign = state.parentStyle()->verticalAlign();
state.style()->setVerticalAlign(verticalAlign);
if (verticalAlign == EVerticalAlign::kLength)
state.style()->setVerticalAlignLength(
state.parentStyle()->getVerticalAlignLength());
}
void StyleBuilderFunctions::applyValueCSSPropertyVerticalAlign(
StyleResolverState& state,
const CSSValue& value) {
if (value.isIdentifierValue()) {
state.style()->setVerticalAlign(
toCSSIdentifierValue(value).convertTo<EVerticalAlign>());
} else {
state.style()->setVerticalAlignLength(
toCSSPrimitiveValue(value).convertToLength(
state.cssToLengthConversionData()));
}
}
static void resetEffectiveZoom(StyleResolverState& state) {
// Reset the zoom in effect. This allows the setZoom method to accurately
// compute a new zoom in effect.
state.setEffectiveZoom(state.parentStyle()
? state.parentStyle()->effectiveZoom()
: ComputedStyle::initialZoom());
}
void StyleBuilderFunctions::applyInitialCSSPropertyZoom(
StyleResolverState& state) {
resetEffectiveZoom(state);
state.setZoom(ComputedStyle::initialZoom());
}
void StyleBuilderFunctions::applyInheritCSSPropertyZoom(
StyleResolverState& state) {
resetEffectiveZoom(state);
state.setZoom(state.parentStyle()->zoom());
}
void StyleBuilderFunctions::applyValueCSSPropertyZoom(StyleResolverState& state,
const CSSValue& value) {
SECURITY_DCHECK(value.isPrimitiveValue() || value.isIdentifierValue());
if (value.isIdentifierValue()) {
const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(value);
if (identifierValue.getValueID() == CSSValueNormal) {
resetEffectiveZoom(state);
state.setZoom(ComputedStyle::initialZoom());
} else if (identifierValue.getValueID() == CSSValueReset) {
state.setEffectiveZoom(ComputedStyle::initialZoom());
state.setZoom(ComputedStyle::initialZoom());
} else if (identifierValue.getValueID() == CSSValueDocument) {
float docZoom = state.rootElementStyle()
? state.rootElementStyle()->zoom()
: ComputedStyle::initialZoom();
state.setEffectiveZoom(docZoom);
state.setZoom(docZoom);
}
} else if (value.isPrimitiveValue()) {
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
if (primitiveValue.isPercentage()) {
resetEffectiveZoom(state);
if (float percent = primitiveValue.getFloatValue())
state.setZoom(percent / 100.0f);
} else if (primitiveValue.isNumber()) {
resetEffectiveZoom(state);
if (float number = primitiveValue.getFloatValue())
state.setZoom(number);
}
}
}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitBorderImage(
StyleResolverState& state,
const CSSValue& value) {
NinePieceImage image;
CSSToStyleMap::mapNinePieceImage(state, CSSPropertyWebkitBorderImage, value,
image);
state.style()->setBorderImage(image);
}
void StyleBuilderFunctions::applyInitialCSSPropertyWebkitTextEmphasisStyle(
StyleResolverState& state) {
state.style()->setTextEmphasisFill(ComputedStyle::initialTextEmphasisFill());
state.style()->setTextEmphasisMark(ComputedStyle::initialTextEmphasisMark());
state.style()->setTextEmphasisCustomMark(
ComputedStyle::initialTextEmphasisCustomMark());
}
void StyleBuilderFunctions::applyInheritCSSPropertyWebkitTextEmphasisStyle(
StyleResolverState& state) {
state.style()->setTextEmphasisFill(
state.parentStyle()->getTextEmphasisFill());
state.style()->setTextEmphasisMark(
state.parentStyle()->getTextEmphasisMark());
state.style()->setTextEmphasisCustomMark(
state.parentStyle()->textEmphasisCustomMark());
}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitTextEmphasisStyle(
StyleResolverState& state,
const CSSValue& value) {
if (value.isValueList()) {
const CSSValueList& list = toCSSValueList(value);
DCHECK_EQ(list.length(), 2U);
for (unsigned i = 0; i < 2; ++i) {
const CSSIdentifierValue& value = toCSSIdentifierValue(list.item(i));
if (value.getValueID() == CSSValueFilled ||
value.getValueID() == CSSValueOpen)
state.style()->setTextEmphasisFill(value.convertTo<TextEmphasisFill>());
else
state.style()->setTextEmphasisMark(value.convertTo<TextEmphasisMark>());
}
state.style()->setTextEmphasisCustomMark(nullAtom);
return;
}
if (value.isStringValue()) {
state.style()->setTextEmphasisFill(TextEmphasisFillFilled);
state.style()->setTextEmphasisMark(TextEmphasisMarkCustom);
state.style()->setTextEmphasisCustomMark(
AtomicString(toCSSStringValue(value).value()));
return;
}
const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(value);
state.style()->setTextEmphasisCustomMark(nullAtom);
if (identifierValue.getValueID() == CSSValueFilled ||
identifierValue.getValueID() == CSSValueOpen) {
state.style()->setTextEmphasisFill(
identifierValue.convertTo<TextEmphasisFill>());
state.style()->setTextEmphasisMark(TextEmphasisMarkAuto);
} else {
state.style()->setTextEmphasisFill(TextEmphasisFillFilled);
state.style()->setTextEmphasisMark(
identifierValue.convertTo<TextEmphasisMark>());
}
}
void StyleBuilderFunctions::applyInitialCSSPropertyWillChange(
StyleResolverState& state) {
state.style()->setWillChangeContents(false);
state.style()->setWillChangeScrollPosition(false);
state.style()->setWillChangeProperties(Vector<CSSPropertyID>());
state.style()->setSubtreeWillChangeContents(
state.parentStyle()->subtreeWillChangeContents());
}
void StyleBuilderFunctions::applyInheritCSSPropertyWillChange(
StyleResolverState& state) {
state.style()->setWillChangeContents(
state.parentStyle()->willChangeContents());
state.style()->setWillChangeScrollPosition(
state.parentStyle()->willChangeScrollPosition());
state.style()->setWillChangeProperties(
state.parentStyle()->willChangeProperties());
state.style()->setSubtreeWillChangeContents(
state.parentStyle()->subtreeWillChangeContents());
}
void StyleBuilderFunctions::applyValueCSSPropertyWillChange(
StyleResolverState& state,
const CSSValue& value) {
bool willChangeContents = false;
bool willChangeScrollPosition = false;
Vector<CSSPropertyID> willChangeProperties;
if (value.isIdentifierValue()) {
DCHECK_EQ(toCSSIdentifierValue(value).getValueID(), CSSValueAuto);
} else {
DCHECK(value.isValueList());
for (auto& willChangeValue : toCSSValueList(value)) {
if (willChangeValue->isCustomIdentValue())
willChangeProperties.push_back(
toCSSCustomIdentValue(*willChangeValue).valueAsPropertyID());
else if (toCSSIdentifierValue(*willChangeValue).getValueID() ==
CSSValueContents)
willChangeContents = true;
else if (toCSSIdentifierValue(*willChangeValue).getValueID() ==
CSSValueScrollPosition)
willChangeScrollPosition = true;
else
NOTREACHED();
}
}
state.style()->setWillChangeContents(willChangeContents);
state.style()->setWillChangeScrollPosition(willChangeScrollPosition);
state.style()->setWillChangeProperties(willChangeProperties);
state.style()->setSubtreeWillChangeContents(
willChangeContents || state.parentStyle()->subtreeWillChangeContents());
}
void StyleBuilderFunctions::applyInitialCSSPropertyContent(
StyleResolverState& state) {
state.style()->setContent(nullptr);
}
void StyleBuilderFunctions::applyInheritCSSPropertyContent(
StyleResolverState&) {
// FIXME: In CSS3, it will be possible to inherit content. In CSS2 it is not.
// This note is a reminder that eventually "inherit" needs to be supported.
}
void StyleBuilderFunctions::applyValueCSSPropertyContent(
StyleResolverState& state,
const CSSValue& value) {
if (value.isIdentifierValue()) {
DCHECK(toCSSIdentifierValue(value).getValueID() == CSSValueNormal ||
toCSSIdentifierValue(value).getValueID() == CSSValueNone);
state.style()->setContent(nullptr);
return;
}
ContentData* firstContent = nullptr;
ContentData* prevContent = nullptr;
for (auto& item : toCSSValueList(value)) {
ContentData* nextContent = nullptr;
if (item->isImageGeneratorValue() || item->isImageSetValue() ||
item->isImageValue()) {
nextContent =
ContentData::create(state.styleImage(CSSPropertyContent, *item));
} else if (item->isCounterValue()) {
const CSSCounterValue* counterValue = toCSSCounterValue(item.get());
EListStyleType listStyleType = EListStyleType::kNone;
CSSValueID listStyleIdent = counterValue->listStyle();
if (listStyleIdent != CSSValueNone)
listStyleType =
static_cast<EListStyleType>(listStyleIdent - CSSValueDisc);
std::unique_ptr<CounterContent> counter =
WTF::wrapUnique(new CounterContent(
AtomicString(counterValue->identifier()), listStyleType,
AtomicString(counterValue->separator())));
nextContent = ContentData::create(std::move(counter));
} else if (item->isIdentifierValue()) {
QuoteType quoteType;
switch (toCSSIdentifierValue(*item).getValueID()) {
default:
NOTREACHED();
case CSSValueOpenQuote:
quoteType = OPEN_QUOTE;
break;
case CSSValueCloseQuote:
quoteType = CLOSE_QUOTE;
break;
case CSSValueNoOpenQuote:
quoteType = NO_OPEN_QUOTE;
break;
case CSSValueNoCloseQuote:
quoteType = NO_CLOSE_QUOTE;
break;
}
nextContent = ContentData::create(quoteType);
} else {
String string;
if (item->isFunctionValue()) {
const CSSFunctionValue* functionValue = toCSSFunctionValue(item.get());
DCHECK_EQ(functionValue->functionType(), CSSValueAttr);
// FIXME: Can a namespace be specified for an attr(foo)?
if (state.style()->styleType() == PseudoIdNone)
state.style()->setUnique();
else
state.parentStyle()->setUnique();
QualifiedName attr(
nullAtom, toCSSCustomIdentValue(functionValue->item(0)).value(),
nullAtom);
const AtomicString& value = state.element()->getAttribute(attr);
string = value.isNull() ? emptyString : value.getString();
} else {
string = toCSSStringValue(*item).value();
}
if (prevContent && prevContent->isText()) {
TextContentData* textContent = toTextContentData(prevContent);
textContent->setText(textContent->text() + string);
continue;
}
nextContent = ContentData::create(string);
}
if (!firstContent)
firstContent = nextContent;
else
prevContent->setNext(nextContent);
prevContent = nextContent;
}
DCHECK(firstContent);
state.style()->setContent(firstContent);
}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitLocale(
StyleResolverState& state,
const CSSValue& value) {
if (value.isIdentifierValue()) {
DCHECK_EQ(toCSSIdentifierValue(value).getValueID(), CSSValueAuto);
state.fontBuilder().setLocale(nullptr);
} else {
state.fontBuilder().setLocale(
LayoutLocale::get(AtomicString(toCSSStringValue(value).value())));
}
}
void StyleBuilderFunctions::applyInitialCSSPropertyWebkitAppRegion(
StyleResolverState&) {}
void StyleBuilderFunctions::applyInheritCSSPropertyWebkitAppRegion(
StyleResolverState&) {}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitAppRegion(
StyleResolverState& state,
const CSSValue& value) {
const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(value);
state.style()->setDraggableRegionMode(
identifierValue.getValueID() == CSSValueDrag ? DraggableRegionDrag
: DraggableRegionNoDrag);
state.document().setHasAnnotatedRegions(true);
}
void StyleBuilderFunctions::applyValueCSSPropertyWritingMode(
StyleResolverState& state,
const CSSValue& value) {
state.setWritingMode(toCSSIdentifierValue(value).convertTo<WritingMode>());
}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitWritingMode(
StyleResolverState& state,
const CSSValue& value) {
state.setWritingMode(toCSSIdentifierValue(value).convertTo<WritingMode>());
}
void StyleBuilderFunctions::applyValueCSSPropertyTextOrientation(
StyleResolverState& state,
const CSSValue& value) {
state.setTextOrientation(
toCSSIdentifierValue(value).convertTo<TextOrientation>());
}
void StyleBuilderFunctions::applyValueCSSPropertyWebkitTextOrientation(
StyleResolverState& state,
const CSSValue& value) {
state.setTextOrientation(
toCSSIdentifierValue(value).convertTo<TextOrientation>());
}
void StyleBuilderFunctions::applyValueCSSPropertyVariable(
StyleResolverState& state,
const CSSValue& value) {
const CSSCustomPropertyDeclaration& declaration =
toCSSCustomPropertyDeclaration(value);
const AtomicString& name = declaration.name();
const PropertyRegistration* registration = nullptr;
const PropertyRegistry* registry = state.document().propertyRegistry();
if (registry)
registration = registry->registration(name);
bool isInheritedProperty = !registration || registration->inherits();
bool initial = declaration.isInitial(isInheritedProperty);
bool inherit = declaration.isInherit(isInheritedProperty);
DCHECK(!(initial && inherit));
if (!initial && !inherit) {
if (declaration.value()->needsVariableResolution()) {
if (isInheritedProperty)
state.style()->setUnresolvedInheritedVariable(name,
declaration.value());
else
state.style()->setUnresolvedNonInheritedVariable(name,
declaration.value());
return;
}
if (!registration) {
state.style()->setResolvedUnregisteredVariable(name, declaration.value());
return;
}
const CSSValue* parsedValue =
declaration.value()->parseForSyntax(registration->syntax());
if (parsedValue) {
DCHECK(parsedValue);
if (isInheritedProperty)
state.style()->setResolvedInheritedVariable(name, declaration.value(),
parsedValue);
else
state.style()->setResolvedNonInheritedVariable(
name, declaration.value(), parsedValue);
return;
}
if (isInheritedProperty)
inherit = true;
else
initial = true;
}
DCHECK(initial ^ inherit);
state.style()->removeVariable(name, isInheritedProperty);
if (initial) {
return;
}
DCHECK(inherit);
CSSVariableData* parentValue =
state.parentStyle()->getVariable(name, isInheritedProperty);
const CSSValue* parentCSSValue =
registration && parentValue
? state.parentStyle()->getRegisteredVariable(name,
isInheritedProperty)
: nullptr;
if (!isInheritedProperty) {
DCHECK(registration);
if (parentValue) {
state.style()->setResolvedNonInheritedVariable(name, parentValue,
parentCSSValue);
}
return;
}
if (parentValue) {
if (!registration) {
state.style()->setResolvedUnregisteredVariable(name, parentValue);
} else {
state.style()->setResolvedInheritedVariable(name, parentValue,
parentCSSValue);
}
}
}
void StyleBuilderFunctions::applyInheritCSSPropertyBaselineShift(
StyleResolverState& state) {
const SVGComputedStyle& parentSvgStyle = state.parentStyle()->svgStyle();
EBaselineShift baselineShift = parentSvgStyle.baselineShift();
SVGComputedStyle& svgStyle = state.style()->accessSVGStyle();
svgStyle.setBaselineShift(baselineShift);
if (baselineShift == BS_LENGTH)
svgStyle.setBaselineShiftValue(parentSvgStyle.baselineShiftValue());
}
void StyleBuilderFunctions::applyValueCSSPropertyBaselineShift(
StyleResolverState& state,
const CSSValue& value) {
SVGComputedStyle& svgStyle = state.style()->accessSVGStyle();
if (!value.isIdentifierValue()) {
svgStyle.setBaselineShift(BS_LENGTH);
svgStyle.setBaselineShiftValue(StyleBuilderConverter::convertLength(
state, toCSSPrimitiveValue(value)));
return;
}
switch (toCSSIdentifierValue(value).getValueID()) {
case CSSValueBaseline:
svgStyle.setBaselineShift(BS_LENGTH);
svgStyle.setBaselineShiftValue(Length(Fixed));
return;
case CSSValueSub:
svgStyle.setBaselineShift(BS_SUB);
return;
case CSSValueSuper:
svgStyle.setBaselineShift(BS_SUPER);
return;
default:
NOTREACHED();
}
}
void StyleBuilderFunctions::applyInheritCSSPropertyPosition(
StyleResolverState& state) {
if (!state.parentNode()->isDocumentNode())
state.style()->setPosition(state.parentStyle()->position());
}
void StyleBuilderFunctions::applyInitialCSSPropertyCaretColor(
StyleResolverState& state) {
StyleAutoColor color = StyleAutoColor::autoColor();
if (state.applyPropertyToRegularStyle())
state.style()->setCaretColor(color);
if (state.applyPropertyToVisitedLinkStyle())
state.style()->setVisitedLinkCaretColor(color);
}
void StyleBuilderFunctions::applyInheritCSSPropertyCaretColor(
StyleResolverState& state) {
StyleAutoColor color = state.parentStyle()->caretColor();
if (state.applyPropertyToRegularStyle())
state.style()->setCaretColor(color);
if (state.applyPropertyToVisitedLinkStyle())
state.style()->setVisitedLinkCaretColor(color);
}
void StyleBuilderFunctions::applyValueCSSPropertyCaretColor(
StyleResolverState& state,
const CSSValue& value) {
if (state.applyPropertyToRegularStyle()) {
state.style()->setCaretColor(
StyleBuilderConverter::convertStyleAutoColor(state, value));
}
if (state.applyPropertyToVisitedLinkStyle()) {
state.style()->setVisitedLinkCaretColor(
StyleBuilderConverter::convertStyleAutoColor(state, value, true));
}
}
} // namespace blink