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 #include "mozilla/Attributes.h"
8 #include "mozilla/StaticPtr.h"
11 #include "nsBoxLayoutState.h"
12 #include "nsBoxFrame.h"
13 #include "nsDOMAttributeMap.h"
14 #include "nsPresContext.h"
16 #include "nsIContent.h"
17 #include "nsContainerFrame.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsGkAtoms.h"
21 #include "nsBoxLayout.h"
22 #include "nsLayoutUtils.h"
23 #include "FrameLayerBuilder.h"
24 #include "mozilla/dom/Attr.h"
25 #include "mozilla/dom/Element.h"
28 using namespace mozilla
;
30 nsresult
nsIFrame::BeginXULLayout(nsBoxLayoutState
& aState
) {
31 // mark ourselves as dirty so no child under us
32 // can post an incremental layout.
33 // XXXldb Is this still needed?
34 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
36 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
37 // If the parent is dirty, all the children are dirty (ReflowInput
40 for (box
= GetChildXULBox(this); box
; box
= GetNextXULBox(box
))
41 box
->MarkSubtreeDirty();
44 // Another copy-over from ReflowInput.
45 // Since we are in reflow, we don't need to store these properties anymore.
46 RemoveProperty(UsedBorderProperty());
47 RemoveProperty(UsedPaddingProperty());
48 RemoveProperty(UsedMarginProperty());
53 nsresult
nsIFrame::EndXULLayout(nsBoxLayoutState
& aState
) {
54 return SyncXULLayout(aState
);
57 nsresult
nsIFrame::GetXULClientRect(nsRect
& aClientRect
) {
59 aClientRect
.MoveTo(0, 0);
61 nsMargin borderPadding
;
62 GetXULBorderAndPadding(borderPadding
);
64 aClientRect
.Deflate(borderPadding
);
66 if (aClientRect
.width
< 0) aClientRect
.width
= 0;
68 if (aClientRect
.height
< 0) aClientRect
.height
= 0;
73 void nsIFrame::SetXULBounds(nsBoxLayoutState
& aState
, const nsRect
& aRect
,
74 bool aRemoveOverflowAreas
) {
77 ReflowChildFlags flags
= GetXULLayoutFlags() | aState
.LayoutFlags();
79 if ((flags
& ReflowChildFlags::NoMoveFrame
) ==
80 ReflowChildFlags::NoMoveFrame
) {
81 SetSize(aRect
.Size());
86 // Nuke the overflow area. The caller is responsible for restoring
88 if (aRemoveOverflowAreas
) {
89 // remove the previously stored overflow area
93 if (!(flags
& ReflowChildFlags::NoMoveView
)) {
94 nsContainerFrame::PositionFrameView(this);
95 if ((rect
.x
!= aRect
.x
) || (rect
.y
!= aRect
.y
))
96 nsContainerFrame::PositionChildViews(this);
100 nsresult
nsIFrame::GetXULBorderAndPadding(nsMargin
& aBorderAndPadding
) {
101 aBorderAndPadding
.SizeTo(0, 0, 0, 0);
102 nsresult rv
= GetXULBorder(aBorderAndPadding
);
103 if (NS_FAILED(rv
)) return rv
;
106 rv
= GetXULPadding(padding
);
107 if (NS_FAILED(rv
)) return rv
;
109 aBorderAndPadding
+= padding
;
114 nsresult
nsIFrame::GetXULBorder(nsMargin
& aBorder
) {
115 aBorder
.SizeTo(0, 0, 0, 0);
117 StyleAppearance appearance
= StyleDisplay()->EffectiveAppearance();
118 if (appearance
!= StyleAppearance::None
) {
119 // Go to the theme for the border.
120 nsPresContext
* pc
= PresContext();
121 nsITheme
* theme
= pc
->Theme();
122 if (theme
->ThemeSupportsWidget(pc
, this, appearance
)) {
123 LayoutDeviceIntMargin margin
=
124 theme
->GetWidgetBorder(pc
->DeviceContext(), this, appearance
);
126 LayoutDevicePixel::ToAppUnits(margin
, pc
->AppUnitsPerDevPixel());
131 aBorder
= StyleBorder()->GetComputedBorder();
136 nsresult
nsIFrame::GetXULPadding(nsMargin
& aBorderAndPadding
) {
137 StyleAppearance appearance
= StyleDisplay()->EffectiveAppearance();
138 if (appearance
!= StyleAppearance::None
) {
139 // Go to the theme for the padding.
140 nsPresContext
* pc
= PresContext();
141 nsITheme
* theme
= pc
->Theme();
142 if (theme
->ThemeSupportsWidget(pc
, this, appearance
)) {
143 LayoutDeviceIntMargin padding
;
144 bool useThemePadding
= theme
->GetWidgetPadding(pc
->DeviceContext(), this,
145 appearance
, &padding
);
146 if (useThemePadding
) {
148 LayoutDevicePixel::ToAppUnits(padding
, pc
->AppUnitsPerDevPixel());
154 aBorderAndPadding
.SizeTo(0, 0, 0, 0);
155 StylePadding()->GetPadding(aBorderAndPadding
);
160 nsresult
nsIFrame::GetXULMargin(nsMargin
& aMargin
) {
161 aMargin
.SizeTo(0, 0, 0, 0);
162 StyleMargin()->GetMargin(aMargin
);
167 void nsIFrame::XULSizeNeedsRecalc(nsSize
& aSize
) {
172 void nsIFrame::XULCoordNeedsRecalc(nscoord
& aCoord
) { aCoord
= -1; }
174 bool nsIFrame::XULNeedsRecalc(const nsSize
& aSize
) {
175 return (aSize
.width
== -1 || aSize
.height
== -1);
178 bool nsIFrame::XULNeedsRecalc(nscoord aCoord
) { return (aCoord
== -1); }
180 nsSize
nsIFrame::GetUncachedXULPrefSize(nsBoxLayoutState
& aBoxLayoutState
) {
181 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
182 "must have rendering context");
185 DISPLAY_PREF_SIZE(this, pref
);
187 if (IsXULCollapsed()) {
191 AddXULBorderAndPadding(pref
);
192 bool widthSet
, heightSet
;
193 nsIFrame::AddXULPrefSize(this, pref
, widthSet
, heightSet
);
195 nsSize minSize
= GetXULMinSize(aBoxLayoutState
);
196 nsSize maxSize
= GetXULMaxSize(aBoxLayoutState
);
197 return XULBoundsCheck(minSize
, pref
, maxSize
);
200 nsSize
nsIFrame::GetUncachedXULMinSize(nsBoxLayoutState
& aBoxLayoutState
) {
201 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
202 "must have rendering context");
205 DISPLAY_MIN_SIZE(this, min
);
207 if (IsXULCollapsed()) {
211 AddXULBorderAndPadding(min
);
212 bool widthSet
, heightSet
;
213 nsIFrame::AddXULMinSize(this, min
, widthSet
, heightSet
);
217 nsSize
nsIFrame::GetXULMinSizeForScrollArea(nsBoxLayoutState
& aBoxLayoutState
) {
221 nsSize
nsIFrame::GetUncachedXULMaxSize(nsBoxLayoutState
& aBoxLayoutState
) {
222 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
223 "must have rendering context");
225 nsSize
maxSize(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
226 DISPLAY_MAX_SIZE(this, maxSize
);
228 if (IsXULCollapsed()) {
232 AddXULBorderAndPadding(maxSize
);
233 bool widthSet
, heightSet
;
234 nsIFrame::AddXULMaxSize(this, maxSize
, widthSet
, heightSet
);
238 bool nsIFrame::IsXULCollapsed() {
239 return StyleVisibility()->mVisible
== StyleVisibility::Collapse
;
242 nsresult
nsIFrame::XULLayout(nsBoxLayoutState
& aState
) {
243 NS_ASSERTION(aState
.GetRenderingContext(), "must have rendering context");
245 nsIFrame
* box
= static_cast<nsIFrame
*>(this);
248 box
->BeginXULLayout(aState
);
250 box
->DoXULLayout(aState
);
252 box
->EndXULLayout(aState
);
257 bool nsIFrame::DoesClipChildrenInBothAxes() {
258 const nsStyleDisplay
* display
= StyleDisplay();
259 return display
->mOverflowX
== StyleOverflow::Clip
&&
260 display
->mOverflowY
== StyleOverflow::Clip
;
263 nsresult
nsIFrame::SyncXULLayout(nsBoxLayoutState
& aBoxLayoutState
) {
265 if (IsXULCollapsed()) {
266 CollapseChild(aBoxLayoutState, this, true);
271 if (HasAnyStateBits(NS_FRAME_IS_DIRTY
)) {
272 XULRedraw(aBoxLayoutState
);
275 RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
| NS_FRAME_IS_DIRTY
|
276 NS_FRAME_FIRST_REFLOW
| NS_FRAME_IN_REFLOW
);
278 nsPresContext
* presContext
= aBoxLayoutState
.PresContext();
280 ReflowChildFlags flags
= GetXULLayoutFlags() | aBoxLayoutState
.LayoutFlags();
284 if (XULComputesOwnOverflowArea()) {
285 inkOverflow
= InkOverflowRect();
287 nsRect
rect(nsPoint(0, 0), GetSize());
288 OverflowAreas
overflowAreas(rect
, rect
);
289 if (!DoesClipChildrenInBothAxes() && !IsXULCollapsed()) {
290 // See if our child frames caused us to overflow after being laid
291 // out. If so, store the overflow area. This normally can't happen
292 // in XUL, but it can happen with the CSS 'outline' property and
293 // possibly with other exotic stuff (e.g. relatively positioned
294 // frames in HTML inside XUL).
295 nsLayoutUtils::UnionChildOverflow(this, overflowAreas
);
298 FinishAndStoreOverflow(overflowAreas
, GetSize());
299 inkOverflow
= overflowAreas
.InkOverflow();
302 nsView
* view
= GetView();
304 // Make sure the frame's view is properly sized and positioned and has
305 // things like opacity correct
306 nsContainerFrame::SyncFrameViewAfterReflow(presContext
, this, view
,
313 nsresult
nsIFrame::XULRedraw(nsBoxLayoutState
& aState
) {
314 if (aState
.PaintingDisabled()) return NS_OK
;
316 // nsStackLayout, at least, expects us to repaint descendants even
317 // if a damage rect is provided
318 InvalidateFrameSubtree();
323 bool nsIFrame::AddXULPrefSize(nsIFrame
* aBox
, nsSize
& aSize
, bool& aWidthSet
,
328 // add in the css min, max, pref
329 const nsStylePosition
* position
= aBox
->StylePosition();
331 // see if the width or height was specifically set
332 // XXX Handle eStyleUnit_Enumerated?
333 // (Handling the eStyleUnit_Enumerated types requires
334 // GetXULPrefSize/GetXULMinSize methods that don't consider
335 // (min-/max-/)(width/height) properties.)
336 const auto& width
= position
->mWidth
;
337 if (width
.ConvertsToLength()) {
338 aSize
.width
= std::max(0, width
.ToLength());
342 const auto& height
= position
->mHeight
;
343 if (height
.ConvertsToLength()) {
344 aSize
.height
= std::max(0, height
.ToLength());
348 nsIContent
* content
= aBox
->GetContent();
349 // ignore 'height' and 'width' attributes if the actual element is not XUL
350 // For example, we might be magic XUL frames whose primary content is an HTML
352 if (content
&& content
->IsXULElement()) {
356 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::width
, value
);
357 if (!value
.IsEmpty()) {
360 aSize
.width
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
364 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::height
, value
);
365 if (!value
.IsEmpty()) {
369 nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
374 return (aWidthSet
&& aHeightSet
);
377 bool nsIFrame::AddXULMinSize(nsIFrame
* aBox
, nsSize
& aSize
, bool& aWidthSet
,
382 bool canOverride
= true;
384 nsPresContext
* pc
= aBox
->PresContext();
386 // See if a native theme wants to supply a minimum size.
387 const nsStyleDisplay
* display
= aBox
->StyleDisplay();
388 if (display
->HasAppearance()) {
389 nsITheme
* theme
= pc
->Theme();
390 StyleAppearance appearance
= display
->EffectiveAppearance();
391 if (theme
->ThemeSupportsWidget(pc
, aBox
, appearance
)) {
392 LayoutDeviceIntSize size
;
393 theme
->GetMinimumWidgetSize(pc
, aBox
, appearance
, &size
, &canOverride
);
395 aSize
.width
= pc
->DevPixelsToAppUnits(size
.width
);
399 aSize
.height
= pc
->DevPixelsToAppUnits(size
.height
);
403 switch (appearance
) {
404 case StyleAppearance::ScrollbarVertical
:
405 case StyleAppearance::ScrollbarHorizontal
: {
406 ComputedStyle
* style
= nsLayoutUtils::StyleForScrollbar(aBox
);
407 auto sizes
= theme
->GetScrollbarSizes(
408 pc
, style
->StyleUIReset()->mScrollbarWidth
,
409 nsITheme::Overlay::No
);
410 if (appearance
== StyleAppearance::ScrollbarVertical
) {
411 aSize
.width
= pc
->DevPixelsToAppUnits(sizes
.mVertical
);
414 aSize
.height
= pc
->DevPixelsToAppUnits(sizes
.mHorizontal
);
425 // add in the css min, max, pref
426 const nsStylePosition
* position
= aBox
->StylePosition();
427 const auto& minWidth
= position
->mMinWidth
;
428 if (minWidth
.ConvertsToLength()) {
429 nscoord min
= minWidth
.ToLength();
430 if (!aWidthSet
|| (min
> aSize
.width
&& canOverride
)) {
434 } else if (minWidth
.ConvertsToPercentage()) {
435 NS_ASSERTION(minWidth
.ToPercentage() == 0.0f
,
436 "Non-zero percentage values not currently supported");
438 aWidthSet
= true; // FIXME: should we really do this for
441 // XXX Handle ExtremumLength?
442 // (Handling them requires GetXULPrefSize/GetXULMinSize methods that don't
443 // consider (min-/max-/)(width/height) properties.
444 // calc() with percentage is treated like '0' (unset)
446 const auto& minHeight
= position
->mMinHeight
;
447 if (minHeight
.ConvertsToLength()) {
448 nscoord min
= minHeight
.ToLength();
449 if (!aHeightSet
|| (min
> aSize
.height
&& canOverride
)) {
453 } else if (minHeight
.ConvertsToPercentage()) {
454 NS_ASSERTION(position
->mMinHeight
.ToPercentage() == 0.0f
,
455 "Non-zero percentage values not currently supported");
457 aHeightSet
= true; // FIXME: should we really do this for
460 // calc() with percentage is treated like '0' (unset)
462 nsIContent
* content
= aBox
->GetContent();
463 if (content
&& content
->IsXULElement()) {
467 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::minwidth
,
469 if (!value
.IsEmpty()) {
472 nscoord val
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
473 if (val
> aSize
.width
) aSize
.width
= val
;
477 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::minheight
,
479 if (!value
.IsEmpty()) {
482 nscoord val
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
483 if (val
> aSize
.height
) aSize
.height
= val
;
489 return (aWidthSet
&& aHeightSet
);
492 bool nsIFrame::AddXULMaxSize(nsIFrame
* aBox
, nsSize
& aSize
, bool& aWidthSet
,
497 // add in the css min, max, pref
498 const nsStylePosition
* position
= aBox
->StylePosition();
501 // see if the width or height was specifically set
502 // XXX Handle eStyleUnit_Enumerated?
503 // (Handling the eStyleUnit_Enumerated types requires
504 // GetXULPrefSize/GetXULMinSize methods that don't consider
505 // (min-/max-/)(width/height) properties.)
506 const auto& maxWidth
= position
->mMaxWidth
;
507 if (maxWidth
.ConvertsToLength()) {
508 aSize
.width
= maxWidth
.ToLength();
511 // percentages and calc() with percentages are treated like 'none'
513 const auto& maxHeight
= position
->mMaxHeight
;
514 if (maxHeight
.ConvertsToLength()) {
515 aSize
.height
= maxHeight
.ToLength();
518 // percentages and calc() with percentages are treated like 'none'
520 nsIContent
* content
= aBox
->GetContent();
521 if (content
&& content
->IsXULElement()) {
525 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::maxwidth
,
527 if (!value
.IsEmpty()) {
530 nscoord val
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
535 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::maxheight
,
537 if (!value
.IsEmpty()) {
540 nscoord val
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
547 return (aWidthSet
|| aHeightSet
);
550 bool nsIFrame::AddXULFlex(nsIFrame
* aBox
, nscoord
& aFlex
) {
551 bool flexSet
= false;
553 // get the flexibility
554 aFlex
= aBox
->StyleXUL()->mBoxFlex
;
556 // attribute value overrides CSS
557 nsIContent
* content
= aBox
->GetContent();
558 if (content
&& content
->IsXULElement()) {
562 content
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::flex
, value
);
563 if (!value
.IsEmpty()) {
565 aFlex
= value
.ToInteger(&error
);
570 if (aFlex
< 0) aFlex
= 0;
571 if (aFlex
>= nscoord_MAX
) aFlex
= nscoord_MAX
- 1;
573 return flexSet
|| aFlex
> 0;
576 void nsIFrame::AddXULBorderAndPadding(nsSize
& aSize
) {
577 AddXULBorderAndPadding(this, aSize
);
580 void nsIFrame::AddXULBorderAndPadding(nsIFrame
* aBox
, nsSize
& aSize
) {
581 nsMargin
borderPadding(0, 0, 0, 0);
582 aBox
->GetXULBorderAndPadding(borderPadding
);
583 AddXULMargin(aSize
, borderPadding
);
586 void nsIFrame::AddXULMargin(nsIFrame
* aChild
, nsSize
& aSize
) {
587 nsMargin
margin(0, 0, 0, 0);
588 aChild
->GetXULMargin(margin
);
589 AddXULMargin(aSize
, margin
);
592 void nsIFrame::AddXULMargin(nsSize
& aSize
, const nsMargin
& aMargin
) {
593 if (aSize
.width
!= NS_UNCONSTRAINEDSIZE
)
594 aSize
.width
+= aMargin
.left
+ aMargin
.right
;
596 if (aSize
.height
!= NS_UNCONSTRAINEDSIZE
)
597 aSize
.height
+= aMargin
.top
+ aMargin
.bottom
;
600 nscoord
nsIFrame::XULBoundsCheck(nscoord aMin
, nscoord aPref
, nscoord aMax
) {
601 if (aPref
> aMax
) aPref
= aMax
;
603 if (aPref
< aMin
) aPref
= aMin
;
608 nsSize
nsIFrame::XULBoundsCheckMinMax(const nsSize
& aMinSize
,
609 const nsSize
& aMaxSize
) {
610 return nsSize(std::max(aMaxSize
.width
, aMinSize
.width
),
611 std::max(aMaxSize
.height
, aMinSize
.height
));
614 nsSize
nsIFrame::XULBoundsCheck(const nsSize
& aMinSize
, const nsSize
& aPrefSize
,
615 const nsSize
& aMaxSize
) {
617 XULBoundsCheck(aMinSize
.width
, aPrefSize
.width
, aMaxSize
.width
),
618 XULBoundsCheck(aMinSize
.height
, aPrefSize
.height
, aMaxSize
.height
));
622 nsIFrame
* nsIFrame::GetChildXULBox(const nsIFrame
* aFrame
) {
623 // box layout ends at box-wrapped frames, so don't allow these frames
624 // to report child boxes.
625 return aFrame
->IsXULBoxFrame() ? aFrame
->PrincipalChildList().FirstChild()
630 nsIFrame
* nsIFrame::GetNextXULBox(const nsIFrame
* aFrame
) {
631 return aFrame
->GetParent() && aFrame
->GetParent()->IsXULBoxFrame()
632 ? aFrame
->GetNextSibling()
637 nsIFrame
* nsIFrame::GetParentXULBox(const nsIFrame
* aFrame
) {
638 return aFrame
->GetParent() && aFrame
->GetParent()->IsXULBoxFrame()
639 ? aFrame
->GetParent()