Backed out 5 changesets (bug 1713334) for causing bug 1716714.

Backed out changeset bd1c37ce2c61 (bug 1713334)
Backed out changeset 876ed18c5126 (bug 1713334)
Backed out changeset 5a4f4514d99a (bug 1713334)
Backed out changeset 7d7feef654c7 (bug 1713334)
Backed out changeset 61e15374e617 (bug 1713334)
This commit is contained in:
Ryan VanderMeulen 2021-06-17 10:57:32 -04:00
Родитель 52692e2ba0
Коммит a337a14382
15 изменённых файлов: 293 добавлений и 149 удалений

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

@ -29,7 +29,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
// ////////////////////////////////////////////////////////////////////////
// setTextContents
et.scheduleTest(et.setTextContents, "hello", startOffset);
et.scheduleTest(et.setTextContents, "katze", startOffset);
et.scheduleTest(et.setTextContents, "olleh", startOffset);
et.scheduleTest(et.setTextContents, "", startOffset);
// ////////////////////////////////////////////////////////////////////////

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

@ -68,9 +68,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=328885
evt.originalTarget.setAttribute("foo", "bar");
ok(mutationCount == 0, "(10) Mutation listener shouldn't have been called! ["+ mutationCount + "]");
// We played with the internal div, now restore its initial state
evt.originalTarget.replaceChildren(new Text());
// Same tests with non-native-anononymous element.
// mutationCount should be increased by 2 each time, since there is
// first a mutation specific event and then DOMSubtreeModified.

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

@ -79,7 +79,12 @@ NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
return NS_OK;
}
MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToDelete->IsText());
if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
uint32_t length = mContentToDelete->AsText()->TextLength();
if (length > 0) {
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
}
}
// Remember which child mContentToDelete was (by remembering which child was
// next). Note that mRefContent can be nullptr.
@ -122,6 +127,17 @@ NS_IMETHODIMP DeleteNodeTransaction::UndoTransaction() {
NS_WARNING("nsINode::InsertBefore() failed");
return error.StealNSResult();
}
if (editorBase->IsTextEditor() && contentToDelete->IsText()) {
uint32_t length = contentToDelete->AsText()->TextLength();
if (length > 0) {
nsresult rv = MOZ_KnownLive(editorBase->AsTextEditor())
->DidInsertText(length, 0, length);
if (NS_FAILED(rv)) {
NS_WARNING("TextEditor::DidInsertText() failed");
return rv;
}
}
}
return NS_OK;
}
@ -135,6 +151,13 @@ NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() {
return NS_OK;
}
if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
uint32_t length = mContentToDelete->AsText()->TextLength();
if (length > 0) {
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
}
}
mEditorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete);
OwningNonNull<nsINode> parentNode = *mParentNode;

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

@ -339,32 +339,9 @@ nsresult EditorBase::Init(Document& aDocument, Element* aRoot,
return NS_OK;
}
nsresult EditorBase::EnsureEmptyTextFirstChild() {
MOZ_ASSERT(IsTextEditor());
RefPtr<Element> root = GetRoot();
nsIContent* firstChild = root->GetFirstChild();
if (!firstChild || !firstChild->IsText()) {
RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
if (!newTextNode) {
NS_WARNING("EditorBase::CreateTextNode() failed");
return NS_ERROR_UNEXPECTED;
}
IgnoredErrorResult ignoredError;
root->InsertChildBefore(newTextNode, nullptr, true, ignoredError);
MOZ_ASSERT(!ignoredError.Failed());
}
return NS_OK;
}
nsresult EditorBase::InitEditorContentAndSelection() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (IsTextEditor()) {
MOZ_TRY(EnsureEmptyTextFirstChild());
}
nsresult rv = MaybeCreatePaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING(
@ -1425,15 +1402,10 @@ nsresult EditorBase::CollapseSelectionToEnd() const {
}
nsCOMPtr<nsIContent> lastContent = rootElement;
if (IsTextEditor()) {
lastContent = rootElement->GetFirstChild();
MOZ_ASSERT(lastContent && lastContent->IsText());
} else {
for (nsIContent* child = lastContent->GetLastChild();
child && HTMLEditUtils::IsContainerNode(*child);
child = child->GetLastChild()) {
lastContent = child;
}
for (nsIContent* child = lastContent->GetLastChild();
child && (IsTextEditor() || HTMLEditUtils::IsContainerNode(*child));
child = child->GetLastChild()) {
lastContent = child;
}
uint32_t length = lastContent->Length();
@ -2150,7 +2122,6 @@ NS_IMETHODIMP EditorBase::InsertNode(nsINode* aNodeToInsert,
nsresult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT_IF(IsTextEditor(), !aContentToInsert.IsText());
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
return NS_ERROR_INVALID_ARG;
@ -2247,7 +2218,6 @@ NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode) {
nsresult EditorBase::DeleteNodeWithTransaction(nsIContent& aContent) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT_IF(IsTextEditor(), !aContent.IsText());
IgnoredErrorResult ignoredError;
AutoEditSubActionNotifier startToHandleEditSubAction(
@ -3197,7 +3167,7 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction(
// already savvy to having multiple ime txns inside them.
// Delete empty IME text node if there is one
if (IsHTMLEditor() && isIMETransaction && mComposition) {
if (isIMETransaction && mComposition) {
RefPtr<Text> textNode = mComposition->GetContainerTextNode();
if (textNode && !textNode->Length()) {
DeleteNodeWithTransaction(*textNode);
@ -4510,7 +4480,7 @@ nsresult EditorBase::DeleteSelectionAsSubAction(
// have been called by mutation event listeners.
return NS_ERROR_FAILURE;
}
if (IsHTMLEditor() && atNewStartOfSelection.IsInTextNode() &&
if (atNewStartOfSelection.IsInTextNode() &&
!atNewStartOfSelection.GetContainer()->Length()) {
nsresult rv = DeleteNodeWithTransaction(
MOZ_KnownLive(*atNewStartOfSelection.ContainerAsText()));
@ -5398,6 +5368,7 @@ nsresult EditorBase::ReplaceTextAsAction(
}
nsresult EditorBase::ReplaceSelectionAsSubAction(const nsAString& aString) {
// TODO: Move this method to `EditorBase`.
if (aString.IsEmpty()) {
nsresult rv = DeleteSelectionAsSubAction(
nsIEditor::eNone,

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

@ -2240,8 +2240,6 @@ class EditorBase : public nsIEditor,
return mIsHTMLEditorClass ? EditorType::HTML : EditorType::Text;
}
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult EnsureEmptyTextFirstChild();
/**
* InitEditorContentAndSelection() may insert a padding `<br>` element for
* if it's required in the anonymous `<div>` element or `<body>` element and

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

@ -91,8 +91,6 @@ NS_IMETHODIMP InsertNodeTransaction::DoTransaction() {
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToInsert->IsText());
if (!mPointToInsert.IsSetAndValid()) {
// It seems that DOM tree has been changed after first DoTransaction()
// and current RedoTranaction() call.
@ -139,6 +137,18 @@ NS_IMETHODIMP InsertNodeTransaction::DoTransaction() {
return error.StealNSResult();
}
if (editorBase->IsTextEditor() && contentToInsert->IsText()) {
uint32_t length = contentToInsert->AsText()->TextLength();
if (length > 0) {
nsresult rv = MOZ_KnownLive(editorBase->AsTextEditor())
->DidInsertText(length, 0, length);
if (NS_FAILED(rv)) {
NS_WARNING("TextEditor::DidInsertText() failed");
return rv;
}
}
}
if (!mEditorBase->AllowsTransactionsToChangeSelection()) {
return NS_OK;
}
@ -169,6 +179,12 @@ NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() {
NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
if (mEditorBase->IsTextEditor() && mContentToInsert->IsText()) {
uint32_t length = mContentToInsert->TextLength();
if (length > 0) {
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
}
}
// XXX If the inserted node has been moved to different container node or
// just removed from the DOM tree, this always fails.
OwningNonNull<nsINode> container = *mPointToInsert.GetContainer();

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

@ -259,19 +259,60 @@ nsresult TextEditor::EnsureCaretNotAtEndOfTextNode() {
// This is usually performed in InitEditorContentAndSelection(), however,
// if the editor is reframed, this may be called by
// OnEndHandlingTopLevelEditSubAction().
if (SelectionRef().RangeCount()) {
if (!SelectionRef().RangeCount()) {
DebugOnly<nsresult> rvIgnored = CollapseSelectionToEnd();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"EditorBase::CollapseSelectionToEnd() failed, but ignored");
}
// If we are at the end of the <textarea> element, we need to set the
// selection to stick to the padding <br> element for empty last line at the
// end of the <textarea>.
EditorRawDOMPoint selectionStartPoint(
EditorBase::GetStartPoint(SelectionRef()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
return NS_ERROR_FAILURE;
}
// Nothing to do if we're not at the end of the text node.
if (!selectionStartPoint.IsInTextNode() ||
!selectionStartPoint.IsEndOfContainer()) {
return NS_OK;
}
DebugOnly<nsresult> rvIgnored = CollapseSelectionToEnd();
Element* anonymousDivElement = GetRoot();
if (NS_WARN_IF(!anonymousDivElement)) {
return NS_ERROR_NULL_POINTER;
}
nsINode* parentNode = selectionStartPoint.GetContainer()->GetParentNode();
if (parentNode != anonymousDivElement) {
return NS_OK;
}
nsIContent* nextContent =
selectionStartPoint.GetContainer()->GetNextSibling();
if (!nextContent ||
!EditorUtils::IsPaddingBRElementForEmptyLastLine(*nextContent)) {
return NS_OK;
}
EditorRawDOMPoint afterStartContainer(
EditorRawDOMPoint::After(*selectionStartPoint.GetContainer()));
if (NS_WARN_IF(!afterStartContainer.IsSet())) {
return NS_ERROR_FAILURE;
}
IgnoredErrorResult ignoredError;
SelectionRef().CollapseInLimiter(afterStartContainer, ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"EditorBase::CollapseSelectionToEnd() failed, but ignored");
return NS_OK;
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Selection::CollapseInLimiter() failed");
return ignoredError.StealNSResult();
}
void TextEditor::HandleNewLinesInStringForSingleLineEditor(
@ -552,22 +593,36 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
}
RefPtr<Element> anonymousDivElement = GetRoot();
RefPtr<Text> textNode =
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
MOZ_ASSERT(textNode);
nsIContent* firstChild = anonymousDivElement->GetFirstChild();
// We can use this fast path only when:
// - we need to insert a text node.
// - we need to replace content of existing text node.
// Additionally, for avoiding odd result, we should check whether we're in
// usual condition.
if (!IsSingleLineEditor()) {
if (IsSingleLineEditor()) {
// If we're a single line text editor, i.e., <input>, there is only padding
// <br> element. Otherwise, there should be only one text node. But note
// that even if there is a padding <br> element for empty editor, it's
// already been removed by `EnsureNoPaddingBRElementForEmptyEditor()`. So,
// at here, there should be only one text node or no children.
if (firstChild && (!firstChild->IsText() || firstChild->GetNextSibling())) {
return EditActionIgnored();
}
} else {
// If we're a multiline text editor, i.e., <textarea>, there is a padding
// <br> element for empty last line followed by scrollbar/resizer elements.
// Otherwise, a text node is followed by them.
if (!textNode->GetNextSibling() ||
!EditorUtils::IsPaddingBRElementForEmptyLastLine(
*textNode->GetNextSibling())) {
if (!firstChild) {
return EditActionIgnored();
}
if (firstChild->IsText()) {
if (!firstChild->GetNextSibling() ||
!EditorUtils::IsPaddingBRElementForEmptyLastLine(
*firstChild->GetNextSibling())) {
return EditActionIgnored();
}
} else if (!EditorUtils::IsPaddingBRElementForEmptyLastLine(*firstChild)) {
return EditActionIgnored();
}
}
@ -579,12 +634,62 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
HandleNewLinesInStringForSingleLineEditor(sanitizedValue);
}
if (!firstChild || !firstChild->IsText()) {
if (sanitizedValue.IsEmpty()) {
return EditActionHandled();
}
RefPtr<Document> document = GetDocument();
if (NS_WARN_IF(!document)) {
return EditActionIgnored();
}
RefPtr<nsTextNode> newTextNode = CreateTextNode(sanitizedValue);
if (!newTextNode) {
NS_WARNING("EditorBase::CreateTextNode() failed");
return EditActionIgnored();
}
nsresult rv = InsertNodeWithTransaction(
*newTextNode, EditorDOMPoint(anonymousDivElement, 0));
if (NS_WARN_IF(Destroyed())) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return EditActionResult(rv);
}
return EditActionHandled();
}
// TODO: If new value is empty string, we should only remove it.
RefPtr<Text> textNode = firstChild->GetAsText();
if (MOZ_UNLIKELY(!textNode)) {
NS_WARNING("The first child was not a text node");
return EditActionIgnored();
}
rv = SetTextNodeWithoutTransaction(sanitizedValue, *textNode);
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::SetTextNodeWithoutTransaction() failed");
return EditActionResult(rv);
}
// If we replaced non-empty value with empty string, we need to delete the
// text node.
if (sanitizedValue.IsEmpty() && !textNode->Length()) {
nsresult rv = DeleteNodeWithTransaction(*textNode);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::DeleteNodeWithTransaction() failed, but ignored");
// XXX I don't think this is necessary because the anonymous `<div>`
// element has now only padding `<br>` element even if there are
// something.
IgnoredErrorResult ignoredError;
SelectionRef().SetInterlinePosition(true, ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Selection::SetInterlinePoisition(true) failed");
}
return EditActionHandled();
}
@ -598,7 +703,9 @@ EditActionResult TextEditor::HandleDeleteSelection(
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
if (IsEmpty()) {
// if there is only padding <br> element for empty editor, cancel the
// operation.
if (mPaddingBRElementForEmptyEditor) {
return EditActionCanceled();
}
EditActionResult result =
@ -689,15 +796,37 @@ EditActionResult TextEditor::ComputeValueFromTextNodeAndBRElement(
return EditActionHandled();
}
Text* textNode = Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
MOZ_ASSERT(textNode);
if (!textNode->Length()) {
nsIContent* textNodeOrPaddingBRElement = anonymousDivElement->GetFirstChild();
if (!textNodeOrPaddingBRElement ||
textNodeOrPaddingBRElement == mPaddingBRElementForEmptyEditor) {
aValue.Truncate();
return EditActionHandled();
}
nsIContent* firstChildExceptText = textNode->GetNextSibling();
// If it's an <input type="text"> element, the DOM tree should be:
// <div (::-moz-text-control-editing-root)>
// #text
// </div>
//
// If it's a <textarea> element, the DOM tree should be:
// <div (::-moz-text-control-editing-root)>
// #text (if there is)
// <br type="_moz">
// <scrollbar orient="horizontal">
// ...
// </div>
Text* textNode = textNodeOrPaddingBRElement->GetAsText();
if (!textNode) {
// If there is no text node in the expected DOM tree, we can say that it's
// just empty.
aValue.Truncate();
return EditActionHandled();
}
nsIContent* firstChildExceptText =
textNode ? textNodeOrPaddingBRElement->GetNextSibling()
: textNodeOrPaddingBRElement;
// If the DOM tree is unexpected, fall back to the expensive path.
bool isInput = IsSingleLineEditor();
bool isTextarea = !isInput;

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

@ -309,9 +309,34 @@ nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) {
// shouldn't receive such selectionchange before the first mutation.
AutoUpdateViewBatch preventSelectionChangeEvent(*this);
RefPtr<Element> rootElement = GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
// We want to select trailing `<br>` element to remove all nodes to replace
// all, but TextEditor::SelectEntireDocument() doesn't select such `<br>`
// elements.
// XXX We should make ReplaceSelectionAsSubAction() take range. Then,
// we can saving the expensive cost of modifying `Selection` here.
if (NS_SUCCEEDED(SelectEntireDocument())) {
nsresult rv;
if (IsEmpty()) {
rv = SelectionRef().CollapseInLimiter(rootElement, 0);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"Selection::CollapseInLimiter() failed, but ignored");
} else {
// XXX Oh, we shouldn't select padding `<br>` element for empty last
// line here since we will need to recreate it in multiline
// text editor.
ErrorResult error;
SelectionRef().SelectAllChildren(*rootElement, error);
NS_WARNING_ASSERTION(
!error.Failed(),
"Selection::SelectAllChildren() failed, but ignored");
rv = error.StealNSResult();
}
if (NS_SUCCEEDED(rv)) {
DebugOnly<nsresult> rvIgnored = ReplaceSelectionAsSubAction(aString);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
@ -341,11 +366,10 @@ bool TextEditor::IsEmpty() const {
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();
return !anonymousDivElement->GetFirstChild() ||
!anonymousDivElement->GetFirstChild()->IsText() ||
!anonymousDivElement->GetFirstChild()->Length();
}
NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
@ -569,7 +593,13 @@ nsresult TextEditor::SelectEntireDocument() {
RefPtr<Text> text =
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
MOZ_ASSERT(text);
if (!text) {
ErrorResult error;
SelectionRef().CollapseInLimiter(*anonymousDivElement, 0, error);
NS_WARNING_ASSERTION(!error.Failed(),
"Selection::SetStartAndEndInLimiter() failed");
return error.StealNSResult();
}
MOZ_TRY(SelectionRef().SetStartAndEndInLimiter(
*text, 0, *text, text->TextDataLength(), eDirNext,
@ -703,7 +733,7 @@ nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
return NS_ERROR_NOT_INITIALIZED;
}
Text* text = Text::FromNodeOrNull(rootElement->GetFirstChild());
if (!text || !text->Length()) {
if (!text) {
// There is no anonymous text node in the editor.
return aStart > 0 && aStart != UINT32_MAX ? NS_ERROR_INVALID_ARG : NS_OK;
}

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

@ -77,9 +77,10 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed {
using EditorBase::CanPaste;
// Overrides of EditorBase
MOZ_CAN_RUN_SCRIPT virtual nsresult Init(
Document& aDoc, Element* aRoot, nsISelectionController* aSelCon,
uint32_t aFlags, const nsAString& aInitialValue) override;
MOZ_CAN_RUN_SCRIPT virtual nsresult Init(Document& aDoc, Element* aRoot,
nsISelectionController* aSelCon,
uint32_t aFlags,
const nsAString& aValue) override;
bool IsEmpty() const override;

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

@ -224,7 +224,7 @@ skip-if = headless
[test_bug1399722.html]
[test_bug1406726.html]
[test_bug1409520.html]
[test_bug1425997.html]
[test_bug1425997.html]
[test_bug1497480.html]
skip-if = toolkit == 'android'
[test_bug1543312.html]
@ -313,7 +313,6 @@ skip-if = (verify && debug && os == 'win') # bug 1485293
skip-if = toolkit == 'android'
[test_state_change_on_reframe.html]
[test_textarea_value_not_include_cr.html]
[test_texteditor_textnode.html]
[test_typing_at_edge_of_anchor.html]
[test_undo_after_spellchecker_replaces_word.html]
skip-if = toolkit == 'android'

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

@ -15,11 +15,7 @@ https://bugzilla.mozilla.org/show_bug.cgi=id=1368544
<pre id="test">
</pre>
<script class="testbody">
function hasEmptyTextNode(div) {
return div.firstChild.nodeType === Node.TEXT_NODE && div.firstChild.length === 0;
}
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(() => {
let textarea = document.getElementById("textarea");
@ -55,14 +51,18 @@ SimpleTest.waitForFocus(() => {
SpecialPowers.wrap(textarea).setUserInput("");
is(textarea.value, "",
"textarea should become empty when setUserInput() is called with empty string");
ok(hasEmptyTextNode(editor.rootElement),
"editor of textarea should only have an empty text node when user input emulation set the value to empty");
todo(editor.rootElement.childNodes.length === 1, "editor of textarea should only have a single child");
if (editor.rootElement.childNodes.length > 1) {
is(editor.rootElement.childNodes.length, 2, "There should be only one additional <br> node");
is(editor.rootElement.lastChild.tagName.toLowerCase(), "br", "The node should be a <br> element node");
ok(!SpecialPowers.wrap(editor.rootElement.lastChild).isPaddingForEmptyEditor,
"The <br> should not be a padding <br> element");
if (navigator.appVersion.includes("Android")) {
todo(!editor.rootElement.hasChildNodes(),
"editor of textarea should have no children when user input emulation set the value to empty");
if (editor.rootElement.childNodes.length > 0) {
is(editor.rootElement.childNodes.length, 1, "There should be only one <br> node");
is(editor.rootElement.firstChild.tagName.toLowerCase(), "br", "The node should be a <br> element node");
ok(!SpecialPowers.wrap(editor.rootElement.firstChild).isPaddingForEmptyEditor,
"The <br> should not be a padding <br> element");
}
} else {
ok(!editor.rootElement.hasChildNodes(),
"editor of textarea should have no children when user input emulation set the value to empty");
}
textarea.value = "ABC";
synthesizeKey("KEY_Enter", {repeat: 2});

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

@ -1,41 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test for Bug 1713334</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<input id="input">
<textarea id="textarea"></textarea>
<script>
"use strict";
function assertChild(div, content) {
const name = div.parentElement.localName;
is(div.firstChild.nodeType, Node.TEXT_NODE, `<${name}>: The first node of the root element must be a text node`);
is(div.firstChild.textContent, content, `<${name}>: The content of the text node is wrong`);
}
function test(element) {
element.focus();
const { rootElement } = SpecialPowers.wrap(element).editor;
assertChild(rootElement, "");
element.value = "";
assertChild(rootElement, "");
element.value = "foo"
assertChild(rootElement, "foo");
element.value = "";
assertChild(rootElement, "");
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(() => {
test(document.all.input);
test(document.all.textarea);
SimpleTest.finish();
});
</script>

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

@ -907,15 +907,37 @@ nsresult nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect) {
return rv;
}
RefPtr<nsINode> rootNode = mRootNode;
nsCOMPtr<nsINode> rootNode;
rootNode = mRootNode;
NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
RefPtr<Text> text = Text::FromNodeOrNull(rootNode->GetFirstChild());
MOZ_ASSERT(text);
int32_t numChildren = mRootNode->GetChildCount();
uint32_t length = text->Length();
if (numChildren > 0) {
// We never want to place the selection after the last
// br under the root node!
nsIContent* child = mRootNode->GetLastChild();
if (child) {
if (child->IsHTMLElement(nsGkAtoms::br)) {
child = child->GetPreviousSibling();
--numChildren;
} else if (child->IsText() && !child->Length()) {
// Editor won't remove text node when empty value.
--numChildren;
}
}
if (!aSelect && numChildren) {
child = child->GetPreviousSibling();
if (child && child->IsText()) {
rootNode = child;
numChildren = child->AsText()->TextDataLength();
}
}
}
rv = SetSelectionInternal(text, aSelect ? 0 : length, text, length);
rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren, rootNode,
numChildren);
NS_ENSURE_SUCCESS(rv, rv);
ScrollSelectionIntoViewAsync();
@ -1160,9 +1182,8 @@ void nsTextControlFrame::SetInitialChildList(ChildListID aListID,
nsresult nsTextControlFrame::UpdateValueDisplay(bool aNotify,
bool aBeforeEditorInit,
const nsAString* aValue) {
if (!IsSingleLineTextControl()) { // textareas don't use this
if (!IsSingleLineTextControl()) // textareas don't use this
return NS_OK;
}
MOZ_ASSERT(mRootNode, "Must have a div content\n");
MOZ_ASSERT(!mEditorHasBeenInitialized,
@ -1199,6 +1220,16 @@ nsresult nsTextControlFrame::UpdateValueDisplay(bool aNotify,
textControlElement->GetTextEditorValue(value, true);
}
// Update the display of the placeholder value and preview text if needed.
// We don't need to do this if we're about to initialize the editor, since
// EnsureEditorInitialized takes care of this.
if (aBeforeEditorInit && value.IsEmpty()) {
if (nsIContent* node = mRootNode->GetFirstChild()) {
mRootNode->RemoveChildNode(node, true);
}
return NS_OK;
}
return textContent->SetText(value, aNotify);
}

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

@ -198,15 +198,5 @@
await data.assert_empty_spin();
assert_equals(collector.events.length, 4);
}, `Calling setRangeText() repeatedly on ${name}`);
promise_test(async () => {
await data.initialize();
target.value = "";
target.setRangeText("foo");
await data.assert_empty_spin();
assert_equals(collector.events.length, 0);
}, `Calling setRangeText() on empty ${name}`);
}
</script>

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

@ -1946,7 +1946,7 @@ function runCompositionTest()
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
"runCompositionTest", "#6-3") ||
!checkSelection(4, "", "runCompositionTest", "#6-3")) {
!checkSelection(4, "\u6700", "runCompositionTest", "#6-3")) {
return;
}