Bug 1220696 - part 5: Support "contentReadOnly" and "getHTML" commands in `<input>` and `<textarea>` r=smaug

Although these commands supported supported only by Gecko, we shouldn't stop
supporting them unless we know the usage in the wild.  Therefore, this patch
adds the handling code for `TextEditor` too.

Differential Revision: https://phabricator.services.mozilla.com/D108571
This commit is contained in:
Masayuki Nakano 2021-03-24 01:55:09 +00:00
Родитель b2fd51c051
Коммит fad3ed3447
5 изменённых файлов: 72 добавлений и 102 удалений

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

@ -5376,7 +5376,8 @@ bool Document::QueryCommandEnabled(const nsAString& aHTMLCommandName,
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
if (commandData.IsAvailableOnlyWhenEditable() &&
!editCommandTarget.IsEditable(this)) {
return false;
}
@ -5415,7 +5416,8 @@ bool Document::QueryCommandIndeterm(const nsAString& aHTMLCommandName,
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
if (commandData.IsAvailableOnlyWhenEditable() &&
!editCommandTarget.IsEditable(this)) {
return false;
}
RefPtr<nsCommandParams> params = new nsCommandParams();
@ -5469,7 +5471,8 @@ bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
if (commandData.IsAvailableOnlyWhenEditable() &&
!editCommandTarget.IsEditable(this)) {
return false;
}
RefPtr<nsCommandParams> params = new nsCommandParams();
@ -5600,7 +5603,8 @@ void Document::QueryCommandValue(const nsAString& aHTMLCommandName,
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
if (commandData.IsAvailableOnlyWhenEditable() &&
!editCommandTarget.IsEditable(this)) {
return;
}
RefPtr<nsCommandParams> params = new nsCommandParams();

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

@ -4170,7 +4170,9 @@ class Document : public nsINode,
bool IsAvailableOnlyWhenEditable() const {
return mCommand != mozilla::Command::Cut &&
mCommand != mozilla::Command::Copy &&
mCommand != mozilla::Command::Paste;
mCommand != mozilla::Command::Paste &&
mCommand != mozilla::Command::SetDocumentReadOnly &&
mCommand != mozilla::Command::GetHTML;
}
bool IsCutOrCopyCommand() const {
return mCommand == mozilla::Command::Cut ||

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

@ -288,6 +288,12 @@ class EditorBase : public nsIEditor,
*/
Element* GetRoot() const { return mRootElement; }
/**
* Likewise, but gets the text control element instead of the root for
* plaintext editors.
*/
Element* GetExposedRoot() const;
/**
* Set or unset TextInputListener. If setting non-nullptr when the editor
* already has a TextInputListener, this will crash in debug build.
@ -1861,12 +1867,6 @@ class EditorBase : public nsIEditor,
*/
virtual Element* GetEditorRoot() const;
/**
* Likewise, but gets the text control element instead of the root for
* plaintext editors.
*/
Element* GetExposedRoot() const;
/**
* Whether the editor is active on the DOM window. Note that when this
* returns true but GetFocusedContent() returns null, it means that this

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

@ -5,16 +5,19 @@
#include "mozilla/EditorCommands.h"
#include "mozilla/HTMLEditor.h" // for HTMLEditor
#include "mozilla/TextEditor.h" // for TextEditor
#include "mozilla/dom/Document.h" // for Document
#include "nsCommandParams.h" // for nsCommandParams
#include "nsIEditingSession.h" // for nsIEditingSession, etc
#include "nsIPrincipal.h" // for nsIPrincipal
#include "nsISupportsImpl.h" // for nsPresContext::Release
#include "nsISupportsUtils.h" // for NS_IF_ADDREF
#include "nsIURI.h" // for nsIURI
#include "nsPresContext.h" // for nsPresContext
#include "mozilla/HTMLEditor.h" // for HTMLEditor
#include "mozilla/TextEditor.h" // for TextEditor
#include "mozilla/dom/Element.h" // for Element
#include "mozilla/dom/Document.h" // for Document
#include "mozilla/dom/HTMLInputElement.h" // for HTMLInputElement
#include "mozilla/dom/HTMLTextAreaElement.h" // for HTMLTextAreaElement
#include "nsCommandParams.h" // for nsCommandParams
#include "nsIEditingSession.h" // for nsIEditingSession, etc
#include "nsIPrincipal.h" // for nsIPrincipal
#include "nsISupportsImpl.h" // for nsPresContext::Release
#include "nsISupportsUtils.h" // for NS_IF_ADDREF
#include "nsIURI.h" // for nsIURI
#include "nsPresContext.h" // for nsPresContext
// defines
#define STATE_ENABLED "state_enabled"
@ -24,6 +27,8 @@
namespace mozilla {
using namespace dom;
/*****************************************************************************
* mozilla::SetDocumentStateCommand
*
@ -38,7 +43,10 @@ StaticRefPtr<SetDocumentStateCommand> SetDocumentStateCommand::sInstance;
bool SetDocumentStateCommand::IsCommandEnabled(Command aCommand,
TextEditor* aTextEditor) const {
// These commands are always enabled if given editor is an HTMLEditor.
if (aCommand == Command::SetDocumentReadOnly) {
return !!aTextEditor;
}
// The other commands are always enabled if given editor is an HTMLEditor.
return aTextEditor && aTextEditor->AsHTMLEditor();
}
@ -55,7 +63,8 @@ nsresult SetDocumentStateCommand::DoCommandParam(
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!aTextEditor.AsHTMLEditor())) {
if (aCommand != Command::SetDocumentReadOnly &&
NS_WARN_IF(!aTextEditor.IsHTMLEditor())) {
return NS_ERROR_FAILURE;
}
@ -73,6 +82,39 @@ nsresult SetDocumentStateCommand::DoCommandParam(
return rv;
}
case Command::SetDocumentReadOnly: {
if (aTextEditor.IsTextEditor()) {
Element* inputOrTextArea = aTextEditor.GetExposedRoot();
if (NS_WARN_IF(!inputOrTextArea)) {
return NS_ERROR_FAILURE;
}
// Perhaps, this legacy command shouldn't work with
// `<input type="file">` and `<input type="number">.
if (inputOrTextArea->IsInNativeAnonymousSubtree()) {
return NS_ERROR_FAILURE;
}
if (RefPtr<HTMLInputElement> inputElement =
HTMLInputElement::FromNode(inputOrTextArea)) {
if (inputElement->ReadOnly() == aBoolParam.value()) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
ErrorResult error;
inputElement->SetReadOnly(aBoolParam.value(), error);
return error.StealNSResult();
}
if (RefPtr<HTMLTextAreaElement> textAreaElement =
HTMLTextAreaElement::FromNode(inputOrTextArea)) {
if (textAreaElement->ReadOnly() == aBoolParam.value()) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
ErrorResult error;
textAreaElement->SetReadOnly(aBoolParam.value(), error);
return error.StealNSResult();
}
NS_ASSERTION(
false,
"Unexpected exposed root element, fallthrough to directly make the "
"editor readonly");
}
ErrorResult error;
if (aBoolParam.value()) {
nsresult rv = aTextEditor.AddFlags(nsIEditor::eEditorReadonlyMask);
@ -391,7 +433,7 @@ nsresult DocumentStateCommand::GetCommandStateParams(
if (!aTextEditor) {
return NS_OK;
}
dom::Document* document = aTextEditor->GetDocument();
Document* document = aTextEditor->GetDocument();
if (NS_WARN_IF(!document)) {
return NS_ERROR_FAILURE;
}

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

@ -1,13 +1,4 @@
[exec-command-with-text-editor.tentative.html]
[In <input>, execCommand("getHTML", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("getHTML", false, null), a[b\]c): getHTML value should be "b" when <input> has focus]
expected: FAIL
[In <input>, execCommand("getHTML", false, null), a[b\]c): getHTML value should be "ere <b>is</b> T" when <input> does not have focus]
expected: FAIL
[In <input>, execCommand("cut", false, null), a[b\]c): The command should be enabled]
expected: FAIL
@ -56,24 +47,9 @@
[In <input>, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, true), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, true), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, true), a[b\]c): contentReadOnly state should be true when <input> has focus]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, true), a[b\]c): readonly property should be true]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, false), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("contentReadOnly", false, false), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("defaultParagraphSeparator", false, p), a[b\]c): defaultParagraphSeparator value should be "p" when <input> has focus]
expected: FAIL
@ -86,15 +62,6 @@
[In <input>, execCommand("defaultParagraphSeparator", false, div), a[b\]c): defaultParagraphSeparator value should be "div" when <input> does not have focus]
expected: FAIL
[In <textarea>, execCommand("getHTML", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("getHTML", false, null), a[b\]c): getHTML value should be "b" when <textarea> has focus]
expected: FAIL
[In <textarea>, execCommand("getHTML", false, null), a[b\]c): getHTML value should be "ere <b>is</b> T" when <textarea> does not have focus]
expected: FAIL
[In <textarea>, execCommand("cut", false, null), a[b\]c): The command should be enabled]
expected: FAIL
@ -173,24 +140,9 @@
[In <textarea>, execCommand("insertlinebreak", false, null), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, true), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, true), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, true), a[b\]c): contentReadOnly state should be true when <textarea> has focus]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, true), a[b\]c): readonly property should be true]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, false), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("contentReadOnly", false, false), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("defaultParagraphSeparator", false, p), a[b\]c): defaultParagraphSeparator value should be "p" when <textarea> has focus]
expected: FAIL
@ -248,9 +200,6 @@
[In <input> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): contentReadOnly state should be true when <input> has focus]
expected: FAIL
[In <input> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): readonly property should be true]
expected: FAIL
[In <textarea> in contenteditable, execCommand("cut", false, null), a[b\]c): The command should be enabled]
expected: FAIL
@ -329,39 +278,12 @@
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): contentReadOnly state should be true when <textarea> has focus]
expected: FAIL
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): readonly property should be true]
expected: FAIL
[In <input> in contenteditable, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): The command should be enabled]
expected: FAIL
[In <input> in contenteditable, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): The command should be enabled]
expected: FAIL
[In <input> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input> in contenteditable, execCommand("contentReadOnly", false, false), a[b\]c): The command should be enabled]
expected: FAIL
[In <input> in contenteditable, execCommand("contentReadOnly", false, false), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea> in contenteditable, execCommand("undo", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, true), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, false), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea> in contenteditable, execCommand("contentReadOnly", false, false), a[b\]c): execCommand() should return true]
expected: FAIL