layout/mathml/nsMathMLContainerFrame.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial