LCOV - code coverage report
Current view: top level - layout/generic - nsAbsoluteContainingBlock.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 102 325 31.4 %
Date: 2017-07-14 16:53:18 Functions: 8 18 44.4 %
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             : /*
       7             :  * code for managing absolutely positioned children of a rendering
       8             :  * object that is a containing block for them
       9             :  */
      10             : 
      11             : #include "nsAbsoluteContainingBlock.h"
      12             : 
      13             : #include "nsContainerFrame.h"
      14             : #include "nsGkAtoms.h"
      15             : #include "nsIPresShell.h"
      16             : #include "mozilla/CSSAlignUtils.h"
      17             : #include "mozilla/ReflowInput.h"
      18             : #include "nsPresContext.h"
      19             : #include "nsCSSFrameConstructor.h"
      20             : #include "nsGridContainerFrame.h"
      21             : 
      22             : #include "mozilla/Sprintf.h"
      23             : 
      24             : #ifdef DEBUG
      25             : #include "nsBlockFrame.h"
      26             : 
      27           0 : static void PrettyUC(nscoord aSize, char* aBuf, int aBufSize)
      28             : {
      29           0 :   if (NS_UNCONSTRAINEDSIZE == aSize) {
      30           0 :     strcpy(aBuf, "UC");
      31             :   } else {
      32           0 :     if((int32_t)0xdeadbeef == aSize) {
      33           0 :       strcpy(aBuf, "deadbeef");
      34             :     } else {
      35           0 :       snprintf(aBuf, aBufSize, "%d", aSize);
      36             :     }
      37             :   }
      38           0 : }
      39             : #endif
      40             : 
      41             : using namespace mozilla;
      42             : 
      43             : typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
      44             : 
      45             : void
      46           1 : nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame*       aDelegatingFrame,
      47             :                                                ChildListID     aListID,
      48             :                                                nsFrameList&    aChildList)
      49             : {
      50           1 :   NS_PRECONDITION(mChildListID == aListID, "unexpected child list name");
      51             : #ifdef DEBUG
      52           1 :   nsFrame::VerifyDirtyBitSet(aChildList);
      53             : #endif
      54           1 :   mAbsoluteFrames.SetFrames(aChildList);
      55           1 : }
      56             : 
      57             : void
      58           2 : nsAbsoluteContainingBlock::AppendFrames(nsIFrame*      aDelegatingFrame,
      59             :                                         ChildListID    aListID,
      60             :                                         nsFrameList&   aFrameList)
      61             : {
      62           2 :   NS_ASSERTION(mChildListID == aListID, "unexpected child list");
      63             : 
      64             :   // Append the frames to our list of absolutely positioned frames
      65             : #ifdef DEBUG
      66           2 :   nsFrame::VerifyDirtyBitSet(aFrameList);
      67             : #endif
      68           2 :   mAbsoluteFrames.AppendFrames(nullptr, aFrameList);
      69             : 
      70             :   // no damage to intrinsic widths, since absolutely positioned frames can't
      71             :   // change them
      72           2 :   aDelegatingFrame->PresContext()->PresShell()->
      73             :     FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
      74           2 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
      75           2 : }
      76             : 
      77             : void
      78           0 : nsAbsoluteContainingBlock::InsertFrames(nsIFrame*      aDelegatingFrame,
      79             :                                         ChildListID    aListID,
      80             :                                         nsIFrame*      aPrevFrame,
      81             :                                         nsFrameList&   aFrameList)
      82             : {
      83           0 :   NS_ASSERTION(mChildListID == aListID, "unexpected child list");
      84           0 :   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame,
      85             :                "inserting after sibling frame with different parent");
      86             : 
      87             : #ifdef DEBUG
      88           0 :   nsFrame::VerifyDirtyBitSet(aFrameList);
      89             : #endif
      90           0 :   mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
      91             : 
      92             :   // no damage to intrinsic widths, since absolutely positioned frames can't
      93             :   // change them
      94           0 :   aDelegatingFrame->PresContext()->PresShell()->
      95             :     FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
      96           0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
      97           0 : }
      98             : 
      99             : void
     100           2 : nsAbsoluteContainingBlock::RemoveFrame(nsIFrame*       aDelegatingFrame,
     101             :                                        ChildListID     aListID,
     102             :                                        nsIFrame*       aOldFrame)
     103             : {
     104           2 :   NS_ASSERTION(mChildListID == aListID, "unexpected child list");
     105           2 :   nsIFrame* nif = aOldFrame->GetNextInFlow();
     106           2 :   if (nif) {
     107           0 :     nif->GetParent()->DeleteNextInFlowChild(nif, false);
     108             :   }
     109             : 
     110           2 :   mAbsoluteFrames.DestroyFrame(aOldFrame);
     111           2 : }
     112             : 
     113             : void
     114          23 : nsAbsoluteContainingBlock::Reflow(nsContainerFrame*        aDelegatingFrame,
     115             :                                   nsPresContext*           aPresContext,
     116             :                                   const ReflowInput&       aReflowInput,
     117             :                                   nsReflowStatus&          aReflowStatus,
     118             :                                   const nsRect&            aContainingBlock,
     119             :                                   AbsPosReflowFlags        aFlags,
     120             :                                   nsOverflowAreas*         aOverflowAreas)
     121             : {
     122          23 :   nsReflowStatus reflowStatus;
     123             : 
     124          23 :   const bool reflowAll = aReflowInput.ShouldReflowAllKids();
     125          23 :   const bool isGrid = !!(aFlags & AbsPosReflowFlags::eIsGridContainerCB);
     126             :   nsIFrame* kidFrame;
     127          23 :   nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
     128         100 :   for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
     129         257 :     bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) ||
     130         120 :       FrameDependsOnContainer(kidFrame,
     131         197 :                               !!(aFlags & AbsPosReflowFlags::eCBWidthChanged),
     132         274 :                               !!(aFlags & AbsPosReflowFlags::eCBHeightChanged));
     133          77 :     nscoord availBSize = aReflowInput.AvailableBSize();
     134          77 :     const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
     135          77 :                               : aContainingBlock;
     136          77 :     WritingMode containerWM = aReflowInput.GetWritingMode();
     137          77 :     if (!kidNeedsReflow && availBSize != NS_UNCONSTRAINEDSIZE) {
     138             :       // If we need to redo pagination on the kid, we need to reflow it.
     139             :       // This can happen either if the available height shrunk and the
     140             :       // kid (or its overflow that creates overflow containers) is now
     141             :       // too large to fit in the available height, or if the available
     142             :       // height has increased and the kid has a next-in-flow that we
     143             :       // might need to pull from.
     144           0 :       WritingMode kidWM = kidFrame->GetWritingMode();
     145           0 :       if (containerWM.GetBlockDir() != kidWM.GetBlockDir()) {
     146             :         // Not sure what the right test would be here.
     147           0 :         kidNeedsReflow = true;
     148             :       } else {
     149           0 :         nscoord kidBEnd = kidFrame->GetLogicalRect(cb.Size()).BEnd(kidWM);
     150             :         nscoord kidOverflowBEnd =
     151           0 :           LogicalRect(containerWM,
     152             :                       // Use ...RelativeToSelf to ignore transforms
     153           0 :                       kidFrame->GetScrollableOverflowRectRelativeToSelf() +
     154           0 :                         kidFrame->GetPosition(),
     155           0 :                       aContainingBlock.Size()).BEnd(containerWM);
     156           0 :         MOZ_ASSERT(kidOverflowBEnd >= kidBEnd);
     157           0 :         if (kidOverflowBEnd > availBSize ||
     158           0 :             (kidBEnd < availBSize && kidFrame->GetNextInFlow())) {
     159           0 :           kidNeedsReflow = true;
     160             :         }
     161             :       }
     162             :     }
     163          77 :     if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
     164             :       // Reflow the frame
     165          77 :       nsReflowStatus kidStatus;
     166             :       ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput, cb,
     167          77 :                           aFlags, kidFrame, kidStatus, aOverflowAreas);
     168          77 :       nsIFrame* nextFrame = kidFrame->GetNextInFlow();
     169          77 :       if (!kidStatus.IsFullyComplete() &&
     170           0 :           aDelegatingFrame->IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
     171             :         // Need a continuation
     172           0 :         if (!nextFrame) {
     173             :           nextFrame =
     174             :             aPresContext->PresShell()->FrameConstructor()->
     175           0 :               CreateContinuingFrame(aPresContext, kidFrame, aDelegatingFrame);
     176             :         }
     177             :         // Add it as an overflow container.
     178             :         //XXXfr This is a hack to fix some of our printing dataloss.
     179             :         // See bug 154892. Not sure how to do it "right" yet; probably want
     180             :         // to keep continuations within an nsAbsoluteContainingBlock eventually.
     181           0 :         tracker.Insert(nextFrame, kidStatus);
     182           0 :         reflowStatus.MergeCompletionStatusFrom(kidStatus);
     183             :       }
     184             :       else {
     185             :         // Delete any continuations
     186          77 :         if (nextFrame) {
     187           0 :           nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame);
     188           0 :           nextFrame->GetParent()->DeleteNextInFlowChild(nextFrame, true);
     189             :         }
     190             :       }
     191             :     }
     192             :     else {
     193           0 :       tracker.Skip(kidFrame, reflowStatus);
     194           0 :       if (aOverflowAreas) {
     195           0 :         aDelegatingFrame->ConsiderChildOverflow(*aOverflowAreas, kidFrame);
     196             :       }
     197             :     }
     198             : 
     199             :     // Make a CheckForInterrupt call, here, not just HasPendingInterrupt.  That
     200             :     // will make sure that we end up reflowing aDelegatingFrame in cases when
     201             :     // one of our kids interrupted.  Otherwise we'd set the dirty or
     202             :     // dirty-children bit on the kid in the condition below, and then when
     203             :     // reflow completes and we go to mark dirty bits on all ancestors of that
     204             :     // kid we'll immediately bail out, because the kid already has a dirty bit.
     205             :     // In particular, we won't set any dirty bits on aDelegatingFrame, so when
     206             :     // the following reflow happens we won't reflow the kid in question.  This
     207             :     // might be slightly suboptimal in cases where |kidFrame| itself did not
     208             :     // interrupt, since we'll trigger a reflow of it too when it's not strictly
     209             :     // needed.  But the logic to not do that is enough more complicated, and
     210             :     // the case enough of an edge case, that this is probably better.
     211          77 :     if (kidNeedsReflow && aPresContext->CheckForInterrupt(aDelegatingFrame)) {
     212           0 :       if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
     213           0 :         kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
     214             :       } else {
     215           0 :         kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
     216             :       }
     217             :     }
     218             :   }
     219             : 
     220             :   // Abspos frames can't cause their parent to be incomplete,
     221             :   // only overflow incomplete.
     222          23 :   if (reflowStatus.IsIncomplete())
     223           0 :     reflowStatus.SetOverflowIncomplete();
     224             : 
     225          23 :   aReflowStatus.MergeCompletionStatusFrom(reflowStatus);
     226          23 : }
     227             : 
     228           0 : static inline bool IsFixedPaddingSize(const nsStyleCoord& aCoord)
     229           0 :   { return aCoord.ConvertsToLength(); }
     230           0 : static inline bool IsFixedMarginSize(const nsStyleCoord& aCoord)
     231           0 :   { return aCoord.ConvertsToLength(); }
     232           0 : static inline bool IsFixedOffset(const nsStyleCoord& aCoord)
     233           0 :   { return aCoord.ConvertsToLength(); }
     234             : 
     235             : bool
     236          60 : nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f,
     237             :                                                    bool aCBWidthChanged,
     238             :                                                    bool aCBHeightChanged)
     239             : {
     240          60 :   const nsStylePosition* pos = f->StylePosition();
     241             :   // See if f's position might have changed because it depends on a
     242             :   // placeholder's position
     243             :   // This can happen in the following cases:
     244             :   // 1) Vertical positioning.  "top" must be auto and "bottom" must be auto
     245             :   //    (otherwise the vertical position is completely determined by
     246             :   //    whichever of them is not auto and the height).
     247             :   // 2) Horizontal positioning.  "left" must be auto and "right" must be auto
     248             :   //    (otherwise the horizontal position is completely determined by
     249             :   //    whichever of them is not auto and the width).
     250             :   // See ReflowInput::InitAbsoluteConstraints -- these are the
     251             :   // only cases when we call CalculateHypotheticalBox().
     252         180 :   if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto &&
     253         120 :        pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) ||
     254           0 :       (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
     255           0 :        pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
     256          60 :     return true;
     257             :   }
     258           0 :   if (!aCBWidthChanged && !aCBHeightChanged) {
     259             :     // skip getting style data
     260           0 :     return false;
     261             :   }
     262           0 :   const nsStylePadding* padding = f->StylePadding();
     263           0 :   const nsStyleMargin* margin = f->StyleMargin();
     264           0 :   WritingMode wm = f->GetWritingMode();
     265           0 :   if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) {
     266             :     // See if f's inline-size might have changed.
     267             :     // If margin-inline-start/end, padding-inline-start/end,
     268             :     // inline-size, min/max-inline-size are all lengths, 'none', or enumerated,
     269             :     // then our frame isize does not depend on the parent isize.
     270             :     // Note that borders never depend on the parent isize.
     271             :     // XXX All of the enumerated values except -moz-available are ok too.
     272           0 :     if (pos->ISizeDependsOnContainer(wm) ||
     273           0 :         pos->MinISizeDependsOnContainer(wm) ||
     274           0 :         pos->MaxISizeDependsOnContainer(wm) ||
     275           0 :         !IsFixedPaddingSize(padding->mPadding.GetIStart(wm)) ||
     276           0 :         !IsFixedPaddingSize(padding->mPadding.GetIEnd(wm))) {
     277           0 :       return true;
     278             :     }
     279             : 
     280             :     // See if f's position might have changed. If we're RTL then the
     281             :     // rules are slightly different. We'll assume percentage or auto
     282             :     // margins will always induce a dependency on the size
     283           0 :     if (!IsFixedMarginSize(margin->mMargin.GetIStart(wm)) ||
     284           0 :         !IsFixedMarginSize(margin->mMargin.GetIEnd(wm))) {
     285           0 :       return true;
     286             :     }
     287             :   }
     288           0 :   if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) {
     289             :     // See if f's block-size might have changed.
     290             :     // If margin-block-start/end, padding-block-start/end,
     291             :     // min-block-size, and max-block-size are all lengths or 'none',
     292             :     // and bsize is a length or bsize and bend are auto and bstart is not auto,
     293             :     // then our frame bsize does not depend on the parent bsize.
     294             :     // Note that borders never depend on the parent bsize.
     295           0 :     if ((pos->BSizeDependsOnContainer(wm) &&
     296           0 :          !(pos->BSize(wm).GetUnit() == eStyleUnit_Auto &&
     297           0 :            pos->mOffset.GetBEndUnit(wm) == eStyleUnit_Auto &&
     298           0 :            pos->mOffset.GetBStartUnit(wm) != eStyleUnit_Auto)) ||
     299           0 :         pos->MinBSizeDependsOnContainer(wm) ||
     300           0 :         pos->MaxBSizeDependsOnContainer(wm) ||
     301           0 :         !IsFixedPaddingSize(padding->mPadding.GetBStart(wm)) ||
     302           0 :         !IsFixedPaddingSize(padding->mPadding.GetBEnd(wm))) {
     303           0 :       return true;
     304             :     }
     305             : 
     306             :     // See if f's position might have changed.
     307           0 :     if (!IsFixedMarginSize(margin->mMargin.GetBStart(wm)) ||
     308           0 :         !IsFixedMarginSize(margin->mMargin.GetBEnd(wm))) {
     309           0 :       return true;
     310             :     }
     311             :   }
     312             : 
     313             :   // Since we store coordinates relative to top and left, the position
     314             :   // of a frame depends on that of its container if it is fixed relative
     315             :   // to the right or bottom, or if it is positioned using percentages
     316             :   // relative to the left or top.  Because of the dependency on the
     317             :   // sides (left and top) that we use to store coordinates, these tests
     318             :   // are easier to do using physical coordinates rather than logical.
     319           0 :   if (aCBWidthChanged) {
     320           0 :     if (!IsFixedOffset(pos->mOffset.GetLeft())) {
     321           0 :       return true;
     322             :     }
     323             :     // Note that even if 'left' is a length, our position can still
     324             :     // depend on the containing block width, because if our direction or
     325             :     // writing-mode moves from right to left (in either block or inline
     326             :     // progression) and 'right' is not 'auto', we will discard 'left'
     327             :     // and be positioned relative to the containing block right edge.
     328             :     // 'left' length and 'right' auto is the only combination we can be
     329             :     // sure of.
     330           0 :     if ((wm.GetInlineDir() == WritingMode::eInlineRTL ||
     331           0 :          wm.GetBlockDir() == WritingMode::eBlockRL) &&
     332           0 :         pos->mOffset.GetRightUnit() != eStyleUnit_Auto) {
     333           0 :       return true;
     334             :     }
     335             :   }
     336           0 :   if (aCBHeightChanged) {
     337           0 :     if (!IsFixedOffset(pos->mOffset.GetTop())) {
     338           0 :       return true;
     339             :     }
     340             :     // See comment above for width changes.
     341           0 :     if (wm.GetInlineDir() == WritingMode::eInlineBTT &&
     342           0 :         pos->mOffset.GetBottomUnit() != eStyleUnit_Auto) {
     343           0 :       return true;
     344             :     }
     345             :   }
     346             : 
     347           0 :   return false;
     348             : }
     349             : 
     350             : void
     351           2 : nsAbsoluteContainingBlock::DestroyFrames(nsIFrame* aDelegatingFrame,
     352             :                                          nsIFrame* aDestructRoot)
     353             : {
     354           2 :   mAbsoluteFrames.DestroyFramesFrom(aDestructRoot);
     355           2 : }
     356             : 
     357             : void
     358           0 : nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty()
     359             : {
     360           0 :   DoMarkFramesDirty(false);
     361           0 : }
     362             : 
     363             : void
     364           0 : nsAbsoluteContainingBlock::MarkAllFramesDirty()
     365             : {
     366           0 :   DoMarkFramesDirty(true);
     367           0 : }
     368             : 
     369             : void
     370           0 : nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty)
     371             : {
     372           0 :   for (nsIFrame* kidFrame : mAbsoluteFrames) {
     373           0 :     if (aMarkAllDirty) {
     374           0 :       kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
     375           0 :     } else if (FrameDependsOnContainer(kidFrame, true, true)) {
     376             :       // Add the weakest flags that will make sure we reflow this frame later
     377           0 :       kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
     378             :     }
     379             :   }
     380           0 : }
     381             : 
     382             : // Given an out-of-flow frame, this method returns the parent frame of its
     383             : // placeholder frame or null if it doesn't have a placeholder for some reason.
     384             : static nsContainerFrame*
     385           0 : GetPlaceholderContainer(nsIFrame* aPositionedFrame)
     386             : {
     387           0 :   nsIFrame* placeholder = aPositionedFrame->GetPlaceholderFrame();
     388           0 :   return placeholder ? placeholder->GetParent() : nullptr;
     389             : }
     390             : 
     391             : /**
     392             :  * This function returns the offset of an abs/fixed-pos child's static
     393             :  * position, with respect to the "start" corner of its alignment container,
     394             :  * according to CSS Box Alignment.  This function only operates in a single
     395             :  * axis at a time -- callers can choose which axis via the |aAbsPosCBAxis|
     396             :  * parameter.
     397             :  *
     398             :  * @param aKidReflowInput The ReflowInput for the to-be-aligned abspos child.
     399             :  * @param aKidSizeInAbsPosCBWM The child frame's size (after it's been given
     400             :  *                             the opportunity to reflow), in terms of
     401             :  *                             aAbsPosCBWM.
     402             :  * @param aAbsPosCBSize The abspos CB size, in terms of aAbsPosCBWM.
     403             :  * @param aPlaceholderContainer The parent of the child frame's corresponding
     404             :  *                              placeholder frame, cast to a nsContainerFrame.
     405             :  *                              (This will help us choose which alignment enum
     406             :  *                              we should use for the child.)
     407             :  * @param aAbsPosCBWM The child frame's containing block's WritingMode.
     408             :  * @param aAbsPosCBAxis The axis (of the containing block) that we should
     409             :  *                      be doing this computation for.
     410             :  */
     411             : static nscoord
     412           0 : OffsetToAlignedStaticPos(const ReflowInput& aKidReflowInput,
     413             :                          const LogicalSize& aKidSizeInAbsPosCBWM,
     414             :                          const LogicalSize& aAbsPosCBSize,
     415             :                          nsContainerFrame* aPlaceholderContainer,
     416             :                          WritingMode aAbsPosCBWM,
     417             :                          LogicalAxis aAbsPosCBAxis)
     418             : {
     419           0 :   if (!aPlaceholderContainer) {
     420             :     // (The placeholder container should be the thing that kicks this whole
     421             :     // process off, by setting PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN.  So it
     422             :     // should exist... but bail gracefully if it doesn't.)
     423           0 :     NS_ERROR("Missing placeholder-container when computing a "
     424             :              "CSS Box Alignment static position");
     425           0 :     return 0;
     426             :   }
     427             : 
     428             :   // (Most of this function is simply preparing args that we'll pass to
     429             :   // AlignJustifySelf at the end.)
     430             : 
     431             :   // NOTE: Our alignment container is aPlaceholderContainer's content-box
     432             :   // (or an area within it, if aPlaceholderContainer is a grid). So, we'll
     433             :   // perform most of our arithmetic/alignment in aPlaceholderContainer's
     434             :   // WritingMode. For brevity, we use the abbreviation "pc" for "placeholder
     435             :   // container" in variables below.
     436           0 :   WritingMode pcWM = aPlaceholderContainer->GetWritingMode();
     437             : 
     438             :   // Find what axis aAbsPosCBAxis corresponds to, in placeholder's parent's
     439             :   // writing-mode.
     440           0 :   LogicalAxis pcAxis = (pcWM.IsOrthogonalTo(aAbsPosCBWM)
     441           0 :                         ? GetOrthogonalAxis(aAbsPosCBAxis)
     442           0 :                         : aAbsPosCBAxis);
     443             : 
     444           0 :   LayoutFrameType parentType = aPlaceholderContainer->Type();
     445           0 :   LogicalSize alignAreaSize(pcWM);
     446           0 :   if (parentType == LayoutFrameType::FlexContainer) {
     447             :     // The alignment container is the flex container's content box:
     448           0 :     alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM);
     449             :     LogicalMargin pcBorderPadding =
     450           0 :       aPlaceholderContainer->GetLogicalUsedBorderAndPadding(pcWM);
     451           0 :     alignAreaSize -= pcBorderPadding.Size(pcWM);
     452           0 :   } else if (parentType == LayoutFrameType::GridContainer) {
     453             :     // This abspos elem's parent is a grid container. Per CSS Grid 10.1 & 10.2:
     454             :     //  - If the grid container *also* generates the abspos containing block (a
     455             :     // grid area) for this abspos child, we use that abspos containing block as
     456             :     // the alignment container, too. (And its size is aAbsPosCBSize.)
     457             :     //  - Otherwise, we use the grid's padding box as the alignment container.
     458             :     // https://drafts.csswg.org/css-grid/#static-position
     459           0 :     if (aPlaceholderContainer == aKidReflowInput.mCBReflowInput->mFrame) {
     460             :       // The alignment container is the grid area that we're using as the
     461             :       // absolute containing block.
     462           0 :       alignAreaSize = aAbsPosCBSize.ConvertTo(pcWM, aAbsPosCBWM);
     463             :     } else {
     464             :       // The alignment container is a the grid container's padding box (which
     465             :       // we can get by subtracting away its border from frame's size):
     466           0 :       alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM);
     467             :       LogicalMargin pcBorder =
     468           0 :         aPlaceholderContainer->GetLogicalUsedBorder(pcWM);
     469           0 :       alignAreaSize -= pcBorder.Size(pcWM);
     470             :     }
     471             :   } else {
     472           0 :     NS_ERROR("Unsupported container for abpsos CSS Box Alignment");
     473           0 :     return 0; // (leave the child at the start of its alignment container)
     474             :   }
     475             : 
     476             :   nscoord alignAreaSizeInAxis = (pcAxis == eLogicalAxisInline)
     477           0 :     ? alignAreaSize.ISize(pcWM)
     478           0 :     : alignAreaSize.BSize(pcWM);
     479             : 
     480           0 :   AlignJustifyFlags flags = AlignJustifyFlags::eIgnoreAutoMargins;
     481             :   uint16_t alignConst =
     482           0 :     aPlaceholderContainer->CSSAlignmentForAbsPosChild(aKidReflowInput, pcAxis);
     483             :   // XXXdholbert: Handle <overflow-position> in bug 1311892 (by conditionally
     484             :   // setting AlignJustifyFlags::eOverflowSafe in |flags|.)  For now, we behave
     485             :   // as if "unsafe" was the specified value (which is basically equivalent to
     486             :   // the default behavior, when no value is specified -- though the default
     487             :   // behavior also has some [at-risk] extra nuance about scroll containers...)
     488             :   // For now we ignore & strip off <overflow-position> bits, until bug 1311892.
     489           0 :   alignConst &= ~NS_STYLE_ALIGN_FLAG_BITS;
     490             : 
     491             :   // Find out if placeholder-container & the OOF child have the same start-sides
     492             :   // in the placeholder-container's pcAxis.
     493           0 :   WritingMode kidWM = aKidReflowInput.GetWritingMode();
     494           0 :   if (pcWM.ParallelAxisStartsOnSameSide(pcAxis, kidWM)) {
     495           0 :     flags |= AlignJustifyFlags::eSameSide;
     496             :   }
     497             : 
     498             :   // (baselineAdjust is unused. CSSAlignmentForAbsPosChild() should've
     499             :   // converted 'baseline'/'last baseline' enums to their fallback values.)
     500           0 :   const nscoord baselineAdjust = nscoord(0);
     501             : 
     502             :   // AlignJustifySelf operates in the kid's writing mode, so we need to
     503             :   // represent the child's size and the desired axis in that writing mode:
     504             :   LogicalSize kidSizeInOwnWM = aKidSizeInAbsPosCBWM.ConvertTo(kidWM,
     505           0 :                                                               aAbsPosCBWM);
     506           0 :   LogicalAxis kidAxis = (kidWM.IsOrthogonalTo(aAbsPosCBWM)
     507           0 :                          ? GetOrthogonalAxis(aAbsPosCBAxis)
     508           0 :                          : aAbsPosCBAxis);
     509             : 
     510             :   nscoord offset =
     511           0 :     CSSAlignUtils::AlignJustifySelf(alignConst, kidAxis, flags,
     512             :                                     baselineAdjust, alignAreaSizeInAxis,
     513           0 :                                     aKidReflowInput, kidSizeInOwnWM);
     514             : 
     515             :   // "offset" is in terms of the CSS Box Alignment container (i.e. it's in
     516             :   // terms of pcWM). But our return value needs to in terms of the containing
     517             :   // block's writing mode, which might have the opposite directionality in the
     518             :   // given axis. In that case, we just need to negate "offset" when returning,
     519             :   // to make it have the right effect as an offset for coordinates in the
     520             :   // containing block's writing mode.
     521           0 :   if (!pcWM.ParallelAxisStartsOnSameSide(pcAxis, aAbsPosCBWM)) {
     522           0 :     return -offset;
     523             :   }
     524           0 :   return offset;
     525             : }
     526             : 
     527             : void
     528          77 : nsAbsoluteContainingBlock::ResolveSizeDependentOffsets(
     529             :   nsPresContext* aPresContext,
     530             :   ReflowInput& aKidReflowInput,
     531             :   const LogicalSize& aKidSize,
     532             :   const LogicalMargin& aMargin,
     533             :   LogicalMargin* aOffsets,
     534             :   LogicalSize* aLogicalCBSize)
     535             : {
     536          77 :   WritingMode wm = aKidReflowInput.GetWritingMode();
     537          77 :   WritingMode outerWM = aKidReflowInput.mParentReflowInput->GetWritingMode();
     538             : 
     539             :   // Now that we know the child's size, we resolve any sentinel values in its
     540             :   // IStart/BStart offset coordinates that depend on that size.
     541             :   //  * NS_AUTOOFFSET indicates that the child's position in the given axis
     542             :   // is determined by its end-wards offset property, combined with its size and
     543             :   // available space. e.g.: "top: auto; height: auto; bottom: 50px"
     544             :   //  * m{I,B}OffsetsResolvedAfterSize indicate that the child is using its
     545             :   // static position in that axis, *and* its static position is determined by
     546             :   // the axis-appropriate css-align property (which may require the child's
     547             :   // size, e.g. to center it within the parent).
     548         231 :   if ((NS_AUTOOFFSET == aOffsets->IStart(outerWM)) ||
     549         154 :       (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) ||
     550         231 :       aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign ||
     551          77 :       aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
     552           0 :     if (-1 == aLogicalCBSize->ISize(wm)) {
     553             :       // Get the containing block width/height
     554           0 :       const ReflowInput* parentRI = aKidReflowInput.mParentReflowInput;
     555             :       *aLogicalCBSize =
     556             :         aKidReflowInput.ComputeContainingBlockRectangle(aPresContext,
     557           0 :                                                         parentRI);
     558             :     }
     559             : 
     560             :     const LogicalSize logicalCBSizeOuterWM = aLogicalCBSize->ConvertTo(outerWM,
     561           0 :                                                                        wm);
     562             : 
     563             :     // placeholderContainer is used in each of the m{I,B}OffsetsNeedCSSAlign
     564             :     // clauses. We declare it at this scope so we can avoid having to look
     565             :     // it up twice (and only look it up if it's needed).
     566           0 :     nsContainerFrame* placeholderContainer = nullptr;
     567             : 
     568           0 :     if (NS_AUTOOFFSET == aOffsets->IStart(outerWM)) {
     569           0 :       NS_ASSERTION(NS_AUTOOFFSET != aOffsets->IEnd(outerWM),
     570             :                    "Can't solve for both start and end");
     571           0 :       aOffsets->IStart(outerWM) =
     572           0 :         logicalCBSizeOuterWM.ISize(outerWM) -
     573           0 :         aOffsets->IEnd(outerWM) - aMargin.IStartEnd(outerWM) -
     574           0 :         aKidSize.ISize(outerWM);
     575           0 :     } else if (aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign) {
     576           0 :       placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame);
     577             :       nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize,
     578             :                                                 logicalCBSizeOuterWM,
     579             :                                                 placeholderContainer,
     580           0 :                                                 outerWM, eLogicalAxisInline);
     581             :       // Shift IStart from its current position (at start corner of the
     582             :       // alignment container) by the returned offset.  And set IEnd to the
     583             :       // distance between the kid's end edge to containing block's end edge.
     584           0 :       aOffsets->IStart(outerWM) += offset;
     585           0 :       aOffsets->IEnd(outerWM) =
     586           0 :         logicalCBSizeOuterWM.ISize(outerWM) -
     587           0 :         (aOffsets->IStart(outerWM) + aKidSize.ISize(outerWM));
     588             :     }
     589             : 
     590           0 :     if (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) {
     591           0 :       aOffsets->BStart(outerWM) =
     592           0 :         logicalCBSizeOuterWM.BSize(outerWM) -
     593           0 :         aOffsets->BEnd(outerWM) - aMargin.BStartEnd(outerWM) -
     594           0 :         aKidSize.BSize(outerWM);
     595           0 :     } else if (aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
     596           0 :       if (!placeholderContainer) {
     597           0 :         placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame);
     598             :       }
     599             :       nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize,
     600             :                                                 logicalCBSizeOuterWM,
     601             :                                                 placeholderContainer,
     602           0 :                                                 outerWM, eLogicalAxisBlock);
     603             :       // Shift BStart from its current position (at start corner of the
     604             :       // alignment container) by the returned offset.  And set BEnd to the
     605             :       // distance between the kid's end edge to containing block's end edge.
     606           0 :       aOffsets->BStart(outerWM) += offset;
     607           0 :       aOffsets->BEnd(outerWM) =
     608           0 :         logicalCBSizeOuterWM.BSize(outerWM) -
     609           0 :         (aOffsets->BStart(outerWM) + aKidSize.BSize(outerWM));
     610             :     }
     611           0 :     aKidReflowInput.SetComputedLogicalOffsets(aOffsets->ConvertTo(wm, outerWM));
     612             :   }
     613          77 : }
     614             : 
     615             : // XXX Optimize the case where it's a resize reflow and the absolutely
     616             : // positioned child has the exact same size and position and skip the
     617             : // reflow...
     618             : 
     619             : // When bug 154892 is checked in, make sure that when
     620             : // mChildListID == kFixedList, the height is unconstrained.
     621             : // since we don't allow replicated frames to split.
     622             : 
     623             : void
     624          77 : nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame*                aDelegatingFrame,
     625             :                                                nsPresContext*           aPresContext,
     626             :                                                const ReflowInput& aReflowInput,
     627             :                                                const nsRect&            aContainingBlock,
     628             :                                                AbsPosReflowFlags        aFlags,
     629             :                                                nsIFrame*                aKidFrame,
     630             :                                                nsReflowStatus&          aStatus,
     631             :                                                nsOverflowAreas*         aOverflowAreas)
     632             : {
     633             : #ifdef DEBUG
     634          77 :   if (nsBlockFrame::gNoisyReflow) {
     635           0 :     nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent);
     636           0 :     printf("abs pos ");
     637           0 :     nsAutoString name;
     638           0 :     aKidFrame->GetFrameName(name);
     639           0 :     printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
     640             : 
     641             :     char width[16];
     642             :     char height[16];
     643           0 :     PrettyUC(aReflowInput.AvailableWidth(), width, 16);
     644           0 :     PrettyUC(aReflowInput.AvailableHeight(), height, 16);
     645           0 :     printf(" a=%s,%s ", width, height);
     646           0 :     PrettyUC(aReflowInput.ComputedWidth(), width, 16);
     647           0 :     PrettyUC(aReflowInput.ComputedHeight(), height, 16);
     648           0 :     printf("c=%s,%s \n", width, height);
     649             :   }
     650         154 :   AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
     651             : #endif // DEBUG
     652             : 
     653          77 :   WritingMode wm = aKidFrame->GetWritingMode();
     654          77 :   LogicalSize logicalCBSize(wm, aContainingBlock.Size());
     655          77 :   nscoord availISize = logicalCBSize.ISize(wm);
     656          77 :   if (availISize == -1) {
     657           0 :     NS_ASSERTION(aReflowInput.ComputedSize(wm).ISize(wm) !=
     658             :                    NS_UNCONSTRAINEDSIZE,
     659             :                  "Must have a useful inline-size _somewhere_");
     660           0 :     availISize =
     661           0 :       aReflowInput.ComputedSizeWithPadding(wm).ISize(wm);
     662             :   }
     663             : 
     664          77 :   uint32_t rsFlags = 0;
     665          77 :   if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) {
     666             :     // When a grid container generates the abs.pos. CB for a *child* then
     667             :     // the static position is determined via CSS Box Alignment within the
     668             :     // abs.pos. CB (a grid area, i.e. a piece of the grid). In this scenario,
     669             :     // due to the multiple coordinate spaces in play, we use a convenience flag
     670             :     // to simply have the child's ReflowInput give it a static position at its
     671             :     // abs.pos. CB origin, and then we'll align & offset it from there.
     672           0 :     nsIFrame* placeholder = aKidFrame->GetPlaceholderFrame();
     673           0 :     if (placeholder && placeholder->GetParent() == aDelegatingFrame) {
     674           0 :       rsFlags |= ReflowInput::STATIC_POS_IS_CB_ORIGIN;
     675             :     }
     676             :   }
     677             :   ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame,
     678         154 :                                    LogicalSize(wm, availISize,
     679             :                                                NS_UNCONSTRAINEDSIZE),
     680          77 :                                    &logicalCBSize, rsFlags);
     681             : 
     682             :   // Get the border values
     683          77 :   WritingMode outerWM = aReflowInput.GetWritingMode();
     684             :   const LogicalMargin border(outerWM,
     685          77 :                              aReflowInput.mStyleBorder->GetComputedBorder());
     686             :   LogicalMargin margin =
     687          77 :     kidReflowInput.ComputedLogicalMargin().ConvertTo(outerWM, wm);
     688             : 
     689             :   // If we're doing CSS Box Alignment in either axis, that will apply the
     690             :   // margin for us in that axis (since the thing that's aligned is the margin
     691             :   // box).  So, we clear out the margin here to avoid applying it twice.
     692          77 :   if (kidReflowInput.mFlags.mIOffsetsNeedCSSAlign) {
     693           0 :     margin.IStart(outerWM) = margin.IEnd(outerWM) = 0;
     694             :   }
     695          77 :   if (kidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
     696           0 :     margin.BStart(outerWM) = margin.BEnd(outerWM) = 0;
     697             :   }
     698             : 
     699          77 :   bool constrainBSize = (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)
     700         231 :     && (aFlags & AbsPosReflowFlags::eConstrainHeight)
     701             :        // Don't split if told not to (e.g. for fixed frames)
     702           0 :     && !aDelegatingFrame->IsInlineFrame()
     703             :        //XXX we don't handle splitting frames for inline absolute containing blocks yet
     704         231 :     && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <=
     705          77 :         aReflowInput.AvailableBSize());
     706             :        // Don't split things below the fold. (Ideally we shouldn't *have*
     707             :        // anything totally below the fold, but we can't position frames
     708             :        // across next-in-flow breaks yet.
     709          77 :   if (constrainBSize) {
     710           0 :     kidReflowInput.AvailableBSize() =
     711           0 :       aReflowInput.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) -
     712           0 :       kidReflowInput.ComputedLogicalMargin().BStart(wm);
     713           0 :     if (NS_AUTOOFFSET != kidReflowInput.ComputedLogicalOffsets().BStart(wm)) {
     714           0 :       kidReflowInput.AvailableBSize() -=
     715           0 :         kidReflowInput.ComputedLogicalOffsets().BStart(wm);
     716             :     }
     717             :   }
     718             : 
     719             :   // Do the reflow
     720         154 :   ReflowOutput kidDesiredSize(kidReflowInput);
     721          77 :   aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowInput, aStatus);
     722             : 
     723          77 :   const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm);
     724             : 
     725             :   LogicalMargin offsets =
     726          77 :     kidReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm);
     727             : 
     728             :   // If we're solving for start in either inline or block direction,
     729             :   // then compute it now that we know the dimensions.
     730             :   ResolveSizeDependentOffsets(aPresContext, kidReflowInput, kidSize, margin,
     731          77 :                               &offsets, &logicalCBSize);
     732             : 
     733             :   // Position the child relative to our padding edge
     734             :   LogicalRect rect(outerWM,
     735          77 :                    border.IStart(outerWM) + offsets.IStart(outerWM) +
     736          77 :                      margin.IStart(outerWM),
     737          77 :                    border.BStart(outerWM) + offsets.BStart(outerWM) +
     738          77 :                      margin.BStart(outerWM),
     739         308 :                    kidSize.ISize(outerWM), kidSize.BSize(outerWM));
     740             :   nsRect r =
     741         154 :     rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) +
     742         308 :                          border.Size(outerWM).GetPhysicalSize(outerWM));
     743             : 
     744             :   // Offset the frame rect by the given origin of the absolute containing block.
     745             :   // If the frame is auto-positioned on both sides of an axis, it will be
     746             :   // positioned based on its containing block and we don't need to offset
     747             :   // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)).
     748          77 :   if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
     749           0 :     const nsStyleSides& offsets = kidReflowInput.mStylePosition->mOffset;
     750           0 :     if (!(offsets.GetLeftUnit() == eStyleUnit_Auto &&
     751           0 :           offsets.GetRightUnit() == eStyleUnit_Auto) ||
     752           0 :         (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
     753           0 :       r.x += aContainingBlock.x;
     754             :     }
     755           0 :     if (!(offsets.GetTopUnit() == eStyleUnit_Auto &&
     756           0 :           offsets.GetBottomUnit() == eStyleUnit_Auto) ||
     757           0 :         (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
     758           0 :       r.y += aContainingBlock.y;
     759             :     }
     760             :   }
     761             : 
     762          77 :   aKidFrame->SetRect(r);
     763             : 
     764          77 :   nsView* view = aKidFrame->GetView();
     765          77 :   if (view) {
     766             :     // Size and position the view and set its opacity, visibility, content
     767             :     // transparency, and clip
     768             :     nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
     769           0 :                                                kidDesiredSize.VisualOverflow());
     770             :   } else {
     771          77 :     nsContainerFrame::PositionChildViews(aKidFrame);
     772             :   }
     773             : 
     774             :   aKidFrame->DidReflow(aPresContext, &kidReflowInput,
     775          77 :                        nsDidReflowStatus::FINISHED);
     776             : 
     777             : #ifdef DEBUG
     778          77 :   if (nsBlockFrame::gNoisyReflow) {
     779           0 :     nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1);
     780           0 :     printf("abs pos ");
     781           0 :     nsAutoString name;
     782           0 :     aKidFrame->GetFrameName(name);
     783           0 :     printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
     784           0 :     printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame),
     785           0 :            r.x, r.y, r.width, r.height);
     786             :   }
     787             : #endif
     788             : 
     789          77 :   if (aOverflowAreas) {
     790          77 :     aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft());
     791             :   }
     792          77 : }

Generated by: LCOV version 1.13