1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsMathMLTokenFrame.h"
9 #include "mozilla/PresShell.h"
10 #include "nsLayoutUtils.h"
11 #include "nsPresContext.h"
12 #include "nsContentUtils.h"
13 #include "nsTextFrame.h"
14 #include "gfxContext.h"
17 using namespace mozilla
;
19 nsIFrame
* NS_NewMathMLTokenFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
20 return new (aPresShell
)
21 nsMathMLTokenFrame(aStyle
, aPresShell
->GetPresContext());
24 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame
)
26 nsMathMLTokenFrame::~nsMathMLTokenFrame() = default;
29 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame
* aParent
) {
30 // let the base class get the default from our parent
31 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
36 eMathMLFrameType
nsMathMLTokenFrame::GetMathMLFrameType() {
37 // treat everything other than <mi> as ordinary...
38 if (!mContent
->IsMathMLElement(nsGkAtoms::mi_
)) {
39 return eMathMLFrameType_Ordinary
;
42 StyleMathVariant mathVariant
= StyleFont()->mMathVariant
;
43 if ((mathVariant
== StyleMathVariant::None
&&
44 (StyleFont()->mFont
.style
.IsItalic() ||
45 HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI
))) ||
46 mathVariant
== StyleMathVariant::Italic
||
47 mathVariant
== StyleMathVariant::BoldItalic
||
48 mathVariant
== StyleMathVariant::SansSerifItalic
||
49 mathVariant
== StyleMathVariant::SansSerifBoldItalic
) {
50 return eMathMLFrameType_ItalicIdentifier
;
52 return eMathMLFrameType_UprightIdentifier
;
55 void nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() {
56 nsIFrame
* child
= nullptr;
57 uint32_t childCount
= 0;
59 // Set flags on child text frames
60 // - to force them to trim their leading and trailing whitespaces.
61 // - Indicate which frames are suitable for mathvariant
62 // - flag single character <mi> frames for special italic treatment
63 for (nsIFrame
* childFrame
= PrincipalChildList().FirstChild(); childFrame
;
64 childFrame
= childFrame
->GetNextSibling()) {
65 for (nsIFrame
* childFrame2
= childFrame
->PrincipalChildList().FirstChild();
66 childFrame2
; childFrame2
= childFrame2
->GetNextSibling()) {
67 if (childFrame2
->IsTextFrame()) {
68 childFrame2
->AddStateBits(TEXT_IS_IN_TOKEN_MATHML
);
74 if (mContent
->IsMathMLElement(nsGkAtoms::mi_
) && childCount
== 1) {
76 nsContentUtils::GetNodeTextContent(mContent
, false, data
);
78 data
.CompressWhitespace();
79 int32_t length
= data
.Length();
81 bool isSingleCharacter
=
82 length
== 1 || (length
== 2 && NS_IS_HIGH_SURROGATE(data
[0]));
84 if (isSingleCharacter
) {
85 child
->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI
);
86 AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI
);
91 void nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID
,
92 nsFrameList
&& aChildList
) {
93 // First, let the base class do its work
94 nsMathMLContainerFrame::SetInitialChildList(aListID
, std::move(aChildList
));
95 MarkTextFramesAsTokenMathML();
98 void nsMathMLTokenFrame::AppendFrames(ChildListID aListID
,
99 nsFrameList
&& aChildList
) {
100 nsMathMLContainerFrame::AppendFrames(aListID
, std::move(aChildList
));
101 MarkTextFramesAsTokenMathML();
104 void nsMathMLTokenFrame::InsertFrames(
105 ChildListID aListID
, nsIFrame
* aPrevFrame
,
106 const nsLineList::iterator
* aPrevFrameLine
, nsFrameList
&& aChildList
) {
107 nsMathMLContainerFrame::InsertFrames(aListID
, aPrevFrame
, aPrevFrameLine
,
108 std::move(aChildList
));
109 MarkTextFramesAsTokenMathML();
112 void nsMathMLTokenFrame::Reflow(nsPresContext
* aPresContext
,
113 ReflowOutput
& aDesiredSize
,
114 const ReflowInput
& aReflowInput
,
115 nsReflowStatus
& aStatus
) {
117 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
119 mPresentationData
.flags
&= ~NS_MATHML_ERROR
;
121 // initializations needed for empty markup like <mtag></mtag>
122 aDesiredSize
.ClearSize();
123 aDesiredSize
.SetBlockStartAscent(0);
124 aDesiredSize
.mBoundingMetrics
= nsBoundingMetrics();
126 for (nsIFrame
* childFrame
: PrincipalChildList()) {
127 // ask our children to compute their bounding metrics
128 ReflowOutput
childDesiredSize(aReflowInput
.GetWritingMode());
129 WritingMode wm
= childFrame
->GetWritingMode();
130 LogicalSize availSize
= aReflowInput
.ComputedSize(wm
);
131 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
132 ReflowInput
childReflowInput(aPresContext
, aReflowInput
, childFrame
,
134 nsReflowStatus childStatus
;
135 ReflowChild(childFrame
, aPresContext
, childDesiredSize
, childReflowInput
,
137 NS_ASSERTION(childStatus
.IsComplete(),
138 "We gave the child unconstrained available block-size, so its "
139 "status should be complete!");
140 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
141 childDesiredSize
.mBoundingMetrics
);
144 // place and size children
145 FinalizeReflow(aReflowInput
.mRenderingContext
->GetDrawTarget(), aDesiredSize
);
147 aStatus
.Reset(); // This type of frame can't be split.
150 // For token elements, mBoundingMetrics is computed at the ReflowToken
151 // pass, it is not computed here because our children may be text frames
152 // that do not implement the GetBoundingMetrics() interface.
154 nsresult
nsMathMLTokenFrame::Place(DrawTarget
* aDrawTarget
, bool aPlaceOrigin
,
155 ReflowOutput
& aDesiredSize
) {
156 mBoundingMetrics
= nsBoundingMetrics();
157 for (nsIFrame
* childFrame
: PrincipalChildList()) {
158 ReflowOutput
childSize(aDesiredSize
.GetWritingMode());
159 GetReflowAndBoundingMetricsFor(childFrame
, childSize
,
160 childSize
.mBoundingMetrics
, nullptr);
161 // compute and cache the bounding metrics
162 mBoundingMetrics
+= childSize
.mBoundingMetrics
;
165 RefPtr
<nsFontMetrics
> fm
=
166 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
167 nscoord ascent
= fm
->MaxAscent();
168 nscoord descent
= fm
->MaxDescent();
170 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
171 aDesiredSize
.Width() = mBoundingMetrics
.width
;
172 aDesiredSize
.SetBlockStartAscent(std::max(mBoundingMetrics
.ascent
, ascent
));
173 aDesiredSize
.Height() = aDesiredSize
.BlockStartAscent() +
174 std::max(mBoundingMetrics
.descent
, descent
);
178 for (nsIFrame
* childFrame
: PrincipalChildList()) {
179 ReflowOutput
childSize(aDesiredSize
.GetWritingMode());
180 GetReflowAndBoundingMetricsFor(childFrame
, childSize
,
181 childSize
.mBoundingMetrics
);
183 // place and size the child; (dx,0) makes the caret happy - bug 188146
184 dy
= childSize
.Height() == 0
186 : aDesiredSize
.BlockStartAscent() - childSize
.BlockStartAscent();
187 FinishReflowChild(childFrame
, PresContext(), childSize
, nullptr, dx
, dy
,
188 ReflowChildFlags::Default
);
189 dx
+= childSize
.Width();
193 SetReference(nsPoint(0, aDesiredSize
.BlockStartAscent()));