|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsMathMLTokenFrame.h" |
|
7 #include "nsPresContext.h" |
|
8 #include "nsContentUtils.h" |
|
9 #include "nsTextFrame.h" |
|
10 #include "RestyleManager.h" |
|
11 #include <algorithm> |
|
12 |
|
13 nsIFrame* |
|
14 NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
15 { |
|
16 return new (aPresShell) nsMathMLTokenFrame(aContext); |
|
17 } |
|
18 |
|
19 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame) |
|
20 |
|
21 nsMathMLTokenFrame::~nsMathMLTokenFrame() |
|
22 { |
|
23 } |
|
24 |
|
25 NS_IMETHODIMP |
|
26 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) |
|
27 { |
|
28 // let the base class get the default from our parent |
|
29 nsMathMLContainerFrame::InheritAutomaticData(aParent); |
|
30 |
|
31 return NS_OK; |
|
32 } |
|
33 |
|
34 eMathMLFrameType |
|
35 nsMathMLTokenFrame::GetMathMLFrameType() |
|
36 { |
|
37 // treat everything other than <mi> as ordinary... |
|
38 if (mContent->Tag() != nsGkAtoms::mi_) { |
|
39 return eMathMLFrameType_Ordinary; |
|
40 } |
|
41 |
|
42 uint8_t mathVariant = StyleFont()->mMathVariant; |
|
43 if ((mathVariant == NS_MATHML_MATHVARIANT_NONE && |
|
44 (StyleFont()->mFont.style == NS_STYLE_FONT_STYLE_ITALIC || |
|
45 HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) || |
|
46 mathVariant == NS_MATHML_MATHVARIANT_ITALIC || |
|
47 mathVariant == NS_MATHML_MATHVARIANT_BOLD_ITALIC || |
|
48 mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC || |
|
49 mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC) { |
|
50 return eMathMLFrameType_ItalicIdentifier; |
|
51 } |
|
52 return eMathMLFrameType_UprightIdentifier; |
|
53 } |
|
54 |
|
55 void |
|
56 nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() |
|
57 { |
|
58 nsIFrame* child = nullptr; |
|
59 uint32_t childCount = 0; |
|
60 |
|
61 // Set flags on child text frames |
|
62 // - to force them to trim their leading and trailing whitespaces. |
|
63 // - Indicate which frames are suitable for mathvariant |
|
64 // - flag single character <mi> frames for special italic treatment |
|
65 for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; |
|
66 childFrame = childFrame->GetNextSibling()) { |
|
67 for (nsIFrame* childFrame2 = childFrame->GetFirstPrincipalChild(); |
|
68 childFrame2; childFrame2 = childFrame2->GetNextSibling()) { |
|
69 if (childFrame2->GetType() == nsGkAtoms::textFrame) { |
|
70 childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML); |
|
71 child = childFrame2; |
|
72 childCount++; |
|
73 } |
|
74 } |
|
75 } |
|
76 if (mContent->Tag() == nsGkAtoms::mi_ && childCount == 1) { |
|
77 nsAutoString data; |
|
78 if (!nsContentUtils::GetNodeTextContent(mContent, false, data)) { |
|
79 NS_RUNTIMEABORT("OOM"); |
|
80 } |
|
81 |
|
82 data.CompressWhitespace(); |
|
83 int32_t length = data.Length(); |
|
84 |
|
85 bool isSingleCharacter = length == 1 || |
|
86 (length == 2 && NS_IS_HIGH_SURROGATE(data[0])); |
|
87 |
|
88 if (isSingleCharacter) { |
|
89 child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); |
|
90 AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); |
|
91 } |
|
92 } |
|
93 } |
|
94 |
|
95 nsresult |
|
96 nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID, |
|
97 nsFrameList& aChildList) |
|
98 { |
|
99 // First, let the base class do its work |
|
100 nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList); |
|
101 if (NS_FAILED(rv)) |
|
102 return rv; |
|
103 |
|
104 MarkTextFramesAsTokenMathML(); |
|
105 |
|
106 return rv; |
|
107 } |
|
108 |
|
109 nsresult |
|
110 nsMathMLTokenFrame::AppendFrames(ChildListID aListID, |
|
111 nsFrameList& aChildList) |
|
112 { |
|
113 nsresult rv = nsMathMLContainerFrame::AppendFrames(aListID, aChildList); |
|
114 if (NS_FAILED(rv)) |
|
115 return rv; |
|
116 |
|
117 MarkTextFramesAsTokenMathML(); |
|
118 |
|
119 return rv; |
|
120 } |
|
121 |
|
122 nsresult |
|
123 nsMathMLTokenFrame::InsertFrames(ChildListID aListID, |
|
124 nsIFrame* aPrevFrame, |
|
125 nsFrameList& aChildList) |
|
126 { |
|
127 nsresult rv = nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, |
|
128 aChildList); |
|
129 if (NS_FAILED(rv)) |
|
130 return rv; |
|
131 |
|
132 MarkTextFramesAsTokenMathML(); |
|
133 |
|
134 return rv; |
|
135 } |
|
136 |
|
137 nsresult |
|
138 nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext, |
|
139 nsHTMLReflowMetrics& aDesiredSize, |
|
140 const nsHTMLReflowState& aReflowState, |
|
141 nsReflowStatus& aStatus) |
|
142 { |
|
143 nsresult rv = NS_OK; |
|
144 |
|
145 // initializations needed for empty markup like <mtag></mtag> |
|
146 aDesiredSize.Width() = aDesiredSize.Height() = 0; |
|
147 aDesiredSize.SetTopAscent(0); |
|
148 aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); |
|
149 |
|
150 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); |
|
151 nsIFrame* childFrame = GetFirstPrincipalChild(); |
|
152 while (childFrame) { |
|
153 // ask our children to compute their bounding metrics |
|
154 nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), |
|
155 aDesiredSize.mFlags |
|
156 | NS_REFLOW_CALC_BOUNDING_METRICS); |
|
157 nsHTMLReflowState childReflowState(aPresContext, aReflowState, |
|
158 childFrame, availSize); |
|
159 rv = ReflowChild(childFrame, aPresContext, childDesiredSize, |
|
160 childReflowState, aStatus); |
|
161 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); |
|
162 if (NS_FAILED(rv)) { |
|
163 // Call DidReflow() for the child frames we successfully did reflow. |
|
164 DidReflowChildren(GetFirstPrincipalChild(), childFrame); |
|
165 return rv; |
|
166 } |
|
167 |
|
168 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
|
169 childDesiredSize.mBoundingMetrics); |
|
170 |
|
171 childFrame = childFrame->GetNextSibling(); |
|
172 } |
|
173 |
|
174 |
|
175 // place and size children |
|
176 FinalizeReflow(*aReflowState.rendContext, aDesiredSize); |
|
177 |
|
178 aStatus = NS_FRAME_COMPLETE; |
|
179 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 // For token elements, mBoundingMetrics is computed at the ReflowToken |
|
184 // pass, it is not computed here because our children may be text frames |
|
185 // that do not implement the GetBoundingMetrics() interface. |
|
186 /* virtual */ nsresult |
|
187 nsMathMLTokenFrame::Place(nsRenderingContext& aRenderingContext, |
|
188 bool aPlaceOrigin, |
|
189 nsHTMLReflowMetrics& aDesiredSize) |
|
190 { |
|
191 mBoundingMetrics = nsBoundingMetrics(); |
|
192 for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; |
|
193 childFrame = childFrame->GetNextSibling()) { |
|
194 nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); |
|
195 GetReflowAndBoundingMetricsFor(childFrame, childSize, |
|
196 childSize.mBoundingMetrics, nullptr); |
|
197 // compute and cache the bounding metrics |
|
198 mBoundingMetrics += childSize.mBoundingMetrics; |
|
199 } |
|
200 |
|
201 nsRefPtr<nsFontMetrics> fm; |
|
202 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); |
|
203 nscoord ascent = fm->MaxAscent(); |
|
204 nscoord descent = fm->MaxDescent(); |
|
205 |
|
206 aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
|
207 aDesiredSize.Width() = mBoundingMetrics.width; |
|
208 aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, ascent)); |
|
209 aDesiredSize.Height() = aDesiredSize.TopAscent() + |
|
210 std::max(mBoundingMetrics.descent, descent); |
|
211 |
|
212 if (aPlaceOrigin) { |
|
213 nscoord dy, dx = 0; |
|
214 for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; |
|
215 childFrame = childFrame->GetNextSibling()) { |
|
216 nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); |
|
217 GetReflowAndBoundingMetricsFor(childFrame, childSize, |
|
218 childSize.mBoundingMetrics); |
|
219 |
|
220 // place and size the child; (dx,0) makes the caret happy - bug 188146 |
|
221 dy = childSize.Height() == 0 ? 0 : aDesiredSize.TopAscent() - childSize.TopAscent(); |
|
222 FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy, 0); |
|
223 dx += childSize.Width(); |
|
224 } |
|
225 } |
|
226 |
|
227 SetReference(nsPoint(0, aDesiredSize.TopAscent())); |
|
228 |
|
229 return NS_OK; |
|
230 } |
|
231 |