Bug 1473515 - Make AutoDisableUndo restores enabled state of undo/redo with previous number of maximum transactions r?m_kato
Calling EditorBase::EnableUndoRedo() without argument means that editor supports
unlimited undo/redo stack. AutoDisableUndo class calls it without argument
when it needs to restore undo/redo feature.
However, <input type="text"> and <textarea> limits number of maximum
transactions up to 1,000, perhaps for footprint. So, AutoDisableUndo should
store the last number of maximum transactions before disabling undo/redo from
the constructor.
MozReview-Commit-ID: CoI6ZXyTd3X
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -162,43 +162,53 @@ private:
};
class MOZ_RAII AutoDisableUndo final
{
public:
explicit AutoDisableUndo(TextEditor* aTextEditor
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mTextEditor(aTextEditor)
- , mPreviousEnabled(true)
+ , mNumberOfMaximumTransactions(0)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(mTextEditor);
- mPreviousEnabled = mTextEditor->IsUndoRedoEnabled();
+ mNumberOfMaximumTransactions =
+ mTextEditor ? mTextEditor->NumberOfMaximumTransactions() : 0;
DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
NS_WARNING_ASSERTION(disabledUndoRedo,
"Failed to disable undo/redo transactions");
}
~AutoDisableUndo()
{
- if (mPreviousEnabled) {
- DebugOnly<bool> enabledUndoRedo = mTextEditor->EnableUndoRedo();
+ // Don't change enable/disable of undo/redo if it's enabled after
+ // it's disabled by the constructor because we shouldn't change
+ // the maximum undo/redo count to the old value.
+ if (mTextEditor->IsUndoRedoEnabled()) {
+ return;
+ }
+ // If undo/redo was enabled, mNumberOfMaximumTransactions is -1 or lager
+ // than 0. Only when it's 0, it was disabled.
+ if (mNumberOfMaximumTransactions) {
+ DebugOnly<bool> enabledUndoRedo =
+ mTextEditor->EnableUndoRedo(mNumberOfMaximumTransactions);
NS_WARNING_ASSERTION(enabledUndoRedo,
"Failed to enable undo/redo transactions");
} else {
DebugOnly<bool> disabledUndoRedo = mTextEditor->DisableUndoRedo();
NS_WARNING_ASSERTION(disabledUndoRedo,
"Failed to disable undo/redo transactions");
}
}
private:
TextEditor* mTextEditor;
- bool mPreviousEnabled;
+ int32_t mNumberOfMaximumTransactions;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/*static*/
bool
nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
nsITextControlElement::nsHTMLTextWrap& aWrapProp)
{
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -384,21 +384,31 @@ public:
return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0;
}
size_t NumberOfRedoItems() const
{
return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0;
}
/**
+ * Returns number of maximum undo/redo transactions.
+ */
+ int32_t NumberOfMaximumTransactions() const
+ {
+ return mTransactionManager ?
+ mTransactionManager->NumberOfMaximumTransactions() : 0;
+ }
+
+ /**
* Returns true if this editor can store transactions for undo/redo.
*/
bool IsUndoRedoEnabled() const
{
- return !!mTransactionManager;
+ return mTransactionManager &&
+ mTransactionManager->NumberOfMaximumTransactions();
}
/**
* Return true if it's possible to undo/redo right now.
*/
bool CanUndo() const
{
return IsUndoRedoEnabled() && NumberOfUndoItems() > 0;
@@ -419,18 +429,16 @@ public:
}
return mTransactionManager->EnableUndoRedo(aMaxTransactionCount);
}
bool DisableUndoRedo()
{
if (!mTransactionManager) {
return true;
}
- // XXX Even we clear the transaction manager, IsUndoRedoEnabled() keep
- // returning true...
return mTransactionManager->DisableUndoRedo();
}
bool ClearUndoRedo()
{
if (!mTransactionManager) {
return true;
}
return mTransactionManager->ClearUndoRedo();
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -274,15 +274,16 @@ skip-if = os == 'android'
[test_inlineTableEditing.html]
[test_insertParagraph_in_inline_editing_host.html]
[test_keypress_untrusted_event.html]
[test_objectResizing.html]
[test_root_element_replacement.html]
[test_select_all_without_body.html]
[test_spellcheck_pref.html]
skip-if = toolkit == 'android'
+[test_undo_redo_stack_after_setting_value.html]
[test_backspace_vs.html]
[test_css_chrome_load_access.html]
skip-if = toolkit == 'android' # chrome urls not available due to packaging
[test_selection_move_commands.html]
[test_pasteImgTextarea.html]
skip-if = toolkit == 'android' # bug 1299578
[test_execCommandPaste_noTarget.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_undo_redo_stack_after_setting_value.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1473515
+-->
+<html>
+<head>
+ <title>Test for Bug 1473515</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1473515">Mozilla Bug 1473515</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<input id="input">
+<textarea id="textarea"></textarea>
+
+<pre id="test">
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ let editableElements = [
+ document.getElementById("input"),
+ document.getElementById("textarea"),
+ ];
+ for (let editableElement of editableElements) {
+ editableElement.focus();
+ synthesizeKey("a");
+ synthesizeKey("c");
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("b");
+ let editor = SpecialPowers.wrap(editableElement).editor;
+ let transactionManager = editor.transactionManager;
+ is(transactionManager.numberOfUndoItems, 2,
+ editableElement.tagName + ": Initially, there should be 2 undo items");
+ // Defined as nsITextControlElement::DEFAULT_UNDO_CAP
+ is(transactionManager.maxTransactionCount, 1000,
+ editableElement.tagName + ": Initially, transaction manager should be able to have 1,000 undo items");
+ editableElement.value = "def";
+ is(transactionManager.numberOfUndoItems, 0,
+ editableElement.tagName + ": After setting value, all undo items must be deleted");
+ is(transactionManager.maxTransactionCount, 1000,
+ editableElement.tagName + ": After setting value, maximum transaction count should be restored to the previous value");
+ synthesizeKey("a");
+ synthesizeKey("z", { accelKey: true });
+ is(editableElement.value, "def",
+ editableElement.tagName + ": undo should work after setting value");
+
+ // Disable undo/redo.
+ editor.enableUndo(0);
+ is(transactionManager.maxTransactionCount, 0,
+ editableElement.tagName + ": Transaction manager should not be able to have undo items");
+ editableElement.value = "hij";
+ is(transactionManager.maxTransactionCount, 0,
+ editableElement.tagName + ": Transaction manager should not be able to have undo items after setting value");
+ }
+ SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/editor/txmgr/TransactionManager.h
+++ b/editor/txmgr/TransactionManager.h
@@ -43,16 +43,18 @@ public:
{
return mUndoStack.GetSize();
}
size_t NumberOfRedoItems() const
{
return mRedoStack.GetSize();
}
+ int32_t NumberOfMaximumTransactions() const { return mMaxTransactionCount; }
+
bool EnableUndoRedo(int32_t aMaxTransactionCount = -1);
bool DisableUndoRedo()
{
return EnableUndoRedo(0);
}
bool ClearUndoRedo()
{
if (NS_WARN_IF(!mDoStack.IsEmpty())) {