diff options
Diffstat (limited to 'layout/xul/nsBox.cpp')
-rw-r--r-- | layout/xul/nsBox.cpp | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/layout/xul/nsBox.cpp b/layout/xul/nsBox.cpp new file mode 100644 index 000000000..f7ec5fead --- /dev/null +++ b/layout/xul/nsBox.cpp @@ -0,0 +1,981 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsBoxLayoutState.h" +#include "nsBox.h" +#include "nsBoxFrame.h" +#include "nsPresContext.h" +#include "nsCOMPtr.h" +#include "nsIContent.h" +#include "nsContainerFrame.h" +#include "nsNameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsIDOMNode.h" +#include "nsIDOMMozNamedAttrMap.h" +#include "nsIDOMAttr.h" +#include "nsITheme.h" +#include "nsIServiceManager.h" +#include "nsBoxLayout.h" +#include "FrameLayerBuilder.h" +#include <algorithm> + +using namespace mozilla; + +#ifdef DEBUG_LAYOUT +int32_t gIndent = 0; +#endif + +#ifdef DEBUG_LAYOUT +void +nsBoxAddIndents() +{ + for(int32_t i=0; i < gIndent; i++) + { + printf(" "); + } +} +#endif + +#ifdef DEBUG_LAYOUT +void +nsBox::AppendAttribute(const nsAutoString& aAttribute, const nsAutoString& aValue, nsAutoString& aResult) +{ + aResult.Append(aAttribute); + aResult.AppendLiteral("='"); + aResult.Append(aValue); + aResult.AppendLiteral("' "); +} + +void +nsBox::ListBox(nsAutoString& aResult) +{ + nsAutoString name; + GetBoxName(name); + + char addr[100]; + sprintf(addr, "[@%p] ", static_cast<void*>(this)); + + aResult.AppendASCII(addr); + aResult.Append(name); + aResult.Append(' '); + + nsIContent* content = GetContent(); + + // add on all the set attributes + if (content) { + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content)); + nsCOMPtr<nsIDOMMozNamedAttrMap> namedMap; + + node->GetAttributes(getter_AddRefs(namedMap)); + uint32_t length; + namedMap->GetLength(&length); + + nsCOMPtr<nsIDOMAttr> attribute; + for (uint32_t i = 0; i < length; ++i) + { + namedMap->Item(i, getter_AddRefs(attribute)); + attribute->GetName(name); + nsAutoString value; + attribute->GetValue(value); + AppendAttribute(name, value, aResult); + } + } +} + +nsresult +nsBox::XULDumpBox(FILE* aFile) +{ + nsAutoString s; + ListBox(s); + fprintf(aFile, "%s", NS_LossyConvertUTF16toASCII(s).get()); + return NS_OK; +} + +void +nsBox::PropagateDebug(nsBoxLayoutState& aState) +{ + // propagate debug information + if (mState & NS_STATE_DEBUG_WAS_SET) { + if (mState & NS_STATE_SET_TO_DEBUG) + SetXULDebug(aState, true); + else + SetXULDebug(aState, false); + } else if (mState & NS_STATE_IS_ROOT) { + SetXULDebug(aState, gDebug); + } +} +#endif + +#ifdef DEBUG_LAYOUT +void +nsBox::GetBoxName(nsAutoString& aName) +{ + aName.AssignLiteral("Box"); +} +#endif + +nsresult +nsBox::BeginXULLayout(nsBoxLayoutState& aState) +{ +#ifdef DEBUG_LAYOUT + + nsBoxAddIndents(); + printf("XULLayout: "); + XULDumpBox(stdout); + printf("\n"); + gIndent++; +#endif + + // mark ourselves as dirty so no child under us + // can post an incremental layout. + // XXXldb Is this still needed? + mState |= NS_FRAME_HAS_DIRTY_CHILDREN; + + if (GetStateBits() & NS_FRAME_IS_DIRTY) + { + // If the parent is dirty, all the children are dirty (ReflowInput + // does this too). + nsIFrame* box; + for (box = GetChildXULBox(this); box; box = GetNextXULBox(box)) + box->AddStateBits(NS_FRAME_IS_DIRTY); + } + + // Another copy-over from ReflowInput. + // Since we are in reflow, we don't need to store these properties anymore. + FrameProperties props = Properties(); + props.Delete(UsedBorderProperty()); + props.Delete(UsedPaddingProperty()); + props.Delete(UsedMarginProperty()); + +#ifdef DEBUG_LAYOUT + PropagateDebug(aState); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +nsBox::DoXULLayout(nsBoxLayoutState& aState) +{ + return NS_OK; +} + +nsresult +nsBox::EndXULLayout(nsBoxLayoutState& aState) +{ + + #ifdef DEBUG_LAYOUT + --gIndent; + #endif + + return SyncLayout(aState); +} + +bool nsBox::gGotTheme = false; +nsITheme* nsBox::gTheme = nullptr; + +nsBox::nsBox() +{ + MOZ_COUNT_CTOR(nsBox); + //mX = 0; + //mY = 0; + if (!gGotTheme) { + gGotTheme = true; + CallGetService("@mozilla.org/chrome/chrome-native-theme;1", &gTheme); + } +} + +nsBox::~nsBox() +{ + // NOTE: This currently doesn't get called for |nsBoxToBlockAdaptor| + // objects, so don't rely on putting anything here. + MOZ_COUNT_DTOR(nsBox); +} + +/* static */ void +nsBox::Shutdown() +{ + gGotTheme = false; + NS_IF_RELEASE(gTheme); +} + +nsresult +nsBox::XULRelayoutChildAtOrdinal(nsIFrame* aChild) +{ + return NS_OK; +} + +nsresult +nsIFrame::GetXULClientRect(nsRect& aClientRect) +{ + aClientRect = mRect; + aClientRect.MoveTo(0,0); + + nsMargin borderPadding; + GetXULBorderAndPadding(borderPadding); + + aClientRect.Deflate(borderPadding); + + if (aClientRect.width < 0) + aClientRect.width = 0; + + if (aClientRect.height < 0) + aClientRect.height = 0; + + // NS_ASSERTION(aClientRect.width >=0 && aClientRect.height >= 0, "Content Size < 0"); + + return NS_OK; +} + +void +nsBox::SetXULBounds(nsBoxLayoutState& aState, const nsRect& aRect, bool aRemoveOverflowAreas) +{ + NS_BOX_ASSERTION(this, aRect.width >=0 && aRect.height >= 0, "SetXULBounds Size < 0"); + + nsRect rect(mRect); + + uint32_t flags = GetXULLayoutFlags(); + + uint32_t stateFlags = aState.LayoutFlags(); + + flags |= stateFlags; + + if ((flags & NS_FRAME_NO_MOVE_FRAME) == NS_FRAME_NO_MOVE_FRAME) + SetSize(aRect.Size()); + else + SetRect(aRect); + + // Nuke the overflow area. The caller is responsible for restoring + // it if necessary. + if (aRemoveOverflowAreas) { + // remove the previously stored overflow area + ClearOverflowRects(); + } + + if (!(flags & NS_FRAME_NO_MOVE_VIEW)) + { + nsContainerFrame::PositionFrameView(this); + if ((rect.x != aRect.x) || (rect.y != aRect.y)) + nsContainerFrame::PositionChildViews(this); + } + + + /* + // only if the origin changed + if ((rect.x != aRect.x) || (rect.y != aRect.y)) { + if (frame->HasView()) { + nsContainerFrame::PositionFrameView(presContext, frame, + frame->GetView()); + } else { + nsContainerFrame::PositionChildViews(presContext, frame); + } + } + */ +} + +nsresult +nsIFrame::GetXULBorderAndPadding(nsMargin& aBorderAndPadding) +{ + aBorderAndPadding.SizeTo(0, 0, 0, 0); + nsresult rv = GetXULBorder(aBorderAndPadding); + if (NS_FAILED(rv)) + return rv; + + nsMargin padding; + rv = GetXULPadding(padding); + if (NS_FAILED(rv)) + return rv; + + aBorderAndPadding += padding; + + return rv; +} + +nsresult +nsBox::GetXULBorder(nsMargin& aMargin) +{ + aMargin.SizeTo(0,0,0,0); + + const nsStyleDisplay* disp = StyleDisplay(); + if (disp->mAppearance && gTheme) { + // Go to the theme for the border. + nsPresContext *context = PresContext(); + if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { + nsIntMargin margin(0, 0, 0, 0); + gTheme->GetWidgetBorder(context->DeviceContext(), this, + disp->mAppearance, &margin); + aMargin.top = context->DevPixelsToAppUnits(margin.top); + aMargin.right = context->DevPixelsToAppUnits(margin.right); + aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom); + aMargin.left = context->DevPixelsToAppUnits(margin.left); + return NS_OK; + } + } + + aMargin = StyleBorder()->GetComputedBorder(); + + return NS_OK; +} + +nsresult +nsBox::GetXULPadding(nsMargin& aMargin) +{ + const nsStyleDisplay *disp = StyleDisplay(); + if (disp->mAppearance && gTheme) { + // Go to the theme for the padding. + nsPresContext *context = PresContext(); + if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { + nsIntMargin margin(0, 0, 0, 0); + bool useThemePadding; + + useThemePadding = gTheme->GetWidgetPadding(context->DeviceContext(), + this, disp->mAppearance, + &margin); + if (useThemePadding) { + aMargin.top = context->DevPixelsToAppUnits(margin.top); + aMargin.right = context->DevPixelsToAppUnits(margin.right); + aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom); + aMargin.left = context->DevPixelsToAppUnits(margin.left); + return NS_OK; + } + } + } + + aMargin.SizeTo(0,0,0,0); + StylePadding()->GetPadding(aMargin); + + return NS_OK; +} + +nsresult +nsBox::GetXULMargin(nsMargin& aMargin) +{ + aMargin.SizeTo(0,0,0,0); + StyleMargin()->GetMargin(aMargin); + + return NS_OK; +} + +void +nsBox::SizeNeedsRecalc(nsSize& aSize) +{ + aSize.width = -1; + aSize.height = -1; +} + +void +nsBox::CoordNeedsRecalc(nscoord& aFlex) +{ + aFlex = -1; +} + +bool +nsBox::DoesNeedRecalc(const nsSize& aSize) +{ + return (aSize.width == -1 || aSize.height == -1); +} + +bool +nsBox::DoesNeedRecalc(nscoord aCoord) +{ + return (aCoord == -1); +} + +nsSize +nsBox::GetXULPrefSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize pref(0,0); + DISPLAY_PREF_SIZE(this, pref); + + if (IsXULCollapsed()) + return pref; + + AddBorderAndPadding(pref); + bool widthSet, heightSet; + nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet); + + nsSize minSize = GetXULMinSize(aState); + nsSize maxSize = GetXULMaxSize(aState); + return BoundsCheck(minSize, pref, maxSize); +} + +nsSize +nsBox::GetXULMinSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize min(0,0); + DISPLAY_MIN_SIZE(this, min); + + if (IsXULCollapsed()) + return min; + + AddBorderAndPadding(min); + bool widthSet, heightSet; + nsIFrame::AddXULMinSize(aState, this, min, widthSet, heightSet); + return min; +} + +nsSize +nsBox::GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) +{ + return nsSize(0, 0); +} + +nsSize +nsBox::GetXULMaxSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE); + DISPLAY_MAX_SIZE(this, maxSize); + + if (IsXULCollapsed()) + return maxSize; + + AddBorderAndPadding(maxSize); + bool widthSet, heightSet; + nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet); + return maxSize; +} + +nscoord +nsBox::GetXULFlex() +{ + nscoord flex = 0; + + nsIFrame::AddXULFlex(this, flex); + + return flex; +} + +uint32_t +nsIFrame::GetXULOrdinal() +{ + uint32_t ordinal = StyleXUL()->mBoxOrdinal; + + // When present, attribute value overrides CSS. + nsIContent* content = GetContent(); + if (content && content->IsXULElement()) { + nsresult error; + nsAutoString value; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, value); + if (!value.IsEmpty()) { + ordinal = value.ToInteger(&error); + } + } + + return ordinal; +} + +nscoord +nsBox::GetXULBoxAscent(nsBoxLayoutState& aState) +{ + if (IsXULCollapsed()) + return 0; + + return GetXULPrefSize(aState).height; +} + +bool +nsBox::IsXULCollapsed() +{ + return StyleVisibility()->mVisible == NS_STYLE_VISIBILITY_COLLAPSE; +} + +nsresult +nsIFrame::XULLayout(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsBox *box = static_cast<nsBox*>(this); + DISPLAY_LAYOUT(box); + + box->BeginXULLayout(aState); + + box->DoXULLayout(aState); + + box->EndXULLayout(aState); + + return NS_OK; +} + +bool +nsBox::DoesClipChildren() +{ + const nsStyleDisplay* display = StyleDisplay(); + NS_ASSERTION((display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == + (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP), + "If one overflow is clip, the other should be too"); + return display->mOverflowX == NS_STYLE_OVERFLOW_CLIP; +} + +nsresult +nsBox::SyncLayout(nsBoxLayoutState& aState) +{ + /* + if (IsXULCollapsed()) { + CollapseChild(aState, this, true); + return NS_OK; + } + */ + + + if (GetStateBits() & NS_FRAME_IS_DIRTY) + XULRedraw(aState); + + RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY + | NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW); + + nsPresContext* presContext = aState.PresContext(); + + uint32_t flags = GetXULLayoutFlags(); + + uint32_t stateFlags = aState.LayoutFlags(); + + flags |= stateFlags; + + nsRect visualOverflow; + + if (ComputesOwnOverflowArea()) { + visualOverflow = GetVisualOverflowRect(); + } + else { + nsRect rect(nsPoint(0, 0), GetSize()); + nsOverflowAreas overflowAreas(rect, rect); + if (!DoesClipChildren() && !IsXULCollapsed()) { + // See if our child frames caused us to overflow after being laid + // out. If so, store the overflow area. This normally can't happen + // in XUL, but it can happen with the CSS 'outline' property and + // possibly with other exotic stuff (e.g. relatively positioned + // frames in HTML inside XUL). + nsLayoutUtils::UnionChildOverflow(this, overflowAreas); + } + + FinishAndStoreOverflow(overflowAreas, GetSize()); + visualOverflow = overflowAreas.VisualOverflow(); + } + + nsView* view = GetView(); + if (view) { + // Make sure the frame's view is properly sized and positioned and has + // things like opacity correct + nsContainerFrame::SyncFrameViewAfterReflow(presContext, this, view, + visualOverflow, flags); + } + + return NS_OK; +} + +nsresult +nsIFrame::XULRedraw(nsBoxLayoutState& aState) +{ + if (aState.PaintingDisabled()) + return NS_OK; + + // nsStackLayout, at least, expects us to repaint descendants even + // if a damage rect is provided + InvalidateFrameSubtree(); + + return NS_OK; +} + +bool +nsIFrame::AddXULPrefSize(nsIFrame* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet) +{ + aWidthSet = false; + aHeightSet = false; + + // add in the css min, max, pref + const nsStylePosition* position = aBox->StylePosition(); + + // see if the width or height was specifically set + // XXX Handle eStyleUnit_Enumerated? + // (Handling the eStyleUnit_Enumerated types requires + // GetXULPrefSize/GetXULMinSize methods that don't consider + // (min-/max-/)(width/height) properties.) + const nsStyleCoord &width = position->mWidth; + if (width.GetUnit() == eStyleUnit_Coord) { + aSize.width = width.GetCoordValue(); + aWidthSet = true; + } else if (width.IsCalcUnit()) { + if (!width.CalcHasPercent()) { + // pass 0 for percentage basis since we know there are no %s + aSize.width = nsRuleNode::ComputeComputedCalc(width, 0); + if (aSize.width < 0) + aSize.width = 0; + aWidthSet = true; + } + } + + const nsStyleCoord &height = position->mHeight; + if (height.GetUnit() == eStyleUnit_Coord) { + aSize.height = height.GetCoordValue(); + aHeightSet = true; + } else if (height.IsCalcUnit()) { + if (!height.CalcHasPercent()) { + // pass 0 for percentage basis since we know there are no %s + aSize.height = nsRuleNode::ComputeComputedCalc(height, 0); + if (aSize.height < 0) + aSize.height = 0; + aHeightSet = true; + } + } + + nsIContent* content = aBox->GetContent(); + // ignore 'height' and 'width' attributes if the actual element is not XUL + // For example, we might be magic XUL frames whose primary content is an HTML + // <select> + if (content && content->IsXULElement()) { + nsAutoString value; + nsresult error; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value); + if (!value.IsEmpty()) { + value.Trim("%"); + + aSize.width = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + aWidthSet = true; + } + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value); + if (!value.IsEmpty()) { + value.Trim("%"); + + aSize.height = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + aHeightSet = true; + } + } + + return (aWidthSet && aHeightSet); +} + + +bool +nsIFrame::AddXULMinSize(nsBoxLayoutState& aState, nsIFrame* aBox, nsSize& aSize, + bool &aWidthSet, bool &aHeightSet) +{ + aWidthSet = false; + aHeightSet = false; + + bool canOverride = true; + + // See if a native theme wants to supply a minimum size. + const nsStyleDisplay* display = aBox->StyleDisplay(); + if (display->mAppearance) { + nsITheme *theme = aState.PresContext()->GetTheme(); + if (theme && theme->ThemeSupportsWidget(aState.PresContext(), aBox, display->mAppearance)) { + LayoutDeviceIntSize size; + theme->GetMinimumWidgetSize(aState.PresContext(), aBox, + display->mAppearance, &size, &canOverride); + if (size.width) { + aSize.width = aState.PresContext()->DevPixelsToAppUnits(size.width); + aWidthSet = true; + } + if (size.height) { + aSize.height = aState.PresContext()->DevPixelsToAppUnits(size.height); + aHeightSet = true; + } + } + } + + // add in the css min, max, pref + const nsStylePosition* position = aBox->StylePosition(); + + // same for min size. Unfortunately min size is always set to 0. So for now + // we will assume 0 (as a coord) means not set. + const nsStyleCoord &minWidth = position->mMinWidth; + if ((minWidth.GetUnit() == eStyleUnit_Coord && + minWidth.GetCoordValue() != 0) || + (minWidth.IsCalcUnit() && !minWidth.CalcHasPercent())) { + nscoord min = nsRuleNode::ComputeCoordPercentCalc(minWidth, 0); + if (!aWidthSet || (min > aSize.width && canOverride)) { + aSize.width = min; + aWidthSet = true; + } + } else if (minWidth.GetUnit() == eStyleUnit_Percent) { + NS_ASSERTION(minWidth.GetPercentValue() == 0.0f, + "Non-zero percentage values not currently supported"); + aSize.width = 0; + aWidthSet = true; // FIXME: should we really do this for + // nonzero values? + } + // XXX Handle eStyleUnit_Enumerated? + // (Handling the eStyleUnit_Enumerated types requires + // GetXULPrefSize/GetXULMinSize methods that don't consider + // (min-/max-/)(width/height) properties. + // calc() with percentage is treated like '0' (unset) + + const nsStyleCoord &minHeight = position->mMinHeight; + if ((minHeight.GetUnit() == eStyleUnit_Coord && + minHeight.GetCoordValue() != 0) || + (minHeight.IsCalcUnit() && !minHeight.CalcHasPercent())) { + nscoord min = nsRuleNode::ComputeCoordPercentCalc(minHeight, 0); + if (!aHeightSet || (min > aSize.height && canOverride)) { + aSize.height = min; + aHeightSet = true; + } + } else if (minHeight.GetUnit() == eStyleUnit_Percent) { + NS_ASSERTION(position->mMinHeight.GetPercentValue() == 0.0f, + "Non-zero percentage values not currently supported"); + aSize.height = 0; + aHeightSet = true; // FIXME: should we really do this for + // nonzero values? + } + // calc() with percentage is treated like '0' (unset) + + nsIContent* content = aBox->GetContent(); + if (content && content->IsXULElement()) { + nsAutoString value; + nsresult error; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::minwidth, value); + if (!value.IsEmpty()) + { + value.Trim("%"); + + nscoord val = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + if (val > aSize.width) + aSize.width = val; + aWidthSet = true; + } + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::minheight, value); + if (!value.IsEmpty()) + { + value.Trim("%"); + + nscoord val = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + if (val > aSize.height) + aSize.height = val; + + aHeightSet = true; + } + } + + return (aWidthSet && aHeightSet); +} + +bool +nsIFrame::AddXULMaxSize(nsIFrame* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet) +{ + aWidthSet = false; + aHeightSet = false; + + // add in the css min, max, pref + const nsStylePosition* position = aBox->StylePosition(); + + // and max + // see if the width or height was specifically set + // XXX Handle eStyleUnit_Enumerated? + // (Handling the eStyleUnit_Enumerated types requires + // GetXULPrefSize/GetXULMinSize methods that don't consider + // (min-/max-/)(width/height) properties.) + const nsStyleCoord maxWidth = position->mMaxWidth; + if (maxWidth.ConvertsToLength()) { + aSize.width = nsRuleNode::ComputeCoordPercentCalc(maxWidth, 0); + aWidthSet = true; + } + // percentages and calc() with percentages are treated like 'none' + + const nsStyleCoord &maxHeight = position->mMaxHeight; + if (maxHeight.ConvertsToLength()) { + aSize.height = nsRuleNode::ComputeCoordPercentCalc(maxHeight, 0); + aHeightSet = true; + } + // percentages and calc() with percentages are treated like 'none' + + nsIContent* content = aBox->GetContent(); + if (content && content->IsXULElement()) { + nsAutoString value; + nsresult error; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth, value); + if (!value.IsEmpty()) { + value.Trim("%"); + + nscoord val = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + aSize.width = val; + aWidthSet = true; + } + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::maxheight, value); + if (!value.IsEmpty()) { + value.Trim("%"); + + nscoord val = + nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error)); + aSize.height = val; + + aHeightSet = true; + } + } + + return (aWidthSet || aHeightSet); +} + +bool +nsIFrame::AddXULFlex(nsIFrame* aBox, nscoord& aFlex) +{ + bool flexSet = false; + + // get the flexibility + aFlex = aBox->StyleXUL()->mBoxFlex; + + // attribute value overrides CSS + nsIContent* content = aBox->GetContent(); + if (content && content->IsXULElement()) { + nsresult error; + nsAutoString value; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::flex, value); + if (!value.IsEmpty()) { + value.Trim("%"); + aFlex = value.ToInteger(&error); + flexSet = true; + } + } + + if (aFlex < 0) + aFlex = 0; + if (aFlex >= nscoord_MAX) + aFlex = nscoord_MAX - 1; + + return flexSet || aFlex > 0; +} + +void +nsBox::AddBorderAndPadding(nsSize& aSize) +{ + AddBorderAndPadding(this, aSize); +} + +void +nsBox::AddBorderAndPadding(nsIFrame* aBox, nsSize& aSize) +{ + nsMargin borderPadding(0,0,0,0); + aBox->GetXULBorderAndPadding(borderPadding); + AddMargin(aSize, borderPadding); +} + +void +nsBox::AddMargin(nsIFrame* aChild, nsSize& aSize) +{ + nsMargin margin(0,0,0,0); + aChild->GetXULMargin(margin); + AddMargin(aSize, margin); +} + +void +nsBox::AddMargin(nsSize& aSize, const nsMargin& aMargin) +{ + if (aSize.width != NS_INTRINSICSIZE) + aSize.width += aMargin.left + aMargin.right; + + if (aSize.height != NS_INTRINSICSIZE) + aSize.height += aMargin.top + aMargin.bottom; +} + +nscoord +nsBox::BoundsCheck(nscoord aMin, nscoord aPref, nscoord aMax) +{ + if (aPref > aMax) + aPref = aMax; + + if (aPref < aMin) + aPref = aMin; + + return aPref; +} + +nsSize +nsBox::BoundsCheckMinMax(const nsSize& aMinSize, const nsSize& aMaxSize) +{ + return nsSize(std::max(aMaxSize.width, aMinSize.width), + std::max(aMaxSize.height, aMinSize.height)); +} + +nsSize +nsBox::BoundsCheck(const nsSize& aMinSize, const nsSize& aPrefSize, const nsSize& aMaxSize) +{ + return nsSize(BoundsCheck(aMinSize.width, aPrefSize.width, aMaxSize.width), + BoundsCheck(aMinSize.height, aPrefSize.height, aMaxSize.height)); +} + +/*static*/ nsIFrame* +nsBox::GetChildXULBox(const nsIFrame* aFrame) +{ + // box layout ends at box-wrapped frames, so don't allow these frames + // to report child boxes. + return aFrame->IsXULBoxFrame() ? aFrame->PrincipalChildList().FirstChild() : nullptr; +} + +/*static*/ nsIFrame* +nsBox::GetNextXULBox(const nsIFrame* aFrame) +{ + return aFrame->GetParent() && + aFrame->GetParent()->IsXULBoxFrame() ? aFrame->GetNextSibling() : nullptr; +} + +/*static*/ nsIFrame* +nsBox::GetParentXULBox(const nsIFrame* aFrame) +{ + return aFrame->GetParent() && + aFrame->GetParent()->IsXULBoxFrame() ? aFrame->GetParent() : nullptr; +} + +#ifdef DEBUG_LAYOUT +nsresult +nsBox::SetXULDebug(nsBoxLayoutState& aState, bool aDebug) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsBox::GetDebugBoxAt( const nsPoint& aPoint, + nsIFrame** aBox) +{ + nsRect thisRect(nsPoint(0,0), GetSize()); + if (!thisRect.Contains(aPoint)) + return NS_ERROR_FAILURE; + + nsIFrame* child = nsBox::GetChildXULBox(this); + nsIFrame* hit = nullptr; + + *aBox = nullptr; + while (nullptr != child) { + nsresult rv = child->GetDebugBoxAt(aPoint - child->GetOffsetTo(this), &hit); + + if (NS_SUCCEEDED(rv) && hit) { + *aBox = hit; + } + child = GetNextXULBox(child); + } + + // found a child + if (*aBox) { + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + + +nsresult +nsBox::GetXULDebug(bool& aDebug) +{ + aDebug = false; + return NS_OK; +} + +#endif |