diff --git a/mozilla/layout/reftests/bugs/363329-1-ref.html b/mozilla/layout/reftests/bugs/363329-1-ref.html
new file mode 100644
index 00000000000..81339b059df
--- /dev/null
+++ b/mozilla/layout/reftests/bugs/363329-1-ref.html
@@ -0,0 +1,13 @@
+
+
+ Testcase for bug 363329
+
+
+
+
+ | colspan 3 |
+ | colspan 2 | x |
+
+
+
+
diff --git a/mozilla/layout/reftests/bugs/363329-1.html b/mozilla/layout/reftests/bugs/363329-1.html
new file mode 100644
index 00000000000..3ddb2e5f481
--- /dev/null
+++ b/mozilla/layout/reftests/bugs/363329-1.html
@@ -0,0 +1,13 @@
+
+
+ Testcase for bug 363329
+
+
+
+
+ | colspan 3 |
+ | colspan 2 | x |
+
+
+
+
diff --git a/mozilla/layout/reftests/bugs/363329-2-ref.html b/mozilla/layout/reftests/bugs/363329-2-ref.html
new file mode 100644
index 00000000000..9c34fb17353
--- /dev/null
+++ b/mozilla/layout/reftests/bugs/363329-2-ref.html
@@ -0,0 +1,13 @@
+
+
+ Testcase for bug 363329
+
+
+
+
+
+
+
diff --git a/mozilla/layout/reftests/bugs/363329-2.html b/mozilla/layout/reftests/bugs/363329-2.html
new file mode 100644
index 00000000000..611b2d7c6c0
--- /dev/null
+++ b/mozilla/layout/reftests/bugs/363329-2.html
@@ -0,0 +1,13 @@
+
+
+ Testcase for bug 363329
+
+
+
+
+
+
+
diff --git a/mozilla/layout/reftests/reftest.list b/mozilla/layout/reftests/reftest.list
index 43e51e70fda..e0b75a26536 100644
--- a/mozilla/layout/reftests/reftest.list
+++ b/mozilla/layout/reftests/reftest.list
@@ -28,6 +28,8 @@ f== bugs/360065-1.html bugs/360065-1-ref.html # bug 18217
!= bugs/315620-1b.html bugs/315620-1-ref.html
== bugs/315620-2a.xhtml bugs/315620-2-ref.xhtml
!= bugs/315620-2b.xhtml bugs/315620-2-ref.xhtml
+#== bugs/363329-1.html bugs/363329-1-ref.html # may fail randomly (bug 366865)
+== bugs/363329-2.html bugs/363329-2-ref.html
== bugs/363637-1.html bugs/363637-1-ref.html
== bugs/364079-1.html bugs/364079-1-ref.html
== bugs/364861-1.html bugs/364861-1-ref.html
diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp
index ecad44675ff..52a617bbbb9 100644
--- a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp
+++ b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp
@@ -46,6 +46,7 @@
#include "nsTableCellFrame.h"
#include "nsLayoutUtils.h"
#include "nsGkAtoms.h"
+#include "SpanningCellSorter.h"
#undef DEBUG_TABLE_STRATEGY
@@ -221,10 +222,12 @@ void
BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRenderingContext)
{
nsTableFrame *tableFrame = mTableFrame;
- float p2t = tableFrame->GetPresContext()->ScaledPixelsToTwips();
+ nsPresContext *presContext = tableFrame->GetPresContext();
+ float p2t = presContext->ScaledPixelsToTwips();
nsTableCellMap *cellMap = tableFrame->GetCellMap();
nscoord spacing = tableFrame->GetCellSpacingX();
+ SpanningCellSorter spanningCells(presContext->PresShell());
// Loop over the columns to consider the columns and cells *without*
// a colspan.
@@ -272,8 +275,13 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
PRInt32 colSpan;
nsTableCellFrame *cellFrame =
cellMap->GetCellInfoAt(row, col, &originates, &colSpan);
- if (!cellFrame || !originates || colSpan > 1)
+ if (!cellFrame || !originates) {
continue;
+ }
+ if (colSpan > 1) {
+ spanningCells.AddCell(colSpan, row, col);
+ continue;
+ }
CellWidthInfo info = GetCellWidthInfo(aRenderingContext, cellFrame);
@@ -290,26 +298,38 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
- // Loop over the columns to consider cells *with* a colspan.
- // We consider these cells by seeing if they require adding to the
- // widths as they were when we considered only non-spanning cells.
- // We then accumulate the *additions* to the non-spanning values in
- // the column frame's Span* members. Considering things only
- // relative to the widths resulting from the non-spanning cells
- // (rather than incrementally including the results from spanning
- // cells, or doing spanning and non-spanning cells in a single pass)
- // means that layout remains row-order-invariant.
- for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
- for (PRInt32 row = 0, row_end = cellMap->GetRowCount();
- row < row_end; ++row) {
- PRBool originates;
- PRInt32 colSpan;
- nsTableCellFrame *cellFrame =
- cellMap->GetCellInfoAt(row, col, &originates, &colSpan);
- if (!cellFrame || !originates || colSpan == 1)
- continue;
-
- NS_ASSERTION(colSpan > 1, "bad colspan");
+ // Consider the cells with a colspan that we saved in the loop above
+ // into the spanning cell sorter. We consider these cells by seeing
+ // if they require adding to the widths resulting only from cells
+ // with a smaller colspan, and therefore we must process them sorted
+ // in increasing order by colspan. For each colspan group, we
+ // accumulate the *additions* to the prior values in the column
+ // frame's Span* members, since this makes the distribution process
+ // simpler.
+ //
+ // Considering things only relative to the widths resulting from
+ // cells with smaller colspans (rather than incrementally including
+ // the results from spanning cells, or doing spanning and
+ // non-spanning cells in a single pass) means that layout remains
+ // row-order-invariant and (except for percentage widths that add to
+ // more than 100%) column-order invariant.
+ //
+ // Starting with smaller colspans makes it more likely that we
+ // satisfy all the constraints given and don't distribute space to
+ // columns where we don't need it.
+ SpanningCellSorter::Item *item;
+ PRInt32 colSpan;
+ while ((item = spanningCells.GetNext(&colSpan))) {
+ NS_ASSERTION(colSpan > 1,
+ "cell should not have been put in spanning cell sorter");
+ do {
+ PRInt32 row = item->row;
+ col = item->col;
+ CellData *cellData = cellMap->GetDataAt(row, col);
+ nsTableCellFrame *cellFrame = cellData->GetCellFrame();
+ NS_ASSERTION(cellData && cellData->IsOrig(),
+ "bogus result from spanning cell sorter");
+ NS_ASSERTION(cellFrame, "bogus result from spanning cell sorter");
CellWidthInfo info = GetCellWidthInfo(aRenderingContext, cellFrame);
@@ -325,11 +345,9 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
NS_ERROR("column frames out of sync with cell map");
continue;
}
- if (!mTableFrame->GetNumCellsOriginatingInCol(scol)) {
- continue;
- }
- if (scol != col) {
+ if (mTableFrame->GetNumCellsOriginatingInCol(scol) &&
+ scol != col) {
info.minCoord -= spacing;
info.prefCoord -= spacing;
}
@@ -394,14 +412,41 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
scolFrame->AddSpanPrefCoord(NSToCoordRound(
float(info.prefCoord) * coordRatio));
}
+ } while ((item = item->next));
+
+ // Combine the results of the span analysis into the main results,
+ // for each increment of colspan.
+
+ for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
+ nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
+ if (!colFrame) {
+ NS_ERROR("column frames out of sync with cell map");
+ continue;
+ }
+
+ // Since PrefCoord is really a shorthand for two values (XXX
+ // this isn't really a space savings since we have to store
+ // mHasSpecifiedCoord; we should probably just store the values
+ // since it's less confusing) and calling AddMinCoord can
+ // influence the result of GetPrefCoord, save the value as it
+ // was during the loop over spanning cells before messing with
+ // anything.
+ nscoord prefCoord = colFrame->GetPrefCoord();
+ colFrame->AddMinCoord(colFrame->GetMinCoord() +
+ colFrame->GetSpanMinCoord());
+ colFrame->AddPrefCoord(prefCoord +
+ PR_MAX(colFrame->GetSpanMinCoord(),
+ colFrame->GetSpanPrefCoord()),
+ colFrame->GetHasSpecifiedCoord());
+ NS_ASSERTION(colFrame->GetMinCoord() <= colFrame->GetPrefCoord(),
+ "min larger than pref");
+ colFrame->AddPrefPercent(colFrame->GetSpanPrefPercent());
+
+ colFrame->ResetSpanMinCoord();
+ colFrame->ResetSpanPrefCoord();
+ colFrame->ResetSpanPrefPercent();
}
}
-#ifdef DEBUG_TABLE_STRATEGY
- printf("ComputeColumnIntrinsicWidths span incr.\n");
- mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
-#endif
-
- // Combine the results of the span analysis into the main results.
// Prevent percentages from adding to more than 100% by (to be
// compatible with other browsers) treating any percentages that would
@@ -416,28 +461,11 @@ BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRen
continue;
}
- // Since PrefCoord is really a shorthand for two values (XXX
- // this isn't really a space savings since we have to store
- // mHasSpecifiedCoord; we should probably just store the values
- // since it's less confusing) and calling AddMinCoord can
- // influence the result of GetPrefCoord, save the value as it
- // was during the loop over spanning cells before messing with
- // anything.
- nscoord prefCoord = colFrame->GetPrefCoord();
- colFrame->AddMinCoord(colFrame->GetMinCoord() +
- colFrame->GetSpanMinCoord());
- colFrame->AddPrefCoord(prefCoord +
- PR_MAX(colFrame->GetSpanMinCoord(),
- colFrame->GetSpanPrefCoord()),
- colFrame->GetHasSpecifiedCoord());
- NS_ASSERTION(colFrame->GetMinCoord() <= colFrame->GetPrefCoord(),
- "min larger than pref");
- colFrame->AddPrefPercent(colFrame->GetSpanPrefPercent());
-
colFrame->AdjustPrefPercent(&pct_used);
}
+
#ifdef DEBUG_TABLE_STRATEGY
- printf("ComputeColumnIntrinsicWidths adjust\n");
+ printf("ComputeColumnIntrinsicWidths spanning\n");
mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
}
diff --git a/mozilla/layout/tables/Makefile.in b/mozilla/layout/tables/Makefile.in
index 770ee855901..ec97c65cadc 100644
--- a/mozilla/layout/tables/Makefile.in
+++ b/mozilla/layout/tables/Makefile.in
@@ -71,6 +71,7 @@ EXPORTS = \
CPPSRCS = \
BasicTableLayoutStrategy.cpp \
FixedTableLayoutStrategy.cpp \
+ SpanningCellSorter.cpp \
nsCellMap.cpp \
nsTableCellFrame.cpp \
nsTableColFrame.cpp \
diff --git a/mozilla/layout/tables/SpanningCellSorter.cpp b/mozilla/layout/tables/SpanningCellSorter.cpp
new file mode 100644
index 00000000000..a0ab9aed04e
--- /dev/null
+++ b/mozilla/layout/tables/SpanningCellSorter.cpp
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla's table layout code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * L. David Baron (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Code to sort cells by their colspan, used by BasicTableLayoutStrategy.
+ */
+
+#include "SpanningCellSorter.h"
+#include "nsQuickSort.h"
+
+//#define DEBUG_SPANNING_CELL_SORTER
+
+SpanningCellSorter::SpanningCellSorter(nsIPresShell *aPresShell)
+ : mPresShell(aPresShell)
+ , mState(ADDING)
+ , mSortedHashTable(nsnull)
+{
+ memset(mArray, 0, sizeof(mArray));
+ mHashTable.entryCount = 0;
+ mPresShell->PushStackMemory();
+}
+
+SpanningCellSorter::~SpanningCellSorter()
+{
+ if (mHashTable.entryCount) {
+ PL_DHashTableFinish(&mHashTable);
+ mHashTable.entryCount = 0;
+ }
+ delete mSortedHashTable;
+ mPresShell->PopStackMemory();
+}
+
+/* static */ PLDHashTableOps
+SpanningCellSorter::HashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ HashTableGetKey,
+ HashTableHashKey,
+ HashTableMatchEntry,
+ PL_DHashMoveEntryStub,
+ PL_DHashClearEntryStub,
+ PL_DHashFinalizeStub,
+ nsnull
+};
+
+/* static */ PR_CALLBACK const void*
+SpanningCellSorter::HashTableGetKey(PLDHashTable *table,
+ PLDHashEntryHdr *hdr)
+{
+ HashTableEntry *entry = NS_STATIC_CAST(HashTableEntry*, hdr);
+ return NS_INT32_TO_PTR(entry->mColSpan);
+}
+
+/* static */ PR_CALLBACK PLDHashNumber
+SpanningCellSorter::HashTableHashKey(PLDHashTable *table, const void *key)
+{
+ return NS_PTR_TO_INT32(key);
+}
+
+/* static */ PR_CALLBACK PRBool
+SpanningCellSorter::HashTableMatchEntry(PLDHashTable *table,
+ const PLDHashEntryHdr *hdr,
+ const void *key)
+{
+ const HashTableEntry *entry = NS_STATIC_CAST(const HashTableEntry*, hdr);
+ return NS_PTR_TO_INT32(key) == entry->mColSpan;
+}
+
+PRBool
+SpanningCellSorter::AddCell(PRInt32 aColSpan, PRInt32 aRow, PRInt32 aCol)
+{
+ NS_ASSERTION(mState == ADDING, "cannot call AddCell after GetNext");
+ NS_ASSERTION(aColSpan >= ARRAY_BASE, "cannot add cells with colspan<2");
+
+ Item *i;
+ nsresult rv = mPresShell->AllocateStackMemory(sizeof(Item), (void**)&i);
+ NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+ i->row = aRow;
+ i->col = aCol;
+
+ if (UseArrayForSpan(aColSpan)) {
+ PRInt32 index = SpanToIndex(aColSpan);
+ i->next = mArray[index];
+ mArray[index] = i;
+ } else {
+ if (!mHashTable.entryCount &&
+ !PL_DHashTableInit(&mHashTable, &HashTableOps, nsnull,
+ sizeof(HashTableEntry), PL_DHASH_MIN_SIZE)) {
+ NS_NOTREACHED("table init failed");
+ mHashTable.entryCount = 0;
+ return PR_FALSE;
+ }
+ HashTableEntry *entry = NS_STATIC_CAST(HashTableEntry*,
+ PL_DHashTableOperate(&mHashTable, NS_INT32_TO_PTR(aColSpan),
+ PL_DHASH_ADD));
+ NS_ENSURE_TRUE(entry, PR_FALSE);
+
+ NS_ASSERTION(entry->mColSpan == 0 || entry->mColSpan == aColSpan,
+ "wrong entry");
+ NS_ASSERTION((entry->mColSpan == 0) == (entry->mItems == nsnull),
+ "entry should be either new or properly initialized");
+ entry->mColSpan = aColSpan;
+
+ i->next = entry->mItems;
+ entry->mItems = i;
+ }
+
+ return PR_TRUE;
+}
+
+/* static */ PR_CALLBACK PLDHashOperator
+SpanningCellSorter::FillSortedArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
+ PRUint32 number, void *arg)
+{
+ HashTableEntry *entry = NS_STATIC_CAST(HashTableEntry*, hdr);
+ HashTableEntry **sh = NS_STATIC_CAST(HashTableEntry**, arg);
+
+ sh[number] = entry;
+
+ return PL_DHASH_NEXT;
+}
+
+/* static */ int
+SpanningCellSorter::SortArray(const void *a, const void *b, void *closure)
+{
+ PRInt32 spanA = (*NS_STATIC_CAST(HashTableEntry*const*, a))->mColSpan;
+ PRInt32 spanB = (*NS_STATIC_CAST(HashTableEntry*const*, b))->mColSpan;
+
+ if (spanA < spanB)
+ return -1;
+ if (spanA == spanB)
+ return 0;
+ return 1;
+}
+
+SpanningCellSorter::Item*
+SpanningCellSorter::GetNext(PRInt32 *aColSpan)
+{
+ NS_ASSERTION(mState != DONE, "done enumerating, stop calling");
+
+ switch (mState) {
+ case ADDING:
+ /* prepare to enumerate the array */
+ mState = ENUMERATING_ARRAY;
+ mEnumerationIndex = 0;
+ /* fall through */
+ case ENUMERATING_ARRAY:
+ while (mEnumerationIndex < ARRAY_SIZE && !mArray[mEnumerationIndex])
+ ++mEnumerationIndex;
+ if (mEnumerationIndex < ARRAY_SIZE) {
+ Item *result = mArray[mEnumerationIndex];
+ *aColSpan = IndexToSpan(mEnumerationIndex);
+ NS_ASSERTION(result, "logic error");
+#ifdef DEBUG_SPANNING_CELL_SORTER
+ printf("SpanningCellSorter[%p]:"
+ " returning list for colspan=%d from array\n",
+ NS_STATIC_CAST(void*, this), *aColSpan);
+#endif
+ ++mEnumerationIndex;
+ return result;
+ }
+ /* prepare to enumerate the hash */
+ mState = ENUMERATING_HASH;
+ mEnumerationIndex = 0;
+ if (mHashTable.entryCount) {
+ HashTableEntry **sh =
+ new HashTableEntry*[mHashTable.entryCount];
+ if (!sh) {
+ // give up
+ mState = DONE;
+ return nsnull;
+ }
+ PL_DHashTableEnumerate(&mHashTable, FillSortedArray, sh);
+ NS_QuickSort(sh, mHashTable.entryCount, sizeof(sh[0]),
+ SortArray, nsnull);
+ mSortedHashTable = sh;
+ }
+ /* fall through */
+ case ENUMERATING_HASH:
+ if (mEnumerationIndex < mHashTable.entryCount) {
+ Item *result = mSortedHashTable[mEnumerationIndex]->mItems;
+ *aColSpan = mSortedHashTable[mEnumerationIndex]->mColSpan;
+ NS_ASSERTION(result, "holes in hash table");
+#ifdef DEBUG_SPANNING_CELL_SORTER
+ printf("SpanningCellSorter[%p]:"
+ " returning list for colspan=%d from hash\n",
+ NS_STATIC_CAST(void*, this), *aColSpan);
+#endif
+ ++mEnumerationIndex;
+ return result;
+ }
+ mState = DONE;
+ /* fall through */
+ case DONE:
+ ;
+ }
+ return nsnull;
+}
diff --git a/mozilla/layout/tables/SpanningCellSorter.h b/mozilla/layout/tables/SpanningCellSorter.h
new file mode 100644
index 00000000000..05c4f2f19ca
--- /dev/null
+++ b/mozilla/layout/tables/SpanningCellSorter.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+// vim:cindent:ts=4:et:sw=4:
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla's table layout code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * L. David Baron (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Code to sort cells by their colspan, used by BasicTableLayoutStrategy.
+ */
+
+#include "nsIPresShell.h"
+#include "pldhash.h"
+
+/**
+ * The SpanningCellSorter is responsible for accumulating lists of cells
+ * with colspans so that those cells can later be enumerated, sorted
+ * from lowest number of columns spanned to highest. It does not use a
+ * stable sort (in fact, it currently reverses).
+ */
+class SpanningCellSorter {
+public:
+ SpanningCellSorter(nsIPresShell *aPresShell);
+ ~SpanningCellSorter();
+
+ struct Item {
+ PRInt32 row, col;
+ Item *next;
+ };
+
+ /**
+ * Add a cell to the sorter. Returns false on out of memory.
+ * aColSpan is the number of columns spanned, and aRow/aCol are the
+ * position of the cell in the table (for GetCellInfoAt).
+ */
+ PRBool AddCell(PRInt32 aColSpan, PRInt32 aRow, PRInt32 aCol);
+
+ /**
+ * Get the next *list* of cells. Each list contains all the cells
+ * for a colspan value, and the lists are given in order from lowest
+ * to highest colspan. The colspan value is filled in to *aColSpan.
+ */
+ Item* GetNext(PRInt32 *aColSpan);
+private:
+ nsIPresShell *mPresShell;
+
+ enum State { ADDING, ENUMERATING_ARRAY, ENUMERATING_HASH, DONE };
+ State mState;
+
+ // store small colspans in an array for fast sorting and
+ // enumeration, and large colspans in a hash table
+
+ enum { ARRAY_BASE = 2 };
+ enum { ARRAY_SIZE = 8 };
+ Item *mArray[ARRAY_SIZE];
+ PRInt32 SpanToIndex(PRInt32 aSpan) { return aSpan - ARRAY_BASE; }
+ PRInt32 IndexToSpan(PRInt32 aIndex) { return aIndex + ARRAY_BASE; }
+ PRBool UseArrayForSpan(PRInt32 aSpan) {
+ NS_ASSERTION(SpanToIndex(aSpan) >= 0, "cell without colspan");
+ return SpanToIndex(aSpan) < ARRAY_SIZE;
+ }
+
+ PLDHashTable mHashTable;
+ struct HashTableEntry : public PLDHashEntryHdr {
+ PRInt32 mColSpan;
+ Item *mItems;
+ };
+
+ static PLDHashTableOps HashTableOps;
+
+ PR_STATIC_CALLBACK(const void*)
+ HashTableGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr);
+ PR_STATIC_CALLBACK(PLDHashNumber)
+ HashTableHashKey(PLDHashTable *table, const void *key);
+ PR_STATIC_CALLBACK(PRBool)
+ HashTableMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
+ const void *key);
+
+ PR_STATIC_CALLBACK(PLDHashOperator)
+ FillSortedArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
+ PRUint32 number, void *arg);
+
+ static int SortArray(const void *a, const void *b, void *closure);
+
+ /* state used only during enumeration */
+ PRUint32 mEnumerationIndex; // into mArray or mSortedHashTable
+ HashTableEntry **mSortedHashTable;
+
+ /*
+ * operator new is forbidden since we use the pres shell's stack
+ * memory, which much be pushed and popped at points matching a
+ * push/pop on the C++ stack.
+ */
+ void* operator new(size_t sz) CPP_THROW_NEW { return nsnull; };
+};
+