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