Bug 1717155 - part 1: Make TextEditor use GetTextNode() more r=m_kato

The anonymous `<div>` of `TextEditor` has only one `Text` as its first child.
Therefore, we can make `TextEditor` simpler to access the `Text` node.

Differential Revision: https://phabricator.services.mozilla.com/D240419
This commit is contained in:
Masayuki Nakano
2025-03-13 05:25:45 +00:00
parent 773705ea27
commit 617c636f1f
3 changed files with 22 additions and 54 deletions

View File

@@ -527,9 +527,7 @@ Result<EditActionResult, nsresult> TextEditor::SetTextWithoutTransaction(
MaybeDoAutoPasswordMasking();
RefPtr<Element> anonymousDivElement = GetRoot();
RefPtr<Text> textNode =
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
const RefPtr<Text> textNode = GetTextNode();
MOZ_ASSERT(textNode);
// We can use this fast path only when:
@@ -624,8 +622,8 @@ Result<EditActionResult, nsresult> TextEditor::HandleDeleteSelectionInternal(
return Err(NS_ERROR_FAILURE);
}
if (const Text* theTextNode = AsTextEditor()->GetTextNode()) {
rangesToDelete.EnsureRangesInTextNode(*theTextNode);
if (const Text* const textNode = GetTextNode()) {
rangesToDelete.EnsureRangesInTextNode(*textNode);
}
Result<CaretPoint, nsresult> caretPointOrError =
@@ -706,7 +704,7 @@ TextEditor::ComputeValueFromTextNodeAndBRElement(nsAString& aValue) const {
return EditActionResult::HandledResult();
}
Text* textNode = Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
const Text* const textNode = GetTextNode();
MOZ_ASSERT(textNode);
if (!textNode->Length()) {

View File

@@ -480,56 +480,23 @@ already_AddRefed<Element> TextEditor::GetInputEventTargetElement() const {
}
bool TextEditor::IsEmpty() const {
// Even if there is no padding <br> element for empty editor, we should be
// detected as empty editor if all the children are text nodes and these
// have no content.
Element* anonymousDivElement = GetRoot();
if (!anonymousDivElement) {
return true; // Don't warn it, this is possible, e.g., 997805.html
}
MOZ_ASSERT(anonymousDivElement->GetFirstChild() &&
anonymousDivElement->GetFirstChild()->IsText());
// Only when there is non-empty text node, we are not empty.
return !anonymousDivElement->GetFirstChild()->Length();
const Text* const textNode = GetTextNode();
MOZ_DIAGNOSTIC_ASSERT_IF(textNode,
!Text::FromNodeOrNull(textNode->GetNextSibling()));
return !textNode || !textNode->TextDataLength();
}
NS_IMETHODIMP TextEditor::GetTextLength(uint32_t* aCount) {
MOZ_ASSERT(aCount);
// initialize out params
*aCount = 0;
// special-case for empty document, to account for the padding <br> element
// for empty editor.
// XXX This should be overridden by `HTMLEditor` and we should return the
// first text node's length from `TextEditor` instead. The following
// code is too expensive.
if (IsEmpty()) {
return NS_OK;
}
Element* rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
if (NS_WARN_IF(!GetRoot())) {
return NS_ERROR_FAILURE;
}
uint32_t totalLength = 0;
PostContentIterator postOrderIter;
DebugOnly<nsresult> rvIgnored = postOrderIter.Init(rootElement);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"PostContentIterator::Init() failed, but ignored");
EditorType editorType = GetEditorType();
for (; !postOrderIter.IsDone(); postOrderIter.Next()) {
nsINode* currentNode = postOrderIter.GetCurrentNode();
if (currentNode && currentNode->IsText() &&
EditorUtils::IsEditableContent(*currentNode->AsText(), editorType)) {
totalLength += currentNode->Length();
}
}
*aCount = totalLength;
const Text* const textNode = GetTextNode();
MOZ_DIAGNOSTIC_ASSERT_IF(textNode,
!Text::FromNodeOrNull(textNode->GetNextSibling()));
*aCount = textNode ? textNode->TextDataLength() : 0u;
return NS_OK;
}
@@ -947,11 +914,10 @@ nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
return NS_ERROR_NOT_AVAILABLE;
}
Element* rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
if (NS_WARN_IF(!GetRoot())) {
return NS_ERROR_NOT_INITIALIZED;
}
Text* text = Text::FromNodeOrNull(rootElement->GetFirstChild());
Text* const text = GetTextNode();
if (!text || !text->Length()) {
// There is no anonymous text node in the editor.
return aStart > 0 && aStart != UINT32_MAX ? NS_ERROR_INVALID_ARG : NS_OK;
@@ -965,8 +931,8 @@ nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
// If aStart is middle of a surrogate pair, expand it to include the
// preceding high surrogate because the caller may want to show a
// character before the character at `aStart + 1`.
const nsTextFragment* textFragment = text->GetText();
if (textFragment->IsLowSurrogateFollowingHighSurrogateAt(aStart)) {
const nsTextFragment& textFragment = text->TextFragment();
if (textFragment.IsLowSurrogateFollowingHighSurrogateAt(aStart)) {
mPasswordMaskData->mUnmaskedStart = aStart - 1;
// If caller collapses the range, keep it. Otherwise, expand the length.
if (aLength > 0) {
@@ -981,7 +947,7 @@ nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
// the following low surrogate because the caller may want to show a
// character after the character at `aStart + aLength`.
if (UnmaskedEnd() < valueLength &&
textFragment->IsLowSurrogateFollowingHighSurrogateAt(UnmaskedEnd())) {
textFragment.IsLowSurrogateFollowingHighSurrogateAt(UnmaskedEnd())) {
mPasswordMaskData->mUnmaskedLength++;
}
// If it's first time to mask the unmasking characters with timer, create

View File

@@ -259,6 +259,10 @@ class TextEditor final : public EditorBase,
}
}
/**
* Return the `Text` node in the anonymous <div>. Note that the anonymous
* <div> can have only one `Text` for the storage of the value of this editor.
*/
dom::Text* GetTextNode() {
MOZ_DIAGNOSTIC_ASSERT(GetRoot());
MOZ_DIAGNOSTIC_ASSERT(GetRoot()->GetFirstChild());