/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Mozilla MathML Project.
 *
 * The Initial Developer of the Original Code is The University Of
 * Queensland.  Portions created by The University Of Queensland are
 * Copyright (C) 1999 The University Of Queensland.  All Rights Reserved.
 *
 * Contributor(s):
 *   Roger B. Sidje <rbs@maths.uq.edu.au>
 *   David J. Fiddes <D.J.Fiddes@hw.ac.uk>
 */

#include "nsCOMPtr.h"
#include "nsHTMLParts.h"
#include "nsIHTMLContent.h"
#include "nsFrame.h"
#include "nsLineLayout.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsHTMLAtoms.h"
#include "nsUnitConversion.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsINameSpaceManager.h"
#include "nsIRenderingContext.h"
#include "nsIFontMetrics.h"
#include "nsStyleUtil.h"

#include "nsITextContent.h"

#include "nsMathMLAtoms.h"
#include "nsMathMLParts.h"
#include "nsMathMLChar.h"
#include "nsMathMLContainerFrame.h"

//
// nsMathMLContainerFrame implementation
//

// TODO: Proper management of ignorable whitespace 
//       and handling of non-math related frames.
// * Should math markups only enclose other math markups?
//   Why bother... we can leave them in, as hinted in the newsgroup.
//
// * Space doesn't count. Handling space is more complicated than it seems. 
//   We have to clean inside and outside:
//       <mi> a </mi> <mo> + </mo> <mi> b </mi>
//           ^ ^     ^    ^ ^     ^    ^ ^
//   *Outside* currently handled using IsOnlyWhitespace().
//   For whitespace only frames, their rect is set zero.
//
//   *Inside* not currently handled... so the user must do <mi>a</mi>
//   What to do?!
//   - Add a nsTextFrame::CompressWhitespace() *if* it is MathML content?!
//   - via CSS property? Support for "none/compress"? (white-space: normal | pre | nowrap)


// nsISupports
// =============================================================================

NS_IMETHODIMP
nsMathMLContainerFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }
  *aInstancePtr = nsnull;
  if (aIID.Equals(kIMathMLFrameIID)) {
    *aInstancePtr = (void*)(nsIMathMLFrame*)this;
    NS_ADDREF_THIS(); // not effectual, frames are not Refcounted 
    return NS_OK;
  }
  return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}

NS_IMETHODIMP_(nsrefcnt) 
nsMathMLContainerFrame::AddRef(void)
{
  return NS_OK;
}

NS_IMETHODIMP_(nsrefcnt) 
nsMathMLContainerFrame::Release(void)
{
  return NS_OK;
}

/* ///////////////
 * MathML specific - Whitespace management ...
 * WHITESPACE: don't forget that whitespace doesn't count in MathML!
 * empty frames shouldn't be created in MathML, oh well that's another story.
 * =============================================================================
 */

PRBool
nsMathMLContainerFrame::IsOnlyWhitespace(nsIFrame* aFrame)
{
  NS_PRECONDITION(aFrame, "null arg");
  PRBool rv = PR_FALSE;

  // by empty frame we mean a leaf frame whose text content is empty...
  nsCOMPtr<nsIContent> aContent;
  aFrame->GetContent(getter_AddRefs(aContent));
  if (!aContent) return PR_TRUE;
  PRInt32 numKids;
  aContent->ChildCount(numKids);
  if (0 == numKids) {
    nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
    if (tc.get()) tc->IsOnlyWhitespace(&rv);
  }
  return rv;
}

void
nsMathMLContainerFrame::ReflowEmptyChild(nsIPresContext* aPresContext,
                                         nsIFrame*       aFrame)
{
//  nsHTMLReflowMetrics emptySize(nsnull);
//  nsHTMLReflowState emptyReflowState(aPresContext, aReflowState, aFrame, nsSize(0,0));
//  nsresult rv = ReflowChild(aFrame, aPresContext, emptySize, emptyReflowState, aStatus);
 
  // 0-size the frame
  aFrame->SetRect(aPresContext, nsRect(0,0,0,0));

  // 0-size the view, if any
  nsIView* view = nsnull;
  aFrame->GetView(aPresContext, &view);
  if (view) {
    nsCOMPtr<nsIViewManager> vm;
    view->GetViewManager(*getter_AddRefs(vm));
    vm->ResizeView(view, 0,0);
  }
}

/* /////////////
 * nsIMathMLFrame - support methods for precise positioning 
 * =============================================================================
 */

NS_IMETHODIMP
nsMathMLContainerFrame::GetBoundingMetrics(nsBoundingMetrics& aBoundingMetrics)
{
  aBoundingMetrics = mBoundingMetrics;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::SetBoundingMetrics(const nsBoundingMetrics& aBoundingMetrics)
{
  mBoundingMetrics = aBoundingMetrics;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::GetReference(nsPoint& aReference)
{
  aReference = mReference;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::SetReference(const nsPoint& aReference)
{
  mReference = aReference;
  return NS_OK;
}

// helper method to facilitate getting the reflow and bounding metrics
void
nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
                                                       nsHTMLReflowMetrics& aReflowMetrics,
                                                       nsBoundingMetrics&   aBoundingMetrics)
{
  NS_PRECONDITION(aFrame, "null arg");

  // IMPORTANT: This function is only meant to be called in Place() methods 
  // where it is assumed that the frame's rect is still acting as place holder
  // for the frame's ascent and descent information
  
  nsRect aRect;
  aFrame->GetRect(aRect);
  aReflowMetrics.descent = aRect.x; 
  aReflowMetrics.ascent  = aRect.y;
  aReflowMetrics.width   = aRect.width; 
  aReflowMetrics.height  = aRect.height;
	
  aBoundingMetrics.Clear();
  nsIMathMLFrame* aMathMLFrame = nsnull;
  nsresult rv = aFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
  if (NS_SUCCEEDED(rv) && aMathMLFrame) {
    aMathMLFrame->GetBoundingMetrics(aBoundingMetrics);
#if 0
    nsFrame::ListTag(stdout, aFrame);
    printf(" subItalicCorrection:%d supItalicCorrection:%d\n",
    aBoundingMetrics.subItalicCorrection, aBoundingMetrics.supItalicCorrection);
#endif
  }
  else { // aFrame is not a MathML frame, just return the reflow metrics
    aBoundingMetrics.descent = aReflowMetrics.descent;
    aBoundingMetrics.ascent  = aReflowMetrics.ascent;
    aBoundingMetrics.width   = aReflowMetrics.width;
#if 0
    printf("GetBoundingMetrics() failed for: "); /* getchar(); */
    nsFrame::ListTag(stdout, aFrame);
    printf("\n");
#endif
  }
}

/* /////////////
 * nsIMathMLFrame - support methods for stretchy elements
 * =============================================================================
 */

NS_IMETHODIMP
nsMathMLContainerFrame::Stretch(nsIPresContext*      aPresContext,
                                nsIRenderingContext& aRenderingContext,
                                nsStretchDirection   aStretchDirection,
                                nsStretchMetrics&    aContainerSize,
                                nsStretchMetrics&    aDesiredStretchSize)
{
  nsresult rv = NS_OK;
  if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {

    if (NS_MATHML_STRETCH_WAS_DONE(mEmbellishData.flags)) {
      printf("WARNING *** it is wrong to fire stretch more than once on a frame...\n");
//      NS_ASSERTION(PR_FALSE,"Stretch() was fired more than once on a frame!");
      return NS_OK;
    }
    mEmbellishData.flags |= NS_MATHML_STRETCH_DONE;


    // Pass the stretch to the first non-empty child ...

    nsIFrame* childFrame = mEmbellishData.firstChild;
    NS_ASSERTION(childFrame, "Something is wrong somewhere");

    if (childFrame) {
      nsIMathMLFrame* aMathMLFrame = nsnull;
      rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
      NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Something is wrong somewhere");
      if (NS_SUCCEEDED(rv) && aMathMLFrame) {
        nsHTMLReflowMetrics aReflowMetrics(nsnull);

        nsRect rect;
        childFrame->GetRect(rect);
        // And the trick is that rect.x is still holding the descent, and rect.y 
        // is still holding the ascent ...
        nsStretchMetrics childSize(rect.x, rect.y, rect.width, rect.height);
        nsStretchMetrics container(aContainerSize);

        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT && aStretchDirection != mEmbellishData.direction) {
          // change the direction and confine the stretch to us
#if 0
          GetDesiredStretchSize(aPresContext, aRenderingContext, container);
#endif
          GetRect(rect);
          container = nsStretchMetrics(rect.x, rect.y, rect.width, rect.height);
        }

        aMathMLFrame->Stretch(aPresContext, aRenderingContext,
                              mEmbellishData.direction, container, childSize);
        childFrame->SetRect(aPresContext,
                            nsRect(childSize.descent, childSize.ascent, 
                                   childSize.width, childSize.height));

        // We now have one child that may have changed, re-position all our children
        Place(aPresContext, aRenderingContext, PR_TRUE, aReflowMetrics);

        // Prepare the metrics to be returned
        aDesiredStretchSize = nsStretchMetrics(aReflowMetrics);
        aDesiredStretchSize.leftSpace = childSize.leftSpace;
        aDesiredStretchSize.rightSpace = childSize.rightSpace;

        // If our parent is not embellished, it means we are the outermost embellished
        // container and so we put the spacing, otherwise we don't include the spacing,
        // the outermost embellished container will take care of it.

        if (!IsEmbellishOperator(mParent)) {

          nsStyleFont font;
          mStyleContext->GetStyle(eStyleStruct_Font, font);
          nscoord em = NSToCoordRound(float(font.mFont.size));

          // cache these values
          mEmbellishData.leftSpace = nscoord( em * aDesiredStretchSize.leftSpace );
          mEmbellishData.rightSpace = nscoord( em * aDesiredStretchSize.rightSpace );

          aDesiredStretchSize.width += nscoord( (aDesiredStretchSize.leftSpace + aDesiredStretchSize.rightSpace) * em );
          nscoord dx = nscoord( aDesiredStretchSize.leftSpace * em );        
          if (0 == dx) return NS_OK;
    
          nsPoint origin;
          childFrame = mFrames.FirstChild();
          while (childFrame) {
            childFrame->GetOrigin(origin);
            childFrame->MoveTo(aPresContext, origin.x + dx, origin.y);
            childFrame->GetNextSibling(&childFrame);
          }
        }
      }
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::FinalizeReflow(PRInt32              aDirection,
                                       nsIPresContext*      aPresContext,
                                       nsIRenderingContext& aRenderingContext,
                                       nsHTMLReflowMetrics& aDesiredSize)
{
  // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
  // and descent in expectation of a stretch command. Hence we need to ensure that
  // a stretch command will actually be fired later on, after exiting from our
  // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
  // with inappropriate data causing children to be improperly positioned.
  // This helper method checks to see if our parent will fire a stretch command
  // targeted at us. If not, we go ahead and fire an involutive stretch on 
  // ourselves. This will clear all the rect.x and rect.y, and return our
  // desired size.


  // First, complete the post-reflow hook.
  // We use the information in our children rectangles to position them.
  // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
  // They will still be holding the ascent and descent for each child.
  PRBool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags);
  Place(aPresContext, aRenderingContext, placeOrigin, aDesiredSize);

  if (!placeOrigin) {
    // This means the rect.x and rect.y of our children were not set!! 
    // Don't go without checking to see if our parent will later fire a Stretch() command
    // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
    PRBool parentWillFireStretch = PR_FALSE;
    nsEmbellishData parentData;
    nsIMathMLFrame* aMathMLFrame = nsnull;
    nsresult rv = mParent->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
    if (NS_SUCCEEDED(rv) && aMathMLFrame) {
      aMathMLFrame->GetEmbellishData(parentData);
      if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(parentData.flags) ||
         (NS_MATHML_WILL_STRETCH_FIRST_CHILD(parentData.flags) && 
          parentData.firstChild == this)) {
        parentWillFireStretch = PR_TRUE;
      }
    }
    if (!parentWillFireStretch) {
      // There is nobody who will fire the stretch for us, we do it ourselves!

       // BEGIN of GETTING THE STRETCH SIZE
       // What is the size that we should use to stretch our stretchy children ????

// 1) With this code, vertical stretching works. But horizontal stretching 
// does not work when the firstChild happens to be the core embellished mo...
//      nsRect rect;
//      nsIFrame* childFrame = mEmbellishData.firstChild;
//      NS_ASSERTION(childFrame, "Something is wrong somewhere");
//      childFrame->GetRect(rect);
//      nsStretchMetrics curSize(rect.x, rect.y, rect.width, rect.height);


// 2) With this code, horizontal stretching works. But vertical stretching
// is done in some cases where frames could have simply been kept as is.
      nsStretchMetrics curSize(aDesiredSize);


// 3) With this code, we should get appropriate size when it is done !!
//      GetDesiredSize(aDirection, aPresContext, aRenderingContext, curSize);

      // XXX It is not clear if a direction should be imposed. 
      // With the default direction, the MathMLChar will attempt to stretch 
      // in its preferred direction.

      nsStretchMetrics newSize(curSize);
      Stretch(aPresContext, aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, 
              curSize, newSize);

      aDesiredSize.width = newSize.width; 
      aDesiredSize.height = newSize.height;
      aDesiredSize.ascent = newSize.ascent; 
      aDesiredSize.descent = newSize.descent;
    }
  }
  if (nsnull != aDesiredSize.maxElementSize) {
    aDesiredSize.maxElementSize->width = aDesiredSize.width;
    aDesiredSize.maxElementSize->height = aDesiredSize.height;
  }
  // Also return our bounding metrics
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  return NS_OK;
}

// This is the method used to set the frame as an embellished container.
// It checks if the first (non-empty) child is embellished. Hence, calls 
// must be bottom-up. The method must only be called from within frames who are 
// entitled to be potential embellished operators as per the MathML REC. 
NS_IMETHODIMP
nsMathMLContainerFrame::EmbellishOperator()
{
  // Get the first non-empty child
  nsIFrame* firstChild = mFrames.FirstChild();
  while (firstChild) {
    if (!IsOnlyWhitespace(firstChild)) break;
    firstChild->GetNextSibling(&firstChild);
  }
  if (firstChild && IsEmbellishOperator(firstChild)) {
    // Cache the first child
    mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
    mEmbellishData.firstChild = firstChild;
    // Cache also the inner-most embellished frame at the core of the hierarchy
    nsIMathMLFrame* aMathMLFrame = nsnull;
    nsresult rv = firstChild->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
    NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
    nsEmbellishData embellishData;
    aMathMLFrame->GetEmbellishData(embellishData);
    mEmbellishData.core = embellishData.core;
    mEmbellishData.direction = embellishData.direction;
  }
  else {
    mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_OPERATOR;
    mEmbellishData.firstChild = nsnull;
    mEmbellishData.core = nsnull;
    mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::GetEmbellishData(nsEmbellishData& aEmbellishData)
{
  aEmbellishData = mEmbellishData;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::SetEmbellishData(const nsEmbellishData& aEmbellishData)
{
  mEmbellishData = aEmbellishData;
  return NS_OK;
}

PRBool
nsMathMLContainerFrame::IsEmbellishOperator(nsIFrame* aFrame)
{
  NS_PRECONDITION(aFrame, "null arg");
  if (!aFrame) return PR_FALSE;
  nsIMathMLFrame* aMathMLFrame = nsnull;
  nsresult rv = aFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
  if (NS_FAILED(rv) || !aMathMLFrame) return PR_FALSE;
  nsEmbellishData aEmbellishData;
  aMathMLFrame->GetEmbellishData(aEmbellishData);
  return NS_MATHML_IS_EMBELLISH_OPERATOR(aEmbellishData.flags);
}

/* /////////////
 * nsIMathMLFrame - support methods for scripting elements (nested frames
 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts, 
 * mfrac, mroot, mtable).
 * =============================================================================
 */

NS_IMETHODIMP
nsMathMLContainerFrame::GetPresentationData(nsPresentationData& aPresentationData)
{
  aPresentationData = mPresentationData;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::SetPresentationData(const nsPresentationData& aPresentationData)
{
  mPresentationData = aPresentationData;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::UpdatePresentationData(PRInt32 aScriptLevelIncrement, 
                                               PRBool  aDisplayStyle)
{
 mPresentationData.scriptLevel += aScriptLevelIncrement;
 if (aDisplayStyle)
   mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
 else
   mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::UpdatePresentationDataFromChildAt(PRInt32 aIndex, 
                                                          PRInt32 aScriptLevelIncrement,
                                                          PRBool  aDisplayStyle)
{
  nsIFrame* childFrame = mFrames.FirstChild();
  while (nsnull != childFrame) {
    if (!IsOnlyWhitespace(childFrame)) {
      if (0 >= aIndex--) {
        nsIMathMLFrame* aMathMLFrame = nsnull;
        nsresult rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
        if (NS_SUCCEEDED(rv) && nsnull != aMathMLFrame) {
          // update
      	  aMathMLFrame->UpdatePresentationData(aScriptLevelIncrement, aDisplayStyle);
          // propagate down the subtrees
          aMathMLFrame->UpdatePresentationDataFromChildAt(0, aScriptLevelIncrement, aDisplayStyle);
      	}
      }
    }
    childFrame->GetNextSibling(&childFrame);
  }
  return NS_OK;
}

PRInt32
nsMathMLContainerFrame::FindSmallestFontSizeFor(nsIFrame* aFrame)
{
  nsStyleFont aFont;
  nsCOMPtr<nsIStyleContext> aStyleContext;
  aFrame->GetStyleContext(getter_AddRefs(aStyleContext));
  aStyleContext->GetStyle(eStyleStruct_Font, aFont);
//  PRInt32 fontSize = NSTwipsToFloorIntPoints(aFont.mFont.size);
  PRInt32 fontSize = aFont.mFont.size;

  PRInt32 childSize;
  nsIFrame* childFrame;
  aFrame->FirstChild(nsnull, &childFrame);
  while (nsnull != childFrame) {
    if (!IsOnlyWhitespace(childFrame)) {
      childSize = FindSmallestFontSizeFor(childFrame);
      if (fontSize > childSize) fontSize = childSize;
    }
    childFrame->GetNextSibling(&childFrame);
  }
  return fontSize;
}

// helper method to alter the style context
// This method is used for switching the font to a subscript/superscript font in
// mfrac, msub, msup, msubsup, munder, mover, munderover, mmultiscripts 

NS_IMETHODIMP
nsMathMLContainerFrame::InsertScriptLevelStyleContext(nsIPresContext* aPresContext)
{
  nsresult rv = NS_OK;
  nsIFrame* nextFrame = mFrames.FirstChild();
  while (nsnull != nextFrame && NS_SUCCEEDED(rv)) { 	
    nsIFrame* childFrame = nextFrame;
    rv = nextFrame->GetNextSibling(&nextFrame);
    if (!IsOnlyWhitespace(childFrame) && NS_SUCCEEDED(rv)) {

      // see if the child frame implements the nsIMathMLFrame interface
      nsIMathMLFrame* aMathMLFrame = nsnull;
      rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
      if (nsnull != aMathMLFrame && NS_SUCCEEDED(rv)) {

        // get the scriptlevel of the child
        nsPresentationData childData;
        aMathMLFrame->GetPresentationData(childData);

        // Iteration to set a style context for the script level font.
        // Wow, here is what is happening: the style system requires that any style context
        // *must* be uniquely associated to a frame. So we insert as many frames as needed
        // to scale-down (or scale-up) the fontsize.

        PRInt32 gap = childData.scriptLevel - mPresentationData.scriptLevel;
        if (0 != gap) {
          // We are about to change the font-size... We first see if we
          // are in the scope of a <mstyle> that tells us what to do.
          // This is one of the most obscure part to implement in the spec...
          /*
          The REC says:

          Whenever the scriptlevel is changed, either automatically or by being
          explicitly incremented, decremented, or set, the current font size is
          multiplied by the value of scriptsizemultiplier to the power of the
          change in scriptlevel. For example, if scriptlevel is increased by 2,
          the font size is multiplied by scriptsizemultiplier twice in succession;
          if scriptlevel is explicitly set to 2 when it had been 3, the font size
          is divided by scriptsizemultiplier. 

          The default value of scriptsizemultiplier is less than one (in fact, it
          is approximately the square root of 1/2), resulting in a smaller font size
          with increasing scriptlevel. To prevent scripts from becoming unreadably
          small, the font size is never allowed to go below the value of
          scriptminsize as a result of a change to scriptlevel, though it can be
          set to a lower value using the fontsize attribute  on <mstyle> or on
          token elements. If a change to scriptlevel would cause the font size to
          become lower than scriptminsize using the above formula, the font size
          is instead set equal to scriptminsize within the subexpression for which
          scriptlevel was changed. 

          In the syntax for scriptminsize, v-unit represents a unit of vertical
          length. The most common unit for specifying font sizes in typesetting
          is pt (points). 
          */

          // default scriptsizemultiplier = 0.71 
          // default scriptminsize = 8pt 

          // here we only consider scriptminsize, and use the default
          // smaller-font-size algorithm of the style system
          PRInt32 scriptminsize = NSIntPointsToTwips(8);

          // see if the scriptminsize attribute is on <mstyle> that wraps us
          nsAutoString value;
          nsIFrame* mstyleFrame = mPresentationData.mstyle;
          if (mstyleFrame) {
            nsCOMPtr<nsIContent> mstyleContent;
            mstyleFrame->GetContent(getter_AddRefs(mstyleContent));
            if (NS_CONTENT_ATTR_HAS_VALUE == mstyleContent->GetAttribute(kNameSpaceID_None, 
                             nsMathMLAtoms::scriptminsize_, value))
            {
              PRInt32 errorCode;
              PRInt32 userValue = value.ToInteger(&errorCode);
              if (NS_SUCCEEDED(errorCode)) {
                // assume unit is point 
                // XXX need consistent, default unit throughout the code
                scriptminsize = NSIntPointsToTwips(userValue);
              }
              else {
                // XXX TODO: try to see if it is a h/v-unit like 1ex, 2px, 1em
              }
            }
          }

          // get Nav's magic font scaler
          PRInt32 scaler;
          aPresContext->GetFontScaler(&scaler);
          float scaleFactor = nsStyleUtil::GetScalingFactor(scaler);
          const nsFont& defaultFont = aPresContext->GetDefaultFontDeprecated();

          nsCOMPtr<nsIContent> childContent;
          childFrame->GetContent(getter_AddRefs(childContent));

          nsCOMPtr<nsIPresShell> shell;
          aPresContext->GetShell(getter_AddRefs(shell));

          nsIFrame* firstFrame = nsnull;
          nsIFrame* lastFrame = this;
          nsIStyleContext* lastStyleContext = mStyleContext;
          nsCOMPtr<nsIStyleContext> newStyleContext;

          // XXX seems not to decrease when the initail font-size is large (100pt)
          nsAutoString fontSize = (0 < gap)
                                ? ":-moz-math-font-size-smaller"
                                : ":-moz-math-font-size-larger";
          nsCOMPtr<nsIAtom> fontAtom(getter_AddRefs(NS_NewAtom(fontSize)));

          PRBool isSmaller = PR_TRUE;
          if (0 > gap) { isSmaller = PR_FALSE; gap = -gap; } // absolute value

          PRInt32 smallestFontSize, smallestFontIndex;
          if (isSmaller) {
            // find the smallest font-size in this subtree
            smallestFontSize = FindSmallestFontSizeFor(childFrame);
          }

          while (0 < gap--) {

            if (isSmaller) {
              // look ahead for the next smallest font size that will be in the subtree
              smallestFontIndex = nsStyleUtil::FindNextSmallerFontSize(smallestFontSize, (PRInt32)defaultFont.size, scaleFactor);
              smallestFontSize = nsStyleUtil::CalcFontPointSize(smallestFontIndex, (PRInt32)defaultFont.size, scaleFactor);
//printf("About to move to fontsize:%dpt(%dtwips)\n", 
//NSTwipsToFloorIntPoints(smallestFontSize), smallestFontSize);
              if (smallestFontSize < scriptminsize) {
                // don't bother doing any work
//printf("..... stopping ......\n");
// XXX there should be a mechanism so that we never try this subtree again
                break;
              }
            }

            aPresContext->ResolvePseudoStyleContextFor(childContent, fontAtom, lastStyleContext,
                                                       PR_FALSE, getter_AddRefs(newStyleContext));          
            if (newStyleContext && newStyleContext.get() != lastStyleContext) {
              // create a new wrapper frame and append it as sole child of the last created frame
              nsIFrame* newFrame = nsnull;
              NS_NewMathMLWrapperFrame(shell, &newFrame);
              NS_ASSERTION(newFrame, "Failed to create new frame");
              if (!newFrame) break;
              newFrame->Init(aPresContext, childContent, lastFrame, newStyleContext, nsnull);

              if (nsnull == firstFrame) {
                firstFrame = newFrame; 
              }
              if (this != lastFrame) {
                lastFrame->SetInitialChildList(aPresContext, nsnull, newFrame);
              }
              lastStyleContext = newStyleContext;
              lastFrame = newFrame;    
            }
            else {
              break;
            }
          }
          if (nsnull != firstFrame) { // at least one new frame was created
            mFrames.ReplaceFrame(this, childFrame, firstFrame);
            childFrame->SetParent(lastFrame);
            childFrame->SetNextSibling(nsnull);
            aPresContext->ReParentStyleContext(childFrame, lastStyleContext);
            lastFrame->SetInitialChildList(aPresContext, nsnull, childFrame);

            // if the child was an embellished operator,
            // make the whole list embellished as well
            nsEmbellishData embellishData;
            aMathMLFrame->GetEmbellishData(embellishData);
            if (0 != embellishData.flags && nsnull != embellishData.firstChild) {
              do { // walk the hierarchy in a bottom-up manner
                rv = lastFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
                NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
                if (NS_FAILED(rv) || !aMathMLFrame) break;
                embellishData.firstChild = childFrame;
                aMathMLFrame->SetEmbellishData(embellishData);
                childFrame = lastFrame;
                lastFrame->GetParent(&lastFrame);
              } while (lastFrame != this);
            }
          }
        }
      }
    }
  }
  return rv;
}


/* //////////////////
 * Frame construction
 * =============================================================================
 */

NS_IMETHODIMP
nsMathMLContainerFrame::Paint(nsIPresContext*      aPresContext,
                              nsIRenderingContext& aRenderingContext,
                              const nsRect&        aDirtyRect,
                              nsFramePaintLayer    aWhichLayer)
{
  nsresult rv = NS_OK;

  rv = nsHTMLContainerFrame::Paint(aPresContext,
                                   aRenderingContext,
                                   aDirtyRect,
                                   aWhichLayer);
#ifdef SHOW_BOUNDING_BOX
  // for visual debug
  // ----------------
  // if you want to see your bounding box, make sure to properly fill
  // your mBoundingMetrics and mReference point, and set
  // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
  // in the Init() of your sub-class

  if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer &&
      NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
  {
    aRenderingContext.SetColor(NS_RGB(0,0,255));

    nscoord x = mReference.x + mBoundingMetrics.leftBearing;
    nscoord y = mReference.y;
    nscoord w = mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    nscoord h = mBoundingMetrics.ascent + mBoundingMetrics.descent;

    aRenderingContext.DrawRect(x,y,w,h);
  }
#endif
  return rv;
}

NS_IMETHODIMP
nsMathMLContainerFrame::Init(nsIPresContext*  aPresContext,
                             nsIContent*      aContent,
                             nsIFrame*        aParent,
                             nsIStyleContext* aContext,
                             nsIFrame*        aPrevInFlow)
{
  nsresult rv;
  // first, let the base class do its Init()
  rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
  
  // now, if our parent implements the nsIMathMLFrame interface, we inherit
  // its scriptlevel and displaystyle. If the parent later wishes to increment
  // with other values, it will do so in its SetInitialChildList() method.
 
  mPresentationData.flags = NS_MATHML_DISPLAYSTYLE;
  mPresentationData.scriptLevel = 0;
  mPresentationData.mstyle = nsnull;

  mEmbellishData.flags = 0;
  mEmbellishData.firstChild = nsnull;

  nsIMathMLFrame* aMathMLFrame = nsnull;
  nsresult res = aParent->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
  if (NS_SUCCEEDED(res) && nsnull != aMathMLFrame) {
    nsPresentationData parentData;
    aMathMLFrame->GetPresentationData(parentData);

    mPresentationData.mstyle = parentData.mstyle;
    mPresentationData.scriptLevel = parentData.scriptLevel;
    if (NS_MATHML_IS_DISPLAYSTYLE(parentData.flags))
      mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
    else
      mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
  }
  return rv;
}

NS_IMETHODIMP
nsMathMLContainerFrame::SetInitialChildList(nsIPresContext* aPresContext,
                                            nsIAtom*        aListName,
                                            nsIFrame*       aChildList)
{
  // First, let the base class do its job
  nsresult rv;
  rv = nsHTMLContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);

  // Next, since we are an inline frame, and since we are a container, we have to
  // be very careful with the way we treat our children. Things look okay when
  // all of our children are only MathML frames. But there are problems if one of
  // our children happens to be an nsInlineFrame, e.g., from generated content such
  // as :before { content: open-quote } or :after { content: close-quote }
  // The code asserts during reflow (in nsLineLayout::BeginSpan)
  // Also there are problems when our children are hybrid, e.g., from html markups.
  // In short, the nsInlineFrame class expects a number of *invariants* that are not
  // met when we mix things.

  // So what we do here is to wrap children that happen to be nsInlineFrames in
  // anonymous block frames.
  // XXX Question: Do we have to handle Insert/Remove/Append on behalf of
  //     these anonymous blocks?
  //     Note: By construction, our anonymous blocks have only one child.

  nsIFrame* next = mFrames.FirstChild();
  while (next) {
    nsIFrame* child = next;
    rv = next->GetNextSibling(&next);
    if (!IsOnlyWhitespace(child)) {
      nsInlineFrame* inlineFrame = nsnull;
      rv = child->QueryInterface(nsInlineFrame::kInlineFrameCID, (void**)&inlineFrame);  
      if (NS_SUCCEEDED(rv) && inlineFrame) {
        // create a new anonymous block frame to wrap this child...
        nsCOMPtr<nsIPresShell> shell;
        aPresContext->GetShell(getter_AddRefs(shell));
        nsIFrame* anonymous;
        rv = NS_NewBlockFrame(shell, &anonymous);
        if (NS_FAILED(rv))
          return rv;    
        nsCOMPtr<nsIStyleContext> newStyleContext;
        aPresContext->ResolvePseudoStyleContextFor(mContent, nsHTMLAtoms::mozAnonymousBlock, 
                                                   mStyleContext, PR_FALSE, 
                                                   getter_AddRefs(newStyleContext));
        rv = anonymous->Init(aPresContext, mContent, this, newStyleContext, nsnull);
        if (NS_FAILED(rv)) {
          anonymous->Destroy(aPresContext);
          return rv;
        }
        mFrames.ReplaceFrame(this, child, anonymous);
        child->SetParent(anonymous);
        child->SetNextSibling(nsnull);
        aPresContext->ReParentStyleContext(child, newStyleContext);
        anonymous->SetInitialChildList(aPresContext, nsnull, child);
      }
    }
  }

  return rv;
}

#if 0
// helper method to compute the desired size of a frame
NS_IMETHODIMP
nsMathMLContainerFrame::GetDesiredStretchSize(nsIPresContext*      aPresContext,
                                              nsIRenderingContext& aRenderingContext,
                                              nsHTMLReflowMetrics& aDesiredSize)
{
  nsresult rv = NS_OK;
  nsRect rect;
  nscoord count = 0;
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    if (!IsOnlyWhitespace(childFrame)) {
      childFrame->GetRect(rect);
      if (IsEmbellishOperator(childFrame)) {
        // We have encountered an embellished operator...
        // It is treated as if the embellishments were not there!
        nsIMathMLFrame* aMathMLFrame = nsnull;
        rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
        NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
        if (NS_SUCCEEDED(rv) && aMathMLFrame) {
          nsEmbellishData embellishData;
          aMathMLFrame->GetEmbellishData(embellishData);
          embellishData.core->GetRect(rect);
        }
      }
      if (0 == aDirection) { // for horizontal positioning of child frames like in mrow
        aDesiredSize.width += rect.width;
        if (aDesiredSize.ascent < rect.y) aDesiredSize.ascent = rect.y;
        if (aDesiredSize.descent < rect.x) aDesiredSize.descent = rect.x;
      }
      else if (1 == aDirection) { // for vertical positioning of child frames like in mfrac, munder
        if (0 == count) { // it is the ascent and descent of the 'base' that is returned by defaul
          aDesiredSize.ascent = rect.y;
          aDesiredSize.descent = rect.x;
        }
        if (aDesiredSize.width < rect.width) aDesiredSize.width = rect.width;
      }
      count++;
    }
    rv = childFrame->GetNextSibling(&childFrame);
    NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
  }
  aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
  
  // Check if embellished operators didn't have (non-empty) siblings...
  if (0 < count && 0 == aDirection && 0 == aDesiredSize.height) {
    // Return the font ascent and font descent
    nsCOMPtr<nsIFontMetrics> fm;
    nsStyleFont font;
    mStyleContext->GetStyle(eStyleStruct_Font, font);
    aPresContext->GetMetricsFor(font.mFont, getter_AddRefs(fm));
    fm->GetMaxAscent(aDesiredSize.ascent);
    fm->GetMaxDescent(aDesiredSize.descent);
    aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
  }
  if (0 < count && 1 == aDirection && 0 == aDesiredSize.width) {
    // Return em
    nsStyleFont font;
    mStyleContext->GetStyle(eStyleStruct_Font, font);
    aDesiredSize.width = NSToCoordRound(float(font.mFont.size));
  }
  return NS_OK;
}
#endif


// helper function to reflow token elements
// note that mBoundingMetrics is computed here
nsresult
nsMathMLContainerFrame::ReflowTokenFor(nsIFrame*                aFrame,
                                       nsIPresContext*          aPresContext,
                                       nsHTMLReflowMetrics&     aDesiredSize,
                                       const nsHTMLReflowState& aReflowState,
                                       nsReflowStatus&          aStatus)
{
  NS_PRECONDITION(aFrame, "null arg");
  nsresult rv = NS_OK;

  // ask our children to compute their bounding metrics 
  nsHTMLReflowMetrics childDesiredSize(aDesiredSize.maxElementSize,
                      aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
  nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
  nsBoundingMetrics bm;
  PRInt32 count = 0; 
  nsIFrame* childFrame;
  aFrame->FirstChild(nsnull, &childFrame);
  while (childFrame) {
    if (IsOnlyWhitespace(childFrame)) {
      ReflowEmptyChild(aPresContext, childFrame);      
    }
    else {
      nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                         childFrame, availSize);
      rv = NS_STATIC_CAST(nsMathMLContainerFrame*, 
                          aFrame)->ReflowChild(childFrame, 
                                               aPresContext, childDesiredSize,
                                               childReflowState, aStatus);
      NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
      if (NS_FAILED(rv)) return rv;

      // origins are used as placeholders to store the child's ascent and descent.
      childFrame->SetRect(aPresContext,
                          nsRect(childDesiredSize.descent, childDesiredSize.ascent,
                                 childDesiredSize.width, childDesiredSize.height));
      // compute and cache the bounding metrics
      if (0 == count)
        bm  = childDesiredSize.mBoundingMetrics;
      else 
        bm += childDesiredSize.mBoundingMetrics;

      count++;
    }
    rv = childFrame->GetNextSibling(&childFrame);
    NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
  }
  NS_STATIC_CAST(nsMathMLContainerFrame*, aFrame)->SetBoundingMetrics(bm);

  // Place and size children
  NS_STATIC_CAST(nsMathMLContainerFrame*, 
                 aFrame)->FinalizeReflow(0, aPresContext, *aReflowState.rendContext, 
                                         aDesiredSize);
  return NS_OK;
}

// helper function to place token elements
// mBoundingMetrics is computed at the ReflowToken pass, it is
// not computed here because our children may be text frames that
// do not implement the GetBoundingMetrics() interface.
nsresult
nsMathMLContainerFrame::PlaceTokenFor(nsIFrame*            aFrame,
                                      nsIPresContext*      aPresContext,
                                      nsIRenderingContext& aRenderingContext,
                                      PRBool               aPlaceOrigin,  
                                      nsHTMLReflowMetrics& aDesiredSize)
{
  aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;
 
  nsRect rect;
  nsIFrame* childFrame;
  aFrame->FirstChild(nsnull, &childFrame);
  while (childFrame) {
    if (!IsOnlyWhitespace(childFrame)) {
      childFrame->GetRect(rect);
      aDesiredSize.width += rect.width;
      if (aDesiredSize.descent < rect.x) aDesiredSize.descent = rect.x;
      if (aDesiredSize.ascent < rect.y) aDesiredSize.ascent = rect.y;
    }
    childFrame->GetNextSibling(&childFrame);
  }
  aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
  
  if (aPlaceOrigin) {
    nscoord dy;
    nscoord dx = 0;

    aFrame->FirstChild(nsnull, &childFrame);
    while (childFrame) {
      childFrame->GetRect(rect);

      nsHTMLReflowMetrics childSize(nsnull);
      childSize.width = rect.width;
      childSize.height = rect.height;

      // Place and size the child
      dy = aDesiredSize.ascent - rect.y;
      NS_STATIC_CAST(nsMathMLContainerFrame*, 
                     aFrame)->FinishReflowChild(childFrame, aPresContext, 
                                                childSize, dx, dy, 0);
      dx += rect.width;
      childFrame->GetNextSibling(&childFrame);
    }
  }
  nsBoundingMetrics bm;
  NS_STATIC_CAST(nsMathMLContainerFrame*,
                 aFrame)->GetBoundingMetrics(bm);
  NS_STATIC_CAST(nsMathMLContainerFrame*,
                 aFrame)->SetReference(nsPoint(0,aDesiredSize.ascent-bm.ascent));
  return NS_OK;
}

// helper function to reflow all children
NS_IMETHODIMP
nsMathMLContainerFrame::ReflowChildren(PRInt32                  aDirection,
                                       nsIPresContext*          aPresContext,
                                       nsHTMLReflowMetrics&     aDesiredSize,
                                       const nsHTMLReflowState& aReflowState,
                                       nsReflowStatus&          aStatus)
{
  NS_PRECONDITION(aDirection==0 || aDirection==1, "Unknown direction");

  nsresult rv = NS_OK;
  nsReflowStatus childStatus;
  nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);

  // Asks the child to cache its bounding metrics
  nsHTMLReflowMetrics childDesiredSize(aDesiredSize.maxElementSize, 
                      aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);

  aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;

  nscoord count = 0;
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    //////////////
    // WHITESPACE: don't forget that whitespace doesn't count in MathML!
    if (IsOnlyWhitespace(childFrame)) {
      ReflowEmptyChild(aPresContext, childFrame);      
    }
    else {
      nsHTMLReflowState childReflowState(aPresContext, aReflowState,
                                         childFrame, availSize);
      rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
                       childReflowState, childStatus);
      NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
      if (NS_FAILED(rv)) return rv;

      // At this stage, the origin points of the children have no use, so we will use the
      // origins as placeholders to store the child's ascent and descent. Later on,
      // we should set the origins so as to overwrite what we are storing there now.
      childFrame->SetRect(aPresContext,
                          nsRect(childDesiredSize.descent, childDesiredSize.ascent,
                                 childDesiredSize.width, childDesiredSize.height));

      if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(mEmbellishData.flags) && 
          IsEmbellishOperator(childFrame)) {
        // We have encountered an embellished operator...
        // It is treated as if the embellishments were not there!
        nsIMathMLFrame* aMathMLFrame = nsnull;
        rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
        NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
        if (NS_SUCCEEDED(rv) && aMathMLFrame) {

          nsEmbellishData embellishData;
          aMathMLFrame->GetEmbellishData(embellishData);
          nsRect rect;
          embellishData.core->GetRect(rect);
          childDesiredSize.descent = rect.x;
          childDesiredSize.ascent = rect.y;
          childDesiredSize.width = rect.width;
          childDesiredSize.height = rect.height;

#if 0
          nsIRenderingContext& renderingContext = *aReflowState.rendContext;
          nsStretchMetrics stretchSize;
          aMathMLFrame->GetDesiredStretchSize(aPresContext, renderingContext, stretchSize);
          childDesiredSize.descent = stretchSize.descent;
          childDesiredSize.ascent = stretchSize.ascent;
          childDesiredSize.width = stretchSize.width;
          childDesiredSize.height = stretchSize.height;
#endif
        }
      }
      if (0 == aDirection) { // for horizontal positioning of child frames like in mrow
        aDesiredSize.width += childDesiredSize.width;
        if (aDesiredSize.ascent < childDesiredSize.ascent) aDesiredSize.ascent = childDesiredSize.ascent;
        if (aDesiredSize.descent < childDesiredSize.descent) aDesiredSize.descent = childDesiredSize.descent;
      }
      else if (1 == aDirection) { // for vertical positioning of child frames like in mfrac, munder
        if (0 == count) { // it is the ascent and descent of the 'base' that is returned by defaul
          aDesiredSize.ascent = childDesiredSize.ascent;
          aDesiredSize.descent = childDesiredSize.descent;
        }
        if (aDesiredSize.width < childDesiredSize.width) aDesiredSize.width = childDesiredSize.width;
      }
      count++;
    }
    rv = childFrame->GetNextSibling(&childFrame);
    NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
  }
  aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;

  // Check if embellished operators didn't have (non-empty) siblings...
  if (0 < count && 0 == aDirection && 0 == aDesiredSize.height) {
    // Return the font ascent and font descent
    nsCOMPtr<nsIFontMetrics> fm;
    nsStyleFont font;
    mStyleContext->GetStyle(eStyleStruct_Font, font);
    aPresContext->GetMetricsFor(font.mFont, getter_AddRefs(fm));
    fm->GetMaxAscent(aDesiredSize.ascent);
    fm->GetMaxDescent(aDesiredSize.descent);
    aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
  }
  if (0 < count && 1 == aDirection && 0 == aDesiredSize.width) {
    // Return em
    nsStyleFont font;
    mStyleContext->GetStyle(eStyleStruct_Font, font);
    aDesiredSize.width = NSToCoordRound(float(font.mFont.size));
  }

  return NS_OK;
}

// helper function to stretch all children
NS_IMETHODIMP
nsMathMLContainerFrame::StretchChildren(nsIPresContext*      aPresContext,
                                        nsIRenderingContext& aRenderingContext,
                                        nsStretchDirection   aStretchDirection,
                                        nsStretchMetrics&    aContainerSize)
{
  nsRect rect;
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    if (mEmbellishData.firstChild == childFrame) {
      // Skip this child... because:
      // If we are here it means we are an embellished container and
      // for now, we don't touch our embellished child frame.
      // Its stretch will be handled separatedly when we receive
      // stretch command fired by our parent frame.
    }
    else {
      nsIMathMLFrame* aMathMLFrame;
      nsresult rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
      if (NS_SUCCEEDED(rv) && aMathMLFrame) {
        // retrieve the metrics that was stored at the ReflowChildren pass
        childFrame->GetRect(rect);
        nsStretchMetrics childSize(rect.x, rect.y, rect.width, rect.height);
        aMathMLFrame->Stretch(aPresContext, aRenderingContext, 
                              aStretchDirection, aContainerSize, childSize);
        // store the updated metrics
        childFrame->SetRect(aPresContext,
                            nsRect(childSize.descent, childSize.ascent,
                                   childSize.width, childSize.height));
      }
    }
    childFrame->GetNextSibling(&childFrame);
  }
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::Reflow(nsIPresContext*          aPresContext,
                               nsHTMLReflowMetrics&     aDesiredSize,
                               const nsHTMLReflowState& aReflowState,
                               nsReflowStatus&          aStatus)
{
  nsresult rv = NS_OK;

  /////////////
  // Reflow children
  ReflowChildren(0, aPresContext, aDesiredSize, aReflowState, aStatus);

  /////////////
  // If we are a container which is entitled to stretch its children, then we 
  // ask our stretchy children to stretch themselves

  nsIRenderingContext& renderingContext = *aReflowState.rendContext;

  if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(mEmbellishData.flags)) {
    nsStretchMetrics containerSize(aDesiredSize);
    nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;

    StretchChildren(aPresContext, renderingContext, stretchDir, containerSize);
  }

  /////////////
  // Place children now by re-adjusting the origins to align the baselines
  FinalizeReflow(0, aPresContext, renderingContext, aDesiredSize);

  aStatus = NS_FRAME_COMPLETE;
  return NS_OK;
}

NS_IMETHODIMP
nsMathMLContainerFrame::Place(nsIPresContext*      aPresContext,
                              nsIRenderingContext& aRenderingContext,
                              PRBool               aPlaceOrigin,
                              nsHTMLReflowMetrics& aDesiredSize)
{
  aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;
 
  PRInt32 count = 0; 
  nsBoundingMetrics bm;
  nsHTMLReflowMetrics childSize(nsnull);
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    if (!IsOnlyWhitespace(childFrame)) {
      GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);

      aDesiredSize.width += childSize.width;
      if (aDesiredSize.descent < childSize.descent) aDesiredSize.descent = childSize.descent;
      if (aDesiredSize.ascent < childSize.ascent) aDesiredSize.ascent = childSize.ascent;

      // Compute and cache our bounding metrics
      if (0 == count)   
        mBoundingMetrics  = bm;
      else
        mBoundingMetrics += bm;

      count++;
    }
    childFrame->GetNextSibling(&childFrame);
  }
  aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;

  if (aPlaceOrigin) {
    nsRect rect;
  
    nscoord dy;
    nscoord dx = 0;
    childFrame = mFrames.FirstChild();
    while (childFrame) {
      childFrame->GetRect(rect);

      childSize.width = rect.width;
      childSize.height = rect.height;

      // Place and size the child
      dy = aDesiredSize.ascent - rect.y;
      FinishReflowChild(childFrame, aPresContext, childSize, dx, dy, 0);

      dx += rect.width;
      childFrame->GetNextSibling(&childFrame);
    }
  }

  mReference.x = 0;
  mReference.y = aDesiredSize.ascent - mBoundingMetrics.ascent;
  return NS_OK;
}


//==========================
// *BEWARE* of the wrapper frame!
// This is the frame that is inserted to alter the style context of
// scripting elements. What this means is that looking for your parent
// or your siblings with (possible several) wrapper frames around you
// can make you wonder what is going on. For example, the direct parent
// of the subscript within <msub> is not <msub>, but instead the wrapper
// frame that was insterted to alter the style context of the subscript!
// You will seldom need to find out who is exactly your parent. You should
// first rethink your code to see if you can avoid finding who is your parent. 
// Be careful, there are wrapper frames all over the place, and probably 
// one or many are wrapping you if you are in a position where the
// scriptlevel is non zero.

nsresult
NS_NewMathMLWrapperFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
{
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
  if (nsnull == aNewFrame) {
    return NS_ERROR_NULL_POINTER;
  }
  nsMathMLWrapperFrame* it = new (aPresShell) nsMathMLWrapperFrame;
  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  *aNewFrame = it;  
  return NS_OK;
}

nsMathMLWrapperFrame::nsMathMLWrapperFrame()
{
}

nsMathMLWrapperFrame::~nsMathMLWrapperFrame()
{
}

// For Reflow() and Place(), pretend we are a token to re-use code
NS_IMETHODIMP
nsMathMLWrapperFrame::Reflow(nsIPresContext*          aPresContext,
                             nsHTMLReflowMetrics&     aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus&          aStatus)
{
  return ReflowTokenFor(this, aPresContext, aDesiredSize,
                        aReflowState, aStatus);
}

NS_IMETHODIMP
nsMathMLWrapperFrame::Place(nsIPresContext*      aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            PRBool               aPlaceOrigin,
                            nsHTMLReflowMetrics& aDesiredSize)
{
  return PlaceTokenFor(this, aPresContext, aRenderingContext,
                       aPlaceOrigin, aDesiredSize);
}
