Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLContainerFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla MathML Project.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * The University Of Queensland.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00024  *   David J. Fiddes <D.J.Fiddes@hw.ac.uk>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsCOMPtr.h"
00042 #include "nsHTMLParts.h"
00043 #include "nsFrame.h"
00044 #include "nsPresContext.h"
00045 #include "nsIPresShell.h"
00046 #include "nsCSSAnonBoxes.h"
00047 #include "nsUnitConversion.h"
00048 #include "nsStyleContext.h"
00049 #include "nsStyleConsts.h"
00050 #include "nsINameSpaceManager.h"
00051 #include "nsIRenderingContext.h"
00052 #include "nsIFontMetrics.h"
00053 
00054 #include "nsIDOMText.h"
00055 #include "nsITextContent.h"
00056 #include "nsIDOMMutationEvent.h"
00057 #include "nsFrameManager.h"
00058 #include "nsStyleChangeList.h"
00059 
00060 #include "nsMathMLAtoms.h"
00061 #include "nsMathMLParts.h"
00062 #include "nsMathMLChar.h"
00063 #include "nsMathMLContainerFrame.h"
00064 #include "nsAutoPtr.h"
00065 #include "nsStyleSet.h"
00066 
00067 NS_DEFINE_CID(kInlineFrameCID, NS_INLINE_FRAME_CID);
00068 
00069 //
00070 // nsMathMLContainerFrame implementation
00071 //
00072 
00073 // nsISupports
00074 // =============================================================================
00075 
00076 NS_IMPL_ADDREF_INHERITED(nsMathMLContainerFrame, nsMathMLFrame)
00077 NS_IMPL_RELEASE_INHERITED(nsMathMLContainerFrame, nsMathMLFrame)
00078 NS_IMPL_QUERY_INTERFACE_INHERITED1(nsMathMLContainerFrame, nsHTMLContainerFrame, nsMathMLFrame)
00079 
00080 // =============================================================================
00081 
00082 // error handlers
00083 // provide a feedback to the user when a frame with bad markup can not be rendered
00084 nsresult
00085 nsMathMLContainerFrame::ReflowError(nsIRenderingContext& aRenderingContext,
00086                                     nsHTMLReflowMetrics& aDesiredSize)
00087 {
00088   nsresult rv;
00089 
00090   // clear all other flags and record that there is an error with this frame
00091   mEmbellishData.flags = 0;
00092   mPresentationData.flags = NS_MATHML_ERROR;
00093 
00095   // Set font
00096   aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull);
00097 
00098   // bounding metrics
00099   nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
00100   rv = aRenderingContext.GetBoundingMetrics(errorMsg.get(),
00101                                             PRUint32(errorMsg.Length()),
00102                                             mBoundingMetrics);
00103   if (NS_FAILED(rv)) {
00104     NS_WARNING("GetBoundingMetrics failed");
00105     aDesiredSize.width = aDesiredSize.height = 0;
00106     aDesiredSize.ascent = aDesiredSize.descent = 0;
00107     return NS_OK;
00108   }
00109 
00110   // reflow metrics
00111   nsCOMPtr<nsIFontMetrics> fm;
00112   aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
00113   fm->GetMaxAscent(aDesiredSize.ascent);
00114   fm->GetMaxDescent(aDesiredSize.descent);
00115   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
00116   aDesiredSize.width = mBoundingMetrics.width;
00117 
00118   if (aDesiredSize.mComputeMEW) {
00119     aDesiredSize.mMaxElementWidth = aDesiredSize.width;
00120   }
00121   // Also return our bounding metrics
00122   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00123 
00124   return NS_OK;
00125 }
00126 
00127 nsresult
00128 nsMathMLContainerFrame::PaintError(nsIRenderingContext& aRenderingContext,
00129                                    const nsRect&        aDirtyRect,
00130                                    nsFramePaintLayer    aWhichLayer)
00131 {
00132   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
00133     NS_ASSERTION(NS_MATHML_HAS_ERROR(mPresentationData.flags),
00134                  "There is nothing wrong with this frame!");
00135     // Set color and font ...
00136     aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull);
00137 
00138     aRenderingContext.SetColor(NS_RGB(255,0,0));
00139     aRenderingContext.FillRect(0, 0, mRect.width, mRect.height);
00140     aRenderingContext.SetColor(NS_RGB(255,255,255));
00141 
00142     nscoord ascent;
00143     nsCOMPtr<nsIFontMetrics> fm;
00144     aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
00145     fm->GetMaxAscent(ascent);
00146 
00147     nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
00148     aRenderingContext.DrawString(errorMsg.get(),
00149                                  PRUint32(errorMsg.Length()),
00150                                  0, ascent);
00151   }
00152   return NS_OK;
00153 }
00154 
00155 /* /////////////
00156  * nsIMathMLFrame - support methods for stretchy elements
00157  * =============================================================================
00158  */
00159 
00160 // helper method to facilitate getting the reflow and bounding metrics
00161 void
00162 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
00163                                                        nsHTMLReflowMetrics& aReflowMetrics,
00164                                                        nsBoundingMetrics&   aBoundingMetrics,
00165                                                        eMathMLFrameType*    aMathMLFrameType)
00166 {
00167   NS_PRECONDITION(aFrame, "null arg");
00168 
00169   // IMPORTANT: This function is only meant to be called in Place() methods
00170   // where it is assumed that the frame's rect is still acting as place holder
00171   // for the frame's ascent and descent information
00172 
00173   nsRect rect = aFrame->GetRect();
00174   aReflowMetrics.descent = rect.x;
00175   aReflowMetrics.ascent  = rect.y;
00176   aReflowMetrics.width   = rect.width;
00177   aReflowMetrics.height  = rect.height;
00178 
00179   if (aFrame->IsFrameOfType(nsIFrame::eMathML)) {
00180     nsIMathMLFrame* mathMLFrame;
00181     CallQueryInterface(aFrame, &mathMLFrame);
00182     if (mathMLFrame) {
00183       mathMLFrame->GetBoundingMetrics(aBoundingMetrics);
00184       if (aMathMLFrameType)
00185         *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
00186 
00187       return;
00188     }
00189   }
00190 
00191  // aFrame is not a MathML frame, just return the reflow metrics
00192  aBoundingMetrics.descent = aReflowMetrics.descent;
00193  aBoundingMetrics.ascent  = aReflowMetrics.ascent;
00194  aBoundingMetrics.width   = aReflowMetrics.width;
00195  aBoundingMetrics.rightBearing = aReflowMetrics.width;
00196  if (aMathMLFrameType)
00197    *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
00198 }
00199 
00200 // helper to get the preferred size that a container frame should use to fire
00201 // the stretch on its stretchy child frames.
00202 void
00203 nsMathMLContainerFrame::GetPreferredStretchSize(nsIRenderingContext& aRenderingContext,
00204                                                 PRUint32             aOptions,
00205                                                 nsStretchDirection   aStretchDirection,
00206                                                 nsBoundingMetrics&   aPreferredStretchSize)
00207 {
00208   if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
00209     // when our actual size is ok, just use it
00210     aPreferredStretchSize = mBoundingMetrics;
00211   }
00212   else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
00213     // compute our up-to-date size using Place()
00214     nsHTMLReflowMetrics metrics(nsnull);
00215     Place(aRenderingContext, PR_FALSE, metrics);
00216     aPreferredStretchSize = metrics.mBoundingMetrics;
00217   }
00218   else {
00219     // compute a size that doesn't include embellishements
00220     NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
00221                  NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) ||
00222                  NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags),
00223                  "invalid call to GetPreferredStretchSize");
00224     PRBool firstTime = PR_TRUE;
00225     nsBoundingMetrics bm, bmChild;
00226     // XXXrbs need overloaded FirstChild() and clean integration of <maction> throughout
00227     nsIFrame* childFrame = GetFirstChild(nsnull);
00228     while (childFrame) {
00229       // initializations in case this child happens not to be a MathML frame
00230       nsRect rect = childFrame->GetRect();
00231       bmChild.ascent = rect.y;
00232       bmChild.descent = rect.x;
00233       bmChild.width = rect.width;
00234       bmChild.rightBearing = rect.width;
00235       bmChild.leftBearing = 0;
00236 
00237       nsIMathMLFrame* mathMLFrame;
00238       childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00239       if (mathMLFrame) {
00240         nsEmbellishData embellishData;
00241         nsPresentationData presentationData;
00242         mathMLFrame->GetEmbellishData(embellishData);
00243         mathMLFrame->GetPresentationData(presentationData);
00244         if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
00245             embellishData.direction == aStretchDirection &&
00246             presentationData.baseFrame) {
00247           // embellishements are not included, only consider the inner first child itself
00248           nsIMathMLFrame* mathMLchildFrame;
00249           presentationData.baseFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLchildFrame);
00250           if (mathMLchildFrame) {
00251             mathMLFrame = mathMLchildFrame;
00252           }
00253         }
00254         mathMLFrame->GetBoundingMetrics(bmChild);
00255       }
00256 
00257       if (firstTime) {
00258         firstTime = PR_FALSE;
00259         bm = bmChild;
00260         if (!NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) &&
00261             !NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
00262           // we may get here for cases such as <msup><mo>...</mo> ... </msup>
00263           break;
00264         }
00265       }
00266       else {
00267         if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
00268           // if we get here, it means this is container that will stack its children
00269           // vertically and fire an horizontal stretch on each them. This is the case
00270           // for \munder, \mover, \munderover. We just sum-up the size vertically.
00271           bm.descent += bmChild.ascent + bmChild.descent;
00272           if (bm.leftBearing > bmChild.leftBearing)
00273             bm.leftBearing = bmChild.leftBearing;
00274           if (bm.rightBearing < bmChild.rightBearing)
00275             bm.rightBearing = bmChild.rightBearing;
00276         }
00277         else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
00278           // just sum-up the sizes horizontally.
00279           bm += bmChild;
00280         }
00281         else {
00282           NS_ERROR("unexpected case in GetPreferredStretchSize");
00283           break;
00284         }
00285       }
00286       childFrame = childFrame->GetNextSibling();
00287     }
00288     aPreferredStretchSize = bm;
00289   }
00290 }
00291 
00292 NS_IMETHODIMP
00293 nsMathMLContainerFrame::Stretch(nsIRenderingContext& aRenderingContext,
00294                                 nsStretchDirection   aStretchDirection,
00295                                 nsBoundingMetrics&   aContainerSize,
00296                                 nsHTMLReflowMetrics& aDesiredStretchSize)
00297 {
00298   if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
00299 
00300     if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
00301       NS_WARNING("it is wrong to fire stretch more than once on a frame");
00302       return NS_OK;
00303     }
00304     mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
00305 
00306     if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
00307       NS_WARNING("it is wrong to fire stretch on a erroneous frame");
00308       return NS_OK;
00309     }
00310 
00311     // Pass the stretch to the base child ...
00312 
00313     nsIFrame* childFrame = mPresentationData.baseFrame;
00314     if (childFrame) {
00315       nsIMathMLFrame* mathMLFrame;
00316       childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00317       NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
00318       if (mathMLFrame) {
00319         PRBool stretchAll =
00320           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
00321           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
00322 
00323         // And the trick is that the child's rect.x is still holding the descent,
00324         // and rect.y is still holding the ascent ...
00325         nsHTMLReflowMetrics childSize(aDesiredStretchSize);
00326         GetReflowAndBoundingMetricsFor(childFrame, childSize, childSize.mBoundingMetrics);
00327 
00328         // See if we should downsize and confine the stretch to us...
00329         // XXX there may be other cases where we can downsize the stretch,
00330         // e.g., the first &Sum; might appear big in the following situation
00331         // <math xmlns='http://www.w3.org/1998/Math/MathML'>
00332         //   <mstyle>
00333         //     <msub>
00334         //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
00335         //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
00336         //      </msub>
00337         //   </mstyle>
00338         // </math>
00339         nsBoundingMetrics containerSize = aContainerSize;
00340         if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
00341             aStretchDirection != mEmbellishData.direction) {
00342           if (mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) {
00343             containerSize = childSize.mBoundingMetrics;
00344           }
00345           else {
00346             GetPreferredStretchSize(aRenderingContext, 
00347                                     stretchAll ? STRETCH_CONSIDER_EMBELLISHMENTS : 0,
00348                                     mEmbellishData.direction, containerSize);
00349           }
00350         }
00351 
00352         // do the stretching...
00353         mathMLFrame->Stretch(aRenderingContext,
00354                              mEmbellishData.direction, containerSize, childSize);
00355 
00356         // store the updated metrics
00357         childFrame->SetRect(nsRect(childSize.descent, childSize.ascent,
00358                                    childSize.width, childSize.height));
00359 
00360         // Remember the siblings which were _deferred_.
00361         // Now that this embellished child may have changed, we need to
00362         // fire the stretch on its siblings using our updated size
00363 
00364         if (stretchAll) {
00365 
00366           nsStretchDirection stretchDir =
00367             NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
00368               NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
00369 
00370           GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
00371                                   stretchDir, containerSize);
00372 
00373           childFrame = mFrames.FirstChild();
00374           while (childFrame) {
00375             if (childFrame != mPresentationData.baseFrame) {
00376               childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00377               if (mathMLFrame) {
00378                 // retrieve the metrics that was stored at the previous pass
00379                 GetReflowAndBoundingMetricsFor(childFrame, 
00380                   childSize, childSize.mBoundingMetrics);
00381                 // do the stretching...
00382                 mathMLFrame->Stretch(aRenderingContext, stretchDir,
00383                                      containerSize, childSize);
00384                 // store the updated metrics
00385                 childFrame->SetRect(nsRect(childSize.descent, childSize.ascent,
00386                                            childSize.width, childSize.height));
00387               }
00388             }
00389             childFrame = childFrame->GetNextSibling();
00390           }
00391         }
00392 
00393         // re-position all our children
00394         Place(aRenderingContext, PR_TRUE, aDesiredStretchSize);
00395 
00396         // If our parent is not embellished, it means we are the outermost embellished
00397         // container and so we put the spacing, otherwise we don't include the spacing,
00398         // the outermost embellished container will take care of it.
00399 
00400         nsEmbellishData parentData;
00401         GetEmbellishDataFrom(mParent, parentData);
00402         // ensure that we are the embellished child, not just a sibling
00403         // (need to test coreFrame since <mfrac> resets other things)
00404         if (parentData.coreFrame != mEmbellishData.coreFrame) {
00405           // (we fetch values from the core since they may use units that depend
00406           // on style data, and style changes could have occured in the core since
00407           // our last visit there)
00408           nsEmbellishData coreData;
00409           GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
00410 
00411           mBoundingMetrics.width += coreData.leftSpace + coreData.rightSpace;
00412           aDesiredStretchSize.width = mBoundingMetrics.width;
00413           aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
00414 
00415           nscoord dx = coreData.leftSpace;
00416           if (!dx) return NS_OK;
00417 
00418           mBoundingMetrics.leftBearing += dx;
00419           mBoundingMetrics.rightBearing += dx;
00420           aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
00421           aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
00422 
00423           childFrame = mFrames.FirstChild();
00424           while (childFrame) {
00425             childFrame->SetPosition(childFrame->GetPosition()
00426                                 + nsPoint(dx, 0));
00427             childFrame = childFrame->GetNextSibling();
00428           }
00429         }
00430       }
00431     }
00432   }
00433   return NS_OK;
00434 }
00435 
00436 nsresult
00437 nsMathMLContainerFrame::FinalizeReflow(nsIRenderingContext& aRenderingContext,
00438                                        nsHTMLReflowMetrics& aDesiredSize)
00439 {
00440   // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
00441   // and descent in expectation of a stretch command. Hence we need to ensure that
00442   // a stretch command will actually be fired later on, after exiting from our
00443   // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
00444   // with inappropriate data causing children to be improperly positioned.
00445   // This helper method checks to see if our parent will fire a stretch command
00446   // targeted at us. If not, we go ahead and fire an involutive stretch on
00447   // ourselves. This will clear all the rect.x and rect.y, and return our
00448   // desired size.
00449 
00450 
00451   // First, complete the post-reflow hook.
00452   // We use the information in our children rectangles to position them.
00453   // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
00454   // They will still be holding the ascent and descent for each child.
00455 
00456   // The first clause caters for any non-embellished container.
00457   // The second clause is for a container which won't fire stretch even though it is
00458   // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
00459   // because it excludes the particular case of the core <mo>...</mo> itself.
00460   // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
00461   PRBool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
00462                        (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
00463                         mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
00464   Place(aRenderingContext, placeOrigin, aDesiredSize);
00465 
00466   if (!placeOrigin) {
00467     // This means the rect.x and rect.y of our children were not set!!
00468     // Don't go without checking to see if our parent will later fire a Stretch() command
00469     // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
00470     PRBool parentWillFireStretch = PR_FALSE;
00471     nsIMathMLFrame* mathMLFrame;
00472     mParent->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00473     if (mathMLFrame) {
00474       nsEmbellishData embellishData;
00475       nsPresentationData presentationData;
00476       mathMLFrame->GetEmbellishData(embellishData);
00477       mathMLFrame->GetPresentationData(presentationData);
00478       if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
00479           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
00480           (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
00481             && presentationData.baseFrame == this))
00482       {
00483         parentWillFireStretch = PR_TRUE;
00484       }
00485     }
00486     if (!parentWillFireStretch) {
00487       // There is nobody who will fire the stretch for us, we do it ourselves!
00488 
00489       PRBool stretchAll =
00490         /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
00491         NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
00492 
00493       nsBoundingMetrics defaultSize;
00494       if (mEmbellishData.coreFrame == this /* case of a bare <mo>...</mo> itself */
00495           || stretchAll) { /* or <mover><mo>...</mo>...</mover>, or friends */
00496         // use our current size as computed earlier by Place()
00497         defaultSize = aDesiredSize.mBoundingMetrics;
00498       }
00499       else { /* case of <msup><mo>...</mo>...</msup> or friends */
00500         // compute a size that doesn't include embellishments
00501         GetPreferredStretchSize(aRenderingContext, 0, mEmbellishData.direction,
00502                                 defaultSize);
00503       }
00504       Stretch(aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, defaultSize,
00505               aDesiredSize);
00506     }
00507   }
00508   if (aDesiredSize.mComputeMEW) {
00509     aDesiredSize.mMaxElementWidth = aDesiredSize.width;
00510   }
00511   // Also return our bounding metrics
00512   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00513 
00514   // see if we should fix the spacing
00515   FixInterFrameSpacing(aDesiredSize);
00516 
00517   return NS_OK;
00518 }
00519 
00520 
00521 /* /////////////
00522  * nsIMathMLFrame - support methods for scripting elements (nested frames
00523  * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
00524  * mfrac, mroot, mtable).
00525  * =============================================================================
00526  */
00527 
00528 // helper to let the update of presentation data pass through
00529 // a subtree that may contain non-mathml container frames
00530 /* static */ void
00531 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame*       aFrame,
00532                                                      PRInt32         aScriptLevelIncrement,
00533                                                      PRUint32        aFlagsValues,
00534                                                      PRUint32        aFlagsToUpdate)
00535 {
00536   if (!aFrame || (!aFlagsToUpdate && !aScriptLevelIncrement))
00537     return;
00538   nsIMathMLFrame* mathMLFrame;
00539   aFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00540   if (mathMLFrame) {
00541     // update
00542     mathMLFrame->UpdatePresentationData(aScriptLevelIncrement, aFlagsValues,
00543                                         aFlagsToUpdate);
00544     // propagate using the base method to make sure that the control
00545     // is passed on to MathML frames that may be overloading the method
00546     mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
00547       aScriptLevelIncrement, aFlagsValues, aFlagsToUpdate);
00548   }
00549   else {
00550     // propagate down the subtrees
00551     nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
00552     while (childFrame) {
00553       PropagatePresentationDataFor(childFrame,
00554         aScriptLevelIncrement, aFlagsValues, aFlagsToUpdate);
00555       childFrame = childFrame->GetNextSibling();
00556     }
00557   }
00558 }
00559 
00560 /* static */ void
00561 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame*       aParentFrame,
00562                                                              PRInt32         aFirstChildIndex,
00563                                                              PRInt32         aLastChildIndex,
00564                                                              PRInt32         aScriptLevelIncrement,
00565                                                              PRUint32        aFlagsValues,
00566                                                              PRUint32        aFlagsToUpdate)
00567 {
00568   if (!aParentFrame || (!aFlagsToUpdate && !aScriptLevelIncrement))
00569     return;
00570   PRInt32 index = 0;
00571   nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
00572   while (childFrame) {
00573     if ((index >= aFirstChildIndex) &&
00574         ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
00575          (index <= aLastChildIndex)))) {
00576       PropagatePresentationDataFor(childFrame,
00577         aScriptLevelIncrement, aFlagsValues, aFlagsToUpdate);
00578     }
00579     index++;
00580     childFrame = childFrame->GetNextSibling();
00581   }
00582 }
00583 
00584 // helper to let the scriptstyle re-resolution pass through
00585 // a subtree that may contain non-mathml container frames.
00586 // This function is *very* expensive. Unfortunately, there isn't much
00587 // to do about it at the moment. For background on the problem @see 
00588 // http://groups.google.com/groups?selm=3A9192B5.D22B6C38%40maths.uq.edu.au
00589 /* static */ void
00590 nsMathMLContainerFrame::PropagateScriptStyleFor(nsIFrame*       aFrame,
00591                                                 PRInt32         aParentScriptLevel)
00592 {
00593   if (!aFrame)
00594     return;
00595   nsIMathMLFrame* mathMLFrame;
00596   aFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00597   if (mathMLFrame) {
00598     // we will re-resolve our style data based on our current scriptlevel
00599     nsPresentationData presentationData;
00600     mathMLFrame->GetPresentationData(presentationData);
00601     PRInt32 gap = presentationData.scriptLevel - aParentScriptLevel;
00602 
00603     // since we are a MathML frame, our current scriptlevel becomes
00604     // the one to use when we will propagate the recursion
00605     aParentScriptLevel = presentationData.scriptLevel;
00606 
00607     nsStyleContext* oldStyleContext = aFrame->GetStyleContext();
00608     nsStyleContext* parentContext = oldStyleContext->GetParent();
00609 
00610     nsIContent* content = aFrame->GetContent();
00611     if (!gap) {
00612       // unset any -moz-math-font-size attribute without notifying that we want a reflow
00613       content->UnsetAttr(kNameSpaceID_None, nsMathMLAtoms::MOZfontsize, PR_FALSE);
00614     }
00615     else {
00616       // By default scriptminsize=8pt and scriptsizemultiplier=0.71
00617       nscoord scriptminsize = NSIntPointsToTwips(NS_MATHML_SCRIPTMINSIZE);
00618       float scriptsizemultiplier = NS_MATHML_SCRIPTSIZEMULTIPLIER;
00619 #if 0
00620        // XXX Bug 44201
00621        // user-supplied scriptminsize and scriptsizemultiplier that are
00622        // restricted to particular elements are not supported because our
00623        // css rules are fixed in mathml.css and are applicable to all elements.
00624 
00625        // see if there is a scriptminsize attribute on a <mstyle> that wraps us
00626        if (NS_CONTENT_ATTR_HAS_VALUE ==
00627            GetAttribute(nsnull, presentationData.mstyle,
00628                         nsMathMLAtoms::scriptminsize_, fontsize)) {
00629          nsCSSValue cssValue;
00630          if (ParseNumericValue(fontsize, cssValue)) {
00631            nsCSSUnit unit = cssValue.GetUnit();
00632            if (eCSSUnit_Number == unit)
00633              scriptminsize = nscoord(float(scriptminsize) * cssValue.GetFloatValue());
00634            else if (eCSSUnit_Percent == unit)
00635              scriptminsize = nscoord(float(scriptminsize) * cssValue.GetPercentValue());
00636            else if (eCSSUnit_Null != unit)
00637              scriptminsize = CalcLength(mStyleContext, cssValue);
00638          }
00639        }
00640 #endif
00641 
00642       // figure out the incremental factor
00643       nsAutoString fontsize;
00644       if (0 > gap) { // the size is going to be increased
00645         if (gap < NS_MATHML_CSS_NEGATIVE_SCRIPTLEVEL_LIMIT)
00646           gap = NS_MATHML_CSS_NEGATIVE_SCRIPTLEVEL_LIMIT;
00647         gap = -gap;
00648         scriptsizemultiplier = 1.0f / scriptsizemultiplier;
00649         fontsize.AssignLiteral("-");
00650       }
00651       else { // the size is going to be decreased
00652         if (gap > NS_MATHML_CSS_POSITIVE_SCRIPTLEVEL_LIMIT)
00653           gap = NS_MATHML_CSS_POSITIVE_SCRIPTLEVEL_LIMIT;
00654         fontsize.AssignLiteral("+");
00655       }
00656       fontsize.AppendInt(gap, 10);
00657       // we want to make sure that the size will stay readable
00658       const nsStyleFont* font = parentContext->GetStyleFont();
00659       nscoord newFontSize = font->mFont.size;
00660       while (0 < gap--) {
00661         newFontSize = (nscoord)((float)(newFontSize) * scriptsizemultiplier);
00662       }
00663       if (newFontSize <= scriptminsize) {
00664         fontsize.AssignLiteral("scriptminsize");
00665       }
00666 
00667       // set the -moz-math-font-size attribute without notifying that we want a reflow
00668       content->SetAttr(kNameSpaceID_None, nsMathMLAtoms::MOZfontsize,
00669                        fontsize, PR_FALSE);
00670     }
00671 
00672     // now, re-resolve the style contexts in our subtree
00673     nsFrameManager *fm = aFrame->GetPresContext()->FrameManager();
00674     nsStyleChangeList changeList;
00675     fm->ComputeStyleChangeFor(aFrame, &changeList, NS_STYLE_HINT_NONE);
00676 #ifdef DEBUG
00677     // Use the parent frame to make sure we catch in-flows and such
00678     nsIFrame* parentFrame = aFrame->GetParent();
00679     fm->DebugVerifyStyleTree(parentFrame ? parentFrame : aFrame);
00680 #endif
00681   }
00682 
00683   // recurse down the subtrees for changes that may arise deep down
00684   nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
00685   while (childFrame) {
00686     childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00687     if (mathMLFrame) {
00688       // propagate using the base method to make sure that the control
00689       // is passed on to MathML frames that may be overloading the method
00690       mathMLFrame->ReResolveScriptStyle(aParentScriptLevel);
00691     }
00692     else {
00693       PropagateScriptStyleFor(childFrame, aParentScriptLevel);
00694     }
00695     childFrame = childFrame->GetNextSibling();
00696   }
00697 }
00698 
00699 
00700 /* //////////////////
00701  * Frame construction
00702  * =============================================================================
00703  */
00704 
00705 
00706 NS_IMETHODIMP
00707 nsMathMLContainerFrame::Paint(nsPresContext*      aPresContext,
00708                               nsIRenderingContext& aRenderingContext,
00709                               const nsRect&        aDirtyRect,
00710                               nsFramePaintLayer    aWhichLayer,
00711                               PRUint32             aFlags)
00712 {
00713   if (NS_FRAME_IS_UNFLOWABLE & mState) {
00714     return NS_OK;
00715   }
00716 
00717   // report an error if something wrong was found in this frame
00718   if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
00719     return PaintError(aRenderingContext, aDirtyRect, aWhichLayer);
00720   }
00721 
00722   // Paint inline element backgrounds in the foreground layer (bug 36710).
00723   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
00724     PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
00725   }
00726 
00727   PaintDecorationsAndChildren(aPresContext, aRenderingContext, aDirtyRect,
00728                               aWhichLayer, PR_FALSE, aFlags);
00729 
00730 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
00731   // for visual debug
00732   // ----------------
00733   // if you want to see your bounding box, make sure to properly fill
00734   // your mBoundingMetrics and mReference point, and set
00735   // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
00736   // in the Init() of your sub-class
00737 
00738   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer &&
00739       NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
00740   {
00741     aRenderingContext.SetColor(NS_RGB(0,0,255));
00742 
00743     nscoord x = mReference.x + mBoundingMetrics.leftBearing;
00744     nscoord y = mReference.y - mBoundingMetrics.ascent;
00745     nscoord w = mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
00746     nscoord h = mBoundingMetrics.ascent + mBoundingMetrics.descent;
00747 
00748     aRenderingContext.DrawRect(x,y,w,h);
00749   }
00750 #endif
00751   return NS_OK;
00752 }
00753 
00754 // This method is called in a top-down manner, as we descend the frame tree
00755 // during its construction
00756 NS_IMETHODIMP
00757 nsMathMLContainerFrame::Init(nsPresContext*  aPresContext,
00758                              nsIContent*      aContent,
00759                              nsIFrame*        aParent,
00760                              nsStyleContext*  aContext,
00761                              nsIFrame*        aPrevInFlow)
00762 {
00763   MapAttributesIntoCSS(aPresContext, aContent);
00764 
00765   // let the base class do its Init()
00766   return nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00767 
00768   // ...We will build our automatic MathML data once the entire <math>...</math>
00769   // tree is constructed.
00770 }
00771 
00772 // This method is called in a bottom-up manner, as we ascend the frame tree
00773 // after its construction
00774 NS_IMETHODIMP
00775 nsMathMLContainerFrame::SetInitialChildList(nsPresContext* aPresContext,
00776                                             nsIAtom*        aListName,
00777                                             nsIFrame*       aChildList)
00778 {
00779   // let the base class do its job
00780   return nsHTMLContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00781 
00782   // ...We will build our automatic MathML data once the entire <math>...</math>
00783   // tree is constructed.
00784 }
00785 
00786 // Note that this method re-builds the automatic data in the children -- not
00787 // in aParentFrame itself (except for those particular operations that the
00788 // parent frame may do in its TransmitAutomaticData()).
00789 /* static */ void
00790 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
00791 {
00792   // 1. As we descend the tree, make each child frame inherit data from
00793   // the parent
00794   // 2. As we ascend the tree, transmit any specific change that we want
00795   // down the subtrees
00796   nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
00797   while (childFrame) {
00798     nsIMathMLFrame* childMathMLFrame;
00799     childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&childMathMLFrame);
00800     if (childMathMLFrame) {
00801       childMathMLFrame->InheritAutomaticData(aParentFrame);
00802     }
00803     RebuildAutomaticDataForChildren(childFrame);
00804     childFrame = childFrame->GetNextSibling();
00805   }
00806   nsIMathMLFrame* mathMLFrame;
00807   aParentFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00808   if (mathMLFrame) {
00809     mathMLFrame->TransmitAutomaticData();
00810   }
00811 }
00812 
00813 /* static */ nsresult
00814 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
00815 {
00816   if (!aParentFrame)
00817     return NS_OK;
00818 
00819   // walk-up to the first frame that is a MathML frame, stop if we reach <math>
00820   PRInt32 parentScriptLevel = 0;
00821   nsIFrame* frame = aParentFrame;
00822   while (1) {
00823      nsIFrame* parent = frame->GetParent();
00824      if (!parent || !parent->GetContent())
00825        break;
00826 
00827     // stop if it is a MathML frame
00828     nsIMathMLFrame* mathMLFrame;
00829     frame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00830     if (mathMLFrame) {
00831       nsPresentationData parentData;
00832       mathMLFrame->GetPresentationData(parentData);
00833       parentScriptLevel = parentData.scriptLevel;
00834       break;
00835     }
00836 
00837     // stop if we reach the root <math> tag
00838     nsIContent* content = frame->GetContent();
00839     NS_ASSERTION(content, "dangling frame without a content node");
00840     if (!content)
00841       break;
00842     if (content->Tag() == nsMathMLAtoms::math)
00843       break;
00844 
00845     // mark the frame dirty, and continue to climb up
00846     frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
00847 
00848     frame = parent;
00849   }
00850 
00851   // re-sync the presentation data and embellishment data of our children
00852   RebuildAutomaticDataForChildren(frame);
00853 
00854   // re-resolve the style data to sync any change of script sizes
00855   nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
00856   while (childFrame) {
00857     nsIMathMLFrame* mathMLFrame;
00858     childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00859     if (mathMLFrame) {
00860       // propagate using the base method to make sure that the control
00861       // is passed on to MathML frames that may be overloading the method
00862       mathMLFrame->ReResolveScriptStyle(parentScriptLevel);
00863     }
00864     else {
00865       PropagateScriptStyleFor(childFrame, parentScriptLevel);
00866     }
00867     childFrame = childFrame->GetNextSibling();
00868   }
00869 
00870   // Ask our parent frame to reflow us
00871   return frame->ReflowDirtyChild(frame->GetPresContext()->PresShell(), nsnull);
00872 }
00873 
00874 // There are precise rules governing children of a MathML frame,
00875 // and properties such as the scriptlevel depends on those rules.
00876 // Hence for things to work, callers must use Append/Insert/etc wisely.
00877 
00878 nsresult
00879 nsMathMLContainerFrame::ChildListChanged(PRInt32 aModType)
00880 {
00881   // If this is an embellished frame we need to rebuild the
00882   // embellished hierarchy by walking-up to the parent of the
00883   // outermost embellished container.
00884   nsIFrame* frame = this;
00885   if (mEmbellishData.coreFrame) {
00886     nsIFrame* parent = mParent;
00887     nsEmbellishData embellishData;
00888     for ( ; parent; frame = parent, parent = parent->GetParent()) {
00889       frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
00890       GetEmbellishDataFrom(parent, embellishData);
00891       if (embellishData.coreFrame != mEmbellishData.coreFrame)
00892         break;
00893     }
00894   }
00895   return ReLayoutChildren(frame);
00896 }
00897 
00898 NS_IMETHODIMP
00899 nsMathMLContainerFrame::AppendFrames(nsIAtom*        aListName,
00900                                      nsIFrame*       aFrameList)
00901 {
00902   if (aListName) {
00903     return NS_ERROR_INVALID_ARG;
00904   }
00905   if (aFrameList) {
00906     mFrames.AppendFrames(this, aFrameList);
00907     return ChildListChanged(nsIDOMMutationEvent::ADDITION);
00908   }
00909   return NS_OK;
00910 }
00911 
00912 NS_IMETHODIMP
00913 nsMathMLContainerFrame::InsertFrames(nsIAtom*        aListName,
00914                                      nsIFrame*       aPrevFrame,
00915                                      nsIFrame*       aFrameList)
00916 {
00917   if (aListName) {
00918     return NS_ERROR_INVALID_ARG;
00919   }
00920   if (aFrameList) {
00921     // Insert frames after aPrevFrame
00922     mFrames.InsertFrames(this, aPrevFrame, aFrameList);
00923     return ChildListChanged(nsIDOMMutationEvent::ADDITION);
00924   }
00925   return NS_OK;
00926 }
00927 
00928 NS_IMETHODIMP
00929 nsMathMLContainerFrame::RemoveFrame(nsIAtom*        aListName,
00930                                     nsIFrame*       aOldFrame)
00931 {
00932   if (aListName) {
00933     return NS_ERROR_INVALID_ARG;
00934   }
00935   // remove the child frame
00936   mFrames.DestroyFrame(GetPresContext(), aOldFrame);
00937   return ChildListChanged(nsIDOMMutationEvent::REMOVAL);
00938 }
00939 
00940 NS_IMETHODIMP
00941 nsMathMLContainerFrame::ReplaceFrame(nsIAtom*        aListName,
00942                                      nsIFrame*       aOldFrame,
00943                                      nsIFrame*       aNewFrame)
00944 {
00945   if (aListName || !aOldFrame || !aNewFrame) {
00946     return NS_ERROR_INVALID_ARG;
00947   }
00948   // Replace the old frame with the new frame in the list
00949   mFrames.ReplaceFrame(this, aOldFrame, aNewFrame, PR_TRUE);
00950 
00951   return ChildListChanged(nsIDOMMutationEvent::MODIFICATION);
00952 }
00953 
00954 NS_IMETHODIMP
00955 nsMathMLContainerFrame::AttributeChanged(nsIContent*     aChild,
00956                                          PRInt32         aNameSpaceID,
00957                                          nsIAtom*        aAttribute,
00958                                          PRInt32         aModType)
00959 {
00960   if (aAttribute == nsMathMLAtoms::mathcolor_      ||
00961       aAttribute == nsMathMLAtoms::color_          ||
00962       aAttribute == nsMathMLAtoms::mathsize_       ||
00963       aAttribute == nsMathMLAtoms::fontsize_       ||
00964       aAttribute == nsMathMLAtoms::fontfamily_     ||
00965       aAttribute == nsMathMLAtoms::mathbackground_ ||
00966       aAttribute == nsMathMLAtoms::background_) {
00967     MapAttributesIntoCSS(GetPresContext(), this);
00968   }
00969 
00970   return ReflowDirtyChild(GetPresContext()->PresShell(), nsnull);
00971 }
00972 
00973 // We are an inline frame, so we handle dirty request like nsInlineFrame
00974 NS_IMETHODIMP
00975 nsMathMLContainerFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild)
00976 {
00977   // The inline container frame does not handle the reflow
00978   // request.  It passes it up to its parent container.
00979 
00980   // If you don't already have dirty children,
00981   if (!(mState & NS_FRAME_HAS_DIRTY_CHILDREN)) {
00982     if (mParent) {
00983       // Record that you are dirty and have dirty children
00984       mState |= NS_FRAME_IS_DIRTY;
00985       mState |= NS_FRAME_HAS_DIRTY_CHILDREN;
00986 
00987       // Pass the reflow request up to the parent
00988       mParent->ReflowDirtyChild(aPresShell, (nsIFrame*) this);
00989     }
00990     else {
00991       NS_ASSERTION(0, "No parent to pass the reflow request up to.");
00992     }
00993   }
00994 
00995   return NS_OK;
00996 }
00997 
00998 nsresult 
00999 nsMathMLContainerFrame::ReflowChild(nsIFrame*                aChildFrame,
01000                                     nsPresContext*           aPresContext,
01001                                     nsHTMLReflowMetrics&     aDesiredSize,
01002                                     const nsHTMLReflowState& aReflowState,
01003                                     nsReflowStatus&          aStatus)
01004 {
01005   aDesiredSize.width = aDesiredSize.height = 0;
01006   aDesiredSize.ascent = aDesiredSize.descent = 0;
01007   aDesiredSize.mBoundingMetrics.Clear();
01008   aDesiredSize.mFlags |= NS_REFLOW_CALC_BOUNDING_METRICS;
01009 
01010   // Having foreign/hybrid children, e.g., from html markups, is not defined by
01011   // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
01012   // to do some cool demos... or we may have a child that is an nsInlineFrame
01013   // from a generated content such as :before { content: open-quote } or 
01014   // :after { content: close-quote }. Unfortunately, the other frames out-there
01015   // may expect their own invariants that are not met when we mix things.
01016   // Hence we do not claim their support, but we will nevertheless attempt to keep
01017   // them in the flow, if we can get their desired size. We observed that most
01018   // frames may be reflowed generically, but nsInlineFrames need extra care.
01019 
01020   nsInlineFrame* inlineFrame;
01021   aChildFrame->QueryInterface(kInlineFrameCID, (void**)&inlineFrame);
01022   if (!inlineFrame)
01023     return nsHTMLContainerFrame::
01024            ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
01025                        0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
01026 
01027   // extra care for an nsInlineFrame
01028   return ReflowForeignChild(aChildFrame, aPresContext, aDesiredSize, aReflowState, aStatus);                       
01029 }
01030 
01031 nsresult 
01032 nsMathMLContainerFrame::ReflowForeignChild(nsIFrame*                aChildFrame,
01033                                            nsPresContext*           aPresContext,
01034                                            nsHTMLReflowMetrics&     aDesiredSize,
01035                                            const nsHTMLReflowState& aReflowState,
01036                                            nsReflowStatus&          aStatus)
01037 {
01038   // don't bother trying to span words as if they were non-breaking beyond this point
01039   if (aReflowState.mLineLayout)
01040     aReflowState.mLineLayout->ForgetWordFrames();
01041 
01042   nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState));
01043   nsresult rv = autoSpaceManager.CreateSpaceManagerFor(aPresContext, this);
01044   NS_ENSURE_SUCCESS(rv, rv);
01045 
01046   // provide a local, self-contained linelayout where to reflow the nsInlineFrame
01047   nsSize availSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
01048   nsLineLayout ll(aPresContext, aReflowState.mSpaceManager, aReflowState.parentReflowState,
01049                   aDesiredSize.mComputeMEW);
01050   ll.BeginLineReflow(0, 0, availSize.width, availSize.height, PR_FALSE, PR_FALSE);
01051   PRBool pushedFrame;
01052   ll.ReflowFrame(aChildFrame, aStatus, &aDesiredSize, pushedFrame);
01053   NS_ASSERTION(!pushedFrame, "unexpected");
01054   ll.EndLineReflow();
01055 
01056   // make up the bounding metrics from the reflow metrics.
01057   aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.ascent;
01058   aDesiredSize.mBoundingMetrics.descent = aDesiredSize.descent;
01059   aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
01060   aDesiredSize.mBoundingMetrics.rightBearing = aDesiredSize.width;
01061 
01062   // Note: MathML's vertical & horizontal alignments happen much later in 
01063   // Place(), which is ultimately called from within FinalizeReflow().
01064 
01065   aStatus = NS_FRAME_COMPLETE;
01066   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
01067   return NS_OK;
01068 }
01069 
01070 NS_IMETHODIMP
01071 nsMathMLContainerFrame::Reflow(nsPresContext*          aPresContext,
01072                                nsHTMLReflowMetrics&     aDesiredSize,
01073                                const nsHTMLReflowState& aReflowState,
01074                                nsReflowStatus&          aStatus)
01075 {
01076   nsresult rv;
01077   aDesiredSize.width = aDesiredSize.height = 0;
01078   aDesiredSize.ascent = aDesiredSize.descent = 0;
01079   aDesiredSize.mBoundingMetrics.Clear();
01080 
01081   // See if this is an incremental reflow
01082   if (aReflowState.reason == eReflowReason_Incremental) {
01083 #ifdef MATHML_NOISY_INCREMENTAL_REFLOW
01084 printf("nsMathMLContainerFrame::Reflow:IncrementalReflow received by: ");
01085 nsFrame::ListTag(stdout, this);
01086 printf("\n");
01087 #endif
01088   }
01089 
01091   // Reflow children
01092   // Asking each child to cache its bounding metrics
01093 
01094   nsReflowStatus childStatus;
01095   nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
01096   nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mComputeMEW,
01097                       aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
01098   nsIFrame* childFrame = mFrames.FirstChild();
01099   while (childFrame) {
01100     nsReflowReason reason = (childFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)
01101       ? eReflowReason_Initial : aReflowState.reason;
01102     nsHTMLReflowState childReflowState(aPresContext, aReflowState,
01103                                        childFrame, availSize, reason);
01104     rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
01105                      childReflowState, childStatus);
01106     //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
01107     if (NS_FAILED(rv)) return rv;
01108 
01109     // At this stage, the origin points of the children have no use, so we will use the
01110     // origins as placeholders to store the child's ascent and descent. Later on,
01111     // we should set the origins so as to overwrite what we are storing there now.
01112     childFrame->SetRect(nsRect(childDesiredSize.descent, childDesiredSize.ascent,
01113                                childDesiredSize.width, childDesiredSize.height));
01114     childFrame = childFrame->GetNextSibling();
01115   }
01116 
01118   // If we are a container which is entitled to stretch its children, then we
01119   // ask our stretchy children to stretch themselves
01120 
01121   // The stretching of siblings of an embellished child is _deferred_ until
01122   // after finishing the stretching of the embellished child - bug 117652
01123 
01124   if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
01125       (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
01126        NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
01127 
01128     // get the stretchy direction
01129     nsStretchDirection stretchDir =
01130       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) 
01131       ? NS_STRETCH_DIRECTION_VERTICAL 
01132       : NS_STRETCH_DIRECTION_HORIZONTAL;
01133 
01134     // what size should we use to stretch our stretchy children
01135     // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
01136     // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
01137     // include them in the caculations of the size of stretchy elements
01138     nsBoundingMetrics containerSize;
01139     GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
01140                             containerSize);
01141 
01142     // fire the stretch on each child
01143     childFrame = mFrames.FirstChild();
01144     while (childFrame) {
01145       nsIMathMLFrame* mathMLFrame;
01146       childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
01147       if (mathMLFrame) {
01148         // retrieve the metrics that was stored at the previous pass
01149         GetReflowAndBoundingMetricsFor(childFrame,
01150           childDesiredSize, childDesiredSize.mBoundingMetrics);
01151 
01152         mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
01153                              containerSize, childDesiredSize);
01154         // store the updated metrics
01155         childFrame->SetRect(nsRect(childDesiredSize.descent, childDesiredSize.ascent,
01156                                    childDesiredSize.width, childDesiredSize.height));
01157       }
01158       childFrame = childFrame->GetNextSibling();
01159     }
01160   }
01161 
01162   if (aDesiredSize.mComputeMEW) {
01163     aDesiredSize.mMaxElementWidth = childDesiredSize.mMaxElementWidth;
01164   }
01165 
01167   // Place children now by re-adjusting the origins to align the baselines
01168   FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
01169 
01170   aStatus = NS_FRAME_COMPLETE;
01171   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
01172   return NS_OK;
01173 }
01174 
01175 PRBool
01176 nsMathMLContainerFrame::IsFrameOfType(PRUint32 aFlags) const
01177 {
01178   return !(aFlags & ~nsIFrame::eMathML);
01179 }
01180 
01181 // see spacing table in Chapter 18, TeXBook (p.170)
01182 // Our table isn't quite identical to TeX because operators have 
01183 // built-in values for lspace & rspace in the Operator Dictionary.
01184 static PRInt32 kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
01185 {
01186   // in units of muspace.
01187   // upper half of the byte is set if the
01188   // spacing is not to be used for scriptlevel > 0
01189 
01190   /*           Ord  OpOrd OpInv OpUsr Inner Italic Upright */
01191   /*Ord  */   {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
01192   /*OpOrd*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
01193   /*OpInv*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
01194   /*OpUsr*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
01195   /*Inner*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
01196   /*Italic*/  {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
01197   /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}
01198 };
01199 
01200 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_)  \
01201    /* no space if there is a frame that we know nothing about */        \
01202    if (frametype1_ == eMathMLFrameType_UNKNOWN ||                       \
01203        frametype2_ == eMathMLFrameType_UNKNOWN)                         \
01204     space_ = 0;                                                         \
01205   else {                                                                \
01206     space_ = kInterFrameSpacingTable[frametype1_][frametype2_];         \
01207     space_ = (scriptlevel_ > 0 && (space_ & 0xF0))                      \
01208       ? 0 /* spacing is disabled */                                     \
01209       : space_ & 0x0F;                                                  \
01210   }                                                                     \
01211 
01212 // This function computes the inter-space between two frames. However, 
01213 // since invisible operators need special treatment, the inter-space may
01214 // be delayed when an invisible operator is encountered. In this case,
01215 // the function will carry the inter-space forward until it is determined
01216 // that it can be applied properly (i.e., until we encounter a visible
01217 // frame where to decide whether to accept or reject the inter-space).
01218 // aFromFrameType: remembers the frame when the carry-forward initiated.
01219 // aCarrySpace: keeps track of the inter-space that is delayed.
01220 // @returns: current inter-space (which is 0 when the true inter-space is
01221 // delayed -- and thus has no effect since the frame is invisible anyway).
01222 static nscoord
01223 GetInterFrameSpacing(PRInt32           aScriptLevel,
01224                      eMathMLFrameType  aFirstFrameType,
01225                      eMathMLFrameType  aSecondFrameType,
01226                      eMathMLFrameType* aFromFrameType, // IN/OUT
01227                      PRInt32*          aCarrySpace)    // IN/OUT
01228 {
01229   eMathMLFrameType firstType = aFirstFrameType;
01230   eMathMLFrameType secondType = aSecondFrameType;
01231 
01232   PRInt32 space;
01233   GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
01234 
01235   // feedback control to avoid the inter-space to be added when not necessary
01236   if (secondType == eMathMLFrameType_OperatorInvisible) {
01237     // see if we should start to carry the space forward until we
01238     // encounter a visible frame
01239     if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
01240       *aFromFrameType = firstType;
01241       *aCarrySpace = space;
01242     }
01243     // keep carrying *aCarrySpace forward, while returning 0 for this stage
01244     space = 0;
01245   }
01246   else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
01247     // no carry-forward anymore, get the real inter-space between
01248     // the two frames of interest
01249 
01250     firstType = *aFromFrameType;
01251 
01252     // But... the invisible operator that we encountered earlier could
01253     // be sitting between italic and upright identifiers, e.g.,
01254     //
01255     // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
01256     // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
01257     //
01258     // the trick to get the inter-space in either situation
01259     // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
01260     // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
01261     if (firstType == eMathMLFrameType_UprightIdentifier) {
01262       firstType = eMathMLFrameType_OperatorUserDefined;
01263     }
01264     else if (secondType == eMathMLFrameType_UprightIdentifier) {
01265       secondType = eMathMLFrameType_OperatorUserDefined;
01266     }
01267 
01268     GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
01269 
01270     // Now, we have two values: the computed space and the space that
01271     // has been carried forward until now. Which value do we pick?
01272     // If the second type is an operator (e.g., fence), it already has
01273     // built-in lspace & rspace, so we let them win. Otherwise we pick
01274     // the max between the two values that we have.
01275     if (secondType != eMathMLFrameType_OperatorOrdinary &&
01276         space < *aCarrySpace)
01277       space = *aCarrySpace;
01278 
01279     // reset everything now that the carry-forward is done
01280     *aFromFrameType = eMathMLFrameType_UNKNOWN;
01281     *aCarrySpace = 0;
01282   }
01283 
01284   return space;
01285 }
01286 
01287 NS_IMETHODIMP
01288 nsMathMLContainerFrame::Place(nsIRenderingContext& aRenderingContext,
01289                               PRBool               aPlaceOrigin,
01290                               nsHTMLReflowMetrics& aDesiredSize)
01291 {
01292   // these are needed in case this frame is empty (i.e., we don't enter the loop)
01293   aDesiredSize.width = aDesiredSize.height = 0;
01294   aDesiredSize.ascent = aDesiredSize.descent = 0;
01295   mBoundingMetrics.Clear();
01296 
01297   // cache away thinspace
01298   const nsStyleFont* font = GetStyleFont();
01299   nscoord thinSpace = NSToCoordRound(float(font->mFont.size)*float(3) / float(18));
01300 
01301   PRInt32 count = 0;
01302   PRInt32 carrySpace = 0;
01303   nsHTMLReflowMetrics childSize (nsnull);
01304   nsBoundingMetrics bmChild;
01305   nscoord leftCorrection = 0, italicCorrection = 0;
01306   eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
01307   eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
01308   eMathMLFrameType childFrameType;
01309 
01310   nsIFrame* childFrame = mFrames.FirstChild();
01311   while (childFrame) {
01312     GetReflowAndBoundingMetricsFor(childFrame, childSize, bmChild, &childFrameType);
01313     GetItalicCorrection(bmChild, leftCorrection, italicCorrection);
01314     if (0 == count) {
01315       aDesiredSize.ascent = childSize.ascent;
01316       aDesiredSize.descent = childSize.descent;
01317       mBoundingMetrics = bmChild;
01318       // update to include the left correction
01319       // but leave <msqrt> alone because the sqrt glyph itself is there first
01320 
01321       if (mContent->Tag() == nsMathMLAtoms::msqrt_)
01322         leftCorrection = 0;
01323       else
01324         mBoundingMetrics.leftBearing += leftCorrection;
01325     }
01326     else {
01327       if (aDesiredSize.descent < childSize.descent)
01328         aDesiredSize.descent = childSize.descent;
01329       if (aDesiredSize.ascent < childSize.ascent)
01330         aDesiredSize.ascent = childSize.ascent;
01331       // add inter frame spacing
01332       nscoord space = GetInterFrameSpacing(mPresentationData.scriptLevel,
01333         prevFrameType, childFrameType, &fromFrameType, &carrySpace);
01334       mBoundingMetrics.width += space * thinSpace;
01335       // add the child size
01336       mBoundingMetrics += bmChild;
01337     }
01338     count++;
01339     prevFrameType = childFrameType;
01340     // add left correction -- this fixes the problem of the italic 'f'
01341     // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo> 
01342     mBoundingMetrics.width += leftCorrection;
01343     mBoundingMetrics.rightBearing += leftCorrection;
01344     // add the italic correction at the end (including the last child).
01345     // this gives a nice gap between math and non-math frames, and still
01346     // gives the same math inter-spacing in case this frame connects to
01347     // another math frame
01348     mBoundingMetrics.width += italicCorrection;
01349 
01350     childFrame = childFrame->GetNextSibling();
01351   }
01352   aDesiredSize.width = mBoundingMetrics.width;
01353   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
01354   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
01355 
01356   mReference.x = 0;
01357   mReference.y = aDesiredSize.ascent;
01358 
01360   // Place Children
01361 
01362   if (aPlaceOrigin) {
01363     count = 0;
01364     nscoord dx = 0, dy = 0;
01365     italicCorrection = 0;
01366     carrySpace = 0;
01367     fromFrameType = eMathMLFrameType_UNKNOWN;
01368     childFrame = mFrames.FirstChild();
01369     while (childFrame) {
01370       GetReflowAndBoundingMetricsFor(childFrame, childSize, bmChild, &childFrameType);
01371       GetItalicCorrection(bmChild, leftCorrection, italicCorrection);
01372       dy = aDesiredSize.ascent - childSize.ascent;
01373       if (0 == count) {
01374         // for <msqrt>, the sqrt glyph itself is there first
01375 
01376         if (mContent->Tag() == nsMathMLAtoms::msqrt_)
01377           leftCorrection = 0;
01378       }
01379       else {
01380         // add inter frame spacing
01381         nscoord space = GetInterFrameSpacing(mPresentationData.scriptLevel,
01382           prevFrameType, childFrameType, &fromFrameType, &carrySpace);
01383         dx += space * thinSpace;
01384       }
01385       count++;
01386       prevFrameType = childFrameType;
01387       // add left correction
01388       dx += leftCorrection;
01389       FinishReflowChild(childFrame, GetPresContext(), nsnull, childSize,
01390                         dx, dy, 0);
01391       // add child size + italic correction
01392       dx += bmChild.width + italicCorrection;
01393       childFrame = childFrame->GetNextSibling();
01394     }
01395   }
01396 
01397   return NS_OK;
01398 }
01399 
01400 // helpers to fix the inter-spacing when <math> is the only parent
01401 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
01402 
01403 static nscoord
01404 GetInterFrameSpacingFor(PRInt32         aScriptLevel,
01405                         nsIFrame*       aParentFrame,
01406                         nsIFrame*       aChildFrame)
01407 {
01408   nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
01409   if (!childFrame || aChildFrame == childFrame)
01410     return 0;
01411 
01412   PRInt32 carrySpace = 0;
01413   eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
01414   eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
01415   eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
01416   childFrame = childFrame->GetNextSibling();
01417   while (childFrame) {
01418     prevFrameType = childFrameType;
01419     childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
01420     nscoord space = GetInterFrameSpacing(aScriptLevel,
01421       prevFrameType, childFrameType, &fromFrameType, &carrySpace);
01422     if (aChildFrame == childFrame) {
01423       // get thinspace
01424       nsStyleContext* parentContext = aParentFrame->GetStyleContext();
01425       const nsStyleFont* font = parentContext->GetStyleFont();
01426       nscoord thinSpace = NSToCoordRound(float(font->mFont.size)*float(3) / float(18));
01427       // we are done
01428       return space * thinSpace;
01429     }
01430     childFrame = childFrame->GetNextSibling();
01431   }
01432 
01433   NS_NOTREACHED("child not in the childlist of its parent");
01434   return 0;
01435 }
01436 
01437 nscoord
01438 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
01439 {
01440   nscoord gap = 0;
01441   nsIContent* parentContent = mParent->GetContent();
01442   if (NS_UNLIKELY(!parentContent)) {
01443     return 0;
01444   }
01445   nsIAtom *parentTag = parentContent->Tag();
01446   if (parentTag == nsMathMLAtoms::math ||
01447       parentTag == nsMathMLAtoms::mtd_) {
01448     gap = GetInterFrameSpacingFor(mPresentationData.scriptLevel, mParent, this);
01449     // add our own italic correction
01450     nscoord leftCorrection = 0, italicCorrection = 0;
01451     GetItalicCorrection(mBoundingMetrics, leftCorrection, italicCorrection);
01452     gap += leftCorrection;
01453     // see if we should shift our children to account for the correction
01454     if (gap) {
01455       nsIFrame* childFrame = mFrames.FirstChild();
01456       while (childFrame) {
01457         childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
01458         childFrame = childFrame->GetNextSibling();
01459       }
01460       mBoundingMetrics.leftBearing += gap;
01461       mBoundingMetrics.rightBearing += gap;
01462       mBoundingMetrics.width += gap;
01463       aDesiredSize.width += gap;
01464     }
01465     mBoundingMetrics.width += italicCorrection;
01466     aDesiredSize.width += italicCorrection;
01467   }
01468   return gap;
01469 }
01470 
01471 
01472 
01473 //==========================
01474 
01475 nsresult
01476 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
01477 {
01478   NS_PRECONDITION(aNewFrame, "null OUT ptr");
01479   if (nsnull == aNewFrame) {
01480     return NS_ERROR_NULL_POINTER;
01481   }
01482   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame;
01483   if (nsnull == it) {
01484     return NS_ERROR_OUT_OF_MEMORY;
01485   }
01486   *aNewFrame = it;
01487   return NS_OK;
01488 }
01489 
01490 //==========================
01491 
01492 nsresult
01493 NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
01494 {
01495   NS_PRECONDITION(aNewFrame, "null OUT ptr");
01496   if (nsnull == aNewFrame) {
01497     return NS_ERROR_NULL_POINTER;
01498   }
01499   nsMathMLmathInlineFrame* it = new (aPresShell) nsMathMLmathInlineFrame;
01500   if (nsnull == it) {
01501     return NS_ERROR_OUT_OF_MEMORY;
01502   }
01503   *aNewFrame = it;
01504   return NS_OK;
01505 }