Bug 415413 - Incorrect widths and heights of MathML with italics in table cells. r=karlt, r=roc
authorFrédéric Wang <fred.wang@free.fr>
Mon, 25 Nov 2013 09:20:20 -0500
changeset 172007 f1e9448e639d85d7ed6146fce8bb16607b3668e4
parent 172006 0dbba019d66bf9e2c42c2907c915e358226b5d22
child 172008 7e31cf975f22f209c04c67c13a6065efef0cf10e
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, roc
bugs415413
milestone28.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 415413 - Incorrect widths and heights of MathML with italics in table cells. r=karlt, r=roc
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/mathml/nsMathMLContainerFrame.cpp
layout/mathml/nsMathMLContainerFrame.h
layout/mathml/nsMathMLmfencedFrame.cpp
layout/mathml/nsMathMLmfencedFrame.h
layout/mathml/nsMathMLmoFrame.cpp
layout/mathml/nsMathMLmoFrame.h
layout/mathml/nsMathMLmrootFrame.cpp
layout/mathml/nsMathMLmrootFrame.h
layout/reftests/mathml/reftest.list
layout/reftests/mathml/table-width-3-ref.html
layout/reftests/mathml/table-width-3.html
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -781,16 +781,77 @@ nsBlockFrame::ComputeTightBounds(gfxCont
 {
   // be conservative
   if (StyleContext()->HasTextDecorationLines()) {
     return GetVisualOverflowRect();
   }
   return ComputeSimpleTightBounds(aContext);
 }
 
+/* virtual */ nsresult
+nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext,
+                                      nscoord* aX,
+                                      nscoord* aXMost)
+{
+  nsIFrame* firstInFlow = FirstContinuation();
+  if (firstInFlow != this) {
+    return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
+  }
+
+  *aX = 0;
+  *aXMost = 0;
+
+  nsresult rv;
+  InlinePrefWidthData data;
+  for (nsBlockFrame* curFrame = this; curFrame;
+       curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
+    for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
+         line != line_end; ++line)
+    {
+      nscoord childX, childXMost;
+      if (line->IsBlock()) {
+        data.ForceBreak(aRenderingContext);
+        rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
+                                                        &childX, &childXMost);
+        NS_ENSURE_SUCCESS(rv, rv);
+        *aX = std::min(*aX, childX);
+        *aXMost = std::max(*aXMost, childXMost);
+      } else {
+        if (!curFrame->GetPrevContinuation() &&
+            line == curFrame->begin_lines()) {
+          // Only add text-indent if it has no percentages; using a
+          // percentage basis of 0 unconditionally would give strange
+          // behavior for calc(10%-3px).
+          const nsStyleCoord &indent = StyleText()->mTextIndent;
+          if (indent.ConvertsToLength()) {
+            data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
+          }
+        }
+        // XXX Bug NNNNNN Should probably handle percentage text-indent.
+
+        data.line = &line;
+        data.lineContainer = curFrame;
+        nsIFrame *kid = line->mFirstChild;
+        for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
+             ++i, kid = kid->GetNextSibling()) {
+          rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
+                                            &childXMost);
+          NS_ENSURE_SUCCESS(rv, rv);
+          *aX = std::min(*aX, data.currentLine + childX);
+          *aXMost = std::max(*aXMost, data.currentLine + childXMost);
+          kid->AddInlinePrefWidth(aRenderingContext, &data);
+        }
+      }
+    }
+  }
+  data.ForceBreak(aRenderingContext);
+
+  return NS_OK;
+}
+
 static bool
 AvailableSpaceShrunk(const nsRect& aOldAvailableSpace,
                      const nsRect& aNewAvailableSpace)
 {
   if (aNewAvailableSpace.width == 0) {
     // Positions are not significant if the width is zero.
     return aOldAvailableSpace.width != 0;
   }
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -256,16 +256,20 @@ public:
 private:
   void CheckIntrinsicCacheAgainstShrinkWrapState();
 public:
   virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
 
   virtual nsRect ComputeTightBounds(gfxContext* aContext) const MOZ_OVERRIDE;
   
+  virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                           nscoord* aX,
+                                           nscoord* aXMost) MOZ_OVERRIDE;
+
   /**
    * Compute the final height of this frame.
    *
    * @param aReflowState Data structure passed from parent during reflow.
    * @param aReflowStatus A pointed to the reflow status for when we're finished
    *        doing reflow. this will get set appropriately if the height causes
    *        us to exceed the current available (page) height.
    * @param aContentHeight The height of content, precomputed outside of this
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4044,16 +4044,24 @@ nsFrame::ComputeSimpleTightBounds(gfxCon
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
       r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition());
     }
   }
   return r;
 }
 
+/* virtual */ nsresult
+nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                  nscoord* aX,
+                                  nscoord* aXMost)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 /* virtual */ nsSize
 nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
                          nsSize aCBSize, nscoord aAvailableWidth,
                          nsSize aMargin, nsSize aBorder, nsSize aPadding,
                          bool aShrinkWrap)
 {
   // Use basic shrink-wrapping as a default implementation.
   nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1828,16 +1828,36 @@ public:
    * contribute to visual overflow.
    *
    * @param aContext a rendering context that can be used if we need
    * to do measurement
    */
   virtual nsRect ComputeTightBounds(gfxContext* aContext) const;
 
   /**
+   * This function is similar to GetPrefWidth and ComputeTightBounds: it
+   * computes the left and right coordinates of a preferred tight bounding
+   * rectangle for the frame. This is a rectangle that would enclose the pixels
+   * that are drawn if we lay out the element without taking any optional line
+   * breaks. The rectangle is in appunits and relative to the origin of this
+   * frame. Currently, this function is only implemented for nsBlockFrame and
+   * nsTextFrame and is used to determine intrinsic widths of MathML token
+   * elements.
+
+   * @param aContext a rendering context that can be used if we need
+   * to do measurement
+   * @param aX      computed left coordinate of the tight bounding rectangle
+   * @param aXMost  computed intrinsic width of the tight bounding rectangle
+   *
+   */
+  virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                           nscoord* aX,
+                                           nscoord* aXMost);
+
+  /**
    * Pre-reflow hook. Before a frame is reflowed this method will be called.
    * This call will always be invoked at least once before a subsequent Reflow
    * and DidReflow call. It may be called more than once, In general you will
    * receive on WillReflow notification before each Reflow request.
    *
    * XXX Is this really the semantics we want? Because we have the NS_FRAME_IN_REFLOW
    * bit we can ensure we don't call it more than once...
    */
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2604,44 +2604,47 @@ GetEndOfTrimmedText(const nsTextFragment
     if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset(), aStyleText))
       return aIterator->GetSkippedOffset() + 1;
   }
   return aStart;
 }
 
 nsTextFrame::TrimmedOffsets
 nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
-                               bool aTrimAfter)
+                               bool aTrimAfter, bool aPostReflow)
 {
   NS_ASSERTION(mTextRun, "Need textrun here");
-  // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
-  // to be set correctly.  If our parent wasn't reflowed due to the frame
-  // tree being too deep then the return value doesn't matter.
-  NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
-               (GetParent()->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
-               "Can only call this on frames that have been reflowed");
-  NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
-               "Can only call this on frames that are not being reflowed");
+  if (aPostReflow) {
+    // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
+    // to be set correctly.  If our parent wasn't reflowed due to the frame
+    // tree being too deep then the return value doesn't matter.
+    NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
+                 (GetParent()->GetStateBits() &
+                  NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
+                 "Can only call this on frames that have been reflowed");
+    NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
+                 "Can only call this on frames that are not being reflowed");
+  }
 
   TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
   const nsStyleText* textStyle = StyleText();
   // Note that pre-line newlines should still allow us to trim spaces
   // for display
   if (textStyle->WhiteSpaceIsSignificant())
     return offsets;
 
-  if (GetStateBits() & TEXT_START_OF_LINE) {
+  if (!aPostReflow || (GetStateBits() & TEXT_START_OF_LINE)) {
     int32_t whitespaceCount =
       GetTrimmableWhitespaceCount(aFrag,
                                   offsets.mStart, offsets.mLength, 1);
     offsets.mStart += whitespaceCount;
     offsets.mLength -= whitespaceCount;
   }
 
-  if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE)) {
+  if (aTrimAfter && (!aPostReflow || (GetStateBits() & TEXT_END_OF_LINE))) {
     // This treats a trailing 'pre-line' newline as trimmable. That's fine,
     // it's actually what we want since we want whitespace before it to
     // be trimmed.
     int32_t whitespaceCount =
       GetTrimmableWhitespaceCount(aFrag,
                                   offsets.GetEnd() - 1, offsets.mLength, -1);
     offsets.mLength -= whitespaceCount;
   }
@@ -2804,16 +2807,18 @@ public:
       mWhichTextRun(aWhichTextRun)
   {
     NS_ASSERTION(mTextRun, "Textrun not initialized!");
   }
 
   // Call this after construction if you're not going to reflow the text
   void InitializeForDisplay(bool aTrimAfter);
 
+  void InitializeForMeasure();
+
   virtual void GetSpacing(uint32_t aStart, uint32_t aLength, Spacing* aSpacing);
   virtual gfxFloat GetHyphenWidth();
   virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength,
                                     bool* aBreakBefore);
   virtual int8_t GetHyphensOption() {
     return mTextStyle->mHyphens;
   }
 
@@ -3244,16 +3249,27 @@ PropertyProvider::InitializeForDisplay(b
 {
   nsTextFrame::TrimmedOffsets trimmed =
     mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
   mStart.SetOriginalOffset(trimmed.mStart);
   mLength = trimmed.mLength;
   SetupJustificationSpacing();
 }
 
+void
+PropertyProvider::InitializeForMeasure()
+{
+  nsTextFrame::TrimmedOffsets trimmed =
+    mFrame->GetTrimmedOffsets(mFrag, true, false);
+  mStart.SetOriginalOffset(trimmed.mStart);
+  mLength = trimmed.mLength;
+  SetupJustificationSpacing();
+}
+
+
 static uint32_t GetSkippedDistance(const gfxSkipCharsIterator& aStart,
                                    const gfxSkipCharsIterator& aEnd)
 {
   return aEnd.GetSkippedOffset() - aStart.GetSkippedOffset();
 }
 
 void
 PropertyProvider::FindJustificationRange(gfxSkipCharsIterator* aStart,
@@ -7282,16 +7298,41 @@ nsTextFrame::ComputeTightBounds(gfxConte
                               ComputeTransformedLength(provider),
                               gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
                               aContext, &provider);
   // mAscent should be the same as metrics.mAscent, but it's what we use to
   // paint so that's the one we'll use.
   return RoundOut(metrics.mBoundingBox) + nsPoint(0, mAscent);
 }
 
+/* virtual */ nsresult
+nsTextFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                     nscoord* aX,
+                                     nscoord* aXMost)
+{
+  gfxSkipCharsIterator iter =
+    const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
+  if (!mTextRun)
+    return NS_ERROR_FAILURE;
+
+  PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
+                            nsTextFrame::eInflated);
+  provider.InitializeForMeasure();
+
+  gfxTextRun::Metrics metrics =
+        mTextRun->MeasureText(provider.GetStart().GetSkippedOffset(),
+                              ComputeTransformedLength(provider),
+                              gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
+                              aContext->ThebesContext(), &provider);
+  *aX = metrics.mBoundingBox.x;
+  *aXMost = metrics.mBoundingBox.XMost();
+
+  return NS_OK;
+}
+
 static bool
 HasSoftHyphenBefore(const nsTextFragment* aFrag, gfxTextRun* aTextRun,
                     int32_t aStartOffset, const gfxSkipCharsIterator& aIter)
 {
   if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
       aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
     return true;
   }
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -216,16 +216,19 @@ public:
                                  InlineMinWidthData *aData) MOZ_OVERRIDE;
   virtual void AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
                                   InlinePrefWidthData *aData) MOZ_OVERRIDE;
   virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext,
                              nsSize aCBSize, nscoord aAvailableWidth,
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
                              uint32_t aFlags) MOZ_OVERRIDE;
   virtual nsRect ComputeTightBounds(gfxContext* aContext) const MOZ_OVERRIDE;
+  virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                           nscoord* aX,
+                                           nscoord* aXMost) MOZ_OVERRIDE;
   NS_IMETHOD Reflow(nsPresContext* aPresContext,
                     nsHTMLReflowMetrics& aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus& aStatus) MOZ_OVERRIDE;
   virtual bool CanContinueTextRun() const MOZ_OVERRIDE;
   // Method that is called for a text frame that is logically
   // adjacent to the end of the line (i.e. followed only by empty text frames,
   // placeholders or inlines containing such).
@@ -506,17 +509,17 @@ public:
   // whitespace subject to start-of-line and end-of-line trimming.
   // The textrun must have been created before calling this.
   struct TrimmedOffsets {
     int32_t mStart;
     int32_t mLength;
     int32_t GetEnd() const { return mStart + mLength; }
   };
   TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
-                                   bool aTrimAfter);
+                                   bool aTrimAfter, bool aPostReflow = true);
 
   // Similar to Reflow(), but for use from nsLineLayout
   void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
                   nsRenderingContext* aRenderingContext,
                   nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus);
 
   bool IsFloatingFirstLetterChild() const;
 
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -24,16 +24,17 @@ using namespace mozilla;
 //
 // nsMathMLContainerFrame implementation
 //
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
 
 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
+  NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 // =============================================================================
 
 // error handlers
 // provide a feedback to the user when a frame with bad markup can not be rendered
 nsresult
 nsMathMLContainerFrame::ReflowError(nsRenderingContext& aRenderingContext,
@@ -980,65 +981,83 @@ nsMathMLContainerFrame::Reflow(nsPresCon
   return NS_OK;
 }
 
 /* virtual */ nscoord
 nsMathMLContainerFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
 {
   nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
-  result = GetIntrinsicWidth(aRenderingContext);
+  nsHTMLReflowMetrics desiredSize;
+  GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
+  nsBoundingMetrics bm = desiredSize.mBoundingMetrics;
+  // We include the overflow to compensate for FixInterFrameSpacing.
+  result = std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
   return result;
 }
 
 /* virtual */ nscoord
 nsMathMLContainerFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
 {
   nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
-  result = GetIntrinsicWidth(aRenderingContext);
+  nsHTMLReflowMetrics desiredSize;
+  GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
+  nsBoundingMetrics bm = desiredSize.mBoundingMetrics;
+  // We include the overflow to compensate for FixInterFrameSpacing.
+  result = std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
   return result;
 }
 
-/* virtual */ nscoord
-nsMathMLContainerFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
+/* virtual */ void
+nsMathMLContainerFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
 {
   // Get child widths
   nsIFrame* childFrame = mFrames.FirstChild();
   while (childFrame) {
-    // XXX This includes margin while Reflow currently doesn't consider
-    // margin, so we may end up with too much space, but, with stretchy
-    // characters, this is an approximation anyway.
-    nscoord width =
-      nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
-                                           nsLayoutUtils::PREF_WIDTH);
+    nsHTMLReflowMetrics childDesiredSize;
 
-    nsHTMLReflowMetrics childDesiredSize;
-    childDesiredSize.width = width;
-    childDesiredSize.mBoundingMetrics.width = width;
-    // TODO: we need nsIFrame::GetIntrinsicHBounds() for better values here.
-    childDesiredSize.mBoundingMetrics.leftBearing = 0;
-    childDesiredSize.mBoundingMetrics.rightBearing = width;
+    nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
+    if (containerFrame) {
+      containerFrame->GetIntrinsicWidthMetrics(aRenderingContext,
+                                               childDesiredSize);
+    } else {
+      // XXX This includes margin while Reflow currently doesn't consider
+      // margin, so we may end up with too much space, but, with stretchy
+      // characters, this is an approximation anyway.
+      nscoord width =
+        nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+                                             nsLayoutUtils::PREF_WIDTH);
+
+      childDesiredSize.width = width;
+      childDesiredSize.mBoundingMetrics.width = width;
+      childDesiredSize.mBoundingMetrics.leftBearing = 0;
+      childDesiredSize.mBoundingMetrics.rightBearing = width;
+
+      nscoord x, xMost;
+      if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
+                                                           &x, &xMost))) {
+        childDesiredSize.mBoundingMetrics.leftBearing = x;
+        childDesiredSize.mBoundingMetrics.rightBearing = xMost;
+      }
+    }
 
     SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
                                     childDesiredSize.mBoundingMetrics);
 
     childFrame = childFrame->GetNextSibling();
   }
 
   // Measure
-  nsHTMLReflowMetrics desiredSize;
-  nsresult rv = MeasureForWidth(*aRenderingContext, desiredSize);
+  nsresult rv = MeasureForWidth(*aRenderingContext, aDesiredSize);
   if (NS_FAILED(rv)) {
-    ReflowError(*aRenderingContext, desiredSize);
+    ReflowError(*aRenderingContext, aDesiredSize);
   }
 
   ClearSavedChildMetrics();
-
-  return desiredSize.width;
 }
 
 /* virtual */ nsresult
 nsMathMLContainerFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
                                         nsHTMLReflowMetrics& aDesiredSize)
 {
   return Place(aRenderingContext, false, aDesiredSize);
 }
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -28,16 +28,17 @@
 #define STRETCH_CONSIDER_EMBELLISHMENTS 0x00000002 // size calculations include embellishments
 
 class nsMathMLContainerFrame : public nsContainerFrame,
                                public nsMathMLFrame {
   friend class nsMathMLmfencedFrame;
 public:
   nsMathMLContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
 
+  NS_DECL_QUERYFRAME_TARGET(nsMathMLContainerFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // --------------------------------------------------------------------------
   // Overloaded nsMathMLFrame methods -- see documentation in nsIMathMLFrame.h
 
   NS_IMETHOD
   Stretch(nsRenderingContext& aRenderingContext,
@@ -87,26 +88,28 @@ public:
                nsIFrame*       aPrevFrame,
                nsFrameList&    aFrameList) MOZ_OVERRIDE;
 
   NS_IMETHOD
   RemoveFrame(ChildListID     aListID,
               nsIFrame*       aOldFrame) MOZ_OVERRIDE;
 
   /**
-   * Both GetMinWidth and GetPrefWidth return whatever
-   * GetIntrinsicWidth returns.
+   * Both GetMinWidth and GetPrefWidth use the intrinsic width metrics
+   * returned by GetIntrinsicMetrics, including ink overflow.
    */
   virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
 
   /**
-   * Return the intrinsic width of the frame's content area.
+   * Return the intrinsic horizontal metrics of the frame's content area.
    */
-  virtual nscoord GetIntrinsicWidth(nsRenderingContext *aRenderingContext);
+  virtual void
+  GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext,
+                           nsHTMLReflowMetrics& aDesiredSize);
 
   NS_IMETHOD
   Reflow(nsPresContext*          aPresContext,
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
   NS_IMETHOD
--- a/layout/mathml/nsMathMLmfencedFrame.cpp
+++ b/layout/mathml/nsMathMLmfencedFrame.cpp
@@ -559,18 +559,18 @@ GetMaxCharWidth(nsPresContext*       aPr
     GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
 
     width += leftSpace + rightSpace;
   }
   
   return width;
 }
 
-/* virtual */ nscoord
-nsMathMLmfencedFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
+/* virtual */ void
+nsMathMLmfencedFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
 {
   nscoord width = 0;
 
   nsPresContext* presContext = PresContext();
   const nsStyleFont* font = StyleFont();
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   nscoord em;
@@ -602,17 +602,20 @@ nsMathMLmfencedFrame::GetIntrinsicWidth(
   }
 
   if (mCloseChar) {
     width +=
       GetMaxCharWidth(presContext, aRenderingContext, mCloseChar,
                       NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em);
   }
 
-  return width;
+  aDesiredSize.width = width;
+  aDesiredSize.mBoundingMetrics.width = width;
+  aDesiredSize.mBoundingMetrics.leftBearing = 0;
+  aDesiredSize.mBoundingMetrics.rightBearing = width;
 }
 
 nscoord
 nsMathMLmfencedFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
 {
   nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
   if (!gap) return 0;
 
--- a/layout/mathml/nsMathMLmfencedFrame.h
+++ b/layout/mathml/nsMathMLmfencedFrame.h
@@ -37,18 +37,19 @@ public:
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
-  virtual nscoord
-  GetIntrinsicWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
+  virtual void
+  GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext,
+                           nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
   NS_IMETHOD
   AttributeChanged(int32_t         aNameSpaceID,
                    nsIAtom*        aAttribute,
                    int32_t         aModType) MOZ_OVERRIDE;
 
   // override the base method because we must keep separators in sync
   virtual nsresult
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -981,38 +981,46 @@ nsMathMLmoFrame::MarkIntrinsicWidthsDirt
   // we have automatic data to update in the children of the target frame
   // XXXldb This should really be marking dirty rather than rebuilding
   // so that we don't rebuild multiple times for the same change.
   RebuildAutomaticDataForChildren(target);
 
   nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
 }
 
-/* virtual */ nscoord
-nsMathMLmoFrame::GetIntrinsicWidth(nsRenderingContext *aRenderingContext)
+/* virtual */ void
+nsMathMLmoFrame::GetIntrinsicWidthMetrics(nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
 {
   ProcessOperatorData();
-  nscoord width;
   if (UseMathMLChar()) {
     uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true);
-    width = mMathMLChar.
+    aDesiredSize.width = mMathMLChar.
       GetMaxWidth(PresContext(), *aRenderingContext,
                   stretchHint, mMaxSize,
                   NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags));
   }
   else {
-    width = nsMathMLTokenFrame::GetIntrinsicWidth(aRenderingContext);
+    nsMathMLTokenFrame::GetIntrinsicWidthMetrics(aRenderingContext,
+                                                 aDesiredSize);
   }
 
   // leadingSpace and trailingSpace are actually applied to the outermost
   // embellished container but for determining total intrinsic width it should
   // be safe to include it for the core here instead.
-  width += mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
-
-  return width;
+  bool isRTL = StyleVisibility()->mDirection;
+  aDesiredSize.width +=
+    mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
+  aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
+  if (isRTL) {
+    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
+    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
+  } else {
+    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
+    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
+  }
 }
 
 NS_IMETHODIMP
 nsMathMLmoFrame::AttributeChanged(int32_t         aNameSpaceID,
                                   nsIAtom*        aAttribute,
                                   int32_t         aModType)
 {
   // check if this is an attribute that can affect the embellished hierarchy
--- a/layout/mathml/nsMathMLmoFrame.h
+++ b/layout/mathml/nsMathMLmoFrame.h
@@ -41,18 +41,19 @@ public:
   NS_IMETHOD
   Reflow(nsPresContext*          aPresContext,
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
   virtual void MarkIntrinsicWidthsDirty() MOZ_OVERRIDE;
 
-  virtual nscoord
-  GetIntrinsicWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual void
+  GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext,
+                           nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
   NS_IMETHOD
   AttributeChanged(int32_t         aNameSpaceID,
                    nsIAtom*        aAttribute,
                    int32_t         aModType) MOZ_OVERRIDE;
 
   // This method is called by the parent frame to ask <mo> 
   // to stretch itself.
--- a/layout/mathml/nsMathMLmrootFrame.cpp
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -345,42 +345,46 @@ nsMathMLmrootFrame::Reflow(nsPresContext
   mReference.x = 0;
   mReference.y = aDesiredSize.ascent;
 
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return NS_OK;
 }
 
-/* virtual */ nscoord
-nsMathMLmrootFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
+/* virtual */ void
+nsMathMLmrootFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
 {
   nsIFrame* baseFrame = mFrames.FirstChild();
   nsIFrame* indexFrame = nullptr;
   if (baseFrame)
     indexFrame = baseFrame->GetNextSibling();
   if (!indexFrame || indexFrame->GetNextSibling()) {
-    nsHTMLReflowMetrics desiredSize;
-    ReflowError(*aRenderingContext, desiredSize);
-    return desiredSize.width;
+    ReflowError(*aRenderingContext, aDesiredSize);
+    return;
   }
 
   nscoord baseWidth =
     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
                                          nsLayoutUtils::PREF_WIDTH);
   nscoord indexWidth =
     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
                                          nsLayoutUtils::PREF_WIDTH);
   nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
 
   nscoord dxSqr;
   GetRadicalXOffsets(indexWidth, sqrWidth, aRenderingContext->FontMetrics(),
                      nullptr, &dxSqr);
 
-  return dxSqr + sqrWidth + baseWidth;
+  nscoord width = dxSqr + sqrWidth + baseWidth;
+
+  aDesiredSize.width = width;
+  aDesiredSize.mBoundingMetrics.width = width;
+  aDesiredSize.mBoundingMetrics.leftBearing = 0;
+  aDesiredSize.mBoundingMetrics.rightBearing = width;
 }
 
 // ----------------------
 // the Style System will use these to pass the proper style context to our MathMLChar
 nsStyleContext*
 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
 {
   switch (aIndex) {
--- a/layout/mathml/nsMathMLmrootFrame.h
+++ b/layout/mathml/nsMathMLmrootFrame.h
@@ -35,18 +35,19 @@ public:
   TransmitAutomaticData() MOZ_OVERRIDE;
 
   NS_IMETHOD
   Reflow(nsPresContext*          aPresContext,
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
-  virtual nscoord
-  GetIntrinsicWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE;
+  virtual void
+  GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext,
+                           nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
 protected:
   nsMathMLmrootFrame(nsStyleContext* aContext);
   virtual ~nsMathMLmrootFrame();
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -34,16 +34,17 @@ fails-if(winWidget) == mfenced-10.html m
 == mi-mathvariant-1.xhtml mi-mathvariant-1-ref.xhtml
 == mi-mathvariant-2.xhtml mi-mathvariant-2-ref.xhtml
 != non-spacing-accent-1.xhtml non-spacing-accent-1-ref.xhtml
 == overbar-width-1.xhtml overbar-width-1-ref.xhtml
 skip-if(B2G) == quotes-1.xhtml quotes-1-ref.xhtml
 != stretchy-underbar-1.xhtml stretchy-underbar-1-ref.xhtml 
 == table-width-1.xhtml table-width-1-ref.xhtml
 == table-width-2.html table-width-2-ref.html
+fails-if(OSX||/^Windows\x20NT\x206\.[12]/.test(http.oscpu)||Android) == table-width-3.html table-width-3-ref.html
 == underbar-width-1.xhtml underbar-width-1-ref.xhtml
 == mathml-type-supported.xhtml mathml-type-supported-ref.xml
 == mtable-align-negative-rownumber.html mtable-align-negative-rownumber-ref.html
 != embellished-op-1-1.html embellished-op-1-1-ref.html
 != embellished-op-1-2.html embellished-op-1-2-ref.html
 != embellished-op-1-3.html embellished-op-1-3-ref.html
 != embellished-op-1-4.html embellished-op-1-4-ref.html
 != embellished-op-1-5.html embellished-op-1-5-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/table-width-3-ref.html
@@ -0,0 +1,100 @@
+<!doctype>
+<html>
+  <head>
+    <title>table-width-3</title>
+    <meta charset="utf-8"/>
+    <style type="text/css">
+      html { background-color: grey; }
+      td { border: 1px solid white;
+      padding: 0;
+      background-color: black;
+      color: red; }
+      mi, mtext { font-size: 3em; }
+      span { font-style: italic; display: inline-block; }
+    </style>
+  </head>
+  <body>
+
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mi>f</mi>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mi>f</mi>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mi>f</mi>
+              <mi>f</mi>
+              <mi>f</mi>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mi style="font-style: italic;">fff</mi>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mtext><span>fff</span></mtext>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mtext><span>f</span></mtext>
+              <mtext><span>f</span></mtext>
+              <mtext><span>f</span></mtext>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mphantom>
+              <mtext><span>f</span><span>f</span><span>f</span></mtext>
+            </mphantom>
+          </math>
+        </td>
+      </tr>
+    </table>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/table-width-3.html
@@ -0,0 +1,87 @@
+<!doctype>
+<html>
+  <head>
+    <title>table-width-3</title>
+    <meta charset="utf-8"/>
+    <style type="text/css">
+      html { background-color: grey; }
+      td { border: 1px solid white;
+      padding: 0;
+      background-color: black;
+      color: black; }
+      mi, mtext { font-size: 3em; }
+      span { font-style: italic; display: inline-block; }
+    </style>
+  </head>
+  <body>
+
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mi>f</mi>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mi>    f    </mi>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mi>f</mi>
+            <mi>f</mi>
+            <mi>f</mi>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mi style="font-style: italic;">fff</mi>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mtext><span>fff</span></mtext>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mtext><span>f</span></mtext>
+            <mtext><span>f</span></mtext>
+            <mtext><span>f</span></mtext>
+          </math>
+        </td>
+      </tr>
+    </table>
+    <table>
+      <tr>
+        <td>
+          <math>
+            <mtext><span>f</span><span>f</span><span>f</span></mtext>
+          </math>
+        </td>
+      </tr>
+    </table>
+
+  </body>
+</html>