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