/src/mozilla-central/layout/mathml/nsMathMLmfencedFrame.cpp
Line | Count | Source (jump to first uncovered line) |
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 "gfxContext.h" |
8 | | #include "nsMathMLmfencedFrame.h" |
9 | | #include "nsMathMLChar.h" |
10 | | #include <algorithm> |
11 | | |
12 | | using namespace mozilla; |
13 | | |
14 | | // |
15 | | // <mfenced> -- surround content with a pair of fences |
16 | | // |
17 | | |
18 | | nsIFrame* |
19 | | NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
20 | 0 | { |
21 | 0 | return new (aPresShell) nsMathMLmfencedFrame(aStyle); |
22 | 0 | } |
23 | | |
24 | | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfencedFrame) |
25 | | |
26 | | void |
27 | | nsMathMLmfencedFrame::DestroyFrom(nsIFrame* aDestructRoot, |
28 | | PostDestroyData& aPostDestroyData) |
29 | 0 | { |
30 | 0 | RemoveFencesAndSeparators(); |
31 | 0 | nsMathMLContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
32 | 0 | } |
33 | | |
34 | | NS_IMETHODIMP |
35 | | nsMathMLmfencedFrame::InheritAutomaticData(nsIFrame* aParent) |
36 | 0 | { |
37 | 0 | // let the base class get the default from our parent |
38 | 0 | nsMathMLContainerFrame::InheritAutomaticData(aParent); |
39 | 0 |
|
40 | 0 | mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; |
41 | 0 |
|
42 | 0 | RemoveFencesAndSeparators(); |
43 | 0 | CreateFencesAndSeparators(PresContext()); |
44 | 0 |
|
45 | 0 | return NS_OK; |
46 | 0 | } |
47 | | |
48 | | void |
49 | | nsMathMLmfencedFrame::SetInitialChildList(ChildListID aListID, |
50 | | nsFrameList& aChildList) |
51 | 0 | { |
52 | 0 | // First, let the base class do its work |
53 | 0 | nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList); |
54 | 0 |
|
55 | 0 | // InheritAutomaticData will not get called if our parent is not a mathml |
56 | 0 | // frame, so initialize NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY for |
57 | 0 | // GetPreferredStretchSize() from Reflow(). |
58 | 0 | mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; |
59 | 0 | // No need to track the ComputedStyle given to our MathML chars. |
60 | 0 | // The Style System will use Get/SetAdditionalComputedStyle() to keep them |
61 | 0 | // up-to-date if dynamic changes arise. |
62 | 0 | CreateFencesAndSeparators(PresContext()); |
63 | 0 | } |
64 | | |
65 | | nsresult |
66 | | nsMathMLmfencedFrame::AttributeChanged(int32_t aNameSpaceID, |
67 | | nsAtom* aAttribute, |
68 | | int32_t aModType) |
69 | 0 | { |
70 | 0 | RemoveFencesAndSeparators(); |
71 | 0 | CreateFencesAndSeparators(PresContext()); |
72 | 0 |
|
73 | 0 | return nsMathMLContainerFrame:: |
74 | 0 | AttributeChanged(aNameSpaceID, aAttribute, aModType); |
75 | 0 | } |
76 | | |
77 | | nsresult |
78 | | nsMathMLmfencedFrame::ChildListChanged(int32_t aModType) |
79 | 0 | { |
80 | 0 | RemoveFencesAndSeparators(); |
81 | 0 | CreateFencesAndSeparators(PresContext()); |
82 | 0 |
|
83 | 0 | return nsMathMLContainerFrame::ChildListChanged(aModType); |
84 | 0 | } |
85 | | |
86 | | void |
87 | | nsMathMLmfencedFrame::RemoveFencesAndSeparators() |
88 | 0 | { |
89 | 0 | MarkNeedsDisplayItemRebuild(); |
90 | 0 | delete mOpenChar; |
91 | 0 | delete mCloseChar; |
92 | 0 | if (mSeparatorsChar) delete[] mSeparatorsChar; |
93 | 0 |
|
94 | 0 | mOpenChar = nullptr; |
95 | 0 | mCloseChar = nullptr; |
96 | 0 | mSeparatorsChar = nullptr; |
97 | 0 | mSeparatorsCount = 0; |
98 | 0 | } |
99 | | |
100 | | void |
101 | | nsMathMLmfencedFrame::CreateFencesAndSeparators(nsPresContext* aPresContext) |
102 | 0 | { |
103 | 0 | nsAutoString value; |
104 | 0 |
|
105 | 0 | ////////////// |
106 | 0 | // see if the opening fence is there ... |
107 | 0 | if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::open, value)) { |
108 | 0 | value = char16_t('('); // default as per the MathML REC |
109 | 0 | } else { |
110 | 0 | value.CompressWhitespace(); |
111 | 0 | } |
112 | 0 |
|
113 | 0 | if (!value.IsEmpty()) { |
114 | 0 | mOpenChar = new nsMathMLChar; |
115 | 0 | mOpenChar->SetData(value); |
116 | 0 | ResolveMathMLCharStyle(aPresContext, mContent, mComputedStyle, mOpenChar); |
117 | 0 | } |
118 | 0 |
|
119 | 0 | ////////////// |
120 | 0 | // see if the closing fence is there ... |
121 | 0 | if(!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::close, value)) { |
122 | 0 | value = char16_t(')'); // default as per the MathML REC |
123 | 0 | } else { |
124 | 0 | value.CompressWhitespace(); |
125 | 0 | } |
126 | 0 |
|
127 | 0 | if (!value.IsEmpty()) { |
128 | 0 | mCloseChar = new nsMathMLChar; |
129 | 0 | mCloseChar->SetData(value); |
130 | 0 | ResolveMathMLCharStyle(aPresContext, mContent, mComputedStyle, mCloseChar); |
131 | 0 | } |
132 | 0 |
|
133 | 0 | ////////////// |
134 | 0 | // see if separators are there ... |
135 | 0 | if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::separators_, value)) { |
136 | 0 | value = char16_t(','); // default as per the MathML REC |
137 | 0 | } else { |
138 | 0 | value.StripWhitespace(); |
139 | 0 | } |
140 | 0 |
|
141 | 0 | mSeparatorsCount = value.Length(); |
142 | 0 | if (0 < mSeparatorsCount) { |
143 | 0 | int32_t sepCount = mFrames.GetLength() - 1; |
144 | 0 | if (0 < sepCount) { |
145 | 0 | mSeparatorsChar = new nsMathMLChar[sepCount]; |
146 | 0 | nsAutoString sepChar; |
147 | 0 | for (int32_t i = 0; i < sepCount; i++) { |
148 | 0 | if (i < mSeparatorsCount) { |
149 | 0 | sepChar = value[i]; |
150 | 0 | } |
151 | 0 | else { |
152 | 0 | sepChar = value[mSeparatorsCount-1]; |
153 | 0 | } |
154 | 0 | mSeparatorsChar[i].SetData(sepChar); |
155 | 0 | ResolveMathMLCharStyle(aPresContext, mContent, mComputedStyle, &mSeparatorsChar[i]); |
156 | 0 | } |
157 | 0 | mSeparatorsCount = sepCount; |
158 | 0 | } else { |
159 | 0 | // No separators. Note that sepCount can be -1 here, so don't |
160 | 0 | // set mSeparatorsCount to it. |
161 | 0 | mSeparatorsCount = 0; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | void |
167 | | nsMathMLmfencedFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
168 | | const nsDisplayListSet& aLists) |
169 | 0 | { |
170 | 0 | ///////////// |
171 | 0 | // display the content |
172 | 0 | nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists); |
173 | 0 |
|
174 | 0 | //////////// |
175 | 0 | // display fences and separators |
176 | 0 | uint32_t count = 0; |
177 | 0 | if (mOpenChar) { |
178 | 0 | mOpenChar->Display(aBuilder, this, aLists, count++); |
179 | 0 | } |
180 | 0 |
|
181 | 0 | if (mCloseChar) { |
182 | 0 | mCloseChar->Display(aBuilder, this, aLists, count++); |
183 | 0 | } |
184 | 0 |
|
185 | 0 | for (int32_t i = 0; i < mSeparatorsCount; i++) { |
186 | 0 | mSeparatorsChar[i].Display(aBuilder, this, aLists, count++); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | /* @param aMetrics is an IN/OUT. Provide the current metrics for the mFenced |
191 | | frame and it will be enlarged as necessary. |
192 | | For simplicity the width of the container is always incremented by the width |
193 | | of the nsMathMLChar. As we only stretch fences and separators in the vertical |
194 | | direction, this has no impact on overall appearance. |
195 | | */ |
196 | | static void |
197 | | ApplyUnstretchedMetrics(nsIFrame* aFrame, |
198 | | DrawTarget* aDrawTarget, |
199 | | float aFontSizeInflation, |
200 | | nsMathMLChar* aMathMLChar, |
201 | | nsBoundingMetrics& aMetrics, |
202 | | bool aIsRTL) |
203 | 0 | { |
204 | 0 | if (aMathMLChar && 0 < aMathMLChar->Length()) { |
205 | 0 | nsBoundingMetrics charSize; |
206 | 0 | aMathMLChar->Stretch(aFrame, aDrawTarget, aFontSizeInflation, |
207 | 0 | NS_STRETCH_DIRECTION_DEFAULT, |
208 | 0 | aMetrics, // size is unimportant as we aren't stretching |
209 | 0 | charSize, NS_STRETCH_NONE, aIsRTL); |
210 | 0 | aMetrics += charSize; |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | void |
215 | | nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext, |
216 | | ReflowOutput& aDesiredSize, |
217 | | const ReflowInput& aReflowInput, |
218 | | nsReflowStatus& aStatus) |
219 | 0 | { |
220 | 0 | MarkInReflow(); |
221 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
222 | 0 |
|
223 | 0 | mPresentationData.flags &= ~NS_MATHML_ERROR; |
224 | 0 | aDesiredSize.ClearSize(); |
225 | 0 | aDesiredSize.SetBlockStartAscent(0); |
226 | 0 | aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); |
227 | 0 |
|
228 | 0 | int32_t i; |
229 | 0 | const nsStyleFont* font = StyleFont(); |
230 | 0 | float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); |
231 | 0 | RefPtr<nsFontMetrics> fm = |
232 | 0 | nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); |
233 | 0 | nscoord axisHeight, em; |
234 | 0 | GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm, axisHeight); |
235 | 0 | GetEmHeight(fm, em); |
236 | 0 | // leading to be left at the top and the bottom of stretched chars |
237 | 0 | nscoord leading = NSToCoordRound(0.2f * em); |
238 | 0 |
|
239 | 0 | ///////////// |
240 | 0 | // Reflow children |
241 | 0 | // Asking each child to cache its bounding metrics |
242 | 0 |
|
243 | 0 | // Note that we don't use the base method nsMathMLContainerFrame::Reflow() |
244 | 0 | // because we want to stretch our fences, separators and stretchy frames using |
245 | 0 | // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base |
246 | 0 | // method here, our stretchy frames will be stretched and placed, and we may |
247 | 0 | // end up stretching our fences/separators with a different aDesiredSize. |
248 | 0 | // XXX The above decision was revisited in bug 121748 and this code can be |
249 | 0 | // refactored to use nsMathMLContainerFrame::Reflow() at some stage. |
250 | 0 |
|
251 | 0 | nsReflowStatus childStatus; |
252 | 0 | nsIFrame* firstChild = PrincipalChildList().FirstChild(); |
253 | 0 | nsIFrame* childFrame = firstChild; |
254 | 0 | nscoord ascent = 0, descent = 0; |
255 | 0 | if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) { |
256 | 0 | // We use the ASCII metrics to get our minimum height. This way, |
257 | 0 | // if we have borders or a background, they will fit better with |
258 | 0 | // other elements on the line. |
259 | 0 | ascent = fm->MaxAscent(); |
260 | 0 | descent = fm->MaxDescent(); |
261 | 0 | } |
262 | 0 | while (childFrame) { |
263 | 0 | ReflowOutput childDesiredSize(aReflowInput); |
264 | 0 | WritingMode wm = childFrame->GetWritingMode(); |
265 | 0 | LogicalSize availSize = aReflowInput.ComputedSize(wm); |
266 | 0 | availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; |
267 | 0 | ReflowInput childReflowInput(aPresContext, aReflowInput, |
268 | 0 | childFrame, availSize); |
269 | 0 | ReflowChild(childFrame, aPresContext, childDesiredSize, |
270 | 0 | childReflowInput, childStatus); |
271 | 0 | //NS_ASSERTION(childStatus.IsComplete(), "bad status"); |
272 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
273 | 0 | childDesiredSize.mBoundingMetrics); |
274 | 0 |
|
275 | 0 | mozilla::WritingMode outerWM = aReflowInput.GetWritingMode(); |
276 | 0 | nscoord childDescent = childDesiredSize.BSize(outerWM) - |
277 | 0 | childDesiredSize.BlockStartAscent(); |
278 | 0 | if (descent < childDescent) |
279 | 0 | descent = childDescent; |
280 | 0 | if (ascent < childDesiredSize.BlockStartAscent()) |
281 | 0 | ascent = childDesiredSize.BlockStartAscent(); |
282 | 0 |
|
283 | 0 | childFrame = childFrame->GetNextSibling(); |
284 | 0 | } |
285 | 0 |
|
286 | 0 | ///////////// |
287 | 0 | // Ask stretchy children to stretch themselves |
288 | 0 |
|
289 | 0 | nsBoundingMetrics containerSize; |
290 | 0 | nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL; |
291 | 0 |
|
292 | 0 | DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget(); |
293 | 0 |
|
294 | 0 | GetPreferredStretchSize(drawTarget, |
295 | 0 | 0, /* i.e., without embellishments */ |
296 | 0 | stretchDir, containerSize); |
297 | 0 | childFrame = firstChild; |
298 | 0 | while (childFrame) { |
299 | 0 | nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame); |
300 | 0 | if (mathmlChild) { |
301 | 0 | ReflowOutput childDesiredSize(aReflowInput); |
302 | 0 | // retrieve the metrics that was stored at the previous pass |
303 | 0 | GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
304 | 0 | childDesiredSize.mBoundingMetrics); |
305 | 0 |
|
306 | 0 | mathmlChild->Stretch(drawTarget, |
307 | 0 | stretchDir, containerSize, childDesiredSize); |
308 | 0 | // store the updated metrics |
309 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
310 | 0 | childDesiredSize.mBoundingMetrics); |
311 | 0 |
|
312 | 0 | nscoord childDescent = childDesiredSize.Height() - childDesiredSize.BlockStartAscent(); |
313 | 0 | if (descent < childDescent) |
314 | 0 | descent = childDescent; |
315 | 0 | if (ascent < childDesiredSize.BlockStartAscent()) |
316 | 0 | ascent = childDesiredSize.BlockStartAscent(); |
317 | 0 | } |
318 | 0 | childFrame = childFrame->GetNextSibling(); |
319 | 0 | } |
320 | 0 |
|
321 | 0 | // bug 121748: for surrounding fences & separators, use a size that covers everything |
322 | 0 | GetPreferredStretchSize(drawTarget, STRETCH_CONSIDER_EMBELLISHMENTS, |
323 | 0 | stretchDir, containerSize); |
324 | 0 |
|
325 | 0 | bool isRTL = StyleVisibility()->mDirection; |
326 | 0 |
|
327 | 0 | // To achieve a minimum size of "1", the container should be enlarged by the |
328 | 0 | // unstretched metrics of the fences and separators. |
329 | 0 | ApplyUnstretchedMetrics(this, drawTarget, |
330 | 0 | fontSizeInflation, mOpenChar, |
331 | 0 | containerSize, isRTL); |
332 | 0 | for (i = 0; i < mSeparatorsCount; i++) { |
333 | 0 | ApplyUnstretchedMetrics(this, drawTarget, |
334 | 0 | fontSizeInflation, &mSeparatorsChar[i], |
335 | 0 | containerSize, isRTL); |
336 | 0 | } |
337 | 0 | ApplyUnstretchedMetrics(this, drawTarget, |
338 | 0 | fontSizeInflation, mCloseChar, |
339 | 0 | containerSize, isRTL); |
340 | 0 |
|
341 | 0 | ////////////////////////////////////////// |
342 | 0 | // Prepare the opening fence, separators, and closing fence, and |
343 | 0 | // adjust the origin of children. |
344 | 0 |
|
345 | 0 | // we need to center around the axis |
346 | 0 | nscoord delta = std::max(containerSize.ascent - axisHeight, |
347 | 0 | containerSize.descent + axisHeight); |
348 | 0 | containerSize.ascent = delta + axisHeight; |
349 | 0 | containerSize.descent = delta - axisHeight; |
350 | 0 |
|
351 | 0 | ///////////////// |
352 | 0 | // opening fence ... |
353 | 0 | ReflowChar(drawTarget, *fm, |
354 | 0 | fontSizeInflation, mOpenChar, |
355 | 0 | NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, |
356 | 0 | axisHeight, leading, em, containerSize, ascent, descent, isRTL); |
357 | 0 | ///////////////// |
358 | 0 | // separators ... |
359 | 0 | for (i = 0; i < mSeparatorsCount; i++) { |
360 | 0 | ReflowChar(drawTarget, *fm, |
361 | 0 | fontSizeInflation, &mSeparatorsChar[i], |
362 | 0 | NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, |
363 | 0 | axisHeight, leading, em, containerSize, ascent, descent, isRTL); |
364 | 0 | } |
365 | 0 | ///////////////// |
366 | 0 | // closing fence ... |
367 | 0 | ReflowChar(drawTarget, *fm, |
368 | 0 | fontSizeInflation, mCloseChar, |
369 | 0 | NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, |
370 | 0 | axisHeight, leading, em, containerSize, ascent, descent, isRTL); |
371 | 0 |
|
372 | 0 | ////////////////// |
373 | 0 | // Adjust the origins of each child. |
374 | 0 | // and update our bounding metrics |
375 | 0 |
|
376 | 0 | i = 0; |
377 | 0 | nscoord dx = 0; |
378 | 0 | nsBoundingMetrics bm; |
379 | 0 | bool firstTime = true; |
380 | 0 | nsMathMLChar *leftChar, *rightChar; |
381 | 0 | if (isRTL) { |
382 | 0 | leftChar = mCloseChar; |
383 | 0 | rightChar = mOpenChar; |
384 | 0 | } else { |
385 | 0 | leftChar = mOpenChar; |
386 | 0 | rightChar = mCloseChar; |
387 | 0 | } |
388 | 0 |
|
389 | 0 | if (leftChar) { |
390 | 0 | PlaceChar(leftChar, ascent, bm, dx); |
391 | 0 | aDesiredSize.mBoundingMetrics = bm; |
392 | 0 | firstTime = false; |
393 | 0 | } |
394 | 0 |
|
395 | 0 | if (isRTL) { |
396 | 0 | childFrame = this->GetChildList(nsIFrame::kPrincipalList).LastChild(); |
397 | 0 | } else { |
398 | 0 | childFrame = firstChild; |
399 | 0 | } |
400 | 0 | while (childFrame) { |
401 | 0 | ReflowOutput childSize(aReflowInput); |
402 | 0 | GetReflowAndBoundingMetricsFor(childFrame, childSize, bm); |
403 | 0 | if (firstTime) { |
404 | 0 | firstTime = false; |
405 | 0 | aDesiredSize.mBoundingMetrics = bm; |
406 | 0 | } |
407 | 0 | else |
408 | 0 | aDesiredSize.mBoundingMetrics += bm; |
409 | 0 |
|
410 | 0 | FinishReflowChild(childFrame, aPresContext, childSize, nullptr, |
411 | 0 | dx, ascent - childSize.BlockStartAscent(), 0); |
412 | 0 | dx += childSize.Width(); |
413 | 0 |
|
414 | 0 | if (i < mSeparatorsCount) { |
415 | 0 | PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i], |
416 | 0 | ascent, bm, dx); |
417 | 0 | aDesiredSize.mBoundingMetrics += bm; |
418 | 0 | } |
419 | 0 | i++; |
420 | 0 |
|
421 | 0 | if (isRTL) { |
422 | 0 | childFrame = childFrame->GetPrevSibling(); |
423 | 0 | } else { |
424 | 0 | childFrame = childFrame->GetNextSibling(); |
425 | 0 | } |
426 | 0 | } |
427 | 0 |
|
428 | 0 | if (rightChar) { |
429 | 0 | PlaceChar(rightChar, ascent, bm, dx); |
430 | 0 | if (firstTime) |
431 | 0 | aDesiredSize.mBoundingMetrics = bm; |
432 | 0 | else |
433 | 0 | aDesiredSize.mBoundingMetrics += bm; |
434 | 0 | } |
435 | 0 |
|
436 | 0 | aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width; |
437 | 0 | aDesiredSize.Height() = ascent + descent; |
438 | 0 | aDesiredSize.SetBlockStartAscent(ascent); |
439 | 0 |
|
440 | 0 | SetBoundingMetrics(aDesiredSize.mBoundingMetrics); |
441 | 0 | SetReference(nsPoint(0, aDesiredSize.BlockStartAscent())); |
442 | 0 |
|
443 | 0 | // see if we should fix the spacing |
444 | 0 | FixInterFrameSpacing(aDesiredSize); |
445 | 0 |
|
446 | 0 | // Finished with these: |
447 | 0 | ClearSavedChildMetrics(); |
448 | 0 |
|
449 | 0 | // Set our overflow area |
450 | 0 | GatherAndStoreOverflow(&aDesiredSize); |
451 | 0 |
|
452 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "This type of frame can't be split."); |
453 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
454 | 0 | } |
455 | | |
456 | | static void |
457 | | GetCharSpacing(nsMathMLChar* aMathMLChar, |
458 | | nsOperatorFlags aForm, |
459 | | int32_t aScriptLevel, |
460 | | nscoord em, |
461 | | nscoord& aLeftSpace, |
462 | | nscoord& aRightSpace) |
463 | 0 | { |
464 | 0 | nsAutoString data; |
465 | 0 | aMathMLChar->GetData(data); |
466 | 0 | nsOperatorFlags flags = 0; |
467 | 0 | float lspace = 0.0f; |
468 | 0 | float rspace = 0.0f; |
469 | 0 | bool found = nsMathMLOperators::LookupOperator(data, aForm, |
470 | 0 | &flags, &lspace, &rspace); |
471 | 0 |
|
472 | 0 | // We don't want extra space when we are a script |
473 | 0 | if (found && aScriptLevel > 0) { |
474 | 0 | lspace /= 2.0f; |
475 | 0 | rspace /= 2.0f; |
476 | 0 | } |
477 | 0 |
|
478 | 0 | aLeftSpace = NSToCoordRound(lspace * em); |
479 | 0 | aRightSpace = NSToCoordRound(rspace * em); |
480 | 0 | } |
481 | | |
482 | | // helper functions to perform the common task of formatting our chars |
483 | | nsresult |
484 | | nsMathMLmfencedFrame::ReflowChar(DrawTarget* aDrawTarget, |
485 | | nsFontMetrics& aFontMetrics, |
486 | | float aFontSizeInflation, |
487 | | nsMathMLChar* aMathMLChar, |
488 | | nsOperatorFlags aForm, |
489 | | int32_t aScriptLevel, |
490 | | nscoord axisHeight, |
491 | | nscoord leading, |
492 | | nscoord em, |
493 | | nsBoundingMetrics& aContainerSize, |
494 | | nscoord& aAscent, |
495 | | nscoord& aDescent, |
496 | | bool aRTL) |
497 | 0 | { |
498 | 0 | if (aMathMLChar && 0 < aMathMLChar->Length()) { |
499 | 0 | nscoord leftSpace; |
500 | 0 | nscoord rightSpace; |
501 | 0 | GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace); |
502 | 0 |
|
503 | 0 | // stretch the char to the appropriate height if it is not big enough. |
504 | 0 | nsBoundingMetrics charSize; |
505 | 0 | nsresult res = aMathMLChar->Stretch(this, aDrawTarget, |
506 | 0 | aFontSizeInflation, |
507 | 0 | NS_STRETCH_DIRECTION_VERTICAL, |
508 | 0 | aContainerSize, charSize, |
509 | 0 | NS_STRETCH_NORMAL, aRTL); |
510 | 0 |
|
511 | 0 | if (NS_STRETCH_DIRECTION_UNSUPPORTED != aMathMLChar->GetStretchDirection()) { |
512 | 0 | // has changed... so center the char around the axis |
513 | 0 | nscoord height = charSize.ascent + charSize.descent; |
514 | 0 | charSize.ascent = height/2 + axisHeight; |
515 | 0 | charSize.descent = height - charSize.ascent; |
516 | 0 | } |
517 | 0 | else { |
518 | 0 | // either it hasn't changed or stretching the char failed (i.e., |
519 | 0 | // nsLayoutUtils::AppUnitBoundsOfString failed) |
520 | 0 | leading = 0; |
521 | 0 | if (NS_FAILED(res)) { |
522 | 0 | nsAutoString data; |
523 | 0 | aMathMLChar->GetData(data); |
524 | 0 | nsBoundingMetrics metrics = |
525 | 0 | nsLayoutUtils::AppUnitBoundsOfString(data.get(), data.Length(), |
526 | 0 | aFontMetrics, aDrawTarget); |
527 | 0 | charSize.ascent = metrics.ascent; |
528 | 0 | charSize.descent = metrics.descent; |
529 | 0 | charSize.width = metrics.width; |
530 | 0 | // Set this as the bounding metrics of the MathMLChar to leave |
531 | 0 | // the necessary room to paint the char. |
532 | 0 | aMathMLChar->SetBoundingMetrics(charSize); |
533 | 0 | } |
534 | 0 | } |
535 | 0 |
|
536 | 0 | if (aAscent < charSize.ascent + leading) |
537 | 0 | aAscent = charSize.ascent + leading; |
538 | 0 | if (aDescent < charSize.descent + leading) |
539 | 0 | aDescent = charSize.descent + leading; |
540 | 0 |
|
541 | 0 | // account the spacing |
542 | 0 | charSize.width += leftSpace + rightSpace; |
543 | 0 |
|
544 | 0 | // x-origin is used to store lspace ... |
545 | 0 | // y-origin is used to stored the ascent ... |
546 | 0 | aMathMLChar->SetRect(nsRect(leftSpace, |
547 | 0 | charSize.ascent, charSize.width, |
548 | 0 | charSize.ascent + charSize.descent)); |
549 | 0 | } |
550 | 0 | return NS_OK; |
551 | 0 | } |
552 | | |
553 | | /*static*/ void |
554 | | nsMathMLmfencedFrame::PlaceChar(nsMathMLChar* aMathMLChar, |
555 | | nscoord aDesiredAscent, |
556 | | nsBoundingMetrics& bm, |
557 | | nscoord& dx) |
558 | 0 | { |
559 | 0 | aMathMLChar->GetBoundingMetrics(bm); |
560 | 0 |
|
561 | 0 | // the char's x-origin was used to store lspace ... |
562 | 0 | // the char's y-origin was used to store the ascent ... |
563 | 0 | // the char's width was used to store the advance with (with spacing) ... |
564 | 0 | nsRect rect; |
565 | 0 | aMathMLChar->GetRect(rect); |
566 | 0 |
|
567 | 0 | nscoord dy = aDesiredAscent - rect.y; |
568 | 0 | if (aMathMLChar->GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) { |
569 | 0 | // the stretchy char will be centered around the axis |
570 | 0 | // so we adjust the returned bounding metrics accordingly |
571 | 0 | bm.descent = (bm.ascent + bm.descent) - rect.y; |
572 | 0 | bm.ascent = rect.y; |
573 | 0 | } |
574 | 0 |
|
575 | 0 | aMathMLChar->SetRect(nsRect(dx + rect.x, dy, bm.width, rect.height)); |
576 | 0 |
|
577 | 0 | bm.leftBearing += rect.x; |
578 | 0 | bm.rightBearing += rect.x; |
579 | 0 |
|
580 | 0 | // return rect.width since it includes lspace and rspace |
581 | 0 | bm.width = rect.width; |
582 | 0 | dx += rect.width; |
583 | 0 | } |
584 | | |
585 | | static nscoord |
586 | | GetMaxCharWidth(nsIFrame* aFrame, |
587 | | DrawTarget* aDrawTarget, |
588 | | float aFontSizeInflation, |
589 | | nsMathMLChar* aMathMLChar, |
590 | | nsOperatorFlags aForm, |
591 | | int32_t aScriptLevel, |
592 | | nscoord em) |
593 | 0 | { |
594 | 0 | nscoord width = aMathMLChar->GetMaxWidth(aFrame, aDrawTarget, |
595 | 0 | aFontSizeInflation); |
596 | 0 |
|
597 | 0 | if (0 < aMathMLChar->Length()) { |
598 | 0 | nscoord leftSpace; |
599 | 0 | nscoord rightSpace; |
600 | 0 | GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace); |
601 | 0 |
|
602 | 0 | width += leftSpace + rightSpace; |
603 | 0 | } |
604 | 0 |
|
605 | 0 | return width; |
606 | 0 | } |
607 | | |
608 | | /* virtual */ void |
609 | | nsMathMLmfencedFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext, ReflowOutput& aDesiredSize) |
610 | 0 | { |
611 | 0 | nscoord width = 0; |
612 | 0 |
|
613 | 0 | const nsStyleFont* font = StyleFont(); |
614 | 0 | float fontSizeInflation = nsLayoutUtils:: FontSizeInflationFor(this); |
615 | 0 | RefPtr<nsFontMetrics> fm = |
616 | 0 | nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); |
617 | 0 | nscoord em; |
618 | 0 | GetEmHeight(fm, em); |
619 | 0 |
|
620 | 0 | if (mOpenChar) { |
621 | 0 | width += |
622 | 0 | GetMaxCharWidth(this, aRenderingContext->GetDrawTarget(), |
623 | 0 | fontSizeInflation, mOpenChar, |
624 | 0 | NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, em); |
625 | 0 | } |
626 | 0 |
|
627 | 0 | int32_t i = 0; |
628 | 0 | for (nsIFrame* childFrame : PrincipalChildList()) { |
629 | 0 | // XXX This includes margin while Reflow currently doesn't consider |
630 | 0 | // margin, so we may end up with too much space, but, with stretchy |
631 | 0 | // characters, this is an approximation anyway. |
632 | 0 | width += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame, |
633 | 0 | nsLayoutUtils::PREF_ISIZE); |
634 | 0 |
|
635 | 0 | if (i < mSeparatorsCount) { |
636 | 0 | width += |
637 | 0 | GetMaxCharWidth(this, aRenderingContext->GetDrawTarget(), |
638 | 0 | fontSizeInflation, &mSeparatorsChar[i], |
639 | 0 | NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, em); |
640 | 0 | } |
641 | 0 | i++; |
642 | 0 | } |
643 | 0 |
|
644 | 0 | if (mCloseChar) { |
645 | 0 | width += |
646 | 0 | GetMaxCharWidth(this, aRenderingContext->GetDrawTarget(), |
647 | 0 | fontSizeInflation, mCloseChar, |
648 | 0 | NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em); |
649 | 0 | } |
650 | 0 |
|
651 | 0 | aDesiredSize.Width() = width; |
652 | 0 | aDesiredSize.mBoundingMetrics.width = width; |
653 | 0 | aDesiredSize.mBoundingMetrics.leftBearing = 0; |
654 | 0 | aDesiredSize.mBoundingMetrics.rightBearing = width; |
655 | 0 | } |
656 | | |
657 | | nscoord |
658 | | nsMathMLmfencedFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize) |
659 | 0 | { |
660 | 0 | nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); |
661 | 0 | if (!gap) return 0; |
662 | 0 | |
663 | 0 | nsRect rect; |
664 | 0 | if (mOpenChar) { |
665 | 0 | mOpenChar->GetRect(rect); |
666 | 0 | rect.MoveBy(gap, 0); |
667 | 0 | mOpenChar->SetRect(rect); |
668 | 0 | } |
669 | 0 | if (mCloseChar) { |
670 | 0 | mCloseChar->GetRect(rect); |
671 | 0 | rect.MoveBy(gap, 0); |
672 | 0 | mCloseChar->SetRect(rect); |
673 | 0 | } |
674 | 0 | for (int32_t i = 0; i < mSeparatorsCount; i++) { |
675 | 0 | mSeparatorsChar[i].GetRect(rect); |
676 | 0 | rect.MoveBy(gap, 0); |
677 | 0 | mSeparatorsChar[i].SetRect(rect); |
678 | 0 | } |
679 | 0 | return gap; |
680 | 0 | } |
681 | | |
682 | | // ---------------------- |
683 | | // the Style System will use these to pass the proper ComputedStyle to our |
684 | | // MathMLChar |
685 | | ComputedStyle* |
686 | | nsMathMLmfencedFrame::GetAdditionalComputedStyle(int32_t aIndex) const |
687 | 0 | { |
688 | 0 | int32_t openIndex = -1; |
689 | 0 | int32_t closeIndex = -1; |
690 | 0 | int32_t lastIndex = mSeparatorsCount-1; |
691 | 0 |
|
692 | 0 | if (mOpenChar) { |
693 | 0 | lastIndex++; |
694 | 0 | openIndex = lastIndex; |
695 | 0 | } |
696 | 0 | if (mCloseChar) { |
697 | 0 | lastIndex++; |
698 | 0 | closeIndex = lastIndex; |
699 | 0 | } |
700 | 0 | if (aIndex < 0 || aIndex > lastIndex) { |
701 | 0 | return nullptr; |
702 | 0 | } |
703 | 0 | |
704 | 0 | if (aIndex < mSeparatorsCount) { |
705 | 0 | return mSeparatorsChar[aIndex].GetComputedStyle(); |
706 | 0 | } |
707 | 0 | else if (aIndex == openIndex) { |
708 | 0 | return mOpenChar->GetComputedStyle(); |
709 | 0 | } |
710 | 0 | else if (aIndex == closeIndex) { |
711 | 0 | return mCloseChar->GetComputedStyle(); |
712 | 0 | } |
713 | 0 | return nullptr; |
714 | 0 | } |
715 | | |
716 | | void |
717 | | nsMathMLmfencedFrame::SetAdditionalComputedStyle(int32_t aIndex, |
718 | | ComputedStyle* aComputedStyle) |
719 | 0 | { |
720 | 0 | int32_t openIndex = -1; |
721 | 0 | int32_t closeIndex = -1; |
722 | 0 | int32_t lastIndex = mSeparatorsCount-1; |
723 | 0 |
|
724 | 0 | if (mOpenChar) { |
725 | 0 | lastIndex++; |
726 | 0 | openIndex = lastIndex; |
727 | 0 | } |
728 | 0 | if (mCloseChar) { |
729 | 0 | lastIndex++; |
730 | 0 | closeIndex = lastIndex; |
731 | 0 | } |
732 | 0 | if (aIndex < 0 || aIndex > lastIndex) { |
733 | 0 | return; |
734 | 0 | } |
735 | 0 | |
736 | 0 | if (aIndex < mSeparatorsCount) { |
737 | 0 | mSeparatorsChar[aIndex].SetComputedStyle(aComputedStyle); |
738 | 0 | } |
739 | 0 | else if (aIndex == openIndex) { |
740 | 0 | mOpenChar->SetComputedStyle(aComputedStyle); |
741 | 0 | } |
742 | 0 | else if (aIndex == closeIndex) { |
743 | 0 | mCloseChar->SetComputedStyle(aComputedStyle); |
744 | 0 | } |
745 | 0 | } |