Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / layout / mathml / nsMathMLContainerFrame.cpp
blobb308c7018dba6087e6a6b4fc36435a89dfdfe9a1
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
24 * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsCOMPtr.h"
43 #include "nsHTMLParts.h"
44 #include "nsFrame.h"
45 #include "nsPresContext.h"
46 #include "nsIPresShell.h"
47 #include "nsCSSAnonBoxes.h"
48 #include "nsStyleContext.h"
49 #include "nsStyleConsts.h"
50 #include "nsINameSpaceManager.h"
51 #include "nsIRenderingContext.h"
52 #include "nsIFontMetrics.h"
54 #include "nsIDOMText.h"
55 #include "nsIDOMMutationEvent.h"
56 #include "nsFrameManager.h"
57 #include "nsStyleChangeList.h"
59 #include "nsGkAtoms.h"
60 #include "nsMathMLParts.h"
61 #include "nsMathMLContainerFrame.h"
62 #include "nsAutoPtr.h"
63 #include "nsStyleSet.h"
64 #include "nsDisplayList.h"
65 #include "nsCSSFrameConstructor.h"
66 #include "nsIReflowCallback.h"
68 using namespace mozilla;
71 // nsMathMLContainerFrame implementation
74 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
76 NS_QUERYFRAME_HEAD(nsMathMLContainerFrame)
77 NS_QUERYFRAME_ENTRY(nsMathMLFrame)
78 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame)
80 // =============================================================================
82 // error handlers
83 // provide a feedback to the user when a frame with bad markup can not be rendered
84 nsresult
85 nsMathMLContainerFrame::ReflowError(nsIRenderingContext& aRenderingContext,
86 nsHTMLReflowMetrics& aDesiredSize)
88 nsresult rv;
90 // clear all other flags and record that there is an error with this frame
91 mEmbellishData.flags = 0;
92 mPresentationData.flags = NS_MATHML_ERROR;
94 ///////////////
95 // Set font
96 nsLayoutUtils::SetFontFromStyle(&aRenderingContext, GetStyleContext());
98 // bounding metrics
99 nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
100 rv = aRenderingContext.GetBoundingMetrics(errorMsg.get(),
101 PRUint32(errorMsg.Length()),
102 mBoundingMetrics);
103 if (NS_FAILED(rv)) {
104 NS_WARNING("GetBoundingMetrics failed");
105 aDesiredSize.width = aDesiredSize.height = 0;
106 aDesiredSize.ascent = 0;
107 return NS_OK;
110 // reflow metrics
111 nsCOMPtr<nsIFontMetrics> fm;
112 aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
113 fm->GetMaxAscent(aDesiredSize.ascent);
114 nscoord descent;
115 fm->GetMaxDescent(descent);
116 aDesiredSize.height = aDesiredSize.ascent + descent;
117 aDesiredSize.width = mBoundingMetrics.width;
119 // Also return our bounding metrics
120 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
122 return NS_OK;
125 class nsDisplayMathMLError : public nsDisplayItem {
126 public:
127 nsDisplayMathMLError(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
128 : nsDisplayItem(aBuilder, aFrame) {
129 MOZ_COUNT_CTOR(nsDisplayMathMLError);
131 #ifdef NS_BUILD_REFCNT_LOGGING
132 virtual ~nsDisplayMathMLError() {
133 MOZ_COUNT_DTOR(nsDisplayMathMLError);
135 #endif
137 virtual void Paint(nsDisplayListBuilder* aBuilder,
138 nsIRenderingContext* aCtx);
139 NS_DISPLAY_DECL_NAME("MathMLError", TYPE_MATHML_ERROR)
142 void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
143 nsIRenderingContext* aCtx)
145 // Set color and font ...
146 nsLayoutUtils::SetFontFromStyle(aCtx, mFrame->GetStyleContext());
148 nsPoint pt = ToReferenceFrame();
149 aCtx->SetColor(NS_RGB(255,0,0));
150 aCtx->FillRect(nsRect(pt, mFrame->GetSize()));
151 aCtx->SetColor(NS_RGB(255,255,255));
153 nscoord ascent;
154 nsCOMPtr<nsIFontMetrics> fm;
155 aCtx->GetFontMetrics(*getter_AddRefs(fm));
156 fm->GetMaxAscent(ascent);
158 nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
159 aCtx->DrawString(errorMsg.get(), PRUint32(errorMsg.Length()), pt.x, pt.y+ascent);
162 /* /////////////
163 * nsIMathMLFrame - support methods for stretchy elements
164 * =============================================================================
167 static PRBool
168 IsForeignChild(const nsIFrame* aFrame)
170 // This counts nsMathMLmathBlockFrame as a foreign child, because it
171 // uses block reflow
172 return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
173 aFrame->GetType() == nsGkAtoms::blockFrame;
176 static void
177 DestroyHTMLReflowMetrics(void *aPropertyValue)
179 delete static_cast<nsHTMLReflowMetrics*>(aPropertyValue);
182 NS_DECLARE_FRAME_PROPERTY(HTMLReflowMetricsProperty, DestroyHTMLReflowMetrics)
184 /* static */ void
185 nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame* aFrame,
186 const nsHTMLReflowMetrics& aReflowMetrics,
187 const nsBoundingMetrics& aBoundingMetrics)
189 nsHTMLReflowMetrics *metrics = new nsHTMLReflowMetrics(aReflowMetrics);
190 metrics->mBoundingMetrics = aBoundingMetrics;
191 aFrame->Properties().Set(HTMLReflowMetricsProperty(), metrics);
194 // helper method to facilitate getting the reflow and bounding metrics
195 /* static */ void
196 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame* aFrame,
197 nsHTMLReflowMetrics& aReflowMetrics,
198 nsBoundingMetrics& aBoundingMetrics,
199 eMathMLFrameType* aMathMLFrameType)
201 NS_PRECONDITION(aFrame, "null arg");
203 nsHTMLReflowMetrics *metrics = static_cast<nsHTMLReflowMetrics*>
204 (aFrame->Properties().Get(HTMLReflowMetricsProperty()));
206 // IMPORTANT: This function is only meant to be called in Place() methods
207 // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
208 // information.
209 NS_ASSERTION(metrics, "Didn't SaveReflowAndBoundingMetricsFor frame!");
210 if (metrics) {
211 aReflowMetrics = *metrics;
212 aBoundingMetrics = metrics->mBoundingMetrics;
215 if (aMathMLFrameType) {
216 if (!IsForeignChild(aFrame)) {
217 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
218 if (mathMLFrame) {
219 *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
220 return;
223 *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
228 void
229 nsMathMLContainerFrame::ClearSavedChildMetrics()
231 nsIFrame* childFrame = mFrames.FirstChild();
232 FramePropertyTable* props = PresContext()->PropertyTable();
233 while (childFrame) {
234 props->Delete(childFrame, HTMLReflowMetricsProperty());
235 childFrame = childFrame->GetNextSibling();
239 // helper to get the preferred size that a container frame should use to fire
240 // the stretch on its stretchy child frames.
241 void
242 nsMathMLContainerFrame::GetPreferredStretchSize(nsIRenderingContext& aRenderingContext,
243 PRUint32 aOptions,
244 nsStretchDirection aStretchDirection,
245 nsBoundingMetrics& aPreferredStretchSize)
247 if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
248 // when our actual size is ok, just use it
249 aPreferredStretchSize = mBoundingMetrics;
251 else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
252 // compute our up-to-date size using Place()
253 nsHTMLReflowMetrics metrics;
254 Place(aRenderingContext, PR_FALSE, metrics);
255 aPreferredStretchSize = metrics.mBoundingMetrics;
257 else {
258 // compute a size that doesn't include embellishements
259 NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
260 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) ||
261 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags),
262 "invalid call to GetPreferredStretchSize");
263 PRBool firstTime = PR_TRUE;
264 nsBoundingMetrics bm, bmChild;
265 // XXXrbs need overloaded FirstChild() and clean integration of <maction> throughout
266 nsIFrame* childFrame = GetFirstChild(nsnull);
267 while (childFrame) {
268 // initializations in case this child happens not to be a MathML frame
269 nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
270 if (mathMLFrame) {
271 nsEmbellishData embellishData;
272 nsPresentationData presentationData;
273 mathMLFrame->GetEmbellishData(embellishData);
274 mathMLFrame->GetPresentationData(presentationData);
275 if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
276 embellishData.direction == aStretchDirection &&
277 presentationData.baseFrame) {
278 // embellishements are not included, only consider the inner first child itself
279 // XXXkt Does that mean the core descendent frame should be used
280 // instead of the base child?
281 nsIMathMLFrame* mathMLchildFrame = do_QueryFrame(presentationData.baseFrame);
282 if (mathMLchildFrame) {
283 mathMLFrame = mathMLchildFrame;
286 mathMLFrame->GetBoundingMetrics(bmChild);
288 else {
289 nsHTMLReflowMetrics unused;
290 GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
293 if (firstTime) {
294 firstTime = PR_FALSE;
295 bm = bmChild;
296 if (!NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) &&
297 !NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
298 // we may get here for cases such as <msup><mo>...</mo> ... </msup>
299 break;
302 else {
303 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
304 // if we get here, it means this is container that will stack its children
305 // vertically and fire an horizontal stretch on each them. This is the case
306 // for \munder, \mover, \munderover. We just sum-up the size vertically.
307 bm.descent += bmChild.ascent + bmChild.descent;
308 // Sometimes non-spacing marks (when width is zero) are positioned
309 // to the left of the origin, but it is the distance between left
310 // and right bearing that is important rather than the offsets from
311 // the origin.
312 if (bmChild.width == 0) {
313 bmChild.rightBearing -= bmChild.leftBearing;
314 bmChild.leftBearing = 0;
316 if (bm.leftBearing > bmChild.leftBearing)
317 bm.leftBearing = bmChild.leftBearing;
318 if (bm.rightBearing < bmChild.rightBearing)
319 bm.rightBearing = bmChild.rightBearing;
321 else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
322 // just sum-up the sizes horizontally.
323 bm += bmChild;
325 else {
326 NS_ERROR("unexpected case in GetPreferredStretchSize");
327 break;
330 childFrame = childFrame->GetNextSibling();
332 aPreferredStretchSize = bm;
336 NS_IMETHODIMP
337 nsMathMLContainerFrame::Stretch(nsIRenderingContext& aRenderingContext,
338 nsStretchDirection aStretchDirection,
339 nsBoundingMetrics& aContainerSize,
340 nsHTMLReflowMetrics& aDesiredStretchSize)
342 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
344 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
345 NS_WARNING("it is wrong to fire stretch more than once on a frame");
346 return NS_OK;
348 mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
350 if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
351 NS_WARNING("it is wrong to fire stretch on a erroneous frame");
352 return NS_OK;
355 // Pass the stretch to the base child ...
357 nsIFrame* baseFrame = mPresentationData.baseFrame;
358 if (baseFrame) {
359 nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame);
360 NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
361 if (mathMLFrame) {
362 PRBool stretchAll =
363 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
364 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
366 // And the trick is that the child's rect.x is still holding the descent,
367 // and rect.y is still holding the ascent ...
368 nsHTMLReflowMetrics childSize(aDesiredStretchSize);
369 GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
371 // See if we should downsize and confine the stretch to us...
372 // XXX there may be other cases where we can downsize the stretch,
373 // e.g., the first &Sum; might appear big in the following situation
374 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
375 // <mstyle>
376 // <msub>
377 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
378 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
379 // </msub>
380 // </mstyle>
381 // </math>
382 nsBoundingMetrics containerSize = aContainerSize;
383 if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
384 aStretchDirection != mEmbellishData.direction) {
385 if (mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) {
386 containerSize = childSize.mBoundingMetrics;
388 else {
389 GetPreferredStretchSize(aRenderingContext,
390 stretchAll ? STRETCH_CONSIDER_EMBELLISHMENTS : 0,
391 mEmbellishData.direction, containerSize);
395 // do the stretching...
396 mathMLFrame->Stretch(aRenderingContext,
397 mEmbellishData.direction, containerSize, childSize);
398 // store the updated metrics
399 SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
400 childSize.mBoundingMetrics);
402 // Remember the siblings which were _deferred_.
403 // Now that this embellished child may have changed, we need to
404 // fire the stretch on its siblings using our updated size
406 if (stretchAll) {
408 nsStretchDirection stretchDir =
409 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
410 NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
412 GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
413 stretchDir, containerSize);
415 nsIFrame* childFrame = mFrames.FirstChild();
416 while (childFrame) {
417 if (childFrame != mPresentationData.baseFrame) {
418 mathMLFrame = do_QueryFrame(childFrame);
419 if (mathMLFrame) {
420 // retrieve the metrics that was stored at the previous pass
421 GetReflowAndBoundingMetricsFor(childFrame,
422 childSize, childSize.mBoundingMetrics);
423 // do the stretching...
424 mathMLFrame->Stretch(aRenderingContext, stretchDir,
425 containerSize, childSize);
426 // store the updated metrics
427 SaveReflowAndBoundingMetricsFor(childFrame, childSize,
428 childSize.mBoundingMetrics);
431 childFrame = childFrame->GetNextSibling();
435 // re-position all our children
436 nsresult rv = Place(aRenderingContext, PR_TRUE, aDesiredStretchSize);
437 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
438 // Make sure the child frames get their DidReflow() calls.
439 DidReflowChildren(mFrames.FirstChild());
442 // If our parent is not embellished, it means we are the outermost embellished
443 // container and so we put the spacing, otherwise we don't include the spacing,
444 // the outermost embellished container will take care of it.
446 nsEmbellishData parentData;
447 GetEmbellishDataFrom(mParent, parentData);
448 // ensure that we are the embellished child, not just a sibling
449 // (need to test coreFrame since <mfrac> resets other things)
450 if (parentData.coreFrame != mEmbellishData.coreFrame) {
451 // (we fetch values from the core since they may use units that depend
452 // on style data, and style changes could have occurred in the core since
453 // our last visit there)
454 nsEmbellishData coreData;
455 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
457 mBoundingMetrics.width += coreData.leftSpace + coreData.rightSpace;
458 aDesiredStretchSize.width = mBoundingMetrics.width;
459 aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
461 nscoord dx = coreData.leftSpace;
462 if (dx != 0) {
463 mBoundingMetrics.leftBearing += dx;
464 mBoundingMetrics.rightBearing += dx;
465 aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
466 aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
468 nsIFrame* childFrame = mFrames.FirstChild();
469 while (childFrame) {
470 childFrame->SetPosition(childFrame->GetPosition()
471 + nsPoint(dx, 0));
472 childFrame = childFrame->GetNextSibling();
477 // Finished with these:
478 ClearSavedChildMetrics();
479 // Set our overflow area
480 GatherAndStoreOverflow(&aDesiredStretchSize);
484 return NS_OK;
487 nsresult
488 nsMathMLContainerFrame::FinalizeReflow(nsIRenderingContext& aRenderingContext,
489 nsHTMLReflowMetrics& aDesiredSize)
491 // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
492 // and descent in expectation of a stretch command. Hence we need to ensure that
493 // a stretch command will actually be fired later on, after exiting from our
494 // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
495 // with inappropriate data causing children to be improperly positioned.
496 // This helper method checks to see if our parent will fire a stretch command
497 // targeted at us. If not, we go ahead and fire an involutive stretch on
498 // ourselves. This will clear all the rect.x and rect.y, and return our
499 // desired size.
502 // First, complete the post-reflow hook.
503 // We use the information in our children rectangles to position them.
504 // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
505 // They will still be holding the ascent and descent for each child.
507 // The first clause caters for any non-embellished container.
508 // The second clause is for a container which won't fire stretch even though it is
509 // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
510 // because it excludes the particular case of the core <mo>...</mo> itself.
511 // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
512 PRBool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
513 (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
514 mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
515 nsresult rv = Place(aRenderingContext, placeOrigin, aDesiredSize);
517 // Place() will call FinishReflowChild() when placeOrigin is true but if
518 // it returns before reaching FinishReflowChild() due to errors we need
519 // to fulfill the reflow protocol by calling DidReflow for the child frames
520 // that still needs it here (or we may crash - bug 366012).
521 // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
522 // through Stretch() eventually.
523 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
524 DidReflowChildren(GetFirstChild(nsnull));
525 return rv;
528 PRBool parentWillFireStretch = PR_FALSE;
529 if (!placeOrigin) {
530 // This means the rect.x and rect.y of our children were not set!!
531 // Don't go without checking to see if our parent will later fire a Stretch() command
532 // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
533 nsIMathMLFrame* mathMLFrame = do_QueryFrame(mParent);
534 if (mathMLFrame) {
535 nsEmbellishData embellishData;
536 nsPresentationData presentationData;
537 mathMLFrame->GetEmbellishData(embellishData);
538 mathMLFrame->GetPresentationData(presentationData);
539 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
540 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
541 (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
542 && presentationData.baseFrame == this))
544 parentWillFireStretch = PR_TRUE;
547 if (!parentWillFireStretch) {
548 // There is nobody who will fire the stretch for us, we do it ourselves!
550 PRBool stretchAll =
551 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
552 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
554 nsBoundingMetrics defaultSize;
555 if (mEmbellishData.coreFrame == this /* case of a bare <mo>...</mo> itself */
556 || stretchAll) { /* or <mover><mo>...</mo>...</mover>, or friends */
557 // use our current size as computed earlier by Place()
558 defaultSize = aDesiredSize.mBoundingMetrics;
560 else { /* case of <msup><mo>...</mo>...</msup> or friends */
561 // compute a size that doesn't include embellishments
562 GetPreferredStretchSize(aRenderingContext, 0, mEmbellishData.direction,
563 defaultSize);
565 Stretch(aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, defaultSize,
566 aDesiredSize);
567 #ifdef NS_DEBUG
569 // The Place() call above didn't request FinishReflowChild(),
570 // so let's check that we eventually did through Stretch().
571 nsIFrame* childFrame = GetFirstChild(nsnull);
572 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
573 NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
574 "DidReflow() was never called");
577 #endif
581 // see if we should fix the spacing
582 FixInterFrameSpacing(aDesiredSize);
584 // Also return our bounding metrics
585 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
587 if (!parentWillFireStretch) {
588 // Not expecting a stretch.
589 // Finished with these:
590 ClearSavedChildMetrics();
591 // Set our overflow area.
592 GatherAndStoreOverflow(&aDesiredSize);
595 return NS_OK;
599 /* /////////////
600 * nsIMathMLFrame - support methods for scripting elements (nested frames
601 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
602 * mfrac, mroot, mtable).
603 * =============================================================================
606 // helper to let the update of presentation data pass through
607 // a subtree that may contain non-mathml container frames
608 /* static */ void
609 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame* aFrame,
610 PRUint32 aFlagsValues,
611 PRUint32 aFlagsToUpdate)
613 if (!aFrame || !aFlagsToUpdate)
614 return;
615 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
616 if (mathMLFrame) {
617 // update
618 mathMLFrame->UpdatePresentationData(aFlagsValues,
619 aFlagsToUpdate);
620 // propagate using the base method to make sure that the control
621 // is passed on to MathML frames that may be overloading the method
622 mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
623 aFlagsValues, aFlagsToUpdate);
625 else {
626 // propagate down the subtrees
627 nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
628 while (childFrame) {
629 PropagatePresentationDataFor(childFrame,
630 aFlagsValues, aFlagsToUpdate);
631 childFrame = childFrame->GetNextSibling();
636 /* static */ void
637 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame* aParentFrame,
638 PRInt32 aFirstChildIndex,
639 PRInt32 aLastChildIndex,
640 PRUint32 aFlagsValues,
641 PRUint32 aFlagsToUpdate)
643 if (!aParentFrame || !aFlagsToUpdate)
644 return;
645 PRInt32 index = 0;
646 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
647 while (childFrame) {
648 if ((index >= aFirstChildIndex) &&
649 ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
650 (index <= aLastChildIndex)))) {
651 PropagatePresentationDataFor(childFrame,
652 aFlagsValues, aFlagsToUpdate);
654 index++;
655 childFrame = childFrame->GetNextSibling();
659 /* //////////////////
660 * Frame construction
661 * =============================================================================
665 NS_IMETHODIMP
666 nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
667 const nsRect& aDirtyRect,
668 const nsDisplayListSet& aLists)
670 // report an error if something wrong was found in this frame
671 if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
672 if (!IsVisibleForPainting(aBuilder))
673 return NS_OK;
675 return aLists.Content()->AppendNewToTop(
676 new (aBuilder) nsDisplayMathMLError(aBuilder, this));
679 nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
680 NS_ENSURE_SUCCESS(rv, rv);
682 rv = DisplayTextDecorationsAndChildren(aBuilder, aDirtyRect, aLists);
683 NS_ENSURE_SUCCESS(rv, rv);
685 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
686 // for visual debug
687 // ----------------
688 // if you want to see your bounding box, make sure to properly fill
689 // your mBoundingMetrics and mReference point, and set
690 // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
691 // in the Init() of your sub-class
692 rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
693 #endif
694 return rv;
697 // Note that this method re-builds the automatic data in the children -- not
698 // in aParentFrame itself (except for those particular operations that the
699 // parent frame may do in its TransmitAutomaticData()).
700 /* static */ void
701 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
703 // 1. As we descend the tree, make each child frame inherit data from
704 // the parent
705 // 2. As we ascend the tree, transmit any specific change that we want
706 // down the subtrees
707 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
708 while (childFrame) {
709 nsIMathMLFrame* childMathMLFrame = do_QueryFrame(childFrame);
710 if (childMathMLFrame) {
711 childMathMLFrame->InheritAutomaticData(aParentFrame);
713 RebuildAutomaticDataForChildren(childFrame);
714 childFrame = childFrame->GetNextSibling();
716 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aParentFrame);
717 if (mathMLFrame) {
718 mathMLFrame->TransmitAutomaticData();
722 /* static */ nsresult
723 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame)
725 if (!aParentFrame)
726 return NS_OK;
728 // walk-up to the first frame that is a MathML frame, stop if we reach <math>
729 nsIFrame* frame = aParentFrame;
730 while (1) {
731 nsIFrame* parent = frame->GetParent();
732 if (!parent || !parent->GetContent())
733 break;
735 // stop if it is a MathML frame
736 nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
737 if (mathMLFrame)
738 break;
740 // stop if we reach the root <math> tag
741 nsIContent* content = frame->GetContent();
742 NS_ASSERTION(content, "dangling frame without a content node");
743 if (!content)
744 break;
745 // XXXldb This should check namespaces too.
746 if (content->Tag() == nsGkAtoms::math)
747 break;
749 // mark the frame dirty, and continue to climb up. It's important that
750 // we're NOT doing this to the frame we plan to pass to FrameNeedsReflow()
751 // XXXldb Why do we need to bother with this? Marking ancestor
752 // dirty (which we do below) should do a superset of the work this
753 // does.
754 frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
756 frame = parent;
759 // re-sync the presentation data and embellishment data of our children
760 RebuildAutomaticDataForChildren(frame);
762 // Ask our parent frame to reflow us
763 nsIFrame* parent = frame->GetParent();
764 NS_ASSERTION(parent, "No parent to pass the reflow request up to");
765 if (!parent)
766 return NS_OK;
768 frame->PresContext()->PresShell()->
769 FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
771 return NS_OK;
774 // There are precise rules governing children of a MathML frame,
775 // and properties such as the scriptlevel depends on those rules.
776 // Hence for things to work, callers must use Append/Insert/etc wisely.
778 nsresult
779 nsMathMLContainerFrame::ChildListChanged(PRInt32 aModType)
781 // If this is an embellished frame we need to rebuild the
782 // embellished hierarchy by walking-up to the parent of the
783 // outermost embellished container.
784 nsIFrame* frame = this;
785 if (mEmbellishData.coreFrame) {
786 nsIFrame* parent = mParent;
787 nsEmbellishData embellishData;
788 for ( ; parent; frame = parent, parent = parent->GetParent()) {
789 GetEmbellishDataFrom(parent, embellishData);
790 if (embellishData.coreFrame != mEmbellishData.coreFrame)
791 break;
793 // Important: do not do this to the frame we plan to pass to
794 // ReLayoutChildren
795 // XXXldb Why do we need to bother with this? Marking ancestor
796 // dirty (which we do below) should do a superset of the work this
797 // does.
798 frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
801 return ReLayoutChildren(frame);
804 NS_IMETHODIMP
805 nsMathMLContainerFrame::AppendFrames(nsIAtom* aListName,
806 nsFrameList& aFrameList)
808 if (aListName) {
809 return NS_ERROR_INVALID_ARG;
811 mFrames.AppendFrames(this, aFrameList);
812 return ChildListChanged(nsIDOMMutationEvent::ADDITION);
815 NS_IMETHODIMP
816 nsMathMLContainerFrame::InsertFrames(nsIAtom* aListName,
817 nsIFrame* aPrevFrame,
818 nsFrameList& aFrameList)
820 if (aListName) {
821 return NS_ERROR_INVALID_ARG;
823 // Insert frames after aPrevFrame
824 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
825 return ChildListChanged(nsIDOMMutationEvent::ADDITION);
828 NS_IMETHODIMP
829 nsMathMLContainerFrame::RemoveFrame(nsIAtom* aListName,
830 nsIFrame* aOldFrame)
832 if (aListName) {
833 return NS_ERROR_INVALID_ARG;
835 // remove the child frame
836 mFrames.DestroyFrame(aOldFrame);
837 return ChildListChanged(nsIDOMMutationEvent::REMOVAL);
840 NS_IMETHODIMP
841 nsMathMLContainerFrame::AttributeChanged(PRInt32 aNameSpaceID,
842 nsIAtom* aAttribute,
843 PRInt32 aModType)
845 // XXX Since they are numerous MathML attributes that affect layout, and
846 // we can't check all of them here, play safe by requesting a reflow.
847 // XXXldb This should only do work for attributes that cause changes!
848 PresContext()->PresShell()->
849 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
851 return NS_OK;
854 void
855 nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
857 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
858 // frame rectangle.
859 aMetrics->SetOverflowAreasToDesiredBounds();
861 // Text-shadow overflows.
862 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) {
863 nsRect frameRect(0, 0, aMetrics->width, aMetrics->height);
864 nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(frameRect, this);
865 // shadows contribute only to visual overflow
866 nsRect& visOverflow = aMetrics->VisualOverflow();
867 visOverflow.UnionRect(visOverflow, shadowRect);
870 // All non-child-frame content such as nsMathMLChars (and most child-frame
871 // content) is included in mBoundingMetrics.
872 nsRect boundingBox(mBoundingMetrics.leftBearing,
873 aMetrics->ascent - mBoundingMetrics.ascent,
874 mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
875 mBoundingMetrics.ascent + mBoundingMetrics.descent);
877 // REVIEW: Maybe this should contribute only to visual overflow
878 // and not scrollable?
879 aMetrics->mOverflowAreas.UnionAllWith(boundingBox);
881 // mBoundingMetrics does not necessarily include content of <mpadded>
882 // elements whose mBoundingMetrics may not be representative of the true
883 // bounds, and doesn't include the CSS2 outline rectangles of children, so
884 // make such to include child overflow areas.
885 nsIFrame* childFrame = mFrames.FirstChild();
886 while (childFrame) {
887 ConsiderChildOverflow(aMetrics->mOverflowAreas, childFrame);
888 childFrame = childFrame->GetNextSibling();
891 FinishAndStoreOverflow(aMetrics);
894 nsresult
895 nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
896 nsPresContext* aPresContext,
897 nsHTMLReflowMetrics& aDesiredSize,
898 const nsHTMLReflowState& aReflowState,
899 nsReflowStatus& aStatus)
901 // Having foreign/hybrid children, e.g., from html markups, is not defined by
902 // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
903 // to do some cool demos... or we may have a child that is an nsInlineFrame
904 // from a generated content such as :before { content: open-quote } or
905 // :after { content: close-quote }. Unfortunately, the other frames out-there
906 // may expect their own invariants that are not met when we mix things.
907 // Hence we do not claim their support, but we will nevertheless attempt to keep
908 // them in the flow, if we can get their desired size. We observed that most
909 // frames may be reflowed generically, but nsInlineFrames need extra care.
911 #ifdef DEBUG
912 nsInlineFrame* inlineFrame = do_QueryFrame(aChildFrame);
913 NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
914 #endif
916 nsresult rv = nsHTMLContainerFrame::
917 ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
918 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
920 if (NS_FAILED(rv))
921 return rv;
923 if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
924 // This will be suitable for inline frames, which are wrapped in a block.
925 if(!nsLayoutUtils::GetLastLineBaseline(aChildFrame,
926 &aDesiredSize.ascent)) {
927 // We don't expect any other block children so just place the frame on
928 // the baseline instead of going through DidReflow() and
929 // GetBaseline(). This is what nsFrame::GetBaseline() will do anyway.
930 aDesiredSize.ascent = aDesiredSize.height;
933 if (IsForeignChild(aChildFrame)) {
934 // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
935 nsRect r = aChildFrame->ComputeTightBounds(aReflowState.rendContext->ThebesContext());
936 aDesiredSize.mBoundingMetrics.leftBearing = r.x;
937 aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
938 aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.ascent - r.y;
939 aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.ascent;
940 aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
942 return rv;
945 NS_IMETHODIMP
946 nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
947 nsHTMLReflowMetrics& aDesiredSize,
948 const nsHTMLReflowState& aReflowState,
949 nsReflowStatus& aStatus)
951 aDesiredSize.width = aDesiredSize.height = 0;
952 aDesiredSize.ascent = 0;
953 aDesiredSize.mBoundingMetrics.Clear();
955 /////////////
956 // Reflow children
957 // Asking each child to cache its bounding metrics
959 nsReflowStatus childStatus;
960 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
961 nsIFrame* childFrame = mFrames.FirstChild();
962 while (childFrame) {
963 nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags);
964 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
965 childFrame, availSize);
966 nsresult rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
967 childReflowState, childStatus);
968 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
969 if (NS_FAILED(rv)) {
970 // Call DidReflow() for the child frames we successfully did reflow.
971 DidReflowChildren(mFrames.FirstChild(), childFrame);
972 return rv;
975 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
976 childDesiredSize.mBoundingMetrics);
977 childFrame = childFrame->GetNextSibling();
980 /////////////
981 // If we are a container which is entitled to stretch its children, then we
982 // ask our stretchy children to stretch themselves
984 // The stretching of siblings of an embellished child is _deferred_ until
985 // after finishing the stretching of the embellished child - bug 117652
987 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
988 (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
989 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
991 // get the stretchy direction
992 nsStretchDirection stretchDir =
993 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
994 ? NS_STRETCH_DIRECTION_VERTICAL
995 : NS_STRETCH_DIRECTION_HORIZONTAL;
997 // what size should we use to stretch our stretchy children
998 // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
999 // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
1000 // include them in the caculations of the size of stretchy elements
1001 nsBoundingMetrics containerSize;
1002 GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
1003 containerSize);
1005 // fire the stretch on each child
1006 childFrame = mFrames.FirstChild();
1007 while (childFrame) {
1008 nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
1009 if (mathMLFrame) {
1010 // retrieve the metrics that was stored at the previous pass
1011 nsHTMLReflowMetrics childDesiredSize;
1012 GetReflowAndBoundingMetricsFor(childFrame,
1013 childDesiredSize, childDesiredSize.mBoundingMetrics);
1015 mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
1016 containerSize, childDesiredSize);
1017 // store the updated metrics
1018 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
1019 childDesiredSize.mBoundingMetrics);
1021 childFrame = childFrame->GetNextSibling();
1025 /////////////
1026 // Place children now by re-adjusting the origins to align the baselines
1027 FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
1029 aStatus = NS_FRAME_COMPLETE;
1030 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1031 return NS_OK;
1034 /* virtual */ nscoord
1035 nsMathMLContainerFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
1037 nscoord result;
1038 DISPLAY_MIN_WIDTH(this, result);
1039 result = GetIntrinsicWidth(aRenderingContext);
1040 return result;
1043 /* virtual */ nscoord
1044 nsMathMLContainerFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
1046 nscoord result;
1047 DISPLAY_MIN_WIDTH(this, result);
1048 result = GetIntrinsicWidth(aRenderingContext);
1049 return result;
1052 /* virtual */ nscoord
1053 nsMathMLContainerFrame::GetIntrinsicWidth(nsIRenderingContext* aRenderingContext)
1055 // Get child widths
1056 nsIFrame* childFrame = mFrames.FirstChild();
1057 while (childFrame) {
1058 // XXX This includes margin while Reflow currently doesn't consider
1059 // margin, so we may end up with too much space, but, with stretchy
1060 // characters, this is an approximation anyway.
1061 nscoord width =
1062 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
1063 nsLayoutUtils::PREF_WIDTH);
1065 nsHTMLReflowMetrics childDesiredSize;
1066 childDesiredSize.width = width;
1067 childDesiredSize.mBoundingMetrics.width = width;
1068 // TODO: we need nsIFrame::GetIntrinsicHBounds() for better values here.
1069 childDesiredSize.mBoundingMetrics.leftBearing = 0;
1070 childDesiredSize.mBoundingMetrics.rightBearing = width;
1072 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
1073 childDesiredSize.mBoundingMetrics);
1075 childFrame = childFrame->GetNextSibling();
1078 // Measure
1079 nsHTMLReflowMetrics desiredSize;
1080 nsresult rv = MeasureForWidth(*aRenderingContext, desiredSize);
1081 if (NS_FAILED(rv)) {
1082 ReflowError(*aRenderingContext, desiredSize);
1085 ClearSavedChildMetrics();
1087 return desiredSize.width;
1090 /* virtual */ nsresult
1091 nsMathMLContainerFrame::MeasureForWidth(nsIRenderingContext& aRenderingContext,
1092 nsHTMLReflowMetrics& aDesiredSize)
1094 return Place(aRenderingContext, PR_FALSE, aDesiredSize);
1098 // see spacing table in Chapter 18, TeXBook (p.170)
1099 // Our table isn't quite identical to TeX because operators have
1100 // built-in values for lspace & rspace in the Operator Dictionary.
1101 static PRInt32 kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
1103 // in units of muspace.
1104 // upper half of the byte is set if the
1105 // spacing is not to be used for scriptlevel > 0
1107 /* Ord OpOrd OpInv OpUsr Inner Italic Upright */
1108 /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
1109 /*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1110 /*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1111 /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1112 /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1113 /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
1114 /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}
1117 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
1118 /* no space if there is a frame that we know nothing about */ \
1119 if (frametype1_ == eMathMLFrameType_UNKNOWN || \
1120 frametype2_ == eMathMLFrameType_UNKNOWN) \
1121 space_ = 0; \
1122 else { \
1123 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
1124 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
1125 ? 0 /* spacing is disabled */ \
1126 : space_ & 0x0F; \
1129 // This function computes the inter-space between two frames. However,
1130 // since invisible operators need special treatment, the inter-space may
1131 // be delayed when an invisible operator is encountered. In this case,
1132 // the function will carry the inter-space forward until it is determined
1133 // that it can be applied properly (i.e., until we encounter a visible
1134 // frame where to decide whether to accept or reject the inter-space).
1135 // aFromFrameType: remembers the frame when the carry-forward initiated.
1136 // aCarrySpace: keeps track of the inter-space that is delayed.
1137 // @returns: current inter-space (which is 0 when the true inter-space is
1138 // delayed -- and thus has no effect since the frame is invisible anyway).
1139 static nscoord
1140 GetInterFrameSpacing(PRInt32 aScriptLevel,
1141 eMathMLFrameType aFirstFrameType,
1142 eMathMLFrameType aSecondFrameType,
1143 eMathMLFrameType* aFromFrameType, // IN/OUT
1144 PRInt32* aCarrySpace) // IN/OUT
1146 eMathMLFrameType firstType = aFirstFrameType;
1147 eMathMLFrameType secondType = aSecondFrameType;
1149 PRInt32 space;
1150 GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
1152 // feedback control to avoid the inter-space to be added when not necessary
1153 if (secondType == eMathMLFrameType_OperatorInvisible) {
1154 // see if we should start to carry the space forward until we
1155 // encounter a visible frame
1156 if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
1157 *aFromFrameType = firstType;
1158 *aCarrySpace = space;
1160 // keep carrying *aCarrySpace forward, while returning 0 for this stage
1161 space = 0;
1163 else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
1164 // no carry-forward anymore, get the real inter-space between
1165 // the two frames of interest
1167 firstType = *aFromFrameType;
1169 // But... the invisible operator that we encountered earlier could
1170 // be sitting between italic and upright identifiers, e.g.,
1172 // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
1173 // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
1175 // the trick to get the inter-space in either situation
1176 // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
1177 // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
1178 if (firstType == eMathMLFrameType_UprightIdentifier) {
1179 firstType = eMathMLFrameType_OperatorUserDefined;
1181 else if (secondType == eMathMLFrameType_UprightIdentifier) {
1182 secondType = eMathMLFrameType_OperatorUserDefined;
1185 GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
1187 // Now, we have two values: the computed space and the space that
1188 // has been carried forward until now. Which value do we pick?
1189 // If the second type is an operator (e.g., fence), it already has
1190 // built-in lspace & rspace, so we let them win. Otherwise we pick
1191 // the max between the two values that we have.
1192 if (secondType != eMathMLFrameType_OperatorOrdinary &&
1193 space < *aCarrySpace)
1194 space = *aCarrySpace;
1196 // reset everything now that the carry-forward is done
1197 *aFromFrameType = eMathMLFrameType_UNKNOWN;
1198 *aCarrySpace = 0;
1201 return space;
1204 static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
1206 return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
1209 class nsMathMLContainerFrame::RowChildFrameIterator {
1210 public:
1211 explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
1212 mParentFrame(aParentFrame),
1213 mChildFrame(aParentFrame->mFrames.FirstChild()),
1214 mX(0),
1215 mCarrySpace(0),
1216 mFromFrameType(eMathMLFrameType_UNKNOWN)
1218 if (!mChildFrame)
1219 return;
1221 InitMetricsForChild();
1222 // Remove left correction in <msqrt> because the sqrt glyph itself is
1223 // there first.
1224 if (mParentFrame->GetContent()->Tag() == nsGkAtoms::msqrt_) {
1225 mX = 0;
1229 RowChildFrameIterator& operator++()
1231 // add child size + italic correction
1232 mX += mSize.mBoundingMetrics.width + mItalicCorrection;
1234 mChildFrame = mChildFrame->GetNextSibling();
1235 if (!mChildFrame)
1236 return *this;
1238 eMathMLFrameType prevFrameType = mChildFrameType;
1239 InitMetricsForChild();
1241 // add inter frame spacing
1242 const nsStyleFont* font = mParentFrame->GetStyleFont();
1243 nscoord space =
1244 GetInterFrameSpacing(font->mScriptLevel,
1245 prevFrameType, mChildFrameType,
1246 &mFromFrameType, &mCarrySpace);
1247 mX += space * GetThinSpace(font);
1248 return *this;
1251 nsIFrame* Frame() const { return mChildFrame; }
1252 nscoord X() const { return mX; }
1253 const nsHTMLReflowMetrics& ReflowMetrics() const { return mSize; }
1254 nscoord Ascent() const { return mSize.ascent; }
1255 nscoord Descent() const { return mSize.height - mSize.ascent; }
1256 const nsBoundingMetrics& BoundingMetrics() const {
1257 return mSize.mBoundingMetrics;
1260 private:
1261 const nsMathMLContainerFrame* mParentFrame;
1262 nsIFrame* mChildFrame;
1263 nsHTMLReflowMetrics mSize;
1264 nscoord mX;
1266 nscoord mItalicCorrection;
1267 eMathMLFrameType mChildFrameType;
1268 PRInt32 mCarrySpace;
1269 eMathMLFrameType mFromFrameType;
1271 void InitMetricsForChild()
1273 GetReflowAndBoundingMetricsFor(mChildFrame, mSize, mSize.mBoundingMetrics,
1274 &mChildFrameType);
1275 nscoord leftCorrection;
1276 GetItalicCorrection(mSize.mBoundingMetrics, leftCorrection,
1277 mItalicCorrection);
1278 // add left correction -- this fixes the problem of the italic 'f'
1279 // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
1280 mX += leftCorrection;
1284 /* virtual */ nsresult
1285 nsMathMLContainerFrame::Place(nsIRenderingContext& aRenderingContext,
1286 PRBool aPlaceOrigin,
1287 nsHTMLReflowMetrics& aDesiredSize)
1289 // This is needed in case this frame is empty (i.e., no child frames)
1290 mBoundingMetrics.Clear();
1292 RowChildFrameIterator child(this);
1293 nscoord ascent = 0, descent = 0;
1294 while (child.Frame()) {
1295 if (descent < child.Descent())
1296 descent = child.Descent();
1297 if (ascent < child.Ascent())
1298 ascent = child.Ascent();
1299 // add the child size
1300 mBoundingMetrics.width = child.X();
1301 mBoundingMetrics += child.BoundingMetrics();
1302 ++child;
1304 // Add the italic correction at the end (including the last child).
1305 // This gives a nice gap between math and non-math frames, and still
1306 // gives the same math inter-spacing in case this frame connects to
1307 // another math frame
1308 mBoundingMetrics.width = child.X();
1310 aDesiredSize.width = mBoundingMetrics.width;
1311 aDesiredSize.height = ascent + descent;
1312 aDesiredSize.ascent = ascent;
1313 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
1315 mReference.x = 0;
1316 mReference.y = aDesiredSize.ascent;
1318 //////////////////
1319 // Place Children
1321 if (aPlaceOrigin) {
1322 PositionRowChildFrames(0, aDesiredSize.ascent);
1325 return NS_OK;
1328 void
1329 nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
1330 nscoord aBaseline)
1332 RowChildFrameIterator child(this);
1333 while (child.Frame()) {
1334 nscoord dx = aOffsetX + child.X();
1335 nscoord dy = aBaseline - child.Ascent();
1336 FinishReflowChild(child.Frame(), PresContext(), nsnull,
1337 child.ReflowMetrics(), dx, dy, 0);
1338 ++child;
1342 class ForceReflow : public nsIReflowCallback {
1343 public:
1344 virtual PRBool ReflowFinished() {
1345 return PR_TRUE;
1347 virtual void ReflowCallbackCanceled() {}
1350 // We only need one of these so we just make it a static global, no need
1351 // to dynamically allocate/destroy it.
1352 static ForceReflow gForceReflow;
1354 void
1355 nsMathMLContainerFrame::SetIncrementScriptLevel(PRInt32 aChildIndex, PRBool aIncrement)
1357 nsIFrame* child = GetChildList(nsnull).FrameAt(aChildIndex);
1358 if (!child)
1359 return;
1360 nsIContent* content = child->GetContent();
1361 if (!content->IsMathML())
1362 return;
1363 nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
1365 if (element->GetIncrementScriptLevel() == aIncrement)
1366 return;
1368 // XXXroc this does a ContentStatesChanged, is it safe to call here? If
1369 // not we should do it in a post-reflow callback.
1370 element->SetIncrementScriptLevel(aIncrement, PR_TRUE);
1371 PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
1374 // helpers to fix the inter-spacing when <math> is the only parent
1375 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
1377 static nscoord
1378 GetInterFrameSpacingFor(PRInt32 aScriptLevel,
1379 nsIFrame* aParentFrame,
1380 nsIFrame* aChildFrame)
1382 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
1383 if (!childFrame || aChildFrame == childFrame)
1384 return 0;
1386 PRInt32 carrySpace = 0;
1387 eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
1388 eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
1389 eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
1390 childFrame = childFrame->GetNextSibling();
1391 while (childFrame) {
1392 prevFrameType = childFrameType;
1393 childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
1394 nscoord space = GetInterFrameSpacing(aScriptLevel,
1395 prevFrameType, childFrameType, &fromFrameType, &carrySpace);
1396 if (aChildFrame == childFrame) {
1397 // get thinspace
1398 nsStyleContext* parentContext = aParentFrame->GetStyleContext();
1399 nscoord thinSpace = GetThinSpace(parentContext->GetStyleFont());
1400 // we are done
1401 return space * thinSpace;
1403 childFrame = childFrame->GetNextSibling();
1406 NS_NOTREACHED("child not in the childlist of its parent");
1407 return 0;
1410 nscoord
1411 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
1413 nscoord gap = 0;
1414 nsIContent* parentContent = mParent->GetContent();
1415 if (NS_UNLIKELY(!parentContent)) {
1416 return 0;
1418 // XXXldb This should check namespaces too.
1419 nsIAtom *parentTag = parentContent->Tag();
1420 if (parentTag == nsGkAtoms::math ||
1421 parentTag == nsGkAtoms::mtd_) {
1422 gap = GetInterFrameSpacingFor(GetStyleFont()->mScriptLevel, mParent, this);
1423 // add our own italic correction
1424 nscoord leftCorrection = 0, italicCorrection = 0;
1425 GetItalicCorrection(mBoundingMetrics, leftCorrection, italicCorrection);
1426 gap += leftCorrection;
1427 // see if we should shift our children to account for the correction
1428 if (gap) {
1429 nsIFrame* childFrame = mFrames.FirstChild();
1430 while (childFrame) {
1431 childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
1432 childFrame = childFrame->GetNextSibling();
1434 mBoundingMetrics.leftBearing += gap;
1435 mBoundingMetrics.rightBearing += gap;
1436 mBoundingMetrics.width += gap;
1437 aDesiredSize.width += gap;
1439 mBoundingMetrics.width += italicCorrection;
1440 aDesiredSize.width += italicCorrection;
1442 return gap;
1445 /* static */ void
1446 nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
1449 if (NS_UNLIKELY(!aFirst))
1450 return;
1452 for (nsIFrame* frame = aFirst;
1453 frame != aStop;
1454 frame = frame->GetNextSibling()) {
1455 NS_ASSERTION(frame, "aStop isn't a sibling");
1456 if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
1457 // finish off principal descendants, too
1458 nsIFrame* grandchild = frame->GetFirstChild(nsnull);
1459 if (grandchild)
1460 DidReflowChildren(grandchild, nsnull);
1462 frame->DidReflow(frame->PresContext(), nsnull,
1463 NS_FRAME_REFLOW_FINISHED);
1468 //==========================
1470 nsIFrame*
1471 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
1472 PRUint32 aFlags)
1474 nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
1475 if (it) {
1476 it->SetFlags(aFlags);
1478 return it;
1481 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
1483 nsIFrame*
1484 NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1486 return new (aPresShell) nsMathMLmathInlineFrame(aContext);
1489 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)