diff --git a/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp b/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp index 56315c7aa4a..3f6ea70cd88 100644 --- a/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp +++ b/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -102,7 +103,7 @@ Optional PropertyOwningCSSStyleDeclaration::property(PropertyID p } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty -WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(PropertyID property_id, StringView value, StringView priority) +WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(PropertyID property_id, Optional property_name, StringView value, StringView priority) { // 1. If the computed flag is set, then throw a NoModificationAllowedError exception. // NOTE: This is handled by the virtual override in ResolvedCSSStyleDeclaration. @@ -114,7 +115,10 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(Proper // 3. If value is the empty string, invoke removeProperty() with property as argument and return. if (value.is_empty()) { - MUST(remove_property(property_id)); + if (property_name.has_value()) + MUST(remove_property(property_id, property_name.value())); + else + MUST(remove_property(property_id, string_from_property_id(property_id))); return {}; } @@ -137,11 +141,11 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(Proper // 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) { + StyleComputer::for_each_property_expanding_shorthands(property_id, *component_value_list, StyleComputer::AllowUnresolved::Yes, [this, &updated, property_name, 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); + updated |= set_a_css_declaration(longhand_property_id, property_name, longhand_value, !priority.is_empty() ? Important::Yes : Important::No); }); } // 9. Otherwise, @@ -149,7 +153,7 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(Proper // 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); + updated = set_a_css_declaration(property_id, property_name, *component_value_list, !priority.is_empty() ? Important::Yes : Important::No); } // 10. If updated is true, update style attribute for the CSS declaration block. @@ -160,7 +164,7 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::set_property(Proper } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty -WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::remove_property(PropertyID property_id) +WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::remove_property(PropertyID property_id, StringView property_name) { // 1. If the computed flag is set, then throw a NoModificationAllowedError exception. // NOTE: This is handled by the virtual override in ResolvedCSSStyleDeclaration. @@ -169,8 +173,7 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::remove_property(P // NOTE: We've already converted it to a PropertyID enum value. // 3. Let value be the return value of invoking getPropertyValue() with property as argument. - // FIXME: The trip through string_from_property_id() here is silly. - auto value = get_property_value(string_from_property_id(property_id)); + auto value = get_property_value(property_name); // 4. Let removed be false. bool removed = false; @@ -180,7 +183,11 @@ WebIDL::ExceptionOr PropertyOwningCSSStyleDeclaration::remove_property(P // 2. Remove that CSS declaration and let removed be true. // 6. Otherwise, if property is a case-sensitive match for a property name of a CSS declaration in the declarations, remove that CSS declaration and let removed be true. - removed = m_properties.remove_first_matching([&](auto& entry) { return entry.property_id == property_id; }); + if (property_id == PropertyID::Custom) { + removed = m_custom_properties.remove(MUST(FlyString::from_utf8(property_name))); + } else { + removed = m_properties.remove_first_matching([&](auto& entry) { return entry.property_id == property_id; }); + } // 7. If removed is true, Update style attribute for the CSS declaration block. if (removed) @@ -212,10 +219,16 @@ void ElementInlineCSSStyleDeclaration::update_style_attribute() } // https://drafts.csswg.org/cssom/#set-a-css-declaration -bool PropertyOwningCSSStyleDeclaration::set_a_css_declaration(PropertyID property_id, NonnullRefPtr value, Important important) +bool PropertyOwningCSSStyleDeclaration::set_a_css_declaration(PropertyID property_id, Optional property_name, NonnullRefPtr value, Important important) { // FIXME: Handle logical property groups. + if (property_id == PropertyID::Custom && property_name.has_value()) { + auto fly_string_property = MUST(FlyString::from_utf8(property_name.release_value())); + m_custom_properties.set(fly_string_property, StyleProperty { .important = important, .property_id = property_id, .value = move(value), .custom_name = fly_string_property }); + return true; + } + for (auto& property : m_properties) { if (property.property_id == property_id) { if (property.important == important && *property.value == *value) @@ -237,6 +250,15 @@ bool PropertyOwningCSSStyleDeclaration::set_a_css_declaration(PropertyID propert // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue String CSSStyleDeclaration::get_property_value(StringView property_name) const { + // check for a custom property and return early + if (is_a_custom_property_name_string(property_name)) { + auto maybe_custom_property = custom_property(MUST(FlyString::from_utf8(property_name))); + if (maybe_custom_property.has_value()) { + return maybe_custom_property.value().value->to_string(); + } + return {}; + } + auto property_id = property_id_from_string(property_name); if (!property_id.has_value()) return {}; @@ -282,6 +304,13 @@ String CSSStyleDeclaration::get_property_value(StringView property_name) const // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority StringView CSSStyleDeclaration::get_property_priority(StringView property_name) const { + if (is_a_custom_property_name_string(property_name)) { + auto maybe_property = custom_property(MUST(FlyString::from_utf8(property_name))); + if (maybe_property.has_value()) { + return maybe_property->important == Important::Yes ? "important"sv : ""sv; + } + } + auto property_id = property_id_from_string(property_name); if (!property_id.has_value()) return {}; @@ -293,18 +322,23 @@ StringView CSSStyleDeclaration::get_property_priority(StringView property_name) WebIDL::ExceptionOr CSSStyleDeclaration::set_property(StringView property_name, StringView css_text, StringView priority) { + if (is_a_custom_property_name_string(property_name)) { + return set_property(PropertyID::Custom, property_name, css_text, priority); + } auto property_id = property_id_from_string(property_name); if (!property_id.has_value()) return {}; - return set_property(property_id.value(), css_text, priority); + return set_property(property_id.value(), property_name, css_text, priority); } WebIDL::ExceptionOr CSSStyleDeclaration::remove_property(StringView property_name) { auto property_id = property_id_from_string(property_name); + if (is_a_custom_property_name_string(property_name)) + property_id = PropertyID::Custom; if (!property_id.has_value()) return String {}; - return remove_property(property_id.value()); + return remove_property(property_id.value(), property_name); } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext diff --git a/Libraries/LibWeb/CSS/CSSStyleDeclaration.h b/Libraries/LibWeb/CSS/CSSStyleDeclaration.h index 8fb18c23f06..49e228e8da5 100644 --- a/Libraries/LibWeb/CSS/CSSStyleDeclaration.h +++ b/Libraries/LibWeb/CSS/CSSStyleDeclaration.h @@ -29,9 +29,11 @@ public: virtual String item(size_t index) const = 0; virtual Optional property(PropertyID) const = 0; + virtual Optional custom_property(FlyString const& custom_property_name) const = 0; - virtual WebIDL::ExceptionOr set_property(PropertyID, StringView css_text, StringView priority = ""sv) = 0; - virtual WebIDL::ExceptionOr remove_property(PropertyID) = 0; + // property_name only needs to be passed for custom properties. + virtual WebIDL::ExceptionOr set_property(PropertyID, Optional property_name, StringView css_text, StringView priority = ""sv) = 0; + virtual WebIDL::ExceptionOr remove_property(PropertyID, StringView property_name) = 0; virtual WebIDL::ExceptionOr set_property(StringView property_name, StringView css_text, StringView priority); virtual WebIDL::ExceptionOr remove_property(StringView property_name); @@ -76,12 +78,12 @@ public: virtual Optional property(PropertyID) const override; - virtual WebIDL::ExceptionOr set_property(PropertyID, StringView css_text, StringView priority) override; - virtual WebIDL::ExceptionOr remove_property(PropertyID) override; + virtual WebIDL::ExceptionOr set_property(PropertyID, Optional property_name, StringView css_text, StringView priority) override; + virtual WebIDL::ExceptionOr remove_property(PropertyID, StringView) override; Vector const& properties() const { return m_properties; } HashMap const& custom_properties() const { return m_custom_properties; } - Optional custom_property(FlyString const& custom_property_name) const { return m_custom_properties.get(custom_property_name); } + virtual Optional custom_property(FlyString const& custom_property_name) const override { return m_custom_properties.get(custom_property_name); } size_t custom_property_count() const { return m_custom_properties.size(); } virtual String serialized() const final override; @@ -99,7 +101,7 @@ protected: void set_the_declarations(Vector properties, HashMap custom_properties); private: - bool set_a_css_declaration(PropertyID, NonnullRefPtr, Important); + bool set_a_css_declaration(PropertyID, Optional, NonnullRefPtr, Important); virtual void visit_edges(Cell::Visitor&) override; diff --git a/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index ac95dc85adf..c26b2fe6b12 100644 --- a/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -601,13 +601,19 @@ Optional ResolvedCSSStyleDeclaration::property(PropertyID propert }; } +Optional ResolvedCSSStyleDeclaration::custom_property(FlyString const&) const +{ + dbgln("FIXME: ResolvedCSSStyleDeclaration should support custom property"); + return {}; +} + static WebIDL::ExceptionOr cannot_modify_computed_property_error(JS::Realm& realm) { return WebIDL::NoModificationAllowedError::create(realm, "Cannot modify properties in result of getComputedStyle()"_string); } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty -WebIDL::ExceptionOr ResolvedCSSStyleDeclaration::set_property(PropertyID, StringView, StringView) +WebIDL::ExceptionOr ResolvedCSSStyleDeclaration::set_property(PropertyID, Optional, StringView, StringView) { // 1. If the computed flag is set, then throw a NoModificationAllowedError exception. return cannot_modify_computed_property_error(realm()); @@ -626,7 +632,7 @@ static WebIDL::ExceptionOr cannot_remove_computed_property_error(JS::Rea } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty -WebIDL::ExceptionOr ResolvedCSSStyleDeclaration::remove_property(PropertyID) +WebIDL::ExceptionOr ResolvedCSSStyleDeclaration::remove_property(PropertyID, StringView) { // 1. If the computed flag is set, then throw a NoModificationAllowedError exception. return cannot_remove_computed_property_error(realm()); diff --git a/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h b/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h index 9a663016ed7..259e1406fca 100644 --- a/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h +++ b/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h @@ -24,9 +24,10 @@ public: virtual String item(size_t index) const override; virtual Optional property(PropertyID) const override; - virtual WebIDL::ExceptionOr set_property(PropertyID, StringView css_text, StringView priority) override; + virtual Optional custom_property(FlyString const& custom_property_name) const override; + virtual WebIDL::ExceptionOr set_property(PropertyID, Optional property_name, StringView css_text, StringView priority) override; virtual WebIDL::ExceptionOr set_property(StringView property_name, StringView css_text, StringView priority) override; - virtual WebIDL::ExceptionOr remove_property(PropertyID) override; + virtual WebIDL::ExceptionOr remove_property(PropertyID, StringView) override; virtual WebIDL::ExceptionOr remove_property(StringView property_name) override; virtual String serialized() const override; diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 7ea12b8d7c4..8692b528983 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -657,9 +657,9 @@ void HTMLInputElement::update_placeholder_visibility() if (!m_placeholder_element) return; if (this->placeholder_value().has_value()) { - MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv)); + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "block"sv)); } else { - MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv)); + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "none"sv)); } } @@ -962,7 +962,7 @@ void HTMLInputElement::create_color_input_shadow_tree() border: 1px solid ButtonBorder; box-sizing: border-box; )~~~"_string)); - MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, color)); + MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, {}, color)); MUST(border->append_child(*m_color_well_element)); MUST(shadow_root->append_child(border)); @@ -974,7 +974,7 @@ void HTMLInputElement::update_color_well_element() if (!m_color_well_element) return; - MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value)); + MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, {}, m_value)); } void HTMLInputElement::create_file_input_shadow_tree() @@ -1141,9 +1141,9 @@ void HTMLInputElement::computed_css_values_changed() accent_color = accent_color_property.to_string(); if (m_slider_progress_element) - MUST(m_slider_progress_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, accent_color)); + MUST(m_slider_progress_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, {}, accent_color)); if (m_slider_thumb) - MUST(m_slider_thumb->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, accent_color)); + MUST(m_slider_thumb->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, {}, accent_color)); } void HTMLInputElement::user_interaction_did_change_input_value() @@ -1171,10 +1171,10 @@ void HTMLInputElement::update_slider_shadow_tree_elements() double position = (value - minimum) / (maximum - minimum) * 100; if (m_slider_progress_element) - MUST(m_slider_progress_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position)))); + MUST(m_slider_progress_element->style_for_bindings()->set_property(CSS::PropertyID::Width, {}, MUST(String::formatted("{}%", position)))); if (m_slider_thumb) - MUST(m_slider_thumb->style_for_bindings()->set_property(CSS::PropertyID::MarginLeft, MUST(String::formatted("{}%", position)))); + MUST(m_slider_thumb->style_for_bindings()->set_property(CSS::PropertyID::MarginLeft, {}, MUST(String::formatted("{}%", position)))); } void HTMLInputElement::did_receive_focus() diff --git a/Libraries/LibWeb/HTML/HTMLMeterElement.cpp b/Libraries/LibWeb/HTML/HTMLMeterElement.cpp index 39b8648a9a2..70f4eabb38b 100644 --- a/Libraries/LibWeb/HTML/HTMLMeterElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLMeterElement.cpp @@ -240,6 +240,6 @@ void HTMLMeterElement::update_meter_value_element() } double position = (value - min) / (max - min) * 100; - MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position)))); + MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, {}, MUST(String::formatted("{}%", position)))); } } diff --git a/Libraries/LibWeb/HTML/HTMLProgressElement.cpp b/Libraries/LibWeb/HTML/HTMLProgressElement.cpp index bb71d1e08d6..632ed1e050f 100644 --- a/Libraries/LibWeb/HTML/HTMLProgressElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLProgressElement.cpp @@ -127,7 +127,7 @@ void HTMLProgressElement::create_shadow_tree_if_needed() void HTMLProgressElement::update_progress_value_element() { if (m_progress_value_element) - MUST(m_progress_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position() * 100)))); + MUST(m_progress_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, {}, MUST(String::formatted("{}%", position() * 100)))); } void HTMLProgressElement::computed_css_values_changed() @@ -140,7 +140,7 @@ void HTMLProgressElement::computed_css_values_changed() accent_color = accent_color_property.to_string(); if (m_progress_value_element) - MUST(m_progress_value_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, accent_color)); + MUST(m_progress_value_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, {}, accent_color)); } } diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index 82e2490d2f2..93ea9af01c2 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -508,9 +508,9 @@ void HTMLSelectElement::computed_css_values_changed() if (m_chevron_icon_element) { auto appearance = computed_css_values()->appearance(); if (appearance.has_value() && *appearance == CSS::Appearance::None) { - MUST(m_chevron_icon_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"_string)); + MUST(m_chevron_icon_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "none"_string)); } else { - MUST(m_chevron_icon_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"_string)); + MUST(m_chevron_icon_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "block"_string)); } } } diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 17da0fab735..29091100cb7 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -415,11 +415,11 @@ void HTMLTextAreaElement::update_placeholder_visibility() return; auto placeholder_text = get_attribute(AttributeNames::placeholder); if (placeholder_text.has_value() && m_text_node->data().is_empty()) { - MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv)); - MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv)); + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "block"sv)); + MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "none"sv)); } else { - MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv)); - MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv)); + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "none"sv)); + MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Display, {}, "block"sv)); } } diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-custom-properties.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-custom-properties.txt new file mode 100644 index 00000000000..d07d9f84857 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-custom-properties.txt @@ -0,0 +1,3 @@ +--foo= bar +--foo= baz important +--foo= blank diff --git a/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-custom-properties.html b/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-custom-properties.html new file mode 100644 index 00000000000..b0e9274b1e1 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/CSSStyleDeclaration-custom-properties.html @@ -0,0 +1,19 @@ + +
+