Bug 1149009 - Suppress line break inside text frame if it is directly contained by ruby content box. r=dbaron
authorXidorn Quan <quanxunzhen@gmail.com>
Mon, 04 May 2015 09:52:53 +1200
changeset 273539 e52a585302320e44109c975a38ba9a111c7fd61f
parent 273538 8ec02fc2f048a7edab852cc9fbd5ecc549415b9f
child 273540 9f48bf75703363aae494595ad70a95c1bf28dc27
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1149009
milestone40.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 1149009 - Suppress line break inside text frame if it is directly contained by ruby content box. r=dbaron
layout/base/nsBidiPresUtils.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/reftests/css-ruby/line-break-suppression-4-ref.html
layout/reftests/css-ruby/line-break-suppression-4.html
layout/reftests/css-ruby/reftest.list
layout/style/nsStyleContext.h
layout/style/nsStyleStruct.h
layout/style/nsStyleStructInlines.h
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1050,17 +1050,18 @@ nsBidiPresUtils::TraverseFrames(nsBlockF
       nsIContent* content = frame->GetContent();
       aBpd->AppendFrame(frame, aLineIter, content);
 
       // Append the content of the frame to the paragraph buffer
       nsIAtom* frameType = frame->GetType();
       if (nsGkAtoms::textFrame == frameType) {
         if (content != aBpd->mPrevContent) {
           aBpd->mPrevContent = content;
-          if (!frame->StyleText()->NewlineIsSignificant(frame)) {
+          if (!frame->StyleText()->NewlineIsSignificant(
+                static_cast<nsTextFrame*>(frame))) {
             content->AppendTextTo(aBpd->mBuffer);
           } else {
             /*
              * For preformatted text we have to do bidi resolution on each line
              * separately. 
              */
             nsAutoString text;
             content->AppendTextTo(text);
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -673,17 +673,17 @@ static bool IsSpaceCombiningSequenceTail
     return false;
   return nsTextFrameUtils::IsSpaceCombiningSequenceTail(
     aFrag->Get2b() + aPos, aFrag->GetLength() - aPos);
 }
 
 // Check whether aPos is a space for CSS 'word-spacing' purposes
 static bool
 IsCSSWordSpacingSpace(const nsTextFragment* aFrag, uint32_t aPos,
-                      nsIFrame* aFrame, const nsStyleText* aStyleText)
+                      nsTextFrame* aFrame, const nsStyleText* aStyleText)
 {
   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
 
   char16_t ch = aFrag->CharAt(aPos);
   switch (ch) {
   case ' ':
   case CH_NBSP:
     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
@@ -1937,17 +1937,17 @@ BuildTextRunsScanner::BuildTextRunForFra
     textStyle = f->StyleText();
     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
       anyTextTransformStyle = true;
     }
     textFlags |= GetSpacingFlags(LetterSpacing(f));
     textFlags |= GetSpacingFlags(WordSpacing(f));
     nsTextFrameUtils::CompressionMode compression =
       GetCSSWhitespaceToCompressionMode(f, textStyle);
-    if ((enabledJustification || f->StyleContext()->ShouldSuppressLineBreak()) &&
+    if ((enabledJustification || f->ShouldSuppressLineBreak()) &&
         !textStyle->WhiteSpaceIsSignificant() && !isSVG) {
       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
     }
     fontStyle = f->StyleFont();
     nsIFrame* parent = mLineContainer->GetParent();
     if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) {
       if (NS_MATHML_MATHVARIANT_NORMAL != fontStyle->mMathVariant) {
         anyMathMLStyling = true;
@@ -2811,19 +2811,19 @@ static int32_t FindChar(const nsTextFrag
       const void* p = memchr(str, ch, aLength);
       if (p)
         return (static_cast<const char*>(p) - str) + aOffset;
     }
   }
   return -1;
 }
 
-static bool IsChineseOrJapanese(nsIFrame* aFrame)
-{
-  if (aFrame->StyleContext()->ShouldSuppressLineBreak()) {
+static bool IsChineseOrJapanese(nsTextFrame* aFrame)
+{
+  if (aFrame->ShouldSuppressLineBreak()) {
     // Always treat ruby as CJ language so that those characters can
     // be expanded properly even when surrounded by other language.
     return true;
   }
 
   nsIAtom* language = aFrame->StyleFont()->mLanguage;
   if (!language) {
     return false;
@@ -8338,17 +8338,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
   uint32_t transformedLastBreak = 0;
   bool usedHyphenation;
   gfxFloat trimmedWidth = 0;
   gfxFloat availWidth = aAvailableWidth;
   bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
                                    (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML);
   gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
   gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
-  if (StyleContext()->ShouldSuppressLineBreak()) {
+  if (ShouldSuppressLineBreak()) {
     suppressBreak = gfxTextRun::eSuppressAllBreaks;
   } else if (!aLineLayout.LineIsBreakable()) {
     suppressBreak = gfxTextRun::eSuppressInitialBreak;
   }
   uint32_t transformedCharsFit =
     mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
                                   (GetStateBits() & TEXT_START_OF_LINE) != 0,
                                   availWidth,
@@ -8547,17 +8547,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     aLineLayout.NotifyOptionalBreakPosition(this, length, fits,
                                             gfxBreakPriority::eNormalBreak);
   }
   bool breakAfter = forceBreakAfter;
   // length == 0 means either the text is empty or it's all collapsed away
   bool emptyTextAtStartOfLine = atStartOfLine && length == 0;
   if (!breakAfter && charsFit == length && !emptyTextAtStartOfLine &&
       transformedOffset + transformedLength == mTextRun->GetLength() &&
-      !StyleContext()->ShouldSuppressLineBreak() &&
+      !ShouldSuppressLineBreak() &&
       (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK)) {
     // We placed all the text in the textrun and we have a break opportunity at
     // the end of the textrun. We need to record it because the following
     // content may not care about nsLineBreaker.
 
     // Note that because we didn't break, we can be sure that (thanks to the
     // code up above) textMetrics.mAdvanceWidth includes the width of any
     // trailing whitespace. So we need to subtract trimmableWidth here
@@ -8609,17 +8609,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
   } else if (cachedNewlineOffset) {
     mContent->DeleteProperty(nsGkAtoms::newline);
   }
 
   // Compute space and letter counts for justification, if required
   if (!textStyle->WhiteSpaceIsSignificant() &&
       (lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
        lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
-       StyleContext()->ShouldSuppressLineBreak()) &&
+       ShouldSuppressLineBreak()) &&
       !lineContainer->IsSVGText()) {
     AddStateBits(TEXT_JUSTIFICATION_ENABLED);
     provider.ComputeJustification(offset, charsFit);
     aLineLayout.SetJustificationInfo(provider.GetJustificationInfo());
   }
 
   SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
 
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -10,16 +10,17 @@
 #include "mozilla/gfx/2D.h"
 #include "nsFrame.h"
 #include "nsSplittableFrame.h"
 #include "nsLineBox.h"
 #include "gfxSkipChars.h"
 #include "gfxTextRun.h"
 #include "nsDisplayList.h"
 #include "JustificationUtils.h"
+#include "RubyUtils.h"
 
 // Undo the windows.h damage
 #if defined(XP_WIN) && defined(DrawText)
 #undef DrawText
 #endif
 
 class nsTextPaintStyle;
 class PropertyProvider;
@@ -117,16 +118,28 @@ public:
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     // Set the frame state bit for text frames to mark them as replaced.
     // XXX kipp: temporary
     return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
                                              nsIFrame::eLineParticipant));
   }
 
+  bool ShouldSuppressLineBreak() const
+  {
+    // If the parent frame of the text frame is ruby content box, it must
+    // suppress line break inside. This check is necessary, because when
+    // a whitespace is only contained by pseudo ruby frames, its style
+    // context won't have SuppressLineBreak bit set.
+    if (mozilla::RubyUtils::IsRubyContentBox(GetParent()->GetType())) {
+      return true;
+    }
+    return StyleContext()->ShouldSuppressLineBreak();
+  }
+
   virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
   virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
 
 #ifdef DEBUG_FRAME_DUMP
   void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
   virtual nsresult GetFrameName(nsAString& aResult) const override;
   void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const;
 #endif
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/line-break-suppression-4-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1149009 - Line break suppression on whitespaces wrapped but not contained in ruby boxes</title>
+</head>
+<body>
+  <span style="white-space: pre"><rb>a</rb><rb>
+</rb><rb>b</rb></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/line-break-suppression-4.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1149009 - Line break suppression on whitespaces wrapped but not contained in ruby boxes</title>
+</head>
+<body>
+  <span style="white-space: pre"><rb>a</rb>
+<rb>b</rb></span>
+</body>
+</html>
--- a/layout/reftests/css-ruby/reftest.list
+++ b/layout/reftests/css-ruby/reftest.list
@@ -26,16 +26,17 @@ test-pref(dom.meta-viewport.enabled,true
 == justification-1.html justification-1-ref.html
 == justification-2.html justification-2-ref.html
 fuzzy-if(winWidget,255,792) == lang-specific-style-1.html lang-specific-style-1-ref.html # bug 1134947
 == line-breaking-1.html line-breaking-1-ref.html
 == line-breaking-2.html line-breaking-2-ref.html
 == line-break-suppression-1.html line-break-suppression-1-ref.html
 == line-break-suppression-2.html line-break-suppression-2-ref.html
 == line-break-suppression-3.html line-break-suppression-3-ref.html
+== line-break-suppression-4.html line-break-suppression-4-ref.html
 == line-height-1.html line-height-1-ref.html
 == line-height-2.html line-height-2-ref.html
 == line-height-3.html line-height-3-ref.html
 == line-height-4.html line-height-4-ref.html
 load nested-ruby-1.html
 == no-transform.html no-transform-ref.html
 == relative-positioning-1.html relative-positioning-1-ref.html
 == relative-positioning-2.html relative-positioning-2-ref.html
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -152,16 +152,18 @@ public:
   bool HasTextDecorationLines() const
     { return !!(mBits & NS_STYLE_HAS_TEXT_DECORATION_LINES); }
 
   // Whether any line break inside should be suppressed? If this returns
   // true, the line should not be broken inside, which means inlines act
   // as if nowrap is set, <br> is suppressed, and blocks are inlinized.
   // This bit is propogated to all children of line partitipants. It is
   // currently used by ruby to make its content frames unbreakable.
+  // NOTE: for nsTextFrame, use nsTextFrame::ShouldSuppressLineBreak()
+  // instead of this method.
   bool ShouldSuppressLineBreak() const
     { return !!(mBits & NS_STYLE_SUPPRESS_LINEBREAK); }
 
   // Does this style context or any of its ancestors have display:none set?
   bool IsInDisplayNoneSubtree() const
     { return !!(mBits & NS_STYLE_IN_DISPLAY_NONE_SUBTREE); }
 
   // Does this style context represent the style for a pseudo-element or
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -25,16 +25,17 @@
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsCSSValue.h"
 #include "imgRequestProxy.h"
 #include "Orientation.h"
 #include "CounterStyleManager.h"
 
 class nsIFrame;
+class nsTextFrame;
 class nsIURI;
 class imgIContainer;
 
 // Includes nsStyleStructID.
 #include "nsStyleStructFwd.h"
 
 // Bits for each struct.
 // NS_STYLE_INHERIT_BIT defined in nsStyleStructFwd.h
@@ -1661,17 +1662,17 @@ struct nsStyleText {
   // These are defined in nsStyleStructInlines.h.
   inline bool HasTextShadow() const;
   inline nsCSSShadowArray* GetTextShadow() const;
 
   // The aContextFrame argument on each of these is the frame this
   // style struct is for.  If the frame is for SVG text or inside ruby,
   // the return value will be massaged to be something that makes sense
   // for those cases.
-  inline bool NewlineIsSignificant(const nsIFrame* aContextFrame) const;
+  inline bool NewlineIsSignificant(const nsTextFrame* aContextFrame) const;
   inline bool WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const;
   inline bool WordCanWrap(const nsIFrame* aContextFrame) const;
 };
 
 struct nsStyleImageOrientation {
   static nsStyleImageOrientation CreateAsAngleAndFlip(double aRadians,
                                                       bool aFlip) {
     uint8_t orientation(0);
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -9,16 +9,17 @@
  */
 
 #ifndef nsStyleStructInlines_h_
 #define nsStyleStructInlines_h_
 
 #include "nsIFrame.h"
 #include "nsStyleStruct.h"
 #include "nsIContent.h" // for GetParent()
+#include "nsTextFrame.h" // for nsTextFrame::ShouldSuppressLineBreak
 
 inline void
 nsStyleImage::SetSubImage(uint8_t aIndex, imgIContainer* aSubImage) const
 {
   const_cast<nsStyleImage*>(this)->mSubImages.ReplaceObjectAt(aSubImage, aIndex);
 }
 
 inline imgIContainer*
@@ -38,21 +39,21 @@ nsStyleText::HasTextShadow() const
 
 nsCSSShadowArray*
 nsStyleText::GetTextShadow() const
 {
   return mTextShadow;
 }
 
 bool
-nsStyleText::NewlineIsSignificant(const nsIFrame* aContextFrame) const
+nsStyleText::NewlineIsSignificant(const nsTextFrame* aContextFrame) const
 {
   NS_ASSERTION(aContextFrame->StyleText() == this, "unexpected aContextFrame");
   return NewlineIsSignificantStyle() &&
-    !aContextFrame->StyleContext()->ShouldSuppressLineBreak();
+    !aContextFrame->ShouldSuppressLineBreak();
 }
 
 bool
 nsStyleText::WhiteSpaceCanWrap(const nsIFrame* aContextFrame) const
 {
   NS_ASSERTION(aContextFrame->StyleText() == this, "unexpected aContextFrame");
   return WhiteSpaceCanWrapStyle() && !aContextFrame->IsSVGText();
 }