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/. */
6
7 #include "nsMathMLTokenFrame.h"
8
9 #include "mozilla/PresShell.h"
10 #include "nsPresContext.h"
11 #include "nsContentUtils.h"
12 #include "nsTextFrame.h"
13 #include <algorithm>
14
15 using namespace mozilla;
16
NS_NewMathMLTokenFrame(PresShell * aPresShell,ComputedStyle * aStyle)17 nsIFrame* NS_NewMathMLTokenFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
18 return new (aPresShell)
19 nsMathMLTokenFrame(aStyle, aPresShell->GetPresContext());
20 }
21
22 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame)
23
24 nsMathMLTokenFrame::~nsMathMLTokenFrame() = default;
25
26 NS_IMETHODIMP
InheritAutomaticData(nsIFrame * aParent)27 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) {
28 // let the base class get the default from our parent
29 nsMathMLContainerFrame::InheritAutomaticData(aParent);
30
31 return NS_OK;
32 }
33
GetMathMLFrameType()34 eMathMLFrameType nsMathMLTokenFrame::GetMathMLFrameType() {
35 // treat everything other than <mi> as ordinary...
36 if (!mContent->IsMathMLElement(nsGkAtoms::mi_)) {
37 return eMathMLFrameType_Ordinary;
38 }
39
40 uint8_t mathVariant = StyleFont()->mMathVariant;
41 if ((mathVariant == NS_MATHML_MATHVARIANT_NONE &&
42 (StyleFont()->mFont.style == FontSlantStyle::Italic() ||
43 HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) ||
44 mathVariant == NS_MATHML_MATHVARIANT_ITALIC ||
45 mathVariant == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
46 mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC ||
47 mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC) {
48 return eMathMLFrameType_ItalicIdentifier;
49 }
50 return eMathMLFrameType_UprightIdentifier;
51 }
52
MarkTextFramesAsTokenMathML()53 void nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() {
54 nsIFrame* child = nullptr;
55 uint32_t childCount = 0;
56
57 // Set flags on child text frames
58 // - to force them to trim their leading and trailing whitespaces.
59 // - Indicate which frames are suitable for mathvariant
60 // - flag single character <mi> frames for special italic treatment
61 for (nsIFrame* childFrame = PrincipalChildList().FirstChild(); childFrame;
62 childFrame = childFrame->GetNextSibling()) {
63 for (nsIFrame* childFrame2 = childFrame->PrincipalChildList().FirstChild();
64 childFrame2; childFrame2 = childFrame2->GetNextSibling()) {
65 if (childFrame2->IsTextFrame()) {
66 childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
67 child = childFrame2;
68 childCount++;
69 }
70 }
71 }
72 if (mContent->IsMathMLElement(nsGkAtoms::mi_) && childCount == 1) {
73 nsAutoString data;
74 nsContentUtils::GetNodeTextContent(mContent, false, data);
75
76 data.CompressWhitespace();
77 int32_t length = data.Length();
78
79 bool isSingleCharacter =
80 length == 1 || (length == 2 && NS_IS_HIGH_SURROGATE(data[0]));
81
82 if (isSingleCharacter) {
83 child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
84 AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
85 }
86 }
87 }
88
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)89 void nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
90 nsFrameList& aChildList) {
91 // First, let the base class do its work
92 nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
93 MarkTextFramesAsTokenMathML();
94 }
95
AppendFrames(ChildListID aListID,nsFrameList & aChildList)96 void nsMathMLTokenFrame::AppendFrames(ChildListID aListID,
97 nsFrameList& aChildList) {
98 nsMathMLContainerFrame::AppendFrames(aListID, aChildList);
99 MarkTextFramesAsTokenMathML();
100 }
101
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aChildList)102 void nsMathMLTokenFrame::InsertFrames(
103 ChildListID aListID, nsIFrame* aPrevFrame,
104 const nsLineList::iterator* aPrevFrameLine, nsFrameList& aChildList) {
105 nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
106 aChildList);
107 MarkTextFramesAsTokenMathML();
108 }
109
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)110 void nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
111 ReflowOutput& aDesiredSize,
112 const ReflowInput& aReflowInput,
113 nsReflowStatus& aStatus) {
114 MarkInReflow();
115 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
116
117 mPresentationData.flags &= ~NS_MATHML_ERROR;
118
119 // initializations needed for empty markup like <mtag></mtag>
120 aDesiredSize.ClearSize();
121 aDesiredSize.SetBlockStartAscent(0);
122 aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
123
124 for (nsIFrame* childFrame : PrincipalChildList()) {
125 // ask our children to compute their bounding metrics
126 ReflowOutput childDesiredSize(aReflowInput.GetWritingMode());
127 WritingMode wm = childFrame->GetWritingMode();
128 LogicalSize availSize = aReflowInput.ComputedSize(wm);
129 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
130 ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
131 availSize);
132 ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
133 aStatus);
134 // NS_ASSERTION(aStatus.IsComplete(), "bad status");
135 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
136 childDesiredSize.mBoundingMetrics);
137 }
138
139 // place and size children
140 FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize);
141
142 aStatus.Reset(); // This type of frame can't be split.
143 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
144 }
145
146 // For token elements, mBoundingMetrics is computed at the ReflowToken
147 // pass, it is not computed here because our children may be text frames
148 // that do not implement the GetBoundingMetrics() interface.
149 /* virtual */
Place(DrawTarget * aDrawTarget,bool aPlaceOrigin,ReflowOutput & aDesiredSize)150 nsresult nsMathMLTokenFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin,
151 ReflowOutput& aDesiredSize) {
152 mBoundingMetrics = nsBoundingMetrics();
153 for (nsIFrame* childFrame : PrincipalChildList()) {
154 ReflowOutput childSize(aDesiredSize.GetWritingMode());
155 GetReflowAndBoundingMetricsFor(childFrame, childSize,
156 childSize.mBoundingMetrics, nullptr);
157 // compute and cache the bounding metrics
158 mBoundingMetrics += childSize.mBoundingMetrics;
159 }
160
161 RefPtr<nsFontMetrics> fm =
162 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
163 nscoord ascent = fm->MaxAscent();
164 nscoord descent = fm->MaxDescent();
165
166 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
167 aDesiredSize.Width() = mBoundingMetrics.width;
168 aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, ascent));
169 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
170 std::max(mBoundingMetrics.descent, descent);
171
172 if (aPlaceOrigin) {
173 nscoord dy, dx = 0;
174 for (nsIFrame* childFrame : PrincipalChildList()) {
175 ReflowOutput childSize(aDesiredSize.GetWritingMode());
176 GetReflowAndBoundingMetricsFor(childFrame, childSize,
177 childSize.mBoundingMetrics);
178
179 // place and size the child; (dx,0) makes the caret happy - bug 188146
180 dy = childSize.Height() == 0
181 ? 0
182 : aDesiredSize.BlockStartAscent() - childSize.BlockStartAscent();
183 FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy,
184 ReflowChildFlags::Default);
185 dx += childSize.Width();
186 }
187 }
188
189 SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
190
191 return NS_OK;
192 }
193