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