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/. */
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"
22 using namespace mozilla
;
25 // nsMathMLContainerFrame implementation
28 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame
)
30 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame
)
31 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
32 NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame
)
33 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
35 // =============================================================================
38 // provide a feedback to the user when a frame with bad markup can not be rendered
40 nsMathMLContainerFrame::ReflowError(nsRenderingContext
& aRenderingContext
,
41 nsHTMLReflowMetrics
& aDesiredSize
)
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
;
49 nsRefPtr
<nsFontMetrics
> fm
;
50 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
51 aRenderingContext
.SetFont(fm
);
54 nsAutoString errorMsg
; errorMsg
.AssignLiteral("invalid-markup");
56 aRenderingContext
.GetBoundingMetrics(errorMsg
.get(), errorMsg
.Length());
59 WritingMode wm
= aDesiredSize
.GetWritingMode();
60 aDesiredSize
.SetBlockStartAscent(fm
->MaxAscent());
61 nscoord descent
= fm
->MaxDescent();
62 aDesiredSize
.BSize(wm
) = aDesiredSize
.BlockStartAscent() + descent
;
63 aDesiredSize
.ISize(wm
) = mBoundingMetrics
.width
;
65 // Also return our bounding metrics
66 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
71 class nsDisplayMathMLError
: public nsDisplayItem
{
73 nsDisplayMathMLError(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
74 : nsDisplayItem(aBuilder
, aFrame
) {
75 MOZ_COUNT_CTOR(nsDisplayMathMLError
);
77 #ifdef NS_BUILD_REFCNT_LOGGING
78 virtual ~nsDisplayMathMLError() {
79 MOZ_COUNT_DTOR(nsDisplayMathMLError
);
83 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
84 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
85 NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR
)
88 void nsDisplayMathMLError::Paint(nsDisplayListBuilder
* aBuilder
,
89 nsRenderingContext
* aCtx
)
91 // Set color and font ...
92 nsRefPtr
<nsFontMetrics
> fm
;
93 nsLayoutUtils::GetFontMetricsForFrame(mFrame
, getter_AddRefs(fm
));
96 nsPoint pt
= ToReferenceFrame();
97 aCtx
->SetColor(NS_RGB(255,0,0));
98 aCtx
->FillRect(nsRect(pt
, mFrame
->GetSize()));
99 aCtx
->SetColor(NS_RGB(255,255,255));
101 nscoord ascent
= aCtx
->FontMetrics()->MaxAscent();
103 NS_NAMED_LITERAL_STRING(errorMsg
, "invalid-markup");
104 aCtx
->DrawString(errorMsg
.get(), uint32_t(errorMsg
.Length()),
109 * nsIMathMLFrame - support methods for stretchy elements
110 * =============================================================================
114 IsForeignChild(const nsIFrame
* aFrame
)
116 // This counts nsMathMLmathBlockFrame as a foreign child, because it
118 return !(aFrame
->IsFrameOfType(nsIFrame::eMathML
)) ||
119 aFrame
->GetType() == nsGkAtoms::blockFrame
;
123 DestroyHTMLReflowMetrics(void *aPropertyValue
)
125 delete static_cast<nsHTMLReflowMetrics
*>(aPropertyValue
);
128 NS_DECLARE_FRAME_PROPERTY(HTMLReflowMetricsProperty
, DestroyHTMLReflowMetrics
)
131 nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame
* aFrame
,
132 const nsHTMLReflowMetrics
& aReflowMetrics
,
133 const nsBoundingMetrics
& aBoundingMetrics
)
135 nsHTMLReflowMetrics
*metrics
= new nsHTMLReflowMetrics(aReflowMetrics
);
136 metrics
->mBoundingMetrics
= aBoundingMetrics
;
137 aFrame
->Properties().Set(HTMLReflowMetricsProperty(), metrics
);
140 // helper method to facilitate getting the reflow and bounding metrics
142 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame
* aFrame
,
143 nsHTMLReflowMetrics
& aReflowMetrics
,
144 nsBoundingMetrics
& aBoundingMetrics
,
145 eMathMLFrameType
* aMathMLFrameType
)
147 NS_PRECONDITION(aFrame
, "null arg");
149 nsHTMLReflowMetrics
*metrics
= static_cast<nsHTMLReflowMetrics
*>
150 (aFrame
->Properties().Get(HTMLReflowMetricsProperty()));
152 // IMPORTANT: This function is only meant to be called in Place() methods
153 // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
155 NS_ASSERTION(metrics
, "Didn't SaveReflowAndBoundingMetricsFor frame!");
157 aReflowMetrics
= *metrics
;
158 aBoundingMetrics
= metrics
->mBoundingMetrics
;
161 if (aMathMLFrameType
) {
162 if (!IsForeignChild(aFrame
)) {
163 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
165 *aMathMLFrameType
= mathMLFrame
->GetMathMLFrameType();
169 *aMathMLFrameType
= eMathMLFrameType_UNKNOWN
;
175 nsMathMLContainerFrame::ClearSavedChildMetrics()
177 nsIFrame
* childFrame
= mFrames
.FirstChild();
178 FramePropertyTable
* props
= PresContext()->PropertyTable();
180 props
->Delete(childFrame
, HTMLReflowMetricsProperty());
181 childFrame
= childFrame
->GetNextSibling();
185 // helper to get the preferred size that a container frame should use to fire
186 // the stretch on its stretchy child frames.
188 nsMathMLContainerFrame::GetPreferredStretchSize(nsRenderingContext
& aRenderingContext
,
190 nsStretchDirection aStretchDirection
,
191 nsBoundingMetrics
& aPreferredStretchSize
)
193 if (aOptions
& STRETCH_CONSIDER_ACTUAL_SIZE
) {
194 // when our actual size is ok, just use it
195 aPreferredStretchSize
= mBoundingMetrics
;
197 else if (aOptions
& STRETCH_CONSIDER_EMBELLISHMENTS
) {
198 // compute our up-to-date size using Place()
199 nsHTMLReflowMetrics
metrics(GetWritingMode()); // ???
200 Place(aRenderingContext
, false, metrics
);
201 aPreferredStretchSize
= metrics
.mBoundingMetrics
;
204 // compute a size that doesn't include embellishements
206 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ||
207 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
);
208 NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) ||
210 "invalid call to GetPreferredStretchSize");
211 bool firstTime
= true;
212 nsBoundingMetrics bm
, bmChild
;
213 nsIFrame
* childFrame
=
214 stretchAll
? GetFirstPrincipalChild() : mPresentationData
.baseFrame
;
216 // initializations in case this child happens not to be a MathML frame
217 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
219 nsEmbellishData embellishData
;
220 nsPresentationData presentationData
;
221 mathMLFrame
->GetEmbellishData(embellishData
);
222 mathMLFrame
->GetPresentationData(presentationData
);
223 if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
) &&
224 embellishData
.direction
== aStretchDirection
&&
225 presentationData
.baseFrame
) {
226 // embellishements are not included, only consider the inner first child itself
227 // XXXkt Does that mean the core descendent frame should be used
228 // instead of the base child?
229 nsIMathMLFrame
* mathMLchildFrame
= do_QueryFrame(presentationData
.baseFrame
);
230 if (mathMLchildFrame
) {
231 mathMLFrame
= mathMLchildFrame
;
234 mathMLFrame
->GetBoundingMetrics(bmChild
);
237 nsHTMLReflowMetrics
unused(GetWritingMode());
238 GetReflowAndBoundingMetricsFor(childFrame
, unused
, bmChild
);
245 // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
246 // or <maction>...<mo>...</mo></maction>.
251 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
)) {
252 // if we get here, it means this is container that will stack its children
253 // vertically and fire an horizontal stretch on each them. This is the case
254 // for \munder, \mover, \munderover. We just sum-up the size vertically.
255 bm
.descent
+= bmChild
.ascent
+ bmChild
.descent
;
256 // Sometimes non-spacing marks (when width is zero) are positioned
257 // to the left of the origin, but it is the distance between left
258 // and right bearing that is important rather than the offsets from
260 if (bmChild
.width
== 0) {
261 bmChild
.rightBearing
-= bmChild
.leftBearing
;
262 bmChild
.leftBearing
= 0;
264 if (bm
.leftBearing
> bmChild
.leftBearing
)
265 bm
.leftBearing
= bmChild
.leftBearing
;
266 if (bm
.rightBearing
< bmChild
.rightBearing
)
267 bm
.rightBearing
= bmChild
.rightBearing
;
269 else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)) {
270 // just sum-up the sizes horizontally.
274 NS_ERROR("unexpected case in GetPreferredStretchSize");
278 childFrame
= childFrame
->GetNextSibling();
280 aPreferredStretchSize
= bm
;
285 nsMathMLContainerFrame::Stretch(nsRenderingContext
& aRenderingContext
,
286 nsStretchDirection aStretchDirection
,
287 nsBoundingMetrics
& aContainerSize
,
288 nsHTMLReflowMetrics
& aDesiredStretchSize
)
290 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
)) {
292 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData
.flags
)) {
293 NS_WARNING("it is wrong to fire stretch more than once on a frame");
296 mPresentationData
.flags
|= NS_MATHML_STRETCH_DONE
;
298 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
299 NS_WARNING("it is wrong to fire stretch on a erroneous frame");
303 // Pass the stretch to the base child ...
305 nsIFrame
* baseFrame
= mPresentationData
.baseFrame
;
307 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(baseFrame
);
308 NS_ASSERTION(mathMLFrame
, "Something is wrong somewhere");
311 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ||
312 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
);
314 // And the trick is that the child's rect.x is still holding the descent,
315 // and rect.y is still holding the ascent ...
316 nsHTMLReflowMetrics
childSize(aDesiredStretchSize
);
317 GetReflowAndBoundingMetricsFor(baseFrame
, childSize
, childSize
.mBoundingMetrics
);
319 // See if we should downsize and confine the stretch to us...
320 // XXX there may be other cases where we can downsize the stretch,
321 // e.g., the first ∑ might appear big in the following situation
322 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
325 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
326 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
330 nsBoundingMetrics containerSize
= aContainerSize
;
331 if (aStretchDirection
!= NS_STRETCH_DIRECTION_DEFAULT
&&
332 aStretchDirection
!= mEmbellishData
.direction
) {
333 if (mEmbellishData
.direction
== NS_STRETCH_DIRECTION_UNSUPPORTED
) {
334 containerSize
= childSize
.mBoundingMetrics
;
337 GetPreferredStretchSize(aRenderingContext
,
338 stretchAll
? STRETCH_CONSIDER_EMBELLISHMENTS
: 0,
339 mEmbellishData
.direction
, containerSize
);
343 // do the stretching...
344 mathMLFrame
->Stretch(aRenderingContext
,
345 mEmbellishData
.direction
, containerSize
, childSize
);
346 // store the updated metrics
347 SaveReflowAndBoundingMetricsFor(baseFrame
, childSize
,
348 childSize
.mBoundingMetrics
);
350 // Remember the siblings which were _deferred_.
351 // Now that this embellished child may have changed, we need to
352 // fire the stretch on its siblings using our updated size
356 nsStretchDirection stretchDir
=
357 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ?
358 NS_STRETCH_DIRECTION_VERTICAL
: NS_STRETCH_DIRECTION_HORIZONTAL
;
360 GetPreferredStretchSize(aRenderingContext
, STRETCH_CONSIDER_EMBELLISHMENTS
,
361 stretchDir
, containerSize
);
363 nsIFrame
* childFrame
= mFrames
.FirstChild();
365 if (childFrame
!= mPresentationData
.baseFrame
) {
366 mathMLFrame
= do_QueryFrame(childFrame
);
368 // retrieve the metrics that was stored at the previous pass
369 GetReflowAndBoundingMetricsFor(childFrame
,
370 childSize
, childSize
.mBoundingMetrics
);
371 // do the stretching...
372 mathMLFrame
->Stretch(aRenderingContext
, stretchDir
,
373 containerSize
, childSize
);
374 // store the updated metrics
375 SaveReflowAndBoundingMetricsFor(childFrame
, childSize
,
376 childSize
.mBoundingMetrics
);
379 childFrame
= childFrame
->GetNextSibling();
383 // re-position all our children
384 nsresult rv
= Place(aRenderingContext
, true, aDesiredStretchSize
);
385 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
386 // Make sure the child frames get their DidReflow() calls.
387 DidReflowChildren(mFrames
.FirstChild());
390 // If our parent is not embellished, it means we are the outermost embellished
391 // container and so we put the spacing, otherwise we don't include the spacing,
392 // the outermost embellished container will take care of it.
394 nsEmbellishData parentData
;
395 GetEmbellishDataFrom(GetParent(), parentData
);
396 // ensure that we are the embellished child, not just a sibling
397 // (need to test coreFrame since <mfrac> resets other things)
398 if (parentData
.coreFrame
!= mEmbellishData
.coreFrame
) {
399 // (we fetch values from the core since they may use units that depend
400 // on style data, and style changes could have occurred in the core since
401 // our last visit there)
402 nsEmbellishData coreData
;
403 GetEmbellishDataFrom(mEmbellishData
.coreFrame
, coreData
);
405 mBoundingMetrics
.width
+=
406 coreData
.leadingSpace
+ coreData
.trailingSpace
;
407 aDesiredStretchSize
.Width() = mBoundingMetrics
.width
;
408 aDesiredStretchSize
.mBoundingMetrics
.width
= mBoundingMetrics
.width
;
410 nscoord dx
= (StyleVisibility()->mDirection
?
411 coreData
.trailingSpace
: coreData
.leadingSpace
);
413 mBoundingMetrics
.leftBearing
+= dx
;
414 mBoundingMetrics
.rightBearing
+= dx
;
415 aDesiredStretchSize
.mBoundingMetrics
.leftBearing
+= dx
;
416 aDesiredStretchSize
.mBoundingMetrics
.rightBearing
+= dx
;
418 nsIFrame
* childFrame
= mFrames
.FirstChild();
420 childFrame
->SetPosition(childFrame
->GetPosition()
422 childFrame
= childFrame
->GetNextSibling();
427 // Finished with these:
428 ClearSavedChildMetrics();
429 // Set our overflow area
430 GatherAndStoreOverflow(&aDesiredStretchSize
);
438 nsMathMLContainerFrame::FinalizeReflow(nsRenderingContext
& aRenderingContext
,
439 nsHTMLReflowMetrics
& aDesiredSize
)
441 // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
442 // and descent in expectation of a stretch command. Hence we need to ensure that
443 // a stretch command will actually be fired later on, after exiting from our
444 // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
445 // with inappropriate data causing children to be improperly positioned.
446 // This helper method checks to see if our parent will fire a stretch command
447 // targeted at us. If not, we go ahead and fire an involutive stretch on
448 // ourselves. This will clear all the rect.x and rect.y, and return our
452 // First, complete the post-reflow hook.
453 // We use the information in our children rectangles to position them.
454 // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
455 // They will still be holding the ascent and descent for each child.
457 // The first clause caters for any non-embellished container.
458 // The second clause is for a container which won't fire stretch even though it is
459 // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
460 // because it excludes the particular case of the core <mo>...</mo> itself.
461 // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
462 bool placeOrigin
= !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) ||
463 (mEmbellishData
.coreFrame
!= this && !mPresentationData
.baseFrame
&&
464 mEmbellishData
.direction
== NS_STRETCH_DIRECTION_UNSUPPORTED
);
465 nsresult rv
= Place(aRenderingContext
, placeOrigin
, aDesiredSize
);
467 // Place() will call FinishReflowChild() when placeOrigin is true but if
468 // it returns before reaching FinishReflowChild() due to errors we need
469 // to fulfill the reflow protocol by calling DidReflow for the child frames
470 // that still needs it here (or we may crash - bug 366012).
471 // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
472 // through Stretch() eventually.
473 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
474 GatherAndStoreOverflow(&aDesiredSize
);
475 DidReflowChildren(GetFirstPrincipalChild());
479 bool parentWillFireStretch
= false;
481 // This means the rect.x and rect.y of our children were not set!!
482 // Don't go without checking to see if our parent will later fire a Stretch() command
483 // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
484 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(GetParent());
486 nsEmbellishData embellishData
;
487 nsPresentationData presentationData
;
488 mathMLFrame
->GetEmbellishData(embellishData
);
489 mathMLFrame
->GetPresentationData(presentationData
);
490 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData
.flags
) ||
491 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData
.flags
) ||
492 (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
)
493 && presentationData
.baseFrame
== this))
495 parentWillFireStretch
= true;
498 if (!parentWillFireStretch
) {
499 // There is nobody who will fire the stretch for us, we do it ourselves!
502 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
503 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
);
505 nsBoundingMetrics defaultSize
;
506 if (mEmbellishData
.coreFrame
== this /* case of a bare <mo>...</mo> itself */
507 || stretchAll
) { /* or <mover><mo>...</mo>...</mover>, or friends */
508 // use our current size as computed earlier by Place()
509 defaultSize
= aDesiredSize
.mBoundingMetrics
;
511 else { /* case of <msup><mo>...</mo>...</msup> or friends */
512 // compute a size that doesn't include embellishments
513 GetPreferredStretchSize(aRenderingContext
, 0, mEmbellishData
.direction
,
516 Stretch(aRenderingContext
, NS_STRETCH_DIRECTION_DEFAULT
, defaultSize
,
520 // The Place() call above didn't request FinishReflowChild(),
521 // so let's check that we eventually did through Stretch().
522 nsIFrame
* childFrame
= GetFirstPrincipalChild();
523 for ( ; childFrame
; childFrame
= childFrame
->GetNextSibling()) {
524 NS_ASSERTION(!(childFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
),
525 "DidReflow() was never called");
532 // Also return our bounding metrics
533 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
535 // see if we should fix the spacing
536 FixInterFrameSpacing(aDesiredSize
);
538 if (!parentWillFireStretch
) {
539 // Not expecting a stretch.
540 // Finished with these:
541 ClearSavedChildMetrics();
542 // Set our overflow area.
543 GatherAndStoreOverflow(&aDesiredSize
);
551 * nsIMathMLFrame - support methods for scripting elements (nested frames
552 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
553 * mfrac, mroot, mtable).
554 * =============================================================================
557 // helper to let the update of presentation data pass through
558 // a subtree that may contain non-mathml container frames
560 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame
* aFrame
,
561 uint32_t aFlagsValues
,
562 uint32_t aFlagsToUpdate
)
564 if (!aFrame
|| !aFlagsToUpdate
)
566 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
569 mathMLFrame
->UpdatePresentationData(aFlagsValues
,
571 // propagate using the base method to make sure that the control
572 // is passed on to MathML frames that may be overloading the method
573 mathMLFrame
->UpdatePresentationDataFromChildAt(0, -1,
574 aFlagsValues
, aFlagsToUpdate
);
577 // propagate down the subtrees
578 nsIFrame
* childFrame
= aFrame
->GetFirstPrincipalChild();
580 PropagatePresentationDataFor(childFrame
,
581 aFlagsValues
, aFlagsToUpdate
);
582 childFrame
= childFrame
->GetNextSibling();
588 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame
* aParentFrame
,
589 int32_t aFirstChildIndex
,
590 int32_t aLastChildIndex
,
591 uint32_t aFlagsValues
,
592 uint32_t aFlagsToUpdate
)
594 if (!aParentFrame
|| !aFlagsToUpdate
)
597 nsIFrame
* childFrame
= aParentFrame
->GetFirstPrincipalChild();
599 if ((index
>= aFirstChildIndex
) &&
600 ((aLastChildIndex
<= 0) || ((aLastChildIndex
> 0) &&
601 (index
<= aLastChildIndex
)))) {
602 PropagatePresentationDataFor(childFrame
,
603 aFlagsValues
, aFlagsToUpdate
);
606 childFrame
= childFrame
->GetNextSibling();
610 /* //////////////////
612 * =============================================================================
617 nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
618 const nsRect
& aDirtyRect
,
619 const nsDisplayListSet
& aLists
)
621 // report an error if something wrong was found in this frame
622 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
623 if (!IsVisibleForPainting(aBuilder
))
626 aLists
.Content()->AppendNewToTop(
627 new (aBuilder
) nsDisplayMathMLError(aBuilder
, this));
631 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
633 BuildDisplayListForNonBlockChildren(aBuilder
, aDirtyRect
, aLists
,
634 DISPLAY_CHILD_INLINE
);
636 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
639 // if you want to see your bounding box, make sure to properly fill
640 // your mBoundingMetrics and mReference point, and set
641 // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
642 // in the Init() of your sub-class
643 DisplayBoundingMetrics(aBuilder
, this, mReference
, mBoundingMetrics
, aLists
);
647 // Note that this method re-builds the automatic data in the children -- not
648 // in aParentFrame itself (except for those particular operations that the
649 // parent frame may do in its TransmitAutomaticData()).
651 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame
* aParentFrame
)
653 // 1. As we descend the tree, make each child frame inherit data from
655 // 2. As we ascend the tree, transmit any specific change that we want
657 nsIFrame
* childFrame
= aParentFrame
->GetFirstPrincipalChild();
659 nsIMathMLFrame
* childMathMLFrame
= do_QueryFrame(childFrame
);
660 if (childMathMLFrame
) {
661 childMathMLFrame
->InheritAutomaticData(aParentFrame
);
663 RebuildAutomaticDataForChildren(childFrame
);
664 childFrame
= childFrame
->GetNextSibling();
666 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aParentFrame
);
668 mathMLFrame
->TransmitAutomaticData();
672 /* static */ nsresult
673 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame
* aParentFrame
)
678 // walk-up to the first frame that is a MathML frame, stop if we reach <math>
679 nsIFrame
* frame
= aParentFrame
;
681 nsIFrame
* parent
= frame
->GetParent();
682 if (!parent
|| !parent
->GetContent())
685 // stop if it is a MathML frame
686 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(frame
);
690 // stop if we reach the root <math> tag
691 nsIContent
* content
= frame
->GetContent();
692 NS_ASSERTION(content
, "dangling frame without a content node");
695 if (content
->GetNameSpaceID() == kNameSpaceID_MathML
&&
696 content
->Tag() == nsGkAtoms::math
)
702 // re-sync the presentation data and embellishment data of our children
703 RebuildAutomaticDataForChildren(frame
);
705 // Ask our parent frame to reflow us
706 nsIFrame
* parent
= frame
->GetParent();
707 NS_ASSERTION(parent
, "No parent to pass the reflow request up to");
711 frame
->PresContext()->PresShell()->
712 FrameNeedsReflow(frame
, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
717 // There are precise rules governing children of a MathML frame,
718 // and properties such as the scriptlevel depends on those rules.
719 // Hence for things to work, callers must use Append/Insert/etc wisely.
722 nsMathMLContainerFrame::ChildListChanged(int32_t aModType
)
724 // If this is an embellished frame we need to rebuild the
725 // embellished hierarchy by walking-up to the parent of the
726 // outermost embellished container.
727 nsIFrame
* frame
= this;
728 if (mEmbellishData
.coreFrame
) {
729 nsIFrame
* parent
= GetParent();
730 nsEmbellishData embellishData
;
731 for ( ; parent
; frame
= parent
, parent
= parent
->GetParent()) {
732 GetEmbellishDataFrom(parent
, embellishData
);
733 if (embellishData
.coreFrame
!= mEmbellishData
.coreFrame
)
737 return ReLayoutChildren(frame
);
741 nsMathMLContainerFrame::AppendFrames(ChildListID aListID
,
742 nsFrameList
& aFrameList
)
744 MOZ_ASSERT(aListID
== kPrincipalList
);
745 mFrames
.AppendFrames(this, aFrameList
);
746 ChildListChanged(nsIDOMMutationEvent::ADDITION
);
750 nsMathMLContainerFrame::InsertFrames(ChildListID aListID
,
751 nsIFrame
* aPrevFrame
,
752 nsFrameList
& aFrameList
)
754 MOZ_ASSERT(aListID
== kPrincipalList
);
755 mFrames
.InsertFrames(this, aPrevFrame
, aFrameList
);
756 ChildListChanged(nsIDOMMutationEvent::ADDITION
);
760 nsMathMLContainerFrame::RemoveFrame(ChildListID aListID
,
763 MOZ_ASSERT(aListID
== kPrincipalList
);
764 mFrames
.DestroyFrame(aOldFrame
);
765 ChildListChanged(nsIDOMMutationEvent::REMOVAL
);
769 nsMathMLContainerFrame::AttributeChanged(int32_t aNameSpaceID
,
773 // XXX Since they are numerous MathML attributes that affect layout, and
774 // we can't check all of them here, play safe by requesting a reflow.
775 // XXXldb This should only do work for attributes that cause changes!
776 PresContext()->PresShell()->
777 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
783 nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics
* aMetrics
)
785 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
787 aMetrics
->SetOverflowAreasToDesiredBounds();
789 // All non-child-frame content such as nsMathMLChars (and most child-frame
790 // content) is included in mBoundingMetrics.
791 nsRect
boundingBox(mBoundingMetrics
.leftBearing
,
792 aMetrics
->BlockStartAscent() - mBoundingMetrics
.ascent
,
793 mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
,
794 mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
);
796 // REVIEW: Maybe this should contribute only to visual overflow
797 // and not scrollable?
798 aMetrics
->mOverflowAreas
.UnionAllWith(boundingBox
);
800 // mBoundingMetrics does not necessarily include content of <mpadded>
801 // elements whose mBoundingMetrics may not be representative of the true
802 // bounds, and doesn't include the CSS2 outline rectangles of children, so
803 // make such to include child overflow areas.
804 nsIFrame
* childFrame
= mFrames
.FirstChild();
806 ConsiderChildOverflow(aMetrics
->mOverflowAreas
, childFrame
);
807 childFrame
= childFrame
->GetNextSibling();
810 FinishAndStoreOverflow(aMetrics
);
814 nsMathMLContainerFrame::UpdateOverflow()
816 // Our overflow areas may have changed, so reflow the frame.
817 PresContext()->PresShell()->FrameNeedsReflow(
818 this, nsIPresShell::eResize
, NS_FRAME_IS_DIRTY
);
820 // As we're reflowing, there's no need to propagate this change.
825 nsMathMLContainerFrame::ReflowChild(nsIFrame
* aChildFrame
,
826 nsPresContext
* aPresContext
,
827 nsHTMLReflowMetrics
& aDesiredSize
,
828 const nsHTMLReflowState
& aReflowState
,
829 nsReflowStatus
& aStatus
)
831 // Having foreign/hybrid children, e.g., from html markups, is not defined by
832 // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
833 // to do some cool demos... or we may have a child that is an nsInlineFrame
834 // from a generated content such as :before { content: open-quote } or
835 // :after { content: close-quote }. Unfortunately, the other frames out-there
836 // may expect their own invariants that are not met when we mix things.
837 // Hence we do not claim their support, but we will nevertheless attempt to keep
838 // them in the flow, if we can get their desired size. We observed that most
839 // frames may be reflowed generically, but nsInlineFrames need extra care.
842 nsInlineFrame
* inlineFrame
= do_QueryFrame(aChildFrame
);
843 NS_ASSERTION(!inlineFrame
, "Inline frames should be wrapped in blocks");
847 ReflowChild(aChildFrame
, aPresContext
, aDesiredSize
, aReflowState
,
848 0, 0, NS_FRAME_NO_MOVE_FRAME
, aStatus
);
850 if (aDesiredSize
.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
851 // This will be suitable for inline frames, which are wrapped in a block.
853 WritingMode wm
= aDesiredSize
.GetWritingMode();
854 if (!nsLayoutUtils::GetLastLineBaseline(wm
, aChildFrame
, &ascent
)) {
855 // We don't expect any other block children so just place the frame on
856 // the baseline instead of going through DidReflow() and
857 // GetBaseline(). This is what nsFrame::GetBaseline() will do anyway.
858 aDesiredSize
.SetBlockStartAscent(aDesiredSize
.BSize(wm
));
860 aDesiredSize
.SetBlockStartAscent(ascent
);
863 if (IsForeignChild(aChildFrame
)) {
864 // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
865 nsRect r
= aChildFrame
->ComputeTightBounds(aReflowState
.rendContext
->ThebesContext());
866 aDesiredSize
.mBoundingMetrics
.leftBearing
= r
.x
;
867 aDesiredSize
.mBoundingMetrics
.rightBearing
= r
.XMost();
868 aDesiredSize
.mBoundingMetrics
.ascent
= aDesiredSize
.BlockStartAscent() - r
.y
;
869 aDesiredSize
.mBoundingMetrics
.descent
= r
.YMost() - aDesiredSize
.BlockStartAscent();
870 aDesiredSize
.mBoundingMetrics
.width
= aDesiredSize
.Width();
875 nsMathMLContainerFrame::Reflow(nsPresContext
* aPresContext
,
876 nsHTMLReflowMetrics
& aDesiredSize
,
877 const nsHTMLReflowState
& aReflowState
,
878 nsReflowStatus
& aStatus
)
880 aDesiredSize
.Width() = aDesiredSize
.Height() = 0;
881 aDesiredSize
.SetBlockStartAscent(0);
882 aDesiredSize
.mBoundingMetrics
= nsBoundingMetrics();
886 // Asking each child to cache its bounding metrics
888 nsReflowStatus childStatus
;
889 nsIFrame
* childFrame
= mFrames
.FirstChild();
891 nsHTMLReflowMetrics
childDesiredSize(aReflowState
, // ???
892 aDesiredSize
.mFlags
);
893 WritingMode wm
= childFrame
->GetWritingMode();
894 LogicalSize availSize
= aReflowState
.ComputedSize(wm
);
895 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
896 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
897 childFrame
, availSize
);
898 ReflowChild(childFrame
, aPresContext
, childDesiredSize
,
899 childReflowState
, childStatus
);
900 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
901 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
902 childDesiredSize
.mBoundingMetrics
);
903 childFrame
= childFrame
->GetNextSibling();
907 // If we are a container which is entitled to stretch its children, then we
908 // ask our stretchy children to stretch themselves
910 // The stretching of siblings of an embellished child is _deferred_ until
911 // after finishing the stretching of the embellished child - bug 117652
913 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) &&
914 (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ||
915 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
))) {
917 // get the stretchy direction
918 nsStretchDirection stretchDir
=
919 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)
920 ? NS_STRETCH_DIRECTION_VERTICAL
921 : NS_STRETCH_DIRECTION_HORIZONTAL
;
923 // what size should we use to stretch our stretchy children
924 // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
925 // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
926 // include them in the caculations of the size of stretchy elements
927 nsBoundingMetrics containerSize
;
928 GetPreferredStretchSize(*aReflowState
.rendContext
, 0, stretchDir
,
931 // fire the stretch on each child
932 childFrame
= mFrames
.FirstChild();
934 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
936 // retrieve the metrics that was stored at the previous pass
937 nsHTMLReflowMetrics
childDesiredSize(aReflowState
);
938 GetReflowAndBoundingMetricsFor(childFrame
,
939 childDesiredSize
, childDesiredSize
.mBoundingMetrics
);
941 mathMLFrame
->Stretch(*aReflowState
.rendContext
, stretchDir
,
942 containerSize
, childDesiredSize
);
943 // store the updated metrics
944 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
945 childDesiredSize
.mBoundingMetrics
);
947 childFrame
= childFrame
->GetNextSibling();
952 // Place children now by re-adjusting the origins to align the baselines
953 FinalizeReflow(*aReflowState
.rendContext
, aDesiredSize
);
955 aStatus
= NS_FRAME_COMPLETE
;
956 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
959 static nscoord
AddInterFrameSpacingToSize(nsHTMLReflowMetrics
& aDesiredSize
,
960 nsMathMLContainerFrame
* aFrame
);
962 /* virtual */ nscoord
963 nsMathMLContainerFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
966 DISPLAY_MIN_WIDTH(this, result
);
967 nsHTMLReflowMetrics
desiredSize(GetWritingMode());
968 GetIntrinsicISizeMetrics(aRenderingContext
, desiredSize
);
970 // Include the additional width added by FixInterFrameSpacing to ensure
971 // consistent width calculations.
972 AddInterFrameSpacingToSize(desiredSize
, this);
973 result
= desiredSize
.ISize(GetWritingMode());
977 /* virtual */ nscoord
978 nsMathMLContainerFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
981 DISPLAY_PREF_WIDTH(this, result
);
982 nsHTMLReflowMetrics
desiredSize(GetWritingMode());
983 GetIntrinsicISizeMetrics(aRenderingContext
, desiredSize
);
985 // Include the additional width added by FixInterFrameSpacing to ensure
986 // consistent width calculations.
987 AddInterFrameSpacingToSize(desiredSize
, this);
988 result
= desiredSize
.ISize(GetWritingMode());
993 nsMathMLContainerFrame::GetIntrinsicISizeMetrics(nsRenderingContext
* aRenderingContext
, nsHTMLReflowMetrics
& aDesiredSize
)
996 nsIFrame
* childFrame
= mFrames
.FirstChild();
998 nsHTMLReflowMetrics
childDesiredSize(GetWritingMode()); // ???
1000 nsMathMLContainerFrame
* containerFrame
= do_QueryFrame(childFrame
);
1001 if (containerFrame
) {
1002 containerFrame
->GetIntrinsicISizeMetrics(aRenderingContext
,
1005 // XXX This includes margin while Reflow currently doesn't consider
1006 // margin, so we may end up with too much space, but, with stretchy
1007 // characters, this is an approximation anyway.
1009 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, childFrame
,
1010 nsLayoutUtils::PREF_ISIZE
);
1012 childDesiredSize
.Width() = width
;
1013 childDesiredSize
.mBoundingMetrics
.width
= width
;
1014 childDesiredSize
.mBoundingMetrics
.leftBearing
= 0;
1015 childDesiredSize
.mBoundingMetrics
.rightBearing
= width
;
1018 if (NS_SUCCEEDED(childFrame
->GetPrefWidthTightBounds(aRenderingContext
,
1020 childDesiredSize
.mBoundingMetrics
.leftBearing
= x
;
1021 childDesiredSize
.mBoundingMetrics
.rightBearing
= xMost
;
1025 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
1026 childDesiredSize
.mBoundingMetrics
);
1028 childFrame
= childFrame
->GetNextSibling();
1032 nsresult rv
= MeasureForWidth(*aRenderingContext
, aDesiredSize
);
1033 if (NS_FAILED(rv
)) {
1034 ReflowError(*aRenderingContext
, aDesiredSize
);
1037 ClearSavedChildMetrics();
1040 /* virtual */ nsresult
1041 nsMathMLContainerFrame::MeasureForWidth(nsRenderingContext
& aRenderingContext
,
1042 nsHTMLReflowMetrics
& aDesiredSize
)
1044 return Place(aRenderingContext
, false, aDesiredSize
);
1048 // see spacing table in Chapter 18, TeXBook (p.170)
1049 // Our table isn't quite identical to TeX because operators have
1050 // built-in values for lspace & rspace in the Operator Dictionary.
1051 static int32_t kInterFrameSpacingTable
[eMathMLFrameType_COUNT
][eMathMLFrameType_COUNT
] =
1053 // in units of muspace.
1054 // upper half of the byte is set if the
1055 // spacing is not to be used for scriptlevel > 0
1057 /* Ord OpOrd OpInv OpUsr Inner Italic Upright */
1058 /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
1059 /*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1060 /*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1061 /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1062 /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1063 /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
1064 /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}
1067 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
1068 /* no space if there is a frame that we know nothing about */ \
1069 if (frametype1_ == eMathMLFrameType_UNKNOWN || \
1070 frametype2_ == eMathMLFrameType_UNKNOWN) \
1073 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
1074 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
1075 ? 0 /* spacing is disabled */ \
1079 // This function computes the inter-space between two frames. However,
1080 // since invisible operators need special treatment, the inter-space may
1081 // be delayed when an invisible operator is encountered. In this case,
1082 // the function will carry the inter-space forward until it is determined
1083 // that it can be applied properly (i.e., until we encounter a visible
1084 // frame where to decide whether to accept or reject the inter-space).
1085 // aFromFrameType: remembers the frame when the carry-forward initiated.
1086 // aCarrySpace: keeps track of the inter-space that is delayed.
1087 // @returns: current inter-space (which is 0 when the true inter-space is
1088 // delayed -- and thus has no effect since the frame is invisible anyway).
1090 GetInterFrameSpacing(int32_t aScriptLevel
,
1091 eMathMLFrameType aFirstFrameType
,
1092 eMathMLFrameType aSecondFrameType
,
1093 eMathMLFrameType
* aFromFrameType
, // IN/OUT
1094 int32_t* aCarrySpace
) // IN/OUT
1096 eMathMLFrameType firstType
= aFirstFrameType
;
1097 eMathMLFrameType secondType
= aSecondFrameType
;
1100 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
1102 // feedback control to avoid the inter-space to be added when not necessary
1103 if (secondType
== eMathMLFrameType_OperatorInvisible
) {
1104 // see if we should start to carry the space forward until we
1105 // encounter a visible frame
1106 if (*aFromFrameType
== eMathMLFrameType_UNKNOWN
) {
1107 *aFromFrameType
= firstType
;
1108 *aCarrySpace
= space
;
1110 // keep carrying *aCarrySpace forward, while returning 0 for this stage
1113 else if (*aFromFrameType
!= eMathMLFrameType_UNKNOWN
) {
1114 // no carry-forward anymore, get the real inter-space between
1115 // the two frames of interest
1117 firstType
= *aFromFrameType
;
1119 // But... the invisible operator that we encountered earlier could
1120 // be sitting between italic and upright identifiers, e.g.,
1122 // 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi>
1123 // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
1125 // the trick to get the inter-space in either situation
1126 // is to promote "<mi>sin</mi><mo>⁡</mo>" and
1127 // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
1128 if (firstType
== eMathMLFrameType_UprightIdentifier
) {
1129 firstType
= eMathMLFrameType_OperatorUserDefined
;
1131 else if (secondType
== eMathMLFrameType_UprightIdentifier
) {
1132 secondType
= eMathMLFrameType_OperatorUserDefined
;
1135 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
1137 // Now, we have two values: the computed space and the space that
1138 // has been carried forward until now. Which value do we pick?
1139 // If the second type is an operator (e.g., fence), it already has
1140 // built-in lspace & rspace, so we let them win. Otherwise we pick
1141 // the max between the two values that we have.
1142 if (secondType
!= eMathMLFrameType_OperatorOrdinary
&&
1143 space
< *aCarrySpace
)
1144 space
= *aCarrySpace
;
1146 // reset everything now that the carry-forward is done
1147 *aFromFrameType
= eMathMLFrameType_UNKNOWN
;
1154 static nscoord
GetThinSpace(const nsStyleFont
* aStyleFont
)
1156 return NSToCoordRound(float(aStyleFont
->mFont
.size
)*float(3) / float(18));
1159 class nsMathMLContainerFrame::RowChildFrameIterator
{
1161 explicit RowChildFrameIterator(nsMathMLContainerFrame
* aParentFrame
) :
1162 mParentFrame(aParentFrame
),
1163 mSize(aParentFrame
->GetWritingMode()), // ???
1166 mFromFrameType(eMathMLFrameType_UNKNOWN
),
1167 mRTL(aParentFrame
->StyleVisibility()->mDirection
)
1170 mChildFrame
= aParentFrame
->mFrames
.FirstChild();
1172 mChildFrame
= aParentFrame
->mFrames
.LastChild();
1178 InitMetricsForChild();
1181 RowChildFrameIterator
& operator++()
1183 // add child size + italic correction
1184 mX
+= mSize
.mBoundingMetrics
.width
+ mItalicCorrection
;
1187 mChildFrame
= mChildFrame
->GetNextSibling();
1189 mChildFrame
= mChildFrame
->GetPrevSibling();
1195 eMathMLFrameType prevFrameType
= mChildFrameType
;
1196 InitMetricsForChild();
1198 // add inter frame spacing
1199 const nsStyleFont
* font
= mParentFrame
->StyleFont();
1201 GetInterFrameSpacing(font
->mScriptLevel
,
1202 prevFrameType
, mChildFrameType
,
1203 &mFromFrameType
, &mCarrySpace
);
1204 mX
+= space
* GetThinSpace(font
);
1208 nsIFrame
* Frame() const { return mChildFrame
; }
1209 nscoord
X() const { return mX
; }
1210 const nsHTMLReflowMetrics
& ReflowMetrics() const { return mSize
; }
1211 nscoord
Ascent() const { return mSize
.BlockStartAscent(); }
1212 nscoord
Descent() const { return mSize
.Height() - mSize
.BlockStartAscent(); }
1213 const nsBoundingMetrics
& BoundingMetrics() const {
1214 return mSize
.mBoundingMetrics
;
1218 const nsMathMLContainerFrame
* mParentFrame
;
1219 nsIFrame
* mChildFrame
;
1220 nsHTMLReflowMetrics mSize
;
1223 nscoord mItalicCorrection
;
1224 eMathMLFrameType mChildFrameType
;
1225 int32_t mCarrySpace
;
1226 eMathMLFrameType mFromFrameType
;
1230 void InitMetricsForChild()
1232 GetReflowAndBoundingMetricsFor(mChildFrame
, mSize
, mSize
.mBoundingMetrics
,
1234 nscoord leftCorrection
, rightCorrection
;
1235 GetItalicCorrection(mSize
.mBoundingMetrics
,
1236 leftCorrection
, rightCorrection
);
1237 if (!mChildFrame
->GetPrevSibling() &&
1238 mParentFrame
->GetContent()->Tag() == nsGkAtoms::msqrt_
) {
1239 // Remove leading correction in <msqrt> because the sqrt glyph itself is
1244 rightCorrection
= 0;
1247 // add left correction -- this fixes the problem of the italic 'f'
1248 // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
1249 mX
+= leftCorrection
;
1250 mItalicCorrection
= rightCorrection
;
1254 /* virtual */ nsresult
1255 nsMathMLContainerFrame::Place(nsRenderingContext
& aRenderingContext
,
1257 nsHTMLReflowMetrics
& aDesiredSize
)
1259 // This is needed in case this frame is empty (i.e., no child frames)
1260 mBoundingMetrics
= nsBoundingMetrics();
1262 RowChildFrameIterator
child(this);
1263 nscoord ascent
= 0, descent
= 0;
1264 while (child
.Frame()) {
1265 if (descent
< child
.Descent())
1266 descent
= child
.Descent();
1267 if (ascent
< child
.Ascent())
1268 ascent
= child
.Ascent();
1269 // add the child size
1270 mBoundingMetrics
.width
= child
.X();
1271 mBoundingMetrics
+= child
.BoundingMetrics();
1274 // Add the italic correction at the end (including the last child).
1275 // This gives a nice gap between math and non-math frames, and still
1276 // gives the same math inter-spacing in case this frame connects to
1277 // another math frame
1278 mBoundingMetrics
.width
= child
.X();
1280 aDesiredSize
.Width() = std::max(0, mBoundingMetrics
.width
);
1281 aDesiredSize
.Height() = ascent
+ descent
;
1282 aDesiredSize
.SetBlockStartAscent(ascent
);
1283 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
1286 mReference
.y
= aDesiredSize
.BlockStartAscent();
1292 PositionRowChildFrames(0, aDesiredSize
.BlockStartAscent());
1299 nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX
,
1302 RowChildFrameIterator
child(this);
1303 while (child
.Frame()) {
1304 nscoord dx
= aOffsetX
+ child
.X();
1305 nscoord dy
= aBaseline
- child
.Ascent();
1306 FinishReflowChild(child
.Frame(), PresContext(), child
.ReflowMetrics(),
1307 nullptr, dx
, dy
, 0);
1312 class ForceReflow
: public nsIReflowCallback
{
1314 virtual bool ReflowFinished() MOZ_OVERRIDE
{
1317 virtual void ReflowCallbackCanceled() MOZ_OVERRIDE
{}
1320 // We only need one of these so we just make it a static global, no need
1321 // to dynamically allocate/destroy it.
1322 static ForceReflow gForceReflow
;
1325 nsMathMLContainerFrame::SetIncrementScriptLevel(int32_t aChildIndex
, bool aIncrement
)
1327 nsIFrame
* child
= PrincipalChildList().FrameAt(aChildIndex
);
1330 nsIContent
* content
= child
->GetContent();
1331 if (!content
->IsMathML())
1333 nsMathMLElement
* element
= static_cast<nsMathMLElement
*>(content
);
1335 if (element
->GetIncrementScriptLevel() == aIncrement
)
1338 // XXXroc this does a ContentStatesChanged, is it safe to call here? If
1339 // not we should do it in a post-reflow callback.
1340 element
->SetIncrementScriptLevel(aIncrement
, true);
1341 PresContext()->PresShell()->PostReflowCallback(&gForceReflow
);
1344 // helpers to fix the inter-spacing when <math> is the only parent
1345 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
1348 GetInterFrameSpacingFor(int32_t aScriptLevel
,
1349 nsIFrame
* aParentFrame
,
1350 nsIFrame
* aChildFrame
)
1352 nsIFrame
* childFrame
= aParentFrame
->GetFirstPrincipalChild();
1353 if (!childFrame
|| aChildFrame
== childFrame
)
1356 int32_t carrySpace
= 0;
1357 eMathMLFrameType fromFrameType
= eMathMLFrameType_UNKNOWN
;
1358 eMathMLFrameType prevFrameType
= eMathMLFrameType_UNKNOWN
;
1359 eMathMLFrameType childFrameType
= nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1360 childFrame
= childFrame
->GetNextSibling();
1361 while (childFrame
) {
1362 prevFrameType
= childFrameType
;
1363 childFrameType
= nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1364 nscoord space
= GetInterFrameSpacing(aScriptLevel
,
1365 prevFrameType
, childFrameType
, &fromFrameType
, &carrySpace
);
1366 if (aChildFrame
== childFrame
) {
1368 nsStyleContext
* parentContext
= aParentFrame
->StyleContext();
1369 nscoord thinSpace
= GetThinSpace(parentContext
->StyleFont());
1371 return space
* thinSpace
;
1373 childFrame
= childFrame
->GetNextSibling();
1376 NS_NOTREACHED("child not in the childlist of its parent");
1381 AddInterFrameSpacingToSize(nsHTMLReflowMetrics
& aDesiredSize
,
1382 nsMathMLContainerFrame
* aFrame
)
1385 nsIFrame
* parent
= aFrame
->GetParent();
1386 nsIContent
* parentContent
= parent
->GetContent();
1387 if (MOZ_UNLIKELY(!parentContent
)) {
1390 nsIAtom
*parentTag
= parentContent
->Tag();
1391 if (parentContent
->GetNameSpaceID() == kNameSpaceID_MathML
&&
1392 (parentTag
== nsGkAtoms::math
|| parentTag
== nsGkAtoms::mtd_
)) {
1393 gap
= GetInterFrameSpacingFor(aFrame
->StyleFont()->mScriptLevel
,
1395 // add our own italic correction
1396 nscoord leftCorrection
= 0, italicCorrection
= 0;
1397 aFrame
->GetItalicCorrection(aDesiredSize
.mBoundingMetrics
,
1398 leftCorrection
, italicCorrection
);
1399 gap
+= leftCorrection
;
1401 aDesiredSize
.mBoundingMetrics
.leftBearing
+= gap
;
1402 aDesiredSize
.mBoundingMetrics
.rightBearing
+= gap
;
1403 aDesiredSize
.mBoundingMetrics
.width
+= gap
;
1404 aDesiredSize
.Width() += gap
;
1406 aDesiredSize
.mBoundingMetrics
.width
+= italicCorrection
;
1407 aDesiredSize
.Width() += italicCorrection
;
1413 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics
& aDesiredSize
)
1416 gap
= AddInterFrameSpacingToSize(aDesiredSize
, this);
1418 // Shift our children to account for the correction
1419 nsIFrame
* childFrame
= mFrames
.FirstChild();
1420 while (childFrame
) {
1421 childFrame
->SetPosition(childFrame
->GetPosition() + nsPoint(gap
, 0));
1422 childFrame
= childFrame
->GetNextSibling();
1429 nsMathMLContainerFrame::DidReflowChildren(nsIFrame
* aFirst
, nsIFrame
* aStop
)
1432 if (MOZ_UNLIKELY(!aFirst
))
1435 for (nsIFrame
* frame
= aFirst
;
1437 frame
= frame
->GetNextSibling()) {
1438 NS_ASSERTION(frame
, "aStop isn't a sibling");
1439 if (frame
->GetStateBits() & NS_FRAME_IN_REFLOW
) {
1440 // finish off principal descendants, too
1441 nsIFrame
* grandchild
= frame
->GetFirstPrincipalChild();
1443 DidReflowChildren(grandchild
, nullptr);
1445 frame
->DidReflow(frame
->PresContext(), nullptr,
1446 nsDidReflowStatus::FINISHED
);
1451 // helper used by mstyle, mphantom, mpadded and mrow in their implementations
1452 // of TransmitAutomaticData().
1454 nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement()
1457 // One loop to check both conditions below:
1459 // 1) whether all the children of the mrow-like element are space-like.
1461 // The REC defines the following elements to be "space-like":
1462 // * an mstyle, mphantom, or mpadded element, all of whose direct
1463 // sub-expressions are space-like;
1464 // * an mrow all of whose direct sub-expressions are space-like.
1466 // 2) whether all but one child of the mrow-like element are space-like and
1467 // this non-space-like child is an embellished operator.
1469 // The REC defines the following elements to be embellished operators:
1470 // * one of the elements mstyle, mphantom, or mpadded, such that an mrow
1471 // containing the same arguments would be an embellished operator;
1472 // * an mrow whose arguments consist (in any order) of one embellished
1473 // operator and zero or more space-like elements.
1475 nsIFrame
*childFrame
, *baseFrame
;
1476 bool embellishedOpFound
= false;
1477 nsEmbellishData embellishData
;
1479 for (childFrame
= GetFirstPrincipalChild();
1481 childFrame
= childFrame
->GetNextSibling()) {
1482 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
1483 if (!mathMLFrame
) break;
1484 if (!mathMLFrame
->IsSpaceLike()) {
1485 if (embellishedOpFound
) break;
1486 baseFrame
= childFrame
;
1487 GetEmbellishDataFrom(baseFrame
, embellishData
);
1488 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
)) break;
1489 embellishedOpFound
= true;
1494 // we successfully went to the end of the loop. This means that one of
1495 // condition 1) or 2) holds.
1496 if (!embellishedOpFound
) {
1497 // the mrow-like element is space-like.
1498 mPresentationData
.flags
|= NS_MATHML_SPACE_LIKE
;
1500 // the mrow-like element is an embellished operator.
1501 // let the state of the embellished operator found bubble to us.
1502 mPresentationData
.baseFrame
= baseFrame
;
1503 mEmbellishData
= embellishData
;
1507 if (childFrame
|| !embellishedOpFound
) {
1508 // The element is not embellished operator
1509 mPresentationData
.baseFrame
= nullptr;
1510 mEmbellishData
.flags
= 0;
1511 mEmbellishData
.coreFrame
= nullptr;
1512 mEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1513 mEmbellishData
.leadingSpace
= 0;
1514 mEmbellishData
.trailingSpace
= 0;
1517 if (childFrame
|| embellishedOpFound
) {
1518 // The element is not space-like
1519 mPresentationData
.flags
&= ~NS_MATHML_SPACE_LIKE
;
1526 nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame
* aFrame
,
1527 nsFrameState aFlags
)
1529 if (!aFrame
|| !aFlags
)
1532 aFrame
->AddStateBits(aFlags
);
1533 nsIFrame
* childFrame
= aFrame
->GetFirstPrincipalChild();
1534 while (childFrame
) {
1535 PropagateFrameFlagFor(childFrame
, aFlags
);
1536 childFrame
= childFrame
->GetNextSibling();
1541 nsMathMLContainerFrame::ReportErrorToConsole(const char* errorMsgId
,
1542 const char16_t
** aParams
,
1543 uint32_t aParamCount
)
1545 return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
1546 NS_LITERAL_CSTRING("Layout: MathML"), mContent
->OwnerDoc(),
1547 nsContentUtils::eMATHML_PROPERTIES
,
1548 errorMsgId
, aParams
, aParamCount
);
1552 nsMathMLContainerFrame::ReportParseError(const char16_t
* aAttribute
,
1553 const char16_t
* aValue
)
1555 const char16_t
* argv
[] =
1556 { aValue
, aAttribute
, mContent
->Tag()->GetUTF16String() };
1557 return ReportErrorToConsole("AttributeParsingError", argv
, 3);
1561 nsMathMLContainerFrame::ReportChildCountError()
1563 const char16_t
* arg
= mContent
->Tag()->GetUTF16String();
1564 return ReportErrorToConsole("ChildCountIncorrect", &arg
, 1);
1568 nsMathMLContainerFrame::ReportInvalidChildError(nsIAtom
* aChildTag
)
1570 const char16_t
* argv
[] =
1571 { aChildTag
->GetUTF16String(), mContent
->Tag()->GetUTF16String() };
1572 return ReportErrorToConsole("InvalidChild", argv
, 2);
1575 //==========================
1578 NS_NewMathMLmathBlockFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
,
1579 nsFrameState aFlags
)
1581 nsMathMLmathBlockFrame
* it
= new (aPresShell
) nsMathMLmathBlockFrame(aContext
);
1582 it
->SetFlags(aFlags
);
1586 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame
)
1588 NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame
)
1589 NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame
)
1590 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
1593 NS_NewMathMLmathInlineFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1595 return new (aPresShell
) nsMathMLmathInlineFrame(aContext
);
1598 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame
)
1600 NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame
)
1601 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
1602 NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame
)