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