blob: eeb8ed19b1e336aff937146abc5a56d3845f5298 [file] [log] [blame]
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* 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 THE COPYRIGHT HOLDER “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 HOLDER 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 "CSSStyleValueFactory.h"
#if ENABLE(CSS_TYPED_OM)
#include "CSSCustomPropertyValue.h"
#include "CSSKeywordValue.h"
#include "CSSNumericFactory.h"
#include "CSSParser.h"
#include "CSSPendingSubstitutionValue.h"
#include "CSSPropertyParser.h"
#include "CSSStyleImageValue.h"
#include "CSSStyleValue.h"
#include "CSSUnitValue.h"
#include "CSSUnparsedValue.h"
#include "CSSValueList.h"
#include "CSSVariableData.h"
#include "CSSVariableReferenceValue.h"
#include "StylePropertyShorthand.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringView.h>
namespace WebCore {
ExceptionOr<void> CSSStyleValueFactory::extractCSSValues(Vector<Ref<CSSValue>>& cssValues, const CSSPropertyID& propertyID, const String& cssText)
{
auto styleDeclaration = MutableStyleProperties::create();
constexpr bool important = true;
CSSParser::ParseResult parseResult = CSSParser::parseValue(styleDeclaration, propertyID, cssText, important, strictCSSParserContext());
if (parseResult == CSSParser::ParseResult::Error)
return Exception { TypeError, makeString(cssText, " cannot be parsed.")};
if (auto cssValue = styleDeclaration->getPropertyCSSValue(propertyID))
cssValues.append(cssValue.releaseNonNull());
return { };
}
ExceptionOr<void> CSSStyleValueFactory::extractShorthandCSSValues(Vector<Ref<CSSValue>>& cssValues, const CSSPropertyID& propertyID, const String& cssText)
{
auto styleDeclaration = MutableStyleProperties::create();
constexpr bool important = true;
CSSParser::ParseResult parseResult = CSSParser::parseValue(styleDeclaration, propertyID, cssText, important, strictCSSParserContext());
if (parseResult == CSSParser::ParseResult::Error)
return Exception { TypeError, makeString(cssText, " cannot be parsed.")};
auto shorthand = shorthandForProperty(propertyID);
for (auto longhand : shorthand) {
if (auto cssValue = styleDeclaration->getPropertyCSSValue(longhand))
cssValues.append(cssValue.releaseNonNull());
}
return { };
}
ExceptionOr<void> CSSStyleValueFactory::extractCustomCSSValues(Vector<Ref<CSSValue>>& cssValues, const String& customPropertyName, const String& cssText)
{
auto styleDeclaration = MutableStyleProperties::create();
constexpr bool important = true;
CSSParser::ParseResult parseResult = CSSParser::parseCustomPropertyValue(styleDeclaration, customPropertyName, cssText, important, strictCSSParserContext());
if (parseResult == CSSParser::ParseResult::Error)
return Exception { TypeError, makeString(cssText, " cannot be parsed.")};
if (auto customValue = styleDeclaration->getPropertyCSSValue(CSSPropertyCustom))
cssValues.append(customValue.releaseNonNull());
return { };
}
ExceptionOr<Vector<Ref<CSSStyleValue>>> CSSStyleValueFactory::parseStyleValue(const String& cssProperty, const String& cssText, bool parseMultiple)
{
// https://www.w3.org/TR/css-typed-om-1/#cssstylevalue
Vector<Ref<CSSValue>> cssValues;
// Extract the CSSValue from cssText given cssProperty
if (isCustomPropertyName(cssProperty)) {
auto result = extractCustomCSSValues(cssValues, cssProperty, cssText);
if (result.hasException())
return result.releaseException();
} else {
String property = cssProperty.convertToASCIILowercase();
auto propertyID = cssPropertyID(property);
if (propertyID == CSSPropertyInvalid)
return Exception { TypeError, "Property String is not a valid CSS property."_s };
if (isShorthandCSSProperty(propertyID)) {
auto result = extractShorthandCSSValues(cssValues, propertyID, cssText);
if (result.hasException())
return result.releaseException();
} else {
auto result = extractCSSValues(cssValues, propertyID, cssText);
if (result.hasException())
return result.releaseException();
}
}
Vector<Ref<CSSStyleValue>> results;
for (auto& cssValue : cssValues) {
auto reifiedValue = reifyValue(WTFMove(cssValue));
if (reifiedValue.hasException())
return reifiedValue.releaseException();
results.append(reifiedValue.releaseReturnValue());
if (!parseMultiple)
break;
}
return results;
}
ExceptionOr<Ref<CSSStyleValue>> CSSStyleValueFactory::reifyValue(Ref<CSSValue>&& cssValue, Document* document)
{
if (is<CSSPrimitiveValue>(cssValue)) {
auto primitiveValue = downcast<CSSPrimitiveValue>(cssValue.ptr());
switch (primitiveValue->primitiveType()) {
case CSSUnitType::CSS_NUMBER:
return Ref<CSSStyleValue> { CSSNumericFactory::number(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_PERCENTAGE:
return Ref<CSSStyleValue> { CSSNumericFactory::percent(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_EMS:
return Ref<CSSStyleValue> { CSSNumericFactory::em(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_EXS:
return Ref<CSSStyleValue> { CSSNumericFactory::ex(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_CHS:
return Ref<CSSStyleValue> { CSSNumericFactory::ch(primitiveValue->doubleValue()) };
// FIXME: Add CSSNumericFactory::ic
case CSSUnitType::CSS_REMS:
return Ref<CSSStyleValue> { CSSNumericFactory::rem(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_LHS:
return Ref<CSSStyleValue> { CSSNumericFactory::lh(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_RLHS:
return Ref<CSSStyleValue> { CSSNumericFactory::rlh(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_VW:
return Ref<CSSStyleValue> { CSSNumericFactory::vw(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_VH:
return Ref<CSSStyleValue> { CSSNumericFactory::vh(primitiveValue->doubleValue()) };
// FIXME: Add CSSNumericFactory::vi & ::vb
case CSSUnitType::CSS_VMIN:
return Ref<CSSStyleValue> { CSSNumericFactory::vmin(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_VMAX:
return Ref<CSSStyleValue> { CSSNumericFactory::vmax(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_CM:
return Ref<CSSStyleValue> { CSSNumericFactory::cm(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_MM:
return Ref<CSSStyleValue> { CSSNumericFactory::mm(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_Q:
return Ref<CSSStyleValue> { CSSNumericFactory::q(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_IN:
return Ref<CSSStyleValue> { CSSNumericFactory::in(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_PT:
return Ref<CSSStyleValue> { CSSNumericFactory::pt(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_PC:
return Ref<CSSStyleValue> { CSSNumericFactory::pc(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_PX:
return Ref<CSSStyleValue> { CSSNumericFactory::px(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_DEG:
return Ref<CSSStyleValue> { CSSNumericFactory::deg(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_GRAD:
return Ref<CSSStyleValue> { CSSNumericFactory::grad(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_RAD:
return Ref<CSSStyleValue> { CSSNumericFactory::rad(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_TURN:
return Ref<CSSStyleValue> { CSSNumericFactory::turn(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_S:
return Ref<CSSStyleValue> { CSSNumericFactory::s(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_MS:
return Ref<CSSStyleValue> { CSSNumericFactory::ms(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_HZ:
return Ref<CSSStyleValue> { CSSNumericFactory::hz(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_KHZ:
return Ref<CSSStyleValue> { CSSNumericFactory::kHz(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_DPI:
return Ref<CSSStyleValue> { CSSNumericFactory::dpi(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_DPCM:
return Ref<CSSStyleValue> { CSSNumericFactory::dpcm(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_DPPX:
return Ref<CSSStyleValue> { CSSNumericFactory::dppx(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_FR:
return Ref<CSSStyleValue> { CSSNumericFactory::fr(primitiveValue->doubleValue()) };
case CSSUnitType::CSS_STRING: {
auto value = CSSKeywordValue::create(primitiveValue->stringValue());
if (value.hasException())
return value.releaseException();
return Ref<CSSStyleValue> { value.releaseReturnValue() };
}
default:
break;
}
} else if (is<CSSImageValue>(cssValue))
return Ref<CSSStyleValue> { CSSStyleImageValue::create(downcast<CSSImageValue>(cssValue.get()), document) };
else if (is<CSSVariableReferenceValue>(cssValue)) {
return Ref<CSSStyleValue> { CSSUnparsedValue::create(downcast<CSSVariableReferenceValue>(cssValue.get()).data().tokenRange()) };
} else if (is<CSSPendingSubstitutionValue>(cssValue)) {
return Ref<CSSStyleValue> { CSSUnparsedValue::create(downcast<CSSPendingSubstitutionValue>(cssValue.get()).shorthandValue().data().tokenRange()) };
} else if (is<CSSCustomPropertyValue>(cssValue)) {
// FIXME: remove CSSStyleValue::create(WTFMove(cssValue)), add reification control flow
return WTF::switchOn(downcast<CSSCustomPropertyValue>(cssValue.get()).value(), [&](const Ref<CSSVariableReferenceValue>& value) {
return reifyValue(value.copyRef(), document);
}, [&](Ref<CSSVariableData>& value) {
return reifyValue(CSSVariableReferenceValue::create(WTFMove(value)));
}, [&](auto&) {
// FIXME: Property reify the other cases.
return ExceptionOr<Ref<CSSStyleValue>> { CSSStyleValue::create(WTFMove(cssValue)) };
});
} else if (is<CSSValueList>(cssValue)) {
// Reifying the first value in value list.
// FIXME: Verify this is the expected behavior.
// Refer to LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/inline/get.html
auto valueList = downcast<CSSValueList>(cssValue.ptr());
if (!valueList->length())
return Exception { TypeError, "The CSSValueList should not be empty." };
return reifyValue(WTFMove(*valueList->begin()), document);
}
return CSSStyleValue::create(WTFMove(cssValue));
}
} // namespace WebCore
#endif