Bug 1119503 - Part 2: Insert a line break between preformatted block boundaries when creating raw output. r=bzbarsky, a=lmandel
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 17 Jan 2015 17:31:59 -0500
changeset 244624 8af1c37a245c2b47b912dd7cd257c6a83e11c981
parent 244623 aec2b73d90380027a7782810ea60a845f8fef16c
child 244625 5225cddf3c3a6a1fd3ef671485f0c66a700e71bd
push id734
push usermartin.thomson@gmail.com
push dateThu, 19 Feb 2015 22:06:18 +0000
reviewersbzbarsky, lmandel
bugs1119503
milestone37.0a2
Bug 1119503 - Part 2: Insert a line break between preformatted block boundaries when creating raw output. r=bzbarsky, a=lmandel
dom/base/nsCopySupport.cpp
dom/base/nsIDocumentEncoder.idl
dom/base/nsPlainTextSerializer.cpp
dom/base/nsPlainTextSerializer.h
dom/base/test/test_bug116083.html
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -92,17 +92,18 @@ SelectionCopyHelper(nsISelection *aSel, 
   // is. if it is a selection into input/textarea element or in a html content
   // with pre-wrap style : text/plain. Otherwise text/html.
   // see nsHTMLCopyEncoder::SetSelection
   nsAutoString mimeType;
   mimeType.AssignLiteral(kUnicodeMime);
 
   // Do the first and potentially trial encoding as preformatted and raw.
   uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
-                          | nsIDocumentEncoder::OutputRaw;
+                          | nsIDocumentEncoder::OutputRaw
+                          | nsIDocumentEncoder::OutputForPlainTextClipboardCopy;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = docEncoder->SetSelection(aSel);
--- a/dom/base/nsIDocumentEncoder.idl
+++ b/dom/base/nsIDocumentEncoder.idl
@@ -223,16 +223,23 @@ interface nsIDocumentEncoder : nsISuppor
   const unsigned long OutputNonTextContentAsPlaceholder = (1 << 23);
 
   /**
    * Don't Strip ending spaces from a line (only for serializing to plaintext).
    */
   const unsigned long OutputDontRemoveLineEndingSpaces = (1 << 24);
 
   /**
+   * Serialize in a way that is suitable for copying a plaintext version of the
+   * document to the clipboard.  This can for example cause line endings to be
+   * injected at preformatted block element boundaries.
+   */
+  const unsigned long OutputForPlainTextClipboardCopy = (1 << 25);
+
+  /**
    * Initialize with a pointer to the document and the mime type.
    * @param aDocument Document to encode.
    * @param aMimeType MimeType to use. May also be set by SetMimeType.
    * @param aFlags Flags to use while encoding. May also be set by SetFlags.
    */
   void init(in nsIDOMDocument aDocument,
             in AString aMimeType,
             in unsigned long aFlags);
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -86,16 +86,18 @@ nsPlainTextSerializer::nsPlainTextSerial
   mCurrentLineWidth = 0;
 
   // Flow
   mEmptyLines = 1; // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormatted = false;
   mStartedOutput = false;
 
+  mPreformattedBlockBoundary = false;
+
   // initialize the tag stack to zero:
   // The stack only ever contains pointers to static atoms, so they don't
   // need refcounting.
   mTagStack = new nsIAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
@@ -162,16 +164,18 @@ nsPlainTextSerializer::Init(uint32_t aFl
   else {
     // Platform/default
     mLineBreak.AssignLiteral(NS_LINEBREAK);
   }
 
   mLineBreakDue = false;
   mFloatingLines = -1;
 
+  mPreformattedBlockBoundary = false;
+
   if (mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
     mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs);
 
     mHeaderStrategy =
       Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy);
 
     // DontWrapAnyQuotes is set according to whether plaintext mail
@@ -432,16 +436,26 @@ nsPlainTextSerializer::DoOpenContainer(n
       // Serialize current node as placeholder character
       Write(NS_LITERAL_STRING("\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (mPreformattedBlockBoundary && DoOutput()) {
+      // Should always end a line, but get no more whitespace
+      if (mFloatingLines < 0)
+        mFloatingLines = 0;
+      mLineBreakDue = true;
+    }
+    mPreformattedBlockBoundary = false;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -762,16 +776,24 @@ nsPlainTextSerializer::DoOpenContainer(n
 nsresult
 nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
   if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (DoOutput() && IsInPre() && IsElementBlock(mElement)) {
+      // If we're closing a preformatted block element, output a line break
+      // when we find a new container.
+      mPreformattedBlockBoundary = true;
+    }
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -1031,16 +1053,18 @@ nsPlainTextSerializer::DoAddText(bool aI
     mURL.Truncate();
   }
   Write(aText);
 }
 
 nsresult
 nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag)
 {
+  mPreformattedBlockBoundary = false;
+
   // If we don't want any output, just return
   if (!DoOutput()) {
     return NS_OK;
   }
 
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -166,17 +166,19 @@ protected:
 
   bool             mInWhitespace;
   bool             mPreFormatted;
   bool             mStartedOutput; // we've produced at least a character
 
   // While handling a new tag, this variable should remind if any line break
   // is due because of a closing tag. Setting it to "TRUE" while closing the tags.
   // Hence opening tags are guaranteed to start with appropriate line breaks.
-  bool             mLineBreakDue; 
+  bool             mLineBreakDue;
+
+  bool             mPreformattedBlockBoundary;
 
   nsString         mURL;
   int32_t          mHeaderStrategy;    /* Header strategy (pref)
                                           0 = no indention
                                           1 = indention, increased with
                                               header level (default)
                                           2 = numbering and slight indention */
   int32_t          mHeaderCounter[7];  /* For header-numbering:
--- a/dom/base/test/test_bug116083.html
+++ b/dom/base/test/test_bug116083.html
@@ -15,16 +15,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div style="white-space: pre">foo  bar</div>
 <div style="white-space: pre-wrap">foo  bar</div>
 <div style="white-space: pre-line">foo  bar</div>
 <div style="white-space: -moz-pre-space">foo  bar</div>
 <div data-result="bar  baz"><span style="white-space: pre">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-wrap">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-line">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: -moz-pre-space">bar  </span>baz</div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
 <div data-result="&#10;foo bar&#10;">foo  bar</div>
 </div>
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function nextTest() {
   var div = document.querySelector("#content>div");
   if (!div) {