From 719dd668634954d517f224bd3bb1172c1ca3662e Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 8 Dec 2021 12:20:40 +0000 Subject: [PATCH] Bug 1702924 - Add support for the 'cap' font-relative unit. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D133101 --- layout/base/nsPresContext.cpp | 16 ++++++---- layout/base/nsPresContext.h | 10 ++++-- layout/mathml/nsMathMLFrame.cpp | 2 +- layout/style/GeckoBindings.cpp | 6 ++-- layout/style/GeckoBindings.h | 4 ++- layout/style/nsFontFaceUtils.cpp | 2 +- servo/components/style/font_metrics.rs | 24 ++++++++++++-- servo/components/style/gecko/wrapper.rs | 9 +++++- .../components/style/values/generics/calc.rs | 1 + .../components/style/values/specified/calc.rs | 1 + .../style/values/specified/length.rs | 32 +++++++++++++++++-- .../tests/css/css-values/cap-unit-001.html | 32 +++++++++++++++++++ .../reference/cap-unit-001-ref.html | 15 +++++++++ 13 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 testing/web-platform/tests/css/css-values/cap-unit-001.html create mode 100644 testing/web-platform/tests/css/css-values/reference/cap-unit-001-ref.html diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index e5b5f561f1d6..69433a617bfa 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -214,8 +214,9 @@ void nsPresContext::ForceReflowForFontInfoUpdate(bool aNeedsReframe) { // We also need to trigger restyling for ex/ch units changes to take effect, // if needed. - auto restyleHint = - UsesExChUnits() ? RestyleHint::RecascadeSubtree() : RestyleHint{0}; + auto restyleHint = UsesFontMetricDependentFontUnits() + ? RestyleHint::RecascadeSubtree() + : RestyleHint{0}; RebuildAllStyleData(changeHint, restyleHint); } @@ -271,7 +272,7 @@ nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType) mPendingUIResolutionChanged(false), mPendingFontInfoUpdateReflowFromStyle(false), mIsGlyph(false), - mUsesExChUnits(false), + mUsesFontMetricDependentFontUnits(false), mCounterStylesDirty(true), mFontFeatureValuesDirty(true), mSuppressResizeReflow(false), @@ -658,7 +659,7 @@ void nsPresContext::PreferenceChanged(const char* aPrefName) { // Changes to font_rendering prefs need to trigger a reflow StringBeginsWith(prefName, "gfx.font_rendering."_ns)) { changeHint |= NS_STYLE_HINT_REFLOW; - if (UsesExChUnits()) { + if (UsesFontMetricDependentFontUnits()) { restyleHint |= RestyleHint::RecascadeSubtree(); } } @@ -1772,7 +1773,7 @@ void nsPresContext::PostRebuildAllStyleDataEvent( return; } if (aRestyleHint.DefinitelyRecascadesAllSubtree()) { - mUsesExChUnits = false; + mUsesFontMetricDependentFontUnits = false; } RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint); } @@ -1970,8 +1971,9 @@ void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) { // TODO(emilio): We could be more granular if we knew which families have // potentially changed. if (!aUpdatedFont) { - auto hint = - UsesExChUnits() ? RestyleHint::RecascadeSubtree() : RestyleHint{0}; + auto hint = UsesFontMetricDependentFontUnits() + ? RestyleHint::RecascadeSubtree() + : RestyleHint{0}; PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, hint); return; } diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 2459b3203f8a..d49c7cb96014 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -1051,9 +1051,13 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr { bool HasEverBuiltInvisibleText() const { return mHasEverBuiltInvisibleText; } void SetBuiltInvisibleText() { mHasEverBuiltInvisibleText = true; } - bool UsesExChUnits() const { return mUsesExChUnits; } + bool UsesFontMetricDependentFontUnits() const { + return mUsesFontMetricDependentFontUnits; + } - void SetUsesExChUnits(bool aValue) { mUsesExChUnits = aValue; } + void SetUsesFontMetricDependentFontUnits(bool aValue) { + mUsesFontMetricDependentFontUnits = aValue; + } bool IsDeviceSizePageSize(); @@ -1330,7 +1334,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr { // // TODO(emilio): It's a bit weird that this lives here but all the other // relevant bits live in Device on the rust side. - unsigned mUsesExChUnits : 1; + unsigned mUsesFontMetricDependentFontUnits : 1; // Is the current mCounterStyleManager valid? unsigned mCounterStylesDirty : 1; diff --git a/layout/mathml/nsMathMLFrame.cpp b/layout/mathml/nsMathMLFrame.cpp index 90c312dafa20..d95cb0ebfbaa 100644 --- a/layout/mathml/nsMathMLFrame.cpp +++ b/layout/mathml/nsMathMLFrame.cpp @@ -199,7 +199,7 @@ nscoord nsMathMLFrame::CalcLength(nsPresContext* aPresContext, } if (eCSSUnit_XHeight == unit) { - aPresContext->SetUsesExChUnits(true); + aPresContext->SetUsesFontMetricDependentFontUnits(true); RefPtr fm = nsLayoutUtils::GetFontMetricsForComputedStyle( aComputedStyle, aPresContext, aFontSizeInflation); nscoord xHeight = fm->XHeight(); diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp index 6c5c5e0fa1e4..ba1a25c9bc60 100644 --- a/layout/style/GeckoBindings.cpp +++ b/layout/style/GeckoBindings.cpp @@ -1426,7 +1426,7 @@ GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext, // ArrayBuffer-backed FontFace objects are handled synchronously. nsPresContext* presContext = const_cast(aPresContext); - presContext->SetUsesExChUnits(true); + presContext->SetUsesFontMetricDependentFontUnits(true); RefPtr fm = nsLayoutUtils::GetMetricsFor( presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet); @@ -1439,7 +1439,9 @@ GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext, return Length::FromPixels(CSSPixel::FromAppUnits(aLen)); }; return {ToLength(NS_round(metrics.xHeight * d2a)), - ToLength(NS_round(metrics.zeroWidth * d2a))}; + ToLength(NS_round(metrics.zeroWidth * d2a)), + ToLength(NS_round(metrics.capHeight * d2a)), + ToLength(NS_round(metrics.maxAscent * d2a))}; } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h index a235e29eee1a..aa0916b9e7ef 100644 --- a/layout/style/GeckoBindings.h +++ b/layout/style/GeckoBindings.h @@ -483,7 +483,9 @@ mozilla::StyleDefaultFontSizes Gecko_GetBaseSize(nsAtom* lang); struct GeckoFontMetrics { mozilla::Length mXSize; - mozilla::Length mChSize; // negatives indicate not found. + mozilla::Length mChSize; // negatives indicate not found. + mozilla::Length mCapHeight; // negatives indicate not found. + mozilla::Length mAscent; }; GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext*, bool is_vertical, diff --git a/layout/style/nsFontFaceUtils.cpp b/layout/style/nsFontFaceUtils.cpp index eedf65de3125..b04f242059bf 100644 --- a/layout/style/nsFontFaceUtils.cpp +++ b/layout/style/nsFontFaceUtils.cpp @@ -210,7 +210,7 @@ void nsFontFaceUtils::MarkDirtyForFontChange(nsIFrame* aSubtreeRoot, } if (alreadyScheduled == ReflowAlreadyScheduled::No || - pc->UsesExChUnits()) { + pc->UsesFontMetricDependentFontUnits()) { if (f->IsPlaceholderFrame()) { nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) { diff --git a/servo/components/style/font_metrics.rs b/servo/components/style/font_metrics.rs index b521fdf76c9b..db435539735e 100644 --- a/servo/components/style/font_metrics.rs +++ b/servo/components/style/font_metrics.rs @@ -12,20 +12,38 @@ use crate::Atom; /// Represents the font metrics that style needs from a font to compute the /// value of certain CSS units like `ex`. -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct FontMetrics { /// The x-height of the font. pub x_height: Option, /// The zero advance. This is usually writing mode dependent pub zero_advance_measure: Option, + /// The cap-height of the font. + pub cap_height: Option, + /// The ascent of the font (a value is always available for this). + pub ascent: Length, +} + +impl Default for FontMetrics { + fn default() -> Self { + FontMetrics { + x_height: None, + zero_advance_measure: None, + cap_height: None, + ascent: Length::new(0.0), + } + } } /// Type of font metrics to retrieve. #[derive(Clone, Debug, PartialEq)] pub enum FontMetricsOrientation { /// Get metrics for horizontal or vertical according to the Context's - /// writing mode. - MatchContext, + /// writing mode, using horizontal metrics for vertical/mixed + MatchContextPreferHorizontal, + /// Get metrics for horizontal or vertical according to the Context's + /// writing mode, using vertical metrics for vertical/mixed + MatchContextPreferVertical, /// Force getting horizontal metrics. Horizontal, } diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs index 77d3d458674d..f6e178044d6e 100644 --- a/servo/components/style/gecko/wrapper.rs +++ b/servo/components/style/gecko/wrapper.rs @@ -991,7 +991,8 @@ impl FontMetricsProvider for GeckoFontMetricsProvider { }; let vertical_metrics = match orientation { - FontMetricsOrientation::MatchContext => wm.is_vertical() && wm.is_upright(), + FontMetricsOrientation::MatchContextPreferHorizontal => wm.is_vertical() && wm.is_upright(), + FontMetricsOrientation::MatchContextPreferVertical => wm.is_vertical() && !wm.is_sideways(), FontMetricsOrientation::Horizontal => false, }; let gecko_metrics = unsafe { @@ -1011,6 +1012,12 @@ impl FontMetricsProvider for GeckoFontMetricsProvider { } else { None }, + cap_height: if gecko_metrics.mCapHeight.px() >= 0. { + Some(gecko_metrics.mCapHeight) + } else { + None + }, + ascent: gecko_metrics.mAscent, } } } diff --git a/servo/components/style/values/generics/calc.rs b/servo/components/style/values/generics/calc.rs index 0d791e5d00df..d2bc2a855230 100644 --- a/servo/components/style/values/generics/calc.rs +++ b/servo/components/style/values/generics/calc.rs @@ -42,6 +42,7 @@ pub enum MinMaxOp { pub enum SortKey { Number, Percentage, + Cap, Ch, Deg, Em, diff --git a/servo/components/style/values/specified/calc.rs b/servo/components/style/values/specified/calc.rs index 358604be144a..a960fe01a4d4 100644 --- a/servo/components/style/values/specified/calc.rs +++ b/servo/components/style/values/specified/calc.rs @@ -184,6 +184,7 @@ impl generic::CalcNodeLeaf for Leaf { FontRelativeLength::Ch(..) => SortKey::Ch, FontRelativeLength::Em(..) => SortKey::Em, FontRelativeLength::Ex(..) => SortKey::Ex, + FontRelativeLength::Cap(..) => SortKey::Cap, FontRelativeLength::Rem(..) => SortKey::Rem, }, NoCalcLength::ViewportPercentage(ref vp) => match *vp { diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs index 6a27929eda39..a92700822fc2 100644 --- a/servo/components/style/values/specified/length.rs +++ b/servo/components/style/values/specified/length.rs @@ -59,6 +59,9 @@ pub enum FontRelativeLength { /// A "ch" value: https://drafts.csswg.org/css-values/#ch #[css(dimension)] Ch(CSSFloat), + /// A "cap" value: https://drafts.csswg.org/css-values/#cap + #[css(dimension)] + Cap(CSSFloat), /// A "rem" value: https://drafts.csswg.org/css-values/#rem #[css(dimension)] Rem(CSSFloat), @@ -92,6 +95,7 @@ impl FontRelativeLength { FontRelativeLength::Em(v) | FontRelativeLength::Ex(v) | FontRelativeLength::Ch(v) | + FontRelativeLength::Cap(v) | FontRelativeLength::Rem(v) => v == 0., } } @@ -101,6 +105,7 @@ impl FontRelativeLength { FontRelativeLength::Em(v) | FontRelativeLength::Ex(v) | FontRelativeLength::Ch(v) | + FontRelativeLength::Cap(v) | FontRelativeLength::Rem(v) => v < 0., } } @@ -116,12 +121,13 @@ impl FontRelativeLength { (&Em(one), &Em(other)) => Em(one + other), (&Ex(one), &Ex(other)) => Ex(one + other), (&Ch(one), &Ch(other)) => Ch(one + other), + (&Cap(one), &Cap(other)) => Cap(one + other), (&Rem(one), &Rem(other)) => Rem(one + other), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { - Em(..) | Ex(..) | Ch(..) | Rem(..) => {}, + Em(..) | Ex(..) | Ch(..) | Cap(..) | Rem(..) => {}, } debug_unreachable!("Forgot to handle unit in try_sum()") }, @@ -210,7 +216,7 @@ impl FontRelativeLength { // whichever is in the inline axis of the element.) // let metrics = - query_font_metrics(context, base_size, FontMetricsOrientation::MatchContext); + query_font_metrics(context, base_size, FontMetricsOrientation::MatchContextPreferHorizontal); let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ch // @@ -231,6 +237,23 @@ impl FontRelativeLength { }); (reference_size, length) }, + FontRelativeLength::Cap(length) => { + if context.for_non_inherited_property.is_some() { + context.rule_cache_conditions.borrow_mut().set_uncacheable(); + } + context.builder.add_flags(font_metrics_flag); + let metrics = + query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal); + let reference_size = metrics.cap_height.unwrap_or_else(|| { + // https://drafts.csswg.org/css-values/#cap + // + // In the cases where it is impossible or impractical to + // determine the cap-height, the font’s ascent must be used. + // + metrics.ascent + }); + (reference_size, length) + }, FontRelativeLength::Rem(length) => { // https://drafts.csswg.org/css-values/#rem: // @@ -542,6 +565,7 @@ impl NoCalcLength { "em" => NoCalcLength::FontRelative(FontRelativeLength::Em(value)), "ex" => NoCalcLength::FontRelative(FontRelativeLength::Ex(value)), "ch" => NoCalcLength::FontRelative(FontRelativeLength::Ch(value)), + "cap" => NoCalcLength::FontRelative(FontRelativeLength::Cap(value)), "rem" => NoCalcLength::FontRelative(FontRelativeLength::Rem(value)), // viewport percentages "vw" if !context.in_page_rule() => { @@ -701,12 +725,13 @@ impl PartialOrd for FontRelativeLength { (&Em(ref one), &Em(ref other)) => one.partial_cmp(other), (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other), (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other), + (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other), (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { - Em(..) | Ex(..) | Ch(..) | Rem(..) => {}, + Em(..) | Ex(..) | Ch(..) | Cap(..) | Rem(..) => {}, } debug_unreachable!("Forgot an arm in partial_cmp?") }, @@ -723,6 +748,7 @@ impl Mul for FontRelativeLength { FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar), FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar), FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar), + FontRelativeLength::Cap(v) => FontRelativeLength::Cap(v * scalar), FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar), } } diff --git a/testing/web-platform/tests/css/css-values/cap-unit-001.html b/testing/web-platform/tests/css/css-values/cap-unit-001.html new file mode 100644 index 000000000000..ef12127754c7 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/cap-unit-001.html @@ -0,0 +1,32 @@ + + +CSS Values and Units Test: support for the cap unit + + + + + + + +

Test passes if there is a filled green square and no red.

+
+ diff --git a/testing/web-platform/tests/css/css-values/reference/cap-unit-001-ref.html b/testing/web-platform/tests/css/css-values/reference/cap-unit-001-ref.html new file mode 100644 index 000000000000..b1816deb43c2 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/reference/cap-unit-001-ref.html @@ -0,0 +1,15 @@ + + +CSS Values and Units reference: support for the cap unit + + + +

Test passes if there is a filled green square and no red.

+
+