Bug 1296042 - Make word-break: break-word behave like word-break: normal; overflow-wrap: anywhere. r=jfkthame
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 01 Mar 2019 02:33:07 +0000
changeset 519736 c1075c1f1605435c69a6964895600db8a711fbb5
parent 519735 7c3df2c8c8a7283fd33f055a7e504b777ef9380d
child 519797 d4e19870e27fbe3552bec2b961acc96f3222ea80
child 519800 fe2cba661d5eae557c0d7611bb5442fc0bfc02a2
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1296042
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1296042 - Make word-break: break-word behave like word-break: normal; overflow-wrap: anywhere. r=jfkthame Differential Revision: https://phabricator.services.mozilla.com/D21398
devtools/shared/css/generated/properties-db.js
layout/generic/nsTextFrame.cpp
layout/style/ServoBindings.toml
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/cbindgen.toml
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/inherited_text.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/text.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/text.rs
testing/web-platform/tests/css/css-text/word-break/word-break-break-word-overflow-wrap-interactions-ref.html
testing/web-platform/tests/css/css-text/word-break/word-break-break-word-overflow-wrap-interactions.html
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -9617,16 +9617,17 @@ exports.CSS_PROPERTIES = {
   "word-break": {
     "isInherited": true,
     "subproperties": [
       "word-break"
     ],
     "supports": [],
     "values": [
       "break-all",
+      "break-word",
       "inherit",
       "initial",
       "keep-all",
       "normal",
       "unset"
     ]
   },
   "word-spacing": {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2593,25 +2593,27 @@ static bool HasCompressedLeadingWhitespa
   }
   return false;
 }
 
 void BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
                                                      const void* aTextPtr) {
   using mozilla::intl::LineBreaker;
 
-  // for word-break style
-  switch (mLineContainer->StyleText()->mWordBreak) {
-    case NS_STYLE_WORDBREAK_BREAK_ALL:
+  auto wordBreak = mLineContainer->StyleText()->EffectiveWordBreak();
+  switch (wordBreak) {
+    case StyleWordBreak::BreakAll:
       mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_BreakAll);
       break;
-    case NS_STYLE_WORDBREAK_KEEP_ALL:
+    case StyleWordBreak::KeepAll:
       mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_KeepAll);
       break;
+    case StyleWordBreak::Normal:
     default:
+      MOZ_ASSERT(wordBreak == StyleWordBreak::Normal);
       mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_Normal);
       break;
   }
 
   // textruns have uniform language
   const nsStyleFont* styleFont = mMappedFlows[0].mStartFrame->StyleFont();
   // We should only use a language for hyphenation if it was specified
   // explicitly.
@@ -8310,17 +8312,17 @@ void nsTextFrame::AddInlineMinISizeForFl
     if (start < flowEndInTextRun && textRun->CanBreakLineBefore(start)) {
       aData->OptionallyBreak();
     }
     aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
     aData->mTrailingWhitespace = 0;
     return;
   }
 
-  if (textStyle->mOverflowWrap == mozilla::StyleOverflowWrap::Anywhere &&
+  if (textStyle->EffectiveOverflowWrap() == StyleOverflowWrap::Anywhere &&
       textStyle->WordCanWrap(this)) {
     aData->OptionallyBreak();
     aData->mCurrentLine +=
         textRun->GetMinAdvanceWidth(Range(start, flowEndInTextRun));
     aData->mTrailingWhitespace = 0;
     aData->mAtStartOfLine = false;
     aData->OptionallyBreak();
     return;
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -397,16 +397,17 @@ cbindgen-types = [
     { gecko = "StyleExtremumLength", servo = "values::computed::length::ExtremumLength" },
     { gecko = "StyleFillRule", servo = "values::generics::basic_shape::FillRule" },
     { gecko = "StyleFontDisplay", servo = "font_face::FontDisplay" },
     { gecko = "StyleFontFaceSourceListComponent", servo = "font_face::FontFaceSourceListComponent" },
     { gecko = "StyleFontLanguageOverride", servo = "values::computed::font::FontLanguageOverride" },
     { gecko = "StylePathCommand", servo = "values::specified::svg_path::PathCommand" },
     { gecko = "StyleUnicodeRange", servo = "cssparser::UnicodeRange" },
     { gecko = "StyleOverflowWrap", servo = "values::computed::OverflowWrap" },
+    { gecko = "StyleWordBreak", servo = "values::computed::WordBreak" },
     { gecko = "StyleUserSelect", servo = "values::computed::UserSelect" },
     { gecko = "StyleBreakBetween", servo = "values::computed::BreakBetween" },
     { gecko = "StyleBreakWithin", servo = "values::computed::BreakWithin" },
     { gecko = "StyleBorderStyle", servo = "values::computed::BorderStyle" },
     { gecko = "StyleOutlineStyle", servo = "values::computed::OutlineStyle" },
     { gecko = "StyleScrollSnapAlign", servo = "values::computed::ScrollSnapAlign" },
     { gecko = "StyleScrollSnapType", servo = "values::computed::ScrollSnapType" },
     { gecko = "StyleResize", servo = "values::computed::Resize" },
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -658,21 +658,16 @@ enum class StyleWhiteSpace : uint8_t {
   Normal = 0,
   Pre,
   Nowrap,
   PreWrap,
   PreLine,
   PreSpace,
 };
 
-// See nsStyleText
-#define NS_STYLE_WORDBREAK_NORMAL 0
-#define NS_STYLE_WORDBREAK_BREAK_ALL 1
-#define NS_STYLE_WORDBREAK_KEEP_ALL 2
-
 // ruby-align, see nsStyleText
 #define NS_STYLE_RUBY_ALIGN_START 0
 #define NS_STYLE_RUBY_ALIGN_CENTER 1
 #define NS_STYLE_RUBY_ALIGN_SPACE_BETWEEN 2
 #define NS_STYLE_RUBY_ALIGN_SPACE_AROUND 3
 
 // ruby-position, see nsStyleText
 #define NS_STYLE_RUBY_POSITION_OVER 0
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3693,18 +3693,16 @@ static bool AreShadowArraysEqual(nsCSSSh
 //
 
 nsStyleText::nsStyleText(const Document& aDocument)
     : mTextAlign(NS_STYLE_TEXT_ALIGN_START),
       mTextAlignLast(NS_STYLE_TEXT_ALIGN_AUTO),
       mTextJustify(StyleTextJustify::Auto),
       mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE),
       mWhiteSpace(StyleWhiteSpace::Normal),
-      mWordBreak(NS_STYLE_WORDBREAK_NORMAL),
-      mOverflowWrap(StyleOverflowWrap::Normal),
       mHyphens(StyleHyphens::Manual),
       mRubyAlign(NS_STYLE_RUBY_ALIGN_SPACE_AROUND),
       mRubyPosition(NS_STYLE_RUBY_POSITION_OVER),
       mTextSizeAdjust(NS_STYLE_TEXT_SIZE_ADJUST_AUTO),
       mTextCombineUpright(NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE),
       mControlCharacterVisibility(
           nsLayoutUtils::ControlCharVisibilityDefault()),
       mTextEmphasisStyle(NS_STYLE_TEXT_EMPHASIS_STYLE_NONE),
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1450,18 +1450,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   nsChangeHint CalcDifference(const nsStyleText& aNewData) const;
 
   uint8_t mTextAlign;      // NS_STYLE_TEXT_ALIGN_*
   uint8_t mTextAlignLast;  // NS_STYLE_TEXT_ALIGN_*
   mozilla::StyleTextJustify mTextJustify;
   uint8_t mTextTransform;  // NS_STYLE_TEXT_TRANSFORM_*
   mozilla::StyleWhiteSpace mWhiteSpace;
-  uint8_t mWordBreak;  // NS_STYLE_WORDBREAK_*
-  mozilla::StyleOverflowWrap mOverflowWrap;
+
+ private:
+  mozilla::StyleWordBreak mWordBreak = mozilla::StyleWordBreak::Normal;
+  mozilla::StyleOverflowWrap mOverflowWrap = mozilla::StyleOverflowWrap::Normal;
+
+ public:
   mozilla::StyleHyphens mHyphens;
   uint8_t mRubyAlign;           // NS_STYLE_RUBY_ALIGN_*
   uint8_t mRubyPosition;        // NS_STYLE_RUBY_POSITION_*
   uint8_t mTextSizeAdjust;      // NS_STYLE_TEXT_SIZE_ADJUST_*
   uint8_t mTextCombineUpright;  // NS_STYLE_TEXT_COMBINE_UPRIGHT_*
   uint8_t
       mControlCharacterVisibility;  // NS_STYLE_CONTROL_CHARACTER_VISIBILITY_*
   uint8_t mTextEmphasisPosition;    // NS_STYLE_TEXT_EMPHASIS_POSITION_*
@@ -1477,16 +1481,30 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsStyleCoord mLineHeight;     // coord, factor, normal
   mozilla::LengthPercentage mTextIndent;
   nscoord mWebkitTextStrokeWidth;  // coord
 
   RefPtr<nsCSSShadowArray> mTextShadow;  // nullptr in case of a zero-length
 
   nsString mTextEmphasisStyleString;
 
+  mozilla::StyleWordBreak EffectiveWordBreak() const {
+    if (mWordBreak == mozilla::StyleWordBreak::BreakWord) {
+      return mozilla::StyleWordBreak::Normal;
+    }
+    return mWordBreak;
+  }
+
+  mozilla::StyleOverflowWrap EffectiveOverflowWrap() const {
+    if (mWordBreak == mozilla::StyleWordBreak::BreakWord) {
+      return mozilla::StyleOverflowWrap::Anywhere;
+    }
+    return mOverflowWrap;
+  }
+
   bool WhiteSpaceIsSignificant() const {
     return mWhiteSpace == mozilla::StyleWhiteSpace::Pre ||
            mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap ||
            mWhiteSpace == mozilla::StyleWhiteSpace::PreSpace;
   }
 
   bool NewlineIsSignificantStyle() const {
     return mWhiteSpace == mozilla::StyleWhiteSpace::Pre ||
@@ -1511,18 +1529,19 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
            mWhiteSpace == mozilla::StyleWhiteSpace::PreWrap ||
            mWhiteSpace == mozilla::StyleWhiteSpace::PreLine;
   }
 
   bool WordCanWrapStyle() const {
     if (!WhiteSpaceCanWrapStyle()) {
       return false;
     }
-    return mOverflowWrap == mozilla::StyleOverflowWrap::BreakWord ||
-           mOverflowWrap == mozilla::StyleOverflowWrap::Anywhere;
+    auto owrap = EffectiveOverflowWrap();
+    return owrap == mozilla::StyleOverflowWrap::BreakWord ||
+           owrap == mozilla::StyleOverflowWrap::Anywhere;
   }
 
   bool HasTextEmphasis() const { return !mTextEmphasisStyleString.IsEmpty(); }
 
   bool HasWebkitTextStroke() const { return mWebkitTextStrokeWidth > 0; }
 
   // These are defined in nsStyleStructInlines.h.
   inline bool HasTextShadow() const;
--- a/servo/components/style/cbindgen.toml
+++ b/servo/components/style/cbindgen.toml
@@ -90,16 +90,17 @@ include = [
   "BackgroundSize",
   "BorderImageSlice",
   "BorderSpacing",
   "BorderRadius",
   "NonNegativeLengthOrNumberRect",
   "Perspective",
   "ZIndex",
   "TransformOrigin",
+  "WordBreak",
 ]
 item_types = ["enums", "structs", "typedefs"]
 
 [export.body]
 "CSSPixelLength" = """
   inline nscoord ToAppUnits() const;
 """
 
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -342,16 +342,17 @@ class Longhand(object):
                 "ScrollSnapAlign",
                 "ScrollSnapType",
                 "TextAlign",
                 "TextDecorationLine",
                 "TextEmphasisPosition",
                 "TouchAction",
                 "TransformStyle",
                 "UserSelect",
+                "WordBreak",
                 "XSpan",
                 "XTextZoom",
                 "ZIndex",
             }
         if self.name == "overflow-y":
             return True
         return bool(self.keyword)
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1323,16 +1323,17 @@ impl Clone for ${style_struct.gecko_stru
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
         "Transform": impl_transform,
         "TransformOrigin": impl_simple,
         "UserSelect": impl_simple,
         "url::UrlOrNone": impl_css_url,
+        "WordBreak": impl_simple,
         "ZIndex": impl_simple,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
--- a/servo/components/style/properties/longhands/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_text.mako.rs
@@ -69,23 +69,23 @@
     "computed::OverflowWrap::Normal",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
     alias="word-wrap",
     needs_context=False,
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
-// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
-${helpers.single_keyword(
+${helpers.predefined_type(
     "word-break",
-    "normal break-all keep-all",
-    gecko_constant_prefix="NS_STYLE_WORDBREAK",
+    "WordBreak",
+    "computed::WordBreak::Normal",
     animation_value_type="discrete",
     spec="https://drafts.csswg.org/css-text/#propdef-word-break",
+    needs_context=False,
     servo_restyle_damage="rebuild_and_reflow",
 )}
 
 // TODO(pcwalton): Support `text-justify: distribute`.
 <%helpers:single_keyword
     name="text-justify"
     values="auto none inter-word"
     extra_gecko_values="inter-character"
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -73,17 +73,17 @@ pub use self::percentage::{NonNegativePe
 pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex};
 pub use self::rect::NonNegativeLengthOrNumberRect;
 pub use self::resolution::Resolution;
 pub use self::svg::MozContextProperties;
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight};
-pub use self::text::{OverflowWrap, TextOverflow, WordSpacing};
+pub use self::text::{OverflowWrap, TextOverflow, WordSpacing, WordBreak};
 pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle};
 pub use self::time::Time;
 pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
 pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 #[cfg(feature = "gecko")]
 pub use self::ui::CursorImage;
 pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect};
 pub use super::specified::{BorderStyle, TextDecorationLine};
--- a/servo/components/style/values/computed/text.rs
+++ b/servo/components/style/values/computed/text.rs
@@ -12,17 +12,17 @@ use crate::values::generics::text::Initi
 use crate::values::generics::text::LineHeight as GenericLineHeight;
 use crate::values::generics::text::Spacing;
 use crate::values::specified::text::TextOverflowSide;
 use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
 use crate::values::{CSSFloat, CSSInteger};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
-pub use crate::values::specified::OverflowWrap;
+pub use crate::values::specified::{OverflowWrap, WordBreak};
 pub use crate::values::specified::TextAlignKeyword as TextAlign;
 pub use crate::values::specified::TextEmphasisPosition;
 
 /// A computed value for the `initial-letter` property.
 pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
 
 /// A computed value for the `letter-spacing` property.
 pub type LetterSpacing = Spacing<Length>;
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -75,17 +75,17 @@ pub use self::position::{PositionCompone
 pub use self::rect::NonNegativeLengthOrNumberRect;
 pub use self::resolution::Resolution;
 pub use self::svg::MozContextProperties;
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::svg_path::SVGPathData;
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign};
-pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle};
+pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
 pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
 pub use self::time::Time;
 pub use self::transform::{Rotate, Scale, Transform};
 pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 #[cfg(feature = "gecko")]
 pub use self::ui::CursorImage;
 pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect};
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -808,16 +808,42 @@ impl From<TextEmphasisPosition> for u8 {
             TextEmphasisVerticalWritingModeValue::Left => {
                 result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT;
             },
         };
         result as u8
     }
 }
 
+/// Values for the `word-break` property.
+#[repr(u8)]
+#[derive(
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    MallocSizeOf,
+    Parse,
+    PartialEq,
+    SpecifiedValueInfo,
+    ToComputedValue,
+    ToCss,
+)]
+#[allow(missing_docs)]
+pub enum WordBreak {
+    Normal,
+    BreakAll,
+    KeepAll,
+    /// The break-word value, needed for compat.
+    ///
+    /// Specifying `word-break: break-word` makes `overflow-wrap` behave as
+    /// `anywhere`, and `word-break` behave like `normal`.
+    BreakWord,
+}
+
 /// Values for the `overflow-wrap` property.
 #[repr(u8)]
 #[derive(
     Clone,
     Copy,
     Debug,
     Eq,
     MallocSizeOf,
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-word-overflow-wrap-interactions-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>CSS Test: word-break: break-word and overflow-wrap interaction</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1296042">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<style>
+  div {
+    float: left;
+    width: 100px;
+    height: 100px;
+    border: 1px solid black;
+    margin: 10px;
+    overflow-wrap: anywhere;
+  }
+</style>
+<p>The boxes below should look the same.</p>
+<div>FillerFillerFillerFiller</div>
+<div>FillerFillerFillerFiller</div>
+<div>FillerFillerFillerFiller</div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-text/word-break/word-break-break-word-overflow-wrap-interactions.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>CSS Test: word-break: break-word and overflow-wrap interaction</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1296042">
+<link rel="help" href="https://drafts.csswg.org/css-text/#propdef-word-break">
+<link rel="help" href="https://drafts.csswg.org/css-text/#propdef-overflow-wrap">
+<link rel="help" href="https://drafts.csswg.org/css-text/#valdef-overflow-wrap-anywhere">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="match" href="word-break-break-word-overflow-wrap-interactions-ref.html">
+<style>
+  div {
+    float: left;
+    width: 100px;
+    height: 100px;
+    border: 1px solid black;
+    margin: 10px;
+  }
+</style>
+<p>The boxes below should look the same.</p>
+<div style="word-break: break-word; overflow-wrap: anywhere;">FillerFillerFillerFiller</div>
+<div style="word-break: break-word; overflow-wrap: break-word;">FillerFillerFillerFiller</div>
+<div style="word-break: break-word; overflow-wrap: normal;">FillerFillerFillerFiller</div>