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 2x
+ + + 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 2x
+ + + 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 + + + + + + +
x
x
+ + + 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 + + + + + + +
x
x
+ + + 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; }; +}; +