Bug 1584901 - Convert tail space to NBSP in composition string. r=masayuki
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Mon, 07 Oct 2019 08:16:18 +0000
changeset 496504 669c9f530d92dd7229fa314f74fcddee1cd3895c
parent 496503 5027978b314f254a3d494ee8661bcbc0a2ab5e79
child 496505 a69d0ed7e27cc91839844fdf49f16185ab906941
push id97282
push userm_kato@ga2.so-net.ne.jp
push dateMon, 07 Oct 2019 09:04:13 +0000
treeherderautoland@669c9f530d92 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1584901, 1530649
milestone71.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 1584901 - Convert tail space to NBSP in composition string. r=masayuki This is related to bug 1530649. When using <span> element with contentedtiable, we won't insert <br> element at last. When Korean IME on macOS commits composition by space key, composition string has space. Gekco removes U+0020 space when it is last character into editing host. To keep whitespace, we have to replace with NBSP when it is last. Differential Revision: https://phabricator.services.mozilla.com/D48146
editor/libeditor/WSRunObject.cpp
widget/tests/window_composition_text_querycontent.xul
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -357,17 +357,22 @@ nsresult WSRunObject::InsertText(Documen
       if (afterRun->mType & WSType::trailingWS) {
         theString.SetCharAt(kNBSP, lastCharIndex);
       } else if (afterRun->mType & WSType::normalWS) {
         WSPoint wspoint = GetNextCharPoint(pointToInsert);
         if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) {
           theString.SetCharAt(kNBSP, lastCharIndex);
         }
       }
-    } else if (mEndReason & WSType::block) {
+    } else if (afterRunObject.mEndReason & WSType::block) {
+      // When afterRun is null, it means that mScanEndPoint is last point in
+      // editing host or editing block.
+      // If this text insertion replaces composition, this.mEndReason is
+      // start position of compositon. So we have to use afterRunObject's
+      // reason instead.
       theString.SetCharAt(kNBSP, lastCharIndex);
     }
   }
 
   // Next, scan string for adjacent ws and convert to nbsp/space combos
   // MOOSE: don't need to convert tabs here since that is done by
   // WillInsertText() before we are called.  Eventually, all that logic will be
   // pushed down into here and made more efficient.
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -95,16 +95,18 @@ var div = document.getElementById("div")
 var textarea = document.getElementById("textarea");
 var panel = document.getElementById("panel");
 var textbox = document.getElementById("textbox");
 var iframe = document.getElementById("iframe");
 var iframe2 = document.getElementById("iframe2");
 var iframe3 = document.getElementById("iframe3");
 var contenteditable;
 var windowOfContenteditable;
+var contenteditableBySpan;
+var windowOfContenteditableBySpan;
 var input = document.getElementById("input");
 var password = document.getElementById("password");
 var textareaInFrame;
 
 const nsITextInputProcessorCallback = Ci.nsITextInputProcessorCallback;
 const nsIInterfaceRequestor = Ci.nsIInterfaceRequestor;
 const nsIWebNavigation = Ci.nsIWebNavigation;
 const nsIDocShell = Ci.nsIDocShell;
@@ -5185,18 +5187,16 @@ function runBug1530649Test()
   synthesizeComposition({type: "compositioncommitasis", key: "KEY_Enter"});
 
   is(contenteditable.innerHTML, "abc d<br>",
      "runBug1530649Test: Committing the new composition string shouldn't remove the last space");
 }
 
 function runBug1571375Test()
 {
-  let contenteditableBySpan = document.getElementById("iframe7").contentDocument.getElementById("contenteditable");
-  let windowOfContenteditableBySpan = document.getElementById("iframe7").contentWindow;
   let selection = windowOfContenteditableBySpan.getSelection();
   let doc = document.getElementById("iframe7").contentDocument;
 
   contenteditableBySpan.focus();
 
   contenteditableBySpan.innerHTML = "hello world";
   let range = doc.createRange();
   range.setStart(contenteditableBySpan.firstChild, 6);
@@ -5238,16 +5238,41 @@ function runBug1571375Test()
     composition: {string: "world", clauses: [{length: 5, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
     caret: {start: 0, length: 0}}
   );
   synthesizeComposition({type: "compositioncommit", data: "world", key: " "});
   is(contenteditableBySpan.innerHTML, "hello world<div>.</div>",
      "runBug1571375Test: space must not be removed by commit");
 }
 
+async function runBug1584901Test()
+{
+  contenteditableBySpan.focus();
+  contenteditableBySpan.innerHTML = "";
+
+  // XXX synthesizeCompositionChange won't work without wait.
+  await waitForTick();
+
+  synthesizeCompositionChange({
+    composition: {string: "a ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
+  });
+  synthesizeComposition({type: "compositioncommitasis", key: " "});
+
+  is(contenteditableBySpan.innerHTML, "a&nbsp;",
+     "runBug1584901Test: space must not be removed by composition change");
+
+  synthesizeCompositionChange({
+    composition: {string: "b ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
+  });
+  synthesizeComposition({type: "compositioncommitasis", key: " "});
+
+  is(contenteditableBySpan.innerHTML, "a b&nbsp;",
+     "runBug1584901Test: space must not be removed by composition change");
+}
+
 function runCSSTransformTest()
 {
   textarea.focus();
   textarea.value = "some text";
   textarea.selectionStart = textarea.selectionEnd = textarea.value.length;
   let editorRect = synthesizeQueryEditorRect();
   if (!checkQueryContentResult(editorRect,
         "runCSSTransformTest: editorRect")) {
@@ -9163,22 +9188,26 @@ async function runPasswordMaskDelayTest(
 async function runTest()
 {
   window.addEventListener("unload", window.arguments[0].SimpleTest.finish, {once: true, capture: true});
 
   contenteditable = document.getElementById("iframe4").contentDocument.getElementById("contenteditable");
   windowOfContenteditable = document.getElementById("iframe4").contentWindow;
   textareaInFrame = iframe.contentDocument.getElementById("textarea");
 
+  contenteditableBySpan = document.getElementById("iframe7").contentDocument.getElementById("contenteditable");
+  windowOfContenteditableBySpan = document.getElementById("iframe7").contentWindow;
+
   await runIMEContentObserverTest();
   await runEditorReframeTests();
   await runAsyncForceCommitTest();
   await runRemoveContentTest();
   await runPanelTest();
   await runPasswordMaskDelayTest();
+  await runBug1584901Test();
 
   runUndoRedoTest();
   runCompositionCommitAsIsTest();
   runCompositionCommitTest();
   runCompositionTest();
   runCompositionEventTest();
   runQueryTextRectInContentEditableTest();
   runCharAtPointTest(textarea, "textarea in the document");