mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 08:11:13 +00:00
LibWeb: Allow setting shorthand CSS properties via CSSStyleDeclaration
We now expand shorthands into their respective longhand values when assigning to a shorthand named property on a CSSStyleDeclaration. We also make sure that shorthands can be round-tripped by correctly routing named property access through the getPropertyValue() AO, and expanding it to handle shorthands as well. A lot of WPT tests for CSS parsing rely on these mechanisms and should now start working. :^) Note that multi-level recursive shorthands like `border` don't work 100% correctly yet. We're going to need a bunch more logic to properly serialize e.g `border-width` or `border` itself.
This commit is contained in:
parent
6fe43e9f73
commit
e40ad73ae7
Notes:
github-actions[bot]
2024-09-22 07:46:48 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/e40ad73ae79 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1479
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<script>
|
||||||
|
function testFlexValue(value) {
|
||||||
|
let e = document.createElement("div");
|
||||||
|
e.style.flex = value;
|
||||||
|
println("Setting flex: '" + value + "'; becomes...");
|
||||||
|
println(" flex-basis: '" + e.style.flexBasis + "'");
|
||||||
|
println(" flex-grow: '" + e.style.flexGrow + "'");
|
||||||
|
println(" flex-shrink: '" + e.style.flexShrink + "'");
|
||||||
|
println(" flex: '" + e.style.flex + "'");
|
||||||
|
println(" e.style.length: " + e.style.length);
|
||||||
|
for (let p = 0; p < e.style.length; ++p) {
|
||||||
|
println(" > [" + p + "] " + e.style[p]);
|
||||||
|
}
|
||||||
|
println("");
|
||||||
|
}
|
||||||
|
function testBorderValue(value) {
|
||||||
|
let e = document.createElement("div");
|
||||||
|
e.style.border = value;
|
||||||
|
println("Setting border: '" + value + "'; becomes...");
|
||||||
|
println(" border-width: '" + e.style.getPropertyValue('border-width') + "'");
|
||||||
|
println(" border-style: '" + e.style.borderStyle + "'");
|
||||||
|
println(" border-color: '" + e.style.borderColor + "'");
|
||||||
|
println(" border: '" + e.style.border + "'");
|
||||||
|
println(" e.style.length: " + e.style.length);
|
||||||
|
for (let p = 0; p < e.style.length; ++p) {
|
||||||
|
println(" > [" + p + "] " + e.style[p]);
|
||||||
|
}
|
||||||
|
println("");
|
||||||
|
}
|
||||||
|
test(() => {
|
||||||
|
testFlexValue("none");
|
||||||
|
testFlexValue("auto 1 2");
|
||||||
|
testFlexValue("");
|
||||||
|
|
||||||
|
testBorderValue("1px solid red");
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,7 @@
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||||
#include <LibWeb/CSS/Parser/Parser.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
|
#include <LibWeb/CSS/StyleComputer.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Element.h>
|
#include <LibWeb/DOM/Element.h>
|
||||||
|
@ -130,15 +131,23 @@ WebIDL::ExceptionOr<void> PropertyOwningCSSStyleDeclaration::set_property(Proper
|
||||||
// 7. Let updated be false.
|
// 7. Let updated be false.
|
||||||
bool updated = 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:
|
// 8. If property is a shorthand property,
|
||||||
// FIXME: 1. Let longhand result be the result of set the CSS declaration longhand with the appropriate value(s) from component value list,
|
if (property_is_shorthand(property_id)) {
|
||||||
// with the important flag set if priority is not the empty string, and unset otherwise, and with the list of declarations being the declarations.
|
// then for each longhand property longhand that property maps to, in canonical order, follow these substeps:
|
||||||
// FIXME: 2. If longhand result is true, let updated be true.
|
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,
|
||||||
// 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.
|
||||||
// with the important flag set if priority is not the empty string, and unset otherwise,
|
// 2. If longhand result is true, let updated be true.
|
||||||
// and with the list of declarations being the declarations.
|
updated |= set_a_css_declaration(longhand_property_id, longhand_value, !priority.is_empty() ? Important::Yes : Important::No);
|
||||||
updated = set_a_css_declaration(property_id, component_value_list.release_nonnull(), !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.
|
// 10. If updated is true, update style attribute for the CSS declaration block.
|
||||||
if (updated)
|
if (updated)
|
||||||
|
@ -222,11 +231,45 @@ bool PropertyOwningCSSStyleDeclaration::set_a_css_declaration(PropertyID propert
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
|
||||||
String CSSStyleDeclaration::get_property_value(StringView property_name) const
|
String CSSStyleDeclaration::get_property_value(StringView property_name) const
|
||||||
{
|
{
|
||||||
auto property_id = property_id_from_string(property_name);
|
auto property_id = property_id_from_string(property_name);
|
||||||
if (!property_id.has_value())
|
if (!property_id.has_value())
|
||||||
return {};
|
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<Important> 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());
|
auto maybe_property = property(property_id.value());
|
||||||
if (!maybe_property.has_value())
|
if (!maybe_property.has_value())
|
||||||
return {};
|
return {};
|
||||||
|
@ -424,9 +467,7 @@ JS::ThrowCompletionOr<JS::Value> CSSStyleDeclaration::internal_get(JS::PropertyK
|
||||||
auto property_id = property_id_from_name(name.to_string());
|
auto property_id = property_id_from_name(name.to_string());
|
||||||
if (property_id == CSS::PropertyID::Invalid)
|
if (property_id == CSS::PropertyID::Invalid)
|
||||||
return Base::internal_get(name, receiver, cacheable_metadata, phase);
|
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(), get_property_value(string_from_property_id(property_id)));
|
||||||
return { JS::PrimitiveString::create(vm(), maybe_property->value->to_string()) };
|
|
||||||
return { JS::PrimitiveString::create(vm(), String {}) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::ThrowCompletionOr<bool> CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)
|
JS::ThrowCompletionOr<bool> CSSStyleDeclaration::internal_set(JS::PropertyKey const& name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata)
|
||||||
|
|
Loading…
Reference in a new issue