1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsMathMLContainerFrame.h"
9 #include "gfxContext.h"
11 #include "mozilla/Likely.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/dom/MutationEventBinding.h"
14 #include "mozilla/gfx/2D.h"
15 #include "nsLayoutUtils.h"
16 #include "nsPresContext.h"
17 #include "nsNameSpaceManager.h"
18 #include "nsGkAtoms.h"
19 #include "nsDisplayList.h"
20 #include "nsIScriptError.h"
21 #include "nsContentUtils.h"
22 #include "mozilla/dom/MathMLElement.h"
24 using namespace mozilla
;
25 using namespace mozilla::gfx
;
28 // nsMathMLContainerFrame implementation
31 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame
)
32 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
33 NS_QUERYFRAME_ENTRY(nsMathMLContainerFrame
)
34 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
37 * nsIMathMLFrame - support methods for stretchy elements
38 * =============================================================================
41 static bool IsForeignChild(const nsIFrame
* aFrame
) {
42 // This counts nsMathMLmathBlockFrame as a foreign child, because it
44 return !aFrame
->IsMathMLFrame() || aFrame
->IsBlockFrame();
47 NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTMLReflowOutputProperty
, ReflowOutput
)
50 void nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(
51 nsIFrame
* aFrame
, const ReflowOutput
& aReflowOutput
,
52 const nsBoundingMetrics
& aBoundingMetrics
) {
53 ReflowOutput
* reflowOutput
= new ReflowOutput(aReflowOutput
);
54 reflowOutput
->mBoundingMetrics
= aBoundingMetrics
;
55 aFrame
->SetProperty(HTMLReflowOutputProperty(), reflowOutput
);
58 // helper method to facilitate getting the reflow and bounding metrics
60 void nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(
61 nsIFrame
* aFrame
, ReflowOutput
& aReflowOutput
,
62 nsBoundingMetrics
& aBoundingMetrics
, eMathMLFrameType
* aMathMLFrameType
) {
63 MOZ_ASSERT(aFrame
, "null arg");
65 ReflowOutput
* reflowOutput
= aFrame
->GetProperty(HTMLReflowOutputProperty());
67 // IMPORTANT: This function is only meant to be called in Place() methods
68 // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
70 NS_ASSERTION(reflowOutput
, "Didn't SaveReflowAndBoundingMetricsFor frame!");
72 aReflowOutput
= *reflowOutput
;
73 aBoundingMetrics
= reflowOutput
->mBoundingMetrics
;
76 if (aMathMLFrameType
) {
77 if (!IsForeignChild(aFrame
)) {
78 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
80 *aMathMLFrameType
= mathMLFrame
->GetMathMLFrameType();
84 *aMathMLFrameType
= eMathMLFrameType_UNKNOWN
;
88 void nsMathMLContainerFrame::ClearSavedChildMetrics() {
89 nsIFrame
* childFrame
= mFrames
.FirstChild();
91 childFrame
->RemoveProperty(HTMLReflowOutputProperty());
92 childFrame
= childFrame
->GetNextSibling();
96 // helper to get the preferred size that a container frame should use to fire
97 // the stretch on its stretchy child frames.
98 void nsMathMLContainerFrame::GetPreferredStretchSize(
99 DrawTarget
* aDrawTarget
, uint32_t aOptions
,
100 nsStretchDirection aStretchDirection
,
101 nsBoundingMetrics
& aPreferredStretchSize
) {
102 if (aOptions
& STRETCH_CONSIDER_ACTUAL_SIZE
) {
103 // when our actual size is ok, just use it
104 aPreferredStretchSize
= mBoundingMetrics
;
105 } else if (aOptions
& STRETCH_CONSIDER_EMBELLISHMENTS
) {
106 // compute our up-to-date size using Place()
107 ReflowOutput
reflowOutput(GetWritingMode());
108 Place(aDrawTarget
, false, reflowOutput
);
109 aPreferredStretchSize
= reflowOutput
.mBoundingMetrics
;
111 // compute a size that includes embellishments iff the container stretches
112 // in the same direction as the embellished operator.
113 bool stretchAll
= aStretchDirection
== NS_STRETCH_DIRECTION_VERTICAL
114 ? NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
115 mPresentationData
.flags
)
116 : NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
117 mPresentationData
.flags
);
118 NS_ASSERTION(aStretchDirection
== NS_STRETCH_DIRECTION_HORIZONTAL
||
119 aStretchDirection
== NS_STRETCH_DIRECTION_VERTICAL
,
120 "You must specify a direction in which to stretch");
122 NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) || stretchAll
,
123 "invalid call to GetPreferredStretchSize");
124 bool firstTime
= true;
125 nsBoundingMetrics bm
, bmChild
;
126 nsIFrame
* childFrame
= stretchAll
? PrincipalChildList().FirstChild()
127 : mPresentationData
.baseFrame
;
129 // initializations in case this child happens not to be a MathML frame
130 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
132 nsEmbellishData embellishData
;
133 nsPresentationData presentationData
;
134 mathMLFrame
->GetEmbellishData(embellishData
);
135 mathMLFrame
->GetPresentationData(presentationData
);
136 if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
) &&
137 embellishData
.direction
== aStretchDirection
&&
138 presentationData
.baseFrame
) {
139 // embellishements are not included, only consider the inner first
141 // XXXkt Does that mean the core descendent frame should be used
142 // instead of the base child?
143 nsIMathMLFrame
* mathMLchildFrame
=
144 do_QueryFrame(presentationData
.baseFrame
);
145 if (mathMLchildFrame
) {
146 mathMLFrame
= mathMLchildFrame
;
149 mathMLFrame
->GetBoundingMetrics(bmChild
);
151 ReflowOutput
unused(GetWritingMode());
152 GetReflowAndBoundingMetricsFor(childFrame
, unused
, bmChild
);
159 // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
160 // or <maction>...<mo>...</mo></maction>.
164 if (aStretchDirection
== NS_STRETCH_DIRECTION_HORIZONTAL
) {
165 // if we get here, it means this is container that will stack its
166 // children vertically and fire an horizontal stretch on each them.
167 // This is the case for \munder, \mover, \munderover. We just sum-up
168 // the size vertically.
169 bm
.descent
+= bmChild
.ascent
+ bmChild
.descent
;
170 // Sometimes non-spacing marks (when width is zero) are positioned
171 // to the left of the origin, but it is the distance between left
172 // and right bearing that is important rather than the offsets from
174 if (bmChild
.width
== 0) {
175 bmChild
.rightBearing
-= bmChild
.leftBearing
;
176 bmChild
.leftBearing
= 0;
178 if (bm
.leftBearing
> bmChild
.leftBearing
)
179 bm
.leftBearing
= bmChild
.leftBearing
;
180 if (bm
.rightBearing
< bmChild
.rightBearing
)
181 bm
.rightBearing
= bmChild
.rightBearing
;
182 } else if (aStretchDirection
== NS_STRETCH_DIRECTION_VERTICAL
) {
183 // just sum-up the sizes horizontally.
186 NS_ERROR("unexpected case in GetPreferredStretchSize");
190 childFrame
= childFrame
->GetNextSibling();
192 aPreferredStretchSize
= bm
;
197 nsMathMLContainerFrame::Stretch(DrawTarget
* aDrawTarget
,
198 nsStretchDirection aStretchDirection
,
199 nsBoundingMetrics
& aContainerSize
,
200 ReflowOutput
& aDesiredStretchSize
) {
201 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
)) {
202 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData
.flags
)) {
203 NS_WARNING("it is wrong to fire stretch more than once on a frame");
206 mPresentationData
.flags
|= NS_MATHML_STRETCH_DONE
;
208 // Pass the stretch to the base child ...
210 nsIFrame
* baseFrame
= mPresentationData
.baseFrame
;
212 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(baseFrame
);
213 NS_ASSERTION(mathMLFrame
, "Something is wrong somewhere");
215 // And the trick is that the child's rect.x is still holding the
216 // descent, and rect.y is still holding the ascent ...
217 ReflowOutput
childSize(aDesiredStretchSize
);
218 GetReflowAndBoundingMetricsFor(baseFrame
, childSize
,
219 childSize
.mBoundingMetrics
);
221 // See if we should downsize and confine the stretch to us...
222 // XXX there may be other cases where we can downsize the stretch,
223 // e.g., the first ∑ might appear big in the following situation
224 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
227 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
228 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
232 nsBoundingMetrics containerSize
= aContainerSize
;
233 if (aStretchDirection
!= mEmbellishData
.direction
&&
234 mEmbellishData
.direction
!= NS_STRETCH_DIRECTION_UNSUPPORTED
) {
236 mEmbellishData
.direction
!= NS_STRETCH_DIRECTION_DEFAULT
,
237 "Stretches may have a default direction, operators can not.");
238 if (mEmbellishData
.direction
== NS_STRETCH_DIRECTION_VERTICAL
239 ? NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
240 mPresentationData
.flags
)
241 : NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
242 mPresentationData
.flags
)) {
243 GetPreferredStretchSize(aDrawTarget
, 0, mEmbellishData
.direction
,
245 // Stop further recalculations
246 aStretchDirection
= mEmbellishData
.direction
;
248 // We aren't going to stretch the child, so just use the child
250 containerSize
= childSize
.mBoundingMetrics
;
254 // do the stretching...
255 mathMLFrame
->Stretch(aDrawTarget
, aStretchDirection
, containerSize
,
257 // store the updated metrics
258 SaveReflowAndBoundingMetricsFor(baseFrame
, childSize
,
259 childSize
.mBoundingMetrics
);
261 // Remember the siblings which were _deferred_.
262 // Now that this embellished child may have changed, we need to
263 // fire the stretch on its siblings using our updated size
265 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
266 mPresentationData
.flags
) ||
267 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
268 mPresentationData
.flags
)) {
269 nsStretchDirection stretchDir
=
270 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
271 mPresentationData
.flags
)
272 ? NS_STRETCH_DIRECTION_VERTICAL
273 : NS_STRETCH_DIRECTION_HORIZONTAL
;
275 GetPreferredStretchSize(aDrawTarget
, STRETCH_CONSIDER_EMBELLISHMENTS
,
276 stretchDir
, containerSize
);
278 nsIFrame
* childFrame
= mFrames
.FirstChild();
280 if (childFrame
!= mPresentationData
.baseFrame
) {
281 mathMLFrame
= do_QueryFrame(childFrame
);
283 // retrieve the metrics that was stored at the previous pass
284 GetReflowAndBoundingMetricsFor(childFrame
, childSize
,
285 childSize
.mBoundingMetrics
);
286 // do the stretching...
287 mathMLFrame
->Stretch(aDrawTarget
, stretchDir
, containerSize
,
289 // store the updated metrics
290 SaveReflowAndBoundingMetricsFor(childFrame
, childSize
,
291 childSize
.mBoundingMetrics
);
294 childFrame
= childFrame
->GetNextSibling();
298 // re-position all our children
299 nsresult rv
= Place(aDrawTarget
, true, aDesiredStretchSize
);
301 // Make sure the child frames get their DidReflow() calls.
302 DidReflowChildren(mFrames
.FirstChild());
305 // If our parent is not embellished, it means we are the outermost
306 // embellished container and so we put the spacing, otherwise we don't
307 // include the spacing, the outermost embellished container will take
310 nsEmbellishData parentData
;
311 GetEmbellishDataFrom(GetParent(), parentData
);
312 // ensure that we are the embellished child, not just a sibling
313 // (need to test coreFrame since <mfrac> resets other things)
314 if (parentData
.coreFrame
!= mEmbellishData
.coreFrame
) {
315 // (we fetch values from the core since they may use units that depend
316 // on style data, and style changes could have occurred in the core
317 // since our last visit there)
318 nsEmbellishData coreData
;
319 GetEmbellishDataFrom(mEmbellishData
.coreFrame
, coreData
);
321 mBoundingMetrics
.width
+=
322 coreData
.leadingSpace
+ coreData
.trailingSpace
;
323 aDesiredStretchSize
.Width() = mBoundingMetrics
.width
;
324 aDesiredStretchSize
.mBoundingMetrics
.width
= mBoundingMetrics
.width
;
326 nscoord dx
= StyleVisibility()->mDirection
== StyleDirection::Rtl
327 ? coreData
.trailingSpace
328 : coreData
.leadingSpace
;
330 mBoundingMetrics
.leftBearing
+= dx
;
331 mBoundingMetrics
.rightBearing
+= dx
;
332 aDesiredStretchSize
.mBoundingMetrics
.leftBearing
+= dx
;
333 aDesiredStretchSize
.mBoundingMetrics
.rightBearing
+= dx
;
335 nsIFrame
* childFrame
= mFrames
.FirstChild();
337 childFrame
->SetPosition(childFrame
->GetPosition() +
339 childFrame
= childFrame
->GetNextSibling();
344 // Finished with these:
345 ClearSavedChildMetrics();
346 // Set our overflow area
347 GatherAndStoreOverflow(&aDesiredStretchSize
);
354 nsresult
nsMathMLContainerFrame::FinalizeReflow(DrawTarget
* aDrawTarget
,
355 ReflowOutput
& aDesiredSize
) {
356 // During reflow, we use rect.x and rect.y as placeholders for the child's
357 // ascent and descent in expectation of a stretch command. Hence we need to
358 // ensure that a stretch command will actually be fired later on, after
359 // exiting from our reflow. If the stretch is not fired, the rect.x, and
360 // rect.y will remain with inappropriate data causing children to be
361 // improperly positioned. This helper method checks to see if our parent will
362 // fire a stretch command targeted at us. If not, we go ahead and fire an
363 // involutive stretch on ourselves. This will clear all the rect.x and rect.y,
364 // and return our desired size.
366 // First, complete the post-reflow hook.
367 // We use the information in our children rectangles to position them.
368 // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
369 // They will still be holding the ascent and descent for each child.
371 // The first clause caters for any non-embellished container.
372 // The second clause is for a container which won't fire stretch even though
373 // it is embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test
374 // is convoluted because it excludes the particular case of the core
375 // <mo>...</mo> itself.
376 // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
378 !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) ||
379 (mEmbellishData
.coreFrame
!= this && !mPresentationData
.baseFrame
&&
380 mEmbellishData
.direction
== NS_STRETCH_DIRECTION_UNSUPPORTED
);
381 nsresult rv
= Place(aDrawTarget
, placeOrigin
, aDesiredSize
);
383 // Place() will call FinishReflowChild() when placeOrigin is true but if
384 // it returns before reaching FinishReflowChild() due to errors we need
385 // to fulfill the reflow protocol by calling DidReflow for the child frames
386 // that still needs it here (or we may crash - bug 366012).
387 // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
388 // through Stretch() eventually.
390 GatherAndStoreOverflow(&aDesiredSize
);
391 DidReflowChildren(PrincipalChildList().FirstChild());
395 bool parentWillFireStretch
= false;
397 // This means the rect.x and rect.y of our children were not set!!
398 // Don't go without checking to see if our parent will later fire a
399 // Stretch() command targeted at us. The Stretch() will cause the rect.x and
400 // rect.y to clear...
401 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(GetParent());
403 nsEmbellishData embellishData
;
404 nsPresentationData presentationData
;
405 mathMLFrame
->GetEmbellishData(embellishData
);
406 mathMLFrame
->GetPresentationData(presentationData
);
407 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
408 presentationData
.flags
) ||
409 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
410 presentationData
.flags
) ||
411 (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
) &&
412 presentationData
.baseFrame
== this)) {
413 parentWillFireStretch
= true;
416 if (!parentWillFireStretch
) {
417 // There is nobody who will fire the stretch for us, we do it ourselves!
420 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
422 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
423 mPresentationData
.flags
);
425 nsStretchDirection stretchDir
;
426 if (mEmbellishData
.coreFrame
==
427 this || /* case of a bare <mo>...</mo> itself */
428 (mEmbellishData
.direction
== NS_STRETCH_DIRECTION_HORIZONTAL
&&
429 stretchAll
) || /* or <mover><mo>...</mo>...</mover>, or friends */
430 mEmbellishData
.direction
==
431 NS_STRETCH_DIRECTION_UNSUPPORTED
) { /* Doesn't stretch */
432 stretchDir
= mEmbellishData
.direction
;
434 // Let the Stretch() call decide the direction.
435 stretchDir
= NS_STRETCH_DIRECTION_DEFAULT
;
437 // Use our current size as computed earlier by Place()
438 // The stretch call will detect if this is incorrect and recalculate the
440 nsBoundingMetrics defaultSize
= aDesiredSize
.mBoundingMetrics
;
442 Stretch(aDrawTarget
, stretchDir
, defaultSize
, aDesiredSize
);
445 // The Place() call above didn't request FinishReflowChild(),
446 // so let's check that we eventually did through Stretch().
447 for (nsIFrame
* childFrame
: PrincipalChildList()) {
448 NS_ASSERTION(!childFrame
->HasAnyStateBits(NS_FRAME_IN_REFLOW
),
449 "DidReflow() was never called");
456 // Also return our bounding metrics
457 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
459 // see if we should fix the spacing
460 FixInterFrameSpacing(aDesiredSize
);
462 if (!parentWillFireStretch
) {
463 // Not expecting a stretch.
464 // Finished with these:
465 ClearSavedChildMetrics();
466 // Set our overflow area.
467 GatherAndStoreOverflow(&aDesiredSize
);
474 * nsIMathMLFrame - support methods for scripting elements (nested frames
475 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
476 * mfrac, mroot, mtable).
477 * =============================================================================
480 // helper to let the update of presentation data pass through
481 // a subtree that may contain non-mathml container frames
483 void nsMathMLContainerFrame::PropagatePresentationDataFor(
484 nsIFrame
* aFrame
, uint32_t aFlagsValues
, uint32_t aFlagsToUpdate
) {
485 if (!aFrame
|| !aFlagsToUpdate
) return;
486 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
489 mathMLFrame
->UpdatePresentationData(aFlagsValues
, aFlagsToUpdate
);
490 // propagate using the base method to make sure that the control
491 // is passed on to MathML frames that may be overloading the method
492 mathMLFrame
->UpdatePresentationDataFromChildAt(0, -1, aFlagsValues
,
495 // propagate down the subtrees
496 for (nsIFrame
* childFrame
: aFrame
->PrincipalChildList()) {
497 PropagatePresentationDataFor(childFrame
, aFlagsValues
, aFlagsToUpdate
);
503 void nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(
504 nsIFrame
* aParentFrame
, int32_t aFirstChildIndex
, int32_t aLastChildIndex
,
505 uint32_t aFlagsValues
, uint32_t aFlagsToUpdate
) {
506 if (!aParentFrame
|| !aFlagsToUpdate
) return;
508 for (nsIFrame
* childFrame
: aParentFrame
->PrincipalChildList()) {
509 if ((index
>= aFirstChildIndex
) &&
510 ((aLastChildIndex
<= 0) ||
511 ((aLastChildIndex
> 0) && (index
<= aLastChildIndex
)))) {
512 PropagatePresentationDataFor(childFrame
, aFlagsValues
, aFlagsToUpdate
);
518 /* //////////////////
520 * =============================================================================
523 void nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
524 const nsDisplayListSet
& aLists
) {
525 BuildDisplayListForInline(aBuilder
, aLists
);
527 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
530 // if you want to see your bounding box, make sure to properly fill
531 // your mBoundingMetrics and mReference point, and set
532 // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
533 // in the Init() of your sub-class
534 DisplayBoundingMetrics(aBuilder
, this, mReference
, mBoundingMetrics
, aLists
);
538 // Note that this method re-builds the automatic data in the children -- not
539 // in aParentFrame itself (except for those particular operations that the
540 // parent frame may do in its TransmitAutomaticData()).
542 void nsMathMLContainerFrame::RebuildAutomaticDataForChildren(
543 nsIFrame
* aParentFrame
) {
544 // 1. As we descend the tree, make each child frame inherit data from
546 // 2. As we ascend the tree, transmit any specific change that we want
548 for (nsIFrame
* childFrame
: aParentFrame
->PrincipalChildList()) {
549 nsIMathMLFrame
* childMathMLFrame
= do_QueryFrame(childFrame
);
550 if (childMathMLFrame
) {
551 childMathMLFrame
->InheritAutomaticData(aParentFrame
);
553 RebuildAutomaticDataForChildren(childFrame
);
555 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aParentFrame
);
557 mathMLFrame
->TransmitAutomaticData();
562 nsresult
nsMathMLContainerFrame::ReLayoutChildren(nsIFrame
* aParentFrame
) {
563 if (!aParentFrame
) return NS_OK
;
565 // walk-up to the first frame that is a MathML frame, stop if we reach <math>
566 nsIFrame
* frame
= aParentFrame
;
568 nsIFrame
* parent
= frame
->GetParent();
569 if (!parent
|| !parent
->GetContent()) break;
571 // stop if it is a MathML frame
572 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(frame
);
573 if (mathMLFrame
) break;
575 // stop if we reach the root <math> tag
576 nsIContent
* content
= frame
->GetContent();
577 NS_ASSERTION(content
, "dangling frame without a content node");
579 if (content
->IsMathMLElement(nsGkAtoms::math
)) break;
584 // re-sync the presentation data and embellishment data of our children
585 RebuildAutomaticDataForChildren(frame
);
587 // Ask our parent frame to reflow us
588 nsIFrame
* parent
= frame
->GetParent();
589 NS_ASSERTION(parent
, "No parent to pass the reflow request up to");
590 if (!parent
) return NS_OK
;
592 frame
->PresShell()->FrameNeedsReflow(
593 frame
, IntrinsicDirty::FrameAncestorsAndDescendants
, NS_FRAME_IS_DIRTY
);
598 // There are precise rules governing children of a MathML frame,
599 // and properties such as the scriptlevel depends on those rules.
600 // Hence for things to work, callers must use Append/Insert/etc wisely.
602 nsresult
nsMathMLContainerFrame::ChildListChanged(int32_t aModType
) {
603 // If this is an embellished frame we need to rebuild the
604 // embellished hierarchy by walking-up to the parent of the
605 // outermost embellished container.
606 nsIFrame
* frame
= this;
607 if (mEmbellishData
.coreFrame
) {
608 nsIFrame
* parent
= GetParent();
609 nsEmbellishData embellishData
;
610 for (; parent
; frame
= parent
, parent
= parent
->GetParent()) {
611 GetEmbellishDataFrom(parent
, embellishData
);
612 if (embellishData
.coreFrame
!= mEmbellishData
.coreFrame
) break;
615 return ReLayoutChildren(frame
);
618 void nsMathMLContainerFrame::AppendFrames(ChildListID aListID
,
619 nsFrameList
&& aFrameList
) {
620 MOZ_ASSERT(aListID
== FrameChildListID::Principal
);
621 mFrames
.AppendFrames(this, std::move(aFrameList
));
622 ChildListChanged(dom::MutationEvent_Binding::ADDITION
);
625 void nsMathMLContainerFrame::InsertFrames(
626 ChildListID aListID
, nsIFrame
* aPrevFrame
,
627 const nsLineList::iterator
* aPrevFrameLine
, nsFrameList
&& aFrameList
) {
628 MOZ_ASSERT(aListID
== FrameChildListID::Principal
);
629 mFrames
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
630 ChildListChanged(dom::MutationEvent_Binding::ADDITION
);
633 void nsMathMLContainerFrame::RemoveFrame(DestroyContext
& aContext
,
635 nsIFrame
* aOldFrame
) {
636 MOZ_ASSERT(aListID
== FrameChildListID::Principal
);
637 mFrames
.DestroyFrame(aContext
, aOldFrame
);
638 ChildListChanged(dom::MutationEvent_Binding::REMOVAL
);
641 nsresult
nsMathMLContainerFrame::AttributeChanged(int32_t aNameSpaceID
,
644 // XXX Since they are numerous MathML attributes that affect layout, and
645 // we can't check all of them here, play safe by requesting a reflow.
646 // XXXldb This should only do work for attributes that cause changes!
647 PresShell()->FrameNeedsReflow(
648 this, IntrinsicDirty::FrameAncestorsAndDescendants
, NS_FRAME_IS_DIRTY
);
653 void nsMathMLContainerFrame::GatherAndStoreOverflow(ReflowOutput
* aMetrics
) {
654 mBlockStartAscent
= aMetrics
->BlockStartAscent();
656 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
658 aMetrics
->SetOverflowAreasToDesiredBounds();
660 ComputeCustomOverflow(aMetrics
->mOverflowAreas
);
662 // mBoundingMetrics does not necessarily include content of <mpadded>
663 // elements whose mBoundingMetrics may not be representative of the true
664 // bounds, and doesn't include the CSS2 outline rectangles of children, so
665 // make such to include child overflow areas.
666 UnionChildOverflow(aMetrics
->mOverflowAreas
);
668 FinishAndStoreOverflow(aMetrics
);
671 bool nsMathMLContainerFrame::ComputeCustomOverflow(
672 OverflowAreas
& aOverflowAreas
) {
673 // All non-child-frame content such as nsMathMLChars (and most child-frame
674 // content) is included in mBoundingMetrics.
676 mBoundingMetrics
.leftBearing
, mBlockStartAscent
- mBoundingMetrics
.ascent
,
677 mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
,
678 mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
);
680 // REVIEW: Maybe this should contribute only to ink overflow
681 // and not scrollable?
682 aOverflowAreas
.UnionAllWith(boundingBox
);
683 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas
);
686 void nsMathMLContainerFrame::ReflowChild(nsIFrame
* aChildFrame
,
687 nsPresContext
* aPresContext
,
688 ReflowOutput
& aDesiredSize
,
689 const ReflowInput
& aReflowInput
,
690 nsReflowStatus
& aStatus
) {
691 // Having foreign/hybrid children, e.g., from html markups, is not defined by
692 // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
693 // to do some cool demos... or we may have a child that is an nsInlineFrame
694 // from a generated content such as :before { content: open-quote } or
695 // :after { content: close-quote }. Unfortunately, the other frames out-there
696 // may expect their own invariants that are not met when we mix things.
697 // Hence we do not claim their support, but we will nevertheless attempt to
698 // keep them in the flow, if we can get their desired size. We observed that
699 // most frames may be reflowed generically, but nsInlineFrames need extra
703 nsInlineFrame
* inlineFrame
= do_QueryFrame(aChildFrame
);
704 NS_ASSERTION(!inlineFrame
, "Inline frames should be wrapped in blocks");
707 nsContainerFrame::ReflowChild(aChildFrame
, aPresContext
, aDesiredSize
,
709 ReflowChildFlags::NoMoveFrame
, aStatus
);
711 if (aDesiredSize
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
712 // This will be suitable for inline frames, which are wrapped in a block.
714 WritingMode wm
= aDesiredSize
.GetWritingMode();
715 if (!nsLayoutUtils::GetLastLineBaseline(wm
, aChildFrame
, &ascent
)) {
716 // We don't expect any other block children so just place the frame on
717 // the baseline instead of going through DidReflow() and
718 // GetBaseline(). This is what nsIFrame::GetBaseline() will do anyway.
719 aDesiredSize
.SetBlockStartAscent(aDesiredSize
.BSize(wm
));
721 aDesiredSize
.SetBlockStartAscent(ascent
);
724 if (IsForeignChild(aChildFrame
)) {
725 // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
726 nsRect r
= aChildFrame
->ComputeTightBounds(
727 aReflowInput
.mRenderingContext
->GetDrawTarget());
728 aDesiredSize
.mBoundingMetrics
.leftBearing
= r
.x
;
729 aDesiredSize
.mBoundingMetrics
.rightBearing
= r
.XMost();
730 aDesiredSize
.mBoundingMetrics
.ascent
=
731 aDesiredSize
.BlockStartAscent() - r
.y
;
732 aDesiredSize
.mBoundingMetrics
.descent
=
733 r
.YMost() - aDesiredSize
.BlockStartAscent();
734 aDesiredSize
.mBoundingMetrics
.width
= aDesiredSize
.Width();
738 void nsMathMLContainerFrame::Reflow(nsPresContext
* aPresContext
,
739 ReflowOutput
& aDesiredSize
,
740 const ReflowInput
& aReflowInput
,
741 nsReflowStatus
& aStatus
) {
742 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
747 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
749 aDesiredSize
.Width() = aDesiredSize
.Height() = 0;
750 aDesiredSize
.SetBlockStartAscent(0);
751 aDesiredSize
.mBoundingMetrics
= nsBoundingMetrics();
755 // Asking each child to cache its bounding metrics
757 nsReflowStatus childStatus
;
758 nsIFrame
* childFrame
= mFrames
.FirstChild();
760 ReflowOutput
childDesiredSize(aReflowInput
);
761 WritingMode wm
= childFrame
->GetWritingMode();
762 LogicalSize availSize
= aReflowInput
.ComputedSize(wm
);
763 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
764 ReflowInput
childReflowInput(aPresContext
, aReflowInput
, childFrame
,
766 ReflowChild(childFrame
, aPresContext
, childDesiredSize
, childReflowInput
,
768 // NS_ASSERTION(childStatus.IsComplete(), "bad status");
769 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
770 childDesiredSize
.mBoundingMetrics
);
771 childFrame
= childFrame
->GetNextSibling();
775 // If we are a container which is entitled to stretch its children, then we
776 // ask our stretchy children to stretch themselves
778 // The stretching of siblings of an embellished child is _deferred_ until
779 // after finishing the stretching of the embellished child - bug 117652
781 DrawTarget
* drawTarget
= aReflowInput
.mRenderingContext
->GetDrawTarget();
783 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) &&
784 (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(
785 mPresentationData
.flags
) ||
786 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(
787 mPresentationData
.flags
))) {
788 // get the stretchy direction
789 nsStretchDirection stretchDir
=
790 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)
791 ? NS_STRETCH_DIRECTION_VERTICAL
792 : NS_STRETCH_DIRECTION_HORIZONTAL
;
794 // what size should we use to stretch our stretchy children
795 // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not
796 // known yet We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we
797 // don't want to include them in the caculations of the size of stretchy
799 nsBoundingMetrics containerSize
;
800 GetPreferredStretchSize(drawTarget
, 0, stretchDir
, containerSize
);
802 // fire the stretch on each child
803 childFrame
= mFrames
.FirstChild();
805 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
807 // retrieve the metrics that was stored at the previous pass
808 ReflowOutput
childDesiredSize(aReflowInput
);
809 GetReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
810 childDesiredSize
.mBoundingMetrics
);
812 mathMLFrame
->Stretch(drawTarget
, stretchDir
, containerSize
,
814 // store the updated metrics
815 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
816 childDesiredSize
.mBoundingMetrics
);
818 childFrame
= childFrame
->GetNextSibling();
823 // Place children now by re-adjusting the origins to align the baselines
824 FinalizeReflow(drawTarget
, aDesiredSize
);
827 static nscoord
AddInterFrameSpacingToSize(ReflowOutput
& aDesiredSize
,
828 nsMathMLContainerFrame
* aFrame
);
831 void nsMathMLContainerFrame::MarkIntrinsicISizesDirty() {
832 mIntrinsicISize
= NS_INTRINSIC_ISIZE_UNKNOWN
;
833 nsContainerFrame::MarkIntrinsicISizesDirty();
836 void nsMathMLContainerFrame::UpdateIntrinsicISize(
837 gfxContext
* aRenderingContext
) {
838 if (mIntrinsicISize
== NS_INTRINSIC_ISIZE_UNKNOWN
) {
839 ReflowOutput
desiredSize(GetWritingMode());
840 GetIntrinsicISizeMetrics(aRenderingContext
, desiredSize
);
842 // Include the additional width added by FixInterFrameSpacing to ensure
843 // consistent width calculations.
844 AddInterFrameSpacingToSize(desiredSize
, this);
845 mIntrinsicISize
= desiredSize
.ISize(GetWritingMode());
850 nscoord
nsMathMLContainerFrame::GetMinISize(gfxContext
* aRenderingContext
) {
851 UpdateIntrinsicISize(aRenderingContext
);
852 return mIntrinsicISize
;
856 nscoord
nsMathMLContainerFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
857 UpdateIntrinsicISize(aRenderingContext
);
858 return mIntrinsicISize
;
862 void nsMathMLContainerFrame::GetIntrinsicISizeMetrics(
863 gfxContext
* aRenderingContext
, ReflowOutput
& aDesiredSize
) {
865 nsIFrame
* childFrame
= mFrames
.FirstChild();
867 ReflowOutput
childDesiredSize(GetWritingMode()); // ???
869 nsMathMLContainerFrame
* containerFrame
= do_QueryFrame(childFrame
);
870 if (containerFrame
) {
871 containerFrame
->GetIntrinsicISizeMetrics(aRenderingContext
,
874 // XXX This includes margin while Reflow currently doesn't consider
875 // margin, so we may end up with too much space, but, with stretchy
876 // characters, this is an approximation anyway.
877 nscoord width
= nsLayoutUtils::IntrinsicForContainer(
878 aRenderingContext
, childFrame
, IntrinsicISizeType::PrefISize
);
880 childDesiredSize
.Width() = width
;
881 childDesiredSize
.mBoundingMetrics
.width
= width
;
882 childDesiredSize
.mBoundingMetrics
.leftBearing
= 0;
883 childDesiredSize
.mBoundingMetrics
.rightBearing
= width
;
886 if (NS_SUCCEEDED(childFrame
->GetPrefWidthTightBounds(aRenderingContext
,
888 childDesiredSize
.mBoundingMetrics
.leftBearing
= x
;
889 childDesiredSize
.mBoundingMetrics
.rightBearing
= xMost
;
893 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
894 childDesiredSize
.mBoundingMetrics
);
896 childFrame
= childFrame
->GetNextSibling();
901 MeasureForWidth(aRenderingContext
->GetDrawTarget(), aDesiredSize
);
903 PlaceAsMrow(aRenderingContext
->GetDrawTarget(), false, aDesiredSize
);
906 ClearSavedChildMetrics();
910 nsresult
nsMathMLContainerFrame::MeasureForWidth(DrawTarget
* aDrawTarget
,
911 ReflowOutput
& aDesiredSize
) {
912 return Place(aDrawTarget
, false, aDesiredSize
);
915 // see spacing table in Chapter 18, TeXBook (p.170)
916 // Our table isn't quite identical to TeX because operators have
917 // built-in values for lspace & rspace in the Operator Dictionary.
919 kInterFrameSpacingTable
[eMathMLFrameType_COUNT
][eMathMLFrameType_COUNT
] = {
920 // in units of muspace.
921 // upper half of the byte is set if the
922 // spacing is not to be used for scriptlevel > 0
924 /* Ord OpOrd OpInv OpUsr Inner Italic Upright */
925 /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
926 /*OpOrd */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
927 /*OpInv */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
928 /*OpUsr */ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
929 /*Inner */ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
930 /*Italic */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
931 /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00}};
933 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
934 /* no space if there is a frame that we know nothing about */ \
935 if (frametype1_ == eMathMLFrameType_UNKNOWN || \
936 frametype2_ == eMathMLFrameType_UNKNOWN) \
939 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
940 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
941 ? 0 /* spacing is disabled */ \
945 // This function computes the inter-space between two frames. However,
946 // since invisible operators need special treatment, the inter-space may
947 // be delayed when an invisible operator is encountered. In this case,
948 // the function will carry the inter-space forward until it is determined
949 // that it can be applied properly (i.e., until we encounter a visible
950 // frame where to decide whether to accept or reject the inter-space).
951 // aFromFrameType: remembers the frame when the carry-forward initiated.
952 // aCarrySpace: keeps track of the inter-space that is delayed.
953 // @returns: current inter-space (which is 0 when the true inter-space is
954 // delayed -- and thus has no effect since the frame is invisible anyway).
955 static nscoord
GetInterFrameSpacing(int32_t aScriptLevel
,
956 eMathMLFrameType aFirstFrameType
,
957 eMathMLFrameType aSecondFrameType
,
958 eMathMLFrameType
* aFromFrameType
, // IN/OUT
959 int32_t* aCarrySpace
) // IN/OUT
961 eMathMLFrameType firstType
= aFirstFrameType
;
962 eMathMLFrameType secondType
= aSecondFrameType
;
965 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
967 // feedback control to avoid the inter-space to be added when not necessary
968 if (secondType
== eMathMLFrameType_OperatorInvisible
) {
969 // see if we should start to carry the space forward until we
970 // encounter a visible frame
971 if (*aFromFrameType
== eMathMLFrameType_UNKNOWN
) {
972 *aFromFrameType
= firstType
;
973 *aCarrySpace
= space
;
975 // keep carrying *aCarrySpace forward, while returning 0 for this stage
977 } else if (*aFromFrameType
!= eMathMLFrameType_UNKNOWN
) {
978 // no carry-forward anymore, get the real inter-space between
979 // the two frames of interest
981 firstType
= *aFromFrameType
;
983 // But... the invisible operator that we encountered earlier could
984 // be sitting between italic and upright identifiers, e.g.,
986 // 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi>
987 // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
989 // the trick to get the inter-space in either situation
990 // is to promote "<mi>sin</mi><mo>⁡</mo>" and
991 // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
992 if (firstType
== eMathMLFrameType_UprightIdentifier
) {
993 firstType
= eMathMLFrameType_OperatorUserDefined
;
994 } else if (secondType
== eMathMLFrameType_UprightIdentifier
) {
995 secondType
= eMathMLFrameType_OperatorUserDefined
;
998 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
1000 // Now, we have two values: the computed space and the space that
1001 // has been carried forward until now. Which value do we pick?
1002 // If the second type is an operator (e.g., fence), it already has
1003 // built-in lspace & rspace, so we let them win. Otherwise we pick
1004 // the max between the two values that we have.
1005 if (secondType
!= eMathMLFrameType_OperatorOrdinary
&& space
< *aCarrySpace
)
1006 space
= *aCarrySpace
;
1008 // reset everything now that the carry-forward is done
1009 *aFromFrameType
= eMathMLFrameType_UNKNOWN
;
1016 static nscoord
GetThinSpace(const nsStyleFont
* aStyleFont
) {
1017 return aStyleFont
->mFont
.size
.ScaledBy(3.0f
/ 18.0f
).ToAppUnits();
1020 class nsMathMLContainerFrame::RowChildFrameIterator
{
1022 explicit RowChildFrameIterator(nsMathMLContainerFrame
* aParentFrame
)
1023 : mParentFrame(aParentFrame
),
1024 mReflowOutput(aParentFrame
->GetWritingMode()),
1026 mChildFrameType(eMathMLFrameType_UNKNOWN
),
1028 mFromFrameType(eMathMLFrameType_UNKNOWN
),
1029 mRTL(aParentFrame
->StyleVisibility()->mDirection
==
1030 StyleDirection::Rtl
) {
1032 mChildFrame
= aParentFrame
->mFrames
.FirstChild();
1034 mChildFrame
= aParentFrame
->mFrames
.LastChild();
1037 if (!mChildFrame
) return;
1039 InitMetricsForChild();
1042 RowChildFrameIterator
& operator++() {
1043 // add child size + italic correction
1044 mX
+= mReflowOutput
.mBoundingMetrics
.width
+ mItalicCorrection
;
1047 mChildFrame
= mChildFrame
->GetNextSibling();
1049 mChildFrame
= mChildFrame
->GetPrevSibling();
1052 if (!mChildFrame
) return *this;
1054 eMathMLFrameType prevFrameType
= mChildFrameType
;
1055 InitMetricsForChild();
1057 // add inter frame spacing
1058 const nsStyleFont
* font
= mParentFrame
->StyleFont();
1060 GetInterFrameSpacing(font
->mMathDepth
, prevFrameType
, mChildFrameType
,
1061 &mFromFrameType
, &mCarrySpace
);
1062 mX
+= space
* GetThinSpace(font
);
1066 nsIFrame
* Frame() const { return mChildFrame
; }
1067 nscoord
X() const { return mX
; }
1068 const ReflowOutput
& GetReflowOutput() const { return mReflowOutput
; }
1069 nscoord
Ascent() const { return mReflowOutput
.BlockStartAscent(); }
1070 nscoord
Descent() const {
1071 return mReflowOutput
.Height() - mReflowOutput
.BlockStartAscent();
1073 const nsBoundingMetrics
& BoundingMetrics() const {
1074 return mReflowOutput
.mBoundingMetrics
;
1078 const nsMathMLContainerFrame
* mParentFrame
;
1079 nsIFrame
* mChildFrame
;
1080 ReflowOutput mReflowOutput
;
1083 nscoord mItalicCorrection
;
1084 eMathMLFrameType mChildFrameType
;
1085 int32_t mCarrySpace
;
1086 eMathMLFrameType mFromFrameType
;
1090 void InitMetricsForChild() {
1091 GetReflowAndBoundingMetricsFor(mChildFrame
, mReflowOutput
,
1092 mReflowOutput
.mBoundingMetrics
,
1094 nscoord leftCorrection
, rightCorrection
;
1095 GetItalicCorrection(mReflowOutput
.mBoundingMetrics
, leftCorrection
,
1097 if (!mChildFrame
->GetPrevSibling() &&
1098 mParentFrame
->GetContent()->IsMathMLElement(nsGkAtoms::msqrt_
)) {
1099 // Remove leading correction in <msqrt> because the sqrt glyph itself is
1104 rightCorrection
= 0;
1107 // add left correction -- this fixes the problem of the italic 'f'
1108 // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
1109 mX
+= leftCorrection
;
1110 mItalicCorrection
= rightCorrection
;
1115 nsresult
nsMathMLContainerFrame::Place(DrawTarget
* aDrawTarget
,
1117 ReflowOutput
& aDesiredSize
) {
1118 // This is needed in case this frame is empty (i.e., no child frames)
1119 mBoundingMetrics
= nsBoundingMetrics();
1121 RowChildFrameIterator
child(this);
1122 nscoord ascent
= 0, descent
= 0;
1123 while (child
.Frame()) {
1124 if (descent
< child
.Descent()) descent
= child
.Descent();
1125 if (ascent
< child
.Ascent()) ascent
= child
.Ascent();
1126 // add the child size
1127 mBoundingMetrics
.width
= child
.X();
1128 mBoundingMetrics
+= child
.BoundingMetrics();
1131 // Add the italic correction at the end (including the last child).
1132 // This gives a nice gap between math and non-math frames, and still
1133 // gives the same math inter-spacing in case this frame connects to
1134 // another math frame
1135 mBoundingMetrics
.width
= child
.X();
1137 aDesiredSize
.Width() = std::max(0, mBoundingMetrics
.width
);
1138 aDesiredSize
.Height() = ascent
+ descent
;
1139 aDesiredSize
.SetBlockStartAscent(ascent
);
1140 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
1143 mReference
.y
= aDesiredSize
.BlockStartAscent();
1149 PositionRowChildFrames(0, aDesiredSize
.BlockStartAscent());
1155 nsresult
nsMathMLContainerFrame::PlaceAsMrow(DrawTarget
* aDrawTarget
,
1157 ReflowOutput
& aDesiredSize
) {
1158 return nsMathMLContainerFrame::Place(aDrawTarget
, aPlaceOrigin
, aDesiredSize
);
1161 void nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX
,
1162 nscoord aBaseline
) {
1163 RowChildFrameIterator
child(this);
1164 while (child
.Frame()) {
1165 nscoord dx
= aOffsetX
+ child
.X();
1166 nscoord dy
= aBaseline
- child
.Ascent();
1167 FinishReflowChild(child
.Frame(), PresContext(), child
.GetReflowOutput(),
1168 nullptr, dx
, dy
, ReflowChildFlags::Default
);
1173 // helpers to fix the inter-spacing when <math> is the only parent
1174 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
1176 static nscoord
GetInterFrameSpacingFor(int32_t aScriptLevel
,
1177 nsIFrame
* aParentFrame
,
1178 nsIFrame
* aChildFrame
) {
1179 nsIFrame
* childFrame
= aParentFrame
->PrincipalChildList().FirstChild();
1180 if (!childFrame
|| aChildFrame
== childFrame
) return 0;
1182 int32_t carrySpace
= 0;
1183 eMathMLFrameType fromFrameType
= eMathMLFrameType_UNKNOWN
;
1184 eMathMLFrameType prevFrameType
= eMathMLFrameType_UNKNOWN
;
1185 eMathMLFrameType childFrameType
=
1186 nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1187 childFrame
= childFrame
->GetNextSibling();
1188 while (childFrame
) {
1189 prevFrameType
= childFrameType
;
1190 childFrameType
= nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1192 GetInterFrameSpacing(aScriptLevel
, prevFrameType
, childFrameType
,
1193 &fromFrameType
, &carrySpace
);
1194 if (aChildFrame
== childFrame
) {
1196 ComputedStyle
* parentContext
= aParentFrame
->Style();
1197 nscoord thinSpace
= GetThinSpace(parentContext
->StyleFont());
1199 return space
* thinSpace
;
1201 childFrame
= childFrame
->GetNextSibling();
1204 MOZ_ASSERT_UNREACHABLE("child not in the childlist of its parent");
1208 static nscoord
AddInterFrameSpacingToSize(ReflowOutput
& aDesiredSize
,
1209 nsMathMLContainerFrame
* aFrame
) {
1211 nsIFrame
* parent
= aFrame
->GetParent();
1212 nsIContent
* parentContent
= parent
->GetContent();
1213 if (MOZ_UNLIKELY(!parentContent
)) {
1216 if (parentContent
->IsAnyOfMathMLElements(nsGkAtoms::math
, nsGkAtoms::mtd_
)) {
1217 gap
= GetInterFrameSpacingFor(aFrame
->StyleFont()->mMathDepth
, parent
,
1219 // add our own italic correction
1220 nscoord leftCorrection
= 0, italicCorrection
= 0;
1221 nsMathMLContainerFrame::GetItalicCorrection(
1222 aDesiredSize
.mBoundingMetrics
, leftCorrection
, italicCorrection
);
1223 gap
+= leftCorrection
;
1225 aDesiredSize
.mBoundingMetrics
.leftBearing
+= gap
;
1226 aDesiredSize
.mBoundingMetrics
.rightBearing
+= gap
;
1227 aDesiredSize
.mBoundingMetrics
.width
+= gap
;
1228 aDesiredSize
.Width() += gap
;
1230 aDesiredSize
.mBoundingMetrics
.width
+= italicCorrection
;
1231 aDesiredSize
.Width() += italicCorrection
;
1236 nscoord
nsMathMLContainerFrame::FixInterFrameSpacing(
1237 ReflowOutput
& aDesiredSize
) {
1239 gap
= AddInterFrameSpacingToSize(aDesiredSize
, this);
1241 // Shift our children to account for the correction
1242 nsIFrame
* childFrame
= mFrames
.FirstChild();
1243 while (childFrame
) {
1244 childFrame
->SetPosition(childFrame
->GetPosition() + nsPoint(gap
, 0));
1245 childFrame
= childFrame
->GetNextSibling();
1252 void nsMathMLContainerFrame::DidReflowChildren(nsIFrame
* aFirst
,
1256 if (MOZ_UNLIKELY(!aFirst
)) return;
1258 for (nsIFrame
* frame
= aFirst
; frame
!= aStop
;
1259 frame
= frame
->GetNextSibling()) {
1260 NS_ASSERTION(frame
, "aStop isn't a sibling");
1261 if (frame
->HasAnyStateBits(NS_FRAME_IN_REFLOW
)) {
1262 // finish off principal descendants, too
1263 nsIFrame
* grandchild
= frame
->PrincipalChildList().FirstChild();
1264 if (grandchild
) DidReflowChildren(grandchild
, nullptr);
1266 frame
->DidReflow(frame
->PresContext(), nullptr);
1271 // helper used by mstyle, mphantom, mpadded and mrow in their implementations
1272 // of TransmitAutomaticData().
1273 nsresult
nsMathMLContainerFrame::TransmitAutomaticDataForMrowLikeElement() {
1275 // One loop to check both conditions below:
1277 // 1) whether all the children of the mrow-like element are space-like.
1279 // The REC defines the following elements to be "space-like":
1280 // * an mstyle, mphantom, or mpadded element, all of whose direct
1281 // sub-expressions are space-like;
1282 // * an mrow all of whose direct sub-expressions are space-like.
1284 // 2) whether all but one child of the mrow-like element are space-like and
1285 // this non-space-like child is an embellished operator.
1287 // The REC defines the following elements to be embellished operators:
1288 // * one of the elements mstyle, mphantom, or mpadded, such that an mrow
1289 // containing the same arguments would be an embellished operator;
1290 // * an mrow whose arguments consist (in any order) of one embellished
1291 // operator and zero or more space-like elements.
1293 nsIFrame
*childFrame
, *baseFrame
;
1294 bool embellishedOpFound
= false;
1295 nsEmbellishData embellishData
;
1297 for (childFrame
= PrincipalChildList().FirstChild(); childFrame
;
1298 childFrame
= childFrame
->GetNextSibling()) {
1299 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(childFrame
);
1300 if (!mathMLFrame
) break;
1301 if (!mathMLFrame
->IsSpaceLike()) {
1302 if (embellishedOpFound
) break;
1303 baseFrame
= childFrame
;
1304 GetEmbellishDataFrom(baseFrame
, embellishData
);
1305 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
)) break;
1306 embellishedOpFound
= true;
1311 // we successfully went to the end of the loop. This means that one of
1312 // condition 1) or 2) holds.
1313 if (!embellishedOpFound
) {
1314 // the mrow-like element is space-like.
1315 mPresentationData
.flags
|= NS_MATHML_SPACE_LIKE
;
1317 // the mrow-like element is an embellished operator.
1318 // let the state of the embellished operator found bubble to us.
1319 mPresentationData
.baseFrame
= baseFrame
;
1320 mEmbellishData
= embellishData
;
1324 if (childFrame
|| !embellishedOpFound
) {
1325 // The element is not embellished operator
1326 mPresentationData
.baseFrame
= nullptr;
1327 mEmbellishData
.flags
= 0;
1328 mEmbellishData
.coreFrame
= nullptr;
1329 mEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
1330 mEmbellishData
.leadingSpace
= 0;
1331 mEmbellishData
.trailingSpace
= 0;
1334 if (childFrame
|| embellishedOpFound
) {
1335 // The element is not space-like
1336 mPresentationData
.flags
&= ~NS_MATHML_SPACE_LIKE
;
1343 void nsMathMLContainerFrame::PropagateFrameFlagFor(nsIFrame
* aFrame
,
1344 nsFrameState aFlags
) {
1345 if (!aFrame
|| !aFlags
) return;
1347 aFrame
->AddStateBits(aFlags
);
1348 for (nsIFrame
* childFrame
: aFrame
->PrincipalChildList()) {
1349 PropagateFrameFlagFor(childFrame
, aFlags
);
1353 nsresult
nsMathMLContainerFrame::ReportErrorToConsole(
1354 const char* errorMsgId
, const nsTArray
<nsString
>& aParams
) {
1355 return nsContentUtils::ReportToConsole(
1356 nsIScriptError::errorFlag
, "Layout: MathML"_ns
, mContent
->OwnerDoc(),
1357 nsContentUtils::eMATHML_PROPERTIES
, errorMsgId
, aParams
);
1360 nsresult
nsMathMLContainerFrame::ReportParseError(const char16_t
* aAttribute
,
1361 const char16_t
* aValue
) {
1362 AutoTArray
<nsString
, 3> argv
;
1363 argv
.AppendElement(aValue
);
1364 argv
.AppendElement(aAttribute
);
1365 argv
.AppendElement(nsDependentAtomString(mContent
->NodeInfo()->NameAtom()));
1366 return ReportErrorToConsole("AttributeParsingError", argv
);
1369 nsresult
nsMathMLContainerFrame::ReportChildCountError() {
1370 AutoTArray
<nsString
, 1> arg
= {
1371 nsDependentAtomString(mContent
->NodeInfo()->NameAtom())};
1372 return ReportErrorToConsole("ChildCountIncorrect", arg
);
1375 nsresult
nsMathMLContainerFrame::ReportInvalidChildError(nsAtom
* aChildTag
) {
1376 AutoTArray
<nsString
, 2> argv
= {
1377 nsDependentAtomString(aChildTag
),
1378 nsDependentAtomString(mContent
->NodeInfo()->NameAtom())};
1379 return ReportErrorToConsole("InvalidChild", argv
);
1382 //==========================
1384 nsContainerFrame
* NS_NewMathMLmathBlockFrame(PresShell
* aPresShell
,
1385 ComputedStyle
* aStyle
) {
1386 auto newFrame
= new (aPresShell
)
1387 nsMathMLmathBlockFrame(aStyle
, aPresShell
->GetPresContext());
1391 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame
)
1393 NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame
)
1394 NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame
)
1395 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
1397 nsContainerFrame
* NS_NewMathMLmathInlineFrame(PresShell
* aPresShell
,
1398 ComputedStyle
* aStyle
) {
1399 return new (aPresShell
)
1400 nsMathMLmathInlineFrame(aStyle
, aPresShell
->GetPresContext());
1403 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame
)
1405 NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame
)
1406 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
1407 NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame
)