Files
firefox/layout/mathml/nsMathMLTokenFrame.cpp
T
Frédéric Wang 8a08b8f2c0 Bug 1908069 - Introduce PlaceFlags parameter to MathML layout methods. r=emilio
MathML classes has a few Place*() methods  used for layout and intrinsic
size calculation. These methods have a parameter "aPlaceOrigin"
indicating whether the children and other painted objects should have
their final positions set, or if the method is only called for measuring.
This parameter is typically set to false when doing intrinsic size
calculation or when performing some non-final measurement for operator
stretching.

For intrinsic size calculation, it is generally enough to perform
placement with aPlaceOrigin=false and ignoring the vertical metrics.
Some Place*() methods also have a parameter "aWidthOnly" for special
handling. For example, msqrt stretches a radical symbol vertically to
match the height of the content but when doing intrinsic size
calculation this is approximated to nsMathMLChar::GetMaxWidth() instead.
When we implement border/padding/margin we should also be able to
choose between using IntrinsicISizeOffsets() or GetUsed*() methods.

Finally, some Place*() methods initially call a place routing of a
parent class before further tweaking the layout. For example, msqrt
uses mrow layout on children and adds some radical symbol on top of
them. When we implement border/padding, we should make sure we don't
add the border/padding before the final result.

In order to handle all these placement behaviors, a new EnumSet is
introduced. This commit does not change behavior. Handling of padding,
border and margin will be handled in follow-up patches.

Differential Revision: https://phabricator.services.mozilla.com/D216669
2024-08-07 06:00:43 +00:00

196 lines
7.4 KiB
C++

/* -*- 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/. */
#include "nsMathMLTokenFrame.h"
#include "mozilla/PresShell.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsContentUtils.h"
#include "nsTextFrame.h"
#include "gfxContext.h"
#include <algorithm>
using namespace mozilla;
nsIFrame* NS_NewMathMLTokenFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell)
nsMathMLTokenFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame)
nsMathMLTokenFrame::~nsMathMLTokenFrame() = default;
NS_IMETHODIMP
nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) {
// let the base class get the default from our parent
nsMathMLContainerFrame::InheritAutomaticData(aParent);
return NS_OK;
}
eMathMLFrameType nsMathMLTokenFrame::GetMathMLFrameType() {
// treat everything other than <mi> as ordinary...
if (!mContent->IsMathMLElement(nsGkAtoms::mi_)) {
return eMathMLFrameType_Ordinary;
}
StyleMathVariant mathVariant = StyleFont()->mMathVariant;
if ((mathVariant == StyleMathVariant::None &&
(StyleFont()->mFont.style.IsItalic() ||
HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) ||
mathVariant == StyleMathVariant::Italic ||
mathVariant == StyleMathVariant::BoldItalic ||
mathVariant == StyleMathVariant::SansSerifItalic ||
mathVariant == StyleMathVariant::SansSerifBoldItalic) {
return eMathMLFrameType_ItalicIdentifier;
}
return eMathMLFrameType_UprightIdentifier;
}
void nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() {
nsIFrame* child = nullptr;
uint32_t childCount = 0;
// Set flags on child text frames
// - to force them to trim their leading and trailing whitespaces.
// - Indicate which frames are suitable for mathvariant
// - flag single character <mi> frames for special italic treatment
for (nsIFrame* childFrame = PrincipalChildList().FirstChild(); childFrame;
childFrame = childFrame->GetNextSibling()) {
for (nsIFrame* childFrame2 = childFrame->PrincipalChildList().FirstChild();
childFrame2; childFrame2 = childFrame2->GetNextSibling()) {
if (childFrame2->IsTextFrame()) {
childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
child = childFrame2;
childCount++;
}
}
}
if (mContent->IsMathMLElement(nsGkAtoms::mi_) && childCount == 1) {
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, false, data);
data.CompressWhitespace();
int32_t length = data.Length();
bool isSingleCharacter =
length == 1 || (length == 2 && NS_IS_HIGH_SURROGATE(data[0]));
if (isSingleCharacter) {
child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
}
}
}
void nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
nsFrameList&& aChildList) {
// First, let the base class do its work
nsMathMLContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
MarkTextFramesAsTokenMathML();
}
void nsMathMLTokenFrame::AppendFrames(ChildListID aListID,
nsFrameList&& aChildList) {
nsMathMLContainerFrame::AppendFrames(aListID, std::move(aChildList));
MarkTextFramesAsTokenMathML();
}
void nsMathMLTokenFrame::InsertFrames(
ChildListID aListID, nsIFrame* aPrevFrame,
const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aChildList) {
nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
std::move(aChildList));
MarkTextFramesAsTokenMathML();
}
void nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) {
MarkInReflow();
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
// initializations needed for empty markup like <mtag></mtag>
aDesiredSize.ClearSize();
aDesiredSize.SetBlockStartAscent(0);
aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
for (nsIFrame* childFrame : PrincipalChildList()) {
// ask our children to compute their bounding metrics
ReflowOutput childDesiredSize(aReflowInput.GetWritingMode());
WritingMode wm = childFrame->GetWritingMode();
LogicalSize availSize = aReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
availSize);
nsReflowStatus childStatus;
ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
childStatus);
NS_ASSERTION(childStatus.IsComplete(),
"We gave the child unconstrained available block-size, so its "
"status should be complete!");
SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
childDesiredSize.mBoundingMetrics);
}
// place and size children
FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize);
aStatus.Reset(); // This type of frame can't be split.
}
// For token elements, mBoundingMetrics is computed at the ReflowToken
// pass, it is not computed here because our children may be text frames
// that do not implement the GetBoundingMetrics() interface.
/* virtual */
nsresult nsMathMLTokenFrame::Place(DrawTarget* aDrawTarget,
const PlaceFlags& aFlags,
ReflowOutput& aDesiredSize) {
mBoundingMetrics = nsBoundingMetrics();
for (nsIFrame* childFrame : PrincipalChildList()) {
ReflowOutput childSize(aDesiredSize.GetWritingMode());
GetReflowAndBoundingMetricsFor(childFrame, childSize,
childSize.mBoundingMetrics, nullptr);
// compute and cache the bounding metrics
mBoundingMetrics += childSize.mBoundingMetrics;
}
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
nscoord ascent = fm->MaxAscent();
nscoord descent = fm->MaxDescent();
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
aDesiredSize.Width() = mBoundingMetrics.width;
aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, ascent));
aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
std::max(mBoundingMetrics.descent, descent);
if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
nscoord dy, dx = 0;
for (nsIFrame* childFrame : PrincipalChildList()) {
ReflowOutput childSize(aDesiredSize.GetWritingMode());
GetReflowAndBoundingMetricsFor(childFrame, childSize,
childSize.mBoundingMetrics);
// place and size the child; (dx,0) makes the caret happy - bug 188146
dy = childSize.Height() == 0
? 0
: aDesiredSize.BlockStartAscent() - childSize.BlockStartAscent();
FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy,
ReflowChildFlags::Default);
dx += childSize.Width();
}
}
SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
return NS_OK;
}