Bug 827713 - Part 1: Changes to mmultiscripts to support merging of script elements. r=karlt
☠☠ backed out by a59ad3157c17 ☠ ☠
authorJames Kitchener <jkitch.bug@gmail.com>
Mon, 29 Jul 2013 12:36:23 -0400
changeset 153571 b67a72618c669e643e625ab6dbfddd920d78d95b
parent 153570 1279664e0d41273c4bbee2755d4c8a17bb7c5564
child 153572 6448c7e05f11ef896eff0f70874743deec772a22
push id382
push userakeybl@mozilla.com
push dateMon, 21 Oct 2013 21:47:13 +0000
treeherdermozilla-release@5f1868ee45cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs827713
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 827713 - Part 1: Changes to mmultiscripts to support merging of script elements. r=karlt
layout/mathml/nsMathMLContainerFrame.cpp
layout/mathml/nsMathMLContainerFrame.h
layout/mathml/nsMathMLmmultiscriptsFrame.cpp
layout/mathml/nsMathMLmmultiscriptsFrame.h
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -1539,16 +1539,24 @@ nsMathMLContainerFrame::ReportParseError
 
 nsresult
 nsMathMLContainerFrame::ReportChildCountError()
 {
   const PRUnichar* arg = mContent->Tag()->GetUTF16String();
   return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
 }
 
+nsresult
+nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
+{
+  const PRUnichar* argv[] =
+    { aChildTag->GetUTF16String(), mContent->Tag()->GetUTF16String() };
+  return ReportErrorToConsole("InvalidChild", argv, 2);
+}
+
 //==========================
 
 nsIFrame*
 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
                            uint32_t aFlags)
 {
   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
   if (it) {
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -254,16 +254,24 @@ public:
   /*
    * Helper to call ReportErrorToConsole when certain tags
    * have more than the expected amount of children.
    */
   nsresult
   ReportChildCountError();
 
   /*
+   * Helper to call ReportErrorToConsole when certain tags have
+   * invalid child tags
+   * @param aChildTag The tag which is forbidden in this context
+   */
+  nsresult
+  ReportInvalidChildError(nsIAtom* aChildTag);
+
+  /*
    * Helper to call ReportToConsole when an error occurs.
    * @param aParams see nsContentUtils::ReportToConsole
    */
   nsresult
   ReportErrorToConsole(const char*       aErrorMsgId,
                        const PRUnichar** aParams = nullptr,
                        uint32_t          aParamCount = 0);
 
--- a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
+++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
@@ -11,16 +11,19 @@
 #include "nsStyleConsts.h"
 #include "nsRenderingContext.h"
 
 #include "nsMathMLmmultiscriptsFrame.h"
 #include <algorithm>
 
 //
 // <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
+// <msub> -- attach a subscript to a base - implementation
+// <msubsup> -- attach a subscript-superscript pair to a base - implementation
+// <msup> -- attach a superscript to a base - implementation
 //
 
 nsIFrame*
 NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsMathMLmmultiscriptsFrame(aContext);
 }
 
@@ -33,431 +36,549 @@ nsMathMLmmultiscriptsFrame::~nsMathMLmmu
 NS_IMETHODIMP
 nsMathMLmmultiscriptsFrame::TransmitAutomaticData()
 {
   // if our base is an embellished operator, let its state bubble to us
   mPresentationData.baseFrame = mFrames.FirstChild();
   GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
 
   // The REC says:
-  // The <mmultiscripts> element increments scriptlevel by 1, and sets
+  // The script element increments scriptlevel by 1, and sets
   // displaystyle to "false", within each of its arguments except base
   UpdatePresentationDataFromChildAt(1, -1,
     ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE);
 
   // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
   // while the subscript is compressed. So here we collect subscripts and set
   // the compression flag in them.
+
+  if (mContent->Tag() == nsGkAtoms::msup_)
+    return NS_OK;
+
   int32_t count = 0;
-  bool isSubScript = false;
+  bool isSubScript = true;
+
   nsAutoTArray<nsIFrame*, 8> subScriptFrames;
   nsIFrame* childFrame = mFrames.FirstChild();
   while (childFrame) {
     if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
       // mprescripts frame
-    }
-    else if (0 == count) {
+    } else if (0 == count) {
       // base frame
-    }
-    else {
+    } else {
       // super/subscript block
       if (isSubScript) {
         // subscript
         subScriptFrames.AppendElement(childFrame);
-      }
-      else {
+      } else {
         // superscript
       }
       isSubScript = !isSubScript;
     }
     count++;
     childFrame = childFrame->GetNextSibling();
   }
   for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) {
     childFrame = subScriptFrames[i];
     PropagatePresentationDataFor(childFrame,
       NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
   }
 
   return NS_OK;
 }
 
-void
-nsMathMLmmultiscriptsFrame::ProcessAttributes()
+/* virtual */ nsresult
+nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
+                                  bool                 aPlaceOrigin,
+                                  nsHTMLReflowMetrics& aDesiredSize)
 {
-  mSubScriptShift = 0;
-  mSupScriptShift = 0;
+  nscoord subScriptShift = 0;
+  nscoord supScriptShift = 0;
+  nsIAtom* tag = mContent->Tag();
 
   // subscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of subscript down; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
   // As a minimum, negative values can be ignored.
   //
   nsAutoString value;
-  GetAttribute(mContent, mPresentationData.mstyle,
-               nsGkAtoms::subscriptshift_, value);
-  if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSubScriptShift, 0, PresContext(),
-                      mStyleContext);
+  if (tag != nsGkAtoms::msup_) {
+    GetAttribute(mContent, mPresentationData.mstyle,
+                 nsGkAtoms::subscriptshift_, value);
+    if (!value.IsEmpty()) {
+      ParseNumericValue(value, &subScriptShift, 0, PresContext(),
+                        mStyleContext);
+    }
   }
   // superscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of superscript up; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
   // As a minimum, negative values can be ignored.
   //
-  GetAttribute(mContent, mPresentationData.mstyle,
-               nsGkAtoms::superscriptshift_, value);
-  if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSupScriptShift, 0, PresContext(),
-                      mStyleContext);
+  if (tag != nsGkAtoms::msub_) {
+    GetAttribute(mContent, mPresentationData.mstyle,
+                 nsGkAtoms::superscriptshift_, value);
+    if (!value.IsEmpty()) {
+      ParseNumericValue(value, &supScriptShift, 0, PresContext(),
+                        mStyleContext);
+    }
   }
+  // scriptspace from TeX for extra spacing after sup/subscript 
+  // (0.5pt in plain TeX)
+  nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
+
+  return PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin,
+                          aDesiredSize, this, subScriptShift, supScriptShift,
+                          scriptSpace);
 }
 
-/* virtual */ nsresult
-nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
-                                  bool                 aPlaceOrigin,
-                                  nsHTMLReflowMetrics& aDesiredSize)
+// exported routine that both munderover and mmultiscripts share.
+// munderover uses this when movablelimits is set.
+nsresult
+nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext*      aPresContext,
+                                        nsRenderingContext& aRenderingContext,
+                                        bool                 aPlaceOrigin,
+                                        nsHTMLReflowMetrics& aDesiredSize,
+                                        nsMathMLContainerFrame* aFrame,
+                                        nscoord              aUserSubScriptShift,
+                                        nscoord              aUserSupScriptShift,
+                                        nscoord              aScriptSpace)
 {
-  ////////////////////////////////////
-  // Get the children's desired sizes
+  nsIAtom* tag = aFrame->GetContent()->Tag();
+
+  // This function deals with both munderover etc. as well as msubsup etc.
+  // As the former behaves identically to the later, we treat it as such
+  // to avoid additional checks later.
+  if (tag == nsGkAtoms::mover_)
+    tag = nsGkAtoms::msup_;
+  else if (tag == nsGkAtoms::munder_)
+    tag = nsGkAtoms::msub_;
+  else if (tag  == nsGkAtoms::munderover_)
+    tag = nsGkAtoms::msubsup_;
+
+  nsBoundingMetrics bmFrame;
+  nsHTMLReflowMetrics frameSize;
 
   nscoord minShiftFromXHeight, subDrop, supDrop;
 
   ////////////////////////////////////////
   // Initialize super/sub shifts that
   // depend only on the current font
   ////////////////////////////////////////
 
-  ProcessAttributes();
+  nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
+
+  if (!baseFrame) {
+    if (tag == nsGkAtoms::mmultiscripts_)
+      aFrame->ReportErrorToConsole("NoBase");
+    else
+      aFrame->ReportChildCountError();
+    return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+  }
+
 
   // get x-height (an ex)
-  const nsStyleFont* font = StyleFont();
+  const nsStyleFont* font = aFrame->StyleFont();
   nsRefPtr<nsFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm));
   aRenderingContext.SetFont(fm);
 
   nscoord xHeight = fm->XHeight();
 
   nscoord ruleSize;
   GetRuleThickness (aRenderingContext, fm, ruleSize);
 
-  // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX)
-  // forced to be at least 1 pixel here
+  // force the scriptSpace to be at least 1 pixel
   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
-  nscoord scriptSpace = std::max(nsPresContext::CSSPointsToAppUnits(0.5f), onePixel);
+  aScriptSpace = std::max(onePixel, aScriptSpace);
 
   /////////////////////////////////////
   // first the shift for the subscript
 
   // subScriptShift{1,2}
   // = minimum amount to shift the subscript down
   // = sub{1,2} in TeXbook
   // subScriptShift1 = subscriptshift attribute * x-height
   nscoord subScriptShift1, subScriptShift2;
 
   // Get subScriptShift{1,2} default from font
   GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
-  if (0 < mSubScriptShift) {
+  nscoord subScriptShift;
+  if (tag == nsGkAtoms::msub_) {
+    subScriptShift = subScriptShift1;
+  } else {
+    subScriptShift = std::max(subScriptShift1, subScriptShift2);
+  }
+  if (0 < aUserSubScriptShift) {
     // the user has set the subscriptshift attribute
-    float scaler = ((float) subScriptShift2) / subScriptShift1;
-    subScriptShift1 = std::max(subScriptShift1, mSubScriptShift);
-    subScriptShift2 = NSToCoordRound(scaler * subScriptShift1);
+    subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
   }
-  // the font dependent shift
-  nscoord subScriptShift = std::max(subScriptShift1,subScriptShift2);
 
   /////////////////////////////////////
   // next the shift for the superscript
 
   // supScriptShift{1,2,3}
   // = minimum amount to shift the supscript up
   // = sup{1,2,3} in TeX
   // supScriptShift1 = superscriptshift attribute * x-height
   // Note that there are THREE values for supscript shifts depending
   // on the current style
   nscoord supScriptShift1, supScriptShift2, supScriptShift3;
   // Set supScriptShift{1,2,3} default from font
   GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
-  if (0 < mSupScriptShift) {
-    // the user has set the superscriptshift attribute
-    float scaler2 = ((float) supScriptShift2) / supScriptShift1;
-    float scaler3 = ((float) supScriptShift3) / supScriptShift1;
-    supScriptShift1 = std::max(supScriptShift1, mSupScriptShift);
-    supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1);
-    supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1);
-  }
 
   // get sup script shift depending on current script level and display style
   // Rule 18c, App. G, TeXbook
+  nsPresentationData presentationData;
+  aFrame->GetPresentationData(presentationData);
   nscoord supScriptShift;
   if ( font->mScriptLevel == 0 &&
-       NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) &&
-      !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
+       NS_MATHML_IS_DISPLAYSTYLE(presentationData.flags) &&
+      !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
     // Style D in TeXbook
     supScriptShift = supScriptShift1;
-  }
-  else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) {
+  } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
     // Style C' in TeXbook = D',T',S',SS'
     supScriptShift = supScriptShift3;
-  }
-  else {
+  } else {
     // everything else = T,S,SS
     supScriptShift = supScriptShift2;
   }
 
+  if (0 < aUserSupScriptShift) {
+    // the user has set the supscriptshift attribute
+    supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
+  }
+
   ////////////////////////////////////
   // Get the children's sizes
   ////////////////////////////////////
 
   nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
-  nsIFrame* mprescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
-  bool isSubScript = false;
   nscoord minSubScriptShift = 0, minSupScriptShift = 0;
   nscoord trySubScriptShift = subScriptShift;
   nscoord trySupScriptShift = supScriptShift;
   nscoord maxSubScriptShift = subScriptShift;
   nscoord maxSupScriptShift = supScriptShift;
-  int32_t count = 0;
   nsHTMLReflowMetrics baseSize;
   nsHTMLReflowMetrics subScriptSize;
   nsHTMLReflowMetrics supScriptSize;
-  nsIFrame* baseFrame = nullptr;
+  baseFrame = nullptr;
   nsIFrame* subScriptFrame = nullptr;
   nsIFrame* supScriptFrame = nullptr;
+  nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
 
   bool firstPrescriptsPair = false;
   nsBoundingMetrics bmBase, bmSubScript, bmSupScript;
   nscoord italicCorrection = 0;
 
-  mBoundingMetrics.width = 0;
-  mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF;
+  nsBoundingMetrics boundingMetrics;
+  boundingMetrics.width = 0;
+  boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
   nscoord ascent = -0x7FFFFFFF, descent = -0x7FFFFFFF;
   aDesiredSize.width = aDesiredSize.height = 0;
 
-  nsIFrame* childFrame = mFrames.FirstChild();
+  int32_t count = 0;
+  bool foundNoneTag = false;
+
+  // Boolean to determine whether the current child is a subscript.
+  // Note that only msup starts with a superscript.
+  bool isSubScript = (tag != nsGkAtoms::msup_);
+
+  nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
   while (childFrame) {
-    if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
-      if (mprescriptsFrame) {
+    nsIAtom* childTag = childFrame->GetContent()->Tag();
+    if (childTag == nsGkAtoms::mprescripts_) {
+      if (tag != nsGkAtoms::mmultiscripts_) {
+        if (aPlaceOrigin) {
+          aFrame->ReportInvalidChildError(childTag);
+        }
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+      }
+      if (prescriptsFrame) {
         // duplicate <mprescripts/> found
         // report an error, encourage people to get their markups in order
         if (aPlaceOrigin) {
-          ReportErrorToConsole("DuplicateMprescripts");
+          aFrame->ReportErrorToConsole("DuplicateMprescripts");
         }
-        return ReflowError(aRenderingContext, aDesiredSize);
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
       }
-      mprescriptsFrame = childFrame;
+      if (!isSubScript) {
+        if (aPlaceOrigin) {
+          aFrame->ReportErrorToConsole("SubSupMismatch");
+        }
+        return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+      }
+
+      prescriptsFrame = childFrame;
       firstPrescriptsPair = true;
-    }
-    else {
-      if (0 == count) {
-        // base
-        baseFrame = childFrame;
-        GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
+    } else if (0 == count) {
+      // base
+
+      if (childTag == nsGkAtoms::none) {
+        if (tag == nsGkAtoms::mmultiscripts_) {
+          if (aPlaceOrigin) {
+            aFrame->ReportErrorToConsole("NoBase");
+          }
+          return aFrame->ReflowError(aRenderingContext, aDesiredSize);
+        } else {
+          //A different error message is triggered later for the other tags
+          foundNoneTag = true;
+        }
+      }
+      baseFrame = childFrame;
+      GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
+
+      if (tag != nsGkAtoms::msub_) {
+        // Apply italics correction if there is the potential for a 
+        // postsupscript.
         GetItalicCorrection(bmBase, italicCorrection);
-        // for the superscript, we always add "a little to spare"
+        // If italics correction is applied, we always add "a little to spare"
+        // (see TeXbook Ch.11, p.64), as we estimate the italic creation
+        // ourselves and it isn't the same as TeX.
         italicCorrection += onePixel;
+      }
 
-        // we update mBoundingMetrics.{ascent,descent} with that
-        // of the baseFrame only after processing all the sup/sub pairs
-        // XXX need italic correction only *if* there are postscripts ?
-        mBoundingMetrics.width = bmBase.width + italicCorrection;
-        mBoundingMetrics.rightBearing = bmBase.rightBearing;
-        mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
+      // we update boundingMetrics.{ascent,descent} with that
+      // of the baseFrame only after processing all the sup/sub pairs
+      // XXX need italic correction only *if* there are postscripts ?
+      boundingMetrics.width = bmBase.width + italicCorrection;
+      boundingMetrics.rightBearing = bmBase.rightBearing;
+      boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
+    } else {
+      // super/subscript block
+      if ( childTag == nsGkAtoms::none) {
+        foundNoneTag = true;
       }
-      else {
-        // super/subscript block
-        if (isSubScript) {
-          // subscript
-          subScriptFrame = childFrame;
-          GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
-          // get the subdrop from the subscript font
-          GetSubDropFromChild (subScriptFrame, subDrop);
-          // parameter v, Rule 18a, App. G, TeXbook
-          minSubScriptShift = bmBase.descent + subDrop;
-          trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
-          mBoundingMetrics.descent =
-            std::max(mBoundingMetrics.descent,bmSubScript.descent);
-          descent = std::max(descent,subScriptSize.height - subScriptSize.ascent);
-          width = bmSubScript.width + scriptSpace;
-          rightBearing = bmSubScript.rightBearing;
+
+      if (isSubScript) {
+        // subscript
+        subScriptFrame = childFrame;
+        GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
+        // get the subdrop from the subscript font
+        GetSubDropFromChild (subScriptFrame, subDrop);
+        // parameter v, Rule 18a, App. G, TeXbook
+        minSubScriptShift = bmBase.descent + subDrop;
+        trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
+        boundingMetrics.descent =
+          std::max(boundingMetrics.descent,bmSubScript.descent);
+        descent = std::max(descent,subScriptSize.height - subScriptSize.ascent);
+        if (bmSubScript.width)
+          width = bmSubScript.width + aScriptSpace;
+        rightBearing = bmSubScript.rightBearing;
+
+        if (tag == nsGkAtoms::msub_) {
+          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
+          boundingMetrics.width += width;
+
+          // get min subscript shift limit from x-height
+          // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
+          nscoord minShiftFromXHeight = (nscoord) 
+            (bmSubScript.ascent - (4.0f/5.0f) * xHeight);
+          maxSubScriptShift = std::max(trySubScriptShift,minShiftFromXHeight);
+
+          maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
+          trySubScriptShift = subScriptShift;
         }
-        else {
-          // supscript
-          supScriptFrame = childFrame;
-          GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
-          // get the supdrop from the supscript font
-          GetSupDropFromChild (supScriptFrame, supDrop);
-          // parameter u, Rule 18a, App. G, TeXbook
-          minSupScriptShift = bmBase.ascent - supDrop;
-          // get min supscript shift limit from x-height
-          // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
-          minShiftFromXHeight = NSToCoordRound
-            ((bmSupScript.descent + (1.0f/4.0f) * xHeight));
-          trySupScriptShift =
-            std::max(minSupScriptShift,std::max(minShiftFromXHeight,supScriptShift));
-          mBoundingMetrics.ascent =
-            std::max(mBoundingMetrics.ascent,bmSupScript.ascent);
-          ascent = std::max(ascent,supScriptSize.ascent);
-          width = std::max(width, bmSupScript.width + scriptSpace);
-          rightBearing = std::max(rightBearing, bmSupScript.rightBearing);
+      } else {
+        // supscript
+        supScriptFrame = childFrame;
+        GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
+        // get the supdrop from the supscript font
+        GetSupDropFromChild (supScriptFrame, supDrop);
+        // parameter u, Rule 18a, App. G, TeXbook
+        minSupScriptShift = bmBase.ascent - supDrop;
+        // get min supscript shift limit from x-height
+        // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
+        minShiftFromXHeight = NSToCoordRound
+          ((bmSupScript.descent + (1.0f/4.0f) * xHeight));
+        trySupScriptShift =
+          std::max(minSupScriptShift,std::max(minShiftFromXHeight,supScriptShift));
+        boundingMetrics.ascent =
+          std::max(boundingMetrics.ascent,bmSupScript.ascent);
+        ascent = std::max(ascent,supScriptSize.ascent);
+        if (bmSupScript.width)
+          width = std::max(width, bmSupScript.width + aScriptSpace);
+        rightBearing = std::max(rightBearing, bmSupScript.rightBearing);
 
-          if (!mprescriptsFrame) { // we are still looping over base & postscripts
-            mBoundingMetrics.rightBearing = mBoundingMetrics.width + rightBearing;
-            mBoundingMetrics.width += width;
+        if (!prescriptsFrame) { // we are still looping over base & postscripts
+          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
+          boundingMetrics.width += width;
+        } else {
+          prescriptsWidth += width;
+          if (firstPrescriptsPair) {
+            firstPrescriptsPair = false;
+            boundingMetrics.leftBearing =
+              std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
           }
-          else {
-            prescriptsWidth += width;
-            if (firstPrescriptsPair) {
-              firstPrescriptsPair = false;
-              mBoundingMetrics.leftBearing =
-                std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
-            }
-          }
-          width = rightBearing = 0;
+        }
+        width = rightBearing = 0;
 
-          // negotiate between the various shifts so that
-          // there is enough gap between the sup and subscripts
-          // Rule 18e, App. G, TeXbook
+        // negotiate between the various shifts so that
+        // there is enough gap between the sup and subscripts
+        // Rule 18e, App. G, TeXbook
+        if (tag == nsGkAtoms::mmultiscripts_ || 
+            tag == nsGkAtoms::msubsup_) {
           nscoord gap =
             (trySupScriptShift - bmSupScript.descent) -
             (bmSubScript.ascent - trySubScriptShift);
           if (gap < 4.0f * ruleSize) {
             // adjust trySubScriptShift to get a gap of (4.0 * ruleSize)
             trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap);
           }
 
           // next we want to ensure that the bottom of the superscript
           // will be > (4/5) * x-height above baseline
           gap = NSToCoordRound ((4.0f/5.0f) * xHeight -
                   (trySupScriptShift - bmSupScript.descent));
-          if (gap > 0.0f) {
+          if (gap > 0) {
             trySupScriptShift += gap;
             trySubScriptShift -= gap;
           }
-          
-          maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
-          maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
+        }
 
-          trySubScriptShift = subScriptShift;
-          trySupScriptShift = supScriptShift;
-        }
+        maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
+        maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
+
+        trySubScriptShift = subScriptShift;
+        trySupScriptShift = supScriptShift;
       }
 
       isSubScript = !isSubScript;
     }
     count++;
     childFrame = childFrame->GetNextSibling();
   }
-  // note: width=0 if all sup-sub pairs match correctly
-  if ((0 != width) || !baseFrame) {
+
+  //NoBase error may also have been reported above
+  if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
+      (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
+      (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) ||
+      (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
     // report an error, encourage people to get their markups in order
     if (aPlaceOrigin) {
-      if (!baseFrame) {
-        ReportErrorToConsole("NoBase");
+      if ((count != 2 && (tag == nsGkAtoms::msup_ || 
+          tag == nsGkAtoms::msub_)) ||
+          (count != 3 && tag == nsGkAtoms::msubsup_ )) {
+        aFrame->ReportChildCountError();
+      } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) {
+        aFrame->ReportInvalidChildError(nsGkAtoms::none);
+      } else if (!baseFrame) {
+        aFrame->ReportErrorToConsole("NoBase");
       } else {
-        ReportErrorToConsole("SubSupMismatch");
+        aFrame->ReportErrorToConsole("SubSupMismatch");
       }
     }
-    return ReflowError(aRenderingContext, aDesiredSize);
+    return aFrame->ReflowError(aRenderingContext, aDesiredSize);
   }
 
   // we left out the width of prescripts, so ...
-  mBoundingMetrics.rightBearing += prescriptsWidth;
-  mBoundingMetrics.width += prescriptsWidth;
+  boundingMetrics.rightBearing += prescriptsWidth;
+  boundingMetrics.width += prescriptsWidth;
 
   // we left out the base during our bounding box updates, so ...
-  mBoundingMetrics.ascent =
-    std::max(mBoundingMetrics.ascent+maxSupScriptShift,bmBase.ascent);
-  mBoundingMetrics.descent =
-    std::max(mBoundingMetrics.descent+maxSubScriptShift,bmBase.descent);
+  boundingMetrics.ascent =
+    std::max(boundingMetrics.ascent+maxSupScriptShift,bmBase.ascent);
+  boundingMetrics.descent =
+    std::max(boundingMetrics.descent+maxSubScriptShift,bmBase.descent);
+  aFrame->SetBoundingMetrics(boundingMetrics);
 
   // get the reflow metrics ...
   aDesiredSize.ascent =
     std::max(ascent+maxSupScriptShift,baseSize.ascent);
   aDesiredSize.height = aDesiredSize.ascent +
     std::max(descent+maxSubScriptShift,baseSize.height - baseSize.ascent);
-  aDesiredSize.width = mBoundingMetrics.width;
-  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
+  aDesiredSize.width = boundingMetrics.width;
+  aDesiredSize.mBoundingMetrics = boundingMetrics;
 
-  mReference.x = 0;
-  mReference.y = aDesiredSize.ascent;
+  aFrame->SetReference(nsPoint(0, aDesiredSize.ascent));
 
   //////////////////
   // Place Children
 
   // Place prescripts, followed by base, and then postscripts.
   // The list of frames is in the order: {base} {postscripts} {prescripts}
   // We go over the list in a circular manner, starting at <prescripts/>
 
   if (aPlaceOrigin) {
     nscoord dx = 0, dy = 0;
 
-    count = 0;
-    childFrame = mprescriptsFrame;
+    // With msub and msup there is only one element and 
+    // subscriptFrame/supScriptFrame have already been set above where
+    // relevant.  In these cases we skip to the reflow part.
+    if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
+      count = 1;
+    else
+      count = 0;
+    childFrame = prescriptsFrame;
     do {
       if (!childFrame) { // end of prescripts,
         // place the base ...
         childFrame = baseFrame;
         dy = aDesiredSize.ascent - baseSize.ascent;
-        FinishReflowChild (baseFrame, PresContext(), nullptr, baseSize,
-                           MirrorIfRTL(aDesiredSize.width,
-                                       baseSize.width,
-                                       dx),
+        FinishReflowChild (baseFrame, aPresContext, nullptr, baseSize,
+                           aFrame->MirrorIfRTL(aDesiredSize.width,
+                                               baseSize.width,
+                                               dx),
                            dy, 0);
         dx += bmBase.width + italicCorrection;
-      }
-      else if (mprescriptsFrame != childFrame) {
+      } else if (prescriptsFrame != childFrame) {
         // process each sup/sub pair
         if (0 == count) {
           subScriptFrame = childFrame;
           count = 1;
-        }
-        else if (1 == count) {
-          supScriptFrame = childFrame;
+        } else if (1 == count) {
+          if (tag != nsGkAtoms::msub_)
+            supScriptFrame = childFrame;
           count = 0;
 
           // get the ascent/descent of sup/subscripts stored in their rects
           // rect.x = descent, rect.y = ascent
-          GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
-          GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
+          if (subScriptFrame)
+            GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
+          if (supScriptFrame)
+            GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
 
           // center w.r.t. largest width
           width = std::max(subScriptSize.width, supScriptSize.width);
 
-          dy = aDesiredSize.ascent - subScriptSize.ascent +
-            maxSubScriptShift;
-          FinishReflowChild (subScriptFrame, PresContext(), nullptr,
-                             subScriptSize,
-                             MirrorIfRTL(aDesiredSize.width,
-                                         subScriptSize.width,
-                                         dx + (width-subScriptSize.width)/2),
-                             dy, 0);
+          if (subScriptFrame) {
+            dy = aDesiredSize.ascent - subScriptSize.ascent +
+              maxSubScriptShift;
+            FinishReflowChild (subScriptFrame, aPresContext, nullptr,
+                               subScriptSize,
+                               aFrame->MirrorIfRTL(aDesiredSize.width,
+                                                   subScriptSize.width,
+                                                   dx + (width-subScriptSize.width)/2),
+                               dy, 0);
+          }
 
-          dy = aDesiredSize.ascent - supScriptSize.ascent -
-            maxSupScriptShift;
-          FinishReflowChild (supScriptFrame, PresContext(), nullptr,
-                             supScriptSize,
-                             MirrorIfRTL(aDesiredSize.width,
-                                         supScriptSize.width,
-                                         dx + (width-supScriptSize.width)/2),
-                             dy, 0);
-
-          dx += width + scriptSpace;
+          if (supScriptFrame) {
+            dy = aDesiredSize.ascent - supScriptSize.ascent -
+              maxSupScriptShift;
+            FinishReflowChild (supScriptFrame, aPresContext, nullptr,
+                               supScriptSize,
+                               aFrame->MirrorIfRTL(aDesiredSize.width,
+                                                   supScriptSize.width,
+                                                   dx + (width-supScriptSize.width)/2),
+                               dy, 0);
+          }
+          dx += width + aScriptSpace;
         }
       }
       childFrame = childFrame->GetNextSibling();
-    } while (mprescriptsFrame != childFrame);
+    } while (prescriptsFrame != childFrame);
   }
 
   return NS_OK;
 }
--- a/layout/mathml/nsMathMLmmultiscriptsFrame.h
+++ b/layout/mathml/nsMathMLmmultiscriptsFrame.h
@@ -7,37 +7,45 @@
 #define nsMathMLmmultiscriptsFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsMathMLContainerFrame.h"
 
 //
 // <mmultiscripts> -- attach prescripts and tensor indices to a base 
+// <msub> -- attach a subscript to a base
+// <msubsup> -- attach a subscript-superscript pair to a base
+// <msup> -- attach a superscript to a base
 //
 
 class nsMathMLmmultiscriptsFrame : public nsMathMLContainerFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   NS_IMETHOD
   TransmitAutomaticData() MOZ_OVERRIDE;
 
   virtual nsresult
   Place(nsRenderingContext& aRenderingContext,
         bool                 aPlaceOrigin,
         nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
+  static nsresult
+  PlaceMultiScript(nsPresContext*      aPresContext,
+                    nsRenderingContext& aRenderingContext,
+                    bool                 aPlaceOrigin,
+                    nsHTMLReflowMetrics& aDesiredSize,
+                    nsMathMLContainerFrame* aForFrame,
+                    nscoord              aUserSubScriptShift,
+                    nscoord              aUserSupScriptShift,
+                    nscoord              aScriptSpace);
+
 protected:
   nsMathMLmmultiscriptsFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
   virtual ~nsMathMLmmultiscriptsFrame();
   
-private:
-  nscoord mSubScriptShift;
-  nscoord mSupScriptShift;
 
-  void
-  ProcessAttributes();
 };
 
 #endif /* nsMathMLmmultiscriptsFrame_h___ */