layout/mathml/nsMathMLContainerFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsMathMLContainerFrame.h"
     7 #include "nsPresContext.h"
     8 #include "nsIPresShell.h"
     9 #include "nsStyleContext.h"
    10 #include "nsNameSpaceManager.h"
    11 #include "nsRenderingContext.h"
    12 #include "nsIDOMMutationEvent.h"
    13 #include "nsGkAtoms.h"
    14 #include "nsAutoPtr.h"
    15 #include "nsDisplayList.h"
    16 #include "nsIReflowCallback.h"
    17 #include "mozilla/Likely.h"
    18 #include "nsIScriptError.h"
    19 #include "nsContentUtils.h"
    20 #include "nsMathMLElement.h"
    22 using namespace mozilla;
    24 //
    25 // nsMathMLContainerFrame implementation
    26 //
    28 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
    30 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
    31   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
    32   NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame)
    33 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    35 // =============================================================================
    37 // error handlers
    38 // provide a feedback to the user when a frame with bad markup can not be rendered
    39 nsresult
    40 nsMathMLContainerFrame::ReflowError(nsRenderingContext& aRenderingContext,
    41                                     nsHTMLReflowMetrics& aDesiredSize)
    42 {
    43   // clear all other flags and record that there is an error with this frame
    44   mEmbellishData.flags = 0;
    45   mPresentationData.flags = NS_MATHML_ERROR;
    47   ///////////////
    48   // Set font
    49   nsRefPtr<nsFontMetrics> fm;
    50   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
    51   aRenderingContext.SetFont(fm);
    53   // bounding metrics
    54   nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
    55   mBoundingMetrics =
    56     aRenderingContext.GetBoundingMetrics(errorMsg.get(), errorMsg.Length());
    58   // reflow metrics
    59   aDesiredSize.SetTopAscent(fm->MaxAscent());
    60   nscoord descent = fm->MaxDescent();
    61   aDesiredSize.Height() = aDesiredSize.TopAscent() + descent;
    62   aDesiredSize.Width() = mBoundingMetrics.width;
    64   // Also return our bounding metrics
    65   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
    67   return NS_OK;
    68 }
    70 class nsDisplayMathMLError : public nsDisplayItem {
    71 public:
    72   nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
    73     : nsDisplayItem(aBuilder, aFrame) {
    74     MOZ_COUNT_CTOR(nsDisplayMathMLError);
    75   }
    76 #ifdef NS_BUILD_REFCNT_LOGGING
    77   virtual ~nsDisplayMathMLError() {
    78     MOZ_COUNT_DTOR(nsDisplayMathMLError);
    79   }
    80 #endif
    82   virtual void Paint(nsDisplayListBuilder* aBuilder,
    83                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
    84   NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR)
    85 };
    87 void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
    88                                  nsRenderingContext* aCtx)
    89 {
    90   // Set color and font ...
    91   nsRefPtr<nsFontMetrics> fm;
    92   nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
    93   aCtx->SetFont(fm);
    95   nsPoint pt = ToReferenceFrame();
    96   aCtx->SetColor(NS_RGB(255,0,0));
    97   aCtx->FillRect(nsRect(pt, mFrame->GetSize()));
    98   aCtx->SetColor(NS_RGB(255,255,255));
   100   nscoord ascent = aCtx->FontMetrics()->MaxAscent();
   102   NS_NAMED_LITERAL_STRING(errorMsg, "invalid-markup");
   103   aCtx->DrawString(errorMsg.get(), uint32_t(errorMsg.Length()),
   104                    pt.x, pt.y+ascent);
   105 }
   107 /* /////////////
   108  * nsIMathMLFrame - support methods for stretchy elements
   109  * =============================================================================
   110  */
   112 static bool
   113 IsForeignChild(const nsIFrame* aFrame)
   114 {
   115   // This counts nsMathMLmathBlockFrame as a foreign child, because it
   116   // uses block reflow
   117   return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
   118     aFrame->GetType() == nsGkAtoms::blockFrame;
   119 }
   121 static void
   122 DestroyHTMLReflowMetrics(void *aPropertyValue)
   123 {
   124   delete static_cast<nsHTMLReflowMetrics*>(aPropertyValue);
   125 }
   127 NS_DECLARE_FRAME_PROPERTY(HTMLReflowMetricsProperty, DestroyHTMLReflowMetrics)
   129 /* static */ void
   130 nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame*                  aFrame,
   131                                                         const nsHTMLReflowMetrics& aReflowMetrics,
   132                                                         const nsBoundingMetrics&   aBoundingMetrics)
   133 {
   134   nsHTMLReflowMetrics *metrics = new nsHTMLReflowMetrics(aReflowMetrics);
   135   metrics->mBoundingMetrics = aBoundingMetrics;
   136   aFrame->Properties().Set(HTMLReflowMetricsProperty(), metrics);
   137 }
   139 // helper method to facilitate getting the reflow and bounding metrics
   140 /* static */ void
   141 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
   142                                                        nsHTMLReflowMetrics& aReflowMetrics,
   143                                                        nsBoundingMetrics&   aBoundingMetrics,
   144                                                        eMathMLFrameType*    aMathMLFrameType)
   145 {
   146   NS_PRECONDITION(aFrame, "null arg");
   148   nsHTMLReflowMetrics *metrics = static_cast<nsHTMLReflowMetrics*>
   149     (aFrame->Properties().Get(HTMLReflowMetricsProperty()));
   151   // IMPORTANT: This function is only meant to be called in Place() methods
   152   // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
   153   // information.
   154   NS_ASSERTION(metrics, "Didn't SaveReflowAndBoundingMetricsFor frame!");
   155   if (metrics) {
   156     aReflowMetrics = *metrics;
   157     aBoundingMetrics = metrics->mBoundingMetrics;
   158   }
   160   if (aMathMLFrameType) {
   161     if (!IsForeignChild(aFrame)) {
   162       nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
   163       if (mathMLFrame) {
   164         *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
   165         return;
   166       }
   167     }
   168     *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
   169   }
   171 }
   173 void
   174 nsMathMLContainerFrame::ClearSavedChildMetrics()
   175 {
   176   nsIFrame* childFrame = mFrames.FirstChild();
   177   FramePropertyTable* props = PresContext()->PropertyTable();
   178   while (childFrame) {
   179     props->Delete(childFrame, HTMLReflowMetricsProperty());
   180     childFrame = childFrame->GetNextSibling();
   181   }
   182 }
   184 // helper to get the preferred size that a container frame should use to fire
   185 // the stretch on its stretchy child frames.
   186 void
   187 nsMathMLContainerFrame::GetPreferredStretchSize(nsRenderingContext& aRenderingContext,
   188                                                 uint32_t             aOptions,
   189                                                 nsStretchDirection   aStretchDirection,
   190                                                 nsBoundingMetrics&   aPreferredStretchSize)
   191 {
   192   if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
   193     // when our actual size is ok, just use it
   194     aPreferredStretchSize = mBoundingMetrics;
   195   }
   196   else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
   197     // compute our up-to-date size using Place()
   198     nsHTMLReflowMetrics metrics(GetWritingMode()); // ???
   199     Place(aRenderingContext, false, metrics);
   200     aPreferredStretchSize = metrics.mBoundingMetrics;
   201   }
   202   else {
   203     // compute a size that doesn't include embellishements
   204     bool stretchAll =
   205       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   206       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   207     NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
   208                  stretchAll,
   209                  "invalid call to GetPreferredStretchSize");
   210     bool firstTime = true;
   211     nsBoundingMetrics bm, bmChild;
   212     nsIFrame* childFrame =
   213       stretchAll ? GetFirstPrincipalChild() : mPresentationData.baseFrame;
   214     while (childFrame) {
   215       // initializations in case this child happens not to be a MathML frame
   216       nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
   217       if (mathMLFrame) {
   218         nsEmbellishData embellishData;
   219         nsPresentationData presentationData;
   220         mathMLFrame->GetEmbellishData(embellishData);
   221         mathMLFrame->GetPresentationData(presentationData);
   222         if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
   223             embellishData.direction == aStretchDirection &&
   224             presentationData.baseFrame) {
   225           // embellishements are not included, only consider the inner first child itself
   226           // XXXkt Does that mean the core descendent frame should be used
   227           // instead of the base child?
   228           nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame);
   229           if (mathMLchildFrame) {
   230             mathMLFrame = mathMLchildFrame;
   231           }
   232         }
   233         mathMLFrame->GetBoundingMetrics(bmChild);
   234       }
   235       else {
   236         nsHTMLReflowMetrics unused(GetWritingMode());
   237         GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
   238       }
   240       if (firstTime) {
   241         firstTime = false;
   242         bm = bmChild;
   243         if (!stretchAll) {
   244           // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
   245           // or <maction>...<mo>...</mo></maction>.
   246           break;
   247         }
   248       }
   249       else {
   250         if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
   251           // if we get here, it means this is container that will stack its children
   252           // vertically and fire an horizontal stretch on each them. This is the case
   253           // for \munder, \mover, \munderover. We just sum-up the size vertically.
   254           bm.descent += bmChild.ascent + bmChild.descent;
   255           // Sometimes non-spacing marks (when width is zero) are positioned
   256           // to the left of the origin, but it is the distance between left
   257           // and right bearing that is important rather than the offsets from
   258           // the origin.
   259           if (bmChild.width == 0) {
   260             bmChild.rightBearing -= bmChild.leftBearing;
   261             bmChild.leftBearing = 0;
   262           }
   263           if (bm.leftBearing > bmChild.leftBearing)
   264             bm.leftBearing = bmChild.leftBearing;
   265           if (bm.rightBearing < bmChild.rightBearing)
   266             bm.rightBearing = bmChild.rightBearing;
   267         }
   268         else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
   269           // just sum-up the sizes horizontally.
   270           bm += bmChild;
   271         }
   272         else {
   273           NS_ERROR("unexpected case in GetPreferredStretchSize");
   274           break;
   275         }
   276       }
   277       childFrame = childFrame->GetNextSibling();
   278     }
   279     aPreferredStretchSize = bm;
   280   }
   281 }
   283 NS_IMETHODIMP
   284 nsMathMLContainerFrame::Stretch(nsRenderingContext& aRenderingContext,
   285                                 nsStretchDirection   aStretchDirection,
   286                                 nsBoundingMetrics&   aContainerSize,
   287                                 nsHTMLReflowMetrics& aDesiredStretchSize)
   288 {
   289   if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
   291     if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
   292       NS_WARNING("it is wrong to fire stretch more than once on a frame");
   293       return NS_OK;
   294     }
   295     mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
   297     if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
   298       NS_WARNING("it is wrong to fire stretch on a erroneous frame");
   299       return NS_OK;
   300     }
   302     // Pass the stretch to the base child ...
   304     nsIFrame* baseFrame = mPresentationData.baseFrame;
   305     if (baseFrame) {
   306       nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
   307       NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
   308       if (mathMLFrame) {
   309         bool stretchAll =
   310           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   311           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   313         // And the trick is that the child's rect.x is still holding the descent,
   314         // and rect.y is still holding the ascent ...
   315         nsHTMLReflowMetrics childSize(aDesiredStretchSize);
   316         GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
   318         // See if we should downsize and confine the stretch to us...
   319         // XXX there may be other cases where we can downsize the stretch,
   320         // e.g., the first &Sum; might appear big in the following situation
   321         // <math xmlns='http://www.w3.org/1998/Math/MathML'>
   322         //   <mstyle>
   323         //     <msub>
   324         //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
   325         //        <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
   326         //      </msub>
   327         //   </mstyle>
   328         // </math>
   329         nsBoundingMetrics containerSize = aContainerSize;
   330         if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
   331             aStretchDirection != mEmbellishData.direction) {
   332           if (mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) {
   333             containerSize = childSize.mBoundingMetrics;
   334           }
   335           else {
   336             GetPreferredStretchSize(aRenderingContext, 
   337                                     stretchAll ? STRETCH_CONSIDER_EMBELLISHMENTS : 0,
   338                                     mEmbellishData.direction, containerSize);
   339           }
   340         }
   342         // do the stretching...
   343         mathMLFrame->Stretch(aRenderingContext,
   344                              mEmbellishData.direction, containerSize, childSize);
   345         // store the updated metrics
   346         SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
   347                                         childSize.mBoundingMetrics);
   349         // Remember the siblings which were _deferred_.
   350         // Now that this embellished child may have changed, we need to
   351         // fire the stretch on its siblings using our updated size
   353         if (stretchAll) {
   355           nsStretchDirection stretchDir =
   356             NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
   357               NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
   359           GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
   360                                   stretchDir, containerSize);
   362           nsIFrame* childFrame = mFrames.FirstChild();
   363           while (childFrame) {
   364             if (childFrame != mPresentationData.baseFrame) {
   365               mathMLFrame = do_QueryFrame(childFrame);
   366               if (mathMLFrame) {
   367                 // retrieve the metrics that was stored at the previous pass
   368                 GetReflowAndBoundingMetricsFor(childFrame, 
   369                   childSize, childSize.mBoundingMetrics);
   370                 // do the stretching...
   371                 mathMLFrame->Stretch(aRenderingContext, stretchDir,
   372                                      containerSize, childSize);
   373                 // store the updated metrics
   374                 SaveReflowAndBoundingMetricsFor(childFrame, childSize,
   375                                                 childSize.mBoundingMetrics);
   376               }
   377             }
   378             childFrame = childFrame->GetNextSibling();
   379           }
   380         }
   382         // re-position all our children
   383         nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
   384         if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   385           // Make sure the child frames get their DidReflow() calls.
   386           DidReflowChildren(mFrames.FirstChild());
   387         }
   389         // If our parent is not embellished, it means we are the outermost embellished
   390         // container and so we put the spacing, otherwise we don't include the spacing,
   391         // the outermost embellished container will take care of it.
   393         nsEmbellishData parentData;
   394         GetEmbellishDataFrom(mParent, parentData);
   395         // ensure that we are the embellished child, not just a sibling
   396         // (need to test coreFrame since <mfrac> resets other things)
   397         if (parentData.coreFrame != mEmbellishData.coreFrame) {
   398           // (we fetch values from the core since they may use units that depend
   399           // on style data, and style changes could have occurred in the core since
   400           // our last visit there)
   401           nsEmbellishData coreData;
   402           GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
   404           mBoundingMetrics.width +=
   405             coreData.leadingSpace + coreData.trailingSpace;
   406           aDesiredStretchSize.Width() = mBoundingMetrics.width;
   407           aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
   409           nscoord dx = (StyleVisibility()->mDirection ?
   410                         coreData.trailingSpace : coreData.leadingSpace);
   411           if (dx != 0) {
   412             mBoundingMetrics.leftBearing += dx;
   413             mBoundingMetrics.rightBearing += dx;
   414             aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
   415             aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
   417             nsIFrame* childFrame = mFrames.FirstChild();
   418             while (childFrame) {
   419               childFrame->SetPosition(childFrame->GetPosition()
   420                                       + nsPoint(dx, 0));
   421               childFrame = childFrame->GetNextSibling();
   422             }
   423           }
   424         }
   426         // Finished with these:
   427         ClearSavedChildMetrics();
   428         // Set our overflow area
   429         GatherAndStoreOverflow(&aDesiredStretchSize);
   430       }
   431     }
   432   }
   433   return NS_OK;
   434 }
   436 nsresult
   437 nsMathMLContainerFrame::FinalizeReflow(nsRenderingContext& aRenderingContext,
   438                                        nsHTMLReflowMetrics& aDesiredSize)
   439 {
   440   // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
   441   // and descent in expectation of a stretch command. Hence we need to ensure that
   442   // a stretch command will actually be fired later on, after exiting from our
   443   // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
   444   // with inappropriate data causing children to be improperly positioned.
   445   // This helper method checks to see if our parent will fire a stretch command
   446   // targeted at us. If not, we go ahead and fire an involutive stretch on
   447   // ourselves. This will clear all the rect.x and rect.y, and return our
   448   // desired size.
   451   // First, complete the post-reflow hook.
   452   // We use the information in our children rectangles to position them.
   453   // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
   454   // They will still be holding the ascent and descent for each child.
   456   // The first clause caters for any non-embellished container.
   457   // The second clause is for a container which won't fire stretch even though it is
   458   // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
   459   // because it excludes the particular case of the core <mo>...</mo> itself.
   460   // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
   461   bool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
   462                        (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
   463                         mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
   464   nsresult rv = Place(aRenderingContext, placeOrigin, aDesiredSize);
   466   // Place() will call FinishReflowChild() when placeOrigin is true but if
   467   // it returns before reaching FinishReflowChild() due to errors we need
   468   // to fulfill the reflow protocol by calling DidReflow for the child frames
   469   // that still needs it here (or we may crash - bug 366012).
   470   // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
   471   // through Stretch() eventually.
   472   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   473     DidReflowChildren(GetFirstPrincipalChild());
   474     return rv;
   475   }
   477   bool parentWillFireStretch = false;
   478   if (!placeOrigin) {
   479     // This means the rect.x and rect.y of our children were not set!!
   480     // Don't go without checking to see if our parent will later fire a Stretch() command
   481     // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
   482     nsIMathMLFrame* mathMLFrame = do_QueryFrame(mParent);
   483     if (mathMLFrame) {
   484       nsEmbellishData embellishData;
   485       nsPresentationData presentationData;
   486       mathMLFrame->GetEmbellishData(embellishData);
   487       mathMLFrame->GetPresentationData(presentationData);
   488       if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
   489           NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
   490           (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
   491             && presentationData.baseFrame == this))
   492       {
   493         parentWillFireStretch = true;
   494       }
   495     }
   496     if (!parentWillFireStretch) {
   497       // There is nobody who will fire the stretch for us, we do it ourselves!
   499       bool stretchAll =
   500         /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
   501         NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
   503       nsBoundingMetrics defaultSize;
   504       if (mEmbellishData.coreFrame == this /* case of a bare <mo>...</mo> itself */
   505           || stretchAll) { /* or <mover><mo>...</mo>...</mover>, or friends */
   506         // use our current size as computed earlier by Place()
   507         defaultSize = aDesiredSize.mBoundingMetrics;
   508       }
   509       else { /* case of <msup><mo>...</mo>...</msup> or friends */
   510         // compute a size that doesn't include embellishments
   511         GetPreferredStretchSize(aRenderingContext, 0, mEmbellishData.direction,
   512                                 defaultSize);
   513       }
   514       Stretch(aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, defaultSize,
   515               aDesiredSize);
   516 #ifdef DEBUG
   517       {
   518         // The Place() call above didn't request FinishReflowChild(),
   519         // so let's check that we eventually did through Stretch().
   520         nsIFrame* childFrame = GetFirstPrincipalChild();
   521         for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
   522           NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
   523                        "DidReflow() was never called");
   524         }
   525       }
   526 #endif
   527     }
   528   }
   530   // Also return our bounding metrics
   531   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   533   // see if we should fix the spacing
   534   FixInterFrameSpacing(aDesiredSize);
   536   if (!parentWillFireStretch) {
   537     // Not expecting a stretch.
   538     // Finished with these:
   539     ClearSavedChildMetrics();
   540     // Set our overflow area.
   541     GatherAndStoreOverflow(&aDesiredSize);
   542   }
   544   return NS_OK;
   545 }
   548 /* /////////////
   549  * nsIMathMLFrame - support methods for scripting elements (nested frames
   550  * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
   551  * mfrac, mroot, mtable).
   552  * =============================================================================
   553  */
   555 // helper to let the update of presentation data pass through
   556 // a subtree that may contain non-mathml container frames
   557 /* static */ void
   558 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame*       aFrame,
   559                                                      uint32_t        aFlagsValues,
   560                                                      uint32_t        aFlagsToUpdate)
   561 {
   562   if (!aFrame || !aFlagsToUpdate)
   563     return;
   564   nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
   565   if (mathMLFrame) {
   566     // update
   567     mathMLFrame->UpdatePresentationData(aFlagsValues,
   568                                         aFlagsToUpdate);
   569     // propagate using the base method to make sure that the control
   570     // is passed on to MathML frames that may be overloading the method
   571     mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
   572       aFlagsValues, aFlagsToUpdate);
   573   }
   574   else {
   575     // propagate down the subtrees
   576     nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
   577     while (childFrame) {
   578       PropagatePresentationDataFor(childFrame,
   579         aFlagsValues, aFlagsToUpdate);
   580       childFrame = childFrame->GetNextSibling();
   581     }
   582   }
   583 }
   585 /* static */ void
   586 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame*       aParentFrame,
   587                                                              int32_t         aFirstChildIndex,
   588                                                              int32_t         aLastChildIndex,
   589                                                              uint32_t        aFlagsValues,
   590                                                              uint32_t        aFlagsToUpdate)
   591 {
   592   if (!aParentFrame || !aFlagsToUpdate)
   593     return;
   594   int32_t index = 0;
   595   nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
   596   while (childFrame) {
   597     if ((index >= aFirstChildIndex) &&
   598         ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
   599          (index <= aLastChildIndex)))) {
   600       PropagatePresentationDataFor(childFrame,
   601         aFlagsValues, aFlagsToUpdate);
   602     }
   603     index++;
   604     childFrame = childFrame->GetNextSibling();
   605   }
   606 }
   608 /* //////////////////
   609  * Frame construction
   610  * =============================================================================
   611  */
   614 void
   615 nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   616                                          const nsRect&           aDirtyRect,
   617                                          const nsDisplayListSet& aLists)
   618 {
   619   // report an error if something wrong was found in this frame
   620   if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
   621     if (!IsVisibleForPainting(aBuilder))
   622       return;
   624     aLists.Content()->AppendNewToTop(
   625       new (aBuilder) nsDisplayMathMLError(aBuilder, this));
   626     return;
   627   }
   629   DisplayBorderBackgroundOutline(aBuilder, aLists);
   631   BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists,
   632                                       DISPLAY_CHILD_INLINE);
   634 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   635   // for visual debug
   636   // ----------------
   637   // if you want to see your bounding box, make sure to properly fill
   638   // your mBoundingMetrics and mReference point, and set
   639   // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
   640   // in the Init() of your sub-class
   641   DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
   642 #endif
   643 }
   645 // Note that this method re-builds the automatic data in the children -- not
   646 // in aParentFrame itself (except for those particular operations that the
   647 // parent frame may do in its TransmitAutomaticData()).
   648 /* static */ void
   649 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
   650 {
   651   // 1. As we descend the tree, make each child frame inherit data from
   652   // the parent
   653   // 2. As we ascend the tree, transmit any specific change that we want
   654   // down the subtrees
   655   nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
   656   while (childFrame) {
   657     nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
   658     if (childMathMLFrame) {
   659       childMathMLFrame->InheritAutomaticData(aParentFrame);
   660     }
   661     RebuildAutomaticDataForChildren(childFrame);
   662     childFrame = childFrame->GetNextSibling();
   663   }
   664   nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
   665   if (mathMLFrame) {
   666     mathMLFrame->TransmitAutomaticData();
   667   }
   668 }
   670 /* static */ nsresult
   671 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
   672 {
   673   if (!aParentFrame)
   674     return NS_OK;
   676   // walk-up to the first frame that is a MathML frame, stop if we reach <math>
   677   nsIFrame* frame = aParentFrame;
   678   while (1) {
   679      nsIFrame* parent = frame->GetParent();
   680      if (!parent || !parent->GetContent())
   681        break;
   683     // stop if it is a MathML frame
   684     nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
   685     if (mathMLFrame)
   686       break;
   688     // stop if we reach the root <math> tag
   689     nsIContent* content = frame->GetContent();
   690     NS_ASSERTION(content, "dangling frame without a content node");
   691     if (!content)
   692       break;
   693     if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
   694         content->Tag() == nsGkAtoms::math)
   695       break;
   697     frame = parent;
   698   }
   700   // re-sync the presentation data and embellishment data of our children
   701   RebuildAutomaticDataForChildren(frame);
   703   // Ask our parent frame to reflow us
   704   nsIFrame* parent = frame->GetParent();
   705   NS_ASSERTION(parent, "No parent to pass the reflow request up to");
   706   if (!parent)
   707     return NS_OK;
   709   frame->PresContext()->PresShell()->
   710     FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   712   return NS_OK;
   713 }
   715 // There are precise rules governing children of a MathML frame,
   716 // and properties such as the scriptlevel depends on those rules.
   717 // Hence for things to work, callers must use Append/Insert/etc wisely.
   719 nsresult
   720 nsMathMLContainerFrame::ChildListChanged(int32_t aModType)
   721 {
   722   // If this is an embellished frame we need to rebuild the
   723   // embellished hierarchy by walking-up to the parent of the
   724   // outermost embellished container.
   725   nsIFrame* frame = this;
   726   if (mEmbellishData.coreFrame) {
   727     nsIFrame* parent = mParent;
   728     nsEmbellishData embellishData;
   729     for ( ; parent; frame = parent, parent = parent->GetParent()) {
   730       GetEmbellishDataFrom(parent, embellishData);
   731       if (embellishData.coreFrame != mEmbellishData.coreFrame)
   732         break;
   733     }
   734   }
   735   return ReLayoutChildren(frame);
   736 }
   738 nsresult
   739 nsMathMLContainerFrame::AppendFrames(ChildListID     aListID,
   740                                      nsFrameList&    aFrameList)
   741 {
   742   if (aListID != kPrincipalList) {
   743     return NS_ERROR_INVALID_ARG;
   744   }
   745   mFrames.AppendFrames(this, aFrameList);
   746   return ChildListChanged(nsIDOMMutationEvent::ADDITION);
   747 }
   749 nsresult
   750 nsMathMLContainerFrame::InsertFrames(ChildListID     aListID,
   751                                      nsIFrame*       aPrevFrame,
   752                                      nsFrameList&    aFrameList)
   753 {
   754   if (aListID != kPrincipalList) {
   755     return NS_ERROR_INVALID_ARG;
   756   }
   757   // Insert frames after aPrevFrame
   758   mFrames.InsertFrames(this, aPrevFrame, aFrameList);
   759   return ChildListChanged(nsIDOMMutationEvent::ADDITION);
   760 }
   762 nsresult
   763 nsMathMLContainerFrame::RemoveFrame(ChildListID     aListID,
   764                                     nsIFrame*       aOldFrame)
   765 {
   766   if (aListID != kPrincipalList) {
   767     return NS_ERROR_INVALID_ARG;
   768   }
   769   // remove the child frame
   770   mFrames.DestroyFrame(aOldFrame);
   771   return ChildListChanged(nsIDOMMutationEvent::REMOVAL);
   772 }
   774 nsresult
   775 nsMathMLContainerFrame::AttributeChanged(int32_t         aNameSpaceID,
   776                                          nsIAtom*        aAttribute,
   777                                          int32_t         aModType)
   778 {
   779   // XXX Since they are numerous MathML attributes that affect layout, and
   780   // we can't check all of them here, play safe by requesting a reflow.
   781   // XXXldb This should only do work for attributes that cause changes!
   782   PresContext()->PresShell()->
   783     FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   785   return NS_OK;
   786 }
   788 void
   789 nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
   790 {
   791   // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
   792   // frame rectangle.
   793   aMetrics->SetOverflowAreasToDesiredBounds();
   795   // All non-child-frame content such as nsMathMLChars (and most child-frame
   796   // content) is included in mBoundingMetrics.
   797   nsRect boundingBox(mBoundingMetrics.leftBearing,
   798                      aMetrics->TopAscent() - mBoundingMetrics.ascent,
   799                      mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
   800                      mBoundingMetrics.ascent + mBoundingMetrics.descent);
   802   // REVIEW: Maybe this should contribute only to visual overflow
   803   // and not scrollable?
   804   aMetrics->mOverflowAreas.UnionAllWith(boundingBox);
   806   // mBoundingMetrics does not necessarily include content of <mpadded>
   807   // elements whose mBoundingMetrics may not be representative of the true
   808   // bounds, and doesn't include the CSS2 outline rectangles of children, so
   809   // make such to include child overflow areas.
   810   nsIFrame* childFrame = mFrames.FirstChild();
   811   while (childFrame) {
   812     ConsiderChildOverflow(aMetrics->mOverflowAreas, childFrame);
   813     childFrame = childFrame->GetNextSibling();
   814   }
   816   FinishAndStoreOverflow(aMetrics);
   817 }
   819 bool
   820 nsMathMLContainerFrame::UpdateOverflow()
   821 {
   822   // Our overflow areas may have changed, so reflow the frame.
   823   PresContext()->PresShell()->FrameNeedsReflow(
   824     this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
   826   // As we're reflowing, there's no need to propagate this change.
   827   return false;
   828 }
   830 nsresult 
   831 nsMathMLContainerFrame::ReflowChild(nsIFrame*                aChildFrame,
   832                                     nsPresContext*           aPresContext,
   833                                     nsHTMLReflowMetrics&     aDesiredSize,
   834                                     const nsHTMLReflowState& aReflowState,
   835                                     nsReflowStatus&          aStatus)
   836 {
   837   // Having foreign/hybrid children, e.g., from html markups, is not defined by
   838   // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
   839   // to do some cool demos... or we may have a child that is an nsInlineFrame
   840   // from a generated content such as :before { content: open-quote } or 
   841   // :after { content: close-quote }. Unfortunately, the other frames out-there
   842   // may expect their own invariants that are not met when we mix things.
   843   // Hence we do not claim their support, but we will nevertheless attempt to keep
   844   // them in the flow, if we can get their desired size. We observed that most
   845   // frames may be reflowed generically, but nsInlineFrames need extra care.
   847 #ifdef DEBUG
   848   nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
   849   NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
   850 #endif
   852   nsresult rv = nsContainerFrame::
   853          ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
   854                      0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
   856   if (NS_FAILED(rv))
   857     return rv;
   859   if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
   860     // This will be suitable for inline frames, which are wrapped in a block.
   861     nscoord ascent;
   862     if (!nsLayoutUtils::GetLastLineBaseline(aChildFrame,
   863                                             &ascent)) {
   864       // We don't expect any other block children so just place the frame on
   865       // the baseline instead of going through DidReflow() and
   866       // GetBaseline().  This is what nsFrame::GetBaseline() will do anyway.
   867       aDesiredSize.SetTopAscent(aDesiredSize.Height());
   868     } else {
   869       aDesiredSize.SetTopAscent(ascent);
   870     }
   871   }
   872   if (IsForeignChild(aChildFrame)) {
   873     // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
   874     nsRect r = aChildFrame->ComputeTightBounds(aReflowState.rendContext->ThebesContext());
   875     aDesiredSize.mBoundingMetrics.leftBearing = r.x;
   876     aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
   877     aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.TopAscent() - r.y;
   878     aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.TopAscent();
   879     aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
   880   }
   881   return rv;
   882 }
   884 nsresult
   885 nsMathMLContainerFrame::Reflow(nsPresContext*           aPresContext,
   886                                nsHTMLReflowMetrics&     aDesiredSize,
   887                                const nsHTMLReflowState& aReflowState,
   888                                nsReflowStatus&          aStatus)
   889 {
   890   aDesiredSize.Width() = aDesiredSize.Height() = 0;
   891   aDesiredSize.SetTopAscent(0);
   892   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
   894   /////////////
   895   // Reflow children
   896   // Asking each child to cache its bounding metrics
   898   nsReflowStatus childStatus;
   899   nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
   900   nsIFrame* childFrame = mFrames.FirstChild();
   901   while (childFrame) {
   902     nsHTMLReflowMetrics childDesiredSize(aReflowState, // ???
   903                                          aDesiredSize.mFlags);
   904     nsHTMLReflowState childReflowState(aPresContext, aReflowState,
   905                                        childFrame, availSize);
   906     nsresult rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
   907                               childReflowState, childStatus);
   908     //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
   909     if (NS_FAILED(rv)) {
   910       // Call DidReflow() for the child frames we successfully did reflow.
   911       DidReflowChildren(mFrames.FirstChild(), childFrame);
   912       return rv;
   913     }
   915     SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
   916                                     childDesiredSize.mBoundingMetrics);
   917     childFrame = childFrame->GetNextSibling();
   918   }
   920   /////////////
   921   // If we are a container which is entitled to stretch its children, then we
   922   // ask our stretchy children to stretch themselves
   924   // The stretching of siblings of an embellished child is _deferred_ until
   925   // after finishing the stretching of the embellished child - bug 117652
   927   if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
   928       (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
   929        NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
   931     // get the stretchy direction
   932     nsStretchDirection stretchDir =
   933       NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) 
   934       ? NS_STRETCH_DIRECTION_VERTICAL 
   935       : NS_STRETCH_DIRECTION_HORIZONTAL;
   937     // what size should we use to stretch our stretchy children
   938     // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
   939     // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
   940     // include them in the caculations of the size of stretchy elements
   941     nsBoundingMetrics containerSize;
   942     GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
   943                             containerSize);
   945     // fire the stretch on each child
   946     childFrame = mFrames.FirstChild();
   947     while (childFrame) {
   948       nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
   949       if (mathMLFrame) {
   950         // retrieve the metrics that was stored at the previous pass
   951         nsHTMLReflowMetrics childDesiredSize(aReflowState);
   952         GetReflowAndBoundingMetricsFor(childFrame,
   953           childDesiredSize, childDesiredSize.mBoundingMetrics);
   955         mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
   956                              containerSize, childDesiredSize);
   957         // store the updated metrics
   958         SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
   959                                         childDesiredSize.mBoundingMetrics);
   960       }
   961       childFrame = childFrame->GetNextSibling();
   962     }
   963   }
   965   /////////////
   966   // Place children now by re-adjusting the origins to align the baselines
   967   FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
   969   aStatus = NS_FRAME_COMPLETE;
   970   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   971   return NS_OK;
   972 }
   974 static nscoord AddInterFrameSpacingToSize(nsHTMLReflowMetrics&    aDesiredSize,
   975                                           nsMathMLContainerFrame* aFrame);
   977 /* virtual */ nscoord
   978 nsMathMLContainerFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   979 {
   980   nscoord result;
   981   DISPLAY_MIN_WIDTH(this, result);
   982   nsHTMLReflowMetrics desiredSize(GetWritingMode());
   983   GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
   985   // Include the additional width added by FixInterFrameSpacing to ensure
   986   // consistent width calculations.
   987   AddInterFrameSpacingToSize(desiredSize, this);
   988   result = desiredSize.Width();
   989   return result;
   990 }
   992 /* virtual */ nscoord
   993 nsMathMLContainerFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   994 {
   995   nscoord result;
   996   DISPLAY_PREF_WIDTH(this, result);
   997   nsHTMLReflowMetrics desiredSize(GetWritingMode());
   998   GetIntrinsicWidthMetrics(aRenderingContext, desiredSize);
  1000   // Include the additional width added by FixInterFrameSpacing to ensure
  1001   // consistent width calculations.
  1002   AddInterFrameSpacingToSize(desiredSize, this);
  1003   result = desiredSize.Width();
  1004   return result;
  1007 /* virtual */ void
  1008 nsMathMLContainerFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
  1010   // Get child widths
  1011   nsIFrame* childFrame = mFrames.FirstChild();
  1012   while (childFrame) {
  1013     nsHTMLReflowMetrics childDesiredSize(GetWritingMode()); // ???
  1015     nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
  1016     if (containerFrame) {
  1017       containerFrame->GetIntrinsicWidthMetrics(aRenderingContext,
  1018                                                childDesiredSize);
  1019     } else {
  1020       // XXX This includes margin while Reflow currently doesn't consider
  1021       // margin, so we may end up with too much space, but, with stretchy
  1022       // characters, this is an approximation anyway.
  1023       nscoord width =
  1024         nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
  1025                                              nsLayoutUtils::PREF_WIDTH);
  1027       childDesiredSize.Width() = width;
  1028       childDesiredSize.mBoundingMetrics.width = width;
  1029       childDesiredSize.mBoundingMetrics.leftBearing = 0;
  1030       childDesiredSize.mBoundingMetrics.rightBearing = width;
  1032       nscoord x, xMost;
  1033       if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
  1034                                                            &x, &xMost))) {
  1035         childDesiredSize.mBoundingMetrics.leftBearing = x;
  1036         childDesiredSize.mBoundingMetrics.rightBearing = xMost;
  1040     SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
  1041                                     childDesiredSize.mBoundingMetrics);
  1043     childFrame = childFrame->GetNextSibling();
  1046   // Measure
  1047   nsresult rv = MeasureForWidth(*aRenderingContext, aDesiredSize);
  1048   if (NS_FAILED(rv)) {
  1049     ReflowError(*aRenderingContext, aDesiredSize);
  1052   ClearSavedChildMetrics();
  1055 /* virtual */ nsresult
  1056 nsMathMLContainerFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
  1057                                         nsHTMLReflowMetrics& aDesiredSize)
  1059   return Place(aRenderingContext, false, aDesiredSize);
  1063 // see spacing table in Chapter 18, TeXBook (p.170)
  1064 // Our table isn't quite identical to TeX because operators have 
  1065 // built-in values for lspace & rspace in the Operator Dictionary.
  1066 static int32_t kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
  1068   // in units of muspace.
  1069   // upper half of the byte is set if the
  1070   // spacing is not to be used for scriptlevel > 0
  1072   /*           Ord  OpOrd OpInv OpUsr Inner Italic Upright */
  1073   /*Ord  */   {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
  1074   /*OpOrd*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  1075   /*OpInv*/   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  1076   /*OpUsr*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
  1077   /*Inner*/   {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
  1078   /*Italic*/  {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
  1079   /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}
  1080 };
  1082 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_)  \
  1083    /* no space if there is a frame that we know nothing about */        \
  1084    if (frametype1_ == eMathMLFrameType_UNKNOWN ||                       \
  1085        frametype2_ == eMathMLFrameType_UNKNOWN)                         \
  1086     space_ = 0;                                                         \
  1087   else {                                                                \
  1088     space_ = kInterFrameSpacingTable[frametype1_][frametype2_];         \
  1089     space_ = (scriptlevel_ > 0 && (space_ & 0xF0))                      \
  1090       ? 0 /* spacing is disabled */                                     \
  1091       : space_ & 0x0F;                                                  \
  1092   }                                                                     \
  1094 // This function computes the inter-space between two frames. However, 
  1095 // since invisible operators need special treatment, the inter-space may
  1096 // be delayed when an invisible operator is encountered. In this case,
  1097 // the function will carry the inter-space forward until it is determined
  1098 // that it can be applied properly (i.e., until we encounter a visible
  1099 // frame where to decide whether to accept or reject the inter-space).
  1100 // aFromFrameType: remembers the frame when the carry-forward initiated.
  1101 // aCarrySpace: keeps track of the inter-space that is delayed.
  1102 // @returns: current inter-space (which is 0 when the true inter-space is
  1103 // delayed -- and thus has no effect since the frame is invisible anyway).
  1104 static nscoord
  1105 GetInterFrameSpacing(int32_t           aScriptLevel,
  1106                      eMathMLFrameType  aFirstFrameType,
  1107                      eMathMLFrameType  aSecondFrameType,
  1108                      eMathMLFrameType* aFromFrameType, // IN/OUT
  1109                      int32_t*          aCarrySpace)    // IN/OUT
  1111   eMathMLFrameType firstType = aFirstFrameType;
  1112   eMathMLFrameType secondType = aSecondFrameType;
  1114   int32_t space;
  1115   GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
  1117   // feedback control to avoid the inter-space to be added when not necessary
  1118   if (secondType == eMathMLFrameType_OperatorInvisible) {
  1119     // see if we should start to carry the space forward until we
  1120     // encounter a visible frame
  1121     if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
  1122       *aFromFrameType = firstType;
  1123       *aCarrySpace = space;
  1125     // keep carrying *aCarrySpace forward, while returning 0 for this stage
  1126     space = 0;
  1128   else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
  1129     // no carry-forward anymore, get the real inter-space between
  1130     // the two frames of interest
  1132     firstType = *aFromFrameType;
  1134     // But... the invisible operator that we encountered earlier could
  1135     // be sitting between italic and upright identifiers, e.g.,
  1136     //
  1137     // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
  1138     // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
  1139     //
  1140     // the trick to get the inter-space in either situation
  1141     // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
  1142     // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
  1143     if (firstType == eMathMLFrameType_UprightIdentifier) {
  1144       firstType = eMathMLFrameType_OperatorUserDefined;
  1146     else if (secondType == eMathMLFrameType_UprightIdentifier) {
  1147       secondType = eMathMLFrameType_OperatorUserDefined;
  1150     GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
  1152     // Now, we have two values: the computed space and the space that
  1153     // has been carried forward until now. Which value do we pick?
  1154     // If the second type is an operator (e.g., fence), it already has
  1155     // built-in lspace & rspace, so we let them win. Otherwise we pick
  1156     // the max between the two values that we have.
  1157     if (secondType != eMathMLFrameType_OperatorOrdinary &&
  1158         space < *aCarrySpace)
  1159       space = *aCarrySpace;
  1161     // reset everything now that the carry-forward is done
  1162     *aFromFrameType = eMathMLFrameType_UNKNOWN;
  1163     *aCarrySpace = 0;
  1166   return space;
  1169 static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
  1171   return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
  1174 class nsMathMLContainerFrame::RowChildFrameIterator {
  1175 public:
  1176   explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
  1177     mParentFrame(aParentFrame),
  1178     mSize(aParentFrame->GetWritingMode()), // ???
  1179     mX(0),
  1180     mCarrySpace(0),
  1181     mFromFrameType(eMathMLFrameType_UNKNOWN),
  1182     mRTL(aParentFrame->StyleVisibility()->mDirection)
  1184     if (!mRTL) {
  1185       mChildFrame = aParentFrame->mFrames.FirstChild();
  1186     } else {
  1187       mChildFrame = aParentFrame->mFrames.LastChild();
  1190     if (!mChildFrame)
  1191       return;
  1193     InitMetricsForChild();
  1196   RowChildFrameIterator& operator++()
  1198     // add child size + italic correction
  1199     mX += mSize.mBoundingMetrics.width + mItalicCorrection;
  1201     if (!mRTL) {
  1202       mChildFrame = mChildFrame->GetNextSibling();
  1203     } else {
  1204       mChildFrame = mChildFrame->GetPrevSibling();
  1207     if (!mChildFrame)
  1208       return *this;
  1210     eMathMLFrameType prevFrameType = mChildFrameType;
  1211     InitMetricsForChild();
  1213     // add inter frame spacing
  1214     const nsStyleFont* font = mParentFrame->StyleFont();
  1215     nscoord space =
  1216       GetInterFrameSpacing(font->mScriptLevel,
  1217                            prevFrameType, mChildFrameType,
  1218                            &mFromFrameType, &mCarrySpace);
  1219     mX += space * GetThinSpace(font);
  1220     return *this;
  1223   nsIFrame* Frame() const { return mChildFrame; }
  1224   nscoord X() const { return mX; }
  1225   const nsHTMLReflowMetrics& ReflowMetrics() const { return mSize; }
  1226   nscoord Ascent() const { return mSize.TopAscent(); }
  1227   nscoord Descent() const { return mSize.Height() - mSize.TopAscent(); }
  1228   const nsBoundingMetrics& BoundingMetrics() const {
  1229     return mSize.mBoundingMetrics;
  1232 private:
  1233   const nsMathMLContainerFrame* mParentFrame;
  1234   nsIFrame* mChildFrame;
  1235   nsHTMLReflowMetrics mSize;
  1236   nscoord mX;
  1238   nscoord mItalicCorrection;
  1239   eMathMLFrameType mChildFrameType;
  1240   int32_t mCarrySpace;
  1241   eMathMLFrameType mFromFrameType;
  1243   bool mRTL;
  1245   void InitMetricsForChild()
  1247     GetReflowAndBoundingMetricsFor(mChildFrame, mSize, mSize.mBoundingMetrics,
  1248                                    &mChildFrameType);
  1249     nscoord leftCorrection, rightCorrection;
  1250     GetItalicCorrection(mSize.mBoundingMetrics,
  1251                         leftCorrection, rightCorrection);
  1252     if (!mChildFrame->GetPrevSibling() &&
  1253         mParentFrame->GetContent()->Tag() == nsGkAtoms::msqrt_) {
  1254       // Remove leading correction in <msqrt> because the sqrt glyph itself is
  1255       // there first.
  1256       if (!mRTL) {
  1257         leftCorrection = 0;
  1258       } else {
  1259         rightCorrection = 0;
  1262     // add left correction -- this fixes the problem of the italic 'f'
  1263     // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo> 
  1264     mX += leftCorrection;
  1265     mItalicCorrection = rightCorrection;
  1267 };
  1269 /* virtual */ nsresult
  1270 nsMathMLContainerFrame::Place(nsRenderingContext& aRenderingContext,
  1271                               bool                 aPlaceOrigin,
  1272                               nsHTMLReflowMetrics& aDesiredSize)
  1274   // This is needed in case this frame is empty (i.e., no child frames)
  1275   mBoundingMetrics = nsBoundingMetrics();
  1277   RowChildFrameIterator child(this);
  1278   nscoord ascent = 0, descent = 0;
  1279   while (child.Frame()) {
  1280     if (descent < child.Descent())
  1281       descent = child.Descent();
  1282     if (ascent < child.Ascent())
  1283       ascent = child.Ascent();
  1284     // add the child size
  1285     mBoundingMetrics.width = child.X();
  1286     mBoundingMetrics += child.BoundingMetrics();
  1287     ++child;
  1289   // Add the italic correction at the end (including the last child).
  1290   // This gives a nice gap between math and non-math frames, and still
  1291   // gives the same math inter-spacing in case this frame connects to
  1292   // another math frame
  1293   mBoundingMetrics.width = child.X();
  1295   aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
  1296   aDesiredSize.Height() = ascent + descent;
  1297   aDesiredSize.SetTopAscent(ascent);
  1298   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  1300   mReference.x = 0;
  1301   mReference.y = aDesiredSize.TopAscent();
  1303   //////////////////
  1304   // Place Children
  1306   if (aPlaceOrigin) {
  1307     PositionRowChildFrames(0, aDesiredSize.TopAscent());
  1310   return NS_OK;
  1313 void
  1314 nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
  1315                                                nscoord aBaseline)
  1317   RowChildFrameIterator child(this);
  1318   while (child.Frame()) {
  1319     nscoord dx = aOffsetX + child.X();
  1320     nscoord dy = aBaseline - child.Ascent();
  1321     FinishReflowChild(child.Frame(), PresContext(), child.ReflowMetrics(),
  1322                       nullptr, dx, dy, 0);
  1323     ++child;
  1327 class ForceReflow : public nsIReflowCallback {
  1328 public:
  1329   virtual bool ReflowFinished() MOZ_OVERRIDE {
  1330     return true;
  1332   virtual void ReflowCallbackCanceled() MOZ_OVERRIDE {}
  1333 };
  1335 // We only need one of these so we just make it a static global, no need
  1336 // to dynamically allocate/destroy it.
  1337 static ForceReflow gForceReflow;
  1339 void
  1340 nsMathMLContainerFrame::SetIncrementScriptLevel(int32_t aChildIndex, bool aIncrement)
  1342   nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
  1343   if (!child)
  1344     return;
  1345   nsIContent* content = child->GetContent();
  1346   if (!content->IsMathML())
  1347     return;
  1348   nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
  1350   if (element->GetIncrementScriptLevel() == aIncrement)
  1351     return;
  1353   // XXXroc this does a ContentStatesChanged, is it safe to call here? If
  1354   // not we should do it in a post-reflow callback.
  1355   element->SetIncrementScriptLevel(aIncrement, true);
  1356   PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
  1359 // helpers to fix the inter-spacing when <math> is the only parent
  1360 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
  1362 static nscoord
  1363 GetInterFrameSpacingFor(int32_t         aScriptLevel,
  1364                         nsIFrame*       aParentFrame,
  1365                         nsIFrame*       aChildFrame)
  1367   nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
  1368   if (!childFrame || aChildFrame == childFrame)
  1369     return 0;
  1371   int32_t carrySpace = 0;
  1372   eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
  1373   eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
  1374   eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
  1375   childFrame = childFrame->GetNextSibling();
  1376   while (childFrame) {
  1377     prevFrameType = childFrameType;
  1378     childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
  1379     nscoord space = GetInterFrameSpacing(aScriptLevel,
  1380       prevFrameType, childFrameType, &fromFrameType, &carrySpace);
  1381     if (aChildFrame == childFrame) {
  1382       // get thinspace
  1383       nsStyleContext* parentContext = aParentFrame->StyleContext();
  1384       nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
  1385       // we are done
  1386       return space * thinSpace;
  1388     childFrame = childFrame->GetNextSibling();
  1391   NS_NOTREACHED("child not in the childlist of its parent");
  1392   return 0;
  1395 static nscoord
  1396 AddInterFrameSpacingToSize(nsHTMLReflowMetrics&    aDesiredSize,
  1397                            nsMathMLContainerFrame* aFrame)
  1399   nscoord gap = 0;
  1400   nsIFrame* parent = aFrame->GetParent();
  1401   nsIContent* parentContent = parent->GetContent();
  1402   if (MOZ_UNLIKELY(!parentContent)) {
  1403     return 0;
  1405   nsIAtom *parentTag = parentContent->Tag();
  1406   if (parentContent->GetNameSpaceID() == kNameSpaceID_MathML && 
  1407       (parentTag == nsGkAtoms::math || parentTag == nsGkAtoms::mtd_)) {
  1408     gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mScriptLevel,
  1409                                   parent, aFrame);
  1410     // add our own italic correction
  1411     nscoord leftCorrection = 0, italicCorrection = 0;
  1412     aFrame->GetItalicCorrection(aDesiredSize.mBoundingMetrics,
  1413                                 leftCorrection, italicCorrection);
  1414     gap += leftCorrection;
  1415     if (gap) {
  1416       aDesiredSize.mBoundingMetrics.leftBearing += gap;
  1417       aDesiredSize.mBoundingMetrics.rightBearing += gap;
  1418       aDesiredSize.mBoundingMetrics.width += gap;
  1419       aDesiredSize.Width() += gap;
  1421     aDesiredSize.mBoundingMetrics.width += italicCorrection;
  1422     aDesiredSize.Width() += italicCorrection;
  1424   return gap;
  1427 nscoord
  1428 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
  1430   nscoord gap = 0;
  1431   gap = AddInterFrameSpacingToSize(aDesiredSize, this);
  1432   if (gap) {
  1433     // Shift our children to account for the correction
  1434     nsIFrame* childFrame = mFrames.FirstChild();
  1435     while (childFrame) {
  1436       childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
  1437       childFrame = childFrame->GetNextSibling();
  1440   return gap;
  1443 /* static */ void
  1444 nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
  1447   if (MOZ_UNLIKELY(!aFirst))
  1448     return;
  1450   for (nsIFrame* frame = aFirst;
  1451        frame != aStop;
  1452        frame = frame->GetNextSibling()) {
  1453     NS_ASSERTION(frame, "aStop isn't a sibling");
  1454     if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
  1455       // finish off principal descendants, too
  1456       nsIFrame* grandchild = frame->GetFirstPrincipalChild();
  1457       if (grandchild)
  1458         DidReflowChildren(grandchild, nullptr);
  1460       frame->DidReflow(frame->PresContext(), nullptr,
  1461                        nsDidReflowStatus::FINISHED);
  1466 // helper used by mstyle, mphantom, mpadded and mrow in their implementations
  1467 // of TransmitAutomaticData().
  1468 nsresult
  1469 nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement()
  1471   //
  1472   // One loop to check both conditions below:
  1473   //
  1474   // 1) whether all the children of the mrow-like element are space-like.
  1475   //
  1476   //   The REC defines the following elements to be "space-like":
  1477   //   * an mstyle, mphantom, or mpadded element, all of whose direct
  1478   //     sub-expressions are space-like;
  1479   //   * an mrow all of whose direct sub-expressions are space-like.
  1480   //
  1481   // 2) whether all but one child of the mrow-like element are space-like and
  1482   //    this non-space-like child is an embellished operator.
  1483   //
  1484   //   The REC defines the following elements to be embellished operators:
  1485   //   * one of the elements mstyle, mphantom, or mpadded, such that an mrow
  1486   //     containing the same arguments would be an embellished operator;
  1487   //   * an mrow whose arguments consist (in any order) of one embellished
  1488   //     operator and zero or more space-like elements.
  1489   //
  1490   nsIFrame *childFrame, *baseFrame;
  1491   bool embellishedOpFound = false;
  1492   nsEmbellishData embellishData;
  1494   for (childFrame = GetFirstPrincipalChild();
  1495        childFrame;
  1496        childFrame = childFrame->GetNextSibling()) {
  1497     nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
  1498     if (!mathMLFrame) break;
  1499     if (!mathMLFrame->IsSpaceLike()) {
  1500       if (embellishedOpFound) break;
  1501       baseFrame = childFrame;
  1502       GetEmbellishDataFrom(baseFrame, embellishData);
  1503       if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)) break;
  1504       embellishedOpFound = true;
  1508   if (!childFrame) {
  1509     // we successfully went to the end of the loop. This means that one of
  1510     // condition 1) or 2) holds.
  1511     if (!embellishedOpFound) {
  1512       // the mrow-like element is space-like.
  1513       mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
  1514     } else {
  1515       // the mrow-like element is an embellished operator.
  1516       // let the state of the embellished operator found bubble to us.
  1517       mPresentationData.baseFrame = baseFrame;
  1518       mEmbellishData = embellishData;
  1522   if (childFrame || !embellishedOpFound) {
  1523     // The element is not embellished operator
  1524     mPresentationData.baseFrame = nullptr;
  1525     mEmbellishData.flags = 0;
  1526     mEmbellishData.coreFrame = nullptr;
  1527     mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
  1528     mEmbellishData.leadingSpace = 0;
  1529     mEmbellishData.trailingSpace = 0;
  1532   if (childFrame || embellishedOpFound) {
  1533     // The element is not space-like
  1534     mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
  1537   return NS_OK;
  1540 /*static*/ void
  1541 nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame,
  1542                                               nsFrameState  aFlags)
  1544   if (!aFrame || !aFlags)
  1545     return;
  1547   aFrame->AddStateBits(aFlags);
  1548   nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
  1549   while (childFrame) {
  1550     PropagateFrameFlagFor(childFrame, aFlags);
  1551     childFrame = childFrame->GetNextSibling();
  1555 nsresult
  1556 nsMathMLContainerFrame::ReportErrorToConsole(const char*       errorMsgId,
  1557                                              const char16_t** aParams,
  1558                                              uint32_t          aParamCount)
  1560   return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
  1561                                          NS_LITERAL_CSTRING("MathML"), mContent->OwnerDoc(),
  1562                                          nsContentUtils::eMATHML_PROPERTIES,
  1563                                          errorMsgId, aParams, aParamCount);
  1566 nsresult
  1567 nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute,
  1568                                          const char16_t* aValue)
  1570   const char16_t* argv[] = 
  1571     { aValue, aAttribute, mContent->Tag()->GetUTF16String() };
  1572   return ReportErrorToConsole("AttributeParsingError", argv, 3);
  1575 nsresult
  1576 nsMathMLContainerFrame::ReportChildCountError()
  1578   const char16_t* arg = mContent->Tag()->GetUTF16String();
  1579   return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
  1582 nsresult
  1583 nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom* aChildTag)
  1585   const char16_t* argv[] =
  1586     { aChildTag->GetUTF16String(), mContent->Tag()->GetUTF16String() };
  1587   return ReportErrorToConsole("InvalidChild", argv, 2);
  1590 //==========================
  1592 nsIFrame*
  1593 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
  1594                            nsFrameState aFlags)
  1596   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
  1597   it->SetFlags(aFlags);
  1598   return it;
  1601 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
  1603 NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
  1604   NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
  1605 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
  1607 nsIFrame*
  1608 NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1610   return new (aPresShell) nsMathMLmathInlineFrame(aContext);
  1613 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
  1615 NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
  1616   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
  1617 NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)

mercurial