mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1757492 - part 1: Clean up HTMLEditor::DoJoinNodes
r=m_kato
This patch just cleans up the method code for making the changes for bug 1735608 simpler. This does not change any behavior. Differential Revision: https://phabricator.services.mozilla.com/D140010
This commit is contained in:
parent
3339cabe9d
commit
5bbb9ea55a
@ -4783,13 +4783,10 @@ void HTMLEditor::DidJoinNodesTransaction(
|
||||
}
|
||||
|
||||
nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
|
||||
nsIContent& aContentToJoin) {
|
||||
nsIContent& aContentToRemove) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
uint32_t firstNodeLength = aContentToJoin.Length();
|
||||
|
||||
EditorRawDOMPoint atNodeToJoin(&aContentToJoin);
|
||||
EditorRawDOMPoint atNodeToKeep(&aContentToKeep);
|
||||
const uint32_t removingNodeLength = aContentToRemove.Length();
|
||||
|
||||
// Remember all selection points.
|
||||
// XXX Do we need to restore all types of selections by ourselves? Normal
|
||||
@ -4798,58 +4795,62 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
|
||||
// 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;
|
||||
range.mSelection = GetSelection(selectionType);
|
||||
if (selectionType == SelectionType::eNormal) {
|
||||
if (NS_WARN_IF(!range.mSelection)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else if (!range.mSelection) {
|
||||
// For non-normal selections, skip over the non-existing ones.
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t rangeCount = range.mSelection->RangeCount();
|
||||
for (const uint32_t j : IntegerRange(rangeCount)) {
|
||||
MOZ_ASSERT(range.mSelection->RangeCount() == rangeCount);
|
||||
const RefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
|
||||
MOZ_ASSERT(r);
|
||||
MOZ_ASSERT(r->IsPositioned());
|
||||
range.mStartContainer = r->GetStartContainer();
|
||||
range.mStartOffset = r->StartOffset();
|
||||
range.mEndContainer = r->GetEndContainer();
|
||||
range.mEndOffset = r->EndOffset();
|
||||
|
||||
// If selection endpoint is between the nodes, remember it as being
|
||||
// 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 == atNodeToKeep.GetContainer() &&
|
||||
atNodeToJoin.Offset() < range.mStartOffset &&
|
||||
range.mStartOffset <= atNodeToKeep.Offset()) {
|
||||
range.mStartContainer = &aContentToJoin;
|
||||
range.mStartOffset = firstNodeLength;
|
||||
}
|
||||
if (range.mEndContainer == atNodeToKeep.GetContainer() &&
|
||||
atNodeToJoin.Offset() < range.mEndOffset &&
|
||||
range.mEndOffset <= atNodeToKeep.Offset()) {
|
||||
range.mEndContainer = &aContentToJoin;
|
||||
range.mEndOffset = firstNodeLength;
|
||||
{
|
||||
EditorRawDOMPoint atRemovingNode(&aContentToRemove);
|
||||
EditorRawDOMPoint atNodeToKeep(&aContentToKeep);
|
||||
for (SelectionType selectionType : kPresentSelectionTypes) {
|
||||
SavedRange savingRange;
|
||||
savingRange.mSelection = GetSelection(selectionType);
|
||||
if (selectionType == SelectionType::eNormal) {
|
||||
if (NS_WARN_IF(!savingRange.mSelection)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else if (!savingRange.mSelection) {
|
||||
// For non-normal selections, skip over the non-existing ones.
|
||||
continue;
|
||||
}
|
||||
|
||||
savedRanges.AppendElement(range);
|
||||
const uint32_t rangeCount = savingRange.mSelection->RangeCount();
|
||||
for (const uint32_t j : IntegerRange(rangeCount)) {
|
||||
MOZ_ASSERT(savingRange.mSelection->RangeCount() == rangeCount);
|
||||
const RefPtr<nsRange> r = savingRange.mSelection->GetRangeAt(j);
|
||||
MOZ_ASSERT(r);
|
||||
MOZ_ASSERT(r->IsPositioned());
|
||||
savingRange.mStartContainer = r->GetStartContainer();
|
||||
savingRange.mStartOffset = r->StartOffset();
|
||||
savingRange.mEndContainer = r->GetEndContainer();
|
||||
savingRange.mEndOffset = r->EndOffset();
|
||||
|
||||
// If selection endpoint is between the nodes, remember it as being
|
||||
// in the one that is going away instead. This simplifies later
|
||||
// selection adjustment logic at end of this method.
|
||||
if (savingRange.mStartContainer) {
|
||||
if (savingRange.mStartContainer == atNodeToKeep.GetContainer() &&
|
||||
atRemovingNode.Offset() < savingRange.mStartOffset &&
|
||||
savingRange.mStartOffset <= atNodeToKeep.Offset()) {
|
||||
savingRange.mStartContainer = &aContentToRemove;
|
||||
savingRange.mStartOffset = removingNodeLength;
|
||||
}
|
||||
if (savingRange.mEndContainer == atNodeToKeep.GetContainer() &&
|
||||
atRemovingNode.Offset() < savingRange.mEndOffset &&
|
||||
savingRange.mEndOffset <= atNodeToKeep.Offset()) {
|
||||
savingRange.mEndContainer = &aContentToRemove;
|
||||
savingRange.mEndOffset = removingNodeLength;
|
||||
}
|
||||
}
|
||||
|
||||
savedRanges.AppendElement(savingRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OK, ready to do join now.
|
||||
// If it's a text node, just shuffle around some text.
|
||||
if (aContentToKeep.IsText() && aContentToJoin.IsText()) {
|
||||
if (aContentToKeep.IsText() && aContentToRemove.IsText()) {
|
||||
nsAutoString rightText;
|
||||
nsAutoString leftText;
|
||||
aContentToKeep.AsText()->GetData(rightText);
|
||||
aContentToJoin.AsText()->GetData(leftText);
|
||||
aContentToRemove.AsText()->GetData(leftText);
|
||||
leftText += rightText;
|
||||
IgnoredErrorResult ignoredError;
|
||||
DoSetText(MOZ_KnownLive(*aContentToKeep.AsText()), leftText, ignoredError);
|
||||
@ -4860,101 +4861,96 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
|
||||
"EditorBase::DoSetText() failed, but ignored");
|
||||
} else {
|
||||
// Otherwise it's an interior node, so shuffle around the children.
|
||||
nsCOMPtr<nsINodeList> childNodes = aContentToJoin.ChildNodes();
|
||||
nsCOMPtr<nsINodeList> childNodes = aContentToRemove.ChildNodes();
|
||||
MOZ_ASSERT(childNodes);
|
||||
|
||||
// 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();
|
||||
// of aContentToRemove in front of it GetFirstChild returns nullptr
|
||||
// firstChild if aContentToKeep has no children, that's OK.
|
||||
nsCOMPtr<nsIContent> firstChild = 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 aContentToJoin
|
||||
ErrorResult error;
|
||||
aContentToKeep.InsertBefore(*childNode, firstNode, error);
|
||||
// prepend children of aContentToRemove
|
||||
IgnoredErrorResult error;
|
||||
aContentToKeep.InsertBefore(*childNode, firstChild, error);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
error.SuppressException();
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
if (error.Failed()) {
|
||||
NS_WARNING("nsINode::InsertBefore() failed");
|
||||
return error.StealNSResult();
|
||||
}
|
||||
firstNode = std::move(childNode);
|
||||
firstChild = std::move(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the extra node.
|
||||
aContentToJoin.Remove();
|
||||
aContentToRemove.Remove();
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
||||
bool allowedTransactionsToChangeSelection =
|
||||
const bool allowedTransactionsToChangeSelection =
|
||||
AllowsTransactionsToChangeSelection();
|
||||
|
||||
// And adjust the selection if needed.
|
||||
RefPtr<Selection> previousSelection;
|
||||
for (size_t i = 0; i < savedRanges.Length(); ++i) {
|
||||
// And adjust the selection if needed.
|
||||
SavedRange& range = savedRanges[i];
|
||||
|
||||
for (SavedRange& savedRange : savedRanges) {
|
||||
// If we have not seen the selection yet, clear all of its ranges.
|
||||
if (range.mSelection != previousSelection) {
|
||||
ErrorResult error;
|
||||
MOZ_KnownLive(range.mSelection)->RemoveAllRanges(error);
|
||||
if (savedRange.mSelection != previousSelection) {
|
||||
IgnoredErrorResult error;
|
||||
MOZ_KnownLive(savedRange.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();
|
||||
}
|
||||
previousSelection = range.mSelection;
|
||||
previousSelection = savedRange.mSelection;
|
||||
}
|
||||
|
||||
if (allowedTransactionsToChangeSelection &&
|
||||
range.mSelection->Type() == SelectionType::eNormal) {
|
||||
savedRange.mSelection->Type() == SelectionType::eNormal) {
|
||||
// If the editor should adjust the selection, don't bother restoring
|
||||
// the ranges for the normal selection here.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if we joined nodes where selection starts.
|
||||
if (range.mStartContainer == &aContentToJoin) {
|
||||
range.mStartContainer = &aContentToKeep;
|
||||
} else if (range.mStartContainer == &aContentToKeep) {
|
||||
range.mStartOffset += firstNodeLength;
|
||||
if (savedRange.mStartContainer == &aContentToRemove) {
|
||||
savedRange.mStartContainer = &aContentToKeep;
|
||||
} else if (savedRange.mStartContainer == &aContentToKeep) {
|
||||
savedRange.mStartOffset += removingNodeLength;
|
||||
}
|
||||
|
||||
// Check to see if we joined nodes where selection ends.
|
||||
if (range.mEndContainer == &aContentToJoin) {
|
||||
range.mEndContainer = &aContentToKeep;
|
||||
} else if (range.mEndContainer == &aContentToKeep) {
|
||||
range.mEndOffset += firstNodeLength;
|
||||
if (savedRange.mEndContainer == &aContentToRemove) {
|
||||
savedRange.mEndContainer = &aContentToKeep;
|
||||
} else if (savedRange.mEndContainer == &aContentToKeep) {
|
||||
savedRange.mEndOffset += removingNodeLength;
|
||||
}
|
||||
|
||||
RefPtr<nsRange> newRange =
|
||||
nsRange::Create(range.mStartContainer, range.mStartOffset,
|
||||
range.mEndContainer, range.mEndOffset, IgnoreErrors());
|
||||
const RefPtr<nsRange> newRange = nsRange::Create(
|
||||
savedRange.mStartContainer, savedRange.mStartOffset,
|
||||
savedRange.mEndContainer, savedRange.mEndOffset, IgnoreErrors());
|
||||
if (!newRange) {
|
||||
NS_WARNING("nsRange::Create() failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ErrorResult error;
|
||||
IgnoredErrorResult error;
|
||||
// The `MOZ_KnownLive` annotation is only necessary because of a bug
|
||||
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1622253) in the
|
||||
// static analyzer.
|
||||
MOZ_KnownLive(range.mSelection)
|
||||
MOZ_KnownLive(savedRange.mSelection)
|
||||
->AddRangeAndSelectFramesAndNotifyListeners(*newRange, error);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
error.SuppressException();
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
@ -4965,7 +4961,7 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
|
||||
if (allowedTransactionsToChangeSelection) {
|
||||
// Editor wants us to set selection at join point.
|
||||
DebugOnly<nsresult> rvIgnored =
|
||||
SelectionRef().CollapseInLimiter(&aContentToKeep, firstNodeLength);
|
||||
SelectionRef().CollapseInLimiter(&aContentToKeep, removingNodeLength);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -978,18 +978,19 @@ class HTMLEditor final : public EditorBase,
|
||||
const EditorDOMPoint& aStartOfRightNode, nsIContent& aNewLeftNode);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* DoJoinNodes() merges contents in aContentToRemove to aContentToKeep and
|
||||
* remove aContentToRemove from the DOM tree. aContentToRemove and
|
||||
* aContentToKeep must have same parent. Additionally, if one of
|
||||
* aContentToRemove or aContentToKeep is a text node, the other must be a
|
||||
* text node.
|
||||
*
|
||||
* @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.
|
||||
* @param aContentToKeep The node that will remain after the join.
|
||||
* @param aContentToRemove The node that will be joined with aContentToKeep.
|
||||
* There is no requirement that the two nodes be of
|
||||
* the same type.
|
||||
*/
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
|
||||
DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToJoin);
|
||||
DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToRemove);
|
||||
|
||||
/**
|
||||
* Routines for managing the preservation of selection across
|
||||
|
Loading…
Reference in New Issue
Block a user