Bug 1375910 - Don't remove text node when setting value is empty string. r?masayuki
When setting empty value by input.value, we remove text node and insert bogus node. But creating and removing node are expensive. So we should keep text node for performance if possible.
Now, DocumentIsEmpty only checks bogus node to detect empty. So, keeping text node change causes that document cannot detect as empty. If root has only text node and all is empty, we should detect empty document.
This change should be only plain text editor. HTML editor already allows multiple text nodes, so we should keep old behaviour on HTML editor.
MozReview-Commit-ID: Gt8GmdWAA3E
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -705,16 +705,22 @@ HTMLEditRules::DidDoAction(Selection* aS
return DidAbsolutePosition();
}
default:
// pass through to TextEditRules
return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
}
+NS_IMETHODIMP_(bool)
+HTMLEditRules::DocumentIsEmpty()
+{
+ return !!mBogusNode;
+}
+
nsresult
HTMLEditRules::GetListState(bool* aMixed,
bool* aOL,
bool* aUL,
bool* aDL)
{
NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
*aMixed = false;
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -89,16 +89,17 @@ public:
NS_IMETHOD BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection) override;
NS_IMETHOD AfterEdit(EditAction action,
nsIEditor::EDirection aDirection) override;
NS_IMETHOD WillDoAction(Selection* aSelection, RulesInfo* aInfo,
bool* aCancel, bool* aHandled) override;
NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo,
nsresult aResult) override;
+ NS_IMETHOD_(bool) DocumentIsEmpty() override;
NS_IMETHOD DocumentModified() override;
nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -344,20 +344,47 @@ TextEditRules::DidDoAction(Selection* aS
case EditAction::outputText:
return DidOutputText(aSelection, aResult);
default:
// Don't fail on transactions we don't handle here!
return NS_OK;
}
}
+/**
+ * Return false if the editor has non-empty text nodes or non-text
+ * nodes. Otherwise, i.e., there is no meaningful content,
+ * return true.
+ */
NS_IMETHODIMP_(bool)
TextEditRules::DocumentIsEmpty()
{
- return (mBogusNode != nullptr);
+ if (mBogusNode) {
+ return true;
+ }
+
+ // Even if there is no bogus node, we should detect as empty document
+ // if all children are text node and these are no content.
+ if (NS_WARN_IF(!mTextEditor)) {
+ return true;
+ }
+ Element* rootElement = mTextEditor->GetRoot();
+ if (!rootElement) {
+ return true;
+ }
+
+ uint32_t childCount = rootElement->GetChildCount();
+ for (uint32_t i = 0; i < childCount; i++) {
+ nsINode* node = rootElement->GetChildAt(i);
+ if (!EditorBase::IsTextNode(node) ||
+ node->Length()) {
+ return false;
+ }
+ }
+ return true;
}
void
TextEditRules::WillInsert(Selection& aSelection, bool* aCancel)
{
MOZ_ASSERT(aCancel);
if (IsReadonly() || IsDisabled()) {
@@ -867,26 +894,19 @@ TextEditRules::WillSetText(Selection& aS
return NS_OK;
}
nsINode* curNode = rootElement->GetFirstChild();
if (NS_WARN_IF(!EditorBase::IsTextNode(curNode))) {
return NS_OK;
}
- if (tString.IsEmpty()) {
- nsresult rv = textEditor->DeleteNode(curNode);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aHandled = true;
- return NS_OK;
- }
-
+ // Even if empty text, we don't remove text node and set empty text
+ // for performance
nsresult rv = textEditor->SetTextImpl(tString, *curNode->GetAsText());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
*aHandled = true;
ASSERT_PASSWORD_LENGTHS_EQUAL();