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