296 lines
11 KiB
C++
296 lines
11 KiB
C++
/*
|
|
* 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.get()))
|
|
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
|