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