Bug 1627175 - part 36: Make `EditorBase::CreateTransactionForCollapsedRange()` use `EditorBase::GetNextContent()` and `EditorBase::GetPreviousContent()` only when it's an `HTMLEditor` r=m_kato

If the editor instance is a `TextEditor`, the root element has to be
anonymous `<div>` element and it has only one text node when it has non-empty
value.  Therefore, if it's in `TextEditor`, the method does not need to use
the complicated APIs for finding a text node from the anonymous `<div>` element
or padding `<br>` element since it can adjust the given point into the text
node without such API.

Differential Revision: https://phabricator.services.mozilla.com/D113240
This commit is contained in:
Masayuki Nakano 2021-04-27 11:17:43 +00:00
Родитель 4e78552ddb
Коммит 267025f6e5
1 изменённых файлов: 80 добавлений и 27 удалений

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

@ -3462,6 +3462,45 @@ EditorBase::CreateTransactionForCollapsedRange(
if (NS_WARN_IF(!point.IsSet())) { if (NS_WARN_IF(!point.IsSet())) {
return nullptr; return nullptr;
} }
if (IsTextEditor()) {
// There should be only one text node in the anonymous `<div>` (but may
// be followed by a padding `<br>`). We should adjust the point into
// the text node (or return nullptr if there is no text to delete) for
// avoiding finding the text node with complicated API.
if (!point.IsInTextNode()) {
const Element* anonymousDiv = GetRoot();
if (NS_WARN_IF(!anonymousDiv)) {
return nullptr;
}
if (!anonymousDiv->GetFirstChild() ||
!anonymousDiv->GetFirstChild()->IsText()) {
return nullptr; // The value is empty.
}
if (point.GetContainer() == anonymousDiv) {
if (point.IsStartOfContainer()) {
point.Set(anonymousDiv->GetFirstChild(), 0);
} else {
point.SetToEndOf(anonymousDiv->GetFirstChild());
}
} else {
// Must be referring a padding `<br>` element or after the text node.
point.SetToEndOf(anonymousDiv->GetFirstChild());
}
}
MOZ_ASSERT(!point.ContainerAsText()->GetPreviousSibling());
MOZ_ASSERT(!point.ContainerAsText()->GetNextSibling() ||
!point.ContainerAsText()->GetNextSibling()->IsText());
if (aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::ExtendBackward &&
point.IsStartOfContainer()) {
return nullptr;
}
if (aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::ExtendForward &&
point.IsEndOfContainer()) {
return nullptr;
}
}
// XXX: if the container of point is empty, then we'll need to delete the node // XXX: if the container of point is empty, then we'll need to delete the node
// as well as the 1 child // as well as the 1 child
@ -3470,11 +3509,12 @@ EditorBase::CreateTransactionForCollapsedRange(
// XXX: this has to come from rule section // XXX: this has to come from rule section
if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward && if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward &&
point.IsStartOfContainer()) { point.IsStartOfContainer()) {
MOZ_ASSERT(IsHTMLEditor());
// We're backspacing from the beginning of a node. Delete the last thing // We're backspacing from the beginning of a node. Delete the last thing
// of previous editable content. // of previous editable content.
nsIContent* previousEditableContent = EditorBase::GetPreviousContent( nsIContent* previousEditableContent = EditorBase::GetPreviousContent(
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode}, *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
GetEditorType(), GetEditorRoot()); EditorType::HTML, GetEditorRoot());
if (!previousEditableContent) { if (!previousEditableContent) {
NS_WARNING("There was no editable content before the collapsed range"); NS_WARNING("There was no editable content before the collapsed range");
return nullptr; return nullptr;
@ -3513,11 +3553,12 @@ EditorBase::CreateTransactionForCollapsedRange(
if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendForward && if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendForward &&
point.IsEndOfContainer()) { point.IsEndOfContainer()) {
MOZ_ASSERT(IsHTMLEditor());
// We're deleting from the end of a node. Delete the first thing of // We're deleting from the end of a node. Delete the first thing of
// next editable content. // next editable content.
nsIContent* nextEditableContent = EditorBase::GetNextContent( nsIContent* nextEditableContent = EditorBase::GetNextContent(
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode}, *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
GetEditorType(), GetEditorRoot()); EditorType::HTML, GetEditorRoot());
if (!nextEditableContent) { if (!nextEditableContent) {
NS_WARNING("There was no editable content after the collapsed range"); NS_WARNING("There was no editable content after the collapsed range");
return nullptr; return nullptr;
@ -3574,14 +3615,16 @@ EditorBase::CreateTransactionForCollapsedRange(
return deleteTextTransaction.forget(); return deleteTextTransaction.forget();
} }
nsIContent* editableContent = nsIContent* editableContent = nullptr;
if (IsHTMLEditor()) {
editableContent =
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
? EditorBase::GetPreviousContent( ? EditorBase::GetPreviousContent(
point, {WalkTreeOption::IgnoreNonEditableNode}, GetEditorType(), point, {WalkTreeOption::IgnoreNonEditableNode},
GetEditorRoot()) EditorType::HTML, GetEditorRoot())
: EditorBase::GetNextContent(point, : EditorBase::GetNextContent(
{WalkTreeOption::IgnoreNonEditableNode}, point, {WalkTreeOption::IgnoreNonEditableNode},
GetEditorType(), GetEditorRoot()); EditorType::HTML, GetEditorRoot());
if (!editableContent) { if (!editableContent) {
NS_WARNING("There was no editable content around the collapsed range"); NS_WARNING("There was no editable content around the collapsed range");
return nullptr; return nullptr;
@ -3590,20 +3633,29 @@ EditorBase::CreateTransactionForCollapsedRange(
!editableContent->Length()) { !editableContent->Length()) {
// Can't delete an empty text node (bug 762183) // Can't delete an empty text node (bug 762183)
editableContent = editableContent =
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::ExtendBackward
? EditorBase::GetPreviousContent( ? EditorBase::GetPreviousContent(
*editableContent, {WalkTreeOption::IgnoreNonEditableNode}, *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
GetEditorType(), GetEditorRoot()) EditorType::HTML, GetEditorRoot())
: EditorBase::GetNextContent( : EditorBase::GetNextContent(
*editableContent, {WalkTreeOption::IgnoreNonEditableNode}, *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
GetEditorType(), GetEditorRoot()); EditorType::HTML, GetEditorRoot());
} }
if (NS_WARN_IF(!editableContent)) { if (!editableContent) {
NS_WARNING( NS_WARNING(
"There was no editable content which is not empty around the collapsed " "There was no editable content which is not empty around the "
"range"); "collapsed range");
return nullptr; return nullptr;
} }
} else {
MOZ_ASSERT(point.IsInTextNode());
editableContent = point.GetContainerAsContent();
if (!editableContent) {
NS_WARNING("If there was no text node, should've been handled first");
return nullptr;
}
}
if (editableContent->IsText()) { if (editableContent->IsText()) {
if (aHowToHandleCollapsedRange == if (aHowToHandleCollapsedRange ==
@ -3626,6 +3678,7 @@ EditorBase::CreateTransactionForCollapsedRange(
return deleteTextTransaction.forget(); return deleteTextTransaction.forget();
} }
MOZ_ASSERT(IsHTMLEditor());
RefPtr<DeleteNodeTransaction> deleteNodeTransaction = RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
DeleteNodeTransaction::MaybeCreate(*this, *editableContent); DeleteNodeTransaction::MaybeCreate(*this, *editableContent);
NS_WARNING_ASSERTION(deleteNodeTransaction, NS_WARNING_ASSERTION(deleteNodeTransaction,