Bug 1540037 - part 19: Make `IsEmpty()` be a virtual method of `EditorBase` and implement `nsIEditor::GetDocumentIsEmpty()` in `EditorBase` r=m_kato

Currently, `EditorBase::GetDocumentIsEmpty()` is implemented by `TextEditor`,
and it refers only `IsEmpty()` which is implemented both by `TextEditor` and
`HTMLEditor`.  So, `IsEmpty()` should be a virtual method of `EditorBase`,
then, `EditorBase` can implement `GetDocumentIsEmpty()`.

Depends on D115786

Differential Revision: https://phabricator.services.mozilla.com/D115787
This commit is contained in:
Masayuki Nakano 2021-05-25 04:53:01 +00:00
Родитель ae69b73075
Коммит 95c41d54c3
7 изменённых файлов: 163 добавлений и 22 удалений

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

@ -1069,7 +1069,9 @@ NS_IMETHODIMP EditorBase::SetShouldTxnSetSelection(bool aShould) {
}
NS_IMETHODIMP EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty) {
return NS_ERROR_NOT_IMPLEMENTED;
MOZ_ASSERT(aDocumentIsEmpty);
*aDocumentIsEmpty = IsEmpty();
return NS_OK;
}
// XXX: The rule system should tell us which node to select all on (ie, the

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

@ -578,6 +578,13 @@ class EditorBase : public nsIEditor,
*/
bool IsInEditSubAction() const { return mIsInEditSubAction; }
/**
* IsEmpty() checks whether the editor is empty. If editor has only padding
* <br> element for empty editor, returns true. If editor's root element has
* non-empty text nodes or other nodes like <br>, returns false.
*/
virtual bool IsEmpty() const = 0;
/**
* SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
* "input" event.

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

@ -151,13 +151,7 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD BeginningOfDocument() override;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD SetFlags(uint32_t aFlags) override;
/**
* IsEmpty() checks whether the editor is empty. If editor has only padding
* <br> element for empty editor, returns true. If editor's root element has
* non-empty text nodes or other nodes like <br>, returns false even if there
* are only empty blocks.
*/
virtual bool IsEmpty() const override;
bool IsEmpty() const final;
virtual bool CanPaste(int32_t aClipboardType) const override;
using EditorBase::CanPaste;

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

@ -532,12 +532,6 @@ bool TextEditor::IsEmpty() const {
!anonymousDivElement->GetFirstChild()->Length();
}
NS_IMETHODIMP TextEditor::GetDocumentIsEmpty(bool* aDocumentIsEmpty) {
MOZ_ASSERT(aDocumentIsEmpty);
*aDocumentIsEmpty = IsEmpty();
return NS_OK;
}
NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
MOZ_ASSERT(aCount);

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

@ -56,8 +56,6 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed {
NS_DECL_NSINAMED
// Overrides of nsIEditor
NS_IMETHOD GetDocumentIsEmpty(bool* aDocumentIsEmpty) override;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD
SetDocumentCharacterSet(const nsACString& characterSet) override;
@ -163,12 +161,7 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed {
uint32_t aFlags,
const nsAString& aValue) override;
/**
* IsEmpty() checks whether the editor is empty. If editor has only padding
* <br> element for empty editor, returns true. If editor's root element has
* non-empty text nodes or other nodes like <br>, returns false.
*/
virtual bool IsEmpty() const;
bool IsEmpty() const override;
MOZ_CAN_RUN_SCRIPT virtual nsresult HandleKeyPressEvent(
WidgetKeyboardEvent* aKeyboardEvent) override;

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

@ -266,6 +266,7 @@ skip-if = os == 'android'
[test_middle_click_paste.html]
skip-if = headless
[test_nsIEditor_beginningOfDocument.html]
[test_nsIEditor_documentIsEmpty.html]
[test_nsIEditor_insertLineBreak.html]
[test_nsIEditor_isSelectionEditable.html]
[test_nsIEditorMailSupport_insertAsCitedQuotation.html]

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

@ -0,0 +1,150 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tests of nsIEditor#documentIsEmpty</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(() => {
const originalBody = document.body.innerHTML;
(function test_with_text_editor() {
for (const test of [
{
tag: "input",
innerHTML: '<input><input value="abc"><input placeholder="abc">',
},
{
tag: "textarea",
innerHTML: '<textarea></textarea><textarea>abc</textarea><textarea placeholder="abc"></textarea>',
},
]) {
document.body.innerHTML = test.innerHTML;
let textControl = document.body.querySelector(test.tag);
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if value of <${test.tag}> is empty by default`);
textControl.focus();
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if value of <${test.tag}> is empty by default after getting focus`);
textControl.value = "abc";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, false,
`nsIEditor.documentIsEmpty should be false if <${test.tag}>.value is set to non-empty string`);
textControl.value = "";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if <${test.tag}>.value is set to empty string`);
textControl = textControl.nextSibling;
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, false,
`nsIEditor.documentIsEmpty should be false if value of <${test.tag}> is non-empty by default`);
textControl.focus();
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, false,
`nsIEditor.documentIsEmpty should be false if value of <${test.tag}> is non-empty by default after getting focus`);
textControl.value = "def";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, false,
`nsIEditor.documentIsEmpty should be false if <${test.tag}>.value is set to different non-empty string`);
textControl.value = "";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if <${test.tag}>.value is set to empty string from non-empty string`);
textControl = textControl.nextSibling;
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if value of <${test.tag}> is empty by default (placeholder isn't empty)`);
textControl.focus();
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if value of <${test.tag}> is empty by default after getting focus (placeholder isn't empty)`);
textControl.value = "abc";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, false,
`nsIEditor.documentIsEmpty should be false if <${test.tag}>.value is set to non-empty string (placeholder isn't empty)`);
textControl.value = "";
is(SpecialPowers.wrap(textControl).editor.documentIsEmpty, true,
`nsIEditor.documentIsEmpty should be true if <${test.tag}>.value is set to empty string (placeholder isn't empty)`);
}
})();
function getHTMLEditor() {
const editingSession = SpecialPowers.wrap(window).docShell.editingSession;
if (!editingSession) {
return null;
}
return editingSession.getEditorForWindow(window);
}
(function test_with_contenteditable() {
document.body.innerHTML = "<div contenteditable></div>";
try {
getHTMLEditor().documentIsEmpty;
todo(false, "nsIEditor.documentIsEmpty should throw an exception when no editing host has focus");
} catch (e) {
ok(true, "nsIEditor.documentIsEmpty should throw an exception when no editing host has focus");
}
document.querySelector("div[contenteditable]").focus();
todo_is(getHTMLEditor().documentIsEmpty, true,
"nsIEditor.documentIsEmpty should be true when editing host does not have contents");
document.body.innerHTML = "<div contenteditable><br></div>";
document.querySelector("div[contenteditable]").focus();
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when editing host has only a <br> element");
document.body.innerHTML = "<div contenteditable><p><br></p></div>";
document.querySelector("div[contenteditable]").focus();
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when editing host has only an empty paragraph");
document.body.innerHTML = "<div contenteditable><p>abc</p></div>";
document.querySelector("div[contenteditable]").focus();
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when editing host has text in a paragraph");
document.body.innerHTML = "<div contenteditable>abc</div>";
document.querySelector("div[contenteditable]").focus();
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when editing host has text directly");
document.execCommand("selectall");
document.execCommand("delete");
todo_is(getHTMLEditor().documentIsEmpty, true,
"nsIEditor.documentIsEmpty should be true when all contents in editing host are deleted");
})();
document.designMode = "on";
(function test_with_designMode() {
document.body.innerHTML = "";
is(getHTMLEditor().documentIsEmpty, true,
"nsIEditor.documentIsEmpty should be true when <body> is empty in designMode");
document.body.focus();
is(getHTMLEditor().documentIsEmpty, true,
"nsIEditor.documentIsEmpty should be true when <body> is empty in designMode (after setting focus explicitly)");
document.body.innerHTML = "<div><br></div>";
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when <body> has only an empty paragraph in designMode");
document.body.innerHTML = "<div>abc</div>";
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when <body> has text in a paragraph in designMode");
document.body.innerHTML = "abc";
is(getHTMLEditor().documentIsEmpty, false,
"nsIEditor.documentIsEmpty should be false when <body> has text directly in designMode");
document.execCommand("selectall");
document.execCommand("delete");
todo_is(getHTMLEditor().documentIsEmpty, true,
"nsIEditor.documentIsEmpty should be true when all contents in designMode are deleted");
})();
document.designMode = "off";
document.body.innerHTML = originalBody;
SimpleTest.finish();
});
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
</body>
</html>