1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/nsSprocketLayout.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1643 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// 1.10 +// Eric Vaughan 1.11 +// Netscape Communications 1.12 +// 1.13 +// See documentation in associated header file 1.14 +// 1.15 + 1.16 +#include "nsBoxLayoutState.h" 1.17 +#include "nsSprocketLayout.h" 1.18 +#include "nsPresContext.h" 1.19 +#include "nsCOMPtr.h" 1.20 +#include "nsIContent.h" 1.21 +#include "nsIPresShell.h" 1.22 +#include "nsContainerFrame.h" 1.23 +#include "nsBoxFrame.h" 1.24 +#include "StackArena.h" 1.25 +#include "mozilla/Likely.h" 1.26 +#include <algorithm> 1.27 + 1.28 +nsBoxLayout* nsSprocketLayout::gInstance = nullptr; 1.29 + 1.30 +//#define DEBUG_GROW 1.31 + 1.32 +#define DEBUG_SPRING_SIZE 8 1.33 +#define DEBUG_BORDER_SIZE 2 1.34 +#define COIL_SIZE 8 1.35 + 1.36 + 1.37 +nsresult 1.38 +NS_NewSprocketLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout) 1.39 +{ 1.40 + if (!nsSprocketLayout::gInstance) { 1.41 + nsSprocketLayout::gInstance = new nsSprocketLayout(); 1.42 + NS_IF_ADDREF(nsSprocketLayout::gInstance); 1.43 + } 1.44 + // we have not instance variables so just return our static one. 1.45 + aNewLayout = nsSprocketLayout::gInstance; 1.46 + return NS_OK; 1.47 +} 1.48 + 1.49 +/*static*/ void 1.50 +nsSprocketLayout::Shutdown() 1.51 +{ 1.52 + NS_IF_RELEASE(gInstance); 1.53 +} 1.54 + 1.55 +nsSprocketLayout::nsSprocketLayout() 1.56 +{ 1.57 +} 1.58 + 1.59 +bool 1.60 +nsSprocketLayout::IsHorizontal(nsIFrame* aBox) 1.61 +{ 1.62 + return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0; 1.63 +} 1.64 + 1.65 +void 1.66 +nsSprocketLayout::GetFrameState(nsIFrame* aBox, nsFrameState& aState) 1.67 +{ 1.68 + aState = aBox->GetStateBits(); 1.69 +} 1.70 + 1.71 +static uint8_t 1.72 +GetFrameDirection(nsIFrame* aBox) 1.73 +{ 1.74 + return aBox->StyleVisibility()->mDirection; 1.75 +} 1.76 + 1.77 +static void 1.78 +HandleBoxPack(nsIFrame* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY, 1.79 + const nsRect& aOriginalRect, const nsRect& aClientRect) 1.80 +{ 1.81 + // In the normal direction we lay out our kids in the positive direction (e.g., |x| will get 1.82 + // bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse 1.83 + // direction, the opposite is true. We'll be laying out each child at a smaller |x| or 1.84 + // |y|. 1.85 + uint8_t frameDirection = GetFrameDirection(aBox); 1.86 + 1.87 + if (aFrameState & NS_STATE_IS_HORIZONTAL) { 1.88 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { 1.89 + // The normal direction. |x| increases as we move through our children. 1.90 + aX = aClientRect.x; 1.91 + } 1.92 + else { 1.93 + // The reverse direction. |x| decreases as we move through our children. 1.94 + aX = aClientRect.x + aOriginalRect.width; 1.95 + } 1.96 + // |y| is always in the normal direction in horizontal boxes 1.97 + aY = aClientRect.y; 1.98 + } 1.99 + else { 1.100 + // take direction property into account for |x| in vertical boxes 1.101 + if (frameDirection == NS_STYLE_DIRECTION_LTR) { 1.102 + // The normal direction. |x| increases as we move through our children. 1.103 + aX = aClientRect.x; 1.104 + } 1.105 + else { 1.106 + // The reverse direction. |x| decreases as we move through our children. 1.107 + aX = aClientRect.x + aOriginalRect.width; 1.108 + } 1.109 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { 1.110 + // The normal direction. |y| increases as we move through our children. 1.111 + aY = aClientRect.y; 1.112 + } 1.113 + else { 1.114 + // The reverse direction. |y| decreases as we move through our children. 1.115 + aY = aClientRect.y + aOriginalRect.height; 1.116 + } 1.117 + } 1.118 + 1.119 + // Get our pack/alignment information. 1.120 + nsIFrame::Halignment halign = aBox->GetHAlign(); 1.121 + nsIFrame::Valignment valign = aBox->GetVAlign(); 1.122 + 1.123 + // The following code handles box PACKING. Packing comes into play in the case where the computed size for 1.124 + // all of our children (now stored in our client rect) is smaller than the size available for 1.125 + // the box (stored in |aOriginalRect|). 1.126 + // 1.127 + // Here we adjust our |x| and |y| variables accordingly so that we start at the beginning, 1.128 + // middle, or end of the box. 1.129 + // 1.130 + // XXXdwh JUSTIFY needs to be implemented! 1.131 + if (aFrameState & NS_STATE_IS_HORIZONTAL) { 1.132 + switch(halign) { 1.133 + case nsBoxFrame::hAlign_Left: 1.134 + break; // Nothing to do. The default initialized us properly. 1.135 + 1.136 + case nsBoxFrame::hAlign_Center: 1.137 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) 1.138 + aX += (aOriginalRect.width - aClientRect.width)/2; 1.139 + else 1.140 + aX -= (aOriginalRect.width - aClientRect.width)/2; 1.141 + break; 1.142 + 1.143 + case nsBoxFrame::hAlign_Right: 1.144 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) 1.145 + aX += (aOriginalRect.width - aClientRect.width); 1.146 + else 1.147 + aX -= (aOriginalRect.width - aClientRect.width); 1.148 + break; // Nothing to do for the reverse dir. The default initialized us properly. 1.149 + } 1.150 + } else { 1.151 + switch(valign) { 1.152 + case nsBoxFrame::vAlign_Top: 1.153 + case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack. 1.154 + break; // Don't do anything. We were initialized correctly. 1.155 + 1.156 + case nsBoxFrame::vAlign_Middle: 1.157 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) 1.158 + aY += (aOriginalRect.height - aClientRect.height)/2; 1.159 + else 1.160 + aY -= (aOriginalRect.height - aClientRect.height)/2; 1.161 + break; 1.162 + 1.163 + case nsBoxFrame::vAlign_Bottom: 1.164 + if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) 1.165 + aY += (aOriginalRect.height - aClientRect.height); 1.166 + else 1.167 + aY -= (aOriginalRect.height - aClientRect.height); 1.168 + break; 1.169 + } 1.170 + } 1.171 +} 1.172 + 1.173 +NS_IMETHODIMP 1.174 +nsSprocketLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) 1.175 +{ 1.176 + // See if we are collapsed. If we are, then simply iterate over all our 1.177 + // children and give them a rect of 0 width and height. 1.178 + if (aBox->IsCollapsed()) { 1.179 + nsIFrame* child = aBox->GetChildBox(); 1.180 + while(child) 1.181 + { 1.182 + nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0)); 1.183 + child = child->GetNextBox(); 1.184 + } 1.185 + return NS_OK; 1.186 + } 1.187 + 1.188 + nsBoxLayoutState::AutoReflowDepth depth(aState); 1.189 + mozilla::AutoStackArena arena; 1.190 + 1.191 + // ----- figure out our size ---------- 1.192 + const nsSize originalSize = aBox->GetSize(); 1.193 + 1.194 + // -- make sure we remove our border and padding ---- 1.195 + nsRect clientRect; 1.196 + aBox->GetClientRect(clientRect); 1.197 + 1.198 + // |originalClientRect| represents the rect of the entire box (excluding borders 1.199 + // and padding). We store it here because we're going to use |clientRect| to hold 1.200 + // the required size for all our kids. As an example, consider an hbox with a 1.201 + // specified width of 300. If the kids total only 150 pixels of width, then 1.202 + // we have 150 pixels left over. |clientRect| is going to hold a width of 150 and 1.203 + // is going to be adjusted based off the value of the PACK property. If flexible 1.204 + // objects are in the box, then the two rects will match. 1.205 + nsRect originalClientRect(clientRect); 1.206 + 1.207 + // The frame state contains cached knowledge about our box, such as our orientation 1.208 + // and direction. 1.209 + nsFrameState frameState = nsFrameState(0); 1.210 + GetFrameState(aBox, frameState); 1.211 + 1.212 + // Build a list of our children's desired sizes and computed sizes 1.213 + nsBoxSize* boxSizes = nullptr; 1.214 + nsComputedBoxSize* computedBoxSizes = nullptr; 1.215 + 1.216 + nscoord min = 0; 1.217 + nscoord max = 0; 1.218 + int32_t flexes = 0; 1.219 + PopulateBoxSizes(aBox, aState, boxSizes, min, max, flexes); 1.220 + 1.221 + // The |size| variable will hold the total size of children along the axis of 1.222 + // the box. Continuing with the example begun in the comment above, size would 1.223 + // be 150 pixels. 1.224 + nscoord size = clientRect.width; 1.225 + if (!IsHorizontal(aBox)) 1.226 + size = clientRect.height; 1.227 + ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes); 1.228 + 1.229 + // After the call to ComputeChildSizes, the |size| variable contains the 1.230 + // total required size of all the children. We adjust our clientRect in the 1.231 + // appropriate dimension to match this size. In our example, we now assign 1.232 + // 150 pixels into the clientRect.width. 1.233 + // 1.234 + // The variables |min| and |max| hold the minimum required size box must be 1.235 + // in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum 1.236 + // height we require to enclose our children, and |max| is the maximum height 1.237 + // required to enclose our children. 1.238 + if (IsHorizontal(aBox)) { 1.239 + clientRect.width = size; 1.240 + if (clientRect.height < min) 1.241 + clientRect.height = min; 1.242 + 1.243 + if (frameState & NS_STATE_AUTO_STRETCH) { 1.244 + if (clientRect.height > max) 1.245 + clientRect.height = max; 1.246 + } 1.247 + } else { 1.248 + clientRect.height = size; 1.249 + if (clientRect.width < min) 1.250 + clientRect.width = min; 1.251 + 1.252 + if (frameState & NS_STATE_AUTO_STRETCH) { 1.253 + if (clientRect.width > max) 1.254 + clientRect.width = max; 1.255 + } 1.256 + } 1.257 + 1.258 + // With the sizes computed, now it's time to lay out our children. 1.259 + bool finished; 1.260 + nscoord passes = 0; 1.261 + 1.262 + // We flow children at their preferred locations (along with the appropriate computed flex). 1.263 + // After we flow a child, it is possible that the child will change its size. If/when this happens, 1.264 + // we have to do another pass. Typically only 2 passes are required, but the code is prepared to 1.265 + // do as many passes as are necessary to achieve equilibrium. 1.266 + nscoord x = 0; 1.267 + nscoord y = 0; 1.268 + nscoord origX = 0; 1.269 + nscoord origY = 0; 1.270 + 1.271 + // |childResized| lets us know if a child changed its size after we attempted to lay it out at 1.272 + // the specified size. If this happens, we usually have to do another pass. 1.273 + bool childResized = false; 1.274 + 1.275 + // |passes| stores our number of passes. If for any reason we end up doing more than, say, 10 1.276 + // passes, we assert to indicate that something is seriously screwed up. 1.277 + passes = 0; 1.278 + do 1.279 + { 1.280 +#ifdef DEBUG_REFLOW 1.281 + if (passes > 0) { 1.282 + AddIndents(); 1.283 + printf("ChildResized doing pass: %d\n", passes); 1.284 + } 1.285 +#endif 1.286 + 1.287 + // Always assume that we're done. This will change if, for example, children don't stay 1.288 + // the same size after being flowed. 1.289 + finished = true; 1.290 + 1.291 + // Handle box packing. 1.292 + HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); 1.293 + 1.294 + // Now that packing is taken care of we set up a few additional 1.295 + // tracking variables. 1.296 + origX = x; 1.297 + origY = y; 1.298 + 1.299 + nscoord nextX = x; 1.300 + nscoord nextY = y; 1.301 + 1.302 + // Now we iterate over our box children and our box size lists in 1.303 + // parallel. For each child, we look at its sizes and figure out 1.304 + // where to place it. 1.305 + nsComputedBoxSize* childComputedBoxSize = computedBoxSizes; 1.306 + nsBoxSize* childBoxSize = boxSizes; 1.307 + 1.308 + nsIFrame* child = aBox->GetChildBox(); 1.309 + 1.310 + int32_t count = 0; 1.311 + while (child || (childBoxSize && childBoxSize->bogus)) 1.312 + { 1.313 + // If for some reason, our lists are not the same length, we guard 1.314 + // by bailing out of the loop. 1.315 + if (childBoxSize == nullptr) { 1.316 + NS_NOTREACHED("Lists not the same length."); 1.317 + break; 1.318 + } 1.319 + 1.320 + nscoord width = clientRect.width; 1.321 + nscoord height = clientRect.height; 1.322 + 1.323 + if (!childBoxSize->bogus) { 1.324 + // We have a valid box size entry. This entry already contains information about our 1.325 + // sizes along the axis of the box (e.g., widths in a horizontal box). If our default 1.326 + // ALIGN is not stretch, however, then we also need to know the child's size along the 1.327 + // opposite axis. 1.328 + if (!(frameState & NS_STATE_AUTO_STRETCH)) { 1.329 + nsSize prefSize = child->GetPrefSize(aState); 1.330 + nsSize minSize = child->GetMinSize(aState); 1.331 + nsSize maxSize = child->GetMaxSize(aState); 1.332 + prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize); 1.333 + 1.334 + AddMargin(child, prefSize); 1.335 + width = std::min(prefSize.width, originalClientRect.width); 1.336 + height = std::min(prefSize.height, originalClientRect.height); 1.337 + } 1.338 + } 1.339 + 1.340 + // Obtain the computed size along the axis of the box for this child from the computedBoxSize entry. 1.341 + // We store the result in |width| for horizontal boxes and |height| for vertical boxes. 1.342 + if (frameState & NS_STATE_IS_HORIZONTAL) 1.343 + width = childComputedBoxSize->size; 1.344 + else 1.345 + height = childComputedBoxSize->size; 1.346 + 1.347 + // Adjust our x/y for the left/right spacing. 1.348 + if (frameState & NS_STATE_IS_HORIZONTAL) { 1.349 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.350 + x += (childBoxSize->left); 1.351 + else 1.352 + x -= (childBoxSize->right); 1.353 + } else { 1.354 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.355 + y += (childBoxSize->left); 1.356 + else 1.357 + y -= (childBoxSize->right); 1.358 + } 1.359 + 1.360 + nextX = x; 1.361 + nextY = y; 1.362 + 1.363 + // Now we build a child rect. 1.364 + nscoord rectX = x; 1.365 + nscoord rectY = y; 1.366 + if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { 1.367 + if (frameState & NS_STATE_IS_HORIZONTAL) 1.368 + rectX -= width; 1.369 + else 1.370 + rectY -= height; 1.371 + } 1.372 + 1.373 + // We now create an accurate child rect based off our computed size information. 1.374 + nsRect childRect(rectX, rectY, width, height); 1.375 + 1.376 + // Sanity check against our clientRect. It is possible that a child specified 1.377 + // a size that is too large to fit. If that happens, then we have to grow 1.378 + // our client rect. Remember, clientRect is not the total rect of the enclosing 1.379 + // box. It currently holds our perception of how big the children needed to 1.380 + // be. 1.381 + if (childRect.width > clientRect.width) 1.382 + clientRect.width = childRect.width; 1.383 + 1.384 + if (childRect.height > clientRect.height) 1.385 + clientRect.height = childRect.height; 1.386 + 1.387 + // Either |nextX| or |nextY| is updated by this function call, according 1.388 + // to our axis. 1.389 + ComputeChildsNextPosition(aBox, x, y, nextX, nextY, childRect); 1.390 + 1.391 + // Now we further update our nextX/Y along our axis. 1.392 + // We also set childRect.y/x along the opposite axis appropriately for a 1.393 + // stretch alignment. (Non-stretch alignment is handled below.) 1.394 + if (frameState & NS_STATE_IS_HORIZONTAL) { 1.395 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.396 + nextX += (childBoxSize->right); 1.397 + else 1.398 + nextX -= (childBoxSize->left); 1.399 + childRect.y = originalClientRect.y; 1.400 + } 1.401 + else { 1.402 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.403 + nextY += (childBoxSize->right); 1.404 + else 1.405 + nextY -= (childBoxSize->left); 1.406 + childRect.x = originalClientRect.x; 1.407 + } 1.408 + 1.409 + // If we encounter a completely bogus box size, we just leave this child completely 1.410 + // alone and continue through the loop to the next child. 1.411 + if (childBoxSize->bogus) 1.412 + { 1.413 + childComputedBoxSize = childComputedBoxSize->next; 1.414 + childBoxSize = childBoxSize->next; 1.415 + count++; 1.416 + x = nextX; 1.417 + y = nextY; 1.418 + continue; 1.419 + } 1.420 + 1.421 + nsMargin margin(0,0,0,0); 1.422 + 1.423 + bool layout = true; 1.424 + 1.425 + // Deflate the rect of our child by its margin. 1.426 + child->GetMargin(margin); 1.427 + childRect.Deflate(margin); 1.428 + if (childRect.width < 0) 1.429 + childRect.width = 0; 1.430 + if (childRect.height < 0) 1.431 + childRect.height = 0; 1.432 + 1.433 + // Now we're trying to figure out if we have to lay out this child, i.e., to call 1.434 + // the child's Layout method. 1.435 + if (passes > 0) { 1.436 + layout = false; 1.437 + } else { 1.438 + // Always perform layout if we are dirty or have dirty children 1.439 + if (!NS_SUBTREE_DIRTY(child)) 1.440 + layout = false; 1.441 + } 1.442 + 1.443 + nsRect oldRect(child->GetRect()); 1.444 + 1.445 + // Non-stretch alignment will be handled in AlignChildren(), so don't 1.446 + // change child out-of-axis positions yet. 1.447 + if (!(frameState & NS_STATE_AUTO_STRETCH)) { 1.448 + if (frameState & NS_STATE_IS_HORIZONTAL) { 1.449 + childRect.y = oldRect.y; 1.450 + } else { 1.451 + childRect.x = oldRect.x; 1.452 + } 1.453 + } 1.454 + 1.455 + // We computed a childRect. Now we want to set the bounds of the child to be that rect. 1.456 + // If our old rect is different, then we know our size changed and we cache that fact 1.457 + // in the |sizeChanged| variable. 1.458 + 1.459 + child->SetBounds(aState, childRect); 1.460 + bool sizeChanged = (childRect.width != oldRect.width || 1.461 + childRect.height != oldRect.height); 1.462 + 1.463 + if (sizeChanged) { 1.464 + // Our size is different. Sanity check against our maximum allowed size to ensure 1.465 + // we didn't exceed it. 1.466 + nsSize minSize = child->GetMinSize(aState); 1.467 + nsSize maxSize = child->GetMaxSize(aState); 1.468 + maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize); 1.469 + 1.470 + // make sure the size is in our max size. 1.471 + if (childRect.width > maxSize.width) 1.472 + childRect.width = maxSize.width; 1.473 + 1.474 + if (childRect.height > maxSize.height) 1.475 + childRect.height = maxSize.height; 1.476 + 1.477 + // set it again 1.478 + child->SetBounds(aState, childRect); 1.479 + } 1.480 + 1.481 + // If we already determined that layout was required or if our size has changed, then 1.482 + // we make sure to call layout on the child, since its children may need to be shifted 1.483 + // around as a result of the size change. 1.484 + if (layout || sizeChanged) 1.485 + child->Layout(aState); 1.486 + 1.487 + // If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout. 1.488 + // We have to check for this. 1.489 + nsRect newChildRect(child->GetRect()); 1.490 + 1.491 + if (!newChildRect.IsEqualInterior(childRect)) { 1.492 +#ifdef DEBUG_GROW 1.493 + child->DumpBox(stdout); 1.494 + printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height); 1.495 +#endif 1.496 + newChildRect.Inflate(margin); 1.497 + childRect.Inflate(margin); 1.498 + 1.499 + // The child changed size during layout. The ChildResized method handles this 1.500 + // scenario. 1.501 + ChildResized(aBox, 1.502 + aState, 1.503 + child, 1.504 + childBoxSize, 1.505 + childComputedBoxSize, 1.506 + boxSizes, 1.507 + computedBoxSizes, 1.508 + childRect, 1.509 + newChildRect, 1.510 + clientRect, 1.511 + flexes, 1.512 + finished); 1.513 + 1.514 + // We note that a child changed size, which means that another pass will be required. 1.515 + childResized = true; 1.516 + 1.517 + // Now that a child resized, it's entirely possible that OUR rect is too small. Now we 1.518 + // ensure that |originalClientRect| is grown to accommodate the size of |clientRect|. 1.519 + if (clientRect.width > originalClientRect.width) 1.520 + originalClientRect.width = clientRect.width; 1.521 + 1.522 + if (clientRect.height > originalClientRect.height) 1.523 + originalClientRect.height = clientRect.height; 1.524 + 1.525 + if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { 1.526 + // Our childRect had its XMost() or YMost() (depending on our layout 1.527 + // direction), positioned at a certain point. Ensure that the 1.528 + // newChildRect satisfies the same constraint. Note that this is 1.529 + // just equivalent to adjusting the x/y by the difference in 1.530 + // width/height between childRect and newChildRect. So we don't need 1.531 + // to reaccount for the left and right of the box layout state again. 1.532 + if (frameState & NS_STATE_IS_HORIZONTAL) 1.533 + newChildRect.x = childRect.XMost() - newChildRect.width; 1.534 + else 1.535 + newChildRect.y = childRect.YMost() - newChildRect.height; 1.536 + } 1.537 + 1.538 + // If the child resized then recompute its position. 1.539 + ComputeChildsNextPosition(aBox, x, y, nextX, nextY, newChildRect); 1.540 + 1.541 + if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom) 1.542 + newChildRect.Deflate(margin); 1.543 + 1.544 + if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom) 1.545 + childRect.Deflate(margin); 1.546 + 1.547 + child->SetBounds(aState, newChildRect); 1.548 + 1.549 + // If we are the first box that changed size, then we don't need to do a second pass 1.550 + if (count == 0) 1.551 + finished = true; 1.552 + } 1.553 + 1.554 + // Now update our x/y finally. 1.555 + x = nextX; 1.556 + y = nextY; 1.557 + 1.558 + // Move to the next child. 1.559 + childComputedBoxSize = childComputedBoxSize->next; 1.560 + childBoxSize = childBoxSize->next; 1.561 + 1.562 + child = child->GetNextBox(); 1.563 + count++; 1.564 + } 1.565 + 1.566 + // Sanity-checking code to ensure we don't do an infinite # of passes. 1.567 + passes++; 1.568 + NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!"); 1.569 + if (passes > 10) 1.570 + break; 1.571 + } while (false == finished); 1.572 + 1.573 + // Get rid of our size lists. 1.574 + while(boxSizes) 1.575 + { 1.576 + nsBoxSize* toDelete = boxSizes; 1.577 + boxSizes = boxSizes->next; 1.578 + delete toDelete; 1.579 + } 1.580 + 1.581 + while(computedBoxSizes) 1.582 + { 1.583 + nsComputedBoxSize* toDelete = computedBoxSizes; 1.584 + computedBoxSizes = computedBoxSizes->next; 1.585 + delete toDelete; 1.586 + } 1.587 + 1.588 + if (childResized) { 1.589 + // See if one of our children forced us to get bigger 1.590 + nsRect tmpClientRect(originalClientRect); 1.591 + nsMargin bp(0,0,0,0); 1.592 + aBox->GetBorderAndPadding(bp); 1.593 + tmpClientRect.Inflate(bp); 1.594 + 1.595 + if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height) 1.596 + { 1.597 + // if it did reset our bounds. 1.598 + nsRect bounds(aBox->GetRect()); 1.599 + if (tmpClientRect.width > originalSize.width) 1.600 + bounds.width = tmpClientRect.width; 1.601 + 1.602 + if (tmpClientRect.height > originalSize.height) 1.603 + bounds.height = tmpClientRect.height; 1.604 + 1.605 + aBox->SetBounds(aState, bounds); 1.606 + } 1.607 + } 1.608 + 1.609 + // Because our size grew, we now have to readjust because of box packing. Repack 1.610 + // in order to update our x and y to the correct values. 1.611 + HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); 1.612 + 1.613 + // Compare against our original x and y and only worry about adjusting the children if 1.614 + // we really did have to change the positions because of packing (typically for 'center' 1.615 + // or 'end' pack values). 1.616 + if (x != origX || y != origY) { 1.617 + nsIFrame* child = aBox->GetChildBox(); 1.618 + 1.619 + // reposition all our children 1.620 + while (child) 1.621 + { 1.622 + nsRect childRect(child->GetRect()); 1.623 + childRect.x += (x - origX); 1.624 + childRect.y += (y - origY); 1.625 + child->SetBounds(aState, childRect); 1.626 + child = child->GetNextBox(); 1.627 + } 1.628 + } 1.629 + 1.630 + // Perform out-of-axis alignment for non-stretch alignments 1.631 + if (!(frameState & NS_STATE_AUTO_STRETCH)) { 1.632 + AlignChildren(aBox, aState); 1.633 + } 1.634 + 1.635 + // That's it! If you made it this far without having a nervous breakdown, 1.636 + // congratulations! Go get yourself a beer. 1.637 + return NS_OK; 1.638 +} 1.639 + 1.640 +void 1.641 +nsSprocketLayout::PopulateBoxSizes(nsIFrame* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, int32_t& aFlexes) 1.642 +{ 1.643 + // used for the equal size flag 1.644 + nscoord biggestPrefWidth = 0; 1.645 + nscoord biggestMinWidth = 0; 1.646 + nscoord smallestMaxWidth = NS_INTRINSICSIZE; 1.647 + 1.648 + nsFrameState frameState = nsFrameState(0); 1.649 + GetFrameState(aBox, frameState); 1.650 + 1.651 + //if (frameState & NS_STATE_CURRENTLY_IN_DEBUG) 1.652 + // printf("In debug\n"); 1.653 + 1.654 + aMinSize = 0; 1.655 + aMaxSize = NS_INTRINSICSIZE; 1.656 + 1.657 + bool isHorizontal; 1.658 + 1.659 + if (IsHorizontal(aBox)) 1.660 + isHorizontal = true; 1.661 + else 1.662 + isHorizontal = false; 1.663 + 1.664 + // this is a nice little optimization 1.665 + // it turns out that if we only have 1 flexable child 1.666 + // then it does not matter what its preferred size is 1.667 + // there is nothing to flex it relative. This is great 1.668 + // because we can avoid asking for a preferred size in this 1.669 + // case. Why is this good? Well you might have html inside it 1.670 + // and asking html for its preferred size is rather expensive. 1.671 + // so we can just optimize it out this way. 1.672 + 1.673 + // set flexes 1.674 + nsIFrame* child = aBox->GetChildBox(); 1.675 + 1.676 + aFlexes = 0; 1.677 + nsBoxSize* currentBox = nullptr; 1.678 + 1.679 +#if 0 1.680 + nsBoxSize* start = aBoxSizes; 1.681 + 1.682 + while(child) 1.683 + { 1.684 + // ok if we started with a list move down the list 1.685 + // until we reach the end. Then start looking at childen. 1.686 + // This feature is used extensively for Grid. 1.687 + nscoord flex = 0; 1.688 + 1.689 + if (!start) { 1.690 + if (!currentBox) { 1.691 + aBoxSizes = new (aState) nsBoxSize(); 1.692 + currentBox = aBoxSizes; 1.693 + } else { 1.694 + currentBox->next = new (aState) nsBoxSize(); 1.695 + currentBox = currentBox->next; 1.696 + } 1.697 + 1.698 + 1.699 + flex = child->GetFlex(aState); 1.700 + 1.701 + currentBox->flex = flex; 1.702 + currentBox->collapsed = child->IsCollapsed(); 1.703 + } else { 1.704 + flex = start->flex; 1.705 + start = start->next; 1.706 + } 1.707 + 1.708 + if (flex > 0) 1.709 + aFlexes++; 1.710 + 1.711 + child = child->GetNextBox(); 1.712 + } 1.713 +#endif 1.714 + 1.715 + // get pref, min, max 1.716 + child = aBox->GetChildBox(); 1.717 + currentBox = aBoxSizes; 1.718 + nsBoxSize* last = nullptr; 1.719 + 1.720 + nscoord maxFlex = 0; 1.721 + int32_t childCount = 0; 1.722 + 1.723 + while(child) 1.724 + { 1.725 + while (currentBox && currentBox->bogus) { 1.726 + last = currentBox; 1.727 + currentBox = currentBox->next; 1.728 + } 1.729 + ++childCount; 1.730 + nsSize pref(0,0); 1.731 + nsSize minSize(0,0); 1.732 + nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE); 1.733 + nscoord ascent = 0; 1.734 + bool collapsed = child->IsCollapsed(); 1.735 + 1.736 + if (!collapsed) { 1.737 + // only one flexible child? Cool we will just make its preferred size 1.738 + // 0 then and not even have to ask for it. 1.739 + //if (flexes != 1) { 1.740 + 1.741 + pref = child->GetPrefSize(aState); 1.742 + minSize = child->GetMinSize(aState); 1.743 + maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetMaxSize(aState)); 1.744 + ascent = child->GetBoxAscent(aState); 1.745 + nsMargin margin; 1.746 + child->GetMargin(margin); 1.747 + ascent += margin.top; 1.748 + //} 1.749 + 1.750 + pref = nsBox::BoundsCheck(minSize, pref, maxSize); 1.751 + 1.752 + AddMargin(child, pref); 1.753 + AddMargin(child, minSize); 1.754 + AddMargin(child, maxSize); 1.755 + } 1.756 + 1.757 + if (!currentBox) { 1.758 + // create one. 1.759 + currentBox = new (aState) nsBoxSize(); 1.760 + if (!aBoxSizes) { 1.761 + aBoxSizes = currentBox; 1.762 + last = aBoxSizes; 1.763 + } else { 1.764 + last->next = currentBox; 1.765 + last = currentBox; 1.766 + } 1.767 + 1.768 + nscoord minWidth; 1.769 + nscoord maxWidth; 1.770 + nscoord prefWidth; 1.771 + 1.772 + // get sizes from child 1.773 + if (isHorizontal) { 1.774 + minWidth = minSize.width; 1.775 + maxWidth = maxSize.width; 1.776 + prefWidth = pref.width; 1.777 + } else { 1.778 + minWidth = minSize.height; 1.779 + maxWidth = maxSize.height; 1.780 + prefWidth = pref.height; 1.781 + } 1.782 + 1.783 + nscoord flex = child->GetFlex(aState); 1.784 + 1.785 + // set them if you collapsed you are not flexible. 1.786 + if (collapsed) { 1.787 + currentBox->flex = 0; 1.788 + } 1.789 + else { 1.790 + if (flex > maxFlex) { 1.791 + maxFlex = flex; 1.792 + } 1.793 + currentBox->flex = flex; 1.794 + } 1.795 + 1.796 + // we specified all our children are equal size; 1.797 + if (frameState & NS_STATE_EQUAL_SIZE) { 1.798 + 1.799 + if (prefWidth > biggestPrefWidth) 1.800 + biggestPrefWidth = prefWidth; 1.801 + 1.802 + if (minWidth > biggestMinWidth) 1.803 + biggestMinWidth = minWidth; 1.804 + 1.805 + if (maxWidth < smallestMaxWidth) 1.806 + smallestMaxWidth = maxWidth; 1.807 + } else { // not we can set our children right now. 1.808 + currentBox->pref = prefWidth; 1.809 + currentBox->min = minWidth; 1.810 + currentBox->max = maxWidth; 1.811 + } 1.812 + 1.813 + NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!"); 1.814 + 1.815 + } 1.816 + 1.817 + if (!isHorizontal) { 1.818 + if (minSize.width > aMinSize) 1.819 + aMinSize = minSize.width; 1.820 + 1.821 + if (maxSize.width < aMaxSize) 1.822 + aMaxSize = maxSize.width; 1.823 + 1.824 + } else { 1.825 + if (minSize.height > aMinSize) 1.826 + aMinSize = minSize.height; 1.827 + 1.828 + if (maxSize.height < aMaxSize) 1.829 + aMaxSize = maxSize.height; 1.830 + } 1.831 + 1.832 + currentBox->collapsed = collapsed; 1.833 + aFlexes += currentBox->flex; 1.834 + 1.835 + child = child->GetNextBox(); 1.836 + 1.837 + last = currentBox; 1.838 + currentBox = currentBox->next; 1.839 + 1.840 + } 1.841 + 1.842 + if (childCount > 0) { 1.843 + nscoord maxAllowedFlex = nscoord_MAX / childCount; 1.844 + 1.845 + if (MOZ_UNLIKELY(maxFlex > maxAllowedFlex)) { 1.846 + // clamp all the flexes 1.847 + currentBox = aBoxSizes; 1.848 + while (currentBox) { 1.849 + currentBox->flex = std::min(currentBox->flex, maxAllowedFlex); 1.850 + currentBox = currentBox->next; 1.851 + } 1.852 + } 1.853 + } 1.854 +#ifdef DEBUG 1.855 + else { 1.856 + NS_ASSERTION(maxFlex == 0, "How did that happen?"); 1.857 + } 1.858 +#endif 1.859 + 1.860 + // we specified all our children are equal size; 1.861 + if (frameState & NS_STATE_EQUAL_SIZE) { 1.862 + smallestMaxWidth = std::max(smallestMaxWidth, biggestMinWidth); 1.863 + biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth); 1.864 + 1.865 + currentBox = aBoxSizes; 1.866 + 1.867 + while(currentBox) 1.868 + { 1.869 + if (!currentBox->collapsed) { 1.870 + currentBox->pref = biggestPrefWidth; 1.871 + currentBox->min = biggestMinWidth; 1.872 + currentBox->max = smallestMaxWidth; 1.873 + } else { 1.874 + currentBox->pref = 0; 1.875 + currentBox->min = 0; 1.876 + currentBox->max = 0; 1.877 + } 1.878 + currentBox = currentBox->next; 1.879 + } 1.880 + } 1.881 + 1.882 +} 1.883 + 1.884 +void 1.885 +nsSprocketLayout::ComputeChildsNextPosition(nsIFrame* aBox, 1.886 + const nscoord& aCurX, 1.887 + const nscoord& aCurY, 1.888 + nscoord& aNextX, 1.889 + nscoord& aNextY, 1.890 + const nsRect& aCurrentChildSize) 1.891 +{ 1.892 + // Get the position along the box axis for the child. 1.893 + // The out-of-axis position is not set. 1.894 + nsFrameState frameState = nsFrameState(0); 1.895 + GetFrameState(aBox, frameState); 1.896 + 1.897 + if (IsHorizontal(aBox)) { 1.898 + // horizontal box's children. 1.899 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.900 + aNextX = aCurX + aCurrentChildSize.width; 1.901 + else 1.902 + aNextX = aCurX - aCurrentChildSize.width; 1.903 + 1.904 + } else { 1.905 + // vertical box's children. 1.906 + if (frameState & NS_STATE_IS_DIRECTION_NORMAL) 1.907 + aNextY = aCurY + aCurrentChildSize.height; 1.908 + else 1.909 + aNextY = aCurY - aCurrentChildSize.height; 1.910 + } 1.911 +} 1.912 + 1.913 +void 1.914 +nsSprocketLayout::AlignChildren(nsIFrame* aBox, 1.915 + nsBoxLayoutState& aState) 1.916 +{ 1.917 + nsFrameState frameState = nsFrameState(0); 1.918 + GetFrameState(aBox, frameState); 1.919 + bool isHorizontal = (frameState & NS_STATE_IS_HORIZONTAL) != 0; 1.920 + nsRect clientRect; 1.921 + aBox->GetClientRect(clientRect); 1.922 + 1.923 + NS_PRECONDITION(!(frameState & NS_STATE_AUTO_STRETCH), 1.924 + "Only AlignChildren() with non-stretch alignment"); 1.925 + 1.926 + // These are only calculated if needed 1.927 + nsIFrame::Halignment halign; 1.928 + nsIFrame::Valignment valign; 1.929 + nscoord maxAscent; 1.930 + bool isLTR; 1.931 + 1.932 + if (isHorizontal) { 1.933 + valign = aBox->GetVAlign(); 1.934 + if (valign == nsBoxFrame::vAlign_BaseLine) { 1.935 + maxAscent = aBox->GetBoxAscent(aState); 1.936 + } 1.937 + } else { 1.938 + isLTR = GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR; 1.939 + halign = aBox->GetHAlign(); 1.940 + } 1.941 + 1.942 + nsIFrame* child = aBox->GetChildBox(); 1.943 + while (child) { 1.944 + 1.945 + nsMargin margin; 1.946 + child->GetMargin(margin); 1.947 + nsRect childRect = child->GetRect(); 1.948 + 1.949 + if (isHorizontal) { 1.950 + const nscoord startAlign = clientRect.y + margin.top; 1.951 + const nscoord endAlign = 1.952 + clientRect.YMost() - margin.bottom - childRect.height; 1.953 + 1.954 + nscoord y; 1.955 + switch (valign) { 1.956 + case nsBoxFrame::vAlign_Top: 1.957 + y = startAlign; 1.958 + break; 1.959 + case nsBoxFrame::vAlign_Middle: 1.960 + // Should this center the border box? 1.961 + // This centers the margin box, the historical behavior. 1.962 + y = (startAlign + endAlign) / 2; 1.963 + break; 1.964 + case nsBoxFrame::vAlign_Bottom: 1.965 + y = endAlign; 1.966 + break; 1.967 + case nsBoxFrame::vAlign_BaseLine: 1.968 + // Alignments don't force the box to grow (only sizes do), 1.969 + // so keep the children within the box. 1.970 + y = maxAscent - child->GetBoxAscent(aState); 1.971 + y = std::max(startAlign, y); 1.972 + y = std::min(y, endAlign); 1.973 + break; 1.974 + } 1.975 + 1.976 + childRect.y = y; 1.977 + 1.978 + } else { // vertical box 1.979 + const nscoord leftAlign = clientRect.x + margin.left; 1.980 + const nscoord rightAlign = 1.981 + clientRect.XMost() - margin.right - childRect.width; 1.982 + 1.983 + nscoord x; 1.984 + switch (halign) { 1.985 + case nsBoxFrame::hAlign_Left: // start 1.986 + x = isLTR ? leftAlign : rightAlign; 1.987 + break; 1.988 + case nsBoxFrame::hAlign_Center: 1.989 + x = (leftAlign + rightAlign) / 2; 1.990 + break; 1.991 + case nsBoxFrame::hAlign_Right: // end 1.992 + x = isLTR ? rightAlign : leftAlign; 1.993 + break; 1.994 + } 1.995 + 1.996 + childRect.x = x; 1.997 + } 1.998 + 1.999 + if (childRect.TopLeft() != child->GetPosition()) { 1.1000 + child->SetBounds(aState, childRect); 1.1001 + } 1.1002 + 1.1003 + child = child->GetNextBox(); 1.1004 + } 1.1005 +} 1.1006 + 1.1007 +void 1.1008 +nsSprocketLayout::ChildResized(nsIFrame* aBox, 1.1009 + nsBoxLayoutState& aState, 1.1010 + nsIFrame* aChild, 1.1011 + nsBoxSize* aChildBoxSize, 1.1012 + nsComputedBoxSize* aChildComputedSize, 1.1013 + nsBoxSize* aBoxSizes, 1.1014 + nsComputedBoxSize* aComputedBoxSizes, 1.1015 + const nsRect& aChildLayoutRect, 1.1016 + nsRect& aChildActualRect, 1.1017 + nsRect& aContainingRect, 1.1018 + int32_t aFlexes, 1.1019 + bool& aFinished) 1.1020 + 1.1021 +{ 1.1022 + nsRect childCurrentRect(aChildLayoutRect); 1.1023 + 1.1024 + bool isHorizontal = IsHorizontal(aBox); 1.1025 + nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal); 1.1026 + nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal); 1.1027 + nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal); 1.1028 + 1.1029 + //nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal); 1.1030 + nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal); 1.1031 + nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal); 1.1032 + 1.1033 + bool recompute = false; 1.1034 + 1.1035 + // if we are a horizontal box see if the child will fit inside us. 1.1036 + if ( childActualHeight > containingHeight) { 1.1037 + // if we are a horizontal box and the child is bigger than our height 1.1038 + 1.1039 + // ok if the height changed then we need to reflow everyone but us at the new height 1.1040 + // so we will set the changed index to be us. And signal that we need a new pass. 1.1041 + 1.1042 + nsSize min = aChild->GetMinSize(aState); 1.1043 + nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); 1.1044 + AddMargin(aChild, max); 1.1045 + 1.1046 + if (isHorizontal) 1.1047 + childActualHeight = max.height < childActualHeight ? max.height : childActualHeight; 1.1048 + else 1.1049 + childActualHeight = max.width < childActualHeight ? max.width : childActualHeight; 1.1050 + 1.1051 + // only set if it changes 1.1052 + if (childActualHeight > containingHeight) { 1.1053 + containingHeight = childActualHeight; 1.1054 + 1.1055 + // remember we do not need to clear the resized list because changing the height of a horizontal box 1.1056 + // will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me 1.1057 + // on this one. 1.1058 + aFinished = false; 1.1059 + 1.1060 + // only recompute if there are flexes. 1.1061 + if (aFlexes > 0) { 1.1062 + // relayout everything 1.1063 + recompute = true; 1.1064 + InvalidateComputedSizes(aComputedBoxSizes); 1.1065 + nsComputedBoxSize* node = aComputedBoxSizes; 1.1066 + 1.1067 + while(node) { 1.1068 + node->resized = false; 1.1069 + node = node->next; 1.1070 + } 1.1071 + 1.1072 + } 1.1073 + } 1.1074 + } 1.1075 + 1.1076 + if (childActualWidth > childLayoutWidth) { 1.1077 + nsSize min = aChild->GetMinSize(aState); 1.1078 + nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); 1.1079 + 1.1080 + AddMargin(aChild, max); 1.1081 + 1.1082 + // our width now becomes the new size 1.1083 + 1.1084 + if (isHorizontal) 1.1085 + childActualWidth = max.width < childActualWidth ? max.width : childActualWidth; 1.1086 + else 1.1087 + childActualWidth = max.height < childActualWidth ? max.height : childActualWidth; 1.1088 + 1.1089 + if (childActualWidth > childLayoutWidth) { 1.1090 + aChildComputedSize->size = childActualWidth; 1.1091 + aChildBoxSize->min = childActualWidth; 1.1092 + if (aChildBoxSize->pref < childActualWidth) 1.1093 + aChildBoxSize->pref = childActualWidth; 1.1094 + if (aChildBoxSize->max < childActualWidth) 1.1095 + aChildBoxSize->max = childActualWidth; 1.1096 + 1.1097 + // if we have flexible elements with us then reflex things. Otherwise we can skip doing it. 1.1098 + if (aFlexes > 0) { 1.1099 + InvalidateComputedSizes(aComputedBoxSizes); 1.1100 + 1.1101 + nsComputedBoxSize* node = aComputedBoxSizes; 1.1102 + aChildComputedSize->resized = true; 1.1103 + 1.1104 + while(node) { 1.1105 + if (node->resized) 1.1106 + node->valid = true; 1.1107 + 1.1108 + node = node->next; 1.1109 + } 1.1110 + 1.1111 + recompute = true; 1.1112 + aFinished = false; 1.1113 + } else { 1.1114 + containingWidth += aChildComputedSize->size - childLayoutWidth; 1.1115 + } 1.1116 + } 1.1117 + } 1.1118 + 1.1119 + if (recompute) 1.1120 + ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes); 1.1121 + 1.1122 + if (!childCurrentRect.IsEqualInterior(aChildActualRect)) { 1.1123 + // the childRect includes the margin 1.1124 + // make sure we remove it before setting 1.1125 + // the bounds. 1.1126 + nsMargin margin(0,0,0,0); 1.1127 + aChild->GetMargin(margin); 1.1128 + nsRect rect(aChildActualRect); 1.1129 + if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom) 1.1130 + rect.Deflate(margin); 1.1131 + 1.1132 + aChild->SetBounds(aState, rect); 1.1133 + aChild->Layout(aState); 1.1134 + } 1.1135 + 1.1136 +} 1.1137 + 1.1138 +void 1.1139 +nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes) 1.1140 +{ 1.1141 + while(aComputedBoxSizes) { 1.1142 + aComputedBoxSizes->valid = false; 1.1143 + aComputedBoxSizes = aComputedBoxSizes->next; 1.1144 + } 1.1145 +} 1.1146 + 1.1147 +void 1.1148 +nsSprocketLayout::ComputeChildSizes(nsIFrame* aBox, 1.1149 + nsBoxLayoutState& aState, 1.1150 + nscoord& aGivenSize, 1.1151 + nsBoxSize* aBoxSizes, 1.1152 + nsComputedBoxSize*& aComputedBoxSizes) 1.1153 +{ 1.1154 + 1.1155 + //nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1); 1.1156 + 1.1157 + int32_t sizeRemaining = aGivenSize; 1.1158 + int32_t spacerConstantsRemaining = 0; 1.1159 + 1.1160 + // ----- calculate the spacers constants and the size remaining ----- 1.1161 + 1.1162 + if (!aComputedBoxSizes) 1.1163 + aComputedBoxSizes = new (aState) nsComputedBoxSize(); 1.1164 + 1.1165 + nsBoxSize* boxSizes = aBoxSizes; 1.1166 + nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes; 1.1167 + int32_t count = 0; 1.1168 + int32_t validCount = 0; 1.1169 + 1.1170 + while (boxSizes) 1.1171 + { 1.1172 + 1.1173 + NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size"); 1.1174 + 1.1175 + 1.1176 + // ignore collapsed children 1.1177 + // if (boxSizes->collapsed) 1.1178 + // { 1.1179 + // computedBoxSizes->valid = true; 1.1180 + // computedBoxSizes->size = boxSizes->pref; 1.1181 + // validCount++; 1.1182 + // boxSizes->flex = 0; 1.1183 + // }// else { 1.1184 + 1.1185 + if (computedBoxSizes->valid) { 1.1186 + sizeRemaining -= computedBoxSizes->size; 1.1187 + validCount++; 1.1188 + } else { 1.1189 + if (boxSizes->flex == 0) 1.1190 + { 1.1191 + computedBoxSizes->valid = true; 1.1192 + computedBoxSizes->size = boxSizes->pref; 1.1193 + validCount++; 1.1194 + } 1.1195 + 1.1196 + spacerConstantsRemaining += boxSizes->flex; 1.1197 + sizeRemaining -= boxSizes->pref; 1.1198 + } 1.1199 + 1.1200 + sizeRemaining -= (boxSizes->left + boxSizes->right); 1.1201 + 1.1202 + //} 1.1203 + 1.1204 + boxSizes = boxSizes->next; 1.1205 + 1.1206 + if (boxSizes && !computedBoxSizes->next) 1.1207 + computedBoxSizes->next = new (aState) nsComputedBoxSize(); 1.1208 + 1.1209 + computedBoxSizes = computedBoxSizes->next; 1.1210 + count++; 1.1211 + } 1.1212 + 1.1213 + // everything accounted for? 1.1214 + if (validCount < count) 1.1215 + { 1.1216 + // ----- Ok we are give a size to fit into so stretch or squeeze to fit 1.1217 + // ----- Make sure we look at our min and max size 1.1218 + bool limit = true; 1.1219 + for (int pass=1; true == limit; pass++) 1.1220 + { 1.1221 + limit = false; 1.1222 + boxSizes = aBoxSizes; 1.1223 + computedBoxSizes = aComputedBoxSizes; 1.1224 + 1.1225 + while (boxSizes) { 1.1226 + 1.1227 + // ignore collapsed spacers 1.1228 + 1.1229 + // if (!boxSizes->collapsed) { 1.1230 + 1.1231 + nscoord pref = 0; 1.1232 + nscoord max = NS_INTRINSICSIZE; 1.1233 + nscoord min = 0; 1.1234 + nscoord flex = 0; 1.1235 + 1.1236 + pref = boxSizes->pref; 1.1237 + min = boxSizes->min; 1.1238 + max = boxSizes->max; 1.1239 + flex = boxSizes->flex; 1.1240 + 1.1241 + // ----- look at our min and max limits make sure we aren't too small or too big ----- 1.1242 + if (!computedBoxSizes->valid) { 1.1243 + int32_t newSize = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); 1.1244 + 1.1245 + if (newSize<=min) { 1.1246 + computedBoxSizes->size = min; 1.1247 + computedBoxSizes->valid = true; 1.1248 + spacerConstantsRemaining -= flex; 1.1249 + sizeRemaining += pref; 1.1250 + sizeRemaining -= min; 1.1251 + limit = true; 1.1252 + } else if (newSize>=max) { 1.1253 + computedBoxSizes->size = max; 1.1254 + computedBoxSizes->valid = true; 1.1255 + spacerConstantsRemaining -= flex; 1.1256 + sizeRemaining += pref; 1.1257 + sizeRemaining -= max; 1.1258 + limit = true; 1.1259 + } 1.1260 + } 1.1261 + // } 1.1262 + boxSizes = boxSizes->next; 1.1263 + computedBoxSizes = computedBoxSizes->next; 1.1264 + } 1.1265 + } 1.1266 + } 1.1267 + 1.1268 + // ---- once we have removed and min and max issues just stretch us out in the remaining space 1.1269 + // ---- or shrink us. Depends on the size remaining and the spacer constants 1.1270 + aGivenSize = 0; 1.1271 + boxSizes = aBoxSizes; 1.1272 + computedBoxSizes = aComputedBoxSizes; 1.1273 + 1.1274 + while (boxSizes) { 1.1275 + 1.1276 + // ignore collapsed spacers 1.1277 + // if (!(boxSizes && boxSizes->collapsed)) { 1.1278 + 1.1279 + nscoord pref = 0; 1.1280 + nscoord flex = 0; 1.1281 + pref = boxSizes->pref; 1.1282 + flex = boxSizes->flex; 1.1283 + 1.1284 + if (!computedBoxSizes->valid) { 1.1285 + computedBoxSizes->size = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); 1.1286 + computedBoxSizes->valid = true; 1.1287 + } 1.1288 + 1.1289 + aGivenSize += (boxSizes->left + boxSizes->right); 1.1290 + aGivenSize += computedBoxSizes->size; 1.1291 + 1.1292 + // } 1.1293 + 1.1294 + boxSizes = boxSizes->next; 1.1295 + computedBoxSizes = computedBoxSizes->next; 1.1296 + } 1.1297 +} 1.1298 + 1.1299 + 1.1300 +nsSize 1.1301 +nsSprocketLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) 1.1302 +{ 1.1303 + nsSize vpref (0, 0); 1.1304 + bool isHorizontal = IsHorizontal(aBox); 1.1305 + 1.1306 + nscoord biggestPref = 0; 1.1307 + 1.1308 + // run through all the children and get their min, max, and preferred sizes 1.1309 + // return us the size of the box 1.1310 + 1.1311 + nsIFrame* child = aBox->GetChildBox(); 1.1312 + nsFrameState frameState = nsFrameState(0); 1.1313 + GetFrameState(aBox, frameState); 1.1314 + bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); 1.1315 + int32_t count = 0; 1.1316 + 1.1317 + while (child) 1.1318 + { 1.1319 + // ignore collapsed children 1.1320 + if (!child->IsCollapsed()) 1.1321 + { 1.1322 + nsSize pref = child->GetPrefSize(aState); 1.1323 + AddMargin(child, pref); 1.1324 + 1.1325 + if (isEqual) { 1.1326 + if (isHorizontal) 1.1327 + { 1.1328 + if (pref.width > biggestPref) 1.1329 + biggestPref = pref.width; 1.1330 + } else { 1.1331 + if (pref.height > biggestPref) 1.1332 + biggestPref = pref.height; 1.1333 + } 1.1334 + } 1.1335 + 1.1336 + AddLargestSize(vpref, pref, isHorizontal); 1.1337 + count++; 1.1338 + } 1.1339 + 1.1340 + child = child->GetNextBox(); 1.1341 + } 1.1342 + 1.1343 + if (isEqual) { 1.1344 + if (isHorizontal) 1.1345 + vpref.width = biggestPref*count; 1.1346 + else 1.1347 + vpref.height = biggestPref*count; 1.1348 + } 1.1349 + 1.1350 + // now add our border and padding 1.1351 + AddBorderAndPadding(aBox, vpref); 1.1352 + 1.1353 + return vpref; 1.1354 +} 1.1355 + 1.1356 +nsSize 1.1357 +nsSprocketLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) 1.1358 +{ 1.1359 + nsSize minSize (0, 0); 1.1360 + bool isHorizontal = IsHorizontal(aBox); 1.1361 + 1.1362 + nscoord biggestMin = 0; 1.1363 + 1.1364 + 1.1365 + // run through all the children and get their min, max, and preferred sizes 1.1366 + // return us the size of the box 1.1367 + 1.1368 + nsIFrame* child = aBox->GetChildBox(); 1.1369 + nsFrameState frameState = nsFrameState(0); 1.1370 + GetFrameState(aBox, frameState); 1.1371 + bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); 1.1372 + int32_t count = 0; 1.1373 + 1.1374 + while (child) 1.1375 + { 1.1376 + // ignore collapsed children 1.1377 + if (!child->IsCollapsed()) 1.1378 + { 1.1379 + nsSize min = child->GetMinSize(aState); 1.1380 + nsSize pref(0,0); 1.1381 + 1.1382 + // if the child is not flexible then 1.1383 + // its min size is its pref size. 1.1384 + if (child->GetFlex(aState) == 0) { 1.1385 + pref = child->GetPrefSize(aState); 1.1386 + if (isHorizontal) 1.1387 + min.width = pref.width; 1.1388 + else 1.1389 + min.height = pref.height; 1.1390 + } 1.1391 + 1.1392 + if (isEqual) { 1.1393 + if (isHorizontal) 1.1394 + { 1.1395 + if (min.width > biggestMin) 1.1396 + biggestMin = min.width; 1.1397 + } else { 1.1398 + if (min.height > biggestMin) 1.1399 + biggestMin = min.height; 1.1400 + } 1.1401 + } 1.1402 + 1.1403 + AddMargin(child, min); 1.1404 + AddLargestSize(minSize, min, isHorizontal); 1.1405 + count++; 1.1406 + } 1.1407 + 1.1408 + child = child->GetNextBox(); 1.1409 + } 1.1410 + 1.1411 + 1.1412 + if (isEqual) { 1.1413 + if (isHorizontal) 1.1414 + minSize.width = biggestMin*count; 1.1415 + else 1.1416 + minSize.height = biggestMin*count; 1.1417 + } 1.1418 + 1.1419 + // now add our border and padding 1.1420 + AddBorderAndPadding(aBox, minSize); 1.1421 + 1.1422 + return minSize; 1.1423 +} 1.1424 + 1.1425 +nsSize 1.1426 +nsSprocketLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) 1.1427 +{ 1.1428 + 1.1429 + bool isHorizontal = IsHorizontal(aBox); 1.1430 + 1.1431 + nscoord smallestMax = NS_INTRINSICSIZE; 1.1432 + nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE); 1.1433 + 1.1434 + // run through all the children and get their min, max, and preferred sizes 1.1435 + // return us the size of the box 1.1436 + 1.1437 + nsIFrame* child = aBox->GetChildBox(); 1.1438 + nsFrameState frameState = nsFrameState(0); 1.1439 + GetFrameState(aBox, frameState); 1.1440 + bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); 1.1441 + int32_t count = 0; 1.1442 + 1.1443 + while (child) 1.1444 + { 1.1445 + // ignore collapsed children 1.1446 + if (!child->IsCollapsed()) 1.1447 + { 1.1448 + // if completely redefined don't even ask our child for its size. 1.1449 + nsSize min = child->GetMinSize(aState); 1.1450 + nsSize max = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState)); 1.1451 + 1.1452 + AddMargin(child, max); 1.1453 + AddSmallestSize(maxSize, max, isHorizontal); 1.1454 + 1.1455 + if (isEqual) { 1.1456 + if (isHorizontal) 1.1457 + { 1.1458 + if (max.width < smallestMax) 1.1459 + smallestMax = max.width; 1.1460 + } else { 1.1461 + if (max.height < smallestMax) 1.1462 + smallestMax = max.height; 1.1463 + } 1.1464 + } 1.1465 + count++; 1.1466 + } 1.1467 + 1.1468 + child = child->GetNextBox(); 1.1469 + } 1.1470 + 1.1471 + if (isEqual) { 1.1472 + if (isHorizontal) { 1.1473 + if (smallestMax != NS_INTRINSICSIZE) 1.1474 + maxSize.width = smallestMax*count; 1.1475 + else 1.1476 + maxSize.width = NS_INTRINSICSIZE; 1.1477 + } else { 1.1478 + if (smallestMax != NS_INTRINSICSIZE) 1.1479 + maxSize.height = smallestMax*count; 1.1480 + else 1.1481 + maxSize.height = NS_INTRINSICSIZE; 1.1482 + } 1.1483 + } 1.1484 + 1.1485 + // now add our border and padding 1.1486 + AddBorderAndPadding(aBox, maxSize); 1.1487 + 1.1488 + return maxSize; 1.1489 +} 1.1490 + 1.1491 + 1.1492 +nscoord 1.1493 +nsSprocketLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) 1.1494 +{ 1.1495 + nscoord vAscent = 0; 1.1496 + 1.1497 + bool isHorizontal = IsHorizontal(aBox); 1.1498 + 1.1499 + // run through all the children and get their min, max, and preferred sizes 1.1500 + // return us the size of the box 1.1501 + 1.1502 + nsIFrame* child = aBox->GetChildBox(); 1.1503 + 1.1504 + while (child) 1.1505 + { 1.1506 + // ignore collapsed children 1.1507 + //if (!child->IsCollapsed()) 1.1508 + //{ 1.1509 + // if completely redefined don't even ask our child for its size. 1.1510 + nscoord ascent = child->GetBoxAscent(aState); 1.1511 + 1.1512 + nsMargin margin; 1.1513 + child->GetMargin(margin); 1.1514 + ascent += margin.top; 1.1515 + 1.1516 + if (isHorizontal) 1.1517 + { 1.1518 + if (ascent > vAscent) 1.1519 + vAscent = ascent; 1.1520 + } else { 1.1521 + if (vAscent == 0) 1.1522 + vAscent = ascent; 1.1523 + } 1.1524 + //} 1.1525 + 1.1526 + child = child->GetNextBox(); 1.1527 + } 1.1528 + 1.1529 + nsMargin borderPadding; 1.1530 + aBox->GetBorderAndPadding(borderPadding); 1.1531 + 1.1532 + return vAscent + borderPadding.top; 1.1533 +} 1.1534 + 1.1535 +void 1.1536 +nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) 1.1537 +{ 1.1538 + if (aIsHorizontal) 1.1539 + { 1.1540 + if (aSize1.height < aSize2.height) 1.1541 + aSize1.height = aSize2.height; 1.1542 + } else { 1.1543 + if (aSize1.width < aSize2.width) 1.1544 + aSize1.width = aSize2.width; 1.1545 + } 1.1546 +} 1.1547 + 1.1548 +void 1.1549 +nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) 1.1550 +{ 1.1551 + if (aIsHorizontal) 1.1552 + { 1.1553 + if (aSize1.height > aSize2.height) 1.1554 + aSize1.height = aSize2.height; 1.1555 + } else { 1.1556 + if (aSize1.width > aSize2.width) 1.1557 + aSize1.width = aSize2.width; 1.1558 + 1.1559 + } 1.1560 +} 1.1561 + 1.1562 +void 1.1563 +nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) 1.1564 +{ 1.1565 + if (aIsHorizontal) 1.1566 + AddCoord(aSize.width, aSizeToAdd.width); 1.1567 + else 1.1568 + AddCoord(aSize.height, aSizeToAdd.height); 1.1569 + 1.1570 + SetLargestSize(aSize, aSizeToAdd, aIsHorizontal); 1.1571 +} 1.1572 + 1.1573 +void 1.1574 +nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd) 1.1575 +{ 1.1576 + if (aCoord != NS_INTRINSICSIZE) 1.1577 + { 1.1578 + if (aCoordToAdd == NS_INTRINSICSIZE) 1.1579 + aCoord = aCoordToAdd; 1.1580 + else 1.1581 + aCoord += aCoordToAdd; 1.1582 + } 1.1583 +} 1.1584 +void 1.1585 +nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) 1.1586 +{ 1.1587 + if (aIsHorizontal) 1.1588 + AddCoord(aSize.width, aSizeToAdd.width); 1.1589 + else 1.1590 + AddCoord(aSize.height, aSizeToAdd.height); 1.1591 + 1.1592 + SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal); 1.1593 +} 1.1594 + 1.1595 +bool 1.1596 +nsSprocketLayout::GetDefaultFlex(int32_t& aFlex) 1.1597 +{ 1.1598 + aFlex = 0; 1.1599 + return true; 1.1600 +} 1.1601 + 1.1602 +nsComputedBoxSize::nsComputedBoxSize() 1.1603 +{ 1.1604 + resized = false; 1.1605 + valid = false; 1.1606 + size = 0; 1.1607 + next = nullptr; 1.1608 +} 1.1609 + 1.1610 +nsBoxSize::nsBoxSize() 1.1611 +{ 1.1612 + pref = 0; 1.1613 + min = 0; 1.1614 + max = NS_INTRINSICSIZE; 1.1615 + collapsed = false; 1.1616 + left = 0; 1.1617 + right = 0; 1.1618 + flex = 0; 1.1619 + next = nullptr; 1.1620 + bogus = false; 1.1621 +} 1.1622 + 1.1623 + 1.1624 +void* 1.1625 +nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW 1.1626 +{ 1.1627 + return mozilla::AutoStackArena::Allocate(sz); 1.1628 +} 1.1629 + 1.1630 + 1.1631 +void 1.1632 +nsBoxSize::operator delete(void* aPtr, size_t sz) 1.1633 +{ 1.1634 +} 1.1635 + 1.1636 + 1.1637 +void* 1.1638 +nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW 1.1639 +{ 1.1640 + return mozilla::AutoStackArena::Allocate(sz); 1.1641 +} 1.1642 + 1.1643 +void 1.1644 +nsComputedBoxSize::operator delete(void* aPtr, size_t sz) 1.1645 +{ 1.1646 +}