зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1713334 - Part 3: Assume TextEditor always have a text node r=masayuki
Differential Revision: https://phabricator.services.mozilla.com/D117023
This commit is contained in:
Родитель
4a9087283b
Коммит
d6d3699b31
|
@ -68,6 +68,9 @@ 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.
|
||||
|
|
|
@ -5393,7 +5393,6 @@ nsresult EditorBase::ReplaceTextAsAction(
|
|||
}
|
||||
|
||||
nsresult EditorBase::ReplaceSelectionAsSubAction(const nsAString& aString) {
|
||||
// TODO: Move this method to `EditorBase`.
|
||||
if (aString.IsEmpty()) {
|
||||
nsresult rv = DeleteSelectionAsSubAction(
|
||||
nsIEditor::eNone,
|
||||
|
|
|
@ -600,36 +600,22 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
|
|||
}
|
||||
|
||||
RefPtr<Element> anonymousDivElement = GetRoot();
|
||||
nsIContent* firstChild = anonymousDivElement->GetFirstChild();
|
||||
RefPtr<Text> textNode =
|
||||
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
|
||||
MOZ_ASSERT(textNode);
|
||||
|
||||
// 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 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 (!IsSingleLineEditor()) {
|
||||
// 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 (!firstChild) {
|
||||
return EditActionIgnored();
|
||||
}
|
||||
if (firstChild->IsText()) {
|
||||
if (!firstChild->GetNextSibling() ||
|
||||
if (!textNode->GetNextSibling() ||
|
||||
!EditorUtils::IsPaddingBRElementForEmptyLastLine(
|
||||
*firstChild->GetNextSibling())) {
|
||||
return EditActionIgnored();
|
||||
}
|
||||
} else if (!EditorUtils::IsPaddingBRElementForEmptyLastLine(*firstChild)) {
|
||||
*textNode->GetNextSibling())) {
|
||||
return EditActionIgnored();
|
||||
}
|
||||
}
|
||||
|
@ -641,36 +627,6 @@ 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();
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -691,9 +647,7 @@ EditActionResult TextEditor::HandleDeleteSelection(
|
|||
|
||||
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
|
||||
|
||||
// if there is only padding <br> element for empty editor, cancel the
|
||||
// operation.
|
||||
if (mPaddingBRElementForEmptyEditor) {
|
||||
if (IsEmpty()) {
|
||||
return EditActionCanceled();
|
||||
}
|
||||
EditActionResult result =
|
||||
|
@ -785,37 +739,15 @@ EditActionResult TextEditor::ComputeValueFromTextNodeAndBRElement(
|
|||
return EditActionHandled();
|
||||
}
|
||||
|
||||
nsIContent* textNodeOrPaddingBRElement = anonymousDivElement->GetFirstChild();
|
||||
if (!textNodeOrPaddingBRElement ||
|
||||
textNodeOrPaddingBRElement == mPaddingBRElementForEmptyEditor) {
|
||||
Text* textNode = Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
|
||||
MOZ_ASSERT(textNode);
|
||||
|
||||
if (!textNode->Length()) {
|
||||
aValue.Truncate();
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
// 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;
|
||||
nsIContent* firstChildExceptText = textNode->GetNextSibling();
|
||||
// If the DOM tree is unexpected, fall back to the expensive path.
|
||||
bool isInput = IsSingleLineEditor();
|
||||
bool isTextarea = !isInput;
|
||||
|
|
|
@ -368,10 +368,11 @@ 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() ||
|
||||
!anonymousDivElement->GetFirstChild()->IsText() ||
|
||||
!anonymousDivElement->GetFirstChild()->Length();
|
||||
return !anonymousDivElement->GetFirstChild()->Length();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
|
||||
|
@ -596,13 +597,7 @@ nsresult TextEditor::SelectEntireDocument() {
|
|||
|
||||
RefPtr<Text> text =
|
||||
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
|
||||
if (!text) {
|
||||
ErrorResult error;
|
||||
SelectionRef().CollapseInLimiter(*anonymousDivElement, 0, error);
|
||||
NS_WARNING_ASSERTION(!error.Failed(),
|
||||
"Selection::SetStartAndEndInLimiter() failed");
|
||||
return error.StealNSResult();
|
||||
}
|
||||
MOZ_ASSERT(text);
|
||||
|
||||
MOZ_TRY(SelectionRef().SetStartAndEndInLimiter(
|
||||
*text, 0, *text, text->TextDataLength(), eDirNext,
|
||||
|
|
|
@ -311,6 +311,7 @@ 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'
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<!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>
|
Загрузка…
Ссылка в новой задаче