LCOV - code coverage report
Current view: top level - layout/generic - nsRubyFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 188 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 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" */
       8             : 
       9             : #include "nsRubyFrame.h"
      10             : 
      11             : #include "RubyUtils.h"
      12             : #include "mozilla/Maybe.h"
      13             : #include "mozilla/WritingModes.h"
      14             : #include "nsLineLayout.h"
      15             : #include "nsPresContext.h"
      16             : #include "nsRubyBaseContainerFrame.h"
      17             : #include "nsRubyTextContainerFrame.h"
      18             : #include "nsStyleContext.h"
      19             : 
      20             : using namespace mozilla;
      21             : 
      22             : //----------------------------------------------------------------------
      23             : 
      24             : // Frame class boilerplate
      25             : // =======================
      26             : 
      27           0 : NS_QUERYFRAME_HEAD(nsRubyFrame)
      28           0 :   NS_QUERYFRAME_ENTRY(nsRubyFrame)
      29           0 : NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
      30             : 
      31           0 : NS_IMPL_FRAMEARENA_HELPERS(nsRubyFrame)
      32             : 
      33             : nsContainerFrame*
      34           0 : NS_NewRubyFrame(nsIPresShell* aPresShell,
      35             :                 nsStyleContext* aContext)
      36             : {
      37           0 :   return new (aPresShell) nsRubyFrame(aContext);
      38             : }
      39             : 
      40             : //----------------------------------------------------------------------
      41             : 
      42             : // nsRubyFrame Method Implementations
      43             : // ==================================
      44             : 
      45             : /* virtual */ bool
      46           0 : nsRubyFrame::IsFrameOfType(uint32_t aFlags) const
      47             : {
      48           0 :   if (aFlags & eBidiInlineContainer) {
      49           0 :     return false;
      50             :   }
      51           0 :   return nsInlineFrame::IsFrameOfType(aFlags);
      52             : }
      53             : 
      54             : #ifdef DEBUG_FRAME_DUMP
      55             : nsresult
      56           0 : nsRubyFrame::GetFrameName(nsAString& aResult) const
      57             : {
      58           0 :   return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult);
      59             : }
      60             : #endif
      61             : 
      62             : /* virtual */ void
      63           0 : nsRubyFrame::AddInlineMinISize(gfxContext *aRenderingContext,
      64             :                                nsIFrame::InlineMinISizeData *aData)
      65             : {
      66           0 :   for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
      67           0 :     for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(frame));
      68           0 :          !e.AtEnd(); e.Next()) {
      69           0 :       e.GetBaseContainer()->AddInlineMinISize(aRenderingContext, aData);
      70             :     }
      71             :   }
      72           0 : }
      73             : 
      74             : /* virtual */ void
      75           0 : nsRubyFrame::AddInlinePrefISize(gfxContext *aRenderingContext,
      76             :                                 nsIFrame::InlinePrefISizeData *aData)
      77             : {
      78           0 :   for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) {
      79           0 :     for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(frame));
      80           0 :          !e.AtEnd(); e.Next()) {
      81           0 :       e.GetBaseContainer()->AddInlinePrefISize(aRenderingContext, aData);
      82             :     }
      83             :   }
      84           0 :   aData->mLineIsEmpty = false;
      85           0 : }
      86             : 
      87             : static nsRubyBaseContainerFrame*
      88           0 : FindRubyBaseContainerAncestor(nsIFrame* aFrame)
      89             : {
      90           0 :   for (nsIFrame* ancestor = aFrame->GetParent();
      91           0 :        ancestor && ancestor->IsFrameOfType(nsIFrame::eLineParticipant);
      92             :        ancestor = ancestor->GetParent()) {
      93           0 :     if (ancestor->IsRubyBaseContainerFrame()) {
      94           0 :       return static_cast<nsRubyBaseContainerFrame*>(ancestor);
      95             :     }
      96             :   }
      97           0 :   return nullptr;
      98             : }
      99             : 
     100             : /* virtual */ void
     101           0 : nsRubyFrame::Reflow(nsPresContext* aPresContext,
     102             :                     ReflowOutput& aDesiredSize,
     103             :                     const ReflowInput& aReflowInput,
     104             :                     nsReflowStatus& aStatus)
     105             : {
     106           0 :   MarkInReflow();
     107           0 :   DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
     108           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
     109             : 
     110           0 :   if (!aReflowInput.mLineLayout) {
     111           0 :     NS_ASSERTION(aReflowInput.mLineLayout,
     112             :                  "No line layout provided to RubyFrame reflow method.");
     113           0 :     aStatus.Reset();
     114           0 :     return;
     115             :   }
     116             : 
     117             :   // Grab overflow frames from prev-in-flow and its own.
     118           0 :   MoveOverflowToChildList();
     119             : 
     120             :   // Clear leadings
     121           0 :   mLeadings.Reset();
     122             : 
     123             :   // Since the ruby base container is going to reflow not only the ruby
     124             :   // base frames, but also the ruby text frames, and then *afterwards*
     125             :   // we're going to reflow the ruby text containers (which do not reflow
     126             :   // their children), we need to transfer NS_FRAME_IS_DIRTY status from
     127             :   // the ruby text containers to their child ruby texts now, both so
     128             :   // that the ruby texts are marked dirty if needed, and so that the
     129             :   // ruby text container doesn't mark the ruby text frames dirty *after*
     130             :   // they're reflowed and leave dirty bits in a clean tree (suppressing
     131             :   // future reflows, due to lack of a queued reflow to clean them).
     132           0 :   for (nsIFrame* child : PrincipalChildList()) {
     133           0 :     if (child->HasAnyStateBits(NS_FRAME_IS_DIRTY) &&
     134           0 :         child->IsRubyTextContainerFrame()) {
     135           0 :       for (nsIFrame* grandchild : child->PrincipalChildList()) {
     136           0 :         grandchild->AddStateBits(NS_FRAME_IS_DIRTY);
     137             :       }
     138             :       // Replace NS_FRAME_IS_DIRTY with NS_FRAME_HAS_DIRTY_CHILDREN so
     139             :       // we still have a dirty marking, but one that we won't transfer
     140             :       // to children again.
     141           0 :       child->RemoveStateBits(NS_FRAME_IS_DIRTY);
     142           0 :       child->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
     143             :     }
     144             :   }
     145             : 
     146             :   // Begin the span for the ruby frame
     147           0 :   WritingMode frameWM = aReflowInput.GetWritingMode();
     148           0 :   WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
     149           0 :   LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
     150           0 :   nscoord startEdge = 0;
     151             :   const bool boxDecorationBreakClone =
     152           0 :     StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone;
     153           0 :   if (boxDecorationBreakClone || !GetPrevContinuation()) {
     154           0 :     startEdge = borderPadding.IStart(frameWM);
     155             :   }
     156           0 :   NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE,
     157             :                "should no longer use available widths");
     158           0 :   nscoord availableISize = aReflowInput.AvailableISize();
     159           0 :   availableISize -= startEdge + borderPadding.IEnd(frameWM);
     160           0 :   aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
     161           0 :                                       startEdge, availableISize, &mBaseline);
     162             : 
     163           0 :   aStatus.Reset();
     164           0 :   for (RubySegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
     165           0 :     ReflowSegment(aPresContext, aReflowInput, e.GetBaseContainer(), aStatus);
     166             : 
     167           0 :     if (aStatus.IsInlineBreak()) {
     168             :       // A break occurs when reflowing the segment.
     169             :       // Don't continue reflowing more segments.
     170           0 :       break;
     171             :     }
     172             :   }
     173             : 
     174           0 :   ContinuationTraversingState pullState(this);
     175           0 :   while (aStatus.IsEmpty()) {
     176             :     nsRubyBaseContainerFrame* baseContainer =
     177           0 :       PullOneSegment(aReflowInput.mLineLayout, pullState);
     178           0 :     if (!baseContainer) {
     179             :       // No more continuations after, finish now.
     180           0 :       break;
     181             :     }
     182           0 :     ReflowSegment(aPresContext, aReflowInput, baseContainer, aStatus);
     183             :   }
     184             :   // We never handle overflow in ruby.
     185           0 :   MOZ_ASSERT(!aStatus.IsOverflowIncomplete());
     186             : 
     187           0 :   aDesiredSize.ISize(lineWM) = aReflowInput.mLineLayout->EndSpan(this);
     188           0 :   if (boxDecorationBreakClone || !GetPrevContinuation()) {
     189           0 :     aDesiredSize.ISize(lineWM) += borderPadding.IStart(frameWM);
     190             :   }
     191           0 :   if (boxDecorationBreakClone || aStatus.IsComplete()) {
     192           0 :     aDesiredSize.ISize(lineWM) += borderPadding.IEnd(frameWM);
     193             :   }
     194             : 
     195             :   // Update descendant leadings of ancestor ruby base container.
     196           0 :   if (nsRubyBaseContainerFrame* rbc = FindRubyBaseContainerAncestor(this)) {
     197           0 :     rbc->UpdateDescendantLeadings(mLeadings);
     198             :   }
     199             : 
     200           0 :   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
     201           0 :                                          borderPadding, lineWM, frameWM);
     202             : }
     203             : 
     204             : void
     205           0 : nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
     206             :                            const ReflowInput& aReflowInput,
     207             :                            nsRubyBaseContainerFrame* aBaseContainer,
     208             :                            nsReflowStatus& aStatus)
     209             : {
     210           0 :   WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
     211             :   LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
     212           0 :                         aReflowInput.AvailableBSize());
     213           0 :   WritingMode rubyWM = GetWritingMode();
     214           0 :   NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM),
     215             :                "Ruby frame writing-mode shouldn't be orthogonal to its line");
     216             : 
     217           0 :   AutoRubyTextContainerArray textContainers(aBaseContainer);
     218           0 :   const uint32_t rtcCount = textContainers.Length();
     219             : 
     220           0 :   ReflowOutput baseMetrics(aReflowInput);
     221             :   bool pushedFrame;
     222           0 :   aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus,
     223           0 :                                         &baseMetrics, pushedFrame);
     224             : 
     225           0 :   if (aStatus.IsInlineBreakBefore()) {
     226           0 :     if (aBaseContainer != mFrames.FirstChild()) {
     227             :       // Some segments may have been reflowed before, hence it is not
     228             :       // a break-before for the ruby container.
     229           0 :       aStatus.Reset();
     230           0 :       aStatus.SetInlineLineBreakAfter();
     231           0 :       aStatus.SetIncomplete();
     232           0 :       PushChildren(aBaseContainer, aBaseContainer->GetPrevSibling());
     233           0 :       aReflowInput.mLineLayout->SetDirtyNextLine();
     234             :     }
     235             :     // This base container is not placed at all, we can skip all
     236             :     // text containers paired with it.
     237           0 :     return;
     238             :   }
     239           0 :   if (aStatus.IsIncomplete()) {
     240             :     // It always promise that if the status is incomplete, there is a
     241             :     // break occurs. Break before has been processed above. However,
     242             :     // it is possible that break after happens with the frame reflow
     243             :     // completed. It happens if there is a force break at the end.
     244           0 :     MOZ_ASSERT(aStatus.IsInlineBreakAfter());
     245             :     // Find the previous sibling which we will
     246             :     // insert new continuations after.
     247             :     nsIFrame* lastChild;
     248           0 :     if (rtcCount > 0) {
     249           0 :       lastChild = textContainers.LastElement();
     250             :     } else {
     251           0 :       lastChild = aBaseContainer;
     252             :     }
     253             : 
     254             :     // Create continuations for the base container
     255           0 :     nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer);
     256             :     // newBaseContainer is null if there are existing next-in-flows.
     257             :     // We only need to move and push if there were not.
     258           0 :     if (newBaseContainer) {
     259             :       // Move the new frame after all the text containers
     260           0 :       mFrames.RemoveFrame(newBaseContainer);
     261           0 :       mFrames.InsertFrame(nullptr, lastChild, newBaseContainer);
     262             : 
     263             :       // Create continuations for text containers
     264           0 :       nsIFrame* newLastChild = newBaseContainer;
     265           0 :       for (uint32_t i = 0; i < rtcCount; i++) {
     266           0 :         nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]);
     267           0 :         MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist "
     268             :                    "if the corresponding rbc does not");
     269           0 :         mFrames.RemoveFrame(newTextContainer);
     270           0 :         mFrames.InsertFrame(nullptr, newLastChild, newTextContainer);
     271           0 :         newLastChild = newTextContainer;
     272             :       }
     273             :     }
     274           0 :     if (lastChild != mFrames.LastChild()) {
     275             :       // Always push the next frame after the last child in this segment.
     276             :       // It is possible that we pulled it back before our next-in-flow
     277             :       // drain our overflow.
     278           0 :       PushChildren(lastChild->GetNextSibling(), lastChild);
     279           0 :       aReflowInput.mLineLayout->SetDirtyNextLine();
     280             :     }
     281             :   } else {
     282             :     // If the ruby base container is reflowed completely, the line
     283             :     // layout will remove the next-in-flows of that frame. But the
     284             :     // line layout is not aware of the ruby text containers, hence
     285             :     // it is necessary to remove them here.
     286           0 :     for (uint32_t i = 0; i < rtcCount; i++) {
     287           0 :       nsIFrame* nextRTC = textContainers[i]->GetNextInFlow();
     288           0 :       if (nextRTC) {
     289           0 :         nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true);
     290             :       }
     291             :     }
     292             :   }
     293             : 
     294           0 :   nscoord segmentISize = baseMetrics.ISize(lineWM);
     295           0 :   const nsSize dummyContainerSize;
     296             :   LogicalRect baseRect =
     297           0 :     aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize);
     298             :   // We need to position our rtc frames on one side or the other of the
     299             :   // base container's rect, using a coordinate space that's relative to
     300             :   // the ruby frame. Right now, the base container's rect's block-axis
     301             :   // position is relative to the block container frame containing the
     302             :   // lines, so we use 0 instead. (i.e. we assume that the base container
     303             :   // is adjacent to the ruby frame's block-start edge.)
     304             :   // XXX We may need to add border/padding here. See bug 1055667.
     305           0 :   baseRect.BStart(lineWM) = 0;
     306             :   // The rect for offsets of text containers.
     307           0 :   LogicalRect offsetRect = baseRect;
     308           0 :   RubyBlockLeadings descLeadings = aBaseContainer->GetDescendantLeadings();
     309           0 :   offsetRect.BStart(lineWM) -= descLeadings.mStart;
     310           0 :   offsetRect.BSize(lineWM) += descLeadings.mStart + descLeadings.mEnd;
     311           0 :   for (uint32_t i = 0; i < rtcCount; i++) {
     312           0 :     nsRubyTextContainerFrame* textContainer = textContainers[i];
     313           0 :     WritingMode rtcWM = textContainer->GetWritingMode();
     314           0 :     nsReflowStatus textReflowStatus;
     315           0 :     ReflowOutput textMetrics(aReflowInput);
     316             :     ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer,
     317           0 :                                       availSize.ConvertTo(rtcWM, lineWM));
     318             :     // FIXME We probably shouldn't be using the same nsLineLayout for
     319             :     //       the text containers. But it should be fine now as we are
     320             :     //       not actually using this line layout to reflow something,
     321             :     //       but just read the writing mode from it.
     322           0 :     textReflowInput.mLineLayout = aReflowInput.mLineLayout;
     323             :     textContainer->Reflow(aPresContext, textMetrics,
     324           0 :                           textReflowInput, textReflowStatus);
     325             :     // Ruby text containers always return complete reflow status even when
     326             :     // they have continuations, because the breaking has already been
     327             :     // handled when reflowing the base containers.
     328           0 :     NS_ASSERTION(textReflowStatus.IsEmpty(),
     329             :                  "Ruby text container must not break itself inside");
     330             :     // The metrics is initialized with reflow state of this ruby frame,
     331             :     // hence the writing-mode is tied to rubyWM instead of rtcWM.
     332           0 :     LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM);
     333           0 :     textContainer->SetSize(lineWM, size);
     334             : 
     335           0 :     nscoord reservedISize = RubyUtils::GetReservedISize(textContainer);
     336           0 :     segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize);
     337             : 
     338           0 :     uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition;
     339           0 :     MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER ||
     340             :                rubyPosition == NS_STYLE_RUBY_POSITION_UNDER);
     341           0 :     Maybe<LogicalSide> side;
     342           0 :     if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) {
     343           0 :       side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver));
     344           0 :     } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) {
     345           0 :       side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder));
     346             :     } else {
     347             :       // XXX inter-character support in bug 1055672
     348           0 :       MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position");
     349             :     }
     350             : 
     351           0 :     LogicalPoint position(lineWM);
     352           0 :     if (side.isSome()) {
     353           0 :       if (side.value() == eLogicalSideBStart) {
     354           0 :         offsetRect.BStart(lineWM) -= size.BSize(lineWM);
     355           0 :         offsetRect.BSize(lineWM) += size.BSize(lineWM);
     356           0 :         position = offsetRect.Origin(lineWM);
     357           0 :       } else if (side.value() == eLogicalSideBEnd) {
     358           0 :         position = offsetRect.Origin(lineWM) +
     359           0 :           LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM));
     360           0 :         offsetRect.BSize(lineWM) += size.BSize(lineWM);
     361             :       } else {
     362           0 :         MOZ_ASSERT_UNREACHABLE("???");
     363             :       }
     364             :     }
     365             :     // Using a dummy container-size here, so child positioning may not be
     366             :     // correct. We will fix it in nsLineLayout after the whole line is
     367             :     // reflowed.
     368             :     FinishReflowChild(textContainer, aPresContext, textMetrics,
     369           0 :                       &textReflowInput, lineWM, position, dummyContainerSize, 0);
     370             :   }
     371           0 :   MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM),
     372             :              "Annotations should only be placed on the block directions");
     373             : 
     374           0 :   nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM);
     375           0 :   if (deltaISize <= 0) {
     376           0 :     RubyUtils::ClearReservedISize(aBaseContainer);
     377             :   } else {
     378           0 :     RubyUtils::SetReservedISize(aBaseContainer, deltaISize);
     379           0 :     aReflowInput.mLineLayout->AdvanceICoord(deltaISize);
     380             :   }
     381             : 
     382             :   // Set block leadings of the base container
     383           0 :   nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM);
     384           0 :   nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM);
     385             :   // XXX When bug 765861 gets fixed, this warning should be upgraded.
     386           0 :   NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0,
     387             :                        "Leadings should be non-negative (because adding "
     388             :                        "ruby annotation can only increase the size)");
     389           0 :   mLeadings.Update(startLeading, endLeading);
     390             : }
     391             : 
     392             : nsRubyBaseContainerFrame*
     393           0 : nsRubyFrame::PullOneSegment(const nsLineLayout* aLineLayout,
     394             :                             ContinuationTraversingState& aState)
     395             : {
     396             :   // Pull a ruby base container
     397           0 :   nsIFrame* baseFrame = GetNextInFlowChild(aState);
     398           0 :   if (!baseFrame) {
     399           0 :     return nullptr;
     400             :   }
     401           0 :   MOZ_ASSERT(baseFrame->IsRubyBaseContainerFrame());
     402             : 
     403             :   // Get the float containing block of the base frame before we pull it.
     404             :   nsBlockFrame* oldFloatCB =
     405           0 :     nsLayoutUtils::GetFloatContainingBlock(baseFrame);
     406           0 :   PullNextInFlowChild(aState);
     407             : 
     408             :   // Pull all ruby text containers following the base container
     409             :   nsIFrame* nextFrame;
     410           0 :   while ((nextFrame = GetNextInFlowChild(aState)) != nullptr &&
     411           0 :          nextFrame->IsRubyTextContainerFrame()) {
     412           0 :     PullNextInFlowChild(aState);
     413             :   }
     414             : 
     415           0 :   if (nsBlockFrame* newFloatCB =
     416           0 :       nsLayoutUtils::GetAsBlock(aLineLayout->LineContainerFrame())) {
     417           0 :     if (oldFloatCB && oldFloatCB != newFloatCB) {
     418           0 :       newFloatCB->ReparentFloats(baseFrame, oldFloatCB, true);
     419             :     }
     420             :   }
     421             : 
     422           0 :   return static_cast<nsRubyBaseContainerFrame*>(baseFrame);
     423             : }

Generated by: LCOV version 1.13