michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Eric Vaughan michael@0: // Netscape Communications michael@0: // michael@0: // See documentation in associated header file michael@0: // michael@0: michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsSprocketLayout.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsBoxFrame.h" michael@0: #include "StackArena.h" michael@0: #include "mozilla/Likely.h" michael@0: #include michael@0: michael@0: nsBoxLayout* nsSprocketLayout::gInstance = nullptr; michael@0: michael@0: //#define DEBUG_GROW michael@0: michael@0: #define DEBUG_SPRING_SIZE 8 michael@0: #define DEBUG_BORDER_SIZE 2 michael@0: #define COIL_SIZE 8 michael@0: michael@0: michael@0: nsresult michael@0: NS_NewSprocketLayout( nsIPresShell* aPresShell, nsCOMPtr& aNewLayout) michael@0: { michael@0: if (!nsSprocketLayout::gInstance) { michael@0: nsSprocketLayout::gInstance = new nsSprocketLayout(); michael@0: NS_IF_ADDREF(nsSprocketLayout::gInstance); michael@0: } michael@0: // we have not instance variables so just return our static one. michael@0: aNewLayout = nsSprocketLayout::gInstance; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ void michael@0: nsSprocketLayout::Shutdown() michael@0: { michael@0: NS_IF_RELEASE(gInstance); michael@0: } michael@0: michael@0: nsSprocketLayout::nsSprocketLayout() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: nsSprocketLayout::IsHorizontal(nsIFrame* aBox) michael@0: { michael@0: return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0; michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::GetFrameState(nsIFrame* aBox, nsFrameState& aState) michael@0: { michael@0: aState = aBox->GetStateBits(); michael@0: } michael@0: michael@0: static uint8_t michael@0: GetFrameDirection(nsIFrame* aBox) michael@0: { michael@0: return aBox->StyleVisibility()->mDirection; michael@0: } michael@0: michael@0: static void michael@0: HandleBoxPack(nsIFrame* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY, michael@0: const nsRect& aOriginalRect, const nsRect& aClientRect) michael@0: { michael@0: // In the normal direction we lay out our kids in the positive direction (e.g., |x| will get michael@0: // bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse michael@0: // direction, the opposite is true. We'll be laying out each child at a smaller |x| or michael@0: // |y|. michael@0: uint8_t frameDirection = GetFrameDirection(aBox); michael@0: michael@0: if (aFrameState & NS_STATE_IS_HORIZONTAL) { michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { michael@0: // The normal direction. |x| increases as we move through our children. michael@0: aX = aClientRect.x; michael@0: } michael@0: else { michael@0: // The reverse direction. |x| decreases as we move through our children. michael@0: aX = aClientRect.x + aOriginalRect.width; michael@0: } michael@0: // |y| is always in the normal direction in horizontal boxes michael@0: aY = aClientRect.y; michael@0: } michael@0: else { michael@0: // take direction property into account for |x| in vertical boxes michael@0: if (frameDirection == NS_STYLE_DIRECTION_LTR) { michael@0: // The normal direction. |x| increases as we move through our children. michael@0: aX = aClientRect.x; michael@0: } michael@0: else { michael@0: // The reverse direction. |x| decreases as we move through our children. michael@0: aX = aClientRect.x + aOriginalRect.width; michael@0: } michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { michael@0: // The normal direction. |y| increases as we move through our children. michael@0: aY = aClientRect.y; michael@0: } michael@0: else { michael@0: // The reverse direction. |y| decreases as we move through our children. michael@0: aY = aClientRect.y + aOriginalRect.height; michael@0: } michael@0: } michael@0: michael@0: // Get our pack/alignment information. michael@0: nsIFrame::Halignment halign = aBox->GetHAlign(); michael@0: nsIFrame::Valignment valign = aBox->GetVAlign(); michael@0: michael@0: // The following code handles box PACKING. Packing comes into play in the case where the computed size for michael@0: // all of our children (now stored in our client rect) is smaller than the size available for michael@0: // the box (stored in |aOriginalRect|). michael@0: // michael@0: // Here we adjust our |x| and |y| variables accordingly so that we start at the beginning, michael@0: // middle, or end of the box. michael@0: // michael@0: // XXXdwh JUSTIFY needs to be implemented! michael@0: if (aFrameState & NS_STATE_IS_HORIZONTAL) { michael@0: switch(halign) { michael@0: case nsBoxFrame::hAlign_Left: michael@0: break; // Nothing to do. The default initialized us properly. michael@0: michael@0: case nsBoxFrame::hAlign_Center: michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aX += (aOriginalRect.width - aClientRect.width)/2; michael@0: else michael@0: aX -= (aOriginalRect.width - aClientRect.width)/2; michael@0: break; michael@0: michael@0: case nsBoxFrame::hAlign_Right: michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aX += (aOriginalRect.width - aClientRect.width); michael@0: else michael@0: aX -= (aOriginalRect.width - aClientRect.width); michael@0: break; // Nothing to do for the reverse dir. The default initialized us properly. michael@0: } michael@0: } else { michael@0: switch(valign) { michael@0: case nsBoxFrame::vAlign_Top: michael@0: case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack. michael@0: break; // Don't do anything. We were initialized correctly. michael@0: michael@0: case nsBoxFrame::vAlign_Middle: michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aY += (aOriginalRect.height - aClientRect.height)/2; michael@0: else michael@0: aY -= (aOriginalRect.height - aClientRect.height)/2; michael@0: break; michael@0: michael@0: case nsBoxFrame::vAlign_Bottom: michael@0: if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aY += (aOriginalRect.height - aClientRect.height); michael@0: else michael@0: aY -= (aOriginalRect.height - aClientRect.height); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSprocketLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: // See if we are collapsed. If we are, then simply iterate over all our michael@0: // children and give them a rect of 0 width and height. michael@0: if (aBox->IsCollapsed()) { michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: while(child) michael@0: { michael@0: nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0)); michael@0: child = child->GetNextBox(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsBoxLayoutState::AutoReflowDepth depth(aState); michael@0: mozilla::AutoStackArena arena; michael@0: michael@0: // ----- figure out our size ---------- michael@0: const nsSize originalSize = aBox->GetSize(); michael@0: michael@0: // -- make sure we remove our border and padding ---- michael@0: nsRect clientRect; michael@0: aBox->GetClientRect(clientRect); michael@0: michael@0: // |originalClientRect| represents the rect of the entire box (excluding borders michael@0: // and padding). We store it here because we're going to use |clientRect| to hold michael@0: // the required size for all our kids. As an example, consider an hbox with a michael@0: // specified width of 300. If the kids total only 150 pixels of width, then michael@0: // we have 150 pixels left over. |clientRect| is going to hold a width of 150 and michael@0: // is going to be adjusted based off the value of the PACK property. If flexible michael@0: // objects are in the box, then the two rects will match. michael@0: nsRect originalClientRect(clientRect); michael@0: michael@0: // The frame state contains cached knowledge about our box, such as our orientation michael@0: // and direction. michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: michael@0: // Build a list of our children's desired sizes and computed sizes michael@0: nsBoxSize* boxSizes = nullptr; michael@0: nsComputedBoxSize* computedBoxSizes = nullptr; michael@0: michael@0: nscoord min = 0; michael@0: nscoord max = 0; michael@0: int32_t flexes = 0; michael@0: PopulateBoxSizes(aBox, aState, boxSizes, min, max, flexes); michael@0: michael@0: // The |size| variable will hold the total size of children along the axis of michael@0: // the box. Continuing with the example begun in the comment above, size would michael@0: // be 150 pixels. michael@0: nscoord size = clientRect.width; michael@0: if (!IsHorizontal(aBox)) michael@0: size = clientRect.height; michael@0: ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes); michael@0: michael@0: // After the call to ComputeChildSizes, the |size| variable contains the michael@0: // total required size of all the children. We adjust our clientRect in the michael@0: // appropriate dimension to match this size. In our example, we now assign michael@0: // 150 pixels into the clientRect.width. michael@0: // michael@0: // The variables |min| and |max| hold the minimum required size box must be michael@0: // in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum michael@0: // height we require to enclose our children, and |max| is the maximum height michael@0: // required to enclose our children. michael@0: if (IsHorizontal(aBox)) { michael@0: clientRect.width = size; michael@0: if (clientRect.height < min) michael@0: clientRect.height = min; michael@0: michael@0: if (frameState & NS_STATE_AUTO_STRETCH) { michael@0: if (clientRect.height > max) michael@0: clientRect.height = max; michael@0: } michael@0: } else { michael@0: clientRect.height = size; michael@0: if (clientRect.width < min) michael@0: clientRect.width = min; michael@0: michael@0: if (frameState & NS_STATE_AUTO_STRETCH) { michael@0: if (clientRect.width > max) michael@0: clientRect.width = max; michael@0: } michael@0: } michael@0: michael@0: // With the sizes computed, now it's time to lay out our children. michael@0: bool finished; michael@0: nscoord passes = 0; michael@0: michael@0: // We flow children at their preferred locations (along with the appropriate computed flex). michael@0: // After we flow a child, it is possible that the child will change its size. If/when this happens, michael@0: // we have to do another pass. Typically only 2 passes are required, but the code is prepared to michael@0: // do as many passes as are necessary to achieve equilibrium. michael@0: nscoord x = 0; michael@0: nscoord y = 0; michael@0: nscoord origX = 0; michael@0: nscoord origY = 0; michael@0: michael@0: // |childResized| lets us know if a child changed its size after we attempted to lay it out at michael@0: // the specified size. If this happens, we usually have to do another pass. michael@0: bool childResized = false; michael@0: michael@0: // |passes| stores our number of passes. If for any reason we end up doing more than, say, 10 michael@0: // passes, we assert to indicate that something is seriously screwed up. michael@0: passes = 0; michael@0: do michael@0: { michael@0: #ifdef DEBUG_REFLOW michael@0: if (passes > 0) { michael@0: AddIndents(); michael@0: printf("ChildResized doing pass: %d\n", passes); michael@0: } michael@0: #endif michael@0: michael@0: // Always assume that we're done. This will change if, for example, children don't stay michael@0: // the same size after being flowed. michael@0: finished = true; michael@0: michael@0: // Handle box packing. michael@0: HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); michael@0: michael@0: // Now that packing is taken care of we set up a few additional michael@0: // tracking variables. michael@0: origX = x; michael@0: origY = y; michael@0: michael@0: nscoord nextX = x; michael@0: nscoord nextY = y; michael@0: michael@0: // Now we iterate over our box children and our box size lists in michael@0: // parallel. For each child, we look at its sizes and figure out michael@0: // where to place it. michael@0: nsComputedBoxSize* childComputedBoxSize = computedBoxSizes; michael@0: nsBoxSize* childBoxSize = boxSizes; michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: int32_t count = 0; michael@0: while (child || (childBoxSize && childBoxSize->bogus)) michael@0: { michael@0: // If for some reason, our lists are not the same length, we guard michael@0: // by bailing out of the loop. michael@0: if (childBoxSize == nullptr) { michael@0: NS_NOTREACHED("Lists not the same length."); michael@0: break; michael@0: } michael@0: michael@0: nscoord width = clientRect.width; michael@0: nscoord height = clientRect.height; michael@0: michael@0: if (!childBoxSize->bogus) { michael@0: // We have a valid box size entry. This entry already contains information about our michael@0: // sizes along the axis of the box (e.g., widths in a horizontal box). If our default michael@0: // ALIGN is not stretch, however, then we also need to know the child's size along the michael@0: // opposite axis. michael@0: if (!(frameState & NS_STATE_AUTO_STRETCH)) { michael@0: nsSize prefSize = child->GetPrefSize(aState); michael@0: nsSize minSize = child->GetMinSize(aState); michael@0: nsSize maxSize = child->GetMaxSize(aState); michael@0: prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize); michael@0: michael@0: AddMargin(child, prefSize); michael@0: width = std::min(prefSize.width, originalClientRect.width); michael@0: height = std::min(prefSize.height, originalClientRect.height); michael@0: } michael@0: } michael@0: michael@0: // Obtain the computed size along the axis of the box for this child from the computedBoxSize entry. michael@0: // We store the result in |width| for horizontal boxes and |height| for vertical boxes. michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) michael@0: width = childComputedBoxSize->size; michael@0: else michael@0: height = childComputedBoxSize->size; michael@0: michael@0: // Adjust our x/y for the left/right spacing. michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) { michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: x += (childBoxSize->left); michael@0: else michael@0: x -= (childBoxSize->right); michael@0: } else { michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: y += (childBoxSize->left); michael@0: else michael@0: y -= (childBoxSize->right); michael@0: } michael@0: michael@0: nextX = x; michael@0: nextY = y; michael@0: michael@0: // Now we build a child rect. michael@0: nscoord rectX = x; michael@0: nscoord rectY = y; michael@0: if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) michael@0: rectX -= width; michael@0: else michael@0: rectY -= height; michael@0: } michael@0: michael@0: // We now create an accurate child rect based off our computed size information. michael@0: nsRect childRect(rectX, rectY, width, height); michael@0: michael@0: // Sanity check against our clientRect. It is possible that a child specified michael@0: // a size that is too large to fit. If that happens, then we have to grow michael@0: // our client rect. Remember, clientRect is not the total rect of the enclosing michael@0: // box. It currently holds our perception of how big the children needed to michael@0: // be. michael@0: if (childRect.width > clientRect.width) michael@0: clientRect.width = childRect.width; michael@0: michael@0: if (childRect.height > clientRect.height) michael@0: clientRect.height = childRect.height; michael@0: michael@0: // Either |nextX| or |nextY| is updated by this function call, according michael@0: // to our axis. michael@0: ComputeChildsNextPosition(aBox, x, y, nextX, nextY, childRect); michael@0: michael@0: // Now we further update our nextX/Y along our axis. michael@0: // We also set childRect.y/x along the opposite axis appropriately for a michael@0: // stretch alignment. (Non-stretch alignment is handled below.) michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) { michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: nextX += (childBoxSize->right); michael@0: else michael@0: nextX -= (childBoxSize->left); michael@0: childRect.y = originalClientRect.y; michael@0: } michael@0: else { michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: nextY += (childBoxSize->right); michael@0: else michael@0: nextY -= (childBoxSize->left); michael@0: childRect.x = originalClientRect.x; michael@0: } michael@0: michael@0: // If we encounter a completely bogus box size, we just leave this child completely michael@0: // alone and continue through the loop to the next child. michael@0: if (childBoxSize->bogus) michael@0: { michael@0: childComputedBoxSize = childComputedBoxSize->next; michael@0: childBoxSize = childBoxSize->next; michael@0: count++; michael@0: x = nextX; michael@0: y = nextY; michael@0: continue; michael@0: } michael@0: michael@0: nsMargin margin(0,0,0,0); michael@0: michael@0: bool layout = true; michael@0: michael@0: // Deflate the rect of our child by its margin. michael@0: child->GetMargin(margin); michael@0: childRect.Deflate(margin); michael@0: if (childRect.width < 0) michael@0: childRect.width = 0; michael@0: if (childRect.height < 0) michael@0: childRect.height = 0; michael@0: michael@0: // Now we're trying to figure out if we have to lay out this child, i.e., to call michael@0: // the child's Layout method. michael@0: if (passes > 0) { michael@0: layout = false; michael@0: } else { michael@0: // Always perform layout if we are dirty or have dirty children michael@0: if (!NS_SUBTREE_DIRTY(child)) michael@0: layout = false; michael@0: } michael@0: michael@0: nsRect oldRect(child->GetRect()); michael@0: michael@0: // Non-stretch alignment will be handled in AlignChildren(), so don't michael@0: // change child out-of-axis positions yet. michael@0: if (!(frameState & NS_STATE_AUTO_STRETCH)) { michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) { michael@0: childRect.y = oldRect.y; michael@0: } else { michael@0: childRect.x = oldRect.x; michael@0: } michael@0: } michael@0: michael@0: // We computed a childRect. Now we want to set the bounds of the child to be that rect. michael@0: // If our old rect is different, then we know our size changed and we cache that fact michael@0: // in the |sizeChanged| variable. michael@0: michael@0: child->SetBounds(aState, childRect); michael@0: bool sizeChanged = (childRect.width != oldRect.width || michael@0: childRect.height != oldRect.height); michael@0: michael@0: if (sizeChanged) { michael@0: // Our size is different. Sanity check against our maximum allowed size to ensure michael@0: // we didn't exceed it. michael@0: nsSize minSize = child->GetMinSize(aState); michael@0: nsSize maxSize = child->GetMaxSize(aState); michael@0: maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize); michael@0: michael@0: // make sure the size is in our max size. michael@0: if (childRect.width > maxSize.width) michael@0: childRect.width = maxSize.width; michael@0: michael@0: if (childRect.height > maxSize.height) michael@0: childRect.height = maxSize.height; michael@0: michael@0: // set it again michael@0: child->SetBounds(aState, childRect); michael@0: } michael@0: michael@0: // If we already determined that layout was required or if our size has changed, then michael@0: // we make sure to call layout on the child, since its children may need to be shifted michael@0: // around as a result of the size change. michael@0: if (layout || sizeChanged) michael@0: child->Layout(aState); michael@0: michael@0: // If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout. michael@0: // We have to check for this. michael@0: nsRect newChildRect(child->GetRect()); michael@0: michael@0: if (!newChildRect.IsEqualInterior(childRect)) { michael@0: #ifdef DEBUG_GROW michael@0: child->DumpBox(stdout); michael@0: printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height); michael@0: #endif michael@0: newChildRect.Inflate(margin); michael@0: childRect.Inflate(margin); michael@0: michael@0: // The child changed size during layout. The ChildResized method handles this michael@0: // scenario. michael@0: ChildResized(aBox, michael@0: aState, michael@0: child, michael@0: childBoxSize, michael@0: childComputedBoxSize, michael@0: boxSizes, michael@0: computedBoxSizes, michael@0: childRect, michael@0: newChildRect, michael@0: clientRect, michael@0: flexes, michael@0: finished); michael@0: michael@0: // We note that a child changed size, which means that another pass will be required. michael@0: childResized = true; michael@0: michael@0: // Now that a child resized, it's entirely possible that OUR rect is too small. Now we michael@0: // ensure that |originalClientRect| is grown to accommodate the size of |clientRect|. michael@0: if (clientRect.width > originalClientRect.width) michael@0: originalClientRect.width = clientRect.width; michael@0: michael@0: if (clientRect.height > originalClientRect.height) michael@0: originalClientRect.height = clientRect.height; michael@0: michael@0: if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { michael@0: // Our childRect had its XMost() or YMost() (depending on our layout michael@0: // direction), positioned at a certain point. Ensure that the michael@0: // newChildRect satisfies the same constraint. Note that this is michael@0: // just equivalent to adjusting the x/y by the difference in michael@0: // width/height between childRect and newChildRect. So we don't need michael@0: // to reaccount for the left and right of the box layout state again. michael@0: if (frameState & NS_STATE_IS_HORIZONTAL) michael@0: newChildRect.x = childRect.XMost() - newChildRect.width; michael@0: else michael@0: newChildRect.y = childRect.YMost() - newChildRect.height; michael@0: } michael@0: michael@0: // If the child resized then recompute its position. michael@0: ComputeChildsNextPosition(aBox, x, y, nextX, nextY, newChildRect); michael@0: michael@0: if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom) michael@0: newChildRect.Deflate(margin); michael@0: michael@0: if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom) michael@0: childRect.Deflate(margin); michael@0: michael@0: child->SetBounds(aState, newChildRect); michael@0: michael@0: // If we are the first box that changed size, then we don't need to do a second pass michael@0: if (count == 0) michael@0: finished = true; michael@0: } michael@0: michael@0: // Now update our x/y finally. michael@0: x = nextX; michael@0: y = nextY; michael@0: michael@0: // Move to the next child. michael@0: childComputedBoxSize = childComputedBoxSize->next; michael@0: childBoxSize = childBoxSize->next; michael@0: michael@0: child = child->GetNextBox(); michael@0: count++; michael@0: } michael@0: michael@0: // Sanity-checking code to ensure we don't do an infinite # of passes. michael@0: passes++; michael@0: NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!"); michael@0: if (passes > 10) michael@0: break; michael@0: } while (false == finished); michael@0: michael@0: // Get rid of our size lists. michael@0: while(boxSizes) michael@0: { michael@0: nsBoxSize* toDelete = boxSizes; michael@0: boxSizes = boxSizes->next; michael@0: delete toDelete; michael@0: } michael@0: michael@0: while(computedBoxSizes) michael@0: { michael@0: nsComputedBoxSize* toDelete = computedBoxSizes; michael@0: computedBoxSizes = computedBoxSizes->next; michael@0: delete toDelete; michael@0: } michael@0: michael@0: if (childResized) { michael@0: // See if one of our children forced us to get bigger michael@0: nsRect tmpClientRect(originalClientRect); michael@0: nsMargin bp(0,0,0,0); michael@0: aBox->GetBorderAndPadding(bp); michael@0: tmpClientRect.Inflate(bp); michael@0: michael@0: if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height) michael@0: { michael@0: // if it did reset our bounds. michael@0: nsRect bounds(aBox->GetRect()); michael@0: if (tmpClientRect.width > originalSize.width) michael@0: bounds.width = tmpClientRect.width; michael@0: michael@0: if (tmpClientRect.height > originalSize.height) michael@0: bounds.height = tmpClientRect.height; michael@0: michael@0: aBox->SetBounds(aState, bounds); michael@0: } michael@0: } michael@0: michael@0: // Because our size grew, we now have to readjust because of box packing. Repack michael@0: // in order to update our x and y to the correct values. michael@0: HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); michael@0: michael@0: // Compare against our original x and y and only worry about adjusting the children if michael@0: // we really did have to change the positions because of packing (typically for 'center' michael@0: // or 'end' pack values). michael@0: if (x != origX || y != origY) { michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: // reposition all our children michael@0: while (child) michael@0: { michael@0: nsRect childRect(child->GetRect()); michael@0: childRect.x += (x - origX); michael@0: childRect.y += (y - origY); michael@0: child->SetBounds(aState, childRect); michael@0: child = child->GetNextBox(); michael@0: } michael@0: } michael@0: michael@0: // Perform out-of-axis alignment for non-stretch alignments michael@0: if (!(frameState & NS_STATE_AUTO_STRETCH)) { michael@0: AlignChildren(aBox, aState); michael@0: } michael@0: michael@0: // That's it! If you made it this far without having a nervous breakdown, michael@0: // congratulations! Go get yourself a beer. michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::PopulateBoxSizes(nsIFrame* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, int32_t& aFlexes) michael@0: { michael@0: // used for the equal size flag michael@0: nscoord biggestPrefWidth = 0; michael@0: nscoord biggestMinWidth = 0; michael@0: nscoord smallestMaxWidth = NS_INTRINSICSIZE; michael@0: michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: michael@0: //if (frameState & NS_STATE_CURRENTLY_IN_DEBUG) michael@0: // printf("In debug\n"); michael@0: michael@0: aMinSize = 0; michael@0: aMaxSize = NS_INTRINSICSIZE; michael@0: michael@0: bool isHorizontal; michael@0: michael@0: if (IsHorizontal(aBox)) michael@0: isHorizontal = true; michael@0: else michael@0: isHorizontal = false; michael@0: michael@0: // this is a nice little optimization michael@0: // it turns out that if we only have 1 flexable child michael@0: // then it does not matter what its preferred size is michael@0: // there is nothing to flex it relative. This is great michael@0: // because we can avoid asking for a preferred size in this michael@0: // case. Why is this good? Well you might have html inside it michael@0: // and asking html for its preferred size is rather expensive. michael@0: // so we can just optimize it out this way. michael@0: michael@0: // set flexes michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: aFlexes = 0; michael@0: nsBoxSize* currentBox = nullptr; michael@0: michael@0: #if 0 michael@0: nsBoxSize* start = aBoxSizes; michael@0: michael@0: while(child) michael@0: { michael@0: // ok if we started with a list move down the list michael@0: // until we reach the end. Then start looking at childen. michael@0: // This feature is used extensively for Grid. michael@0: nscoord flex = 0; michael@0: michael@0: if (!start) { michael@0: if (!currentBox) { michael@0: aBoxSizes = new (aState) nsBoxSize(); michael@0: currentBox = aBoxSizes; michael@0: } else { michael@0: currentBox->next = new (aState) nsBoxSize(); michael@0: currentBox = currentBox->next; michael@0: } michael@0: michael@0: michael@0: flex = child->GetFlex(aState); michael@0: michael@0: currentBox->flex = flex; michael@0: currentBox->collapsed = child->IsCollapsed(); michael@0: } else { michael@0: flex = start->flex; michael@0: start = start->next; michael@0: } michael@0: michael@0: if (flex > 0) michael@0: aFlexes++; michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: #endif michael@0: michael@0: // get pref, min, max michael@0: child = aBox->GetChildBox(); michael@0: currentBox = aBoxSizes; michael@0: nsBoxSize* last = nullptr; michael@0: michael@0: nscoord maxFlex = 0; michael@0: int32_t childCount = 0; michael@0: michael@0: while(child) michael@0: { michael@0: while (currentBox && currentBox->bogus) { michael@0: last = currentBox; michael@0: currentBox = currentBox->next; michael@0: } michael@0: ++childCount; michael@0: nsSize pref(0,0); michael@0: nsSize minSize(0,0); michael@0: nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE); michael@0: nscoord ascent = 0; michael@0: bool collapsed = child->IsCollapsed(); michael@0: michael@0: if (!collapsed) { michael@0: // only one flexible child? Cool we will just make its preferred size michael@0: // 0 then and not even have to ask for it. michael@0: //if (flexes != 1) { michael@0: michael@0: pref = child->GetPrefSize(aState); michael@0: minSize = child->GetMinSize(aState); michael@0: maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetMaxSize(aState)); michael@0: ascent = child->GetBoxAscent(aState); michael@0: nsMargin margin; michael@0: child->GetMargin(margin); michael@0: ascent += margin.top; michael@0: //} michael@0: michael@0: pref = nsBox::BoundsCheck(minSize, pref, maxSize); michael@0: michael@0: AddMargin(child, pref); michael@0: AddMargin(child, minSize); michael@0: AddMargin(child, maxSize); michael@0: } michael@0: michael@0: if (!currentBox) { michael@0: // create one. michael@0: currentBox = new (aState) nsBoxSize(); michael@0: if (!aBoxSizes) { michael@0: aBoxSizes = currentBox; michael@0: last = aBoxSizes; michael@0: } else { michael@0: last->next = currentBox; michael@0: last = currentBox; michael@0: } michael@0: michael@0: nscoord minWidth; michael@0: nscoord maxWidth; michael@0: nscoord prefWidth; michael@0: michael@0: // get sizes from child michael@0: if (isHorizontal) { michael@0: minWidth = minSize.width; michael@0: maxWidth = maxSize.width; michael@0: prefWidth = pref.width; michael@0: } else { michael@0: minWidth = minSize.height; michael@0: maxWidth = maxSize.height; michael@0: prefWidth = pref.height; michael@0: } michael@0: michael@0: nscoord flex = child->GetFlex(aState); michael@0: michael@0: // set them if you collapsed you are not flexible. michael@0: if (collapsed) { michael@0: currentBox->flex = 0; michael@0: } michael@0: else { michael@0: if (flex > maxFlex) { michael@0: maxFlex = flex; michael@0: } michael@0: currentBox->flex = flex; michael@0: } michael@0: michael@0: // we specified all our children are equal size; michael@0: if (frameState & NS_STATE_EQUAL_SIZE) { michael@0: michael@0: if (prefWidth > biggestPrefWidth) michael@0: biggestPrefWidth = prefWidth; michael@0: michael@0: if (minWidth > biggestMinWidth) michael@0: biggestMinWidth = minWidth; michael@0: michael@0: if (maxWidth < smallestMaxWidth) michael@0: smallestMaxWidth = maxWidth; michael@0: } else { // not we can set our children right now. michael@0: currentBox->pref = prefWidth; michael@0: currentBox->min = minWidth; michael@0: currentBox->max = maxWidth; michael@0: } michael@0: michael@0: NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!"); michael@0: michael@0: } michael@0: michael@0: if (!isHorizontal) { michael@0: if (minSize.width > aMinSize) michael@0: aMinSize = minSize.width; michael@0: michael@0: if (maxSize.width < aMaxSize) michael@0: aMaxSize = maxSize.width; michael@0: michael@0: } else { michael@0: if (minSize.height > aMinSize) michael@0: aMinSize = minSize.height; michael@0: michael@0: if (maxSize.height < aMaxSize) michael@0: aMaxSize = maxSize.height; michael@0: } michael@0: michael@0: currentBox->collapsed = collapsed; michael@0: aFlexes += currentBox->flex; michael@0: michael@0: child = child->GetNextBox(); michael@0: michael@0: last = currentBox; michael@0: currentBox = currentBox->next; michael@0: michael@0: } michael@0: michael@0: if (childCount > 0) { michael@0: nscoord maxAllowedFlex = nscoord_MAX / childCount; michael@0: michael@0: if (MOZ_UNLIKELY(maxFlex > maxAllowedFlex)) { michael@0: // clamp all the flexes michael@0: currentBox = aBoxSizes; michael@0: while (currentBox) { michael@0: currentBox->flex = std::min(currentBox->flex, maxAllowedFlex); michael@0: currentBox = currentBox->next; michael@0: } michael@0: } michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: NS_ASSERTION(maxFlex == 0, "How did that happen?"); michael@0: } michael@0: #endif michael@0: michael@0: // we specified all our children are equal size; michael@0: if (frameState & NS_STATE_EQUAL_SIZE) { michael@0: smallestMaxWidth = std::max(smallestMaxWidth, biggestMinWidth); michael@0: biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth); michael@0: michael@0: currentBox = aBoxSizes; michael@0: michael@0: while(currentBox) michael@0: { michael@0: if (!currentBox->collapsed) { michael@0: currentBox->pref = biggestPrefWidth; michael@0: currentBox->min = biggestMinWidth; michael@0: currentBox->max = smallestMaxWidth; michael@0: } else { michael@0: currentBox->pref = 0; michael@0: currentBox->min = 0; michael@0: currentBox->max = 0; michael@0: } michael@0: currentBox = currentBox->next; michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::ComputeChildsNextPosition(nsIFrame* aBox, michael@0: const nscoord& aCurX, michael@0: const nscoord& aCurY, michael@0: nscoord& aNextX, michael@0: nscoord& aNextY, michael@0: const nsRect& aCurrentChildSize) michael@0: { michael@0: // Get the position along the box axis for the child. michael@0: // The out-of-axis position is not set. michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: michael@0: if (IsHorizontal(aBox)) { michael@0: // horizontal box's children. michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aNextX = aCurX + aCurrentChildSize.width; michael@0: else michael@0: aNextX = aCurX - aCurrentChildSize.width; michael@0: michael@0: } else { michael@0: // vertical box's children. michael@0: if (frameState & NS_STATE_IS_DIRECTION_NORMAL) michael@0: aNextY = aCurY + aCurrentChildSize.height; michael@0: else michael@0: aNextY = aCurY - aCurrentChildSize.height; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::AlignChildren(nsIFrame* aBox, michael@0: nsBoxLayoutState& aState) michael@0: { michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: bool isHorizontal = (frameState & NS_STATE_IS_HORIZONTAL) != 0; michael@0: nsRect clientRect; michael@0: aBox->GetClientRect(clientRect); michael@0: michael@0: NS_PRECONDITION(!(frameState & NS_STATE_AUTO_STRETCH), michael@0: "Only AlignChildren() with non-stretch alignment"); michael@0: michael@0: // These are only calculated if needed michael@0: nsIFrame::Halignment halign; michael@0: nsIFrame::Valignment valign; michael@0: nscoord maxAscent; michael@0: bool isLTR; michael@0: michael@0: if (isHorizontal) { michael@0: valign = aBox->GetVAlign(); michael@0: if (valign == nsBoxFrame::vAlign_BaseLine) { michael@0: maxAscent = aBox->GetBoxAscent(aState); michael@0: } michael@0: } else { michael@0: isLTR = GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR; michael@0: halign = aBox->GetHAlign(); michael@0: } michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: while (child) { michael@0: michael@0: nsMargin margin; michael@0: child->GetMargin(margin); michael@0: nsRect childRect = child->GetRect(); michael@0: michael@0: if (isHorizontal) { michael@0: const nscoord startAlign = clientRect.y + margin.top; michael@0: const nscoord endAlign = michael@0: clientRect.YMost() - margin.bottom - childRect.height; michael@0: michael@0: nscoord y; michael@0: switch (valign) { michael@0: case nsBoxFrame::vAlign_Top: michael@0: y = startAlign; michael@0: break; michael@0: case nsBoxFrame::vAlign_Middle: michael@0: // Should this center the border box? michael@0: // This centers the margin box, the historical behavior. michael@0: y = (startAlign + endAlign) / 2; michael@0: break; michael@0: case nsBoxFrame::vAlign_Bottom: michael@0: y = endAlign; michael@0: break; michael@0: case nsBoxFrame::vAlign_BaseLine: michael@0: // Alignments don't force the box to grow (only sizes do), michael@0: // so keep the children within the box. michael@0: y = maxAscent - child->GetBoxAscent(aState); michael@0: y = std::max(startAlign, y); michael@0: y = std::min(y, endAlign); michael@0: break; michael@0: } michael@0: michael@0: childRect.y = y; michael@0: michael@0: } else { // vertical box michael@0: const nscoord leftAlign = clientRect.x + margin.left; michael@0: const nscoord rightAlign = michael@0: clientRect.XMost() - margin.right - childRect.width; michael@0: michael@0: nscoord x; michael@0: switch (halign) { michael@0: case nsBoxFrame::hAlign_Left: // start michael@0: x = isLTR ? leftAlign : rightAlign; michael@0: break; michael@0: case nsBoxFrame::hAlign_Center: michael@0: x = (leftAlign + rightAlign) / 2; michael@0: break; michael@0: case nsBoxFrame::hAlign_Right: // end michael@0: x = isLTR ? rightAlign : leftAlign; michael@0: break; michael@0: } michael@0: michael@0: childRect.x = x; michael@0: } michael@0: michael@0: if (childRect.TopLeft() != child->GetPosition()) { michael@0: child->SetBounds(aState, childRect); michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::ChildResized(nsIFrame* aBox, michael@0: nsBoxLayoutState& aState, michael@0: nsIFrame* aChild, michael@0: nsBoxSize* aChildBoxSize, michael@0: nsComputedBoxSize* aChildComputedSize, michael@0: nsBoxSize* aBoxSizes, michael@0: nsComputedBoxSize* aComputedBoxSizes, michael@0: const nsRect& aChildLayoutRect, michael@0: nsRect& aChildActualRect, michael@0: nsRect& aContainingRect, michael@0: int32_t aFlexes, michael@0: bool& aFinished) michael@0: michael@0: { michael@0: nsRect childCurrentRect(aChildLayoutRect); michael@0: michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal); michael@0: nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal); michael@0: nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal); michael@0: michael@0: //nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal); michael@0: nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal); michael@0: nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal); michael@0: michael@0: bool recompute = false; michael@0: michael@0: // if we are a horizontal box see if the child will fit inside us. michael@0: if ( childActualHeight > containingHeight) { michael@0: // if we are a horizontal box and the child is bigger than our height michael@0: michael@0: // ok if the height changed then we need to reflow everyone but us at the new height michael@0: // so we will set the changed index to be us. And signal that we need a new pass. michael@0: michael@0: nsSize min = aChild->GetMinSize(aState); michael@0: nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); michael@0: AddMargin(aChild, max); michael@0: michael@0: if (isHorizontal) michael@0: childActualHeight = max.height < childActualHeight ? max.height : childActualHeight; michael@0: else michael@0: childActualHeight = max.width < childActualHeight ? max.width : childActualHeight; michael@0: michael@0: // only set if it changes michael@0: if (childActualHeight > containingHeight) { michael@0: containingHeight = childActualHeight; michael@0: michael@0: // remember we do not need to clear the resized list because changing the height of a horizontal box michael@0: // will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me michael@0: // on this one. michael@0: aFinished = false; michael@0: michael@0: // only recompute if there are flexes. michael@0: if (aFlexes > 0) { michael@0: // relayout everything michael@0: recompute = true; michael@0: InvalidateComputedSizes(aComputedBoxSizes); michael@0: nsComputedBoxSize* node = aComputedBoxSizes; michael@0: michael@0: while(node) { michael@0: node->resized = false; michael@0: node = node->next; michael@0: } michael@0: michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (childActualWidth > childLayoutWidth) { michael@0: nsSize min = aChild->GetMinSize(aState); michael@0: nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); michael@0: michael@0: AddMargin(aChild, max); michael@0: michael@0: // our width now becomes the new size michael@0: michael@0: if (isHorizontal) michael@0: childActualWidth = max.width < childActualWidth ? max.width : childActualWidth; michael@0: else michael@0: childActualWidth = max.height < childActualWidth ? max.height : childActualWidth; michael@0: michael@0: if (childActualWidth > childLayoutWidth) { michael@0: aChildComputedSize->size = childActualWidth; michael@0: aChildBoxSize->min = childActualWidth; michael@0: if (aChildBoxSize->pref < childActualWidth) michael@0: aChildBoxSize->pref = childActualWidth; michael@0: if (aChildBoxSize->max < childActualWidth) michael@0: aChildBoxSize->max = childActualWidth; michael@0: michael@0: // if we have flexible elements with us then reflex things. Otherwise we can skip doing it. michael@0: if (aFlexes > 0) { michael@0: InvalidateComputedSizes(aComputedBoxSizes); michael@0: michael@0: nsComputedBoxSize* node = aComputedBoxSizes; michael@0: aChildComputedSize->resized = true; michael@0: michael@0: while(node) { michael@0: if (node->resized) michael@0: node->valid = true; michael@0: michael@0: node = node->next; michael@0: } michael@0: michael@0: recompute = true; michael@0: aFinished = false; michael@0: } else { michael@0: containingWidth += aChildComputedSize->size - childLayoutWidth; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (recompute) michael@0: ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes); michael@0: michael@0: if (!childCurrentRect.IsEqualInterior(aChildActualRect)) { michael@0: // the childRect includes the margin michael@0: // make sure we remove it before setting michael@0: // the bounds. michael@0: nsMargin margin(0,0,0,0); michael@0: aChild->GetMargin(margin); michael@0: nsRect rect(aChildActualRect); michael@0: if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom) michael@0: rect.Deflate(margin); michael@0: michael@0: aChild->SetBounds(aState, rect); michael@0: aChild->Layout(aState); michael@0: } michael@0: michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes) michael@0: { michael@0: while(aComputedBoxSizes) { michael@0: aComputedBoxSizes->valid = false; michael@0: aComputedBoxSizes = aComputedBoxSizes->next; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::ComputeChildSizes(nsIFrame* aBox, michael@0: nsBoxLayoutState& aState, michael@0: nscoord& aGivenSize, michael@0: nsBoxSize* aBoxSizes, michael@0: nsComputedBoxSize*& aComputedBoxSizes) michael@0: { michael@0: michael@0: //nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1); michael@0: michael@0: int32_t sizeRemaining = aGivenSize; michael@0: int32_t spacerConstantsRemaining = 0; michael@0: michael@0: // ----- calculate the spacers constants and the size remaining ----- michael@0: michael@0: if (!aComputedBoxSizes) michael@0: aComputedBoxSizes = new (aState) nsComputedBoxSize(); michael@0: michael@0: nsBoxSize* boxSizes = aBoxSizes; michael@0: nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes; michael@0: int32_t count = 0; michael@0: int32_t validCount = 0; michael@0: michael@0: while (boxSizes) michael@0: { michael@0: michael@0: NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size"); michael@0: michael@0: michael@0: // ignore collapsed children michael@0: // if (boxSizes->collapsed) michael@0: // { michael@0: // computedBoxSizes->valid = true; michael@0: // computedBoxSizes->size = boxSizes->pref; michael@0: // validCount++; michael@0: // boxSizes->flex = 0; michael@0: // }// else { michael@0: michael@0: if (computedBoxSizes->valid) { michael@0: sizeRemaining -= computedBoxSizes->size; michael@0: validCount++; michael@0: } else { michael@0: if (boxSizes->flex == 0) michael@0: { michael@0: computedBoxSizes->valid = true; michael@0: computedBoxSizes->size = boxSizes->pref; michael@0: validCount++; michael@0: } michael@0: michael@0: spacerConstantsRemaining += boxSizes->flex; michael@0: sizeRemaining -= boxSizes->pref; michael@0: } michael@0: michael@0: sizeRemaining -= (boxSizes->left + boxSizes->right); michael@0: michael@0: //} michael@0: michael@0: boxSizes = boxSizes->next; michael@0: michael@0: if (boxSizes && !computedBoxSizes->next) michael@0: computedBoxSizes->next = new (aState) nsComputedBoxSize(); michael@0: michael@0: computedBoxSizes = computedBoxSizes->next; michael@0: count++; michael@0: } michael@0: michael@0: // everything accounted for? michael@0: if (validCount < count) michael@0: { michael@0: // ----- Ok we are give a size to fit into so stretch or squeeze to fit michael@0: // ----- Make sure we look at our min and max size michael@0: bool limit = true; michael@0: for (int pass=1; true == limit; pass++) michael@0: { michael@0: limit = false; michael@0: boxSizes = aBoxSizes; michael@0: computedBoxSizes = aComputedBoxSizes; michael@0: michael@0: while (boxSizes) { michael@0: michael@0: // ignore collapsed spacers michael@0: michael@0: // if (!boxSizes->collapsed) { michael@0: michael@0: nscoord pref = 0; michael@0: nscoord max = NS_INTRINSICSIZE; michael@0: nscoord min = 0; michael@0: nscoord flex = 0; michael@0: michael@0: pref = boxSizes->pref; michael@0: min = boxSizes->min; michael@0: max = boxSizes->max; michael@0: flex = boxSizes->flex; michael@0: michael@0: // ----- look at our min and max limits make sure we aren't too small or too big ----- michael@0: if (!computedBoxSizes->valid) { michael@0: int32_t newSize = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); michael@0: michael@0: if (newSize<=min) { michael@0: computedBoxSizes->size = min; michael@0: computedBoxSizes->valid = true; michael@0: spacerConstantsRemaining -= flex; michael@0: sizeRemaining += pref; michael@0: sizeRemaining -= min; michael@0: limit = true; michael@0: } else if (newSize>=max) { michael@0: computedBoxSizes->size = max; michael@0: computedBoxSizes->valid = true; michael@0: spacerConstantsRemaining -= flex; michael@0: sizeRemaining += pref; michael@0: sizeRemaining -= max; michael@0: limit = true; michael@0: } michael@0: } michael@0: // } michael@0: boxSizes = boxSizes->next; michael@0: computedBoxSizes = computedBoxSizes->next; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ---- once we have removed and min and max issues just stretch us out in the remaining space michael@0: // ---- or shrink us. Depends on the size remaining and the spacer constants michael@0: aGivenSize = 0; michael@0: boxSizes = aBoxSizes; michael@0: computedBoxSizes = aComputedBoxSizes; michael@0: michael@0: while (boxSizes) { michael@0: michael@0: // ignore collapsed spacers michael@0: // if (!(boxSizes && boxSizes->collapsed)) { michael@0: michael@0: nscoord pref = 0; michael@0: nscoord flex = 0; michael@0: pref = boxSizes->pref; michael@0: flex = boxSizes->flex; michael@0: michael@0: if (!computedBoxSizes->valid) { michael@0: computedBoxSizes->size = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); michael@0: computedBoxSizes->valid = true; michael@0: } michael@0: michael@0: aGivenSize += (boxSizes->left + boxSizes->right); michael@0: aGivenSize += computedBoxSizes->size; michael@0: michael@0: // } michael@0: michael@0: boxSizes = boxSizes->next; michael@0: computedBoxSizes = computedBoxSizes->next; michael@0: } michael@0: } michael@0: michael@0: michael@0: nsSize michael@0: nsSprocketLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: nsSize vpref (0, 0); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: nscoord biggestPref = 0; michael@0: michael@0: // run through all the children and get their min, max, and preferred sizes michael@0: // return us the size of the box michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); michael@0: int32_t count = 0; michael@0: michael@0: while (child) michael@0: { michael@0: // ignore collapsed children michael@0: if (!child->IsCollapsed()) michael@0: { michael@0: nsSize pref = child->GetPrefSize(aState); michael@0: AddMargin(child, pref); michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) michael@0: { michael@0: if (pref.width > biggestPref) michael@0: biggestPref = pref.width; michael@0: } else { michael@0: if (pref.height > biggestPref) michael@0: biggestPref = pref.height; michael@0: } michael@0: } michael@0: michael@0: AddLargestSize(vpref, pref, isHorizontal); michael@0: count++; michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) michael@0: vpref.width = biggestPref*count; michael@0: else michael@0: vpref.height = biggestPref*count; michael@0: } michael@0: michael@0: // now add our border and padding michael@0: AddBorderAndPadding(aBox, vpref); michael@0: michael@0: return vpref; michael@0: } michael@0: michael@0: nsSize michael@0: nsSprocketLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: nsSize minSize (0, 0); michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: nscoord biggestMin = 0; michael@0: michael@0: michael@0: // run through all the children and get their min, max, and preferred sizes michael@0: // return us the size of the box michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); michael@0: int32_t count = 0; michael@0: michael@0: while (child) michael@0: { michael@0: // ignore collapsed children michael@0: if (!child->IsCollapsed()) michael@0: { michael@0: nsSize min = child->GetMinSize(aState); michael@0: nsSize pref(0,0); michael@0: michael@0: // if the child is not flexible then michael@0: // its min size is its pref size. michael@0: if (child->GetFlex(aState) == 0) { michael@0: pref = child->GetPrefSize(aState); michael@0: if (isHorizontal) michael@0: min.width = pref.width; michael@0: else michael@0: min.height = pref.height; michael@0: } michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) michael@0: { michael@0: if (min.width > biggestMin) michael@0: biggestMin = min.width; michael@0: } else { michael@0: if (min.height > biggestMin) michael@0: biggestMin = min.height; michael@0: } michael@0: } michael@0: michael@0: AddMargin(child, min); michael@0: AddLargestSize(minSize, min, isHorizontal); michael@0: count++; michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) michael@0: minSize.width = biggestMin*count; michael@0: else michael@0: minSize.height = biggestMin*count; michael@0: } michael@0: michael@0: // now add our border and padding michael@0: AddBorderAndPadding(aBox, minSize); michael@0: michael@0: return minSize; michael@0: } michael@0: michael@0: nsSize michael@0: nsSprocketLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: nscoord smallestMax = NS_INTRINSICSIZE; michael@0: nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: michael@0: // run through all the children and get their min, max, and preferred sizes michael@0: // return us the size of the box michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: nsFrameState frameState = nsFrameState(0); michael@0: GetFrameState(aBox, frameState); michael@0: bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); michael@0: int32_t count = 0; michael@0: michael@0: while (child) michael@0: { michael@0: // ignore collapsed children michael@0: if (!child->IsCollapsed()) michael@0: { michael@0: // if completely redefined don't even ask our child for its size. michael@0: nsSize min = child->GetMinSize(aState); michael@0: nsSize max = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState)); michael@0: michael@0: AddMargin(child, max); michael@0: AddSmallestSize(maxSize, max, isHorizontal); michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) michael@0: { michael@0: if (max.width < smallestMax) michael@0: smallestMax = max.width; michael@0: } else { michael@0: if (max.height < smallestMax) michael@0: smallestMax = max.height; michael@0: } michael@0: } michael@0: count++; michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: michael@0: if (isEqual) { michael@0: if (isHorizontal) { michael@0: if (smallestMax != NS_INTRINSICSIZE) michael@0: maxSize.width = smallestMax*count; michael@0: else michael@0: maxSize.width = NS_INTRINSICSIZE; michael@0: } else { michael@0: if (smallestMax != NS_INTRINSICSIZE) michael@0: maxSize.height = smallestMax*count; michael@0: else michael@0: maxSize.height = NS_INTRINSICSIZE; michael@0: } michael@0: } michael@0: michael@0: // now add our border and padding michael@0: AddBorderAndPadding(aBox, maxSize); michael@0: michael@0: return maxSize; michael@0: } michael@0: michael@0: michael@0: nscoord michael@0: nsSprocketLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: nscoord vAscent = 0; michael@0: michael@0: bool isHorizontal = IsHorizontal(aBox); michael@0: michael@0: // run through all the children and get their min, max, and preferred sizes michael@0: // return us the size of the box michael@0: michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: while (child) michael@0: { michael@0: // ignore collapsed children michael@0: //if (!child->IsCollapsed()) michael@0: //{ michael@0: // if completely redefined don't even ask our child for its size. michael@0: nscoord ascent = child->GetBoxAscent(aState); michael@0: michael@0: nsMargin margin; michael@0: child->GetMargin(margin); michael@0: ascent += margin.top; michael@0: michael@0: if (isHorizontal) michael@0: { michael@0: if (ascent > vAscent) michael@0: vAscent = ascent; michael@0: } else { michael@0: if (vAscent == 0) michael@0: vAscent = ascent; michael@0: } michael@0: //} michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: michael@0: nsMargin borderPadding; michael@0: aBox->GetBorderAndPadding(borderPadding); michael@0: michael@0: return vAscent + borderPadding.top; michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) michael@0: { michael@0: if (aIsHorizontal) michael@0: { michael@0: if (aSize1.height < aSize2.height) michael@0: aSize1.height = aSize2.height; michael@0: } else { michael@0: if (aSize1.width < aSize2.width) michael@0: aSize1.width = aSize2.width; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) michael@0: { michael@0: if (aIsHorizontal) michael@0: { michael@0: if (aSize1.height > aSize2.height) michael@0: aSize1.height = aSize2.height; michael@0: } else { michael@0: if (aSize1.width > aSize2.width) michael@0: aSize1.width = aSize2.width; michael@0: michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) michael@0: { michael@0: if (aIsHorizontal) michael@0: AddCoord(aSize.width, aSizeToAdd.width); michael@0: else michael@0: AddCoord(aSize.height, aSizeToAdd.height); michael@0: michael@0: SetLargestSize(aSize, aSizeToAdd, aIsHorizontal); michael@0: } michael@0: michael@0: void michael@0: nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd) michael@0: { michael@0: if (aCoord != NS_INTRINSICSIZE) michael@0: { michael@0: if (aCoordToAdd == NS_INTRINSICSIZE) michael@0: aCoord = aCoordToAdd; michael@0: else michael@0: aCoord += aCoordToAdd; michael@0: } michael@0: } michael@0: void michael@0: nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) michael@0: { michael@0: if (aIsHorizontal) michael@0: AddCoord(aSize.width, aSizeToAdd.width); michael@0: else michael@0: AddCoord(aSize.height, aSizeToAdd.height); michael@0: michael@0: SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal); michael@0: } michael@0: michael@0: bool michael@0: nsSprocketLayout::GetDefaultFlex(int32_t& aFlex) michael@0: { michael@0: aFlex = 0; michael@0: return true; michael@0: } michael@0: michael@0: nsComputedBoxSize::nsComputedBoxSize() michael@0: { michael@0: resized = false; michael@0: valid = false; michael@0: size = 0; michael@0: next = nullptr; michael@0: } michael@0: michael@0: nsBoxSize::nsBoxSize() michael@0: { michael@0: pref = 0; michael@0: min = 0; michael@0: max = NS_INTRINSICSIZE; michael@0: collapsed = false; michael@0: left = 0; michael@0: right = 0; michael@0: flex = 0; michael@0: next = nullptr; michael@0: bogus = false; michael@0: } michael@0: michael@0: michael@0: void* michael@0: nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW michael@0: { michael@0: return mozilla::AutoStackArena::Allocate(sz); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsBoxSize::operator delete(void* aPtr, size_t sz) michael@0: { michael@0: } michael@0: michael@0: michael@0: void* michael@0: nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW michael@0: { michael@0: return mozilla::AutoStackArena::Allocate(sz); michael@0: } michael@0: michael@0: void michael@0: nsComputedBoxSize::operator delete(void* aPtr, size_t sz) michael@0: { michael@0: }