Bug 1852478 - Convert CSS white-space into a shorthand that expands to white-space-collapse and text-wrap-mode longhands. r=firefox-style-system-reviewers,emilio

Note that although this builds, it would (by itself) result in some test breakage;
this is resolved in the following patches that build on this.

Differential Revision: https://phabricator.services.mozilla.com/D198790
This commit is contained in:
Jonathan Kew
2024-01-25 22:20:05 +00:00
parent a1ea168c77
commit 87225af2ba
18 changed files with 267 additions and 162 deletions

View File

@@ -39267,6 +39267,7 @@ use.counter.css.doc:
send_in_pings: send_in_pings:
- use-counters - use-counters
webkit_column_rule_color: webkit_column_rule_color:
type: counter type: counter
description: > description: >

View File

@@ -38,8 +38,11 @@ void HTMLPreElement::MapAttributesIntoRule(
MappedDeclarationsBuilder& aBuilder) { MappedDeclarationsBuilder& aBuilder) {
// wrap: empty // wrap: empty
if (aBuilder.GetAttr(nsGkAtoms::wrap)) { if (aBuilder.GetAttr(nsGkAtoms::wrap)) {
aBuilder.SetKeywordValue(eCSSProperty_white_space, // Equivalent to expanding `white-space: pre-wrap`
StyleWhiteSpace::PreWrap); aBuilder.SetKeywordValue(eCSSProperty_white_space_collapse,
StyleWhiteSpaceCollapse::Preserve);
aBuilder.SetKeywordValue(eCSSProperty_text_wrap_mode,
StyleTextWrapMode::Wrap);
} }
nsGenericHTMLElement::MapCommonAttributesInto(aBuilder); nsGenericHTMLElement::MapCommonAttributesInto(aBuilder);

View File

@@ -163,7 +163,7 @@ void HTMLTableCellElement::MapAttributesIntoRule(
MappedDeclarationsBuilder& aBuilder) { MappedDeclarationsBuilder& aBuilder) {
MapImageSizeAttributesInto(aBuilder); MapImageSizeAttributesInto(aBuilder);
if (!aBuilder.PropertyIsSet(eCSSProperty_white_space)) { if (!aBuilder.PropertyIsSet(eCSSProperty_text_wrap_mode)) {
// nowrap: enum // nowrap: enum
if (aBuilder.GetAttr(nsGkAtoms::nowrap)) { if (aBuilder.GetAttr(nsGkAtoms::nowrap)) {
// See if our width is not a nonzero integer width. // See if our width is not a nonzero integer width.
@@ -171,8 +171,8 @@ void HTMLTableCellElement::MapAttributesIntoRule(
nsCompatibility mode = aBuilder.Document().GetCompatibilityMode(); nsCompatibility mode = aBuilder.Document().GetCompatibilityMode();
if (!value || value->Type() != nsAttrValue::eInteger || if (!value || value->Type() != nsAttrValue::eInteger ||
value->GetIntegerValue() == 0 || eCompatibility_NavQuirks != mode) { value->GetIntegerValue() == 0 || eCompatibility_NavQuirks != mode) {
aBuilder.SetKeywordValue(eCSSProperty_white_space, aBuilder.SetKeywordValue(eCSSProperty_text_wrap_mode,
StyleWhiteSpace::Nowrap); StyleTextWrapMode::Nowrap);
} }
} }
} }

View File

@@ -365,12 +365,14 @@ bool HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID,
void HTMLTextAreaElement::MapAttributesIntoRule( void HTMLTextAreaElement::MapAttributesIntoRule(
MappedDeclarationsBuilder& aBuilder) { MappedDeclarationsBuilder& aBuilder) {
// wrap=off // wrap=off
if (!aBuilder.PropertyIsSet(eCSSProperty_white_space)) { const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::wrap);
const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::wrap); if (value && value->Type() == nsAttrValue::eString &&
if (value && value->Type() == nsAttrValue::eString && value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
value->Equals(nsGkAtoms::OFF, eIgnoreCase)) { // Equivalent to expanding `white-space; pre`
aBuilder.SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Pre); aBuilder.SetKeywordValue(eCSSProperty_white_space_collapse,
} StyleWhiteSpaceCollapse::Preserve);
aBuilder.SetKeywordValue(eCSSProperty_text_wrap_mode,
StyleTextWrapMode::Nowrap);
} }
nsGenericHTMLFormControlElementWithState::MapDivAlignAttributeInto(aBuilder); nsGenericHTMLFormControlElementWithState::MapDivAlignAttributeInto(aBuilder);

View File

@@ -89,8 +89,8 @@ bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
} }
// static // static
Maybe<StyleWhiteSpace> EditorUtils::GetComputedWhiteSpaceStyle( Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
const nsIContent& aContent) { EditorUtils::GetComputedWhiteSpaceStyles(const nsIContent& aContent) {
if (MOZ_UNLIKELY(!aContent.IsElement() && !aContent.GetParentElement())) { if (MOZ_UNLIKELY(!aContent.IsElement() && !aContent.GetParentElement())) {
return Nothing(); return Nothing();
} }
@@ -101,7 +101,9 @@ Maybe<StyleWhiteSpace> EditorUtils::GetComputedWhiteSpaceStyle(
if (NS_WARN_IF(!elementStyle)) { if (NS_WARN_IF(!elementStyle)) {
return Nothing(); return Nothing();
} }
return Some(elementStyle->StyleText()->mWhiteSpace); const auto* styleText = elementStyle->StyleText();
return Some(
std::pair(styleText->mWhiteSpaceCollapse, styleText->mTextWrapMode));
} }
// static // static
@@ -164,7 +166,8 @@ bool EditorUtils::IsOnlyNewLinePreformatted(const nsIContent& aContent) {
return false; return false;
} }
return elementStyle->StyleText()->mWhiteSpace == StyleWhiteSpace::PreLine; return elementStyle->StyleText()->mWhiteSpaceCollapse ==
StyleWhiteSpaceCollapse::PreserveBreaks;
} }
// static // static

View File

@@ -401,10 +401,10 @@ class EditorUtils final {
} }
/** /**
* Get computed white-space style of aContent. * Get the two longhands that make up computed white-space style of aContent.
*/ */
static Maybe<StyleWhiteSpace> GetComputedWhiteSpaceStyle( static Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
const nsIContent& aContent); GetComputedWhiteSpaceStyles(const nsIContent& aContent);
/** /**
* IsWhiteSpacePreformatted() checks the style info for the node for the * IsWhiteSpacePreformatted() checks the style info for the node for the
@@ -421,7 +421,7 @@ class EditorUtils final {
/** /**
* IsOnlyNewLinePreformatted() checks whether the linefeed characters are * IsOnlyNewLinePreformatted() checks whether the linefeed characters are
* preformated but white-spaces are collapsed, or otherwise. I.e., this * preformated but white-spaces are collapsed, or otherwise. I.e., this
* returns true only when `white-space:pre-line`. * returns true only when `white-space-collapse:pre-line`.
*/ */
static bool IsOnlyNewLinePreformatted(const nsIContent& aContent); static bool IsOnlyNewLinePreformatted(const nsIContent& aContent);

View File

@@ -5353,8 +5353,10 @@ HTMLEditor::AutoMoveOneLineHandler::ConsiderWhetherPreserveWhiteSpaceStyle(
// If the content has different `white-space` style from <pre>, we // If the content has different `white-space` style from <pre>, we
// shouldn't treat it as a descendant of <pre> because web apps or // shouldn't treat it as a descendant of <pre> because web apps or
// the user intent to treat the white-spaces in aContent not as `pre`. // the user intent to treat the white-spaces in aContent not as `pre`.
if (EditorUtils::GetComputedWhiteSpaceStyle(aContent).valueOr( if (EditorUtils::GetComputedWhiteSpaceStyles(aContent).valueOr(std::pair(
StyleWhiteSpace::Normal) != StyleWhiteSpace::Pre) { StyleWhiteSpaceCollapse::Collapse, StyleTextWrapMode::Wrap)) !=
std::pair(StyleWhiteSpaceCollapse::Preserve,
StyleTextWrapMode::Nowrap)) {
return false; return false;
} }
for (const Element* element : for (const Element* element :
@@ -5801,52 +5803,67 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aPointToInsert.IsInContentNode()); MOZ_ASSERT(aPointToInsert.IsInContentNode());
const auto destWhiteSpaceStyle = [&]() -> Maybe<StyleWhiteSpace> { const auto destWhiteSpaceStyles =
[&]() -> Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>> {
if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No || if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No ||
!aPointToInsert.IsInContentNode()) { !aPointToInsert.IsInContentNode()) {
return Nothing(); return Nothing();
} }
auto style = EditorUtils::GetComputedWhiteSpaceStyle( auto styles = EditorUtils::GetComputedWhiteSpaceStyles(
*aPointToInsert.ContainerAs<nsIContent>()); *aPointToInsert.ContainerAs<nsIContent>());
if (NS_WARN_IF(style.isSome() && if (NS_WARN_IF(styles.isSome() &&
style.value() == StyleWhiteSpace::PreSpace)) { styles.value().first ==
StyleWhiteSpaceCollapse::PreserveSpaces)) {
return Nothing(); return Nothing();
} }
return style; return styles;
}(); }();
const auto srcWhiteSpaceStyle = [&]() -> Maybe<StyleWhiteSpace> { const auto srcWhiteSpaceStyles =
[&]() -> Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>> {
if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No) { if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No) {
return Nothing(); return Nothing();
} }
auto style = EditorUtils::GetComputedWhiteSpaceStyle(aContentToMove); auto styles = EditorUtils::GetComputedWhiteSpaceStyles(aContentToMove);
if (NS_WARN_IF(style.isSome() && if (NS_WARN_IF(styles.isSome() &&
style.value() == StyleWhiteSpace::PreSpace)) { styles.value().first ==
StyleWhiteSpaceCollapse::PreserveSpaces)) {
return Nothing(); return Nothing();
} }
return style; return styles;
}(); }();
const auto GetWhiteSpaceStyleValue = [](StyleWhiteSpace aStyleWhiteSpace) { // Get the `white-space` shorthand form for the given collapse + mode pair.
switch (aStyleWhiteSpace) { const auto GetWhiteSpaceStyleValue =
case StyleWhiteSpace::Normal: [](std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode> aStyles) {
if (aStyles.second == StyleTextWrapMode::Wrap) {
switch (aStyles.first) {
case StyleWhiteSpaceCollapse::Collapse:
return u"normal"_ns;
case StyleWhiteSpaceCollapse::Preserve:
return u"pre-wrap"_ns;
case StyleWhiteSpaceCollapse::PreserveBreaks:
return u"pre-line"_ns;
case StyleWhiteSpaceCollapse::PreserveSpaces:
return u"preserve-spaces"_ns;
case StyleWhiteSpaceCollapse::BreakSpaces:
return u"break-spaces"_ns;
}
} else {
switch (aStyles.first) {
case StyleWhiteSpaceCollapse::Collapse:
return u"nowrap"_ns;
case StyleWhiteSpaceCollapse::Preserve:
return u"pre"_ns;
case StyleWhiteSpaceCollapse::PreserveBreaks:
return u"nowrap preserve-breaks"_ns;
case StyleWhiteSpaceCollapse::PreserveSpaces:
return u"nowrap preserve-spaces"_ns;
case StyleWhiteSpaceCollapse::BreakSpaces:
return u"nowrap break-spaces"_ns;
}
}
MOZ_ASSERT_UNREACHABLE("all values should be handled above!");
return u"normal"_ns; return u"normal"_ns;
case StyleWhiteSpace::Pre: };
return u"pre"_ns;
case StyleWhiteSpace::Nowrap:
return u"nowrap"_ns;
case StyleWhiteSpace::PreWrap:
return u"pre-wrap"_ns;
case StyleWhiteSpace::PreLine:
return u"pre-line"_ns;
case StyleWhiteSpace::BreakSpaces:
return u"break-spaces"_ns;
case StyleWhiteSpace::PreSpace:
MOZ_ASSERT_UNREACHABLE("Don't handle -moz-pre-space");
return u""_ns;
default:
MOZ_ASSERT_UNREACHABLE("Handle the new white-space value");
return u""_ns;
}
};
if (aRemoveIfCommentNode == RemoveIfCommentNode::Yes && if (aRemoveIfCommentNode == RemoveIfCommentNode::Yes &&
aContentToMove.IsComment()) { aContentToMove.IsComment()) {
@@ -5872,15 +5889,15 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
// Preserve white-space in the new position with using `style` attribute. // Preserve white-space in the new position with using `style` attribute.
// This is additional path from point of view of our traditional behavior. // This is additional path from point of view of our traditional behavior.
// Therefore, ignore errors especially if we got unexpected DOM tree. // Therefore, ignore errors especially if we got unexpected DOM tree.
if (destWhiteSpaceStyle.isSome() && srcWhiteSpaceStyle.isSome() && if (destWhiteSpaceStyles.isSome() && srcWhiteSpaceStyles.isSome() &&
destWhiteSpaceStyle.value() != srcWhiteSpaceStyle.value()) { destWhiteSpaceStyles.value() != srcWhiteSpaceStyles.value()) {
// Set `white-space` with `style` attribute if it's nsStyledElement. // Set `white-space` with `style` attribute if it's nsStyledElement.
if (nsStyledElement* styledElement = if (nsStyledElement* styledElement =
nsStyledElement::FromNode(&aContentToMove)) { nsStyledElement::FromNode(&aContentToMove)) {
DebugOnly<nsresult> rvIgnored = DebugOnly<nsresult> rvIgnored =
CSSEditUtils::SetCSSPropertyWithTransaction( CSSEditUtils::SetCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::white_space, *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::white_space,
GetWhiteSpaceStyleValue(srcWhiteSpaceStyle.value())); GetWhiteSpaceStyleValue(srcWhiteSpaceStyles.value()));
if (NS_WARN_IF(Destroyed())) { if (NS_WARN_IF(Destroyed())) {
return Err(NS_ERROR_EDITOR_DESTROYED); return Err(NS_ERROR_EDITOR_DESTROYED);
} }
@@ -5900,7 +5917,7 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
} }
nsAutoString styleAttrValue(u"white-space: "_ns); nsAutoString styleAttrValue(u"white-space: "_ns);
styleAttrValue.Append( styleAttrValue.Append(
GetWhiteSpaceStyleValue(srcWhiteSpaceStyle.value())); GetWhiteSpaceStyleValue(srcWhiteSpaceStyles.value()));
IgnoredErrorResult error; IgnoredErrorResult error;
newSpanElement->SetAttr(nsGkAtoms::style, styleAttrValue, error); newSpanElement->SetAttr(nsGkAtoms::style, styleAttrValue, error);
NS_WARNING_ASSERTION(!error.Failed(), NS_WARNING_ASSERTION(!error.Failed(),

View File

@@ -724,8 +724,8 @@ Result<EditActionResult, nsresult> WhiteSpaceVisibilityKeeper::
// a bug to manage only the change. // a bug to manage only the change.
(aLeftBlockElement.NodeInfo()->NameAtom() == (aLeftBlockElement.NodeInfo()->NameAtom() ==
aRightBlockElement.NodeInfo()->NameAtom() && aRightBlockElement.NodeInfo()->NameAtom() &&
EditorUtils::GetComputedWhiteSpaceStyle(aLeftBlockElement) == EditorUtils::GetComputedWhiteSpaceStyles(aLeftBlockElement) ==
EditorUtils::GetComputedWhiteSpaceStyle(aRightBlockElement))) { EditorUtils::GetComputedWhiteSpaceStyles(aRightBlockElement))) {
// Nodes are same type. merge them. // Nodes are same type. merge them.
EditorDOMPoint atFirstChildOfRightNode; EditorDOMPoint atFirstChildOfRightNode;
nsresult rv = aHTMLEditor.JoinNearestEditableNodesWithTransaction( nsresult rv = aHTMLEditor.JoinNearestEditableNodesWithTransaction(

View File

@@ -9128,8 +9128,8 @@ nsresult nsIFrame::PeekOffsetForWord(PeekOffsetStruct* aPos, int32_t aOffset) {
// significant. // significant.
if (next.mJumpedLine && wordSelectEatSpace && if (next.mJumpedLine && wordSelectEatSpace &&
current.mFrame->HasSignificantTerminalNewline() && current.mFrame->HasSignificantTerminalNewline() &&
current.mFrame->StyleText()->mWhiteSpace != current.mFrame->StyleText()->mWhiteSpaceCollapse !=
StyleWhiteSpace::PreLine) { StyleWhiteSpaceCollapse::PreserveBreaks) {
current.mOffset -= 1; current.mOffset -= 1;
} }
break; break;

View File

@@ -781,7 +781,8 @@ static bool IsTrimmableSpace(const nsTextFragment* aFrag, uint32_t aPos,
!IsSpaceCombiningSequenceTail(aFrag, aPos + 1); !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
case '\n': case '\n':
return !aStyleText->NewlineIsSignificantStyle() && return !aStyleText->NewlineIsSignificantStyle() &&
aStyleText->mWhiteSpace != mozilla::StyleWhiteSpace::PreSpace; aStyleText->mWhiteSpaceCollapse !=
StyleWhiteSpaceCollapse::PreserveSpaces;
case '\t': case '\t':
case '\r': case '\r':
case '\f': case '\f':
@@ -1171,27 +1172,23 @@ static bool TextContainsLineBreakerWhiteSpace(const void* aText,
static nsTextFrameUtils::CompressionMode GetCSSWhitespaceToCompressionMode( static nsTextFrameUtils::CompressionMode GetCSSWhitespaceToCompressionMode(
nsTextFrame* aFrame, const nsStyleText* aStyleText) { nsTextFrame* aFrame, const nsStyleText* aStyleText) {
switch (aStyleText->mWhiteSpace) { switch (aStyleText->mWhiteSpaceCollapse) {
case StyleWhiteSpace::Normal: case StyleWhiteSpaceCollapse::Collapse:
case StyleWhiteSpace::Nowrap:
return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE; return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE;
case StyleWhiteSpace::Pre: case StyleWhiteSpaceCollapse::PreserveBreaks:
case StyleWhiteSpace::PreWrap: return nsTextFrameUtils::COMPRESS_WHITESPACE;
case StyleWhiteSpace::BreakSpaces: case StyleWhiteSpaceCollapse::Preserve:
case StyleWhiteSpaceCollapse::PreserveSpaces:
case StyleWhiteSpaceCollapse::BreakSpaces:
if (!aStyleText->NewlineIsSignificant(aFrame)) { if (!aStyleText->NewlineIsSignificant(aFrame)) {
// If newline is set to be preserved, but then suppressed, // If newline is set to be preserved, but then suppressed,
// transform newline to space. // transform newline to space.
return nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE; return nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
} }
return nsTextFrameUtils::COMPRESS_NONE; return nsTextFrameUtils::COMPRESS_NONE;
case StyleWhiteSpace::PreSpace:
return nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
case StyleWhiteSpace::PreLine:
return nsTextFrameUtils::COMPRESS_WHITESPACE;
default:
MOZ_ASSERT_UNREACHABLE("Unknown white-space value");
return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE;
} }
MOZ_ASSERT_UNREACHABLE("Unknown white-space-collapse value");
return nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE;
} }
struct FrameTextTraversal { struct FrameTextTraversal {
@@ -9539,7 +9536,8 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
} }
bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() || bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML); HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML);
bool isBreakSpaces = textStyle->mWhiteSpace == StyleWhiteSpace::BreakSpaces; bool isBreakSpaces =
textStyle->mWhiteSpaceCollapse == StyleWhiteSpaceCollapse::BreakSpaces;
// allow whitespace to overflow the container // allow whitespace to overflow the container
bool whitespaceCanHang = textStyle->WhiteSpaceCanHangOrVisuallyCollapse(); bool whitespaceCanHang = textStyle->WhiteSpaceCanHangOrVisuallyCollapse();
gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority(); gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
@@ -10307,9 +10305,9 @@ bool nsTextFrame::IsEmpty() {
return true; return true;
} }
bool isEmpty = bool isEmpty = IsAllWhitespace(TextFragment(),
IsAllWhitespace(TextFragment(), textStyle->mWhiteSpace != textStyle->mWhiteSpaceCollapse !=
mozilla::StyleWhiteSpace::PreLine); StyleWhiteSpaceCollapse::PreserveBreaks);
AddStateBits(isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE); AddStateBits(isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
return isEmpty; return isEmpty;
} }

View File

@@ -137,7 +137,8 @@ rusty-enums = [
"mozilla::StyleListStylePosition", "mozilla::StyleListStylePosition",
"mozilla::StylePointerEvents", "mozilla::StylePointerEvents",
"mozilla::StyleScrollbarWidth", "mozilla::StyleScrollbarWidth",
"mozilla::StyleWhiteSpace", "mozilla::StyleWhiteSpaceCollapse",
"mozilla::StyleTextWrapMode",
"mozilla::StyleTextRendering", "mozilla::StyleTextRendering",
"mozilla::StyleFlexDirection", "mozilla::StyleFlexDirection",
"mozilla::StyleStrokeLinecap", "mozilla::StyleStrokeLinecap",

View File

@@ -389,16 +389,21 @@ enum class StyleVisibility : uint8_t {
}; };
// See nsStyleText // See nsStyleText
enum class StyleWhiteSpace : uint8_t { enum class StyleWhiteSpaceCollapse : uint8_t {
Normal = 0, Collapse = 0,
Pre, // TODO: Discard not yet supported
Nowrap, Preserve,
PreWrap, PreserveBreaks,
PreLine, PreserveSpaces,
PreSpace,
BreakSpaces, BreakSpaces,
}; };
// See nsStyleText
enum class StyleTextWrapMode : uint8_t {
Wrap = 0,
Nowrap,
};
// See nsStyleText // See nsStyleText
// TODO: this will become StyleTextWrapStyle when we turn text-wrap // TODO: this will become StyleTextWrapStyle when we turn text-wrap
// (see https://bugzilla.mozilla.org/show_bug.cgi?id=1758391) and // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1758391) and

View File

@@ -2791,7 +2791,6 @@ nsStyleText::nsStyleText(const Document& aDocument)
mTextAlign(StyleTextAlign::Start), mTextAlign(StyleTextAlign::Start),
mTextAlignLast(StyleTextAlignLast::Auto), mTextAlignLast(StyleTextAlignLast::Auto),
mTextJustify(StyleTextJustify::Auto), mTextJustify(StyleTextJustify::Auto),
mWhiteSpace(StyleWhiteSpace::Normal),
mHyphens(StyleHyphens::Manual), mHyphens(StyleHyphens::Manual),
mRubyAlign(StyleRubyAlign::SpaceAround), mRubyAlign(StyleRubyAlign::SpaceAround),
mRubyPosition(StyleRubyPosition::AlternateOver), mRubyPosition(StyleRubyPosition::AlternateOver),
@@ -2828,7 +2827,8 @@ nsStyleText::nsStyleText(const nsStyleText& aSource)
mTextAlign(aSource.mTextAlign), mTextAlign(aSource.mTextAlign),
mTextAlignLast(aSource.mTextAlignLast), mTextAlignLast(aSource.mTextAlignLast),
mTextJustify(aSource.mTextJustify), mTextJustify(aSource.mTextJustify),
mWhiteSpace(aSource.mWhiteSpace), mWhiteSpaceCollapse(aSource.mWhiteSpaceCollapse),
mTextWrapMode(aSource.mTextWrapMode),
mLineBreak(aSource.mLineBreak), mLineBreak(aSource.mLineBreak),
mWordBreak(aSource.mWordBreak), mWordBreak(aSource.mWordBreak),
mOverflowWrap(aSource.mOverflowWrap), mOverflowWrap(aSource.mOverflowWrap),
@@ -2875,7 +2875,8 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
if ((mTextAlign != aNewData.mTextAlign) || if ((mTextAlign != aNewData.mTextAlign) ||
(mTextAlignLast != aNewData.mTextAlignLast) || (mTextAlignLast != aNewData.mTextAlignLast) ||
(mTextTransform != aNewData.mTextTransform) || (mTextTransform != aNewData.mTextTransform) ||
(mWhiteSpace != aNewData.mWhiteSpace) || (mWhiteSpaceCollapse != aNewData.mWhiteSpaceCollapse) ||
(mTextWrapMode != aNewData.mTextWrapMode) ||
(mLineBreak != aNewData.mLineBreak) || (mLineBreak != aNewData.mLineBreak) ||
(mWordBreak != aNewData.mWordBreak) || (mWordBreak != aNewData.mWordBreak) ||
(mOverflowWrap != aNewData.mOverflowWrap) || (mOverflowWrap != aNewData.mOverflowWrap) ||

View File

@@ -845,7 +845,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText {
mozilla::StyleTextAlign mTextAlign; mozilla::StyleTextAlign mTextAlign;
mozilla::StyleTextAlignLast mTextAlignLast; mozilla::StyleTextAlignLast mTextAlignLast;
mozilla::StyleTextJustify mTextJustify; mozilla::StyleTextJustify mTextJustify;
mozilla::StyleWhiteSpace mWhiteSpace; mozilla::StyleWhiteSpaceCollapse mWhiteSpaceCollapse =
mozilla::StyleWhiteSpaceCollapse::Collapse;
mozilla::StyleTextWrapMode mTextWrapMode = mozilla::StyleTextWrapMode::Wrap;
mozilla::StyleLineBreak mLineBreak = mozilla::StyleLineBreak::Auto; mozilla::StyleLineBreak mLineBreak = mozilla::StyleLineBreak::Auto;
private: private:
@@ -918,10 +920,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText {
} }
bool WhiteSpaceIsSignificant() const { bool WhiteSpaceIsSignificant() const {
return mWhiteSpace == mozilla::StyleWhiteSpace::Pre || return mWhiteSpaceCollapse != mozilla::StyleWhiteSpaceCollapse::Collapse &&
mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap || mWhiteSpaceCollapse !=
mWhiteSpace == mozilla::StyleWhiteSpace::BreakSpaces || mozilla::StyleWhiteSpaceCollapse::PreserveBreaks;
mWhiteSpace == mozilla::StyleWhiteSpace::PreSpace;
} }
bool WhiteSpaceCanHangOrVisuallyCollapse() const { bool WhiteSpaceCanHangOrVisuallyCollapse() const {
@@ -930,35 +931,28 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText {
// WhiteSpaceCanWrapStyle() && // WhiteSpaceCanWrapStyle() &&
// WhiteSpaceIsSignificant() // WhiteSpaceIsSignificant()
// which simplifies to: // which simplifies to:
return mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap; return mTextWrapMode == mozilla::StyleTextWrapMode::Wrap &&
mWhiteSpaceCollapse != mozilla::StyleWhiteSpaceCollapse::BreakSpaces;
} }
bool NewlineIsSignificantStyle() const { bool NewlineIsSignificantStyle() const {
return mWhiteSpace == mozilla::StyleWhiteSpace::Pre || return mWhiteSpaceCollapse == mozilla::StyleWhiteSpaceCollapse::Preserve ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap || mWhiteSpaceCollapse ==
mWhiteSpace == mozilla::StyleWhiteSpace::BreakSpaces || mozilla::StyleWhiteSpaceCollapse::PreserveBreaks ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreLine; mWhiteSpaceCollapse == mozilla::StyleWhiteSpaceCollapse::BreakSpaces;
} }
bool WhiteSpaceOrNewlineIsSignificant() const { bool WhiteSpaceOrNewlineIsSignificant() const {
return mWhiteSpace == mozilla::StyleWhiteSpace::Pre || return NewlineIsSignificantStyle() || WhiteSpaceIsSignificant();
mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap ||
mWhiteSpace == mozilla::StyleWhiteSpace::BreakSpaces ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreLine ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreSpace;
} }
bool TabIsSignificant() const { bool TabIsSignificant() const {
return mWhiteSpace == mozilla::StyleWhiteSpace::Pre || return mWhiteSpaceCollapse == mozilla::StyleWhiteSpaceCollapse::Preserve ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap || mWhiteSpaceCollapse == mozilla::StyleWhiteSpaceCollapse::BreakSpaces;
mWhiteSpace == mozilla::StyleWhiteSpace::BreakSpaces;
} }
bool WhiteSpaceCanWrapStyle() const { bool WhiteSpaceCanWrapStyle() const {
return mWhiteSpace == mozilla::StyleWhiteSpace::Normal || return mTextWrapMode == mozilla::StyleTextWrapMode::Wrap;
mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap ||
mWhiteSpace == mozilla::StyleWhiteSpace::BreakSpaces ||
mWhiteSpace == mozilla::StyleWhiteSpace::PreLine;
} }
bool WordCanWrapStyle() const { bool WordCanWrapStyle() const {

View File

@@ -880,8 +880,8 @@ def _remove_common_first_line_and_first_letter_properties(props, engine):
props.remove("overflow-wrap") props.remove("overflow-wrap")
props.remove("text-align") props.remove("text-align")
props.remove("text-justify") props.remove("text-justify")
props.remove("white-space") props.remove("white-space-collapse")
props.remove("text-wrap") props.remove("text-wrap-mode")
props.remove("word-break") props.remove("word-break")
props.remove("text-indent") props.remove("text-indent")
@@ -983,11 +983,12 @@ class PropertyRestrictions:
def placeholder(data): def placeholder(data):
props = PropertyRestrictions.first_line(data) props = PropertyRestrictions.first_line(data)
props.add("opacity") props.add("opacity")
props.add("white-space")
props.add("text-wrap") props.add("text-wrap")
props.add("text-overflow") props.add("text-overflow")
props.add("text-align") props.add("text-align")
props.add("text-justify") props.add("text-justify")
for p in PropertyRestrictions.shorthand(data, "white-space"):
props.add(p)
return props return props
# https://drafts.csswg.org/css-pseudo/#marker-pseudo # https://drafts.csswg.org/css-pseudo/#marker-pseudo
@@ -995,7 +996,6 @@ class PropertyRestrictions:
def marker(data): def marker(data):
return set( return set(
[ [
"white-space",
"text-wrap", "text-wrap",
"color", "color",
"text-combine-upright", "text-combine-upright",
@@ -1006,6 +1006,7 @@ class PropertyRestrictions:
"line-height", "line-height",
"-moz-osx-font-smoothing", "-moz-osx-font-smoothing",
] ]
+ PropertyRestrictions.shorthand(data, "white-space")
+ PropertyRestrictions.spec(data, "css-fonts") + PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-animations") + PropertyRestrictions.spec(data, "css-animations")
+ PropertyRestrictions.spec(data, "css-transitions") + PropertyRestrictions.spec(data, "css-transitions")
@@ -1020,7 +1021,6 @@ class PropertyRestrictions:
"opacity", "opacity",
"visibility", "visibility",
"text-shadow", "text-shadow",
"white-space",
"text-wrap", "text-wrap",
"text-combine-upright", "text-combine-upright",
"ruby-position", "ruby-position",
@@ -1032,6 +1032,7 @@ class PropertyRestrictions:
"background-blend-mode", "background-blend-mode",
] ]
+ PropertyRestrictions.shorthand(data, "text-decoration") + PropertyRestrictions.shorthand(data, "text-decoration")
+ PropertyRestrictions.shorthand(data, "white-space")
+ PropertyRestrictions.shorthand(data, "background") + PropertyRestrictions.shorthand(data, "background")
+ PropertyRestrictions.shorthand(data, "outline") + PropertyRestrictions.shorthand(data, "outline")
+ PropertyRestrictions.shorthand(data, "font") + PropertyRestrictions.shorthand(data, "font")

View File

@@ -149,52 +149,26 @@ ${helpers.predefined_type(
affects="layout", affects="layout",
)} )}
<%helpers:single_keyword // TODO: `white-space-collapse: discard` not yet supported
name="white-space" ${helpers.single_keyword(
values="normal pre nowrap pre-wrap pre-line" name="white-space-collapse",
engines="gecko servo-2013 servo-2020", values="collapse preserve preserve-breaks preserve-spaces break-spaces",
extra_gecko_values="break-spaces -moz-pre-space" engines="gecko",
gecko_enum_prefix="StyleWhiteSpace" gecko_enum_prefix="StyleWhiteSpaceCollapse",
needs_conversion="True" animation_value_type="discrete",
animation_value_type="discrete" spec="https://drafts.csswg.org/css-text-4/#propdef-white-space-collapse",
spec="https://drafts.csswg.org/css-text/#propdef-white-space" affects="layout",
servo_restyle_damage="rebuild_and_reflow" )}
affects="layout"
>
% if engine in ["servo-2013", "servo-2020"]:
impl SpecifiedValue {
pub fn allow_wrap(&self) -> bool {
match *self {
SpecifiedValue::Nowrap |
SpecifiedValue::Pre => false,
SpecifiedValue::Normal |
SpecifiedValue::PreWrap |
SpecifiedValue::PreLine => true,
}
}
pub fn preserve_newlines(&self) -> bool { ${helpers.single_keyword(
match *self { name="text-wrap-mode",
SpecifiedValue::Normal | values="wrap nowrap",
SpecifiedValue::Nowrap => false, engines="gecko",
SpecifiedValue::Pre | gecko_enum_prefix="StyleTextWrapMode",
SpecifiedValue::PreWrap | animation_value_type="discrete",
SpecifiedValue::PreLine => true, spec="https://drafts.csswg.org/css-text-4/#propdef-text-wrap-mode",
} affects="layout",
} )}
pub fn preserve_spaces(&self) -> bool {
match *self {
SpecifiedValue::Normal |
SpecifiedValue::Nowrap |
SpecifiedValue::PreLine => false,
SpecifiedValue::Pre |
SpecifiedValue::PreWrap => true,
}
}
}
% endif
</%helpers:single_keyword>
${helpers.predefined_type( ${helpers.predefined_type(
"text-shadow", "text-shadow",

View File

@@ -46,6 +46,110 @@
} }
</%helpers:shorthand> </%helpers:shorthand>
<%helpers:shorthand
name="white-space"
engines="gecko"
sub_properties="text-wrap-mode white-space-collapse"
spec="https://www.w3.org/TR/css-text-4/#white-space-property"
>
use crate::properties::longhands::{text_wrap_mode, white_space_collapse};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
use white_space_collapse::computed_value::T as Collapse;
use text_wrap_mode::computed_value::T as Wrap;
fn parse_special_shorthands<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Longhands, ParseError<'i>> {
let (mode, collapse) = try_match_ident_ignore_ascii_case! { input,
"normal" => (Wrap::Wrap, Collapse::Collapse),
"pre" => (Wrap::Nowrap, Collapse::Preserve),
"pre-wrap" => (Wrap::Wrap, Collapse::Preserve),
"pre-line" => (Wrap::Wrap, Collapse::PreserveBreaks),
// TODO: deprecate/remove -moz-pre-space; the white-space-collapse: preserve-spaces value
// should serve this purpose?
"-moz-pre-space" => (Wrap::Wrap, Collapse::PreserveSpaces),
};
Ok(expanded! {
text_wrap_mode: mode,
white_space_collapse: collapse,
})
}
if let Ok(result) = input.try_parse(parse_special_shorthands) {
return Ok(result);
}
let mut wrap = None;
let mut collapse = None;
loop {
if wrap.is_none() {
if let Ok(value) = input.try_parse(|input| text_wrap_mode::parse(context, input)) {
wrap = Some(value);
continue
}
}
if collapse.is_none() {
if let Ok(value) = input.try_parse(|input| white_space_collapse::parse(context, input)) {
collapse = Some(value);
continue
}
}
break
}
if wrap.is_some() || collapse.is_some() {
Ok(expanded! {
text_wrap_mode: unwrap_or_initial!(text_wrap_mode, wrap),
white_space_collapse: unwrap_or_initial!(white_space_collapse, collapse),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
use white_space_collapse::computed_value::T as Collapse;
use text_wrap_mode::computed_value::T as Wrap;
match *self.text_wrap_mode {
Wrap::Wrap => {
match *self.white_space_collapse {
Collapse::Collapse => return dest.write_str("normal"),
Collapse::Preserve => return dest.write_str("pre-wrap"),
Collapse::PreserveBreaks => return dest.write_str("pre-line"),
Collapse::PreserveSpaces => return dest.write_str("-moz-pre-space"),
_ => (),
}
},
Wrap::Nowrap => {
if let Collapse::Preserve = *self.white_space_collapse {
return dest.write_str("pre");
}
},
}
let mut has_value = false;
if *self.white_space_collapse != Collapse::Collapse {
self.white_space_collapse.to_css(dest)?;
has_value = true;
}
if *self.text_wrap_mode != Wrap::Wrap {
if has_value {
dest.write_char(' ')?;
}
self.text_wrap_mode.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
// CSS Compatibility // CSS Compatibility
// https://compat.spec.whatwg.org/ // https://compat.spec.whatwg.org/
<%helpers:shorthand name="-webkit-text-stroke" <%helpers:shorthand name="-webkit-text-stroke"

View File

@@ -5311,7 +5311,8 @@ pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)), ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value), MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value), MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
WhiteSpace => longhands::white_space::SpecifiedValue::from_gecko_keyword(value), WhiteSpaceCollapse => get_from_computed::<longhands::white_space_collapse::SpecifiedValue>(value),
TextWrapMode => get_from_computed::<longhands::text_wrap_mode::SpecifiedValue>(value),
CaptionSide => get_from_computed::<CaptionSide>(value), CaptionSide => get_from_computed::<CaptionSide>(value),
BorderTopStyle => get_from_computed::<BorderStyle>(value), BorderTopStyle => get_from_computed::<BorderStyle>(value),
BorderRightStyle => get_from_computed::<BorderStyle>(value), BorderRightStyle => get_from_computed::<BorderStyle>(value),