Bug 1011369 - Pass word-break and line-break properties to the line-breaker as two distinct enum class parameters. r=m_kato
authorJonathan Kew <jkew@mozilla.com>
Mon, 20 May 2019 20:46:35 +0000
changeset 474596 f339909acbc30e4b1380e45318ccbe187a0d49ec
parent 474595 e7b525e15531245d12672fa3c08eef5703f93b70
child 474597 436e3199c386c6a7a5e70cc8685dd1247827cb90
push id36042
push userdvarga@mozilla.com
push dateTue, 21 May 2019 04:19:40 +0000
treeherdermozilla-central@ca560ff55451 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1011369
milestone69.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 1011369 - Pass word-break and line-break properties to the line-breaker as two distinct enum class parameters. r=m_kato Differential Revision: https://phabricator.services.mozilla.com/D30786
dom/base/nsLineBreaker.cpp
dom/base/nsLineBreaker.h
intl/lwbrk/LineBreaker.cpp
intl/lwbrk/LineBreaker.h
layout/generic/nsTextFrame.cpp
--- a/dom/base/nsLineBreaker.cpp
+++ b/dom/base/nsLineBreaker.cpp
@@ -15,17 +15,18 @@
 using mozilla::intl::LineBreaker;
 
 nsLineBreaker::nsLineBreaker()
     : mCurrentWordLanguage(nullptr),
       mCurrentWordContainsMixedLang(false),
       mCurrentWordContainsComplexChar(false),
       mAfterBreakableSpace(false),
       mBreakHere(false),
-      mWordBreak(LineBreaker::kWordBreak_Normal) {}
+      mWordBreak(LineBreaker::WordBreak::Normal),
+      mStrictness(LineBreaker::Strictness::Auto) {}
 
 nsLineBreaker::~nsLineBreaker() {
   NS_ASSERTION(mCurrentWord.Length() == 0,
                "Should have Reset() before destruction!");
 }
 
 static void SetupCapitalization(const char16_t* aWord, uint32_t aLength,
                                 bool* aCapitalization) {
@@ -52,32 +53,38 @@ static void SetupCapitalization(const ch
       capitalizeNextChar = true;
     }
   }
 }
 
 nsresult nsLineBreaker::FlushCurrentWord() {
   uint32_t length = mCurrentWord.Length();
   AutoTArray<uint8_t, 4000> breakState;
-  if (!breakState.AppendElements(length)) return NS_ERROR_OUT_OF_MEMORY;
+  if (!breakState.AppendElements(length)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   nsTArray<bool> capitalizationState;
 
-  if (!mCurrentWordContainsComplexChar ||
-      mWordBreak == LineBreaker::kWordBreak_Anywhere) {
+  if (mStrictness == LineBreaker::Strictness::Anywhere) {
+    memset(breakState.Elements(),
+           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL,
+           length * sizeof(uint8_t));
+  } else if (!mCurrentWordContainsComplexChar) {
     // For break-strict set everything internal to "break", otherwise
     // to "no break"!
     memset(breakState.Elements(),
-           mWordBreak >= LineBreaker::kWordBreak_BreakAll
+           mWordBreak == LineBreaker::WordBreak::BreakAll
                ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
                : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
            length * sizeof(uint8_t));
   } else {
     nsContentUtils::LineBreaker()->GetJISx4051Breaks(
-        mCurrentWord.Elements(), length, mWordBreak, breakState.Elements());
+        mCurrentWord.Elements(), length, mWordBreak, mStrictness,
+        breakState.Elements());
   }
 
   bool autoHyphenate = mCurrentWordLanguage && !mCurrentWordContainsMixedLang;
   uint32_t i;
   for (i = 0; autoHyphenate && i < mTextItems.Length(); ++i) {
     TextItem* ti = &mTextItems[i];
     if (!(ti->mFlags & BREAK_USE_AUTO_HYPHENATION)) {
       autoHyphenate = false;
@@ -221,32 +228,33 @@ nsresult nsLineBreaker::AppendText(nsAto
   for (;;) {
     char16_t ch = aText[offset];
     bool isSpace = IsSpace(ch);
     bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink && !noBreaksNeeded) {
       breakState[offset] =
           mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
-                  (mWordBreak >= LineBreaker::kWordBreak_BreakAll)
+                  mWordBreak == LineBreaker::WordBreak::BreakAll ||
+                  mStrictness == LineBreaker::Strictness::Anywhere
               ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
               : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = false;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace || ch == '\n') {
       if (offset > wordStart && aSink) {
         if (!(aFlags & BREAK_SUPPRESS_INSIDE)) {
           if (wordHasComplexChar) {
             // Save current start-of-word state because GetJISx4051Breaks will
             // set it to false
             uint8_t currentStart = breakState[wordStart];
             nsContentUtils::LineBreaker()->GetJISx4051Breaks(
-                aText + wordStart, offset - wordStart, mWordBreak,
+                aText + wordStart, offset - wordStart, mWordBreak, mStrictness,
                 breakState.Elements() + wordStart);
             breakState[wordStart] = currentStart;
           }
           if (hyphenator) {
             FindHyphenationPoints(hyphenator, aText + wordStart, aText + offset,
                                   breakState.Elements() + wordStart);
           }
         }
@@ -379,31 +387,32 @@ nsresult nsLineBreaker::AppendText(nsAto
     bool isSpace = IsSpace(ch);
     bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink) {
       // Consider word-break style.  Since the break position of CJK scripts
       // will be set by nsILineBreaker, we don't consider CJK at this point.
       breakState[offset] =
           mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
-                  (mWordBreak >= LineBreaker::kWordBreak_BreakAll)
+                  mWordBreak == LineBreaker::WordBreak::BreakAll ||
+                  mStrictness == LineBreaker::Strictness::Anywhere
               ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
               : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = false;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace) {
       if (offset > wordStart && wordHasComplexChar) {
         if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
           // Save current start-of-word state because GetJISx4051Breaks will
           // set it to false
           uint8_t currentStart = breakState[wordStart];
           nsContentUtils::LineBreaker()->GetJISx4051Breaks(
-              aText + wordStart, offset - wordStart, mWordBreak,
+              aText + wordStart, offset - wordStart, mWordBreak, mStrictness,
               breakState.Elements() + wordStart);
           breakState[wordStart] = currentStart;
         }
         wordHasComplexChar = false;
       }
 
       ++offset;
       if (offset >= aLength) break;
--- a/dom/base/nsLineBreaker.h
+++ b/dom/base/nsLineBreaker.h
@@ -170,35 +170,54 @@ class nsLineBreaker {
    * @param aTrailingBreak this is set to true when there is a break opportunity
    * at the end of the text. This will normally only be declared true when there
    * is breakable whitespace at the end.
    */
   nsresult Reset(bool* aTrailingBreak);
 
   /*
    * Set word-break mode for linebreaker.  This is set by word-break property.
-   * @param aMode is LineBreaker::kWordBreak_* value.
+   * @param aMode is LineBreaker::WordBreak::* value.
    */
-  void SetWordBreak(uint8_t aMode) {
+  void SetWordBreak(mozilla::intl::LineBreaker::WordBreak aMode) {
     // If current word is non-empty and mode is changing, flush the breaker.
     if (aMode != mWordBreak && !mCurrentWord.IsEmpty()) {
       nsresult rv = FlushCurrentWord();
       if (NS_FAILED(rv)) {
         NS_WARNING("FlushCurrentWord failed, line-breaks may be wrong");
       }
       // If previous mode was break-all, we should allow a break here.
       // XXX (jfkthame) css-text spec seems unclear on this, raised question in
       // https://github.com/w3c/csswg-drafts/issues/3897
-      if (mWordBreak == mozilla::intl::LineBreaker::kWordBreak_BreakAll) {
+      if (mWordBreak == mozilla::intl::LineBreaker::WordBreak::BreakAll) {
         mBreakHere = true;
       }
     }
     mWordBreak = aMode;
   }
 
+  /*
+   * Set line-break rule strictness mode for linebreaker.  This is set by the
+   * line-break property.
+   * @param aMode is LineBreaker::Strictness::* value.
+   */
+  void SetStrictness(mozilla::intl::LineBreaker::Strictness aMode) {
+    if (aMode != mStrictness && !mCurrentWord.IsEmpty()) {
+      nsresult rv = FlushCurrentWord();
+      if (NS_FAILED(rv)) {
+        NS_WARNING("FlushCurrentWord failed, line-breaks may be wrong");
+      }
+      // If previous mode was anywhere, we should allow a break here.
+      if (mStrictness == mozilla::intl::LineBreaker::Strictness::Anywhere) {
+        mBreakHere = true;
+      }
+    }
+    mStrictness = aMode;
+  }
+
  private:
   // This is a list of text sources that make up the "current word" (i.e.,
   // run of text which does not contain any whitespace). All the mLengths
   // are are nonzero, these cannot overlap.
   struct TextItem {
     TextItem(nsILineBreakSink* aSink, uint32_t aSinkOffset, uint32_t aLength,
              uint32_t aFlags)
         : mSink(aSink),
@@ -234,12 +253,14 @@ class nsLineBreaker {
   bool mCurrentWordContainsComplexChar;
 
   // True if the previous character was breakable whitespace
   bool mAfterBreakableSpace;
   // True if a break must be allowed at the current position because
   // a run of breakable whitespace ends here
   bool mBreakHere;
   // line break mode by "word-break" style
-  uint8_t mWordBreak;
+  mozilla::intl::LineBreaker::WordBreak mWordBreak;
+  // strictness of break rules, from line-break property
+  mozilla::intl::LineBreaker::Strictness mStrictness;
 };
 
 #endif /*NSLINEBREAKER_H_*/
--- a/intl/lwbrk/LineBreaker.cpp
+++ b/intl/lwbrk/LineBreaker.cpp
@@ -819,18 +819,18 @@ int32_t LineBreaker::WordMove(const char
     // (This is required for serializers. See Bug #344816.)
     // Also fall back to this when out of memory.
     if (aDirection < 0) {
       ret = (begin == int32_t(aPos)) ? begin - 1 : begin;
     } else {
       ret = end;
     }
   } else {
-    GetJISx4051Breaks(aText + begin, end - begin,
-                      LineBreaker::kWordBreak_Normal, breakState.Elements());
+    GetJISx4051Breaks(aText + begin, end - begin, WordBreak::Normal,
+                      Strictness::Auto, breakState.Elements());
 
     ret = aPos;
     do {
       ret += aDirection;
     } while (begin < ret && ret < end && !breakState[ret - begin]);
   }
 
   return ret;
@@ -850,17 +850,18 @@ int32_t LineBreaker::Prev(const char16_t
   NS_ASSERTION(aLen >= aPos && aPos > 0,
                "Bad position passed to nsJISx4051LineBreaker::Prev");
 
   int32_t prevPos = WordMove(aText, aLen, aPos, -1);
   return prevPos > 0 ? prevPos : NS_LINEBREAKER_NEED_MORE_TEXT;
 }
 
 void LineBreaker::GetJISx4051Breaks(const char16_t* aChars, uint32_t aLength,
-                                    uint8_t aWordBreak, uint8_t* aBreakBefore) {
+                                    WordBreak aWordBreak, Strictness aLevel,
+                                    uint8_t* aBreakBefore) {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
     char32_t ch = state.GetUnicodeCharAt(cur);
     uint32_t chLen = ch > 0xFFFFu ? 2 : 1;
     int8_t cl;
@@ -891,17 +892,17 @@ void LineBreaker::GetJISx4051Breaks(cons
     }
 
     // To implement word-break:break-all, we overwrite the line-break class of
     // alphanumeric characters so they are treated the same as ideographic.
     // The relevant characters will have been assigned CLASS_CHARACTER or
     // CLASS_CLOSE by GetClass(), but those classes also include others that
     // we don't want to touch here, so we re-check the Unicode line-break class
     // to determine which ones to modify.
-    if (aWordBreak == LineBreaker::kWordBreak_BreakAll &&
+    if (aWordBreak == WordBreak::BreakAll &&
         (cl == CLASS_CHARACTER || cl == CLASS_CLOSE)) {
       auto cls = GetLineBreakClass(ch);
       if (cls == U_LB_ALPHABETIC || cls == U_LB_NUMERIC ||
           cls == U_LB_AMBIGUOUS || cls == U_LB_COMPLEX_CONTEXT ||
           /* Additional Japanese and Korean LB classes; CSS Text spec doesn't
              explicitly mention these, but this appears to give expected
              behavior (spec issue?) */
           cls == U_LB_CONDITIONAL_JAPANESE_STARTER ||
@@ -909,18 +910,18 @@ void LineBreaker::GetJISx4051Breaks(cons
         cl = CLASS_BREAKABLE;
       }
     }
 
     bool allowBreak = false;
     if (cur > 0) {
       NS_ASSERTION(CLASS_COMPLEX != lastClass || CLASS_COMPLEX != cl,
                    "Loop should have prevented adjacent complex chars here");
-      if (aWordBreak == LineBreaker::kWordBreak_Normal ||
-          aWordBreak == LineBreaker::kWordBreak_BreakAll) {
+      if (aWordBreak == WordBreak::Normal ||
+          aWordBreak == WordBreak::BreakAll) {
         allowBreak = (state.UseConservativeBreaking())
                          ? GetPairConservative(lastClass, cl)
                          : GetPair(lastClass, cl);
       }
     }
     aBreakBefore[cur] = allowBreak;
     if (allowBreak) state.NotifyBreakBefore();
     lastClass = cl;
@@ -933,17 +934,17 @@ void LineBreaker::GetJISx4051Breaks(cons
           break;
         }
         ++end;
         if (c > 0xFFFFU) {  // it was a surrogate pair
           ++end;
         }
       }
 
-      if (aWordBreak == LineBreaker::kWordBreak_BreakAll) {
+      if (aWordBreak == WordBreak::BreakAll) {
         // For break-all, we don't need to run a dictionary-based breaking
         // algorithm, we just allow breaks between all grapheme clusters.
         ClusterIterator ci(aChars + cur, end - cur);
         while (!ci.AtEnd()) {
           ci.Next();
           aBreakBefore[ci - aChars] = true;
         }
       } else {
@@ -962,17 +963,18 @@ void LineBreaker::GetJISx4051Breaks(cons
       ++cur;
       aBreakBefore[cur] = false;
       state.AdvanceIndex();
     }
   }
 }
 
 void LineBreaker::GetJISx4051Breaks(const uint8_t* aChars, uint32_t aLength,
-                                    uint8_t aWordBreak, uint8_t* aBreakBefore) {
+                                    WordBreak aWordBreak, Strictness aLevel,
+                                    uint8_t* aBreakBefore) {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
     char32_t ch = aChars[cur];
     int8_t cl;
 
@@ -980,30 +982,30 @@ void LineBreaker::GetJISx4051Breaks(cons
       cl = ContextualAnalysis(cur > 0 ? aChars[cur - 1] : U_NULL, ch,
                               cur + 1 < aLength ? aChars[cur + 1] : U_NULL,
                               state);
     } else {
       if (ch == U_EQUAL) state.NotifySeenEqualsSign();
       state.NotifyNonHyphenCharacter(ch);
       cl = GetClass(ch);
     }
-    if (aWordBreak == LineBreaker::kWordBreak_BreakAll &&
+    if (aWordBreak == WordBreak::BreakAll &&
         (cl == CLASS_CHARACTER || cl == CLASS_CLOSE)) {
       auto cls = GetLineBreakClass(ch);
       // Don't need to check additional Japanese/Korean classes in 8-bit
       if (cls == U_LB_ALPHABETIC || cls == U_LB_NUMERIC ||
           cls == U_LB_COMPLEX_CONTEXT) {
         cl = CLASS_BREAKABLE;
       }
     }
 
     bool allowBreak = false;
     if (cur > 0) {
-      if (aWordBreak == LineBreaker::kWordBreak_Normal ||
-          aWordBreak == LineBreaker::kWordBreak_BreakAll) {
+      if (aWordBreak == WordBreak::Normal ||
+          aWordBreak == WordBreak::BreakAll) {
         allowBreak = (state.UseConservativeBreaking())
                          ? GetPairConservative(lastClass, cl)
                          : GetPair(lastClass, cl);
       }
     }
     aBreakBefore[cur] = allowBreak;
     if (allowBreak) state.NotifyBreakBefore();
     lastClass = cl;
--- a/intl/lwbrk/LineBreaker.h
+++ b/intl/lwbrk/LineBreaker.h
@@ -12,40 +12,48 @@
 
 namespace mozilla {
 namespace intl {
 
 class LineBreaker {
  public:
   NS_INLINE_DECL_REFCOUNTING(LineBreaker)
 
-  enum {
-    // Order is important here, because of tests for value >= BreakAll
-    kWordBreak_Normal = 0,    // default
-    kWordBreak_KeepAll = 1,   // always keep
-    kWordBreak_BreakAll = 2,  // break all
-    kWordBreak_Anywhere = 3   // line-break:anywhere
+  enum class WordBreak : uint8_t {
+    Normal = 0,    // default
+    BreakAll = 1,  // break all
+    KeepAll = 2    // always keep
+  };
+
+  enum class Strictness : uint8_t {
+    Auto = 0,
+    Loose = 1,
+    Normal = 2,
+    Strict = 3,
+    Anywhere = 4
   };
 
   static already_AddRefed<LineBreaker> Create();
 
   int32_t Next(const char16_t* aText, uint32_t aLen, uint32_t aPos);
 
   int32_t Prev(const char16_t* aText, uint32_t aLen, uint32_t aPos);
 
   // Call this on a word with whitespace at either end. We will apply JISx4051
   // rules to find breaks inside the word. aBreakBefore is set to the break-
   // before status of each character; aBreakBefore[0] will always be false
   // because we never return a break before the first character.
   // aLength is the length of the aText array and also the length of the
   // aBreakBefore output array.
   void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength,
-                         uint8_t aWordBreak, uint8_t* aBreakBefore);
+                         WordBreak aWordBreak, Strictness aLevel,
+                         uint8_t* aBreakBefore);
   void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength,
-                         uint8_t aWordBreak, uint8_t* aBreakBefore);
+                         WordBreak aWordBreak, Strictness aLevel,
+                         uint8_t* aBreakBefore);
 
  private:
   ~LineBreaker() {}
 
   int32_t WordMove(const char16_t* aText, uint32_t aLen, uint32_t aPos,
                    int8_t aDirection);
 };
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2584,33 +2584,46 @@ void BuildTextRunsScanner::SetupBreakSin
   gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
 
   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
     MappedFlow* mappedFlow = &mMappedFlows[i];
     // The CSS word-break value may change within a word, so we reset it for
     // each MappedFlow. The line-breaker will flush its text if the property
     // actually changes.
     const auto* styleText = mappedFlow->mStartFrame->StyleText();
-    if (styleText->mLineBreak == StyleLineBreak::Anywhere) {
-        mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_Anywhere);
-    } else {
-      auto wordBreak = styleText->EffectiveWordBreak();
-      switch (wordBreak) {
-        case StyleWordBreak::BreakAll:
-          mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_BreakAll);
-          break;
-        case StyleWordBreak::KeepAll:
-          mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_KeepAll);
-          break;
-        case StyleWordBreak::Normal:
-        default:
-          MOZ_ASSERT(wordBreak == StyleWordBreak::Normal);
-          mLineBreaker.SetWordBreak(LineBreaker::kWordBreak_Normal);
-          break;
-      }
+    auto wordBreak = styleText->EffectiveWordBreak();
+    switch (wordBreak) {
+      case StyleWordBreak::BreakAll:
+        mLineBreaker.SetWordBreak(LineBreaker::WordBreak::BreakAll);
+        break;
+      case StyleWordBreak::KeepAll:
+        mLineBreaker.SetWordBreak(LineBreaker::WordBreak::KeepAll);
+        break;
+      case StyleWordBreak::Normal:
+      default:
+        MOZ_ASSERT(wordBreak == StyleWordBreak::Normal);
+        mLineBreaker.SetWordBreak(LineBreaker::WordBreak::Normal);
+        break;
+    }
+    switch (styleText->mLineBreak) {
+      case StyleLineBreak::Auto:
+        mLineBreaker.SetStrictness(LineBreaker::Strictness::Auto);
+        break;
+      case StyleLineBreak::Normal:
+        mLineBreaker.SetStrictness(LineBreaker::Strictness::Normal);
+        break;
+      case StyleLineBreak::Loose:
+        mLineBreaker.SetStrictness(LineBreaker::Strictness::Loose);
+        break;
+      case StyleLineBreak::Strict:
+        mLineBreaker.SetStrictness(LineBreaker::Strictness::Strict);
+        break;
+      case StyleLineBreak::Anywhere:
+        mLineBreaker.SetStrictness(LineBreaker::Strictness::Anywhere);
+        break;
     }
 
     uint32_t offset = iter.GetSkippedOffset();
     gfxSkipCharsIterator iterNext = iter;
     iterNext.AdvanceOriginal(mappedFlow->GetContentEnd() -
                              mappedFlow->mStartFrame->GetContentOffset());
 
     UniquePtr<BreakSink>* breakSink = mBreakSinks.AppendElement(