Bug 1572375 - part 3: Get rid of `TextEditRules::WillUndo()`, `TextEditRules::DidUndo()`, `TextEditRules::WillRedo()` and `TextEditRules::DidRedo()` r=m_kato

`TextEditRules::WillUndo()` and `TextEditRules::WillRedo()` only check whether
the editor is readonly/disabled or not.  So, `TextEditor::UndoAsAction()` and
`TextEditor::RedoAsAction()` should do it first.

`TextEditRules::DidUndo()` and `TextEditRules::DidRedo()` only set or unset
`mPaddingBRElementForEmptyEditor` if it's restored by undo or redo.
Therefore, we can move the code into `TextEditor::UndoAsAction()` and
`TextEditor::RedoAsAction()`.

Note that this patch makes `TextEditor::UndoAsAction()` discard the result of
`TransactionManager::Undo()` because this is inconsistent from what
`TextEditor::RedoAsAction()` does and this was changed by part 5 of bug 1447924.
https://hg.mozilla.org/mozilla-central/rev/869a1445816be7f43f54f7c97f28e4c6273fa75f

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-08-09 08:57:00 +00:00
Родитель b0606032ba
Коммит d88036fb98
4 изменённых файлов: 77 добавлений и 134 удалений

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

@ -665,9 +665,7 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
// Deal with actions for which we don't need to check whether the selection is
// editable.
if (aInfo.mEditSubAction == EditSubAction::eComputeTextToOutput ||
aInfo.mEditSubAction == EditSubAction::eUndo ||
aInfo.mEditSubAction == EditSubAction::eRedo) {
if (aInfo.mEditSubAction == EditSubAction::eComputeTextToOutput) {
return TextEditRules::WillDoAction(aInfo, aCancel, aHandled);
}
@ -774,6 +772,10 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillRelativeChangeZIndex(-1, aCancel, aHandled);
case EditSubAction::eIncreaseZIndex:
return WillRelativeChangeZIndex(1, aCancel, aHandled);
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
return TextEditRules::WillDoAction(aInfo, aCancel, aHandled);
}
@ -807,6 +809,13 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
}
return DidAbsolutePosition();
}
case EditSubAction::eInsertElement:
case EditSubAction::eInsertQuotedText:
return NS_OK;
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
return TextEditRules::DidDoAction(aInfo, aResult);
}

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

@ -318,10 +318,6 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillSetText(aCancel, aHandled, aInfo.inString, aInfo.maxLength);
case EditSubAction::eDeleteSelectedContent:
return WillDeleteSelection(aInfo.collapsedAction, aCancel, aHandled);
case EditSubAction::eUndo:
return WillUndo(aCancel, aHandled);
case EditSubAction::eRedo:
return WillRedo(aCancel, aHandled);
case EditSubAction::eSetTextProperty:
return WillSetTextProperty(aCancel, aHandled);
case EditSubAction::eRemoveTextProperty:
@ -343,6 +339,8 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return rv;
}
case EditSubAction::eInsertElement:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
@ -365,10 +363,11 @@ nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
switch (aInfo.mEditSubAction) {
case EditSubAction::eDeleteSelectedContent:
return DidDeleteSelection();
case EditSubAction::eInsertElement:
case EditSubAction::eUndo:
return DidUndo(aResult);
case EditSubAction::eRedo:
return DidRedo(aResult);
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;
@ -1124,89 +1123,6 @@ nsresult TextEditRules::DidDeleteSelection() {
return err.StealNSResult();
}
nsresult TextEditRules::WillUndo(bool* aCancel, bool* aHandled) {
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
return NS_ERROR_INVALID_ARG;
}
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
return NS_OK;
}
nsresult TextEditRules::DidUndo(nsresult aResult) {
MOZ_ASSERT(IsEditorDataAvailable());
// If aResult is an error, we return it.
if (NS_WARN_IF(NS_FAILED(aResult))) {
return aResult;
}
Element* rootElement = TextEditorRef().GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
// The idea here is to see if the magic empty node has suddenly reappeared as
// the result of the undo. If it has, set our state so we remember it.
// There is a tradeoff between doing here and at redo, or doing it everywhere
// else that might care. Since undo and redo are relatively rare, it makes
// sense to take the (small) performance hit here.
nsIContent* node = TextEditorRef().GetLeftmostChild(rootElement);
if (node && EditorBase::IsPaddingBRElementForEmptyEditor(*node)) {
TextEditorRef().mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(node);
} else {
TextEditorRef().mPaddingBRElementForEmptyEditor = nullptr;
}
return aResult;
}
nsresult TextEditRules::WillRedo(bool* aCancel, bool* aHandled) {
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
return NS_ERROR_INVALID_ARG;
}
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
return NS_OK;
}
nsresult TextEditRules::DidRedo(nsresult aResult) {
MOZ_ASSERT(IsEditorDataAvailable());
if (NS_FAILED(aResult)) {
return aResult; // if aResult is an error, we return it.
}
Element* rootElement = TextEditorRef().GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIHTMLCollection> nodeList =
rootElement->GetElementsByTagName(NS_LITERAL_STRING("br"));
MOZ_ASSERT(nodeList);
uint32_t len = nodeList->Length();
if (len != 1) {
// only in the case of one br could there be the padding <br> element.
TextEditorRef().mPaddingBRElementForEmptyEditor = nullptr;
return NS_OK;
}
Element* brElement = nodeList->Item(0);
if (EditorBase::IsPaddingBRElementForEmptyEditor(*brElement)) {
TextEditorRef().mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(brElement);
} else {
TextEditorRef().mPaddingBRElementForEmptyEditor = nullptr;
}
return NS_OK;
}
nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
nsAString* aOutString, uint32_t aFlags,
bool* aCancel, bool* aHandled) {

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

@ -224,12 +224,6 @@ class TextEditRules {
nsresult WillRemoveTextProperty(bool* aCancel, bool* aHandled);
nsresult WillUndo(bool* aCancel, bool* aHandled);
nsresult DidUndo(nsresult aResult);
nsresult WillRedo(bool* aCancel, bool* aHandled);
nsresult DidRedo(nsresult aResult);
/**
* Called prior to nsIEditor::OutputToString.
*

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

@ -1514,6 +1514,10 @@ TextEditor::SetNewlineHandling(int32_t aNewlineHandling) {
}
nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
if (aCount == 0 || IsReadonly() || IsDisabled()) {
return NS_OK;
}
// If we don't have transaction in the undo stack, we shouldn't notify
// anybody of trying to undo since it's not useful notification but we
// need to pay some runtime cost.
@ -1534,9 +1538,6 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying.
RefPtr<TextEditRules> rules(mRules);
AutoUpdateViewBatch preventSelectionChangeEvent(*this);
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
@ -1544,26 +1545,36 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
return NS_ERROR_FAILURE;
}
nsresult rv;
nsresult rv = NS_OK;
{
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
*this, EditSubAction::eUndo, nsIEditor::eNone);
EditSubActionInfo subActionInfo(EditSubAction::eUndo);
bool cancel, handled;
rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (!cancel && NS_SUCCEEDED(rv)) {
RefPtr<TransactionManager> transactionManager(mTransactionManager);
for (uint32_t i = 0; i < aCount; ++i) {
rv = transactionManager->Undo();
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
DoAfterUndoTransaction();
RefPtr<TransactionManager> transactionManager(mTransactionManager);
for (uint32_t i = 0; i < aCount; ++i) {
if (NS_WARN_IF(NS_FAILED(transactionManager->Undo()))) {
break;
}
DoAfterUndoTransaction();
}
if (NS_WARN_IF(!mRootElement)) {
rv = NS_ERROR_FAILURE;
} else {
// The idea here is to see if the magic empty node has suddenly
// reappeared as the result of the undo. If it has, set our state
// so we remember it. There is a tradeoff between doing here and
// at redo, or doing it everywhere else that might care. Since undo
// and redo are relatively rare, it makes sense to take the (small)
// performance hit here.
nsIContent* leftMostChild = GetLeftmostChild(mRootElement);
if (leftMostChild &&
EditorBase::IsPaddingBRElementForEmptyEditor(*leftMostChild)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(leftMostChild);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}
rv = rules->DidDoAction(subActionInfo, rv);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"TextEditRules::DidDoAction() failed");
}
}
@ -1575,6 +1586,10 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
}
nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
if (aCount == 0 || IsReadonly() || IsDisabled()) {
return NS_OK;
}
// If we don't have transaction in the redo stack, we shouldn't notify
// anybody of trying to redo since it's not useful notification but we
// need to pay some runtime cost.
@ -1595,9 +1610,6 @@ nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying.
RefPtr<TextEditRules> rules(mRules);
AutoUpdateViewBatch preventSelectionChangeEvent(*this);
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
@ -1605,26 +1617,38 @@ nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
return NS_ERROR_FAILURE;
}
nsresult rv;
nsresult rv = NS_OK;
{
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
*this, EditSubAction::eRedo, nsIEditor::eNone);
EditSubActionInfo subActionInfo(EditSubAction::eRedo);
bool cancel, handled;
rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (!cancel && NS_SUCCEEDED(rv)) {
RefPtr<TransactionManager> transactionManager(mTransactionManager);
for (uint32_t i = 0; i < aCount; ++i) {
nsresult rv = transactionManager->Redo();
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
DoAfterRedoTransaction();
RefPtr<TransactionManager> transactionManager(mTransactionManager);
for (uint32_t i = 0; i < aCount; ++i) {
if (NS_WARN_IF(NS_FAILED(transactionManager->Redo()))) {
break;
}
DoAfterRedoTransaction();
}
if (NS_WARN_IF(!mRootElement)) {
rv = NS_ERROR_FAILURE;
} else {
// We may take empty <br> element for empty editor back with this redo.
// We need to store it again.
// XXX Looks like that this is too slow if there are a lot of nodes.
// Shouldn't we just scan children in the root?
nsCOMPtr<nsIHTMLCollection> nodeList =
mRootElement->GetElementsByTagName(NS_LITERAL_STRING("br"));
MOZ_ASSERT(nodeList);
Element* brElement =
nodeList->Length() == 1 ? nodeList->Item(0) : nullptr;
if (brElement &&
EditorBase::IsPaddingBRElementForEmptyEditor(*brElement)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(brElement);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}
rv = rules->DidDoAction(subActionInfo, rv);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"TextEditRules::DidDoAction() failed");
}
}