Bug 1627175 - part 5: Get rid of `EditorBase::GetChildOffset()` and `EditorBase::GetNodeLocation()` r=m_kato

Their users should use `EditorDOMPoint` or something instead.

This patch cleans up `EditorBase::DoJoinNodes()` too because of their heavy
user.  It requires only joined nodes because `aParent` is used only for
removing `aContentToJoin`, but we now have `nsINode::Remove()` which does
not require parent node and never fails.

Depends on D70877

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2020-04-16 01:21:17 +00:00
Родитель fb0662edda
Коммит aeeb323f57
5 изменённых файлов: 94 добавлений и 136 удалений

Просмотреть файл

@ -1642,8 +1642,7 @@ already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
NS_WARN_IF(!aStartOfRightNode.GetContainerAsContent())) {
if (NS_WARN_IF(!aStartOfRightNode.IsInContentNode())) {
aError.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}
@ -3542,23 +3541,22 @@ void EditorBase::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
}
}
nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
nsINode* aParent) {
nsresult EditorBase::DoJoinNodes(nsIContent& aContentToKeep,
nsIContent& aContentToJoin) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_DIAGNOSTIC_ASSERT(AsHTMLEditor());
MOZ_ASSERT(aNodeToKeep);
MOZ_ASSERT(aNodeToJoin);
MOZ_ASSERT(aParent);
uint32_t firstNodeLength = aContentToJoin.Length();
uint32_t firstNodeLength = aNodeToJoin->Length();
int32_t joinOffset;
GetNodeLocation(aNodeToJoin, &joinOffset);
int32_t keepOffset;
nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
EditorRawDOMPoint atNodeToJoin(&aContentToJoin);
EditorRawDOMPoint atNodeToKeep(&aContentToKeep);
// Remember all selection points.
// XXX Do we need to restore all types of selections by ourselves? Normal
// selection should be modified later as result of handling edit action.
// IME selections shouldn't be there when nodes are joined. Spellcheck
// selections should be recreated with newer text. URL selections
// shouldn't be there because of used only by the URL bar.
AutoTArray<SavedRange, 10> savedRanges;
for (SelectionType selectionType : kPresentSelectionTypes) {
SavedRange range;
@ -3584,15 +3582,17 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
// in the one that is going away instead. This simplifies later selection
// adjustment logic at end of this method.
if (range.mStartContainer) {
if (range.mStartContainer == parent &&
joinOffset < range.mStartOffset &&
range.mStartOffset <= keepOffset) {
range.mStartContainer = aNodeToJoin;
if (range.mStartContainer == atNodeToKeep.GetContainer() &&
atNodeToJoin.Offset() < static_cast<uint32_t>(range.mStartOffset) &&
static_cast<uint32_t>(range.mStartOffset) <=
atNodeToKeep.Offset()) {
range.mStartContainer = &aContentToJoin;
range.mStartOffset = firstNodeLength;
}
if (range.mEndContainer == parent && joinOffset < range.mEndOffset &&
range.mEndOffset <= keepOffset) {
range.mEndContainer = aNodeToJoin;
if (range.mEndContainer == atNodeToKeep.GetContainer() &&
atNodeToJoin.Offset() < static_cast<uint32_t>(range.mEndOffset) &&
static_cast<uint32_t>(range.mEndOffset) <= atNodeToKeep.Offset()) {
range.mEndContainer = &aContentToJoin;
range.mEndOffset = firstNodeLength;
}
}
@ -3603,35 +3603,41 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
// OK, ready to do join now.
// If it's a text node, just shuffle around some text.
if (aNodeToKeep->IsText() && aNodeToJoin->IsText()) {
if (aContentToKeep.IsText() && aContentToJoin.IsText()) {
nsAutoString rightText;
nsAutoString leftText;
aNodeToKeep->GetAsText()->GetData(rightText);
aNodeToJoin->GetAsText()->GetData(leftText);
aContentToKeep.AsText()->GetData(rightText);
aContentToJoin.AsText()->GetData(leftText);
leftText += rightText;
// XXX This call may destroy us.
IgnoredErrorResult ignoredError;
DoSetText(MOZ_KnownLive(*aNodeToKeep->GetAsText()), leftText, ignoredError);
DoSetText(MOZ_KnownLive(*aContentToKeep.AsText()), leftText, ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"EditorBase::DoSetText() failed, but ignored");
} else {
// Otherwise it's an interior node, so shuffle around the children.
nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
nsCOMPtr<nsINodeList> childNodes = aContentToJoin.ChildNodes();
MOZ_ASSERT(childNodes);
// Remember the first child in aNodeToKeep, we'll insert all the children of
// aNodeToJoin in front of it GetFirstChild returns nullptr firstNode if
// aNodeToKeep has no children, that's OK.
nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
// Remember the first child in aContentToKeep, we'll insert all the children
// of aContentToJoin in front of it GetFirstChild returns nullptr firstNode
// if aContentToKeep has no children, that's OK.
nsCOMPtr<nsIContent> firstNode = aContentToKeep.GetFirstChild();
// Have to go through the list backwards to keep deletes from interfering
// with iteration.
for (uint32_t i = childNodes->Length(); i; --i) {
nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
if (childNode) {
// prepend children of aNodeToJoin
// prepend children of aContentToJoin
ErrorResult error;
aNodeToKeep->InsertBefore(*childNode, firstNode, error);
aContentToKeep.InsertBefore(*childNode, firstNode, error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (error.Failed()) {
NS_WARNING("nsINode::InsertBefore() failed");
return error.StealNSResult();
@ -3642,9 +3648,10 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
}
// Delete the extra node.
ErrorResult error;
aParent->RemoveChild(*aNodeToJoin, error);
NS_WARNING_ASSERTION(!error.Failed(), "nsINode::RemoveChild() failed");
aContentToJoin.Remove();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
bool allowedTransactionsToChangeSelection =
AllowsTransactionsToChangeSelection();
@ -3658,6 +3665,10 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
if (range.mSelection != previousSelection) {
ErrorResult error;
range.mSelection->RemoveAllRanges(error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (error.Failed()) {
NS_WARNING("Selection::RemoveAllRanges() failed");
return error.StealNSResult();
@ -3673,16 +3684,16 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
}
// Check to see if we joined nodes where selection starts.
if (range.mStartContainer == aNodeToJoin) {
range.mStartContainer = aNodeToKeep;
} else if (range.mStartContainer == aNodeToKeep) {
if (range.mStartContainer == &aContentToJoin) {
range.mStartContainer = &aContentToKeep;
} else if (range.mStartContainer == &aContentToKeep) {
range.mStartOffset += firstNodeLength;
}
// Check to see if we joined nodes where selection ends.
if (range.mEndContainer == aNodeToJoin) {
range.mEndContainer = aNodeToKeep;
} else if (range.mEndContainer == aNodeToKeep) {
if (range.mEndContainer == &aContentToJoin) {
range.mEndContainer = &aContentToKeep;
} else if (range.mEndContainer == &aContentToKeep) {
range.mEndOffset += firstNodeLength;
}
@ -3700,6 +3711,10 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
// static analyzer.
MOZ_KnownLive(range.mSelection)
->AddRangeAndSelectFramesAndNotifyListeners(*newRange, error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
@ -3708,53 +3723,15 @@ nsresult EditorBase::DoJoinNodes(nsINode* aNodeToKeep, nsINode* aNodeToJoin,
if (allowedTransactionsToChangeSelection) {
// Editor wants us to set selection at join point.
DebugOnly<nsresult> rvIgnored = SelectionRefPtr()->Collapse(
aNodeToKeep, AssertedCast<int32_t>(firstNodeLength));
&aContentToKeep, AssertedCast<int32_t>(firstNodeLength));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Selection::Collapse() failed, but ignored");
}
return error.StealNSResult();
}
// static
int32_t EditorBase::GetChildOffset(nsINode* aChild, nsINode* aParent) {
MOZ_ASSERT(aChild);
MOZ_ASSERT(aParent);
// nsINode::ComputeIndexOf() is expensive. So, if we can return index
// without calling it, we should do that.
// If there is no previous siblings, it means that it's the first child.
if (aParent->GetFirstChild() == aChild) {
MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == 0);
return 0;
}
// If there is no next siblings, it means that it's the last child.
if (aParent->GetLastChild() == aChild) {
int32_t lastChildIndex = static_cast<int32_t>(aParent->Length() - 1);
MOZ_ASSERT(aParent->ComputeIndexOf(aChild) == lastChildIndex);
return lastChildIndex;
}
int32_t index = aParent->ComputeIndexOf(aChild);
MOZ_ASSERT(index != -1);
return index;
}
// static
nsINode* EditorBase::GetNodeLocation(nsINode* aChild, int32_t* aOffset) {
MOZ_ASSERT(aChild);
MOZ_ASSERT(aOffset);
nsINode* parent = aChild->GetParentNode();
if (parent) {
*aOffset = GetChildOffset(aChild, parent);
MOZ_ASSERT(*aOffset != -1);
} else {
*aOffset = -1;
}
return parent;
return NS_OK;
}
nsIContent* EditorBase::GetPreviousNodeInternal(nsINode& aNode,
@ -4806,19 +4783,18 @@ nsresult EditorBase::CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
nsresult EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aNode)) {
if (NS_WARN_IF(!aNode) && NS_WARN_IF(!aNode->IsContent())) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
if (NS_WARN_IF(!parentNode)) {
EditorRawDOMPoint atContent(aNode->AsContent());
if (NS_WARN_IF(!atContent.IsSet())) {
return NS_ERROR_FAILURE;
}
int32_t offset = GetChildOffset(aNode, parentNode);
RefPtr<nsRange> range;
nsresult rv = CreateRange(parentNode, offset, parentNode, offset + 1,
nsresult rv = CreateRange(atContent.GetContainer(), atContent.Offset(),
atContent.GetContainer(), atContent.Offset() + 1,
getter_AddRefs(range));
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::CreateRange() failed");

Просмотреть файл

@ -1865,20 +1865,18 @@ class EditorBase : public nsIEditor,
ErrorResult& aError);
/**
* DoJoinNodes() merges contents in aNodeToJoin to aNodeToKeep and remove
* aNodeToJoin from the DOM tree. aNodeToJoin and aNodeToKeep must have
* same parent, aParent. Additionally, if one of aNodeToJoin or aNodeToKeep
* is a text node, the other must be a text node.
* DoJoinNodes() merges contents in aContentToJoin to aContentToKeep and
* remove aContentToJoin from the DOM tree. aContentToJoin and aContentToKeep
* must have same parent, aParent. Additionally, if one of aContentToJoin or
* aContentToKeep is a text node, the other must be a text node.
*
* @param aNodeToKeep The node that will remain after the join.
* @param aNodeToJoin The node that will be joined with aNodeToKeep.
* There is no requirement that the two nodes be of the
* same type.
* @param aParent The parent of aNodeToKeep
* @param aContentToKeep The node that will remain after the join.
* @param aContentToJoin The node that will be joined with aContentToKeep.
* There is no requirement that the two nodes be of the
* same type.
*/
MOZ_CAN_RUN_SCRIPT nsresult DoJoinNodes(nsINode* aNodeToKeep,
nsINode* aNodeToJoin,
nsINode* aParent);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToJoin);
/**
* SplitNodeDeepWithTransaction() splits aMostAncestorToSplit deeply.
@ -1926,12 +1924,6 @@ class EditorBase : public nsIEditor,
MOZ_CAN_RUN_SCRIPT nsresult DoTransactionInternal(nsITransaction* aTxn);
/**
* Set outOffset to the offset of aChild in the parent.
* Returns the parent of aChild.
*/
static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset);
/**
* Get the previous node.
*/
@ -2483,15 +2475,6 @@ class EditorBase : public nsIEditor,
*/
virtual void InitializeSelectionAncestorLimit(nsIContent& aAncestorLimit);
/**
* Return the offset of aChild in aParent. Asserts fatally if parent or
* child is null, or parent is not child's parent.
* FYI: aChild must not be being removed from aParent. In such case, these
* methods may return wrong index if aChild doesn't have previous
* sibling or next sibling.
*/
static int32_t GetChildOffset(nsINode* aChild, nsINode* aParent);
/**
* Creates a range with just the supplied node and appends that to the
* selection.

Просмотреть файл

@ -3863,19 +3863,18 @@ nsresult HTMLEditor::GetCellContext(Element** aTable, Element** aCell,
}
if (aCellParent) {
// Get the immediate parent of the cell
nsCOMPtr<nsINode> cellParent = cell->GetParentNode();
// Cell has to have a parent, so fail if not found
if (NS_WARN_IF(!cellParent)) {
EditorRawDOMPoint atCellElement(cell);
if (NS_WARN_IF(!atCellElement.IsSet())) {
return NS_ERROR_FAILURE;
}
if (aCellOffset) {
*aCellOffset = GetChildOffset(cell, cellParent);
*aCellOffset = atCellElement.Offset();
}
// Now it's safe to hand over the reference to cellParent, since
// we don't need it anymore.
cellParent.forget(aCellParent);
*aCellParent = do_AddRef(atCellElement.GetContainer()).take();
}
return NS_OK;

Просмотреть файл

@ -63,7 +63,7 @@ NS_IMETHODIMP JoinNodeTransaction::DoTransaction() {
}
// Get the parent node
nsCOMPtr<nsINode> leftContentParent = mLeftContent->GetParentNode();
nsINode* leftContentParent = mLeftContent->GetParentNode();
if (NS_WARN_IF(!leftContentParent)) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -80,9 +80,9 @@ NS_IMETHODIMP JoinNodeTransaction::DoTransaction() {
mOffset = mLeftContent->Length();
OwningNonNull<EditorBase> editorBase = *mEditorBase;
OwningNonNull<nsINode> leftNode = *mLeftContent;
OwningNonNull<nsINode> rightNode = *mRightContent;
nsresult rv = editorBase->DoJoinNodes(rightNode, leftNode, leftContentParent);
OwningNonNull<nsIContent> leftContent = *mLeftContent;
OwningNonNull<nsIContent> rightContent = *mRightContent;
nsresult rv = editorBase->DoJoinNodes(rightContent, leftContent);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DoJoinNodes() failed");
return rv;
}

Просмотреть файл

@ -37,8 +37,7 @@ SplitNodeTransaction::SplitNodeTransaction(
EditorBase& aEditorBase,
const EditorDOMPointBase<PT, CT>& aStartOfRightContent)
: mEditorBase(&aEditorBase), mStartOfRightContent(aStartOfRightContent) {
MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.IsSet());
MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.GetContainerAsContent());
MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.IsInContentNode());
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase,
@ -51,7 +50,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitNodeTransaction)
NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
NS_IMETHODIMP SplitNodeTransaction::DoTransaction() {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mStartOfRightContent.IsSet())) {
if (NS_WARN_IF(!mEditorBase) ||
NS_WARN_IF(!mStartOfRightContent.IsInContentNode())) {
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_ASSERT(mStartOfRightContent.IsSetAndValid());
@ -120,7 +120,7 @@ NS_IMETHODIMP SplitNodeTransaction::DoTransaction() {
NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mNewLeftContent) ||
NS_WARN_IF(!mContainerParentNode) ||
NS_WARN_IF(!mStartOfRightContent.IsSet())) {
NS_WARN_IF(!mStartOfRightContent.IsInContentNode())) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -128,11 +128,10 @@ NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() {
// XXX Perhaps, we should reset mStartOfRightNode with current first child
// of the right node.
OwningNonNull<EditorBase> editorBase = *mEditorBase;
OwningNonNull<nsINode> containerNode = *mStartOfRightContent.GetContainer();
OwningNonNull<nsINode> newLeftContent = *mNewLeftContent;
OwningNonNull<nsINode> containerParentNode = *mContainerParentNode;
nsresult rv = editorBase->DoJoinNodes(containerNode, newLeftContent,
containerParentNode);
OwningNonNull<nsIContent> containerContent =
*mStartOfRightContent.ContainerAsContent();
OwningNonNull<nsIContent> newLeftContent = *mNewLeftContent;
nsresult rv = editorBase->DoJoinNodes(containerContent, newLeftContent);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DoJoinNodes() failed");
return rv;
}
@ -143,7 +142,8 @@ NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() {
*/
NS_IMETHODIMP SplitNodeTransaction::RedoTransaction() {
if (NS_WARN_IF(!mNewLeftContent) || NS_WARN_IF(!mContainerParentNode) ||
NS_WARN_IF(!mStartOfRightContent.IsSet()) || NS_WARN_IF(!mEditorBase)) {
NS_WARN_IF(!mStartOfRightContent.IsInContentNode()) ||
NS_WARN_IF(!mEditorBase)) {
return NS_ERROR_NOT_AVAILABLE;
}