/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et 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/. */

//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//

// How boxes layout
// ----------------
// Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down.
// 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes.
// 2) It then adds them up to determine its size.
// 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size
//    otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if 
//    Necessary.
//
// However there is a catch. Some html components like block frames can not determine their preferred size. 
// this is their size if they were laid out intrinsically. So the box will flow the child to determine this can
// cache the value.

// Boxes and Incremental Reflow
// ----------------------------
// Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental
// reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change
// any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared
// so when asked for there current size they can relayout themselves. 

#include "nsBoxFrame.h"

#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "nsBoxLayoutState.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/Move.h"
#include "nsStyleContext.h"
#include "nsPlaceholderFrame.h"
#include "nsPresContext.h"
#include "nsCOMPtr.h"
#include "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsHTMLParts.h"
#include "nsViewManager.h"
#include "nsView.h"
#include "nsIPresShell.h"
#include "nsCSSRendering.h"
#include "nsIServiceManager.h"
#include "nsBoxLayout.h"
#include "nsSprocketLayout.h"
#include "nsIScrollableFrame.h"
#include "nsWidgetsCID.h"
#include "nsCSSAnonBoxes.h"
#include "nsContainerFrame.h"
#include "nsIDOMElement.h"
#include "nsITheme.h"
#include "nsTransform2D.h"
#include "mozilla/EventStateManager.h"
#include "nsIDOMEvent.h"
#include "nsDisplayList.h"
#include "mozilla/Preferences.h"
#include "nsThemeConstants.h"
#include "nsLayoutUtils.h"
#include "nsSliderFrame.h"
#include <algorithm>

// Needed for Print Preview
#include "nsIURI.h"

#include "mozilla/TouchEvents.h"

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;

//define DEBUG_REDRAW

#define DEBUG_SPRING_SIZE 8
#define DEBUG_BORDER_SIZE 2
#define COIL_SIZE 8

//#define TEST_SANITY

#ifdef DEBUG_rods
//#define DO_NOISY_REFLOW
#endif

#ifdef DEBUG_LAYOUT
bool nsBoxFrame::gDebug = false;
nsIFrame* nsBoxFrame::mDebugChild = nullptr;
#endif

nsIFrame*
NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager)
{
  return new (aPresShell) nsBoxFrame(aContext, aIsRoot, aLayoutManager);
}

nsIFrame*
NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
  return new (aPresShell) nsBoxFrame(aContext);
}

NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)

#ifdef DEBUG
NS_QUERYFRAME_HEAD(nsBoxFrame)
  NS_QUERYFRAME_ENTRY(nsBoxFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
#endif

nsBoxFrame::nsBoxFrame(nsStyleContext* aContext,
                       bool aIsRoot,
                       nsBoxLayout* aLayoutManager) :
  nsContainerFrame(aContext)
{
  mState |= NS_STATE_IS_HORIZONTAL;
  mState |= NS_STATE_AUTO_STRETCH;

  if (aIsRoot) 
     mState |= NS_STATE_IS_ROOT;

  mValign = vAlign_Top;
  mHalign = hAlign_Left;
  
  // if no layout manager specified us the static sprocket layout
  nsCOMPtr<nsBoxLayout> layout = aLayoutManager;

  if (layout == nullptr) {
    NS_NewSprocketLayout(layout);
  }

  SetXULLayoutManager(layout);
}

nsBoxFrame::~nsBoxFrame()
{
}

void
nsBoxFrame::SetInitialChildList(ChildListID     aListID,
                                nsFrameList&    aChildList)
{
  nsContainerFrame::SetInitialChildList(aListID, aChildList);
  if (aListID == kPrincipalList) {
    // initialize our list of infos.
    nsBoxLayoutState state(PresContext());
    CheckBoxOrder();
    if (mLayoutManager)
      mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
  }
}

/* virtual */ void
nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{
  nsContainerFrame::DidSetStyleContext(aOldStyleContext);

  // The values that CacheAttributes() computes depend on our style,
  // so we need to recompute them here...
  CacheAttributes();
}

/**
 * Initialize us. This is a good time to get the alignment of the box
 */
void
nsBoxFrame::Init(nsIContent*       aContent,
                 nsContainerFrame* aParent,
                 nsIFrame*         aPrevInFlow)
{
  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);

  if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
    AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
  }

  MarkIntrinsicISizesDirty();

  CacheAttributes();

#ifdef DEBUG_LAYOUT
    // if we are root and this
  if (mState & NS_STATE_IS_ROOT) {
    GetDebugPref();
  }
#endif

  UpdateMouseThrough();

  // register access key
  RegUnregAccessKey(true);
}

void nsBoxFrame::UpdateMouseThrough()
{
  if (mContent) {
    static nsIContent::AttrValuesArray strings[] =
      {&nsGkAtoms::never, &nsGkAtoms::always, nullptr};
    switch (mContent->FindAttrValueIn(kNameSpaceID_None,
              nsGkAtoms::mousethrough, strings, eCaseMatters)) {
      case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
      case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
      case 2: {
        RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
        RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
        break;
      }
    }
  }
}

void
nsBoxFrame::CacheAttributes()
{
  /*
  printf("Caching: ");
  XULDumpBox(stdout);
  printf("\n");
   */

  mValign = vAlign_Top;
  mHalign = hAlign_Left;

  bool orient = false;
  GetInitialOrientation(orient); 
  if (orient)
    mState |= NS_STATE_IS_HORIZONTAL;
  else
    mState &= ~NS_STATE_IS_HORIZONTAL;

  bool normal = true;
  GetInitialDirection(normal); 
  if (normal)
    mState |= NS_STATE_IS_DIRECTION_NORMAL;
  else
    mState &= ~NS_STATE_IS_DIRECTION_NORMAL;

  GetInitialVAlignment(mValign);
  GetInitialHAlignment(mHalign);
  
  bool equalSize = false;
  GetInitialEqualSize(equalSize); 
  if (equalSize)
        mState |= NS_STATE_EQUAL_SIZE;
    else
        mState &= ~NS_STATE_EQUAL_SIZE;

  bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
  GetInitialAutoStretch(autostretch);
  if (autostretch)
        mState |= NS_STATE_AUTO_STRETCH;
     else
        mState &= ~NS_STATE_AUTO_STRETCH;


#ifdef DEBUG_LAYOUT
  bool debug = mState & NS_STATE_SET_TO_DEBUG;
  bool debugSet = GetInitialDebug(debug); 
  if (debugSet) {
        mState |= NS_STATE_DEBUG_WAS_SET;
        if (debug)
            mState |= NS_STATE_SET_TO_DEBUG;
        else
            mState &= ~NS_STATE_SET_TO_DEBUG;
  } else {
        mState &= ~NS_STATE_DEBUG_WAS_SET;
  }
#endif
}

#ifdef DEBUG_LAYOUT
bool
nsBoxFrame::GetInitialDebug(bool& aDebug)
{
  if (!GetContent())
    return false;

  static nsIContent::AttrValuesArray strings[] =
    {&nsGkAtoms::_false, &nsGkAtoms::_true, nullptr};
  int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
      nsGkAtoms::debug, strings, eCaseMatters);
  if (index >= 0) {
    aDebug = index == 1;
    return true;
  }

  return false;
}
#endif

bool
nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
{
  if (!GetContent())
    return false;

  // XXXdwh Everything inside this if statement is deprecated code.
  static nsIContent::AttrValuesArray alignStrings[] =
    {&nsGkAtoms::left, &nsGkAtoms::right, nullptr};
  static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
  int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
      alignStrings, eCaseMatters);
  if (index >= 0) {
    aHalign = alignValues[index];
    return true;
  }
      
  // Now that the deprecated stuff is out of the way, we move on to check the appropriate 
  // attribute.  For horizontal boxes, we are checking the PACK attribute.  For vertical boxes
  // we are checking the ALIGN attribute.
  nsIAtom* attrName = IsXULHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
  static nsIContent::AttrValuesArray strings[] =
    {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr};
  static const Halignment values[] =
    {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
  index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
      strings, eCaseMatters);

  if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
    // The attr was present but had a nonsensical value. Revert to the default.
    return false;
  }
  if (index > 0) {    
    aHalign = values[index];
    return true;
  }

  // Now that we've checked for the attribute it's time to check CSS.  For 
  // horizontal boxes we're checking PACK.  For vertical boxes we are checking
  // ALIGN.
  const nsStyleXUL* boxInfo = StyleXUL();
  if (IsXULHorizontal()) {
    switch (boxInfo->mBoxPack) {
      case StyleBoxPack::Start:
        aHalign = nsBoxFrame::hAlign_Left;
        return true;
      case StyleBoxPack::Center:
        aHalign = nsBoxFrame::hAlign_Center;
        return true;
      case StyleBoxPack::End:
        aHalign = nsBoxFrame::hAlign_Right;
        return true;
      default: // Nonsensical value. Just bail.
        return false;
    }
  }
  else {
    switch (boxInfo->mBoxAlign) {
      case StyleBoxAlign::Start:
        aHalign = nsBoxFrame::hAlign_Left;
        return true;
      case StyleBoxAlign::Center:
        aHalign = nsBoxFrame::hAlign_Center;
        return true;
      case StyleBoxAlign::End:
        aHalign = nsBoxFrame::hAlign_Right;
        return true;
      default: // Nonsensical value. Just bail.
        return false;
    }
  }

  return false;
}

bool
nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
{
  if (!GetContent())
    return false;

  static nsIContent::AttrValuesArray valignStrings[] =
    {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr};
  static const Valignment valignValues[] =
    {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
  int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
      valignStrings, eCaseMatters);
  if (index >= 0) {
    aValign = valignValues[index];
    return true;
  }

  // Now that the deprecated stuff is out of the way, we move on to check the appropriate 
  // attribute.  For horizontal boxes, we are checking the ALIGN attribute.  For vertical boxes
  // we are checking the PACK attribute.
  nsIAtom* attrName = IsXULHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
  static nsIContent::AttrValuesArray strings[] =
    {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
     &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr};
  static const Valignment values[] =
    {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
  index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
      strings, eCaseMatters);
  if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
    // The attr was present but had a nonsensical value. Revert to the default.
    return false;
  }
  if (index > 0) {
    aValign = values[index];
    return true;
  }

  // Now that we've checked for the attribute it's time to check CSS.  For 
  // horizontal boxes we're checking ALIGN.  For vertical boxes we are checking
  // PACK.
  const nsStyleXUL* boxInfo = StyleXUL();
  if (IsXULHorizontal()) {
    switch (boxInfo->mBoxAlign) {
      case StyleBoxAlign::Start:
        aValign = nsBoxFrame::vAlign_Top;
        return true;
      case StyleBoxAlign::Center:
        aValign = nsBoxFrame::vAlign_Middle;
        return true;
      case StyleBoxAlign::Baseline:
        aValign = nsBoxFrame::vAlign_BaseLine;
        return true;
      case StyleBoxAlign::End:
        aValign = nsBoxFrame::vAlign_Bottom;
        return true;
      default: // Nonsensical value. Just bail.
        return false;
    }
  }
  else {
    switch (boxInfo->mBoxPack) {
      case StyleBoxPack::Start:
        aValign = nsBoxFrame::vAlign_Top;
        return true;
      case StyleBoxPack::Center:
        aValign = nsBoxFrame::vAlign_Middle;
        return true;
      case StyleBoxPack::End:
        aValign = nsBoxFrame::vAlign_Bottom;
        return true;
      default: // Nonsensical value. Just bail.
        return false;
    }
  }

  return false;
}

void
nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
{
 // see if we are a vertical or horizontal box.
  if (!GetContent())
    return;

  // Check the style system first.
  const nsStyleXUL* boxInfo = StyleXUL();
  if (boxInfo->mBoxOrient == StyleBoxOrient::Horizontal) {
    aIsHorizontal = true;
  } else {
    aIsHorizontal = false;
  }

  // Now see if we have an attribute.  The attribute overrides
  // the style system value.
  static nsIContent::AttrValuesArray strings[] =
    {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr};
  int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
      strings, eCaseMatters);
  if (index >= 0) {
    aIsHorizontal = index == 1;
  }
}

void
nsBoxFrame::GetInitialDirection(bool& aIsNormal)
{
  if (!GetContent())
    return;

  if (IsXULHorizontal()) {
    // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
    // This means that BiDI users will end up with horizontally inverted chrome.
    aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
  }
  else
    aIsNormal = true; // Assume a normal direction in the vertical case.

  // Now check the style system to see if we should invert aIsNormal.
  const nsStyleXUL* boxInfo = StyleXUL();
  if (boxInfo->mBoxDirection == StyleBoxDirection::Reverse) {
    aIsNormal = !aIsNormal; // Invert our direction.
  }

  // Now see if we have an attribute.  The attribute overrides
  // the style system value.
  if (IsXULHorizontal()) {
    static nsIContent::AttrValuesArray strings[] =
      {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
    int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
        strings, eCaseMatters);
    if (index >= 0) {
      bool values[] = {!aIsNormal, true, false};
      aIsNormal = values[index];
    }
  } else if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
                                       nsGkAtoms::reverse, eCaseMatters)) {
    aIsNormal = !aIsNormal;
  }
}

/* Returns true if it was set.
 */
bool
nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
{
 // see if we are a vertical or horizontal box.
  if (!GetContent())
     return false;

  if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize,
                           nsGkAtoms::always, eCaseMatters)) {
    aEqualSize = true;
    return true;
  }

  return false;
}

/* Returns true if it was set.
 */
bool
nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
{
  if (!GetContent())
     return false;
  
  // Check the align attribute.
  static nsIContent::AttrValuesArray strings[] =
    {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr};
  int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
      strings, eCaseMatters);
  if (index != nsIContent::ATTR_MISSING && index != 0) {
    aStretch = index == 1;
    return true;
  }

  // Check the CSS box-align property.
  const nsStyleXUL* boxInfo = StyleXUL();
  aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch);

  return true;
}

void
nsBoxFrame::DidReflow(nsPresContext*           aPresContext,
                      const ReflowInput*  aReflowInput,
                      nsDidReflowStatus         aStatus)
{
  nsFrameState preserveBits =
    mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
  nsFrame::DidReflow(aPresContext, aReflowInput, aStatus);
  mState |= preserveBits;
}

bool
nsBoxFrame::HonorPrintBackgroundSettings()
{
  return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
    nsContainerFrame::HonorPrintBackgroundSettings();
}

#ifdef DO_NOISY_REFLOW
static int myCounter = 0;
static void printSize(char * aDesc, nscoord aSize) 
{
  printf(" %s: ", aDesc);
  if (aSize == NS_UNCONSTRAINEDSIZE) {
    printf("UC");
  } else {
    printf("%d", aSize);
  }
}
#endif

/* virtual */ nscoord
nsBoxFrame::GetMinISize(nsRenderingContext *aRenderingContext)
{
  nscoord result;
  DISPLAY_MIN_WIDTH(this, result);

  nsBoxLayoutState state(PresContext(), aRenderingContext);
  nsSize minSize = GetXULMinSize(state);

  // GetXULMinSize returns border-box width, and we want to return content
  // width.  Since Reflow uses the reflow state's border and padding, we
  // actually just want to subtract what GetXULMinSize added, which is the
  // result of GetXULBorderAndPadding.
  nsMargin bp;
  GetXULBorderAndPadding(bp);

  result = minSize.width - bp.LeftRight();
  result = std::max(result, 0);

  return result;
}

/* virtual */ nscoord
nsBoxFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
{
  nscoord result;
  DISPLAY_PREF_WIDTH(this, result);

  nsBoxLayoutState state(PresContext(), aRenderingContext);
  nsSize prefSize = GetXULPrefSize(state);

  // GetXULPrefSize returns border-box width, and we want to return content
  // width.  Since Reflow uses the reflow state's border and padding, we
  // actually just want to subtract what GetXULPrefSize added, which is the
  // result of GetXULBorderAndPadding.
  nsMargin bp;
  GetXULBorderAndPadding(bp);

  result = prefSize.width - bp.LeftRight();
  result = std::max(result, 0);

  return result;
}

void
nsBoxFrame::Reflow(nsPresContext*          aPresContext,
                   ReflowOutput&     aDesiredSize,
                   const ReflowInput& aReflowInput,
                   nsReflowStatus&          aStatus)
{
  MarkInReflow();
  // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
  // in sync, if the changes are applicable there.

  DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);

  NS_ASSERTION(aReflowInput.ComputedWidth() >=0 &&
               aReflowInput.ComputedHeight() >= 0, "Computed Size < 0");

#ifdef DO_NOISY_REFLOW
  printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
  printf("%p ** nsBF::Reflow %d ", this, myCounter++);
  
  printSize("AW", aReflowInput.AvailableWidth());
  printSize("AH", aReflowInput.AvailableHeight());
  printSize("CW", aReflowInput.ComputedWidth());
  printSize("CH", aReflowInput.ComputedHeight());

  printf(" *\n");

#endif

  aStatus = NS_FRAME_COMPLETE;

  // create the layout state
  nsBoxLayoutState state(aPresContext, aReflowInput.mRenderingContext,
                         &aReflowInput, aReflowInput.mReflowDepth);

  WritingMode wm = aReflowInput.GetWritingMode();
  LogicalSize computedSize(wm, aReflowInput.ComputedISize(),
                           aReflowInput.ComputedBSize());

  LogicalMargin m = aReflowInput.ComputedLogicalBorderPadding();
  // GetXULBorderAndPadding(m);

  LogicalSize prefSize(wm);

  // if we are told to layout intrinsic then get our preferred size.
  NS_ASSERTION(computedSize.ISize(wm) != NS_INTRINSICSIZE,
               "computed inline size should always be computed");
  if (computedSize.BSize(wm) == NS_INTRINSICSIZE) {
    nsSize physicalPrefSize = GetXULPrefSize(state);
    nsSize minSize = GetXULMinSize(state);
    nsSize maxSize = GetXULMaxSize(state);
    // XXXbz isn't GetXULPrefSize supposed to bounds-check for us?
    physicalPrefSize = BoundsCheck(minSize, physicalPrefSize, maxSize);
    prefSize = LogicalSize(wm, physicalPrefSize);
  }

  // get our desiredSize
  computedSize.ISize(wm) += m.IStart(wm) + m.IEnd(wm);

  if (aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
    computedSize.BSize(wm) = prefSize.BSize(wm);
    // prefSize is border-box but min/max constraints are content-box.
    nscoord blockDirBorderPadding =
      aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
    nscoord contentBSize = computedSize.BSize(wm) - blockDirBorderPadding;
    // Note: contentHeight might be negative, but that's OK because min-height
    // is never negative.
    computedSize.BSize(wm) = aReflowInput.ApplyMinMaxHeight(contentBSize) +
                             blockDirBorderPadding;
  } else {
    computedSize.BSize(wm) += m.BStart(wm) + m.BEnd(wm);
  }

  nsSize physicalSize = computedSize.GetPhysicalSize(wm);
  nsRect r(mRect.x, mRect.y, physicalSize.width, physicalSize.height);

  SetXULBounds(state, r);
 
  // layout our children
  XULLayout(state);
  
  // ok our child could have gotten bigger. So lets get its bounds
  
  // get the ascent
  LogicalSize boxSize = GetLogicalSize(wm);
  nscoord ascent = boxSize.BSize(wm);

  // getting the ascent could be a lot of work. Don't get it if
  // we are the root. The viewport doesn't care about it.
  if (!(mState & NS_STATE_IS_ROOT)) {
    ascent = GetXULBoxAscent(state);
  }

  aDesiredSize.SetSize(wm, boxSize);
  aDesiredSize.SetBlockStartAscent(ascent);

  aDesiredSize.mOverflowAreas = GetOverflowAreas();

#ifdef DO_NOISY_REFLOW
  {
    printf("%p ** nsBF(done) W:%d H:%d  ", this, aDesiredSize.Width(), aDesiredSize.Height());

    if (maxElementSize) {
      printf("MW:%d\n", *maxElementWidth); 
    } else {
      printf("MW:?\n"); 
    }

  }
#endif

  ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}

nsSize
nsBoxFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
{
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
               "must have rendering context");

  nsSize size(0,0);
  DISPLAY_PREF_SIZE(this, size);
  if (!DoesNeedRecalc(mPrefSize)) {
     return mPrefSize;
  }

#ifdef DEBUG_LAYOUT
  PropagateDebug(aBoxLayoutState);
#endif

  if (IsXULCollapsed())
    return size;

  // if the size was not completely redefined in CSS then ask our children
  bool widthSet, heightSet;
  if (!nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet))
  {
    if (mLayoutManager) {
      nsSize layoutSize = mLayoutManager->GetXULPrefSize(this, aBoxLayoutState);
      if (!widthSet)
        size.width = layoutSize.width;
      if (!heightSet)
        size.height = layoutSize.height;
    }
    else {
      size = nsBox::GetXULPrefSize(aBoxLayoutState);
    }
  }

  nsSize minSize = GetXULMinSize(aBoxLayoutState);
  nsSize maxSize = GetXULMaxSize(aBoxLayoutState);
  mPrefSize = BoundsCheck(minSize, size, maxSize);
 
  return mPrefSize;
}

nscoord
nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState)
{
  if (!DoesNeedRecalc(mAscent))
     return mAscent;

#ifdef DEBUG_LAYOUT
  PropagateDebug(aBoxLayoutState);
#endif

  if (IsXULCollapsed())
    return 0;

  if (mLayoutManager)
    mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
  else
    mAscent = nsBox::GetXULBoxAscent(aBoxLayoutState);

  return mAscent;
}

nsSize
nsBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
{
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
               "must have rendering context");

  nsSize size(0,0);
  DISPLAY_MIN_SIZE(this, size);
  if (!DoesNeedRecalc(mMinSize)) {
    return mMinSize;
  }

#ifdef DEBUG_LAYOUT
  PropagateDebug(aBoxLayoutState);
#endif

  if (IsXULCollapsed())
    return size;

  // if the size was not completely redefined in CSS then ask our children
  bool widthSet, heightSet;
  if (!nsIFrame::AddXULMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
  {
    if (mLayoutManager) {
      nsSize layoutSize = mLayoutManager->GetXULMinSize(this, aBoxLayoutState);
      if (!widthSet)
        size.width = layoutSize.width;
      if (!heightSet)
        size.height = layoutSize.height;
    }
    else {
      size = nsBox::GetXULMinSize(aBoxLayoutState);
    }
  }
  
  mMinSize = size;

  return size;
}

nsSize
nsBoxFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState)
{
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
               "must have rendering context");

  nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
  DISPLAY_MAX_SIZE(this, size);
  if (!DoesNeedRecalc(mMaxSize)) {
    return mMaxSize;
  }

#ifdef DEBUG_LAYOUT
  PropagateDebug(aBoxLayoutState);
#endif

  if (IsXULCollapsed())
    return size;

  // if the size was not completely redefined in CSS then ask our children
  bool widthSet, heightSet;
  if (!nsIFrame::AddXULMaxSize(this, size, widthSet, heightSet))
  {
    if (mLayoutManager) {
      nsSize layoutSize = mLayoutManager->GetXULMaxSize(this, aBoxLayoutState);
      if (!widthSet)
        size.width = layoutSize.width;
      if (!heightSet)
        size.height = layoutSize.height;
    }
    else {
      size = nsBox::GetXULMaxSize(aBoxLayoutState);
    }
  }

  mMaxSize = size;

  return size;
}

nscoord
nsBoxFrame::GetXULFlex()
{
  if (!DoesNeedRecalc(mFlex))
     return mFlex;

  mFlex = nsBox::GetXULFlex();

  return mFlex;
}

/**
 * If subclassing please subclass this method not layout. 
 * layout will call this method.
 */
NS_IMETHODIMP
nsBoxFrame::DoXULLayout(nsBoxLayoutState& aState)
{
  uint32_t oldFlags = aState.LayoutFlags();
  aState.SetLayoutFlags(0);

  nsresult rv = NS_OK;
  if (mLayoutManager) {
    CoordNeedsRecalc(mAscent);
    rv = mLayoutManager->XULLayout(this, aState);
  }

  aState.SetLayoutFlags(oldFlags);

  if (HasAbsolutelyPositionedChildren()) {
    // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
    WritingMode wm = GetWritingMode();
    ReflowInput reflowInput(aState.PresContext(), this,
                                  aState.GetRenderingContext(),
                                  LogicalSize(wm, GetLogicalSize().ISize(wm),
                                              NS_UNCONSTRAINEDSIZE));

    // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
    ReflowOutput desiredSize(reflowInput);
    desiredSize.Width() = mRect.width;
    desiredSize.Height() = mRect.height;

    // get the ascent (cribbed from ::Reflow)
    nscoord ascent = mRect.height;

    // getting the ascent could be a lot of work. Don't get it if
    // we are the root. The viewport doesn't care about it.
    if (!(mState & NS_STATE_IS_ROOT)) {
      ascent = GetXULBoxAscent(aState);
    }
    desiredSize.SetBlockStartAscent(ascent);
    desiredSize.mOverflowAreas = GetOverflowAreas();

    AddStateBits(NS_FRAME_IN_REFLOW);
    // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
    // (just a dummy value; hopefully that's OK)
    nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
    ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
                         reflowInput, reflowStatus);
    RemoveStateBits(NS_FRAME_IN_REFLOW);
  }

  return rv;
}

void
nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
  // unregister access key
  RegUnregAccessKey(false);

  // clean up the container box's layout manager and child boxes
  SetXULLayoutManager(nullptr);

  nsContainerFrame::DestroyFrom(aDestructRoot);
} 

#ifdef DEBUG_LAYOUT
nsresult
nsBoxFrame::SetXULDebug(nsBoxLayoutState& aState, bool aDebug)
{
  // see if our state matches the given debug state
  bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
  bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);

  // if it doesn't then tell each child below us the new debug state
  if (debugChanged)
  {
     if (aDebug) {
         mState |= NS_STATE_CURRENTLY_IN_DEBUG;
     } else {
         mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
     }
 
     SetDebugOnChildList(aState, mFirstChild, aDebug);

    MarkIntrinsicISizesDirty();
  }

  return NS_OK;
}
#endif

/* virtual */ void
nsBoxFrame::MarkIntrinsicISizesDirty()
{
  SizeNeedsRecalc(mPrefSize);
  SizeNeedsRecalc(mMinSize);
  SizeNeedsRecalc(mMaxSize);
  CoordNeedsRecalc(mFlex);
  CoordNeedsRecalc(mAscent);

  if (mLayoutManager) {
    nsBoxLayoutState state(PresContext());
    mLayoutManager->IntrinsicISizesDirty(this, state);
  }

  // Don't call base class method, since everything it does is within an
  // IsXULBoxWrapped check.
}

void
nsBoxFrame::RemoveFrame(ChildListID     aListID,
                        nsIFrame*       aOldFrame)
{
  NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
  nsPresContext* presContext = PresContext();
  nsBoxLayoutState state(presContext);

  // remove the child frame
  mFrames.RemoveFrame(aOldFrame);

  // notify the layout manager
  if (mLayoutManager)
    mLayoutManager->ChildrenRemoved(this, state, aOldFrame);

  // destroy the child frame
  aOldFrame->Destroy();

  // mark us dirty and generate a reflow command
  PresContext()->PresShell()->
    FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                     NS_FRAME_HAS_DIRTY_CHILDREN);
}

void
nsBoxFrame::InsertFrames(ChildListID     aListID,
                         nsIFrame*       aPrevFrame,
                         nsFrameList&    aFrameList)
{
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
   NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
                "inserting after sibling frame not in our child list");
   NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
   nsBoxLayoutState state(PresContext());

   // insert the child frames
   const nsFrameList::Slice& newFrames =
     mFrames.InsertFrames(this, aPrevFrame, aFrameList);

   // notify the layout manager
   if (mLayoutManager)
     mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);

   // Make sure to check box order _after_ notifying the layout
   // manager; otherwise the slice we give the layout manager will
   // just be bogus.  If the layout manager cares about the order, we
   // just lose.
   CheckBoxOrder();

#ifdef DEBUG_LAYOUT
   // if we are in debug make sure our children are in debug as well.
   if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
       SetDebugOnChildList(state, mFrames.FirstChild(), true);
#endif

   PresContext()->PresShell()->
     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                      NS_FRAME_HAS_DIRTY_CHILDREN);
}


void
nsBoxFrame::AppendFrames(ChildListID     aListID,
                         nsFrameList&    aFrameList)
{
   NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
   nsBoxLayoutState state(PresContext());

   // append the new frames
   const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);

   // notify the layout manager
   if (mLayoutManager)
     mLayoutManager->ChildrenAppended(this, state, newFrames);

   // Make sure to check box order _after_ notifying the layout
   // manager; otherwise the slice we give the layout manager will
   // just be bogus.  If the layout manager cares about the order, we
   // just lose.
   CheckBoxOrder();

#ifdef DEBUG_LAYOUT
   // if we are in debug make sure our children are in debug as well.
   if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
       SetDebugOnChildList(state, mFrames.FirstChild(), true);
#endif

   // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
   }
}

/* virtual */ nsContainerFrame*
nsBoxFrame::GetContentInsertionFrame()
{
  if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
    return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
  return nsContainerFrame::GetContentInsertionFrame();
}

nsresult
nsBoxFrame::AttributeChanged(int32_t aNameSpaceID,
                             nsIAtom* aAttribute,
                             int32_t aModType)
{
  nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                                   aModType);

  // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
  // <window>.
  if (mContent->IsAnyOfXULElements(nsGkAtoms::window,
                                   nsGkAtoms::page,
                                   nsGkAtoms::dialog,
                                   nsGkAtoms::wizard) &&
      (nsGkAtoms::width == aAttribute ||
       nsGkAtoms::height == aAttribute ||
       nsGkAtoms::screenX == aAttribute ||
       nsGkAtoms::screenY == aAttribute ||
       nsGkAtoms::sizemode == aAttribute)) {
    return rv;
  }

  if (aAttribute == nsGkAtoms::width       ||
      aAttribute == nsGkAtoms::height      ||
      aAttribute == nsGkAtoms::align       ||
      aAttribute == nsGkAtoms::valign      ||
      aAttribute == nsGkAtoms::left        ||
      aAttribute == nsGkAtoms::top         ||
      aAttribute == nsGkAtoms::right        ||
      aAttribute == nsGkAtoms::bottom       ||
      aAttribute == nsGkAtoms::start        ||
      aAttribute == nsGkAtoms::end          ||
      aAttribute == nsGkAtoms::minwidth     ||
      aAttribute == nsGkAtoms::maxwidth     ||
      aAttribute == nsGkAtoms::minheight    ||
      aAttribute == nsGkAtoms::maxheight    ||
      aAttribute == nsGkAtoms::flex         ||
      aAttribute == nsGkAtoms::orient       ||
      aAttribute == nsGkAtoms::pack         ||
      aAttribute == nsGkAtoms::dir          ||
      aAttribute == nsGkAtoms::mousethrough ||
      aAttribute == nsGkAtoms::equalsize) {

    if (aAttribute == nsGkAtoms::align  ||
        aAttribute == nsGkAtoms::valign ||
        aAttribute == nsGkAtoms::orient  ||
        aAttribute == nsGkAtoms::pack    ||
#ifdef DEBUG_LAYOUT
        aAttribute == nsGkAtoms::debug   ||
#endif
        aAttribute == nsGkAtoms::dir) {

      mValign = nsBoxFrame::vAlign_Top;
      mHalign = nsBoxFrame::hAlign_Left;

      bool orient = true;
      GetInitialOrientation(orient); 
      if (orient)
        mState |= NS_STATE_IS_HORIZONTAL;
      else
        mState &= ~NS_STATE_IS_HORIZONTAL;

      bool normal = true;
      GetInitialDirection(normal);
      if (normal)
        mState |= NS_STATE_IS_DIRECTION_NORMAL;
      else
        mState &= ~NS_STATE_IS_DIRECTION_NORMAL;

      GetInitialVAlignment(mValign);
      GetInitialHAlignment(mHalign);

      bool equalSize = false;
      GetInitialEqualSize(equalSize); 
      if (equalSize)
        mState |= NS_STATE_EQUAL_SIZE;
      else
        mState &= ~NS_STATE_EQUAL_SIZE;

#ifdef DEBUG_LAYOUT
      bool debug = mState & NS_STATE_SET_TO_DEBUG;
      bool debugSet = GetInitialDebug(debug);
      if (debugSet) {
        mState |= NS_STATE_DEBUG_WAS_SET;

        if (debug)
          mState |= NS_STATE_SET_TO_DEBUG;
        else
          mState &= ~NS_STATE_SET_TO_DEBUG;
      } else {
        mState &= ~NS_STATE_DEBUG_WAS_SET;
      }
#endif

      bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
      GetInitialAutoStretch(autostretch);
      if (autostretch)
        mState |= NS_STATE_AUTO_STRETCH;
      else
        mState &= ~NS_STATE_AUTO_STRETCH;
    }
    else if (aAttribute == nsGkAtoms::left ||
             aAttribute == nsGkAtoms::top ||
             aAttribute == nsGkAtoms::right ||
             aAttribute == nsGkAtoms::bottom ||
             aAttribute == nsGkAtoms::start ||
             aAttribute == nsGkAtoms::end) {
      mState &= ~NS_STATE_STACK_NOT_POSITIONED;
    }
    else if (aAttribute == nsGkAtoms::mousethrough) {
      UpdateMouseThrough();
    }

    PresContext()->PresShell()->
      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  }
  else if (aAttribute == nsGkAtoms::ordinal) {
    nsIFrame* parent = GetParentXULBox(this);
    // If our parent is not a box, there's not much we can do... but in that
    // case our ordinal doesn't matter anyway, so that's ok.
    // Also don't bother with popup frames since they are kept on the 
    // kPopupList and XULRelayoutChildAtOrdinal() only handles
    // principal children.
    if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
        StyleDisplay()->mDisplay != mozilla::StyleDisplay::Popup) {
      parent->XULRelayoutChildAtOrdinal(this);
      // XXXldb Should this instead be a tree change on the child or parent?
      PresContext()->PresShell()->
        FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
                         NS_FRAME_IS_DIRTY);
    }
  }
  // If the accesskey changed, register for the new value
  // The old value has been unregistered in nsXULElement::SetAttr
  else if (aAttribute == nsGkAtoms::accesskey) {
    RegUnregAccessKey(true);
  }
  else if (aAttribute == nsGkAtoms::rows &&
           mContent->IsXULElement(nsGkAtoms::tree)) {
    // Reflow ourselves and all our children if "rows" changes, since
    // nsTreeBodyFrame's layout reads this from its parent (this frame).
    PresContext()->PresShell()->
      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
  }

  return rv;
}

#ifdef DEBUG_LAYOUT
void
nsBoxFrame::GetDebugPref()
{
  gDebug = Preferences::GetBool("xul.debug.box");
}

class nsDisplayXULDebug : public nsDisplayItem {
public:
  nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
    nsDisplayItem(aBuilder, aFrame) {
    MOZ_COUNT_CTOR(nsDisplayXULDebug);
  }
#ifdef NS_BUILD_REFCNT_LOGGING
  virtual ~nsDisplayXULDebug() {
    MOZ_COUNT_DTOR(nsDisplayXULDebug);
  }
#endif

  virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
    nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
    static_cast<nsBoxFrame*>(mFrame)->
      DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame());
    aOutFrames->AppendElement(this);
  }
  virtual void Paint(nsDisplayListBuilder* aBuilder
                     nsRenderingContext* aCtx);
  NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG)
};

void
nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
                         nsRenderingContext* aCtx)
{
  static_cast<nsBoxFrame*>(mFrame)->
    PaintXULDebugOverlay(*aCtx->GetDrawTarget(), ToReferenceFrame());
}

static void
PaintXULDebugBackground(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                        const nsRect& aDirtyRect, nsPoint aPt)
{
  static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(aDrawTarget, aPt);
}
#endif

void
nsBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                             const nsRect&           aDirtyRect,
                             const nsDisplayListSet& aLists)
{
  bool forceLayer = false;

  if (GetContent()->IsXULElement()) {
    // forcelayer is only supported on XUL elements with box layout
    if (GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
      forceLayer = true;
    }
    // Check for frames that are marked as a part of the region used
    // in calculating glass margins on Windows.
    const nsStyleDisplay* styles = StyleDisplay();
    if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) {
      aBuilder->AddWindowExcludeGlassRegion(
          nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
    }
  }

  nsDisplayListCollection tempLists;
  const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;

  DisplayBorderBackgroundOutline(aBuilder, destination);

#ifdef DEBUG_LAYOUT
  if (mState & NS_STATE_CURRENTLY_IN_DEBUG) {
    destination.BorderBackground()->AppendNewToTop(new (aBuilder)
      nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
                       "XULDebugBackground"));
    destination.Outlines()->AppendNewToTop(new (aBuilder)
      nsDisplayXULDebug(aBuilder, this));
  }
#endif

  BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);

  // see if we have to draw a selection frame around this container
  DisplaySelectionOverlay(aBuilder, destination.Content());

  if (forceLayer) {
    // This is a bit of a hack. Collect up all descendant display items
    // and merge them into a single Content() list. This can cause us
    // to violate CSS stacking order, but forceLayer is a magic
    // XUL-only extension anyway.
    nsDisplayList masterList;
    masterList.AppendToTop(tempLists.BorderBackground());
    masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
    masterList.AppendToTop(tempLists.Floats());
    masterList.AppendToTop(tempLists.Content());
    masterList.AppendToTop(tempLists.PositionedDescendants());
    masterList.AppendToTop(tempLists.Outlines());

    // Wrap the list to make it its own layer
    aLists.Content()->AppendNewToTop(new (aBuilder)
      nsDisplayOwnLayer(aBuilder, this, &masterList));
  }
}

void
nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                        const nsRect&           aDirtyRect,
                                        const nsDisplayListSet& aLists)
{
  nsIFrame* kid = mFrames.FirstChild();
  // Put each child's background onto the BlockBorderBackgrounds list
  // to emulate the existing two-layer XUL painting scheme.
  nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
  // The children should be in the right order
  while (kid) {
    BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set);
    kid = kid->GetNextSibling();
  }
}

// REVIEW: PaintChildren did a few things none of which are a big deal
// anymore:
// * Paint some debugging rects for this frame.
// This is done by nsDisplayXULDebugBackground, which goes in the
// BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP.
// * Apply OVERFLOW_CLIP to the children.
// This is now in nsFrame::BuildDisplayListForStackingContext/Child.
// * Actually paint the children.
// Moved to BuildDisplayList.
// * Paint per-kid debug information.
// This is done by nsDisplayXULDebug, which is in the Outlines()
// layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
// whereas it did used to respect OVERFLOW_CLIP, but too bad.
#ifdef DEBUG_LAYOUT
void
nsBoxFrame::PaintXULDebugBackground(DrawTarget* aDrawTarget, nsPoint aPt)
{
  nsMargin border;
  GetXULBorder(border);

  nsMargin debugBorder;
  nsMargin debugMargin;
  nsMargin debugPadding;

  bool isHorizontal = IsXULHorizontal();

  GetDebugBorder(debugBorder);
  PixelMarginToTwips(debugBorder);

  GetDebugMargin(debugMargin);
  PixelMarginToTwips(debugMargin);

  GetDebugPadding(debugPadding);
  PixelMarginToTwips(debugPadding);

  nsRect inner(mRect);
  inner.MoveTo(aPt);
  inner.Deflate(debugMargin);
  inner.Deflate(border);
  //nsRect borderRect(inner);

  int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();

  ColorPattern color(ToDeviceColor(isHorizontal ? Color(0.f, 0.f, 1.f, 1.f) :
                                                  Color(1.f, 0.f, 0.f, 1.f)));

  //left
  nsRect r(inner);
  r.width = debugBorder.left;
  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);

  // top
  r = inner;
  r.height = debugBorder.top;
  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);

  //right
  r = inner;
  r.x = r.x + r.width - debugBorder.right;
  r.width = debugBorder.right;
  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);

  //bottom
  r = inner;
  r.y = r.y + r.height - debugBorder.bottom;
  r.height = debugBorder.bottom;
  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);

  // If we have dirty children or we are dirty place a green border around us.
  if (NS_SUBTREE_DIRTY(this)) {
    nsRect dirty(inner);
    ColorPattern green(ToDeviceColor(Color(0.f, 1.f, 0.f, 1.f)));
    aDrawTarget->StrokeRect(NSRectToRect(dirty, appUnitsPerDevPixel), green);
  }
}

void
nsBoxFrame::PaintXULDebugOverlay(DrawTarget& aDrawTarget, nsPoint aPt)
{
  nsMargin border;
  GetXULBorder(border);

  nsMargin debugMargin;
  GetDebugMargin(debugMargin);
  PixelMarginToTwips(debugMargin);

  nsRect inner(mRect);
  inner.MoveTo(aPt);
  inner.Deflate(debugMargin);
  inner.Deflate(border);

  nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);

  kid = nsBox::GetChildXULBox(this);
  while (nullptr != kid) {
    bool isHorizontal = IsXULHorizontal();

    nscoord x, y, borderSize, spacerSize;
    
    nsRect cr(kid->mRect);
    nsMargin margin;
    kid->GetXULMargin(margin);
    cr.Inflate(margin);
    
    if (isHorizontal) 
    {
        cr.y = inner.y;
        x = cr.x;
        y = cr.y + onePixel;
        spacerSize = debugBorder.top - onePixel*4;
    } else {
        cr.x = inner.x;
        x = cr.y;
        y = cr.x + onePixel;
        spacerSize = debugBorder.left - onePixel*4;
    }

    nscoord flex = kid->GetXULFlex();

    if (!kid->IsXULCollapsed()) {
      if (isHorizontal) 
          borderSize = cr.width;
      else 
          borderSize = cr.height;

      DrawSpacer(GetPresContext(), aDrawTarget, isHorizontal, flex, x, y, borderSize, spacerSize);
    }

    kid = GetNextXULBox(kid);
  }
}
#endif

#ifdef DEBUG_LAYOUT
void
nsBoxFrame::GetBoxName(nsAutoString& aName)
{
   GetFrameName(aName);
}
#endif

#ifdef DEBUG_FRAME_DUMP
nsresult
nsBoxFrame::GetFrameName(nsAString& aResult) const
{
  return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
}
#endif

nsIAtom*
nsBoxFrame::GetType() const
{
  return nsGkAtoms::boxFrame;
}

#ifdef DEBUG_LAYOUT
nsresult
nsBoxFrame::GetXULDebug(bool& aDebug)
{
  aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG);
  return NS_OK;
}
#endif

// REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough'
// attribute support. Here's how it works:
// * For each child frame F, we determine the target frame T(F) by recursively
// invoking GetFrameForPoint on the child
// * Let F' be the last child frame such that T(F') doesn't have mousethrough.
// If F' exists, return T(F')
// * Otherwise let F'' be the first child frame such that T(F'') is non-null.
// If F'' exists, return T(F'')
// * Otherwise return this frame, if this frame contains the point
// * Otherwise return null
// It's not clear how this should work for more complex z-ordering situations.
// The basic principle seems to be that if a frame F has a descendant
// 'mousethrough' frame that includes the target position, then F
// will not receive events (unless it overrides GetFrameForPoint).
// A 'mousethrough' frame will only receive an event if, after applying that rule,
// all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough'
// frame is then chosen (the first eligible frame reached in a
// traversal of the frame tree --- pre/post is irrelevant since ancestors
// of the mousethrough frames can't be eligible).
// IMHO this is very bogus and adds a great deal of complexity for something
// that is very rarely used. So I'm redefining 'mousethrough' to the following:
// a frame with mousethrough is transparent to mouse events. This is compatible
// with the way 'mousethrough' is used in Seamonkey's navigator.xul and
// Firefox's browser.xul. The only other place it's used is in the 'expander'
// XBL binding, which in our tree is only used by Thunderbird SMIME Advanced
// Preferences, and I can't figure out what that does, so I'll have to test it.
// If it's broken I'll probably just change the binding to use it more sensibly.
// This new behaviour is implemented in nsDisplayList::HitTest. 
// REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes
// in the outline layer and avoid GetDebugBoxAt.

// REVIEW: GetCursor had debug-only event dumping code. I have replaced it
// with instrumentation in nsDisplayXULDebug.

#ifdef DEBUG_LAYOUT
void
nsBoxFrame::DrawLine(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2)
{
    nsPoint p1(x1, y1);
    nsPoint p2(x2, y2);
    if (!aHorizontal) {
      Swap(p1.x, p1.y);
      Swap(p2.x, p2.y);
    }
    ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
    StrokeLineWithSnapping(p1, p2, PresContext()->AppUnitsPerDevPixel(),
                           aDrawTarget, color);
}

void
nsBoxFrame::FillRect(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height)
{
    Rect rect = NSRectToSnappedRect(aHorizontal ? nsRect(x, y, width, height) :
                                                  nsRect(y, x, height, width),
                                    PresContext()->AppUnitsPerDevPixel(),
                                    aDrawTarget);
    ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
    aDrawTarget.FillRect(rect, white);
}

void 
nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, DrawTarget& aDrawTarget,
                       bool aHorizontal, int32_t flex, nscoord x, nscoord y,
                       nscoord size, nscoord spacerSize)
{    
         nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);

     // if we do draw the coils
        int distance = 0;
        int center = 0;
        int offset = 0;
        int coilSize = COIL_SIZE*onePixel;
        int halfSpacer = spacerSize/2;

        distance = size;
        center = y + halfSpacer;
        offset = x;

        int coils = distance/coilSize;

        int halfCoilSize = coilSize/2;

        if (flex == 0) {
            DrawLine(aDrawTarget, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2);
        } else {
            for (int i=0; i < coils; i++)
            {
                   DrawLine(aDrawTarget, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer);
                   DrawLine(aDrawTarget, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer);

                   offset += coilSize;
            }
        }

        FillRect(aDrawTarget, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize);
        FillRect(aDrawTarget, aHorizontal, x, y, spacerSize/2, spacerSize);
}

void
nsBoxFrame::GetDebugBorder(nsMargin& aInset)
{
    aInset.SizeTo(2,2,2,2);

    if (IsXULHorizontal()) 
       aInset.top = 10;
    else 
       aInset.left = 10;
}

void
nsBoxFrame::GetDebugMargin(nsMargin& aInset)
{
    aInset.SizeTo(2,2,2,2);
}

void
nsBoxFrame::GetDebugPadding(nsMargin& aPadding)
{
    aPadding.SizeTo(2,2,2,2);
}

void 
nsBoxFrame::PixelMarginToTwips(nsMargin& aMarginPixels)
{
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  aMarginPixels.left   *= onePixel;
  aMarginPixels.right  *= onePixel;
  aMarginPixels.top    *= onePixel;
  aMarginPixels.bottom *= onePixel;
}

void
nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch) 
{
    float p2t = aPresContext->ScaledPixelsToTwips();

    char width[100];
    char height[100];
    
    if (a.width == NS_INTRINSICSIZE)
        sprintf(width,"%s","INF");
    else
        sprintf(width,"%d", nscoord(a.width/*/p2t*/));
    
    if (a.height == NS_INTRINSICSIZE)
        sprintf(height,"%s","INF");
    else 
        sprintf(height,"%d", nscoord(a.height/*/p2t*/));
    

    sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""),
                    height, (b.height != NS_INTRINSICSIZE ? "[SET]" : ""));

}

void
nsBoxFrame::GetValue(nsPresContext* aPresContext, int32_t a, int32_t b, char* ch) 
{
    if (a == NS_INTRINSICSIZE)
      sprintf(ch, "%d[SET]", b);             
    else
      sprintf(ch, "%d", a);             
}

nsresult
nsBoxFrame::DisplayDebugInfoFor(nsIFrame*  aBox,
                                nsPoint& aPoint)
{
    nsBoxLayoutState state(GetPresContext());

    nscoord x = aPoint.x;
    nscoord y = aPoint.y;

    // get the area inside our border but not our debug margins.
    nsRect insideBorder(aBox->mRect);
    insideBorder.MoveTo(0,0):
    nsMargin border(0,0,0,0);
    aBox->GetXULBorderAndPadding(border);
    insideBorder.Deflate(border);

    bool isHorizontal = IsXULHorizontal();

    if (!insideBorder.Contains(nsPoint(x,y)))
        return NS_ERROR_FAILURE;

    //printf("%%%%%% inside box %%%%%%%\n");

    int count = 0;
    nsIFrame* child = nsBox::GetChildXULBox(aBox);

    nsMargin m;
    nsMargin m2;
    GetDebugBorder(m);
    PixelMarginToTwips(m);

    GetDebugMargin(m2);
    PixelMarginToTwips(m2);

    m += m2;

    if ((isHorizontal && y < insideBorder.y + m.top) ||
        (!isHorizontal && x < insideBorder.x + m.left)) {
        //printf("**** inside debug border *******\n");
        while (child) 
        {
            const nsRect& r = child->mRect;

            // if we are not in the child. But in the spacer above the child.
            if ((isHorizontal && x >= r.x && x < r.x + r.width) ||
                (!isHorizontal && y >= r.y && y < r.y + r.height)) {
                aCursor = NS_STYLE_CURSOR_POINTER;
                   // found it but we already showed it.
                    if (mDebugChild == child)
                        return NS_OK;

                    if (aBox->GetContent()) {
                      printf("---------------\n");
                      XULDumpBox(stdout);
                      printf("\n");
                    }

                    if (child->GetContent()) {
                        printf("child #%d: ", count);
                        child->XULDumpBox(stdout);
                        printf("\n");
                    }

                    mDebugChild = child;

                    nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
                    nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
                    nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
                    nscoord flexCSS = NS_INTRINSICSIZE;

                    bool widthSet, heightSet;
                    nsIFrame::AddXULPrefSize(child, prefSizeCSS, widthSet, heightSet);
                    nsIFrame::AddXULMinSize (state, child, minSizeCSS, widthSet, heightSet);
                    nsIFrame::AddXULMaxSize (child, maxSizeCSS, widthSet, heightSet);
                    nsIFrame::AddXULFlex    (child, flexCSS);

                    nsSize prefSize = child->GetXULPrefSize(state);
                    nsSize minSize = child->GetXULMinSize(state);
                    nsSize maxSize = child->GetXULMaxSize(state);
                    nscoord flexSize = child->GetXULFlex();
                    nscoord ascentSize = child->GetXULBoxAscent(state);

                    char min[100];
                    char pref[100];
                    char max[100];
                    char calc[100];
                    char flex[100];
                    char ascent[100];
                  
                    nsSize actualSize;
                    GetFrameSizeWithMargin(child, actualSize);
                    nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);

                    GetValue(aPresContext, minSize,  minSizeCSS, min);
                    GetValue(aPresContext, prefSize, prefSizeCSS, pref);
                    GetValue(aPresContext, maxSize,  maxSizeCSS, max);
                    GetValue(aPresContext, actualSize, actualSizeCSS, calc);
                    GetValue(aPresContext, flexSize,  flexCSS, flex);
                    GetValue(aPresContext, ascentSize,  NS_INTRINSICSIZE, ascent);


                    printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n", 
                        min,
                        pref,
                        max,
                        calc,
                        flex,
                        ascent
                    );

                    return NS_OK;   
            }

          child = GetNextXULBox(child);
          count++;
        }
    } else {
    }

    mDebugChild = nullptr;

    return NS_OK;
}

void
nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIFrame* aChild, bool aDebug)
{
    nsIFrame* child = nsBox::GetChildXULBox(this);
     while (child)
     {
        child->SetXULDebug(aState, aDebug);
        child = GetNextXULBox(child);
     }
}

nsresult
nsBoxFrame::GetFrameSizeWithMargin(nsIFrame* aBox, nsSize& aSize)
{
  nsRect rect(aBox->GetRect());
  nsMargin margin(0,0,0,0);
  aBox->GetXULMargin(margin);
  rect.Inflate(margin);
  aSize.width = rect.width;
  aSize.height = rect.height;
  return NS_OK;
}
#endif

// If you make changes to this function, check its counterparts
// in nsTextBoxFrame and nsXULLabelFrame
void
nsBoxFrame::RegUnregAccessKey(bool aDoReg)
{
  MOZ_ASSERT(mContent);

  // only support accesskeys for the following elements
  if (!mContent->IsAnyOfXULElements(nsGkAtoms::button,
                                    nsGkAtoms::toolbarbutton,
                                    nsGkAtoms::checkbox,
                                    nsGkAtoms::textbox,
                                    nsGkAtoms::tab,
                                    nsGkAtoms::radio)) {
    return;
  }

  nsAutoString accessKey;
  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);

  if (accessKey.IsEmpty())
    return;

  // With a valid PresContext we can get the ESM 
  // and register the access key
  EventStateManager* esm = PresContext()->EventStateManager();

  uint32_t key = accessKey.First();
  if (aDoReg)
    esm->RegisterAccessKey(mContent, key);
  else
    esm->UnregisterAccessKey(mContent, key);
}

bool
nsBoxFrame::SupportsOrdinalsInChildren()
{
  return true;
}

// Helper less-than-or-equal function, used in CheckBoxOrder() as a
// template-parameter for the sorting functions.
bool
IsBoxOrdinalLEQ(nsIFrame* aFrame1,
                nsIFrame* aFrame2)
{
  // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
  nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
  nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
  return aRealFrame1->GetXULOrdinal() <= aRealFrame2->GetXULOrdinal();
}

void 
nsBoxFrame::CheckBoxOrder()
{
  if (SupportsOrdinalsInChildren() &&
      !nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) {
    nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames);
  }
}

nsresult
nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect)
{
  // get the current rect
  nsRect oldRect(aBox->GetRect());
  aBox->SetXULBounds(aState, aRect);

  bool layout = NS_SUBTREE_DIRTY(aBox);
  
  if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height))  {
    return aBox->XULLayout(aState);
  }

  return NS_OK;
}

nsresult
nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame* aChild)
{
  if (!SupportsOrdinalsInChildren())
    return NS_OK;

  uint32_t ord = aChild->GetXULOrdinal();
  
  nsIFrame* child = mFrames.FirstChild();
  nsIFrame* newPrevSib = nullptr;

  while (child) {
    if (ord < child->GetXULOrdinal()) {
      break;
    }

    if (child != aChild) {
      newPrevSib = child;
    }

    child = GetNextXULBox(child);
  }

  if (aChild->GetPrevSibling() == newPrevSib) {
    // This box is not moving.
    return NS_OK;
  }

  // Take |aChild| out of its old position in the child list.
  mFrames.RemoveFrame(aChild);

  // Insert it after |newPrevSib| or at the start if it's null.
  mFrames.InsertFrame(nullptr, newPrevSib, aChild);

  return NS_OK;
}

/**
 * This wrapper class lets us redirect mouse hits from descendant frames
 * of a menu to the menu itself, if they didn't specify 'allowevents'.
 * 
 * The wrapper simply turns a hit on a descendant element
 * into a hit on the menu itself, unless there is an element between the target
 * and the menu with the "allowevents" attribute.
 * 
 * This is used by nsMenuFrame and nsTreeColFrame.
 * 
 * Note that turning a hit on a descendant element into nullptr, so events
 * could fall through to the menu background, might be an appealing simplification
 * but it would mean slightly strange behaviour in some cases, because grabber
 * wrappers can be created for many individual lists and items, so the exact
 * fallthrough behaviour would be complex. E.g. an element with "allowevents"
 * on top of the Content() list could receive the event even if it was covered
 * by a PositionedDescenants() element without "allowevents". It is best to
 * never convert a non-null hit into null.
 */
// REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
// I've made 'allowevents' affect child elements because that seems the only
// reasonable thing to do.
class nsDisplayXULEventRedirector : public nsDisplayWrapList {
public:
  nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayItem* aItem,
                              nsIFrame* aTargetFrame)
    : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
  nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayList* aList,
                              nsIFrame* aTargetFrame)
    : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState,
                       nsTArray<nsIFrame*> *aOutFrames) override;
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
    return false;
  }
  NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
private:
  nsIFrame* mTargetFrame;
};

void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
    const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
{
  nsTArray<nsIFrame*> outFrames;
  mList.HitTest(aBuilder, aRect, aState, &outFrames);

  bool topMostAdded = false;
  uint32_t localLength = outFrames.Length();

  for (uint32_t i = 0; i < localLength; i++) {

    for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
         content && content != mTargetFrame->GetContent();
         content = content->GetParent()) {
      if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
                               nsGkAtoms::_true, eCaseMatters)) {
        // Events are allowed on 'frame', so let it go.
        aOutFrames->AppendElement(outFrames.ElementAt(i));
        topMostAdded = true;
      }
    }

    // If there was no hit on the topmost frame or its ancestors,
    // add the target frame itself as the first candidate (see bug 562554).
    if (!topMostAdded) {
      topMostAdded = true;
      aOutFrames->AppendElement(mTargetFrame);
    }
  }
}

class nsXULEventRedirectorWrapper : public nsDisplayWrapper
{
public:
  explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
      : mTargetFrame(aTargetFrame) {}
  virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame,
                                  nsDisplayList* aList) override {
    return new (aBuilder)
        nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame);
  }
  virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
                                  nsDisplayItem* aItem) override {
    return new (aBuilder)
        nsDisplayXULEventRedirector(aBuilder, aItem->Frame(), aItem,
                                    mTargetFrame);
  }
private:
  nsIFrame* mTargetFrame;
};

void
nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder*   aBuilder,
                                  const nsDisplayListSet& aIn,
                                  const nsDisplayListSet& aOut)
{
  nsXULEventRedirectorWrapper wrapper(this);
  wrapper.WrapLists(aBuilder, this, aIn, aOut);
}

bool
nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) {
  LayoutDeviceIntPoint refPoint;
  bool res = GetEventPoint(aEvent, refPoint);
  aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
    aEvent, refPoint, this);
  return res;
}

bool
nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) {
  NS_ENSURE_TRUE(aEvent, false);

  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  if (touchEvent) {
    // return false if there is more than one touch on the page, or if
    // we can't find a touch point
    if (touchEvent->mTouches.Length() != 1) {
      return false;
    }

    dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
    if (!touch) {
      return false;
    }
    aPoint = touch->mRefPoint;
  } else {
    aPoint = aEvent->mRefPoint;
  }
  return true;
}
