Repository: LadybirdBrowser/ladybird
File: Libraries/LibWeb/SVG/SVGElement.cpp (C++)

Code before:
        NamedPropertyID(CSS::PropertyID::StrokeLinecap),
        NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
        NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
        NamedPropertyID(CSS::PropertyID::StrokeOpacity),
        NamedPropertyID(CSS::PropertyID::StrokeWidth),
        NamedPropertyID(CSS::PropertyID::TextAnchor),
        NamedPropertyID(CSS::PropertyID::TextDecoration),
        NamedPropertyID(CSS::PropertyID::TextRendering),
        NamedPropertyID(CSS::PropertyID::TextOverflow),
        NamedPropertyID(CSS::PropertyID::TransformOrigin),
        NamedPropertyID(CSS::PropertyID::UnicodeBidi),
        NamedPropertyID(CSS::PropertyID::Visibility),
        NamedPropertyID(CSS::PropertyID::WhiteSpace),
        NamedPropertyID(CSS::PropertyID::Width, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
        NamedPropertyID(CSS::PropertyID::WordSpacing),
        NamedPropertyID(CSS::PropertyID::WritingMode),
        NamedPropertyID(CSS::PropertyID::X, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
        NamedPropertyID(CSS::PropertyID::Y, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
    };
    return properties;
}

// https://html.spec.whatwg.org/multipage/interaction.html#tabindex-value
bool SVGElement::is_focusable() const
{
    return Web::HTML::parse_integer(get_attribute_value(HTML::AttributeNames::tabindex)).has_value();
}

bool SVGElement::is_presentational_hint(FlyString const& name) const
{
    if (Base::is_presentational_hint(name))
        return true;

    return any_of(attribute_style_properties(), [&](auto& property) { return name.equals_ignoring_ascii_case(property.name); });
}

void SVGElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
{
    CSS::Parser::ParsingParams parsing_context { document(), CSS::Parser::ParsingMode::SVGPresentationAttribute };
    for_each_attribute([&](auto& name, auto& value) {
        for (auto& property : attribute_style_properties()) {
            if (!name.equals_ignoring_ascii_case(property.name))
                continue;
            if (!property.supported_elements.is_empty() && !property.supported_elements.contains_slow(local_name()))
                continue;
            if (property.id == CSS::PropertyID::Mask) {
                // Mask is a shorthand property in CSS, but parse_css_value does not take that into account. For now,
                // just parse as 'mask-image' as anything else is currently not supported.
                // FIXME: properly parse longhand 'mask' property
                if (auto style_value = parse_css_value(parsing_context, value, CSS::PropertyID::MaskImage)) {
                    cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::MaskImage, style_value.release_nonnull());

Review comment (suggestion):
Hm, maybe this could go in `DOM::Element::is_focusable()` instead? Then you'd no longer need this condition in both SVGElement and HTMLElement.

Code after:
        NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
        NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
        NamedPropertyID(CSS::PropertyID::StrokeOpacity),
        NamedPropertyID(CSS::PropertyID::StrokeWidth),
        NamedPropertyID(CSS::PropertyID::TextAnchor),
        NamedPropertyID(CSS::PropertyID::TextDecoration),
        NamedPropertyID(CSS::PropertyID::TextRendering),
        NamedPropertyID(CSS::PropertyID::TextOverflow),
        NamedPropertyID(CSS::PropertyID::TransformOrigin),
        NamedPropertyID(CSS::PropertyID::UnicodeBidi),
        NamedPropertyID(CSS::PropertyID::Visibility),
        NamedPropertyID(CSS::PropertyID::WhiteSpace),
        NamedPropertyID(CSS::PropertyID::Width, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
        NamedPropertyID(CSS::PropertyID::WordSpacing),
        NamedPropertyID(CSS::PropertyID::WritingMode),
        NamedPropertyID(CSS::PropertyID::X, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
        NamedPropertyID(CSS::PropertyID::Y, { SVG::TagNames::foreignObject, SVG::TagNames::image, SVG::TagNames::rect, SVG::TagNames::svg, SVG::TagNames::symbol, SVG::TagNames::use }),
    };
    return properties;
}

bool SVGElement::is_presentational_hint(FlyString const& name) const
{
    if (Base::is_presentational_hint(name))
        return true;

    return any_of(attribute_style_properties(), [&](auto& property) { return name.equals_ignoring_ascii_case(property.name); });
}

void SVGElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
{
    CSS::Parser::ParsingParams parsing_context { document(), CSS::Parser::ParsingMode::SVGPresentationAttribute };
    for_each_attribute([&](auto& name, auto& value) {
        for (auto& property : attribute_style_properties()) {
            if (!name.equals_ignoring_ascii_case(property.name))
                continue;
            if (!property.supported_elements.is_empty() && !property.supported_elements.contains_slow(local_name()))
                continue;
            if (property.id == CSS::PropertyID::Mask) {
                // Mask is a shorthand property in CSS, but parse_css_value does not take that into account. For now,
                // just parse as 'mask-image' as anything else is currently not supported.
                // FIXME: properly parse longhand 'mask' property
                if (auto style_value = parse_css_value(parsing_context, value, CSS::PropertyID::MaskImage)) {
                    cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::MaskImage, style_value.release_nonnull());
                }
            } else {
                if (auto style_value = parse_css_value(parsing_context, value, property.id))
                    cascaded_properties->set_property_from_presentational_hint(property.id, style_value.release_nonnull());
            }
            break;
        }