Bug 367673, Handle width-computation arithmetic with nscoord_MAX. r=roc sr=roc a1.9=roc

This commit is contained in:
dholbert@cs.stanford.edu 2007-09-24 10:30:42 -07:00
Родитель d839a460f6
Коммит 5e31bd00b0
4 изменённых файлов: 158 добавлений и 23 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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

Просмотреть файл

@ -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");