Bug 1919658 - Add support for text-emphasis-position:auto. r=layout-reviewers,emilio

Differential Revision: https://phabricator.services.mozilla.com/D222855
This commit is contained in:
Jonathan Kew 2024-09-20 20:15:19 +00:00
parent 72630b5ab9
commit 106268f97e
6 changed files with 46 additions and 28 deletions

View File

@ -1644,7 +1644,8 @@ void nsLineLayout::AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd,
}
if (aStyleText->HasEffectiveTextEmphasis()) {
nscoord bsize = GetBSizeOfEmphasisMarks(spanFrame, aInflation);
LogicalSide side = aStyleText->TextEmphasisSide(mRootSpan->mWritingMode);
LogicalSide side = aStyleText->TextEmphasisSide(
mRootSpan->mWritingMode, spanFrame->StyleFont()->mLanguage);
if (side == LogicalSide::BStart) {
requiredStartLeading += bsize;
} else {
@ -2272,7 +2273,8 @@ void nsLineLayout::VerticalAlignFrames(PerSpanData* psd) {
blockEnd = descent;
delta = emphasisHeight;
}
LogicalSide side = mStyleText->TextEmphasisSide(lineWM);
LogicalSide side = mStyleText->TextEmphasisSide(
lineWM, spanFrame->StyleFont()->mLanguage);
if (side == LogicalSide::BStart) {
blockStart -= delta;
} else {

View File

@ -5103,7 +5103,7 @@ nsRect nsTextFrame::UpdateTextEmphasis(WritingMode aWM,
info->advance = info->textRun->GetAdvanceWidth();
// Calculate the baseline offset
LogicalSide side = styleText->TextEmphasisSide(aWM);
LogicalSide side = styleText->TextEmphasisSide(aWM, StyleFont()->mLanguage);
LogicalSize frameSize = GetLogicalSize(aWM);
// The overflow rect is inflated in the inline direction by half
// advance of the emphasis mark on each side, so that even if a mark

View File

@ -2818,6 +2818,7 @@ nsStyleText::nsStyleText(const Document& aDocument)
StaticPrefs::layout_css_control_characters_visible()
? StyleMozControlCharacterVisibility::Visible
: StyleMozControlCharacterVisibility::Hidden),
mTextEmphasisPosition(StyleTextEmphasisPosition::AUTO),
mTextRendering(StyleTextRendering::Auto),
mTextEmphasisColor(StyleColor::CurrentColor()),
mWebkitTextFillColor(StyleColor::CurrentColor()),
@ -2831,11 +2832,6 @@ nsStyleText::nsStyleText(const Document& aDocument)
mWebkitTextStrokeWidth(0),
mTextEmphasisStyle(StyleTextEmphasisStyle::None()) {
MOZ_COUNT_CTOR(nsStyleText);
RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
mTextEmphasisPosition =
language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
? StyleTextEmphasisPosition::UNDER
: StyleTextEmphasisPosition::OVER;
}
nsStyleText::nsStyleText(const nsStyleText& aSource)
@ -2962,18 +2958,32 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
return nsChangeHint(0);
}
LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
bool noLeftBit = !(mTextEmphasisPosition & StyleTextEmphasisPosition::LEFT);
DebugOnly<bool> noRightBit =
!(mTextEmphasisPosition & StyleTextEmphasisPosition::RIGHT);
bool noOverBit = !(mTextEmphasisPosition & StyleTextEmphasisPosition::OVER);
DebugOnly<bool> noUnderBit =
!(mTextEmphasisPosition & StyleTextEmphasisPosition::UNDER);
LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM,
const nsAtom* aLanguage) const {
mozilla::Side side;
if (mTextEmphasisPosition & StyleTextEmphasisPosition::AUTO) {
// 'auto' resolves to 'under right' for Chinese, 'over right' otherwise.
if (aWM.IsVertical()) {
side = eSideRight;
} else {
if (nsStyleUtil::MatchesLanguagePrefix(aLanguage, u"zh")) {
side = eSideBottom;
} else {
side = eSideTop;
}
}
} else {
if (aWM.IsVertical()) {
side = mTextEmphasisPosition & StyleTextEmphasisPosition::LEFT
? eSideLeft
: eSideRight;
} else {
side = mTextEmphasisPosition & StyleTextEmphasisPosition::OVER
? eSideTop
: eSideBottom;
}
}
MOZ_ASSERT((noOverBit != noUnderBit) &&
((noLeftBit != noRightBit) || noRightBit));
mozilla::Side side = aWM.IsVertical() ? (noLeftBit ? eSideRight : eSideLeft)
: (noOverBit ? eSideBottom : eSideTop);
LogicalSide result = aWM.LogicalSideForPhysicalSide(side);
MOZ_ASSERT(IsBlock(result));
return result;

View File

@ -1015,7 +1015,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText {
inline bool WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const;
inline bool WordCanWrap(const nsIFrame* aContextFrame) const;
mozilla::LogicalSide TextEmphasisSide(mozilla::WritingMode aWM) const;
mozilla::LogicalSide TextEmphasisSide(mozilla::WritingMode aWM,
const nsAtom* aLanguage) const;
};
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleVisibility {

View File

@ -177,9 +177,9 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"text-emphasis-position",
"TextEmphasisPosition",
"computed::TextEmphasisPosition::OVER",
"computed::TextEmphasisPosition::AUTO",
engines="gecko",
initial_specified_value="specified::TextEmphasisPosition::OVER",
initial_specified_value="specified::TextEmphasisPosition::AUTO",
animation_type="discrete",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
affects="layout",

View File

@ -692,6 +692,7 @@ impl Parse for TextEmphasisStyle {
)]
#[repr(C)]
#[css(bitflags(
single = "auto",
mixed = "over,under,left,right",
validate_mixed = "Self::validate_and_simplify"
))]
@ -700,23 +701,27 @@ impl Parse for TextEmphasisStyle {
pub struct TextEmphasisPosition(u8);
bitflags! {
impl TextEmphasisPosition: u8 {
/// Draws marks to the right of the text in vertical writing mode.
const OVER = 1 << 0;
/// Automatically choose mark position based on language.
const AUTO = 1 << 0;
/// Draw marks over the text in horizontal writing mode.
const OVER = 1 << 1;
/// Draw marks under the text in horizontal writing mode.
const UNDER = 1 << 1;
const UNDER = 1 << 2;
/// Draw marks to the left of the text in vertical writing mode.
const LEFT = 1 << 2;
/// Draws marks to the right of the text in vertical writing mode.
const RIGHT = 1 << 3;
const LEFT = 1 << 3;
/// Draw marks to the right of the text in vertical writing mode.
const RIGHT = 1 << 4;
}
}
impl TextEmphasisPosition {
fn validate_and_simplify(&mut self) -> bool {
// Require one but not both of 'over' and 'under'.
if self.intersects(Self::OVER) == self.intersects(Self::UNDER) {
return false;
}
// If 'left' is present, 'right' must be absent.
if self.intersects(Self::LEFT) {
return !self.intersects(Self::RIGHT);
}