Bug 1574852 - part 112: Make `TextEditor::InsertWithQuotationsAsSubAction()` virtual and `HTMLEditor` override it r=m_kato

It requires different preparation in `TextEditor` and `HTMLEditor` but it's
not split.  Therefore, we should make it virtual and override it to use
different preparation code.  Fortunately, its code is enough simple to
duplicate.

Additionally, this removes unnecessary code from `TextEditRules` and
`HTMLEditRules` including `WillDoAction()` and `DidDoAction()`!

Differential Revision: https://phabricator.services.mozilla.com/D45495

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-09-17 02:47:26 +00:00
Родитель 119bd889bd
Коммит 5b0eb574be
8 изменённых файлов: 88 добавлений и 329 удалений

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

@ -742,136 +742,6 @@ EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
return EditActionIgnored();
}
nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
bool* aHandled) {
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
MOZ_ASSERT(aCancel);
MOZ_ASSERT(aHandled);
*aCancel = false;
*aHandled = false;
AutoSafeEditorData setData(*this, *mHTMLEditor);
EditActionResult result = HTMLEditorRef().CanHandleHTMLEditSubAction();
if (NS_WARN_IF(result.Failed())) {
return result.Rv();
}
if (result.Canceled()) {
if (!SelectionRefPtr()->RangeCount()) {
// Special case. If there is no selection range, we do nothing here,
// but return as not handled nor canceled.
return NS_OK;
}
*aCancel = true;
return NS_OK;
}
switch (aInfo.mEditSubAction) {
case EditSubAction::eInsertQuotedText: {
*aCancel = IsReadonly() || IsDisabled();
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING(
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
return NS_OK;
}
if (!SelectionRefPtr()->IsCollapsed()) {
return NS_OK;
}
rv = MOZ_KnownLive(HTMLEditorRef()).EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING("EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
return NS_OK;
}
rv = MOZ_KnownLive(HTMLEditorRef()).PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
return NS_OK;
}
case EditSubAction::eComputeTextToOutput:
case EditSubAction::eCreateOrChangeDefinitionListItem:
case EditSubAction::eCreateOrChangeList:
case EditSubAction::eCreateOrRemoveBlock:
case EditSubAction::eDecreaseZIndex:
case EditSubAction::eDeleteSelectedContent:
case EditSubAction::eIncreaseZIndex:
case EditSubAction::eIndent:
case EditSubAction::eInsertElement:
case EditSubAction::eInsertHTMLSource:
case EditSubAction::eInsertParagraphSeparator:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eOutdent:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
case EditSubAction::eRemoveTextProperty:
case EditSubAction::eSetOrClearAlignment:
case EditSubAction::eSetPositionToAbsolute:
case EditSubAction::eSetPositionToStatic:
case EditSubAction::eSetTextProperty:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
return TextEditRules::WillDoAction(aInfo, aCancel, aHandled);
}
}
nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
nsresult aResult) {
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
AutoSafeEditorData setData(*this, *mHTMLEditor);
switch (aInfo.mEditSubAction) {
case EditSubAction::eInsertQuotedText:
return NS_OK;
case EditSubAction::eComputeTextToOutput:
case EditSubAction::eCreateOrChangeDefinitionListItem:
case EditSubAction::eCreateOrChangeList:
case EditSubAction::eCreateOrRemoveBlock:
case EditSubAction::eDecreaseZIndex:
case EditSubAction::eDeleteSelectedContent:
case EditSubAction::eIncreaseZIndex:
case EditSubAction::eIndent:
case EditSubAction::eInsertElement:
case EditSubAction::eInsertHTMLSource:
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertParagraphSeparator:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eOutdent:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
case EditSubAction::eRemoveTextProperty:
case EditSubAction::eSetOrClearAlignment:
case EditSubAction::eSetPositionToAbsolute:
case EditSubAction::eSetPositionToStatic:
case EditSubAction::eSetTextProperty:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
return TextEditRules::DidDoAction(aInfo, aResult);
}
}
bool HTMLEditRules::DocumentIsEmpty() const {
// XXX This is wrong. Even if there is no padding <br> element for empty
// editor, the editor may be empty.

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

@ -60,17 +60,6 @@ class HTMLEditRules : public TextEditRules {
virtual nsresult DetachEditor() override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual nsresult BeforeEdit() override;
MOZ_CAN_RUN_SCRIPT virtual nsresult AfterEdit() override;
// NOTE: Don't mark WillDoAction() nor DidDoAction() as MOZ_CAN_RUN_SCRIPT
// because they are too generic and doing it makes a lot of public
// editor methods marked as MOZ_CAN_RUN_SCRIPT too, but some of them
// may not causes running script. So, ideal fix must be that we make
// each method callsed by this method public.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
bool* aHandled) override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult DidDoAction(EditSubActionInfo& aInfo,
nsresult aResult) override;
virtual bool DocumentIsEmpty() const override;
/**

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

@ -3194,6 +3194,16 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT
nsresult PasteInternal(int32_t aClipboardType, bool aDispatchPasteEvent);
/**
* InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">"
* to start of every line.
*
* @param aQuotedText String to insert. This will be quoted by ">"
* automatically.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE virtual nsresult
InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) final;
/**
* InsertAsCitedQuotationInternal() inserts a <blockquote> element whose
* cite attribute is aCitation and whose content is aQuotedText.

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

@ -1872,6 +1872,68 @@ nsresult HTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType) {
return rv;
}
nsresult HTMLEditor::InsertWithQuotationsAsSubAction(
const nsAString& aQuotedText) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (IsReadonly() || IsDisabled()) {
return NS_OK;
}
EditActionResult result = CanHandleHTMLEditSubAction();
if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
return result.Rv();
}
UndefineCaretBidiLevel();
// Let the citer quote it for us:
nsString quotedStuff;
nsresult rv = InternetCiter::GetCiteString(aQuotedText, quotedStuff);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// It's best to put a blank line after the quoted text so that mails
// written without thinking won't be so ugly.
if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != char16_t('\n'))) {
quotedStuff.Append(char16_t('\n'));
}
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eInsertText, nsIEditor::eNext);
rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureNoPaddingBRElementForEmptyEditor() failed, but ignored");
if (NS_SUCCEEDED(rv) && SelectionRefPtr()->IsCollapsed()) {
nsresult rv = EnsureCaretNotAfterPaddingBRElement();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureCaretNotAfterPaddingBRElement() failed, but ignored");
if (NS_SUCCEEDED(rv)) {
nsresult rv = PrepareInlineStylesForCaret();
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"PrepareInlineStylesForCaret() failed, but ignored");
}
}
rv = InsertTextAsSubAction(quotedStuff);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InsertTextAsSubAction() failed");
return rv;
}
nsresult HTMLEditor::InsertTextWithQuotations(
const nsAString& aStringToInsert) {
AutoEditActionDataSetter editActionData(*this, EditAction::eInsertText);

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

@ -45,12 +45,6 @@ namespace mozilla {
using namespace dom;
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
if (IsReadonly() || IsDisabled()) { \
*aCancel = true; \
return NS_OK; \
}
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
if (IsReadonly() || IsDisabled()) { \
return EditActionCanceled(NS_OK); \
@ -186,75 +180,6 @@ nsresult TextEditRules::AfterEdit() {
return NS_OK;
}
nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
bool* aHandled) {
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
MOZ_ASSERT(aCancel);
MOZ_ASSERT(aHandled);
*aCancel = false;
*aHandled = false;
AutoSafeEditorData setData(*this, *mTextEditor);
// my kingdom for dynamic cast
switch (aInfo.mEditSubAction) {
case EditSubAction::eInsertQuotedText: {
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// XXX Do we need to support paste-as-quotation in password editor (and
// also in single line editor)?
TextEditorRef().MaybeDoAutoPasswordMasking();
nsresult rv = MOZ_KnownLive(TextEditorRef())
.EnsureNoPaddingBRElementForEmptyEditor();
NS_WARNING_ASSERTION(NS_FAILED(rv),
"Failed to remove padding <br> element");
return rv;
}
case EditSubAction::eComputeTextToOutput:
case EditSubAction::eDeleteSelectedContent:
case EditSubAction::eInsertElement:
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eSetText:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
return NS_ERROR_FAILURE;
}
}
nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
nsresult aResult) {
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
switch (aInfo.mEditSubAction) {
case EditSubAction::eComputeTextToOutput:
case EditSubAction::eDeleteSelectedContent:
case EditSubAction::eInsertElement:
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eSetText:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
// Don't fail on transactions we don't handle here!
return NS_OK;
}
}
bool TextEditRules::DocumentIsEmpty() const {
bool retVal = false;
if (!mTextEditor || NS_FAILED(mTextEditor->IsEmpty(&retVal))) {
@ -1057,15 +982,6 @@ EditActionResult TextEditor::TruncateInsertionStringForMaxLength(
return EditActionHandled();
}
bool TextEditRules::IsPasswordEditor() const {
return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
}
bool TextEditRules::IsMaskingPassword() const {
MOZ_ASSERT(IsPasswordEditor());
return mTextEditor ? mTextEditor->IsMaskingPassword() : true;
}
bool TextEditRules::IsSingleLineEditor() const {
return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
}
@ -1074,17 +990,6 @@ bool TextEditRules::IsPlaintextEditor() const {
return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
}
bool TextEditRules::IsReadonly() const {
return mTextEditor ? mTextEditor->IsReadonly() : false;
}
bool TextEditRules::IsDisabled() const {
return mTextEditor ? mTextEditor->IsDisabled() : false;
}
bool TextEditRules::IsMailEditor() const {
return mTextEditor ? mTextEditor->IsMailEditor() : false;
}
bool TextEditor::CanEchoPasswordNow() const {
if (!LookAndFeel::GetEchoPassword() ||
(mFlags & nsIPlaintextEditor::eEditorDontEchoPassword)) {

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

@ -21,7 +21,6 @@
namespace mozilla {
class EditSubActionInfo;
class HTMLEditor;
class HTMLEditRules;
namespace dom {
@ -79,16 +78,6 @@ class TextEditRules {
virtual nsresult DetachEditor();
virtual nsresult BeforeEdit();
MOZ_CAN_RUN_SCRIPT virtual nsresult AfterEdit();
// NOTE: Don't mark WillDoAction() nor DidDoAction() as MOZ_CAN_RUN_SCRIPT
// because they are too generic and doing it makes a lot of public
// editor methods marked as MOZ_CAN_RUN_SCRIPT too, but some of them
// may not causes running script. So, ideal fix must be that we make
// each method callsed by this method public.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
bool* aHandled);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult DidDoAction(EditSubActionInfo& aInfo, nsresult aResult);
/**
* Return false if the editor has non-empty text nodes or non-text
@ -97,8 +86,6 @@ class TextEditRules {
*/
virtual bool DocumentIsEmpty() const;
bool DontEchoPassword() const;
protected:
virtual ~TextEditRules() = default;
@ -111,13 +98,6 @@ class TextEditRules {
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult CreateTrailingBRIfNeeded();
/**
* Creates a padding <br> element for empty editor if the root element has no
* editable content.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
CreatePaddingBRElementForEmptyEditorIfNeeded();
/**
* CollapseSelectionToTrailingBRIfNeeded() collapses selection after the
* text node if:
@ -128,13 +108,8 @@ class TextEditRules {
*/
MOZ_MUST_USE nsresult CollapseSelectionToTrailingBRIfNeeded();
bool IsPasswordEditor() const;
bool IsMaskingPassword() const;
bool IsSingleLineEditor() const;
bool IsPlaintextEditor() const;
bool IsReadonly() const;
bool IsDisabled() const;
bool IsMailEditor() const;
private:
TextEditor* MOZ_NON_OWNING_REF mTextEditor;
@ -219,54 +194,6 @@ class TextEditRules {
bool mIsHTMLEditRules;
};
/**
* An object to encapsulate any additional info needed to be passed
* to rules system by the editor.
* TODO: This class (almost struct, though) is ugly and its size isn't
* optimized. Should be refined later.
*/
class MOZ_STACK_CLASS EditSubActionInfo final {
public:
explicit EditSubActionInfo(EditSubAction aEditSubAction)
: mEditSubAction(aEditSubAction),
inString(nullptr),
outString(nullptr),
outputFormat(nullptr),
maxLength(-1),
flags(0),
collapsedAction(nsIEditor::eNext),
stripWrappers(nsIEditor::eStrip),
entireList(false),
bulletType(nullptr),
alignType(nullptr),
blockType(nullptr) {}
EditSubAction mEditSubAction;
// EditSubAction::eInsertText / EditSubAction::eInsertTextComingFromIME
const nsAString* inString;
nsAString* outString;
const nsAString* outputFormat;
int32_t maxLength;
// EditSubAction::eComputeTextToOutput
uint32_t flags;
// EditSubAction::eDeleteSelectedContent
nsIEditor::EDirection collapsedAction;
nsIEditor::EStripWrappers stripWrappers;
// EditSubAction::eCreateOrChangeList
bool entireList;
const nsAString* bulletType;
// EditSubAction::eSetOrClearAlignment
const nsAString* alignType;
// EditSubAction::eCreateOrRemoveBlock
const nsAString* blockType;
};
} // namespace mozilla
#endif // #ifndef mozilla_TextEditRules_h

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

@ -651,7 +651,6 @@ nsresult TextEditor::DeleteSelectionAsSubAction(EDirection aDirectionAndAmount,
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eDeleteSelectedContent, aDirectionAndAmount);
EditSubActionInfo subActionInfo(EditSubAction::eDeleteSelectedContent);
EditActionResult result =
HandleDeleteSelection(aDirectionAndAmount, aStripWrappers);
@ -1945,10 +1944,10 @@ nsresult TextEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
editActionData.SetData(stuffToPaste);
if (!stuffToPaste.IsEmpty()) {
AutoPlaceholderBatch treatAsOneTransaction(*this);
rv = InsertWithQuotationsAsSubAction(stuffToPaste);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
nsresult rv = InsertWithQuotationsAsSubAction(stuffToPaste);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"InsertWithQuotationsAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
}
}
return NS_OK;
@ -1958,8 +1957,9 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
const nsAString& aQuotedText) {
MOZ_ASSERT(IsEditActionDataAvailable());
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
if (IsReadonly() || IsDisabled()) {
return NS_OK;
}
// Let the citer quote it for us:
nsString quotedStuff;
@ -1977,22 +1977,18 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eInsertText, nsIEditor::eNext);
EditSubActionInfo subActionInfo(EditSubAction::eInsertQuotedText);
bool cancel, handled;
rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
// XXX Do we need to support paste-as-quotation in password editor (and
// also in single line editor)?
MaybeDoAutoPasswordMasking();
rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (cancel) {
return NS_OK; // Rules canceled the operation.
}
MOZ_ASSERT(!handled, "WillDoAction() shouldn't handle in this case");
rv = InsertTextAsSubAction(quotedStuff);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// XXX Why don't we call TextEditRules::DidDoAction()?
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InsertTextAsSubAction() failed");
return rv;
}
nsresult TextEditor::SharedOutputString(uint32_t aFlags, bool* aIsCollapsed,

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

@ -769,7 +769,7 @@ class TextEditor : public EditorBase,
* @param aQuotedText String to insert. This will be quoted by ">"
* automatically.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE virtual nsresult
InsertWithQuotationsAsSubAction(const nsAString& aQuotedText);
/**