Bug 1591736 - Fix AddonManagerWebAPI::IsAPIEnabled in out-of-process iframes r=mixedpuppy
[gecko.git] / layout / xul / nsStackLayout.cpp
blob4fe7aadc1698139339dc2f1ad0ec943151d97dbf
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/. */
7 //
8 // Eric Vaughan
9 // Netscape Communications
11 // See documentation in associated header file
14 #include "nsStackLayout.h"
15 #include "nsCOMPtr.h"
16 #include "nsBoxLayoutState.h"
17 #include "nsBox.h"
18 #include "nsBoxFrame.h"
19 #include "nsGkAtoms.h"
20 #include "nsIContent.h"
21 #include "nsNameSpaceManager.h"
23 using namespace mozilla;
25 nsBoxLayout* nsStackLayout::gInstance = nullptr;
27 #define SPECIFIED_LEFT (1 << eSideLeft)
28 #define SPECIFIED_RIGHT (1 << eSideRight)
29 #define SPECIFIED_TOP (1 << eSideTop)
30 #define SPECIFIED_BOTTOM (1 << eSideBottom)
32 nsresult NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout) {
33 if (!nsStackLayout::gInstance) {
34 nsStackLayout::gInstance = new nsStackLayout();
35 NS_IF_ADDREF(nsStackLayout::gInstance);
37 // we have not instance variables so just return our static one.
38 aNewLayout = nsStackLayout::gInstance;
39 return NS_OK;
42 /*static*/
43 void nsStackLayout::Shutdown() { NS_IF_RELEASE(gInstance); }
45 nsStackLayout::nsStackLayout() {}
48 * Sizing: we are as wide as the widest child plus its left offset
49 * we are tall as the tallest child plus its top offset.
51 * Only children which have -moz-stack-sizing set to stretch-to-fit
52 * (the default) will be included in the size computations.
55 nsSize nsStackLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
56 nsSize prefSize(0, 0);
58 nsIFrame* child = nsBox::GetChildXULBox(aBox);
59 while (child) {
60 auto stackSizing = child->StyleXUL()->mStackSizing;
61 if (stackSizing != StyleStackSizing::Ignore) {
62 nsSize pref = child->GetXULPrefSize(aState);
64 AddMargin(child, pref);
65 nsMargin offset;
66 GetOffset(child, offset);
67 pref.width += offset.LeftRight();
68 pref.height += offset.TopBottom();
70 if (pref.width > prefSize.width &&
71 stackSizing != StyleStackSizing::IgnoreHorizontal) {
72 prefSize.width = pref.width;
74 if (pref.height > prefSize.height &&
75 stackSizing != StyleStackSizing::IgnoreVertical) {
76 prefSize.height = pref.height;
80 child = nsBox::GetNextXULBox(child);
83 AddBorderAndPadding(aBox, prefSize);
85 return prefSize;
88 nsSize nsStackLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
89 nsSize minSize(0, 0);
91 nsIFrame* child = nsBox::GetChildXULBox(aBox);
92 while (child) {
93 auto stackSizing = child->StyleXUL()->mStackSizing;
94 if (stackSizing != StyleStackSizing::Ignore) {
95 nsSize min = child->GetXULMinSize(aState);
97 AddMargin(child, min);
98 nsMargin offset;
99 GetOffset(child, offset);
100 min.width += offset.LeftRight();
101 min.height += offset.TopBottom();
103 if (min.width > minSize.width &&
104 stackSizing != StyleStackSizing::IgnoreHorizontal) {
105 minSize.width = min.width;
107 if (min.height > minSize.height &&
108 stackSizing != StyleStackSizing::IgnoreVertical) {
109 minSize.height = min.height;
113 child = nsBox::GetNextXULBox(child);
116 AddBorderAndPadding(aBox, minSize);
118 return minSize;
121 nsSize nsStackLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
122 nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
124 nsIFrame* child = nsBox::GetChildXULBox(aBox);
125 while (child) {
126 auto stackSizing = child->StyleXUL()->mStackSizing;
127 if (stackSizing != StyleStackSizing::Ignore) {
128 nsSize min = child->GetXULMinSize(aState);
129 nsSize max = child->GetXULMaxSize(aState);
131 max = nsBox::BoundsCheckMinMax(min, max);
133 AddMargin(child, max);
134 nsMargin offset;
135 GetOffset(child, offset);
136 max.width += offset.LeftRight();
137 max.height += offset.TopBottom();
139 if (max.width < maxSize.width &&
140 stackSizing != StyleStackSizing::IgnoreHorizontal) {
141 maxSize.width = max.width;
143 if (max.height < maxSize.height &&
144 stackSizing != StyleStackSizing::IgnoreVertical) {
145 maxSize.height = max.height;
149 child = nsBox::GetNextXULBox(child);
152 AddBorderAndPadding(aBox, maxSize);
154 return maxSize;
157 nscoord nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) {
158 nscoord vAscent = 0;
160 nsIFrame* child = nsBox::GetChildXULBox(aBox);
161 while (child) {
162 nscoord ascent = child->GetXULBoxAscent(aState);
163 nsMargin margin;
164 child->GetXULMargin(margin);
165 ascent += margin.top;
166 if (ascent > vAscent) vAscent = ascent;
168 child = nsBox::GetNextXULBox(child);
171 return vAscent;
174 uint8_t nsStackLayout::GetOffset(nsIFrame* aChild, nsMargin& aOffset) {
175 aOffset = nsMargin(0, 0, 0, 0);
177 // get the left, right, top and bottom offsets
179 // As an optimization, we cache the fact that we are not positioned to avoid
180 // wasting time fetching attributes.
181 if (aChild->IsXULBoxFrame() &&
182 (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
183 return 0;
185 uint8_t offsetSpecified = 0;
186 nsIContent* content = aChild->GetContent();
187 if (content && content->IsElement()) {
188 bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
189 nsAutoString value;
190 nsresult error;
192 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
193 if (!value.IsEmpty()) {
194 value.Trim("%");
195 if (ltr) {
196 aOffset.left =
197 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
198 offsetSpecified |= SPECIFIED_LEFT;
199 } else {
200 aOffset.right =
201 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
202 offsetSpecified |= SPECIFIED_RIGHT;
206 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
207 if (!value.IsEmpty()) {
208 value.Trim("%");
209 if (ltr) {
210 aOffset.right =
211 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
212 offsetSpecified |= SPECIFIED_RIGHT;
213 } else {
214 aOffset.left =
215 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
216 offsetSpecified |= SPECIFIED_LEFT;
220 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
221 if (!value.IsEmpty()) {
222 value.Trim("%");
223 aOffset.left =
224 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
225 offsetSpecified |= SPECIFIED_LEFT;
228 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
229 if (!value.IsEmpty()) {
230 value.Trim("%");
231 aOffset.right =
232 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
233 offsetSpecified |= SPECIFIED_RIGHT;
236 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
237 if (!value.IsEmpty()) {
238 value.Trim("%");
239 aOffset.top = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
240 offsetSpecified |= SPECIFIED_TOP;
243 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
244 if (!value.IsEmpty()) {
245 value.Trim("%");
246 aOffset.bottom =
247 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
248 offsetSpecified |= SPECIFIED_BOTTOM;
252 if (!offsetSpecified && aChild->IsXULBoxFrame()) {
253 // If no offset was specified at all, then we cache this fact to avoid
254 // requerying CSS or the content model.
255 aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
258 return offsetSpecified;
261 NS_IMETHODIMP
262 nsStackLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState) {
263 nsRect clientRect;
264 aBox->GetXULClientRect(clientRect);
266 bool grow;
268 do {
269 nsIFrame* child = nsBox::GetChildXULBox(aBox);
270 grow = false;
272 while (child) {
273 nsMargin margin;
274 child->GetXULMargin(margin);
275 nsRect childRect(clientRect);
276 childRect.Deflate(margin);
278 if (childRect.width < 0) childRect.width = 0;
280 if (childRect.height < 0) childRect.height = 0;
282 nsRect oldRect(child->GetRect());
283 bool sizeChanged = !oldRect.IsEqualEdges(childRect);
285 // only lay out dirty children or children whose sizes have changed
286 if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
287 // add in the child's margin
288 nsMargin margin;
289 child->GetXULMargin(margin);
291 // obtain our offset from the top left border of the stack's content
292 // box.
293 nsMargin offset;
294 uint8_t offsetSpecified = GetOffset(child, offset);
296 // Set the position and size based on which offsets have been specified:
297 // left only - offset from left edge, preferred width
298 // right only - offset from right edge, preferred width
299 // left and right - offset from left and right edges, width in between
300 // this neither - no offset, full width of stack
301 // Vertical direction is similar.
303 // Margins on the child are also included in the edge offsets
304 if (offsetSpecified) {
305 nsSize min = child->GetXULMinSize(aState);
306 nsSize max = child->GetXULMaxSize(aState);
307 if (offsetSpecified & SPECIFIED_LEFT) {
308 childRect.x = clientRect.x + offset.left + margin.left;
309 if (offsetSpecified & SPECIFIED_RIGHT) {
310 nscoord width =
311 clientRect.width - offset.LeftRight() - margin.LeftRight();
312 childRect.width = clamped(width, min.width, max.width);
313 } else {
314 nscoord width = child->GetXULPrefSize(aState).width;
315 childRect.width = clamped(width, min.width, max.width);
317 } else if (offsetSpecified & SPECIFIED_RIGHT) {
318 nscoord width = child->GetXULPrefSize(aState).width;
319 childRect.width = clamped(width, min.width, max.width);
320 childRect.x = clientRect.XMost() - offset.right - margin.right -
321 childRect.width;
324 if (offsetSpecified & SPECIFIED_TOP) {
325 childRect.y = clientRect.y + offset.top + margin.top;
326 if (offsetSpecified & SPECIFIED_BOTTOM) {
327 nscoord height =
328 clientRect.height - offset.TopBottom() - margin.TopBottom();
329 childRect.height = clamped(height, min.height, max.height);
330 } else {
331 nscoord height = child->GetXULPrefSize(aState).height;
332 childRect.height = clamped(height, min.height, max.height);
334 } else if (offsetSpecified & SPECIFIED_BOTTOM) {
335 nscoord height = child->GetXULPrefSize(aState).height;
336 childRect.height = clamped(height, min.height, max.height);
337 childRect.y = clientRect.YMost() - offset.bottom - margin.bottom -
338 childRect.height;
342 // Now place the child.
343 child->SetXULBounds(aState, childRect);
345 // Flow the child.
346 child->XULLayout(aState);
348 // Get the child's new rect.
349 childRect = child->GetRect();
350 childRect.Inflate(margin);
352 auto stackSizing = child->StyleXUL()->mStackSizing;
353 if (stackSizing != StyleStackSizing::Ignore) {
354 // Did the child push back on us and get bigger?
355 if (offset.LeftRight() + childRect.width > clientRect.width &&
356 stackSizing != StyleStackSizing::IgnoreHorizontal) {
357 clientRect.width = childRect.width + offset.LeftRight();
358 grow = true;
361 if (offset.TopBottom() + childRect.height > clientRect.height &&
362 stackSizing != StyleStackSizing::IgnoreVertical) {
363 clientRect.height = childRect.height + offset.TopBottom();
364 grow = true;
369 child = nsBox::GetNextXULBox(child);
371 } while (grow);
373 // if some HTML inside us got bigger we need to force ourselves to
374 // get bigger
375 nsRect bounds(aBox->GetRect());
376 nsMargin bp;
377 aBox->GetXULBorderAndPadding(bp);
378 clientRect.Inflate(bp);
380 if (clientRect.width > bounds.width || clientRect.height > bounds.height) {
381 if (clientRect.width > bounds.width) bounds.width = clientRect.width;
382 if (clientRect.height > bounds.height) bounds.height = clientRect.height;
384 aBox->SetXULBounds(aState, bounds);
387 return NS_OK;