diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 2a3e0c70436..45c38a900ef 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -164,6 +164,7 @@ set(SOURCES CSS/StyleValues/EasingStyleValue.cpp CSS/StyleValues/EdgeStyleValue.cpp CSS/StyleValues/FilterValueListStyleValue.cpp + CSS/StyleValues/FontSourceStyleValue.cpp CSS/StyleValues/GridAutoFlowStyleValue.cpp CSS/StyleValues/GridTemplateAreaStyleValue.cpp CSS/StyleValues/GridTrackPlacementStyleValue.cpp diff --git a/Libraries/LibWeb/CSS/CSSStyleValue.cpp b/Libraries/LibWeb/CSS/CSSStyleValue.cpp index 6ba2ac82cfe..03b2886070b 100644 --- a/Libraries/LibWeb/CSS/CSSStyleValue.cpp +++ b/Libraries/LibWeb/CSS/CSSStyleValue.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,12 @@ FlexStyleValue const& CSSStyleValue::as_flex() const return static_cast(*this); } +FontSourceStyleValue const& CSSStyleValue::as_font_source() const +{ + VERIFY(is_font_source()); + return static_cast(*this); +} + FrequencyStyleValue const& CSSStyleValue::as_frequency() const { VERIFY(is_frequency()); diff --git a/Libraries/LibWeb/CSS/CSSStyleValue.h b/Libraries/LibWeb/CSS/CSSStyleValue.h index 0fd3a50dd14..bc7ab385e6a 100644 --- a/Libraries/LibWeb/CSS/CSSStyleValue.h +++ b/Libraries/LibWeb/CSS/CSSStyleValue.h @@ -106,6 +106,7 @@ public: FilterValueList, FitContent, Flex, + FontSource, FontVariant, Frequency, GridAutoFlow, @@ -228,6 +229,10 @@ public: FlexStyleValue const& as_flex() const; FlexStyleValue& as_flex() { return const_cast(const_cast(*this).as_flex()); } + bool is_font_source() const { return type() == Type::FontSource; } + FontSourceStyleValue const& as_font_source() const; + FontSourceStyleValue& as_font_source() { return const_cast(const_cast(*this).as_font_source()); } + bool is_frequency() const { return type() == Type::Frequency; } FrequencyStyleValue const& as_frequency() const; FrequencyStyleValue& as_frequency() { return const_cast(const_cast(*this).as_frequency()); } diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 15dc7b06fdf..765a3e38fcb 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -341,6 +341,7 @@ private: RefPtr parse_position_value(TokenStream&, PositionParsingMode = PositionParsingMode::Normal); RefPtr parse_filter_value_list_value(TokenStream&); RefPtr parse_opentype_tag_value(TokenStream&); + RefPtr parse_font_source_value(TokenStream&); RefPtr parse_angle_value(TokenStream&); RefPtr parse_angle_percentage_value(TokenStream&); diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 19446cde0ec..ddfd5903b14 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -3655,6 +3657,67 @@ RefPtr Parser::parse_opentype_tag_value(TokenStream Parser::parse_font_source_value(TokenStream& tokens) +{ + // = [ format()]? [ tech( #)]? | local() + auto transaction = tokens.begin_transaction(); + + tokens.discard_whitespace(); + + // local() + if (tokens.next_token().is_function("local"sv)) { + auto const& function = tokens.consume_a_token().function(); + TokenStream function_tokens { function.value }; + if (auto family_name = parse_family_name_value(function_tokens)) { + transaction.commit(); + return FontSourceStyleValue::create(FontSourceStyleValue::Local { family_name.release_nonnull() }, {}); + } + return nullptr; + } + + // [ format()]? [ tech( #)]? + auto url = parse_url_function(tokens); + if (!url.has_value() || !url->is_valid()) + return nullptr; + + Optional format; + + tokens.discard_whitespace(); + + // [ format()]? + if (tokens.next_token().is_function("format"sv)) { + auto const& function = tokens.consume_a_token().function(); + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + + TokenStream format_tokens { function.value }; + format_tokens.discard_whitespace(); + auto const& format_name_token = format_tokens.consume_a_token(); + FlyString format_name; + if (format_name_token.is(Token::Type::Ident)) { + format_name = format_name_token.token().ident(); + } else if (format_name_token.is(Token::Type::String)) { + format_name = format_name_token.token().string(); + } else { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`format()` parameter not an ident or string; is: {}); discarding.", format_name_token.to_debug_string()); + return nullptr; + } + + if (!font_format_is_supported(format_name)) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source format({}) not supported; skipping.", format_name); + return nullptr; + } + + format = move(format_name); + } + + tokens.discard_whitespace(); + + // FIXME: [ tech( #)]? + + transaction.commit(); + return FontSourceStyleValue::create(url.release_value(), move(format)); +} + NonnullRefPtr Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) { // Unresolved always contains a var() or attr(), unless it is a custom property's value, in which case we shouldn't be trying diff --git a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp new file mode 100644 index 00000000000..644eb093443 --- /dev/null +++ b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::CSS { + +FontSourceStyleValue::FontSourceStyleValue(Source source, Optional format) + : StyleValueWithDefaultOperators(Type::FontSource) + , m_source(move(source)) + , m_format(move(format)) +{ +} + +FontSourceStyleValue::~FontSourceStyleValue() = default; + +String FontSourceStyleValue::to_string(SerializationMode) const +{ + // = [ format()]? [ tech( #)]? | local() + return m_source.visit( + [](Local const& local) { + // local() + StringBuilder builder; + serialize_a_local(builder, local.name->to_string(SerializationMode::Normal)); + return builder.to_string_without_validation(); + }, + [this](URL::URL const& url) { + // [ format()]? [ tech( #)]? + // FIXME: tech() + StringBuilder builder; + serialize_a_url(builder, url.to_string()); + + if (m_format.has_value()) { + builder.append(" format("sv); + serialize_an_identifier(builder, *m_format); + builder.append(")"sv); + } + + return builder.to_string_without_validation(); + }); +} + +bool FontSourceStyleValue::properties_equal(FontSourceStyleValue const& other) const +{ + bool sources_equal = m_source.visit( + [&other](Local const& local) { + if (auto* other_local = other.m_source.get_pointer()) { + return local.name == other_local->name; + } + return false; + }, + [&other](URL::URL const& url) { + if (auto* other_url = other.m_source.get_pointer()) { + return url == *other_url; + } + return false; + }); + + return sources_equal + && m_format == other.m_format; +} + +} diff --git a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h new file mode 100644 index 00000000000..732f990bdaa --- /dev/null +++ b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::CSS { + +class FontSourceStyleValue final : public StyleValueWithDefaultOperators { +public: + struct Local { + NonnullRefPtr name; + }; + using Source = Variant; + + static ValueComparingNonnullRefPtr create(Source source, Optional format) + { + return adopt_ref(*new (nothrow) FontSourceStyleValue(move(source), move(format))); + } + virtual ~FontSourceStyleValue() override; + + Source const& source() const { return m_source; } + Optional const& format() const { return m_format; } + + virtual String to_string(SerializationMode) const override; + + bool properties_equal(FontSourceStyleValue const&) const; + +private: + FontSourceStyleValue(Source source, Optional format); + + Source m_source; + Optional m_format; +}; + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 5481b19c46c..378fc59c2c6 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -198,6 +198,7 @@ class FlexOrCalculated; class FlexStyleValue; class FontFace; class FontFaceSet; +class FontSourceStyleValue; class Frequency; class FrequencyOrCalculated; class FrequencyPercentage;