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 "nsStackLayout.h"
15 #include "nsCOMPtr.h"
16 #include "nsBoxLayoutState.h"
17 #include "nsBoxFrame.h"
18 #include "nsGkAtoms.h"
19 #include "nsIContent.h"
20 #include "nsNameSpaceManager.h"
21 
22 using namespace mozilla;
23 
24 nsBoxLayout* nsStackLayout::gInstance = nullptr;
25 
NS_NewStackLayout(nsCOMPtr<nsBoxLayout> & aNewLayout)26 nsresult NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout) {
27   if (!nsStackLayout::gInstance) {
28     nsStackLayout::gInstance = new nsStackLayout();
29     NS_IF_ADDREF(nsStackLayout::gInstance);
30   }
31   // we have not instance variables so just return our static one.
32   aNewLayout = nsStackLayout::gInstance;
33   return NS_OK;
34 }
35 
36 /*static*/
Shutdown()37 void nsStackLayout::Shutdown() { NS_IF_RELEASE(gInstance); }
38 
39 nsStackLayout::nsStackLayout() = default;
40 
41 /*
42  * Sizing: we are as wide as the widest child
43  * we are tall as the tallest child.
44  */
45 
GetXULPrefSize(nsIFrame * aBox,nsBoxLayoutState & aState)46 nsSize nsStackLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
47   nsSize prefSize(0, 0);
48 
49   nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
50   while (child) {
51     nsSize pref = child->GetXULPrefSize(aState);
52 
53     AddXULMargin(child, pref);
54 
55     if (pref.width > prefSize.width) {
56       prefSize.width = pref.width;
57     }
58     if (pref.height > prefSize.height) {
59       prefSize.height = pref.height;
60     }
61 
62     child = nsIFrame::GetNextXULBox(child);
63   }
64 
65   AddXULBorderAndPadding(aBox, prefSize);
66 
67   return prefSize;
68 }
69 
GetXULMinSize(nsIFrame * aBox,nsBoxLayoutState & aState)70 nsSize nsStackLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
71   nsSize minSize(0, 0);
72 
73   nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
74   while (child) {
75     nsSize min = child->GetXULMinSize(aState);
76 
77     AddXULMargin(child, min);
78 
79     if (min.width > minSize.width) {
80       minSize.width = min.width;
81     }
82     if (min.height > minSize.height) {
83       minSize.height = min.height;
84     }
85 
86     child = nsIFrame::GetNextXULBox(child);
87   }
88 
89   AddXULBorderAndPadding(aBox, minSize);
90 
91   return minSize;
92 }
93 
GetXULMaxSize(nsIFrame * aBox,nsBoxLayoutState & aState)94 nsSize nsStackLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
95   nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
96 
97   nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
98   while (child) {
99     nsSize min = child->GetXULMinSize(aState);
100     nsSize max = child->GetXULMaxSize(aState);
101 
102     max = nsIFrame::XULBoundsCheckMinMax(min, max);
103 
104     AddXULMargin(child, max);
105 
106     if (max.width < maxSize.width) {
107       maxSize.width = max.width;
108     }
109     if (max.height < maxSize.height) {
110       maxSize.height = max.height;
111     }
112 
113     child = nsIFrame::GetNextXULBox(child);
114   }
115 
116   AddXULBorderAndPadding(aBox, maxSize);
117 
118   return maxSize;
119 }
120 
GetAscent(nsIFrame * aBox,nsBoxLayoutState & aState)121 nscoord nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) {
122   nscoord vAscent = 0;
123 
124   nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
125   while (child) {
126     nscoord ascent = child->GetXULBoxAscent(aState);
127     nsMargin margin;
128     child->GetXULMargin(margin);
129     ascent += margin.top;
130     if (ascent > vAscent) vAscent = ascent;
131 
132     child = nsIFrame::GetNextXULBox(child);
133   }
134 
135   return vAscent;
136 }
137 
138 NS_IMETHODIMP
XULLayout(nsIFrame * aBox,nsBoxLayoutState & aState)139 nsStackLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState) {
140   nsRect clientRect;
141   aBox->GetXULClientRect(clientRect);
142 
143   bool grow;
144 
145   do {
146     nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
147     grow = false;
148 
149     while (child) {
150       nsMargin margin;
151       child->GetXULMargin(margin);
152       nsRect childRect(clientRect);
153       childRect.Deflate(margin);
154 
155       if (childRect.width < 0) childRect.width = 0;
156 
157       if (childRect.height < 0) childRect.height = 0;
158 
159       nsRect oldRect(child->GetRect());
160       bool sizeChanged = !oldRect.IsEqualEdges(childRect);
161 
162       // only lay out dirty children or children whose sizes have changed
163       if (sizeChanged || child->IsSubtreeDirty()) {
164         // add in the child's margin
165         nsMargin margin;
166         child->GetXULMargin(margin);
167 
168         // Now place the child.
169         child->SetXULBounds(aState, childRect);
170 
171         // Flow the child.
172         child->XULLayout(aState);
173 
174         // Get the child's new rect.
175         childRect = child->GetRect();
176         childRect.Inflate(margin);
177 
178         // Did the child push back on us and get bigger?
179         if (childRect.width > clientRect.width) {
180           clientRect.width = childRect.width;
181           grow = true;
182         }
183 
184         if (childRect.height > clientRect.height) {
185           clientRect.height = childRect.height;
186           grow = true;
187         }
188       }
189 
190       child = nsIFrame::GetNextXULBox(child);
191     }
192   } while (grow);
193 
194   // if some HTML inside us got bigger we need to force ourselves to
195   // get bigger
196   nsRect bounds(aBox->GetRect());
197   nsMargin bp;
198   aBox->GetXULBorderAndPadding(bp);
199   clientRect.Inflate(bp);
200 
201   if (clientRect.width > bounds.width || clientRect.height > bounds.height) {
202     if (clientRect.width > bounds.width) bounds.width = clientRect.width;
203     if (clientRect.height > bounds.height) bounds.height = clientRect.height;
204 
205     aBox->SetXULBounds(aState, bounds);
206   }
207 
208   return NS_OK;
209 }
210