| Index: Source/core/css/resolver/CascadedValues.cpp
|
| diff --git a/Source/core/css/resolver/CascadedValues.cpp b/Source/core/css/resolver/CascadedValues.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6249a4c204feea346c5640dba702f780d41195d6
|
| --- /dev/null
|
| +++ b/Source/core/css/resolver/CascadedValues.cpp
|
| @@ -0,0 +1,256 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "config.h"
|
| +#include "core/css/resolver/CascadedValues.h"
|
| +
|
| +#include "core/css/CSSValuePool.h"
|
| +#include "core/css/StylePropertySet.h"
|
| +#include "core/css/resolver/MatchResult.h"
|
| +#include "core/css/resolver/StyleBuilder.h"
|
| +#include "core/css/resolver/StyleResolver.h"
|
| +#include "core/css/resolver/StyleResolverState.h"
|
| +#include <algorithm>
|
| +
|
| +namespace blink {
|
| +
|
| +// We want properties in their prefixed and non-prefixed variants to cascade
|
| +// together, so we map them to the same property before adding to the cascade.
|
| +static CSSPropertyID resolveProperty(CSSPropertyID property, TextDirection direction, WritingMode writingMode)
|
| +{
|
| + switch (property) {
|
| + case CSSPropertyWebkitBorderEndColor:
|
| + case CSSPropertyWebkitBorderBeforeStyle:
|
| + case CSSPropertyWebkitBorderEndStyle:
|
| + case CSSPropertyWebkitPaddingStart:
|
| + case CSSPropertyWebkitBorderStartWidth:
|
| + case CSSPropertyWebkitMaxLogicalWidth:
|
| + case CSSPropertyWebkitLogicalHeight:
|
| + case CSSPropertyWebkitMinLogicalWidth:
|
| + case CSSPropertyWebkitBorderBeforeWidth:
|
| + case CSSPropertyWebkitPaddingBefore:
|
| + case CSSPropertyWebkitBorderBeforeColor:
|
| + case CSSPropertyWebkitMarginEnd:
|
| + case CSSPropertyWebkitBorderAfterWidth:
|
| + case CSSPropertyWebkitMinLogicalHeight:
|
| + case CSSPropertyWebkitBorderEndWidth:
|
| + case CSSPropertyWebkitPaddingEnd:
|
| + case CSSPropertyWebkitLogicalWidth:
|
| + case CSSPropertyWebkitBorderAfterColor:
|
| + case CSSPropertyWebkitMaxLogicalHeight:
|
| + case CSSPropertyWebkitBorderStartColor:
|
| + case CSSPropertyWebkitBorderAfterStyle:
|
| + case CSSPropertyWebkitPaddingAfter:
|
| + case CSSPropertyWebkitBorderStartStyle:
|
| + case CSSPropertyWebkitMarginBefore:
|
| + case CSSPropertyWebkitMarginStart:
|
| + case CSSPropertyWebkitMarginAfter:
|
| + return CSSProperty::resolveDirectionAwareProperty(property, direction, writingMode);
|
| + case CSSPropertyWebkitBackfaceVisibility:
|
| + return CSSPropertyBackfaceVisibility;
|
| + case CSSPropertyWebkitBackgroundClip:
|
| + return CSSPropertyBackgroundClip;
|
| + case CSSPropertyWebkitBackgroundOrigin:
|
| + return CSSPropertyBackgroundOrigin;
|
| + case CSSPropertyWebkitBackgroundSize:
|
| + return CSSPropertyBackgroundSize;
|
| + case CSSPropertyWebkitBoxShadow:
|
| + return CSSPropertyBoxShadow;
|
| + case CSSPropertyWebkitTransform:
|
| + return CSSPropertyTransform;
|
| + case CSSPropertyWebkitTransformStyle:
|
| + return CSSPropertyTransformStyle;
|
| + case CSSPropertyWebkitPerspective:
|
| + return CSSPropertyPerspective;
|
| + case CSSPropertyWebkitPerspectiveOrigin:
|
| + return CSSPropertyPerspectiveOrigin;
|
| + // Used by editing when the css3 text decorations flag is off
|
| + case CSSPropertyTextDecoration:
|
| + ASSERT(!RuntimeEnabledFeatures::css3TextDecorationsEnabled());
|
| + return CSSPropertyTextDecorationLine;
|
| + // As per css3-text, word-wrap is an alias for overflow-wrap
|
| + case CSSPropertyWordWrap:
|
| + return CSSPropertyOverflowWrap;
|
| + default:
|
| + return property;
|
| + }
|
| +}
|
| +
|
| +CascadedValues::CascadedValues(StyleResolverState& state, const MatchResult& matchResult, bool onlyAddUARules)
|
| + : m_state(state)
|
| +{
|
| + memset(m_values, 0, sizeof(m_values));
|
| + if (m_state.style()->insideLink())
|
| + memset(m_visitedLinkValues, 0, sizeof(m_visitedLinkValues));
|
| +
|
| + // The MatchResult has rules in UA - User - Author order, although we only
|
| + // use user rules for callback selectors.
|
| + addMatchResult(matchResult, CSSPropertyDirection, CSSPropertyWebkitWritingMode);
|
| + applyValues(CSSPropertyDirection, CSSPropertyWebkitWritingMode);
|
| + m_direction = state.style()->direction();
|
| + m_writingMode = state.style()->writingMode();
|
| +
|
| + COMPILE_ASSERT(CSSPropertyColor == CSSPropertyWebkitWritingMode + 1, CSS_color_is_after_super_high_priority);
|
| + if (onlyAddUARules) {
|
| + addMatchResultRange(matchResult, false, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, CSSPropertyColor, convertToCSSPropertyID(lastCSSProperty));
|
| + } else {
|
| + addMatchResult(matchResult, CSSPropertyColor, convertToCSSPropertyID(lastCSSProperty));
|
| + }
|
| +
|
| + std::sort(setProperties.begin(), setProperties.end());
|
| + std::sort(setVisitedProperties.begin(), setVisitedProperties.end());
|
| +}
|
| +
|
| +void CascadedValues::addMatchResult(const MatchResult& matchResult, CSSPropertyID firstId, CSSPropertyID lastId)
|
| +{
|
| + addMatchResultRange(matchResult, false, 0, matchResult.matchedProperties.size() - 1, firstId, lastId);
|
| + addMatchResultRange(matchResult, true, matchResult.ranges.firstAuthorRule, matchResult.ranges.lastAuthorRule, firstId, lastId);
|
| + addMatchResultRange(matchResult, true, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, firstId, lastId);
|
| +}
|
| +
|
| +void CascadedValues::addMatchResultRange(const MatchResult& matchResult, bool isImportant, int firstIndex, int lastIndex, CSSPropertyID firstId, CSSPropertyID lastId)
|
| +{
|
| + if (firstIndex == -1)
|
| + return;
|
| +
|
| + for (int i = firstIndex; i <= lastIndex; ++i) {
|
| + const MatchedProperties& matchedProperties = matchResult.matchedProperties[i];
|
| + SelectorChecker::LinkMatchMask linkMatchType = SelectorChecker::MatchLink;
|
| + if (m_state.style()->insideLink())
|
| + linkMatchType = static_cast<SelectorChecker::LinkMatchMask>(matchedProperties.m_types.linkMatchType);
|
| + addStylePropertySet(matchedProperties.properties.get(), matchResult.matchedRules[i], isImportant, firstId, lastId, static_cast<PropertyWhitelistType>(matchedProperties.m_types.whitelistType), linkMatchType);
|
| + }
|
| +}
|
| +
|
| +void CascadedValues::addStylePropertySet(const StylePropertySet* properties, const StyleRule* rule, bool isImportant, CSSPropertyID first, CSSPropertyID last, PropertyWhitelistType propertyWhitelistType, SelectorChecker::LinkMatchMask linkMatchType)
|
| +{
|
| + unsigned propertyCount = properties->propertyCount();
|
| + for (unsigned i = 0; i < propertyCount; ++i) {
|
| + StylePropertySet::PropertyReference current = properties->propertyAt(i);
|
| + if (isImportant != current.isImportant())
|
| + continue;
|
| +
|
| + CSSPropertyID property = current.id();
|
| + if (property < first || property > last)
|
| + continue;
|
| +
|
| + // FIXME: We should check the whitelist, otherwise we might run into
|
| + // problems with e.g. foo:first-letter { all: inherit; }
|
| + if (property == CSSPropertyAll) {
|
| + addAllProperty(current.value(), linkMatchType);
|
| + continue;
|
| + }
|
| +
|
| + if (propertyWhitelistType == PropertyWhitelistCue && !StyleResolver::isValidCueStyleProperty(property))
|
| + continue;
|
| + if (propertyWhitelistType == PropertyWhitelistFirstLetter && !StyleResolver::isValidFirstLetterStyleProperty(property))
|
| + continue;
|
| + addValue(current.id(), current.value(), rule, linkMatchType);
|
| + }
|
| +}
|
| +
|
| +void CascadedValues::addValue(CSSPropertyID property, CSSValue* value, const StyleRule* rule, SelectorChecker::LinkMatchMask linkMatchType)
|
| +{
|
| + property = resolveProperty(property, m_direction, m_writingMode);
|
| +
|
| + if (property == CSSPropertyInternalCallback) {
|
| + ASSERT(value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueInternalPresence);
|
| + m_state.style()->addCallbackSelector(rule->selectorList().selectorsText());
|
| + return;
|
| + }
|
| +
|
| + // Shorthands that are not expanded in the parser need special handling here.
|
| + // The shorthand property occurs earlier in CSSPropertyNames.in than its
|
| + // longhands so we can cascade properly. If a longhand value is explicitly
|
| + // set, with higher priority than the shorthand, then the value computed
|
| + // from the shorthand will be overriden.
|
| +
|
| + // FIXME: font should always be expanded by the parser; crbug.com/353932
|
| + if (property == CSSPropertyFont) {
|
| + clearCascadedValue(CSSPropertyLineHeight);
|
| + clearCascadedValue(CSSPropertyFontStyle);
|
| + clearCascadedValue(CSSPropertyFontFamily);
|
| + clearCascadedValue(CSSPropertyFontVariant);
|
| + clearCascadedValue(CSSPropertyFontWeight);
|
| + clearCascadedValue(CSSPropertyFontStretch);
|
| + clearCascadedValue(CSSPropertyFontSize);
|
| + }
|
| +
|
| + // -webkit-border-image sometimes also sets border-*-width, but properly
|
| + // cascading this requires too much effort
|
| + if (property == CSSPropertyWebkitBorderImage) {
|
| + clearCascadedValue(CSSPropertyBorderImageSource);
|
| + clearCascadedValue(CSSPropertyBorderImageSlice);
|
| + clearCascadedValue(CSSPropertyBorderImageWidth);
|
| + clearCascadedValue(CSSPropertyBorderImageOutset);
|
| + clearCascadedValue(CSSPropertyBorderImageRepeat);
|
| + }
|
| +
|
| + if (linkMatchType & SelectorChecker::MatchLink)
|
| + setCascadedValue(property, value);
|
| + if (linkMatchType & SelectorChecker::MatchVisited)
|
| + setCascadedVisitedLinkValue(property, value);
|
| +}
|
| +
|
| +void CascadedValues::addAllProperty(CSSValue* value, SelectorChecker::LinkMatchMask linkMatchType)
|
| +{
|
| + if (!value->isInitialValue() && !value->isInheritedValue()) {
|
| + ASSERT(value->isPrimitiveValue() && toCSSPrimitiveValue(value)->getValueID() == CSSValueUnset);
|
| + value = nullptr;
|
| + }
|
| +
|
| + for (int i = firstCSSProperty; i <= lastCSSProperty; ++i) {
|
| + CSSPropertyID property = convertToCSSPropertyID(i);
|
| + if (CSSProperty::isAffectedByAllProperty(property))
|
| + addValue(property, value, nullptr, linkMatchType);
|
| + }
|
| +}
|
| +
|
| +void CascadedValues::setCascadedValue(CSSPropertyID id, CSSValue* value)
|
| +{
|
| + if (!m_values[id - firstCSSProperty])
|
| + setProperties.append(id);
|
| + m_values[id - firstCSSProperty] = value;
|
| +}
|
| +
|
| +void CascadedValues::setCascadedVisitedLinkValue(CSSPropertyID id, CSSValue* value)
|
| +{
|
| + ASSERT(m_state.style()->insideLink());
|
| + if (!m_visitedLinkValues[id - firstCSSProperty])
|
| + setVisitedProperties.append(id);
|
| + m_visitedLinkValues[id - firstCSSProperty] = value;
|
| +}
|
| +
|
| +void CascadedValues::applyValues(CSSPropertyID first, CSSPropertyID last, bool inheritedOnly)
|
| +{
|
| + for (size_t i = 0; i < setProperties.size(); i++) {
|
| + CSSPropertyID property = setProperties[i];
|
| + if (property < first || property > last)
|
| + continue;
|
| + // We might not have a value due to the shorthand special-cases in addValue
|
| + if (!cascadedValue(property))
|
| + continue;
|
| + if (!inheritedOnly || CSSProperty::isInheritedProperty(property))
|
| + StyleBuilder::applyProperty(property, m_state, cascadedValue(property));
|
| + }
|
| +
|
| + if (!m_state.style()->insideLink())
|
| + return;
|
| +
|
| + m_state.setApplyPropertyToRegularStyle(false);
|
| + m_state.setApplyPropertyToVisitedLinkStyle(true);
|
| + for (size_t i = 0; i < setVisitedProperties.size(); i++) {
|
| + CSSPropertyID property = setVisitedProperties[i];
|
| + if (property < first || property > last)
|
| + continue;
|
| + ASSERT(cascadedVisitedLinkValue(property));
|
| + if (!inheritedOnly || CSSProperty::isInheritedProperty(property))
|
| + StyleBuilder::applyProperty(property, m_state, cascadedVisitedLinkValue(property));
|
| + }
|
| + m_state.setApplyPropertyToRegularStyle(true);
|
| + m_state.setApplyPropertyToVisitedLinkStyle(false);
|
| +}
|
| +
|
| +
|
| +} // namespace blink
|
|
|