/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
/* ///////////// * nsIMathMLFrame - support methods for stretchy elements * =============================================================================
*/
staticbool IsForeignChild(const nsIFrame* aFrame) { // This counts nsMathMLmathBlockFrame as a foreign child, because it // uses block reflow return !aFrame->IsMathMLFrame() || aFrame->IsBlockFrame();
}
// 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;
}
}
if (aFlags.contains(PlaceFlag::IntrinsicSize)) { // Bug 1910859: Should we provide separate left and right border/padding? return nsMargin(0, IntrinsicISizeOffsets().BorderPadding(), 0, 0);
}
return GetUsedBorderAndPadding();
}
/* static */
nsMargin nsMathMLContainerFrame::GetMarginForPlace(const PlaceFlags& aFlags,
nsIFrame* aChild) { if (aFlags.contains(PlaceFlag::IntrinsicSize)) { // Bug 1910859: Should we provide separate left and right margin? return nsMargin(0, aChild->IntrinsicISizeOffsets().margin, 0, 0);
}
return aChild->GetUsedMargin();
}
void nsMathMLContainerFrame::InflateReflowAndBoundingMetrics( const nsMargin& aBorderPadding, ReflowOutput& aReflowOutput,
nsBoundingMetrics& aBoundingMetrics) { // Bug 1910858: It is not really clear what is the right way to update the // ink bounding box when adding border or padding. Below, we assume that // border/padding inflate it.
aBoundingMetrics.rightBearing += aBorderPadding.LeftRight();
aBoundingMetrics.width += aBorderPadding.LeftRight();
aReflowOutput.mBoundingMetrics = aBoundingMetrics;
aReflowOutput.Width() += aBorderPadding.LeftRight();
aReflowOutput.SetBlockStartAscent(aReflowOutput.BlockStartAscent() +
aBorderPadding.top);
aReflowOutput.Height() += aBorderPadding.TopBottom();
}
// 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;
} elseif (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) { // compute our up-to-date size using Place(), without border/padding.
ReflowOutput reflowOutput(GetWritingMode());
PlaceFlags flags(PlaceFlag::MeasureOnly, PlaceFlag::IgnoreBorderPadding);
Place(aDrawTarget, flags, 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;
}
} elseif (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;
// 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
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
PlaceFlags flags;
nsresult rv = Place(aDrawTarget, flags, aDesiredStretchSize); if (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);
// 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);
PlaceFlags flags; if (!placeOrigin) {
flags += PlaceFlag::MeasureOnly;
}
nsresult rv = Place(aDrawTarget, flags, 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 // PlaceFlag::MeasureOnly unset through Stretch() eventually. if (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!
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->HasAnyStateBits(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);
}
}
}
// 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();
}
}
// 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;
}
// 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);
}
// 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(
OverflowAreas& 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 ink 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
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 nsIFrame::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();
}
}
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);
}
// Include the additional width added by FixInterFrameSpacing to ensure // consistent width calculations.
AddInterFrameSpacingToSize(desiredSize, this);
// ReflowOuput::mSize corresponds to the border box, but callers // expect padding/border are not included.
mIntrinsicISize = desiredSize.ISize(GetWritingMode()) -
IntrinsicISizeOffsets().BorderPadding();
}
}
// 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
#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;
// 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;
} elseif (*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;
} elseif (secondType == eMathMLFrameType_UprightIdentifier) {
secondType = eMathMLFrameType_OperatorUserDefined;
}
// 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;
}
void InitMetricsForChild() {
GetReflowAndBoundingMetricsFor(mChildFrame, mReflowOutput,
mReflowOutput.mBoundingMetrics,
&mChildFrameType);
mMargin = GetMarginForPlace(mFlags, mChildFrame);
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, const PlaceFlags& aFlags,
ReflowOutput& aDesiredSize) { // This is needed in case this frame is empty (i.e., no child frames)
mBoundingMetrics = nsBoundingMetrics();
// 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();
////////////////// // Place Children if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
PositionRowChildFrames(shiftX, aDesiredSize.BlockStartAscent());
}
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) { for (nsIFrame* frame = aFirst; frame; frame = frame->GetNextSibling()) { if (!frame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) { continue;
} if (nsIFrame* grandchild = frame->PrincipalChildList().FirstChild()) { // Finish off principal descendants, too
DidReflowChildren(grandchild);
}
frame->DidReflow(frame->PresContext(), nullptr);
}
}
// 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.