From 8937542fe301f38451be58aeb997c8d9b735a113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=BClker?= Date: Tue, 3 Jun 2025 13:22:44 +0200 Subject: [PATCH] Implement the `size` presentational hint for `
` elements (#37211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This presentational hint either sets the width values of all borders, removes the bottom border or sets the height of the element, depending on the context. This change also implements the corresponding idl attribute (and the `noshade` attribute, which does nothing in html5) Testing: Adds new web platform tests --------- Signed-off-by: Simon Wülker --- components/script/dom/element.rs | 43 +++- components/script/dom/htmlhrelement.rs | 58 +++++ .../webidls/HTMLHRElement.webidl | 15 +- tests/wpt/meta/MANIFEST.json | 34 +++ .../meta/html/dom/idlharness.https.html.ini | 12 - .../html/dom/reflection-grouping.html.ini | 228 ------------------ .../the-hr-element-0/size-ref.html | 20 ++ .../size-with-color-or-noshade-ref.html | 17 ++ .../size-with-color-or-noshade.html | 15 ++ .../the-hr-element-0/size.html | 13 + 10 files changed, 204 insertions(+), 251 deletions(-) create mode 100644 tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-ref.html create mode 100644 tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade-ref.html create mode 100644 tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade.html create mode 100644 tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size.html diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4db03cfb3b3..ed4c37c4378 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -121,7 +121,7 @@ use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers}; use crate::dom::htmlformelement::FormControlElementHelpers; -use crate::dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers}; +use crate::dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers, SizePresentationalHint}; use crate::dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods}; use crate::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers}; use crate::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; @@ -1306,6 +1306,47 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> { PropertyDeclaration::PaddingRight(cellpadding), )); } + + // https://html.spec.whatwg.org/multipage/#the-hr-element-2 + if let Some(size_info) = self + .downcast::() + .and_then(|hr_element| hr_element.get_size_info()) + { + match size_info { + SizePresentationalHint::SetHeightTo(height) => { + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::Height(height), + )); + }, + SizePresentationalHint::SetAllBorderWidthValuesTo(border_width) => { + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::BorderLeftWidth(border_width.clone()), + )); + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::BorderRightWidth(border_width.clone()), + )); + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::BorderTopWidth(border_width.clone()), + )); + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::BorderBottomWidth(border_width), + )); + }, + SizePresentationalHint::SetBottomBorderWidthToZero => { + hints.push(from_declaration( + shared_lock, + PropertyDeclaration::BorderBottomWidth( + specified::border::BorderSideWidth::from_px(0.), + ), + )); + }, + } + } } fn get_span(self) -> Option { diff --git a/components/script/dom/htmlhrelement.rs b/components/script/dom/htmlhrelement.rs index c88a0fcf184..8dc11e4e848 100644 --- a/components/script/dom/htmlhrelement.rs +++ b/components/script/dom/htmlhrelement.rs @@ -2,11 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::str::FromStr; + use dom_struct::dom_struct; use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::color::AbsoluteColor; +use style::values::generics::NonNegative; +use style::values::specified::border::BorderSideWidth; +use style::values::specified::length::Size; +use style::values::specified::{LengthPercentage, NoCalcLength}; use crate::dom::bindings::codegen::Bindings::HTMLHRElementBinding::HTMLHRElementMethods; use crate::dom::bindings::inheritance::Castable; @@ -65,6 +71,18 @@ impl HTMLHRElementMethods for HTMLHRElement { // https://html.spec.whatwg.org/multipage/#dom-hr-color make_legacy_color_setter!(SetColor, "color"); + // https://html.spec.whatwg.org/multipage/#dom-hr-noshade + make_bool_getter!(NoShade, "noshade"); + + // https://html.spec.whatwg.org/multipage/#dom-hr-noshade + make_bool_setter!(SetNoShade, "noshade"); + + // https://html.spec.whatwg.org/multipage/#dom-hr-size + make_getter!(Size, "size"); + + // https://html.spec.whatwg.org/multipage/#dom-hr-size + make_dimension_setter!(SetSize, "size"); + // https://html.spec.whatwg.org/multipage/#dom-hr-width make_getter!(Width, "width"); @@ -72,9 +90,20 @@ impl HTMLHRElementMethods for HTMLHRElement { make_dimension_setter!(SetWidth, "width"); } +/// The result of applying the the presentational hint for the `size` attribute. +/// +/// (This attribute can mean different things depending on its value and other attributes) +#[allow(clippy::enum_variant_names)] +pub(crate) enum SizePresentationalHint { + SetHeightTo(Size), + SetAllBorderWidthValuesTo(BorderSideWidth), + SetBottomBorderWidthToZero, +} + pub(crate) trait HTMLHRLayoutHelpers { fn get_color(self) -> Option; fn get_width(self) -> LengthOrPercentageOrAuto; + fn get_size_info(self) -> Option; } impl HTMLHRLayoutHelpers for LayoutDom<'_, HTMLHRElement> { @@ -92,6 +121,35 @@ impl HTMLHRLayoutHelpers for LayoutDom<'_, HTMLHRElement> { .cloned() .unwrap_or(LengthOrPercentageOrAuto::Auto) } + + fn get_size_info(self) -> Option { + // https://html.spec.whatwg.org/multipage/#the-hr-element-2 + let element = self.upcast::(); + let size_value = element + .get_attr_val_for_layout(&ns!(), &local_name!("size")) + .and_then(|value| usize::from_str(value).ok()) + .filter(|value| *value != 0)?; + + let hint = if element + .get_attr_for_layout(&ns!(), &local_name!("color")) + .is_some() || + element + .get_attr_for_layout(&ns!(), &local_name!("noshade")) + .is_some() + { + SizePresentationalHint::SetAllBorderWidthValuesTo(BorderSideWidth::from_px( + size_value as f32 / 2.0, + )) + } else if size_value == 1 { + SizePresentationalHint::SetBottomBorderWidthToZero + } else { + SizePresentationalHint::SetHeightTo(Size::LengthPercentage(NonNegative( + LengthPercentage::Length(NoCalcLength::from_px((size_value - 2) as f32)), + ))) + }; + + Some(hint) + } } impl VirtualMethods for HTMLHRElement { diff --git a/components/script_bindings/webidls/HTMLHRElement.webidl b/components/script_bindings/webidls/HTMLHRElement.webidl index 8963d5e8901..45828d4da76 100644 --- a/components/script_bindings/webidls/HTMLHRElement.webidl +++ b/components/script_bindings/webidls/HTMLHRElement.webidl @@ -12,14 +12,9 @@ interface HTMLHRElement : HTMLElement { // https://html.spec.whatwg.org/multipage/#HTMLHRElement-partial partial interface HTMLHRElement { - [CEReactions] - attribute DOMString align; - [CEReactions] - attribute DOMString color; - // [CEReactions] - // attribute boolean noShade; - // [CEReactions] - // attribute DOMString size; - [CEReactions] - attribute DOMString width; + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString color; + [CEReactions] attribute boolean noShade; + [CEReactions] attribute DOMString size; + [CEReactions] attribute DOMString width; }; diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 400ef8b2ccf..deff780986e 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -351937,6 +351937,32 @@ {} ] ], + "size-with-color-or-noshade.html": [ + "db1d583934e6df482cde846eda757e010e7d0310", + [ + null, + [ + [ + "/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade-ref.html", + "==" + ] + ], + {} + ] + ], + "size.html": [ + "2162131b853ed77917ab373f4fb2c70c536e453c", + [ + null, + [ + [ + "/html/rendering/non-replaced-elements/the-hr-element-0/size-ref.html", + "==" + ] + ], + {} + ] + ], "width.html": [ "a436d2ae25a6b03f320bda066f32c374b84e0d92", [ @@ -480431,6 +480457,14 @@ "5cd35c83ada3470ad7a16d14a5028b01596bb60c", [] ], + "size-ref.html": [ + "03a21eb45737ef46247d8bdd61fe5ea0dcefef3e", + [] + ], + "size-with-color-or-noshade-ref.html": [ + "d6300e250d97d113e9ef358daa300de4bd593850", + [] + ], "width-ref.html": [ "71e7651c1ab6927f1be436ef8ff749f920924562", [] diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini index 81d2aa5b8a5..b2ea5b118c1 100644 --- a/tests/wpt/meta/html/dom/idlharness.https.html.ini +++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini @@ -5455,18 +5455,6 @@ [HTMLHeadingElement interface: document.createElement("h1") must inherit property "align" with the proper type] expected: FAIL - [HTMLHRElement interface: attribute noShade] - expected: FAIL - - [HTMLHRElement interface: attribute size] - expected: FAIL - - [HTMLHRElement interface: document.createElement("hr") must inherit property "noShade" with the proper type] - expected: FAIL - - [HTMLHRElement interface: document.createElement("hr") must inherit property "size" with the proper type] - expected: FAIL - [HTMLOListElement interface: attribute reversed] expected: FAIL diff --git a/tests/wpt/meta/html/dom/reflection-grouping.html.ini b/tests/wpt/meta/html/dom/reflection-grouping.html.ini index 556eea039e9..ec7075bc5ab 100644 --- a/tests/wpt/meta/html/dom/reflection-grouping.html.ini +++ b/tests/wpt/meta/html/dom/reflection-grouping.html.ini @@ -383,234 +383,6 @@ [hr.tabIndex: IDL set to -2147483648] expected: FAIL - [hr.noShade: typeof IDL attribute] - expected: FAIL - - [hr.noShade: IDL get with DOM attribute unset] - expected: FAIL - - [hr.noShade: setAttribute() to ""] - expected: FAIL - - [hr.noShade: setAttribute() to " foo "] - expected: FAIL - - [hr.noShade: setAttribute() to undefined] - expected: FAIL - - [hr.noShade: setAttribute() to null] - expected: FAIL - - [hr.noShade: setAttribute() to 7] - expected: FAIL - - [hr.noShade: setAttribute() to 1.5] - expected: FAIL - - [hr.noShade: setAttribute() to "5%"] - expected: FAIL - - [hr.noShade: setAttribute() to "+100"] - expected: FAIL - - [hr.noShade: setAttribute() to ".5"] - expected: FAIL - - [hr.noShade: setAttribute() to true] - expected: FAIL - - [hr.noShade: setAttribute() to false] - expected: FAIL - - [hr.noShade: setAttribute() to object "[object Object\]"] - expected: FAIL - - [hr.noShade: setAttribute() to NaN] - expected: FAIL - - [hr.noShade: setAttribute() to Infinity] - expected: FAIL - - [hr.noShade: setAttribute() to -Infinity] - expected: FAIL - - [hr.noShade: setAttribute() to "\\0"] - expected: FAIL - - [hr.noShade: setAttribute() to object "test-toString"] - expected: FAIL - - [hr.noShade: setAttribute() to object "test-valueOf"] - expected: FAIL - - [hr.noShade: setAttribute() to "noShade"] - expected: FAIL - - [hr.noShade: IDL set to ""] - expected: FAIL - - [hr.noShade: IDL set to " foo "] - expected: FAIL - - [hr.noShade: IDL set to undefined] - expected: FAIL - - [hr.noShade: IDL set to null] - expected: FAIL - - [hr.noShade: IDL set to 7] - expected: FAIL - - [hr.noShade: IDL set to 1.5] - expected: FAIL - - [hr.noShade: IDL set to "5%"] - expected: FAIL - - [hr.noShade: IDL set to "+100"] - expected: FAIL - - [hr.noShade: IDL set to ".5"] - expected: FAIL - - [hr.noShade: IDL set to false] - expected: FAIL - - [hr.noShade: IDL set to object "[object Object\]"] - expected: FAIL - - [hr.noShade: IDL set to NaN] - expected: FAIL - - [hr.noShade: IDL set to Infinity] - expected: FAIL - - [hr.noShade: IDL set to -Infinity] - expected: FAIL - - [hr.noShade: IDL set to "\\0"] - expected: FAIL - - [hr.noShade: IDL set to object "test-toString"] - expected: FAIL - - [hr.noShade: IDL set to object "test-valueOf"] - expected: FAIL - - [hr.size: typeof IDL attribute] - expected: FAIL - - [hr.size: IDL get with DOM attribute unset] - expected: FAIL - - [hr.size: setAttribute() to ""] - expected: FAIL - - [hr.size: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [hr.size: setAttribute() to undefined] - expected: FAIL - - [hr.size: setAttribute() to 7] - expected: FAIL - - [hr.size: setAttribute() to 1.5] - expected: FAIL - - [hr.size: setAttribute() to "5%"] - expected: FAIL - - [hr.size: setAttribute() to "+100"] - expected: FAIL - - [hr.size: setAttribute() to ".5"] - expected: FAIL - - [hr.size: setAttribute() to true] - expected: FAIL - - [hr.size: setAttribute() to false] - expected: FAIL - - [hr.size: setAttribute() to object "[object Object\]"] - expected: FAIL - - [hr.size: setAttribute() to NaN] - expected: FAIL - - [hr.size: setAttribute() to Infinity] - expected: FAIL - - [hr.size: setAttribute() to -Infinity] - expected: FAIL - - [hr.size: setAttribute() to "\\0"] - expected: FAIL - - [hr.size: setAttribute() to null] - expected: FAIL - - [hr.size: setAttribute() to object "test-toString"] - expected: FAIL - - [hr.size: setAttribute() to object "test-valueOf"] - expected: FAIL - - [hr.size: IDL set to ""] - expected: FAIL - - [hr.size: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [hr.size: IDL set to undefined] - expected: FAIL - - [hr.size: IDL set to 7] - expected: FAIL - - [hr.size: IDL set to 1.5] - expected: FAIL - - [hr.size: IDL set to "5%"] - expected: FAIL - - [hr.size: IDL set to "+100"] - expected: FAIL - - [hr.size: IDL set to ".5"] - expected: FAIL - - [hr.size: IDL set to true] - expected: FAIL - - [hr.size: IDL set to false] - expected: FAIL - - [hr.size: IDL set to object "[object Object\]"] - expected: FAIL - - [hr.size: IDL set to NaN] - expected: FAIL - - [hr.size: IDL set to Infinity] - expected: FAIL - - [hr.size: IDL set to -Infinity] - expected: FAIL - - [hr.size: IDL set to "\\0"] - expected: FAIL - - [hr.size: IDL set to null] - expected: FAIL - - [hr.size: IDL set to object "test-toString"] - expected: FAIL - - [hr.size: IDL set to object "test-valueOf"] - expected: FAIL - [pre.accessKey: typeof IDL attribute] expected: FAIL diff --git a/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-ref.html b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-ref.html new file mode 100644 index 00000000000..03a21eb4573 --- /dev/null +++ b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-ref.html @@ -0,0 +1,20 @@ + + + + + + +
+
+ + + diff --git a/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade-ref.html b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade-ref.html new file mode 100644 index 00000000000..d6300e250d9 --- /dev/null +++ b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade-ref.html @@ -0,0 +1,17 @@ + + + + + + +
+
+
+
+ + + diff --git a/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade.html b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade.html new file mode 100644 index 00000000000..db1d583934e --- /dev/null +++ b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size-with-color-or-noshade.html @@ -0,0 +1,15 @@ + + + +hr elements: Tests behaviour of a size attribute with color/noshade attributes present + + + + +
+
+
+
+ + + diff --git a/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size.html b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size.html new file mode 100644 index 00000000000..2162131b853 --- /dev/null +++ b/tests/wpt/tests/html/rendering/non-replaced-elements/the-hr-element-0/size.html @@ -0,0 +1,13 @@ + + + +hr elements: Tests behaviour of a size attribute without color/noshade attributes + + + + +
+
+ + +