Bug 1386222 - Ensure that we always respect the undo/redo transaction history when modifying the <xul:textbox>.value dynamically through script; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 01 Aug 2017 12:47:07 -0400
changeset 420925 373c6ebaad861dcb63107fe3557bf28b21a7c425
parent 420923 13750b02d0219aec45da8bbe24aaa235593d5696
child 420926 f23f75bc9d39980d863e4d3d845dbd97d9de8f5c
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1386222
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1386222 - Ensure that we always respect the undo/redo transaction history when modifying the <xul:textbox>.value dynamically through script; r=bzbarsky
dom/html/HTMLInputElement.cpp
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
editor/libeditor/tests/chrome.ini
editor/libeditor/tests/test_bug1386222.xul
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2996,16 +2996,24 @@ HTMLInputElement::UpdateFileList()
 nsresult
 HTMLInputElement::SetValueInternal(const nsAString& aValue,
                                    const nsAString* aOldValue,
                                    uint32_t aFlags)
 {
   NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
                   "Don't call SetValueInternal for file inputs");
 
+  // We want to remember if the SetValueInternal() call is being made for a XUL
+  // element.  We do that by looking at the parent node here, and if that node
+  // is a XUL node, we consider our control a XUL control.
+  nsIContent* parent = GetParent();
+  if (parent && parent->IsXULElement()) {
+    aFlags |= nsTextEditorState::eSetValue_ForXUL;
+  }
+
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
     {
       // At the moment, only single line text control have to sanitize their value
       // Because we have to create a new string for that, we should prevent doing
       // it if it's useless.
       nsAutoString value(aValue);
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -2635,17 +2635,20 @@ nsTextEditorState::SetValue(const nsAStr
         // set the value, restore flags
         {
           AutoRestoreEditorState restoreState(textEditor);
 
           mTextListener->SettingValue(true);
           bool notifyValueChanged = !!(aFlags & eSetValue_Notify);
           mTextListener->SetValueChanged(notifyValueChanged);
 
-          if (aFlags & eSetValue_BySetUserInput) {
+          // We preserve the undo history if we are explicitly setting the
+          // value for the user's input, or if we are setting the value for a
+          // XUL text control.
+          if (aFlags & (eSetValue_BySetUserInput | eSetValue_ForXUL)) {
             nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
             uint32_t currentLength = currentValue.Length();
             uint32_t newlength = newValue.Length();
             if (!currentLength ||
                 !StringBeginsWith(newValue, currentValue)) {
               // Replace the whole text.
               currentLength = 0;
               kungFuDeathGrip->SelectAll();
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -173,16 +173,20 @@ public:
     // Whether the value change should be notified to the frame/contet nor not.
     eSetValue_Notify                = 1 << 2,
     // Whether to move the cursor to end of the value (in the case when we have
     // cached selection offsets), in the case when the value has changed.  If
     // this is not set, the cached selection offsets will simply be clamped to
     // be within the length of the new value.  In either case, if the value has
     // not changed the cursor won't move.
     eSetValue_MoveCursorToEndIfValueChanged = 1 << 3,
+    // The value is changed for a XUL text control as opposed to for an HTML
+    // text control.  Such value changes are different in that they preserve the
+    // undo history.
+    eSetValue_ForXUL                = 1 << 4,
   };
   MOZ_MUST_USE bool SetValue(const nsAString& aValue,
                              const nsAString* aOldValue,
                              uint32_t aFlags);
   MOZ_MUST_USE bool SetValue(const nsAString& aValue,
                              uint32_t aFlags)
   {
     return SetValue(aValue, nullptr, aFlags);
--- a/editor/libeditor/tests/chrome.ini
+++ b/editor/libeditor/tests/chrome.ini
@@ -2,13 +2,14 @@
 skip-if = os == 'android'
 support-files = green.png
 
 [test_bug489202.xul]
 [test_bug599983.xul]
 [test_bug607584.xul]
 [test_bug616590.xul]
 [test_bug780908.xul]
+[test_bug1386222.xul]
 [test_contenteditable_text_input_handling.html]
 [test_htmleditor_keyevent_handling.html]
 [test_texteditor_keyevent_handling.html]
 skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux
 [test_pasteImgTextarea.xul]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1386222.xul
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin"
+                 type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1386222
+-->
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Mozilla Bug 1386222" onload="runTest();">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <textbox id="t"/>
+  <script class="testbody" type="application/javascript">
+  <![CDATA[
+
+function runTest() {
+  var t = document.getElementById("t");
+  var numUndo = () => t.editor.transactionManager.numberOfUndoItems;
+  is(numUndo(), 0, "no undo items to begin with");
+  t.value = "foo";
+  is(t.value, "foo", "value setter worked");
+  is(numUndo(), 1, "one undo item now");
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+]]>
+</script>
+</window>