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-22 12:57:54 +00:00
parent 9901e70871
commit 5f45c5f18d
18 changed files with 339 additions and 170 deletions

View File

@ -131,7 +131,7 @@ use.counter.error:
send_in_pings:
- use-counters
# Total of 2299 use counter metrics (excludes denominators).
# Total of 2303 use counter metrics (excludes denominators).
# Total of 356 'page' use counters.
use.counter.page:
svgsvgelement_getelementbyid:
@ -15667,7 +15667,7 @@ use.counter.deprecated_ops.doc:
send_in_pings:
- use-counters
# Total of 693 'CSS (page)' use counters.
# Total of 695 'CSS (page)' use counters.
use.counter.css.page:
css_align_content:
type: counter
@ -17641,6 +17641,23 @@ use.counter.css.page:
send_in_pings:
- use-counters
css_text_wrap_mode:
type: counter
description: >
Whether a page used the CSS property text-wrap-mode.
Compare against `use.counter.top_level_content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_touch_action:
type: counter
description: >
@ -17794,10 +17811,10 @@ use.counter.css.page:
send_in_pings:
- use-counters
css_white_space:
css_white_space_collapse:
type: counter
description: >
Whether a page used the CSS property white-space.
Whether a page used the CSS property white-space-collapse.
Compare against `use.counter.top_level_content_documents_destroyed`
to calculate the rate.
bugs:
@ -23183,6 +23200,23 @@ use.counter.css.page:
send_in_pings:
- use-counters
css_white_space:
type: counter
description: >
Whether a page used the CSS property white-space.
Compare against `use.counter.top_level_content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_webkit_text_stroke:
type: counter
description: >
@ -27450,7 +27484,7 @@ use.counter.css.page:
send_in_pings:
- use-counters
# Total of 693 'CSS (document)' use counters.
# Total of 695 'CSS (document)' use counters.
use.counter.css.doc:
css_align_content:
type: counter
@ -29424,6 +29458,23 @@ use.counter.css.doc:
send_in_pings:
- use-counters
css_text_wrap_mode:
type: counter
description: >
Whether a document used the CSS property text-wrap-mode.
Compare against `use.counter.content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_touch_action:
type: counter
description: >
@ -29577,10 +29628,10 @@ use.counter.css.doc:
send_in_pings:
- use-counters
css_white_space:
css_white_space_collapse:
type: counter
description: >
Whether a document used the CSS property white-space.
Whether a document used the CSS property white-space-collapse.
Compare against `use.counter.content_documents_destroyed`
to calculate the rate.
bugs:
@ -34966,6 +35017,23 @@ use.counter.css.doc:
send_in_pings:
- use-counters
css_white_space:
type: counter
description: >
Whether a document used the CSS property white-space.
Compare against `use.counter.content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_webkit_text_stroke:
type: counter
description: >

View File

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

View File

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

View File

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

View File

@ -89,8 +89,8 @@ bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
}
// static
Maybe<StyleWhiteSpace> EditorUtils::GetComputedWhiteSpaceStyle(
const nsIContent& aContent) {
Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
EditorUtils::GetComputedWhiteSpaceStyles(const nsIContent& aContent) {
if (MOZ_UNLIKELY(!aContent.IsElement() && !aContent.GetParentElement())) {
return Nothing();
}
@ -101,7 +101,9 @@ Maybe<StyleWhiteSpace> EditorUtils::GetComputedWhiteSpaceStyle(
if (NS_WARN_IF(!elementStyle)) {
return Nothing();
}
return Some(elementStyle->StyleText()->mWhiteSpace);
const auto* styleText = elementStyle->StyleText();
return Some(
std::pair(styleText->mWhiteSpaceCollapse, styleText->mTextWrapMode));
}
// static
@ -164,7 +166,8 @@ bool EditorUtils::IsOnlyNewLinePreformatted(const nsIContent& aContent) {
return false;
}
return elementStyle->StyleText()->mWhiteSpace == StyleWhiteSpace::PreLine;
return elementStyle->StyleText()->mWhiteSpaceCollapse ==
StyleWhiteSpaceCollapse::PreserveBreaks;
}
// 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(
const nsIContent& aContent);
static Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
GetComputedWhiteSpaceStyles(const nsIContent& aContent);
/**
* IsWhiteSpacePreformatted() checks the style info for the node for the
@ -421,7 +421,7 @@ class EditorUtils final {
/**
* IsOnlyNewLinePreformatted() checks whether the linefeed characters are
* 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);

View File

@ -5353,8 +5353,10 @@ HTMLEditor::AutoMoveOneLineHandler::ConsiderWhetherPreserveWhiteSpaceStyle(
// If the content has different `white-space` style from <pre>, we
// 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`.
if (EditorUtils::GetComputedWhiteSpaceStyle(aContent).valueOr(
StyleWhiteSpace::Normal) != StyleWhiteSpace::Pre) {
if (EditorUtils::GetComputedWhiteSpaceStyles(aContent).valueOr(std::pair(
StyleWhiteSpaceCollapse::Collapse, StyleTextWrapMode::Wrap)) !=
std::pair(StyleWhiteSpaceCollapse::Preserve,
StyleTextWrapMode::Nowrap)) {
return false;
}
for (const Element* element :
@ -5801,52 +5803,65 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aPointToInsert.IsInContentNode());
const auto destWhiteSpaceStyle = [&]() -> Maybe<StyleWhiteSpace> {
const auto destWhiteSpaceStyles =
[&]() -> Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>> {
if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No ||
!aPointToInsert.IsInContentNode()) {
return Nothing();
}
auto style = EditorUtils::GetComputedWhiteSpaceStyle(
auto styles = EditorUtils::GetComputedWhiteSpaceStyles(
*aPointToInsert.ContainerAs<nsIContent>());
if (NS_WARN_IF(style.isSome() &&
style.value() == StyleWhiteSpace::PreSpace)) {
if (NS_WARN_IF(styles.isSome() &&
styles.value().first ==
StyleWhiteSpaceCollapse::PreserveSpaces)) {
return Nothing();
}
return style;
return styles;
}();
const auto srcWhiteSpaceStyle = [&]() -> Maybe<StyleWhiteSpace> {
const auto srcWhiteSpaceStyles =
[&]() -> Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>> {
if (aPreserveWhiteSpaceStyle == PreserveWhiteSpaceStyle::No) {
return Nothing();
}
auto style = EditorUtils::GetComputedWhiteSpaceStyle(aContentToMove);
if (NS_WARN_IF(style.isSome() &&
style.value() == StyleWhiteSpace::PreSpace)) {
auto styles = EditorUtils::GetComputedWhiteSpaceStyles(aContentToMove);
if (NS_WARN_IF(styles.isSome() &&
styles.value().first ==
StyleWhiteSpaceCollapse::PreserveSpaces)) {
return Nothing();
}
return style;
return styles;
}();
const auto GetWhiteSpaceStyleValue = [](StyleWhiteSpace aStyleWhiteSpace) {
switch (aStyleWhiteSpace) {
case StyleWhiteSpace::Normal:
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;
}
};
// Get the `white-space` shorthand form for the given collapse + mode pair.
const auto GetWhiteSpaceStyleValue =
[](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;
}
}
};
if (aRemoveIfCommentNode == RemoveIfCommentNode::Yes &&
aContentToMove.IsComment()) {
@ -5872,15 +5887,15 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
// Preserve white-space in the new position with using `style` attribute.
// This is additional path from point of view of our traditional behavior.
// Therefore, ignore errors especially if we got unexpected DOM tree.
if (destWhiteSpaceStyle.isSome() && srcWhiteSpaceStyle.isSome() &&
destWhiteSpaceStyle.value() != srcWhiteSpaceStyle.value()) {
if (destWhiteSpaceStyles.isSome() && srcWhiteSpaceStyles.isSome() &&
destWhiteSpaceStyles.value() != srcWhiteSpaceStyles.value()) {
// Set `white-space` with `style` attribute if it's nsStyledElement.
if (nsStyledElement* styledElement =
nsStyledElement::FromNode(&aContentToMove)) {
DebugOnly<nsresult> rvIgnored =
CSSEditUtils::SetCSSPropertyWithTransaction(
*this, MOZ_KnownLive(*styledElement), *nsGkAtoms::white_space,
GetWhiteSpaceStyleValue(srcWhiteSpaceStyle.value()));
GetWhiteSpaceStyleValue(srcWhiteSpaceStyles.value()));
if (NS_WARN_IF(Destroyed())) {
return Err(NS_ERROR_EDITOR_DESTROYED);
}
@ -5900,7 +5915,7 @@ Result<MoveNodeResult, nsresult> HTMLEditor::MoveNodeOrChildrenWithTransaction(
}
nsAutoString styleAttrValue(u"white-space: "_ns);
styleAttrValue.Append(
GetWhiteSpaceStyleValue(srcWhiteSpaceStyle.value()));
GetWhiteSpaceStyleValue(srcWhiteSpaceStyles.value()));
IgnoredErrorResult error;
newSpanElement->SetAttr(nsGkAtoms::style, styleAttrValue, error);
NS_WARNING_ASSERTION(!error.Failed(),

View File

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

View File

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

View File

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

View File

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

View File

@ -389,16 +389,21 @@ enum class StyleVisibility : uint8_t {
};
// See nsStyleText
enum class StyleWhiteSpace : uint8_t {
Normal = 0,
Pre,
Nowrap,
PreWrap,
PreLine,
PreSpace,
enum class StyleWhiteSpaceCollapse : uint8_t {
Collapse = 0,
// TODO: Discard not yet supported
Preserve,
PreserveBreaks,
PreserveSpaces,
BreakSpaces,
};
// See nsStyleText
enum class StyleTextWrapMode : uint8_t {
Wrap = 0,
Nowrap,
};
// See nsStyleText
// TODO: this will become StyleTextWrapStyle when we turn text-wrap
// (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),
mTextAlignLast(StyleTextAlignLast::Auto),
mTextJustify(StyleTextJustify::Auto),
mWhiteSpace(StyleWhiteSpace::Normal),
mHyphens(StyleHyphens::Manual),
mRubyAlign(StyleRubyAlign::SpaceAround),
mRubyPosition(StyleRubyPosition::AlternateOver),
@ -2828,7 +2827,8 @@ nsStyleText::nsStyleText(const nsStyleText& aSource)
mTextAlign(aSource.mTextAlign),
mTextAlignLast(aSource.mTextAlignLast),
mTextJustify(aSource.mTextJustify),
mWhiteSpace(aSource.mWhiteSpace),
mWhiteSpaceCollapse(aSource.mWhiteSpaceCollapse),
mTextWrapMode(aSource.mTextWrapMode),
mLineBreak(aSource.mLineBreak),
mWordBreak(aSource.mWordBreak),
mOverflowWrap(aSource.mOverflowWrap),
@ -2875,7 +2875,8 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
if ((mTextAlign != aNewData.mTextAlign) ||
(mTextAlignLast != aNewData.mTextAlignLast) ||
(mTextTransform != aNewData.mTextTransform) ||
(mWhiteSpace != aNewData.mWhiteSpace) ||
(mWhiteSpaceCollapse != aNewData.mWhiteSpaceCollapse) ||
(mTextWrapMode != aNewData.mTextWrapMode) ||
(mLineBreak != aNewData.mLineBreak) ||
(mWordBreak != aNewData.mWordBreak) ||
(mOverflowWrap != aNewData.mOverflowWrap) ||

View File

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

View File

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

View File

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

View File

@ -46,6 +46,110 @@
}
</%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
// https://compat.spec.whatwg.org/
<%helpers:shorthand name="-webkit-text-stroke"

View File

@ -5315,7 +5315,8 @@ pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
MathStyle => longhands::math_style::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),
BorderTopStyle => get_from_computed::<BorderStyle>(value),
BorderRightStyle => get_from_computed::<BorderStyle>(value),