layout/generic/nsFirstLetterFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 /* rendering object for CSS :first-letter pseudo-element */
     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 "nsAutoPtr.h"
    15 #include "nsStyleSet.h"
    16 #include "nsFrameManager.h"
    17 #include "RestyleManager.h"
    18 #include "nsPlaceholderFrame.h"
    19 #include "nsCSSFrameConstructor.h"
    21 using namespace mozilla;
    22 using namespace mozilla::layout;
    24 nsIFrame*
    25 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    26 {
    27   return new (aPresShell) nsFirstLetterFrame(aContext);
    28 }
    30 NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
    32 NS_QUERYFRAME_HEAD(nsFirstLetterFrame)
    33   NS_QUERYFRAME_ENTRY(nsFirstLetterFrame)
    34 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    36 #ifdef DEBUG_FRAME_DUMP
    37 nsresult
    38 nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
    39 {
    40   return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
    41 }
    42 #endif
    44 nsIAtom*
    45 nsFirstLetterFrame::GetType() const
    46 {
    47   return nsGkAtoms::letterFrame;
    48 }
    50 void
    51 nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    52                                      const nsRect&           aDirtyRect,
    53                                      const nsDisplayListSet& aLists)
    54 {
    55   BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
    56 }
    58 void
    59 nsFirstLetterFrame::Init(nsIContent*      aContent,
    60                          nsIFrame*        aParent,
    61                          nsIFrame*        aPrevInFlow)
    62 {
    63   nsRefPtr<nsStyleContext> newSC;
    64   if (aPrevInFlow) {
    65     // Get proper style context for ourselves.  We're creating the frame
    66     // that represents everything *except* the first letter, so just create
    67     // a style context like we would for a text node.
    68     nsStyleContext* parentStyleContext = mStyleContext->GetParent();
    69     if (parentStyleContext) {
    70       newSC = PresContext()->StyleSet()->
    71         ResolveStyleForNonElement(parentStyleContext);
    72       SetStyleContextWithoutNotification(newSC);
    73     }
    74   }
    76   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
    77 }
    79 nsresult
    80 nsFirstLetterFrame::SetInitialChildList(ChildListID  aListID,
    81                                         nsFrameList& aChildList)
    82 {
    83   RestyleManager* restyleManager = PresContext()->RestyleManager();
    85   for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
    86     NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
    87     restyleManager->ReparentStyleContext(e.get());
    88     nsLayoutUtils::MarkDescendantsDirty(e.get());
    89   }
    91   mFrames.SetFrames(aChildList);
    92   return NS_OK;
    93 }
    95 nsresult
    96 nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
    97                                                   bool inHint,
    98                                                   int32_t* outFrameContentOffset,
    99                                                   nsIFrame **outChildFrame)
   100 {
   101   nsIFrame *kid = mFrames.FirstChild();
   102   if (kid)
   103   {
   104     return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
   105   }
   106   else
   107     return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
   108 }
   110 // Needed for non-floating first-letter frames and for the continuations
   111 // following the first-letter that we also use nsFirstLetterFrame for.
   112 /* virtual */ void
   113 nsFirstLetterFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
   114                                       nsIFrame::InlineMinWidthData *aData)
   115 {
   116   DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::MIN_WIDTH);
   117 }
   119 // Needed for non-floating first-letter frames and for the continuations
   120 // following the first-letter that we also use nsFirstLetterFrame for.
   121 /* virtual */ void
   122 nsFirstLetterFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
   123                                        nsIFrame::InlinePrefWidthData *aData)
   124 {
   125   DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::PREF_WIDTH);
   126 }
   128 // Needed for floating first-letter frames.
   129 /* virtual */ nscoord
   130 nsFirstLetterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   131 {
   132   return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext);
   133 }
   135 // Needed for floating first-letter frames.
   136 /* virtual */ nscoord
   137 nsFirstLetterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   138 {
   139   return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext);
   140 }
   142 /* virtual */ nsSize
   143 nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext,
   144                                 nsSize aCBSize, nscoord aAvailableWidth,
   145                                 nsSize aMargin, nsSize aBorder, nsSize aPadding,
   146                                 uint32_t aFlags)
   147 {
   148   if (GetPrevInFlow()) {
   149     // We're wrapping the text *after* the first letter, so behave like an
   150     // inline frame.
   151     return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   152   }
   153   return nsContainerFrame::ComputeSize(aRenderingContext,
   154       aCBSize, aAvailableWidth, aMargin, aBorder, aPadding, aFlags);
   155 }
   157 nsresult
   158 nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
   159                            nsHTMLReflowMetrics&     aMetrics,
   160                            const nsHTMLReflowState& aReflowState,
   161                            nsReflowStatus&          aReflowStatus)
   162 {
   163   DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
   164   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
   165   nsresult rv = NS_OK;
   167   // Grab overflow list
   168   DrainOverflowFrames(aPresContext);
   170   nsIFrame* kid = mFrames.FirstChild();
   172   // Setup reflow state for our child
   173   nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
   174   const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding();
   175   nscoord lr = bp.left + bp.right;
   176   nscoord tb = bp.top + bp.bottom;
   177   NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE,
   178                "should no longer use unconstrained widths");
   179   availSize.width -= lr;
   180   if (NS_UNCONSTRAINEDSIZE != availSize.height) {
   181     availSize.height -= tb;
   182   }
   184   // Reflow the child
   185   if (!aReflowState.mLineLayout) {
   186     // When there is no lineLayout provided, we provide our own. The
   187     // only time that the first-letter-frame is not reflowing in a
   188     // line context is when its floating.
   189     nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
   190     nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr);
   192     ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE,
   193                        false, true,
   194                        ll.LineContainerFrame()->GetWritingMode(kid),
   195                        aReflowState.AvailableWidth());
   196     rs.mLineLayout = &ll;
   197     ll.SetInFirstLetter(true);
   198     ll.SetFirstLetterStyleOK(true);
   200     kid->WillReflow(aPresContext);
   201     kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
   203     ll.EndLineReflow();
   204     ll.SetInFirstLetter(false);
   206     // In the floating first-letter case, we need to set this ourselves;
   207     // nsLineLayout::BeginSpan will set it in the other case
   208     mBaseline = aMetrics.TopAscent();
   209   }
   210   else {
   211     // Pretend we are a span and reflow the child frame
   212     nsLineLayout* ll = aReflowState.mLineLayout;
   213     bool          pushedFrame;
   215     ll->SetInFirstLetter(
   216       mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
   217     ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline);
   218     ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame);
   219     ll->EndSpan(this);
   220     ll->SetInFirstLetter(false);
   221   }
   223   // Place and size the child and update the output metrics
   224   kid->SetRect(nsRect(bp.left, bp.top, aMetrics.Width(), aMetrics.Height()));
   225   kid->FinishAndStoreOverflow(&aMetrics);
   226   kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
   228   aMetrics.Width() += lr;
   229   aMetrics.Height() += tb;
   230   aMetrics.SetTopAscent(aMetrics.TopAscent() + bp.top);
   232   // Ensure that the overflow rect contains the child textframe's overflow rect.
   233   // Note that if this is floating, the overline/underline drawable area is in
   234   // the overflow rect of the child textframe.
   235   aMetrics.UnionOverflowAreasWithDesiredBounds();
   236   ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
   238   if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
   239     // Create a continuation or remove existing continuations based on
   240     // the reflow completion status.
   241     if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
   242       if (aReflowState.mLineLayout) {
   243         aReflowState.mLineLayout->SetFirstLetterStyleOK(false);
   244       }
   245       nsIFrame* kidNextInFlow = kid->GetNextInFlow();
   246       if (kidNextInFlow) {
   247         // Remove all of the childs next-in-flows
   248         static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
   249           ->DeleteNextInFlowChild(kidNextInFlow, true);
   250       }
   251     }
   252     else {
   253       // Create a continuation for the child frame if it doesn't already
   254       // have one.
   255       if (!IsFloating()) {
   256         nsIFrame* nextInFlow;
   257         rv = CreateNextInFlow(kid, nextInFlow);
   258         if (NS_FAILED(rv)) {
   259           return rv;
   260         }
   262         // And then push it to our overflow list
   263         const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
   264         if (overflow.NotEmpty()) {
   265           SetOverflowFrames(overflow);
   266         }
   267       } else if (!kid->GetNextInFlow()) {
   268         // For floating first letter frames (if a continuation wasn't already
   269         // created for us) we need to put the continuation with the rest of the
   270         // text that the first letter frame was made out of.
   271         nsIFrame* continuation;
   272         rv = CreateContinuationForFloatingParent(aPresContext, kid,
   273                                                  &continuation, true);
   274       }
   275     }
   276   }
   278   FinishAndStoreOverflow(&aMetrics);
   280   NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
   281   return rv;
   282 }
   284 /* virtual */ bool
   285 nsFirstLetterFrame::CanContinueTextRun() const
   286 {
   287   // We can continue a text run through a first-letter frame.
   288   return true;
   289 }
   291 nsresult
   292 nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
   293                                                         nsIFrame* aChild,
   294                                                         nsIFrame** aContinuation,
   295                                                         bool aIsFluid)
   296 {
   297   NS_ASSERTION(IsFloating(),
   298                "can only call this on floating first letter frames");
   299   NS_PRECONDITION(aContinuation, "bad args");
   301   *aContinuation = nullptr;
   302   nsresult rv = NS_OK;
   304   nsIPresShell* presShell = aPresContext->PresShell();
   305   nsPlaceholderFrame* placeholderFrame =
   306     presShell->FrameManager()->GetPlaceholderFrameFor(this);
   307   nsIFrame* parent = placeholderFrame->GetParent();
   309   nsIFrame* continuation = presShell->FrameConstructor()->
   310     CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
   312   // The continuation will have gotten the first letter style from its
   313   // prev continuation, so we need to repair the style context so it
   314   // doesn't have the first letter styling.
   315   nsStyleContext* parentSC = this->StyleContext()->GetParent();
   316   if (parentSC) {
   317     nsRefPtr<nsStyleContext> newSC;
   318     newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC);
   319     continuation->SetStyleContext(newSC);
   320     nsLayoutUtils::MarkDescendantsDirty(continuation);
   321   }
   323   //XXX Bidi may not be involved but we have to use the list name
   324   // kNoReflowPrincipalList because this is just like creating a continuation
   325   // except we have to insert it in a different place and we don't want a
   326   // reflow command to try to be issued.
   327   nsFrameList temp(continuation, continuation);
   328   rv = parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
   330   *aContinuation = continuation;
   331   return rv;
   332 }
   334 void
   335 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
   336 {
   337   // Check for an overflow list with our prev-in-flow
   338   nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
   339   if (prevInFlow) {
   340     AutoFrameListPtr overflowFrames(aPresContext,
   341                                     prevInFlow->StealOverflowFrames());
   342     if (overflowFrames) {
   343       NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
   345       // When pushing and pulling frames we need to check for whether any
   346       // views need to be reparented.
   347       nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow,
   348                                               this);
   349       mFrames.InsertFrames(this, nullptr, *overflowFrames);
   350     }
   351   }
   353   // It's also possible that we have an overflow list for ourselves
   354   AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
   355   if (overflowFrames) {
   356     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
   357     mFrames.AppendFrames(nullptr, *overflowFrames);
   358   }
   360   // Now repair our first frames style context (since we only reflow
   361   // one frame there is no point in doing any other ones until they
   362   // are reflowed)
   363   nsIFrame* kid = mFrames.FirstChild();
   364   if (kid) {
   365     nsRefPtr<nsStyleContext> sc;
   366     nsIContent* kidContent = kid->GetContent();
   367     if (kidContent) {
   368       NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
   369                    "should contain only text nodes");
   370       nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() :
   371                                               mStyleContext;
   372       sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC);
   373       kid->SetStyleContext(sc);
   374       nsLayoutUtils::MarkDescendantsDirty(kid);
   375     }
   376   }
   377 }
   379 nscoord
   380 nsFirstLetterFrame::GetBaseline() const
   381 {
   382   return mBaseline;
   383 }
   385 int
   386 nsFirstLetterFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
   387 {
   388   if (GetPrevContinuation()) {
   389     // We shouldn't get calls to GetSkipSides for later continuations since
   390     // they have separate style contexts with initial values for all the
   391     // properties that could trigger a call to GetSkipSides.  Then again,
   392     // it's not really an error to call GetSkipSides on any frame, so
   393     // that's why we handle it properly.
   394     return LOGICAL_SIDES_ALL;
   395   }
   396   return 0;  // first continuation displays all sides
   397 }

mercurial