Bug 1962602 - Give gfxFontGroup::GetMetricsForCSSUnits flags to control whether it should load extra font resources to measure the ZERO or WATER IDEOGRAPH characters. r=firefox-style-system-reviewers,emilio

This makes the style system pass flags down to GetMetricsForCSSUnits to specify
whether it needs the 'ch' or 'ic' units, which may trigger downloading additional
font resources.

There should be no change in user-visible behavior, except by observing (e.g. in
the devtools network panel) what resources end up being fetched in an example
like the reporter's.

Differential Revision: https://phabricator.services.mozilla.com/D246780
This commit is contained in:
Jonathan Kew
2025-04-26 16:22:46 +00:00
parent 3a5538d573
commit c68d97e633
13 changed files with 102 additions and 47 deletions

View File

@@ -2919,7 +2919,9 @@ static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
0.0f,
0.0f};
}
auto metrics = fontGroup->GetMetricsForCSSUnits(nsFontMetrics::eHorizontal);
auto metrics = fontGroup->GetMetricsForCSSUnits(
nsFontMetrics::eHorizontal, StyleQueryFontMetricsFlags::NEEDS_CH |
StyleQueryFontMetricsFlags::NEEDS_IC);
return {Length::FromPixels(metrics.xHeight),
Length::FromPixels(metrics.zeroWidth),
Length::FromPixels(metrics.capHeight),
@@ -3037,8 +3039,9 @@ class CanvasUserSpaceMetrics final : public UserSpaceMetricsWithSize {
return Gecko_GetFontMetrics(
mPresContext, WritingMode(mCanvasStyle).IsVertical(),
mCanvasStyle->StyleFont(), mCanvasStyle->StyleFont()->mFont.size,
/* aUseUserFontSet = */ true,
/* aRetrieveMathScales */ false);
StyleQueryFontMetricsFlags::USE_USER_FONT_SET |
StyleQueryFontMetricsFlags::NEEDS_CH |
StyleQueryFontMetricsFlags::NEEDS_IC);
}
case Type::Root:
return GetFontMetrics(mPresContext->Document()->GetRootElement());

View File

@@ -133,8 +133,9 @@ GeckoFontMetrics UserSpaceMetrics::GetFontMetrics(const Element* aElement) {
metrics = Gecko_GetFontMetrics(
presContext, WritingMode(style).IsVertical(), style->StyleFont(),
style->StyleFont()->mFont.size,
/* aUseUserFontSet = */ true,
/* aRetrieveMathScales */ false);
StyleQueryFontMetricsFlags::USE_USER_FONT_SET |
StyleQueryFontMetricsFlags::NEEDS_CH |
StyleQueryFontMetricsFlags::NEEDS_IC);
});
}
return metrics;
@@ -290,8 +291,9 @@ GeckoFontMetrics NonSVGFrameUserSpaceMetrics::GetFontMetricsForType(
return Gecko_GetFontMetrics(
mFrame->PresContext(), mFrame->GetWritingMode().IsVertical(),
mFrame->StyleFont(), mFrame->StyleFont()->mFont.size,
/* aUseUserFontSet = */ true,
/* aRetrieveMathScales */ false);
StyleQueryFontMetricsFlags::USE_USER_FONT_SET |
StyleQueryFontMetricsFlags::NEEDS_CH |
StyleQueryFontMetricsFlags::NEEDS_IC);
case Type::Root:
return GetFontMetrics(
mFrame->PresContext()->Document()->GetRootElement());

View File

@@ -3940,7 +3940,7 @@ already_AddRefed<gfxFont> gfxFontGroup::WhichSystemFontSupportsChar(
}
gfxFont::Metrics gfxFontGroup::GetMetricsForCSSUnits(
gfxFont::Orientation aOrientation) {
gfxFont::Orientation aOrientation, StyleQueryFontMetricsFlags aFlags) {
bool isFirst;
RefPtr<gfxFont> font = GetFirstValidFont(0x20, nullptr, &isFirst);
auto metrics = font->GetMetrics(aOrientation);
@@ -3948,7 +3948,8 @@ gfxFont::Metrics gfxFontGroup::GetMetricsForCSSUnits(
// If the font we used to get metrics was not the first in the list,
// or if it doesn't support the ZERO character, check for the font that
// does support ZERO and use its metrics for the 'ch' unit.
if (!isFirst || !font->HasCharacter('0')) {
if ((aFlags & StyleQueryFontMetricsFlags::NEEDS_CH) &&
(!isFirst || !font->HasCharacter('0'))) {
RefPtr<gfxFont> zeroFont = GetFirstValidFont('0');
if (zeroFont != font) {
const auto& zeroMetrics = zeroFont->GetMetrics(aOrientation);
@@ -3957,7 +3958,8 @@ gfxFont::Metrics gfxFontGroup::GetMetricsForCSSUnits(
}
// Likewise for the WATER ideograph character used as the basis for 'ic'.
if (!isFirst || !font->HasCharacter(0x6C34)) {
if ((aFlags & StyleQueryFontMetricsFlags::NEEDS_IC) &&
(!isFirst || !font->HasCharacter(0x6C34))) {
RefPtr<gfxFont> icFont = GetFirstValidFont(0x6C34);
if (icFont != font) {
const auto& icMetrics = icFont->GetMetrics(aOrientation);

View File

@@ -1072,7 +1072,11 @@ class gfxFontGroup final : public gfxTextRunFactory {
// to render specific characters, not simply the "first available" font.
// https://drafts.csswg.org/css-values-4/#ch
// https://drafts.csswg.org/css-values-4/#ic
gfxFont::Metrics GetMetricsForCSSUnits(gfxFont::Orientation aOrientation);
// Whether extra font resources may be loaded to resolve 'ch' and 'ic'
// depends on the corresponding flags passed by the caller.
gfxFont::Metrics GetMetricsForCSSUnits(
gfxFont::Orientation aOrientation,
mozilla::StyleQueryFontMetricsFlags aFlags);
protected:
friend class mozilla::PostTraversalTask;

View File

@@ -1285,8 +1285,8 @@ void AssertIsMainThreadOrServoFontMetricsLocked() {
GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext,
bool aIsVertical,
const nsStyleFont* aFont,
Length aFontSize, bool aUseUserFontSet,
bool aRetrieveMathScales) {
Length aFontSize,
StyleQueryFontMetricsFlags flags) {
AutoWriteLock guard(*sServoFFILock);
// Getting font metrics can require some main thread only work to be
@@ -1302,13 +1302,14 @@ GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext,
nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext);
RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor(
presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet);
presContext, aIsVertical, aFont, aFontSize,
bool(flags & StyleQueryFontMetricsFlags::USE_USER_FONT_SET));
auto* fontGroup = fm->GetThebesFontGroup();
auto metrics = fontGroup->GetMetricsForCSSUnits(fm->Orientation());
auto metrics = fontGroup->GetMetricsForCSSUnits(fm->Orientation(), flags);
float scriptPercentScaleDown = 0;
float scriptScriptPercentScaleDown = 0;
if (aRetrieveMathScales) {
if (flags & StyleQueryFontMetricsFlags::NEEDS_MATH_SCALES) {
RefPtr<gfxFont> font = fontGroup->GetFirstValidFont();
if (font->TryGetMathTable()) {
scriptPercentScaleDown = static_cast<float>(

View File

@@ -472,11 +472,9 @@ struct GeckoFontMetrics {
float mScriptScriptPercentScaleDown; // zero is invalid or means not found.
};
GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext*, bool is_vertical,
const nsStyleFont* font,
mozilla::Length font_size,
bool use_user_font_set,
bool retrieve_math_scales);
GeckoFontMetrics Gecko_GetFontMetrics(
const nsPresContext*, bool is_vertical, const nsStyleFont* font,
mozilla::Length font_size, mozilla::StyleQueryFontMetricsFlags flags);
mozilla::StyleSheet* Gecko_StyleSheet_Clone(const mozilla::StyleSheet* aSheet);

View File

@@ -634,7 +634,8 @@ cbindgen-types = [
{ gecko = "StyleInert", servo = "crate::values::computed::ui::Inert" },
{ gecko = "StyleMargin", servo = "crate::values::computed::length::Margin" },
{ gecko = "StyleGenericAnchorFunction", servo = "crate::values::generics::position::GenericAnchorFunction" },
{ gecko = "StylePositionProperty", servo = "crate::values::computed::PositionProperty"},
{ gecko = "StylePositionProperty", servo = "crate::values::computed::PositionProperty" },
{ gecko = "StyleQueryFontMetricsFlags", servo = "crate::gecko::media_queries::QueryFontMetricsFlags" },
]
mapped-generic-types = [

View File

@@ -29,6 +29,23 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::{cmp, fmt};
use style_traits::{CSSPixel, DevicePixel};
/// Flags for the query_font_metrics() function.
#[repr(C)]
pub struct QueryFontMetricsFlags(u8);
bitflags! {
impl QueryFontMetricsFlags: u8 {
/// Should we use the user font set?
const USE_USER_FONT_SET = 1 << 0;
/// Does the caller need the `ch` unit (width of the ZERO glyph)?
const NEEDS_CH = 1 << 1;
/// Does the caller need the `ic` unit (width of the WATER ideograph)?
const NEEDS_IC = 1 << 2;
/// Does the caller need math scales to be retrieved?
const NEEDS_MATH_SCALES = 1 << 3;
}
}
/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
@@ -227,8 +244,7 @@ impl Device {
vertical: bool,
font: &crate::properties::style_structs::Font,
base_size: Length,
in_media_query: bool,
retrieve_math_scales: bool,
flags: QueryFontMetricsFlags,
) -> FontMetrics {
self.used_font_metrics.store(true, Ordering::Relaxed);
let pc = match self.pres_context() {
@@ -241,9 +257,7 @@ impl Device {
vertical,
&**font,
base_size,
// we don't use the user font set in a media query
!in_media_query,
retrieve_math_scales,
flags,
)
};
FontMetrics {

View File

@@ -1355,6 +1355,8 @@ impl<'b> Cascade<'b> {
}
let (new_size, new_unconstrained_size) = {
use crate::gecko::media_queries::QueryFontMetricsFlags;
let builder = &context.builder;
let font = builder.get_font();
let parent_font = builder.get_parent_font();
@@ -1376,7 +1378,7 @@ impl<'b> Cascade<'b> {
let font_metrics = context.query_font_metrics(
FontBaseSize::InheritedStyle,
FontMetricsOrientation::Horizontal,
/* retrieve_math_scales = */ true,
QueryFontMetricsFlags::NEEDS_MATH_SCALES,
);
scale_factor_for_math_depth_change(
parent_font.mMathDepth as i32,

View File

@@ -4,6 +4,7 @@
//! Computed values for font properties
use crate::gecko::media_queries::QueryFontMetricsFlags;
use crate::parser::{Parse, ParserContext};
use crate::values::animated::ToAnimatedValue;
use crate::values::computed::{
@@ -802,13 +803,13 @@ impl ToComputedValue for specified::FontSizeAdjust {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
use crate::font_metrics::FontMetricsOrientation;
let font_metrics = |vertical| {
let font_metrics = |vertical, flags| {
let orient = if vertical {
FontMetricsOrientation::MatchContextPreferVertical
} else {
FontMetricsOrientation::Horizontal
};
let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, false);
let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
let font_size = context.style().get_font().clone_font_size().used_size.0;
(metrics, font_size)
};
@@ -817,13 +818,13 @@ impl ToComputedValue for specified::FontSizeAdjust {
// returns the fallback value, or if that is negative, resolves using ascent instead
// of the missing field (this is the fallback for cap-height).
macro_rules! resolve {
($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr) => {{
($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
match $value {
specified::FontSizeAdjustFactor::Number(f) => {
FontSizeAdjust::$basis(f.to_computed_value(context))
},
specified::FontSizeAdjustFactor::FromFont => {
let (metrics, font_size) = font_metrics($vertical);
let (metrics, font_size) = font_metrics($vertical, $flags);
let ratio = if let Some(metric) = metrics.$field {
metric / font_size
} else if $fallback >= 0.0 {
@@ -843,13 +844,21 @@ impl ToComputedValue for specified::FontSizeAdjust {
match *self {
Self::None => FontSizeAdjust::None,
Self::ExHeight(val) => resolve!(ExHeight, val, false, x_height, 0.5),
Self::CapHeight(val) => {
resolve!(CapHeight, val, false, cap_height, -1.0 /* fall back to ascent */)
Self::ExHeight(val) => {
resolve!(ExHeight, val, false, x_height, 0.5, QueryFontMetricsFlags::empty())
},
Self::CapHeight(val) => {
resolve!(CapHeight, val, false, cap_height, -1.0 /* fall back to ascent */, QueryFontMetricsFlags::empty())
},
Self::ChWidth(val) => {
resolve!(ChWidth, val, false, zero_advance_measure, 0.5, QueryFontMetricsFlags::NEEDS_CH)
},
Self::IcWidth(val) => {
resolve!(IcWidth, val, false, ic_width, 1.0, QueryFontMetricsFlags::NEEDS_IC)
},
Self::IcHeight(val) => {
resolve!(IcHeight, val, true, ic_width, 1.0, QueryFontMetricsFlags::NEEDS_IC)
},
Self::ChWidth(val) => resolve!(ChWidth, val, false, zero_advance_measure, 0.5),
Self::IcWidth(val) => resolve!(IcWidth, val, false, ic_width, 1.0),
Self::IcHeight(val) => resolve!(IcHeight, val, true, ic_width, 1.0),
}
}

View File

@@ -18,6 +18,7 @@ use crate::computed_value_flags::ComputedValueFlags;
use crate::context::QuirksMode;
use crate::custom_properties::ComputedCustomProperties;
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
use crate::gecko::media_queries::QueryFontMetricsFlags;
use crate::media_queries::Device;
#[cfg(feature = "gecko")]
use crate::properties;
@@ -363,7 +364,7 @@ impl<'a> Context<'a> {
&self,
base_size: FontBaseSize,
orientation: FontMetricsOrientation,
retrieve_math_scales: bool,
mut flags: QueryFontMetricsFlags,
) -> FontMetrics {
if self.for_non_inherited_property {
self.rule_cache_conditions.borrow_mut().set_uncacheable();
@@ -390,12 +391,14 @@ impl<'a> Context<'a> {
FontMetricsOrientation::MatchContextPreferVertical => wm.is_text_vertical(),
FontMetricsOrientation::Horizontal => false,
};
if !self.in_media_or_container_query() {
flags |= QueryFontMetricsFlags::USE_USER_FONT_SET
}
self.device().query_font_metrics(
vertical,
font,
size,
self.in_media_or_container_query(),
retrieve_math_scales,
flags,
)
}

View File

@@ -9,6 +9,7 @@
use super::{AllowQuirks, Number, Percentage, ToComputedValue};
use crate::computed_value_flags::ComputedValueFlags;
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
use crate::gecko::media_queries::QueryFontMetricsFlags;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::parser::{Parse, ParserContext};
@@ -249,9 +250,13 @@ impl FontRelativeLength {
context: &Context,
base_size: FontBaseSize,
orientation: FontMetricsOrientation,
flags: QueryFontMetricsFlags,
) -> FontMetrics {
let retrieve_math_scales = false;
context.query_font_metrics(base_size, orientation, retrieve_math_scales)
context.query_font_metrics(
base_size,
orientation,
flags,
)
}
let reference_font_size = base_size.resolve(context);
@@ -268,8 +273,12 @@ impl FontRelativeLength {
},
Self::Ex(length) => {
// The x-height is an intrinsically horizontal metric.
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
let metrics = query_font_metrics(
context,
base_size,
FontMetricsOrientation::Horizontal,
QueryFontMetricsFlags::empty(),
);
let reference_size = metrics.x_height.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ex
//
@@ -295,6 +304,7 @@ impl FontRelativeLength {
context,
base_size,
FontMetricsOrientation::MatchContextPreferHorizontal,
QueryFontMetricsFlags::NEEDS_CH,
);
let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ch
@@ -319,8 +329,12 @@ impl FontRelativeLength {
(reference_size, length)
},
Self::Cap(length) => {
let metrics =
query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
let metrics = query_font_metrics(
context,
base_size,
FontMetricsOrientation::Horizontal,
QueryFontMetricsFlags::empty(),
);
let reference_size = metrics.cap_height.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#cap
//
@@ -337,6 +351,7 @@ impl FontRelativeLength {
context,
base_size,
FontMetricsOrientation::MatchContextPreferVertical,
QueryFontMetricsFlags::NEEDS_IC,
);
let reference_size = metrics.ic_width.unwrap_or_else(|| {
// https://drafts.csswg.org/css-values/#ic

View File

@@ -331,6 +331,7 @@ include = [
"Margin",
"PositionProperty",
"PhysicalAxis",
"QueryFontMetricsFlags",
]
item_types = [
"enums",