diff --git a/mozilla/gfx/public/nsCoord.h b/mozilla/gfx/public/nsCoord.h index e90259a811f..00874c161e2 100644 --- a/mozilla/gfx/public/nsCoord.h +++ b/mozilla/gfx/public/nsCoord.h @@ -121,6 +121,103 @@ inline nscoord NSCoordDivide(nscoord aCoord, PRInt32 aVal) { #endif } +/** + * Returns a + b, capping the sum to nscoord_MAX. + * + * This function assumes that neither argument is nscoord_MIN. + * + * Note: If/when we start using floats for nscoords, this function won't be as + * necessary. Normal float addition correctly handles adding with infinity, + * assuming we aren't adding nscoord_MIN. (-infinity) + */ +inline nscoord +NSCoordSaturatingAdd(nscoord a, nscoord b) +{ + VERIFY_COORD(a); + VERIFY_COORD(b); + NS_ASSERTION(a != nscoord_MIN && b != nscoord_MIN, + "NSCoordSaturatingAdd got nscoord_MIN as argument"); + +#ifdef NS_COORD_IS_FLOAT + // Float math correctly handles a+b, given that neither is -infinity. + return a + b; +#else + if (a == nscoord_MAX || b == nscoord_MAX) { + // infinity + anything = anything + infinity = infinity + return nscoord_MAX; + } else { + // a + b = a + b + NS_ASSERTION(a < nscoord_MAX && b < nscoord_MAX, + "Doing nscoord addition with values > nscoord_MAX"); + NS_ASSERTION((PRInt64)a + (PRInt64)b < (PRInt64)nscoord_MAX, + "nscoord addition will reach or pass nscoord_MAX"); + NS_ASSERTION((PRInt64)a + (PRInt64)b > (PRInt64)nscoord_MIN, + "nscoord addition will reach or pass nscoord_MIN"); + + // Cap the result, just in case we're dealing with numbers near nscoord_MAX + return PR_MIN(nscoord_MAX, a + b); + } +#endif +} + +/** + * Returns a - b, gracefully handling cases involving nscoord_MAX. + * This function assumes that neither argument is nscoord_MIN. + * + * The behavior is as follows: + * + * a) infinity - infinity -> infMinusInfResult + * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED) + * c) infinity - N -> infinity + * d) N1 - N2 -> N1 - N2 + * + * Note: For float nscoords, cases (c) and (d) are handled by normal float + * math. We still need to explicitly specify the behavior for cases (a) + * and (b), though. (Under normal float math, those cases would return NaN + * and -infinity, respectively.) + */ +inline nscoord +NSCoordSaturatingSubtract(nscoord a, nscoord b, + nscoord infMinusInfResult) +{ + VERIFY_COORD(a); + VERIFY_COORD(b); + NS_ASSERTION(a != nscoord_MIN && b != nscoord_MIN, + "NSCoordSaturatingSubtract got nscoord_MIN as argument"); + + if (b == nscoord_MAX) { + if (a == nscoord_MAX) { + // case (a) + return infMinusInfResult; + } else { + // case (b) + NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]"); + return 0; + } + } else { +#ifdef NS_COORD_IS_FLOAT + // case (c) and (d) for floats. (float math handles both) + return a - b; +#else + if (a == nscoord_MAX) { + // case (c) for integers + return nscoord_MAX; + } else { + // case (d) for integers + NS_ASSERTION(a < nscoord_MAX && b < nscoord_MAX, + "Doing nscoord subtraction with values > nscoord_MAX"); + NS_ASSERTION((PRInt64)a - (PRInt64)b < (PRInt64)nscoord_MAX, + "nscoord subtraction will reach or pass nscoord_MAX"); + NS_ASSERTION((PRInt64)a - (PRInt64)b > (PRInt64)nscoord_MIN, + "nscoord subtraction will reach or pass nscoord_MIN"); + + // Cap the result, in case we're dealing with numbers near nscoord_MAX + return PR_MIN(nscoord_MAX, a - b); + } + } +#endif +} + /** * Convert an nscoord to a PRInt32. This *does not* do rounding because * coords are never fractional. They can be out of range, so this does diff --git a/mozilla/layout/base/nsLayoutUtils.cpp b/mozilla/layout/base/nsLayoutUtils.cpp index 08c4c0e5813..a46beff6927 100644 --- a/mozilla/layout/base/nsLayoutUtils.cpp +++ b/mozilla/layout/base/nsLayoutUtils.cpp @@ -1508,7 +1508,7 @@ nsLayoutUtils::IntrinsicForContainer(nsIRenderingContext *aRenderingContext, if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) { min += coordOutsideWidth; - result += coordOutsideWidth; + result = NSCoordSaturatingAdd(result, coordOutsideWidth); pctTotal += pctOutsideWidth; coordOutsideWidth = 0; @@ -1519,7 +1519,7 @@ nsLayoutUtils::IntrinsicForContainer(nsIRenderingContext *aRenderingContext, if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) { min += coordOutsideWidth; - result += coordOutsideWidth; + result = NSCoordSaturatingAdd(result, coordOutsideWidth); pctTotal += pctOutsideWidth; coordOutsideWidth = 0; @@ -1530,7 +1530,7 @@ nsLayoutUtils::IntrinsicForContainer(nsIRenderingContext *aRenderingContext, pctOutsideWidth += offsets.hPctMargin; min += coordOutsideWidth; - result += coordOutsideWidth; + result = NSCoordSaturatingAdd(result, coordOutsideWidth); pctTotal += pctOutsideWidth; nscoord w; diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index 70257620713..758d88029a3 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -2790,8 +2790,9 @@ nsFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext, { aData->trailingWhitespace = 0; aData->skipWhitespace = PR_FALSE; - aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, - this, nsLayoutUtils::PREF_WIDTH); + nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::PREF_WIDTH); + aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref); } void diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp index f565931247d..9eb291c1d91 100644 --- a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp +++ b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp @@ -219,7 +219,7 @@ GetWidthInfo(nsIRenderingContext *aRenderingContext, // XXX Should we ignore percentage padding? nscoord add = offsets.hPadding + offsets.hBorder; minCoord += add; - prefCoord += add; + prefCoord = NSCoordSaturatingAdd(prefCoord, add); } return CellWidthInfo(minCoord, prefCoord, prefPercent, hasSpecifiedWidth); @@ -490,8 +490,10 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen NSToCoordRound(float(minWithinPref) * minRatio); nscoord allocatedMinOutsidePref = NSToCoordRound(float(minOutsidePref) * coordRatio); - nscoord allocatedPref = - NSToCoordRound(float(info.prefCoord) * coordRatio); + nscoord allocatedPref = + (info.prefCoord == nscoord_MAX ? + nscoord_MAX : + NSToCoordRound(float(info.prefCoord) * coordRatio)); nscoord spanMin = scolFrame->GetMinCoord() + allocatedMinWithinPref + allocatedMinOutsidePref; nscoord spanPref = scolFrame->GetPrefCoord() + allocatedPref; @@ -503,7 +505,9 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen // passed from the totals. minWithinPref -= allocatedMinWithinPref; minOutsidePref -= allocatedMinOutsidePref; - info.prefCoord -= allocatedPref; + info.prefCoord = NSCoordSaturatingSubtract(info.prefCoord, + allocatedPref, + nscoord_MAX); info.prefPercent -= allocatedPct; totalSPref -= scolFrame->GetPrefCoord(); totalSMin -= scolFrame->GetMinCoord(); @@ -521,7 +525,8 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen NS_ASSERTION(totalSPref == 0 && totalSMin == 0 && totalSNonPctPref == 0 && nonPctCount == 0 && minOutsidePref == 0 && minWithinPref == 0 && - info.prefCoord == 0 && + (info.prefCoord == 0 || + info.prefCoord == nscoord_MAX) && (info.prefPercent == 0.0f || !spanHasNonPct), "didn't subtract all that we added"); } while ((item = item->next)); @@ -593,7 +598,7 @@ BasicTableLayoutStrategy::ComputeIntrinsicWidths(nsIRenderingContext* aRendering add += spacing; } min += colFrame->GetMinCoord(); - pref += colFrame->GetPrefCoord(); + pref = NSCoordSaturatingAdd(pref, colFrame->GetPrefCoord()); // Percentages are of the table, so we have to reverse them for // intrinsic widths. @@ -640,8 +645,8 @@ BasicTableLayoutStrategy::ComputeIntrinsicWidths(nsIRenderingContext* aRendering // border-spacing isn't part of the basis for percentages if (colCount > 0) { min += add; - pref += add; - pref_pct_expand += add; + pref = NSCoordSaturatingAdd(pref, add); + pref_pct_expand = NSCoordSaturatingAdd(pref_pct_expand, add); } mMinWidth = min; @@ -749,6 +754,7 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt total_flex_pref = 0, total_fixed_pref = 0; float total_pct = 0.0f; // 0.0f to 1.0f + PRInt32 numInfiniteWidthCols = 0; PRInt32 col; for (col = 0; col < colCount; ++col) { @@ -769,19 +775,26 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt guess_pref += val; } else { nscoord pref_width = colFrame->GetPrefCoord(); - guess_pref += pref_width; + if (pref_width == nscoord_MAX) { + numInfiniteWidthCols++; + } + guess_pref = NSCoordSaturatingAdd(guess_pref, pref_width); guess_min_pct += min_width; if (colFrame->GetHasSpecifiedCoord()) { // we'll add on the rest of guess_min_spec outside the // loop - guess_min_spec += pref_width - min_width; - total_fixed_pref += pref_width; + nscoord delta = NSCoordSaturatingSubtract(pref_width, + min_width, 0); + guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, delta); + total_fixed_pref = NSCoordSaturatingAdd(total_fixed_pref, + pref_width); } else { - total_flex_pref += pref_width; + total_flex_pref = NSCoordSaturatingAdd(total_flex_pref, + pref_width); } } } - guess_min_spec += guess_min_pct; + guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, guess_min_pct); // Determine what we're flexing: enum Loop2Type { @@ -812,13 +825,18 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt } else if (width < guess_min_spec) { l2t = FLEX_FIXED_SMALL; space = width - guess_min_pct; - basis.c = guess_min_spec - guess_min_pct; + basis.c = NSCoordSaturatingSubtract(guess_min_spec, guess_min_pct, + nscoord_MAX); } else { l2t = FLEX_FLEX_SMALL; space = width - guess_min_spec; - basis.c = guess_pref - guess_min_spec; + basis.c = NSCoordSaturatingSubtract(guess_pref, guess_min_spec, + nscoord_MAX); } } else { + // Note: Shouldn't have to check for nscoord_MAX in this case, because + // width should be much less than nscoord_MAX, and being here means + // guess_pref is no larger than width. space = width - guess_pref; if (total_flex_pref > 0) { l2t = FLEX_FLEX_LARGE; @@ -903,11 +921,30 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt NS_ASSERTION(col_width == colFrame->GetPrefCoord(), "wrong width assigned"); nscoord col_min = colFrame->GetMinCoord(); - nscoord pref_minus_min = col_width - col_min; + nscoord pref_minus_min = + NSCoordSaturatingSubtract(col_width, col_min, 0); col_width = col_width_before_adjust = col_min; if (pref_minus_min != 0) { float c = float(space) / float(basis.c); - basis.c -= pref_minus_min; + // If we have infinite-width cols, then the standard + // adjustment to col_width using 'c' won't work, + // because basis.c and pref_minus_min are both + // nscoord_MAX and will cancel each other out in the + // col_width adjustment (making us assign all the + // space to the first inf-width col). To correct for + // this, we'll also divide by numInfiniteWidthCols to + // spread the space equally among the inf-width cols. + if (numInfiniteWidthCols) { + if (colFrame->GetPrefCoord() == nscoord_MAX) { + c = c / float(numInfiniteWidthCols); + numInfiniteWidthCols--; + } else { + c = 0.0f; + } + } + basis.c = NSCoordSaturatingSubtract(basis.c, + pref_minus_min, + nscoord_MAX); col_width += NSToCoordRound( float(pref_minus_min) * c); } @@ -971,7 +1008,7 @@ BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowSt NS_ASSERTION(space == 0 && ((l2t == FLEX_PCT_LARGE) ? (-0.001f < basis.f && basis.f < 0.001f) - : (basis.c == 0)), + : (basis.c == 0 || basis.c == nscoord_MAX)), "didn't subtract all that we added"); #ifdef DEBUG_TABLE_STRATEGY printf("ComputeColumnWidths final\n");