Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / mathml / base / src / nsMathMLContainerFrame.cpp
blobac7ecd299eccc1c30e46206422c6eb6bd9f0d374
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 NS_DEFINE_CID(kInlineFrameCID, NS_INLINE_FRAME_CID);
71 // nsMathMLContainerFrame implementation
74 // nsISupports
75 // =============================================================================
77 NS_IMPL_ADDREF_INHERITED(nsMathMLContainerFrame, nsMathMLFrame)
78 NS_IMPL_RELEASE_INHERITED(nsMathMLContainerFrame, nsMathMLFrame)
79 NS_IMPL_QUERY_INTERFACE_INHERITED1(nsMathMLContainerFrame, nsHTMLContainerFrame, nsMathMLFrame)
81 // =============================================================================
83 // error handlers
84 // provide a feedback to the user when a frame with bad markup can not be rendered
85 nsresult
86 nsMathMLContainerFrame::ReflowError(nsIRenderingContext& aRenderingContext,
87 nsHTMLReflowMetrics& aDesiredSize)
89 nsresult rv;
91 // clear all other flags and record that there is an error with this frame
92 mEmbellishData.flags = 0;
93 mPresentationData.flags = NS_MATHML_ERROR;
95 ///////////////
96 // Set font
97 nsLayoutUtils::SetFontFromStyle(&aRenderingContext, GetStyleContext());
99 // bounding metrics
100 nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
101 rv = aRenderingContext.GetBoundingMetrics(errorMsg.get(),
102 PRUint32(errorMsg.Length()),
103 mBoundingMetrics);
104 if (NS_FAILED(rv)) {
105 NS_WARNING("GetBoundingMetrics failed");
106 aDesiredSize.width = aDesiredSize.height = 0;
107 aDesiredSize.ascent = 0;
108 return NS_OK;
111 // reflow metrics
112 nsCOMPtr<nsIFontMetrics> fm;
113 aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
114 fm->GetMaxAscent(aDesiredSize.ascent);
115 nscoord descent;
116 fm->GetMaxDescent(descent);
117 aDesiredSize.height = aDesiredSize.ascent + descent;
118 aDesiredSize.width = mBoundingMetrics.width;
120 // Also return our bounding metrics
121 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
123 return NS_OK;
126 class nsDisplayMathMLError : public nsDisplayItem {
127 public:
128 nsDisplayMathMLError(nsIFrame* aFrame)
129 : nsDisplayItem(aFrame) {
130 MOZ_COUNT_CTOR(nsDisplayMathMLError);
132 #ifdef NS_BUILD_REFCNT_LOGGING
133 virtual ~nsDisplayMathMLError() {
134 MOZ_COUNT_DTOR(nsDisplayMathMLError);
136 #endif
138 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
139 const nsRect& aDirtyRect);
140 NS_DISPLAY_DECL_NAME("MathMLError")
143 void nsDisplayMathMLError::Paint(nsDisplayListBuilder* aBuilder,
144 nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
146 // Set color and font ...
147 nsLayoutUtils::SetFontFromStyle(aCtx, mFrame->GetStyleContext());
149 nsPoint pt = aBuilder->ToReferenceFrame(mFrame);
150 aCtx->SetColor(NS_RGB(255,0,0));
151 aCtx->FillRect(nsRect(pt, mFrame->GetSize()));
152 aCtx->SetColor(NS_RGB(255,255,255));
154 nscoord ascent;
155 nsCOMPtr<nsIFontMetrics> fm;
156 aCtx->GetFontMetrics(*getter_AddRefs(fm));
157 fm->GetMaxAscent(ascent);
159 nsAutoString errorMsg; errorMsg.AssignLiteral("invalid-markup");
160 aCtx->DrawString(errorMsg.get(), PRUint32(errorMsg.Length()), pt.x, pt.y+ascent);
163 /* /////////////
164 * nsIMathMLFrame - support methods for stretchy elements
165 * =============================================================================
168 static PRBool
169 IsForeignChild(const nsIFrame* aFrame)
171 // This counts nsMathMLmathBlockFrame as a foreign child, because it
172 // uses block reflow
173 return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
174 aFrame->GetType() == nsGkAtoms::blockFrame;
177 static void
178 DeleteHTMLReflowMetrics(void *aObject, nsIAtom *aPropertyName,
179 void *aPropertyValue, void *aData)
181 delete static_cast<nsHTMLReflowMetrics*>(aPropertyValue);
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->SetProperty(nsGkAtoms::HTMLReflowMetricsProperty, metrics,
192 DeleteHTMLReflowMetrics);
195 // helper method to facilitate getting the reflow and bounding metrics
196 /* static */ void
197 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame* aFrame,
198 nsHTMLReflowMetrics& aReflowMetrics,
199 nsBoundingMetrics& aBoundingMetrics,
200 eMathMLFrameType* aMathMLFrameType)
202 NS_PRECONDITION(aFrame, "null arg");
204 nsHTMLReflowMetrics *metrics = static_cast<nsHTMLReflowMetrics*>
205 (aFrame->GetProperty(nsGkAtoms::HTMLReflowMetricsProperty));
207 // IMPORTANT: This function is only meant to be called in Place() methods
208 // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
209 // information.
210 NS_ASSERTION(metrics, "Didn't SaveReflowAndBoundingMetricsFor frame!");
211 if (metrics) {
212 aReflowMetrics = *metrics;
213 aBoundingMetrics = metrics->mBoundingMetrics;
216 if (aMathMLFrameType) {
217 if (!IsForeignChild(aFrame)) {
218 nsIMathMLFrame* mathMLFrame;
219 CallQueryInterface(aFrame, &mathMLFrame);
220 if (mathMLFrame) {
221 *aMathMLFrameType = mathMLFrame->GetMathMLFrameType();
222 return;
225 *aMathMLFrameType = eMathMLFrameType_UNKNOWN;
230 void
231 nsMathMLContainerFrame::ClearSavedChildMetrics()
233 nsIFrame* childFrame = mFrames.FirstChild();
234 while (childFrame) {
235 childFrame->DeleteProperty(nsGkAtoms::HTMLReflowMetricsProperty);
236 childFrame = childFrame->GetNextSibling();
240 // helper to get the preferred size that a container frame should use to fire
241 // the stretch on its stretchy child frames.
242 void
243 nsMathMLContainerFrame::GetPreferredStretchSize(nsIRenderingContext& aRenderingContext,
244 PRUint32 aOptions,
245 nsStretchDirection aStretchDirection,
246 nsBoundingMetrics& aPreferredStretchSize)
248 if (aOptions & STRETCH_CONSIDER_ACTUAL_SIZE) {
249 // when our actual size is ok, just use it
250 aPreferredStretchSize = mBoundingMetrics;
252 else if (aOptions & STRETCH_CONSIDER_EMBELLISHMENTS) {
253 // compute our up-to-date size using Place()
254 nsHTMLReflowMetrics metrics;
255 Place(aRenderingContext, PR_FALSE, metrics);
256 aPreferredStretchSize = metrics.mBoundingMetrics;
258 else {
259 // compute a size that doesn't include embellishements
260 NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
261 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) ||
262 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags),
263 "invalid call to GetPreferredStretchSize");
264 PRBool firstTime = PR_TRUE;
265 nsBoundingMetrics bm, bmChild;
266 // XXXrbs need overloaded FirstChild() and clean integration of <maction> throughout
267 nsIFrame* childFrame = GetFirstChild(nsnull);
268 while (childFrame) {
269 // initializations in case this child happens not to be a MathML frame
270 nsIMathMLFrame* mathMLFrame;
271 childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
272 if (mathMLFrame) {
273 nsEmbellishData embellishData;
274 nsPresentationData presentationData;
275 mathMLFrame->GetEmbellishData(embellishData);
276 mathMLFrame->GetPresentationData(presentationData);
277 if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags) &&
278 embellishData.direction == aStretchDirection &&
279 presentationData.baseFrame) {
280 // embellishements are not included, only consider the inner first child itself
281 // XXXkt Does that mean the core descendent frame should be used
282 // instead of the base child?
283 nsIMathMLFrame* mathMLchildFrame;
284 presentationData.baseFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLchildFrame);
285 if (mathMLchildFrame) {
286 mathMLFrame = mathMLchildFrame;
289 mathMLFrame->GetBoundingMetrics(bmChild);
291 else {
292 nsHTMLReflowMetrics unused;
293 GetReflowAndBoundingMetricsFor(childFrame, unused, bmChild);
296 if (firstTime) {
297 firstTime = PR_FALSE;
298 bm = bmChild;
299 if (!NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags) &&
300 !NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
301 // we may get here for cases such as <msup><mo>...</mo> ... </msup>
302 break;
305 else {
306 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags)) {
307 // if we get here, it means this is container that will stack its children
308 // vertically and fire an horizontal stretch on each them. This is the case
309 // for \munder, \mover, \munderover. We just sum-up the size vertically.
310 bm.descent += bmChild.ascent + bmChild.descent;
311 // Sometimes non-spacing marks (when width is zero) are positioned
312 // to the left of the origin, but it is the distance between left
313 // and right bearing that is important rather than the offsets from
314 // the origin.
315 if (bmChild.width == 0) {
316 bmChild.rightBearing -= bmChild.leftBearing;
317 bmChild.leftBearing = 0;
319 if (bm.leftBearing > bmChild.leftBearing)
320 bm.leftBearing = bmChild.leftBearing;
321 if (bm.rightBearing < bmChild.rightBearing)
322 bm.rightBearing = bmChild.rightBearing;
324 else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)) {
325 // just sum-up the sizes horizontally.
326 bm += bmChild;
328 else {
329 NS_ERROR("unexpected case in GetPreferredStretchSize");
330 break;
333 childFrame = childFrame->GetNextSibling();
335 aPreferredStretchSize = bm;
339 NS_IMETHODIMP
340 nsMathMLContainerFrame::Stretch(nsIRenderingContext& aRenderingContext,
341 nsStretchDirection aStretchDirection,
342 nsBoundingMetrics& aContainerSize,
343 nsHTMLReflowMetrics& aDesiredStretchSize)
345 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
347 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
348 NS_WARNING("it is wrong to fire stretch more than once on a frame");
349 return NS_OK;
351 mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
353 if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
354 NS_WARNING("it is wrong to fire stretch on a erroneous frame");
355 return NS_OK;
358 // Pass the stretch to the base child ...
360 nsIFrame* baseFrame = mPresentationData.baseFrame;
361 if (baseFrame) {
362 nsIMathMLFrame* mathMLFrame;
363 baseFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
364 NS_ASSERTION(mathMLFrame, "Something is wrong somewhere");
365 if (mathMLFrame) {
366 PRBool stretchAll =
367 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
368 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
370 // And the trick is that the child's rect.x is still holding the descent,
371 // and rect.y is still holding the ascent ...
372 nsHTMLReflowMetrics childSize(aDesiredStretchSize);
373 GetReflowAndBoundingMetricsFor(baseFrame, childSize, childSize.mBoundingMetrics);
375 // See if we should downsize and confine the stretch to us...
376 // XXX there may be other cases where we can downsize the stretch,
377 // e.g., the first &Sum; might appear big in the following situation
378 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
379 // <mstyle>
380 // <msub>
381 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
382 // <msub><mo>&Sum;</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
383 // </msub>
384 // </mstyle>
385 // </math>
386 nsBoundingMetrics containerSize = aContainerSize;
387 if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
388 aStretchDirection != mEmbellishData.direction) {
389 if (mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED) {
390 containerSize = childSize.mBoundingMetrics;
392 else {
393 GetPreferredStretchSize(aRenderingContext,
394 stretchAll ? STRETCH_CONSIDER_EMBELLISHMENTS : 0,
395 mEmbellishData.direction, containerSize);
399 // do the stretching...
400 mathMLFrame->Stretch(aRenderingContext,
401 mEmbellishData.direction, containerSize, childSize);
402 // store the updated metrics
403 SaveReflowAndBoundingMetricsFor(baseFrame, childSize,
404 childSize.mBoundingMetrics);
406 // Remember the siblings which were _deferred_.
407 // Now that this embellished child may have changed, we need to
408 // fire the stretch on its siblings using our updated size
410 if (stretchAll) {
412 nsStretchDirection stretchDir =
413 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ?
414 NS_STRETCH_DIRECTION_VERTICAL : NS_STRETCH_DIRECTION_HORIZONTAL;
416 GetPreferredStretchSize(aRenderingContext, STRETCH_CONSIDER_EMBELLISHMENTS,
417 stretchDir, containerSize);
419 nsIFrame* childFrame = mFrames.FirstChild();
420 while (childFrame) {
421 if (childFrame != mPresentationData.baseFrame) {
422 childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
423 if (mathMLFrame) {
424 // retrieve the metrics that was stored at the previous pass
425 GetReflowAndBoundingMetricsFor(childFrame,
426 childSize, childSize.mBoundingMetrics);
427 // do the stretching...
428 mathMLFrame->Stretch(aRenderingContext, stretchDir,
429 containerSize, childSize);
430 // store the updated metrics
431 SaveReflowAndBoundingMetricsFor(childFrame, childSize,
432 childSize.mBoundingMetrics);
435 childFrame = childFrame->GetNextSibling();
439 // re-position all our children
440 nsresult rv = Place(aRenderingContext, PR_TRUE, aDesiredStretchSize);
441 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
442 // Make sure the child frames get their DidReflow() calls.
443 DidReflowChildren(mFrames.FirstChild());
446 // If our parent is not embellished, it means we are the outermost embellished
447 // container and so we put the spacing, otherwise we don't include the spacing,
448 // the outermost embellished container will take care of it.
450 nsEmbellishData parentData;
451 GetEmbellishDataFrom(mParent, parentData);
452 // ensure that we are the embellished child, not just a sibling
453 // (need to test coreFrame since <mfrac> resets other things)
454 if (parentData.coreFrame != mEmbellishData.coreFrame) {
455 // (we fetch values from the core since they may use units that depend
456 // on style data, and style changes could have occurred in the core since
457 // our last visit there)
458 nsEmbellishData coreData;
459 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
461 mBoundingMetrics.width += coreData.leftSpace + coreData.rightSpace;
462 aDesiredStretchSize.width = mBoundingMetrics.width;
463 aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
465 nscoord dx = coreData.leftSpace;
466 if (dx != 0) {
467 mBoundingMetrics.leftBearing += dx;
468 mBoundingMetrics.rightBearing += dx;
469 aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
470 aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
472 nsIFrame* childFrame = mFrames.FirstChild();
473 while (childFrame) {
474 childFrame->SetPosition(childFrame->GetPosition()
475 + nsPoint(dx, 0));
476 childFrame = childFrame->GetNextSibling();
481 // Finished with these:
482 ClearSavedChildMetrics();
483 // Set our overflow area
484 GatherAndStoreOverflow(&aDesiredStretchSize);
488 return NS_OK;
491 nsresult
492 nsMathMLContainerFrame::FinalizeReflow(nsIRenderingContext& aRenderingContext,
493 nsHTMLReflowMetrics& aDesiredSize)
495 // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
496 // and descent in expectation of a stretch command. Hence we need to ensure that
497 // a stretch command will actually be fired later on, after exiting from our
498 // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
499 // with inappropriate data causing children to be improperly positioned.
500 // This helper method checks to see if our parent will fire a stretch command
501 // targeted at us. If not, we go ahead and fire an involutive stretch on
502 // ourselves. This will clear all the rect.x and rect.y, and return our
503 // desired size.
506 // First, complete the post-reflow hook.
507 // We use the information in our children rectangles to position them.
508 // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
509 // They will still be holding the ascent and descent for each child.
511 // The first clause caters for any non-embellished container.
512 // The second clause is for a container which won't fire stretch even though it is
513 // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
514 // because it excludes the particular case of the core <mo>...</mo> itself.
515 // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
516 PRBool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) ||
517 (mEmbellishData.coreFrame != this && !mPresentationData.baseFrame &&
518 mEmbellishData.direction == NS_STRETCH_DIRECTION_UNSUPPORTED);
519 nsresult rv = Place(aRenderingContext, placeOrigin, aDesiredSize);
521 // Place() will call FinishReflowChild() when placeOrigin is true but if
522 // it returns before reaching FinishReflowChild() due to errors we need
523 // to fulfill the reflow protocol by calling DidReflow for the child frames
524 // that still needs it here (or we may crash - bug 366012).
525 // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
526 // through Stretch() eventually.
527 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
528 DidReflowChildren(GetFirstChild(nsnull));
529 return rv;
532 PRBool parentWillFireStretch = PR_FALSE;
533 if (!placeOrigin) {
534 // This means the rect.x and rect.y of our children were not set!!
535 // Don't go without checking to see if our parent will later fire a Stretch() command
536 // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
537 nsIMathMLFrame* mathMLFrame;
538 mParent->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
539 if (mathMLFrame) {
540 nsEmbellishData embellishData;
541 nsPresentationData presentationData;
542 mathMLFrame->GetEmbellishData(embellishData);
543 mathMLFrame->GetPresentationData(presentationData);
544 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData.flags) ||
545 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData.flags) ||
546 (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData.flags)
547 && presentationData.baseFrame == this))
549 parentWillFireStretch = PR_TRUE;
552 if (!parentWillFireStretch) {
553 // There is nobody who will fire the stretch for us, we do it ourselves!
555 PRBool stretchAll =
556 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
557 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags);
559 nsBoundingMetrics defaultSize;
560 if (mEmbellishData.coreFrame == this /* case of a bare <mo>...</mo> itself */
561 || stretchAll) { /* or <mover><mo>...</mo>...</mover>, or friends */
562 // use our current size as computed earlier by Place()
563 defaultSize = aDesiredSize.mBoundingMetrics;
565 else { /* case of <msup><mo>...</mo>...</msup> or friends */
566 // compute a size that doesn't include embellishments
567 GetPreferredStretchSize(aRenderingContext, 0, mEmbellishData.direction,
568 defaultSize);
570 Stretch(aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT, defaultSize,
571 aDesiredSize);
572 #ifdef NS_DEBUG
574 // The Place() call above didn't request FinishReflowChild(),
575 // so let's check that we eventually did through Stretch().
576 nsIFrame* childFrame = GetFirstChild(nsnull);
577 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
578 NS_ASSERTION(!(childFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
579 "DidReflow() was never called");
582 #endif
586 // see if we should fix the spacing
587 FixInterFrameSpacing(aDesiredSize);
589 // Also return our bounding metrics
590 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
592 if (!parentWillFireStretch) {
593 // Not expecting a stretch.
594 // Finished with these:
595 ClearSavedChildMetrics();
596 // Set our overflow area.
597 GatherAndStoreOverflow(&aDesiredSize);
600 return NS_OK;
604 /* /////////////
605 * nsIMathMLFrame - support methods for scripting elements (nested frames
606 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
607 * mfrac, mroot, mtable).
608 * =============================================================================
611 // helper to let the update of presentation data pass through
612 // a subtree that may contain non-mathml container frames
613 /* static */ void
614 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame* aFrame,
615 PRUint32 aFlagsValues,
616 PRUint32 aFlagsToUpdate)
618 if (!aFrame || !aFlagsToUpdate)
619 return;
620 nsIMathMLFrame* mathMLFrame;
621 aFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
622 if (mathMLFrame) {
623 // update
624 mathMLFrame->UpdatePresentationData(aFlagsValues,
625 aFlagsToUpdate);
626 // propagate using the base method to make sure that the control
627 // is passed on to MathML frames that may be overloading the method
628 mathMLFrame->UpdatePresentationDataFromChildAt(0, -1,
629 aFlagsValues, aFlagsToUpdate);
631 else {
632 // propagate down the subtrees
633 nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
634 while (childFrame) {
635 PropagatePresentationDataFor(childFrame,
636 aFlagsValues, aFlagsToUpdate);
637 childFrame = childFrame->GetNextSibling();
642 /* static */ void
643 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame* aParentFrame,
644 PRInt32 aFirstChildIndex,
645 PRInt32 aLastChildIndex,
646 PRUint32 aFlagsValues,
647 PRUint32 aFlagsToUpdate)
649 if (!aParentFrame || !aFlagsToUpdate)
650 return;
651 PRInt32 index = 0;
652 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
653 while (childFrame) {
654 if ((index >= aFirstChildIndex) &&
655 ((aLastChildIndex <= 0) || ((aLastChildIndex > 0) &&
656 (index <= aLastChildIndex)))) {
657 PropagatePresentationDataFor(childFrame,
658 aFlagsValues, aFlagsToUpdate);
660 index++;
661 childFrame = childFrame->GetNextSibling();
665 /* //////////////////
666 * Frame construction
667 * =============================================================================
671 NS_IMETHODIMP
672 nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
673 const nsRect& aDirtyRect,
674 const nsDisplayListSet& aLists)
676 // report an error if something wrong was found in this frame
677 if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
678 if (!IsVisibleForPainting(aBuilder))
679 return NS_OK;
681 return aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayMathMLError(this));
684 nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
685 NS_ENSURE_SUCCESS(rv, rv);
687 rv = DisplayTextDecorationsAndChildren(aBuilder, aDirtyRect, aLists);
688 NS_ENSURE_SUCCESS(rv, rv);
690 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
691 // for visual debug
692 // ----------------
693 // if you want to see your bounding box, make sure to properly fill
694 // your mBoundingMetrics and mReference point, and set
695 // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
696 // in the Init() of your sub-class
697 rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
698 #endif
699 return rv;
702 // Note that this method re-builds the automatic data in the children -- not
703 // in aParentFrame itself (except for those particular operations that the
704 // parent frame may do in its TransmitAutomaticData()).
705 /* static */ void
706 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame* aParentFrame)
708 // 1. As we descend the tree, make each child frame inherit data from
709 // the parent
710 // 2. As we ascend the tree, transmit any specific change that we want
711 // down the subtrees
712 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
713 while (childFrame) {
714 nsIMathMLFrame* childMathMLFrame;
715 childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&childMathMLFrame);
716 if (childMathMLFrame) {
717 childMathMLFrame->InheritAutomaticData(aParentFrame);
719 RebuildAutomaticDataForChildren(childFrame);
720 childFrame = childFrame->GetNextSibling();
722 nsIMathMLFrame* mathMLFrame;
723 aParentFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
724 if (mathMLFrame) {
725 mathMLFrame->TransmitAutomaticData();
729 /* static */ nsresult
730 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame* aParentFrame,
731 nsFrameState aBits)
733 if (!aParentFrame)
734 return NS_OK;
736 // walk-up to the first frame that is a MathML frame, stop if we reach <math>
737 nsIFrame* frame = aParentFrame;
738 while (1) {
739 nsIFrame* parent = frame->GetParent();
740 if (!parent || !parent->GetContent())
741 break;
743 // stop if it is a MathML frame
744 nsIMathMLFrame* mathMLFrame;
745 frame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
746 if (mathMLFrame)
747 break;
749 // stop if we reach the root <math> tag
750 nsIContent* content = frame->GetContent();
751 NS_ASSERTION(content, "dangling frame without a content node");
752 if (!content)
753 break;
754 // XXXldb This should check namespaces too.
755 if (content->Tag() == nsGkAtoms::math)
756 break;
758 // mark the frame dirty, and continue to climb up. It's important that
759 // we're NOT doing this to the frame we plan to pass to FrameNeedsReflow()
760 frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
762 frame = parent;
765 // re-sync the presentation data and embellishment data of our children
766 RebuildAutomaticDataForChildren(frame);
768 // Ask our parent frame to reflow us
769 nsIFrame* parent = frame->GetParent();
770 NS_ASSERTION(parent, "No parent to pass the reflow request up to");
771 if (!parent)
772 return NS_OK;
774 return frame->PresContext()->PresShell()->
775 FrameNeedsReflow(frame, nsIPresShell::eStyleChange, aBits);
778 // There are precise rules governing children of a MathML frame,
779 // and properties such as the scriptlevel depends on those rules.
780 // Hence for things to work, callers must use Append/Insert/etc wisely.
782 nsresult
783 nsMathMLContainerFrame::ChildListChanged(PRInt32 aModType)
785 // If this is an embellished frame we need to rebuild the
786 // embellished hierarchy by walking-up to the parent of the
787 // outermost embellished container.
788 nsIFrame* frame = this;
789 if (mEmbellishData.coreFrame) {
790 nsIFrame* parent = mParent;
791 nsEmbellishData embellishData;
792 for ( ; parent; frame = parent, parent = parent->GetParent()) {
793 GetEmbellishDataFrom(parent, embellishData);
794 if (embellishData.coreFrame != mEmbellishData.coreFrame)
795 break;
797 // Important: do not do this to the frame we plan to pass to
798 // ReLayoutChildren
799 frame->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
802 return ReLayoutChildren(frame, NS_FRAME_IS_DIRTY);
805 NS_IMETHODIMP
806 nsMathMLContainerFrame::AppendFrames(nsIAtom* aListName,
807 nsIFrame* aFrameList)
809 if (aListName) {
810 return NS_ERROR_INVALID_ARG;
812 if (aFrameList) {
813 mFrames.AppendFrames(this, aFrameList);
814 return ChildListChanged(nsIDOMMutationEvent::ADDITION);
816 return NS_OK;
819 NS_IMETHODIMP
820 nsMathMLContainerFrame::InsertFrames(nsIAtom* aListName,
821 nsIFrame* aPrevFrame,
822 nsIFrame* aFrameList)
824 if (aListName) {
825 return NS_ERROR_INVALID_ARG;
827 if (aFrameList) {
828 // Insert frames after aPrevFrame
829 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
830 return ChildListChanged(nsIDOMMutationEvent::ADDITION);
832 return NS_OK;
835 NS_IMETHODIMP
836 nsMathMLContainerFrame::RemoveFrame(nsIAtom* aListName,
837 nsIFrame* aOldFrame)
839 if (aListName) {
840 return NS_ERROR_INVALID_ARG;
842 // remove the child frame
843 mFrames.DestroyFrame(aOldFrame);
844 return ChildListChanged(nsIDOMMutationEvent::REMOVAL);
847 NS_IMETHODIMP
848 nsMathMLContainerFrame::AttributeChanged(PRInt32 aNameSpaceID,
849 nsIAtom* aAttribute,
850 PRInt32 aModType)
852 // XXX Since they are numerous MathML attributes that affect layout, and
853 // we can't check all of them here, play safe by requesting a reflow.
854 // XXXldb This should only do work for attributes that cause changes!
855 return PresContext()->PresShell()->
856 FrameNeedsReflow(this, nsIPresShell::eStyleChange,
857 NS_FRAME_IS_DIRTY);
860 void
861 nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
863 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
864 // frame rectangle.
865 nsRect frameRect(0, 0, aMetrics->width, aMetrics->height);
867 // Text-shadow overflows.
868 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) {
869 nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(frameRect, this);
870 frameRect.UnionRect(frameRect, shadowRect);
873 // All non-child-frame content such as nsMathMLChars (and most child-frame
874 // content) is included in mBoundingMetrics.
875 nsRect boundingBox(mBoundingMetrics.leftBearing,
876 aMetrics->ascent - mBoundingMetrics.ascent,
877 mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing,
878 mBoundingMetrics.ascent + mBoundingMetrics.descent);
880 aMetrics->mOverflowArea.UnionRect(frameRect, boundingBox);
882 // mBoundingMetrics does not necessarily include content of <mpadded>
883 // elements whose mBoundingMetrics may not be representative of the true
884 // bounds, and doesn't include the CSS2 outline rectangles of children, so
885 // make such to include child overflow areas.
886 nsIFrame* childFrame = mFrames.FirstChild();
887 while (childFrame) {
888 ConsiderChildOverflow(aMetrics->mOverflowArea, childFrame);
889 childFrame = childFrame->GetNextSibling();
892 FinishAndStoreOverflow(aMetrics);
895 nsresult
896 nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
897 nsPresContext* aPresContext,
898 nsHTMLReflowMetrics& aDesiredSize,
899 const nsHTMLReflowState& aReflowState,
900 nsReflowStatus& aStatus)
902 // Having foreign/hybrid children, e.g., from html markups, is not defined by
903 // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
904 // to do some cool demos... or we may have a child that is an nsInlineFrame
905 // from a generated content such as :before { content: open-quote } or
906 // :after { content: close-quote }. Unfortunately, the other frames out-there
907 // may expect their own invariants that are not met when we mix things.
908 // Hence we do not claim their support, but we will nevertheless attempt to keep
909 // them in the flow, if we can get their desired size. We observed that most
910 // frames may be reflowed generically, but nsInlineFrames need extra care.
912 #ifdef DEBUG
913 nsInlineFrame* inlineFrame;
914 aChildFrame->QueryInterface(kInlineFrameCID, (void**)&inlineFrame);
915 NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
916 #endif
918 nsresult rv = nsHTMLContainerFrame::
919 ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
920 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
922 if (NS_FAILED(rv))
923 return rv;
925 if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
926 // This will be suitable for inline frames, which are wrapped in a block.
927 if(!nsLayoutUtils::GetLastLineBaseline(aChildFrame,
928 &aDesiredSize.ascent)) {
929 // We don't expect any other block children so just place the frame on
930 // the baseline instead of going through DidReflow() and
931 // GetBaseline(). This is what nsFrame::GetBaseline() will do anyway.
932 aDesiredSize.ascent = aDesiredSize.height;
935 if (IsForeignChild(aChildFrame)) {
936 // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
937 nsRect r = aChildFrame->ComputeTightBounds(aReflowState.rendContext->ThebesContext());
938 aDesiredSize.mBoundingMetrics.leftBearing = r.x;
939 aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
940 aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.ascent - r.y;
941 aDesiredSize.mBoundingMetrics.descent = r.YMost() - aDesiredSize.ascent;
942 aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
944 return rv;
947 NS_IMETHODIMP
948 nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
949 nsHTMLReflowMetrics& aDesiredSize,
950 const nsHTMLReflowState& aReflowState,
951 nsReflowStatus& aStatus)
953 aDesiredSize.width = aDesiredSize.height = 0;
954 aDesiredSize.ascent = 0;
955 aDesiredSize.mBoundingMetrics.Clear();
957 /////////////
958 // Reflow children
959 // Asking each child to cache its bounding metrics
961 nsReflowStatus childStatus;
962 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
963 nsIFrame* childFrame = mFrames.FirstChild();
964 while (childFrame) {
965 nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags);
966 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
967 childFrame, availSize);
968 nsresult rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
969 childReflowState, childStatus);
970 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
971 if (NS_FAILED(rv)) {
972 // Call DidReflow() for the child frames we successfully did reflow.
973 DidReflowChildren(mFrames.FirstChild(), childFrame);
974 return rv;
977 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
978 childDesiredSize.mBoundingMetrics);
979 childFrame = childFrame->GetNextSibling();
982 /////////////
983 // If we are a container which is entitled to stretch its children, then we
984 // ask our stretchy children to stretch themselves
986 // The stretching of siblings of an embellished child is _deferred_ until
987 // after finishing the stretching of the embellished child - bug 117652
989 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags) &&
990 (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) ||
991 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData.flags))) {
993 // get the stretchy direction
994 nsStretchDirection stretchDir =
995 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags)
996 ? NS_STRETCH_DIRECTION_VERTICAL
997 : NS_STRETCH_DIRECTION_HORIZONTAL;
999 // what size should we use to stretch our stretchy children
1000 // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
1001 // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
1002 // include them in the caculations of the size of stretchy elements
1003 nsBoundingMetrics containerSize;
1004 GetPreferredStretchSize(*aReflowState.rendContext, 0, stretchDir,
1005 containerSize);
1007 // fire the stretch on each child
1008 childFrame = mFrames.FirstChild();
1009 while (childFrame) {
1010 nsIMathMLFrame* mathMLFrame;
1011 childFrame->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
1012 if (mathMLFrame) {
1013 // retrieve the metrics that was stored at the previous pass
1014 nsHTMLReflowMetrics childDesiredSize;
1015 GetReflowAndBoundingMetricsFor(childFrame,
1016 childDesiredSize, childDesiredSize.mBoundingMetrics);
1018 mathMLFrame->Stretch(*aReflowState.rendContext, stretchDir,
1019 containerSize, childDesiredSize);
1020 // store the updated metrics
1021 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
1022 childDesiredSize.mBoundingMetrics);
1024 childFrame = childFrame->GetNextSibling();
1028 /////////////
1029 // Place children now by re-adjusting the origins to align the baselines
1030 FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
1032 aStatus = NS_FRAME_COMPLETE;
1033 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1034 return NS_OK;
1037 /* virtual */ nscoord
1038 nsMathMLContainerFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
1040 nscoord result;
1041 DISPLAY_MIN_WIDTH(this, result);
1042 result = GetIntrinsicWidth(aRenderingContext);
1043 return result;
1046 /* virtual */ nscoord
1047 nsMathMLContainerFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
1049 nscoord result;
1050 DISPLAY_MIN_WIDTH(this, result);
1051 result = GetIntrinsicWidth(aRenderingContext);
1052 return result;
1055 /* virtual */ nscoord
1056 nsMathMLContainerFrame::GetIntrinsicWidth(nsIRenderingContext* aRenderingContext)
1058 // Get child widths
1059 nsIFrame* childFrame = mFrames.FirstChild();
1060 while (childFrame) {
1061 // XXX This includes margin while Reflow currently doesn't consider
1062 // margin, so we may end up with too much space, but, with stretchy
1063 // characters, this is an approximation anyway.
1064 nscoord width =
1065 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
1066 nsLayoutUtils::PREF_WIDTH);
1068 nsHTMLReflowMetrics childDesiredSize;
1069 childDesiredSize.width = width;
1070 childDesiredSize.mBoundingMetrics.width = width;
1071 // TODO: we need nsIFrame::GetIntrinsicHBounds() for better values here.
1072 childDesiredSize.mBoundingMetrics.leftBearing = 0;
1073 childDesiredSize.mBoundingMetrics.rightBearing = width;
1075 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
1076 childDesiredSize.mBoundingMetrics);
1078 childFrame = childFrame->GetNextSibling();
1081 // Measure
1082 nsHTMLReflowMetrics desiredSize;
1083 nsresult rv = MeasureChildFrames(*aRenderingContext, desiredSize);
1084 if (NS_FAILED(rv)) {
1085 ReflowError(*aRenderingContext, desiredSize);
1088 ClearSavedChildMetrics();
1090 return desiredSize.width;
1093 /* virtual */ nsresult
1094 nsMathMLContainerFrame::MeasureChildFrames(nsIRenderingContext& aRenderingContext,
1095 nsHTMLReflowMetrics& aDesiredSize)
1097 return Place(aRenderingContext, PR_FALSE, aDesiredSize);
1101 // see spacing table in Chapter 18, TeXBook (p.170)
1102 // Our table isn't quite identical to TeX because operators have
1103 // built-in values for lspace & rspace in the Operator Dictionary.
1104 static PRInt32 kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_COUNT] =
1106 // in units of muspace.
1107 // upper half of the byte is set if the
1108 // spacing is not to be used for scriptlevel > 0
1110 /* Ord OpOrd OpInv OpUsr Inner Italic Upright */
1111 /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
1112 /*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1113 /*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1114 /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1115 /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1116 /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
1117 /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}
1120 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
1121 /* no space if there is a frame that we know nothing about */ \
1122 if (frametype1_ == eMathMLFrameType_UNKNOWN || \
1123 frametype2_ == eMathMLFrameType_UNKNOWN) \
1124 space_ = 0; \
1125 else { \
1126 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
1127 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
1128 ? 0 /* spacing is disabled */ \
1129 : space_ & 0x0F; \
1132 // This function computes the inter-space between two frames. However,
1133 // since invisible operators need special treatment, the inter-space may
1134 // be delayed when an invisible operator is encountered. In this case,
1135 // the function will carry the inter-space forward until it is determined
1136 // that it can be applied properly (i.e., until we encounter a visible
1137 // frame where to decide whether to accept or reject the inter-space).
1138 // aFromFrameType: remembers the frame when the carry-forward initiated.
1139 // aCarrySpace: keeps track of the inter-space that is delayed.
1140 // @returns: current inter-space (which is 0 when the true inter-space is
1141 // delayed -- and thus has no effect since the frame is invisible anyway).
1142 static nscoord
1143 GetInterFrameSpacing(PRInt32 aScriptLevel,
1144 eMathMLFrameType aFirstFrameType,
1145 eMathMLFrameType aSecondFrameType,
1146 eMathMLFrameType* aFromFrameType, // IN/OUT
1147 PRInt32* aCarrySpace) // IN/OUT
1149 eMathMLFrameType firstType = aFirstFrameType;
1150 eMathMLFrameType secondType = aSecondFrameType;
1152 PRInt32 space;
1153 GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
1155 // feedback control to avoid the inter-space to be added when not necessary
1156 if (secondType == eMathMLFrameType_OperatorInvisible) {
1157 // see if we should start to carry the space forward until we
1158 // encounter a visible frame
1159 if (*aFromFrameType == eMathMLFrameType_UNKNOWN) {
1160 *aFromFrameType = firstType;
1161 *aCarrySpace = space;
1163 // keep carrying *aCarrySpace forward, while returning 0 for this stage
1164 space = 0;
1166 else if (*aFromFrameType != eMathMLFrameType_UNKNOWN) {
1167 // no carry-forward anymore, get the real inter-space between
1168 // the two frames of interest
1170 firstType = *aFromFrameType;
1172 // But... the invisible operator that we encountered earlier could
1173 // be sitting between italic and upright identifiers, e.g.,
1175 // 1. <mi>sin</mi> <mo>&ApplyFunction;</mo> <mi>x</mi>
1176 // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
1178 // the trick to get the inter-space in either situation
1179 // is to promote "<mi>sin</mi><mo>&ApplyFunction;</mo>" and
1180 // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
1181 if (firstType == eMathMLFrameType_UprightIdentifier) {
1182 firstType = eMathMLFrameType_OperatorUserDefined;
1184 else if (secondType == eMathMLFrameType_UprightIdentifier) {
1185 secondType = eMathMLFrameType_OperatorUserDefined;
1188 GET_INTERSPACE(aScriptLevel, firstType, secondType, space);
1190 // Now, we have two values: the computed space and the space that
1191 // has been carried forward until now. Which value do we pick?
1192 // If the second type is an operator (e.g., fence), it already has
1193 // built-in lspace & rspace, so we let them win. Otherwise we pick
1194 // the max between the two values that we have.
1195 if (secondType != eMathMLFrameType_OperatorOrdinary &&
1196 space < *aCarrySpace)
1197 space = *aCarrySpace;
1199 // reset everything now that the carry-forward is done
1200 *aFromFrameType = eMathMLFrameType_UNKNOWN;
1201 *aCarrySpace = 0;
1204 return space;
1207 static nscoord GetThinSpace(const nsStyleFont* aStyleFont)
1209 return NSToCoordRound(float(aStyleFont->mFont.size)*float(3) / float(18));
1212 class nsMathMLContainerFrame::RowChildFrameIterator {
1213 public:
1214 explicit RowChildFrameIterator(nsMathMLContainerFrame* aParentFrame) :
1215 mParentFrame(aParentFrame),
1216 mChildFrame(aParentFrame->mFrames.FirstChild()),
1217 mX(0),
1218 mCarrySpace(0),
1219 mFromFrameType(eMathMLFrameType_UNKNOWN)
1221 if (!mChildFrame)
1222 return;
1224 InitMetricsForChild();
1225 // Remove left correction in <msqrt> because the sqrt glyph itself is
1226 // there first.
1227 if (mParentFrame->GetContent()->Tag() == nsGkAtoms::msqrt_) {
1228 mX = 0;
1232 RowChildFrameIterator& operator++()
1234 // add child size + italic correction
1235 mX += mSize.mBoundingMetrics.width + mItalicCorrection;
1237 mChildFrame = mChildFrame->GetNextSibling();
1238 if (!mChildFrame)
1239 return *this;
1241 eMathMLFrameType prevFrameType = mChildFrameType;
1242 InitMetricsForChild();
1244 // add inter frame spacing
1245 const nsStyleFont* font = mParentFrame->GetStyleFont();
1246 nscoord space =
1247 GetInterFrameSpacing(font->mScriptLevel,
1248 prevFrameType, mChildFrameType,
1249 &mFromFrameType, &mCarrySpace);
1250 mX += space * GetThinSpace(font);
1251 return *this;
1254 nsIFrame* Frame() const { return mChildFrame; }
1255 nscoord X() const { return mX; }
1256 const nsHTMLReflowMetrics& ReflowMetrics() const { return mSize; }
1257 nscoord Ascent() const { return mSize.ascent; }
1258 nscoord Descent() const { return mSize.height - mSize.ascent; }
1259 const nsBoundingMetrics& BoundingMetrics() const {
1260 return mSize.mBoundingMetrics;
1263 private:
1264 const nsMathMLContainerFrame* mParentFrame;
1265 nsIFrame* mChildFrame;
1266 nsHTMLReflowMetrics mSize;
1267 nscoord mX;
1269 nscoord mItalicCorrection;
1270 eMathMLFrameType mChildFrameType;
1271 PRInt32 mCarrySpace;
1272 eMathMLFrameType mFromFrameType;
1274 void InitMetricsForChild()
1276 GetReflowAndBoundingMetricsFor(mChildFrame, mSize, mSize.mBoundingMetrics,
1277 &mChildFrameType);
1278 nscoord leftCorrection;
1279 GetItalicCorrection(mSize.mBoundingMetrics, leftCorrection,
1280 mItalicCorrection);
1281 // add left correction -- this fixes the problem of the italic 'f'
1282 // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
1283 mX += leftCorrection;
1287 /* virtual */ nsresult
1288 nsMathMLContainerFrame::Place(nsIRenderingContext& aRenderingContext,
1289 PRBool aPlaceOrigin,
1290 nsHTMLReflowMetrics& aDesiredSize)
1292 // This is needed in case this frame is empty (i.e., no child frames)
1293 mBoundingMetrics.Clear();
1295 RowChildFrameIterator child(this);
1296 nscoord ascent = 0, descent = 0;
1297 while (child.Frame()) {
1298 if (descent < child.Descent())
1299 descent = child.Descent();
1300 if (ascent < child.Ascent())
1301 ascent = child.Ascent();
1302 // add the child size
1303 mBoundingMetrics.width = child.X();
1304 mBoundingMetrics += child.BoundingMetrics();
1305 ++child;
1307 // Add the italic correction at the end (including the last child).
1308 // This gives a nice gap between math and non-math frames, and still
1309 // gives the same math inter-spacing in case this frame connects to
1310 // another math frame
1311 mBoundingMetrics.width = child.X();
1313 aDesiredSize.width = mBoundingMetrics.width;
1314 aDesiredSize.height = ascent + descent;
1315 aDesiredSize.ascent = ascent;
1316 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
1318 mReference.x = 0;
1319 mReference.y = aDesiredSize.ascent;
1321 //////////////////
1322 // Place Children
1324 if (aPlaceOrigin) {
1325 PositionRowChildFrames(0, aDesiredSize.ascent);
1328 return NS_OK;
1331 void
1332 nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX,
1333 nscoord aBaseline)
1335 RowChildFrameIterator child(this);
1336 while (child.Frame()) {
1337 nscoord dx = aOffsetX + child.X();
1338 nscoord dy = aBaseline - child.Ascent();
1339 FinishReflowChild(child.Frame(), PresContext(), nsnull,
1340 child.ReflowMetrics(), dx, dy, 0);
1341 ++child;
1345 class ForceReflow : public nsIReflowCallback {
1346 public:
1347 virtual PRBool ReflowFinished() {
1348 return PR_TRUE;
1350 virtual void ReflowCallbackCanceled() {}
1353 // We only need one of these so we just make it a static global, no need
1354 // to dynamically allocate/destroy it.
1355 static ForceReflow gForceReflow;
1357 void
1358 nsMathMLContainerFrame::SetIncrementScriptLevel(PRInt32 aChildIndex, PRBool aIncrement)
1360 nsIFrame* child = nsFrameList(GetFirstChild(nsnull)).FrameAt(aChildIndex);
1361 if (!child)
1362 return;
1363 nsIContent* content = child->GetContent();
1364 if (!content->IsNodeOfType(nsINode::eMATHML))
1365 return;
1366 nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
1368 if (element->GetIncrementScriptLevel() == aIncrement)
1369 return;
1371 // XXXroc this does a ContentStatesChanged, is it safe to call here? If
1372 // not we should do it in a post-reflow callback.
1373 element->SetIncrementScriptLevel(aIncrement, PR_TRUE);
1374 PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
1377 // helpers to fix the inter-spacing when <math> is the only parent
1378 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
1380 static nscoord
1381 GetInterFrameSpacingFor(PRInt32 aScriptLevel,
1382 nsIFrame* aParentFrame,
1383 nsIFrame* aChildFrame)
1385 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
1386 if (!childFrame || aChildFrame == childFrame)
1387 return 0;
1389 PRInt32 carrySpace = 0;
1390 eMathMLFrameType fromFrameType = eMathMLFrameType_UNKNOWN;
1391 eMathMLFrameType prevFrameType = eMathMLFrameType_UNKNOWN;
1392 eMathMLFrameType childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
1393 childFrame = childFrame->GetNextSibling();
1394 while (childFrame) {
1395 prevFrameType = childFrameType;
1396 childFrameType = nsMathMLFrame::GetMathMLFrameTypeFor(childFrame);
1397 nscoord space = GetInterFrameSpacing(aScriptLevel,
1398 prevFrameType, childFrameType, &fromFrameType, &carrySpace);
1399 if (aChildFrame == childFrame) {
1400 // get thinspace
1401 nsStyleContext* parentContext = aParentFrame->GetStyleContext();
1402 nscoord thinSpace = GetThinSpace(parentContext->GetStyleFont());
1403 // we are done
1404 return space * thinSpace;
1406 childFrame = childFrame->GetNextSibling();
1409 NS_NOTREACHED("child not in the childlist of its parent");
1410 return 0;
1413 nscoord
1414 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
1416 nscoord gap = 0;
1417 nsIContent* parentContent = mParent->GetContent();
1418 if (NS_UNLIKELY(!parentContent)) {
1419 return 0;
1421 // XXXldb This should check namespaces too.
1422 nsIAtom *parentTag = parentContent->Tag();
1423 if (parentTag == nsGkAtoms::math ||
1424 parentTag == nsGkAtoms::mtd_) {
1425 gap = GetInterFrameSpacingFor(GetStyleFont()->mScriptLevel, mParent, this);
1426 // add our own italic correction
1427 nscoord leftCorrection = 0, italicCorrection = 0;
1428 GetItalicCorrection(mBoundingMetrics, leftCorrection, italicCorrection);
1429 gap += leftCorrection;
1430 // see if we should shift our children to account for the correction
1431 if (gap) {
1432 nsIFrame* childFrame = mFrames.FirstChild();
1433 while (childFrame) {
1434 childFrame->SetPosition(childFrame->GetPosition() + nsPoint(gap, 0));
1435 childFrame = childFrame->GetNextSibling();
1437 mBoundingMetrics.leftBearing += gap;
1438 mBoundingMetrics.rightBearing += gap;
1439 mBoundingMetrics.width += gap;
1440 aDesiredSize.width += gap;
1442 mBoundingMetrics.width += italicCorrection;
1443 aDesiredSize.width += italicCorrection;
1445 return gap;
1448 /* static */ void
1449 nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
1452 if (NS_UNLIKELY(!aFirst))
1453 return;
1455 for (nsIFrame* frame = aFirst;
1456 frame != aStop;
1457 frame = frame->GetNextSibling()) {
1458 NS_ASSERTION(frame, "aStop isn't a sibling");
1459 if (frame->GetStateBits() & NS_FRAME_IN_REFLOW) {
1460 // finish off principal descendants, too
1461 nsIFrame* grandchild = frame->GetFirstChild(nsnull);
1462 if (grandchild)
1463 DidReflowChildren(grandchild, nsnull);
1465 frame->DidReflow(frame->PresContext(), nsnull,
1466 NS_FRAME_REFLOW_FINISHED);
1471 //==========================
1473 nsIFrame*
1474 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
1475 PRUint32 aFlags)
1477 nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
1478 if (it) {
1479 it->SetFlags(aFlags);
1481 return it;
1484 nsIFrame*
1485 NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1487 return new (aPresShell) nsMathMLmathInlineFrame(aContext);