forked from mirrors/gecko-dev
		
	 02de79cf14
			
		
	
	
		02de79cf14
		
	
	
	
	
		
			
			MozReview-Commit-ID: 5cpCdxuXNin --HG-- extra : amend_source : a9a3f49d7d9414a4395f6d4d0d80aee7f578ceeb
		
			
				
	
	
		
			1572 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1572 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsMathMLContainerFrame.h"
 | |
| 
 | |
| #include "gfxContext.h"
 | |
| #include "gfxUtils.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsPresContext.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsStyleContext.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "nsIDOMMutationEvent.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsDisplayList.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "nsIScriptError.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsMathMLElement.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| //
 | |
| // nsMathMLContainerFrame implementation
 | |
| //
 | |
| 
 | |
| 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(DrawTarget* aDrawTarget,
 | |
|                                     ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   // clear all other flags and record that there is an error with this frame
 | |
|   mEmbellishData.flags = 0;
 | |
|   mPresentationData.flags = NS_MATHML_ERROR;
 | |
| 
 | |
|   ///////////////
 | |
|   // Set font
 | |
|   RefPtr<nsFontMetrics> fm =
 | |
|     nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
 | |
| 
 | |
|   // bounding metrics
 | |
|   nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
 | |
|   mBoundingMetrics =
 | |
|     nsLayoutUtils::AppUnitBoundsOfString(errorMsg.get(), errorMsg.Length(),
 | |
|                                          *fm, aDrawTarget);
 | |
| 
 | |
|   // reflow metrics
 | |
|   WritingMode wm = aDesiredSize.GetWritingMode();
 | |
|   aDesiredSize.SetBlockStartAscent(fm->MaxAscent());
 | |
|   nscoord descent = fm->MaxDescent();
 | |
|   aDesiredSize.BSize(wm) = aDesiredSize.BlockStartAscent() + descent;
 | |
|   aDesiredSize.ISize(wm) = mBoundingMetrics.width;
 | |
| 
 | |
|   // Also return our bounding metrics
 | |
|   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| class nsDisplayMathMLError : public nsDisplayItem {
 | |
| public:
 | |
|   nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
 | |
|     : nsDisplayItem(aBuilder, aFrame) {
 | |
|     MOZ_COUNT_CTOR(nsDisplayMathMLError);
 | |
|   }
 | |
| #ifdef NS_BUILD_REFCNT_LOGGING
 | |
|   virtual ~nsDisplayMathMLError() {
 | |
|     MOZ_COUNT_DTOR(nsDisplayMathMLError);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   virtual void Paint(nsDisplayListBuilder* aBuilder,
 | |
|                      gfxContext* aCtx) override;
 | |
|   NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR)
 | |
| };
 | |
| 
 | |
| void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
 | |
|                                  gfxContext* aCtx)
 | |
| {
 | |
|   // Set color and font ...
 | |
|   RefPtr<nsFontMetrics> fm =
 | |
|     nsLayoutUtils::GetFontMetricsForFrame(mFrame, 1.0f);
 | |
| 
 | |
|   nsPoint pt = ToReferenceFrame();
 | |
|   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|   DrawTarget* drawTarget = aCtx->GetDrawTarget();
 | |
|   Rect rect = NSRectToSnappedRect(nsRect(pt, mFrame->GetSize()),
 | |
|                                   appUnitsPerDevPixel,
 | |
|                                   *drawTarget);
 | |
|   ColorPattern red(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
 | |
|   drawTarget->FillRect(rect, red);
 | |
| 
 | |
|   aCtx->SetColor(Color(1.f, 1.f, 1.f));
 | |
|   nscoord ascent = fm->MaxAscent();
 | |
|   NS_NAMED_LITERAL_STRING(errorMsg, "invalid-markup");
 | |
|   nsLayoutUtils::DrawUniDirString(errorMsg.get(), uint32_t(errorMsg.Length()),
 | |
|                                   nsPoint(pt.x, pt.y + ascent), *fm, *aCtx);
 | |
| }
 | |
| 
 | |
| /* /////////////
 | |
|  * nsIMathMLFrame - support methods for stretchy elements
 | |
|  * =============================================================================
 | |
|  */
 | |
| 
 | |
| static bool
 | |
| IsForeignChild(const nsIFrame* aFrame)
 | |
| {
 | |
|   // This counts nsMathMLmathBlockFrame as a foreign child, because it
 | |
|   // uses block reflow
 | |
|   return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) || aFrame->IsBlockFrame();
 | |
| }
 | |
| 
 | |
| NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTMLReflowOutputProperty,
 | |
|                                     ReflowOutput)
 | |
| 
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame*                  aFrame,
 | |
|                                                         const ReflowOutput& aReflowOutput,
 | |
|                                                         const nsBoundingMetrics&   aBoundingMetrics)
 | |
| {
 | |
|   ReflowOutput* reflowOutput = new ReflowOutput(aReflowOutput);
 | |
|   reflowOutput->mBoundingMetrics = aBoundingMetrics;
 | |
|   aFrame->SetProperty(HTMLReflowOutputProperty(), reflowOutput);
 | |
| }
 | |
| 
 | |
| // helper method to facilitate getting the reflow and bounding metrics
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
 | |
|                                                        ReflowOutput& aReflowOutput,
 | |
|                                                        nsBoundingMetrics&   aBoundingMetrics,
 | |
|                                                        eMathMLFrameType*    aMathMLFrameType)
 | |
| {
 | |
|   NS_PRECONDITION(aFrame, "null arg");
 | |
| 
 | |
|   ReflowOutput* reflowOutput =
 | |
|     aFrame->GetProperty(HTMLReflowOutputProperty());
 | |
| 
 | |
|   // IMPORTANT: This function is only meant to be called in Place() methods
 | |
|   // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
 | |
|   // information.
 | |
|   NS_ASSERTION(reflowOutput, "Didn't SaveReflowAndBoundingMetricsFor frame!");
 | |
|   if (reflowOutput) {
 | |
|     aReflowOutput = *reflowOutput;
 | |
|     aBoundingMetrics = reflowOutput->mBoundingMetrics;
 | |
|   }
 | |
| 
 | |
|   if (aMathMLFrameType) {
 | |
|     if (!IsForeignChild(aFrame)) {
 | |
|       nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
 | |
|       if (mathMLFrame) {
 | |
|         *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::ClearSavedChildMetrics()
 | |
| {
 | |
|   nsIFrame* childFrame = mFrames.FirstChild();
 | |
|   while (childFrame) {
 | |
|     childFrame->DeleteProperty(HTMLReflowOutputProperty());
 | |
|     childFrame = childFrame->GetNextSibling();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // helper to get the preferred size that a container frame should use to fire
 | |
| // the stretch on its stretchy child frames.
 | |
| void
 | |
| nsMathMLContainerFrame::GetPreferredStretchSize(DrawTarget*          aDrawTarget,
 | |
|                                                 uint32_t             aOptions,
 | |
|                                                 nsStretchDirection   aStretchDirection,
 | |
|                                                 nsBoundingMetrics&   aPreferredStretchSize)
 | |
| {
 | |
|   if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
 | |
|     // when our actual size is ok, just use it
 | |
|     aPreferredStretchSize = mBoundingMetrics;
 | |
|   }
 | |
|   else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
 | |
|     // compute our up-to-date size using Place()
 | |
|     ReflowOutput reflowOutput(GetWritingMode());
 | |
|     Place(aDrawTarget, false, reflowOutput);
 | |
|     aPreferredStretchSize = reflowOutput.mBoundingMetrics;
 | |
|   }
 | |
|   else {
 | |
|     // compute a size that includes embellishments iff the container stretches
 | |
|     // in the same direction as the embellished operator.
 | |
|     bool stretchAll = aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL ?
 | |
|                       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) :
 | |
|                       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
 | |
|     NS_ASSERTION(aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL ||
 | |
|                  aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL,
 | |
|                  "You must specify a direction in which to stretch");
 | |
|     NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
 | |
|                  stretchAll,
 | |
|                  "invalid call to GetPreferredStretchSize");
 | |
|     bool firstTime = true;
 | |
|     nsBoundingMetrics bm, bmChild;
 | |
|     nsIFrame* childFrame =
 | |
|       stretchAll ? PrincipalChildList().FirstChild() : mPresentationData.baseFrame;
 | |
|     while (childFrame) {
 | |
|       // initializations in case this child happens not to be a MathML frame
 | |
|       nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
 | |
|       if (mathMLFrame) {
 | |
|         nsEmbellishData embellishData;
 | |
|         nsPresentationData presentationData;
 | |
|         mathMLFrame->GetEmbellishData(embellishData);
 | |
|         mathMLFrame->GetPresentationData(presentationData);
 | |
|         if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
 | |
|             embellishData.direction == aStretchDirection &&
 | |
|             presentationData.baseFrame) {
 | |
|           // embellishements are not included, only consider the inner first child itself
 | |
|           // XXXkt Does that mean the core descendent frame should be used
 | |
|           // instead of the base child?
 | |
|           nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame);
 | |
|           if (mathMLchildFrame) {
 | |
|             mathMLFrame = mathMLchildFrame;
 | |
|           }
 | |
|         }
 | |
|         mathMLFrame->GetBoundingMetrics(bmChild);
 | |
|       }
 | |
|       else {
 | |
|         ReflowOutput unused(GetWritingMode());
 | |
|         GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
 | |
|       }
 | |
| 
 | |
|       if (firstTime) {
 | |
|         firstTime = false;
 | |
|         bm = bmChild;
 | |
|         if (!stretchAll) {
 | |
|           // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
 | |
|           // or <maction>...<mo>...</mo></maction>.
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         if (aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL) {
 | |
|           // if we get here, it means this is container that will stack its children
 | |
|           // vertically and fire an horizontal stretch on each them. This is the case
 | |
|           // for \munder, \mover, \munderover. We just sum-up the size vertically.
 | |
|           bm.descent += bmChild.ascent + bmChild.descent;
 | |
|           // Sometimes non-spacing marks (when width is zero) are positioned
 | |
|           // to the left of the origin, but it is the distance between left
 | |
|           // and right bearing that is important rather than the offsets from
 | |
|           // the origin.
 | |
|           if (bmChild.width == 0) {
 | |
|             bmChild.rightBearing -= bmChild.leftBearing;
 | |
|             bmChild.leftBearing = 0;
 | |
|           }
 | |
|           if (bm.leftBearing > bmChild.leftBearing)
 | |
|             bm.leftBearing = bmChild.leftBearing;
 | |
|           if (bm.rightBearing < bmChild.rightBearing)
 | |
|             bm.rightBearing = bmChild.rightBearing;
 | |
|         }
 | |
|         else if (aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) {
 | |
|           // just sum-up the sizes horizontally.
 | |
|           bm += bmChild;
 | |
|         }
 | |
|         else {
 | |
|           NS_ERROR("unexpected case in GetPreferredStretchSize");
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       childFrame = childFrame->GetNextSibling();
 | |
|     }
 | |
|     aPreferredStretchSize = bm;
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsMathMLContainerFrame::Stretch(DrawTarget*          aDrawTarget,
 | |
|                                 nsStretchDirection   aStretchDirection,
 | |
|                                 nsBoundingMetrics&   aContainerSize,
 | |
|                                 ReflowOutput& aDesiredStretchSize)
 | |
| {
 | |
|   if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
 | |
| 
 | |
|     if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
 | |
|       NS_WARNING("it is wrong to fire stretch more than once on a frame");
 | |
|       return NS_OK;
 | |
|     }
 | |
|     mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
 | |
| 
 | |
|     if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
 | |
|       NS_WARNING("it is wrong to fire stretch on a erroneous frame");
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     // Pass the stretch to the base child ...
 | |
| 
 | |
|     nsIFrame* baseFrame = mPresentationData.baseFrame;
 | |
|     if (baseFrame) {
 | |
|       nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
 | |
|       NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
 | |
|       if (mathMLFrame) {
 | |
| 
 | |
|         // And the trick is that the child's rect.x is still holding the descent,
 | |
|         // and rect.y is still holding the ascent ...
 | |
|         ReflowOutput childSize(aDesiredStretchSize);
 | |
|         GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
 | |
| 
 | |
|         // See if we should downsize and confine the stretch to us...
 | |
|         // XXX there may be other cases where we can downsize the stretch,
 | |
|         // e.g., the first ∑ might appear big in the following situation
 | |
|         // <math xmlns='http://www.w3.org/1998/Math/MathML'>
 | |
|         //   <mstyle>
 | |
|         //     <msub>
 | |
|         //        <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
 | |
|         //        <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
 | |
|         //      </msub>
 | |
|         //   </mstyle>
 | |
|         // </math>
 | |
|         nsBoundingMetrics containerSize = aContainerSize;
 | |
|         if (aStretchDirection != mEmbellishData.direction &&
 | |
|             mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED) {
 | |
|           NS_ASSERTION(mEmbellishData.direction != NS_STRETCH_DIRECTION_DEFAULT,
 | |
|                        "Stretches may have a default direction, operators can not.");
 | |
|           if (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL ?
 | |
|               NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) :
 | |
|               NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
 | |
|             GetPreferredStretchSize(aDrawTarget, 0,
 | |
|                                     mEmbellishData.direction, containerSize);
 | |
|             // Stop further recalculations
 | |
|             aStretchDirection = mEmbellishData.direction;
 | |
|           } else {
 | |
|             // We aren't going to stretch the child, so just use the child metrics.
 | |
|             containerSize = childSize.mBoundingMetrics;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // do the stretching...
 | |
|         mathMLFrame->Stretch(aDrawTarget,
 | |
|                              aStretchDirection, containerSize, childSize);
 | |
|         // store the updated metrics
 | |
|         SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
 | |
|                                         childSize.mBoundingMetrics);
 | |
| 
 | |
|         // Remember the siblings which were _deferred_.
 | |
|         // Now that this embellished child may have changed, we need to
 | |
|         // fire the stretch on its siblings using our updated size
 | |
| 
 | |
|         if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
 | |
|             NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
 | |
| 
 | |
|           nsStretchDirection stretchDir =
 | |
|             NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
 | |
|               NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
 | |
| 
 | |
|           GetPreferredStretchSize(aDrawTarget, STRETCH_CONSIDER_EMBELLISHMENTS,
 | |
|                                   stretchDir, containerSize);
 | |
| 
 | |
|           nsIFrame* childFrame = mFrames.FirstChild();
 | |
|           while (childFrame) {
 | |
|             if (childFrame != mPresentationData.baseFrame) {
 | |
|               mathMLFrame = do_QueryFrame(childFrame);
 | |
|               if (mathMLFrame) {
 | |
|                 // retrieve the metrics that was stored at the previous pass
 | |
|                 GetReflowAndBoundingMetricsFor(childFrame,
 | |
|                   childSize, childSize.mBoundingMetrics);
 | |
|                 // do the stretching...
 | |
|                 mathMLFrame->Stretch(aDrawTarget, stretchDir,
 | |
|                                      containerSize, childSize);
 | |
|                 // store the updated metrics
 | |
|                 SaveReflowAndBoundingMetricsFor(childFrame, childSize,
 | |
|                                                 childSize.mBoundingMetrics);
 | |
|               }
 | |
|             }
 | |
|             childFrame = childFrame->GetNextSibling();
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // re-position all our children
 | |
|         nsresult rv = Place(aDrawTarget, true, aDesiredStretchSize);
 | |
|         if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
 | |
|           // Make sure the child frames get their DidReflow() calls.
 | |
|           DidReflowChildren(mFrames.FirstChild());
 | |
|         }
 | |
| 
 | |
|         // If our parent is not embellished, it means we are the outermost embellished
 | |
|         // container and so we put the spacing, otherwise we don't include the spacing,
 | |
|         // the outermost embellished container will take care of it.
 | |
| 
 | |
|         nsEmbellishData parentData;
 | |
|         GetEmbellishDataFrom(GetParent(), parentData);
 | |
|         // ensure that we are the embellished child, not just a sibling
 | |
|         // (need to test coreFrame since <mfrac> resets other things)
 | |
|         if (parentData.coreFrame != mEmbellishData.coreFrame) {
 | |
|           // (we fetch values from the core since they may use units that depend
 | |
|           // on style data, and style changes could have occurred in the core since
 | |
|           // our last visit there)
 | |
|           nsEmbellishData coreData;
 | |
|           GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
 | |
| 
 | |
|           mBoundingMetrics.width +=
 | |
|             coreData.leadingSpace + coreData.trailingSpace;
 | |
|           aDesiredStretchSize.Width() = mBoundingMetrics.width;
 | |
|           aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
 | |
| 
 | |
|           nscoord dx = (StyleVisibility()->mDirection ?
 | |
|                         coreData.trailingSpace : coreData.leadingSpace);
 | |
|           if (dx != 0) {
 | |
|             mBoundingMetrics.leftBearing += dx;
 | |
|             mBoundingMetrics.rightBearing += dx;
 | |
|             aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
 | |
|             aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
 | |
| 
 | |
|             nsIFrame* childFrame = mFrames.FirstChild();
 | |
|             while (childFrame) {
 | |
|               childFrame->SetPosition(childFrame->GetPosition()
 | |
|                                       + nsPoint(dx, 0));
 | |
|               childFrame = childFrame->GetNextSibling();
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Finished with these:
 | |
|         ClearSavedChildMetrics();
 | |
|         // Set our overflow area
 | |
|         GatherAndStoreOverflow(&aDesiredStretchSize);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::FinalizeReflow(DrawTarget* aDrawTarget,
 | |
|                                        ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
 | |
|   // and descent in expectation of a stretch command. Hence we need to ensure that
 | |
|   // a stretch command will actually be fired later on, after exiting from our
 | |
|   // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
 | |
|   // with inappropriate data causing children to be improperly positioned.
 | |
|   // This helper method checks to see if our parent will fire a stretch command
 | |
|   // targeted at us. If not, we go ahead and fire an involutive stretch on
 | |
|   // ourselves. This will clear all the rect.x and rect.y, and return our
 | |
|   // desired size.
 | |
| 
 | |
| 
 | |
|   // First, complete the post-reflow hook.
 | |
|   // We use the information in our children rectangles to position them.
 | |
|   // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
 | |
|   // They will still be holding the ascent and descent for each child.
 | |
| 
 | |
|   // The first clause caters for any non-embellished container.
 | |
|   // The second clause is for a container which won't fire stretch even though it is
 | |
|   // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
 | |
|   // because it excludes the particular case of the core <mo>...</mo> itself.
 | |
|   // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
 | |
|   bool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
 | |
|                        (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
 | |
|                         mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
 | |
|   nsresult rv = Place(aDrawTarget, placeOrigin, aDesiredSize);
 | |
| 
 | |
|   // Place() will call FinishReflowChild() when placeOrigin is true but if
 | |
|   // it returns before reaching FinishReflowChild() due to errors we need
 | |
|   // to fulfill the reflow protocol by calling DidReflow for the child frames
 | |
|   // that still needs it here (or we may crash - bug 366012).
 | |
|   // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
 | |
|   // through Stretch() eventually.
 | |
|   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
 | |
|     GatherAndStoreOverflow(&aDesiredSize);
 | |
|     DidReflowChildren(PrincipalChildList().FirstChild());
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   bool parentWillFireStretch = false;
 | |
|   if (!placeOrigin) {
 | |
|     // This means the rect.x and rect.y of our children were not set!!
 | |
|     // Don't go without checking to see if our parent will later fire a Stretch() command
 | |
|     // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
 | |
|     nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetParent());
 | |
|     if (mathMLFrame) {
 | |
|       nsEmbellishData embellishData;
 | |
|       nsPresentationData presentationData;
 | |
|       mathMLFrame->GetEmbellishData(embellishData);
 | |
|       mathMLFrame->GetPresentationData(presentationData);
 | |
|       if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
 | |
|           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
 | |
|           (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
 | |
|             && presentationData.baseFrame == this))
 | |
|       {
 | |
|         parentWillFireStretch = true;
 | |
|       }
 | |
|     }
 | |
|     if (!parentWillFireStretch) {
 | |
|       // There is nobody who will fire the stretch for us, we do it ourselves!
 | |
| 
 | |
|       bool stretchAll =
 | |
|         /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
 | |
|         NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
 | |
| 
 | |
|       nsStretchDirection stretchDir;
 | |
|       if (mEmbellishData.coreFrame == this || /* case of a bare <mo>...</mo> itself */
 | |
|           (mEmbellishData.direction == NS_STRETCH_DIRECTION_HORIZONTAL &&
 | |
|            stretchAll) || /* or <mover><mo>...</mo>...</mover>, or friends */
 | |
|           mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) { /* Doesn't stretch */
 | |
|         stretchDir = mEmbellishData.direction;
 | |
|       } else {
 | |
|         // Let the Stretch() call decide the direction.
 | |
|         stretchDir = NS_STRETCH_DIRECTION_DEFAULT;
 | |
|       }
 | |
|       // Use our current size as computed earlier by Place()
 | |
|       // The stretch call will detect if this is incorrect and recalculate the size.
 | |
|       nsBoundingMetrics defaultSize = aDesiredSize.mBoundingMetrics;
 | |
| 
 | |
|       Stretch(aDrawTarget, stretchDir, defaultSize, aDesiredSize);
 | |
| #ifdef DEBUG
 | |
|       {
 | |
|         // The Place() call above didn't request FinishReflowChild(),
 | |
|         // so let's check that we eventually did through Stretch().
 | |
|         for (nsIFrame* childFrame : PrincipalChildList()) {
 | |
|           NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
 | |
|                        "DidReflow() was never called");
 | |
|         }
 | |
|       }
 | |
| #endif
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Also return our bounding metrics
 | |
|   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
 | |
| 
 | |
|   // see if we should fix the spacing
 | |
|   FixInterFrameSpacing(aDesiredSize);
 | |
| 
 | |
|   if (!parentWillFireStretch) {
 | |
|     // Not expecting a stretch.
 | |
|     // Finished with these:
 | |
|     ClearSavedChildMetrics();
 | |
|     // Set our overflow area.
 | |
|     GatherAndStoreOverflow(&aDesiredSize);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* /////////////
 | |
|  * nsIMathMLFrame - support methods for scripting elements (nested frames
 | |
|  * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
 | |
|  * mfrac, mroot, mtable).
 | |
|  * =============================================================================
 | |
|  */
 | |
| 
 | |
| // helper to let the update of presentation data pass through
 | |
| // a subtree that may contain non-mathml container frames
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame*       aFrame,
 | |
|                                                      uint32_t        aFlagsValues,
 | |
|                                                      uint32_t        aFlagsToUpdate)
 | |
| {
 | |
|   if (!aFrame || !aFlagsToUpdate)
 | |
|     return;
 | |
|   nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
 | |
|   if (mathMLFrame) {
 | |
|     // update
 | |
|     mathMLFrame->UpdatePresentationData(aFlagsValues,
 | |
|                                         aFlagsToUpdate);
 | |
|     // propagate using the base method to make sure that the control
 | |
|     // is passed on to MathML frames that may be overloading the method
 | |
|     mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
 | |
|       aFlagsValues, aFlagsToUpdate);
 | |
|   }
 | |
|   else {
 | |
|     // propagate down the subtrees
 | |
|     for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
 | |
|       PropagatePresentationDataFor(childFrame,
 | |
|         aFlagsValues, aFlagsToUpdate);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame*       aParentFrame,
 | |
|                                                              int32_t         aFirstChildIndex,
 | |
|                                                              int32_t         aLastChildIndex,
 | |
|                                                              uint32_t        aFlagsValues,
 | |
|                                                              uint32_t        aFlagsToUpdate)
 | |
| {
 | |
|   if (!aParentFrame || !aFlagsToUpdate)
 | |
|     return;
 | |
|   int32_t index = 0;
 | |
|   for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) {
 | |
|     if ((index >= aFirstChildIndex) &&
 | |
|         ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
 | |
|          (index <= aLastChildIndex)))) {
 | |
|       PropagatePresentationDataFor(childFrame,
 | |
|         aFlagsValues, aFlagsToUpdate);
 | |
|     }
 | |
|     index++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* //////////////////
 | |
|  * Frame construction
 | |
|  * =============================================================================
 | |
|  */
 | |
| 
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
 | |
|                                          const nsRect&           aDirtyRect,
 | |
|                                          const nsDisplayListSet& aLists)
 | |
| {
 | |
|   // report an error if something wrong was found in this frame
 | |
|   if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
 | |
|     if (!IsVisibleForPainting(aBuilder))
 | |
|       return;
 | |
| 
 | |
|     aLists.Content()->AppendNewToTop(
 | |
|       new (aBuilder) nsDisplayMathMLError(aBuilder, this));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   DisplayBorderBackgroundOutline(aBuilder, aLists);
 | |
| 
 | |
|   BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
 | |
|                                       DISPLAY_CHILD_INLINE);
 | |
| 
 | |
| #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
 | |
|   // for visual debug
 | |
|   // ----------------
 | |
|   // if you want to see your bounding box, make sure to properly fill
 | |
|   // your mBoundingMetrics and mReference point, and set
 | |
|   // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
 | |
|   // in the Init() of your sub-class
 | |
|   DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| // Note that this method re-builds the automatic data in the children -- not
 | |
| // in aParentFrame itself (except for those particular operations that the
 | |
| // parent frame may do in its TransmitAutomaticData()).
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
 | |
| {
 | |
|   // 1. As we descend the tree, make each child frame inherit data from
 | |
|   // the parent
 | |
|   // 2. As we ascend the tree, transmit any specific change that we want
 | |
|   // down the subtrees
 | |
|   for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) {
 | |
|     nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
 | |
|     if (childMathMLFrame) {
 | |
|       childMathMLFrame->InheritAutomaticData(aParentFrame);
 | |
|     }
 | |
|     RebuildAutomaticDataForChildren(childFrame);
 | |
|   }
 | |
|   nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
 | |
|   if (mathMLFrame) {
 | |
|     mathMLFrame->TransmitAutomaticData();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ nsresult
 | |
| nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
 | |
| {
 | |
|   if (!aParentFrame)
 | |
|     return NS_OK;
 | |
| 
 | |
|   // walk-up to the first frame that is a MathML frame, stop if we reach <math>
 | |
|   nsIFrame* frame = aParentFrame;
 | |
|   while (1) {
 | |
|      nsIFrame* parent = frame->GetParent();
 | |
|      if (!parent || !parent->GetContent())
 | |
|        break;
 | |
| 
 | |
|     // stop if it is a MathML frame
 | |
|     nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
 | |
|     if (mathMLFrame)
 | |
|       break;
 | |
| 
 | |
|     // stop if we reach the root <math> tag
 | |
|     nsIContent* content = frame->GetContent();
 | |
|     NS_ASSERTION(content, "dangling frame without a content node");
 | |
|     if (!content)
 | |
|       break;
 | |
|     if (content->IsMathMLElement(nsGkAtoms::math))
 | |
|       break;
 | |
| 
 | |
|     frame = parent;
 | |
|   }
 | |
| 
 | |
|   // re-sync the presentation data and embellishment data of our children
 | |
|   RebuildAutomaticDataForChildren(frame);
 | |
| 
 | |
|   // Ask our parent frame to reflow us
 | |
|   nsIFrame* parent = frame->GetParent();
 | |
|   NS_ASSERTION(parent, "No parent to pass the reflow request up to");
 | |
|   if (!parent)
 | |
|     return NS_OK;
 | |
| 
 | |
|   frame->PresContext()->PresShell()->
 | |
|     FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // There are precise rules governing children of a MathML frame,
 | |
| // and properties such as the scriptlevel depends on those rules.
 | |
| // Hence for things to work, callers must use Append/Insert/etc wisely.
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::ChildListChanged(int32_t aModType)
 | |
| {
 | |
|   // If this is an embellished frame we need to rebuild the
 | |
|   // embellished hierarchy by walking-up to the parent of the
 | |
|   // outermost embellished container.
 | |
|   nsIFrame* frame = this;
 | |
|   if (mEmbellishData.coreFrame) {
 | |
|     nsIFrame* parent = GetParent();
 | |
|     nsEmbellishData embellishData;
 | |
|     for ( ; parent; frame = parent, parent = parent->GetParent()) {
 | |
|       GetEmbellishDataFrom(parent, embellishData);
 | |
|       if (embellishData.coreFrame != mEmbellishData.coreFrame)
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
|   return ReLayoutChildren(frame);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::AppendFrames(ChildListID     aListID,
 | |
|                                      nsFrameList&    aFrameList)
 | |
| {
 | |
|   MOZ_ASSERT(aListID == kPrincipalList);
 | |
|   mFrames.AppendFrames(this, aFrameList);
 | |
|   ChildListChanged(nsIDOMMutationEvent::ADDITION);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::InsertFrames(ChildListID     aListID,
 | |
|                                      nsIFrame*       aPrevFrame,
 | |
|                                      nsFrameList&    aFrameList)
 | |
| {
 | |
|   MOZ_ASSERT(aListID == kPrincipalList);
 | |
|   mFrames.InsertFrames(this, aPrevFrame, aFrameList);
 | |
|   ChildListChanged(nsIDOMMutationEvent::ADDITION);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::RemoveFrame(ChildListID     aListID,
 | |
|                                     nsIFrame*       aOldFrame)
 | |
| {
 | |
|   MOZ_ASSERT(aListID == kPrincipalList);
 | |
|   mFrames.DestroyFrame(aOldFrame);
 | |
|   ChildListChanged(nsIDOMMutationEvent::REMOVAL);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::AttributeChanged(int32_t         aNameSpaceID,
 | |
|                                          nsIAtom*        aAttribute,
 | |
|                                          int32_t         aModType)
 | |
| {
 | |
|   // XXX Since they are numerous MathML attributes that affect layout, and
 | |
|   // we can't check all of them here, play safe by requesting a reflow.
 | |
|   // XXXldb This should only do work for attributes that cause changes!
 | |
|   PresContext()->PresShell()->
 | |
|     FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::GatherAndStoreOverflow(ReflowOutput* aMetrics)
 | |
| {
 | |
|   mBlockStartAscent = aMetrics->BlockStartAscent();
 | |
| 
 | |
|   // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
 | |
|   // frame rectangle.
 | |
|   aMetrics->SetOverflowAreasToDesiredBounds();
 | |
| 
 | |
|   ComputeCustomOverflow(aMetrics->mOverflowAreas);
 | |
| 
 | |
|   // mBoundingMetrics does not necessarily include content of <mpadded>
 | |
|   // elements whose mBoundingMetrics may not be representative of the true
 | |
|   // bounds, and doesn't include the CSS2 outline rectangles of children, so
 | |
|   // make such to include child overflow areas.
 | |
|   UnionChildOverflow(aMetrics->mOverflowAreas);
 | |
| 
 | |
|   FinishAndStoreOverflow(aMetrics);
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsMathMLContainerFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
 | |
| {
 | |
|   // All non-child-frame content such as nsMathMLChars (and most child-frame
 | |
|   // content) is included in mBoundingMetrics.
 | |
|   nsRect boundingBox(mBoundingMetrics.leftBearing,
 | |
|                      mBlockStartAscent - mBoundingMetrics.ascent,
 | |
|                      mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
 | |
|                      mBoundingMetrics.ascent + mBoundingMetrics.descent);
 | |
| 
 | |
|   // REVIEW: Maybe this should contribute only to visual overflow
 | |
|   // and not scrollable?
 | |
|   aOverflowAreas.UnionAllWith(boundingBox);
 | |
|   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::ReflowChild(nsIFrame*                aChildFrame,
 | |
|                                     nsPresContext*           aPresContext,
 | |
|                                     ReflowOutput&     aDesiredSize,
 | |
|                                     const ReflowInput& aReflowInput,
 | |
|                                     nsReflowStatus&          aStatus)
 | |
| {
 | |
|   // Having foreign/hybrid children, e.g., from html markups, is not defined by
 | |
|   // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
 | |
|   // to do some cool demos... or we may have a child that is an nsInlineFrame
 | |
|   // from a generated content such as :before { content: open-quote } or
 | |
|   // :after { content: close-quote }. Unfortunately, the other frames out-there
 | |
|   // may expect their own invariants that are not met when we mix things.
 | |
|   // Hence we do not claim their support, but we will nevertheless attempt to keep
 | |
|   // them in the flow, if we can get their desired size. We observed that most
 | |
|   // frames may be reflowed generically, but nsInlineFrames need extra care.
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
 | |
|   NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
 | |
| #endif
 | |
| 
 | |
|   nsContainerFrame::
 | |
|          ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowInput,
 | |
|                      0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
 | |
| 
 | |
|   if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
 | |
|     // This will be suitable for inline frames, which are wrapped in a block.
 | |
|     nscoord ascent;
 | |
|     WritingMode wm = aDesiredSize.GetWritingMode();
 | |
|     if (!nsLayoutUtils::GetLastLineBaseline(wm, aChildFrame, &ascent)) {
 | |
|       // We don't expect any other block children so just place the frame on
 | |
|       // the baseline instead of going through DidReflow() and
 | |
|       // GetBaseline().  This is what nsFrame::GetBaseline() will do anyway.
 | |
|       aDesiredSize.SetBlockStartAscent(aDesiredSize.BSize(wm));
 | |
|     } else {
 | |
|       aDesiredSize.SetBlockStartAscent(ascent);
 | |
|     }
 | |
|   }
 | |
|   if (IsForeignChild(aChildFrame)) {
 | |
|     // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
 | |
|     nsRect r = aChildFrame->ComputeTightBounds(aReflowInput.mRenderingContext->GetDrawTarget());
 | |
|     aDesiredSize.mBoundingMetrics.leftBearing = r.x;
 | |
|     aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
 | |
|     aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent() - r.y;
 | |
|     aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.BlockStartAscent();
 | |
|     aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::Reflow(nsPresContext*           aPresContext,
 | |
|                                ReflowOutput&     aDesiredSize,
 | |
|                                const ReflowInput& aReflowInput,
 | |
|                                nsReflowStatus&          aStatus)
 | |
| {
 | |
|   MarkInReflow();
 | |
|   mPresentationData.flags &= ~NS_MATHML_ERROR;
 | |
|   aDesiredSize.Width() = aDesiredSize.Height() = 0;
 | |
|   aDesiredSize.SetBlockStartAscent(0);
 | |
|   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
 | |
| 
 | |
|   /////////////
 | |
|   // Reflow children
 | |
|   // Asking each child to cache its bounding metrics
 | |
| 
 | |
|   nsReflowStatus childStatus;
 | |
|   nsIFrame* childFrame = mFrames.FirstChild();
 | |
|   while (childFrame) {
 | |
|     ReflowOutput childDesiredSize(aReflowInput, // ???
 | |
|                                          aDesiredSize.mFlags);
 | |
|     WritingMode wm = childFrame->GetWritingMode();
 | |
|     LogicalSize availSize = aReflowInput.ComputedSize(wm);
 | |
|     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
 | |
|     ReflowInput childReflowInput(aPresContext, aReflowInput,
 | |
|                                        childFrame, availSize);
 | |
|     ReflowChild(childFrame, aPresContext, childDesiredSize,
 | |
|                 childReflowInput, childStatus);
 | |
|     //NS_ASSERTION(childStatus.IsComplete(), "bad status");
 | |
|     SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
 | |
|                                     childDesiredSize.mBoundingMetrics);
 | |
|     childFrame = childFrame->GetNextSibling();
 | |
|   }
 | |
| 
 | |
|   /////////////
 | |
|   // If we are a container which is entitled to stretch its children, then we
 | |
|   // ask our stretchy children to stretch themselves
 | |
| 
 | |
|   // The stretching of siblings of an embellished child is _deferred_ until
 | |
|   // after finishing the stretching of the embellished child - bug 117652
 | |
| 
 | |
|   DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
 | |
| 
 | |
|   if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
 | |
|       (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
 | |
|        NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
 | |
| 
 | |
|     // get the stretchy direction
 | |
|     nsStretchDirection stretchDir =
 | |
|       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
 | |
|       ? NS_STRETCH_DIRECTION_VERTICAL
 | |
|       : NS_STRETCH_DIRECTION_HORIZONTAL;
 | |
| 
 | |
|     // what size should we use to stretch our stretchy children
 | |
|     // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
 | |
|     // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
 | |
|     // include them in the caculations of the size of stretchy elements
 | |
|     nsBoundingMetrics containerSize;
 | |
|     GetPreferredStretchSize(drawTarget, 0, stretchDir, containerSize);
 | |
| 
 | |
|     // fire the stretch on each child
 | |
|     childFrame = mFrames.FirstChild();
 | |
|     while (childFrame) {
 | |
|       nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
 | |
|       if (mathMLFrame) {
 | |
|         // retrieve the metrics that was stored at the previous pass
 | |
|         ReflowOutput childDesiredSize(aReflowInput);
 | |
|         GetReflowAndBoundingMetricsFor(childFrame,
 | |
|           childDesiredSize, childDesiredSize.mBoundingMetrics);
 | |
| 
 | |
|         mathMLFrame->Stretch(drawTarget, stretchDir,
 | |
|                              containerSize, childDesiredSize);
 | |
|         // store the updated metrics
 | |
|         SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
 | |
|                                         childDesiredSize.mBoundingMetrics);
 | |
|       }
 | |
|       childFrame = childFrame->GetNextSibling();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /////////////
 | |
|   // Place children now by re-adjusting the origins to align the baselines
 | |
|   FinalizeReflow(drawTarget, aDesiredSize);
 | |
| 
 | |
|   aStatus.Reset();
 | |
|   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 | |
| }
 | |
| 
 | |
| static nscoord AddInterFrameSpacingToSize(ReflowOutput&    aDesiredSize,
 | |
|                                           nsMathMLContainerFrame* aFrame);
 | |
| 
 | |
| /* virtual */ void
 | |
| nsMathMLContainerFrame::MarkIntrinsicISizesDirty()
 | |
| {
 | |
|   mIntrinsicWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
 | |
|   nsContainerFrame::MarkIntrinsicISizesDirty();
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::UpdateIntrinsicWidth(gfxContext* aRenderingContext)
 | |
| {
 | |
|   if (mIntrinsicWidth == NS_INTRINSIC_WIDTH_UNKNOWN) {
 | |
|     ReflowOutput desiredSize(GetWritingMode());
 | |
|     GetIntrinsicISizeMetrics(aRenderingContext, desiredSize);
 | |
| 
 | |
|     // Include the additional width added by FixInterFrameSpacing to ensure
 | |
|     // consistent width calculations.
 | |
|     AddInterFrameSpacingToSize(desiredSize, this);
 | |
|     mIntrinsicWidth = desiredSize.ISize(GetWritingMode());
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* virtual */ nscoord
 | |
| nsMathMLContainerFrame::GetMinISize(gfxContext* aRenderingContext)
 | |
| {
 | |
|   nscoord result;
 | |
|   DISPLAY_MIN_WIDTH(this, result);
 | |
|   UpdateIntrinsicWidth(aRenderingContext);
 | |
|   result = mIntrinsicWidth;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /* virtual */ nscoord
 | |
| nsMathMLContainerFrame::GetPrefISize(gfxContext* aRenderingContext)
 | |
| {
 | |
|   nscoord result;
 | |
|   DISPLAY_PREF_WIDTH(this, result);
 | |
|   UpdateIntrinsicWidth(aRenderingContext);
 | |
|   result = mIntrinsicWidth;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /* virtual */ void
 | |
| nsMathMLContainerFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext,
 | |
|                                                  ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   // Get child widths
 | |
|   nsIFrame* childFrame = mFrames.FirstChild();
 | |
|   while (childFrame) {
 | |
|     ReflowOutput childDesiredSize(GetWritingMode()); // ???
 | |
| 
 | |
|     nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
 | |
|     if (containerFrame) {
 | |
|       containerFrame->GetIntrinsicISizeMetrics(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_ISIZE);
 | |
| 
 | |
|       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
 | |
|   nsresult rv = MeasureForWidth(aRenderingContext->GetDrawTarget(), aDesiredSize);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize);
 | |
|   }
 | |
| 
 | |
|   ClearSavedChildMetrics();
 | |
| }
 | |
| 
 | |
| /* virtual */ nsresult
 | |
| nsMathMLContainerFrame::MeasureForWidth(DrawTarget* aDrawTarget,
 | |
|                                         ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   return Place(aDrawTarget, false, aDesiredSize);
 | |
| }
 | |
| 
 | |
| 
 | |
| // see spacing table in Chapter 18, TeXBook (p.170)
 | |
| // Our table isn't quite identical to TeX because operators have
 | |
| // built-in values for lspace & rspace in the Operator Dictionary.
 | |
| static int32_t kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
 | |
| {
 | |
|   // in units of muspace.
 | |
|   // upper half of the byte is set if the
 | |
|   // spacing is not to be used for scriptlevel > 0
 | |
| 
 | |
|   /*           Ord  OpOrd OpInv OpUsr Inner Italic Upright */
 | |
|   /*Ord  */   {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
 | |
|   /*OpOrd*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 | |
|   /*OpInv*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 | |
|   /*OpUsr*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
 | |
|   /*Inner*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
 | |
|   /*Italic*/  {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
 | |
|   /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}
 | |
| };
 | |
| 
 | |
| #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_)  \
 | |
|    /* no space if there is a frame that we know nothing about */        \
 | |
|    if (frametype1_ == eMathMLFrameType_UNKNOWN ||                       \
 | |
|        frametype2_ == eMathMLFrameType_UNKNOWN)                         \
 | |
|     space_ = 0;                                                         \
 | |
|   else {                                                                \
 | |
|     space_ = kInterFrameSpacingTable[frametype1_][frametype2_];         \
 | |
|     space_ = (scriptlevel_ > 0 && (space_ & 0xF0))                      \
 | |
|       ? 0 /* spacing is disabled */                                     \
 | |
|       : space_ & 0x0F;                                                  \
 | |
|   }                                                                     \
 | |
| 
 | |
| // This function computes the inter-space between two frames. However,
 | |
| // since invisible operators need special treatment, the inter-space may
 | |
| // be delayed when an invisible operator is encountered. In this case,
 | |
| // the function will carry the inter-space forward until it is determined
 | |
| // that it can be applied properly (i.e., until we encounter a visible
 | |
| // frame where to decide whether to accept or reject the inter-space).
 | |
| // aFromFrameType: remembers the frame when the carry-forward initiated.
 | |
| // aCarrySpace: keeps track of the inter-space that is delayed.
 | |
| // @returns: current inter-space (which is 0 when the true inter-space is
 | |
| // delayed -- and thus has no effect since the frame is invisible anyway).
 | |
| static nscoord
 | |
| GetInterFrameSpacing(int32_t           aScriptLevel,
 | |
|                      eMathMLFrameType  aFirstFrameType,
 | |
|                      eMathMLFrameType  aSecondFrameType,
 | |
|                      eMathMLFrameType* aFromFrameType, // IN/OUT
 | |
|                      int32_t*          aCarrySpace)    // IN/OUT
 | |
| {
 | |
|   eMathMLFrameType firstType = aFirstFrameType;
 | |
|   eMathMLFrameType secondType = aSecondFrameType;
 | |
| 
 | |
|   int32_t space;
 | |
|   GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
 | |
| 
 | |
|   // feedback control to avoid the inter-space to be added when not necessary
 | |
|   if (secondType == eMathMLFrameType_OperatorInvisible) {
 | |
|     // see if we should start to carry the space forward until we
 | |
|     // encounter a visible frame
 | |
|     if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
 | |
|       *aFromFrameType = firstType;
 | |
|       *aCarrySpace = space;
 | |
|     }
 | |
|     // keep carrying *aCarrySpace forward, while returning 0 for this stage
 | |
|     space = 0;
 | |
|   }
 | |
|   else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
 | |
|     // no carry-forward anymore, get the real inter-space between
 | |
|     // the two frames of interest
 | |
| 
 | |
|     firstType = *aFromFrameType;
 | |
| 
 | |
|     // But... the invisible operator that we encountered earlier could
 | |
|     // be sitting between italic and upright identifiers, e.g.,
 | |
|     //
 | |
|     // 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi>
 | |
|     // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
 | |
|     //
 | |
|     // the trick to get the inter-space in either situation
 | |
|     // is to promote "<mi>sin</mi><mo>⁡</mo>" and
 | |
|     // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
 | |
|     if (firstType == eMathMLFrameType_UprightIdentifier) {
 | |
|       firstType = eMathMLFrameType_OperatorUserDefined;
 | |
|     }
 | |
|     else if (secondType == eMathMLFrameType_UprightIdentifier) {
 | |
|       secondType = eMathMLFrameType_OperatorUserDefined;
 | |
|     }
 | |
| 
 | |
|     GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
 | |
| 
 | |
|     // Now, we have two values: the computed space and the space that
 | |
|     // has been carried forward until now. Which value do we pick?
 | |
|     // If the second type is an operator (e.g., fence), it already has
 | |
|     // built-in lspace & rspace, so we let them win. Otherwise we pick
 | |
|     // the max between the two values that we have.
 | |
|     if (secondType != eMathMLFrameType_OperatorOrdinary &&
 | |
|         space < *aCarrySpace)
 | |
|       space = *aCarrySpace;
 | |
| 
 | |
|     // reset everything now that the carry-forward is done
 | |
|     *aFromFrameType = eMathMLFrameType_UNKNOWN;
 | |
|     *aCarrySpace = 0;
 | |
|   }
 | |
| 
 | |
|   return space;
 | |
| }
 | |
| 
 | |
| static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
 | |
| {
 | |
|   return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
 | |
| }
 | |
| 
 | |
| class nsMathMLContainerFrame::RowChildFrameIterator {
 | |
| public:
 | |
|   explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
 | |
|     mParentFrame(aParentFrame),
 | |
|     mReflowOutput(aParentFrame->GetWritingMode()),
 | |
|     mX(0),
 | |
|     mCarrySpace(0),
 | |
|     mFromFrameType(eMathMLFrameType_UNKNOWN),
 | |
|     mRTL(aParentFrame->StyleVisibility()->mDirection)
 | |
|   {
 | |
|     if (!mRTL) {
 | |
|       mChildFrame = aParentFrame->mFrames.FirstChild();
 | |
|     } else {
 | |
|       mChildFrame = aParentFrame->mFrames.LastChild();
 | |
|     }
 | |
| 
 | |
|     if (!mChildFrame)
 | |
|       return;
 | |
| 
 | |
|     InitMetricsForChild();
 | |
|   }
 | |
| 
 | |
|   RowChildFrameIterator& operator++()
 | |
|   {
 | |
|     // add child size + italic correction
 | |
|     mX += mReflowOutput.mBoundingMetrics.width + mItalicCorrection;
 | |
| 
 | |
|     if (!mRTL) {
 | |
|       mChildFrame = mChildFrame->GetNextSibling();
 | |
|     } else {
 | |
|       mChildFrame = mChildFrame->GetPrevSibling();
 | |
|     }
 | |
| 
 | |
|     if (!mChildFrame)
 | |
|       return *this;
 | |
| 
 | |
|     eMathMLFrameType prevFrameType = mChildFrameType;
 | |
|     InitMetricsForChild();
 | |
| 
 | |
|     // add inter frame spacing
 | |
|     const nsStyleFont* font = mParentFrame->StyleFont();
 | |
|     nscoord space =
 | |
|       GetInterFrameSpacing(font->mScriptLevel,
 | |
|                            prevFrameType, mChildFrameType,
 | |
|                            &mFromFrameType, &mCarrySpace);
 | |
|     mX += space * GetThinSpace(font);
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   nsIFrame* Frame() const { return mChildFrame; }
 | |
|   nscoord X() const { return mX; }
 | |
|   const ReflowOutput& GetReflowOutput() const { return mReflowOutput; }
 | |
|   nscoord Ascent() const { return mReflowOutput.BlockStartAscent(); }
 | |
|   nscoord Descent() const { return mReflowOutput.Height() - mReflowOutput.BlockStartAscent(); }
 | |
|   const nsBoundingMetrics& BoundingMetrics() const {
 | |
|     return mReflowOutput.mBoundingMetrics;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   const nsMathMLContainerFrame* mParentFrame;
 | |
|   nsIFrame* mChildFrame;
 | |
|   ReflowOutput mReflowOutput;
 | |
|   nscoord mX;
 | |
| 
 | |
|   nscoord mItalicCorrection;
 | |
|   eMathMLFrameType mChildFrameType;
 | |
|   int32_t mCarrySpace;
 | |
|   eMathMLFrameType mFromFrameType;
 | |
| 
 | |
|   bool mRTL;
 | |
| 
 | |
|   void InitMetricsForChild()
 | |
|   {
 | |
|     GetReflowAndBoundingMetricsFor(mChildFrame, mReflowOutput, mReflowOutput.mBoundingMetrics,
 | |
|                                    &mChildFrameType);
 | |
|     nscoord leftCorrection, rightCorrection;
 | |
|     GetItalicCorrection(mReflowOutput.mBoundingMetrics,
 | |
|                         leftCorrection, rightCorrection);
 | |
|     if (!mChildFrame->GetPrevSibling() &&
 | |
|         mParentFrame->GetContent()->IsMathMLElement(nsGkAtoms::msqrt_)) {
 | |
|       // Remove leading correction in <msqrt> because the sqrt glyph itself is
 | |
|       // there first.
 | |
|       if (!mRTL) {
 | |
|         leftCorrection = 0;
 | |
|       } else {
 | |
|         rightCorrection = 0;
 | |
|       }
 | |
|     }
 | |
|     // add left correction -- this fixes the problem of the italic 'f'
 | |
|     // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
 | |
|     mX += leftCorrection;
 | |
|     mItalicCorrection = rightCorrection;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /* virtual */ nsresult
 | |
| nsMathMLContainerFrame::Place(DrawTarget*          aDrawTarget,
 | |
|                               bool                 aPlaceOrigin,
 | |
|                               ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   // This is needed in case this frame is empty (i.e., no child frames)
 | |
|   mBoundingMetrics = nsBoundingMetrics();
 | |
| 
 | |
|   RowChildFrameIterator child(this);
 | |
|   nscoord ascent = 0, descent = 0;
 | |
|   while (child.Frame()) {
 | |
|     if (descent < child.Descent())
 | |
|       descent = child.Descent();
 | |
|     if (ascent < child.Ascent())
 | |
|       ascent = child.Ascent();
 | |
|     // add the child size
 | |
|     mBoundingMetrics.width = child.X();
 | |
|     mBoundingMetrics += child.BoundingMetrics();
 | |
|     ++child;
 | |
|   }
 | |
|   // Add the italic correction at the end (including the last child).
 | |
|   // This gives a nice gap between math and non-math frames, and still
 | |
|   // gives the same math inter-spacing in case this frame connects to
 | |
|   // another math frame
 | |
|   mBoundingMetrics.width = child.X();
 | |
| 
 | |
|   aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
 | |
|   aDesiredSize.Height() = ascent + descent;
 | |
|   aDesiredSize.SetBlockStartAscent(ascent);
 | |
|   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
 | |
| 
 | |
|   mReference.x = 0;
 | |
|   mReference.y = aDesiredSize.BlockStartAscent();
 | |
| 
 | |
|   //////////////////
 | |
|   // Place Children
 | |
| 
 | |
|   if (aPlaceOrigin) {
 | |
|     PositionRowChildFrames(0, aDesiredSize.BlockStartAscent());
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
 | |
|                                                nscoord aBaseline)
 | |
| {
 | |
|   RowChildFrameIterator child(this);
 | |
|   while (child.Frame()) {
 | |
|     nscoord dx = aOffsetX + child.X();
 | |
|     nscoord dy = aBaseline - child.Ascent();
 | |
|     FinishReflowChild(child.Frame(), PresContext(), child.GetReflowOutput(),
 | |
|                       nullptr, dx, dy, 0);
 | |
|     ++child;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // helpers to fix the inter-spacing when <math> is the only parent
 | |
| // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
 | |
| 
 | |
| static nscoord
 | |
| GetInterFrameSpacingFor(int32_t         aScriptLevel,
 | |
|                         nsIFrame*       aParentFrame,
 | |
|                         nsIFrame*       aChildFrame)
 | |
| {
 | |
|   nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
 | |
|   if (!childFrame || aChildFrame == childFrame)
 | |
|     return 0;
 | |
| 
 | |
|   int32_t carrySpace = 0;
 | |
|   eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
 | |
|   eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
 | |
|   eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
 | |
|   childFrame = childFrame->GetNextSibling();
 | |
|   while (childFrame) {
 | |
|     prevFrameType = childFrameType;
 | |
|     childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
 | |
|     nscoord space = GetInterFrameSpacing(aScriptLevel,
 | |
|       prevFrameType, childFrameType, &fromFrameType, &carrySpace);
 | |
|     if (aChildFrame == childFrame) {
 | |
|       // get thinspace
 | |
|       nsStyleContext* parentContext = aParentFrame->StyleContext();
 | |
|       nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
 | |
|       // we are done
 | |
|       return space * thinSpace;
 | |
|     }
 | |
|     childFrame = childFrame->GetNextSibling();
 | |
|   }
 | |
| 
 | |
|   NS_NOTREACHED("child not in the childlist of its parent");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static nscoord
 | |
| AddInterFrameSpacingToSize(ReflowOutput&    aDesiredSize,
 | |
|                            nsMathMLContainerFrame* aFrame)
 | |
| {
 | |
|   nscoord gap = 0;
 | |
|   nsIFrame* parent = aFrame->GetParent();
 | |
|   nsIContent* parentContent = parent->GetContent();
 | |
|   if (MOZ_UNLIKELY(!parentContent)) {
 | |
|     return 0;
 | |
|   }
 | |
|   if (parentContent->IsAnyOfMathMLElements(nsGkAtoms::math,
 | |
|                                            nsGkAtoms::mtd_)) {
 | |
|     gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mScriptLevel,
 | |
|                                   parent, aFrame);
 | |
|     // add our own italic correction
 | |
|     nscoord leftCorrection = 0, italicCorrection = 0;
 | |
|     aFrame->GetItalicCorrection(aDesiredSize.mBoundingMetrics,
 | |
|                                 leftCorrection, italicCorrection);
 | |
|     gap += leftCorrection;
 | |
|     if (gap) {
 | |
|       aDesiredSize.mBoundingMetrics.leftBearing += gap;
 | |
|       aDesiredSize.mBoundingMetrics.rightBearing += gap;
 | |
|       aDesiredSize.mBoundingMetrics.width += gap;
 | |
|       aDesiredSize.Width() += gap;
 | |
|     }
 | |
|     aDesiredSize.mBoundingMetrics.width += italicCorrection;
 | |
|     aDesiredSize.Width() += italicCorrection;
 | |
|   }
 | |
|   return gap;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsMathMLContainerFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
 | |
| {
 | |
|   nscoord gap = 0;
 | |
|   gap = AddInterFrameSpacingToSize(aDesiredSize, this);
 | |
|   if (gap) {
 | |
|     // Shift our children to account for the correction
 | |
|     nsIFrame* childFrame = mFrames.FirstChild();
 | |
|     while (childFrame) {
 | |
|       childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
 | |
|       childFrame = childFrame->GetNextSibling();
 | |
|     }
 | |
|   }
 | |
|   return gap;
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
 | |
| 
 | |
| {
 | |
|   if (MOZ_UNLIKELY(!aFirst))
 | |
|     return;
 | |
| 
 | |
|   for (nsIFrame* frame = aFirst;
 | |
|        frame != aStop;
 | |
|        frame = frame->GetNextSibling()) {
 | |
|     NS_ASSERTION(frame, "aStop isn't a sibling");
 | |
|     if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
 | |
|       // finish off principal descendants, too
 | |
|       nsIFrame* grandchild = frame->PrincipalChildList().FirstChild();
 | |
|       if (grandchild)
 | |
|         DidReflowChildren(grandchild, nullptr);
 | |
| 
 | |
|       frame->DidReflow(frame->PresContext(), nullptr,
 | |
|                        nsDidReflowStatus::FINISHED);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // helper used by mstyle, mphantom, mpadded and mrow in their implementations
 | |
| // of TransmitAutomaticData().
 | |
| nsresult
 | |
| nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement()
 | |
| {
 | |
|   //
 | |
|   // One loop to check both conditions below:
 | |
|   //
 | |
|   // 1) whether all the children of the mrow-like element are space-like.
 | |
|   //
 | |
|   //   The REC defines the following elements to be "space-like":
 | |
|   //   * an mstyle, mphantom, or mpadded element, all of whose direct
 | |
|   //     sub-expressions are space-like;
 | |
|   //   * an mrow all of whose direct sub-expressions are space-like.
 | |
|   //
 | |
|   // 2) whether all but one child of the mrow-like element are space-like and
 | |
|   //    this non-space-like child is an embellished operator.
 | |
|   //
 | |
|   //   The REC defines the following elements to be embellished operators:
 | |
|   //   * one of the elements mstyle, mphantom, or mpadded, such that an mrow
 | |
|   //     containing the same arguments would be an embellished operator;
 | |
|   //   * an mrow whose arguments consist (in any order) of one embellished
 | |
|   //     operator and zero or more space-like elements.
 | |
|   //
 | |
|   nsIFrame *childFrame, *baseFrame;
 | |
|   bool embellishedOpFound = false;
 | |
|   nsEmbellishData embellishData;
 | |
| 
 | |
|   for (childFrame = PrincipalChildList().FirstChild();
 | |
|        childFrame;
 | |
|        childFrame = childFrame->GetNextSibling()) {
 | |
|     nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
 | |
|     if (!mathMLFrame) break;
 | |
|     if (!mathMLFrame->IsSpaceLike()) {
 | |
|       if (embellishedOpFound) break;
 | |
|       baseFrame = childFrame;
 | |
|       GetEmbellishDataFrom(baseFrame, embellishData);
 | |
|       if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)) break;
 | |
|       embellishedOpFound = true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!childFrame) {
 | |
|     // we successfully went to the end of the loop. This means that one of
 | |
|     // condition 1) or 2) holds.
 | |
|     if (!embellishedOpFound) {
 | |
|       // the mrow-like element is space-like.
 | |
|       mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
 | |
|     } else {
 | |
|       // the mrow-like element is an embellished operator.
 | |
|       // let the state of the embellished operator found bubble to us.
 | |
|       mPresentationData.baseFrame = baseFrame;
 | |
|       mEmbellishData = embellishData;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (childFrame || !embellishedOpFound) {
 | |
|     // The element is not embellished operator
 | |
|     mPresentationData.baseFrame = nullptr;
 | |
|     mEmbellishData.flags = 0;
 | |
|     mEmbellishData.coreFrame = nullptr;
 | |
|     mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
 | |
|     mEmbellishData.leadingSpace = 0;
 | |
|     mEmbellishData.trailingSpace = 0;
 | |
|   }
 | |
| 
 | |
|   if (childFrame || embellishedOpFound) {
 | |
|     // The element is not space-like
 | |
|     mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /*static*/ void
 | |
| nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame,
 | |
|                                               nsFrameState  aFlags)
 | |
| {
 | |
|   if (!aFrame || !aFlags)
 | |
|     return;
 | |
| 
 | |
|   aFrame->AddStateBits(aFlags);
 | |
|   for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
 | |
|     PropagateFrameFlagFor(childFrame, aFlags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::ReportErrorToConsole(const char*       errorMsgId,
 | |
|                                              const char16_t** aParams,
 | |
|                                              uint32_t          aParamCount)
 | |
| {
 | |
|   return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
 | |
|                                          NS_LITERAL_CSTRING("Layout: MathML"), mContent->OwnerDoc(),
 | |
|                                          nsContentUtils::eMATHML_PROPERTIES,
 | |
|                                          errorMsgId, aParams, aParamCount);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute,
 | |
|                                          const char16_t* aValue)
 | |
| {
 | |
|   const char16_t* argv[] =
 | |
|     { aValue, aAttribute, mContent->NodeInfo()->NameAtom()->GetUTF16String() };
 | |
|   return ReportErrorToConsole("AttributeParsingError", argv, 3);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::ReportChildCountError()
 | |
| {
 | |
|   const char16_t* arg = mContent->NodeInfo()->NameAtom()->GetUTF16String();
 | |
|   return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
 | |
| {
 | |
|   const char16_t* argv[] =
 | |
|     { aChildTag->GetUTF16String(),
 | |
|       mContent->NodeInfo()->NameAtom()->GetUTF16String() };
 | |
|   return ReportErrorToConsole("InvalidChild", argv, 2);
 | |
| }
 | |
| 
 | |
| //==========================
 | |
| 
 | |
| nsContainerFrame*
 | |
| NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 | |
| {
 | |
|   return new (aPresShell) nsMathMLmathBlockFrame(aContext);
 | |
| }
 | |
| 
 | |
| NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
 | |
| 
 | |
| NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
 | |
|   NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
 | |
| NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
 | |
| 
 | |
| nsContainerFrame*
 | |
| NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 | |
| {
 | |
|   return new (aPresShell) nsMathMLmathInlineFrame(aContext);
 | |
| }
 | |
| 
 | |
| NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
 | |
| 
 | |
| NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
 | |
|   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
 | |
| NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
 |