/src/mozilla-central/layout/mathml/nsMathMLContainerFrame.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 "nsMathMLContainerFrame.h" |
8 | | |
9 | | #include "gfxContext.h" |
10 | | #include "gfxUtils.h" |
11 | | #include "mozilla/gfx/2D.h" |
12 | | #include "nsLayoutUtils.h" |
13 | | #include "nsPresContext.h" |
14 | | #include "nsIPresShell.h" |
15 | | #include "nsNameSpaceManager.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "nsDisplayList.h" |
18 | | #include "mozilla/Likely.h" |
19 | | #include "nsIScriptError.h" |
20 | | #include "nsContentUtils.h" |
21 | | #include "nsMathMLElement.h" |
22 | | #include "mozilla/dom/MutationEventBinding.h" |
23 | | |
24 | | using namespace mozilla; |
25 | | using namespace mozilla::gfx; |
26 | | |
27 | | // |
28 | | // nsMathMLContainerFrame implementation |
29 | | // |
30 | | |
31 | 0 | NS_QUERYFRAME_HEAD(nsMathMLContainerFrame) |
32 | 0 | NS_QUERYFRAME_ENTRY(nsIMathMLFrame) |
33 | 0 | NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame) |
34 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
35 | | |
36 | | // ============================================================================= |
37 | | |
38 | | // error handlers |
39 | | // provide a feedback to the user when a frame with bad markup can not be rendered |
40 | | nsresult |
41 | | nsMathMLContainerFrame::ReflowError(DrawTarget* aDrawTarget, |
42 | | ReflowOutput& aDesiredSize) |
43 | 0 | { |
44 | 0 | // clear all other flags and record that there is an error with this frame |
45 | 0 | mEmbellishData.flags = 0; |
46 | 0 | mPresentationData.flags = NS_MATHML_ERROR; |
47 | 0 |
|
48 | 0 | /////////////// |
49 | 0 | // Set font |
50 | 0 | RefPtr<nsFontMetrics> fm = |
51 | 0 | nsLayoutUtils::GetInflatedFontMetricsForFrame(this); |
52 | 0 |
|
53 | 0 | // bounding metrics |
54 | 0 | nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup"); |
55 | 0 | mBoundingMetrics = |
56 | 0 | nsLayoutUtils::AppUnitBoundsOfString(errorMsg.get(), errorMsg.Length(), |
57 | 0 | *fm, aDrawTarget); |
58 | 0 |
|
59 | 0 | // reflow metrics |
60 | 0 | WritingMode wm = aDesiredSize.GetWritingMode(); |
61 | 0 | aDesiredSize.SetBlockStartAscent(fm->MaxAscent()); |
62 | 0 | nscoord descent = fm->MaxDescent(); |
63 | 0 | aDesiredSize.BSize(wm) = aDesiredSize.BlockStartAscent() + descent; |
64 | 0 | aDesiredSize.ISize(wm) = mBoundingMetrics.width; |
65 | 0 |
|
66 | 0 | // Also return our bounding metrics |
67 | 0 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
68 | 0 |
|
69 | 0 | return NS_OK; |
70 | 0 | } |
71 | | |
72 | | class nsDisplayMathMLError : public nsDisplayItem { |
73 | | public: |
74 | | nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
75 | 0 | : nsDisplayItem(aBuilder, aFrame) { |
76 | 0 | MOZ_COUNT_CTOR(nsDisplayMathMLError); |
77 | 0 | } |
78 | | #ifdef NS_BUILD_REFCNT_LOGGING |
79 | | virtual ~nsDisplayMathMLError() { |
80 | | MOZ_COUNT_DTOR(nsDisplayMathMLError); |
81 | | } |
82 | | #endif |
83 | | |
84 | | virtual void Paint(nsDisplayListBuilder* aBuilder, |
85 | | gfxContext* aCtx) override; |
86 | | NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR) |
87 | | }; |
88 | | |
89 | | void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder, |
90 | | gfxContext* aCtx) |
91 | 0 | { |
92 | 0 | // Set color and font ... |
93 | 0 | RefPtr<nsFontMetrics> fm = |
94 | 0 | nsLayoutUtils::GetFontMetricsForFrame(mFrame, 1.0f); |
95 | 0 |
|
96 | 0 | nsPoint pt = ToReferenceFrame(); |
97 | 0 | int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); |
98 | 0 | DrawTarget* drawTarget = aCtx->GetDrawTarget(); |
99 | 0 | Rect rect = NSRectToSnappedRect(nsRect(pt, mFrame->GetSize()), |
100 | 0 | appUnitsPerDevPixel, |
101 | 0 | *drawTarget); |
102 | 0 | ColorPattern red(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f))); |
103 | 0 | drawTarget->FillRect(rect, red); |
104 | 0 |
|
105 | 0 | aCtx->SetColor(Color(1.f, 1.f, 1.f)); |
106 | 0 | nscoord ascent = fm->MaxAscent(); |
107 | 0 | NS_NAMED_LITERAL_STRING(errorMsg, "invalid-markup"); |
108 | 0 | nsLayoutUtils::DrawUniDirString(errorMsg.get(), uint32_t(errorMsg.Length()), |
109 | 0 | nsPoint(pt.x, pt.y + ascent), *fm, *aCtx); |
110 | 0 | } |
111 | | |
112 | | /* ///////////// |
113 | | * nsIMathMLFrame - support methods for stretchy elements |
114 | | * ============================================================================= |
115 | | */ |
116 | | |
117 | | static bool |
118 | | IsForeignChild(const nsIFrame* aFrame) |
119 | 0 | { |
120 | 0 | // This counts nsMathMLmathBlockFrame as a foreign child, because it |
121 | 0 | // uses block reflow |
122 | 0 | return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) || aFrame->IsBlockFrame(); |
123 | 0 | } |
124 | | |
125 | | NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTMLReflowOutputProperty, |
126 | | ReflowOutput) |
127 | | |
128 | | /* static */ void |
129 | | nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame* aFrame, |
130 | | const ReflowOutput& aReflowOutput, |
131 | | const nsBoundingMetrics& aBoundingMetrics) |
132 | 0 | { |
133 | 0 | ReflowOutput* reflowOutput = new ReflowOutput(aReflowOutput); |
134 | 0 | reflowOutput->mBoundingMetrics = aBoundingMetrics; |
135 | 0 | aFrame->SetProperty(HTMLReflowOutputProperty(), reflowOutput); |
136 | 0 | } |
137 | | |
138 | | // helper method to facilitate getting the reflow and bounding metrics |
139 | | /* static */ void |
140 | | nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame* aFrame, |
141 | | ReflowOutput& aReflowOutput, |
142 | | nsBoundingMetrics& aBoundingMetrics, |
143 | | eMathMLFrameType* aMathMLFrameType) |
144 | 0 | { |
145 | 0 | MOZ_ASSERT(aFrame, "null arg"); |
146 | 0 |
|
147 | 0 | ReflowOutput* reflowOutput = |
148 | 0 | aFrame->GetProperty(HTMLReflowOutputProperty()); |
149 | 0 |
|
150 | 0 | // IMPORTANT: This function is only meant to be called in Place() methods |
151 | 0 | // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the |
152 | 0 | // information. |
153 | 0 | NS_ASSERTION(reflowOutput, "Didn't SaveReflowAndBoundingMetricsFor frame!"); |
154 | 0 | if (reflowOutput) { |
155 | 0 | aReflowOutput = *reflowOutput; |
156 | 0 | aBoundingMetrics = reflowOutput->mBoundingMetrics; |
157 | 0 | } |
158 | 0 |
|
159 | 0 | if (aMathMLFrameType) { |
160 | 0 | if (!IsForeignChild(aFrame)) { |
161 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame); |
162 | 0 | if (mathMLFrame) { |
163 | 0 | *aMathMLFrameType = mathMLFrame->GetMathMLFrameType(); |
164 | 0 | return; |
165 | 0 | } |
166 | 0 | } |
167 | 0 | *aMathMLFrameType = eMathMLFrameType_UNKNOWN; |
168 | 0 | } |
169 | 0 |
|
170 | 0 | } |
171 | | |
172 | | void |
173 | | nsMathMLContainerFrame::ClearSavedChildMetrics() |
174 | 0 | { |
175 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
176 | 0 | while (childFrame) { |
177 | 0 | childFrame->DeleteProperty(HTMLReflowOutputProperty()); |
178 | 0 | childFrame = childFrame->GetNextSibling(); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | // helper to get the preferred size that a container frame should use to fire |
183 | | // the stretch on its stretchy child frames. |
184 | | void |
185 | | nsMathMLContainerFrame::GetPreferredStretchSize(DrawTarget* aDrawTarget, |
186 | | uint32_t aOptions, |
187 | | nsStretchDirection aStretchDirection, |
188 | | nsBoundingMetrics& aPreferredStretchSize) |
189 | 0 | { |
190 | 0 | if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) { |
191 | 0 | // when our actual size is ok, just use it |
192 | 0 | aPreferredStretchSize = mBoundingMetrics; |
193 | 0 | } |
194 | 0 | else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) { |
195 | 0 | // compute our up-to-date size using Place() |
196 | 0 | ReflowOutput reflowOutput(GetWritingMode()); |
197 | 0 | Place(aDrawTarget, false, reflowOutput); |
198 | 0 | aPreferredStretchSize = reflowOutput.mBoundingMetrics; |
199 | 0 | } |
200 | 0 | else { |
201 | 0 | // compute a size that includes embellishments iff the container stretches |
202 | 0 | // in the same direction as the embellished operator. |
203 | 0 | bool stretchAll = aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL ? |
204 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) : |
205 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags); |
206 | 0 | NS_ASSERTION(aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL || |
207 | 0 | aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL, |
208 | 0 | "You must specify a direction in which to stretch"); |
209 | 0 | NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) || |
210 | 0 | stretchAll, |
211 | 0 | "invalid call to GetPreferredStretchSize"); |
212 | 0 | bool firstTime = true; |
213 | 0 | nsBoundingMetrics bm, bmChild; |
214 | 0 | nsIFrame* childFrame = |
215 | 0 | stretchAll ? PrincipalChildList().FirstChild() : mPresentationData.baseFrame; |
216 | 0 | while (childFrame) { |
217 | 0 | // initializations in case this child happens not to be a MathML frame |
218 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame); |
219 | 0 | if (mathMLFrame) { |
220 | 0 | nsEmbellishData embellishData; |
221 | 0 | nsPresentationData presentationData; |
222 | 0 | mathMLFrame->GetEmbellishData(embellishData); |
223 | 0 | mathMLFrame->GetPresentationData(presentationData); |
224 | 0 | if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) && |
225 | 0 | embellishData.direction == aStretchDirection && |
226 | 0 | presentationData.baseFrame) { |
227 | 0 | // embellishements are not included, only consider the inner first child itself |
228 | 0 | // XXXkt Does that mean the core descendent frame should be used |
229 | 0 | // instead of the base child? |
230 | 0 | nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame); |
231 | 0 | if (mathMLchildFrame) { |
232 | 0 | mathMLFrame = mathMLchildFrame; |
233 | 0 | } |
234 | 0 | } |
235 | 0 | mathMLFrame->GetBoundingMetrics(bmChild); |
236 | 0 | } |
237 | 0 | else { |
238 | 0 | ReflowOutput unused(GetWritingMode()); |
239 | 0 | GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild); |
240 | 0 | } |
241 | 0 |
|
242 | 0 | if (firstTime) { |
243 | 0 | firstTime = false; |
244 | 0 | bm = bmChild; |
245 | 0 | if (!stretchAll) { |
246 | 0 | // we may get here for cases such as <msup><mo>...</mo> ... </msup>, |
247 | 0 | // or <maction>...<mo>...</mo></maction>. |
248 | 0 | break; |
249 | 0 | } |
250 | 0 | } |
251 | 0 | else { |
252 | 0 | if (aStretchDirection == NS_STRETCH_DIRECTION_HORIZONTAL) { |
253 | 0 | // if we get here, it means this is container that will stack its children |
254 | 0 | // vertically and fire an horizontal stretch on each them. This is the case |
255 | 0 | // for \munder, \mover, \munderover. We just sum-up the size vertically. |
256 | 0 | bm.descent += bmChild.ascent + bmChild.descent; |
257 | 0 | // Sometimes non-spacing marks (when width is zero) are positioned |
258 | 0 | // to the left of the origin, but it is the distance between left |
259 | 0 | // and right bearing that is important rather than the offsets from |
260 | 0 | // the origin. |
261 | 0 | if (bmChild.width == 0) { |
262 | 0 | bmChild.rightBearing -= bmChild.leftBearing; |
263 | 0 | bmChild.leftBearing = 0; |
264 | 0 | } |
265 | 0 | if (bm.leftBearing > bmChild.leftBearing) |
266 | 0 | bm.leftBearing = bmChild.leftBearing; |
267 | 0 | if (bm.rightBearing < bmChild.rightBearing) |
268 | 0 | bm.rightBearing = bmChild.rightBearing; |
269 | 0 | } |
270 | 0 | else if (aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) { |
271 | 0 | // just sum-up the sizes horizontally. |
272 | 0 | bm += bmChild; |
273 | 0 | } |
274 | 0 | else { |
275 | 0 | NS_ERROR("unexpected case in GetPreferredStretchSize"); |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | childFrame = childFrame->GetNextSibling(); |
280 | 0 | } |
281 | 0 | aPreferredStretchSize = bm; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | NS_IMETHODIMP |
286 | | nsMathMLContainerFrame::Stretch(DrawTarget* aDrawTarget, |
287 | | nsStretchDirection aStretchDirection, |
288 | | nsBoundingMetrics& aContainerSize, |
289 | | ReflowOutput& aDesiredStretchSize) |
290 | 0 | { |
291 | 0 | if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) { |
292 | 0 |
|
293 | 0 | if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) { |
294 | 0 | NS_WARNING("it is wrong to fire stretch more than once on a frame"); |
295 | 0 | return NS_OK; |
296 | 0 | } |
297 | 0 | mPresentationData.flags |= NS_MATHML_STRETCH_DONE; |
298 | 0 |
|
299 | 0 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) { |
300 | 0 | NS_WARNING("it is wrong to fire stretch on a erroneous frame"); |
301 | 0 | return NS_OK; |
302 | 0 | } |
303 | 0 |
|
304 | 0 | // Pass the stretch to the base child ... |
305 | 0 |
|
306 | 0 | nsIFrame* baseFrame = mPresentationData.baseFrame; |
307 | 0 | if (baseFrame) { |
308 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame); |
309 | 0 | NS_ASSERTION(mathMLFrame, "Something is wrong somewhere"); |
310 | 0 | if (mathMLFrame) { |
311 | 0 |
|
312 | 0 | // And the trick is that the child's rect.x is still holding the descent, |
313 | 0 | // and rect.y is still holding the ascent ... |
314 | 0 | ReflowOutput childSize(aDesiredStretchSize); |
315 | 0 | GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics); |
316 | 0 |
|
317 | 0 | // See if we should downsize and confine the stretch to us... |
318 | 0 | // XXX there may be other cases where we can downsize the stretch, |
319 | 0 | // e.g., the first ∑ might appear big in the following situation |
320 | 0 | // <math xmlns='http://www.w3.org/1998/Math/MathML'> |
321 | 0 | // <mstyle> |
322 | 0 | // <msub> |
323 | 0 | // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub> |
324 | 0 | // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub> |
325 | 0 | // </msub> |
326 | 0 | // </mstyle> |
327 | 0 | // </math> |
328 | 0 | nsBoundingMetrics containerSize = aContainerSize; |
329 | 0 | if (aStretchDirection != mEmbellishData.direction && |
330 | 0 | mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED) { |
331 | 0 | NS_ASSERTION(mEmbellishData.direction != NS_STRETCH_DIRECTION_DEFAULT, |
332 | 0 | "Stretches may have a default direction, operators can not."); |
333 | 0 | if (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL ? |
334 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) : |
335 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) { |
336 | 0 | GetPreferredStretchSize(aDrawTarget, 0, |
337 | 0 | mEmbellishData.direction, containerSize); |
338 | 0 | // Stop further recalculations |
339 | 0 | aStretchDirection = mEmbellishData.direction; |
340 | 0 | } else { |
341 | 0 | // We aren't going to stretch the child, so just use the child metrics. |
342 | 0 | containerSize = childSize.mBoundingMetrics; |
343 | 0 | } |
344 | 0 | } |
345 | 0 |
|
346 | 0 | // do the stretching... |
347 | 0 | mathMLFrame->Stretch(aDrawTarget, |
348 | 0 | aStretchDirection, containerSize, childSize); |
349 | 0 | // store the updated metrics |
350 | 0 | SaveReflowAndBoundingMetricsFor(baseFrame, childSize, |
351 | 0 | childSize.mBoundingMetrics); |
352 | 0 |
|
353 | 0 | // Remember the siblings which were _deferred_. |
354 | 0 | // Now that this embellished child may have changed, we need to |
355 | 0 | // fire the stretch on its siblings using our updated size |
356 | 0 |
|
357 | 0 | if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || |
358 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) { |
359 | 0 |
|
360 | 0 | nsStretchDirection stretchDir = |
361 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ? |
362 | 0 | NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL; |
363 | 0 |
|
364 | 0 | GetPreferredStretchSize(aDrawTarget, STRETCH_CONSIDER_EMBELLISHMENTS, |
365 | 0 | stretchDir, containerSize); |
366 | 0 |
|
367 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
368 | 0 | while (childFrame) { |
369 | 0 | if (childFrame != mPresentationData.baseFrame) { |
370 | 0 | mathMLFrame = do_QueryFrame(childFrame); |
371 | 0 | if (mathMLFrame) { |
372 | 0 | // retrieve the metrics that was stored at the previous pass |
373 | 0 | GetReflowAndBoundingMetricsFor(childFrame, |
374 | 0 | childSize, childSize.mBoundingMetrics); |
375 | 0 | // do the stretching... |
376 | 0 | mathMLFrame->Stretch(aDrawTarget, stretchDir, |
377 | 0 | containerSize, childSize); |
378 | 0 | // store the updated metrics |
379 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childSize, |
380 | 0 | childSize.mBoundingMetrics); |
381 | 0 | } |
382 | 0 | } |
383 | 0 | childFrame = childFrame->GetNextSibling(); |
384 | 0 | } |
385 | 0 | } |
386 | 0 |
|
387 | 0 | // re-position all our children |
388 | 0 | nsresult rv = Place(aDrawTarget, true, aDesiredStretchSize); |
389 | 0 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { |
390 | 0 | // Make sure the child frames get their DidReflow() calls. |
391 | 0 | DidReflowChildren(mFrames.FirstChild()); |
392 | 0 | } |
393 | 0 |
|
394 | 0 | // If our parent is not embellished, it means we are the outermost embellished |
395 | 0 | // container and so we put the spacing, otherwise we don't include the spacing, |
396 | 0 | // the outermost embellished container will take care of it. |
397 | 0 |
|
398 | 0 | nsEmbellishData parentData; |
399 | 0 | GetEmbellishDataFrom(GetParent(), parentData); |
400 | 0 | // ensure that we are the embellished child, not just a sibling |
401 | 0 | // (need to test coreFrame since <mfrac> resets other things) |
402 | 0 | if (parentData.coreFrame != mEmbellishData.coreFrame) { |
403 | 0 | // (we fetch values from the core since they may use units that depend |
404 | 0 | // on style data, and style changes could have occurred in the core since |
405 | 0 | // our last visit there) |
406 | 0 | nsEmbellishData coreData; |
407 | 0 | GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); |
408 | 0 |
|
409 | 0 | mBoundingMetrics.width += |
410 | 0 | coreData.leadingSpace + coreData.trailingSpace; |
411 | 0 | aDesiredStretchSize.Width() = mBoundingMetrics.width; |
412 | 0 | aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width; |
413 | 0 |
|
414 | 0 | nscoord dx = (StyleVisibility()->mDirection ? |
415 | 0 | coreData.trailingSpace : coreData.leadingSpace); |
416 | 0 | if (dx != 0) { |
417 | 0 | mBoundingMetrics.leftBearing += dx; |
418 | 0 | mBoundingMetrics.rightBearing += dx; |
419 | 0 | aDesiredStretchSize.mBoundingMetrics.leftBearing += dx; |
420 | 0 | aDesiredStretchSize.mBoundingMetrics.rightBearing += dx; |
421 | 0 |
|
422 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
423 | 0 | while (childFrame) { |
424 | 0 | childFrame->SetPosition(childFrame->GetPosition() |
425 | 0 | + nsPoint(dx, 0)); |
426 | 0 | childFrame = childFrame->GetNextSibling(); |
427 | 0 | } |
428 | 0 | } |
429 | 0 | } |
430 | 0 |
|
431 | 0 | // Finished with these: |
432 | 0 | ClearSavedChildMetrics(); |
433 | 0 | // Set our overflow area |
434 | 0 | GatherAndStoreOverflow(&aDesiredStretchSize); |
435 | 0 | } |
436 | 0 | } |
437 | 0 | } |
438 | 0 | return NS_OK; |
439 | 0 | } |
440 | | |
441 | | nsresult |
442 | | nsMathMLContainerFrame::FinalizeReflow(DrawTarget* aDrawTarget, |
443 | | ReflowOutput& aDesiredSize) |
444 | 0 | { |
445 | 0 | // During reflow, we use rect.x and rect.y as placeholders for the child's ascent |
446 | 0 | // and descent in expectation of a stretch command. Hence we need to ensure that |
447 | 0 | // a stretch command will actually be fired later on, after exiting from our |
448 | 0 | // reflow. If the stretch is not fired, the rect.x, and rect.y will remain |
449 | 0 | // with inappropriate data causing children to be improperly positioned. |
450 | 0 | // This helper method checks to see if our parent will fire a stretch command |
451 | 0 | // targeted at us. If not, we go ahead and fire an involutive stretch on |
452 | 0 | // ourselves. This will clear all the rect.x and rect.y, and return our |
453 | 0 | // desired size. |
454 | 0 |
|
455 | 0 |
|
456 | 0 | // First, complete the post-reflow hook. |
457 | 0 | // We use the information in our children rectangles to position them. |
458 | 0 | // If placeOrigin==false, then Place() will not touch rect.x, and rect.y. |
459 | 0 | // They will still be holding the ascent and descent for each child. |
460 | 0 |
|
461 | 0 | // The first clause caters for any non-embellished container. |
462 | 0 | // The second clause is for a container which won't fire stretch even though it is |
463 | 0 | // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted |
464 | 0 | // because it excludes the particular case of the core <mo>...</mo> itself. |
465 | 0 | // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it) |
466 | 0 | bool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) || |
467 | 0 | (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame && |
468 | 0 | mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED); |
469 | 0 | nsresult rv = Place(aDrawTarget, placeOrigin, aDesiredSize); |
470 | 0 |
|
471 | 0 | // Place() will call FinishReflowChild() when placeOrigin is true but if |
472 | 0 | // it returns before reaching FinishReflowChild() due to errors we need |
473 | 0 | // to fulfill the reflow protocol by calling DidReflow for the child frames |
474 | 0 | // that still needs it here (or we may crash - bug 366012). |
475 | 0 | // If placeOrigin is false we should reach Place() with aPlaceOrigin == true |
476 | 0 | // through Stretch() eventually. |
477 | 0 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { |
478 | 0 | GatherAndStoreOverflow(&aDesiredSize); |
479 | 0 | DidReflowChildren(PrincipalChildList().FirstChild()); |
480 | 0 | return rv; |
481 | 0 | } |
482 | 0 | |
483 | 0 | bool parentWillFireStretch = false; |
484 | 0 | if (!placeOrigin) { |
485 | 0 | // This means the rect.x and rect.y of our children were not set!! |
486 | 0 | // Don't go without checking to see if our parent will later fire a Stretch() command |
487 | 0 | // targeted at us. The Stretch() will cause the rect.x and rect.y to clear... |
488 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetParent()); |
489 | 0 | if (mathMLFrame) { |
490 | 0 | nsEmbellishData embellishData; |
491 | 0 | nsPresentationData presentationData; |
492 | 0 | mathMLFrame->GetEmbellishData(embellishData); |
493 | 0 | mathMLFrame->GetPresentationData(presentationData); |
494 | 0 | if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) || |
495 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) || |
496 | 0 | (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) |
497 | 0 | && presentationData.baseFrame == this)) |
498 | 0 | { |
499 | 0 | parentWillFireStretch = true; |
500 | 0 | } |
501 | 0 | } |
502 | 0 | if (!parentWillFireStretch) { |
503 | 0 | // There is nobody who will fire the stretch for us, we do it ourselves! |
504 | 0 |
|
505 | 0 | bool stretchAll = |
506 | 0 | /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */ |
507 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags); |
508 | 0 |
|
509 | 0 | nsStretchDirection stretchDir; |
510 | 0 | if (mEmbellishData.coreFrame == this || /* case of a bare <mo>...</mo> itself */ |
511 | 0 | (mEmbellishData.direction == NS_STRETCH_DIRECTION_HORIZONTAL && |
512 | 0 | stretchAll) || /* or <mover><mo>...</mo>...</mover>, or friends */ |
513 | 0 | mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) { /* Doesn't stretch */ |
514 | 0 | stretchDir = mEmbellishData.direction; |
515 | 0 | } else { |
516 | 0 | // Let the Stretch() call decide the direction. |
517 | 0 | stretchDir = NS_STRETCH_DIRECTION_DEFAULT; |
518 | 0 | } |
519 | 0 | // Use our current size as computed earlier by Place() |
520 | 0 | // The stretch call will detect if this is incorrect and recalculate the size. |
521 | 0 | nsBoundingMetrics defaultSize = aDesiredSize.mBoundingMetrics; |
522 | 0 |
|
523 | 0 | Stretch(aDrawTarget, stretchDir, defaultSize, aDesiredSize); |
524 | | #ifdef DEBUG |
525 | | { |
526 | | // The Place() call above didn't request FinishReflowChild(), |
527 | | // so let's check that we eventually did through Stretch(). |
528 | | for (nsIFrame* childFrame : PrincipalChildList()) { |
529 | | NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW), |
530 | | "DidReflow() was never called"); |
531 | | } |
532 | | } |
533 | | #endif |
534 | | } |
535 | 0 | } |
536 | 0 |
|
537 | 0 | // Also return our bounding metrics |
538 | 0 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
539 | 0 |
|
540 | 0 | // see if we should fix the spacing |
541 | 0 | FixInterFrameSpacing(aDesiredSize); |
542 | 0 |
|
543 | 0 | if (!parentWillFireStretch) { |
544 | 0 | // Not expecting a stretch. |
545 | 0 | // Finished with these: |
546 | 0 | ClearSavedChildMetrics(); |
547 | 0 | // Set our overflow area. |
548 | 0 | GatherAndStoreOverflow(&aDesiredSize); |
549 | 0 | } |
550 | 0 |
|
551 | 0 | return NS_OK; |
552 | 0 | } |
553 | | |
554 | | |
555 | | /* ///////////// |
556 | | * nsIMathMLFrame - support methods for scripting elements (nested frames |
557 | | * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts, |
558 | | * mfrac, mroot, mtable). |
559 | | * ============================================================================= |
560 | | */ |
561 | | |
562 | | // helper to let the update of presentation data pass through |
563 | | // a subtree that may contain non-mathml container frames |
564 | | /* static */ void |
565 | | nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame* aFrame, |
566 | | uint32_t aFlagsValues, |
567 | | uint32_t aFlagsToUpdate) |
568 | 0 | { |
569 | 0 | if (!aFrame || !aFlagsToUpdate) |
570 | 0 | return; |
571 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame); |
572 | 0 | if (mathMLFrame) { |
573 | 0 | // update |
574 | 0 | mathMLFrame->UpdatePresentationData(aFlagsValues, |
575 | 0 | aFlagsToUpdate); |
576 | 0 | // propagate using the base method to make sure that the control |
577 | 0 | // is passed on to MathML frames that may be overloading the method |
578 | 0 | mathMLFrame->UpdatePresentationDataFromChildAt(0, -1, |
579 | 0 | aFlagsValues, aFlagsToUpdate); |
580 | 0 | } |
581 | 0 | else { |
582 | 0 | // propagate down the subtrees |
583 | 0 | for (nsIFrame* childFrame : aFrame->PrincipalChildList()) { |
584 | 0 | PropagatePresentationDataFor(childFrame, |
585 | 0 | aFlagsValues, aFlagsToUpdate); |
586 | 0 | } |
587 | 0 | } |
588 | 0 | } |
589 | | |
590 | | /* static */ void |
591 | | nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame* aParentFrame, |
592 | | int32_t aFirstChildIndex, |
593 | | int32_t aLastChildIndex, |
594 | | uint32_t aFlagsValues, |
595 | | uint32_t aFlagsToUpdate) |
596 | 0 | { |
597 | 0 | if (!aParentFrame || !aFlagsToUpdate) |
598 | 0 | return; |
599 | 0 | int32_t index = 0; |
600 | 0 | for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) { |
601 | 0 | if ((index >= aFirstChildIndex) && |
602 | 0 | ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) && |
603 | 0 | (index <= aLastChildIndex)))) { |
604 | 0 | PropagatePresentationDataFor(childFrame, |
605 | 0 | aFlagsValues, aFlagsToUpdate); |
606 | 0 | } |
607 | 0 | index++; |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | /* ////////////////// |
612 | | * Frame construction |
613 | | * ============================================================================= |
614 | | */ |
615 | | |
616 | | |
617 | | void |
618 | | nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
619 | | const nsDisplayListSet& aLists) |
620 | 0 | { |
621 | 0 | // report an error if something wrong was found in this frame |
622 | 0 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) { |
623 | 0 | if (!IsVisibleForPainting(aBuilder)) |
624 | 0 | return; |
625 | 0 | |
626 | 0 | aLists.Content()->AppendToTop( |
627 | 0 | MakeDisplayItem<nsDisplayMathMLError>(aBuilder, this)); |
628 | 0 | return; |
629 | 0 | } |
630 | 0 | |
631 | 0 | DisplayBorderBackgroundOutline(aBuilder, aLists); |
632 | 0 |
|
633 | 0 | BuildDisplayListForNonBlockChildren(aBuilder, aLists, DISPLAY_CHILD_INLINE); |
634 | 0 |
|
635 | | #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) |
636 | | // for visual debug |
637 | | // ---------------- |
638 | | // if you want to see your bounding box, make sure to properly fill |
639 | | // your mBoundingMetrics and mReference point, and set |
640 | | // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS |
641 | | // in the Init() of your sub-class |
642 | | DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists); |
643 | | #endif |
644 | | } |
645 | | |
646 | | // Note that this method re-builds the automatic data in the children -- not |
647 | | // in aParentFrame itself (except for those particular operations that the |
648 | | // parent frame may do in its TransmitAutomaticData()). |
649 | | /* static */ void |
650 | | nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame) |
651 | 0 | { |
652 | 0 | // 1. As we descend the tree, make each child frame inherit data from |
653 | 0 | // the parent |
654 | 0 | // 2. As we ascend the tree, transmit any specific change that we want |
655 | 0 | // down the subtrees |
656 | 0 | for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) { |
657 | 0 | nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame); |
658 | 0 | if (childMathMLFrame) { |
659 | 0 | childMathMLFrame->InheritAutomaticData(aParentFrame); |
660 | 0 | } |
661 | 0 | RebuildAutomaticDataForChildren(childFrame); |
662 | 0 | } |
663 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame); |
664 | 0 | if (mathMLFrame) { |
665 | 0 | mathMLFrame->TransmitAutomaticData(); |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | /* static */ nsresult |
670 | | nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame) |
671 | 0 | { |
672 | 0 | if (!aParentFrame) |
673 | 0 | return NS_OK; |
674 | 0 | |
675 | 0 | // walk-up to the first frame that is a MathML frame, stop if we reach <math> |
676 | 0 | nsIFrame* frame = aParentFrame; |
677 | 0 | while (1) { |
678 | 0 | nsIFrame* parent = frame->GetParent(); |
679 | 0 | if (!parent || !parent->GetContent()) |
680 | 0 | break; |
681 | 0 | |
682 | 0 | // stop if it is a MathML frame |
683 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame); |
684 | 0 | if (mathMLFrame) |
685 | 0 | break; |
686 | 0 | |
687 | 0 | // stop if we reach the root <math> tag |
688 | 0 | nsIContent* content = frame->GetContent(); |
689 | 0 | NS_ASSERTION(content, "dangling frame without a content node"); |
690 | 0 | if (!content) |
691 | 0 | break; |
692 | 0 | if (content->IsMathMLElement(nsGkAtoms::math)) |
693 | 0 | break; |
694 | 0 | |
695 | 0 | frame = parent; |
696 | 0 | } |
697 | 0 |
|
698 | 0 | // re-sync the presentation data and embellishment data of our children |
699 | 0 | RebuildAutomaticDataForChildren(frame); |
700 | 0 |
|
701 | 0 | // Ask our parent frame to reflow us |
702 | 0 | nsIFrame* parent = frame->GetParent(); |
703 | 0 | NS_ASSERTION(parent, "No parent to pass the reflow request up to"); |
704 | 0 | if (!parent) |
705 | 0 | return NS_OK; |
706 | 0 | |
707 | 0 | frame->PresShell()-> |
708 | 0 | FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); |
709 | 0 |
|
710 | 0 | return NS_OK; |
711 | 0 | } |
712 | | |
713 | | // There are precise rules governing children of a MathML frame, |
714 | | // and properties such as the scriptlevel depends on those rules. |
715 | | // Hence for things to work, callers must use Append/Insert/etc wisely. |
716 | | |
717 | | nsresult |
718 | | nsMathMLContainerFrame::ChildListChanged(int32_t aModType) |
719 | 0 | { |
720 | 0 | // If this is an embellished frame we need to rebuild the |
721 | 0 | // embellished hierarchy by walking-up to the parent of the |
722 | 0 | // outermost embellished container. |
723 | 0 | nsIFrame* frame = this; |
724 | 0 | if (mEmbellishData.coreFrame) { |
725 | 0 | nsIFrame* parent = GetParent(); |
726 | 0 | nsEmbellishData embellishData; |
727 | 0 | for ( ; parent; frame = parent, parent = parent->GetParent()) { |
728 | 0 | GetEmbellishDataFrom(parent, embellishData); |
729 | 0 | if (embellishData.coreFrame != mEmbellishData.coreFrame) |
730 | 0 | break; |
731 | 0 | } |
732 | 0 | } |
733 | 0 | return ReLayoutChildren(frame); |
734 | 0 | } |
735 | | |
736 | | void |
737 | | nsMathMLContainerFrame::AppendFrames(ChildListID aListID, |
738 | | nsFrameList& aFrameList) |
739 | 0 | { |
740 | 0 | MOZ_ASSERT(aListID == kPrincipalList); |
741 | 0 | mFrames.AppendFrames(this, aFrameList); |
742 | 0 | ChildListChanged(dom::MutationEvent_Binding::ADDITION); |
743 | 0 | } |
744 | | |
745 | | void |
746 | | nsMathMLContainerFrame::InsertFrames(ChildListID aListID, |
747 | | nsIFrame* aPrevFrame, |
748 | | nsFrameList& aFrameList) |
749 | 0 | { |
750 | 0 | MOZ_ASSERT(aListID == kPrincipalList); |
751 | 0 | mFrames.InsertFrames(this, aPrevFrame, aFrameList); |
752 | 0 | ChildListChanged(dom::MutationEvent_Binding::ADDITION); |
753 | 0 | } |
754 | | |
755 | | void |
756 | | nsMathMLContainerFrame::RemoveFrame(ChildListID aListID, |
757 | | nsIFrame* aOldFrame) |
758 | 0 | { |
759 | 0 | MOZ_ASSERT(aListID == kPrincipalList); |
760 | 0 | mFrames.DestroyFrame(aOldFrame); |
761 | 0 | ChildListChanged(dom::MutationEvent_Binding::REMOVAL); |
762 | 0 | } |
763 | | |
764 | | nsresult |
765 | | nsMathMLContainerFrame::AttributeChanged(int32_t aNameSpaceID, |
766 | | nsAtom* aAttribute, |
767 | | int32_t aModType) |
768 | 0 | { |
769 | 0 | // XXX Since they are numerous MathML attributes that affect layout, and |
770 | 0 | // we can't check all of them here, play safe by requesting a reflow. |
771 | 0 | // XXXldb This should only do work for attributes that cause changes! |
772 | 0 | PresShell()-> |
773 | 0 | FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); |
774 | 0 |
|
775 | 0 | return NS_OK; |
776 | 0 | } |
777 | | |
778 | | void |
779 | | nsMathMLContainerFrame::GatherAndStoreOverflow(ReflowOutput* aMetrics) |
780 | 0 | { |
781 | 0 | mBlockStartAscent = aMetrics->BlockStartAscent(); |
782 | 0 |
|
783 | 0 | // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the |
784 | 0 | // frame rectangle. |
785 | 0 | aMetrics->SetOverflowAreasToDesiredBounds(); |
786 | 0 |
|
787 | 0 | ComputeCustomOverflow(aMetrics->mOverflowAreas); |
788 | 0 |
|
789 | 0 | // mBoundingMetrics does not necessarily include content of <mpadded> |
790 | 0 | // elements whose mBoundingMetrics may not be representative of the true |
791 | 0 | // bounds, and doesn't include the CSS2 outline rectangles of children, so |
792 | 0 | // make such to include child overflow areas. |
793 | 0 | UnionChildOverflow(aMetrics->mOverflowAreas); |
794 | 0 |
|
795 | 0 | FinishAndStoreOverflow(aMetrics); |
796 | 0 | } |
797 | | |
798 | | bool |
799 | | nsMathMLContainerFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) |
800 | 0 | { |
801 | 0 | // All non-child-frame content such as nsMathMLChars (and most child-frame |
802 | 0 | // content) is included in mBoundingMetrics. |
803 | 0 | nsRect boundingBox(mBoundingMetrics.leftBearing, |
804 | 0 | mBlockStartAscent - mBoundingMetrics.ascent, |
805 | 0 | mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing, |
806 | 0 | mBoundingMetrics.ascent + mBoundingMetrics.descent); |
807 | 0 |
|
808 | 0 | // REVIEW: Maybe this should contribute only to visual overflow |
809 | 0 | // and not scrollable? |
810 | 0 | aOverflowAreas.UnionAllWith(boundingBox); |
811 | 0 | return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas); |
812 | 0 | } |
813 | | |
814 | | void |
815 | | nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame, |
816 | | nsPresContext* aPresContext, |
817 | | ReflowOutput& aDesiredSize, |
818 | | const ReflowInput& aReflowInput, |
819 | | nsReflowStatus& aStatus) |
820 | 0 | { |
821 | 0 | // Having foreign/hybrid children, e.g., from html markups, is not defined by |
822 | 0 | // the MathML spec. But it can happen in practice, e.g., <html:img> allows us |
823 | 0 | // to do some cool demos... or we may have a child that is an nsInlineFrame |
824 | 0 | // from a generated content such as :before { content: open-quote } or |
825 | 0 | // :after { content: close-quote }. Unfortunately, the other frames out-there |
826 | 0 | // may expect their own invariants that are not met when we mix things. |
827 | 0 | // Hence we do not claim their support, but we will nevertheless attempt to keep |
828 | 0 | // them in the flow, if we can get their desired size. We observed that most |
829 | 0 | // frames may be reflowed generically, but nsInlineFrames need extra care. |
830 | 0 |
|
831 | | #ifdef DEBUG |
832 | | nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame); |
833 | | NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks"); |
834 | | #endif |
835 | |
|
836 | 0 | nsContainerFrame:: |
837 | 0 | ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowInput, |
838 | 0 | 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); |
839 | 0 |
|
840 | 0 | if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) { |
841 | 0 | // This will be suitable for inline frames, which are wrapped in a block. |
842 | 0 | nscoord ascent; |
843 | 0 | WritingMode wm = aDesiredSize.GetWritingMode(); |
844 | 0 | if (!nsLayoutUtils::GetLastLineBaseline(wm, aChildFrame, &ascent)) { |
845 | 0 | // We don't expect any other block children so just place the frame on |
846 | 0 | // the baseline instead of going through DidReflow() and |
847 | 0 | // GetBaseline(). This is what nsFrame::GetBaseline() will do anyway. |
848 | 0 | aDesiredSize.SetBlockStartAscent(aDesiredSize.BSize(wm)); |
849 | 0 | } else { |
850 | 0 | aDesiredSize.SetBlockStartAscent(ascent); |
851 | 0 | } |
852 | 0 | } |
853 | 0 | if (IsForeignChild(aChildFrame)) { |
854 | 0 | // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set. |
855 | 0 | nsRect r = aChildFrame->ComputeTightBounds(aReflowInput.mRenderingContext->GetDrawTarget()); |
856 | 0 | aDesiredSize.mBoundingMetrics.leftBearing = r.x; |
857 | 0 | aDesiredSize.mBoundingMetrics.rightBearing = r.XMost(); |
858 | 0 | aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent() - r.y; |
859 | 0 | aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.BlockStartAscent(); |
860 | 0 | aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width(); |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | | void |
865 | | nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext, |
866 | | ReflowOutput& aDesiredSize, |
867 | | const ReflowInput& aReflowInput, |
868 | | nsReflowStatus& aStatus) |
869 | 0 | { |
870 | 0 | MarkInReflow(); |
871 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
872 | 0 |
|
873 | 0 | mPresentationData.flags &= ~NS_MATHML_ERROR; |
874 | 0 | aDesiredSize.Width() = aDesiredSize.Height() = 0; |
875 | 0 | aDesiredSize.SetBlockStartAscent(0); |
876 | 0 | aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); |
877 | 0 |
|
878 | 0 | ///////////// |
879 | 0 | // Reflow children |
880 | 0 | // Asking each child to cache its bounding metrics |
881 | 0 |
|
882 | 0 | nsReflowStatus childStatus; |
883 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
884 | 0 | while (childFrame) { |
885 | 0 | ReflowOutput childDesiredSize(aReflowInput); |
886 | 0 | WritingMode wm = childFrame->GetWritingMode(); |
887 | 0 | LogicalSize availSize = aReflowInput.ComputedSize(wm); |
888 | 0 | availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; |
889 | 0 | ReflowInput childReflowInput(aPresContext, aReflowInput, |
890 | 0 | childFrame, availSize); |
891 | 0 | ReflowChild(childFrame, aPresContext, childDesiredSize, |
892 | 0 | childReflowInput, childStatus); |
893 | 0 | //NS_ASSERTION(childStatus.IsComplete(), "bad status"); |
894 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
895 | 0 | childDesiredSize.mBoundingMetrics); |
896 | 0 | childFrame = childFrame->GetNextSibling(); |
897 | 0 | } |
898 | 0 |
|
899 | 0 | ///////////// |
900 | 0 | // If we are a container which is entitled to stretch its children, then we |
901 | 0 | // ask our stretchy children to stretch themselves |
902 | 0 |
|
903 | 0 | // The stretching of siblings of an embellished child is _deferred_ until |
904 | 0 | // after finishing the stretching of the embellished child - bug 117652 |
905 | 0 |
|
906 | 0 | DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget(); |
907 | 0 |
|
908 | 0 | if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) && |
909 | 0 | (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || |
910 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) { |
911 | 0 |
|
912 | 0 | // get the stretchy direction |
913 | 0 | nsStretchDirection stretchDir = |
914 | 0 | NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) |
915 | 0 | ? NS_STRETCH_DIRECTION_VERTICAL |
916 | 0 | : NS_STRETCH_DIRECTION_HORIZONTAL; |
917 | 0 |
|
918 | 0 | // what size should we use to stretch our stretchy children |
919 | 0 | // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet |
920 | 0 | // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to |
921 | 0 | // include them in the caculations of the size of stretchy elements |
922 | 0 | nsBoundingMetrics containerSize; |
923 | 0 | GetPreferredStretchSize(drawTarget, 0, stretchDir, containerSize); |
924 | 0 |
|
925 | 0 | // fire the stretch on each child |
926 | 0 | childFrame = mFrames.FirstChild(); |
927 | 0 | while (childFrame) { |
928 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame); |
929 | 0 | if (mathMLFrame) { |
930 | 0 | // retrieve the metrics that was stored at the previous pass |
931 | 0 | ReflowOutput childDesiredSize(aReflowInput); |
932 | 0 | GetReflowAndBoundingMetricsFor(childFrame, |
933 | 0 | childDesiredSize, childDesiredSize.mBoundingMetrics); |
934 | 0 |
|
935 | 0 | mathMLFrame->Stretch(drawTarget, stretchDir, |
936 | 0 | containerSize, childDesiredSize); |
937 | 0 | // store the updated metrics |
938 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
939 | 0 | childDesiredSize.mBoundingMetrics); |
940 | 0 | } |
941 | 0 | childFrame = childFrame->GetNextSibling(); |
942 | 0 | } |
943 | 0 | } |
944 | 0 |
|
945 | 0 | ///////////// |
946 | 0 | // Place children now by re-adjusting the origins to align the baselines |
947 | 0 | FinalizeReflow(drawTarget, aDesiredSize); |
948 | 0 |
|
949 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
950 | 0 | } |
951 | | |
952 | | static nscoord AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize, |
953 | | nsMathMLContainerFrame* aFrame); |
954 | | |
955 | | /* virtual */ void |
956 | | nsMathMLContainerFrame::MarkIntrinsicISizesDirty() |
957 | 0 | { |
958 | 0 | mIntrinsicWidth = NS_INTRINSIC_WIDTH_UNKNOWN; |
959 | 0 | nsContainerFrame::MarkIntrinsicISizesDirty(); |
960 | 0 | } |
961 | | |
962 | | void |
963 | | nsMathMLContainerFrame::UpdateIntrinsicWidth(gfxContext* aRenderingContext) |
964 | 0 | { |
965 | 0 | if (mIntrinsicWidth == NS_INTRINSIC_WIDTH_UNKNOWN) { |
966 | 0 | ReflowOutput desiredSize(GetWritingMode()); |
967 | 0 | GetIntrinsicISizeMetrics(aRenderingContext, desiredSize); |
968 | 0 |
|
969 | 0 | // Include the additional width added by FixInterFrameSpacing to ensure |
970 | 0 | // consistent width calculations. |
971 | 0 | AddInterFrameSpacingToSize(desiredSize, this); |
972 | 0 | mIntrinsicWidth = desiredSize.ISize(GetWritingMode()); |
973 | 0 | } |
974 | 0 | } |
975 | | |
976 | | /* virtual */ nscoord |
977 | | nsMathMLContainerFrame::GetMinISize(gfxContext* aRenderingContext) |
978 | 0 | { |
979 | 0 | nscoord result; |
980 | 0 | DISPLAY_MIN_INLINE_SIZE(this, result); |
981 | 0 | UpdateIntrinsicWidth(aRenderingContext); |
982 | 0 | result = mIntrinsicWidth; |
983 | 0 | return result; |
984 | 0 | } |
985 | | |
986 | | /* virtual */ nscoord |
987 | | nsMathMLContainerFrame::GetPrefISize(gfxContext* aRenderingContext) |
988 | 0 | { |
989 | 0 | nscoord result; |
990 | 0 | DISPLAY_PREF_INLINE_SIZE(this, result); |
991 | 0 | UpdateIntrinsicWidth(aRenderingContext); |
992 | 0 | result = mIntrinsicWidth; |
993 | 0 | return result; |
994 | 0 | } |
995 | | |
996 | | /* virtual */ void |
997 | | nsMathMLContainerFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext, |
998 | | ReflowOutput& aDesiredSize) |
999 | 0 | { |
1000 | 0 | // Get child widths |
1001 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
1002 | 0 | while (childFrame) { |
1003 | 0 | ReflowOutput childDesiredSize(GetWritingMode()); // ??? |
1004 | 0 |
|
1005 | 0 | nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame); |
1006 | 0 | if (containerFrame) { |
1007 | 0 | containerFrame->GetIntrinsicISizeMetrics(aRenderingContext, |
1008 | 0 | childDesiredSize); |
1009 | 0 | } else { |
1010 | 0 | // XXX This includes margin while Reflow currently doesn't consider |
1011 | 0 | // margin, so we may end up with too much space, but, with stretchy |
1012 | 0 | // characters, this is an approximation anyway. |
1013 | 0 | nscoord width = |
1014 | 0 | nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame, |
1015 | 0 | nsLayoutUtils::PREF_ISIZE); |
1016 | 0 |
|
1017 | 0 | childDesiredSize.Width() = width; |
1018 | 0 | childDesiredSize.mBoundingMetrics.width = width; |
1019 | 0 | childDesiredSize.mBoundingMetrics.leftBearing = 0; |
1020 | 0 | childDesiredSize.mBoundingMetrics.rightBearing = width; |
1021 | 0 |
|
1022 | 0 | nscoord x, xMost; |
1023 | 0 | if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext, |
1024 | 0 | &x, &xMost))) { |
1025 | 0 | childDesiredSize.mBoundingMetrics.leftBearing = x; |
1026 | 0 | childDesiredSize.mBoundingMetrics.rightBearing = xMost; |
1027 | 0 | } |
1028 | 0 | } |
1029 | 0 |
|
1030 | 0 | SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, |
1031 | 0 | childDesiredSize.mBoundingMetrics); |
1032 | 0 |
|
1033 | 0 | childFrame = childFrame->GetNextSibling(); |
1034 | 0 | } |
1035 | 0 |
|
1036 | 0 | // Measure |
1037 | 0 | nsresult rv = MeasureForWidth(aRenderingContext->GetDrawTarget(), aDesiredSize); |
1038 | 0 | if (NS_FAILED(rv)) { |
1039 | 0 | ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize); |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | ClearSavedChildMetrics(); |
1043 | 0 | } |
1044 | | |
1045 | | /* virtual */ nsresult |
1046 | | nsMathMLContainerFrame::MeasureForWidth(DrawTarget* aDrawTarget, |
1047 | | ReflowOutput& aDesiredSize) |
1048 | 0 | { |
1049 | 0 | return Place(aDrawTarget, false, aDesiredSize); |
1050 | 0 | } |
1051 | | |
1052 | | |
1053 | | // see spacing table in Chapter 18, TeXBook (p.170) |
1054 | | // Our table isn't quite identical to TeX because operators have |
1055 | | // built-in values for lspace & rspace in the Operator Dictionary. |
1056 | | static int32_t kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] = |
1057 | | { |
1058 | | // in units of muspace. |
1059 | | // upper half of the byte is set if the |
1060 | | // spacing is not to be used for scriptlevel > 0 |
1061 | | |
1062 | | /* Ord OpOrd OpInv OpUsr Inner Italic Upright */ |
1063 | | /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00}, |
1064 | | /*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |
1065 | | /*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |
1066 | | /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}, |
1067 | | /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}, |
1068 | | /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01}, |
1069 | | /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00} |
1070 | | }; |
1071 | | |
1072 | | #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \ |
1073 | | /* no space if there is a frame that we know nothing about */ \ |
1074 | 0 | if (frametype1_ == eMathMLFrameType_UNKNOWN || \ |
1075 | 0 | frametype2_ == eMathMLFrameType_UNKNOWN) \ |
1076 | 0 | space_ = 0; \ |
1077 | 0 | else { \ |
1078 | 0 | space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \ |
1079 | 0 | space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \ |
1080 | 0 | ? 0 /* spacing is disabled */ \ |
1081 | 0 | : space_ & 0x0F; \ |
1082 | 0 | } \ |
1083 | | |
1084 | | // This function computes the inter-space between two frames. However, |
1085 | | // since invisible operators need special treatment, the inter-space may |
1086 | | // be delayed when an invisible operator is encountered. In this case, |
1087 | | // the function will carry the inter-space forward until it is determined |
1088 | | // that it can be applied properly (i.e., until we encounter a visible |
1089 | | // frame where to decide whether to accept or reject the inter-space). |
1090 | | // aFromFrameType: remembers the frame when the carry-forward initiated. |
1091 | | // aCarrySpace: keeps track of the inter-space that is delayed. |
1092 | | // @returns: current inter-space (which is 0 when the true inter-space is |
1093 | | // delayed -- and thus has no effect since the frame is invisible anyway). |
1094 | | static nscoord |
1095 | | GetInterFrameSpacing(int32_t aScriptLevel, |
1096 | | eMathMLFrameType aFirstFrameType, |
1097 | | eMathMLFrameType aSecondFrameType, |
1098 | | eMathMLFrameType* aFromFrameType, // IN/OUT |
1099 | | int32_t* aCarrySpace) // IN/OUT |
1100 | 0 | { |
1101 | 0 | eMathMLFrameType firstType = aFirstFrameType; |
1102 | 0 | eMathMLFrameType secondType = aSecondFrameType; |
1103 | 0 |
|
1104 | 0 | int32_t space; |
1105 | 0 | GET_INTERSPACE(aScriptLevel, firstType, secondType, space); |
1106 | 0 |
|
1107 | 0 | // feedback control to avoid the inter-space to be added when not necessary |
1108 | 0 | if (secondType == eMathMLFrameType_OperatorInvisible) { |
1109 | 0 | // see if we should start to carry the space forward until we |
1110 | 0 | // encounter a visible frame |
1111 | 0 | if (*aFromFrameType == eMathMLFrameType_UNKNOWN) { |
1112 | 0 | *aFromFrameType = firstType; |
1113 | 0 | *aCarrySpace = space; |
1114 | 0 | } |
1115 | 0 | // keep carrying *aCarrySpace forward, while returning 0 for this stage |
1116 | 0 | space = 0; |
1117 | 0 | } |
1118 | 0 | else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) { |
1119 | 0 | // no carry-forward anymore, get the real inter-space between |
1120 | 0 | // the two frames of interest |
1121 | 0 |
|
1122 | 0 | firstType = *aFromFrameType; |
1123 | 0 |
|
1124 | 0 | // But... the invisible operator that we encountered earlier could |
1125 | 0 | // be sitting between italic and upright identifiers, e.g., |
1126 | 0 | // |
1127 | 0 | // 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi> |
1128 | 0 | // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi> |
1129 | 0 | // |
1130 | 0 | // the trick to get the inter-space in either situation |
1131 | 0 | // is to promote "<mi>sin</mi><mo>⁡</mo>" and |
1132 | 0 | // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators... |
1133 | 0 | if (firstType == eMathMLFrameType_UprightIdentifier) { |
1134 | 0 | firstType = eMathMLFrameType_OperatorUserDefined; |
1135 | 0 | } |
1136 | 0 | else if (secondType == eMathMLFrameType_UprightIdentifier) { |
1137 | 0 | secondType = eMathMLFrameType_OperatorUserDefined; |
1138 | 0 | } |
1139 | 0 |
|
1140 | 0 | GET_INTERSPACE(aScriptLevel, firstType, secondType, space); |
1141 | 0 |
|
1142 | 0 | // Now, we have two values: the computed space and the space that |
1143 | 0 | // has been carried forward until now. Which value do we pick? |
1144 | 0 | // If the second type is an operator (e.g., fence), it already has |
1145 | 0 | // built-in lspace & rspace, so we let them win. Otherwise we pick |
1146 | 0 | // the max between the two values that we have. |
1147 | 0 | if (secondType != eMathMLFrameType_OperatorOrdinary && |
1148 | 0 | space < *aCarrySpace) |
1149 | 0 | space = *aCarrySpace; |
1150 | 0 |
|
1151 | 0 | // reset everything now that the carry-forward is done |
1152 | 0 | *aFromFrameType = eMathMLFrameType_UNKNOWN; |
1153 | 0 | *aCarrySpace = 0; |
1154 | 0 | } |
1155 | 0 |
|
1156 | 0 | return space; |
1157 | 0 | } |
1158 | | |
1159 | | static nscoord GetThinSpace(const nsStyleFont* aStyleFont) |
1160 | 0 | { |
1161 | 0 | return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18)); |
1162 | 0 | } |
1163 | | |
1164 | | class nsMathMLContainerFrame::RowChildFrameIterator { |
1165 | | public: |
1166 | | explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) |
1167 | | : mParentFrame(aParentFrame) |
1168 | | , mReflowOutput(aParentFrame->GetWritingMode()) |
1169 | | , mX(0) |
1170 | | , mChildFrameType(eMathMLFrameType_UNKNOWN) |
1171 | | , mCarrySpace(0) |
1172 | | , mFromFrameType(eMathMLFrameType_UNKNOWN) |
1173 | | , mRTL(aParentFrame->StyleVisibility()->mDirection) |
1174 | 0 | { |
1175 | 0 | if (!mRTL) { |
1176 | 0 | mChildFrame = aParentFrame->mFrames.FirstChild(); |
1177 | 0 | } else { |
1178 | 0 | mChildFrame = aParentFrame->mFrames.LastChild(); |
1179 | 0 | } |
1180 | 0 |
|
1181 | 0 | if (!mChildFrame) |
1182 | 0 | return; |
1183 | 0 | |
1184 | 0 | InitMetricsForChild(); |
1185 | 0 | } |
1186 | | |
1187 | | RowChildFrameIterator& operator++() |
1188 | 0 | { |
1189 | 0 | // add child size + italic correction |
1190 | 0 | mX += mReflowOutput.mBoundingMetrics.width + mItalicCorrection; |
1191 | 0 |
|
1192 | 0 | if (!mRTL) { |
1193 | 0 | mChildFrame = mChildFrame->GetNextSibling(); |
1194 | 0 | } else { |
1195 | 0 | mChildFrame = mChildFrame->GetPrevSibling(); |
1196 | 0 | } |
1197 | 0 |
|
1198 | 0 | if (!mChildFrame) |
1199 | 0 | return *this; |
1200 | 0 | |
1201 | 0 | eMathMLFrameType prevFrameType = mChildFrameType; |
1202 | 0 | InitMetricsForChild(); |
1203 | 0 |
|
1204 | 0 | // add inter frame spacing |
1205 | 0 | const nsStyleFont* font = mParentFrame->StyleFont(); |
1206 | 0 | nscoord space = |
1207 | 0 | GetInterFrameSpacing(font->mScriptLevel, |
1208 | 0 | prevFrameType, mChildFrameType, |
1209 | 0 | &mFromFrameType, &mCarrySpace); |
1210 | 0 | mX += space * GetThinSpace(font); |
1211 | 0 | return *this; |
1212 | 0 | } |
1213 | | |
1214 | 0 | nsIFrame* Frame() const { return mChildFrame; } |
1215 | 0 | nscoord X() const { return mX; } |
1216 | 0 | const ReflowOutput& GetReflowOutput() const { return mReflowOutput; } |
1217 | 0 | nscoord Ascent() const { return mReflowOutput.BlockStartAscent(); } |
1218 | 0 | nscoord Descent() const { return mReflowOutput.Height() - mReflowOutput.BlockStartAscent(); } |
1219 | 0 | const nsBoundingMetrics& BoundingMetrics() const { |
1220 | 0 | return mReflowOutput.mBoundingMetrics; |
1221 | 0 | } |
1222 | | |
1223 | | private: |
1224 | | const nsMathMLContainerFrame* mParentFrame; |
1225 | | nsIFrame* mChildFrame; |
1226 | | ReflowOutput mReflowOutput; |
1227 | | nscoord mX; |
1228 | | |
1229 | | nscoord mItalicCorrection; |
1230 | | eMathMLFrameType mChildFrameType; |
1231 | | int32_t mCarrySpace; |
1232 | | eMathMLFrameType mFromFrameType; |
1233 | | |
1234 | | bool mRTL; |
1235 | | |
1236 | | void InitMetricsForChild() |
1237 | 0 | { |
1238 | 0 | GetReflowAndBoundingMetricsFor(mChildFrame, mReflowOutput, mReflowOutput.mBoundingMetrics, |
1239 | 0 | &mChildFrameType); |
1240 | 0 | nscoord leftCorrection, rightCorrection; |
1241 | 0 | GetItalicCorrection(mReflowOutput.mBoundingMetrics, |
1242 | 0 | leftCorrection, rightCorrection); |
1243 | 0 | if (!mChildFrame->GetPrevSibling() && |
1244 | 0 | mParentFrame->GetContent()->IsMathMLElement(nsGkAtoms::msqrt_)) { |
1245 | 0 | // Remove leading correction in <msqrt> because the sqrt glyph itself is |
1246 | 0 | // there first. |
1247 | 0 | if (!mRTL) { |
1248 | 0 | leftCorrection = 0; |
1249 | 0 | } else { |
1250 | 0 | rightCorrection = 0; |
1251 | 0 | } |
1252 | 0 | } |
1253 | 0 | // add left correction -- this fixes the problem of the italic 'f' |
1254 | 0 | // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo> |
1255 | 0 | mX += leftCorrection; |
1256 | 0 | mItalicCorrection = rightCorrection; |
1257 | 0 | } |
1258 | | }; |
1259 | | |
1260 | | /* virtual */ nsresult |
1261 | | nsMathMLContainerFrame::Place(DrawTarget* aDrawTarget, |
1262 | | bool aPlaceOrigin, |
1263 | | ReflowOutput& aDesiredSize) |
1264 | 0 | { |
1265 | 0 | // This is needed in case this frame is empty (i.e., no child frames) |
1266 | 0 | mBoundingMetrics = nsBoundingMetrics(); |
1267 | 0 |
|
1268 | 0 | RowChildFrameIterator child(this); |
1269 | 0 | nscoord ascent = 0, descent = 0; |
1270 | 0 | while (child.Frame()) { |
1271 | 0 | if (descent < child.Descent()) |
1272 | 0 | descent = child.Descent(); |
1273 | 0 | if (ascent < child.Ascent()) |
1274 | 0 | ascent = child.Ascent(); |
1275 | 0 | // add the child size |
1276 | 0 | mBoundingMetrics.width = child.X(); |
1277 | 0 | mBoundingMetrics += child.BoundingMetrics(); |
1278 | 0 | ++child; |
1279 | 0 | } |
1280 | 0 | // Add the italic correction at the end (including the last child). |
1281 | 0 | // This gives a nice gap between math and non-math frames, and still |
1282 | 0 | // gives the same math inter-spacing in case this frame connects to |
1283 | 0 | // another math frame |
1284 | 0 | mBoundingMetrics.width = child.X(); |
1285 | 0 |
|
1286 | 0 | aDesiredSize.Width() = std::max(0, mBoundingMetrics.width); |
1287 | 0 | aDesiredSize.Height() = ascent + descent; |
1288 | 0 | aDesiredSize.SetBlockStartAscent(ascent); |
1289 | 0 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
1290 | 0 |
|
1291 | 0 | mReference.x = 0; |
1292 | 0 | mReference.y = aDesiredSize.BlockStartAscent(); |
1293 | 0 |
|
1294 | 0 | ////////////////// |
1295 | 0 | // Place Children |
1296 | 0 |
|
1297 | 0 | if (aPlaceOrigin) { |
1298 | 0 | PositionRowChildFrames(0, aDesiredSize.BlockStartAscent()); |
1299 | 0 | } |
1300 | 0 |
|
1301 | 0 | return NS_OK; |
1302 | 0 | } |
1303 | | |
1304 | | void |
1305 | | nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX, |
1306 | | nscoord aBaseline) |
1307 | 0 | { |
1308 | 0 | RowChildFrameIterator child(this); |
1309 | 0 | while (child.Frame()) { |
1310 | 0 | nscoord dx = aOffsetX + child.X(); |
1311 | 0 | nscoord dy = aBaseline - child.Ascent(); |
1312 | 0 | FinishReflowChild(child.Frame(), PresContext(), child.GetReflowOutput(), |
1313 | 0 | nullptr, dx, dy, 0); |
1314 | 0 | ++child; |
1315 | 0 | } |
1316 | 0 | } |
1317 | | |
1318 | | // helpers to fix the inter-spacing when <math> is the only parent |
1319 | | // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math> |
1320 | | |
1321 | | static nscoord |
1322 | | GetInterFrameSpacingFor(int32_t aScriptLevel, |
1323 | | nsIFrame* aParentFrame, |
1324 | | nsIFrame* aChildFrame) |
1325 | 0 | { |
1326 | 0 | nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild(); |
1327 | 0 | if (!childFrame || aChildFrame == childFrame) |
1328 | 0 | return 0; |
1329 | 0 | |
1330 | 0 | int32_t carrySpace = 0; |
1331 | 0 | eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN; |
1332 | 0 | eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN; |
1333 | 0 | eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame); |
1334 | 0 | childFrame = childFrame->GetNextSibling(); |
1335 | 0 | while (childFrame) { |
1336 | 0 | prevFrameType = childFrameType; |
1337 | 0 | childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame); |
1338 | 0 | nscoord space = GetInterFrameSpacing(aScriptLevel, |
1339 | 0 | prevFrameType, childFrameType, &fromFrameType, &carrySpace); |
1340 | 0 | if (aChildFrame == childFrame) { |
1341 | 0 | // get thinspace |
1342 | 0 | ComputedStyle* parentContext = aParentFrame->Style(); |
1343 | 0 | nscoord thinSpace = GetThinSpace(parentContext->StyleFont()); |
1344 | 0 | // we are done |
1345 | 0 | return space * thinSpace; |
1346 | 0 | } |
1347 | 0 | childFrame = childFrame->GetNextSibling(); |
1348 | 0 | } |
1349 | 0 |
|
1350 | 0 | MOZ_ASSERT_UNREACHABLE("child not in the childlist of its parent"); |
1351 | 0 | return 0; |
1352 | 0 | } |
1353 | | |
1354 | | static nscoord |
1355 | | AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize, |
1356 | | nsMathMLContainerFrame* aFrame) |
1357 | 0 | { |
1358 | 0 | nscoord gap = 0; |
1359 | 0 | nsIFrame* parent = aFrame->GetParent(); |
1360 | 0 | nsIContent* parentContent = parent->GetContent(); |
1361 | 0 | if (MOZ_UNLIKELY(!parentContent)) { |
1362 | 0 | return 0; |
1363 | 0 | } |
1364 | 0 | if (parentContent->IsAnyOfMathMLElements(nsGkAtoms::math, |
1365 | 0 | nsGkAtoms::mtd_)) { |
1366 | 0 | gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mScriptLevel, |
1367 | 0 | parent, aFrame); |
1368 | 0 | // add our own italic correction |
1369 | 0 | nscoord leftCorrection = 0, italicCorrection = 0; |
1370 | 0 | nsMathMLContainerFrame::GetItalicCorrection(aDesiredSize.mBoundingMetrics, |
1371 | 0 | leftCorrection, italicCorrection); |
1372 | 0 | gap += leftCorrection; |
1373 | 0 | if (gap) { |
1374 | 0 | aDesiredSize.mBoundingMetrics.leftBearing += gap; |
1375 | 0 | aDesiredSize.mBoundingMetrics.rightBearing += gap; |
1376 | 0 | aDesiredSize.mBoundingMetrics.width += gap; |
1377 | 0 | aDesiredSize.Width() += gap; |
1378 | 0 | } |
1379 | 0 | aDesiredSize.mBoundingMetrics.width += italicCorrection; |
1380 | 0 | aDesiredSize.Width() += italicCorrection; |
1381 | 0 | } |
1382 | 0 | return gap; |
1383 | 0 | } |
1384 | | |
1385 | | nscoord |
1386 | | nsMathMLContainerFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize) |
1387 | 0 | { |
1388 | 0 | nscoord gap = 0; |
1389 | 0 | gap = AddInterFrameSpacingToSize(aDesiredSize, this); |
1390 | 0 | if (gap) { |
1391 | 0 | // Shift our children to account for the correction |
1392 | 0 | nsIFrame* childFrame = mFrames.FirstChild(); |
1393 | 0 | while (childFrame) { |
1394 | 0 | childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0)); |
1395 | 0 | childFrame = childFrame->GetNextSibling(); |
1396 | 0 | } |
1397 | 0 | } |
1398 | 0 | return gap; |
1399 | 0 | } |
1400 | | |
1401 | | /* static */ void |
1402 | | nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop) |
1403 | | |
1404 | 0 | { |
1405 | 0 | if (MOZ_UNLIKELY(!aFirst)) |
1406 | 0 | return; |
1407 | 0 | |
1408 | 0 | for (nsIFrame* frame = aFirst; |
1409 | 0 | frame != aStop; |
1410 | 0 | frame = frame->GetNextSibling()) { |
1411 | 0 | NS_ASSERTION(frame, "aStop isn't a sibling"); |
1412 | 0 | if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) { |
1413 | 0 | // finish off principal descendants, too |
1414 | 0 | nsIFrame* grandchild = frame->PrincipalChildList().FirstChild(); |
1415 | 0 | if (grandchild) |
1416 | 0 | DidReflowChildren(grandchild, nullptr); |
1417 | 0 |
|
1418 | 0 | frame->DidReflow(frame->PresContext(), nullptr); |
1419 | 0 | } |
1420 | 0 | } |
1421 | 0 | } |
1422 | | |
1423 | | // helper used by mstyle, mphantom, mpadded and mrow in their implementations |
1424 | | // of TransmitAutomaticData(). |
1425 | | nsresult |
1426 | | nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement() |
1427 | 0 | { |
1428 | 0 | // |
1429 | 0 | // One loop to check both conditions below: |
1430 | 0 | // |
1431 | 0 | // 1) whether all the children of the mrow-like element are space-like. |
1432 | 0 | // |
1433 | 0 | // The REC defines the following elements to be "space-like": |
1434 | 0 | // * an mstyle, mphantom, or mpadded element, all of whose direct |
1435 | 0 | // sub-expressions are space-like; |
1436 | 0 | // * an mrow all of whose direct sub-expressions are space-like. |
1437 | 0 | // |
1438 | 0 | // 2) whether all but one child of the mrow-like element are space-like and |
1439 | 0 | // this non-space-like child is an embellished operator. |
1440 | 0 | // |
1441 | 0 | // The REC defines the following elements to be embellished operators: |
1442 | 0 | // * one of the elements mstyle, mphantom, or mpadded, such that an mrow |
1443 | 0 | // containing the same arguments would be an embellished operator; |
1444 | 0 | // * an mrow whose arguments consist (in any order) of one embellished |
1445 | 0 | // operator and zero or more space-like elements. |
1446 | 0 | // |
1447 | 0 | nsIFrame *childFrame, *baseFrame; |
1448 | 0 | bool embellishedOpFound = false; |
1449 | 0 | nsEmbellishData embellishData; |
1450 | 0 |
|
1451 | 0 | for (childFrame = PrincipalChildList().FirstChild(); |
1452 | 0 | childFrame; |
1453 | 0 | childFrame = childFrame->GetNextSibling()) { |
1454 | 0 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame); |
1455 | 0 | if (!mathMLFrame) break; |
1456 | 0 | if (!mathMLFrame->IsSpaceLike()) { |
1457 | 0 | if (embellishedOpFound) break; |
1458 | 0 | baseFrame = childFrame; |
1459 | 0 | GetEmbellishDataFrom(baseFrame, embellishData); |
1460 | 0 | if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)) break; |
1461 | 0 | embellishedOpFound = true; |
1462 | 0 | } |
1463 | 0 | } |
1464 | 0 |
|
1465 | 0 | if (!childFrame) { |
1466 | 0 | // we successfully went to the end of the loop. This means that one of |
1467 | 0 | // condition 1) or 2) holds. |
1468 | 0 | if (!embellishedOpFound) { |
1469 | 0 | // the mrow-like element is space-like. |
1470 | 0 | mPresentationData.flags |= NS_MATHML_SPACE_LIKE; |
1471 | 0 | } else { |
1472 | 0 | // the mrow-like element is an embellished operator. |
1473 | 0 | // let the state of the embellished operator found bubble to us. |
1474 | 0 | mPresentationData.baseFrame = baseFrame; |
1475 | 0 | mEmbellishData = embellishData; |
1476 | 0 | } |
1477 | 0 | } |
1478 | 0 |
|
1479 | 0 | if (childFrame || !embellishedOpFound) { |
1480 | 0 | // The element is not embellished operator |
1481 | 0 | mPresentationData.baseFrame = nullptr; |
1482 | 0 | mEmbellishData.flags = 0; |
1483 | 0 | mEmbellishData.coreFrame = nullptr; |
1484 | 0 | mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; |
1485 | 0 | mEmbellishData.leadingSpace = 0; |
1486 | 0 | mEmbellishData.trailingSpace = 0; |
1487 | 0 | } |
1488 | 0 |
|
1489 | 0 | if (childFrame || embellishedOpFound) { |
1490 | 0 | // The element is not space-like |
1491 | 0 | mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE; |
1492 | 0 | } |
1493 | 0 |
|
1494 | 0 | return NS_OK; |
1495 | 0 | } |
1496 | | |
1497 | | /*static*/ void |
1498 | | nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame* aFrame, |
1499 | | nsFrameState aFlags) |
1500 | 0 | { |
1501 | 0 | if (!aFrame || !aFlags) |
1502 | 0 | return; |
1503 | 0 | |
1504 | 0 | aFrame->AddStateBits(aFlags); |
1505 | 0 | for (nsIFrame* childFrame : aFrame->PrincipalChildList()) { |
1506 | 0 | PropagateFrameFlagFor(childFrame, aFlags); |
1507 | 0 | } |
1508 | 0 | } |
1509 | | |
1510 | | nsresult |
1511 | | nsMathMLContainerFrame::ReportErrorToConsole(const char* errorMsgId, |
1512 | | const char16_t** aParams, |
1513 | | uint32_t aParamCount) |
1514 | 0 | { |
1515 | 0 | return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, |
1516 | 0 | NS_LITERAL_CSTRING("Layout: MathML"), mContent->OwnerDoc(), |
1517 | 0 | nsContentUtils::eMATHML_PROPERTIES, |
1518 | 0 | errorMsgId, aParams, aParamCount); |
1519 | 0 | } |
1520 | | |
1521 | | nsresult |
1522 | | nsMathMLContainerFrame::ReportParseError(const char16_t* aAttribute, |
1523 | | const char16_t* aValue) |
1524 | 0 | { |
1525 | 0 | const char16_t* argv[] = |
1526 | 0 | { aValue, aAttribute, mContent->NodeInfo()->NameAtom()->GetUTF16String() }; |
1527 | 0 | return ReportErrorToConsole("AttributeParsingError", argv, 3); |
1528 | 0 | } |
1529 | | |
1530 | | nsresult |
1531 | | nsMathMLContainerFrame::ReportChildCountError() |
1532 | 0 | { |
1533 | 0 | const char16_t* arg = mContent->NodeInfo()->NameAtom()->GetUTF16String(); |
1534 | 0 | return ReportErrorToConsole("ChildCountIncorrect", &arg, 1); |
1535 | 0 | } |
1536 | | |
1537 | | nsresult |
1538 | | nsMathMLContainerFrame::ReportInvalidChildError(nsAtom* aChildTag) |
1539 | 0 | { |
1540 | 0 | const char16_t* argv[] = |
1541 | 0 | { aChildTag->GetUTF16String(), |
1542 | 0 | mContent->NodeInfo()->NameAtom()->GetUTF16String() }; |
1543 | 0 | return ReportErrorToConsole("InvalidChild", argv, 2); |
1544 | 0 | } |
1545 | | |
1546 | | //========================== |
1547 | | |
1548 | | nsContainerFrame* |
1549 | | NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
1550 | 0 | { |
1551 | 0 | auto newFrame = new (aPresShell) nsMathMLmathBlockFrame(aStyle); |
1552 | 0 | newFrame->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS); |
1553 | 0 | return newFrame; |
1554 | 0 | } |
1555 | | |
1556 | | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame) |
1557 | | |
1558 | 0 | NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame) |
1559 | 0 | NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame) |
1560 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) |
1561 | | |
1562 | | nsContainerFrame* |
1563 | | NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
1564 | 0 | { |
1565 | 0 | return new (aPresShell) nsMathMLmathInlineFrame(aStyle); |
1566 | 0 | } |
1567 | | |
1568 | | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame) |
1569 | | |
1570 | 0 | NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame) |
1571 | 0 | NS_QUERYFRAME_ENTRY(nsIMathMLFrame) |
1572 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame) |