| /* |
| * Copyright (C) 2021 Tyler Wilcock <twilco.o@protonmail.com>. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h" |
| #include "CSSCounterStyleRule.h" |
| |
| #include "CSSPropertyParser.h" |
| #include "CSSStyleSheet.h" |
| #include "CSSTokenizer.h" |
| #include "Pair.h" |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebCore { |
| |
| StyleRuleCounterStyle::StyleRuleCounterStyle(const AtomString& name, Ref<StyleProperties>&& properties) |
| : StyleRuleBase(StyleRuleType::CounterStyle) |
| , m_name(name) |
| , m_properties(WTFMove(properties)) |
| { |
| } |
| |
| Ref<StyleRuleCounterStyle> StyleRuleCounterStyle::create(const AtomString& name, Ref<StyleProperties>&& properties) |
| { |
| return adoptRef(*new StyleRuleCounterStyle(name, WTFMove(properties))); |
| } |
| |
| static CounterStyleSystem toCounterStyleSystemEnum(RefPtr<CSSValue> system) |
| { |
| if (!system || !system->isPrimitiveValue()) |
| return CounterStyleSystem::Symbolic; |
| |
| auto& primitiveSystemValue = downcast<CSSPrimitiveValue>(*system); |
| ASSERT(primitiveSystemValue.isValueID() || primitiveSystemValue.isPair()); |
| CSSValueID systemKeyword = CSSValueInvalid; |
| if (primitiveSystemValue.isValueID()) |
| systemKeyword = primitiveSystemValue.valueID(); |
| else if (auto* pair = primitiveSystemValue.pairValue()) { |
| // This value must be `fixed` or `extends`, both of which can or must have an additional component. |
| auto firstValue = pair->first(); |
| ASSERT(firstValue && firstValue->isValueID()); |
| if (firstValue) |
| systemKeyword = firstValue->valueID(); |
| } |
| |
| switch (systemKeyword) { |
| case CSSValueCyclic: |
| return CounterStyleSystem::Cyclic; |
| case CSSValueFixed: |
| return CounterStyleSystem::Fixed; |
| case CSSValueSymbolic: |
| return CounterStyleSystem::Symbolic; |
| case CSSValueAlphabetic: |
| return CounterStyleSystem::Alphabetic; |
| case CSSValueNumeric: |
| return CounterStyleSystem::Numeric; |
| case CSSValueAdditive: |
| return CounterStyleSystem::Additive; |
| case CSSValueExtends: |
| return CounterStyleSystem::Extends; |
| default: |
| ASSERT_NOT_REACHED(); |
| return CounterStyleSystem::Symbolic; |
| } |
| } |
| |
| static bool symbolsValidForSystem(CounterStyleSystem system, RefPtr<CSSValue> symbols, RefPtr<CSSValue> additiveSymbols) |
| { |
| switch (system) { |
| case CounterStyleSystem::Cyclic: |
| case CounterStyleSystem::Fixed: |
| case CounterStyleSystem::Symbolic: |
| return symbols && symbols->isValueList() && downcast<CSSValueList>(*symbols).length(); |
| case CounterStyleSystem::Alphabetic: |
| case CounterStyleSystem::Numeric: |
| return symbols && symbols->isValueList() && downcast<CSSValueList>(*symbols).length() >= 2u; |
| case CounterStyleSystem::Additive: |
| return additiveSymbols && additiveSymbols->isValueList() && downcast<CSSValueList>(*additiveSymbols).length(); |
| case CounterStyleSystem::Extends: |
| return !symbols && !additiveSymbols; |
| default: |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| } |
| |
| bool StyleRuleCounterStyle::newValueInvalidOrEqual(CSSPropertyID propertyID, const RefPtr<CSSValue> newValue) const |
| { |
| auto currentValue = m_properties->getPropertyCSSValue(propertyID); |
| if (compareCSSValuePtr(currentValue, newValue)) |
| return true; |
| |
| RefPtr<CSSValue> symbols; |
| RefPtr<CSSValue> additiveSymbols; |
| switch (propertyID) { |
| case CSSPropertySystem: |
| // If the attribute being set is `system`, and the new value would change the algorithm used, do nothing |
| // and abort these steps. |
| // (It's okay to change an aspect of the algorithm, like the first symbol value of a `fixed` system.) |
| return toCounterStyleSystemEnum(currentValue) != toCounterStyleSystemEnum(newValue); |
| case CSSPropertySymbols: |
| symbols = newValue; |
| additiveSymbols = m_properties->getPropertyCSSValue(CSSPropertyAdditiveSymbols); |
| break; |
| case CSSPropertyAdditiveSymbols: |
| symbols = m_properties->getPropertyCSSValue(CSSPropertySymbols); |
| additiveSymbols = newValue; |
| break; |
| default: |
| return false; |
| } |
| auto system = m_properties->getPropertyCSSValue(CSSPropertySystem); |
| return symbolsValidForSystem(toCounterStyleSystemEnum(system), symbols, additiveSymbols); |
| } |
| |
| StyleRuleCounterStyle::~StyleRuleCounterStyle() = default; |
| |
| MutableStyleProperties& StyleRuleCounterStyle::mutableProperties() |
| { |
| if (!is<MutableStyleProperties>(m_properties)) |
| m_properties = m_properties->mutableCopy(); |
| return downcast<MutableStyleProperties>(m_properties.get()); |
| } |
| |
| Ref<CSSCounterStyleRule> CSSCounterStyleRule::create(StyleRuleCounterStyle& rule, CSSStyleSheet* sheet) |
| { |
| return adoptRef(*new CSSCounterStyleRule(rule, sheet)); |
| } |
| |
| CSSCounterStyleRule::CSSCounterStyleRule(StyleRuleCounterStyle& counterStyleRule, CSSStyleSheet* parent) |
| : CSSRule(parent) |
| , m_counterStyleRule(counterStyleRule) |
| { |
| } |
| |
| CSSCounterStyleRule::~CSSCounterStyleRule() = default; |
| |
| String CSSCounterStyleRule::cssText() const |
| { |
| String systemText = system(); |
| const char* systemPrefix = systemText.isEmpty() ? "" : " system: "; |
| const char* systemSuffix = systemText.isEmpty() ? "" : ";"; |
| |
| String symbolsText = symbols(); |
| const char* symbolsPrefix = symbolsText.isEmpty() ? "" : " symbols: "; |
| const char* symbolsSuffix = symbolsText.isEmpty() ? "" : ";"; |
| |
| String additiveSymbolsText = additiveSymbols(); |
| const char* additiveSymbolsPrefix = additiveSymbolsText.isEmpty() ? "" : " additive-symbols: "; |
| const char* additiveSymbolsSuffix = additiveSymbolsText.isEmpty() ? "" : ";"; |
| |
| String negativeText = negative(); |
| const char* negativePrefix = negativeText.isEmpty() ? "" : " negative: "; |
| const char* negativeSuffix = negativeText.isEmpty() ? "" : ";"; |
| |
| String prefixText = prefix(); |
| const char* prefixTextPrefix = prefixText.isEmpty() ? "" : " prefix: "; |
| const char* prefixTextSuffix = prefixText.isEmpty() ? "" : ";"; |
| |
| String suffixText = suffix(); |
| const char* suffixTextPrefix = suffixText.isEmpty() ? "" : " suffix: "; |
| const char* suffixTextSuffix = suffixText.isEmpty() ? "" : ";"; |
| |
| String padText = pad(); |
| const char* padPrefix = padText.isEmpty() ? "" : " pad: "; |
| const char* padSuffix = padText.isEmpty() ? "" : ";"; |
| |
| String rangeText = range(); |
| const char* rangePrefix = rangeText.isEmpty() ? "" : " range: "; |
| const char* rangeSuffix = rangeText.isEmpty() ? "" : ";"; |
| |
| String fallbackText = fallback(); |
| const char* fallbackPrefix = fallbackText.isEmpty() ? "" : " fallback: "; |
| const char* fallbackSuffix = fallbackText.isEmpty() ? "" : ";"; |
| |
| String speakAsText = speakAs(); |
| const char* speakAsPrefix = speakAsText.isEmpty() ? "" : " speak-as: "; |
| const char* speakAsSuffix = speakAsText.isEmpty() ? "" : ";"; |
| |
| return makeString("@counter-style ", name(), " {", |
| systemPrefix, systemText, systemSuffix, |
| symbolsPrefix, symbolsText, symbolsSuffix, |
| additiveSymbolsPrefix, additiveSymbolsText, additiveSymbolsSuffix, |
| negativePrefix, negativeText, negativeSuffix, |
| prefixTextPrefix, prefixText, prefixTextSuffix, |
| suffixTextPrefix, suffixText, suffixTextSuffix, |
| padPrefix, padText, padSuffix, |
| rangePrefix, rangeText, rangeSuffix, |
| fallbackPrefix, fallbackText, fallbackSuffix, |
| speakAsPrefix, speakAsText, speakAsSuffix, |
| " }"); |
| } |
| |
| void CSSCounterStyleRule::reattach(StyleRuleBase& rule) |
| { |
| m_counterStyleRule = static_cast<StyleRuleCounterStyle&>(rule); |
| } |
| |
| // https://drafts.csswg.org/css-counter-styles-3/#dom-csscounterstylerule-name |
| void CSSCounterStyleRule::setName(const String& text) |
| { |
| auto tokenizer = CSSTokenizer(text); |
| auto tokenRange = tokenizer.tokenRange(); |
| auto name = CSSPropertyParserHelpers::consumeCounterStyleNameInPrelude(tokenRange); |
| if (name.isNull() || name == m_counterStyleRule->name()) |
| return; |
| |
| CSSStyleSheet::RuleMutationScope mutationScope(this); |
| m_counterStyleRule->setName(name); |
| } |
| |
| void CSSCounterStyleRule::setterInternal(CSSPropertyID propertyID, const String& valueText) |
| { |
| auto tokenizer = CSSTokenizer(valueText); |
| auto tokenRange = tokenizer.tokenRange(); |
| auto newValue = CSSPropertyParser::parseCounterStyleDescriptor(propertyID, tokenRange, parserContext()); |
| if (m_counterStyleRule->newValueInvalidOrEqual(propertyID, newValue)) |
| return; |
| |
| CSSStyleSheet::RuleMutationScope mutationScope(this); |
| m_counterStyleRule->mutableProperties().setProperty(propertyID, WTFMove(newValue)); |
| } |
| |
| void CSSCounterStyleRule::setSystem(const String& text) |
| { |
| setterInternal(CSSPropertySystem, text); |
| } |
| |
| void CSSCounterStyleRule::setNegative(const String& text) |
| { |
| setterInternal(CSSPropertyNegative, text); |
| } |
| |
| void CSSCounterStyleRule::setPrefix(const String& text) |
| { |
| setterInternal(CSSPropertyPrefix, text); |
| } |
| |
| void CSSCounterStyleRule::setSuffix(const String& text) |
| { |
| setterInternal(CSSPropertySuffix, text); |
| } |
| |
| void CSSCounterStyleRule::setRange(const String& text) |
| { |
| setterInternal(CSSPropertyRange, text); |
| } |
| |
| void CSSCounterStyleRule::setPad(const String& text) |
| { |
| setterInternal(CSSPropertyPad, text); |
| } |
| |
| void CSSCounterStyleRule::setFallback(const String& text) |
| { |
| setterInternal(CSSPropertyFallback, text); |
| } |
| |
| void CSSCounterStyleRule::setSymbols(const String& text) |
| { |
| setterInternal(CSSPropertySymbols, text); |
| } |
| |
| void CSSCounterStyleRule::setAdditiveSymbols(const String& text) |
| { |
| setterInternal(CSSPropertyAdditiveSymbols, text); |
| } |
| |
| void CSSCounterStyleRule::setSpeakAs(const String& text) |
| { |
| setterInternal(CSSPropertySpeakAs, text); |
| } |
| |
| } // namespace WebCore |