mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
b3541682d0
- bug 1157569 part 1 - Rename BC_BORDER_{TOP,RIGHT,BOTTOM,LEFT}_HALF* to BC_BORDER_{START,END}_HALF*. r=roc (2ef82ce76)
- Bug 1157569 part 2 - Convert output parameters of GetColorAndStyle/GetPaintStyleInfo in nsTableFrame to use pointers. r=roc (c58bbe734)
- Bug 1157569 part 3 - Merge two GetColorAndStyle functions in nsTableFrame. r=roc (009df4d97)
- Bug 1157569 part 4 - Replace mTableIsLTR with mTableWM in BCMapCellInfo & BCPaintBorderIterator. r=roc (9784a46eb)
- Bug 1157569 part 5 - Convert GetColorAndStyle, GetPaintStyleInfo, and CompareBorders in nsTableFrame to accept writing mode and logical side. r=roc (aaddc4a80)
- Bug 1157569 part 6 - Rename methods and fields in BCMapCellInfo from physicals to logicals. r=roc (aeaa0b18c)
- Bug 1157569 part 7 - Rename methods and fields in nsTableColFrame from physicals to logicals. r=roc (fd4e1e667)
- Bug 1157569 part 8 - Move some code in BCPaintBorderIterator::SetDamageArea for less computation. r=roc (634f19ca7)
- missing bit of Bug 895096 - Part 1 (a96e3d35c)
- Bug 1157569 part 9 - Rename methods and fields in nsTableRowFrame from physicals to logicals. r=roc (d097f1e4f)
- Bug 1157569 part 10 - Add operator+= for LogicalMargin. r=jfkthame (c651b685f)
- Bug 1159101 part 4 - Override GetWritingMode() in nsTable{{Col,Row}{,Group},Cell}Frame. r=roc (34d90d10b)
- Bug 1159101 part 5 - Initialize writing mode of desired mode of table cell frame from its table frame on a CLOSED TREE. r=roc (8bc6978ca)
- Bug 1157569 part 11 - Rename methods and fields in nsTableCellFrame from physicals to logicals. r=roc (bcfd4514b)
- Bug 1157569 part 12 - Remove useless m{Start,End}Side from BCMapCellInfo. r=roc (1df59d670)
- Bug 1159127 - Always redirect inserting col group frame to first-in-flow table frame. r=mats (2e41bbeeb)
- Bug 1167765: Misc whitespace cleanup in nsTableFrame.cpp. whitespace/rewrapping-only, no review, DONTBUILD (f256939c9)
- Bug 1167696 part 1: Remove nsTableReflowState's unused constructor & nsPresContext arg. r=mats (5b8beadb9)
- Bug 1167696 part 2: Remove nsTableReflowState's frame constructor-arg; get it from passed-in reflow state instead. r=mats (205dad74f)
- Bug 1155412: Use mozilla::Maybe instead of hardcoded placement-new, for reflow state created in nsTableOuterFrame::OuterBeginReflowChild() and used by its caller. r=dbaron (ebf9e9891)
- Bug 1147834: Use abstract coordinates in nsHTMLReflowState, r=jfkthame (30e644a14)
- Bug 1169432 part 2: Refactor nsTableFrame::Init. r=mats (779928597)
- Bug 1169432 part 3: Use nsTArray::Contains instead of nsTArray::IndexOf(), for brevity, in nsTableFrame. r=mats (aa6aee3c4)
- Bug 1169432 part 4: Convert NS_ASSERTION(false, ...) to NS_ERROR(...) in nsTableFrame. r=mats (9cbb2f53f)
- Bug 1169432 part 5: Use range-based 'for' loops when walking frame lists, in nsTableFrame. r=mats (bd4e7d0d4)
- Bug 1171328 - Convert nsTableFrame::GetChildAreaOffset() and its friends to use LogicalMargin. r=dholbert (196955d0c)
- Remove unneeded code from SmoothScrollAnimation. (bug 1139220 part 4, r=kats) (68c6af9f8)
- Use stronger typing for GetScrollWheelDelta. (bug 1139220 part 5, r=kats) (4669d1f38)
- When APZ is enabled, only apply the root scroll delta override to root frames. (bug 1155800, r=kats) (2a738ee82)
- Bug 1168629. Clear frame metrics on the root layer if we aren't setting new frame metrics because the layer may be recycled and still have frame metrics from last time. r=mstange (dc0889fae)
- Bug 1158424 - Rename FrameMetrics::mIsRoot to mIsRootContent. r=kats (0005fb016)
- Bug 1148078: Replace the word "Logical" with "Flex-Relative" in some vars & functions in flexbox layout. r=mats (601497c55)
- Bug 1148298 part 1: Convert most flexbox-layout IsAxisHorizontal() calls into IsMainAxisHorizontal()/IsCrossAxisHorizontal(). r=mats (ff2aaa5b3)
- Bug 1148298 part 3: Make flexbox helper GetBaselineOffsetFromOuterCrossEdge take a FlexboxAxisTracker instead of an explicit axis. r=mats (009dd9f7e)
- Bug 1148298 part 4: Convert flexbox helper-function IsAxisHorizontal() to be a private implementation detail of FlexboxAxisTracker. r=mats (901f830d6)
- Bug 1148298 part 2: Replace flexbox static helper-function GetSizeProBug 1148298 part 2: Replace flexbox static helper-function GetSizePro (3d2940f88)
- Bug 1158290 part 1: Use AvailableBSize instead of AvailableHeight, throughout flex layout. r=mats (49e2075f8)
- Bug 1148294 part 1: Determine flex axes (in FlexboxAxisTracker) using the flex container's writing mode. r=mats (67f12cf58)
- Bug 1148294 part 2: Add one reftest for writing-mode & direction properties' influence on flex axes. (85e0ede9d)
- Bug 1148294 part 3: Add more reftests for writing-mode & direction properties' influence on flex axes (as modified copies of first test). (fac965a24)
- Bug 1148294 followup: fix spec links in reftests flexbox-writing-mode-* to point to TR instead of ED spec version. (no review) (84333c728)
- Bug 1158290 part 2: Use logical (not physical) skipsides & borderpadding when chipping away from available BSize. r=mats (987b4edfa)
- Bug 1152913 part 1: Make FlexboxAxisTracker store the WritingMode & representation of how flex axes map to it. r=mats (675737981)
- Bug 1152951: Delete the copy-constructor and reassignment operator for FlexboxAxisTracker & PositionTracker state-management classes. r=mats (71c88e7e9)
- Bug 1152913 part 2: Convert flexbox 'IsAxisHorizontal' checks to use new logical-axis member data. r=mats (122b434a5)
- Bug 1158290 part 3: Convert GenerateFlexLines to use logical axes & sizes. r=mats (367cb1c01)
- Bug 1158290 part 4: Clarify documentation & naming of ClampFlexContainerMainSize (now called ResolveFlexContainerMainSize). r=mats (5af11ff23)
- Bug 1149383 part 1: Make flexbox writing-mode tests use different widths vs. heights to reveal bug with vertical writing-modes. (test-only) (c4782feaa)
- Bug 1149383 part 2: Reorder width/height decls in flexbox-writing-mode reftests for consistency. (test-only) (bba907b70)
- Bug 1158290 part 5: Use logical coords & axes in methods that computeBug 1158290 part 5: Use logical coords & axes in methods that compute (b78726528)
- Bug 1158290 part 6: Use new GET_[MAIN|CROSS]_COMPONENT_LOGICAL macros, to reduce explicit width/height usage. r=mats (25d2e20a3)
- Bug 1152913 part 3: Make flexbox's PositionTracker classes track whether axis is reversed, & directly convert flex item final sizes/positions to logical coords. r=mats (1feee7e6d)
- Bug 1174507 - Replace the frame flag CONTAINS_RELATIVE_HEIGHT with CONTAINS_RELATIVE_BSIZE, and adjust callsites appropriately. r=dholbert (0e981d5ac)
- Clip async scrollframes based on the displaylist clip, not the composition bounds. (bug 1148582 part 4, r=tn) (f2b1196ed)
- Bug 1148582 - Add mask layers to FrameMetrics for ancestor scroll frame clips. r=mattwoodrow (008239305)
- Bug 1148582 - Factor mask layer creation out of ContainerState::SetupMaskLayer. r=mstange (1d739614e)
- piece of Bug 1138442 (e98de18dd)
- Bug 1173307 - Convert nsTableCellFrame to work with logical coordinates. r=dholbert (81d0aa811)
- Bug 1174700 - patch 1 - Convert nsTableRowFrame and nsTableRowGroupFrame to work with logical coordinates. r=dholbert (09ee9c814)
- Bug 1174700 - patch 2 - Convert nsTableFrame::SetColumnDimensions to work with logical-order iteration over the table, so that column coordinates and collapsed borders are correct. r=dholbert (7957e377b)
- Bug 1129040 - Provide a way for content processes to query the chrome side blocklist service. r=billm (5ea750b8b)
- Bug 1174711 - patch 1 - Rename nsIPercentHeightObserver to nsIPercentBSizeObserver, and update related frame methods to match. r=dholbert (7a17fbe4f)
- Bug 1174711 - patch 2 - Convert nsTableFrame to work with logical coordinates. r=dholbert (329776abe)
- Bug 1174711 - patch 3 - Rename a couple more frame-state bits from physical to logical. r=dholbert (d61c9624d)
- Bug 1174711 - patch 4 - Rename mSpecialHeightReflow to mSpecialBSizeReflow, and update comments to match. r=dholbert (49969e512)
- Bug 1174711 patch 5 - Copy inline-size rather than width from prev-in-flow when initializing nsTableFrame. r=dholbert (7afb6c80a)
- pointer style (52cd6f9af)
- pointer style (ba99b3f74)
- Bug 1149797 - Use the loop's *static* block object when freshening a loop's block object, then copy in values from the old cloned block. Using the old cloned block directly isn't valid when the cloned block might be extended with additional variables created by eval or added by nested function statements. r=shu (deba22790)
- Bug 1165486 - Cleanup: use standard object allocation functions when allocating scope objects. (r=terrence) (b35a4d7aa)
- pointer style (74ea6d085)
- Bug 1165486 - Add StaticNonSyntacticScopeObjects and teach scope iterators about it. (r=luke) (18a526ed3)
- Bug 1144371: Implement DEBUG-only JS shell function |dumpStringRepresentation|. r=jandem (35524cf42)
- Bug 1148963 - OdinMonkey: add CompileOptions::lazyParsingDisabled and testing function setDiscardSource (r=bz) (53f9c8fe6)
- pointer style (edaa05d69)
- Bug 1165486 - Rename hasPollutedGlobalScope to hasNonSyntacticScope. (r=luke) (505fc8d05)
- pointer style (3c4435038)
- Bug 1165486 - Remove PollutedGlobalScopeOption in favor of using the static scope chain to detect non-syntactic scopes. (r=luke) (73ca126f5)
- Bug 1150106 - Async loadSubscript - Part 1, add `async` to loadSubscriptWithOptions r=bholley,mcrr8 (a0ffeed0d)
- Bug 1150106 - Add an async version of loadSubscript - Part 2, tests r=bholley (6aeaa93e4)
- Bug 1165486 - Use JS::CompileForNonSyntacticScope in Gecko where we used to set polluted global scope. (r=bz, rs=bholley) (073332ac9)
- Bug 1165486 - Split JS::Compile into JS::Compile and JS::CompileForNonSyntacticScope. (r=luke) (0b82c73c4)
- Bug 1165486 - Restructure function and script cloning in light of PollutingGlobal scope changes. (r=Waldo) (b4e39652c)
- Bug 1141865 - Part 7: Make new.target work in generator functions. (r=jorendorff, r=jandem) (a9f5ce1ad)
- Bug 1165486 - Detect with scopes at parse time using the static scope chain for non-function scripts. Also cache static scope properties on SharedGlobalContext. (r=efaust) (a7d4283cb)
- Bug 1176070 - Optimize nsTableFrame's FirstInFlow() lookups from calls to GetColumnISize(). r=dholbert (beed47b93)
- fix misspatch of Bug 1141862 - Part 6 (d091f70a2)
- Bug 1169736 - Temporarily disallow eval and arrow functions inside derived class constructors. (r=jorendorff) (6cf8a2db1)
- Bug 1150855 - Disallow method syntax without curly brackets. r=efaust (26086ee95)
- part of Bug 1150855 - Remove uses of the curly syntax. (bfe86ea19)
- Bug 1169853 - Require semicolon after export default AssignmentExpression. r=shu (580ff7e30)
- Bug 1174009 - Remove a couple of unused AutoRooter types; r=sfink (113aee16a)
1154 lines
45 KiB
C++
1154 lines
45 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* rendering object for css3 multi-column layout */
|
|
|
|
#include "nsColumnSetFrame.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsDisplayList.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::layout;
|
|
|
|
/**
|
|
* Tracking issues:
|
|
*
|
|
* XXX cursor movement around the top and bottom of colums seems to make the editor
|
|
* lose the caret.
|
|
*
|
|
* XXX should we support CSS columns applied to table elements?
|
|
*/
|
|
nsContainerFrame*
|
|
NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags)
|
|
{
|
|
nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame(aContext);
|
|
it->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT);
|
|
return it;
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetFrame)
|
|
|
|
nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext)
|
|
: nsContainerFrame(aContext), mLastBalanceBSize(NS_INTRINSICSIZE),
|
|
mLastFrameStatus(NS_FRAME_COMPLETE)
|
|
{
|
|
}
|
|
|
|
nsIAtom*
|
|
nsColumnSetFrame::GetType() const
|
|
{
|
|
return nsGkAtoms::columnSetFrame;
|
|
}
|
|
|
|
static void
|
|
PaintColumnRule(nsIFrame* aFrame, nsRenderingContext* aCtx,
|
|
const nsRect& aDirtyRect, nsPoint aPt)
|
|
{
|
|
static_cast<nsColumnSetFrame*>(aFrame)->PaintColumnRule(aCtx, aDirtyRect, aPt);
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::PaintColumnRule(nsRenderingContext* aCtx,
|
|
const nsRect& aDirtyRect,
|
|
const nsPoint& aPt)
|
|
{
|
|
nsIFrame* child = mFrames.FirstChild();
|
|
if (!child)
|
|
return; // no columns
|
|
|
|
nsIFrame* nextSibling = child->GetNextSibling();
|
|
if (!nextSibling)
|
|
return; // 1 column only - this means no gap to draw on
|
|
|
|
WritingMode wm = GetWritingMode();
|
|
bool isVertical = wm.IsVertical();
|
|
bool isRTL = !wm.IsBidiLTR();
|
|
const nsStyleColumn* colStyle = StyleColumn();
|
|
|
|
uint8_t ruleStyle;
|
|
// Per spec, inset => ridge and outset => groove
|
|
if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET)
|
|
ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE;
|
|
else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET)
|
|
ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE;
|
|
else
|
|
ruleStyle = colStyle->mColumnRuleStyle;
|
|
|
|
nsPresContext* presContext = PresContext();
|
|
nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
|
|
if (!ruleWidth)
|
|
return;
|
|
|
|
nscolor ruleColor =
|
|
GetVisitedDependentColor(eCSSProperty_column_rule_color);
|
|
|
|
// In order to re-use a large amount of code, we treat the column rule as a border.
|
|
// We create a new border style object and fill in all the details of the column rule as
|
|
// the left border. PaintBorder() does all the rendering for us, so we not
|
|
// only save an enormous amount of code but we'll support all the line styles that
|
|
// we support on borders!
|
|
nsStyleBorder border(presContext);
|
|
Sides skipSides;
|
|
if (isVertical) {
|
|
border.SetBorderWidth(NS_SIDE_TOP, ruleWidth);
|
|
border.SetBorderStyle(NS_SIDE_TOP, ruleStyle);
|
|
border.SetBorderColor(NS_SIDE_TOP, ruleColor);
|
|
skipSides |= mozilla::eSideBitsLeftRight;
|
|
skipSides |= mozilla::eSideBitsBottom;
|
|
} else {
|
|
border.SetBorderWidth(NS_SIDE_LEFT, ruleWidth);
|
|
border.SetBorderStyle(NS_SIDE_LEFT, ruleStyle);
|
|
border.SetBorderColor(NS_SIDE_LEFT, ruleColor);
|
|
skipSides |= mozilla::eSideBitsTopBottom;
|
|
skipSides |= mozilla::eSideBitsRight;
|
|
}
|
|
|
|
// Get our content rect as an absolute coordinate, not relative to
|
|
// our parent (which is what the X and Y normally is)
|
|
nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt;
|
|
nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth)
|
|
: nsSize(ruleWidth, contentRect.height);
|
|
|
|
while (nextSibling) {
|
|
// The frame tree goes RTL in RTL.
|
|
// The |prevFrame| and |nextFrame| frames here are the visually preceding
|
|
// (left/above) and following (right/below) frames, not in logical writing-
|
|
// mode direction.
|
|
nsIFrame* prevFrame = isRTL ? nextSibling : child;
|
|
nsIFrame* nextFrame = isRTL ? child : nextSibling;
|
|
|
|
// Each child frame's position coordinates is actually relative to this
|
|
// nsColumnSetFrame.
|
|
// linePt will be at the top-left edge to paint the line.
|
|
nsPoint linePt;
|
|
if (isVertical) {
|
|
nscoord edgeOfPrev = prevFrame->GetRect().YMost() + aPt.y;
|
|
nscoord edgeOfNext = nextFrame->GetRect().Y() + aPt.y;
|
|
linePt = nsPoint(contentRect.x,
|
|
(edgeOfPrev + edgeOfNext - ruleSize.height) / 2);
|
|
} else {
|
|
nscoord edgeOfPrev = prevFrame->GetRect().XMost() + aPt.x;
|
|
nscoord edgeOfNext = nextFrame->GetRect().X() + aPt.x;
|
|
linePt = nsPoint((edgeOfPrev + edgeOfNext - ruleSize.width) / 2,
|
|
contentRect.y);
|
|
}
|
|
|
|
nsRect lineRect(linePt, ruleSize);
|
|
nsCSSRendering::PaintBorderWithStyleBorder(presContext, *aCtx, this,
|
|
aDirtyRect, lineRect, border, StyleContext(),
|
|
skipSides);
|
|
|
|
child = nextSibling;
|
|
nextSibling = nextSibling->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
static nscoord
|
|
GetAvailableContentISize(const nsHTMLReflowState& aReflowState)
|
|
{
|
|
if (aReflowState.AvailableISize() == NS_INTRINSICSIZE) {
|
|
return NS_INTRINSICSIZE;
|
|
}
|
|
|
|
WritingMode wm = aReflowState.GetWritingMode();
|
|
nscoord borderPaddingISize =
|
|
aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm);
|
|
return std::max(0, aReflowState.AvailableISize() - borderPaddingISize);
|
|
}
|
|
|
|
nscoord
|
|
nsColumnSetFrame::GetAvailableContentBSize(const nsHTMLReflowState& aReflowState)
|
|
{
|
|
if (aReflowState.AvailableBSize() == NS_INTRINSICSIZE) {
|
|
return NS_INTRINSICSIZE;
|
|
}
|
|
|
|
WritingMode wm = aReflowState.GetWritingMode();
|
|
LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding();
|
|
bp.ApplySkipSides(GetLogicalSkipSides(&aReflowState));
|
|
bp.BEnd(wm) = aReflowState.ComputedLogicalBorderPadding().BEnd(wm);
|
|
return std::max(0, aReflowState.AvailableBSize() - bp.BStartEnd(wm));
|
|
}
|
|
|
|
static nscoord
|
|
GetColumnGap(nsColumnSetFrame* aFrame,
|
|
const nsStyleColumn* aColStyle)
|
|
{
|
|
if (eStyleUnit_Normal == aColStyle->mColumnGap.GetUnit())
|
|
return aFrame->StyleFont()->mFont.size;
|
|
if (eStyleUnit_Coord == aColStyle->mColumnGap.GetUnit()) {
|
|
nscoord colGap = aColStyle->mColumnGap.GetCoordValue();
|
|
NS_ASSERTION(colGap >= 0, "negative column gap");
|
|
return colGap;
|
|
}
|
|
|
|
NS_NOTREACHED("Unknown gap type");
|
|
return 0;
|
|
}
|
|
|
|
nsColumnSetFrame::ReflowConfig
|
|
nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState,
|
|
bool aForceAuto = false,
|
|
nscoord aFeasibleBSize = NS_INTRINSICSIZE,
|
|
nscoord aInfeasibleBSize = 0)
|
|
{
|
|
nscoord knownFeasibleBSize = aFeasibleBSize;
|
|
nscoord knownInfeasibleBSize = aInfeasibleBSize;
|
|
|
|
const nsStyleColumn* colStyle = StyleColumn();
|
|
nscoord availContentISize = GetAvailableContentISize(aReflowState);
|
|
if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) {
|
|
availContentISize = aReflowState.ComputedISize();
|
|
}
|
|
|
|
nscoord consumedBSize = GetConsumedBSize();
|
|
|
|
// The effective computed height is the height of the current continuation
|
|
// of the column set frame. This should be the same as the computed height
|
|
// if we have an unconstrained available height.
|
|
nscoord computedBSize = GetEffectiveComputedBSize(aReflowState,
|
|
consumedBSize);
|
|
nscoord colBSize = GetAvailableContentBSize(aReflowState);
|
|
|
|
if (aReflowState.ComputedBSize() != NS_INTRINSICSIZE) {
|
|
colBSize = aReflowState.ComputedBSize();
|
|
} else if (aReflowState.ComputedMaxBSize() != NS_INTRINSICSIZE) {
|
|
colBSize = std::min(colBSize, aReflowState.ComputedMaxBSize());
|
|
}
|
|
|
|
nscoord colGap = GetColumnGap(this, colStyle);
|
|
int32_t numColumns = colStyle->mColumnCount;
|
|
|
|
// If column-fill is set to 'balance', then we want to balance the columns.
|
|
const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE
|
|
&& !aForceAuto;
|
|
if (isBalancing) {
|
|
const uint32_t MAX_NESTED_COLUMN_BALANCING = 2;
|
|
uint32_t cnt = 0;
|
|
for (const nsHTMLReflowState* rs = aReflowState.parentReflowState;
|
|
rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->parentReflowState) {
|
|
if (rs->mFlags.mIsColumnBalancing) {
|
|
++cnt;
|
|
}
|
|
}
|
|
if (cnt == MAX_NESTED_COLUMN_BALANCING) {
|
|
numColumns = 1;
|
|
}
|
|
}
|
|
|
|
nscoord colISize;
|
|
// In vertical writing-mode, "column-width" (inline size) will actually be
|
|
// physical height, but its CSS name is still column-width.
|
|
if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
|
|
colISize = colStyle->mColumnWidth.GetCoordValue();
|
|
NS_ASSERTION(colISize >= 0, "negative column width");
|
|
// Reduce column count if necessary to make columns fit in the
|
|
// available width. Compute max number of columns that fit in
|
|
// availContentISize, satisfying colGap*(maxColumns - 1) +
|
|
// colISize*maxColumns <= availContentISize
|
|
if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0
|
|
&& numColumns > 0) {
|
|
// This expression uses truncated rounding, which is what we
|
|
// want
|
|
int32_t maxColumns =
|
|
std::min(nscoord(nsStyleColumn::kMaxColumnCount),
|
|
(availContentISize + colGap) / (colGap + colISize));
|
|
numColumns = std::max(1, std::min(numColumns, maxColumns));
|
|
}
|
|
} else if (numColumns > 0 && availContentISize != NS_INTRINSICSIZE) {
|
|
nscoord iSizeMinusGaps = availContentISize - colGap * (numColumns - 1);
|
|
colISize = iSizeMinusGaps / numColumns;
|
|
} else {
|
|
colISize = NS_INTRINSICSIZE;
|
|
}
|
|
// Take care of the situation where there's only one column but it's
|
|
// still too wide
|
|
colISize = std::max(1, std::min(colISize, availContentISize));
|
|
|
|
nscoord expectedISizeLeftOver = 0;
|
|
|
|
if (colISize != NS_INTRINSICSIZE && availContentISize != NS_INTRINSICSIZE) {
|
|
// distribute leftover space
|
|
|
|
// First, determine how many columns will be showing if the column
|
|
// count is auto
|
|
if (numColumns <= 0) {
|
|
// choose so that colGap*(nominalColumnCount - 1) +
|
|
// colISize*nominalColumnCount is nearly availContentISize
|
|
// make sure to round down
|
|
if (colGap + colISize > 0) {
|
|
numColumns = (availContentISize + colGap) / (colGap + colISize);
|
|
// The number of columns should never exceed kMaxColumnCount.
|
|
numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount),
|
|
numColumns);
|
|
}
|
|
if (numColumns <= 0) {
|
|
numColumns = 1;
|
|
}
|
|
}
|
|
|
|
// Compute extra space and divide it among the columns
|
|
nscoord extraSpace =
|
|
std::max(0, availContentISize - (colISize * numColumns +
|
|
colGap * (numColumns - 1)));
|
|
nscoord extraToColumns = extraSpace / numColumns;
|
|
colISize += extraToColumns;
|
|
expectedISizeLeftOver = extraSpace - (extraToColumns * numColumns);
|
|
}
|
|
|
|
if (isBalancing) {
|
|
if (numColumns <= 0) {
|
|
// Hmm, auto column count, column width or available width is unknown,
|
|
// and balancing is required. Let's just use one column then.
|
|
numColumns = 1;
|
|
}
|
|
colBSize = std::min(mLastBalanceBSize, colBSize);
|
|
} else {
|
|
// This is the case when the column-fill property is set to 'auto'.
|
|
// No balancing, so don't limit the column count
|
|
numColumns = INT32_MAX;
|
|
|
|
// XXX_jwir3: If a page's height is set to 0, we could continually
|
|
// create continuations, resulting in an infinite loop, since
|
|
// no progress is ever made. This is an issue with the spec
|
|
// (css3-multicol, css3-page, and css3-break) that is
|
|
// unresolved as of 27 Feb 2013. For the time being, we set this
|
|
// to have a minimum of 1 css px. Once a resolution is made
|
|
// on what minimum to have for a page height, we may need to
|
|
// change this value to match the appropriate spec(s).
|
|
colBSize = std::max(colBSize, nsPresContext::CSSPixelsToAppUnits(1));
|
|
}
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colISize=%d,"
|
|
" expectedISizeLeftOver=%d, colBSize=%d, colGap=%d\n",
|
|
numColumns, colISize, expectedISizeLeftOver, colBSize, colGap);
|
|
#endif
|
|
ReflowConfig config = { numColumns, colISize, expectedISizeLeftOver, colGap,
|
|
colBSize, isBalancing, knownFeasibleBSize,
|
|
knownInfeasibleBSize, computedBSize, consumedBSize };
|
|
return config;
|
|
}
|
|
|
|
bool
|
|
nsColumnSetFrame::ReflowColumns(nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aReflowStatus,
|
|
ReflowConfig& aConfig,
|
|
bool aLastColumnUnbounded,
|
|
nsCollapsingMargin* aCarriedOutBEndMargin,
|
|
ColumnBalanceData& aColData)
|
|
{
|
|
bool feasible = ReflowChildren(aDesiredSize, aReflowState,
|
|
aReflowStatus, aConfig, aLastColumnUnbounded,
|
|
aCarriedOutBEndMargin, aColData);
|
|
|
|
if (aColData.mHasExcessBSize) {
|
|
aConfig = ChooseColumnStrategy(aReflowState, true);
|
|
|
|
// We need to reflow our children again one last time, otherwise we might
|
|
// end up with a stale column height for some of our columns, since we
|
|
// bailed out of balancing.
|
|
feasible = ReflowChildren(aDesiredSize, aReflowState, aReflowStatus,
|
|
aConfig, aLastColumnUnbounded,
|
|
aCarriedOutBEndMargin, aColData);
|
|
}
|
|
|
|
return feasible;
|
|
}
|
|
|
|
static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin,
|
|
WritingMode aWM, nscoord aContainerWidth)
|
|
{
|
|
if (aChild->GetLogicalPosition(aWM, aContainerWidth) == aOrigin) {
|
|
return;
|
|
}
|
|
|
|
aChild->SetPosition(aWM, aOrigin, aContainerWidth);
|
|
nsContainerFrame::PlaceFrameView(aChild);
|
|
}
|
|
|
|
nscoord
|
|
nsColumnSetFrame::GetMinISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
nscoord iSize = 0;
|
|
DISPLAY_MIN_WIDTH(this, iSize);
|
|
if (mFrames.FirstChild()) {
|
|
iSize = mFrames.FirstChild()->GetMinISize(aRenderingContext);
|
|
}
|
|
const nsStyleColumn* colStyle = StyleColumn();
|
|
nscoord colISize;
|
|
if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
|
|
colISize = colStyle->mColumnWidth.GetCoordValue();
|
|
// As available width reduces to zero, we reduce our number of columns
|
|
// to one, and don't enforce the column width, so just return the min
|
|
// of the child's min-width with any specified column width.
|
|
iSize = std::min(iSize, colISize);
|
|
} else {
|
|
NS_ASSERTION(colStyle->mColumnCount > 0,
|
|
"column-count and column-width can't both be auto");
|
|
// As available width reduces to zero, we still have mColumnCount columns,
|
|
// so multiply the child's min-width by the number of columns (n) and
|
|
// include n-1 column gaps.
|
|
colISize = iSize;
|
|
iSize *= colStyle->mColumnCount;
|
|
nscoord colGap = GetColumnGap(this, colStyle);
|
|
iSize += colGap * (colStyle->mColumnCount - 1);
|
|
// The multiplication above can make 'width' negative (integer overflow),
|
|
// so use std::max to protect against that.
|
|
iSize = std::max(iSize, colISize);
|
|
}
|
|
// XXX count forced column breaks here? Maybe we should return the child's
|
|
// min-width times the minimum number of columns.
|
|
return iSize;
|
|
}
|
|
|
|
nscoord
|
|
nsColumnSetFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
// Our preferred width is our desired column width, if specified, otherwise
|
|
// the child's preferred width, times the number of columns, plus the width
|
|
// of any required column gaps
|
|
// XXX what about forced column breaks here?
|
|
nscoord result = 0;
|
|
DISPLAY_PREF_WIDTH(this, result);
|
|
const nsStyleColumn* colStyle = StyleColumn();
|
|
nscoord colGap = GetColumnGap(this, colStyle);
|
|
|
|
nscoord colISize;
|
|
if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
|
|
colISize = colStyle->mColumnWidth.GetCoordValue();
|
|
} else if (mFrames.FirstChild()) {
|
|
colISize = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
|
|
} else {
|
|
colISize = 0;
|
|
}
|
|
|
|
int32_t numColumns = colStyle->mColumnCount;
|
|
if (numColumns <= 0) {
|
|
// if column-count is auto, assume one column
|
|
numColumns = 1;
|
|
}
|
|
|
|
nscoord iSize = colISize * numColumns + colGap * (numColumns - 1);
|
|
// The multiplication above can make 'iSize' negative (integer overflow),
|
|
// so use std::max to protect against that.
|
|
result = std::max(iSize, colISize);
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
const ReflowConfig& aConfig,
|
|
bool aUnboundedLastColumn,
|
|
nsCollapsingMargin* aCarriedOutBEndMargin,
|
|
ColumnBalanceData& aColData)
|
|
{
|
|
aColData.Reset();
|
|
bool allFit = true;
|
|
WritingMode wm = GetWritingMode();
|
|
bool isVertical = wm.IsVertical();
|
|
bool isRTL = !wm.IsBidiLTR();
|
|
bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) &&
|
|
mLastBalanceBSize > aConfig.mColMaxBSize;
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n"
|
|
" mBalanceColCount=%d, mColISize=%d, mColGap=%d\n",
|
|
mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount,
|
|
aConfig.mColISize, aConfig.mColGap);
|
|
#endif
|
|
|
|
DrainOverflowColumns();
|
|
|
|
const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize;
|
|
|
|
if (colBSizeChanged) {
|
|
mLastBalanceBSize = aConfig.mColMaxBSize;
|
|
// XXX Seems like this could fire if incremental reflow pushed the column set
|
|
// down so we reflow incrementally with a different available height.
|
|
// We need a way to do an incremental reflow and be sure availableHeight
|
|
// changes are taken account of! Right now I think block frames with absolute
|
|
// children might exit early.
|
|
//NS_ASSERTION(aKidReason != eReflowReason_Incremental,
|
|
// "incremental reflow should not have changed the balance height");
|
|
}
|
|
|
|
// get our border and padding
|
|
LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
|
|
borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState));
|
|
|
|
nsRect contentRect(0, 0, 0, 0);
|
|
nsOverflowAreas overflowRects;
|
|
|
|
nsIFrame* child = mFrames.FirstChild();
|
|
LogicalPoint childOrigin(wm, borderPadding.IStart(wm),
|
|
borderPadding.BStart(wm));
|
|
// In vertical-rl mode we can't use the computed width as the
|
|
// container width because it may be NS_UNCONSTRAINEDSIZE, so we use 0
|
|
// for now and reposition the columns after reflowing them all.
|
|
nscoord containerWidth = wm.IsVerticalRL() ? 0 : aReflowState.ComputedWidth();
|
|
|
|
// For RTL, since the columns might not fill the frame exactly, we
|
|
// need to account for the slop. Otherwise we'll waste time moving the
|
|
// columns by some tiny amount
|
|
|
|
// XXX when all of layout is converted to logical coordinates, we
|
|
// probably won't need to do this hack any more. For now, we
|
|
// confine it to the legacy horizontal-rl case
|
|
if (!isVertical && isRTL) {
|
|
nscoord availISize = aReflowState.AvailableISize();
|
|
if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) {
|
|
availISize = aReflowState.ComputedISize();
|
|
}
|
|
if (availISize != NS_INTRINSICSIZE) {
|
|
childOrigin.I(wm) = containerWidth - borderPadding.Left(wm) - availISize;
|
|
#ifdef DEBUG_roc
|
|
printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int columnCount = 0;
|
|
int contentBEnd = 0;
|
|
bool reflowNext = false;
|
|
|
|
while (child) {
|
|
// Try to skip reflowing the child. We can't skip if the child is dirty. We also can't
|
|
// skip if the next column is dirty, because the next column's first line(s)
|
|
// might be pullable back to this column. We can't skip if it's the last child
|
|
// because we need to obtain the bottom margin. We can't skip
|
|
// if this is the last column and we're supposed to assign unbounded
|
|
// height to it, because that could change the available height from
|
|
// the last time we reflowed it and we should try to pull all the
|
|
// content from its next sibling. (Note that it might be the last
|
|
// column, but not be the last child because the desired number of columns
|
|
// has changed.)
|
|
bool skipIncremental = !aReflowState.ShouldReflowAllKids()
|
|
&& !NS_SUBTREE_DIRTY(child)
|
|
&& child->GetNextSibling()
|
|
&& !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1)
|
|
&& !NS_SUBTREE_DIRTY(child->GetNextSibling());
|
|
// If we need to pull up content from the prev-in-flow then this is not just
|
|
// a height shrink. The prev in flow will have set the dirty bit.
|
|
// Check the overflow rect YMost instead of just the child's content height. The child
|
|
// may have overflowing content that cares about the available height boundary.
|
|
// (It may also have overflowing content that doesn't care about the available height
|
|
// boundary, but if so, too bad, this optimization is defeated.)
|
|
// We want scrollable overflow here since this is a calculation that
|
|
// affects layout.
|
|
bool skipResizeBSizeShrink = false;
|
|
if (shrinkingBSizeOnly) {
|
|
switch (wm.GetBlockDir()) {
|
|
case WritingMode::eBlockTB:
|
|
if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) {
|
|
skipResizeBSizeShrink = true;
|
|
}
|
|
break;
|
|
case WritingMode::eBlockLR:
|
|
if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) {
|
|
skipResizeBSizeShrink = true;
|
|
}
|
|
break;
|
|
case WritingMode::eBlockRL:
|
|
// XXX not sure how to handle this, so for now just don't attempt
|
|
// the optimization
|
|
break;
|
|
default:
|
|
NS_NOTREACHED("unknown block direction");
|
|
break;
|
|
}
|
|
}
|
|
|
|
nscoord childContentBEnd = 0;
|
|
if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) {
|
|
// This child does not need to be reflowed, but we may need to move it
|
|
MoveChildTo(child, childOrigin, wm, containerWidth);
|
|
|
|
// If this is the last frame then make sure we get the right status
|
|
nsIFrame* kidNext = child->GetNextSibling();
|
|
if (kidNext) {
|
|
aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
|
|
? NS_FRAME_OVERFLOW_INCOMPLETE
|
|
: NS_FRAME_NOT_COMPLETE;
|
|
} else {
|
|
aStatus = mLastFrameStatus;
|
|
}
|
|
childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
|
|
#ifdef DEBUG_roc
|
|
printf("*** Skipping child #%d %p (incremental %d, resize block-size shrink %d): status = %d\n",
|
|
columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus);
|
|
#endif
|
|
} else {
|
|
LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize);
|
|
if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
|
|
availSize.BSize(wm) = GetAvailableContentBSize(aReflowState);
|
|
}
|
|
|
|
LogicalSize computedSize = aReflowState.ComputedSize(wm);
|
|
|
|
if (reflowNext)
|
|
child->AddStateBits(NS_FRAME_IS_DIRTY);
|
|
|
|
LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm));
|
|
nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child,
|
|
availSize, &kidCBSize);
|
|
kidReflowState.mFlags.mIsTopOfPage = true;
|
|
kidReflowState.mFlags.mTableIsSplittable = false;
|
|
kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX;
|
|
|
|
// We need to reflow any float placeholders, even if our column height
|
|
// hasn't changed.
|
|
kidReflowState.mFlags.mMustReflowPlaceholders = !colBSizeChanged;
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** Reflowing child #%d %p: availHeight=%d\n",
|
|
columnCount, (void*)child,availSize.BSize(wm));
|
|
#endif
|
|
|
|
// Note if the column's next in flow is not being changed by this incremental reflow.
|
|
// This may allow the current column to avoid trying to pull lines from the next column.
|
|
if (child->GetNextSibling() &&
|
|
!(GetStateBits() & NS_FRAME_IS_DIRTY) &&
|
|
!(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) {
|
|
kidReflowState.mFlags.mNextInFlowUntouched = true;
|
|
}
|
|
|
|
nsHTMLReflowMetrics kidDesiredSize(wm, aDesiredSize.mFlags);
|
|
|
|
// XXX it would be cool to consult the float manager for the
|
|
// previous block to figure out the region of floats from the
|
|
// previous column that extend into this column, and subtract
|
|
// that region from the new float manager. So you could stick a
|
|
// really big float in the first column and text in following
|
|
// columns would flow around it.
|
|
|
|
// Reflow the frame
|
|
LogicalPoint origin(wm,
|
|
childOrigin.I(wm) +
|
|
kidReflowState.ComputedLogicalMargin().IStart(wm),
|
|
childOrigin.B(wm) +
|
|
kidReflowState.ComputedLogicalMargin().BStart(wm));
|
|
ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState,
|
|
wm, origin, containerWidth, 0, aStatus);
|
|
|
|
reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n",
|
|
columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(),
|
|
kidDesiredSize.mCarriedOutBEndMargin.get());
|
|
#endif
|
|
|
|
NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
|
|
|
|
*aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin;
|
|
|
|
FinishReflowChild(child, PresContext(), kidDesiredSize,
|
|
&kidReflowState, wm, childOrigin, containerWidth, 0);
|
|
|
|
childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
|
|
if (childContentBEnd > aConfig.mColMaxBSize) {
|
|
allFit = false;
|
|
}
|
|
if (childContentBEnd > availSize.BSize(wm)) {
|
|
aColData.mMaxOverflowingBSize = std::max(childContentBEnd,
|
|
aColData.mMaxOverflowingBSize);
|
|
}
|
|
}
|
|
|
|
contentRect.UnionRect(contentRect, child->GetRect());
|
|
|
|
ConsiderChildOverflow(overflowRects, child);
|
|
contentBEnd = std::max(contentBEnd, childContentBEnd);
|
|
aColData.mLastBSize = childContentBEnd;
|
|
aColData.mSumBSize += childContentBEnd;
|
|
|
|
// Build a continuation column if necessary
|
|
nsIFrame* kidNextInFlow = child->GetNextInFlow();
|
|
|
|
if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
|
|
NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
|
|
child = nullptr;
|
|
break;
|
|
} else {
|
|
++columnCount;
|
|
// Make sure that the column has a next-in-flow. If not, we must
|
|
// create one to hold the overflowing stuff, even if we're just
|
|
// going to put it on our overflow list and let *our*
|
|
// next in flow handle it.
|
|
if (!kidNextInFlow) {
|
|
NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
|
|
"We have to create a continuation, but the block doesn't want us to reflow it?");
|
|
|
|
// We need to create a continuing column
|
|
kidNextInFlow = CreateNextInFlow(child);
|
|
}
|
|
|
|
// Make sure we reflow a next-in-flow when it switches between being
|
|
// normal or overflow container
|
|
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
|
|
if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
|
|
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
|
|
reflowNext = true;
|
|
kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
|
|
}
|
|
}
|
|
else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
|
|
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
|
|
reflowNext = true;
|
|
kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
|
|
}
|
|
|
|
if ((contentBEnd > aReflowState.ComputedMaxBSize() ||
|
|
contentBEnd > aReflowState.ComputedBSize()) &&
|
|
aConfig.mBalanceColCount < INT32_MAX) {
|
|
// We overflowed vertically, but have not exceeded the number of
|
|
// columns. We're going to go into overflow columns now, so balancing
|
|
// no longer applies.
|
|
aColData.mHasExcessBSize = true;
|
|
}
|
|
|
|
if (columnCount >= aConfig.mBalanceColCount) {
|
|
// No more columns allowed here. Stop.
|
|
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
|
|
kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY);
|
|
// Move any of our leftover columns to our overflow list. Our
|
|
// next-in-flow will eventually pick them up.
|
|
const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child);
|
|
if (continuationColumns.NotEmpty()) {
|
|
SetOverflowFrames(continuationColumns);
|
|
}
|
|
child = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (PresContext()->HasPendingInterrupt()) {
|
|
// Stop the loop now while |child| still points to the frame that bailed
|
|
// out. We could keep going here and condition a bunch of the code in
|
|
// this loop on whether there's an interrupt, or even just keep going and
|
|
// trying to reflow the blocks (even though we know they'll interrupt
|
|
// right after their first line), but stopping now is conceptually the
|
|
// simplest (and probably fastest) thing.
|
|
break;
|
|
}
|
|
|
|
// Advance to the next column
|
|
child = child->GetNextSibling();
|
|
|
|
if (child) {
|
|
childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap;
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (PresContext()->CheckForInterrupt(this) &&
|
|
(GetStateBits() & NS_FRAME_IS_DIRTY)) {
|
|
// Mark all our kids starting with |child| dirty
|
|
|
|
// Note that this is a CheckForInterrupt call, not a HasPendingInterrupt,
|
|
// because we might have interrupted while reflowing |child|, and since
|
|
// we're about to add a dirty bit to |child| we need to make sure that
|
|
// |this| is scheduled to have dirty bits marked on it and its ancestors.
|
|
// Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll
|
|
// bail out immediately, since it'll already have a dirty bit.
|
|
for (; child; child = child->GetNextSibling()) {
|
|
child->AddStateBits(NS_FRAME_IS_DIRTY);
|
|
}
|
|
}
|
|
|
|
aColData.mMaxBSize = contentBEnd;
|
|
LogicalSize contentSize = LogicalSize(wm, contentRect.Size());
|
|
contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd);
|
|
mLastFrameStatus = aStatus;
|
|
|
|
// Apply computed and min/max values
|
|
if (aConfig.mComputedBSize != NS_INTRINSICSIZE) {
|
|
if (aReflowState.AvailableBSize() != NS_INTRINSICSIZE) {
|
|
contentSize.BSize(wm) = std::min(contentSize.BSize(wm),
|
|
aConfig.mComputedBSize);
|
|
} else {
|
|
contentSize.BSize(wm) = aConfig.mComputedBSize;
|
|
}
|
|
} else {
|
|
// We add the "consumed" block-size back in so that we're applying
|
|
// constraints to the correct bSize value, then subtract it again
|
|
// after we've finished with the min/max calculation. This prevents us from
|
|
// having a last continuation that is smaller than the min bSize. but which
|
|
// has prev-in-flows, trigger a larger bSize than actually required.
|
|
contentSize.BSize(wm) =
|
|
aReflowState.ApplyMinMaxBSize(contentSize.BSize(wm),
|
|
aConfig.mConsumedBSize);
|
|
}
|
|
if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) {
|
|
contentSize.ISize(wm) = aReflowState.ComputedISize();
|
|
} else {
|
|
contentSize.ISize(wm) =
|
|
aReflowState.ApplyMinMaxISize(contentSize.ISize(wm));
|
|
}
|
|
|
|
contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
|
|
contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
|
|
aDesiredSize.SetSize(wm, contentSize);
|
|
aDesiredSize.mOverflowAreas = overflowRects;
|
|
aDesiredSize.UnionOverflowAreasWithDesiredBounds();
|
|
|
|
// In vertical-rl mode, make a second pass to reposition the columns
|
|
// with the correct container width
|
|
if (wm.IsVerticalRL()) {
|
|
child = mFrames.FirstChild();
|
|
while (child) {
|
|
// Get the logical position as set before with containerWidth=0
|
|
// and reset with the correct container width (which is the block
|
|
// size in vertical modes).
|
|
child->SetPosition(wm, child->GetLogicalPosition(wm, 0),
|
|
contentSize.BSize(wm));
|
|
child = child->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
|
|
&& !NS_FRAME_IS_TRUNCATED(aStatus));
|
|
#endif
|
|
return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
|
|
&& !NS_FRAME_IS_TRUNCATED(aStatus);
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::DrainOverflowColumns()
|
|
{
|
|
// First grab the prev-in-flows overflows and reparent them to this
|
|
// frame.
|
|
nsPresContext* presContext = PresContext();
|
|
nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
|
|
if (prev) {
|
|
AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
|
|
if (overflows) {
|
|
nsContainerFrame::ReparentFrameViewList(*overflows, prev, this);
|
|
|
|
mFrames.InsertFrames(this, nullptr, *overflows);
|
|
}
|
|
}
|
|
|
|
// Now pull back our own overflows and append them to our children.
|
|
// We don't need to reparent them since we're already their parent.
|
|
AutoFrameListPtr overflows(presContext, StealOverflowFrames());
|
|
if (overflows) {
|
|
// We're already the parent for these frames, so no need to set
|
|
// their parent again.
|
|
mFrames.AppendFrames(nullptr, *overflows);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::FindBestBalanceBSize(const nsHTMLReflowState& aReflowState,
|
|
nsPresContext* aPresContext,
|
|
ReflowConfig& aConfig,
|
|
ColumnBalanceData& aColData,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
nsCollapsingMargin& aOutMargin,
|
|
bool& aUnboundedLastColumn,
|
|
bool& aRunWasFeasible,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
bool feasible = aRunWasFeasible;
|
|
|
|
nsMargin bp = aReflowState.ComputedPhysicalBorderPadding();
|
|
bp.ApplySkipSides(GetSkipSides());
|
|
bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom;
|
|
|
|
nscoord availableContentBSize =
|
|
GetAvailableContentBSize(aReflowState);
|
|
|
|
// Termination of the algorithm below is guaranteed because
|
|
// aConfig.knownFeasibleBSize - aConfig.knownInfeasibleBSize decreases in every
|
|
// iteration.
|
|
|
|
// We set this flag when we detect that we may contain a frame
|
|
// that can break anywhere (thus foiling the linear decrease-by-one
|
|
// search)
|
|
bool maybeContinuousBreakingDetected = false;
|
|
|
|
while (!aPresContext->HasPendingInterrupt()) {
|
|
nscoord lastKnownFeasibleBSize = aConfig.mKnownFeasibleBSize;
|
|
|
|
// Record what we learned from the last reflow
|
|
if (feasible) {
|
|
// maxBSize is feasible. Also, mLastBalanceBSize is feasible.
|
|
aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
|
|
aColData.mMaxBSize);
|
|
aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
|
|
mLastBalanceBSize);
|
|
|
|
// Furthermore, no height less than the height of the last
|
|
// column can ever be feasible. (We might be able to reduce the
|
|
// height of a non-last column by moving content to a later column,
|
|
// but we can't do that with the last column.)
|
|
if (mFrames.GetLength() == aConfig.mBalanceColCount) {
|
|
aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
|
|
aColData.mLastBSize - 1);
|
|
}
|
|
} else {
|
|
aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
|
|
mLastBalanceBSize);
|
|
// If a column didn't fit in its available height, then its current
|
|
// height must be the minimum height for unbreakable content in
|
|
// the column, and therefore no smaller height can be feasible.
|
|
aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
|
|
aColData.mMaxOverflowingBSize - 1);
|
|
|
|
if (aUnboundedLastColumn) {
|
|
// The last column is unbounded, so all content got reflowed, so the
|
|
// mColMaxBSize is feasible.
|
|
aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
|
|
aColData.mMaxBSize);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
|
|
aConfig.mKnownInfeasibleBSize, aConfig.mKnownFeasibleBSize);
|
|
#endif
|
|
|
|
|
|
if (aConfig.mKnownInfeasibleBSize >= aConfig.mKnownFeasibleBSize - 1) {
|
|
// aConfig.mKnownFeasibleBSize is where we want to be
|
|
break;
|
|
}
|
|
|
|
if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
|
|
break;
|
|
}
|
|
|
|
if (lastKnownFeasibleBSize - aConfig.mKnownFeasibleBSize == 1) {
|
|
// We decreased the feasible height by one twip only. This could
|
|
// indicate that there is a continuously breakable child frame
|
|
// that we are crawling through.
|
|
maybeContinuousBreakingDetected = true;
|
|
}
|
|
|
|
nscoord nextGuess = (aConfig.mKnownFeasibleBSize + aConfig.mKnownInfeasibleBSize)/2;
|
|
// The constant of 600 twips is arbitrary. It's about two line-heights.
|
|
if (aConfig.mKnownFeasibleBSize - nextGuess < 600 &&
|
|
!maybeContinuousBreakingDetected) {
|
|
// We're close to our target, so just try shrinking just the
|
|
// minimum amount that will cause one of our columns to break
|
|
// differently.
|
|
nextGuess = aConfig.mKnownFeasibleBSize - 1;
|
|
} else if (aUnboundedLastColumn) {
|
|
// Make a guess by dividing that into N columns. Add some slop
|
|
// to try to make it on the feasible side. The constant of
|
|
// 600 twips is arbitrary. It's about two line-heights.
|
|
nextGuess = aColData.mSumBSize/aConfig.mBalanceColCount + 600;
|
|
// Sanitize it
|
|
nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleBSize + 1,
|
|
aConfig.mKnownFeasibleBSize - 1);
|
|
} else if (aConfig.mKnownFeasibleBSize == NS_INTRINSICSIZE) {
|
|
// This can happen when we had a next-in-flow so we didn't
|
|
// want to do an unbounded height measuring step. Let's just increase
|
|
// from the infeasible height by some reasonable amount.
|
|
nextGuess = aConfig.mKnownInfeasibleBSize*2 + 600;
|
|
}
|
|
// Don't bother guessing more than our height constraint.
|
|
nextGuess = std::min(availableContentBSize, nextGuess);
|
|
|
|
#ifdef DEBUG_roc
|
|
printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
|
|
#endif
|
|
|
|
aConfig.mColMaxBSize = nextGuess;
|
|
|
|
aUnboundedLastColumn = false;
|
|
AddStateBits(NS_FRAME_IS_DIRTY);
|
|
feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, false,
|
|
&aOutMargin, aColData);
|
|
|
|
if (!aConfig.mIsBalancing) {
|
|
// Looks like we had excess height when balancing, so we gave up on
|
|
// trying to balance.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (aConfig.mIsBalancing && !feasible &&
|
|
!aPresContext->HasPendingInterrupt()) {
|
|
// We may need to reflow one more time at the feasible height to
|
|
// get a valid layout.
|
|
bool skip = false;
|
|
if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
|
|
aConfig.mColMaxBSize = availableContentBSize;
|
|
if (mLastBalanceBSize == availableContentBSize) {
|
|
skip = true;
|
|
}
|
|
} else {
|
|
aConfig.mColMaxBSize = aConfig.mKnownFeasibleBSize;
|
|
}
|
|
if (!skip) {
|
|
// If our height is unconstrained, make sure that the last column is
|
|
// allowed to have arbitrary height here, even though we were balancing.
|
|
// Otherwise we'd have to split, and it's not clear what we'd do with
|
|
// that.
|
|
AddStateBits(NS_FRAME_IS_DIRTY);
|
|
feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig,
|
|
availableContentBSize == NS_UNCONSTRAINEDSIZE,
|
|
&aOutMargin, aColData);
|
|
}
|
|
}
|
|
|
|
aRunWasFeasible = feasible;
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
MarkInReflow();
|
|
// Don't support interruption in columns
|
|
nsPresContext::InterruptPreventer noInterrupts(aPresContext);
|
|
|
|
DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame");
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
|
|
|
// Initialize OUT parameter
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
// Our children depend on our block-size if we have a fixed block-size.
|
|
if (aReflowState.ComputedBSize() != NS_AUTOHEIGHT) {
|
|
AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
|
|
} else {
|
|
RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList));
|
|
for (; !oc.AtEnd(); oc.Next()) {
|
|
MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get()));
|
|
}
|
|
nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList));
|
|
for (; !eoc.AtEnd(); eoc.Next()) {
|
|
MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get()));
|
|
}
|
|
#endif
|
|
|
|
nsOverflowAreas ocBounds;
|
|
nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
|
|
if (GetPrevInFlow()) {
|
|
ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0,
|
|
ocStatus);
|
|
}
|
|
|
|
//------------ Handle Incremental Reflow -----------------
|
|
|
|
ReflowConfig config = ChooseColumnStrategy(aReflowState);
|
|
|
|
// If balancing, then we allow the last column to grow to unbounded
|
|
// height during the first reflow. This gives us a way to estimate
|
|
// what the average column height should be, because we can measure
|
|
// the heights of all the columns and sum them up. But don't do this
|
|
// if we have a next in flow because we don't want to suck all its
|
|
// content back here and then have to push it out again!
|
|
nsIFrame* nextInFlow = GetNextInFlow();
|
|
bool unboundedLastColumn = config.mIsBalancing && !nextInFlow;
|
|
nsCollapsingMargin carriedOutBottomMargin;
|
|
ColumnBalanceData colData;
|
|
colData.mHasExcessBSize = false;
|
|
|
|
bool feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, config,
|
|
unboundedLastColumn, &carriedOutBottomMargin,
|
|
colData);
|
|
|
|
// If we're not balancing, then we're already done, since we should have
|
|
// reflown all of our children, and there is no need for a binary search to
|
|
// determine proper column height.
|
|
if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) {
|
|
FindBestBalanceBSize(aReflowState, aPresContext, config, colData,
|
|
aDesiredSize, carriedOutBottomMargin,
|
|
unboundedLastColumn, feasible, aStatus);
|
|
}
|
|
|
|
if (aPresContext->HasPendingInterrupt() &&
|
|
aReflowState.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
|
|
// In this situation, we might be lying about our reflow status, because
|
|
// our last kid (the one that got interrupted) was incomplete. Fix that.
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
}
|
|
|
|
NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
|
|
aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
|
|
"Column set should be complete if the available block-size is unconstrained");
|
|
|
|
// Merge overflow container bounds and status.
|
|
aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
|
|
NS_MergeReflowStatusInto(&aStatus, ocStatus);
|
|
|
|
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false);
|
|
|
|
aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin;
|
|
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsRect& aDirtyRect,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
DisplayBorderBackgroundOutline(aBuilder, aLists);
|
|
|
|
if (IsVisibleForPainting(aBuilder)) {
|
|
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
|
|
nsDisplayGenericOverflow(aBuilder, this, ::PaintColumnRule, "ColumnRule",
|
|
nsDisplayItem::TYPE_COLUMN_RULE));
|
|
}
|
|
|
|
// Our children won't have backgrounds so it doesn't matter where we put them.
|
|
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
|
|
BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, aLists);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
nsColumnSetFrame::SetInitialChildList(ChildListID aListID,
|
|
nsFrameList& aChildList)
|
|
{
|
|
MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
|
|
MOZ_ASSERT(aChildList.OnlyChild(),
|
|
"initial child list must have exactly one child");
|
|
nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList);
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::AppendFrames(ChildListID aListID,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
MOZ_CRASH("unsupported operation");
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::InsertFrames(ChildListID aListID,
|
|
nsIFrame* aPrevFrame,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
MOZ_CRASH("unsupported operation");
|
|
}
|
|
|
|
void
|
|
nsColumnSetFrame::RemoveFrame(ChildListID aListID,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
MOZ_CRASH("unsupported operation");
|
|
}
|
|
#endif
|