diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 63792cf0f07f..b0f957940147 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -3018,7 +3018,8 @@ HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - GetNextSelectedCell(nullptr, getter_AddRefs(cellElement)); + cellElement = + GetNextSelectedTableCellElement(*selection, ignoredError); } return NS_OK; } @@ -3027,7 +3028,8 @@ HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor) if (NS_FAILED(rv)) { return rv; } - GetNextSelectedCell(nullptr, getter_AddRefs(cellElement)); + cellElement = + GetNextSelectedTableCellElement(*selection, ignoredError); } return NS_OK; } diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index c13fb874b6ee..dadb26c6823c 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -537,8 +537,9 @@ protected: // May be called by friends. * a cell element, this returns nullptr. And even if 2nd or later * range of Selection selects a cell element, also returns nullptr. * Note that when this looks for a cell element, this resets the internal - * index of ranges of Selection. When you call GetNextSelectedCell() after - * a call of this, it'll return 2nd selected cell if there is. + * index of ranges of Selection. When you call + * GetNextSelectedTableCellElement() after a call of this, it'll return 2nd + * selected cell if there is. * * @param aSelection Selection for this editor. * @param aRv Returns error if there is no selection or @@ -552,6 +553,32 @@ protected: // May be called by friends. GetFirstSelectedTableCellElement(Selection& aSelection, ErrorResult& aRv) const; + /** + * GetNextSelectedTableCellElement() is a stateful method to retrieve + * selected table cell elements which are selected by 2nd or later ranges + * of Selection. When you call GetFirstSelectedTableCellElement(), it + * resets internal counter of this method. Then, following calls of + * GetNextSelectedTableCellElement() scans the remaining ranges of Selection. + * If a range selects a or element, returns the cell element. + * If a range selects an element but neither nor element, this + * ignores the range. If a range is in a text node, returns null without + * throwing exception, but stops scanning the remaining ranges even you + * call this again. + * Note that this may cross boundaries since this method just + * scans all ranges of Selection. Therefore, returning cells which + * belong to different
elements. + * + * @param Selection Selection for this editor. + * @param aRv Returns error if Selection doesn't have + * range properly. + * @return A
or element if one of remaining + * ranges selects a or element unless + * this does not meet a range in a text node. + */ + already_AddRefed + GetNextSelectedTableCellElement(Selection& aSelection, + ErrorResult& aRv) const; + void IsNextCharInNodeWhitespace(nsIContent* aContent, int32_t aOffset, bool* outIsSpace, @@ -1878,8 +1905,9 @@ protected: UniquePtr mCSSEditUtils; // mSelectedCellIndex is reset by GetFirstSelectedTableCellElement(), - // then, it'll be referred and incremented by GetNextSelectedCell(). - mutable int32_t mSelectedCellIndex; + // then, it'll be referred and incremented by + // GetNextSelectedTableCellElement(). + mutable uint32_t mSelectedCellIndex; nsString mLastStyleSheetURL; nsString mLastOverrideStyleSheetURL; diff --git a/editor/libeditor/HTMLTableEditor.cpp b/editor/libeditor/HTMLTableEditor.cpp index e325335e6799..ea51cd05845d 100644 --- a/editor/libeditor/HTMLTableEditor.cpp +++ b/editor/libeditor/HTMLTableEditor.cpp @@ -812,8 +812,10 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // to continue after we delete this row int32_t nextRow = startRowIndex; while (nextRow == startRowIndex) { - rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); - NS_ENSURE_SUCCESS(rv, rv); + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } if (!cell) { break; } @@ -850,8 +852,10 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) // to continue after we delete this column int32_t nextCol = startColIndex; while (nextCol == startColIndex) { - rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); - NS_ENSURE_SUCCESS(rv, rv); + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } if (!cell) { break; } @@ -875,8 +879,11 @@ HTMLEditor::DeleteTableCell(int32_t aNumber) } if (!deleteCol) { // First get the next cell to delete - RefPtr nextCell; - rv = GetNextSelectedCell(nullptr, getter_AddRefs(nextCell)); + RefPtr nextCell = + GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } NS_ENSURE_SUCCESS(rv, rv); // Then delete the cell @@ -1005,9 +1012,9 @@ HTMLEditor::DeleteTableCellContents() DeleteCellContents(cell); if (firstSelectedCellElement) { // We doing a selected cells, so do all of them - nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); } } else { cell = nullptr; @@ -1109,8 +1116,10 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber) // to continue after we delete this column int32_t nextCol = startColIndex; while (nextCol == startColIndex) { - rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); - NS_ENSURE_SUCCESS(rv, rv); + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } if (!cell) { break; } @@ -1299,8 +1308,10 @@ HTMLEditor::DeleteTableRow(int32_t aNumber) // to continue after we delete this row int32_t nextRow = startRowIndex; while (nextRow == startRowIndex) { - nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell)); - NS_ENSURE_SUCCESS(rv, rv); + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } if (!cell) { break; } @@ -1566,12 +1577,16 @@ HTMLEditor::SelectBlockOfCells(Element* aStartCell, currentCellIndexes.mColumn > maxColumn) { selection->RemoveRange(*range, IgnoreErrors()); // Since we've removed the range, decrement pointer to next range + MOZ_ASSERT(mSelectedCellIndex > 0); mSelectedCellIndex--; } - nsresult rv = - GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + cell = GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + if (cell) { + MOZ_ASSERT(mSelectedCellIndex > 0); + range = selection->GetRangeAt(mSelectedCellIndex - 1); } } @@ -2174,17 +2189,25 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) // is retained after joining. This leaves the target cell selected // as well as the "non-contiguous" cells, so user can see what happened. + RefPtr selection = GetSelection(); + if (NS_WARN_IF(!selection)) { + return NS_ERROR_FAILURE; + } + RefPtr firstCell; int32_t firstRowIndex, firstColIndex; rv = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex, getter_AddRefs(firstCell)); NS_ENSURE_SUCCESS(rv, rv); + ErrorResult error; bool joinSelectedCells = false; if (firstCell) { - RefPtr secondCell; - rv = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell)); - NS_ENSURE_SUCCESS(rv, rv); + RefPtr secondCell = + GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } // If only one cell is selected, join with cell to the right joinSelectedCells = (secondCell != nullptr); @@ -2193,7 +2216,6 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) if (joinSelectedCells) { // We have selected cells: Join just contiguous cells // and just merge contents if not contiguous - ErrorResult error; TableSize tableSize(*this, *table, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); @@ -3353,62 +3375,92 @@ HTMLEditor::GetFirstSelectedTableCellElement(Selection& aSelection, return nullptr; } - // Setup for GetNextSelectedCell() - // XXX Oh, increment it now? Rather than when GetNextSelectedCell() is - // called? + // Setup for GetNextSelectedTableCellElement() + // XXX Oh, increment it now? Rather than when + // GetNextSelectedTableCellElement() is called? mSelectedCellIndex = 1; return selectedCell.forget(); } NS_IMETHODIMP -HTMLEditor::GetNextSelectedCell(nsRange** aRange, - Element** aCell) +HTMLEditor::GetNextSelectedCell(nsRange** aNextSelectedCellRange, + Element** aNextSelectedCellElement) { - NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER); - *aCell = nullptr; - if (aRange) { - *aRange = nullptr; + if (NS_WARN_IF(!aNextSelectedCellElement)) { + return NS_ERROR_INVALID_ARG; + } + + *aNextSelectedCellElement = nullptr; + if (aNextSelectedCellRange) { + *aNextSelectedCellRange = nullptr; } RefPtr selection = GetSelection(); - NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); - - int32_t rangeCount = selection->RangeCount(); - - // Don't even try if index exceeds range count - if (mSelectedCellIndex >= rangeCount) { - return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND; + if (NS_WARN_IF(!selection)) { + return NS_ERROR_FAILURE; } - // Scan through ranges to find next valid selected cell - RefPtr range; - for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) { - range = selection->GetRangeAt(mSelectedCellIndex); - NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); + ErrorResult error; + RefPtr nextSelectedCellElement = + GetNextSelectedTableCellElement(*selection, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } - nsresult rv = HTMLEditor::GetCellFromRange(range, aCell); - // Failure here means the range doesn't contain a cell - NS_ENSURE_SUCCESS(rv, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND); + if (!nextSelectedCellElement) { + // not more range, or met a range which does not select nor . + return NS_OK; + } - // We found a selected cell - if (*aCell) { - break; + if (aNextSelectedCellRange) { + MOZ_ASSERT(mSelectedCellIndex > 0); + *aNextSelectedCellRange = + do_AddRef(selection->GetRangeAt(mSelectedCellIndex - 1)).take(); + } + nextSelectedCellElement.forget(aNextSelectedCellElement); + return NS_OK; +} + +already_AddRefed +HTMLEditor::GetNextSelectedTableCellElement(Selection& aSelection, + ErrorResult& aRv) const +{ + MOZ_ASSERT(!aRv.Failed()); + + if (mSelectedCellIndex >= aSelection.RangeCount()) { + // We've already returned all selected cells. + return nullptr; + } + + MOZ_ASSERT(mSelectedCellIndex > 0); + for (; mSelectedCellIndex < aSelection.RangeCount(); mSelectedCellIndex++) { + nsRange* range = aSelection.GetRangeAt(mSelectedCellIndex); + if (NS_WARN_IF(!range)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - // If we didn't find a cell, continue to next range in selection - } - // No cell means all remaining ranges were collapsed (cells were deleted) - NS_ENSURE_TRUE(*aCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND); + RefPtr nextSelectedCellElement; + nsresult rv = + HTMLEditor::GetCellFromRange(range, + getter_AddRefs(nextSelectedCellElement)); + if (NS_FAILED(rv)) { + // Failure means that the range is in non-element node, e.g., a text node. + // Returns nullptr without error if not found. + // XXX Why don't we just skip such range or incrementing + // mSelectedCellIndex for next call? + return nullptr; + } - if (aRange) { - range.forget(aRange); + if (nextSelectedCellElement) { + mSelectedCellIndex++; + return nextSelectedCellElement.forget(); + } } - // Setup for next cell - mSelectedCellIndex++; - - return NS_OK; + // Returns nullptr without error if not found. + return nullptr; } NS_IMETHODIMP @@ -3667,6 +3719,7 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, bool allCellsInRowAreSelected = false; bool allCellsInColAreSelected = false; + IgnoredErrorResult ignoredError; while (selectedCell) { CellIndexes selectedCellIndexes(*selectedCell, error); if (NS_WARN_IF(error.Failed())) { @@ -3682,9 +3735,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, break; } } - DebugOnly rv = - GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + selectedCell = GetNextSelectedTableCellElement(*selection, ignoredError); + NS_WARNING_ASSERTION(!ignoredError.Failed(), "Failed to get next selected table cell element"); } @@ -3698,7 +3750,6 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, indexArray.Clear(); // Start at first cell again - IgnoredErrorResult ignoredError; selectedCell = GetFirstSelectedTableCellElement(*selection, ignoredError); while (selectedCell) { CellIndexes selectedCellIndexes(*selectedCell, error); @@ -3716,9 +3767,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement, break; } } - DebugOnly rv = - GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell)); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + selectedCell = GetNextSelectedTableCellElement(*selection, ignoredError); + NS_WARNING_ASSERTION(!ignoredError.Failed(), "Failed to get next selected table cell element"); } if (allCellsInColAreSelected) { diff --git a/editor/nsITableEditor.idl b/editor/nsITableEditor.idl index 89c6d40da119..677593d596ee 100644 --- a/editor/nsITableEditor.idl +++ b/editor/nsITableEditor.idl @@ -336,18 +336,27 @@ interface nsITableEditor : nsISupports */ Element getFirstSelectedCellInTable(out long aRowIndex, out long aColIndex); - /** Get next selected cell element from first selection range. - * Assumes cell-selection model where each cell - * is in a separate range (selection parent node is table row) - * Always call GetFirstSelectedCell() to initialize stored index of "next" cell - * @param aCell Selected cell or null if no more selected cells - * or ranges don't contain cell selections - * @param aRange Optional: if not null, return the selection range - * associated with the cell - * - * Returns the DOM cell element - * (in C++: returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found - * passes NS_SUCCEEDED macro) - */ - Element getNextSelectedCell(out Range aRange); + /** + * getNextSelectedCell() is a stateful method to retrieve selected table + * cell elements which are selected by 2nd or later ranges of Selection. + * When you call getFirstSelectedCell(), it resets internal counter of + * this method. Then, following calls of getNextSelectedCell() scans the + * remaining ranges of Selection. If a range selects a or + * element, returns the cell element. If a range selects an element but + * neither nor element, this ignores the range. If a range is + * in a text node, returns null without throwing exception, but stops + * scanning the remaining ranges even you call this again. + * Note that this may cross boundaries since this method just + * scans all ranges of Selection. Therefore, returning cells which + * belong to different
elements. + * + * @param aNextSelectedCellRange [OUT] Returns null if this method returns + * null. Otherwise, i.e., found a range which + * selects a
or element, returns the + * range. + * @return A or element if one of remaining + * ranges selects a or element unless + * this does not meet a range in a text node. + */ + Element getNextSelectedCell(out Range aNextSelectedCellRange); };