/src/mozilla-central/layout/xul/nsBox.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "nsBoxLayoutState.h" |
8 | | #include "nsBox.h" |
9 | | #include "nsBoxFrame.h" |
10 | | #include "nsDOMAttributeMap.h" |
11 | | #include "nsPresContext.h" |
12 | | #include "nsCOMPtr.h" |
13 | | #include "nsIContent.h" |
14 | | #include "nsContainerFrame.h" |
15 | | #include "nsNameSpaceManager.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "nsITheme.h" |
18 | | #include "nsIServiceManager.h" |
19 | | #include "nsBoxLayout.h" |
20 | | #include "FrameLayerBuilder.h" |
21 | | #include "mozilla/dom/Attr.h" |
22 | | #include "mozilla/dom/Element.h" |
23 | | #include <algorithm> |
24 | | |
25 | | using namespace mozilla; |
26 | | |
27 | | nsresult |
28 | | nsBox::BeginXULLayout(nsBoxLayoutState& aState) |
29 | 0 | { |
30 | 0 | // mark ourselves as dirty so no child under us |
31 | 0 | // can post an incremental layout. |
32 | 0 | // XXXldb Is this still needed? |
33 | 0 | AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
34 | 0 |
|
35 | 0 | if (GetStateBits() & NS_FRAME_IS_DIRTY) |
36 | 0 | { |
37 | 0 | // If the parent is dirty, all the children are dirty (ReflowInput |
38 | 0 | // does this too). |
39 | 0 | nsIFrame* box; |
40 | 0 | for (box = GetChildXULBox(this); box; box = GetNextXULBox(box)) |
41 | 0 | box->AddStateBits(NS_FRAME_IS_DIRTY); |
42 | 0 | } |
43 | 0 |
|
44 | 0 | // Another copy-over from ReflowInput. |
45 | 0 | // Since we are in reflow, we don't need to store these properties anymore. |
46 | 0 | DeleteProperty(UsedBorderProperty()); |
47 | 0 | DeleteProperty(UsedPaddingProperty()); |
48 | 0 | DeleteProperty(UsedMarginProperty()); |
49 | 0 |
|
50 | 0 | return NS_OK; |
51 | 0 | } |
52 | | |
53 | | NS_IMETHODIMP |
54 | | nsBox::DoXULLayout(nsBoxLayoutState& aState) |
55 | 0 | { |
56 | 0 | return NS_OK; |
57 | 0 | } |
58 | | |
59 | | nsresult |
60 | | nsBox::EndXULLayout(nsBoxLayoutState& aState) |
61 | 0 | { |
62 | 0 | return SyncLayout(aState); |
63 | 0 | } |
64 | | |
65 | | bool nsBox::gGotTheme = false; |
66 | | StaticRefPtr<nsITheme> nsBox::gTheme; |
67 | | |
68 | | nsBox::nsBox(ClassID aID) |
69 | | : nsIFrame(aID) |
70 | 0 | { |
71 | 0 | MOZ_COUNT_CTOR(nsBox); |
72 | 0 | if (!gGotTheme) { |
73 | 0 | gTheme = do_GetNativeTheme(); |
74 | 0 | if (gTheme) { |
75 | 0 | gGotTheme = true; |
76 | 0 | } |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | nsBox::~nsBox() |
81 | 0 | { |
82 | 0 | // NOTE: This currently doesn't get called for |nsBoxToBlockAdaptor| |
83 | 0 | // objects, so don't rely on putting anything here. |
84 | 0 | MOZ_COUNT_DTOR(nsBox); |
85 | 0 | } |
86 | | |
87 | | /* static */ void |
88 | | nsBox::Shutdown() |
89 | 0 | { |
90 | 0 | gGotTheme = false; |
91 | 0 | gTheme = nullptr; |
92 | 0 | } |
93 | | |
94 | | nsresult |
95 | | nsBox::XULRelayoutChildAtOrdinal(nsIFrame* aChild) |
96 | 0 | { |
97 | 0 | return NS_OK; |
98 | 0 | } |
99 | | |
100 | | nsresult |
101 | | nsIFrame::GetXULClientRect(nsRect& aClientRect) |
102 | 0 | { |
103 | 0 | aClientRect = mRect; |
104 | 0 | aClientRect.MoveTo(0,0); |
105 | 0 |
|
106 | 0 | nsMargin borderPadding; |
107 | 0 | GetXULBorderAndPadding(borderPadding); |
108 | 0 |
|
109 | 0 | aClientRect.Deflate(borderPadding); |
110 | 0 |
|
111 | 0 | if (aClientRect.width < 0) |
112 | 0 | aClientRect.width = 0; |
113 | 0 |
|
114 | 0 | if (aClientRect.height < 0) |
115 | 0 | aClientRect.height = 0; |
116 | 0 |
|
117 | 0 | return NS_OK; |
118 | 0 | } |
119 | | |
120 | | void |
121 | | nsBox::SetXULBounds(nsBoxLayoutState& aState, const nsRect& aRect, bool aRemoveOverflowAreas) |
122 | 0 | { |
123 | 0 | nsRect rect(mRect); |
124 | 0 |
|
125 | 0 | uint32_t flags = GetXULLayoutFlags(); |
126 | 0 |
|
127 | 0 | uint32_t stateFlags = aState.LayoutFlags(); |
128 | 0 |
|
129 | 0 | flags |= stateFlags; |
130 | 0 |
|
131 | 0 | if ((flags & NS_FRAME_NO_MOVE_FRAME) == NS_FRAME_NO_MOVE_FRAME) |
132 | 0 | SetSize(aRect.Size()); |
133 | 0 | else |
134 | 0 | SetRect(aRect); |
135 | 0 |
|
136 | 0 | // Nuke the overflow area. The caller is responsible for restoring |
137 | 0 | // it if necessary. |
138 | 0 | if (aRemoveOverflowAreas) { |
139 | 0 | // remove the previously stored overflow area |
140 | 0 | ClearOverflowRects(); |
141 | 0 | } |
142 | 0 |
|
143 | 0 | if (!(flags & NS_FRAME_NO_MOVE_VIEW)) |
144 | 0 | { |
145 | 0 | nsContainerFrame::PositionFrameView(this); |
146 | 0 | if ((rect.x != aRect.x) || (rect.y != aRect.y)) |
147 | 0 | nsContainerFrame::PositionChildViews(this); |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | nsresult |
152 | | nsIFrame::GetXULBorderAndPadding(nsMargin& aBorderAndPadding) |
153 | 0 | { |
154 | 0 | aBorderAndPadding.SizeTo(0, 0, 0, 0); |
155 | 0 | nsresult rv = GetXULBorder(aBorderAndPadding); |
156 | 0 | if (NS_FAILED(rv)) |
157 | 0 | return rv; |
158 | 0 | |
159 | 0 | nsMargin padding; |
160 | 0 | rv = GetXULPadding(padding); |
161 | 0 | if (NS_FAILED(rv)) |
162 | 0 | return rv; |
163 | 0 | |
164 | 0 | aBorderAndPadding += padding; |
165 | 0 |
|
166 | 0 | return rv; |
167 | 0 | } |
168 | | |
169 | | nsresult |
170 | | nsBox::GetXULBorder(nsMargin& aMargin) |
171 | 0 | { |
172 | 0 | aMargin.SizeTo(0,0,0,0); |
173 | 0 |
|
174 | 0 | const nsStyleDisplay* disp = StyleDisplay(); |
175 | 0 | if (disp->HasAppearance() && gTheme) { |
176 | 0 | // Go to the theme for the border. |
177 | 0 | nsPresContext *context = PresContext(); |
178 | 0 | if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { |
179 | 0 | LayoutDeviceIntMargin margin = |
180 | 0 | gTheme->GetWidgetBorder(context->DeviceContext(), this, |
181 | 0 | disp->mAppearance); |
182 | 0 | aMargin = LayoutDevicePixel::ToAppUnits(margin, |
183 | 0 | context->AppUnitsPerDevPixel()); |
184 | 0 | return NS_OK; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | |
188 | 0 | aMargin = StyleBorder()->GetComputedBorder(); |
189 | 0 |
|
190 | 0 | return NS_OK; |
191 | 0 | } |
192 | | |
193 | | nsresult |
194 | | nsBox::GetXULPadding(nsMargin& aPadding) |
195 | 0 | { |
196 | 0 | const nsStyleDisplay *disp = StyleDisplay(); |
197 | 0 | if (disp->HasAppearance() && gTheme) { |
198 | 0 | // Go to the theme for the padding. |
199 | 0 | nsPresContext *context = PresContext(); |
200 | 0 | if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { |
201 | 0 | LayoutDeviceIntMargin padding; |
202 | 0 | bool useThemePadding = |
203 | 0 | gTheme->GetWidgetPadding(context->DeviceContext(), |
204 | 0 | this, disp->mAppearance, &padding); |
205 | 0 | if (useThemePadding) { |
206 | 0 | aPadding = LayoutDevicePixel::ToAppUnits(padding, |
207 | 0 | context->AppUnitsPerDevPixel()); |
208 | 0 | return NS_OK; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | } |
212 | 0 | |
213 | 0 | aPadding.SizeTo(0,0,0,0); |
214 | 0 | StylePadding()->GetPadding(aPadding); |
215 | 0 |
|
216 | 0 | return NS_OK; |
217 | 0 | } |
218 | | |
219 | | nsresult |
220 | | nsBox::GetXULMargin(nsMargin& aMargin) |
221 | 0 | { |
222 | 0 | aMargin.SizeTo(0,0,0,0); |
223 | 0 | StyleMargin()->GetMargin(aMargin); |
224 | 0 |
|
225 | 0 | return NS_OK; |
226 | 0 | } |
227 | | |
228 | | void |
229 | | nsBox::SizeNeedsRecalc(nsSize& aSize) |
230 | 0 | { |
231 | 0 | aSize.width = -1; |
232 | 0 | aSize.height = -1; |
233 | 0 | } |
234 | | |
235 | | void |
236 | | nsBox::CoordNeedsRecalc(nscoord& aFlex) |
237 | 0 | { |
238 | 0 | aFlex = -1; |
239 | 0 | } |
240 | | |
241 | | bool |
242 | | nsBox::DoesNeedRecalc(const nsSize& aSize) |
243 | 0 | { |
244 | 0 | return (aSize.width == -1 || aSize.height == -1); |
245 | 0 | } |
246 | | |
247 | | bool |
248 | | nsBox::DoesNeedRecalc(nscoord aCoord) |
249 | 0 | { |
250 | 0 | return (aCoord == -1); |
251 | 0 | } |
252 | | |
253 | | nsSize |
254 | | nsBox::GetXULPrefSize(nsBoxLayoutState& aState) |
255 | 0 | { |
256 | 0 | NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); |
257 | 0 |
|
258 | 0 | nsSize pref(0,0); |
259 | 0 | DISPLAY_PREF_SIZE(this, pref); |
260 | 0 |
|
261 | 0 | if (IsXULCollapsed()) |
262 | 0 | return pref; |
263 | 0 | |
264 | 0 | AddBorderAndPadding(pref); |
265 | 0 | bool widthSet, heightSet; |
266 | 0 | nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet); |
267 | 0 |
|
268 | 0 | nsSize minSize = GetXULMinSize(aState); |
269 | 0 | nsSize maxSize = GetXULMaxSize(aState); |
270 | 0 | return BoundsCheck(minSize, pref, maxSize); |
271 | 0 | } |
272 | | |
273 | | nsSize |
274 | | nsBox::GetXULMinSize(nsBoxLayoutState& aState) |
275 | 0 | { |
276 | 0 | NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); |
277 | 0 |
|
278 | 0 | nsSize min(0,0); |
279 | 0 | DISPLAY_MIN_SIZE(this, min); |
280 | 0 |
|
281 | 0 | if (IsXULCollapsed()) |
282 | 0 | return min; |
283 | 0 | |
284 | 0 | AddBorderAndPadding(min); |
285 | 0 | bool widthSet, heightSet; |
286 | 0 | nsIFrame::AddXULMinSize(aState, this, min, widthSet, heightSet); |
287 | 0 | return min; |
288 | 0 | } |
289 | | |
290 | | nsSize |
291 | | nsBox::GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) |
292 | 0 | { |
293 | 0 | return nsSize(0, 0); |
294 | 0 | } |
295 | | |
296 | | nsSize |
297 | | nsBox::GetXULMaxSize(nsBoxLayoutState& aState) |
298 | 0 | { |
299 | 0 | NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); |
300 | 0 |
|
301 | 0 | nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
302 | 0 | DISPLAY_MAX_SIZE(this, maxSize); |
303 | 0 |
|
304 | 0 | if (IsXULCollapsed()) |
305 | 0 | return maxSize; |
306 | 0 | |
307 | 0 | AddBorderAndPadding(maxSize); |
308 | 0 | bool widthSet, heightSet; |
309 | 0 | nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet); |
310 | 0 | return maxSize; |
311 | 0 | } |
312 | | |
313 | | nscoord |
314 | | nsBox::GetXULFlex() |
315 | 0 | { |
316 | 0 | nscoord flex = 0; |
317 | 0 |
|
318 | 0 | nsIFrame::AddXULFlex(this, flex); |
319 | 0 |
|
320 | 0 | return flex; |
321 | 0 | } |
322 | | |
323 | | uint32_t |
324 | | nsIFrame::GetXULOrdinal() |
325 | 0 | { |
326 | 0 | uint32_t ordinal = StyleXUL()->mBoxOrdinal; |
327 | 0 |
|
328 | 0 | // When present, attribute value overrides CSS. |
329 | 0 | nsIContent* content = GetContent(); |
330 | 0 | if (content && content->IsXULElement()) { |
331 | 0 | nsresult error; |
332 | 0 | nsAutoString value; |
333 | 0 |
|
334 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, value); |
335 | 0 | if (!value.IsEmpty()) { |
336 | 0 | ordinal = value.ToInteger(&error); |
337 | 0 | } |
338 | 0 | } |
339 | 0 |
|
340 | 0 | return ordinal; |
341 | 0 | } |
342 | | |
343 | | nscoord |
344 | | nsBox::GetXULBoxAscent(nsBoxLayoutState& aState) |
345 | 0 | { |
346 | 0 | if (IsXULCollapsed()) |
347 | 0 | return 0; |
348 | 0 | |
349 | 0 | return GetXULPrefSize(aState).height; |
350 | 0 | } |
351 | | |
352 | | bool |
353 | | nsBox::IsXULCollapsed() |
354 | 0 | { |
355 | 0 | return StyleVisibility()->mVisible == NS_STYLE_VISIBILITY_COLLAPSE; |
356 | 0 | } |
357 | | |
358 | | nsresult |
359 | | nsIFrame::XULLayout(nsBoxLayoutState& aState) |
360 | 0 | { |
361 | 0 | NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); |
362 | 0 |
|
363 | 0 | nsBox *box = static_cast<nsBox*>(this); |
364 | 0 | DISPLAY_LAYOUT(box); |
365 | 0 |
|
366 | 0 | box->BeginXULLayout(aState); |
367 | 0 |
|
368 | 0 | box->DoXULLayout(aState); |
369 | 0 |
|
370 | 0 | box->EndXULLayout(aState); |
371 | 0 |
|
372 | 0 | return NS_OK; |
373 | 0 | } |
374 | | |
375 | | bool |
376 | | nsBox::DoesClipChildren() |
377 | 0 | { |
378 | 0 | const nsStyleDisplay* display = StyleDisplay(); |
379 | 0 | NS_ASSERTION((display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == |
380 | 0 | (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP), |
381 | 0 | "If one overflow is clip, the other should be too"); |
382 | 0 | return display->mOverflowX == NS_STYLE_OVERFLOW_CLIP; |
383 | 0 | } |
384 | | |
385 | | nsresult |
386 | | nsBox::SyncLayout(nsBoxLayoutState& aState) |
387 | 0 | { |
388 | 0 | /* |
389 | 0 | if (IsXULCollapsed()) { |
390 | 0 | CollapseChild(aState, this, true); |
391 | 0 | return NS_OK; |
392 | 0 | } |
393 | 0 | */ |
394 | 0 |
|
395 | 0 |
|
396 | 0 | if (GetStateBits() & NS_FRAME_IS_DIRTY) |
397 | 0 | XULRedraw(aState); |
398 | 0 |
|
399 | 0 | RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY |
400 | 0 | | NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW); |
401 | 0 |
|
402 | 0 | nsPresContext* presContext = aState.PresContext(); |
403 | 0 |
|
404 | 0 | uint32_t flags = GetXULLayoutFlags(); |
405 | 0 |
|
406 | 0 | uint32_t stateFlags = aState.LayoutFlags(); |
407 | 0 |
|
408 | 0 | flags |= stateFlags; |
409 | 0 |
|
410 | 0 | nsRect visualOverflow; |
411 | 0 |
|
412 | 0 | if (ComputesOwnOverflowArea()) { |
413 | 0 | visualOverflow = GetVisualOverflowRect(); |
414 | 0 | } |
415 | 0 | else { |
416 | 0 | nsRect rect(nsPoint(0, 0), GetSize()); |
417 | 0 | nsOverflowAreas overflowAreas(rect, rect); |
418 | 0 | if (!DoesClipChildren() && !IsXULCollapsed()) { |
419 | 0 | // See if our child frames caused us to overflow after being laid |
420 | 0 | // out. If so, store the overflow area. This normally can't happen |
421 | 0 | // in XUL, but it can happen with the CSS 'outline' property and |
422 | 0 | // possibly with other exotic stuff (e.g. relatively positioned |
423 | 0 | // frames in HTML inside XUL). |
424 | 0 | nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
425 | 0 | } |
426 | 0 |
|
427 | 0 | FinishAndStoreOverflow(overflowAreas, GetSize()); |
428 | 0 | visualOverflow = overflowAreas.VisualOverflow(); |
429 | 0 | } |
430 | 0 |
|
431 | 0 | nsView* view = GetView(); |
432 | 0 | if (view) { |
433 | 0 | // Make sure the frame's view is properly sized and positioned and has |
434 | 0 | // things like opacity correct |
435 | 0 | nsContainerFrame::SyncFrameViewAfterReflow(presContext, this, view, |
436 | 0 | visualOverflow, flags); |
437 | 0 | } |
438 | 0 |
|
439 | 0 | return NS_OK; |
440 | 0 | } |
441 | | |
442 | | nsresult |
443 | | nsIFrame::XULRedraw(nsBoxLayoutState& aState) |
444 | 0 | { |
445 | 0 | if (aState.PaintingDisabled()) |
446 | 0 | return NS_OK; |
447 | 0 | |
448 | 0 | // nsStackLayout, at least, expects us to repaint descendants even |
449 | 0 | // if a damage rect is provided |
450 | 0 | InvalidateFrameSubtree(); |
451 | 0 |
|
452 | 0 | return NS_OK; |
453 | 0 | } |
454 | | |
455 | | bool |
456 | | nsIFrame::AddXULPrefSize(nsIFrame* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet) |
457 | 0 | { |
458 | 0 | aWidthSet = false; |
459 | 0 | aHeightSet = false; |
460 | 0 |
|
461 | 0 | // add in the css min, max, pref |
462 | 0 | const nsStylePosition* position = aBox->StylePosition(); |
463 | 0 |
|
464 | 0 | // see if the width or height was specifically set |
465 | 0 | // XXX Handle eStyleUnit_Enumerated? |
466 | 0 | // (Handling the eStyleUnit_Enumerated types requires |
467 | 0 | // GetXULPrefSize/GetXULMinSize methods that don't consider |
468 | 0 | // (min-/max-/)(width/height) properties.) |
469 | 0 | const nsStyleCoord &width = position->mWidth; |
470 | 0 | if (width.GetUnit() == eStyleUnit_Coord) { |
471 | 0 | aSize.width = width.GetCoordValue(); |
472 | 0 | aWidthSet = true; |
473 | 0 | } else if (width.IsCalcUnit()) { |
474 | 0 | if (!width.CalcHasPercent()) { |
475 | 0 | // pass 0 for percentage basis since we know there are no %s |
476 | 0 | aSize.width = width.ComputeComputedCalc(0); |
477 | 0 | if (aSize.width < 0) |
478 | 0 | aSize.width = 0; |
479 | 0 | aWidthSet = true; |
480 | 0 | } |
481 | 0 | } |
482 | 0 |
|
483 | 0 | const nsStyleCoord &height = position->mHeight; |
484 | 0 | if (height.GetUnit() == eStyleUnit_Coord) { |
485 | 0 | aSize.height = height.GetCoordValue(); |
486 | 0 | aHeightSet = true; |
487 | 0 | } else if (height.IsCalcUnit()) { |
488 | 0 | if (!height.CalcHasPercent()) { |
489 | 0 | // pass 0 for percentage basis since we know there are no %s |
490 | 0 | aSize.height = height.ComputeComputedCalc(0); |
491 | 0 | if (aSize.height < 0) |
492 | 0 | aSize.height = 0; |
493 | 0 | aHeightSet = true; |
494 | 0 | } |
495 | 0 | } |
496 | 0 |
|
497 | 0 | nsIContent* content = aBox->GetContent(); |
498 | 0 | // ignore 'height' and 'width' attributes if the actual element is not XUL |
499 | 0 | // For example, we might be magic XUL frames whose primary content is an HTML |
500 | 0 | // <select> |
501 | 0 | if (content && content->IsXULElement()) { |
502 | 0 | nsAutoString value; |
503 | 0 | nsresult error; |
504 | 0 |
|
505 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value); |
506 | 0 | if (!value.IsEmpty()) { |
507 | 0 | value.Trim("%"); |
508 | 0 |
|
509 | 0 | aSize.width = |
510 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
511 | 0 | aWidthSet = true; |
512 | 0 | } |
513 | 0 |
|
514 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value); |
515 | 0 | if (!value.IsEmpty()) { |
516 | 0 | value.Trim("%"); |
517 | 0 |
|
518 | 0 | aSize.height = |
519 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
520 | 0 | aHeightSet = true; |
521 | 0 | } |
522 | 0 | } |
523 | 0 |
|
524 | 0 | return (aWidthSet && aHeightSet); |
525 | 0 | } |
526 | | |
527 | | // This returns the scrollbar width we want to use when either native |
528 | | // theme is disabled, or the native theme claims that it doesn't support |
529 | | // scrollbar. |
530 | | static nscoord |
531 | | GetScrollbarWidthNoTheme(nsIFrame* aBox) |
532 | | { |
533 | | ComputedStyle* scrollbarStyle = nsLayoutUtils::StyleForScrollbar(aBox); |
534 | | switch (scrollbarStyle->StyleUIReset()->mScrollbarWidth) { |
535 | | default: |
536 | | case StyleScrollbarWidth::Auto: |
537 | | return 12 * AppUnitsPerCSSPixel(); |
538 | | case StyleScrollbarWidth::Thin: |
539 | | return 6 * AppUnitsPerCSSPixel(); |
540 | | case StyleScrollbarWidth::None: |
541 | | return 0; |
542 | | } |
543 | | } |
544 | | |
545 | | bool |
546 | | nsIFrame::AddXULMinSize(nsBoxLayoutState& aState, nsIFrame* aBox, nsSize& aSize, |
547 | | bool &aWidthSet, bool &aHeightSet) |
548 | 0 | { |
549 | 0 | aWidthSet = false; |
550 | 0 | aHeightSet = false; |
551 | 0 |
|
552 | 0 | bool canOverride = true; |
553 | 0 |
|
554 | 0 | // See if a native theme wants to supply a minimum size. |
555 | 0 | const nsStyleDisplay* display = aBox->StyleDisplay(); |
556 | 0 | if (display->HasAppearance()) { |
557 | 0 | nsITheme *theme = aState.PresContext()->GetTheme(); |
558 | 0 | if (theme && theme->ThemeSupportsWidget(aState.PresContext(), aBox, display->mAppearance)) { |
559 | 0 | LayoutDeviceIntSize size; |
560 | 0 | theme->GetMinimumWidgetSize(aState.PresContext(), aBox, |
561 | 0 | display->mAppearance, &size, &canOverride); |
562 | 0 | if (size.width) { |
563 | 0 | aSize.width = aState.PresContext()->DevPixelsToAppUnits(size.width); |
564 | 0 | aWidthSet = true; |
565 | 0 | } |
566 | 0 | if (size.height) { |
567 | 0 | aSize.height = aState.PresContext()->DevPixelsToAppUnits(size.height); |
568 | 0 | aHeightSet = true; |
569 | 0 | } |
570 | 0 | } else { |
571 | 0 | switch (display->mAppearance) { |
572 | 0 | case StyleAppearance::ScrollbarVertical: |
573 | 0 | aSize.width = GetScrollbarWidthNoTheme(aBox); |
574 | 0 | aWidthSet = true; |
575 | 0 | break; |
576 | 0 | case StyleAppearance::ScrollbarHorizontal: |
577 | 0 | aSize.height = GetScrollbarWidthNoTheme(aBox); |
578 | 0 | aHeightSet = true; |
579 | 0 | break; |
580 | 0 | default: |
581 | 0 | break; |
582 | 0 | } |
583 | 0 | } |
584 | 0 | } |
585 | 0 | |
586 | 0 | // add in the css min, max, pref |
587 | 0 | const nsStylePosition* position = aBox->StylePosition(); |
588 | 0 |
|
589 | 0 | // same for min size. Unfortunately min size is always set to 0. So for now |
590 | 0 | // we will assume 0 (as a coord) means not set. |
591 | 0 | const nsStyleCoord &minWidth = position->mMinWidth; |
592 | 0 | if ((minWidth.GetUnit() == eStyleUnit_Coord && |
593 | 0 | minWidth.GetCoordValue() != 0) || |
594 | 0 | (minWidth.IsCalcUnit() && !minWidth.CalcHasPercent())) { |
595 | 0 | nscoord min = minWidth.ComputeCoordPercentCalc(0); |
596 | 0 | if (!aWidthSet || (min > aSize.width && canOverride)) { |
597 | 0 | aSize.width = min; |
598 | 0 | aWidthSet = true; |
599 | 0 | } |
600 | 0 | } else if (minWidth.GetUnit() == eStyleUnit_Percent) { |
601 | 0 | NS_ASSERTION(minWidth.GetPercentValue() == 0.0f, |
602 | 0 | "Non-zero percentage values not currently supported"); |
603 | 0 | aSize.width = 0; |
604 | 0 | aWidthSet = true; // FIXME: should we really do this for |
605 | 0 | // nonzero values? |
606 | 0 | } |
607 | 0 | // XXX Handle eStyleUnit_Enumerated? |
608 | 0 | // (Handling the eStyleUnit_Enumerated types requires |
609 | 0 | // GetXULPrefSize/GetXULMinSize methods that don't consider |
610 | 0 | // (min-/max-/)(width/height) properties. |
611 | 0 | // calc() with percentage is treated like '0' (unset) |
612 | 0 |
|
613 | 0 | const nsStyleCoord &minHeight = position->mMinHeight; |
614 | 0 | if ((minHeight.GetUnit() == eStyleUnit_Coord && |
615 | 0 | minHeight.GetCoordValue() != 0) || |
616 | 0 | (minHeight.IsCalcUnit() && !minHeight.CalcHasPercent())) { |
617 | 0 | nscoord min = minHeight.ComputeCoordPercentCalc(0); |
618 | 0 | if (!aHeightSet || (min > aSize.height && canOverride)) { |
619 | 0 | aSize.height = min; |
620 | 0 | aHeightSet = true; |
621 | 0 | } |
622 | 0 | } else if (minHeight.GetUnit() == eStyleUnit_Percent) { |
623 | 0 | NS_ASSERTION(position->mMinHeight.GetPercentValue() == 0.0f, |
624 | 0 | "Non-zero percentage values not currently supported"); |
625 | 0 | aSize.height = 0; |
626 | 0 | aHeightSet = true; // FIXME: should we really do this for |
627 | 0 | // nonzero values? |
628 | 0 | } |
629 | 0 | // calc() with percentage is treated like '0' (unset) |
630 | 0 |
|
631 | 0 | nsIContent* content = aBox->GetContent(); |
632 | 0 | if (content && content->IsXULElement()) { |
633 | 0 | nsAutoString value; |
634 | 0 | nsresult error; |
635 | 0 |
|
636 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::minwidth, value); |
637 | 0 | if (!value.IsEmpty()) |
638 | 0 | { |
639 | 0 | value.Trim("%"); |
640 | 0 |
|
641 | 0 | nscoord val = |
642 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
643 | 0 | if (val > aSize.width) |
644 | 0 | aSize.width = val; |
645 | 0 | aWidthSet = true; |
646 | 0 | } |
647 | 0 |
|
648 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::minheight, value); |
649 | 0 | if (!value.IsEmpty()) |
650 | 0 | { |
651 | 0 | value.Trim("%"); |
652 | 0 |
|
653 | 0 | nscoord val = |
654 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
655 | 0 | if (val > aSize.height) |
656 | 0 | aSize.height = val; |
657 | 0 |
|
658 | 0 | aHeightSet = true; |
659 | 0 | } |
660 | 0 | } |
661 | 0 |
|
662 | 0 | return (aWidthSet && aHeightSet); |
663 | 0 | } |
664 | | |
665 | | bool |
666 | | nsIFrame::AddXULMaxSize(nsIFrame* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet) |
667 | 0 | { |
668 | 0 | aWidthSet = false; |
669 | 0 | aHeightSet = false; |
670 | 0 |
|
671 | 0 | // add in the css min, max, pref |
672 | 0 | const nsStylePosition* position = aBox->StylePosition(); |
673 | 0 |
|
674 | 0 | // and max |
675 | 0 | // see if the width or height was specifically set |
676 | 0 | // XXX Handle eStyleUnit_Enumerated? |
677 | 0 | // (Handling the eStyleUnit_Enumerated types requires |
678 | 0 | // GetXULPrefSize/GetXULMinSize methods that don't consider |
679 | 0 | // (min-/max-/)(width/height) properties.) |
680 | 0 | const nsStyleCoord maxWidth = position->mMaxWidth; |
681 | 0 | if (maxWidth.ConvertsToLength()) { |
682 | 0 | aSize.width = maxWidth.ComputeCoordPercentCalc(0); |
683 | 0 | aWidthSet = true; |
684 | 0 | } |
685 | 0 | // percentages and calc() with percentages are treated like 'none' |
686 | 0 |
|
687 | 0 | const nsStyleCoord &maxHeight = position->mMaxHeight; |
688 | 0 | if (maxHeight.ConvertsToLength()) { |
689 | 0 | aSize.height = maxHeight.ComputeCoordPercentCalc(0); |
690 | 0 | aHeightSet = true; |
691 | 0 | } |
692 | 0 | // percentages and calc() with percentages are treated like 'none' |
693 | 0 |
|
694 | 0 | nsIContent* content = aBox->GetContent(); |
695 | 0 | if (content && content->IsXULElement()) { |
696 | 0 | nsAutoString value; |
697 | 0 | nsresult error; |
698 | 0 |
|
699 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth, value); |
700 | 0 | if (!value.IsEmpty()) { |
701 | 0 | value.Trim("%"); |
702 | 0 |
|
703 | 0 | nscoord val = |
704 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
705 | 0 | aSize.width = val; |
706 | 0 | aWidthSet = true; |
707 | 0 | } |
708 | 0 |
|
709 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxheight, value); |
710 | 0 | if (!value.IsEmpty()) { |
711 | 0 | value.Trim("%"); |
712 | 0 |
|
713 | 0 | nscoord val = |
714 | 0 | nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); |
715 | 0 | aSize.height = val; |
716 | 0 |
|
717 | 0 | aHeightSet = true; |
718 | 0 | } |
719 | 0 | } |
720 | 0 |
|
721 | 0 | return (aWidthSet || aHeightSet); |
722 | 0 | } |
723 | | |
724 | | bool |
725 | | nsIFrame::AddXULFlex(nsIFrame* aBox, nscoord& aFlex) |
726 | 0 | { |
727 | 0 | bool flexSet = false; |
728 | 0 |
|
729 | 0 | // get the flexibility |
730 | 0 | aFlex = aBox->StyleXUL()->mBoxFlex; |
731 | 0 |
|
732 | 0 | // attribute value overrides CSS |
733 | 0 | nsIContent* content = aBox->GetContent(); |
734 | 0 | if (content && content->IsXULElement()) { |
735 | 0 | nsresult error; |
736 | 0 | nsAutoString value; |
737 | 0 |
|
738 | 0 | content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::flex, value); |
739 | 0 | if (!value.IsEmpty()) { |
740 | 0 | value.Trim("%"); |
741 | 0 | aFlex = value.ToInteger(&error); |
742 | 0 | flexSet = true; |
743 | 0 | } |
744 | 0 | } |
745 | 0 |
|
746 | 0 | if (aFlex < 0) |
747 | 0 | aFlex = 0; |
748 | 0 | if (aFlex >= nscoord_MAX) |
749 | 0 | aFlex = nscoord_MAX - 1; |
750 | 0 |
|
751 | 0 | return flexSet || aFlex > 0; |
752 | 0 | } |
753 | | |
754 | | void |
755 | | nsBox::AddBorderAndPadding(nsSize& aSize) |
756 | 0 | { |
757 | 0 | AddBorderAndPadding(this, aSize); |
758 | 0 | } |
759 | | |
760 | | void |
761 | | nsBox::AddBorderAndPadding(nsIFrame* aBox, nsSize& aSize) |
762 | 0 | { |
763 | 0 | nsMargin borderPadding(0,0,0,0); |
764 | 0 | aBox->GetXULBorderAndPadding(borderPadding); |
765 | 0 | AddMargin(aSize, borderPadding); |
766 | 0 | } |
767 | | |
768 | | void |
769 | | nsBox::AddMargin(nsIFrame* aChild, nsSize& aSize) |
770 | 0 | { |
771 | 0 | nsMargin margin(0,0,0,0); |
772 | 0 | aChild->GetXULMargin(margin); |
773 | 0 | AddMargin(aSize, margin); |
774 | 0 | } |
775 | | |
776 | | void |
777 | | nsBox::AddMargin(nsSize& aSize, const nsMargin& aMargin) |
778 | 0 | { |
779 | 0 | if (aSize.width != NS_INTRINSICSIZE) |
780 | 0 | aSize.width += aMargin.left + aMargin.right; |
781 | 0 |
|
782 | 0 | if (aSize.height != NS_INTRINSICSIZE) |
783 | 0 | aSize.height += aMargin.top + aMargin.bottom; |
784 | 0 | } |
785 | | |
786 | | nscoord |
787 | | nsBox::BoundsCheck(nscoord aMin, nscoord aPref, nscoord aMax) |
788 | 0 | { |
789 | 0 | if (aPref > aMax) |
790 | 0 | aPref = aMax; |
791 | 0 |
|
792 | 0 | if (aPref < aMin) |
793 | 0 | aPref = aMin; |
794 | 0 |
|
795 | 0 | return aPref; |
796 | 0 | } |
797 | | |
798 | | nsSize |
799 | | nsBox::BoundsCheckMinMax(const nsSize& aMinSize, const nsSize& aMaxSize) |
800 | 0 | { |
801 | 0 | return nsSize(std::max(aMaxSize.width, aMinSize.width), |
802 | 0 | std::max(aMaxSize.height, aMinSize.height)); |
803 | 0 | } |
804 | | |
805 | | nsSize |
806 | | nsBox::BoundsCheck(const nsSize& aMinSize, const nsSize& aPrefSize, const nsSize& aMaxSize) |
807 | 0 | { |
808 | 0 | return nsSize(BoundsCheck(aMinSize.width, aPrefSize.width, aMaxSize.width), |
809 | 0 | BoundsCheck(aMinSize.height, aPrefSize.height, aMaxSize.height)); |
810 | 0 | } |
811 | | |
812 | | /*static*/ nsIFrame* |
813 | | nsBox::GetChildXULBox(const nsIFrame* aFrame) |
814 | 0 | { |
815 | 0 | // box layout ends at box-wrapped frames, so don't allow these frames |
816 | 0 | // to report child boxes. |
817 | 0 | return aFrame->IsXULBoxFrame() ? aFrame->PrincipalChildList().FirstChild() : nullptr; |
818 | 0 | } |
819 | | |
820 | | /*static*/ nsIFrame* |
821 | | nsBox::GetNextXULBox(const nsIFrame* aFrame) |
822 | 0 | { |
823 | 0 | return aFrame->GetParent() && |
824 | 0 | aFrame->GetParent()->IsXULBoxFrame() ? aFrame->GetNextSibling() : nullptr; |
825 | 0 | } |
826 | | |
827 | | /*static*/ nsIFrame* |
828 | | nsBox::GetParentXULBox(const nsIFrame* aFrame) |
829 | 0 | { |
830 | 0 | return aFrame->GetParent() && |
831 | 0 | aFrame->GetParent()->IsXULBoxFrame() ? aFrame->GetParent() : nullptr; |
832 | 0 | } |
833 | | |