Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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
11 //
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)
33 {
34 if (!nsStackLayout::gInstance) {
35 nsStackLayout::gInstance = new nsStackLayout();
36 NS_IF_ADDREF(nsStackLayout::gInstance);
37 }
38 // we have not instance variables so just return our static one.
39 aNewLayout = nsStackLayout::gInstance;
40 return NS_OK;
41 }
43 /*static*/ void
44 nsStackLayout::Shutdown()
45 {
46 NS_IF_RELEASE(gInstance);
47 }
49 nsStackLayout::nsStackLayout()
50 {
51 }
53 /*
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.
56 *
57 * Only children which have -moz-stack-sizing set to stretch-to-fit
58 * (the default) will be included in the size computations.
59 */
61 nsSize
62 nsStackLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
63 {
64 nsSize prefSize (0, 0);
66 nsIFrame* child = aBox->GetChildBox();
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);
77 }
79 child = child->GetNextBox();
80 }
82 AddBorderAndPadding(aBox, prefSize);
84 return prefSize;
85 }
87 nsSize
88 nsStackLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
89 {
90 nsSize minSize (0, 0);
92 nsIFrame* child = aBox->GetChildBox();
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);
103 }
105 child = child->GetNextBox();
106 }
108 AddBorderAndPadding(aBox, minSize);
110 return minSize;
111 }
113 nsSize
114 nsStackLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
115 {
116 nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
118 nsIFrame* child = aBox->GetChildBox();
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);
132 }
134 child = child->GetNextBox();
135 }
137 AddBorderAndPadding(aBox, maxSize);
139 return maxSize;
140 }
143 nscoord
144 nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
145 {
146 nscoord vAscent = 0;
148 nsIFrame* child = aBox->GetChildBox();
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 = child->GetNextBox();
158 }
160 return vAscent;
161 }
163 uint8_t
164 nsStackLayout::GetOffset(nsBoxLayoutState& aState, nsIFrame* aChild, nsMargin& aOffset)
165 {
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;
194 }
195 }
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;
208 }
209 }
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;
217 }
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;
225 }
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;
233 }
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;
241 }
242 }
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);
248 }
250 return offsetSpecified;
251 }
254 NS_IMETHODIMP
255 nsStackLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState)
256 {
257 nsRect clientRect;
258 aBox->GetClientRect(clientRect);
260 bool grow;
262 do {
263 nsIFrame* child = aBox->GetChildBox();
264 grow = false;
266 while (child)
267 {
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.
298 //
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);
308 }
309 else {
310 childRect.width = child->GetPrefSize(aState).width;
311 }
312 }
313 else if (offsetSpecified & SPECIFIED_RIGHT) {
314 childRect.width = child->GetPrefSize(aState).width;
315 childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
316 }
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);
325 }
326 else {
327 childRect.height = child->GetPrefSize(aState).height;
328 }
329 }
330 else if (offsetSpecified & SPECIFIED_BOTTOM) {
331 childRect.height = child->GetPrefSize(aState).height;
332 childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
333 }
334 }
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;
351 }
353 if (offset.TopBottom() + childRect.height > clientRect.height) {
354 clientRect.height = childRect.height + offset.TopBottom();
355 grow = true;
356 }
357 }
358 }
360 child = child->GetNextBox();
361 }
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)
372 {
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);
379 }
381 return NS_OK;
382 }