layout/xul/nsBox.cpp
author Lukasz Anforowicz <lukasza@chromium.org>
Mon, 09 Apr 2018 14:33:07 +0000
changeset 466985 861650deaddc7dfc160136f40bf3f7c83c504e09
parent 451996 cbea2cff1cc28d42add02f79930e4d88e8100b88
child 470240 3ab2fbb175bd7fa720d4e990dbaaa0a5fe0c32a9
permissions -rw-r--r--
Bug 1452097 [wpt PR 9952] - Make 2 tests immune to legitimate races between cross-origin subframes, a=testonly Automatic update from web-platform-testsMake 2 tests immune to legitimate races between cross-origin subframes Bug: 820589 Change-Id: I16c02746c61f42fafead5f96ae436b331fba4f85 Reviewed-on: https://chromium-review.googlesource.com/957587 Commit-Queue: Ɓukasz Anforowicz <lukasza@chromium.org> Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Cr-Commit-Position: refs/heads/master@{#542360} wpt-commits: 1f638e87c2b4411c53121108efc7aa19df8bc543 wpt-pr: 9952 wpt-commits: 1f638e87c2b4411c53121108efc7aa19df8bc543 wpt-pr: 9952

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsDOMAttributeMap.h"
#include "nsPresContext.h"
#include "nsCOMPtr.h"
#include "nsIContent.h"
#include "nsContainerFrame.h"
#include "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsIDOMNode.h"
#include "nsITheme.h"
#include "nsIServiceManager.h"
#include "nsBoxLayout.h"
#include "FrameLayerBuilder.h"
#include "mozilla/dom/Attr.h"
#include "mozilla/dom/Element.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 && content->IsElement()) {
      RefPtr<nsDOMAttributeMap> namedMap = content->AsElement()->Attributes();

      uint32_t length = namedMap->Length();

      RefPtr<dom::Attr> attribute;
      for (uint32_t i = 0; i < length; ++i)
      {
        attribute = namedMap->Item(i);
        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?
  AddStateBits(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.
  DeleteProperty(UsedBorderProperty());
  DeleteProperty(UsedPaddingProperty());
  DeleteProperty(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(ClassID aID)
  : nsIFrame(aID)
{
  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->AsElement()->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 = width.ComputeComputedCalc(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 = height.ComputeComputedCalc(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->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
        if (!value.IsEmpty()) {
            value.Trim("%");

            aSize.width =
              nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
            aWidthSet = true;
        }

        content->AsElement()->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 = minWidth.ComputeCoordPercentCalc(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 = minHeight.ComputeCoordPercentCalc(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->AsElement()->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->AsElement()->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 = maxWidth.ComputeCoordPercentCalc(0);
        aWidthSet = true;
    }
    // percentages and calc() with percentages are treated like 'none'

    const nsStyleCoord &maxHeight = position->mMaxHeight;
    if (maxHeight.ConvertsToLength()) {
        aSize.height = maxHeight.ComputeCoordPercentCalc(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->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth, value);
        if (!value.IsEmpty()) {
            value.Trim("%");

            nscoord val =
              nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
            aSize.width = val;
            aWidthSet = true;
        }

        content->AsElement()->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->AsElement()->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