Bug 1220696 - part 4: Make Document consider whether the target is editable or not-editable with target editor r=smaug

Currently, `Document` checks it only with whether the document is editable
or not.  Only with this check, `execCommand` and the other related methods
work only when there is `contenteditable`.

Therefore, this patch makes it to check whether the target is editable or not
with target editor.

Differential Revision: https://phabricator.services.mozilla.com/D108570
This commit is contained in:
Masayuki Nakano 2021-03-24 01:55:08 +00:00
parent f956ee81ea
commit b2fd51c051
3 changed files with 33 additions and 250 deletions

View File

@ -5103,6 +5103,21 @@ TextEditor* Document::AutoEditorCommandTarget::GetTargetEditor() const {
return nullptr;
}
bool Document::AutoEditorCommandTarget::IsEditable(Document* aDocument) const {
if (RefPtr<Document> doc = aDocument->GetInProcessParentDocument()) {
// Make sure frames are up to date, since that can affect whether
// we're editable.
doc->FlushPendingNotifications(FlushType::Frames);
}
TextEditor* targetEditor = GetTargetEditor();
if (targetEditor && targetEditor->IsTextEditor()) {
// FYI: When `disabled` attribute is set, `TextEditor` treats it as
// "readonly" too.
return !targetEditor->IsReadonly();
}
return aDocument->IsEditingOn();
}
bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
TextEditor* targetEditor = GetTargetEditor();
if (!targetEditor) {
@ -5187,11 +5202,6 @@ bool Document::ExecCommand(const nsAString& aHTMLCommandName, bool aShowUI,
return false;
}
// if editing is not on, bail
if (commandData.IsAvailableOnlyWhenEditable() && !IsEditingOnAfterFlush()) {
return false;
}
if (commandData.mCommand == Command::GetHTML) {
return false;
}
@ -5217,6 +5227,11 @@ bool Document::ExecCommand(const nsAString& aHTMLCommandName, bool aShowUI,
// by order of controllers in `nsCommandManager::GetControllerForCommand()`.
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (commandData.IsAvailableOnlyWhenEditable() &&
!editCommandTarget.IsEditable(this)) {
return false;
}
if (editCommandTarget.DoNothing()) {
return false;
}
@ -5359,13 +5374,12 @@ bool Document::QueryCommandEnabled(const nsAString& aHTMLCommandName,
return false;
}
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
return false;
}
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (editCommandTarget.IsEditor()) {
return editCommandTarget.IsCommandEnabled();
}
@ -5399,13 +5413,11 @@ bool Document::QueryCommandIndeterm(const nsAString& aHTMLCommandName,
return false;
}
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
return false;
}
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
return false;
}
RefPtr<nsCommandParams> params = new nsCommandParams();
if (editCommandTarget.IsEditor()) {
if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
@ -5449,11 +5461,6 @@ bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
return false;
}
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
return false;
}
if (aHTMLCommandName.LowerCaseEqualsLiteral("usecss")) {
// Per spec, state is supported for styleWithCSS but not useCSS, so we just
// return false always.
@ -5462,6 +5469,9 @@ bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
return false;
}
RefPtr<nsCommandParams> params = new nsCommandParams();
if (editCommandTarget.IsEditor()) {
if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
@ -5588,13 +5598,11 @@ void Document::QueryCommandValue(const nsAString& aHTMLCommandName,
return;
}
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
return;
}
RefPtr<nsPresContext> presContext = GetPresContext();
AutoEditorCommandTarget editCommandTarget(presContext, commandData);
if (!editCommandTarget.IsEditable(this)) {
return;
}
RefPtr<nsCommandParams> params = new nsCommandParams();
// FYI: Only GetHTML command is not implemented by editor. Use window's
// command table instead.
@ -5655,17 +5663,6 @@ void Document::QueryCommandValue(const nsAString& aHTMLCommandName,
CopyUTF8toUTF16(result, aValue);
}
bool Document::IsEditingOnAfterFlush() {
RefPtr<Document> doc = GetInProcessParentDocument();
if (doc) {
// Make sure frames are up to date, since that can affect whether
// we're editable.
doc->FlushPendingNotifications(FlushType::Frames);
}
return IsEditingOn();
}
void Document::MaybeEditingStateChanged() {
if (!mPendingMaybeEditingStateChanged && mMayStartLayout &&
mUpdateNestLevel == 0 && (mContentEditableCount > 0) != IsEditingOn()) {

View File

@ -1578,11 +1578,6 @@ class Document : public nsINode,
*/
void DisconnectNodeTree();
/**
* Like IsEditingOn(), but will flush as needed first.
*/
bool IsEditingOnAfterFlush();
/**
* MaybeDispatchCheckKeyPressEventModelEvent() dispatches
* "CheckKeyPressEventModel" event to check whether we should dispatch
@ -4197,6 +4192,7 @@ class Document : public nsINode,
delete;
bool DoNothing() const { return mDoNothing; }
MOZ_CAN_RUN_SCRIPT bool IsEditable(Document* aDocument) const;
bool IsEditor() const {
MOZ_ASSERT_IF(mEditorCommand, mActiveEditor || mHTMLEditor);
return !!mEditorCommand;

View File

@ -35,102 +35,12 @@
[In <input>, execCommand("paste", false, null), a[\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("delete", false, null), ab[\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("delete", false, null), ab[\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("delete", false, null), ab[\]c): <input>.value should be "a[\]c"]
expected: FAIL
[In <input>, execCommand("delete", false, null), ab[\]c): input.inputType should be deleteContentBackward]
expected: FAIL
[In <input>, execCommand("delete", false, null), ab[\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("delete", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("delete", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("delete", false, null), a[b\]c): <input>.value should be "a[\]c"]
expected: FAIL
[In <input>, execCommand("delete", false, null), a[b\]c): input.inputType should be deleteContentBackward]
expected: FAIL
[In <input>, execCommand("delete", false, null), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[b\]c): <input>.value should be "a[\]c"]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[b\]c): input.inputType should be deleteContentForward]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[\]bc): The command should be enabled]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[\]bc): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[\]bc): <input>.value should be "a[\]c"]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[\]bc): input.inputType should be deleteContentForward]
expected: FAIL
[In <input>, execCommand("forwarddelete", false, null), a[\]bc): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("selectall", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("selectall", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("selectall", false, null), a[b\]c): <input>.value should be "[abc\]"]
expected: FAIL
[In <input>, execCommand("undo", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("undo", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("undo", false, null), a[b\]c): input.inputType should be historyUndo]
expected: FAIL
[In <input>, execCommand("undo", false, null), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("redo", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("redo", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("redo", false, null), a[b\]c): <input>.value should be "a[\]c"]
expected: FAIL
[In <input>, execCommand("redo", false, null), a[b\]c): input.inputType should be historyRedo]
expected: FAIL
[In <input>, execCommand("redo", false, null), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): The command should be enabled]
expected: FAIL
@ -146,21 +56,6 @@
[In <input>, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): input.target should be [object HTMLInputElement\]]
expected: FAIL
[In <input>, execCommand("inserttext", false, **inserted**), a[b\]c): The command should be enabled]
expected: FAIL
[In <input>, execCommand("inserttext", false, **inserted**), a[b\]c): execCommand() should return true]
expected: FAIL
[In <input>, execCommand("inserttext", false, **inserted**), a[b\]c): <input>.value should be "a**inserted**[\]c"]
expected: FAIL
[In <input>, execCommand("inserttext", false, **inserted**), a[b\]c): input.inputType should be insertText]
expected: FAIL
[In <input>, execCommand("inserttext", false, **inserted**), 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
@ -227,102 +122,12 @@
[In <textarea>, execCommand("paste", false, null), a[\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), ab[\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), ab[\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), ab[\]c): <textarea>.value should be "a[\]c"]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), ab[\]c): input.inputType should be deleteContentBackward]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), ab[\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), a[b\]c): <textarea>.value should be "a[\]c"]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), a[b\]c): input.inputType should be deleteContentBackward]
expected: FAIL
[In <textarea>, execCommand("delete", false, null), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[b\]c): <textarea>.value should be "a[\]c"]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[b\]c): input.inputType should be deleteContentForward]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[\]bc): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[\]bc): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[\]bc): <textarea>.value should be "a[\]c"]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[\]bc): input.inputType should be deleteContentForward]
expected: FAIL
[In <textarea>, execCommand("forwarddelete", false, null), a[\]bc): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("selectall", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("selectall", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("selectall", false, null), a[b\]c): <textarea>.value should be "[abc\]"]
expected: FAIL
[In <textarea>, execCommand("undo", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("undo", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("undo", false, null), a[b\]c): input.inputType should be historyUndo]
expected: FAIL
[In <textarea>, execCommand("undo", false, null), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("redo", false, null), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("redo", false, null), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("redo", false, null), a[b\]c): <textarea>.value should be "a[\]c"]
expected: FAIL
[In <textarea>, execCommand("redo", false, null), a[b\]c): input.inputType should be historyRedo]
expected: FAIL
[In <textarea>, execCommand("redo", false, null), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): The command should be enabled]
expected: FAIL
@ -338,21 +143,6 @@
[In <textarea>, execCommand("inserthtml", false, <b>inserted</b>), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("inserttext", false, **inserted**), a[b\]c): The command should be enabled]
expected: FAIL
[In <textarea>, execCommand("inserttext", false, **inserted**), a[b\]c): execCommand() should return true]
expected: FAIL
[In <textarea>, execCommand("inserttext", false, **inserted**), a[b\]c): <textarea>.value should be "a**inserted**[\]c"]
expected: FAIL
[In <textarea>, execCommand("inserttext", false, **inserted**), a[b\]c): input.inputType should be insertText]
expected: FAIL
[In <textarea>, execCommand("inserttext", false, **inserted**), a[b\]c): input.target should be [object HTMLTextAreaElement\]]
expected: FAIL
[In <textarea>, execCommand("insertparagraph", false, null), a[b\]c): The command should be enabled]
expected: FAIL