From 41eb258e25564a5b4c897beee7861a203faf321d Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Thu, 23 Aug 2018 07:32:16 +0000 Subject: [PATCH] Bug 1484125 - part 1: Create TableSize struct to compute and store number of rows and columns of a element r=m_kato HTMLEditor::GetTableSize() is an XPCOM method but used internally a lot. Therefore, it shouldn't be called for internal use. Additionally, the callers need to declare two int32_t variables, but this causes the code messy. Therefore, this patch creates TableSize struct and it implements HTMLEditor::GetTableSize(). Then, all callers of it is replaced with TableSize struct. New TableSize struct does not support computes
element from anchor of Selection since there is no user of this in C++ code. Differential Revision: https://phabricator.services.mozilla.com/D3953 --HG-- extra : moz-landing-system : lando --- editor/libeditor/HTMLEditor.h | 39 +++ editor/libeditor/HTMLTableEditor.cpp | 475 +++++++++++++++++---------- editor/nsITableEditor.idl | 31 +- 3 files changed, 363 insertions(+), 182 deletions(-) diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index f555729a24e18..d2688df5306b5 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -1065,6 +1065,45 @@ class HTMLEditor final : public TextEditor ErrorResult& aRv); }; + /** + * TableSize stores and computes number of rows and columns of a
+ * element. + */ + struct MOZ_STACK_CLASS TableSize final + { + int32_t mRowCount; + int32_t mColumnCount; + + /** + * @param aHTMLEditor The editor which creates the instance. + * @param aTableOrElementInTable If a
element, computes number + * of rows and columns of it. + * If another element in a
element, + * computes number of rows and columns + * of nearest ancestor
element. + * Otherwise, i.e., non-
element + * not in
, returns error. + * @param aRv Returns error if the element is not + * in
or layout information is + * not available. + */ + TableSize(HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable, + ErrorResult& aRv) + : mRowCount(-1) + , mColumnCount(-1) + { + MOZ_ASSERT(!aRv.Failed()); + Update(aHTMLEditor, aTableOrElementInTable, aRv); + } + + /** + * Update mRowCount and mColumnCount for aTableOrElementInTable. + * See above for the detail. + */ + void Update(HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable, + ErrorResult& aRv); + }; + /** * PasteInternal() pasts text with replacing selected content. * This tries to dispatch ePaste event first. If its defaultPrevent() is diff --git a/editor/libeditor/HTMLTableEditor.cpp b/editor/libeditor/HTMLTableEditor.cpp index fc8a0b0ee239f..6d07a37cbfdc7 100644 --- a/editor/libeditor/HTMLTableEditor.cpp +++ b/editor/libeditor/HTMLTableEditor.cpp @@ -410,9 +410,11 @@ HTMLEditor::InsertTableColumn(int32_t aNumber, } } - int32_t rowCount, colCount, rowIndex; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } //We reset caret in destructor... AutoSelectionSetterAfterTableEdit setCaret(*this, table, startRowIndex, @@ -424,14 +426,13 @@ HTMLEditor::InsertTableColumn(int32_t aNumber, // If we are inserting after all existing columns // Make sure table is "well formed" // before appending new column - if (startColIndex >= colCount) { + if (startColIndex >= tableSize.mColumnCount) { NormalizeTable(table); } - ErrorResult error; RefPtr rowElement; - for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { - if (startColIndex < colCount) { + for (int32_t rowIndex = 0; rowIndex < tableSize.mRowCount; rowIndex++) { + if (startColIndex < tableSize.mColumnCount) { // We are inserting before an existing column rv = GetCellDataAt(table, rowIndex, startColIndex, getter_AddRefs(curCell), @@ -469,7 +470,7 @@ HTMLEditor::InsertTableColumn(int32_t aNumber, } else { if (NS_WARN_IF(!rowElement)) { // XXX Looks like that when rowIndex is 0, startColIndex is always - // same as or larger than colCount. Is it true? + // same as or larger than tableSize.mColumnCount. Is it true? return NS_ERROR_FAILURE; } rowElement = GetNextTableRowElement(*rowElement, error); @@ -531,9 +532,11 @@ HTMLEditor::InsertTableRow(int32_t aNumber, NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE); - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } AutoPlaceholderBatch beginBatching(this); // Prevent auto insertion of BR in new cell until we're done @@ -563,7 +566,7 @@ HTMLEditor::InsertTableRow(int32_t aNumber, RefPtr cellForRowParent; int32_t cellsInRow = 0; - if (startRowIndex < rowCount) { + if (startRowIndex < tableSize.mRowCount) { // We are inserting above an existing row // Get each cell in the insert row to adjust for COLSPAN effects while we // count how many cells are needed @@ -603,12 +606,12 @@ HTMLEditor::InsertTableRow(int32_t aNumber, } else { // We are adding a new row after all others // If it weren't for colspan=0 effect, - // we could simply use colCount for number of new cells... + // we could simply use tableSize.mColumnCount for number of new cells... // XXX colspan=0 support has now been removed in table layout so maybe this can be cleaned up now? (bug 1243183) - cellsInRow = colCount; + cellsInRow = tableSize.mColumnCount; // ...but we must compensate for all cells with rowSpan = 0 in the last row - int32_t lastRow = rowCount-1; + int32_t lastRow = tableSize.mRowCount - 1; int32_t tempColIndex = 0; while (NS_SUCCEEDED(GetCellDataAt(table, lastRow, tempColIndex, getter_AddRefs(curCell), @@ -647,7 +650,7 @@ HTMLEditor::InsertTableRow(int32_t aNumber, int32_t newRowOffset = parentOfRow->ComputeIndexOf(parentRow); // Adjust for when adding past the end - if (aAfter && startRowIndex >= rowCount) { + if (aAfter && startRowIndex >= tableSize.mRowCount) { newRowOffset++; } @@ -666,10 +669,9 @@ HTMLEditor::InsertTableRow(int32_t aNumber, // Don't use transaction system yet! (not until entire row is // inserted) - ErrorResult result; - newRow->AppendChild(*newCell, result); - if (NS_WARN_IF(result.Failed())) { - return result.StealNSResult(); + newRow->AppendChild(*newCell, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); } } @@ -748,9 +750,13 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) nullptr, nullptr, &startRowIndex, &startColIndex); - NS_ENSURE_SUCCESS(rv, rv); - // Don't fail if we didn't find a table or cell - NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!table) || NS_WARN_IF(!cell)) { + // Don't fail if we didn't find a table or cell. + return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND; + } AutoPlaceholderBatch beginBatching(this); // Prevent rules testing until we're done @@ -764,11 +770,12 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // When 2 or more cells are selected, ignore aNumber and use selected cells. if (firstCell && selection->RangeCount() > 1) { - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); - ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + CellIndexes firstCellIndexes(*firstCell, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); @@ -795,7 +802,8 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // Clear so we don't repeat AllCellsInRowSelected within the same row checkToDeleteRow = false; - deleteRow = AllCellsInRowSelected(table, startRowIndex, colCount); + deleteRow = + AllCellsInRowSelected(table, startRowIndex, tableSize.mColumnCount); if (deleteRow) { // First, find the next cell in a different row // to continue after we delete this row @@ -831,7 +839,9 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // Clear this so we don't repeat AllCellsInColSelected within the same Col checkToDeleteColumn = false; - deleteCol = AllCellsInColumnSelected(table, startColIndex, colCount); + deleteCol = + AllCellsInColumnSelected(table, startColIndex, + tableSize.mColumnCount); if (deleteCol) { // First, find the next cell in a different column // to continue after we delete this column @@ -886,6 +896,7 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) } } } else { + ErrorResult error; for (int32_t i = 0; i < aNumber; i++) { rv = GetCellContext(getter_AddRefs(selection), getter_AddRefs(table), @@ -908,11 +919,12 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // We should delete the row instead, // but first check if its the only row left // so we can delete the entire table - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } - if (rowCount == 1) { + if (tableSize.mRowCount == 1) { return DeleteTable2(table, selection); } @@ -1021,7 +1033,7 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber) RefPtr selection; RefPtr table; RefPtr cell; - int32_t startRowIndex, startColIndex, rowCount, colCount; + int32_t startRowIndex, startColIndex; nsresult rv = GetCellContext(getter_AddRefs(selection), getter_AddRefs(table), getter_AddRefs(cell), @@ -1031,18 +1043,21 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber) // Don't fail if no cell found NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND); - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } AutoPlaceholderBatch beginBatching(this); // Shortcut the case of deleting all columns in table - if (!startColIndex && aNumber >= colCount) { + if (!startColIndex && aNumber >= tableSize.mColumnCount) { return DeleteTable2(table, selection); } // Check for counts too high - aNumber = std::min(aNumber,(colCount-startColIndex)); + aNumber = std::min(aNumber, (tableSize.mColumnCount - startColIndex)); // Prevent rules testing until we're done AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction( @@ -1056,7 +1071,6 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber) uint32_t rangeCount = selection->RangeCount(); - ErrorResult error; if (firstCell && rangeCount > 1) { CellIndexes firstCellIndexes(*firstCell, error); if (NS_WARN_IF(error.Failed())) { @@ -1115,13 +1129,16 @@ nsresult HTMLEditor::DeleteColumn(Element* aTable, int32_t aColIndex) { - NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); + if (NS_WARN_IF(!aTable)) { + return NS_ERROR_INVALID_ARG; + } RefPtr cell; int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; bool isSelected; int32_t rowIndex = 0; + ErrorResult error; do { nsresult rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), @@ -1160,11 +1177,12 @@ HTMLEditor::DeleteColumn(Element* aTable, // But first check if its the only row left // so we can delete the entire table // (This should never happen but it's the safe thing to do) - int32_t rowCount, colCount; - rv = GetTableSize(aTable, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + TableSize tableSize(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } - if (rowCount == 1) { + if (tableSize.mRowCount == 1) { RefPtr selection = GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); return DeleteTable2(aTable, selection); @@ -1202,21 +1220,27 @@ HTMLEditor::DeleteTableRow(int32_t aNumber) RefPtr table; RefPtr cell; int32_t startRowIndex, startColIndex; - int32_t rowCount, colCount; nsresult rv = GetCellContext(getter_AddRefs(selection), getter_AddRefs(table), getter_AddRefs(cell), nullptr, nullptr, &startRowIndex, &startColIndex); - NS_ENSURE_SUCCESS(rv, rv); - // Don't fail if no cell found - NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!table) || NS_WARN_IF(!cell)) { + // Don't fail if no cell found. + return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND; + } - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Shortcut the case of deleting all rows in table - if (!startRowIndex && aNumber >= rowCount) { + if (!startRowIndex && aNumber >= tableSize.mRowCount) { return DeleteTable2(table, selection); } @@ -1231,8 +1255,6 @@ HTMLEditor::DeleteTableRow(int32_t aNumber) NS_ENSURE_SUCCESS(rv, rv); uint32_t rangeCount = selection->RangeCount(); - - ErrorResult error; if (firstCell && rangeCount > 1) { // Fetch indexes again - may be different for selected cells CellIndexes firstCellIndexes(*firstCell, error); @@ -1285,7 +1307,7 @@ HTMLEditor::DeleteTableRow(int32_t aNumber) } } else { // Check for counts too high - aNumber = std::min(aNumber,(rowCount-startRowIndex)); + aNumber = std::min(aNumber, (tableSize.mRowCount - startRowIndex)); for (int32_t i = 0; i < aNumber; i++) { rv = DeleteRow(table, startRowIndex); // If failed in current row, try the next @@ -1309,7 +1331,9 @@ nsresult HTMLEditor::DeleteRow(Element* aTable, int32_t aRowIndex) { - NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); + if (NS_WARN_IF(!aTable)) { + return NS_ERROR_INVALID_ARG; + } RefPtr cell; RefPtr cellInDeleteRow; @@ -1327,21 +1351,25 @@ HTMLEditor::DeleteRow(Element* aTable, nsTArray > spanCellList; nsTArray newSpanList; - int32_t rowCount, colCount; - nsresult rv = GetTableSize(aTable, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Scan through cells in row to do rowspan adjustments // Note that after we delete row, startRowIndex will point to the // cells in the next row to be deleted do { - if (aRowIndex >= rowCount || colIndex >= colCount) { + if (aRowIndex >= tableSize.mRowCount || + colIndex >= tableSize.mColumnCount) { break; } - rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + nsresult rv = + GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); // We don't fail if we don't find a cell, so this must be real bad if (NS_FAILED(rv)) { return rv; @@ -1391,7 +1419,7 @@ HTMLEditor::DeleteRow(Element* aTable, RefPtr parentRow = GetElementOrParentByTagNameInternal(*nsGkAtoms::tr, *cellInDeleteRow); if (parentRow) { - rv = DeleteNodeWithTransaction(*parentRow); + nsresult rv = DeleteNodeWithTransaction(*parentRow); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1401,8 +1429,10 @@ HTMLEditor::DeleteRow(Element* aTable, for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) { Element* cellPtr = spanCellList[i]; if (cellPtr) { - rv = SetRowSpan(cellPtr, newSpanList[i]); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = SetRowSpan(cellPtr, newSpanList[i]); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } } return NS_OK; @@ -1582,9 +1612,11 @@ HTMLEditor::SelectAllTableCells() return NS_ERROR_FAILURE; } - int32_t rowCount, colCount; - nsresult rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Suppress nsISelectionListener notification // until all selection changes are finished @@ -1592,14 +1624,16 @@ HTMLEditor::SelectAllTableCells() // It is now safe to clear the selection // BE SURE TO RESET IT BEFORE LEAVING! - rv = ClearSelection(); + nsresult rv = ClearSelection(); // Select all cells in the same column as current cell bool cellSelected = false; int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; bool isSelected; - for (int32_t row = 0; row < rowCount; row++) { - for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) { + for (int32_t row = 0; row < tableSize.mRowCount; row++) { + for (int32_t col = 0; + col < tableSize.mColumnCount; + col += std::max(actualColSpan, 1)) { rv = GetCellDataAt(table, row, col, getter_AddRefs(cell), ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, @@ -1652,12 +1686,18 @@ HTMLEditor::SelectTableRow() getter_AddRefs(cell), nullptr, nullptr, &startRowIndex, &startColIndex); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!table)) { + return NS_ERROR_FAILURE; + } - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } //Note: At this point, we could get first and last cells in row, // then call SelectBlockOfCells, but that would take just @@ -1675,7 +1715,9 @@ HTMLEditor::SelectTableRow() bool cellSelected = false; int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; bool isSelected; - for (int32_t col = 0; col < colCount; col += std::max(actualColSpan, 1)) { + for (int32_t col = 0; + col < tableSize.mColumnCount; + col += std::max(actualColSpan, 1)) { rv = GetCellDataAt(table, startRowIndex, col, getter_AddRefs(cell), ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, &actualRowSpan, &actualColSpan, &isSelected); @@ -1726,12 +1768,18 @@ HTMLEditor::SelectTableColumn() getter_AddRefs(cell), nullptr, nullptr, &startRowIndex, &startColIndex); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!table)) { + return NS_ERROR_FAILURE; + } - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Suppress nsISelectionListener notification // until all selection changes are finished @@ -1745,7 +1793,9 @@ HTMLEditor::SelectTableColumn() bool cellSelected = false; int32_t rowSpan, colSpan, actualRowSpan, actualColSpan, currentRowIndex, currentColIndex; bool isSelected; - for (int32_t row = 0; row < rowCount; row += std::max(actualRowSpan, 1)) { + for (int32_t row = 0; + row < tableSize.mRowCount; + row += std::max(actualRowSpan, 1)) { rv = GetCellDataAt(table, row, startColIndex, getter_AddRefs(cell), ¤tRowIndex, ¤tColIndex, &rowSpan, &colSpan, &actualRowSpan, &actualColSpan, &isSelected); @@ -1918,8 +1968,13 @@ HTMLEditor::SplitCellIntoRows(Element* aTable, int32_t aRowSpanBelow, Element** aNewCell) { - NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); - if (aNewCell) *aNewCell = nullptr; + if (NS_WARN_IF(!aTable)) { + return NS_ERROR_INVALID_ARG; + } + + if (aNewCell) { + *aNewCell = nullptr; + } RefPtr cell; int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -1929,17 +1984,23 @@ HTMLEditor::SplitCellIntoRows(Element* aTable, &startRowIndex, &startColIndex, &rowSpan, &colSpan, &actualRowSpan, &actualColSpan, &isSelected); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!cell)) { + return NS_ERROR_FAILURE; + } // We can't split! if (actualRowSpan <= 1 || (aRowSpanAbove + aRowSpanBelow) > actualRowSpan) { return NS_OK; } - int32_t rowCount, colCount; - rv = GetTableSize(aTable, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } RefPtr cell2; RefPtr lastCellFound; @@ -1989,7 +2050,7 @@ HTMLEditor::SplitCellIntoRows(Element* aTable, colIndex += std::max(actualColSpan2, 1); // Done when past end of total number of columns - if (colIndex > colCount) { + if (colIndex > tableSize.mColumnCount) { break; } } @@ -2118,10 +2179,11 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) if (joinSelectedCells) { // We have selected cells: Join just contiguous cells // and just merge contents if not contiguous - - int32_t rowCount, colCount; - rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Get spans for cell we will merge into int32_t firstRowSpan, firstColSpan; @@ -2142,18 +2204,19 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) // that we will join into one cell, // favoring adjacent cells in the same row for (rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) { - int32_t currentRowCount = rowCount; + int32_t currentRowCount = tableSize.mRowCount; // Be sure each row doesn't have rowspan errors - rv = FixBadRowSpan(table, rowIndex, rowCount); + rv = FixBadRowSpan(table, rowIndex, tableSize.mRowCount); NS_ENSURE_SUCCESS(rv, rv); // Adjust rowcount by number of rows we removed - lastRowIndex -= (currentRowCount-rowCount); + lastRowIndex -= currentRowCount - tableSize.mRowCount; bool cellFoundInRow = false; bool lastRowIsSet = false; int32_t lastColInRow = 0; int32_t firstColInRow = firstColIndex; - for (colIndex = firstColIndex; colIndex < colCount; + for (colIndex = firstColIndex; + colIndex < tableSize.mColumnCount; colIndex += std::max(actualColSpan2, 1)) { rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), &startRowIndex2, &startColIndex2, @@ -2225,8 +2288,9 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) nsTArray> deleteList; // 2nd pass: Do the joining and merging - for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { - for (colIndex = 0; colIndex < colCount; + for (rowIndex = 0; rowIndex < tableSize.mRowCount; rowIndex++) { + for (colIndex = 0; + colIndex < tableSize.mColumnCount; colIndex += std::max(actualColSpan2, 1)) { rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell2), &startRowIndex2, &startColIndex2, @@ -2458,11 +2522,15 @@ HTMLEditor::FixBadRowSpan(Element* aTable, int32_t aRowIndex, int32_t& aNewRowCount) { - NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); + if (NS_WARN_IF(!aTable)) { + return NS_ERROR_INVALID_ARG; + } - int32_t rowCount, colCount; - nsresult rv = GetTableSize(aTable, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } RefPtr cell; int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -2471,11 +2539,13 @@ HTMLEditor::FixBadRowSpan(Element* aTable, int32_t minRowSpan = -1; int32_t colIndex; - for (colIndex = 0; colIndex < colCount; + for (colIndex = 0; + colIndex < tableSize.mColumnCount; colIndex += std::max(actualColSpan, 1)) { - rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + nsresult rv = + GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); // NOTE: This is a *real* failure. // GetCellDataAt passes if cell is missing from cellmap if (NS_FAILED(rv)) { @@ -2495,11 +2565,13 @@ HTMLEditor::FixBadRowSpan(Element* aTable, // The amount to reduce everyone's rowspan // so at least one cell has rowspan = 1 int32_t rowsReduced = minRowSpan - 1; - for (colIndex = 0; colIndex < colCount; + for (colIndex = 0; + colIndex < tableSize.mColumnCount; colIndex += std::max(actualColSpan, 1)) { - rv = GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + nsresult rv = + GetCellDataAt(aTable, aRowIndex, colIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); if (NS_FAILED(rv)) { return rv; } @@ -2515,7 +2587,12 @@ HTMLEditor::FixBadRowSpan(Element* aTable, NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in FixBadRowSpan"); } } - return GetTableSize(aTable, &aNewRowCount, &colCount); + tableSize.Update(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + aNewRowCount = tableSize.mRowCount; + return NS_OK; } nsresult @@ -2523,11 +2600,15 @@ HTMLEditor::FixBadColSpan(Element* aTable, int32_t aColIndex, int32_t& aNewColCount) { - NS_ENSURE_TRUE(aTable, NS_ERROR_NULL_POINTER); + if (NS_WARN_IF(!aTable)) { + return NS_ERROR_INVALID_ARG; + } - int32_t rowCount, colCount; - nsresult rv = GetTableSize(aTable, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } RefPtr cell; int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; @@ -2536,11 +2617,13 @@ HTMLEditor::FixBadColSpan(Element* aTable, int32_t minColSpan = -1; int32_t rowIndex; - for (rowIndex = 0; rowIndex < rowCount; + for (rowIndex = 0; + rowIndex < tableSize.mRowCount; rowIndex += std::max(actualRowSpan, 1)) { - rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + nsresult rv = + GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); // NOTE: This is a *real* failure. // GetCellDataAt passes if cell is missing from cellmap if (NS_FAILED(rv)) { @@ -2560,11 +2643,13 @@ HTMLEditor::FixBadColSpan(Element* aTable, // The amount to reduce everyone's colspan // so at least one cell has colspan = 1 int32_t colsReduced = minColSpan - 1; - for (rowIndex = 0; rowIndex < rowCount; + for (rowIndex = 0; + rowIndex < tableSize.mRowCount; rowIndex += std::max(actualRowSpan, 1)) { - rv = GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + nsresult rv = + GetCellDataAt(aTable, rowIndex, aColIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); if (NS_FAILED(rv)) { return rv; } @@ -2580,7 +2665,12 @@ HTMLEditor::FixBadColSpan(Element* aTable, NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in FixBadColSpan"); } } - return GetTableSize(aTable, &rowCount, &aNewColCount); + tableSize.Update(*this, *aTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + aNewColCount = tableSize.mColumnCount; + return NS_OK; } NS_IMETHODIMP @@ -2600,9 +2690,11 @@ HTMLEditor::NormalizeTable(Element* aTable) return NS_OK; } - int32_t rowCount, colCount, rowIndex, colIndex; - nsresult rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Save current selection AutoSelectionRestorer selectionRestorer(selection, this); @@ -2618,23 +2710,28 @@ HTMLEditor::NormalizeTable(Element* aTable) bool isSelected; // Scan all cells in each row to detect bad rowspan values - for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { - rv = FixBadRowSpan(table, rowIndex, rowCount); - NS_ENSURE_SUCCESS(rv, rv); + for (int32_t rowIndex = 0; rowIndex < tableSize.mRowCount; rowIndex++) { + nsresult rv = FixBadRowSpan(table, rowIndex, tableSize.mRowCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } // and same for colspans - for (colIndex = 0; colIndex < colCount; colIndex++) { - rv = FixBadColSpan(table, colIndex, colCount); - NS_ENSURE_SUCCESS(rv, rv); + for (int32_t colIndex = 0; colIndex < tableSize.mColumnCount; colIndex++) { + nsresult rv = FixBadColSpan(table, colIndex, tableSize.mColumnCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } // Fill in missing cellmap locations with empty cells - for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { + for (int32_t rowIndex = 0; rowIndex < tableSize.mRowCount; rowIndex++) { RefPtr previousCellInRow; - for (colIndex = 0; colIndex < colCount; colIndex++) { - rv = GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell), - &startRowIndex, &startColIndex, &rowSpan, &colSpan, - &actualRowSpan, &actualColSpan, &isSelected); + for (int32_t colIndex = 0; colIndex < tableSize.mColumnCount; colIndex++) { + nsresult rv = + GetCellDataAt(table, rowIndex, colIndex, getter_AddRefs(cell), + &startRowIndex, &startColIndex, &rowSpan, &colSpan, + &actualRowSpan, &actualColSpan, &isSelected); // NOTE: This is a *real* failure. // GetCellDataAt passes if cell is missing from cellmap if (NS_FAILED(rv)) { @@ -2796,45 +2893,66 @@ HTMLEditor::GetNumberOfCellsInRow(Element* aTable, } NS_IMETHODIMP -HTMLEditor::GetTableSize(Element* aTable, +HTMLEditor::GetTableSize(Element* aTableOrElementInTable, int32_t* aRowCount, - int32_t* aColCount) + int32_t* aColumnCount) { - NS_ENSURE_ARG_POINTER(aRowCount); - NS_ENSURE_ARG_POINTER(aColCount); + if (NS_WARN_IF(!aRowCount) || NS_WARN_IF(!aColumnCount)) { + return NS_ERROR_INVALID_ARG; + } + *aRowCount = 0; - *aColCount = 0; - // Get the selected talbe or the table enclosing the selection anchor - // XXX Looks like it's safe to use raw pointer here. However, layout code - // change won't be handled by editor developers so that it must be safe - // to keep using RefPtr here. - RefPtr table; - if (aTable) { - table = GetElementOrParentByTagNameInternal(*nsGkAtoms::table, *aTable); - if (NS_WARN_IF(!table)) { - return NS_ERROR_FAILURE; - } - } else { + *aColumnCount = 0; + + Element* tableOrElementInTable = aTableOrElementInTable; + if (!tableOrElementInTable) { RefPtr selection = GetSelection(); if (NS_WARN_IF(!selection)) { return NS_ERROR_FAILURE; } - table = + tableOrElementInTable = GetElementOrParentByTagNameAtSelection(*selection, *nsGkAtoms::table); - if (NS_WARN_IF(!table)) { + if (NS_WARN_IF(!tableOrElementInTable)) { return NS_ERROR_FAILURE; } } - nsTableWrapperFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame()); - if (NS_WARN_IF(!tableFrame)) { - return NS_ERROR_FAILURE; + ErrorResult error; + TableSize tableSize(*this, *tableOrElementInTable, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); } + *aRowCount = tableSize.mRowCount; + *aColumnCount = tableSize.mColumnCount; + return NS_OK; +} - *aRowCount = tableFrame->GetRowCount(); - *aColCount = tableFrame->GetColCount(); +void +HTMLEditor::TableSize::Update(HTMLEditor& aHTMLEditor, + Element& aTableOrElementInTable, + ErrorResult& aRv) +{ + MOZ_ASSERT(!aRv.Failed()); - return NS_OK; + // Currently, nsTableWrapperFrame::GetRowCount() and + // nsTableWrapperFrame::GetColCount() are safe to use without grabbing + //
element. However, editor developers may not watch layout API + // changes. So, for keeping us safer, we should use RefPtr here. + RefPtr tableElement = + aHTMLEditor.GetElementOrParentByTagNameInternal(*nsGkAtoms::table, + aTableOrElementInTable); + if (NS_WARN_IF(!tableElement)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + nsTableWrapperFrame* tableFrame = + do_QueryFrame(tableElement->GetPrimaryFrame()); + if (NS_WARN_IF(!tableFrame)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + mRowCount = tableFrame->GetRowCount(); + mColumnCount = tableFrame->GetColCount(); } NS_IMETHODIMP @@ -3432,26 +3550,30 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, RefPtr table; if (aElement) { table = GetElementOrParentByTagNameInternal(*nsGkAtoms::table, *aElement); + if (NS_WARN_IF(!table)) { + return NS_ERROR_FAILURE; + } } else { RefPtr selection = GetSelection(); if (NS_WARN_IF(!selection)) { - // If there is no Selection, the following GetTableSize() will return - // nullptr if we set first argument to nullptr. So, let's return error - // in this case. return NS_ERROR_FAILURE; } table = GetElementOrParentByTagNameAtSelection(*selection, *nsGkAtoms::table); + if (NS_WARN_IF(!table)) { + return NS_ERROR_FAILURE; + } } - // table might be null at this point, but if so GetTableSize will fail. - int32_t rowCount, colCount; - nsresult rv = GetTableSize(table, &rowCount, &colCount); - NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; + TableSize tableSize(*this, *table, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // Traverse all selected cells RefPtr selectedCell; - rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); + nsresult rv = GetFirstSelectedCell(nullptr, getter_AddRefs(selectedCell)); NS_ENSURE_SUCCESS(rv, rv); if (rv == NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND) { return NS_OK; @@ -3463,7 +3585,6 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, // Store indexes of each row/col to avoid duplication of searches nsTArray indexArray; - ErrorResult error; bool allCellsInRowAreSelected = false; bool allCellsInColAreSelected = false; while (NS_SUCCEEDED(rv) && selectedCell) { @@ -3474,7 +3595,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, if (!indexArray.Contains(selectedCellIndexes.mColumn)) { indexArray.AppendElement(selectedCellIndexes.mColumn); allCellsInRowAreSelected = - AllCellsInRowSelected(table, selectedCellIndexes.mRow, colCount); + AllCellsInRowSelected(table, selectedCellIndexes.mRow, + tableSize.mColumnCount); // We're done as soon as we fail for any row if (!allCellsInRowAreSelected) { break; @@ -3503,7 +3625,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, if (!indexArray.Contains(selectedCellIndexes.mRow)) { indexArray.AppendElement(selectedCellIndexes.mColumn); allCellsInColAreSelected = - AllCellsInColumnSelected(table, selectedCellIndexes.mColumn, rowCount); + AllCellsInColumnSelected(table, selectedCellIndexes.mColumn, + tableSize.mRowCount); // We're done as soon as we fail for any column if (!allCellsInRowAreSelected) { break; diff --git a/editor/nsITableEditor.idl b/editor/nsITableEditor.idl index 0959e4ac6d27f..9fda8e93fde4b 100644 --- a/editor/nsITableEditor.idl +++ b/editor/nsITableEditor.idl @@ -158,12 +158,31 @@ interface nsITableEditor : nsISupports void getCellIndexes(in Element aCellElement, out long aRowIndex, out long aColumnIndex); - /** Get the number of rows and columns in a table from the layout's cellmap - * If aTable is null, it will try to find enclosing table of selection ancho - * Note that all rows in table will not have this many because of - * ROWSPAN effects or if table is not "rectangular" (has short rows) - */ - void getTableSize(in Element aTable, + /** + * getTableSize() computes number of rows and columns. + * Note that this depends on layout information. Therefore, all pending + * layout should've been flushed before calling this. + * + * @param aTableOrElementInTable If a
element, this computes number + * of rows and columns of it. + * If another element and in a
, this + * computes number of rows and columns of + * the nearest ancestor
element. + * If element is not in
element, + * throwing an exception. + * If null, this looks for nearest ancestor + *
element containing anchor of + * Selection. If found, computes the number + * of rows and columns of the
. + * Otherwise, throwing an exception. + * @param aRowCount Number of *actual* row count. + * I.e., rowspan does NOT increase this value. + * @param aColumnCount Number of column count. + * I.e., if colspan is specified with bigger + * number than actual, the value is used + * as this. + */ + void getTableSize(in Element aTableOrElementInTable, out long aRowCount, out long aColCount); /** Get a cell element at cellmap grid coordinates