Backed out changeset 1d9301697aa0 (bug 1887752) for causing failures on browser_all_f...
[gecko.git] / layout / mathml / nsMathMLContainerFrame.cpp
blobfc334d1caea73d1b42663c985a398984074852cd
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"
10 #include "gfxUtils.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)
36 /* /////////////
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
43 // uses block reflow
44 return !aFrame->IsMathMLFrame() || aFrame->IsBlockFrame();
47 NS_DECLARE_FRAME_PROPERTY_DELETABLE(HTMLReflowOutputProperty, ReflowOutput)
49 /* static */
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
59 /* static */
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
69 // information.
70 NS_ASSERTION(reflowOutput, "Didn't SaveReflowAndBoundingMetricsFor frame!");
71 if (reflowOutput) {
72 aReflowOutput = *reflowOutput;
73 aBoundingMetrics = reflowOutput->mBoundingMetrics;
76 if (aMathMLFrameType) {
77 if (!IsForeignChild(aFrame)) {
78 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
79 if (mathMLFrame) {
80 *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
81 return;
84 *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
88 void nsMathMLContainerFrame::ClearSavedChildMetrics() {
89 nsIFrame* childFrame = mFrames.FirstChild();
90 while (childFrame) {
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;
110 } else {
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");
121 NS_ASSERTION(
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;
128 while (childFrame) {
129 // initializations in case this child happens not to be a MathML frame
130 nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
131 if (mathMLFrame) {
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
140 // child itself
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);
150 } else {
151 ReflowOutput unused(GetWritingMode());
152 GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
155 if (firstTime) {
156 firstTime = false;
157 bm = bmChild;
158 if (!stretchAll) {
159 // we may get here for cases such as <msup><mo>...</mo> ... </msup>,
160 // or <maction>...<mo>...</mo></maction>.
161 break;
163 } else {
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
173 // the origin.
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.
184 bm += bmChild;
185 } else {
186 NS_ERROR("unexpected case in GetPreferredStretchSize");
187 break;
190 childFrame = childFrame->GetNextSibling();
192 aPreferredStretchSize = bm;
196 NS_IMETHODIMP
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");
204 return NS_OK;
206 mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
208 // Pass the stretch to the base child ...
210 nsIFrame* baseFrame = mPresentationData.baseFrame;
211 if (baseFrame) {
212 nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
213 NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
214 if (mathMLFrame) {
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 &Sum; might appear big in the following situation
224 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
225 // <mstyle>
226 // <msub>
227 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
228 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
229 // </msub>
230 // </mstyle>
231 // </math>
232 nsBoundingMetrics containerSize = aContainerSize;
233 if (aStretchDirection != mEmbellishData.direction &&
234 mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED) {
235 NS_ASSERTION(
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,
244 containerSize);
245 // Stop further recalculations
246 aStretchDirection = mEmbellishData.direction;
247 } else {
248 // We aren't going to stretch the child, so just use the child
249 // metrics.
250 containerSize = childSize.mBoundingMetrics;
254 // do the stretching...
255 mathMLFrame->Stretch(aDrawTarget, aStretchDirection, containerSize,
256 childSize);
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();
279 while (childFrame) {
280 if (childFrame != mPresentationData.baseFrame) {
281 mathMLFrame = do_QueryFrame(childFrame);
282 if (mathMLFrame) {
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,
288 childSize);
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);
300 if (NS_FAILED(rv)) {
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
308 // care of it.
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;
329 if (dx != 0) {
330 mBoundingMetrics.leftBearing += dx;
331 mBoundingMetrics.rightBearing += dx;
332 aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
333 aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
335 nsIFrame* childFrame = mFrames.FirstChild();
336 while (childFrame) {
337 childFrame->SetPosition(childFrame->GetPosition() +
338 nsPoint(dx, 0));
339 childFrame = childFrame->GetNextSibling();
344 // Finished with these:
345 ClearSavedChildMetrics();
346 // Set our overflow area
347 GatherAndStoreOverflow(&aDesiredStretchSize);
351 return NS_OK;
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)
377 bool placeOrigin =
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.
389 if (NS_FAILED(rv)) {
390 GatherAndStoreOverflow(&aDesiredSize);
391 DidReflowChildren(PrincipalChildList().FirstChild());
392 return rv;
395 bool parentWillFireStretch = false;
396 if (!placeOrigin) {
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());
402 if (mathMLFrame) {
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!
419 bool stretchAll =
420 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
421 || */
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;
433 } else {
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
439 // size.
440 nsBoundingMetrics defaultSize = aDesiredSize.mBoundingMetrics;
442 Stretch(aDrawTarget, stretchDir, defaultSize, aDesiredSize);
443 #ifdef DEBUG
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");
452 #endif
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);
470 return NS_OK;
473 /* /////////////
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
482 /* static */
483 void nsMathMLContainerFrame::PropagatePresentationDataFor(
484 nsIFrame* aFrame, uint32_t aFlagsValues, uint32_t aFlagsToUpdate) {
485 if (!aFrame || !aFlagsToUpdate) return;
486 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
487 if (mathMLFrame) {
488 // update
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,
493 aFlagsToUpdate);
494 } else {
495 // propagate down the subtrees
496 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
497 PropagatePresentationDataFor(childFrame, aFlagsValues, aFlagsToUpdate);
502 /* static */
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;
507 int32_t index = 0;
508 for (nsIFrame* childFrame : aParentFrame->PrincipalChildList()) {
509 if ((index >= aFirstChildIndex) &&
510 ((aLastChildIndex <= 0) ||
511 ((aLastChildIndex > 0) && (index <= aLastChildIndex)))) {
512 PropagatePresentationDataFor(childFrame, aFlagsValues, aFlagsToUpdate);
514 index++;
518 /* //////////////////
519 * Frame construction
520 * =============================================================================
523 void nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
524 const nsDisplayListSet& aLists) {
525 BuildDisplayListForInline(aBuilder, aLists);
527 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
528 // for visual debug
529 // ----------------
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);
535 #endif
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()).
541 /* static */
542 void nsMathMLContainerFrame::RebuildAutomaticDataForChildren(
543 nsIFrame* aParentFrame) {
544 // 1. As we descend the tree, make each child frame inherit data from
545 // the parent
546 // 2. As we ascend the tree, transmit any specific change that we want
547 // down the subtrees
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);
556 if (mathMLFrame) {
557 mathMLFrame->TransmitAutomaticData();
561 /* static */
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;
567 while (1) {
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");
578 if (!content) break;
579 if (content->IsMathMLElement(nsGkAtoms::math)) break;
581 frame = parent;
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);
595 return NS_OK;
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,
634 ChildListID aListID,
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,
642 nsAtom* aAttribute,
643 int32_t aModType) {
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);
650 return NS_OK;
653 void nsMathMLContainerFrame::GatherAndStoreOverflow(ReflowOutput* aMetrics) {
654 mBlockStartAscent = aMetrics->BlockStartAscent();
656 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
657 // frame rectangle.
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.
675 nsRect boundingBox(
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
700 // care.
702 #ifdef DEBUG
703 nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
704 NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
705 #endif
707 nsContainerFrame::ReflowChild(aChildFrame, aPresContext, aDesiredSize,
708 aReflowInput, 0, 0,
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.
713 nscoord ascent;
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));
720 } else {
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()) {
743 return;
746 MarkInReflow();
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();
753 /////////////
754 // Reflow children
755 // Asking each child to cache its bounding metrics
757 nsReflowStatus childStatus;
758 nsIFrame* childFrame = mFrames.FirstChild();
759 while (childFrame) {
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,
765 availSize);
766 ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
767 childStatus);
768 // NS_ASSERTION(childStatus.IsComplete(), "bad status");
769 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
770 childDesiredSize.mBoundingMetrics);
771 childFrame = childFrame->GetNextSibling();
774 /////////////
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
798 // elements
799 nsBoundingMetrics containerSize;
800 GetPreferredStretchSize(drawTarget, 0, stretchDir, containerSize);
802 // fire the stretch on each child
803 childFrame = mFrames.FirstChild();
804 while (childFrame) {
805 nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
806 if (mathMLFrame) {
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,
813 childDesiredSize);
814 // store the updated metrics
815 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
816 childDesiredSize.mBoundingMetrics);
818 childFrame = childFrame->GetNextSibling();
822 /////////////
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);
830 /* virtual */
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());
849 /* virtual */
850 nscoord nsMathMLContainerFrame::GetMinISize(gfxContext* aRenderingContext) {
851 UpdateIntrinsicISize(aRenderingContext);
852 return mIntrinsicISize;
855 /* virtual */
856 nscoord nsMathMLContainerFrame::GetPrefISize(gfxContext* aRenderingContext) {
857 UpdateIntrinsicISize(aRenderingContext);
858 return mIntrinsicISize;
861 /* virtual */
862 void nsMathMLContainerFrame::GetIntrinsicISizeMetrics(
863 gfxContext* aRenderingContext, ReflowOutput& aDesiredSize) {
864 // Get child widths
865 nsIFrame* childFrame = mFrames.FirstChild();
866 while (childFrame) {
867 ReflowOutput childDesiredSize(GetWritingMode()); // ???
869 nsMathMLContainerFrame* containerFrame = do_QueryFrame(childFrame);
870 if (containerFrame) {
871 containerFrame->GetIntrinsicISizeMetrics(aRenderingContext,
872 childDesiredSize);
873 } else {
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;
885 nscoord x, xMost;
886 if (NS_SUCCEEDED(childFrame->GetPrefWidthTightBounds(aRenderingContext,
887 &x, &xMost))) {
888 childDesiredSize.mBoundingMetrics.leftBearing = x;
889 childDesiredSize.mBoundingMetrics.rightBearing = xMost;
893 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
894 childDesiredSize.mBoundingMetrics);
896 childFrame = childFrame->GetNextSibling();
899 // Measure
900 nsresult rv =
901 MeasureForWidth(aRenderingContext->GetDrawTarget(), aDesiredSize);
902 if (NS_FAILED(rv)) {
903 PlaceAsMrow(aRenderingContext->GetDrawTarget(), false, aDesiredSize);
906 ClearSavedChildMetrics();
909 /* virtual */
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.
918 static int32_t
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) \
937 space_ = 0; \
938 else { \
939 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
940 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
941 ? 0 /* spacing is disabled */ \
942 : space_ & 0x0F; \
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;
964 int32_t space;
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
976 space = 0;
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>&ApplyFunction;</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>&ApplyFunction;</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;
1010 *aCarrySpace = 0;
1013 return space;
1016 static nscoord GetThinSpace(const nsStyleFont* aStyleFont) {
1017 return aStyleFont->mFont.size.ScaledBy(3.0f / 18.0f).ToAppUnits();
1020 class nsMathMLContainerFrame::RowChildFrameIterator {
1021 public:
1022 explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame)
1023 : mParentFrame(aParentFrame),
1024 mReflowOutput(aParentFrame->GetWritingMode()),
1025 mX(0),
1026 mChildFrameType(eMathMLFrameType_UNKNOWN),
1027 mCarrySpace(0),
1028 mFromFrameType(eMathMLFrameType_UNKNOWN),
1029 mRTL(aParentFrame->StyleVisibility()->mDirection ==
1030 StyleDirection::Rtl) {
1031 if (!mRTL) {
1032 mChildFrame = aParentFrame->mFrames.FirstChild();
1033 } else {
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;
1046 if (!mRTL) {
1047 mChildFrame = mChildFrame->GetNextSibling();
1048 } else {
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();
1059 nscoord space =
1060 GetInterFrameSpacing(font->mMathDepth, prevFrameType, mChildFrameType,
1061 &mFromFrameType, &mCarrySpace);
1062 mX += space * GetThinSpace(font);
1063 return *this;
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;
1077 private:
1078 const nsMathMLContainerFrame* mParentFrame;
1079 nsIFrame* mChildFrame;
1080 ReflowOutput mReflowOutput;
1081 nscoord mX;
1083 nscoord mItalicCorrection;
1084 eMathMLFrameType mChildFrameType;
1085 int32_t mCarrySpace;
1086 eMathMLFrameType mFromFrameType;
1088 bool mRTL;
1090 void InitMetricsForChild() {
1091 GetReflowAndBoundingMetricsFor(mChildFrame, mReflowOutput,
1092 mReflowOutput.mBoundingMetrics,
1093 &mChildFrameType);
1094 nscoord leftCorrection, rightCorrection;
1095 GetItalicCorrection(mReflowOutput.mBoundingMetrics, leftCorrection,
1096 rightCorrection);
1097 if (!mChildFrame->GetPrevSibling() &&
1098 mParentFrame->GetContent()->IsMathMLElement(nsGkAtoms::msqrt_)) {
1099 // Remove leading correction in <msqrt> because the sqrt glyph itself is
1100 // there first.
1101 if (!mRTL) {
1102 leftCorrection = 0;
1103 } else {
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;
1114 /* virtual */
1115 nsresult nsMathMLContainerFrame::Place(DrawTarget* aDrawTarget,
1116 bool aPlaceOrigin,
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();
1129 ++child;
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;
1142 mReference.x = 0;
1143 mReference.y = aDesiredSize.BlockStartAscent();
1145 //////////////////
1146 // Place Children
1148 if (aPlaceOrigin) {
1149 PositionRowChildFrames(0, aDesiredSize.BlockStartAscent());
1152 return NS_OK;
1155 nsresult nsMathMLContainerFrame::PlaceAsMrow(DrawTarget* aDrawTarget,
1156 bool aPlaceOrigin,
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);
1169 ++child;
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);
1191 nscoord space =
1192 GetInterFrameSpacing(aScriptLevel, prevFrameType, childFrameType,
1193 &fromFrameType, &carrySpace);
1194 if (aChildFrame == childFrame) {
1195 // get thinspace
1196 ComputedStyle* parentContext = aParentFrame->Style();
1197 nscoord thinSpace = GetThinSpace(parentContext->StyleFont());
1198 // we are done
1199 return space * thinSpace;
1201 childFrame = childFrame->GetNextSibling();
1204 MOZ_ASSERT_UNREACHABLE("child not in the childlist of its parent");
1205 return 0;
1208 static nscoord AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize,
1209 nsMathMLContainerFrame* aFrame) {
1210 nscoord gap = 0;
1211 nsIFrame* parent = aFrame->GetParent();
1212 nsIContent* parentContent = parent->GetContent();
1213 if (MOZ_UNLIKELY(!parentContent)) {
1214 return 0;
1216 if (parentContent->IsAnyOfMathMLElements(nsGkAtoms::math, nsGkAtoms::mtd_)) {
1217 gap = GetInterFrameSpacingFor(aFrame->StyleFont()->mMathDepth, parent,
1218 aFrame);
1219 // add our own italic correction
1220 nscoord leftCorrection = 0, italicCorrection = 0;
1221 nsMathMLContainerFrame::GetItalicCorrection(
1222 aDesiredSize.mBoundingMetrics, leftCorrection, italicCorrection);
1223 gap += leftCorrection;
1224 if (gap) {
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;
1233 return gap;
1236 nscoord nsMathMLContainerFrame::FixInterFrameSpacing(
1237 ReflowOutput& aDesiredSize) {
1238 nscoord gap = 0;
1239 gap = AddInterFrameSpacingToSize(aDesiredSize, this);
1240 if (gap) {
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();
1248 return gap;
1251 /* static */
1252 void nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst,
1253 nsIFrame* aStop)
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;
1310 if (!childFrame) {
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;
1316 } else {
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;
1339 return NS_OK;
1342 /*static*/
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());
1388 return newFrame;
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)