Bumping manifests a=b2g-bump
[gecko.git] / layout / xul / nsStackLayout.cpp
blobb4f243cd3c6ae5b28a39325a147bfd1106b97cf3
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/. */
6 //
7 // Eric Vaughan
8 // Netscape Communications
9 //
10 // See documentation in associated header file
13 #include "nsStackLayout.h"
14 #include "nsCOMPtr.h"
15 #include "nsBoxLayoutState.h"
16 #include "nsBox.h"
17 #include "nsBoxFrame.h"
18 #include "nsGkAtoms.h"
19 #include "nsIContent.h"
20 #include "nsNameSpaceManager.h"
22 using namespace mozilla;
24 nsBoxLayout* nsStackLayout::gInstance = nullptr;
26 #define SPECIFIED_LEFT (1 << NS_SIDE_LEFT)
27 #define SPECIFIED_RIGHT (1 << NS_SIDE_RIGHT)
28 #define SPECIFIED_TOP (1 << NS_SIDE_TOP)
29 #define SPECIFIED_BOTTOM (1 << NS_SIDE_BOTTOM)
31 nsresult
32 NS_NewStackLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout)
34 if (!nsStackLayout::gInstance) {
35 nsStackLayout::gInstance = new nsStackLayout();
36 NS_IF_ADDREF(nsStackLayout::gInstance);
38 // we have not instance variables so just return our static one.
39 aNewLayout = nsStackLayout::gInstance;
40 return NS_OK;
43 /*static*/ void
44 nsStackLayout::Shutdown()
46 NS_IF_RELEASE(gInstance);
49 nsStackLayout::nsStackLayout()
54 * Sizing: we are as wide as the widest child plus its left offset
55 * we are tall as the tallest child plus its top offset.
57 * Only children which have -moz-stack-sizing set to stretch-to-fit
58 * (the default) will be included in the size computations.
61 nsSize
62 nsStackLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
64 nsSize prefSize (0, 0);
66 nsIFrame* child = nsBox::GetChildBox(aBox);
67 while (child) {
68 if (child->StyleXUL()->mStretchStack) {
69 nsSize pref = child->GetPrefSize(aState);
71 AddMargin(child, pref);
72 nsMargin offset;
73 GetOffset(aState, child, offset);
74 pref.width += offset.LeftRight();
75 pref.height += offset.TopBottom();
76 AddLargestSize(prefSize, pref);
79 child = nsBox::GetNextBox(child);
82 AddBorderAndPadding(aBox, prefSize);
84 return prefSize;
87 nsSize
88 nsStackLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
90 nsSize minSize (0, 0);
92 nsIFrame* child = nsBox::GetChildBox(aBox);
93 while (child) {
94 if (child->StyleXUL()->mStretchStack) {
95 nsSize min = child->GetMinSize(aState);
97 AddMargin(child, min);
98 nsMargin offset;
99 GetOffset(aState, child, offset);
100 min.width += offset.LeftRight();
101 min.height += offset.TopBottom();
102 AddLargestSize(minSize, min);
105 child = nsBox::GetNextBox(child);
108 AddBorderAndPadding(aBox, minSize);
110 return minSize;
113 nsSize
114 nsStackLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
116 nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
118 nsIFrame* child = nsBox::GetChildBox(aBox);
119 while (child) {
120 if (child->StyleXUL()->mStretchStack) {
121 nsSize min = child->GetMinSize(aState);
122 nsSize max = child->GetMaxSize(aState);
124 max = nsBox::BoundsCheckMinMax(min, max);
126 AddMargin(child, max);
127 nsMargin offset;
128 GetOffset(aState, child, offset);
129 max.width += offset.LeftRight();
130 max.height += offset.TopBottom();
131 AddSmallestSize(maxSize, max);
134 child = nsBox::GetNextBox(child);
137 AddBorderAndPadding(aBox, maxSize);
139 return maxSize;
143 nscoord
144 nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
146 nscoord vAscent = 0;
148 nsIFrame* child = nsBox::GetChildBox(aBox);
149 while (child) {
150 nscoord ascent = child->GetBoxAscent(aState);
151 nsMargin margin;
152 child->GetMargin(margin);
153 ascent += margin.top;
154 if (ascent > vAscent)
155 vAscent = ascent;
157 child = nsBox::GetNextBox(child);
160 return vAscent;
163 uint8_t
164 nsStackLayout::GetOffset(nsBoxLayoutState& aState, nsIFrame* aChild, nsMargin& aOffset)
166 aOffset = nsMargin(0, 0, 0, 0);
168 // get the left, right, top and bottom offsets
170 // As an optimization, we cache the fact that we are not positioned to avoid
171 // wasting time fetching attributes.
172 if (aChild->IsBoxFrame() &&
173 (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
174 return 0;
176 uint8_t offsetSpecified = 0;
177 nsIContent* content = aChild->GetContent();
178 if (content) {
179 bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
180 nsAutoString value;
181 nsresult error;
183 content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
184 if (!value.IsEmpty()) {
185 value.Trim("%");
186 if (ltr) {
187 aOffset.left =
188 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
189 offsetSpecified |= SPECIFIED_LEFT;
190 } else {
191 aOffset.right =
192 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
193 offsetSpecified |= SPECIFIED_RIGHT;
197 content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
198 if (!value.IsEmpty()) {
199 value.Trim("%");
200 if (ltr) {
201 aOffset.right =
202 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
203 offsetSpecified |= SPECIFIED_RIGHT;
204 } else {
205 aOffset.left =
206 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
207 offsetSpecified |= SPECIFIED_LEFT;
211 content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
212 if (!value.IsEmpty()) {
213 value.Trim("%");
214 aOffset.left =
215 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
216 offsetSpecified |= SPECIFIED_LEFT;
219 content->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
220 if (!value.IsEmpty()) {
221 value.Trim("%");
222 aOffset.right =
223 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
224 offsetSpecified |= SPECIFIED_RIGHT;
227 content->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
228 if (!value.IsEmpty()) {
229 value.Trim("%");
230 aOffset.top =
231 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
232 offsetSpecified |= SPECIFIED_TOP;
235 content->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
236 if (!value.IsEmpty()) {
237 value.Trim("%");
238 aOffset.bottom =
239 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
240 offsetSpecified |= SPECIFIED_BOTTOM;
244 if (!offsetSpecified && aChild->IsBoxFrame()) {
245 // If no offset was specified at all, then we cache this fact to avoid requerying
246 // CSS or the content model.
247 aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
250 return offsetSpecified;
254 NS_IMETHODIMP
255 nsStackLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState)
257 nsRect clientRect;
258 aBox->GetClientRect(clientRect);
260 bool grow;
262 do {
263 nsIFrame* child = nsBox::GetChildBox(aBox);
264 grow = false;
266 while (child)
268 nsMargin margin;
269 child->GetMargin(margin);
270 nsRect childRect(clientRect);
271 childRect.Deflate(margin);
273 if (childRect.width < 0)
274 childRect.width = 0;
276 if (childRect.height < 0)
277 childRect.height = 0;
279 nsRect oldRect(child->GetRect());
280 bool sizeChanged = !oldRect.IsEqualEdges(childRect);
282 // only lay out dirty children or children whose sizes have changed
283 if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
284 // add in the child's margin
285 nsMargin margin;
286 child->GetMargin(margin);
288 // obtain our offset from the top left border of the stack's content box.
289 nsMargin offset;
290 uint8_t offsetSpecified = GetOffset(aState, child, offset);
292 // Set the position and size based on which offsets have been specified:
293 // left only - offset from left edge, preferred width
294 // right only - offset from right edge, preferred width
295 // left and right - offset from left and right edges, width in between this
296 // neither - no offset, full width of stack
297 // Vertical direction is similar.
299 // Margins on the child are also included in the edge offsets
300 if (offsetSpecified) {
301 if (offsetSpecified & SPECIFIED_LEFT) {
302 childRect.x = clientRect.x + offset.left + margin.left;
303 if (offsetSpecified & SPECIFIED_RIGHT) {
304 nsSize min = child->GetMinSize(aState);
305 nsSize max = child->GetMaxSize(aState);
306 nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight();
307 childRect.width = clamped(width, min.width, max.width);
309 else {
310 childRect.width = child->GetPrefSize(aState).width;
313 else if (offsetSpecified & SPECIFIED_RIGHT) {
314 childRect.width = child->GetPrefSize(aState).width;
315 childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
318 if (offsetSpecified & SPECIFIED_TOP) {
319 childRect.y = clientRect.y + offset.top + margin.top;
320 if (offsetSpecified & SPECIFIED_BOTTOM) {
321 nsSize min = child->GetMinSize(aState);
322 nsSize max = child->GetMaxSize(aState);
323 nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom();
324 childRect.height = clamped(height, min.height, max.height);
326 else {
327 childRect.height = child->GetPrefSize(aState).height;
330 else if (offsetSpecified & SPECIFIED_BOTTOM) {
331 childRect.height = child->GetPrefSize(aState).height;
332 childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
336 // Now place the child.
337 child->SetBounds(aState, childRect);
339 // Flow the child.
340 child->Layout(aState);
342 // Get the child's new rect.
343 childRect = child->GetRect();
344 childRect.Inflate(margin);
346 if (child->StyleXUL()->mStretchStack) {
347 // Did the child push back on us and get bigger?
348 if (offset.LeftRight() + childRect.width > clientRect.width) {
349 clientRect.width = childRect.width + offset.LeftRight();
350 grow = true;
353 if (offset.TopBottom() + childRect.height > clientRect.height) {
354 clientRect.height = childRect.height + offset.TopBottom();
355 grow = true;
360 child = nsBox::GetNextBox(child);
362 } while (grow);
364 // if some HTML inside us got bigger we need to force ourselves to
365 // get bigger
366 nsRect bounds(aBox->GetRect());
367 nsMargin bp;
368 aBox->GetBorderAndPadding(bp);
369 clientRect.Inflate(bp);
371 if (clientRect.width > bounds.width || clientRect.height > bounds.height)
373 if (clientRect.width > bounds.width)
374 bounds.width = clientRect.width;
375 if (clientRect.height > bounds.height)
376 bounds.height = clientRect.height;
378 aBox->SetBounds(aState, bounds);
381 return NS_OK;