LCOV - code coverage report
Current view: top level - layout/generic - nsFirstLetterFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 175 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 19 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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* rendering object for CSS :first-letter pseudo-element */
       7             : 
       8             : #include "nsFirstLetterFrame.h"
       9             : #include "nsPresContext.h"
      10             : #include "nsStyleContext.h"
      11             : #include "nsIContent.h"
      12             : #include "nsLineLayout.h"
      13             : #include "nsGkAtoms.h"
      14             : #include "mozilla/StyleSetHandle.h"
      15             : #include "mozilla/StyleSetHandleInlines.h"
      16             : #include "nsFrameManager.h"
      17             : #include "mozilla/RestyleManager.h"
      18             : #include "mozilla/RestyleManagerInlines.h"
      19             : #include "nsPlaceholderFrame.h"
      20             : #include "nsCSSFrameConstructor.h"
      21             : 
      22             : using namespace mozilla;
      23             : using namespace mozilla::layout;
      24             : 
      25             : nsFirstLetterFrame*
      26           0 : NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      27             : {
      28           0 :   return new (aPresShell) nsFirstLetterFrame(aContext);
      29             : }
      30             : 
      31           0 : NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
      32             : 
      33           0 : NS_QUERYFRAME_HEAD(nsFirstLetterFrame)
      34           0 :   NS_QUERYFRAME_ENTRY(nsFirstLetterFrame)
      35           0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
      36             : 
      37             : #ifdef DEBUG_FRAME_DUMP
      38             : nsresult
      39           0 : nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
      40             : {
      41           0 :   return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
      42             : }
      43             : #endif
      44             : 
      45             : void
      46           0 : nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
      47             :                                      const nsRect&           aDirtyRect,
      48             :                                      const nsDisplayListSet& aLists)
      49             : {
      50           0 :   BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
      51           0 : }
      52             : 
      53             : void
      54           0 : nsFirstLetterFrame::Init(nsIContent*       aContent,
      55             :                          nsContainerFrame* aParent,
      56             :                          nsIFrame*         aPrevInFlow)
      57             : {
      58           0 :   RefPtr<nsStyleContext> newSC;
      59           0 :   if (aPrevInFlow) {
      60             :     // Get proper style context for ourselves.  We're creating the frame
      61             :     // that represents everything *except* the first letter, so just create
      62             :     // a style context like we would for a text node.
      63           0 :     nsStyleContext* parentStyleContext = mStyleContext->GetParentAllowServo();
      64           0 :     if (parentStyleContext) {
      65           0 :       newSC = PresContext()->StyleSet()->
      66           0 :         ResolveStyleForFirstLetterContinuation(parentStyleContext);
      67           0 :       SetStyleContextWithoutNotification(newSC);
      68             :     }
      69             :   }
      70             : 
      71           0 :   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
      72           0 : }
      73             : 
      74             : void
      75           0 : nsFirstLetterFrame::SetInitialChildList(ChildListID  aListID,
      76             :                                         nsFrameList& aChildList)
      77             : {
      78           0 :   MOZ_ASSERT(aListID == kPrincipalList, "Principal child list is the only "
      79             :              "list that nsFirstLetterFrame should set via this function");
      80           0 :   RestyleManager* restyleManager = PresContext()->RestyleManager();
      81             : 
      82           0 :   for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
      83           0 :     NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
      84           0 :     restyleManager->ReparentStyleContext(e.get());
      85           0 :     nsLayoutUtils::MarkDescendantsDirty(e.get());
      86             :   }
      87             : 
      88           0 :   mFrames.SetFrames(aChildList);
      89           0 : }
      90             : 
      91             : nsresult
      92           0 : nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
      93             :                                                   bool inHint,
      94             :                                                   int32_t* outFrameContentOffset,
      95             :                                                   nsIFrame **outChildFrame)
      96             : {
      97           0 :   nsIFrame *kid = mFrames.FirstChild();
      98           0 :   if (kid) {
      99           0 :     return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
     100             :   } else {
     101           0 :     return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
     102             :   }
     103             : }
     104             : 
     105             : // Needed for non-floating first-letter frames and for the continuations
     106             : // following the first-letter that we also use nsFirstLetterFrame for.
     107             : /* virtual */ void
     108           0 : nsFirstLetterFrame::AddInlineMinISize(gfxContext *aRenderingContext,
     109             :                                       nsIFrame::InlineMinISizeData *aData)
     110             : {
     111           0 :   DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::MIN_ISIZE);
     112           0 : }
     113             : 
     114             : // Needed for non-floating first-letter frames and for the continuations
     115             : // following the first-letter that we also use nsFirstLetterFrame for.
     116             : /* virtual */ void
     117           0 : nsFirstLetterFrame::AddInlinePrefISize(gfxContext *aRenderingContext,
     118             :                                        nsIFrame::InlinePrefISizeData *aData)
     119             : {
     120           0 :   DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::PREF_ISIZE);
     121           0 :   aData->mLineIsEmpty = false;
     122           0 : }
     123             : 
     124             : // Needed for floating first-letter frames.
     125             : /* virtual */ nscoord
     126           0 : nsFirstLetterFrame::GetMinISize(gfxContext *aRenderingContext)
     127             : {
     128           0 :   return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
     129             : }
     130             : 
     131             : // Needed for floating first-letter frames.
     132             : /* virtual */ nscoord
     133           0 : nsFirstLetterFrame::GetPrefISize(gfxContext *aRenderingContext)
     134             : {
     135           0 :   return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
     136             : }
     137             : 
     138             : /* virtual */
     139             : LogicalSize
     140           0 : nsFirstLetterFrame::ComputeSize(gfxContext *aRenderingContext,
     141             :                                 WritingMode aWM,
     142             :                                 const LogicalSize& aCBSize,
     143             :                                 nscoord aAvailableISize,
     144             :                                 const LogicalSize& aMargin,
     145             :                                 const LogicalSize& aBorder,
     146             :                                 const LogicalSize& aPadding,
     147             :                                 ComputeSizeFlags aFlags)
     148             : {
     149           0 :   if (GetPrevInFlow()) {
     150             :     // We're wrapping the text *after* the first letter, so behave like an
     151             :     // inline frame.
     152           0 :     return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
     153             :   }
     154             :   return nsContainerFrame::ComputeSize(aRenderingContext, aWM,
     155           0 :       aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags);
     156             : }
     157             : 
     158             : void
     159           0 : nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
     160             :                            ReflowOutput&     aMetrics,
     161             :                            const ReflowInput& aReflowInput,
     162             :                            nsReflowStatus&          aReflowStatus)
     163             : {
     164           0 :   MarkInReflow();
     165           0 :   DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
     166           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus);
     167             : 
     168             :   // Grab overflow list
     169           0 :   DrainOverflowFrames(aPresContext);
     170             : 
     171           0 :   nsIFrame* kid = mFrames.FirstChild();
     172             : 
     173             :   // Setup reflow state for our child
     174           0 :   WritingMode wm = aReflowInput.GetWritingMode();
     175           0 :   LogicalSize availSize = aReflowInput.AvailableSize();
     176           0 :   const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding();
     177           0 :   NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE,
     178             :                "should no longer use unconstrained inline size");
     179           0 :   availSize.ISize(wm) -= bp.IStartEnd(wm);
     180           0 :   if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
     181           0 :     availSize.BSize(wm) -= bp.BStartEnd(wm);
     182             :   }
     183             : 
     184           0 :   WritingMode lineWM = aMetrics.GetWritingMode();
     185           0 :   ReflowOutput kidMetrics(lineWM);
     186             : 
     187             :   // Reflow the child
     188           0 :   if (!aReflowInput.mLineLayout) {
     189             :     // When there is no lineLayout provided, we provide our own. The
     190             :     // only time that the first-letter-frame is not reflowing in a
     191             :     // line context is when its floating.
     192           0 :     WritingMode kidWritingMode = WritingModeForLine(wm, kid);
     193           0 :     LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
     194           0 :     ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize);
     195           0 :     nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr);
     196             : 
     197           0 :     ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm),
     198           0 :                        availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
     199             :                        false, true, kidWritingMode,
     200           0 :                        nsSize(aReflowInput.AvailableWidth(),
     201           0 :                               aReflowInput.AvailableHeight()));
     202           0 :     rs.mLineLayout = &ll;
     203           0 :     ll.SetInFirstLetter(true);
     204           0 :     ll.SetFirstLetterStyleOK(true);
     205             : 
     206           0 :     kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
     207             : 
     208           0 :     ll.EndLineReflow();
     209           0 :     ll.SetInFirstLetter(false);
     210             : 
     211             :     // In the floating first-letter case, we need to set this ourselves;
     212             :     // nsLineLayout::BeginSpan will set it in the other case
     213           0 :     mBaseline = kidMetrics.BlockStartAscent();
     214             : 
     215             :     // Place and size the child and update the output metrics
     216           0 :     LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
     217           0 :     kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
     218           0 :                         convertedSize.ISize(wm), convertedSize.BSize(wm)));
     219           0 :     kid->FinishAndStoreOverflow(&kidMetrics, rs.mStyleDisplay);
     220           0 :     kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
     221             : 
     222           0 :     convertedSize.ISize(wm) += bp.IStartEnd(wm);
     223           0 :     convertedSize.BSize(wm) += bp.BStartEnd(wm);
     224           0 :     aMetrics.SetSize(wm, convertedSize);
     225           0 :     aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
     226           0 :                                  bp.BStart(wm));
     227             : 
     228             :     // Ensure that the overflow rect contains the child textframe's
     229             :     // overflow rect.
     230             :     // Note that if this is floating, the overline/underline drawable
     231             :     // area is in the overflow rect of the child textframe.
     232           0 :     aMetrics.UnionOverflowAreasWithDesiredBounds();
     233           0 :     ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
     234             : 
     235           0 :     FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
     236             :   } else {
     237             :     // Pretend we are a span and reflow the child frame
     238           0 :     nsLineLayout* ll = aReflowInput.mLineLayout;
     239             :     bool          pushedFrame;
     240             : 
     241           0 :     ll->SetInFirstLetter(
     242           0 :       mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
     243           0 :     ll->BeginSpan(this, &aReflowInput, bp.IStart(wm),
     244           0 :                   availSize.ISize(wm), &mBaseline);
     245           0 :     ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
     246           0 :     NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
     247             :                  "we're assuming we can mix sizes between lineWM and wm "
     248             :                  "since we shouldn't have orthogonal writing modes within "
     249             :                  "a line.");
     250           0 :     aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
     251           0 :     ll->SetInFirstLetter(false);
     252             : 
     253           0 :     if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) {
     254           0 :       aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
     255           0 :                                    bp.BStart(wm));
     256           0 :       aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm);
     257             :     } else {
     258           0 :       nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
     259             :     }
     260             :   }
     261             : 
     262           0 :   if (!aReflowStatus.IsInlineBreakBefore()) {
     263             :     // Create a continuation or remove existing continuations based on
     264             :     // the reflow completion status.
     265           0 :     if (aReflowStatus.IsComplete()) {
     266           0 :       if (aReflowInput.mLineLayout) {
     267           0 :         aReflowInput.mLineLayout->SetFirstLetterStyleOK(false);
     268             :       }
     269           0 :       nsIFrame* kidNextInFlow = kid->GetNextInFlow();
     270           0 :       if (kidNextInFlow) {
     271             :         // Remove all of the childs next-in-flows
     272           0 :         kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
     273             :       }
     274             :     } else {
     275             :       // Create a continuation for the child frame if it doesn't already
     276             :       // have one.
     277           0 :       if (!IsFloating()) {
     278           0 :         CreateNextInFlow(kid);
     279             :         // And then push it to our overflow list
     280           0 :         const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
     281           0 :         if (overflow.NotEmpty()) {
     282           0 :           SetOverflowFrames(overflow);
     283             :         }
     284           0 :       } else if (!kid->GetNextInFlow()) {
     285             :         // For floating first letter frames (if a continuation wasn't already
     286             :         // created for us) we need to put the continuation with the rest of the
     287             :         // text that the first letter frame was made out of.
     288             :         nsIFrame* continuation;
     289             :         CreateContinuationForFloatingParent(aPresContext, kid,
     290           0 :                                             &continuation, true);
     291             :       }
     292             :     }
     293             :   }
     294             : 
     295           0 :   NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowInput, aMetrics);
     296           0 : }
     297             : 
     298             : /* virtual */ bool
     299           0 : nsFirstLetterFrame::CanContinueTextRun() const
     300             : {
     301             :   // We can continue a text run through a first-letter frame.
     302           0 :   return true;
     303             : }
     304             : 
     305             : nsresult
     306           0 : nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
     307             :                                                         nsIFrame* aChild,
     308             :                                                         nsIFrame** aContinuation,
     309             :                                                         bool aIsFluid)
     310             : {
     311           0 :   NS_ASSERTION(IsFloating(),
     312             :                "can only call this on floating first letter frames");
     313           0 :   NS_PRECONDITION(aContinuation, "bad args");
     314             : 
     315           0 :   *aContinuation = nullptr;
     316             : 
     317           0 :   nsIPresShell* presShell = aPresContext->PresShell();
     318           0 :   nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrame();
     319           0 :   nsContainerFrame* parent = placeholderFrame->GetParent();
     320             : 
     321             :   nsIFrame* continuation = presShell->FrameConstructor()->
     322           0 :     CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
     323             : 
     324             :   // The continuation will have gotten the first letter style from its
     325             :   // prev continuation, so we need to repair the style context so it
     326             :   // doesn't have the first letter styling.
     327             :   //
     328             :   // Note that getting parent frame's style context is different from getting
     329             :   // this frame's style context's parent in the presence of ::first-line,
     330             :   // which we do want the continuation to inherit from.
     331           0 :   nsStyleContext* parentSC = parent->StyleContext();
     332           0 :   if (parentSC) {
     333           0 :     RefPtr<nsStyleContext> newSC;
     334           0 :     newSC = presShell->StyleSet()->ResolveStyleForFirstLetterContinuation(parentSC);
     335           0 :     continuation->SetStyleContext(newSC);
     336           0 :     nsLayoutUtils::MarkDescendantsDirty(continuation);
     337             :   }
     338             : 
     339             :   //XXX Bidi may not be involved but we have to use the list name
     340             :   // kNoReflowPrincipalList because this is just like creating a continuation
     341             :   // except we have to insert it in a different place and we don't want a
     342             :   // reflow command to try to be issued.
     343           0 :   nsFrameList temp(continuation, continuation);
     344           0 :   parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
     345             : 
     346           0 :   *aContinuation = continuation;
     347           0 :   return NS_OK;
     348             : }
     349             : 
     350             : void
     351           0 : nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
     352             : {
     353             :   // Check for an overflow list with our prev-in-flow
     354           0 :   nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
     355           0 :   if (prevInFlow) {
     356             :     AutoFrameListPtr overflowFrames(aPresContext,
     357           0 :                                     prevInFlow->StealOverflowFrames());
     358           0 :     if (overflowFrames) {
     359           0 :       NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
     360             : 
     361             :       // When pushing and pulling frames we need to check for whether any
     362             :       // views need to be reparented.
     363           0 :       nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow,
     364           0 :                                               this);
     365           0 :       mFrames.InsertFrames(this, nullptr, *overflowFrames);
     366             :     }
     367             :   }
     368             : 
     369             :   // It's also possible that we have an overflow list for ourselves
     370           0 :   AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
     371           0 :   if (overflowFrames) {
     372           0 :     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     373           0 :     mFrames.AppendFrames(nullptr, *overflowFrames);
     374             :   }
     375             : 
     376             :   // Now repair our first frames style context (since we only reflow
     377             :   // one frame there is no point in doing any other ones until they
     378             :   // are reflowed)
     379           0 :   nsIFrame* kid = mFrames.FirstChild();
     380           0 :   if (kid) {
     381           0 :     RefPtr<nsStyleContext> sc;
     382           0 :     nsIContent* kidContent = kid->GetContent();
     383           0 :     if (kidContent) {
     384           0 :       NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
     385             :                    "should contain only text nodes");
     386           0 :       nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParentAllowServo() :
     387           0 :                                               mStyleContext;
     388           0 :       sc = aPresContext->StyleSet()->ResolveStyleForText(kidContent, parentSC);
     389           0 :       kid->SetStyleContext(sc);
     390           0 :       nsLayoutUtils::MarkDescendantsDirty(kid);
     391             :     }
     392             :   }
     393           0 : }
     394             : 
     395             : nscoord
     396           0 : nsFirstLetterFrame::GetLogicalBaseline(WritingMode aWritingMode) const
     397             : {
     398           0 :   return mBaseline;
     399             : }
     400             : 
     401             : nsIFrame::LogicalSides
     402           0 : nsFirstLetterFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
     403             : {
     404           0 :   if (GetPrevContinuation()) {
     405             :     // We shouldn't get calls to GetSkipSides for later continuations since
     406             :     // they have separate style contexts with initial values for all the
     407             :     // properties that could trigger a call to GetSkipSides.  Then again,
     408             :     // it's not really an error to call GetSkipSides on any frame, so
     409             :     // that's why we handle it properly.
     410           0 :     return LogicalSides(eLogicalSideBitsAll);
     411             :   }
     412           0 :   return LogicalSides();  // first continuation displays all sides
     413             : }

Generated by: LCOV version 1.13