Skip to content

Commit

Permalink
Bug 1484126 - part 1: Create CellData struct which implements nsITabl…
Browse files Browse the repository at this point in the history
…eEditor::GetCellDataAt() r=m_kato

nsITableEditor::GetCellDataAt() is an XPCOM method but used internally a lot.
Additionally, it scatters a lot of variables (including unused) since it takes
a lot of out arguments to return.  Therefore, this should be implemented as
struct and users should refer each member as result.

This does not replaces the callers yet since it's too risky if the caller is
not tested by automated test.  Following patches will replace the method call
and each variable step by step.

Differential Revision: https://phabricator.services.mozilla.com/D8338

--HG--
extra : moz-landing-system : lando
  • Loading branch information
masayuki-nakano committed Oct 12, 2018
1 parent e7eda01 commit 08c4b07
Show file tree
Hide file tree
Showing 4 changed files with 445 additions and 199 deletions.
189 changes: 189 additions & 0 deletions editor/libeditor/HTMLEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@ class HTMLEditor final : public TextEditor
ErrorResult& aRv) const;

struct CellAndIndexes;
struct CellData;

/**
* CellIndexes store both row index and column index of a table cell.
Expand Down Expand Up @@ -1184,6 +1185,15 @@ class HTMLEditor final : public TextEditor
void Update(HTMLEditor& aHTMLEditor, Selection& aSelection,
ErrorResult& aRv);

bool operator==(const CellIndexes& aOther) const
{
return mRow == aOther.mRow && mColumn == aOther.mColumn;
}
bool operator!=(const CellIndexes& aOther) const
{
return mRow != aOther.mRow || mColumn != aOther.mColumn;
}

private:
CellIndexes()
: mRow(-1)
Expand All @@ -1192,6 +1202,7 @@ class HTMLEditor final : public TextEditor
}

friend struct CellAndIndexes;
friend struct CellData;
};

struct MOZ_STACK_CLASS CellAndIndexes final
Expand Down Expand Up @@ -1221,6 +1232,184 @@ class HTMLEditor final : public TextEditor
ErrorResult& aRv);
};

struct MOZ_STACK_CLASS CellData final
{
RefPtr<Element> mElement;
// Current indexes which this is initialized with.
CellIndexes mCurrent;
// First column/row indexes of the cell. When current position is spanned
// from other column/row, this value becomes different from mCurrent.
CellIndexes mFirst;
// Computed rowspan/colspan values which are specified to the cell.
// Note that if the cell has larger rowspan/colspan value than actual
// table size, these values are the larger values.
int32_t mRowSpan;
int32_t mColSpan;
// Effective rowspan/colspan value at the index. For example, if first
// cell element in first row has rowspan="3", then, if this is initialized
// with 0-0 indexes, effective rowspan is 3. However, if this is
// initialized with 1-0 indexes, effective rowspan is 2.
int32_t mEffectiveRowSpan;
int32_t mEffectiveColSpan;
// mIsSelected is set to true if mElement itself or its parent <tr> or
// <table> is selected. Otherwise, e.g., the cell just contains selection
// range, this is set to false.
bool mIsSelected;

/**
* Those constructors initializes the members with a <table> element and
* both row and column index to specify a cell element.
*/
CellData(HTMLEditor& aHTMLEditor,
Element& aTableElement,
int32_t aRowIndex,
int32_t aColumnIndex,
ErrorResult& aRv)
{
Update(aHTMLEditor, aTableElement, aRowIndex, aColumnIndex, aRv);
}

CellData(HTMLEditor& aHTMLEditor,
Element& aTableElement,
const CellIndexes& aIndexes,
ErrorResult& aRv)
{
Update(aHTMLEditor, aTableElement, aIndexes, aRv);
}

/**
* Those Update() methods updates the members with a <table> element and
* both row and column index to specify a cell element.
*/
void Update(HTMLEditor& aHTMLEditor,
Element& aTableElement,
int32_t aRowIndex,
int32_t aColumnIndex,
ErrorResult& aRv)
{
mCurrent.mRow = aRowIndex;
mCurrent.mColumn = aColumnIndex;
Update(aHTMLEditor, aTableElement, aRv);
}

void Update(HTMLEditor& aHTMLEditor,
Element& aTableElement,
const CellIndexes& aIndexes,
ErrorResult& aRv)
{
mCurrent = aIndexes;
Update(aHTMLEditor, aTableElement, aRv);
}

void Update(HTMLEditor& aHTMLEditor,
Element& aTableElement,
ErrorResult& aRv);

/**
* FailedOrNotFound() returns true if this failed to initialize/update
* or succeeded but found no cell element.
*/
bool FailedOrNotFound() const { return !mElement; }

/**
* IsSpannedFromOtherRowOrColumn(), IsSpannedFromOtherColumn and
* IsSpannedFromOtherRow() return true if there is no cell element
* at the index because of spanning from other row and/or column.
*/
bool IsSpannedFromOtherRowOrColumn() const
{
return mElement && mCurrent != mFirst;
}
bool IsSpannedFromOtherColumn() const
{
return mElement && mCurrent.mColumn != mFirst.mColumn;
}
bool IsSpannedFromOtherRow() const
{
return mElement && mCurrent.mRow != mFirst.mRow;
}

/**
* NextColumnIndex() and NextRowIndex() return column/row index of
* next cell. Note that this does not check whether there is next
* cell or not actually.
*/
int32_t NextColumnIndex() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mColumn + mEffectiveColSpan;
}
int32_t NextRowIndex() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mRow + mEffectiveRowSpan;
}

/**
* LastColumnIndex() and LastRowIndex() return column/row index of
* column/row which is spanned by the cell.
*/
int32_t LastColumnIndex() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return NextColumnIndex() - 1;
}
int32_t LastRowIndex() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return NextRowIndex() - 1;
}

/**
* NumberOfPrecedingColmuns() and NumberOfPrecedingRows() return number of
* preceding columns/rows if current index is spanned from other column/row.
* Otherwise, i.e., current point is not spanned form other column/row,
* returns 0.
*/
int32_t NumberOfPrecedingColmuns() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mColumn - mFirst.mColumn;
}
int32_t NumberOfPrecedingRows() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mRow - mFirst.mRow;
}

/**
* NumberOfFollowingColumns() and NumberOfFollowingRows() return
* number of remaining columns/rows if the cell spans to other
* column/row.
*/
int32_t NumberOfFollowingColumns() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mEffectiveColSpan - 1;
}
int32_t NumberOfFollowingRows() const
{
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mEffectiveRowSpan - 1;
}
};

/**
* TableSize stores and computes number of rows and columns of a <table>
* element.
Expand Down
121 changes: 79 additions & 42 deletions editor/libeditor/HTMLTableEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3358,43 +3358,40 @@ HTMLEditor::TableSize::Update(HTMLEditor& aHTMLEditor,
}

NS_IMETHODIMP
HTMLEditor::GetCellDataAt(Element* aTable,
HTMLEditor::GetCellDataAt(Element* aTableElement,
int32_t aRowIndex,
int32_t aColIndex,
Element** aCell,
int32_t aColumnIndex,
Element** aCellElement,
int32_t* aStartRowIndex,
int32_t* aStartColIndex,
int32_t* aStartColumnIndex,
int32_t* aRowSpan,
int32_t* aColSpan,
int32_t* aActualRowSpan,
int32_t* aActualColSpan,
int32_t* aEffectiveRowSpan,
int32_t* aEffectiveColSpan,
bool* aIsSelected)
{
NS_ENSURE_ARG_POINTER(aStartRowIndex);
NS_ENSURE_ARG_POINTER(aStartColIndex);
NS_ENSURE_ARG_POINTER(aRowSpan);
NS_ENSURE_ARG_POINTER(aColSpan);
NS_ENSURE_ARG_POINTER(aActualRowSpan);
NS_ENSURE_ARG_POINTER(aActualColSpan);
NS_ENSURE_ARG_POINTER(aIsSelected);
NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
if (NS_WARN_IF(!aCellElement) ||
NS_WARN_IF(!aStartRowIndex) || NS_WARN_IF(!aStartColumnIndex) ||
NS_WARN_IF(!aRowSpan) || NS_WARN_IF(!aColSpan) ||
NS_WARN_IF(!aEffectiveRowSpan) || NS_WARN_IF(!aEffectiveColSpan) ||
NS_WARN_IF(!aIsSelected)) {
return NS_ERROR_INVALID_ARG;
}

*aStartRowIndex = 0;
*aStartColIndex = 0;
*aStartColumnIndex = 0;
*aRowSpan = 0;
*aColSpan = 0;
*aActualRowSpan = 0;
*aActualColSpan = 0;
*aEffectiveRowSpan = 0;
*aEffectiveColSpan = 0;
*aIsSelected = false;
*aCellElement = nullptr;

*aCell = nullptr;

// needs to live while we use aTable
// XXX Really? 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<Element> table;
if (!aTable) {
// Let's keep the table element with strong pointer since editor developers
// may not handle layout code of <table>, however, this method depends on
// them.
RefPtr<Element> table = aTableElement;
if (!table) {
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
Expand All @@ -3405,29 +3402,69 @@ HTMLEditor::GetCellDataAt(Element* aTable,
if (NS_WARN_IF(!table)) {
return NS_ERROR_FAILURE;
}
aTable = table;
}

nsTableWrapperFrame* tableFrame = HTMLEditor::GetTableFrame(aTable);
NS_ENSURE_TRUE(tableFrame, NS_ERROR_FAILURE);

nsTableCellFrame* cellFrame =
tableFrame->GetCellFrameAt(aRowIndex, aColIndex);
if (NS_WARN_IF(!cellFrame)) {
IgnoredErrorResult ignoredError;
CellData cellData(*this, *table, aRowIndex, aColumnIndex, ignoredError);
if (NS_WARN_IF(cellData.FailedOrNotFound())) {
return NS_ERROR_FAILURE;
}
cellData.mElement.forget(aCellElement);
*aIsSelected = cellData.mIsSelected;
*aStartRowIndex = cellData.mFirst.mRow;
*aStartColumnIndex = cellData.mFirst.mColumn;
*aRowSpan = cellData.mRowSpan;
*aColSpan = cellData.mColSpan;
*aEffectiveRowSpan = cellData.mEffectiveRowSpan;
*aEffectiveColSpan = cellData.mEffectiveColSpan;
return NS_OK;
}

void
HTMLEditor::CellData::Update(HTMLEditor& aHTMLEditor,
Element& aTableElement,
ErrorResult& aRv)
{
MOZ_ASSERT(!aRv.Failed());

mElement = nullptr;
mIsSelected = false;
mFirst.mRow = -1;
mFirst.mColumn = -1;
mRowSpan = -1;
mColSpan = -1;
mEffectiveRowSpan = -1;
mEffectiveColSpan = -1;

nsTableWrapperFrame* tableFrame = HTMLEditor::GetTableFrame(&aTableElement);
if (NS_WARN_IF(!tableFrame)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}

*aIsSelected = cellFrame->IsSelected();
*aStartRowIndex = cellFrame->RowIndex();
*aStartColIndex = cellFrame->ColIndex();
*aRowSpan = cellFrame->GetRowSpan();
*aColSpan = cellFrame->GetColSpan();
*aActualRowSpan = tableFrame->GetEffectiveRowSpanAt(aRowIndex, aColIndex);
*aActualColSpan = tableFrame->GetEffectiveColSpanAt(aRowIndex, aColIndex);
RefPtr<Element> domCell = cellFrame->GetContent()->AsElement();
domCell.forget(aCell);
// If there is no cell at the indexes. Don't return error.
// XXX If we have pending layout and that causes the cell frame hasn't been
// created, we should return error, but how can we do it?
nsTableCellFrame* cellFrame =
tableFrame->GetCellFrameAt(mCurrent.mRow, mCurrent.mColumn);
if (!cellFrame) {
return;
}

return NS_OK;
mElement = cellFrame->GetContent()->AsElement();
if (NS_WARN_IF(!mElement)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
mIsSelected = cellFrame->IsSelected();
mFirst.mRow = cellFrame->RowIndex();
mFirst.mColumn = cellFrame->ColIndex();
mRowSpan = cellFrame->GetRowSpan();
mColSpan = cellFrame->GetColSpan();
mEffectiveRowSpan =
tableFrame->GetEffectiveRowSpanAt(mCurrent.mRow, mCurrent.mColumn);
mEffectiveColSpan =
tableFrame->GetEffectiveColSpanAt(mCurrent.mRow, mCurrent.mColumn);
}

NS_IMETHODIMP
Expand Down
Loading

0 comments on commit 08c4b07

Please sign in to comment.