Bug 1330796 - Add invisible break to span of display:block in HTMLEditRules::SplitMailCites. r=masayuki
authorJorg K <jorgk@jorgk.com>
Mon, 16 Jan 2017 01:40:00 -0500
changeset 329604 3110a34c9897
parent 329603 ba7751450a45
child 329605 d50140818c65
push id85743
push userryanvm@gmail.com
push date2017-01-16 19:10 +0000
treeherdermozilla-inbound@3110a34c9897 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1330796
milestone53.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 1330796 - Add invisible break to span of display:block in HTMLEditRules::SplitMailCites. r=masayuki
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/tests/mochitest.ini
editor/libeditor/tests/test_bug1330796.html
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -35,16 +35,17 @@
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsID.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMText.h"
+#include "nsIFrame.h"
 #include "nsIHTMLAbsPosEditor.h"
 #include "nsIHTMLDocument.h"
 #include "nsINode.h"
 #include "nsLiteralString.h"
 #include "nsRange.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "nsStringFwd.h"
@@ -1705,24 +1706,43 @@ HTMLEditRules::SplitMailCites(Selection*
     }
 
     NS_ENSURE_STATE(mHTMLEditor);
     NS_ENSURE_STATE(selNode->IsContent());
     int32_t newOffset = mHTMLEditor->SplitNodeDeep(*citeNode,
         *selNode->AsContent(), selOffset, HTMLEditor::EmptyContainers::no,
         getter_AddRefs(leftCite), getter_AddRefs(rightCite));
     NS_ENSURE_STATE(newOffset != -1);
+
+    // Add an invisible <br> to the end of the left part if it was a <span> of
+    // style="display: block". This is important, since when serialising the
+    // cite to plain text, the span which caused the visual break is discarded.
+    // So the added <br> will guarantee that the serialiser will insert a
+    // break where the user saw one.
+    if (leftCite &&
+        leftCite->IsHTMLElement(nsGkAtoms::span) &&
+        leftCite->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eBlockFrame)) {
+       nsCOMPtr<nsINode> lastChild = leftCite->GetLastChild();
+       if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
+         // We ignore the result here.
+         nsCOMPtr<Element> invisBR =
+           mHTMLEditor->CreateBR(leftCite, leftCite->Length());
+      }
+    }
+
     selNode = citeNode->GetParentNode();
     NS_ENSURE_STATE(mHTMLEditor);
     nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(selNode, newOffset);
     NS_ENSURE_STATE(brNode);
+
     // want selection before the break, and on same line
     aSelection->SetInterlinePosition(true);
     rv = aSelection->Collapse(selNode, newOffset);
     NS_ENSURE_SUCCESS(rv, rv);
+
     // if citeNode wasn't a block, we might also want another break before it.
     // We need to examine the content both before the br we just added and also
     // just after it.  If we don't have another br or block boundary adjacent,
     // then we will need a 2nd br added to achieve blank line that user expects.
     if (IsInlineNode(*citeNode)) {
       NS_ENSURE_STATE(mHTMLEditor);
       WSRunObject wsObj(mHTMLEditor, selNode, newOffset);
       nsCOMPtr<nsINode> visNode;
@@ -1732,39 +1752,43 @@ HTMLEditRules::SplitMailCites(Selection*
                              &visOffset, &wsType);
       if (wsType == WSType::normalWS || wsType == WSType::text ||
           wsType == WSType::special) {
         NS_ENSURE_STATE(mHTMLEditor);
         WSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
         wsObjAfterBR.NextVisibleNode(selNode, newOffset + 1,
                                      address_of(visNode), &visOffset, &wsType);
         if (wsType == WSType::normalWS || wsType == WSType::text ||
-            wsType == WSType::special) {
+            wsType == WSType::special ||
+            // In case we're at the very end.
+            wsType == WSType::thisBlock) {
           NS_ENSURE_STATE(mHTMLEditor);
           brNode = mHTMLEditor->CreateBR(selNode, newOffset);
           NS_ENSURE_STATE(brNode);
         }
       }
     }
+
     // delete any empty cites
     bool bEmptyCite = false;
     if (leftCite) {
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, true, false);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (bEmptyCite) {
         NS_ENSURE_STATE(mHTMLEditor);
         rv = mHTMLEditor->DeleteNode(leftCite);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
+
     if (rightCite) {
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (bEmptyCite) {
         NS_ENSURE_STATE(mHTMLEditor);
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -211,16 +211,17 @@ skip-if = toolkit == 'android'
 [test_bug1248185.html]
 [test_bug1258085.html]
 [test_bug1268736.html]
 [test_bug1310912.html]
 skip-if = toolkit == 'android' # bug 1315898
 [test_bug1314790.html]
 [test_bug1315065.html]
 [test_bug1316302.html]
+[test_bug1330796.html]
 
 [test_CF_HTML_clipboard.html]
 subsuite = clipboard
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_focus.html]
 [test_dom_input_event_on_htmleditor.html]
 skip-if = toolkit == 'android' # bug 1054087
 [test_dom_input_event_on_texteditor.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1330796.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1330796
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 772796</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"/>
+  <style> .pre { white-space: pre } </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772796">Mozilla Bug 1330796</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<div id="editable" contenteditable></div>
+
+<pre id="test">
+
+<script type="application/javascript">
+// We want to test what happens when the user splits a mail cite by clicking
+// at the start, the middle and the end of the cite and hitting the enter key.
+// Mail cites are spans, and since bug 1288911 they are displayed as blocks.
+// The _moz_quote attribute is used to give the cite a blue color via CSS.
+// As an internal attribute, it's not returned from the innerHTML.
+// To the user the tests look like:
+// > mailcite
+// This text is 10 characters long, so we position at 0, 5 and 10.
+// Althought since bug 1288911 those cites are displayed as block,
+// the tests are repeated also for inline display.
+// Each entry of the 'tests' array has the original HTML, the offset to click
+// at and the expected result HTML.
+var tests = [
+  // With style="display: block;".
+  [ "<span _moz_quote=true style=\"display: block;\">&gt; mailcite<br></span>", 0,
+    "x<br><span style=\"display: block;\">&gt; mailcite<br></span>" ],
+  [ "<span _moz_quote=true style=\"display: block;\">&gt; mailcite<br></span>", 5,
+    "<span style=\"display: block;\">&gt; mai<br></span>x<br><span style=\"display: block;\">lcite<br></span>"],
+  [ "<span _moz_quote=true style=\"display: block;\">&gt; mailcite<br></span>", 10,
+    "<span style=\"display: block;\">&gt; mailcite<br></span>x<br>" ],
+  // No <br> at the end to simulate prior deletion to the end of the quote.
+  [ "<span _moz_quote=true style=\"display: block;\">&gt; mailcite</span>", 10,
+    "<span style=\"display: block;\">&gt; mailcite<br></span>x<br>" ],
+
+  // Without style="display: block;".
+  [ "<span _moz_quote=true>&gt; mailcite<br></span>", 0,
+    "x<br><span>&gt; mailcite<br></span>" ],
+  [ "<span _moz_quote=true>&gt; mailcite<br></span>", 5,
+    "<span>&gt; mai</span><br>x<br><span>lcite<br></span>" ],
+  [ "<span _moz_quote=true>&gt; mailcite<br></span>", 10,
+    "<span>&gt; mailcite<br></span>x<br>" ],
+  // No <br> at the end to simulate prior deletion to the end of the quote.
+  [ "<span _moz_quote=true>&gt; mailcite</span>", 10,
+    "<span>&gt; mailcite</span><br>x<br>" ]
+];
+
+/** Test for Bug 1330796 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+
+  var sel = window.getSelection();
+  var theEdit = document.getElementById("editable");
+  makeMailEditor();
+
+  for (i = 0; i < tests.length; i++) {
+    theEdit.innerHTML = tests[i][0];
+    theEdit.focus();
+    var theText = theEdit.firstChild.firstChild;
+    // Position set at the beginning , middle and end of the text.
+    sel.collapse(theText, tests[i][1]);
+
+    synthesizeKey("KEY_Enter", {});
+    synthesizeKey("x", {});
+    is(theEdit.innerHTML, tests[i][2], "unexpected HTML for test " + i.toString());
+  }
+
+  SimpleTest.finish();
+
+});
+
+function makeMailEditor() {
+  var Ci = SpecialPowers.Ci;
+  var editingSession = SpecialPowers.wrap(window)
+    .QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIWebNavigation)
+    .QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIEditingSession);
+  var editor = editingSession.getEditorForWindow(window);
+  editor.flags |= Ci.nsIPlaintextEditor.eEditorMailMask;
+}
+</script>
+
+</pre>
+</body>
+</html>