Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : //
7 : // Eric Vaughan
8 : // Netscape Communications
9 : //
10 : // See documentation in associated header file
11 : //
12 :
13 : #include "nsBoxLayoutState.h"
14 : #include "nsSprocketLayout.h"
15 : #include "nsPresContext.h"
16 : #include "nsCOMPtr.h"
17 : #include "nsIContent.h"
18 : #include "nsIPresShell.h"
19 : #include "nsContainerFrame.h"
20 : #include "nsBoxFrame.h"
21 : #include "StackArena.h"
22 : #include "mozilla/Likely.h"
23 : #include <algorithm>
24 :
25 : nsBoxLayout* nsSprocketLayout::gInstance = nullptr;
26 :
27 : //#define DEBUG_GROW
28 :
29 : #define DEBUG_SPRING_SIZE 8
30 : #define DEBUG_BORDER_SIZE 2
31 : #define COIL_SIZE 8
32 :
33 :
34 : nsresult
35 232 : NS_NewSprocketLayout(nsCOMPtr<nsBoxLayout>& aNewLayout)
36 : {
37 232 : if (!nsSprocketLayout::gInstance) {
38 2 : nsSprocketLayout::gInstance = new nsSprocketLayout();
39 2 : NS_IF_ADDREF(nsSprocketLayout::gInstance);
40 : }
41 : // we have not instance variables so just return our static one.
42 232 : aNewLayout = nsSprocketLayout::gInstance;
43 232 : return NS_OK;
44 : }
45 :
46 : /*static*/ void
47 0 : nsSprocketLayout::Shutdown()
48 : {
49 0 : NS_IF_RELEASE(gInstance);
50 0 : }
51 :
52 2 : nsSprocketLayout::nsSprocketLayout()
53 : {
54 2 : }
55 :
56 : bool
57 4031 : nsSprocketLayout::IsXULHorizontal(nsIFrame* aBox)
58 : {
59 4031 : return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
60 : }
61 :
62 : void
63 3116 : nsSprocketLayout::GetFrameState(nsIFrame* aBox, nsFrameState& aState)
64 : {
65 3116 : aState = aBox->GetStateBits();
66 3116 : }
67 :
68 : static uint8_t
69 1249 : GetFrameDirection(nsIFrame* aBox)
70 : {
71 1249 : return aBox->StyleVisibility()->mDirection;
72 : }
73 :
74 : static void
75 876 : HandleBoxPack(nsIFrame* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY,
76 : const nsRect& aOriginalRect, const nsRect& aClientRect)
77 : {
78 : // In the normal direction we lay out our kids in the positive direction (e.g., |x| will get
79 : // bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse
80 : // direction, the opposite is true. We'll be laying out each child at a smaller |x| or
81 : // |y|.
82 876 : uint8_t frameDirection = GetFrameDirection(aBox);
83 :
84 876 : if (aFrameState & NS_STATE_IS_HORIZONTAL) {
85 666 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
86 : // The normal direction. |x| increases as we move through our children.
87 666 : aX = aClientRect.x;
88 : }
89 : else {
90 : // The reverse direction. |x| decreases as we move through our children.
91 0 : aX = aClientRect.x + aOriginalRect.width;
92 : }
93 : // |y| is always in the normal direction in horizontal boxes
94 666 : aY = aClientRect.y;
95 : }
96 : else {
97 : // take direction property into account for |x| in vertical boxes
98 210 : if (frameDirection == NS_STYLE_DIRECTION_LTR) {
99 : // The normal direction. |x| increases as we move through our children.
100 210 : aX = aClientRect.x;
101 : }
102 : else {
103 : // The reverse direction. |x| decreases as we move through our children.
104 0 : aX = aClientRect.x + aOriginalRect.width;
105 : }
106 210 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
107 : // The normal direction. |y| increases as we move through our children.
108 210 : aY = aClientRect.y;
109 : }
110 : else {
111 : // The reverse direction. |y| decreases as we move through our children.
112 0 : aY = aClientRect.y + aOriginalRect.height;
113 : }
114 : }
115 :
116 : // Get our pack/alignment information.
117 876 : nsIFrame::Halignment halign = aBox->GetXULHAlign();
118 876 : nsIFrame::Valignment valign = aBox->GetXULVAlign();
119 :
120 : // The following code handles box PACKING. Packing comes into play in the case where the computed size for
121 : // all of our children (now stored in our client rect) is smaller than the size available for
122 : // the box (stored in |aOriginalRect|).
123 : //
124 : // Here we adjust our |x| and |y| variables accordingly so that we start at the beginning,
125 : // middle, or end of the box.
126 : //
127 : // XXXdwh JUSTIFY needs to be implemented!
128 876 : if (aFrameState & NS_STATE_IS_HORIZONTAL) {
129 666 : switch(halign) {
130 : case nsBoxFrame::hAlign_Left:
131 566 : break; // Nothing to do. The default initialized us properly.
132 :
133 : case nsBoxFrame::hAlign_Center:
134 100 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
135 100 : aX += (aOriginalRect.width - aClientRect.width)/2;
136 : else
137 0 : aX -= (aOriginalRect.width - aClientRect.width)/2;
138 100 : break;
139 :
140 : case nsBoxFrame::hAlign_Right:
141 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
142 0 : aX += (aOriginalRect.width - aClientRect.width);
143 : else
144 0 : aX -= (aOriginalRect.width - aClientRect.width);
145 0 : break; // Nothing to do for the reverse dir. The default initialized us properly.
146 : }
147 : } else {
148 210 : switch(valign) {
149 : case nsBoxFrame::vAlign_Top:
150 : case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack.
151 204 : break; // Don't do anything. We were initialized correctly.
152 :
153 : case nsBoxFrame::vAlign_Middle:
154 6 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
155 6 : aY += (aOriginalRect.height - aClientRect.height)/2;
156 : else
157 0 : aY -= (aOriginalRect.height - aClientRect.height)/2;
158 6 : break;
159 :
160 : case nsBoxFrame::vAlign_Bottom:
161 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
162 0 : aY += (aOriginalRect.height - aClientRect.height);
163 : else
164 0 : aY -= (aOriginalRect.height - aClientRect.height);
165 0 : break;
166 : }
167 : }
168 876 : }
169 :
170 : NS_IMETHODIMP
171 515 : nsSprocketLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState)
172 : {
173 : // See if we are collapsed. If we are, then simply iterate over all our
174 : // children and give them a rect of 0 width and height.
175 515 : if (aBox->IsXULCollapsed()) {
176 77 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
177 321 : while(child)
178 : {
179 122 : nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0));
180 122 : child = nsBox::GetNextXULBox(child);
181 : }
182 77 : return NS_OK;
183 : }
184 :
185 876 : nsBoxLayoutState::AutoReflowDepth depth(aState);
186 876 : mozilla::AutoStackArena arena;
187 :
188 : // ----- figure out our size ----------
189 438 : const nsSize originalSize = aBox->GetSize();
190 :
191 : // -- make sure we remove our border and padding ----
192 876 : nsRect clientRect;
193 438 : aBox->GetXULClientRect(clientRect);
194 :
195 : // |originalClientRect| represents the rect of the entire box (excluding borders
196 : // and padding). We store it here because we're going to use |clientRect| to hold
197 : // the required size for all our kids. As an example, consider an hbox with a
198 : // specified width of 300. If the kids total only 150 pixels of width, then
199 : // we have 150 pixels left over. |clientRect| is going to hold a width of 150 and
200 : // is going to be adjusted based off the value of the PACK property. If flexible
201 : // objects are in the box, then the two rects will match.
202 876 : nsRect originalClientRect(clientRect);
203 :
204 : // The frame state contains cached knowledge about our box, such as our orientation
205 : // and direction.
206 438 : nsFrameState frameState = nsFrameState(0);
207 438 : GetFrameState(aBox, frameState);
208 :
209 : // Build a list of our children's desired sizes and computed sizes
210 438 : nsBoxSize* boxSizes = nullptr;
211 438 : nsComputedBoxSize* computedBoxSizes = nullptr;
212 :
213 438 : nscoord min = 0;
214 438 : nscoord max = 0;
215 438 : int32_t flexes = 0;
216 438 : PopulateBoxSizes(aBox, aState, boxSizes, min, max, flexes);
217 :
218 : // The |size| variable will hold the total size of children along the axis of
219 : // the box. Continuing with the example begun in the comment above, size would
220 : // be 150 pixels.
221 438 : nscoord size = clientRect.width;
222 438 : if (!IsXULHorizontal(aBox))
223 105 : size = clientRect.height;
224 438 : ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes);
225 :
226 : // After the call to ComputeChildSizes, the |size| variable contains the
227 : // total required size of all the children. We adjust our clientRect in the
228 : // appropriate dimension to match this size. In our example, we now assign
229 : // 150 pixels into the clientRect.width.
230 : //
231 : // The variables |min| and |max| hold the minimum required size box must be
232 : // in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum
233 : // height we require to enclose our children, and |max| is the maximum height
234 : // required to enclose our children.
235 438 : if (IsXULHorizontal(aBox)) {
236 333 : clientRect.width = size;
237 333 : if (clientRect.height < min)
238 5 : clientRect.height = min;
239 :
240 333 : if (frameState & NS_STATE_AUTO_STRETCH) {
241 215 : if (clientRect.height > max)
242 0 : clientRect.height = max;
243 : }
244 : } else {
245 105 : clientRect.height = size;
246 105 : if (clientRect.width < min)
247 5 : clientRect.width = min;
248 :
249 105 : if (frameState & NS_STATE_AUTO_STRETCH) {
250 100 : if (clientRect.width > max)
251 0 : clientRect.width = max;
252 : }
253 : }
254 :
255 : // With the sizes computed, now it's time to lay out our children.
256 : bool finished;
257 438 : nscoord passes = 0;
258 :
259 : // We flow children at their preferred locations (along with the appropriate computed flex).
260 : // After we flow a child, it is possible that the child will change its size. If/when this happens,
261 : // we have to do another pass. Typically only 2 passes are required, but the code is prepared to
262 : // do as many passes as are necessary to achieve equilibrium.
263 438 : nscoord x = 0;
264 438 : nscoord y = 0;
265 438 : nscoord origX = 0;
266 438 : nscoord origY = 0;
267 :
268 : // |childResized| lets us know if a child changed its size after we attempted to lay it out at
269 : // the specified size. If this happens, we usually have to do another pass.
270 438 : bool childResized = false;
271 :
272 : // |passes| stores our number of passes. If for any reason we end up doing more than, say, 10
273 : // passes, we assert to indicate that something is seriously screwed up.
274 438 : passes = 0;
275 0 : do
276 : {
277 : #ifdef DEBUG_REFLOW
278 : if (passes > 0) {
279 : AddIndents();
280 : printf("ChildResized doing pass: %d\n", passes);
281 : }
282 : #endif
283 :
284 : // Always assume that we're done. This will change if, for example, children don't stay
285 : // the same size after being flowed.
286 438 : finished = true;
287 :
288 : // Handle box packing.
289 438 : HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
290 :
291 : // Now that packing is taken care of we set up a few additional
292 : // tracking variables.
293 438 : origX = x;
294 438 : origY = y;
295 :
296 : // Now we iterate over our box children and our box size lists in
297 : // parallel. For each child, we look at its sizes and figure out
298 : // where to place it.
299 438 : nsComputedBoxSize* childComputedBoxSize = computedBoxSizes;
300 438 : nsBoxSize* childBoxSize = boxSizes;
301 :
302 438 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
303 :
304 438 : int32_t count = 0;
305 2784 : while (child || (childBoxSize && childBoxSize->bogus))
306 : {
307 : // If for some reason, our lists are not the same length, we guard
308 : // by bailing out of the loop.
309 1173 : if (childBoxSize == nullptr) {
310 0 : NS_NOTREACHED("Lists not the same length.");
311 0 : break;
312 : }
313 :
314 1173 : nscoord width = clientRect.width;
315 1173 : nscoord height = clientRect.height;
316 :
317 1173 : if (!childBoxSize->bogus) {
318 : // We have a valid box size entry. This entry already contains information about our
319 : // sizes along the axis of the box (e.g., widths in a horizontal box). If our default
320 : // ALIGN is not stretch, however, then we also need to know the child's size along the
321 : // opposite axis.
322 1173 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
323 222 : nsSize prefSize = child->GetXULPrefSize(aState);
324 222 : nsSize minSize = child->GetXULMinSize(aState);
325 222 : nsSize maxSize = child->GetXULMaxSize(aState);
326 222 : prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
327 :
328 222 : AddMargin(child, prefSize);
329 222 : width = std::min(prefSize.width, originalClientRect.width);
330 222 : height = std::min(prefSize.height, originalClientRect.height);
331 : }
332 : }
333 :
334 : // Obtain the computed size along the axis of the box for this child from the computedBoxSize entry.
335 : // We store the result in |width| for horizontal boxes and |height| for vertical boxes.
336 1173 : if (frameState & NS_STATE_IS_HORIZONTAL)
337 805 : width = childComputedBoxSize->size;
338 : else
339 368 : height = childComputedBoxSize->size;
340 :
341 : // Adjust our x/y for the left/right spacing.
342 1173 : if (frameState & NS_STATE_IS_HORIZONTAL) {
343 805 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
344 805 : x += (childBoxSize->left);
345 : else
346 0 : x -= (childBoxSize->right);
347 : } else {
348 368 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
349 368 : y += (childBoxSize->left);
350 : else
351 0 : y -= (childBoxSize->right);
352 : }
353 :
354 : // Now we build a child rect.
355 1173 : nscoord rectX = x;
356 1173 : nscoord rectY = y;
357 1173 : if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
358 0 : if (frameState & NS_STATE_IS_HORIZONTAL)
359 0 : rectX -= width;
360 : else
361 0 : rectY -= height;
362 : }
363 :
364 : // We now create an accurate child rect based off our computed size information.
365 2346 : nsRect childRect(rectX, rectY, width, height);
366 :
367 : // Sanity check against our clientRect. It is possible that a child specified
368 : // a size that is too large to fit. If that happens, then we have to grow
369 : // our client rect. Remember, clientRect is not the total rect of the enclosing
370 : // box. It currently holds our perception of how big the children needed to
371 : // be.
372 1173 : if (childRect.width > clientRect.width)
373 0 : clientRect.width = childRect.width;
374 :
375 1173 : if (childRect.height > clientRect.height)
376 0 : clientRect.height = childRect.height;
377 :
378 : // Either |nextX| or |nextY| is updated by this function call, according
379 : // to our axis.
380 1173 : nscoord nextX = x;
381 1173 : nscoord nextY = y;
382 :
383 1173 : ComputeChildsNextPosition(aBox, x, y, nextX, nextY, childRect);
384 :
385 : // Now we further update our nextX/Y along our axis.
386 : // We also set childRect.y/x along the opposite axis appropriately for a
387 : // stretch alignment. (Non-stretch alignment is handled below.)
388 1173 : if (frameState & NS_STATE_IS_HORIZONTAL) {
389 805 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
390 805 : nextX += (childBoxSize->right);
391 : else
392 0 : nextX -= (childBoxSize->left);
393 805 : childRect.y = originalClientRect.y;
394 : }
395 : else {
396 368 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
397 368 : nextY += (childBoxSize->right);
398 : else
399 0 : nextY -= (childBoxSize->left);
400 368 : if (GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR) {
401 368 : childRect.x = originalClientRect.x;
402 : } else {
403 : // keep the right edge of the box the same
404 0 : childRect.x = clientRect.x + originalClientRect.width - childRect.width;
405 : }
406 : }
407 :
408 : // If we encounter a completely bogus box size, we just leave this child completely
409 : // alone and continue through the loop to the next child.
410 1173 : if (childBoxSize->bogus)
411 : {
412 0 : childComputedBoxSize = childComputedBoxSize->next;
413 0 : childBoxSize = childBoxSize->next;
414 0 : count++;
415 0 : x = nextX;
416 0 : y = nextY;
417 0 : continue;
418 : }
419 :
420 1173 : nsMargin margin(0,0,0,0);
421 :
422 1173 : bool layout = true;
423 :
424 : // Deflate the rect of our child by its margin.
425 1173 : child->GetXULMargin(margin);
426 1173 : childRect.Deflate(margin);
427 1173 : if (childRect.width < 0)
428 0 : childRect.width = 0;
429 1173 : if (childRect.height < 0)
430 0 : childRect.height = 0;
431 :
432 : // Now we're trying to figure out if we have to lay out this child, i.e., to call
433 : // the child's XULLayout method.
434 1173 : if (passes > 0) {
435 0 : layout = false;
436 : } else {
437 : // Always perform layout if we are dirty or have dirty children
438 1173 : if (!NS_SUBTREE_DIRTY(child))
439 680 : layout = false;
440 : }
441 :
442 2346 : nsRect oldRect(child->GetRect());
443 :
444 : // Non-stretch alignment will be handled in AlignChildren(), so don't
445 : // change child out-of-axis positions yet.
446 1173 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
447 222 : if (frameState & NS_STATE_IS_HORIZONTAL) {
448 220 : childRect.y = oldRect.y;
449 : } else {
450 2 : childRect.x = oldRect.x;
451 : }
452 : }
453 :
454 : // We computed a childRect. Now we want to set the bounds of the child to be that rect.
455 : // If our old rect is different, then we know our size changed and we cache that fact
456 : // in the |sizeChanged| variable.
457 :
458 1173 : child->SetXULBounds(aState, childRect);
459 2037 : bool sizeChanged = (childRect.width != oldRect.width ||
460 2037 : childRect.height != oldRect.height);
461 :
462 1173 : if (sizeChanged) {
463 : // Our size is different. Sanity check against our maximum allowed size to ensure
464 : // we didn't exceed it.
465 371 : nsSize minSize = child->GetXULMinSize(aState);
466 371 : nsSize maxSize = child->GetXULMaxSize(aState);
467 371 : maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize);
468 :
469 : // make sure the size is in our max size.
470 371 : if (childRect.width > maxSize.width)
471 0 : childRect.width = maxSize.width;
472 :
473 371 : if (childRect.height > maxSize.height)
474 0 : childRect.height = maxSize.height;
475 :
476 : // set it again
477 371 : child->SetXULBounds(aState, childRect);
478 : }
479 :
480 : // If we already determined that layout was required or if our size has changed, then
481 : // we make sure to call layout on the child, since its children may need to be shifted
482 : // around as a result of the size change.
483 1173 : if (layout || sizeChanged)
484 611 : child->XULLayout(aState);
485 :
486 : // If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout.
487 : // We have to check for this.
488 2346 : nsRect newChildRect(child->GetRect());
489 :
490 1173 : if (!newChildRect.IsEqualInterior(childRect)) {
491 : #ifdef DEBUG_GROW
492 : child->XULDumpBox(stdout);
493 : printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height);
494 : #endif
495 0 : newChildRect.Inflate(margin);
496 0 : childRect.Inflate(margin);
497 :
498 : // The child changed size during layout. The ChildResized method handles this
499 : // scenario.
500 : ChildResized(aBox,
501 : aState,
502 : child,
503 : childBoxSize,
504 : childComputedBoxSize,
505 : boxSizes,
506 : computedBoxSizes,
507 : childRect,
508 : newChildRect,
509 : clientRect,
510 : flexes,
511 0 : finished);
512 :
513 : // We note that a child changed size, which means that another pass will be required.
514 0 : childResized = true;
515 :
516 : // Now that a child resized, it's entirely possible that OUR rect is too small. Now we
517 : // ensure that |originalClientRect| is grown to accommodate the size of |clientRect|.
518 0 : if (clientRect.width > originalClientRect.width)
519 0 : originalClientRect.width = clientRect.width;
520 :
521 0 : if (clientRect.height > originalClientRect.height)
522 0 : originalClientRect.height = clientRect.height;
523 :
524 0 : if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
525 : // Our childRect had its XMost() or YMost() (depending on our layout
526 : // direction), positioned at a certain point. Ensure that the
527 : // newChildRect satisfies the same constraint. Note that this is
528 : // just equivalent to adjusting the x/y by the difference in
529 : // width/height between childRect and newChildRect. So we don't need
530 : // to reaccount for the left and right of the box layout state again.
531 0 : if (frameState & NS_STATE_IS_HORIZONTAL)
532 0 : newChildRect.x = childRect.XMost() - newChildRect.width;
533 : else
534 0 : newChildRect.y = childRect.YMost() - newChildRect.height;
535 : }
536 :
537 0 : if (!(frameState & NS_STATE_IS_HORIZONTAL)) {
538 0 : if (GetFrameDirection(aBox) != NS_STYLE_DIRECTION_LTR) {
539 : // keep the right edge the same
540 0 : newChildRect.x = childRect.XMost() - newChildRect.width;
541 : }
542 : }
543 :
544 : // If the child resized then recompute its position.
545 0 : ComputeChildsNextPosition(aBox, x, y, nextX, nextY, newChildRect);
546 :
547 0 : if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom)
548 0 : newChildRect.Deflate(margin);
549 :
550 0 : if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom)
551 0 : childRect.Deflate(margin);
552 :
553 0 : child->SetXULBounds(aState, newChildRect);
554 :
555 : // If we are the first box that changed size, then we don't need to do a second pass
556 0 : if (count == 0)
557 0 : finished = true;
558 : }
559 :
560 : // Now update our x/y finally.
561 1173 : x = nextX;
562 1173 : y = nextY;
563 :
564 : // Move to the next child.
565 1173 : childComputedBoxSize = childComputedBoxSize->next;
566 1173 : childBoxSize = childBoxSize->next;
567 :
568 1173 : child = nsBox::GetNextXULBox(child);
569 1173 : count++;
570 : }
571 :
572 : // Sanity-checking code to ensure we don't do an infinite # of passes.
573 438 : passes++;
574 438 : NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!");
575 438 : if (passes >= 10)
576 0 : break;
577 438 : } while (false == finished);
578 :
579 : // Get rid of our size lists.
580 1173 : while(boxSizes)
581 : {
582 1173 : nsBoxSize* toDelete = boxSizes;
583 1173 : boxSizes = boxSizes->next;
584 1173 : delete toDelete;
585 : }
586 :
587 1198 : while(computedBoxSizes)
588 : {
589 1198 : nsComputedBoxSize* toDelete = computedBoxSizes;
590 1198 : computedBoxSizes = computedBoxSizes->next;
591 1198 : delete toDelete;
592 : }
593 :
594 438 : if (childResized) {
595 : // See if one of our children forced us to get bigger
596 0 : nsRect tmpClientRect(originalClientRect);
597 0 : nsMargin bp(0,0,0,0);
598 0 : aBox->GetXULBorderAndPadding(bp);
599 0 : tmpClientRect.Inflate(bp);
600 :
601 0 : if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height)
602 : {
603 : // if it did reset our bounds.
604 0 : nsRect bounds(aBox->GetRect());
605 0 : if (tmpClientRect.width > originalSize.width)
606 0 : bounds.width = tmpClientRect.width;
607 :
608 0 : if (tmpClientRect.height > originalSize.height)
609 0 : bounds.height = tmpClientRect.height;
610 :
611 0 : aBox->SetXULBounds(aState, bounds);
612 : }
613 : }
614 :
615 : // Because our size grew, we now have to readjust because of box packing. Repack
616 : // in order to update our x and y to the correct values.
617 438 : HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
618 :
619 : // Compare against our original x and y and only worry about adjusting the children if
620 : // we really did have to change the positions because of packing (typically for 'center'
621 : // or 'end' pack values).
622 438 : if (x != origX || y != origY) {
623 0 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
624 :
625 : // reposition all our children
626 0 : while (child)
627 : {
628 0 : nsRect childRect(child->GetRect());
629 0 : childRect.x += (x - origX);
630 0 : childRect.y += (y - origY);
631 0 : child->SetXULBounds(aState, childRect);
632 0 : child = nsBox::GetNextXULBox(child);
633 : }
634 : }
635 :
636 : // Perform out-of-axis alignment for non-stretch alignments
637 438 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
638 123 : AlignChildren(aBox, aState);
639 : }
640 :
641 : // That's it! If you made it this far without having a nervous breakdown,
642 : // congratulations! Go get yourself a beer.
643 438 : return NS_OK;
644 : }
645 :
646 : void
647 438 : nsSprocketLayout::PopulateBoxSizes(nsIFrame* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, int32_t& aFlexes)
648 : {
649 : // used for the equal size flag
650 438 : nscoord biggestPrefWidth = 0;
651 438 : nscoord biggestMinWidth = 0;
652 438 : nscoord smallestMaxWidth = NS_INTRINSICSIZE;
653 :
654 438 : nsFrameState frameState = nsFrameState(0);
655 438 : GetFrameState(aBox, frameState);
656 :
657 : //if (frameState & NS_STATE_CURRENTLY_IN_DEBUG)
658 : // printf("In debug\n");
659 :
660 438 : aMinSize = 0;
661 438 : aMaxSize = NS_INTRINSICSIZE;
662 :
663 : bool isHorizontal;
664 :
665 438 : if (IsXULHorizontal(aBox))
666 333 : isHorizontal = true;
667 : else
668 105 : isHorizontal = false;
669 :
670 : // this is a nice little optimization
671 : // it turns out that if we only have 1 flexable child
672 : // then it does not matter what its preferred size is
673 : // there is nothing to flex it relative. This is great
674 : // because we can avoid asking for a preferred size in this
675 : // case. Why is this good? Well you might have html inside it
676 : // and asking html for its preferred size is rather expensive.
677 : // so we can just optimize it out this way.
678 :
679 : // set flexes
680 438 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
681 :
682 438 : aFlexes = 0;
683 438 : nsBoxSize* currentBox = nullptr;
684 :
685 : #if 0
686 : nsBoxSize* start = aBoxSizes;
687 :
688 : while(child)
689 : {
690 : // ok if we started with a list move down the list
691 : // until we reach the end. Then start looking at childen.
692 : // This feature is used extensively for Grid.
693 : nscoord flex = 0;
694 :
695 : if (!start) {
696 : if (!currentBox) {
697 : aBoxSizes = new (aState) nsBoxSize();
698 : currentBox = aBoxSizes;
699 : } else {
700 : currentBox->next = new (aState) nsBoxSize();
701 : currentBox = currentBox->next;
702 : }
703 :
704 :
705 : flex = child->GetXULFlex();
706 :
707 : currentBox->flex = flex;
708 : currentBox->collapsed = child->IsXULCollapsed();
709 : } else {
710 : flex = start->flex;
711 : start = start->next;
712 : }
713 :
714 : if (flex > 0)
715 : aFlexes++;
716 :
717 : child = GetNextXULBox(child);
718 : }
719 : #endif
720 :
721 : // get pref, min, max
722 438 : child = nsBox::GetChildXULBox(aBox);
723 438 : currentBox = aBoxSizes;
724 438 : nsBoxSize* last = nullptr;
725 :
726 438 : nscoord maxFlex = 0;
727 438 : int32_t childCount = 0;
728 :
729 2784 : while(child)
730 : {
731 1173 : while (currentBox && currentBox->bogus) {
732 0 : last = currentBox;
733 0 : currentBox = currentBox->next;
734 : }
735 1173 : ++childCount;
736 1173 : nsSize pref(0,0);
737 1173 : nsSize minSize(0,0);
738 1173 : nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
739 1173 : nscoord ascent = 0;
740 1173 : bool collapsed = child->IsXULCollapsed();
741 :
742 1173 : if (!collapsed) {
743 : // only one flexible child? Cool we will just make its preferred size
744 : // 0 then and not even have to ask for it.
745 : //if (flexes != 1) {
746 :
747 1008 : pref = child->GetXULPrefSize(aState);
748 1008 : minSize = child->GetXULMinSize(aState);
749 1008 : maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetXULMaxSize(aState));
750 1008 : ascent = child->GetXULBoxAscent(aState);
751 1008 : nsMargin margin;
752 1008 : child->GetXULMargin(margin);
753 1008 : ascent += margin.top;
754 : //}
755 :
756 1008 : pref = nsBox::BoundsCheck(minSize, pref, maxSize);
757 :
758 1008 : AddMargin(child, pref);
759 1008 : AddMargin(child, minSize);
760 1008 : AddMargin(child, maxSize);
761 : }
762 :
763 1173 : if (!currentBox) {
764 : // create one.
765 1173 : currentBox = new (aState) nsBoxSize();
766 1173 : if (!aBoxSizes) {
767 413 : aBoxSizes = currentBox;
768 413 : last = aBoxSizes;
769 : } else {
770 760 : last->next = currentBox;
771 760 : last = currentBox;
772 : }
773 :
774 : nscoord minWidth;
775 : nscoord maxWidth;
776 : nscoord prefWidth;
777 :
778 : // get sizes from child
779 1173 : if (isHorizontal) {
780 805 : minWidth = minSize.width;
781 805 : maxWidth = maxSize.width;
782 805 : prefWidth = pref.width;
783 : } else {
784 368 : minWidth = minSize.height;
785 368 : maxWidth = maxSize.height;
786 368 : prefWidth = pref.height;
787 : }
788 :
789 1173 : nscoord flex = child->GetXULFlex();
790 :
791 : // set them if you collapsed you are not flexible.
792 1173 : if (collapsed) {
793 165 : currentBox->flex = 0;
794 : }
795 : else {
796 1008 : if (flex > maxFlex) {
797 269 : maxFlex = flex;
798 : }
799 1008 : currentBox->flex = flex;
800 : }
801 :
802 : // we specified all our children are equal size;
803 1173 : if (frameState & NS_STATE_EQUAL_SIZE) {
804 :
805 0 : if (prefWidth > biggestPrefWidth)
806 0 : biggestPrefWidth = prefWidth;
807 :
808 0 : if (minWidth > biggestMinWidth)
809 0 : biggestMinWidth = minWidth;
810 :
811 0 : if (maxWidth < smallestMaxWidth)
812 0 : smallestMaxWidth = maxWidth;
813 : } else { // not we can set our children right now.
814 1173 : currentBox->pref = prefWidth;
815 1173 : currentBox->min = minWidth;
816 1173 : currentBox->max = maxWidth;
817 : }
818 :
819 1173 : NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!");
820 :
821 : }
822 :
823 1173 : if (!isHorizontal) {
824 368 : if (minSize.width > aMinSize)
825 127 : aMinSize = minSize.width;
826 :
827 368 : if (maxSize.width < aMaxSize)
828 0 : aMaxSize = maxSize.width;
829 :
830 : } else {
831 805 : if (minSize.height > aMinSize)
832 251 : aMinSize = minSize.height;
833 :
834 805 : if (maxSize.height < aMaxSize)
835 0 : aMaxSize = maxSize.height;
836 : }
837 :
838 1173 : currentBox->collapsed = collapsed;
839 1173 : aFlexes += currentBox->flex;
840 :
841 1173 : child = nsBox::GetNextXULBox(child);
842 :
843 1173 : last = currentBox;
844 1173 : currentBox = currentBox->next;
845 :
846 : }
847 :
848 438 : if (childCount > 0) {
849 413 : nscoord maxAllowedFlex = nscoord_MAX / childCount;
850 :
851 413 : if (MOZ_UNLIKELY(maxFlex > maxAllowedFlex)) {
852 : // clamp all the flexes
853 0 : currentBox = aBoxSizes;
854 0 : while (currentBox) {
855 0 : currentBox->flex = std::min(currentBox->flex, maxAllowedFlex);
856 0 : currentBox = currentBox->next;
857 : }
858 : }
859 : }
860 : #ifdef DEBUG
861 : else {
862 25 : NS_ASSERTION(maxFlex == 0, "How did that happen?");
863 : }
864 : #endif
865 :
866 : // we specified all our children are equal size;
867 438 : if (frameState & NS_STATE_EQUAL_SIZE) {
868 0 : smallestMaxWidth = std::max(smallestMaxWidth, biggestMinWidth);
869 0 : biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth);
870 :
871 0 : currentBox = aBoxSizes;
872 :
873 0 : while(currentBox)
874 : {
875 0 : if (!currentBox->collapsed) {
876 0 : currentBox->pref = biggestPrefWidth;
877 0 : currentBox->min = biggestMinWidth;
878 0 : currentBox->max = smallestMaxWidth;
879 : } else {
880 0 : currentBox->pref = 0;
881 0 : currentBox->min = 0;
882 0 : currentBox->max = 0;
883 : }
884 0 : currentBox = currentBox->next;
885 : }
886 : }
887 :
888 438 : }
889 :
890 : void
891 1173 : nsSprocketLayout::ComputeChildsNextPosition(nsIFrame* aBox,
892 : const nscoord& aCurX,
893 : const nscoord& aCurY,
894 : nscoord& aNextX,
895 : nscoord& aNextY,
896 : const nsRect& aCurrentChildSize)
897 : {
898 : // Get the position along the box axis for the child.
899 : // The out-of-axis position is not set.
900 1173 : nsFrameState frameState = nsFrameState(0);
901 1173 : GetFrameState(aBox, frameState);
902 :
903 1173 : if (IsXULHorizontal(aBox)) {
904 : // horizontal box's children.
905 805 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
906 805 : aNextX = aCurX + aCurrentChildSize.width;
907 : else
908 0 : aNextX = aCurX - aCurrentChildSize.width;
909 :
910 : } else {
911 : // vertical box's children.
912 368 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
913 368 : aNextY = aCurY + aCurrentChildSize.height;
914 : else
915 0 : aNextY = aCurY - aCurrentChildSize.height;
916 : }
917 1173 : }
918 :
919 : void
920 123 : nsSprocketLayout::AlignChildren(nsIFrame* aBox,
921 : nsBoxLayoutState& aState)
922 : {
923 123 : nsFrameState frameState = nsFrameState(0);
924 123 : GetFrameState(aBox, frameState);
925 123 : bool isHorizontal = (frameState & NS_STATE_IS_HORIZONTAL) != 0;
926 246 : nsRect clientRect;
927 123 : aBox->GetXULClientRect(clientRect);
928 :
929 123 : NS_PRECONDITION(!(frameState & NS_STATE_AUTO_STRETCH),
930 : "Only AlignChildren() with non-stretch alignment");
931 :
932 : // These are only calculated if needed
933 : nsIFrame::Halignment halign;
934 : nsIFrame::Valignment valign;
935 123 : nscoord maxAscent = 0;
936 : bool isLTR;
937 :
938 123 : if (isHorizontal) {
939 118 : valign = aBox->GetXULVAlign();
940 118 : if (valign == nsBoxFrame::vAlign_BaseLine) {
941 0 : maxAscent = aBox->GetXULBoxAscent(aState);
942 : }
943 : } else {
944 5 : isLTR = GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR;
945 5 : halign = aBox->GetXULHAlign();
946 : }
947 :
948 123 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
949 567 : while (child) {
950 :
951 222 : nsMargin margin;
952 222 : child->GetXULMargin(margin);
953 444 : nsRect childRect = child->GetRect();
954 :
955 222 : if (isHorizontal) {
956 220 : const nscoord startAlign = clientRect.y + margin.top;
957 : const nscoord endAlign =
958 220 : clientRect.YMost() - margin.bottom - childRect.height;
959 :
960 220 : nscoord y = 0;
961 220 : switch (valign) {
962 : case nsBoxFrame::vAlign_Top:
963 0 : y = startAlign;
964 0 : break;
965 : case nsBoxFrame::vAlign_Middle:
966 : // Should this center the border box?
967 : // This centers the margin box, the historical behavior.
968 215 : y = (startAlign + endAlign) / 2;
969 215 : break;
970 : case nsBoxFrame::vAlign_Bottom:
971 5 : y = endAlign;
972 5 : break;
973 : case nsBoxFrame::vAlign_BaseLine:
974 : // Alignments don't force the box to grow (only sizes do),
975 : // so keep the children within the box.
976 0 : y = maxAscent - child->GetXULBoxAscent(aState);
977 0 : y = std::max(startAlign, y);
978 0 : y = std::min(y, endAlign);
979 0 : break;
980 : }
981 :
982 220 : childRect.y = y;
983 :
984 : } else { // vertical box
985 2 : const nscoord leftAlign = clientRect.x + margin.left;
986 : const nscoord rightAlign =
987 2 : clientRect.XMost() - margin.right - childRect.width;
988 :
989 2 : nscoord x = 0;
990 2 : switch (halign) {
991 : case nsBoxFrame::hAlign_Left: // start
992 0 : x = isLTR ? leftAlign : rightAlign;
993 0 : break;
994 : case nsBoxFrame::hAlign_Center:
995 2 : x = (leftAlign + rightAlign) / 2;
996 2 : break;
997 : case nsBoxFrame::hAlign_Right: // end
998 0 : x = isLTR ? rightAlign : leftAlign;
999 0 : break;
1000 : }
1001 :
1002 2 : childRect.x = x;
1003 : }
1004 :
1005 222 : if (childRect.TopLeft() != child->GetPosition()) {
1006 87 : child->SetXULBounds(aState, childRect);
1007 : }
1008 :
1009 222 : child = nsBox::GetNextXULBox(child);
1010 : }
1011 123 : }
1012 :
1013 : void
1014 0 : nsSprocketLayout::ChildResized(nsIFrame* aBox,
1015 : nsBoxLayoutState& aState,
1016 : nsIFrame* aChild,
1017 : nsBoxSize* aChildBoxSize,
1018 : nsComputedBoxSize* aChildComputedSize,
1019 : nsBoxSize* aBoxSizes,
1020 : nsComputedBoxSize* aComputedBoxSizes,
1021 : const nsRect& aChildLayoutRect,
1022 : nsRect& aChildActualRect,
1023 : nsRect& aContainingRect,
1024 : int32_t aFlexes,
1025 : bool& aFinished)
1026 :
1027 : {
1028 0 : nsRect childCurrentRect(aChildLayoutRect);
1029 :
1030 0 : bool isHorizontal = IsXULHorizontal(aBox);
1031 0 : nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal);
1032 0 : nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal);
1033 0 : nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal);
1034 :
1035 : //nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal);
1036 0 : nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal);
1037 0 : nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal);
1038 :
1039 0 : bool recompute = false;
1040 :
1041 : // if we are a horizontal box see if the child will fit inside us.
1042 0 : if ( childActualHeight > containingHeight) {
1043 : // if we are a horizontal box and the child is bigger than our height
1044 :
1045 : // ok if the height changed then we need to reflow everyone but us at the new height
1046 : // so we will set the changed index to be us. And signal that we need a new pass.
1047 :
1048 0 : nsSize min = aChild->GetXULMinSize(aState);
1049 0 : nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetXULMaxSize(aState));
1050 0 : AddMargin(aChild, max);
1051 :
1052 0 : if (isHorizontal)
1053 0 : childActualHeight = max.height < childActualHeight ? max.height : childActualHeight;
1054 : else
1055 0 : childActualHeight = max.width < childActualHeight ? max.width : childActualHeight;
1056 :
1057 : // only set if it changes
1058 0 : if (childActualHeight > containingHeight) {
1059 0 : containingHeight = childActualHeight;
1060 :
1061 : // remember we do not need to clear the resized list because changing the height of a horizontal box
1062 : // will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me
1063 : // on this one.
1064 0 : aFinished = false;
1065 :
1066 : // only recompute if there are flexes.
1067 0 : if (aFlexes > 0) {
1068 : // relayout everything
1069 0 : recompute = true;
1070 0 : InvalidateComputedSizes(aComputedBoxSizes);
1071 0 : nsComputedBoxSize* node = aComputedBoxSizes;
1072 :
1073 0 : while(node) {
1074 0 : node->resized = false;
1075 0 : node = node->next;
1076 : }
1077 :
1078 : }
1079 : }
1080 : }
1081 :
1082 0 : if (childActualWidth > childLayoutWidth) {
1083 0 : nsSize min = aChild->GetXULMinSize(aState);
1084 0 : nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetXULMaxSize(aState));
1085 :
1086 0 : AddMargin(aChild, max);
1087 :
1088 : // our width now becomes the new size
1089 :
1090 0 : if (isHorizontal)
1091 0 : childActualWidth = max.width < childActualWidth ? max.width : childActualWidth;
1092 : else
1093 0 : childActualWidth = max.height < childActualWidth ? max.height : childActualWidth;
1094 :
1095 0 : if (childActualWidth > childLayoutWidth) {
1096 0 : aChildComputedSize->size = childActualWidth;
1097 0 : aChildBoxSize->min = childActualWidth;
1098 0 : if (aChildBoxSize->pref < childActualWidth)
1099 0 : aChildBoxSize->pref = childActualWidth;
1100 0 : if (aChildBoxSize->max < childActualWidth)
1101 0 : aChildBoxSize->max = childActualWidth;
1102 :
1103 : // if we have flexible elements with us then reflex things. Otherwise we can skip doing it.
1104 0 : if (aFlexes > 0) {
1105 0 : InvalidateComputedSizes(aComputedBoxSizes);
1106 :
1107 0 : nsComputedBoxSize* node = aComputedBoxSizes;
1108 0 : aChildComputedSize->resized = true;
1109 :
1110 0 : while(node) {
1111 0 : if (node->resized)
1112 0 : node->valid = true;
1113 :
1114 0 : node = node->next;
1115 : }
1116 :
1117 0 : recompute = true;
1118 0 : aFinished = false;
1119 : } else {
1120 0 : containingWidth += aChildComputedSize->size - childLayoutWidth;
1121 : }
1122 : }
1123 : }
1124 :
1125 0 : if (recompute)
1126 0 : ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes);
1127 :
1128 0 : if (!childCurrentRect.IsEqualInterior(aChildActualRect)) {
1129 : // the childRect includes the margin
1130 : // make sure we remove it before setting
1131 : // the bounds.
1132 0 : nsMargin margin(0,0,0,0);
1133 0 : aChild->GetXULMargin(margin);
1134 0 : nsRect rect(aChildActualRect);
1135 0 : if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom)
1136 0 : rect.Deflate(margin);
1137 :
1138 0 : aChild->SetXULBounds(aState, rect);
1139 0 : aChild->XULLayout(aState);
1140 : }
1141 :
1142 0 : }
1143 :
1144 : void
1145 0 : nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes)
1146 : {
1147 0 : while(aComputedBoxSizes) {
1148 0 : aComputedBoxSizes->valid = false;
1149 0 : aComputedBoxSizes = aComputedBoxSizes->next;
1150 : }
1151 0 : }
1152 :
1153 : void
1154 438 : nsSprocketLayout::ComputeChildSizes(nsIFrame* aBox,
1155 : nsBoxLayoutState& aState,
1156 : nscoord& aGivenSize,
1157 : nsBoxSize* aBoxSizes,
1158 : nsComputedBoxSize*& aComputedBoxSizes)
1159 : {
1160 :
1161 : //nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1);
1162 :
1163 438 : int32_t sizeRemaining = aGivenSize;
1164 438 : int32_t spacerConstantsRemaining = 0;
1165 :
1166 : // ----- calculate the spacers constants and the size remaining -----
1167 :
1168 438 : if (!aComputedBoxSizes)
1169 438 : aComputedBoxSizes = new (aState) nsComputedBoxSize();
1170 :
1171 438 : nsBoxSize* boxSizes = aBoxSizes;
1172 438 : nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes;
1173 438 : int32_t count = 0;
1174 438 : int32_t validCount = 0;
1175 :
1176 2784 : while (boxSizes)
1177 : {
1178 :
1179 1173 : NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size");
1180 :
1181 :
1182 : // ignore collapsed children
1183 : // if (boxSizes->collapsed)
1184 : // {
1185 : // computedBoxSizes->valid = true;
1186 : // computedBoxSizes->size = boxSizes->pref;
1187 : // validCount++;
1188 : // boxSizes->flex = 0;
1189 : // }// else {
1190 :
1191 1173 : if (computedBoxSizes->valid) {
1192 0 : sizeRemaining -= computedBoxSizes->size;
1193 0 : validCount++;
1194 : } else {
1195 1173 : if (boxSizes->flex == 0)
1196 : {
1197 892 : computedBoxSizes->valid = true;
1198 892 : computedBoxSizes->size = boxSizes->pref;
1199 892 : validCount++;
1200 : }
1201 :
1202 1173 : spacerConstantsRemaining += boxSizes->flex;
1203 1173 : sizeRemaining -= boxSizes->pref;
1204 : }
1205 :
1206 1173 : sizeRemaining -= (boxSizes->left + boxSizes->right);
1207 :
1208 : //}
1209 :
1210 1173 : boxSizes = boxSizes->next;
1211 :
1212 1173 : if (boxSizes && !computedBoxSizes->next)
1213 760 : computedBoxSizes->next = new (aState) nsComputedBoxSize();
1214 :
1215 1173 : computedBoxSizes = computedBoxSizes->next;
1216 1173 : count++;
1217 : }
1218 :
1219 : // everything accounted for?
1220 438 : if (validCount < count)
1221 : {
1222 : // ----- Ok we are give a size to fit into so stretch or squeeze to fit
1223 : // ----- Make sure we look at our min and max size
1224 269 : bool limit = true;
1225 576 : for (int pass=1; true == limit; pass++)
1226 : {
1227 307 : limit = false;
1228 307 : boxSizes = aBoxSizes;
1229 307 : computedBoxSizes = aComputedBoxSizes;
1230 :
1231 2169 : while (boxSizes) {
1232 :
1233 : // ignore collapsed spacers
1234 :
1235 : // if (!boxSizes->collapsed) {
1236 :
1237 931 : nscoord pref = 0;
1238 931 : nscoord max = NS_INTRINSICSIZE;
1239 931 : nscoord min = 0;
1240 931 : nscoord flex = 0;
1241 :
1242 931 : pref = boxSizes->pref;
1243 931 : min = boxSizes->min;
1244 931 : max = boxSizes->max;
1245 931 : flex = boxSizes->flex;
1246 :
1247 : // ----- look at our min and max limits make sure we aren't too small or too big -----
1248 931 : if (!computedBoxSizes->valid) {
1249 281 : int32_t newSize = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining);
1250 :
1251 281 : if (newSize<=min) {
1252 31 : computedBoxSizes->size = min;
1253 31 : computedBoxSizes->valid = true;
1254 31 : spacerConstantsRemaining -= flex;
1255 31 : sizeRemaining += pref;
1256 31 : sizeRemaining -= min;
1257 31 : limit = true;
1258 250 : } else if (newSize>=max) {
1259 10 : computedBoxSizes->size = max;
1260 10 : computedBoxSizes->valid = true;
1261 10 : spacerConstantsRemaining -= flex;
1262 10 : sizeRemaining += pref;
1263 10 : sizeRemaining -= max;
1264 10 : limit = true;
1265 : }
1266 : }
1267 : // }
1268 931 : boxSizes = boxSizes->next;
1269 931 : computedBoxSizes = computedBoxSizes->next;
1270 : }
1271 : }
1272 : }
1273 :
1274 : // ---- once we have removed and min and max issues just stretch us out in the remaining space
1275 : // ---- or shrink us. Depends on the size remaining and the spacer constants
1276 438 : aGivenSize = 0;
1277 438 : boxSizes = aBoxSizes;
1278 438 : computedBoxSizes = aComputedBoxSizes;
1279 :
1280 2784 : while (boxSizes) {
1281 :
1282 : // ignore collapsed spacers
1283 : // if (!(boxSizes && boxSizes->collapsed)) {
1284 :
1285 1173 : nscoord pref = 0;
1286 1173 : nscoord flex = 0;
1287 1173 : pref = boxSizes->pref;
1288 1173 : flex = boxSizes->flex;
1289 :
1290 1173 : if (!computedBoxSizes->valid) {
1291 240 : computedBoxSizes->size = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining);
1292 240 : computedBoxSizes->valid = true;
1293 : }
1294 :
1295 1173 : aGivenSize += (boxSizes->left + boxSizes->right);
1296 1173 : aGivenSize += computedBoxSizes->size;
1297 :
1298 : // }
1299 :
1300 1173 : boxSizes = boxSizes->next;
1301 1173 : computedBoxSizes = computedBoxSizes->next;
1302 : }
1303 438 : }
1304 :
1305 :
1306 : nsSize
1307 324 : nsSprocketLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
1308 : {
1309 324 : nsSize vpref (0, 0);
1310 324 : bool isHorizontal = IsXULHorizontal(aBox);
1311 :
1312 324 : nscoord biggestPref = 0;
1313 :
1314 : // run through all the children and get their min, max, and preferred sizes
1315 : // return us the size of the box
1316 :
1317 324 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
1318 324 : nsFrameState frameState = nsFrameState(0);
1319 324 : GetFrameState(aBox, frameState);
1320 324 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1321 324 : int32_t count = 0;
1322 :
1323 1998 : while (child)
1324 : {
1325 : // ignore collapsed children
1326 837 : if (!child->IsXULCollapsed())
1327 : {
1328 707 : nsSize pref = child->GetXULPrefSize(aState);
1329 707 : AddMargin(child, pref);
1330 :
1331 707 : if (isEqual) {
1332 0 : if (isHorizontal)
1333 : {
1334 0 : if (pref.width > biggestPref)
1335 0 : biggestPref = pref.width;
1336 : } else {
1337 0 : if (pref.height > biggestPref)
1338 0 : biggestPref = pref.height;
1339 : }
1340 : }
1341 :
1342 707 : AddLargestSize(vpref, pref, isHorizontal);
1343 707 : count++;
1344 : }
1345 :
1346 837 : child = nsBox::GetNextXULBox(child);
1347 : }
1348 :
1349 324 : if (isEqual) {
1350 0 : if (isHorizontal)
1351 0 : vpref.width = biggestPref*count;
1352 : else
1353 0 : vpref.height = biggestPref*count;
1354 : }
1355 :
1356 : // now add our border and padding
1357 324 : AddBorderAndPadding(aBox, vpref);
1358 :
1359 324 : return vpref;
1360 : }
1361 :
1362 : nsSize
1363 300 : nsSprocketLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
1364 : {
1365 300 : nsSize minSize (0, 0);
1366 300 : bool isHorizontal = IsXULHorizontal(aBox);
1367 :
1368 300 : nscoord biggestMin = 0;
1369 :
1370 :
1371 : // run through all the children and get their min, max, and preferred sizes
1372 : // return us the size of the box
1373 :
1374 300 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
1375 300 : nsFrameState frameState = nsFrameState(0);
1376 300 : GetFrameState(aBox, frameState);
1377 300 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1378 300 : int32_t count = 0;
1379 :
1380 2014 : while (child)
1381 : {
1382 : // ignore collapsed children
1383 857 : if (!child->IsXULCollapsed())
1384 : {
1385 714 : nsSize min = child->GetXULMinSize(aState);
1386 714 : nsSize pref(0,0);
1387 :
1388 : // if the child is not flexible then
1389 : // its min size is its pref size.
1390 714 : if (child->GetXULFlex() == 0) {
1391 533 : pref = child->GetXULPrefSize(aState);
1392 533 : if (isHorizontal)
1393 307 : min.width = pref.width;
1394 : else
1395 226 : min.height = pref.height;
1396 : }
1397 :
1398 714 : if (isEqual) {
1399 0 : if (isHorizontal)
1400 : {
1401 0 : if (min.width > biggestMin)
1402 0 : biggestMin = min.width;
1403 : } else {
1404 0 : if (min.height > biggestMin)
1405 0 : biggestMin = min.height;
1406 : }
1407 : }
1408 :
1409 714 : AddMargin(child, min);
1410 714 : AddLargestSize(minSize, min, isHorizontal);
1411 714 : count++;
1412 : }
1413 :
1414 857 : child = nsBox::GetNextXULBox(child);
1415 : }
1416 :
1417 :
1418 300 : if (isEqual) {
1419 0 : if (isHorizontal)
1420 0 : minSize.width = biggestMin*count;
1421 : else
1422 0 : minSize.height = biggestMin*count;
1423 : }
1424 :
1425 : // now add our border and padding
1426 300 : AddBorderAndPadding(aBox, minSize);
1427 :
1428 300 : return minSize;
1429 : }
1430 :
1431 : nsSize
1432 320 : nsSprocketLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
1433 : {
1434 :
1435 320 : bool isHorizontal = IsXULHorizontal(aBox);
1436 :
1437 320 : nscoord smallestMax = NS_INTRINSICSIZE;
1438 320 : nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1439 :
1440 : // run through all the children and get their min, max, and preferred sizes
1441 : // return us the size of the box
1442 :
1443 320 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
1444 320 : nsFrameState frameState = nsFrameState(0);
1445 320 : GetFrameState(aBox, frameState);
1446 320 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1447 320 : int32_t count = 0;
1448 :
1449 2142 : while (child)
1450 : {
1451 : // ignore collapsed children
1452 911 : if (!child->IsXULCollapsed())
1453 : {
1454 : // if completely redefined don't even ask our child for its size.
1455 762 : nsSize min = child->GetXULMinSize(aState);
1456 762 : nsSize max = nsBox::BoundsCheckMinMax(min, child->GetXULMaxSize(aState));
1457 :
1458 762 : AddMargin(child, max);
1459 762 : AddSmallestSize(maxSize, max, isHorizontal);
1460 :
1461 762 : if (isEqual) {
1462 0 : if (isHorizontal)
1463 : {
1464 0 : if (max.width < smallestMax)
1465 0 : smallestMax = max.width;
1466 : } else {
1467 0 : if (max.height < smallestMax)
1468 0 : smallestMax = max.height;
1469 : }
1470 : }
1471 762 : count++;
1472 : }
1473 :
1474 911 : child = nsBox::GetNextXULBox(child);
1475 : }
1476 :
1477 320 : if (isEqual) {
1478 0 : if (isHorizontal) {
1479 0 : if (smallestMax != NS_INTRINSICSIZE)
1480 0 : maxSize.width = smallestMax*count;
1481 : else
1482 0 : maxSize.width = NS_INTRINSICSIZE;
1483 : } else {
1484 0 : if (smallestMax != NS_INTRINSICSIZE)
1485 0 : maxSize.height = smallestMax*count;
1486 : else
1487 0 : maxSize.height = NS_INTRINSICSIZE;
1488 : }
1489 : }
1490 :
1491 : // now add our border and padding
1492 320 : AddBorderAndPadding(aBox, maxSize);
1493 :
1494 320 : return maxSize;
1495 : }
1496 :
1497 :
1498 : nscoord
1499 600 : nsSprocketLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
1500 : {
1501 600 : nscoord vAscent = 0;
1502 :
1503 600 : bool isHorizontal = IsXULHorizontal(aBox);
1504 :
1505 : // run through all the children and get their min, max, and preferred sizes
1506 : // return us the size of the box
1507 :
1508 600 : nsIFrame* child = nsBox::GetChildXULBox(aBox);
1509 :
1510 3516 : while (child)
1511 : {
1512 : // ignore collapsed children
1513 : //if (!child->IsXULCollapsed())
1514 : //{
1515 : // if completely redefined don't even ask our child for its size.
1516 1458 : nscoord ascent = child->GetXULBoxAscent(aState);
1517 :
1518 1458 : nsMargin margin;
1519 1458 : child->GetXULMargin(margin);
1520 1458 : ascent += margin.top;
1521 :
1522 1458 : if (isHorizontal)
1523 : {
1524 1172 : if (ascent > vAscent)
1525 462 : vAscent = ascent;
1526 : } else {
1527 286 : if (vAscent == 0)
1528 108 : vAscent = ascent;
1529 : }
1530 : //}
1531 :
1532 1458 : child = nsBox::GetNextXULBox(child);
1533 : }
1534 :
1535 600 : nsMargin borderPadding;
1536 600 : aBox->GetXULBorderAndPadding(borderPadding);
1537 :
1538 600 : return vAscent + borderPadding.top;
1539 : }
1540 :
1541 : void
1542 1421 : nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal)
1543 : {
1544 1421 : if (aIsHorizontal)
1545 : {
1546 941 : if (aSize1.height < aSize2.height)
1547 397 : aSize1.height = aSize2.height;
1548 : } else {
1549 480 : if (aSize1.width < aSize2.width)
1550 188 : aSize1.width = aSize2.width;
1551 : }
1552 1421 : }
1553 :
1554 : void
1555 762 : nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal)
1556 : {
1557 762 : if (aIsHorizontal)
1558 : {
1559 482 : if (aSize1.height > aSize2.height)
1560 0 : aSize1.height = aSize2.height;
1561 : } else {
1562 280 : if (aSize1.width > aSize2.width)
1563 0 : aSize1.width = aSize2.width;
1564 :
1565 : }
1566 762 : }
1567 :
1568 : void
1569 1421 : nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal)
1570 : {
1571 1421 : if (aIsHorizontal)
1572 941 : AddCoord(aSize.width, aSizeToAdd.width);
1573 : else
1574 480 : AddCoord(aSize.height, aSizeToAdd.height);
1575 :
1576 1421 : SetLargestSize(aSize, aSizeToAdd, aIsHorizontal);
1577 1421 : }
1578 :
1579 : void
1580 2183 : nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd)
1581 : {
1582 2183 : if (aCoord != NS_INTRINSICSIZE)
1583 : {
1584 1421 : if (aCoordToAdd == NS_INTRINSICSIZE)
1585 0 : aCoord = aCoordToAdd;
1586 : else
1587 1421 : aCoord += aCoordToAdd;
1588 : }
1589 2183 : }
1590 : void
1591 762 : nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal)
1592 : {
1593 762 : if (aIsHorizontal)
1594 482 : AddCoord(aSize.width, aSizeToAdd.width);
1595 : else
1596 280 : AddCoord(aSize.height, aSizeToAdd.height);
1597 :
1598 762 : SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal);
1599 762 : }
1600 :
1601 : bool
1602 0 : nsSprocketLayout::GetDefaultFlex(int32_t& aFlex)
1603 : {
1604 0 : aFlex = 0;
1605 0 : return true;
1606 : }
1607 :
1608 1198 : nsComputedBoxSize::nsComputedBoxSize()
1609 : {
1610 1198 : resized = false;
1611 1198 : valid = false;
1612 1198 : size = 0;
1613 1198 : next = nullptr;
1614 1198 : }
1615 :
1616 1173 : nsBoxSize::nsBoxSize()
1617 : {
1618 1173 : pref = 0;
1619 1173 : min = 0;
1620 1173 : max = NS_INTRINSICSIZE;
1621 1173 : collapsed = false;
1622 1173 : left = 0;
1623 1173 : right = 0;
1624 1173 : flex = 0;
1625 1173 : next = nullptr;
1626 1173 : bogus = false;
1627 1173 : }
1628 :
1629 :
1630 : void*
1631 1173 : nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
1632 : {
1633 1173 : return mozilla::AutoStackArena::Allocate(sz);
1634 : }
1635 :
1636 :
1637 : void
1638 1173 : nsBoxSize::operator delete(void* aPtr, size_t sz)
1639 : {
1640 1173 : }
1641 :
1642 :
1643 : void*
1644 1198 : nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
1645 : {
1646 1198 : return mozilla::AutoStackArena::Allocate(sz);
1647 : }
1648 :
1649 : void
1650 1198 : nsComputedBoxSize::operator delete(void* aPtr, size_t sz)
1651 : {
1652 1198 : }
|