tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 9ce25678e1d05904637db5c748841f4b42c8622c
parent f285e70076c7a1a056221d226a29648ff4f20968
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Tue, 11 Nov 2025 16:25:33 +0000

Bug 1998285 - Make computed borders not affect the computed value. r=janv

As per the resolution in
https://github.com/w3c/csswg-drafts/issues/11494.

We need to move the resolved value stuff for border properties to style
to avoid regressing shorthand serialization tests. Note we no longer
have layout-dependent borders tho.

Fix a few tests related to inheritance which I think chromium just gets
wrong for border-width, see the issue discussion. What you inherit is
the computed value.

Differential Revision: https://phabricator.services.mozilla.com/D271314

Diffstat:
Mdom/tests/mochitest/general/test_focusrings.xhtml | 27+++++++++++++++++++--------
Mlayout/generic/nsIFrame.cpp | 3+--
Mlayout/generic/nsImageFrame.cpp | 6++----
Mlayout/painting/nsCSSRendering.cpp | 2+-
Mlayout/style/RestyleManager.cpp | 1-
Mlayout/style/ServoBindings.toml | 1+
Mlayout/style/ServoCSSPropList.mako.py | 4----
Mlayout/style/nsChangeHint.h | 50+++++++++++++++++++++-----------------------------
Mlayout/style/nsComputedDOMStyle.cpp | 29-----------------------------
Mlayout/style/nsComputedDOMStyle.h | 8--------
Mlayout/style/nsStyleStruct.cpp | 68+++++++++++++++++---------------------------------------------------
Mlayout/style/nsStyleStruct.h | 114++++++++++++++++++++++++++++---------------------------------------------------
Mlayout/style/test/property_database.js | 4+---
Mservo/components/style/properties/gecko.mako.rs | 122+++----------------------------------------------------------------------------
Mservo/components/style/properties/longhands/border.mako.rs | 6++++--
Mservo/components/style/properties/longhands/column.mako.rs | 2+-
Mservo/components/style/properties/longhands/outline.mako.rs | 2+-
Mservo/components/style/properties/properties.mako.rs | 19+++++--------------
Mservo/components/style/style_adjuster.rs | 45+--------------------------------------------
Mservo/components/style/values/computed/border.rs | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mservo/components/style/values/resolved/mod.rs | 7+++++--
Mservo/components/style/values/specified/border.rs | 19+++++++++++--------
Mservo/ports/geckolib/cbindgen.toml | 1+
Mservo/ports/geckolib/glue.rs | 9+++++----
Dtesting/web-platform/meta/css/css-backgrounds/animations/border-width-interpolation.html.ini | 50--------------------------------------------------
Mtesting/web-platform/meta/css/css-multicol/parsing/column-rule-computed.html.ini | 3---
Dtesting/web-platform/meta/css/css-multicol/parsing/column-rule-width-computed.html.ini | 5-----
Dtesting/web-platform/meta/css/css-ui/animation/outline-width-interpolation.html.ini | 51---------------------------------------------------
Dtesting/web-platform/meta/css/css-ui/outline-009.html.ini | 2--
Dtesting/web-platform/meta/css/css-ui/parsing/outline-width-computed.html.ini | 5-----
Atesting/web-platform/tests/css/CSS2/borders/border-width-011-ref.xht | 12++++++++++++
Mtesting/web-platform/tests/css/CSS2/borders/border-width-011.xht | 9++++-----
Mtesting/web-platform/tests/css/CSS2/borders/border-width-012.xht | 62++++++++++++++++++++++----------------------------------------
Mtesting/web-platform/tests/css/CSS2/ui/outline-width-096-ref.xht | 3++-
Mtesting/web-platform/tests/css/CSS2/ui/outline-width-096.xht | 4++--
Mtesting/web-platform/tests/css/css-backgrounds/animations/border-width-interpolation.html | 10+++++-----
36 files changed, 252 insertions(+), 579 deletions(-)

diff --git a/dom/tests/mochitest/general/test_focusrings.xhtml b/dom/tests/mochitest/general/test_focusrings.xhtml @@ -45,6 +45,19 @@ async function initTest() { runTest(); } +function hasOutline(element) { + let cs = getComputedStyle(element); + return cs.outlineWidth != "0px" && cs.outlineStyle != "none"; +} + +function checkRing(element, width, message) { + const expectOutline = width != "0px"; + is(hasOutline(element), expectOutline, message); + if (expectOutline) { + is(getComputedStyle(element).outlineWidth, width, message); + } +} + function runTest() { // Make sure our snapshots don't depend on mouse position. @@ -54,8 +67,7 @@ function runTest() function checkFocus(element, visible, testid) { - var outline = getComputedStyle(element, "").outlineWidth; - is(outline, visible ? "2px" : "0px", testid); + is(hasOutline(element), visible, testid); } // make sure that a focus ring appears on the focused button @@ -75,7 +87,7 @@ function runTest() // kind of real user input, mouse or key, was last entered. But we can handle // the test regardless of which user input last occurred. $("l1").focus(); - var expectedVisible = (!isWinOrLinux || getComputedStyle($("l1"), "").outlineWidth == "2px"); + var expectedVisible = !isWinOrLinux || hasOutline($("l1")); testHTMLElements(htmlElements, isWinOrLinux && !expectedVisible); $("l1").focus(); @@ -84,7 +96,7 @@ function runTest() checkFocus($("l2"), true, "appearance on list after focus() with :focus"); - is(getComputedStyle($("l1"), "").outlineWidth, "0px", "appearance on previous list after focus() with :focus"); + ok(!hasOutline($("l1")), "appearance on previous list after focus() with :focus"); synthesizeMouse($("l1"), 4, 4, { }); checkFocus($("l1"), false, "appearance on list after mouse focus with :moz-focusring"); @@ -176,8 +188,8 @@ function testHTMLElements(list, expectedNoRingsOnWin) { synthesizeMouse(elem, 8, 8, { }, childwin); is(childdoc.activeElement, shouldFocus ? elem : childdoc.body, "mouse click on " + list[e]); - is(childwin.getComputedStyle(elem).outlineWidth, mouseRingSize, "mouse click on " + list[e] + " ring"); - is(elem.matches(":-moz-focusring"), expectedMatchWithMouse, "mouse click on " + list[e] + " selector"); + is(elem.matches(":focus-visible"), expectedMatchWithMouse, "mouse click on " + list[e] + " selector"); + checkRing(elem, mouseRingSize, "mouse click on " + list[e] + " ring"); if (childdoc.activeElement) childdoc.activeElement.blur(); @@ -189,8 +201,7 @@ function testHTMLElements(list, expectedNoRingsOnWin) { elem.focus(); is(childdoc.activeElement, elem, "focus() on " + list[e]); - is(childwin.getComputedStyle(elem).outlineWidth, ringSize, - "focus() on " + list[e] + " ring"); + checkRing(elem, ringSize, "focus() on " + list[e] + " ring"); childdoc.activeElement.blur(); diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp @@ -10802,8 +10802,7 @@ static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame, pc->Theme()->GetWidgetOverflow(pc->DeviceContext(), aFrame, StyleAppearance::FocusOutline, &outerRect); } else { - const nscoord width = outline->GetOutlineWidth(); - outerRect.Inflate(width); + outerRect.Inflate(outline->mOutlineWidth); } nsRect& vo = aOverflowAreas.InkOverflow(); diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp @@ -1868,10 +1868,8 @@ struct nsRecessedBorder : public nsStyleBorder { explicit nsRecessedBorder(nscoord aBorderWidth) { for (const auto side : AllPhysicalSides()) { BorderColorFor(side) = StyleColor::Black(); - mBorder.Side(side) = aBorderWidth; - // Note: use SetBorderStyle here because we want to affect - // mComputedBorder - SetBorderStyle(side, StyleBorderStyle::Inset); + mBorder.Get(side) = aBorderWidth; + mBorderStyle.Get(side) = StyleBorderStyle::Inset; } } }; diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp @@ -963,7 +963,7 @@ nsCSSRendering::CreateBorderRendererForNonThemedOutline( return Nothing(); } - const nscoord width = ourOutline->GetOutlineWidth(); + const nscoord width = ourOutline->mOutlineWidth; StyleBorderStyle outlineStyle; // Themed outlines are handled by our callers, if supported. diff --git a/layout/style/RestyleManager.cpp b/layout/style/RestyleManager.cpp @@ -687,7 +687,6 @@ nsCString RestyleManager::ChangeHintToString(nsChangeHint aHint) { "ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock", - "BorderStyleNoneChange", "SchedulePaint", "NeutralChange", "InvalidateRenderingObservers", diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml @@ -634,6 +634,7 @@ cbindgen-types = [ { gecko = "StyleAu", servo = "app_units::Au" }, { gecko = "StyleAnchorName", servo = "crate::values::computed::position::AnchorName" }, { gecko = "StyleAnchorScope", servo = "crate::values::computed::position::AnchorScope" }, + { gecko = "StyleBorderSideWidth", servo = "crate::values::computed::border::BorderSideWidth" }, { gecko = "StylePositionAnchor", servo = "crate::values::computed::position::PositionAnchor" }, { gecko = "StylePositionArea", servo = "crate::values::computed::position::PositionArea" }, { gecko = "StylePositionVisibility", servo = "crate::values::computed::position::PositionVisibility" }, diff --git a/layout/style/ServoCSSPropList.mako.py b/layout/style/ServoCSSPropList.mako.py @@ -76,10 +76,6 @@ LONGHANDS_NOT_SERIALIZED_WITH_SERVO = [ "right", "bottom", "left", - "border-top-width", - "border-right-width", - "border-bottom-width", - "border-left-width", "margin-top", "margin-right", "margin-bottom", diff --git a/layout/style/nsChangeHint.h b/layout/style/nsChangeHint.h @@ -136,18 +136,10 @@ enum nsChangeHint : uint32_t { nsChangeHint_UpdateContainingBlock = 1 << 16, /** - * This change hint has *no* change handling behavior. However, it - * exists to be a non-inherited hint, because when the border-style - * changes, and it's inherited by a child, that might require a reflow - * due to the border-width change on the child. - */ - nsChangeHint_BorderStyleNoneChange = 1 << 17, - - /** * This will schedule an invalidating paint. This is useful if something * has changed which will be invalidated by DLBI. */ - nsChangeHint_SchedulePaint = 1 << 18, + nsChangeHint_SchedulePaint = 1 << 17, /** * A hint reflecting that style data changed with no change handling @@ -163,12 +155,12 @@ enum nsChangeHint : uint32_t { * different data would be cached information that would be re-calculated * to the same values, such as nsStyleBorder::mSubImages.) */ - nsChangeHint_NeutralChange = 1 << 19, + nsChangeHint_NeutralChange = 1 << 18, /** * This will cause rendering observers to be invalidated. */ - nsChangeHint_InvalidateRenderingObservers = 1 << 20, + nsChangeHint_InvalidateRenderingObservers = 1 << 19, /** * Indicates that the reflow changes the size or position of the @@ -176,13 +168,13 @@ enum nsChangeHint : uint32_t { * parent. Must be not be set without also setting nsChangeHint_NeedReflow. * And consider adding nsChangeHint_ClearAncestorIntrinsics if needed. */ - nsChangeHint_ReflowChangesSizeOrPosition = 1 << 21, + nsChangeHint_ReflowChangesSizeOrPosition = 1 << 20, /** * Indicates that the style changes the computed BSize --- e.g. 'height'. * Must not be set without also setting nsChangeHint_NeedReflow. */ - nsChangeHint_UpdateComputedBSize = 1 << 22, + nsChangeHint_UpdateComputedBSize = 1 << 21, /** * Indicates that the 'opacity' property changed between 1 and non-1. @@ -192,7 +184,7 @@ enum nsChangeHint : uint32_t { * Note that we do not send this hint if the non-1 value was 0.99 or * greater, since in that case we send a RepaintFrame hint instead. */ - nsChangeHint_UpdateUsesOpacity = 1 << 23, + nsChangeHint_UpdateUsesOpacity = 1 << 22, /** * Indicates that the 'background-position' property changed. @@ -201,13 +193,13 @@ enum nsChangeHint : uint32_t { * the frame does not build individual background image display items * for each background layer. */ - nsChangeHint_UpdateBackgroundPosition = 1 << 24, + nsChangeHint_UpdateBackgroundPosition = 1 << 23, /** * Indicates that a frame has changed to or from having the CSS * transform property set. */ - nsChangeHint_AddOrRemoveTransform = 1 << 25, + nsChangeHint_AddOrRemoveTransform = 1 << 24, /** * Indicates that the presence of scrollbars might have changed. @@ -219,13 +211,13 @@ enum nsChangeHint : uint32_t { * scrollframe, this is instead equivalent to nsChangeHint_AllReflowHints * (because the viewport always has an associated scrollframe). */ - nsChangeHint_ScrollbarChange = 1 << 26, + nsChangeHint_ScrollbarChange = 1 << 25, /** * Indicates that there has been a colspan or rowspan attribute change * on the cells of a table. */ - nsChangeHint_UpdateTableCellSpans = 1 << 27, + nsChangeHint_UpdateTableCellSpans = 1 << 26, /** * Indicates that the visiblity property changed. @@ -233,7 +225,7 @@ enum nsChangeHint : uint32_t { * visibility:hidden elements in the case where the elements have no visible * descendants. */ - nsChangeHint_VisibilityChange = 1u << 28, + nsChangeHint_VisibilityChange = 1u << 27, // IMPORTANT NOTE: When adding a new hint, you will need to add it to // one of: @@ -250,7 +242,7 @@ enum nsChangeHint : uint32_t { /** * Dummy hint value for all hints. It exists for compile time check. */ - nsChangeHint_AllHints = uint32_t((1ull << 29) - 1), + nsChangeHint_AllHints = uint32_t((1ull << 28) - 1), }; // Redefine these operators to return nothing. This will catch any use @@ -322,15 +314,15 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) { nsChangeHint_VisibilityChange) // The change hints that are never handled for descendants. -#define nsChangeHint_Hints_NeverHandledForDescendants \ - (nsChangeHint_BorderStyleNoneChange | nsChangeHint_ChildrenOnlyTransform | \ - nsChangeHint_ScrollbarChange | nsChangeHint_InvalidateRenderingObservers | \ - nsChangeHint_RecomputePosition | nsChangeHint_UpdateBackgroundPosition | \ - nsChangeHint_UpdateComputedBSize | nsChangeHint_UpdateContainingBlock | \ - nsChangeHint_UpdateEffects | nsChangeHint_UpdateOpacityLayer | \ - nsChangeHint_UpdateOverflow | nsChangeHint_UpdateParentOverflow | \ - nsChangeHint_UpdatePostTransformOverflow | \ - nsChangeHint_UpdateTableCellSpans | nsChangeHint_UpdateTransformLayer | \ +#define nsChangeHint_Hints_NeverHandledForDescendants \ + (nsChangeHint_ChildrenOnlyTransform | nsChangeHint_ScrollbarChange | \ + nsChangeHint_InvalidateRenderingObservers | \ + nsChangeHint_RecomputePosition | nsChangeHint_UpdateBackgroundPosition | \ + nsChangeHint_UpdateComputedBSize | nsChangeHint_UpdateContainingBlock | \ + nsChangeHint_UpdateEffects | nsChangeHint_UpdateOpacityLayer | \ + nsChangeHint_UpdateOverflow | nsChangeHint_UpdateParentOverflow | \ + nsChangeHint_UpdatePostTransformOverflow | \ + nsChangeHint_UpdateTableCellSpans | nsChangeHint_UpdateTransformLayer | \ nsChangeHint_UpdateUsesOpacity | nsChangeHint_AddOrRemoveTransform) // The change hints that are sometimes considered to be handled for descendants. diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp @@ -788,14 +788,6 @@ static bool MayNeedToFlushLayout(nsCSSPropertyID aPropID) { case eCSSProperty_perspective_origin: case eCSSProperty_transform_origin: case eCSSProperty_transform: - case eCSSProperty_border_top_width: - case eCSSProperty_border_bottom_width: - case eCSSProperty_border_left_width: - case eCSSProperty_border_right_width: - case eCSSProperty_border_block_start_width: - case eCSSProperty_border_block_end_width: - case eCSSProperty_border_inline_start_width: - case eCSSProperty_border_inline_end_width: case eCSSProperty_top: case eCSSProperty_right: case eCSSProperty_bottom: @@ -1727,22 +1719,6 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() { return GetPaddingWidthFor(eSideRight); } -already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderTopWidth() { - return GetBorderWidthFor(eSideTop); -} - -already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderBottomWidth() { - return GetBorderWidthFor(eSideBottom); -} - -already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderLeftWidth() { - return GetBorderWidthFor(eSideLeft); -} - -already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderRightWidth() { - return GetBorderWidthFor(eSideRight); -} - already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginTop() { return GetMarginFor(eSideTop); } @@ -2065,11 +2041,6 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor( return AppUnitsToCSSValue(mInnerFrame->GetUsedPadding().Side(aSide)); } -already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor( - mozilla::Side aSide) { - return AppUnitsToCSSValue(StyleBorder()->GetComputedBorderWidth(aSide)); -} - already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginFor(Side aSide) { // Use raw margin here, layout-dependent margins should be stored in used // margin. diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h @@ -198,8 +198,6 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration, already_AddRefed<CSSValue> GetPaddingWidthFor(mozilla::Side aSide); - already_AddRefed<CSSValue> GetBorderWidthFor(mozilla::Side aSide); - already_AddRefed<CSSValue> GetMarginFor(mozilla::Side aSide); already_AddRefed<CSSValue> GetTransformValue(const mozilla::StyleTransform&); @@ -253,12 +251,6 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration, already_AddRefed<CSSValue> DoGetPaddingLeft(); already_AddRefed<CSSValue> DoGetPaddingRight(); - /* Border Properties */ - already_AddRefed<CSSValue> DoGetBorderTopWidth(); - already_AddRefed<CSSValue> DoGetBorderBottomWidth(); - already_AddRefed<CSSValue> DoGetBorderLeftWidth(); - already_AddRefed<CSSValue> DoGetBorderRightWidth(); - /* Margin Properties */ already_AddRefed<CSSValue> DoGetMarginTop(); already_AddRefed<CSSValue> DoGetMarginBottom(); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp @@ -411,18 +411,13 @@ nsStyleBorder::nsStyleBorder() StyleBorderImageRepeatKeyword::Stretch}, mFloatEdge(StyleFloatEdge::ContentBox), mBoxDecorationBreak(StyleBoxDecorationBreak::Slice), + mBorderStyle(StyleRectWithAllSides(StyleBorderStyle::None)), + mBorder(StyleRectWithAllSides(kMediumBorderWidth)), mBorderTopColor(StyleColor::CurrentColor()), mBorderRightColor(StyleColor::CurrentColor()), mBorderBottomColor(StyleColor::CurrentColor()), - mBorderLeftColor(StyleColor::CurrentColor()), - mComputedBorder(0, 0, 0, 0) { + mBorderLeftColor(StyleColor::CurrentColor()) { MOZ_COUNT_CTOR(nsStyleBorder); - - nscoord medium = kMediumBorderWidth; - for (const auto side : mozilla::AllPhysicalSides()) { - mBorder.Side(side) = medium; - mBorderStyle[side] = StyleBorderStyle::None; - } } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) @@ -434,16 +429,13 @@ nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) mBorderImageRepeat(aSrc.mBorderImageRepeat), mFloatEdge(aSrc.mFloatEdge), mBoxDecorationBreak(aSrc.mBoxDecorationBreak), + mBorderStyle(aSrc.mBorderStyle), + mBorder(aSrc.mBorder), mBorderTopColor(aSrc.mBorderTopColor), mBorderRightColor(aSrc.mBorderRightColor), mBorderBottomColor(aSrc.mBorderBottomColor), - mBorderLeftColor(aSrc.mBorderLeftColor), - mComputedBorder(aSrc.mComputedBorder), - mBorder(aSrc.mBorder) { + mBorderLeftColor(aSrc.mBorderLeftColor) { MOZ_COUNT_CTOR(nsStyleBorder); - for (const auto side : mozilla::AllPhysicalSides()) { - mBorderStyle[side] = aSrc.mBorderStyle[side]; - } } void nsStyleBorder::TriggerImageLoads(Document& aDocument, @@ -459,6 +451,7 @@ nsMargin nsStyleBorder::GetImageOutset() const { // the initial values yields 0 outset) so that we don't have to // reflow to update overflow areas when an image loads. nsMargin outset; + auto computedBorder = GetComputedBorder(); for (const auto s : mozilla::AllPhysicalSides()) { const auto& coord = mBorderImageOutset.Get(s); nscoord value; @@ -466,7 +459,7 @@ nsMargin nsStyleBorder::GetImageOutset() const { value = coord.AsLength().ToAppUnits(); } else { MOZ_ASSERT(coord.IsNumber()); - value = coord.AsNumber() * mComputedBorder.Side(s); + value = coord.AsNumber() * computedBorder.Side(s); } outset.Side(s) = value; } @@ -490,23 +483,7 @@ nsChangeHint nsStyleBorder::CalcDifference( } for (const auto ix : mozilla::AllPhysicalSides()) { - // See the explanation in nsChangeHint.h of - // nsChangeHint_BorderStyleNoneChange . - // Furthermore, even though we know *this* side is 0 width, just - // assume a repaint hint for some other change rather than bother - // tracking this result through the rest of the function. - if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) { - return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange; - } - } - - // Note that mBorderStyle stores not only the border style but also - // color-related flags. Given that we've already done an mComputedBorder - // comparison, border-style differences can only lead to a repaint hint. So - // it's OK to just compare the values directly -- if either the actual - // style or the color flags differ we want to repaint. - for (const auto ix : mozilla::AllPhysicalSides()) { - if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] || + if (mBorderStyle.Get(ix) != aNewData.mBorderStyle.Get(ix) || BorderColorFor(ix) != aNewData.BorderColorFor(ix)) { return nsChangeHint_RepaintFrame; } @@ -535,12 +512,9 @@ nsChangeHint nsStyleBorder::CalcDifference( // mBorder is the specified border value. Changes to this don't // need any change processing, since we operate on the computed // border values instead. - if (mBorder != aNewData.mBorder) { - return nsChangeHint_NeutralChange; - } - // mBorderImage* fields are checked only when border-image is not 'none'. - if (mBorderImageSource != aNewData.mBorderImageSource || + if (mBorder != aNewData.mBorder || + mBorderImageSource != aNewData.mBorderImageSource || mBorderImageRepeat != aNewData.mBorderImageRepeat || mBorderImageSlice != aNewData.mBorderImageSlice || mBorderImageWidth != aNewData.mBorderImageWidth) { @@ -554,8 +528,7 @@ nsStyleOutline::nsStyleOutline() : mOutlineWidth(kMediumBorderWidth), mOutlineOffset(0), mOutlineColor(StyleColor::CurrentColor()), - mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)), - mActualOutlineWidth(0) { + mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)) { MOZ_COUNT_CTOR(nsStyleOutline); } @@ -563,8 +536,7 @@ nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc) : mOutlineWidth(aSrc.mOutlineWidth), mOutlineOffset(aSrc.mOutlineOffset), mOutlineColor(aSrc.mOutlineColor), - mOutlineStyle(aSrc.mOutlineStyle), - mActualOutlineWidth(aSrc.mActualOutlineWidth) { + mOutlineStyle(aSrc.mOutlineStyle) { MOZ_COUNT_CTOR(nsStyleOutline); } @@ -574,7 +546,7 @@ nsChangeHint nsStyleOutline::CalcDifference( // We need the explicit 'outline-style: auto' check because // 'outline-style: auto' effectively also changes 'outline-width'. if (shouldPaintOutline != aNewData.ShouldPaintOutline() || - mActualOutlineWidth != aNewData.mActualOutlineWidth || + mOutlineWidth != aNewData.mOutlineWidth || mOutlineStyle.IsAuto() != aNewData.mOutlineStyle.IsAuto() || (shouldPaintOutline && mOutlineOffset != aNewData.mOutlineOffset)) { return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint | @@ -702,8 +674,7 @@ nsStyleColumn::nsStyleColumn() : mColumnWidth(LengthOrAuto::Auto()), mColumnRuleColor(StyleColor::CurrentColor()), mColumnRuleStyle(StyleBorderStyle::None), - mColumnRuleWidth(kMediumBorderWidth), - mActualColumnRuleWidth(0) { + mColumnRuleWidth(kMediumBorderWidth) { MOZ_COUNT_CTOR(nsStyleColumn); } @@ -714,8 +685,7 @@ nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) mColumnRuleStyle(aSource.mColumnRuleStyle), mColumnFill(aSource.mColumnFill), mColumnSpan(aSource.mColumnSpan), - mColumnRuleWidth(aSource.mColumnRuleWidth), - mActualColumnRuleWidth(aSource.mActualColumnRuleWidth) { + mColumnRuleWidth(aSource.mColumnRuleWidth) { MOZ_COUNT_CTOR(nsStyleColumn); } @@ -736,16 +706,12 @@ nsChangeHint nsStyleColumn::CalcDifference( return NS_STYLE_HINT_REFLOW; } - if (mActualColumnRuleWidth != aNewData.mActualColumnRuleWidth || + if (mColumnRuleWidth != aNewData.mColumnRuleWidth || mColumnRuleStyle != aNewData.mColumnRuleStyle || mColumnRuleColor != aNewData.mColumnRuleColor) { return NS_STYLE_HINT_VISUAL; } - if (mColumnRuleWidth != aNewData.mColumnRuleWidth) { - return nsChangeHint_NeutralChange; - } - return nsChangeHint(0); } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h @@ -530,9 +530,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePadding { ((l) == 0) ? 0 : std::max((tpp), (l) / (tpp) * (tpp)) // Returns if the given border style type is visible or not -static bool IsVisibleBorderStyle(mozilla::StyleBorderStyle aStyle) { - return (aStyle != mozilla::StyleBorderStyle::None && - aStyle != mozilla::StyleBorderStyle::Hidden); +static inline bool IsVisibleBorderStyle(mozilla::StyleBorderStyle aStyle) { + return aStyle != mozilla::StyleBorderStyle::None && + aStyle != mozilla::StyleBorderStyle::Hidden; } struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder { @@ -546,28 +546,34 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder { // if border-style is none, but there is a loaded border image, // HasVisibleStyle will be false even though there *is* a border. bool HasVisibleStyle(mozilla::Side aSide) const { - return IsVisibleBorderStyle(mBorderStyle[aSide]); + return IsVisibleBorderStyle(mBorderStyle.Get(aSide)); } // aBorderWidth is in twips void SetBorderWidth(mozilla::Side aSide, nscoord aBorderWidth, nscoord aAppUnitsPerDevPixel) { - nscoord roundedWidth = + mBorder.Get(aSide) = NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, aAppUnitsPerDevPixel); - mBorder.Side(aSide) = roundedWidth; - if (HasVisibleStyle(aSide)) { - mComputedBorder.Side(aSide) = roundedWidth; - } } - // Get the computed border (plus rounding). This does consider the - // effects of 'border-style: none', but does not consider - // 'border-image'. - const nsMargin& GetComputedBorder() const { return mComputedBorder; } + // Get the computed border (plus rounding). + nsMargin GetComputedBorder() const { + nsMargin border(mBorder._0, mBorder._1, mBorder._2, mBorder._3); + for (auto side : mozilla::AllPhysicalSides()) { + if (!HasVisibleStyle(side)) { + border.Side(side) = 0; + } + } + return border; + } bool HasBorder() const { - return mComputedBorder != nsMargin(0, 0, 0, 0) || - !mBorderImageSource.IsNone(); + for (auto side : mozilla::AllPhysicalSides()) { + if (mBorder.Get(side) && HasVisibleStyle(side)) { + return true; + } + } + return !mBorderImageSource.IsNone(); } // Get the actual border width for a particular side, in appunits. Note that @@ -575,19 +581,20 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder { // side. That is, this value takes into account the border style and the // value is rounded to the nearest device pixel by NS_ROUND_BORDER_TO_PIXELS. nscoord GetComputedBorderWidth(mozilla::Side aSide) const { - return GetComputedBorder().Side(aSide); + if (!HasVisibleStyle(aSide)) { + return 0; + } + return mBorder.Get(aSide); } mozilla::StyleBorderStyle GetBorderStyle(mozilla::Side aSide) const { NS_ASSERTION(aSide <= mozilla::eSideLeft, "bad side"); - return mBorderStyle[aSide]; + return mBorderStyle.Get(aSide); } void SetBorderStyle(mozilla::Side aSide, mozilla::StyleBorderStyle aStyle) { NS_ASSERTION(aSide <= mozilla::eSideLeft, "bad side"); - mBorderStyle[aSide] = aStyle; - mComputedBorder.Side(aSide) = - (HasVisibleStyle(aSide) ? mBorder.Side(aSide) : 0); + mBorderStyle.Get(aSide) = aStyle; } inline bool IsBorderImageSizeAvailable() const { @@ -611,7 +618,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder { mozilla::StyleBoxDecorationBreak mBoxDecorationBreak; protected: - mozilla::StyleBorderStyle mBorderStyle[4]; // StyleBorderStyle::* + mozilla::StyleRect<mozilla::StyleBorderStyle> mBorderStyle; + mozilla::StyleRect<mozilla::StyleBorderSideWidth> mBorder; public: // the colors to use for a simple border. @@ -668,66 +676,28 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder { } nsStyleBorder& operator=(const nsStyleBorder&) = delete; - - protected: - // mComputedBorder holds the CSS2.1 computed border-width values. - // In particular, these widths take into account the border-style - // for the relevant side, and the values are rounded to the nearest - // device pixel (which is not part of the definition of computed - // values). The presence or absence of a border-image does not - // affect border-width values. - nsMargin mComputedBorder; - - // mBorder holds the nscoord values for the border widths as they - // would be if all the border-style values were visible (not hidden - // or none). This member exists so that when we create structs - // using the copy constructor during style resolution the new - // structs will know what the specified values of the border were in - // case they have more specific rules setting the border style. - // - // Note that this isn't quite the CSS specified value, since this - // has had the enumerated border widths converted to lengths, and - // all lengths converted to twips. But it's not quite the computed - // value either. The values are rounded to the nearest device pixel. - nsMargin mBorder; }; struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleOutline { STYLE_STRUCT(nsStyleOutline) nsStyleOutline(); - // This is the specified value of outline-width, but with length values - // computed to absolute. mActualOutlineWidth stores the outline-width - // value used by layout. (We must store mOutlineWidth for the same - // style struct resolution reasons that we do nsStyleBorder::mBorder; - // see that field's comment.) - nscoord mOutlineWidth; + mozilla::StyleBorderSideWidth mOutlineWidth; mozilla::StyleAu mOutlineOffset; mozilla::StyleColor mOutlineColor; mozilla::StyleOutlineStyle mOutlineStyle; - nscoord GetOutlineWidth() const { return mActualOutlineWidth; } - bool ShouldPaintOutline() const { if (mOutlineStyle.IsAuto()) { return true; } - if (GetOutlineWidth() > 0) { - MOZ_ASSERT( - mOutlineStyle.AsBorderStyle() != mozilla::StyleBorderStyle::None, - "outline-style: none implies outline-width of zero"); - return true; + if (!IsVisibleBorderStyle(mOutlineStyle.AsBorderStyle())) { + return false; } - return false; + return mOutlineWidth > 0; } nsSize EffectiveOffsetFor(const nsRect& aRect) const; - - protected: - // The actual value of outline-width is the computed value (an absolute - // length, forced to zero when outline-style is none) rounded to device - // pixels. This is the value used by layout. - nscoord mActualOutlineWidth; }; struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList { @@ -2190,7 +2160,12 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleColumn { mozilla::StyleColumnFill mColumnFill = mozilla::StyleColumnFill::Balance; mozilla::StyleColumnSpan mColumnSpan = mozilla::StyleColumnSpan::None; - nscoord GetColumnRuleWidth() const { return mActualColumnRuleWidth; } + nscoord GetColumnRuleWidth() const { + if (!IsVisibleBorderStyle(mColumnRuleStyle)) { + return 0; + } + return mColumnRuleWidth; + } bool IsColumnContainerStyle() const { return !mColumnCount.IsAuto() || !mColumnWidth.IsAuto(); @@ -2201,16 +2176,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleColumn { } protected: - // This is the specified value of column-rule-width, but with length values - // computed to absolute. mActualColumnRuleWidth stores the column-rule-width - // value used by layout. (We must store mColumnRuleWidth for the same - // style struct resolution reasons that we do nsStyleBorder::mBorder; - // see that field's comment.) - nscoord mColumnRuleWidth; - // The actual value of column-rule-width is the computed value (an absolute - // length, forced to zero when column-rule-style is none) rounded to device - // pixels. This is the value used by layout. - nscoord mActualColumnRuleWidth; + mozilla::StyleBorderSideWidth mColumnRuleWidth; }; struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVG { diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js @@ -7082,13 +7082,11 @@ var gCSSProperties = { initial_values: [ "none", "medium", - "thin", - // XXX Should be invert, but currently currentcolor. - //"invert", "none medium invert" "currentColor", "none medium currentcolor", ], other_values: [ + "thin", "solid", "medium solid", "green solid", diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs @@ -12,7 +12,6 @@ use crate::Atom; use crate::logical_geometry::PhysicalSide; -use app_units::Au; use crate::computed_value_flags::*; use crate::custom_properties::ComputedCustomProperties; use crate::gecko_bindings::bindings; @@ -36,7 +35,7 @@ use servo_arc::{Arc, UniqueArc}; use std::mem::{forget, MaybeUninit, ManuallyDrop}; use std::{ops, ptr}; use crate::values; -use crate::values::computed::{BorderStyle, Time, Zoom}; +use crate::values::computed::{Time, Zoom}; use crate::values::computed::font::FontSize; @@ -568,67 +567,7 @@ fn static_assert() { } -<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) - for x in SIDES - for y in ["style", "width"]]) %> - -<%self:impl_trait style_struct_name="Border" - skip_longhands="${skip_border_longhands}"> - % for side in SIDES: - pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) { - self.mBorderStyle[${side.index}] = v; - - // This is needed because the initial mComputedBorder value is set to - // zero. - // - // In order to compute stuff, we start from the initial struct, and keep - // going down the tree applying properties. - // - // That means, effectively, that when we set border-style to something - // non-hidden, we should use the initial border instead. - // - // Servo stores the initial border-width in the initial struct, and then - // adjusts as needed in the fixup phase. This means that the initial - // struct is technically not valid without fixups, and that you lose - // pretty much any sharing of the initial struct, which is kind of - // unfortunate. - // - // Gecko has two fields for this, one that stores the "specified" - // border, and other that stores the actual computed one. That means - // that when we set border-style, border-width may change and we need to - // sync back to the specified one. This is what this function does. - // - // Note that this doesn't impose any dependency in the order of - // computation of the properties. This is only relevant if border-style - // is specified, but border-width isn't. If border-width is specified at - // some point, the two mBorder and mComputedBorder fields would be the - // same already. - // - // Once we're here, we know that we'll run style fixups, so it's fine to - // just copy the specified border here, we'll adjust it if it's - // incorrect later. - self.mComputedBorder.${side.ident} = self.mBorder.${side.ident}; - } - - pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) { - self.set_border_${side.ident}_style(other.mBorderStyle[${side.index}]); - } - - pub fn reset_border_${side.ident}_style(&mut self, other: &Self) { - self.copy_border_${side.ident}_style_from(other); - } - - #[inline] - pub fn clone_border_${side.ident}_style(&self) -> BorderStyle { - self.mBorderStyle[${side.index}] - } - - ${impl_border_width("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, "mBorder.%s" % side.ident)} - - pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { - self.mComputedBorder.${side.ident} != 0 - } - % endfor +<%self:impl_trait style_struct_name="Border"> </%self:impl_trait> <%self:impl_trait style_struct_name="Margin"> @@ -645,34 +584,7 @@ fn static_assert() { } </%self:impl_trait> -<%self:impl_trait style_struct_name="Outline" - skip_longhands="outline-style outline-width"> - - pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) { - self.mOutlineStyle = v; - // NB: This is needed to correctly handling the initial value of - // outline-width when outline-style changes, see the - // update_border_${side.ident} comment for more details. - self.mActualOutlineWidth = self.mOutlineWidth; - } - - pub fn copy_outline_style_from(&mut self, other: &Self) { - self.set_outline_style(other.mOutlineStyle); - } - - pub fn reset_outline_style(&mut self, other: &Self) { - self.copy_outline_style_from(other) - } - - pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T { - self.mOutlineStyle.clone() - } - - ${impl_border_width("outline_width", "mActualOutlineWidth", "mOutlineWidth")} - - pub fn outline_has_nonzero_width(&self) -> bool { - self.mActualOutlineWidth != 0 - } +<%self:impl_trait style_struct_name="Outline"> </%self:impl_trait> <% skip_font_longhands = """font-size -x-lang font-feature-settings font-variation-settings""" %> @@ -1273,33 +1185,7 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask- } </%self:impl_trait> -<%self:impl_trait style_struct_name="Column" - skip_longhands="column-rule-width column-rule-style"> - pub fn set_column_rule_style(&mut self, v: longhands::column_rule_style::computed_value::T) { - self.mColumnRuleStyle = v; - // NB: This is needed to correctly handling the initial value of - // column-rule-width when colun-rule-style changes, see the - // update_border_${side.ident} comment for more details. - self.mActualColumnRuleWidth = self.mColumnRuleWidth; - } - - pub fn copy_column_rule_style_from(&mut self, other: &Self) { - self.set_column_rule_style(other.mColumnRuleStyle); - } - - pub fn reset_column_rule_style(&mut self, other: &Self) { - self.copy_column_rule_style_from(other) - } - - pub fn clone_column_rule_style(&self) -> longhands::column_rule_style::computed_value::T { - self.mColumnRuleStyle.clone() - } - - ${impl_border_width("column_rule_width", "mActualColumnRuleWidth", "mColumnRuleWidth")} - - pub fn column_rule_has_nonzero_width(&self) -> bool { - self.mActualColumnRuleWidth != 0 - } +<%self:impl_trait style_struct_name="Column"> </%self:impl_trait> <%self:impl_trait style_struct_name="Counters"> diff --git a/servo/components/style/properties/longhands/border.mako.rs b/servo/components/style/properties/longhands/border.mako.rs @@ -12,7 +12,7 @@ else: return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind) %> -% for side in ALL_SIDES: +% for index, side in enumerate(ALL_SIDES): <% side_name = side[0] is_logical = side[1] @@ -37,6 +37,7 @@ aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"), spec=maybe_logical_spec(side, "style"), animation_type="discrete" if not is_logical else "none", + gecko_ffi_name="mBorderStyle.{}".format(index), logical=is_logical, logical_group="border-style", affects="layout", @@ -45,13 +46,14 @@ ${helpers.predefined_type( "border-%s-width" % side_name, "BorderSideWidth", - "app_units::Au::from_px(3)", + "computed::BorderSideWidth::medium()", engines="gecko servo", aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"), spec=maybe_logical_spec(side, "width"), logical=is_logical, logical_group="border-width", allow_quirks="No" if is_logical else "Yes", + gecko_ffi_name="mBorder.{}".format(index), servo_restyle_damage="rebuild_box", affects="layout", )} diff --git a/servo/components/style/properties/longhands/column.mako.rs b/servo/components/style/properties/longhands/column.mako.rs @@ -41,7 +41,7 @@ ${helpers.single_keyword( ${helpers.predefined_type( "column-rule-width", "BorderSideWidth", - "app_units::Au::from_px(3)", + "computed::BorderSideWidth::medium()", engines="gecko", initial_specified_value="specified::BorderSideWidth::medium()", spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width", diff --git a/servo/components/style/properties/longhands/outline.mako.rs b/servo/components/style/properties/longhands/outline.mako.rs @@ -31,7 +31,7 @@ ${helpers.predefined_type( ${helpers.predefined_type( "outline-width", "BorderSideWidth", - "app_units::Au::from_px(3)", + "computed::BorderSideWidth::medium()", engines="gecko servo", initial_specified_value="specified::BorderSideWidth::medium()", spec="https://drafts.csswg.org/css-ui/#propdef-outline-width", diff --git a/servo/components/style/properties/properties.mako.rs b/servo/components/style/properties/properties.mako.rs @@ -6,7 +6,6 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> -use app_units::Au; use servo_arc::{Arc, UniqueArc}; use std::{ops, ptr}; use std::{fmt, mem}; @@ -1820,7 +1819,7 @@ impl ComputedValues { pub fn computed_or_resolved_value( &self, property_id: LonghandId, - context: Option<&resolved::Context>, + context: Option<&mut resolved::Context>, dest: &mut CssStringWriter, ) -> fmt::Result { use crate::values::resolved::ToResolvedValue; @@ -1839,6 +1838,7 @@ impl ComputedValues { _ => unsafe { debug_unreachable!() }, }; if let Some(c) = context { + c.current_longhand = Some(property_id); value.to_resolved_value(c).to_css(&mut dest) } else { value.to_css(&mut dest) @@ -1877,7 +1877,7 @@ impl ComputedValues { pub fn computed_or_resolved_declaration( &self, property_id: LonghandId, - context: Option<&resolved::Context>, + context: Option<&mut resolved::Context>, ) -> PropertyDeclaration { use crate::values::resolved::ToResolvedValue; use crate::values::computed::ToComputedValue; @@ -1895,6 +1895,7 @@ impl ComputedValues { _ => unsafe { debug_unreachable!() }, }; if let Some(c) = context { + c.current_longhand = Some(physical_property_id); let resolved = computed_value.to_resolved_value(c); computed_value = ToResolvedValue::from_resolved_value(resolved); } @@ -2042,6 +2043,7 @@ impl ComputedValues { let context = resolved::Context { style: self, for_property: id.into(), + current_longhand: Some(id), }; let mut s = String::new(); self.computed_or_resolved_value( @@ -2877,17 +2879,6 @@ pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [ % endfor ]; -/// See StyleAdjuster::adjust_for_border_width. -pub fn adjust_border_width(style: &mut StyleBuilder) { - % for side in ["top", "right", "bottom", "left"]: - // Like calling to_computed_value, which wouldn't type check. - if style.get_border().clone_border_${side}_style().none_or_hidden() && - style.get_border().border_${side}_has_nonzero_width() { - style.set_border_${side}_width(Au(0)); - } - % endfor -} - /// An identifier for a given alias property. #[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)] #[repr(u16)] diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs @@ -17,7 +17,7 @@ use crate::properties::longhands::{ content_visibility::computed_value::T as ContentVisibility, overflow_x::computed_value::T as Overflow, }; -use crate::properties::{self, ComputedValues, StyleBuilder}; +use crate::properties::{ComputedValues, StyleBuilder}; use crate::values::computed::position::{ PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, TryTacticAdjustment, }; @@ -407,45 +407,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } - /// The initial value of border-*-width may be changed at computed value - /// time. - /// - /// This is moved to properties.rs for convenience. - fn adjust_for_border_width(&mut self) { - properties::adjust_border_width(self.style); - } - - /// column-rule-style: none causes a computed column-rule-width of zero - /// at computed value time. - #[cfg(feature = "gecko")] - fn adjust_for_column_rule_width(&mut self) { - let column_style = self.style.get_column(); - if !column_style.clone_column_rule_style().none_or_hidden() { - return; - } - if !column_style.column_rule_has_nonzero_width() { - return; - } - self.style - .mutate_column() - .set_column_rule_width(crate::Zero::zero()); - } - - /// outline-style: none causes a computed outline-width of zero at computed - /// value time. - fn adjust_for_outline_width(&mut self) { - let outline = self.style.get_outline(); - if !outline.clone_outline_style().none_or_hidden() { - return; - } - if !outline.outline_has_nonzero_width() { - return; - } - self.style - .mutate_outline() - .set_outline_width(crate::Zero::zero()); - } - /// CSS overflow-x and overflow-y require some fixup as well in some cases. /// https://drafts.csswg.org/css-overflow-3/#overflow-properties /// "Computed value: as specified, except with `visible`/`clip` computing to @@ -1130,10 +1091,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { self.adjust_for_justify_items(); } self.adjust_for_table_text_align(); - self.adjust_for_border_width(); - #[cfg(feature = "gecko")] - self.adjust_for_column_rule_width(); - self.adjust_for_outline_width(); self.adjust_for_writing_mode(layout_parent_style); #[cfg(feature = "gecko")] { diff --git a/servo/components/style/values/computed/border.rs b/servo/components/style/values/computed/border.rs @@ -4,7 +4,11 @@ //! Computed types for CSS values related to borders. -use crate::values::computed::length::{NonNegativeLength, NonNegativeLengthPercentage}; +use crate::properties::{LogicalGroupId, LonghandId}; +use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue}; +use crate::values::computed::length::{ + CSSPixelLength, NonNegativeLength, NonNegativeLengthPercentage, +}; use crate::values::computed::{NonNegativeNumber, NonNegativeNumberOrPercentage}; use crate::values::generics::border::{ GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice, @@ -13,6 +17,7 @@ use crate::values::generics::border::{ use crate::values::generics::rect::Rect; use crate::values::generics::size::Size2D; use crate::values::generics::NonNegative; +use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; use crate::Zero; use app_units::Au; @@ -22,7 +27,64 @@ pub use crate::values::specified::border::BorderImageRepeat; pub type LineWidth = Au; /// A computed value for border-width (and the like). -pub type BorderSideWidth = Au; +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToTyped, From)] +#[repr(transparent)] +pub struct BorderSideWidth(pub Au); + +impl BorderSideWidth { + /// The `medium` value. + pub fn medium() -> Self { + Self(Au::from_px(3)) + } +} + +impl ToAnimatedValue for BorderSideWidth { + type AnimatedValue = CSSPixelLength; + + #[inline] + fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue { + self.0.to_animated_value(context) + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + Self(Au::from_animated_value(animated)) + } +} + +impl ToResolvedValue for BorderSideWidth { + type ResolvedValue = CSSPixelLength; + + fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { + let resolved_length = CSSPixelLength::from(self.0).to_resolved_value(context); + if !context + .current_longhand + .is_some_and(|l| l.logical_group() == Some(LogicalGroupId::BorderWidth)) + { + return resolved_length; + } + // Only for border widths, a style of none/hidden causes the resolved value to be zero. + let style = match context.current_longhand.unwrap() { + LonghandId::BorderTopWidth => context.style.clone_border_top_style(), + LonghandId::BorderRightWidth => context.style.clone_border_right_style(), + LonghandId::BorderBottomWidth => context.style.clone_border_bottom_style(), + LonghandId::BorderLeftWidth => context.style.clone_border_left_style(), + _ => { + debug_assert!(false, "Expected a physical longhand"); + return resolved_length; + }, + }; + if style.none_or_hidden() { + return CSSPixelLength::new(0.0); + } + resolved_length + } + + #[inline] + fn from_resolved_value(value: Self::ResolvedValue) -> Self { + Self(Au::from_f32_px(value.px())) + } +} /// A computed value for outline-offset pub type BorderSideOffset = Au; diff --git a/servo/components/style/values/resolved/mod.rs b/servo/components/style/values/resolved/mod.rs @@ -7,7 +7,7 @@ #[cfg(feature = "gecko")] use crate::media_queries::Device; -use crate::properties::{ComputedValues, NonCustomPropertyId}; +use crate::properties::{ComputedValues, LonghandId, NonCustomPropertyId}; use crate::ArcSlice; use app_units::Au; use servo_arc::Arc; @@ -37,8 +37,11 @@ pub struct Context<'a> { /// The element-specific information to resolve the value. #[cfg(feature = "gecko")] pub element_info: ResolvedElementInfo<'a>, - /// The property we're resolving the value for. + /// The property we're resolving the value for over-all (might be a shorthand). pub for_property: NonCustomPropertyId, + /// The current (physical) longhand we're resolving the value for. Guaranteed to be `Some()` + /// inside `ToResolvedValue` implementations. + pub current_longhand: Option<LonghandId>, } /// A trait to represent the conversion between resolved and resolved values. diff --git a/servo/components/style/values/specified/border.rs b/servo/components/style/values/specified/border.rs @@ -5,6 +5,7 @@ //! Specified types for CSS values related to borders. use crate::parser::{Parse, ParserContext}; +use crate::values::computed::border::BorderSideWidth as ComputedBorderSideWidth; use crate::values::computed::{Context, ToComputedValue}; use crate::values::generics::border::{ GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice, @@ -210,28 +211,30 @@ fn snap_as_border_width(len: Au, context: &Context) -> Au { } let au_per_dev_px = context.device().app_units_per_device_pixel(); - std::cmp::max( - Au(au_per_dev_px), - Au(len.0 / au_per_dev_px * au_per_dev_px), - ) + std::cmp::max(Au(au_per_dev_px), Au(len.0 / au_per_dev_px * au_per_dev_px)) } impl ToComputedValue for BorderSideWidth { - type ComputedValue = Au; + type ComputedValue = ComputedBorderSideWidth; #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - snap_as_border_width(self.0.to_computed_value(context), context) + ComputedBorderSideWidth(snap_as_border_width( + self.0.to_computed_value(context), + context, + )) } #[inline] fn from_computed_value(computed: &Self::ComputedValue) -> Self { - Self(LineWidth::from_computed_value(computed)) + Self(LineWidth::from_computed_value(&computed.0)) } } /// A specified value for outline-offset. -#[derive(Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] +#[derive( + Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped, +)] pub struct BorderSideOffset(Length); impl ToComputedValue for BorderSideOffset { diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml @@ -108,6 +108,7 @@ include = [ "Au", "BreakBetween", "BreakWithin", + "BorderSideWidth", "BorderStyle", "BorderSpacing", "BoolInteger", diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs @@ -8403,7 +8403,7 @@ pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency( fn computed_or_resolved_value( style: &ComputedValues, prop: NonCustomPropertyId, - context: Option<&resolved::Context>, + mut context: Option<&mut resolved::Context>, value: &mut nsACString, ) { let shorthand = match prop.longhand_or_shorthand() { @@ -8418,7 +8418,7 @@ fn computed_or_resolved_value( let mut block = PropertyDeclarationBlock::new(); for longhand in shorthand.longhands() { block.push( - style.computed_or_resolved_declaration(longhand, context), + style.computed_or_resolved_declaration(longhand, context.as_deref_mut()), Importance::Normal, ); } @@ -8446,16 +8446,17 @@ pub unsafe extern "C" fn Servo_GetResolvedValue( let data = raw_data.borrow(); let device = data.stylist.device(); let prop = NonCustomPropertyId::from_nscsspropertyid(prop).unwrap(); - let context = resolved::Context { + let mut context = resolved::Context { style, device, element_info: resolved::ResolvedElementInfo { element: GeckoElement(element), }, for_property: prop, + current_longhand: None, }; - computed_or_resolved_value(style, prop, Some(&context), value) + computed_or_resolved_value(style, prop, Some(&mut context), value) } #[no_mangle] diff --git a/testing/web-platform/meta/css/css-backgrounds/animations/border-width-interpolation.html.ini b/testing/web-platform/meta/css/css-backgrounds/animations/border-width-interpolation.html.ini @@ -1,50 +0,0 @@ -[border-width-interpolation.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [CSS Animations: property <border-left-width> from [initial\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [initial\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [initial\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [initial\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [initial\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [initial\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [initial\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [initial\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [unset\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [unset\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [unset\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [CSS Animations: property <border-left-width> from [unset\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [unset\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [unset\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [unset\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [Web Animations: property <border-left-width> from [unset\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-multicol/parsing/column-rule-computed.html.ini b/testing/web-platform/meta/css/css-multicol/parsing/column-rule-computed.html.ini @@ -4,6 +4,3 @@ [Property column-rule value '0px none rgb(255, 0, 255)'] expected: FAIL - - [Property column-rule value 'medium hidden currentcolor'] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-multicol/parsing/column-rule-width-computed.html.ini b/testing/web-platform/meta/css/css-multicol/parsing/column-rule-width-computed.html.ini @@ -1,5 +0,0 @@ -[column-rule-width-computed.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [column-rule-width is as specified when column-rule-style is none or hidden] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-ui/animation/outline-width-interpolation.html.ini b/testing/web-platform/meta/css/css-ui/animation/outline-width-interpolation.html.ini @@ -1,51 +0,0 @@ -[outline-width-interpolation.html] - bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1576250 - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [CSS Animations: property <outline-width> from [unset\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [unset\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [initial\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [unset\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [unset\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [initial\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [initial\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [initial\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [initial\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [unset\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [unset\] to [23px\] at (0.3) should be [9px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [unset\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [initial\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [initial\] to [23px\] at (0) should be [3px\]] - expected: FAIL - - [Web Animations: property <outline-width> from [unset\] to [23px\] at (0.6) should be [15px\]] - expected: FAIL - - [CSS Animations: property <outline-width> from [initial\] to [23px\] at (1.5) should be [33px\]] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-ui/outline-009.html.ini b/testing/web-platform/meta/css/css-ui/outline-009.html.ini @@ -1,2 +0,0 @@ -[outline-009.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-ui/parsing/outline-width-computed.html.ini b/testing/web-platform/meta/css/css-ui/parsing/outline-width-computed.html.ini @@ -1,5 +0,0 @@ -[outline-width-computed.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [outline-width is independent of the value of outline-style] - expected: FAIL diff --git a/testing/web-platform/tests/css/CSS2/borders/border-width-011-ref.xht b/testing/web-platform/tests/css/CSS2/borders/border-width-011-ref.xht @@ -0,0 +1,12 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>CSS Test Reference</title> + <style type="text/css"> + p { border-width: 2em; border-style: solid; border-color: green; color: green; } + </style> + </head> + <body> + <p>This text should be green.</p> +</body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/borders/border-width-011.xht b/testing/web-platform/tests/css/CSS2/borders/border-width-011.xht @@ -9,18 +9,17 @@ <link rel="help" href="http://www.w3.org/TR/CSS21/box.html#border-width-properties"/> <link rel="help" href="https://www.w3.org/Style/css2-updates/REC-CSS2-20110607-errata.html#s.6.2.1"/> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2768"/> - <link rel="match" href="../reference/ref-this-text-should-be-green.xht" /> + <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11494"/> + <link rel="match" href="border-width-011-ref.xht" /> <style type="text/css"> - /* this results in a specified value of 2em and a computed value of 0 as border-style is none */ + /* this results in a specified value of 2em */ body { border-width: 2em; } /* this then inherits from the above border-width, and checks the computed value is inherited */ - p { border-width: inherit; border-style: solid; border-color: red; color: green; } + p { border-width: inherit; border-style: solid; border-color: green; color: green; } </style> </head> <body> <p>This text should be green.</p> - - </body> </html> diff --git a/testing/web-platform/tests/css/CSS2/borders/border-width-012.xht b/testing/web-platform/tests/css/CSS2/borders/border-width-012.xht @@ -1,42 +1,25 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - -<html xmlns="http://www.w3.org/1999/xhtml"> - - <head> - - <title>CSS Test: Border-width: inherit - border-style: hidden</title> - - <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> - <link rel="help" title="Section 8.5.1 Border width" href="http://www.w3.org/TR/CSS21/box.html#border-width-properties" /> - <link rel="match" href="../reference/ref-if-there-is-no-red.xht" /> - - <meta content="The border-width computed value is '0' if the border style is 'none' or 'hidden'." name="assert" /> - - <style type="text/css"><![CDATA[ - #parent - { - border-color: red; - border-style: hidden; - border-width: 50px; - } - - #child - { - border-color: red; - border-style: solid; - border-width: inherit; - } - ]]></style> - +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>CSS Test: border-width: inherit</title> + <link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/> + <link rel="reviewer" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact"/> + <link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/border/width/001.html" type="text/html"/> + <link rel="help" href="http://www.w3.org/TR/CSS21/box.html#border-style-properties"/> + <link rel="help" href="http://www.w3.org/TR/CSS21/box.html#border-width-properties"/> + <link rel="help" href="https://www.w3.org/Style/css2-updates/REC-CSS2-20110607-errata.html#s.6.2.1"/> + <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2768"/> + <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11494"/> + <link rel="match" href="border-width-011-ref.xht" /> + + <style type="text/css"> + /* this results in a specified value of 2em */ + body { border-width: 2em; border-style: hidden; } + /* this then inherits from the above border-width, and checks the computed value is inherited */ + p { border-width: inherit; border-style: solid; border-color: green; color: green; } + </style> </head> - <body> - - <p>Test passes if there is <strong>no red</strong>.</p> - - <div id="parent"> - <div id="child"></div> - </div> - - </body> -</html> -\ No newline at end of file + <p>This text should be green.</p> +</body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/ui/outline-width-096-ref.xht b/testing/web-platform/tests/css/CSS2/ui/outline-width-096-ref.xht @@ -11,6 +11,7 @@ div { margin: 40px 20px; + outline: 20px solid green; } ]]></style> @@ -18,7 +19,7 @@ <body> - <p>Test passes if there is <strong>no red</strong>.</p> + <p>Test passes if there is a <strong>green outline</strong>.</p> <div>Filler Text</div> diff --git a/testing/web-platform/tests/css/CSS2/ui/outline-width-096.xht b/testing/web-platform/tests/css/CSS2/ui/outline-width-096.xht @@ -21,7 +21,7 @@ #child { - outline-color: red; + outline-color: green; outline-style: solid; outline-width: inherit; } @@ -31,7 +31,7 @@ <body> - <p>Test passes if there is <strong>no red</strong>.</p> + <p>Test passes if there is a <strong>green outline</strong>.</p> <div id="parent"> <div id="child">Filler Text</div> diff --git a/testing/web-platform/tests/css/css-backgrounds/animations/border-width-interpolation.html b/testing/web-platform/tests/css/css-backgrounds/animations/border-width-interpolation.html @@ -83,12 +83,12 @@ test_interpolation({ from: 'inherit', to: '20px', }, [ - {at: -0.3, expect: '0px'}, - {at: 0, expect: '0px'}, - {at: 0.3, expect: '6px'}, - {at: 0.6, expect: '12px'}, + {at: -0.3, expect: '33px'}, + {at: 0, expect: '30px'}, + {at: 0.3, expect: '27px'}, + {at: 0.6, expect: '24px'}, {at: 1, expect: '20px'}, - {at: 1.5, expect: '30px'}, + {at: 1.5, expect: '15px'}, ]); test_interpolation({