diff --git a/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt b/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt new file mode 100644 index 00000000000..b6c856ce170 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/set-style-declaration-shorthand.txt @@ -0,0 +1,46 @@ +Setting flex: 'none'; becomes... + flex-basis: 'auto' + flex-grow: '0' + flex-shrink: '0' + flex: '0 0 auto' + e.style.length: 3 + > [0] flex-grow + > [1] flex-shrink + > [2] flex-basis + +Setting flex: 'auto 1 2'; becomes... + flex-basis: 'auto' + flex-grow: '1' + flex-shrink: '2' + flex: '1 2 auto' + e.style.length: 3 + > [0] flex-grow + > [1] flex-shrink + > [2] flex-basis + +Setting flex: ''; becomes... + flex-basis: '' + flex-grow: '' + flex-shrink: '' + flex: '' + e.style.length: 0 + +Setting border: '1px solid red'; becomes... + border-width: '1px 1px 1px 1px' + border-style: 'solid solid solid solid' + border-color: 'rgb(255, 0, 0) rgb(255, 0, 0) rgb(255, 0, 0) rgb(255, 0, 0)' + border: '' + e.style.length: 12 + > [0] border-top-width + > [1] border-right-width + > [2] border-bottom-width + > [3] border-left-width + > [4] border-top-style + > [5] border-right-style + > [6] border-bottom-style + > [7] border-left-style + > [8] border-top-color + > [9] border-right-color + > [10] border-bottom-color + > [11] border-left-color + diff --git a/Tests/LibWeb/Text/input/css/set-style-declaration-shorthand.html b/Tests/LibWeb/Text/input/css/set-style-declaration-shorthand.html new file mode 100644 index 00000000000..540d3c7a7fc --- /dev/null +++ b/Tests/LibWeb/Text/input/css/set-style-declaration-shorthand.html @@ -0,0 +1,38 @@ + + diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp index 6b314e3651b..cd81b5c0d54 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2018-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -130,15 +131,23 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(Proper // 7. Let updated be false. bool updated = false; - // FIXME: 8. If property is a shorthand property, then for each longhand property longhand that property maps to, in canonical order, follow these substeps: - // FIXME: 1. Let longhand result be the result of set the CSS declaration longhand with the appropriate value(s) from component value list, - // with the important flag set if priority is not the empty string, and unset otherwise, and with the list of declarations being the declarations. - // FIXME: 2. If longhand result is true, let updated be true. - - // 9. Otherwise, let updated be the result of set the CSS declaration property with value component value list, - // with the important flag set if priority is not the empty string, and unset otherwise, - // and with the list of declarations being the declarations. - updated = set_a_css_declaration(property_id, component_value_list.release_nonnull(), !priority.is_empty() ? Important::Yes : Important::No); + // 8. If property is a shorthand property, + if (property_is_shorthand(property_id)) { + // then for each longhand property longhand that property maps to, in canonical order, follow these substeps: + StyleComputer::for_each_property_expanding_shorthands(property_id, *component_value_list, StyleComputer::AllowUnresolved::Yes, [this, &updated, priority](PropertyID longhand_property_id, CSSStyleValue const& longhand_value) { + // 1. Let longhand result be the result of set the CSS declaration longhand with the appropriate value(s) from component value list, + // with the important flag set if priority is not the empty string, and unset otherwise, and with the list of declarations being the declarations. + // 2. If longhand result is true, let updated be true. + updated |= set_a_css_declaration(longhand_property_id, longhand_value, !priority.is_empty() ? Important::Yes : Important::No); + }); + } + // 9. Otherwise, + else { + // let updated be the result of set the CSS declaration property with value component value list, + // with the important flag set if priority is not the empty string, and unset otherwise, + // and with the list of declarations being the declarations. + updated = set_a_css_declaration(property_id, *component_value_list, !priority.is_empty() ? Important::Yes : Important::No); + } // 10. If updated is true, update style attribute for the CSS declaration block. if (updated) @@ -222,11 +231,45 @@ bool PropertyOwningCSSStyleDeclaration::set_a_css_declaration(PropertyID propert return true; } +// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue String CSSStyleDeclaration::get_property_value(StringView property_name) const { auto property_id = property_id_from_string(property_name); if (!property_id.has_value()) return {}; + + // 2. If property is a shorthand property, then follow these substeps: + if (property_is_shorthand(property_id.value())) { + // 1. Let list be a new empty array. + StringBuilder list; + Optional last_important_flag; + + // 2. For each longhand property longhand that property maps to, in canonical order, follow these substeps: + for (auto longhand_property_id : longhands_for_shorthand(property_id.value())) { + // 1. If longhand is a case-sensitive match for a property name of a CSS declaration in the declarations, let declaration be that CSS declaration, or null otherwise. + auto declaration = property(longhand_property_id); + + // 2. If declaration is null, then return the empty string. + if (!declaration.has_value()) + return {}; + + // 3. Append the declaration to list. + if (!list.is_empty()) + list.append(' '); + list.append(declaration->value->to_string()); + + if (last_important_flag.has_value() && declaration->important != *last_important_flag) + return {}; + last_important_flag = declaration->important; + } + + // 3. If important flags of all declarations in list are same, then return the serialization of list. + return list.to_string_without_validation(); + + // 4. Return the empty string. + // NOTE: This is handled by the loop. + } + auto maybe_property = property(property_id.value()); if (!maybe_property.has_value()) return {}; @@ -424,9 +467,7 @@ JS::ThrowCompletionOr CSSStyleDeclaration::internal_get(JS::PropertyK auto property_id = property_id_from_name(name.to_string()); if (property_id == CSS::PropertyID::Invalid) return Base::internal_get(name, receiver, cacheable_metadata, phase); - if (auto maybe_property = property(property_id); maybe_property.has_value()) - return { JS::PrimitiveString::create(vm(), maybe_property->value->to_string()) }; - return { JS::PrimitiveString::create(vm(), String {}) }; + return JS::PrimitiveString::create(vm(), get_property_value(string_from_property_id(property_id))); } JS::ThrowCompletionOr CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)