Bug 1792387 - part 3-2: Make `HTMLEditor::DoSplitNode()` handle both split node directions r=m_kato

This does not change the existing path's behavior.

Differential Revision: https://phabricator.services.mozilla.com/D158101
This commit is contained in:
Masayuki Nakano 2022-09-30 22:20:20 +00:00
Родитель 8fd837d307
Коммит 6ba25b471e
3 изменённых файлов: 141 добавлений и 52 удалений

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

@ -4902,68 +4902,119 @@ SplitNodeResult HTMLEditor::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
// Fix the child before mutation observer may touch the DOM tree.
nsIContent* firstChildOfRightNode = aStartOfRightNode.GetChild();
IgnoredErrorResult error;
parent->InsertBefore(aNewNode, aStartOfRightNode.GetContainer(), error);
parent->InsertBefore(aNewNode,
aDirection == SplitNodeDirection::LeftNodeIsNewOne
? aStartOfRightNode.GetContainer()
: aStartOfRightNode.GetContainer()->GetNextSibling(),
error);
if (MOZ_UNLIKELY(error.Failed())) {
NS_WARNING("nsINode::InsertBefore() failed");
return SplitNodeResult(error.StealNSResult());
}
// At this point, the existing right node has all the children. Move all
// the children which are before aStartOfRightNode.
if (!aStartOfRightNode.IsStartOfContainer()) {
// If it's a text node, just shuffle around some text
Text* rightAsText = aStartOfRightNode.GetContainerAs<Text>();
Text* leftAsText = aNewNode.GetAsText();
if (rightAsText && leftAsText) {
// Fix right node
nsAutoString leftText;
IgnoredErrorResult ignoredError;
rightAsText->SubstringData(0, aStartOfRightNode.Offset(), leftText,
ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
MOZ_DIAGNOSTIC_ASSERT_IF(aStartOfRightNode.IsInTextNode(), aNewNode.IsText());
MOZ_DIAGNOSTIC_ASSERT_IF(!aStartOfRightNode.IsInTextNode(),
!aNewNode.IsText());
// If we are splitting a text node, we need to move its some data to the
// new text node.
if (aStartOfRightNode.IsInTextNode()) {
if (!(aDirection == SplitNodeDirection::LeftNodeIsNewOne &&
aStartOfRightNode.IsStartOfContainer()) &&
!(aDirection == SplitNodeDirection::RightNodeIsNewOne &&
aStartOfRightNode.IsEndOfContainer())) {
Text* originalTextNode = aStartOfRightNode.ContainerAs<Text>();
Text* newTextNode = aNewNode.AsText();
nsAutoString movingText;
const uint32_t cutStartOffset =
aDirection == SplitNodeDirection::LeftNodeIsNewOne
? 0u
: aStartOfRightNode.Offset();
const uint32_t cutLength =
aDirection == SplitNodeDirection::LeftNodeIsNewOne
? aStartOfRightNode.Offset()
: originalTextNode->Length() - aStartOfRightNode.Offset();
IgnoredErrorResult error;
originalTextNode->SubstringData(cutStartOffset, cutLength, movingText,
error);
NS_WARNING_ASSERTION(!error.Failed(),
"Text::SubstringData() failed, but ignored");
ignoredError.SuppressException();
error.SuppressException();
// XXX This call may destroy us.
DoDeleteText(MOZ_KnownLive(*rightAsText), 0, aStartOfRightNode.Offset(),
ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
DoDeleteText(MOZ_KnownLive(*originalTextNode), cutStartOffset, cutLength,
error);
NS_WARNING_ASSERTION(!error.Failed(),
"EditorBase::DoDeleteText() failed, but ignored");
ignoredError.SuppressException();
error.SuppressException();
// Fix left node
// XXX This call may destroy us.
DoSetText(MOZ_KnownLive(*leftAsText), leftText, ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
DoSetText(MOZ_KnownLive(*newTextNode), movingText, error);
NS_WARNING_ASSERTION(!error.Failed(),
"EditorBase::DoSetText() failed, but ignored");
} else {
MOZ_DIAGNOSTIC_ASSERT(!rightAsText && !leftAsText);
// Otherwise it's an interior node, so shuffle around the children. Go
// through list backwards so deletes don't interfere with the iteration.
if (!firstChildOfRightNode) {
// XXX Why do we ignore an error while moving nodes from the right node
// to the left node?
IgnoredErrorResult ignoredError;
MoveAllChildren(*aStartOfRightNode.GetContainer(),
EditorRawDOMPoint(&aNewNode, 0u), ignoredError);
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"HTMLEditor::MoveAllChildren() failed, but ignored");
} else if (NS_WARN_IF(aStartOfRightNode.GetContainer() !=
firstChildOfRightNode->GetParentNode())) {
// firstChildOfRightNode has been moved by mutation observer.
// In this case, we what should we do? Use offset? But we cannot
// check if the offset is still expected.
} else {
// XXX Why do we ignore an error while moving nodes from the right node
// to the left node?
IgnoredErrorResult ignoredError;
MovePreviousSiblings(*firstChildOfRightNode,
EditorRawDOMPoint(&aNewNode, 0u), ignoredError);
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"HTMLEditor::MovePreviousSiblings() failed, but ignored");
}
}
}
// If the node has been moved to different parent, we should do nothing
// since web apps should handle eventhing in such case.
else if (firstChildOfRightNode &&
aStartOfRightNode.GetContainer() !=
firstChildOfRightNode->GetParentNode()) {
NS_WARNING(
"The web app interupped us and touched the DOM tree, we stopped "
"splitting anything");
} else if (aDirection == SplitNodeDirection::LeftNodeIsNewOne) {
// If Splitting at end of container which is not a text node, we need to
// move all children if the left node is new one. Otherwise, nothing to do.
if (!firstChildOfRightNode) {
// XXX Why do we ignore an error while moving nodes from the right
// node to the left node?
IgnoredErrorResult error;
MoveAllChildren(*aStartOfRightNode.GetContainer(),
EditorRawDOMPoint(&aNewNode, 0u), error);
NS_WARNING_ASSERTION(!error.Failed(),
"HTMLEditor::MoveAllChildren() failed, but ignored");
}
// If the left node is new one and splitting middle of it, we need to
// previous siblings of the given point to the new left node.
else if (firstChildOfRightNode->GetPreviousSibling()) {
// XXX Why do we ignore an error while moving nodes from the right node
// to the left node?
IgnoredErrorResult error;
MovePreviousSiblings(*firstChildOfRightNode,
EditorRawDOMPoint(&aNewNode, 0u), error);
NS_WARNING_ASSERTION(
!error.Failed(),
"HTMLEditor::MovePreviousSiblings() failed, but ignored");
}
} else {
MOZ_ASSERT(aDirection == SplitNodeDirection::RightNodeIsNewOne);
// If the right node is new one and there is no children or splitting at
// end of the node, we need to do nothing.
if (!firstChildOfRightNode) {
// Do nothing.
}
// If the right node is new one and splitting at start of the container,
// we need to move all children to the new right node.
else if (!firstChildOfRightNode->GetPreviousSibling()) {
// XXX Why do we ignore an error while moving nodes from the right
// node to the left node?
IgnoredErrorResult error;
MoveAllChildren(*aStartOfRightNode.GetContainer(),
EditorRawDOMPoint(&aNewNode, 0u), error);
NS_WARNING_ASSERTION(!error.Failed(),
"HTMLEditor::MoveAllChildren() failed, but ignored");
}
// If the right node is new one and splitting at middle of the node, we need
// to move inclusive next siblings of the split point to the new right node.
else {
// XXX Why do we ignore an error while moving nodes from the right node
// to the left node?
IgnoredErrorResult error;
MoveInclusiveNextSiblings(*firstChildOfRightNode,
EditorRawDOMPoint(&aNewNode, 0u), error);
NS_WARNING_ASSERTION(
!error.Failed(),
"HTMLEditor::MoveInclusiveNextSiblings() failed, but ignored");
}
}
@ -5075,8 +5126,12 @@ SplitNodeResult HTMLEditor::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
NS_WARN_IF(parent !=
aStartOfRightNode.GetContainer()->GetParentNode()) ||
NS_WARN_IF(parent != aNewNode.GetParentNode()) ||
NS_WARN_IF(aNewNode.GetNextSibling() !=
aStartOfRightNode.GetContainer()))) {
(aDirection == SplitNodeDirection::LeftNodeIsNewOne &&
NS_WARN_IF(aNewNode.GetNextSibling() !=
aStartOfRightNode.GetContainer())) ||
(aDirection == SplitNodeDirection::RightNodeIsNewOne &&
NS_WARN_IF(aNewNode.GetPreviousSibling() !=
aStartOfRightNode.GetContainer())))) {
return SplitNodeResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}

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

@ -1985,6 +1985,21 @@ class HTMLEditor final : public EditorBase,
const EditorRawDOMPoint& aPointToInsert,
ErrorResult& aError);
/**
* MoveInclusiveNextSiblings() moves aChild and all siblings after it to
* before aPointToInsert.GetChild().
* See explanation of MoveChildrenBetween() for the detail of the behavior.
*
* @param aChild The node which is first node to be moved.
* @param aPointToInsert The insertion point. The container must not
* be a data node like a text node.
* @param aError The result. If this succeeds to move children,
* returns NS_OK. Otherwise, an error.
*/
void MoveInclusiveNextSiblings(nsIContent& aChild,
const EditorRawDOMPoint& aPointToInsert,
ErrorResult& aError);
/**
* MoveOneHardLineContentsWithTransaction() moves the content in a hard line
* which contains aPointInHardLine to aPointToInsert or end of

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

@ -5595,6 +5595,25 @@ void HTMLEditor::MovePreviousSiblings(nsIContent& aChild,
"HTMLEditor::MoveChildrenBetween() failed");
}
void HTMLEditor::MoveInclusiveNextSiblings(
nsIContent& aChild, const EditorRawDOMPoint& aPointToInsert,
ErrorResult& aError) {
MOZ_ASSERT(!aError.Failed());
if (NS_WARN_IF(!aChild.GetParentNode())) {
aError.Throw(NS_ERROR_INVALID_ARG);
return;
}
nsIContent* lastChild = aChild.GetParentNode()->GetLastChild();
if (NS_WARN_IF(!lastChild)) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
MoveChildrenBetween(aChild, *lastChild, aPointToInsert, aError);
NS_WARNING_ASSERTION(!aError.Failed(),
"HTMLEditor::MoveChildrenBetween() failed");
}
nsresult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
DeleteContentButKeepTableStructure(HTMLEditor& aHTMLEditor,
nsIContent& aContent) {