layout/mathml/nsMathMLContainerFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/mathml/nsMathMLContainerFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1617 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsMathMLContainerFrame.h"
    1.10 +#include "nsPresContext.h"
    1.11 +#include "nsIPresShell.h"
    1.12 +#include "nsStyleContext.h"
    1.13 +#include "nsNameSpaceManager.h"
    1.14 +#include "nsRenderingContext.h"
    1.15 +#include "nsIDOMMutationEvent.h"
    1.16 +#include "nsGkAtoms.h"
    1.17 +#include "nsAutoPtr.h"
    1.18 +#include "nsDisplayList.h"
    1.19 +#include "nsIReflowCallback.h"
    1.20 +#include "mozilla/Likely.h"
    1.21 +#include "nsIScriptError.h"
    1.22 +#include "nsContentUtils.h"
    1.23 +#include "nsMathMLElement.h"
    1.24 +
    1.25 +using namespace mozilla;
    1.26 +
    1.27 +//
    1.28 +// nsMathMLContainerFrame implementation
    1.29 +//
    1.30 +
    1.31 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
    1.32 +
    1.33 +NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
    1.34 +  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
    1.35 +  NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame)
    1.36 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    1.37 +
    1.38 +// =============================================================================
    1.39 +
    1.40 +// error handlers
    1.41 +// provide a feedback to the user when a frame with bad markup can not be rendered
    1.42 +nsresult
    1.43 +nsMathMLContainerFrame::ReflowError(nsRenderingContext& aRenderingContext,
    1.44 +                                    nsHTMLReflowMetrics& aDesiredSize)
    1.45 +{
    1.46 +  // clear all other flags and record that there is an error with this frame
    1.47 +  mEmbellishData.flags = 0;
    1.48 +  mPresentationData.flags = NS_MATHML_ERROR;
    1.49 +
    1.50 +  ///////////////
    1.51 +  // Set font
    1.52 +  nsRefPtr<nsFontMetrics> fm;
    1.53 +  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
    1.54 +  aRenderingContext.SetFont(fm);
    1.55 +
    1.56 +  // bounding metrics
    1.57 +  nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
    1.58 +  mBoundingMetrics =
    1.59 +    aRenderingContext.GetBoundingMetrics(errorMsg.get(), errorMsg.Length());
    1.60 +
    1.61 +  // reflow metrics
    1.62 +  aDesiredSize.SetTopAscent(fm->MaxAscent());
    1.63 +  nscoord descent = fm->MaxDescent();
    1.64 +  aDesiredSize.Height() = aDesiredSize.TopAscent() + descent;
    1.65 +  aDesiredSize.Width() = mBoundingMetrics.width;
    1.66 +
    1.67 +  // Also return our bounding metrics
    1.68 +  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
    1.69 +
    1.70 +  return NS_OK;
    1.71 +}
    1.72 +
    1.73 +class nsDisplayMathMLError : public nsDisplayItem {
    1.74 +public:
    1.75 +  nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    1.76 +    : nsDisplayItem(aBuilder, aFrame) {
    1.77 +    MOZ_COUNT_CTOR(nsDisplayMathMLError);
    1.78 +  }
    1.79 +#ifdef NS_BUILD_REFCNT_LOGGING
    1.80 +  virtual ~nsDisplayMathMLError() {
    1.81 +    MOZ_COUNT_DTOR(nsDisplayMathMLError);
    1.82 +  }
    1.83 +#endif
    1.84 +
    1.85 +  virtual void Paint(nsDisplayListBuilder* aBuilder,
    1.86 +                     nsRenderingContext* aCtx) MOZ_OVERRIDE;
    1.87 +  NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR)
    1.88 +};
    1.89 +
    1.90 +void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
    1.91 +                                 nsRenderingContext* aCtx)
    1.92 +{
    1.93 +  // Set color and font ...
    1.94 +  nsRefPtr<nsFontMetrics> fm;
    1.95 +  nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
    1.96 +  aCtx->SetFont(fm);
    1.97 +
    1.98 +  nsPoint pt = ToReferenceFrame();
    1.99 +  aCtx->SetColor(NS_RGB(255,0,0));
   1.100 +  aCtx->FillRect(nsRect(pt, mFrame->GetSize()));
   1.101 +  aCtx->SetColor(NS_RGB(255,255,255));
   1.102 +
   1.103 +  nscoord ascent = aCtx->FontMetrics()->MaxAscent();
   1.104 +
   1.105 +  NS_NAMED_LITERAL_STRING(errorMsg, "invalid-markup");
   1.106 +  aCtx->DrawString(errorMsg.get(), uint32_t(errorMsg.Length()),
   1.107 +                   pt.x, pt.y+ascent);
   1.108 +}
   1.109 +
   1.110 +/* /////////////
   1.111 + * nsIMathMLFrame - support methods for stretchy elements
   1.112 + * =============================================================================
   1.113 + */
   1.114 +
   1.115 +static bool
   1.116 +IsForeignChild(const nsIFrame* aFrame)
   1.117 +{
   1.118 +  // This counts nsMathMLmathBlockFrame as a foreign child, because it
   1.119 +  // uses block reflow
   1.120 +  return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
   1.121 +    aFrame->GetType() == nsGkAtoms::blockFrame;
   1.122 +}
   1.123 +
   1.124 +static void
   1.125 +DestroyHTMLReflowMetrics(void *aPropertyValue)
   1.126 +{
   1.127 +  delete static_cast<nsHTMLReflowMetrics*>(aPropertyValue);
   1.128 +}
   1.129 +
   1.130 +NS_DECLARE_FRAME_PROPERTY(HTMLReflowMetricsProperty, DestroyHTMLReflowMetrics)
   1.131 +
   1.132 +/* static */ void
   1.133 +nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame*                  aFrame,
   1.134 +                                                        const nsHTMLReflowMetrics& aReflowMetrics,
   1.135 +                                                        const nsBoundingMetrics&   aBoundingMetrics)
   1.136 +{
   1.137 +  nsHTMLReflowMetrics *metrics = new nsHTMLReflowMetrics(aReflowMetrics);
   1.138 +  metrics->mBoundingMetrics = aBoundingMetrics;
   1.139 +  aFrame->Properties().Set(HTMLReflowMetricsProperty(), metrics);
   1.140 +}
   1.141 +
   1.142 +// helper method to facilitate getting the reflow and bounding metrics
   1.143 +/* static */ void
   1.144 +nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
   1.145 +                                                       nsHTMLReflowMetrics& aReflowMetrics,
   1.146 +                                                       nsBoundingMetrics&   aBoundingMetrics,
   1.147 +                                                       eMathMLFrameType*    aMathMLFrameType)
   1.148 +{
   1.149 +  NS_PRECONDITION(aFrame, "null arg");
   1.150 +
   1.151 +  nsHTMLReflowMetrics *metrics = static_cast<nsHTMLReflowMetrics*>
   1.152 +    (aFrame->Properties().Get(HTMLReflowMetricsProperty()));
   1.153 +
   1.154 +  // IMPORTANT: This function is only meant to be called in Place() methods
   1.155 +  // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
   1.156 +  // information.
   1.157 +  NS_ASSERTION(metrics, "Didn't SaveReflowAndBoundingMetricsFor frame!");
   1.158 +  if (metrics) {
   1.159 +    aReflowMetrics = *metrics;
   1.160 +    aBoundingMetrics = metrics->mBoundingMetrics;
   1.161 +  }
   1.162 +
   1.163 +  if (aMathMLFrameType) {
   1.164 +    if (!IsForeignChild(aFrame)) {
   1.165 +      nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
   1.166 +      if (mathMLFrame) {
   1.167 +        *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
   1.168 +        return;
   1.169 +      }
   1.170 +    }
   1.171 +    *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
   1.172 +  }
   1.173 +
   1.174 +}
   1.175 +
   1.176 +void
   1.177 +nsMathMLContainerFrame::ClearSavedChildMetrics()
   1.178 +{
   1.179 +  nsIFrame* childFrame = mFrames.FirstChild();
   1.180 +  FramePropertyTable* props = PresContext()->PropertyTable();
   1.181 +  while (childFrame) {
   1.182 +    props->Delete(childFrame, HTMLReflowMetricsProperty());
   1.183 +    childFrame = childFrame->GetNextSibling();
   1.184 +  }
   1.185 +}
   1.186 +
   1.187 +// helper to get the preferred size that a container frame should use to fire
   1.188 +// the stretch on its stretchy child frames.
   1.189 +void
   1.190 +nsMathMLContainerFrame::GetPreferredStretchSize(nsRenderingContext& aRenderingContext,
   1.191 +                                                uint32_t             aOptions,
   1.192 +                                                nsStretchDirection   aStretchDirection,
   1.193 +                                                nsBoundingMetrics&   aPreferredStretchSize)
   1.194 +{
   1.195 +  if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
   1.196 +    // when our actual size is ok, just use it
   1.197 +    aPreferredStretchSize = mBoundingMetrics;
   1.198 +  }
   1.199 +  else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
   1.200 +    // compute our up-to-date size using Place()
   1.201 +    nsHTMLReflowMetrics metrics(GetWritingMode()); // ???
   1.202 +    Place(aRenderingContext, false, metrics);
   1.203 +    aPreferredStretchSize = metrics.mBoundingMetrics;
   1.204 +  }
   1.205 +  else {
   1.206 +    // compute a size that doesn't include embellishements
   1.207 +    bool stretchAll =
   1.208 +      NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   1.209 +      NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   1.210 +    NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
   1.211 +                 stretchAll,
   1.212 +                 "invalid call to GetPreferredStretchSize");
   1.213 +    bool firstTime = true;
   1.214 +    nsBoundingMetrics bm, bmChild;
   1.215 +    nsIFrame* childFrame =
   1.216 +      stretchAll ? GetFirstPrincipalChild() : mPresentationData.baseFrame;
   1.217 +    while (childFrame) {
   1.218 +      // initializations in case this child happens not to be a MathML frame
   1.219 +      nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
   1.220 +      if (mathMLFrame) {
   1.221 +        nsEmbellishData embellishData;
   1.222 +        nsPresentationData presentationData;
   1.223 +        mathMLFrame->GetEmbellishData(embellishData);
   1.224 +        mathMLFrame->GetPresentationData(presentationData);
   1.225 +        if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
   1.226 +            embellishData.direction == aStretchDirection &&
   1.227 +            presentationData.baseFrame) {
   1.228 +          // embellishements are not included, only consider the inner first child itself
   1.229 +          // XXXkt Does that mean the core descendent frame should be used
   1.230 +          // instead of the base child?
   1.231 +          nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame);
   1.232 +          if (mathMLchildFrame) {
   1.233 +            mathMLFrame = mathMLchildFrame;
   1.234 +          }
   1.235 +        }
   1.236 +        mathMLFrame->GetBoundingMetrics(bmChild);
   1.237 +      }
   1.238 +      else {
   1.239 +        nsHTMLReflowMetrics unused(GetWritingMode());
   1.240 +        GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
   1.241 +      }
   1.242 +
   1.243 +      if (firstTime) {
   1.244 +        firstTime = false;
   1.245 +        bm = bmChild;
   1.246 +        if (!stretchAll) {
   1.247 +          // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
   1.248 +          // or <maction>...<mo>...</mo></maction>.
   1.249 +          break;
   1.250 +        }
   1.251 +      }
   1.252 +      else {
   1.253 +        if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
   1.254 +          // if we get here, it means this is container that will stack its children
   1.255 +          // vertically and fire an horizontal stretch on each them. This is the case
   1.256 +          // for \munder, \mover, \munderover. We just sum-up the size vertically.
   1.257 +          bm.descent += bmChild.ascent + bmChild.descent;
   1.258 +          // Sometimes non-spacing marks (when width is zero) are positioned
   1.259 +          // to the left of the origin, but it is the distance between left
   1.260 +          // and right bearing that is important rather than the offsets from
   1.261 +          // the origin.
   1.262 +          if (bmChild.width == 0) {
   1.263 +            bmChild.rightBearing -= bmChild.leftBearing;
   1.264 +            bmChild.leftBearing = 0;
   1.265 +          }
   1.266 +          if (bm.leftBearing > bmChild.leftBearing)
   1.267 +            bm.leftBearing = bmChild.leftBearing;
   1.268 +          if (bm.rightBearing < bmChild.rightBearing)
   1.269 +            bm.rightBearing = bmChild.rightBearing;
   1.270 +        }
   1.271 +        else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
   1.272 +          // just sum-up the sizes horizontally.
   1.273 +          bm += bmChild;
   1.274 +        }
   1.275 +        else {
   1.276 +          NS_ERROR("unexpected case in GetPreferredStretchSize");
   1.277 +          break;
   1.278 +        }
   1.279 +      }
   1.280 +      childFrame = childFrame->GetNextSibling();
   1.281 +    }
   1.282 +    aPreferredStretchSize = bm;
   1.283 +  }
   1.284 +}
   1.285 +
   1.286 +NS_IMETHODIMP
   1.287 +nsMathMLContainerFrame::Stretch(nsRenderingContext& aRenderingContext,
   1.288 +                                nsStretchDirection   aStretchDirection,
   1.289 +                                nsBoundingMetrics&   aContainerSize,
   1.290 +                                nsHTMLReflowMetrics& aDesiredStretchSize)
   1.291 +{
   1.292 +  if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
   1.293 +
   1.294 +    if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
   1.295 +      NS_WARNING("it is wrong to fire stretch more than once on a frame");
   1.296 +      return NS_OK;
   1.297 +    }
   1.298 +    mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
   1.299 +
   1.300 +    if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
   1.301 +      NS_WARNING("it is wrong to fire stretch on a erroneous frame");
   1.302 +      return NS_OK;
   1.303 +    }
   1.304 +
   1.305 +    // Pass the stretch to the base child ...
   1.306 +
   1.307 +    nsIFrame* baseFrame = mPresentationData.baseFrame;
   1.308 +    if (baseFrame) {
   1.309 +      nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
   1.310 +      NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
   1.311 +      if (mathMLFrame) {
   1.312 +        bool stretchAll =
   1.313 +          NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   1.314 +          NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   1.315 +
   1.316 +        // And the trick is that the child's rect.x is still holding the descent,
   1.317 +        // and rect.y is still holding the ascent ...
   1.318 +        nsHTMLReflowMetrics childSize(aDesiredStretchSize);
   1.319 +        GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
   1.320 +
   1.321 +        // See if we should downsize and confine the stretch to us...
   1.322 +        // XXX there may be other cases where we can downsize the stretch,
   1.323 +        // e.g., the first &Sum; might appear big in the following situation
   1.324 +        // <math xmlns='http://www.w3.org/1998/Math/MathML'>
   1.325 +        //   <mstyle>
   1.326 +        //     <msub>
   1.327 +        //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
   1.328 +        //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
   1.329 +        //      </msub>
   1.330 +        //   </mstyle>
   1.331 +        // </math>
   1.332 +        nsBoundingMetrics containerSize = aContainerSize;
   1.333 +        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
   1.334 +            aStretchDirection != mEmbellishData.direction) {
   1.335 +          if (mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) {
   1.336 +            containerSize = childSize.mBoundingMetrics;
   1.337 +          }
   1.338 +          else {
   1.339 +            GetPreferredStretchSize(aRenderingContext, 
   1.340 +                                    stretchAll ? STRETCH_CONSIDER_EMBELLISHMENTS : 0,
   1.341 +                                    mEmbellishData.direction, containerSize);
   1.342 +          }
   1.343 +        }
   1.344 +
   1.345 +        // do the stretching...
   1.346 +        mathMLFrame->Stretch(aRenderingContext,
   1.347 +                             mEmbellishData.direction, containerSize, childSize);
   1.348 +        // store the updated metrics
   1.349 +        SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
   1.350 +                                        childSize.mBoundingMetrics);
   1.351 +        
   1.352 +        // Remember the siblings which were _deferred_.
   1.353 +        // Now that this embellished child may have changed, we need to
   1.354 +        // fire the stretch on its siblings using our updated size
   1.355 +
   1.356 +        if (stretchAll) {
   1.357 +
   1.358 +          nsStretchDirection stretchDir =
   1.359 +            NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
   1.360 +              NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
   1.361 +
   1.362 +          GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
   1.363 +                                  stretchDir, containerSize);
   1.364 +
   1.365 +          nsIFrame* childFrame = mFrames.FirstChild();
   1.366 +          while (childFrame) {
   1.367 +            if (childFrame != mPresentationData.baseFrame) {
   1.368 +              mathMLFrame = do_QueryFrame(childFrame);
   1.369 +              if (mathMLFrame) {
   1.370 +                // retrieve the metrics that was stored at the previous pass
   1.371 +                GetReflowAndBoundingMetricsFor(childFrame, 
   1.372 +                  childSize, childSize.mBoundingMetrics);
   1.373 +                // do the stretching...
   1.374 +                mathMLFrame->Stretch(aRenderingContext, stretchDir,
   1.375 +                                     containerSize, childSize);
   1.376 +                // store the updated metrics
   1.377 +                SaveReflowAndBoundingMetricsFor(childFrame, childSize,
   1.378 +                                                childSize.mBoundingMetrics);
   1.379 +              }
   1.380 +            }
   1.381 +            childFrame = childFrame->GetNextSibling();
   1.382 +          }
   1.383 +        }
   1.384 +
   1.385 +        // re-position all our children
   1.386 +        nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
   1.387 +        if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   1.388 +          // Make sure the child frames get their DidReflow() calls.
   1.389 +          DidReflowChildren(mFrames.FirstChild());
   1.390 +        }
   1.391 +
   1.392 +        // If our parent is not embellished, it means we are the outermost embellished
   1.393 +        // container and so we put the spacing, otherwise we don't include the spacing,
   1.394 +        // the outermost embellished container will take care of it.
   1.395 +
   1.396 +        nsEmbellishData parentData;
   1.397 +        GetEmbellishDataFrom(mParent, parentData);
   1.398 +        // ensure that we are the embellished child, not just a sibling
   1.399 +        // (need to test coreFrame since <mfrac> resets other things)
   1.400 +        if (parentData.coreFrame != mEmbellishData.coreFrame) {
   1.401 +          // (we fetch values from the core since they may use units that depend
   1.402 +          // on style data, and style changes could have occurred in the core since
   1.403 +          // our last visit there)
   1.404 +          nsEmbellishData coreData;
   1.405 +          GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
   1.406 +
   1.407 +          mBoundingMetrics.width +=
   1.408 +            coreData.leadingSpace + coreData.trailingSpace;
   1.409 +          aDesiredStretchSize.Width() = mBoundingMetrics.width;
   1.410 +          aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
   1.411 +
   1.412 +          nscoord dx = (StyleVisibility()->mDirection ?
   1.413 +                        coreData.trailingSpace : coreData.leadingSpace);
   1.414 +          if (dx != 0) {
   1.415 +            mBoundingMetrics.leftBearing += dx;
   1.416 +            mBoundingMetrics.rightBearing += dx;
   1.417 +            aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
   1.418 +            aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
   1.419 +
   1.420 +            nsIFrame* childFrame = mFrames.FirstChild();
   1.421 +            while (childFrame) {
   1.422 +              childFrame->SetPosition(childFrame->GetPosition()
   1.423 +                                      + nsPoint(dx, 0));
   1.424 +              childFrame = childFrame->GetNextSibling();
   1.425 +            }
   1.426 +          }
   1.427 +        }
   1.428 +
   1.429 +        // Finished with these:
   1.430 +        ClearSavedChildMetrics();
   1.431 +        // Set our overflow area
   1.432 +        GatherAndStoreOverflow(&aDesiredStretchSize);
   1.433 +      }
   1.434 +    }
   1.435 +  }
   1.436 +  return NS_OK;
   1.437 +}
   1.438 +
   1.439 +nsresult
   1.440 +nsMathMLContainerFrame::FinalizeReflow(nsRenderingContext& aRenderingContext,
   1.441 +                                       nsHTMLReflowMetrics& aDesiredSize)
   1.442 +{
   1.443 +  // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
   1.444 +  // and descent in expectation of a stretch command. Hence we need to ensure that
   1.445 +  // a stretch command will actually be fired later on, after exiting from our
   1.446 +  // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
   1.447 +  // with inappropriate data causing children to be improperly positioned.
   1.448 +  // This helper method checks to see if our parent will fire a stretch command
   1.449 +  // targeted at us. If not, we go ahead and fire an involutive stretch on
   1.450 +  // ourselves. This will clear all the rect.x and rect.y, and return our
   1.451 +  // desired size.
   1.452 +
   1.453 +
   1.454 +  // First, complete the post-reflow hook.
   1.455 +  // We use the information in our children rectangles to position them.
   1.456 +  // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
   1.457 +  // They will still be holding the ascent and descent for each child.
   1.458 +
   1.459 +  // The first clause caters for any non-embellished container.
   1.460 +  // The second clause is for a container which won't fire stretch even though it is
   1.461 +  // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
   1.462 +  // because it excludes the particular case of the core <mo>...</mo> itself.
   1.463 +  // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
   1.464 +  bool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
   1.465 +                       (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
   1.466 +                        mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
   1.467 +  nsresult rv = Place(aRenderingContext, placeOrigin, aDesiredSize);
   1.468 +
   1.469 +  // Place() will call FinishReflowChild() when placeOrigin is true but if
   1.470 +  // it returns before reaching FinishReflowChild() due to errors we need
   1.471 +  // to fulfill the reflow protocol by calling DidReflow for the child frames
   1.472 +  // that still needs it here (or we may crash - bug 366012).
   1.473 +  // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
   1.474 +  // through Stretch() eventually.
   1.475 +  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   1.476 +    DidReflowChildren(GetFirstPrincipalChild());
   1.477 +    return rv;
   1.478 +  }
   1.479 +
   1.480 +  bool parentWillFireStretch = false;
   1.481 +  if (!placeOrigin) {
   1.482 +    // This means the rect.x and rect.y of our children were not set!!
   1.483 +    // Don't go without checking to see if our parent will later fire a Stretch() command
   1.484 +    // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
   1.485 +    nsIMathMLFrame* mathMLFrame = do_QueryFrame(mParent);
   1.486 +    if (mathMLFrame) {
   1.487 +      nsEmbellishData embellishData;
   1.488 +      nsPresentationData presentationData;
   1.489 +      mathMLFrame->GetEmbellishData(embellishData);
   1.490 +      mathMLFrame->GetPresentationData(presentationData);
   1.491 +      if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
   1.492 +          NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
   1.493 +          (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
   1.494 +            && presentationData.baseFrame == this))
   1.495 +      {
   1.496 +        parentWillFireStretch = true;
   1.497 +      }
   1.498 +    }
   1.499 +    if (!parentWillFireStretch) {
   1.500 +      // There is nobody who will fire the stretch for us, we do it ourselves!
   1.501 +
   1.502 +      bool stretchAll =
   1.503 +        /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
   1.504 +        NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   1.505 +
   1.506 +      nsBoundingMetrics defaultSize;
   1.507 +      if (mEmbellishData.coreFrame == this /* case of a bare <mo>...</mo> itself */
   1.508 +          || stretchAll) { /* or <mover><mo>...</mo>...</mover>, or friends */
   1.509 +        // use our current size as computed earlier by Place()
   1.510 +        defaultSize = aDesiredSize.mBoundingMetrics;
   1.511 +      }
   1.512 +      else { /* case of <msup><mo>...</mo>...</msup> or friends */
   1.513 +        // compute a size that doesn't include embellishments
   1.514 +        GetPreferredStretchSize(aRenderingContext, 0, mEmbellishData.direction,
   1.515 +                                defaultSize);
   1.516 +      }
   1.517 +      Stretch(aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, defaultSize,
   1.518 +              aDesiredSize);
   1.519 +#ifdef DEBUG
   1.520 +      {
   1.521 +        // The Place() call above didn't request FinishReflowChild(),
   1.522 +        // so let's check that we eventually did through Stretch().
   1.523 +        nsIFrame* childFrame = GetFirstPrincipalChild();
   1.524 +        for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
   1.525 +          NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
   1.526 +                       "DidReflow() was never called");
   1.527 +        }
   1.528 +      }
   1.529 +#endif
   1.530 +    }
   1.531 +  }
   1.532 +
   1.533 +  // Also return our bounding metrics
   1.534 +  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   1.535 +
   1.536 +  // see if we should fix the spacing
   1.537 +  FixInterFrameSpacing(aDesiredSize);
   1.538 +
   1.539 +  if (!parentWillFireStretch) {
   1.540 +    // Not expecting a stretch.
   1.541 +    // Finished with these:
   1.542 +    ClearSavedChildMetrics();
   1.543 +    // Set our overflow area.
   1.544 +    GatherAndStoreOverflow(&aDesiredSize);
   1.545 +  }
   1.546 +
   1.547 +  return NS_OK;
   1.548 +}
   1.549 +
   1.550 +
   1.551 +/* /////////////
   1.552 + * nsIMathMLFrame - support methods for scripting elements (nested frames
   1.553 + * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
   1.554 + * mfrac, mroot, mtable).
   1.555 + * =============================================================================
   1.556 + */
   1.557 +
   1.558 +// helper to let the update of presentation data pass through
   1.559 +// a subtree that may contain non-mathml container frames
   1.560 +/* static */ void
   1.561 +nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame*       aFrame,
   1.562 +                                                     uint32_t        aFlagsValues,
   1.563 +                                                     uint32_t        aFlagsToUpdate)
   1.564 +{
   1.565 +  if (!aFrame || !aFlagsToUpdate)
   1.566 +    return;
   1.567 +  nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
   1.568 +  if (mathMLFrame) {
   1.569 +    // update
   1.570 +    mathMLFrame->UpdatePresentationData(aFlagsValues,
   1.571 +                                        aFlagsToUpdate);
   1.572 +    // propagate using the base method to make sure that the control
   1.573 +    // is passed on to MathML frames that may be overloading the method
   1.574 +    mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
   1.575 +      aFlagsValues, aFlagsToUpdate);
   1.576 +  }
   1.577 +  else {
   1.578 +    // propagate down the subtrees
   1.579 +    nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
   1.580 +    while (childFrame) {
   1.581 +      PropagatePresentationDataFor(childFrame,
   1.582 +        aFlagsValues, aFlagsToUpdate);
   1.583 +      childFrame = childFrame->GetNextSibling();
   1.584 +    }
   1.585 +  }
   1.586 +}
   1.587 +
   1.588 +/* static */ void
   1.589 +nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame*       aParentFrame,
   1.590 +                                                             int32_t         aFirstChildIndex,
   1.591 +                                                             int32_t         aLastChildIndex,
   1.592 +                                                             uint32_t        aFlagsValues,
   1.593 +                                                             uint32_t        aFlagsToUpdate)
   1.594 +{
   1.595 +  if (!aParentFrame || !aFlagsToUpdate)
   1.596 +    return;
   1.597 +  int32_t index = 0;
   1.598 +  nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
   1.599 +  while (childFrame) {
   1.600 +    if ((index >= aFirstChildIndex) &&
   1.601 +        ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
   1.602 +         (index <= aLastChildIndex)))) {
   1.603 +      PropagatePresentationDataFor(childFrame,
   1.604 +        aFlagsValues, aFlagsToUpdate);
   1.605 +    }
   1.606 +    index++;
   1.607 +    childFrame = childFrame->GetNextSibling();
   1.608 +  }
   1.609 +}
   1.610 +
   1.611 +/* //////////////////
   1.612 + * Frame construction
   1.613 + * =============================================================================
   1.614 + */
   1.615 +
   1.616 +
   1.617 +void
   1.618 +nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   1.619 +                                         const nsRect&           aDirtyRect,
   1.620 +                                         const nsDisplayListSet& aLists)
   1.621 +{
   1.622 +  // report an error if something wrong was found in this frame
   1.623 +  if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
   1.624 +    if (!IsVisibleForPainting(aBuilder))
   1.625 +      return;
   1.626 +
   1.627 +    aLists.Content()->AppendNewToTop(
   1.628 +      new (aBuilder) nsDisplayMathMLError(aBuilder, this));
   1.629 +    return;
   1.630 +  }
   1.631 +
   1.632 +  DisplayBorderBackgroundOutline(aBuilder, aLists);
   1.633 +
   1.634 +  BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
   1.635 +                                      DISPLAY_CHILD_INLINE);
   1.636 +
   1.637 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   1.638 +  // for visual debug
   1.639 +  // ----------------
   1.640 +  // if you want to see your bounding box, make sure to properly fill
   1.641 +  // your mBoundingMetrics and mReference point, and set
   1.642 +  // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
   1.643 +  // in the Init() of your sub-class
   1.644 +  DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
   1.645 +#endif
   1.646 +}
   1.647 +
   1.648 +// Note that this method re-builds the automatic data in the children -- not
   1.649 +// in aParentFrame itself (except for those particular operations that the
   1.650 +// parent frame may do in its TransmitAutomaticData()).
   1.651 +/* static */ void
   1.652 +nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
   1.653 +{
   1.654 +  // 1. As we descend the tree, make each child frame inherit data from
   1.655 +  // the parent
   1.656 +  // 2. As we ascend the tree, transmit any specific change that we want
   1.657 +  // down the subtrees
   1.658 +  nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
   1.659 +  while (childFrame) {
   1.660 +    nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
   1.661 +    if (childMathMLFrame) {
   1.662 +      childMathMLFrame->InheritAutomaticData(aParentFrame);
   1.663 +    }
   1.664 +    RebuildAutomaticDataForChildren(childFrame);
   1.665 +    childFrame = childFrame->GetNextSibling();
   1.666 +  }
   1.667 +  nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
   1.668 +  if (mathMLFrame) {
   1.669 +    mathMLFrame->TransmitAutomaticData();
   1.670 +  }
   1.671 +}
   1.672 +
   1.673 +/* static */ nsresult
   1.674 +nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
   1.675 +{
   1.676 +  if (!aParentFrame)
   1.677 +    return NS_OK;
   1.678 +
   1.679 +  // walk-up to the first frame that is a MathML frame, stop if we reach <math>
   1.680 +  nsIFrame* frame = aParentFrame;
   1.681 +  while (1) {
   1.682 +     nsIFrame* parent = frame->GetParent();
   1.683 +     if (!parent || !parent->GetContent())
   1.684 +       break;
   1.685 +
   1.686 +    // stop if it is a MathML frame
   1.687 +    nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
   1.688 +    if (mathMLFrame)
   1.689 +      break;
   1.690 +
   1.691 +    // stop if we reach the root <math> tag
   1.692 +    nsIContent* content = frame->GetContent();
   1.693 +    NS_ASSERTION(content, "dangling frame without a content node");
   1.694 +    if (!content)
   1.695 +      break;
   1.696 +    if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
   1.697 +        content->Tag() == nsGkAtoms::math)
   1.698 +      break;
   1.699 +
   1.700 +    frame = parent;
   1.701 +  }
   1.702 +
   1.703 +  // re-sync the presentation data and embellishment data of our children
   1.704 +  RebuildAutomaticDataForChildren(frame);
   1.705 +
   1.706 +  // Ask our parent frame to reflow us
   1.707 +  nsIFrame* parent = frame->GetParent();
   1.708 +  NS_ASSERTION(parent, "No parent to pass the reflow request up to");
   1.709 +  if (!parent)
   1.710 +    return NS_OK;
   1.711 +
   1.712 +  frame->PresContext()->PresShell()->
   1.713 +    FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.714 +
   1.715 +  return NS_OK;
   1.716 +}
   1.717 +
   1.718 +// There are precise rules governing children of a MathML frame,
   1.719 +// and properties such as the scriptlevel depends on those rules.
   1.720 +// Hence for things to work, callers must use Append/Insert/etc wisely.
   1.721 +
   1.722 +nsresult
   1.723 +nsMathMLContainerFrame::ChildListChanged(int32_t aModType)
   1.724 +{
   1.725 +  // If this is an embellished frame we need to rebuild the
   1.726 +  // embellished hierarchy by walking-up to the parent of the
   1.727 +  // outermost embellished container.
   1.728 +  nsIFrame* frame = this;
   1.729 +  if (mEmbellishData.coreFrame) {
   1.730 +    nsIFrame* parent = mParent;
   1.731 +    nsEmbellishData embellishData;
   1.732 +    for ( ; parent; frame = parent, parent = parent->GetParent()) {
   1.733 +      GetEmbellishDataFrom(parent, embellishData);
   1.734 +      if (embellishData.coreFrame != mEmbellishData.coreFrame)
   1.735 +        break;
   1.736 +    }
   1.737 +  }
   1.738 +  return ReLayoutChildren(frame);
   1.739 +}
   1.740 +
   1.741 +nsresult
   1.742 +nsMathMLContainerFrame::AppendFrames(ChildListID     aListID,
   1.743 +                                     nsFrameList&    aFrameList)
   1.744 +{
   1.745 +  if (aListID != kPrincipalList) {
   1.746 +    return NS_ERROR_INVALID_ARG;
   1.747 +  }
   1.748 +  mFrames.AppendFrames(this, aFrameList);
   1.749 +  return ChildListChanged(nsIDOMMutationEvent::ADDITION);
   1.750 +}
   1.751 +
   1.752 +nsresult
   1.753 +nsMathMLContainerFrame::InsertFrames(ChildListID     aListID,
   1.754 +                                     nsIFrame*       aPrevFrame,
   1.755 +                                     nsFrameList&    aFrameList)
   1.756 +{
   1.757 +  if (aListID != kPrincipalList) {
   1.758 +    return NS_ERROR_INVALID_ARG;
   1.759 +  }
   1.760 +  // Insert frames after aPrevFrame
   1.761 +  mFrames.InsertFrames(this, aPrevFrame, aFrameList);
   1.762 +  return ChildListChanged(nsIDOMMutationEvent::ADDITION);
   1.763 +}
   1.764 +
   1.765 +nsresult
   1.766 +nsMathMLContainerFrame::RemoveFrame(ChildListID     aListID,
   1.767 +                                    nsIFrame*       aOldFrame)
   1.768 +{
   1.769 +  if (aListID != kPrincipalList) {
   1.770 +    return NS_ERROR_INVALID_ARG;
   1.771 +  }
   1.772 +  // remove the child frame
   1.773 +  mFrames.DestroyFrame(aOldFrame);
   1.774 +  return ChildListChanged(nsIDOMMutationEvent::REMOVAL);
   1.775 +}
   1.776 +
   1.777 +nsresult
   1.778 +nsMathMLContainerFrame::AttributeChanged(int32_t         aNameSpaceID,
   1.779 +                                         nsIAtom*        aAttribute,
   1.780 +                                         int32_t         aModType)
   1.781 +{
   1.782 +  // XXX Since they are numerous MathML attributes that affect layout, and
   1.783 +  // we can't check all of them here, play safe by requesting a reflow.
   1.784 +  // XXXldb This should only do work for attributes that cause changes!
   1.785 +  PresContext()->PresShell()->
   1.786 +    FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.787 +
   1.788 +  return NS_OK;
   1.789 +}
   1.790 +
   1.791 +void
   1.792 +nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
   1.793 +{
   1.794 +  // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
   1.795 +  // frame rectangle.
   1.796 +  aMetrics->SetOverflowAreasToDesiredBounds();
   1.797 +
   1.798 +  // All non-child-frame content such as nsMathMLChars (and most child-frame
   1.799 +  // content) is included in mBoundingMetrics.
   1.800 +  nsRect boundingBox(mBoundingMetrics.leftBearing,
   1.801 +                     aMetrics->TopAscent() - mBoundingMetrics.ascent,
   1.802 +                     mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
   1.803 +                     mBoundingMetrics.ascent + mBoundingMetrics.descent);
   1.804 +
   1.805 +  // REVIEW: Maybe this should contribute only to visual overflow
   1.806 +  // and not scrollable?
   1.807 +  aMetrics->mOverflowAreas.UnionAllWith(boundingBox);
   1.808 +
   1.809 +  // mBoundingMetrics does not necessarily include content of <mpadded>
   1.810 +  // elements whose mBoundingMetrics may not be representative of the true
   1.811 +  // bounds, and doesn't include the CSS2 outline rectangles of children, so
   1.812 +  // make such to include child overflow areas.
   1.813 +  nsIFrame* childFrame = mFrames.FirstChild();
   1.814 +  while (childFrame) {
   1.815 +    ConsiderChildOverflow(aMetrics->mOverflowAreas, childFrame);
   1.816 +    childFrame = childFrame->GetNextSibling();
   1.817 +  }
   1.818 +
   1.819 +  FinishAndStoreOverflow(aMetrics);
   1.820 +}
   1.821 +
   1.822 +bool
   1.823 +nsMathMLContainerFrame::UpdateOverflow()
   1.824 +{
   1.825 +  // Our overflow areas may have changed, so reflow the frame.
   1.826 +  PresContext()->PresShell()->FrameNeedsReflow(
   1.827 +    this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
   1.828 +
   1.829 +  // As we're reflowing, there's no need to propagate this change.
   1.830 +  return false;
   1.831 +}
   1.832 +
   1.833 +nsresult 
   1.834 +nsMathMLContainerFrame::ReflowChild(nsIFrame*                aChildFrame,
   1.835 +                                    nsPresContext*           aPresContext,
   1.836 +                                    nsHTMLReflowMetrics&     aDesiredSize,
   1.837 +                                    const nsHTMLReflowState& aReflowState,
   1.838 +                                    nsReflowStatus&          aStatus)
   1.839 +{
   1.840 +  // Having foreign/hybrid children, e.g., from html markups, is not defined by
   1.841 +  // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
   1.842 +  // to do some cool demos... or we may have a child that is an nsInlineFrame
   1.843 +  // from a generated content such as :before { content: open-quote } or 
   1.844 +  // :after { content: close-quote }. Unfortunately, the other frames out-there
   1.845 +  // may expect their own invariants that are not met when we mix things.
   1.846 +  // Hence we do not claim their support, but we will nevertheless attempt to keep
   1.847 +  // them in the flow, if we can get their desired size. We observed that most
   1.848 +  // frames may be reflowed generically, but nsInlineFrames need extra care.
   1.849 +
   1.850 +#ifdef DEBUG
   1.851 +  nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
   1.852 +  NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
   1.853 +#endif
   1.854 +  
   1.855 +  nsresult rv = nsContainerFrame::
   1.856 +         ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
   1.857 +                     0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
   1.858 +
   1.859 +  if (NS_FAILED(rv))
   1.860 +    return rv;
   1.861 +
   1.862 +  if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
   1.863 +    // This will be suitable for inline frames, which are wrapped in a block.
   1.864 +    nscoord ascent;
   1.865 +    if (!nsLayoutUtils::GetLastLineBaseline(aChildFrame,
   1.866 +                                            &ascent)) {
   1.867 +      // We don't expect any other block children so just place the frame on
   1.868 +      // the baseline instead of going through DidReflow() and
   1.869 +      // GetBaseline().  This is what nsFrame::GetBaseline() will do anyway.
   1.870 +      aDesiredSize.SetTopAscent(aDesiredSize.Height());
   1.871 +    } else {
   1.872 +      aDesiredSize.SetTopAscent(ascent);
   1.873 +    }
   1.874 +  }
   1.875 +  if (IsForeignChild(aChildFrame)) {
   1.876 +    // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
   1.877 +    nsRect r = aChildFrame->ComputeTightBounds(aReflowState.rendContext->ThebesContext());
   1.878 +    aDesiredSize.mBoundingMetrics.leftBearing = r.x;
   1.879 +    aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
   1.880 +    aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.TopAscent() - r.y;
   1.881 +    aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.TopAscent();
   1.882 +    aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
   1.883 +  }
   1.884 +  return rv;
   1.885 +}
   1.886 +
   1.887 +nsresult
   1.888 +nsMathMLContainerFrame::Reflow(nsPresContext*           aPresContext,
   1.889 +                               nsHTMLReflowMetrics&     aDesiredSize,
   1.890 +                               const nsHTMLReflowState& aReflowState,
   1.891 +                               nsReflowStatus&          aStatus)
   1.892 +{
   1.893 +  aDesiredSize.Width() = aDesiredSize.Height() = 0;
   1.894 +  aDesiredSize.SetTopAscent(0);
   1.895 +  aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
   1.896 +
   1.897 +  /////////////
   1.898 +  // Reflow children
   1.899 +  // Asking each child to cache its bounding metrics
   1.900 +
   1.901 +  nsReflowStatus childStatus;
   1.902 +  nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
   1.903 +  nsIFrame* childFrame = mFrames.FirstChild();
   1.904 +  while (childFrame) {
   1.905 +    nsHTMLReflowMetrics childDesiredSize(aReflowState, // ???
   1.906 +                                         aDesiredSize.mFlags);
   1.907 +    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
   1.908 +                                       childFrame, availSize);
   1.909 +    nsresult rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
   1.910 +                              childReflowState, childStatus);
   1.911 +    //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
   1.912 +    if (NS_FAILED(rv)) {
   1.913 +      // Call DidReflow() for the child frames we successfully did reflow.
   1.914 +      DidReflowChildren(mFrames.FirstChild(), childFrame);
   1.915 +      return rv;
   1.916 +    }
   1.917 +
   1.918 +    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
   1.919 +                                    childDesiredSize.mBoundingMetrics);
   1.920 +    childFrame = childFrame->GetNextSibling();
   1.921 +  }
   1.922 +
   1.923 +  /////////////
   1.924 +  // If we are a container which is entitled to stretch its children, then we
   1.925 +  // ask our stretchy children to stretch themselves
   1.926 +
   1.927 +  // The stretching of siblings of an embellished child is _deferred_ until
   1.928 +  // after finishing the stretching of the embellished child - bug 117652
   1.929 +
   1.930 +  if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
   1.931 +      (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   1.932 +       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
   1.933 +
   1.934 +    // get the stretchy direction
   1.935 +    nsStretchDirection stretchDir =
   1.936 +      NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) 
   1.937 +      ? NS_STRETCH_DIRECTION_VERTICAL 
   1.938 +      : NS_STRETCH_DIRECTION_HORIZONTAL;
   1.939 +
   1.940 +    // what size should we use to stretch our stretchy children
   1.941 +    // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
   1.942 +    // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
   1.943 +    // include them in the caculations of the size of stretchy elements
   1.944 +    nsBoundingMetrics containerSize;
   1.945 +    GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
   1.946 +                            containerSize);
   1.947 +
   1.948 +    // fire the stretch on each child
   1.949 +    childFrame = mFrames.FirstChild();
   1.950 +    while (childFrame) {
   1.951 +      nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
   1.952 +      if (mathMLFrame) {
   1.953 +        // retrieve the metrics that was stored at the previous pass
   1.954 +        nsHTMLReflowMetrics childDesiredSize(aReflowState);
   1.955 +        GetReflowAndBoundingMetricsFor(childFrame,
   1.956 +          childDesiredSize, childDesiredSize.mBoundingMetrics);
   1.957 +
   1.958 +        mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
   1.959 +                             containerSize, childDesiredSize);
   1.960 +        // store the updated metrics
   1.961 +        SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
   1.962 +                                        childDesiredSize.mBoundingMetrics);
   1.963 +      }
   1.964 +      childFrame = childFrame->GetNextSibling();
   1.965 +    }
   1.966 +  }
   1.967 +
   1.968 +  /////////////
   1.969 +  // Place children now by re-adjusting the origins to align the baselines
   1.970 +  FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
   1.971 +
   1.972 +  aStatus = NS_FRAME_COMPLETE;
   1.973 +  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   1.974 +  return NS_OK;
   1.975 +}
   1.976 +
   1.977 +static nscoord AddInterFrameSpacingToSize(nsHTMLReflowMetrics&    aDesiredSize,
   1.978 +                                          nsMathMLContainerFrame* aFrame);
   1.979 +
   1.980 +/* virtual */ nscoord
   1.981 +nsMathMLContainerFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   1.982 +{
   1.983 +  nscoord result;
   1.984 +  DISPLAY_MIN_WIDTH(this, result);
   1.985 +  nsHTMLReflowMetrics desiredSize(GetWritingMode());
   1.986 +  GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
   1.987 +
   1.988 +  // Include the additional width added by FixInterFrameSpacing to ensure
   1.989 +  // consistent width calculations.
   1.990 +  AddInterFrameSpacingToSize(desiredSize, this);
   1.991 +  result = desiredSize.Width();
   1.992 +  return result;
   1.993 +}
   1.994 +
   1.995 +/* virtual */ nscoord
   1.996 +nsMathMLContainerFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   1.997 +{
   1.998 +  nscoord result;
   1.999 +  DISPLAY_PREF_WIDTH(this, result);
  1.1000 +  nsHTMLReflowMetrics desiredSize(GetWritingMode());
  1.1001 +  GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
  1.1002 +
  1.1003 +  // Include the additional width added by FixInterFrameSpacing to ensure
  1.1004 +  // consistent width calculations.
  1.1005 +  AddInterFrameSpacingToSize(desiredSize, this);
  1.1006 +  result = desiredSize.Width();
  1.1007 +  return result;
  1.1008 +}
  1.1009 +
  1.1010 +/* virtual */ void
  1.1011 +nsMathMLContainerFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
  1.1012 +{
  1.1013 +  // Get child widths
  1.1014 +  nsIFrame* childFrame = mFrames.FirstChild();
  1.1015 +  while (childFrame) {
  1.1016 +    nsHTMLReflowMetrics childDesiredSize(GetWritingMode()); // ???
  1.1017 +
  1.1018 +    nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
  1.1019 +    if (containerFrame) {
  1.1020 +      containerFrame->GetIntrinsicWidthMetrics(aRenderingContext,
  1.1021 +                                               childDesiredSize);
  1.1022 +    } else {
  1.1023 +      // XXX This includes margin while Reflow currently doesn't consider
  1.1024 +      // margin, so we may end up with too much space, but, with stretchy
  1.1025 +      // characters, this is an approximation anyway.
  1.1026 +      nscoord width =
  1.1027 +        nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
  1.1028 +                                             nsLayoutUtils::PREF_WIDTH);
  1.1029 +
  1.1030 +      childDesiredSize.Width() = width;
  1.1031 +      childDesiredSize.mBoundingMetrics.width = width;
  1.1032 +      childDesiredSize.mBoundingMetrics.leftBearing = 0;
  1.1033 +      childDesiredSize.mBoundingMetrics.rightBearing = width;
  1.1034 +
  1.1035 +      nscoord x, xMost;
  1.1036 +      if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
  1.1037 +                                                           &x, &xMost))) {
  1.1038 +        childDesiredSize.mBoundingMetrics.leftBearing = x;
  1.1039 +        childDesiredSize.mBoundingMetrics.rightBearing = xMost;
  1.1040 +      }
  1.1041 +    }
  1.1042 +
  1.1043 +    SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
  1.1044 +                                    childDesiredSize.mBoundingMetrics);
  1.1045 +
  1.1046 +    childFrame = childFrame->GetNextSibling();
  1.1047 +  }
  1.1048 +
  1.1049 +  // Measure
  1.1050 +  nsresult rv = MeasureForWidth(*aRenderingContext, aDesiredSize);
  1.1051 +  if (NS_FAILED(rv)) {
  1.1052 +    ReflowError(*aRenderingContext, aDesiredSize);
  1.1053 +  }
  1.1054 +
  1.1055 +  ClearSavedChildMetrics();
  1.1056 +}
  1.1057 +
  1.1058 +/* virtual */ nsresult
  1.1059 +nsMathMLContainerFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
  1.1060 +                                        nsHTMLReflowMetrics& aDesiredSize)
  1.1061 +{
  1.1062 +  return Place(aRenderingContext, false, aDesiredSize);
  1.1063 +}
  1.1064 +
  1.1065 +
  1.1066 +// see spacing table in Chapter 18, TeXBook (p.170)
  1.1067 +// Our table isn't quite identical to TeX because operators have 
  1.1068 +// built-in values for lspace & rspace in the Operator Dictionary.
  1.1069 +static int32_t kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
  1.1070 +{
  1.1071 +  // in units of muspace.
  1.1072 +  // upper half of the byte is set if the
  1.1073 +  // spacing is not to be used for scriptlevel > 0
  1.1074 +
  1.1075 +  /*           Ord  OpOrd OpInv OpUsr Inner Italic Upright */
  1.1076 +  /*Ord  */   {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
  1.1077 +  /*OpOrd*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  1.1078 +  /*OpInv*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  1.1079 +  /*OpUsr*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
  1.1080 +  /*Inner*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
  1.1081 +  /*Italic*/  {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
  1.1082 +  /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}
  1.1083 +};
  1.1084 +
  1.1085 +#define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_)  \
  1.1086 +   /* no space if there is a frame that we know nothing about */        \
  1.1087 +   if (frametype1_ == eMathMLFrameType_UNKNOWN ||                       \
  1.1088 +       frametype2_ == eMathMLFrameType_UNKNOWN)                         \
  1.1089 +    space_ = 0;                                                         \
  1.1090 +  else {                                                                \
  1.1091 +    space_ = kInterFrameSpacingTable[frametype1_][frametype2_];         \
  1.1092 +    space_ = (scriptlevel_ > 0 && (space_ & 0xF0))                      \
  1.1093 +      ? 0 /* spacing is disabled */                                     \
  1.1094 +      : space_ & 0x0F;                                                  \
  1.1095 +  }                                                                     \
  1.1096 +
  1.1097 +// This function computes the inter-space between two frames. However, 
  1.1098 +// since invisible operators need special treatment, the inter-space may
  1.1099 +// be delayed when an invisible operator is encountered. In this case,
  1.1100 +// the function will carry the inter-space forward until it is determined
  1.1101 +// that it can be applied properly (i.e., until we encounter a visible
  1.1102 +// frame where to decide whether to accept or reject the inter-space).
  1.1103 +// aFromFrameType: remembers the frame when the carry-forward initiated.
  1.1104 +// aCarrySpace: keeps track of the inter-space that is delayed.
  1.1105 +// @returns: current inter-space (which is 0 when the true inter-space is
  1.1106 +// delayed -- and thus has no effect since the frame is invisible anyway).
  1.1107 +static nscoord
  1.1108 +GetInterFrameSpacing(int32_t           aScriptLevel,
  1.1109 +                     eMathMLFrameType  aFirstFrameType,
  1.1110 +                     eMathMLFrameType  aSecondFrameType,
  1.1111 +                     eMathMLFrameType* aFromFrameType, // IN/OUT
  1.1112 +                     int32_t*          aCarrySpace)    // IN/OUT
  1.1113 +{
  1.1114 +  eMathMLFrameType firstType = aFirstFrameType;
  1.1115 +  eMathMLFrameType secondType = aSecondFrameType;
  1.1116 +
  1.1117 +  int32_t space;
  1.1118 +  GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
  1.1119 +
  1.1120 +  // feedback control to avoid the inter-space to be added when not necessary
  1.1121 +  if (secondType == eMathMLFrameType_OperatorInvisible) {
  1.1122 +    // see if we should start to carry the space forward until we
  1.1123 +    // encounter a visible frame
  1.1124 +    if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
  1.1125 +      *aFromFrameType = firstType;
  1.1126 +      *aCarrySpace = space;
  1.1127 +    }
  1.1128 +    // keep carrying *aCarrySpace forward, while returning 0 for this stage
  1.1129 +    space = 0;
  1.1130 +  }
  1.1131 +  else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
  1.1132 +    // no carry-forward anymore, get the real inter-space between
  1.1133 +    // the two frames of interest
  1.1134 +
  1.1135 +    firstType = *aFromFrameType;
  1.1136 +
  1.1137 +    // But... the invisible operator that we encountered earlier could
  1.1138 +    // be sitting between italic and upright identifiers, e.g.,
  1.1139 +    //
  1.1140 +    // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
  1.1141 +    // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
  1.1142 +    //
  1.1143 +    // the trick to get the inter-space in either situation
  1.1144 +    // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
  1.1145 +    // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
  1.1146 +    if (firstType == eMathMLFrameType_UprightIdentifier) {
  1.1147 +      firstType = eMathMLFrameType_OperatorUserDefined;
  1.1148 +    }
  1.1149 +    else if (secondType == eMathMLFrameType_UprightIdentifier) {
  1.1150 +      secondType = eMathMLFrameType_OperatorUserDefined;
  1.1151 +    }
  1.1152 +
  1.1153 +    GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
  1.1154 +
  1.1155 +    // Now, we have two values: the computed space and the space that
  1.1156 +    // has been carried forward until now. Which value do we pick?
  1.1157 +    // If the second type is an operator (e.g., fence), it already has
  1.1158 +    // built-in lspace & rspace, so we let them win. Otherwise we pick
  1.1159 +    // the max between the two values that we have.
  1.1160 +    if (secondType != eMathMLFrameType_OperatorOrdinary &&
  1.1161 +        space < *aCarrySpace)
  1.1162 +      space = *aCarrySpace;
  1.1163 +
  1.1164 +    // reset everything now that the carry-forward is done
  1.1165 +    *aFromFrameType = eMathMLFrameType_UNKNOWN;
  1.1166 +    *aCarrySpace = 0;
  1.1167 +  }
  1.1168 +
  1.1169 +  return space;
  1.1170 +}
  1.1171 +
  1.1172 +static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
  1.1173 +{
  1.1174 +  return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
  1.1175 +}
  1.1176 +
  1.1177 +class nsMathMLContainerFrame::RowChildFrameIterator {
  1.1178 +public:
  1.1179 +  explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
  1.1180 +    mParentFrame(aParentFrame),
  1.1181 +    mSize(aParentFrame->GetWritingMode()), // ???
  1.1182 +    mX(0),
  1.1183 +    mCarrySpace(0),
  1.1184 +    mFromFrameType(eMathMLFrameType_UNKNOWN),
  1.1185 +    mRTL(aParentFrame->StyleVisibility()->mDirection)
  1.1186 +  {
  1.1187 +    if (!mRTL) {
  1.1188 +      mChildFrame = aParentFrame->mFrames.FirstChild();
  1.1189 +    } else {
  1.1190 +      mChildFrame = aParentFrame->mFrames.LastChild();
  1.1191 +    }
  1.1192 +
  1.1193 +    if (!mChildFrame)
  1.1194 +      return;
  1.1195 +
  1.1196 +    InitMetricsForChild();
  1.1197 +  }
  1.1198 +
  1.1199 +  RowChildFrameIterator& operator++()
  1.1200 +  {
  1.1201 +    // add child size + italic correction
  1.1202 +    mX += mSize.mBoundingMetrics.width + mItalicCorrection;
  1.1203 +
  1.1204 +    if (!mRTL) {
  1.1205 +      mChildFrame = mChildFrame->GetNextSibling();
  1.1206 +    } else {
  1.1207 +      mChildFrame = mChildFrame->GetPrevSibling();
  1.1208 +    }
  1.1209 +
  1.1210 +    if (!mChildFrame)
  1.1211 +      return *this;
  1.1212 +
  1.1213 +    eMathMLFrameType prevFrameType = mChildFrameType;
  1.1214 +    InitMetricsForChild();
  1.1215 +
  1.1216 +    // add inter frame spacing
  1.1217 +    const nsStyleFont* font = mParentFrame->StyleFont();
  1.1218 +    nscoord space =
  1.1219 +      GetInterFrameSpacing(font->mScriptLevel,
  1.1220 +                           prevFrameType, mChildFrameType,
  1.1221 +                           &mFromFrameType, &mCarrySpace);
  1.1222 +    mX += space * GetThinSpace(font);
  1.1223 +    return *this;
  1.1224 +  }
  1.1225 +
  1.1226 +  nsIFrame* Frame() const { return mChildFrame; }
  1.1227 +  nscoord X() const { return mX; }
  1.1228 +  const nsHTMLReflowMetrics& ReflowMetrics() const { return mSize; }
  1.1229 +  nscoord Ascent() const { return mSize.TopAscent(); }
  1.1230 +  nscoord Descent() const { return mSize.Height() - mSize.TopAscent(); }
  1.1231 +  const nsBoundingMetrics& BoundingMetrics() const {
  1.1232 +    return mSize.mBoundingMetrics;
  1.1233 +  }
  1.1234 +
  1.1235 +private:
  1.1236 +  const nsMathMLContainerFrame* mParentFrame;
  1.1237 +  nsIFrame* mChildFrame;
  1.1238 +  nsHTMLReflowMetrics mSize;
  1.1239 +  nscoord mX;
  1.1240 +
  1.1241 +  nscoord mItalicCorrection;
  1.1242 +  eMathMLFrameType mChildFrameType;
  1.1243 +  int32_t mCarrySpace;
  1.1244 +  eMathMLFrameType mFromFrameType;
  1.1245 +
  1.1246 +  bool mRTL;
  1.1247 +
  1.1248 +  void InitMetricsForChild()
  1.1249 +  {
  1.1250 +    GetReflowAndBoundingMetricsFor(mChildFrame, mSize, mSize.mBoundingMetrics,
  1.1251 +                                   &mChildFrameType);
  1.1252 +    nscoord leftCorrection, rightCorrection;
  1.1253 +    GetItalicCorrection(mSize.mBoundingMetrics,
  1.1254 +                        leftCorrection, rightCorrection);
  1.1255 +    if (!mChildFrame->GetPrevSibling() &&
  1.1256 +        mParentFrame->GetContent()->Tag() == nsGkAtoms::msqrt_) {
  1.1257 +      // Remove leading correction in <msqrt> because the sqrt glyph itself is
  1.1258 +      // there first.
  1.1259 +      if (!mRTL) {
  1.1260 +        leftCorrection = 0;
  1.1261 +      } else {
  1.1262 +        rightCorrection = 0;
  1.1263 +      }
  1.1264 +    }
  1.1265 +    // add left correction -- this fixes the problem of the italic 'f'
  1.1266 +    // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo> 
  1.1267 +    mX += leftCorrection;
  1.1268 +    mItalicCorrection = rightCorrection;
  1.1269 +  }
  1.1270 +};
  1.1271 +
  1.1272 +/* virtual */ nsresult
  1.1273 +nsMathMLContainerFrame::Place(nsRenderingContext& aRenderingContext,
  1.1274 +                              bool                 aPlaceOrigin,
  1.1275 +                              nsHTMLReflowMetrics& aDesiredSize)
  1.1276 +{
  1.1277 +  // This is needed in case this frame is empty (i.e., no child frames)
  1.1278 +  mBoundingMetrics = nsBoundingMetrics();
  1.1279 +
  1.1280 +  RowChildFrameIterator child(this);
  1.1281 +  nscoord ascent = 0, descent = 0;
  1.1282 +  while (child.Frame()) {
  1.1283 +    if (descent < child.Descent())
  1.1284 +      descent = child.Descent();
  1.1285 +    if (ascent < child.Ascent())
  1.1286 +      ascent = child.Ascent();
  1.1287 +    // add the child size
  1.1288 +    mBoundingMetrics.width = child.X();
  1.1289 +    mBoundingMetrics += child.BoundingMetrics();
  1.1290 +    ++child;
  1.1291 +  }
  1.1292 +  // Add the italic correction at the end (including the last child).
  1.1293 +  // This gives a nice gap between math and non-math frames, and still
  1.1294 +  // gives the same math inter-spacing in case this frame connects to
  1.1295 +  // another math frame
  1.1296 +  mBoundingMetrics.width = child.X();
  1.1297 +
  1.1298 +  aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
  1.1299 +  aDesiredSize.Height() = ascent + descent;
  1.1300 +  aDesiredSize.SetTopAscent(ascent);
  1.1301 +  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  1.1302 +
  1.1303 +  mReference.x = 0;
  1.1304 +  mReference.y = aDesiredSize.TopAscent();
  1.1305 +
  1.1306 +  //////////////////
  1.1307 +  // Place Children
  1.1308 +
  1.1309 +  if (aPlaceOrigin) {
  1.1310 +    PositionRowChildFrames(0, aDesiredSize.TopAscent());
  1.1311 +  }
  1.1312 +
  1.1313 +  return NS_OK;
  1.1314 +}
  1.1315 +
  1.1316 +void
  1.1317 +nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
  1.1318 +                                               nscoord aBaseline)
  1.1319 +{
  1.1320 +  RowChildFrameIterator child(this);
  1.1321 +  while (child.Frame()) {
  1.1322 +    nscoord dx = aOffsetX + child.X();
  1.1323 +    nscoord dy = aBaseline - child.Ascent();
  1.1324 +    FinishReflowChild(child.Frame(), PresContext(), child.ReflowMetrics(),
  1.1325 +                      nullptr, dx, dy, 0);
  1.1326 +    ++child;
  1.1327 +  }
  1.1328 +}
  1.1329 +
  1.1330 +class ForceReflow : public nsIReflowCallback {
  1.1331 +public:
  1.1332 +  virtual bool ReflowFinished() MOZ_OVERRIDE {
  1.1333 +    return true;
  1.1334 +  }
  1.1335 +  virtual void ReflowCallbackCanceled() MOZ_OVERRIDE {}
  1.1336 +};
  1.1337 +
  1.1338 +// We only need one of these so we just make it a static global, no need
  1.1339 +// to dynamically allocate/destroy it.
  1.1340 +static ForceReflow gForceReflow;
  1.1341 +
  1.1342 +void
  1.1343 +nsMathMLContainerFrame::SetIncrementScriptLevel(int32_t aChildIndex, bool aIncrement)
  1.1344 +{
  1.1345 +  nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
  1.1346 +  if (!child)
  1.1347 +    return;
  1.1348 +  nsIContent* content = child->GetContent();
  1.1349 +  if (!content->IsMathML())
  1.1350 +    return;
  1.1351 +  nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
  1.1352 +
  1.1353 +  if (element->GetIncrementScriptLevel() == aIncrement)
  1.1354 +    return;
  1.1355 +
  1.1356 +  // XXXroc this does a ContentStatesChanged, is it safe to call here? If
  1.1357 +  // not we should do it in a post-reflow callback.
  1.1358 +  element->SetIncrementScriptLevel(aIncrement, true);
  1.1359 +  PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
  1.1360 +}
  1.1361 +
  1.1362 +// helpers to fix the inter-spacing when <math> is the only parent
  1.1363 +// e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
  1.1364 +
  1.1365 +static nscoord
  1.1366 +GetInterFrameSpacingFor(int32_t         aScriptLevel,
  1.1367 +                        nsIFrame*       aParentFrame,
  1.1368 +                        nsIFrame*       aChildFrame)
  1.1369 +{
  1.1370 +  nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
  1.1371 +  if (!childFrame || aChildFrame == childFrame)
  1.1372 +    return 0;
  1.1373 +
  1.1374 +  int32_t carrySpace = 0;
  1.1375 +  eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
  1.1376 +  eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
  1.1377 +  eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
  1.1378 +  childFrame = childFrame->GetNextSibling();
  1.1379 +  while (childFrame) {
  1.1380 +    prevFrameType = childFrameType;
  1.1381 +    childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
  1.1382 +    nscoord space = GetInterFrameSpacing(aScriptLevel,
  1.1383 +      prevFrameType, childFrameType, &fromFrameType, &carrySpace);
  1.1384 +    if (aChildFrame == childFrame) {
  1.1385 +      // get thinspace
  1.1386 +      nsStyleContext* parentContext = aParentFrame->StyleContext();
  1.1387 +      nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
  1.1388 +      // we are done
  1.1389 +      return space * thinSpace;
  1.1390 +    }
  1.1391 +    childFrame = childFrame->GetNextSibling();
  1.1392 +  }
  1.1393 +
  1.1394 +  NS_NOTREACHED("child not in the childlist of its parent");
  1.1395 +  return 0;
  1.1396 +}
  1.1397 +
  1.1398 +static nscoord
  1.1399 +AddInterFrameSpacingToSize(nsHTMLReflowMetrics&    aDesiredSize,
  1.1400 +                           nsMathMLContainerFrame* aFrame)
  1.1401 +{
  1.1402 +  nscoord gap = 0;
  1.1403 +  nsIFrame* parent = aFrame->GetParent();
  1.1404 +  nsIContent* parentContent = parent->GetContent();
  1.1405 +  if (MOZ_UNLIKELY(!parentContent)) {
  1.1406 +    return 0;
  1.1407 +  }
  1.1408 +  nsIAtom *parentTag = parentContent->Tag();
  1.1409 +  if (parentContent->GetNameSpaceID() == kNameSpaceID_MathML && 
  1.1410 +      (parentTag == nsGkAtoms::math || parentTag == nsGkAtoms::mtd_)) {
  1.1411 +    gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mScriptLevel,
  1.1412 +                                  parent, aFrame);
  1.1413 +    // add our own italic correction
  1.1414 +    nscoord leftCorrection = 0, italicCorrection = 0;
  1.1415 +    aFrame->GetItalicCorrection(aDesiredSize.mBoundingMetrics,
  1.1416 +                                leftCorrection, italicCorrection);
  1.1417 +    gap += leftCorrection;
  1.1418 +    if (gap) {
  1.1419 +      aDesiredSize.mBoundingMetrics.leftBearing += gap;
  1.1420 +      aDesiredSize.mBoundingMetrics.rightBearing += gap;
  1.1421 +      aDesiredSize.mBoundingMetrics.width += gap;
  1.1422 +      aDesiredSize.Width() += gap;
  1.1423 +    }
  1.1424 +    aDesiredSize.mBoundingMetrics.width += italicCorrection;
  1.1425 +    aDesiredSize.Width() += italicCorrection;
  1.1426 +  }
  1.1427 +  return gap;
  1.1428 +}
  1.1429 +
  1.1430 +nscoord
  1.1431 +nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
  1.1432 +{
  1.1433 +  nscoord gap = 0;
  1.1434 +  gap = AddInterFrameSpacingToSize(aDesiredSize, this);
  1.1435 +  if (gap) {
  1.1436 +    // Shift our children to account for the correction
  1.1437 +    nsIFrame* childFrame = mFrames.FirstChild();
  1.1438 +    while (childFrame) {
  1.1439 +      childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
  1.1440 +      childFrame = childFrame->GetNextSibling();
  1.1441 +    }
  1.1442 +  }
  1.1443 +  return gap;
  1.1444 +}
  1.1445 +
  1.1446 +/* static */ void
  1.1447 +nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
  1.1448 +
  1.1449 +{
  1.1450 +  if (MOZ_UNLIKELY(!aFirst))
  1.1451 +    return;
  1.1452 +
  1.1453 +  for (nsIFrame* frame = aFirst;
  1.1454 +       frame != aStop;
  1.1455 +       frame = frame->GetNextSibling()) {
  1.1456 +    NS_ASSERTION(frame, "aStop isn't a sibling");
  1.1457 +    if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
  1.1458 +      // finish off principal descendants, too
  1.1459 +      nsIFrame* grandchild = frame->GetFirstPrincipalChild();
  1.1460 +      if (grandchild)
  1.1461 +        DidReflowChildren(grandchild, nullptr);
  1.1462 +
  1.1463 +      frame->DidReflow(frame->PresContext(), nullptr,
  1.1464 +                       nsDidReflowStatus::FINISHED);
  1.1465 +    }
  1.1466 +  }
  1.1467 +}
  1.1468 +
  1.1469 +// helper used by mstyle, mphantom, mpadded and mrow in their implementations
  1.1470 +// of TransmitAutomaticData().
  1.1471 +nsresult
  1.1472 +nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement()
  1.1473 +{
  1.1474 +  //
  1.1475 +  // One loop to check both conditions below:
  1.1476 +  //
  1.1477 +  // 1) whether all the children of the mrow-like element are space-like.
  1.1478 +  //
  1.1479 +  //   The REC defines the following elements to be "space-like":
  1.1480 +  //   * an mstyle, mphantom, or mpadded element, all of whose direct
  1.1481 +  //     sub-expressions are space-like;
  1.1482 +  //   * an mrow all of whose direct sub-expressions are space-like.
  1.1483 +  //
  1.1484 +  // 2) whether all but one child of the mrow-like element are space-like and
  1.1485 +  //    this non-space-like child is an embellished operator.
  1.1486 +  //
  1.1487 +  //   The REC defines the following elements to be embellished operators:
  1.1488 +  //   * one of the elements mstyle, mphantom, or mpadded, such that an mrow
  1.1489 +  //     containing the same arguments would be an embellished operator;
  1.1490 +  //   * an mrow whose arguments consist (in any order) of one embellished
  1.1491 +  //     operator and zero or more space-like elements.
  1.1492 +  //
  1.1493 +  nsIFrame *childFrame, *baseFrame;
  1.1494 +  bool embellishedOpFound = false;
  1.1495 +  nsEmbellishData embellishData;
  1.1496 +  
  1.1497 +  for (childFrame = GetFirstPrincipalChild();
  1.1498 +       childFrame;
  1.1499 +       childFrame = childFrame->GetNextSibling()) {
  1.1500 +    nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
  1.1501 +    if (!mathMLFrame) break;
  1.1502 +    if (!mathMLFrame->IsSpaceLike()) {
  1.1503 +      if (embellishedOpFound) break;
  1.1504 +      baseFrame = childFrame;
  1.1505 +      GetEmbellishDataFrom(baseFrame, embellishData);
  1.1506 +      if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)) break;
  1.1507 +      embellishedOpFound = true;
  1.1508 +    }
  1.1509 +  }
  1.1510 +
  1.1511 +  if (!childFrame) {
  1.1512 +    // we successfully went to the end of the loop. This means that one of
  1.1513 +    // condition 1) or 2) holds.
  1.1514 +    if (!embellishedOpFound) {
  1.1515 +      // the mrow-like element is space-like.
  1.1516 +      mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
  1.1517 +    } else {
  1.1518 +      // the mrow-like element is an embellished operator.
  1.1519 +      // let the state of the embellished operator found bubble to us.
  1.1520 +      mPresentationData.baseFrame = baseFrame;
  1.1521 +      mEmbellishData = embellishData;
  1.1522 +    }
  1.1523 +  }
  1.1524 +
  1.1525 +  if (childFrame || !embellishedOpFound) {
  1.1526 +    // The element is not embellished operator
  1.1527 +    mPresentationData.baseFrame = nullptr;
  1.1528 +    mEmbellishData.flags = 0;
  1.1529 +    mEmbellishData.coreFrame = nullptr;
  1.1530 +    mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
  1.1531 +    mEmbellishData.leadingSpace = 0;
  1.1532 +    mEmbellishData.trailingSpace = 0;
  1.1533 +  }
  1.1534 +
  1.1535 +  if (childFrame || embellishedOpFound) {
  1.1536 +    // The element is not space-like
  1.1537 +    mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
  1.1538 +  }
  1.1539 +
  1.1540 +  return NS_OK;
  1.1541 +}
  1.1542 +
  1.1543 +/*static*/ void
  1.1544 +nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame,
  1.1545 +                                              nsFrameState  aFlags)
  1.1546 +{
  1.1547 +  if (!aFrame || !aFlags)
  1.1548 +    return;
  1.1549 +
  1.1550 +  aFrame->AddStateBits(aFlags);
  1.1551 +  nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
  1.1552 +  while (childFrame) {
  1.1553 +    PropagateFrameFlagFor(childFrame, aFlags);
  1.1554 +    childFrame = childFrame->GetNextSibling();
  1.1555 +  }
  1.1556 +}
  1.1557 +
  1.1558 +nsresult
  1.1559 +nsMathMLContainerFrame::ReportErrorToConsole(const char*       errorMsgId,
  1.1560 +                                             const char16_t** aParams,
  1.1561 +                                             uint32_t          aParamCount)
  1.1562 +{
  1.1563 +  return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
  1.1564 +                                         NS_LITERAL_CSTRING("MathML"), mContent->OwnerDoc(),
  1.1565 +                                         nsContentUtils::eMATHML_PROPERTIES,
  1.1566 +                                         errorMsgId, aParams, aParamCount);
  1.1567 +}
  1.1568 +
  1.1569 +nsresult
  1.1570 +nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute,
  1.1571 +                                         const char16_t* aValue)
  1.1572 +{
  1.1573 +  const char16_t* argv[] = 
  1.1574 +    { aValue, aAttribute, mContent->Tag()->GetUTF16String() };
  1.1575 +  return ReportErrorToConsole("AttributeParsingError", argv, 3);
  1.1576 +}
  1.1577 +
  1.1578 +nsresult
  1.1579 +nsMathMLContainerFrame::ReportChildCountError()
  1.1580 +{
  1.1581 +  const char16_t* arg = mContent->Tag()->GetUTF16String();
  1.1582 +  return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
  1.1583 +}
  1.1584 +
  1.1585 +nsresult
  1.1586 +nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
  1.1587 +{
  1.1588 +  const char16_t* argv[] =
  1.1589 +    { aChildTag->GetUTF16String(), mContent->Tag()->GetUTF16String() };
  1.1590 +  return ReportErrorToConsole("InvalidChild", argv, 2);
  1.1591 +}
  1.1592 +
  1.1593 +//==========================
  1.1594 +
  1.1595 +nsIFrame*
  1.1596 +NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
  1.1597 +                           nsFrameState aFlags)
  1.1598 +{
  1.1599 +  nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
  1.1600 +  it->SetFlags(aFlags);
  1.1601 +  return it;
  1.1602 +}
  1.1603 +
  1.1604 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
  1.1605 +
  1.1606 +NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
  1.1607 +  NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
  1.1608 +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
  1.1609 +
  1.1610 +nsIFrame*
  1.1611 +NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1.1612 +{
  1.1613 +  return new (aPresShell) nsMathMLmathInlineFrame(aContext);
  1.1614 +}
  1.1615 +
  1.1616 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
  1.1617 +
  1.1618 +NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
  1.1619 +  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
  1.1620 +NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)

mercurial