LCOV - code coverage report
Current view: top level - layout/generic - nsRubyBaseContainerFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 398 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code is subject to the terms of the Mozilla Public License
       4             :  * version 2.0 (the "License"). You can obtain a copy of the License at
       5             :  * http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* rendering object for CSS "display: ruby-base-container" */
       8             : 
       9             : #include "nsRubyBaseContainerFrame.h"
      10             : #include "nsRubyTextContainerFrame.h"
      11             : #include "nsRubyBaseFrame.h"
      12             : #include "nsRubyTextFrame.h"
      13             : #include "mozilla/DebugOnly.h"
      14             : #include "mozilla/Maybe.h"
      15             : #include "mozilla/WritingModes.h"
      16             : #include "nsLayoutUtils.h"
      17             : #include "nsLineLayout.h"
      18             : #include "nsPresContext.h"
      19             : #include "nsStyleContext.h"
      20             : #include "nsStyleStructInlines.h"
      21             : #include "nsTextFrame.h"
      22             : #include "RubyUtils.h"
      23             : 
      24             : using namespace mozilla;
      25             : using namespace mozilla::gfx;
      26             : 
      27             : //----------------------------------------------------------------------
      28             : 
      29             : // Frame class boilerplate
      30             : // =======================
      31             : 
      32           0 : NS_QUERYFRAME_HEAD(nsRubyBaseContainerFrame)
      33           0 :   NS_QUERYFRAME_ENTRY(nsRubyBaseContainerFrame)
      34           0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
      35             : 
      36           0 : NS_IMPL_FRAMEARENA_HELPERS(nsRubyBaseContainerFrame)
      37             : 
      38             : nsContainerFrame*
      39           0 : NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
      40             :                              nsStyleContext* aContext)
      41             : {
      42           0 :   return new (aPresShell) nsRubyBaseContainerFrame(aContext);
      43             : }
      44             : 
      45             : 
      46             : //----------------------------------------------------------------------
      47             : 
      48             : // nsRubyBaseContainerFrame Method Implementations
      49             : // ===============================================
      50             : 
      51             : #ifdef DEBUG_FRAME_DUMP
      52             : nsresult
      53           0 : nsRubyBaseContainerFrame::GetFrameName(nsAString& aResult) const
      54             : {
      55           0 :   return MakeFrameName(NS_LITERAL_STRING("RubyBaseContainer"), aResult);
      56             : }
      57             : #endif
      58             : 
      59             : static gfxBreakPriority
      60           0 : LineBreakBefore(nsIFrame* aFrame,
      61             :                 DrawTarget* aDrawTarget,
      62             :                 nsIFrame* aLineContainerFrame,
      63             :                 const nsLineList::iterator* aLine)
      64             : {
      65           0 :   for (nsIFrame* child = aFrame; child;
      66           0 :        child = child->PrincipalChildList().FirstChild()) {
      67           0 :     if (!child->CanContinueTextRun()) {
      68             :       // It is not an inline element. We can break before it.
      69           0 :       return gfxBreakPriority::eNormalBreak;
      70             :     }
      71           0 :     if (!child->IsTextFrame()) {
      72           0 :       continue;
      73             :     }
      74             : 
      75           0 :     auto textFrame = static_cast<nsTextFrame*>(child);
      76             :     gfxSkipCharsIterator iter =
      77             :       textFrame->EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
      78           0 :                                aLineContainerFrame, aLine);
      79           0 :     iter.SetOriginalOffset(textFrame->GetContentOffset());
      80           0 :     uint32_t pos = iter.GetSkippedOffset();
      81           0 :     gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated);
      82           0 :     MOZ_ASSERT(textRun, "fail to build textrun?");
      83           0 :     if (!textRun || pos >= textRun->GetLength()) {
      84             :       // The text frame contains no character at all.
      85           0 :       return gfxBreakPriority::eNoBreak;
      86             :     }
      87             :     // Return whether we can break before the first character.
      88           0 :     if (textRun->CanBreakLineBefore(pos)) {
      89           0 :       return gfxBreakPriority::eNormalBreak;
      90             :     }
      91             :     // Check whether we can wrap word here.
      92           0 :     const nsStyleText* textStyle = textFrame->StyleText();
      93           0 :     if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) {
      94           0 :       return gfxBreakPriority::eWordWrapBreak;
      95             :     }
      96             :     // We cannot break before.
      97           0 :     return gfxBreakPriority::eNoBreak;
      98             :   }
      99             :   // Neither block, nor text frame is found as a leaf. We won't break
     100             :   // before this base frame. It is the behavior of empty spans.
     101           0 :   return gfxBreakPriority::eNoBreak;
     102             : }
     103             : 
     104             : static void
     105           0 : GetIsLineBreakAllowed(nsIFrame* aFrame, bool aIsLineBreakable,
     106             :                       bool* aAllowInitialLineBreak, bool* aAllowLineBreak)
     107             : {
     108           0 :   nsIFrame* parent = aFrame->GetParent();
     109           0 :   bool lineBreakSuppressed = parent->StyleContext()->ShouldSuppressLineBreak();
     110             :   // Allow line break between ruby bases when white-space allows,
     111             :   // we are not inside a nested ruby, and there is no span.
     112           0 :   bool allowLineBreak = !lineBreakSuppressed &&
     113           0 :                         aFrame->StyleText()->WhiteSpaceCanWrap(aFrame);
     114           0 :   bool allowInitialLineBreak = allowLineBreak;
     115           0 :   if (!aFrame->GetPrevInFlow()) {
     116           0 :     allowInitialLineBreak = !lineBreakSuppressed &&
     117           0 :                             parent->StyleText()->WhiteSpaceCanWrap(parent);
     118             :   }
     119           0 :   if (!aIsLineBreakable) {
     120           0 :     allowInitialLineBreak = false;
     121             :   }
     122           0 :   *aAllowInitialLineBreak = allowInitialLineBreak;
     123           0 :   *aAllowLineBreak = allowLineBreak;
     124           0 : }
     125             : 
     126             : /**
     127             :  * @param aBaseISizeData is an in/out param. This method updates the
     128             :  * `skipWhitespace` and `trailingWhitespace` fields of the struct with
     129             :  * the base level frame. Note that we don't need to do the same thing
     130             :  * for ruby text frames, because they are text run container themselves
     131             :  * (see nsTextFrame.cpp:BuildTextRuns), and thus no whitespace collapse
     132             :  * happens across the boundary of those frames.
     133             :  */
     134             : static nscoord
     135           0 : CalculateColumnPrefISize(gfxContext* aRenderingContext,
     136             :                          const RubyColumnEnumerator& aEnumerator,
     137             :                          nsIFrame::InlineIntrinsicISizeData* aBaseISizeData)
     138             : {
     139           0 :   nscoord max = 0;
     140           0 :   uint32_t levelCount = aEnumerator.GetLevelCount();
     141           0 :   for (uint32_t i = 0; i < levelCount; i++) {
     142           0 :     nsIFrame* frame = aEnumerator.GetFrameAtLevel(i);
     143           0 :     if (frame) {
     144           0 :       nsIFrame::InlinePrefISizeData data;
     145           0 :       if (i == 0) {
     146           0 :         data.SetLineContainer(aBaseISizeData->LineContainer());
     147           0 :         data.mSkipWhitespace = aBaseISizeData->mSkipWhitespace;
     148           0 :         data.mTrailingWhitespace = aBaseISizeData->mTrailingWhitespace;
     149             :       } else {
     150             :         // The line container of ruby text frames is their parent,
     151             :         // ruby text container frame.
     152           0 :         data.SetLineContainer(frame->GetParent());
     153             :       }
     154           0 :       frame->AddInlinePrefISize(aRenderingContext, &data);
     155           0 :       MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines");
     156           0 :       max = std::max(max, data.mCurrentLine);
     157           0 :       if (i == 0) {
     158           0 :         aBaseISizeData->mSkipWhitespace = data.mSkipWhitespace;
     159           0 :         aBaseISizeData->mTrailingWhitespace = data.mTrailingWhitespace;
     160             :       }
     161             :     }
     162             :   }
     163           0 :   return max;
     164             : }
     165             : 
     166             : // FIXME Currently we use pref isize of ruby content frames for
     167             : //       computing min isize of ruby frame, which may cause problem.
     168             : //       See bug 1134945.
     169             : /* virtual */ void
     170           0 : nsRubyBaseContainerFrame::AddInlineMinISize(
     171             :   gfxContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData)
     172             : {
     173           0 :   AutoRubyTextContainerArray textContainers(this);
     174             : 
     175           0 :   for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) {
     176           0 :     if (textContainers[i]->IsSpanContainer()) {
     177             :       // Since spans are not breakable internally, use our pref isize
     178             :       // directly if there is any span.
     179           0 :       nsIFrame::InlinePrefISizeData data;
     180           0 :       data.SetLineContainer(aData->LineContainer());
     181           0 :       data.mSkipWhitespace = aData->mSkipWhitespace;
     182           0 :       data.mTrailingWhitespace = aData->mTrailingWhitespace;
     183           0 :       AddInlinePrefISize(aRenderingContext, &data);
     184           0 :       aData->mCurrentLine += data.mCurrentLine;
     185           0 :       if (data.mCurrentLine > 0) {
     186           0 :         aData->mAtStartOfLine = false;
     187             :       }
     188           0 :       aData->mSkipWhitespace = data.mSkipWhitespace;
     189           0 :       aData->mTrailingWhitespace = data.mTrailingWhitespace;
     190           0 :       return;
     191             :     }
     192             :   }
     193             : 
     194           0 :   bool firstFrame = true;
     195             :   bool allowInitialLineBreak, allowLineBreak;
     196           0 :   GetIsLineBreakAllowed(this, !aData->mAtStartOfLine,
     197           0 :                         &allowInitialLineBreak, &allowLineBreak);
     198           0 :   for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
     199             :     RubyColumnEnumerator enumerator(
     200           0 :       static_cast<nsRubyBaseContainerFrame*>(frame), textContainers);
     201           0 :     for (; !enumerator.AtEnd(); enumerator.Next()) {
     202           0 :       if (firstFrame ? allowInitialLineBreak : allowLineBreak) {
     203           0 :         nsIFrame* baseFrame = enumerator.GetFrameAtLevel(0);
     204           0 :         if (baseFrame) {
     205             :           gfxBreakPriority breakPriority =
     206           0 :             LineBreakBefore(baseFrame, aRenderingContext->GetDrawTarget(),
     207           0 :                             nullptr, nullptr);
     208           0 :           if (breakPriority != gfxBreakPriority::eNoBreak) {
     209           0 :             aData->OptionallyBreak();
     210             :           }
     211             :         }
     212             :       }
     213           0 :       firstFrame = false;
     214             :       nscoord isize = CalculateColumnPrefISize(aRenderingContext,
     215           0 :                                                enumerator, aData);
     216           0 :       aData->mCurrentLine += isize;
     217           0 :       if (isize > 0) {
     218           0 :         aData->mAtStartOfLine = false;
     219             :       }
     220             :     }
     221             :   }
     222             : }
     223             : 
     224             : /* virtual */ void
     225           0 : nsRubyBaseContainerFrame::AddInlinePrefISize(
     226             :   gfxContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData)
     227             : {
     228           0 :   AutoRubyTextContainerArray textContainers(this);
     229             : 
     230           0 :   nscoord sum = 0;
     231           0 :   for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
     232             :     RubyColumnEnumerator enumerator(
     233           0 :       static_cast<nsRubyBaseContainerFrame*>(frame), textContainers);
     234           0 :     for (; !enumerator.AtEnd(); enumerator.Next()) {
     235           0 :       sum += CalculateColumnPrefISize(aRenderingContext, enumerator, aData);
     236             :     }
     237             :   }
     238           0 :   for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) {
     239           0 :     if (textContainers[i]->IsSpanContainer()) {
     240           0 :       nsIFrame* frame = textContainers[i]->PrincipalChildList().FirstChild();
     241           0 :       nsIFrame::InlinePrefISizeData data;
     242           0 :       frame->AddInlinePrefISize(aRenderingContext, &data);
     243           0 :       MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines");
     244           0 :       sum = std::max(sum, data.mCurrentLine);
     245             :     }
     246             :   }
     247           0 :   aData->mCurrentLine += sum;
     248           0 : }
     249             : 
     250             : /* virtual */ bool
     251           0 : nsRubyBaseContainerFrame::IsFrameOfType(uint32_t aFlags) const
     252             : {
     253           0 :   if (aFlags & eSupportsCSSTransforms) {
     254           0 :     return false;
     255             :   }
     256           0 :   return nsContainerFrame::IsFrameOfType(aFlags &
     257           0 :          ~(nsIFrame::eLineParticipant));
     258             : }
     259             : 
     260             : /* virtual */ bool
     261           0 : nsRubyBaseContainerFrame::CanContinueTextRun() const
     262             : {
     263           0 :   return true;
     264             : }
     265             : 
     266             : /* virtual */ LogicalSize
     267           0 : nsRubyBaseContainerFrame::ComputeSize(gfxContext *aRenderingContext,
     268             :                                       WritingMode aWM,
     269             :                                       const LogicalSize& aCBSize,
     270             :                                       nscoord aAvailableISize,
     271             :                                       const LogicalSize& aMargin,
     272             :                                       const LogicalSize& aBorder,
     273             :                                       const LogicalSize& aPadding,
     274             :                                       ComputeSizeFlags aFlags)
     275             : {
     276             :   // Ruby base container frame is inline,
     277             :   // hence don't compute size before reflow.
     278           0 :   return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
     279             : }
     280             : 
     281             : /* virtual */ nscoord
     282           0 : nsRubyBaseContainerFrame::GetLogicalBaseline(WritingMode aWritingMode) const
     283             : {
     284           0 :   return mBaseline;
     285             : }
     286             : 
     287             : struct nsRubyBaseContainerFrame::RubyReflowInput
     288             : {
     289             :   bool mAllowInitialLineBreak;
     290             :   bool mAllowLineBreak;
     291             :   const AutoRubyTextContainerArray& mTextContainers;
     292             :   const ReflowInput& mBaseReflowInput;
     293             :   const nsTArray<UniquePtr<ReflowInput>>& mTextReflowInputs;
     294             : };
     295             : 
     296             : /* virtual */ void
     297           0 : nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
     298             :                                  ReflowOutput& aDesiredSize,
     299             :                                  const ReflowInput& aReflowInput,
     300             :                                  nsReflowStatus& aStatus)
     301             : {
     302           0 :   MarkInReflow();
     303           0 :   DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
     304           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
     305           0 :   aStatus.Reset();
     306             : 
     307           0 :   if (!aReflowInput.mLineLayout) {
     308           0 :     NS_ASSERTION(
     309             :       aReflowInput.mLineLayout,
     310             :       "No line layout provided to RubyBaseContainerFrame reflow method.");
     311           0 :     return;
     312             :   }
     313             : 
     314           0 :   mDescendantLeadings.Reset();
     315             : 
     316           0 :   MoveOverflowToChildList();
     317             :   // Ask text containers to drain overflows
     318           0 :   AutoRubyTextContainerArray textContainers(this);
     319           0 :   const uint32_t rtcCount = textContainers.Length();
     320           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     321           0 :     textContainers[i]->MoveOverflowToChildList();
     322             :   }
     323             : 
     324           0 :   WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
     325             :   LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
     326           0 :                         aReflowInput.AvailableBSize());
     327             : 
     328             :   // We have a reflow state and a line layout for each RTC.
     329             :   // They are conceptually the state of the RTCs, but we don't actually
     330             :   // reflow those RTCs in this code. These two arrays are holders of
     331             :   // the reflow states and line layouts.
     332             :   // Since there are pointers refer to reflow states and line layouts,
     333             :   // it is necessary to guarantee that they won't be moved. For this
     334             :   // reason, they are wrapped in UniquePtr here.
     335           0 :   AutoTArray<UniquePtr<ReflowInput>, RTC_ARRAY_SIZE> reflowInputs;
     336           0 :   AutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts;
     337           0 :   reflowInputs.SetCapacity(rtcCount);
     338           0 :   lineLayouts.SetCapacity(rtcCount);
     339             : 
     340             :   // Begin the line layout for each ruby text container in advance.
     341           0 :   bool hasSpan = false;
     342           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     343           0 :     nsRubyTextContainerFrame* textContainer = textContainers[i];
     344           0 :     if (textContainer->IsSpanContainer()) {
     345           0 :       hasSpan = true;
     346             :     }
     347             : 
     348             :     ReflowInput* reflowInput = new ReflowInput(
     349           0 :       aPresContext, *aReflowInput.mParentReflowInput, textContainer,
     350           0 :       availSize.ConvertTo(textContainer->GetWritingMode(), lineWM));
     351           0 :     reflowInputs.AppendElement(reflowInput);
     352             :     nsLineLayout* lineLayout = new nsLineLayout(aPresContext,
     353             :                                                 reflowInput->mFloatManager,
     354             :                                                 reflowInput, nullptr,
     355           0 :                                                 aReflowInput.mLineLayout);
     356           0 :     lineLayout->SetSuppressLineWrap(true);
     357           0 :     lineLayouts.AppendElement(lineLayout);
     358             : 
     359             :     // Line number is useless for ruby text
     360             :     // XXX nullptr here may cause problem, see comments for
     361             :     //     nsLineLayout::mBlockRI and nsLineLayout::AddFloat
     362           0 :     lineLayout->Init(nullptr, reflowInput->CalcLineHeight(), -1);
     363           0 :     reflowInput->mLineLayout = lineLayout;
     364             : 
     365             :     // Border and padding are suppressed on ruby text containers.
     366             :     // If the writing mode is vertical-rl, the horizontal position of
     367             :     // rt frames will be updated when reflowing this text container,
     368             :     // hence leave container size 0 here for now.
     369           0 :     lineLayout->BeginLineReflow(0, 0, reflowInput->ComputedISize(),
     370             :                                 NS_UNCONSTRAINEDSIZE,
     371           0 :                                 false, false, lineWM, nsSize(0, 0));
     372           0 :     lineLayout->AttachRootFrameToBaseLineLayout();
     373             :   }
     374             : 
     375           0 :   aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
     376             :                                       0, aReflowInput.AvailableISize(),
     377           0 :                                       &mBaseline);
     378             : 
     379             :   bool allowInitialLineBreak, allowLineBreak;
     380           0 :   GetIsLineBreakAllowed(this, aReflowInput.mLineLayout->LineIsBreakable(),
     381           0 :                         &allowInitialLineBreak, &allowLineBreak);
     382             : 
     383           0 :   nscoord isize = 0;
     384             :   // Reflow columns excluding any span
     385             :   RubyReflowInput reflowInput = {
     386           0 :     allowInitialLineBreak, allowLineBreak && !hasSpan,
     387             :     textContainers, aReflowInput, reflowInputs
     388           0 :   };
     389           0 :   isize = ReflowColumns(reflowInput, aStatus);
     390           0 :   DebugOnly<nscoord> lineSpanSize = aReflowInput.mLineLayout->EndSpan(this);
     391           0 :   aDesiredSize.ISize(lineWM) = isize;
     392             :   // When there are no frames inside the ruby base container, EndSpan
     393             :   // will return 0. However, in this case, the actual width of the
     394             :   // container could be non-zero because of non-empty ruby annotations.
     395             :   // XXX When bug 765861 gets fixed, this warning should be upgraded.
     396           0 :   NS_WARNING_ASSERTION(
     397             :     aStatus.IsInlineBreak() || isize == lineSpanSize || mFrames.IsEmpty(),
     398             :     "bad isize");
     399             : 
     400             :   // If there exists any span, the columns must either be completely
     401             :   // reflowed, or be not reflowed at all.
     402           0 :   MOZ_ASSERT(aStatus.IsInlineBreakBefore() ||
     403             :              aStatus.IsComplete() || !hasSpan);
     404           0 :   if (!aStatus.IsInlineBreakBefore() &&
     405           0 :       aStatus.IsComplete() && hasSpan) {
     406             :     // Reflow spans
     407             :     RubyReflowInput reflowInput = {
     408             :       false, false, textContainers, aReflowInput, reflowInputs
     409           0 :     };
     410           0 :     nscoord spanISize = ReflowSpans(reflowInput);
     411           0 :     isize = std::max(isize, spanISize);
     412             :   }
     413             : 
     414           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     415             :     // It happens before the ruby text container is reflowed, and that
     416             :     // when it is reflowed, it will just use this size.
     417           0 :     nsRubyTextContainerFrame* textContainer = textContainers[i];
     418           0 :     nsLineLayout* lineLayout = lineLayouts[i].get();
     419             : 
     420           0 :     RubyUtils::ClearReservedISize(textContainer);
     421           0 :     nscoord rtcISize = lineLayout->GetCurrentICoord();
     422             :     // Only span containers and containers with collapsed annotations
     423             :     // need reserving isize. For normal ruby text containers, their
     424             :     // children will be expanded properly. We only need to expand their
     425             :     // own size.
     426           0 :     if (!textContainer->IsSpanContainer()) {
     427           0 :       rtcISize = isize;
     428           0 :     } else if (isize > rtcISize) {
     429           0 :       RubyUtils::SetReservedISize(textContainer, isize - rtcISize);
     430             :     }
     431             : 
     432           0 :     lineLayout->VerticalAlignLine();
     433           0 :     textContainer->SetISize(rtcISize);
     434           0 :     lineLayout->EndLineReflow();
     435             :   }
     436             : 
     437             :   // Border and padding are suppressed on ruby base container,
     438             :   // create a fake borderPadding for setting BSize.
     439           0 :   WritingMode frameWM = aReflowInput.GetWritingMode();
     440           0 :   LogicalMargin borderPadding(frameWM);
     441           0 :   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
     442           0 :                                          borderPadding, lineWM, frameWM);
     443             : }
     444             : 
     445             : /**
     446             :  * This struct stores the continuations after this frame and
     447             :  * corresponding text containers. It is used to speed up looking
     448             :  * ahead for nonempty continuations.
     449             :  */
     450           0 : struct MOZ_STACK_CLASS nsRubyBaseContainerFrame::PullFrameState
     451             : {
     452             :   ContinuationTraversingState mBase;
     453             :   AutoTArray<ContinuationTraversingState, RTC_ARRAY_SIZE> mTexts;
     454             :   const AutoRubyTextContainerArray& mTextContainers;
     455             : 
     456             :   PullFrameState(nsRubyBaseContainerFrame* aBaseContainer,
     457             :                  const AutoRubyTextContainerArray& aTextContainers);
     458             : };
     459             : 
     460             : nscoord
     461           0 : nsRubyBaseContainerFrame::ReflowColumns(const RubyReflowInput& aReflowInput,
     462             :                                         nsReflowStatus& aStatus)
     463             : {
     464           0 :   nsLineLayout* lineLayout = aReflowInput.mBaseReflowInput.mLineLayout;
     465           0 :   const uint32_t rtcCount = aReflowInput.mTextContainers.Length();
     466           0 :   nscoord icoord = lineLayout->GetCurrentICoord();
     467           0 :   MOZ_ASSERT(icoord == 0, "border/padding of rbc should have been suppressed");
     468           0 :   nsReflowStatus reflowStatus;
     469           0 :   aStatus.Reset();
     470             : 
     471           0 :   uint32_t columnIndex = 0;
     472           0 :   RubyColumn column;
     473           0 :   column.mTextFrames.SetCapacity(rtcCount);
     474           0 :   RubyColumnEnumerator e(this, aReflowInput.mTextContainers);
     475           0 :   for (; !e.AtEnd(); e.Next()) {
     476           0 :     e.GetColumn(column);
     477           0 :     icoord += ReflowOneColumn(aReflowInput, columnIndex, column, reflowStatus);
     478           0 :     if (!reflowStatus.IsInlineBreakBefore()) {
     479           0 :       columnIndex++;
     480             :     }
     481           0 :     if (reflowStatus.IsInlineBreak()) {
     482           0 :       break;
     483             :     }
     484             :     // We are not handling overflow here.
     485           0 :     MOZ_ASSERT(reflowStatus.IsEmpty());
     486             :   }
     487             : 
     488           0 :   bool isComplete = false;
     489           0 :   PullFrameState pullFrameState(this, aReflowInput.mTextContainers);
     490           0 :   while (!reflowStatus.IsInlineBreak()) {
     491             :     // We are not handling overflow here.
     492           0 :     MOZ_ASSERT(reflowStatus.IsEmpty());
     493             : 
     494             :     // Try pull some frames from next continuations. This call replaces
     495             :     // frames in |column| with the frame pulled in each level.
     496           0 :     PullOneColumn(lineLayout, pullFrameState, column, isComplete);
     497           0 :     if (isComplete) {
     498             :       // No more frames can be pulled.
     499           0 :       break;
     500             :     }
     501           0 :     icoord += ReflowOneColumn(aReflowInput, columnIndex, column, reflowStatus);
     502           0 :     if (!reflowStatus.IsInlineBreakBefore()) {
     503           0 :       columnIndex++;
     504             :     }
     505             :   }
     506             : 
     507           0 :   if (!e.AtEnd() && reflowStatus.IsInlineBreakAfter()) {
     508             :     // The current column has been successfully placed.
     509             :     // Skip to the next column and mark break before.
     510           0 :     e.Next();
     511           0 :     e.GetColumn(column);
     512           0 :     reflowStatus.SetInlineLineBreakBeforeAndReset();
     513             :   }
     514           0 :   if (!e.AtEnd() || (GetNextInFlow() && !isComplete)) {
     515           0 :     aStatus.SetIncomplete();
     516             :   }
     517             : 
     518           0 :   if (reflowStatus.IsInlineBreakBefore()) {
     519           0 :     if (!columnIndex || !aReflowInput.mAllowLineBreak) {
     520             :       // If no column has been placed yet, or we have any span,
     521             :       // the whole container should be in the next line.
     522           0 :       aStatus.SetInlineLineBreakBeforeAndReset();
     523           0 :       return 0;
     524             :     }
     525           0 :     aStatus.SetInlineLineBreakAfter();
     526           0 :     MOZ_ASSERT(aStatus.IsComplete() || aReflowInput.mAllowLineBreak);
     527             : 
     528             :     // If we are on an intra-level whitespace column, null values in
     529             :     // column.mBaseFrame and column.mTextFrames don't represent the
     530             :     // end of the frame-sibling-chain at that level -- instead, they
     531             :     // represent an anonymous empty intra-level whitespace box. It is
     532             :     // likely that there are frames in the next column (which can't be
     533             :     // intra-level whitespace). Those frames should be pushed as well.
     534           0 :     Maybe<RubyColumn> nextColumn;
     535           0 :     if (column.mIsIntraLevelWhitespace && !e.AtEnd()) {
     536           0 :       e.Next();
     537           0 :       nextColumn.emplace();
     538           0 :       e.GetColumn(nextColumn.ref());
     539             :     }
     540           0 :     nsIFrame* baseFrame = column.mBaseFrame;
     541           0 :     if (!baseFrame & nextColumn.isSome()) {
     542           0 :       baseFrame = nextColumn->mBaseFrame;
     543             :     }
     544           0 :     if (baseFrame) {
     545           0 :       PushChildren(baseFrame, baseFrame->GetPrevSibling());
     546             :     }
     547           0 :     for (uint32_t i = 0; i < rtcCount; i++) {
     548           0 :       nsRubyTextFrame* textFrame = column.mTextFrames[i];
     549           0 :       if (!textFrame && nextColumn.isSome()) {
     550           0 :         textFrame = nextColumn->mTextFrames[i];
     551             :       }
     552           0 :       if (textFrame) {
     553           0 :         aReflowInput.mTextContainers[i]->PushChildren(
     554           0 :           textFrame, textFrame->GetPrevSibling());
     555             :       }
     556             :     }
     557           0 :   } else if (reflowStatus.IsInlineBreakAfter()) {
     558             :     // |reflowStatus| being break after here may only happen when
     559             :     // there is a break after the column just pulled, or the whole
     560             :     // segment has been completely reflowed. In those cases, we do
     561             :     // not need to push anything.
     562           0 :     MOZ_ASSERT(e.AtEnd());
     563           0 :     aStatus.SetInlineLineBreakAfter();
     564             :   }
     565             : 
     566           0 :   return icoord;
     567             : }
     568             : 
     569             : nscoord
     570           0 : nsRubyBaseContainerFrame::ReflowOneColumn(const RubyReflowInput& aReflowInput,
     571             :                                           uint32_t aColumnIndex,
     572             :                                           const RubyColumn& aColumn,
     573             :                                           nsReflowStatus& aStatus)
     574             : {
     575           0 :   const ReflowInput& baseReflowInput = aReflowInput.mBaseReflowInput;
     576           0 :   const auto& textReflowInputs = aReflowInput.mTextReflowInputs;
     577           0 :   nscoord istart = baseReflowInput.mLineLayout->GetCurrentICoord();
     578             : 
     579           0 :   if (aColumn.mBaseFrame) {
     580           0 :     bool allowBreakBefore = aColumnIndex ?
     581           0 :       aReflowInput.mAllowLineBreak : aReflowInput.mAllowInitialLineBreak;
     582           0 :     if (allowBreakBefore) {
     583           0 :       gfxBreakPriority breakPriority = LineBreakBefore(
     584           0 :         aColumn.mBaseFrame, baseReflowInput.mRenderingContext->GetDrawTarget(),
     585           0 :         baseReflowInput.mLineLayout->LineContainerFrame(),
     586           0 :         baseReflowInput.mLineLayout->GetLine());
     587           0 :       if (breakPriority != gfxBreakPriority::eNoBreak) {
     588             :         gfxBreakPriority lastBreakPriority =
     589           0 :           baseReflowInput.mLineLayout->LastOptionalBreakPriority();
     590           0 :         if (breakPriority >= lastBreakPriority) {
     591             :           // Either we have been overflow, or we are forced
     592             :           // to break here, do break before.
     593           0 :           if (istart > baseReflowInput.AvailableISize() ||
     594           0 :               baseReflowInput.mLineLayout->NotifyOptionalBreakPosition(
     595           0 :                 aColumn.mBaseFrame, 0, true, breakPriority)) {
     596           0 :             aStatus.SetInlineLineBreakBeforeAndReset();
     597           0 :             return 0;
     598             :           }
     599             :         }
     600             :       }
     601             :     }
     602             :   }
     603             : 
     604           0 :   const uint32_t rtcCount = aReflowInput.mTextContainers.Length();
     605           0 :   MOZ_ASSERT(aColumn.mTextFrames.Length() == rtcCount);
     606           0 :   MOZ_ASSERT(textReflowInputs.Length() == rtcCount);
     607           0 :   nscoord columnISize = 0;
     608             : 
     609           0 :   nsAutoString baseText;
     610           0 :   if (aColumn.mBaseFrame) {
     611           0 :     nsLayoutUtils::GetFrameTextContent(aColumn.mBaseFrame, baseText);
     612             :   }
     613             : 
     614             :   // Reflow text frames
     615           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     616           0 :     nsRubyTextFrame* textFrame = aColumn.mTextFrames[i];
     617           0 :     if (textFrame) {
     618           0 :       nsAutoString annotationText;
     619           0 :       nsLayoutUtils::GetFrameTextContent(textFrame, annotationText);
     620             : 
     621             :       // Per CSS Ruby spec, the content comparison for auto-hiding
     622             :       // takes place prior to white spaces collapsing (white-space)
     623             :       // and text transformation (text-transform), and ignores elements
     624             :       // (considers only the textContent of the boxes). Which means
     625             :       // using the content tree text comparison is correct.
     626           0 :       if (annotationText.Equals(baseText)) {
     627           0 :         textFrame->AddStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
     628             :       } else {
     629           0 :         textFrame->RemoveStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
     630             :       }
     631           0 :       RubyUtils::ClearReservedISize(textFrame);
     632             : 
     633             :       bool pushedFrame;
     634           0 :       nsReflowStatus reflowStatus;
     635           0 :       nsLineLayout* lineLayout = textReflowInputs[i]->mLineLayout;
     636           0 :       nscoord textIStart = lineLayout->GetCurrentICoord();
     637           0 :       lineLayout->ReflowFrame(textFrame, reflowStatus, nullptr, pushedFrame);
     638           0 :       if (MOZ_UNLIKELY(reflowStatus.IsInlineBreak() || pushedFrame)) {
     639           0 :         MOZ_ASSERT_UNREACHABLE(
     640             :             "Any line break inside ruby box should have been suppressed");
     641             :         // For safety, always drain the overflow list, so that
     642             :         // no frames are left there after reflow.
     643             :         textFrame->DrainSelfOverflowList();
     644             :       }
     645           0 :       nscoord textISize = lineLayout->GetCurrentICoord() - textIStart;
     646           0 :       columnISize = std::max(columnISize, textISize);
     647             :     }
     648             :   }
     649             : 
     650             :   // Reflow the base frame
     651           0 :   if (aColumn.mBaseFrame) {
     652           0 :     RubyUtils::ClearReservedISize(aColumn.mBaseFrame);
     653             : 
     654             :     bool pushedFrame;
     655           0 :     nsReflowStatus reflowStatus;
     656           0 :     nsLineLayout* lineLayout = baseReflowInput.mLineLayout;
     657           0 :     nscoord baseIStart = lineLayout->GetCurrentICoord();
     658           0 :     lineLayout->ReflowFrame(aColumn.mBaseFrame, reflowStatus,
     659           0 :                             nullptr, pushedFrame);
     660           0 :     if (MOZ_UNLIKELY(reflowStatus.IsInlineBreak() || pushedFrame)) {
     661           0 :       MOZ_ASSERT_UNREACHABLE(
     662             :         "Any line break inside ruby box should have been suppressed");
     663             :       // For safety, always drain the overflow list, so that
     664             :       // no frames are left there after reflow.
     665             :       aColumn.mBaseFrame->DrainSelfOverflowList();
     666             :     }
     667           0 :     nscoord baseISize = lineLayout->GetCurrentICoord() - baseIStart;
     668           0 :     columnISize = std::max(columnISize, baseISize);
     669             :   }
     670             : 
     671             :   // Align all the line layout to the new coordinate.
     672           0 :   nscoord icoord = istart + columnISize;
     673           0 :   nscoord deltaISize = icoord - baseReflowInput.mLineLayout->GetCurrentICoord();
     674           0 :   if (deltaISize > 0) {
     675           0 :     baseReflowInput.mLineLayout->AdvanceICoord(deltaISize);
     676           0 :     if (aColumn.mBaseFrame) {
     677           0 :       RubyUtils::SetReservedISize(aColumn.mBaseFrame, deltaISize);
     678             :     }
     679             :   }
     680           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     681           0 :     if (aReflowInput.mTextContainers[i]->IsSpanContainer()) {
     682           0 :       continue;
     683             :     }
     684           0 :     nsLineLayout* lineLayout = textReflowInputs[i]->mLineLayout;
     685           0 :     nsRubyTextFrame* textFrame = aColumn.mTextFrames[i];
     686           0 :     nscoord deltaISize = icoord - lineLayout->GetCurrentICoord();
     687           0 :     if (deltaISize > 0) {
     688           0 :       lineLayout->AdvanceICoord(deltaISize);
     689           0 :       if (textFrame && !textFrame->IsAutoHidden()) {
     690           0 :         RubyUtils::SetReservedISize(textFrame, deltaISize);
     691             :       }
     692             :     }
     693           0 :     if (aColumn.mBaseFrame && textFrame) {
     694           0 :       lineLayout->AttachLastFrameToBaseLineLayout();
     695             :     }
     696             :   }
     697             : 
     698           0 :   return columnISize;
     699             : }
     700             : 
     701           0 : nsRubyBaseContainerFrame::PullFrameState::PullFrameState(
     702             :     nsRubyBaseContainerFrame* aBaseContainer,
     703           0 :     const AutoRubyTextContainerArray& aTextContainers)
     704             :   : mBase(aBaseContainer)
     705           0 :   , mTextContainers(aTextContainers)
     706             : {
     707           0 :   const uint32_t rtcCount = aTextContainers.Length();
     708           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     709           0 :     mTexts.AppendElement(aTextContainers[i]);
     710             :   }
     711           0 : }
     712             : 
     713             : void
     714           0 : nsRubyBaseContainerFrame::PullOneColumn(nsLineLayout* aLineLayout,
     715             :                                         PullFrameState& aPullFrameState,
     716             :                                         RubyColumn& aColumn,
     717             :                                         bool& aIsComplete)
     718             : {
     719             :   const AutoRubyTextContainerArray& textContainers =
     720           0 :     aPullFrameState.mTextContainers;
     721           0 :   const uint32_t rtcCount = textContainers.Length();
     722             : 
     723           0 :   nsIFrame* nextBase = GetNextInFlowChild(aPullFrameState.mBase);
     724           0 :   MOZ_ASSERT(!nextBase || nextBase->IsRubyBaseFrame());
     725           0 :   aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(nextBase);
     726           0 :   bool foundFrame = !!aColumn.mBaseFrame;
     727             :   bool pullingIntraLevelWhitespace =
     728           0 :     aColumn.mBaseFrame && aColumn.mBaseFrame->IsIntraLevelWhitespace();
     729             : 
     730           0 :   aColumn.mTextFrames.ClearAndRetainStorage();
     731           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     732             :     nsIFrame* nextText =
     733           0 :       textContainers[i]->GetNextInFlowChild(aPullFrameState.mTexts[i]);
     734           0 :     MOZ_ASSERT(!nextText || nextText->IsRubyTextFrame());
     735           0 :     nsRubyTextFrame* textFrame = static_cast<nsRubyTextFrame*>(nextText);
     736           0 :     aColumn.mTextFrames.AppendElement(textFrame);
     737           0 :     foundFrame = foundFrame || nextText;
     738           0 :     if (nextText && !pullingIntraLevelWhitespace) {
     739           0 :       pullingIntraLevelWhitespace = textFrame->IsIntraLevelWhitespace();
     740             :     }
     741             :   }
     742             :   // If there exists any frame in continations, we haven't
     743             :   // completed the reflow process.
     744           0 :   aIsComplete = !foundFrame;
     745           0 :   if (!foundFrame) {
     746           0 :     return;
     747             :   }
     748             : 
     749           0 :   aColumn.mIsIntraLevelWhitespace = pullingIntraLevelWhitespace;
     750           0 :   if (pullingIntraLevelWhitespace) {
     751             :     // We are pulling an intra-level whitespace. Drop all frames which
     752             :     // are not part of this intra-level whitespace column. (Those frames
     753             :     // are really part of the *next* column, after the pulled one.)
     754           0 :     if (aColumn.mBaseFrame && !aColumn.mBaseFrame->IsIntraLevelWhitespace()) {
     755           0 :       aColumn.mBaseFrame = nullptr;
     756             :     }
     757           0 :     for (uint32_t i = 0; i < rtcCount; i++) {
     758           0 :       nsRubyTextFrame*& textFrame = aColumn.mTextFrames[i];
     759           0 :       if (textFrame && !textFrame->IsIntraLevelWhitespace()) {
     760           0 :         textFrame = nullptr;
     761             :       }
     762             :     }
     763             :   } else {
     764             :     // We are not pulling an intra-level whitespace, which means all
     765             :     // elements we are going to pull can have non-whitespace content,
     766             :     // which may contain float which we need to reparent.
     767           0 :     MOZ_ASSERT(aColumn.begin() != aColumn.end(),
     768             :                "Ruby column shouldn't be empty");
     769             :     nsBlockFrame* oldFloatCB =
     770           0 :       nsLayoutUtils::GetFloatContainingBlock(*aColumn.begin());
     771             : #ifdef DEBUG
     772           0 :     MOZ_ASSERT(oldFloatCB, "Must have found a float containing block");
     773           0 :     for (nsIFrame* frame : aColumn) {
     774           0 :       MOZ_ASSERT(nsLayoutUtils::GetFloatContainingBlock(frame) == oldFloatCB,
     775             :                  "All frames in the same ruby column should share "
     776             :                  "the same old float containing block");
     777             :     }
     778             : #endif
     779             :     nsBlockFrame* newFloatCB =
     780           0 :       nsLayoutUtils::GetAsBlock(aLineLayout->LineContainerFrame());
     781           0 :     MOZ_ASSERT(newFloatCB, "Must have a float containing block");
     782           0 :     if (oldFloatCB != newFloatCB) {
     783           0 :       for (nsIFrame* frame : aColumn) {
     784           0 :         newFloatCB->ReparentFloats(frame, oldFloatCB, false);
     785             :       }
     786             :     }
     787             :   }
     788             : 
     789             :   // Pull the frames of this column.
     790           0 :   if (aColumn.mBaseFrame) {
     791           0 :     DebugOnly<nsIFrame*> pulled = PullNextInFlowChild(aPullFrameState.mBase);
     792           0 :     MOZ_ASSERT(pulled == aColumn.mBaseFrame, "pulled a wrong frame?");
     793             :   }
     794           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     795           0 :     if (aColumn.mTextFrames[i]) {
     796             :       DebugOnly<nsIFrame*> pulled =
     797           0 :         textContainers[i]->PullNextInFlowChild(aPullFrameState.mTexts[i]);
     798           0 :       MOZ_ASSERT(pulled == aColumn.mTextFrames[i], "pulled a wrong frame?");
     799             :     }
     800             :   }
     801             : 
     802           0 :   if (!aIsComplete) {
     803             :     // We pulled frames from the next line, hence mark it dirty.
     804           0 :     aLineLayout->SetDirtyNextLine();
     805             :   }
     806             : }
     807             : 
     808             : nscoord
     809           0 : nsRubyBaseContainerFrame::ReflowSpans(const RubyReflowInput& aReflowInput)
     810             : {
     811           0 :   nscoord spanISize = 0;
     812           0 :   for (uint32_t i = 0, iend = aReflowInput.mTextContainers.Length();
     813           0 :        i < iend; i++) {
     814           0 :     nsRubyTextContainerFrame* container = aReflowInput.mTextContainers[i];
     815           0 :     if (!container->IsSpanContainer()) {
     816           0 :       continue;
     817             :     }
     818             : 
     819           0 :     nsIFrame* rtFrame = container->PrincipalChildList().FirstChild();
     820           0 :     nsReflowStatus reflowStatus;
     821             :     bool pushedFrame;
     822           0 :     nsLineLayout* lineLayout = aReflowInput.mTextReflowInputs[i]->mLineLayout;
     823           0 :     MOZ_ASSERT(lineLayout->GetCurrentICoord() == 0,
     824             :                "border/padding of rtc should have been suppressed");
     825           0 :     lineLayout->ReflowFrame(rtFrame, reflowStatus, nullptr, pushedFrame);
     826           0 :     MOZ_ASSERT(!reflowStatus.IsInlineBreak() && !pushedFrame,
     827             :                "Any line break inside ruby box should has been suppressed");
     828           0 :     spanISize = std::max(spanISize, lineLayout->GetCurrentICoord());
     829             :   }
     830           0 :   return spanISize;
     831             : }

Generated by: LCOV version 1.13