Bug 553540 - need new flag to nsPlainTextSerializer to support delsp=yes (RFC 3676). r=laurent sr=jst
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Fri, 02 Apr 2010 18:21:40 +0900
changeset 42943 42c54fb3a4ab5b5a9a386a6a58743c4eca5b602a
parent 42942 b860cc41eb96cf8d88cb09748a27b3257d18400d
child 42944 5a4b63ca41d39b7a5f1c40d9cd296d19a8a4dbc4
push id13540
push userm_kato@ga2.so-net.ne.jp
push dateMon, 31 May 2010 06:17:18 +0000
treeherdermozilla-central@42c54fb3a4ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslaurent, jst
bugs553540
milestone1.9.3a5pre
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 553540 - need new flag to nsPlainTextSerializer to support delsp=yes (RFC 3676). r=laurent sr=jst
content/base/public/nsIDocumentEncoder.idl
content/base/src/nsPlainTextSerializer.cpp
content/base/src/nsPlainTextSerializer.h
content/base/test/TestPlainTextSerializer.cpp
--- a/content/base/public/nsIDocumentEncoder.idl
+++ b/content/base/public/nsIDocumentEncoder.idl
@@ -56,17 +56,17 @@ interface nsIDocumentEncoderNodeFixup : 
    * @param [OUT] aSerializeCloneKids True if the document encoder should
    * apply recursive serialization to the children of the fixed up node
    * instead of the children of the original node.
    * @return The resulting fixed up node.
    */
   nsIDOMNode fixupNode(in nsIDOMNode aNode, out boolean aSerializeCloneKids);
 };
 
-[scriptable, uuid(196a3aee-006e-4f8f-a420-e1c1b0958a26)]
+[scriptable, uuid(794a81f6-bde6-4f76-9f5e-0ea0911a2d9f)]
 interface nsIDocumentEncoder : nsISupports
 {
   // Output methods flag bits. There are a frightening number of these,
   // because everyone wants something a little bit different
    
 
   /** 
    * Output only the selection (as opposed to the whole document).
@@ -209,17 +209,24 @@ interface nsIDocumentEncoder : nsISuppor
   const unsigned long OutputPersistNBSP = (1 << 17);
 
   /**
    * Normally when serializing the whole document using the HTML or 
    * XHTML serializer, the encoding declaration is rewritten to match.
    * This flag suppresses that behavior.
    */
   const unsigned long OutputDontRewriteEncodingDeclaration = (1 << 18);
-  
+ 
+  /**
+   * Output for delsp=yes (RFC 3676). This is used with OutputFormatFlowed
+   * when converting to text for mail sending.
+   * PlainText output only.
+   */
+  const unsigned long OutputFormatDelSp  = (1 << 19);
+ 
   /**
    * When using the HTML or XHTML serializer, skip elements that are not
    * visible when this flag is set.  Elements are not visible when they
    * have CSS style display:none or visibility:collapse, for example.
    */
   const unsigned long SkipInvisibleContent = (1 << 19);
   
   /**
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -1406,18 +1406,20 @@ nsPlainTextSerializer::AddToLine(const P
         // -1 (trim a char at the break position)
         // only if the line break was a space.
         if (nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) {
           mCurrentLine.Right(restOfLine, linelength-goodSpace-1);
         }
         else {
           mCurrentLine.Right(restOfLine, linelength-goodSpace);
         }
+        // if breaker was U+0020, it has to consider for delsp=yes support
+        PRBool breakBySpace = mCurrentLine.CharAt(goodSpace) == ' ';
         mCurrentLine.Truncate(goodSpace); 
-        EndLine(PR_TRUE);
+        EndLine(PR_TRUE, breakBySpace);
         mCurrentLine.Truncate();
         // Space stuff new line?
         if(mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
           if(!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get())
               && mCiteQuoteLevel == 0  // We space-stuff quoted lines anyway
             )
           {
             // Space stuffing a la RFC 2646 (format=flowed).
@@ -1445,17 +1447,17 @@ nsPlainTextSerializer::AddToLine(const P
 
 /**
  * Outputs the contents of mCurrentLine, and resets line specific
  * variables. Also adds an indentation and prefix if there is
  * one specified. Strips ending spaces from the line if it isn't
  * preformatted.
  */
 void
-nsPlainTextSerializer::EndLine(PRBool aSoftlinebreak)
+nsPlainTextSerializer::EndLine(PRBool aSoftlinebreak, PRBool aBreakBySpace)
 {
   PRUint32 currentlinelength = mCurrentLine.Length();
 
   if(aSoftlinebreak && 0 == currentlinelength) {
     // No meaning
     return;
   }
 
@@ -1477,17 +1479,23 @@ nsPlainTextSerializer::EndLine(PRBool aS
   }
   
   if(aSoftlinebreak &&
      (mFlags & nsIDocumentEncoder::OutputFormatFlowed) &&
      (mIndent == 0)) {
     // Add the soft part of the soft linebreak (RFC 2646 4.1)
     // We only do this when there is no indentation since format=flowed
     // lines and indentation doesn't work well together.
-    mCurrentLine.Append(PRUnichar(' '));
+
+    // If breaker character is ASCII space with RFC 3676 support (delsp=yes),
+    // add twice space.
+    if (mFlags & nsIDocumentEncoder::OutputFormatDelSp && aBreakBySpace)
+      mCurrentLine.Append(NS_LITERAL_STRING("  "));
+    else
+      mCurrentLine.Append(PRUnichar(' '));
   }
 
   if(aSoftlinebreak) {
     mEmptyLines=0;
   } 
   else {
     // Hard break
     if(!mCurrentLine.IsEmpty() || !mInIndentString.IsEmpty()) {
--- a/content/base/src/nsPlainTextSerializer.h
+++ b/content/base/src/nsPlainTextSerializer.h
@@ -124,17 +124,17 @@ public:
 
   // nsIHTMLToTextSink
   NS_IMETHOD Initialize(nsAString* aOutString,
                         PRUint32 aFlags, PRUint32 aWrapCol);
 
 protected:
   nsresult GetAttributeValue(const nsIParserNode* node, nsIAtom* aName, nsString& aValueRet);
   void AddToLine(const PRUnichar* aStringToAdd, PRInt32 aLength);
-  void EndLine(PRBool softlinebreak);
+  void EndLine(PRBool softlinebreak, PRBool aBreakBySpace = PR_FALSE);
   void EnsureVerticalSpace(PRInt32 noOfRows);
   void FlushLine();
   void OutputQuotesAndIndent(PRBool stripTrailingSpaces=PR_FALSE);
   void Output(nsString& aString);
   void Write(const nsAString& aString);
   PRBool IsBlockLevel(PRInt32 aId);
   PRBool IsContainer(PRInt32 aId);
   PRBool IsInPre();
--- a/content/base/test/TestPlainTextSerializer.cpp
+++ b/content/base/test/TestPlainTextSerializer.cpp
@@ -40,53 +40,134 @@
 #include "nsIParser.h"
 #include "nsIHTMLToTextSink.h"
 #include "nsIParser.h"
 #include "nsIContentSink.h"
 #include "nsIParserService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringGlue.h"
 #include "nsParserCIID.h"
+#include "nsIDocumentEncoder.h"
 
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 void
-ConvertBufToPlainText(nsString &aConBuf)
+ConvertBufToPlainText(nsString &aConBuf, int aFlag)
 {
   nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
   if (parser) {
     nsCOMPtr<nsIContentSink> sink;
     sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID);
     if (sink) {
       nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
       if (textSink) {
         nsAutoString convertedText;
-        textSink->Initialize(&convertedText, 0, 72);
+        textSink->Initialize(&convertedText, aFlag, 72);
         parser->SetContentSink(sink);
         parser->Parse(aConBuf, 0, NS_LITERAL_CSTRING("text/html"), PR_TRUE);
         aConBuf = convertedText;
       }
     }
   }
 }
 
+// Test for ASCII with format=flowed; delsp=yes
+nsresult
+TestASCIIWithFlowedDelSp()
+{
+  nsString test;
+  nsString result;
+
+  test.AssignLiteral("<html><body>"
+                     "Firefox Firefox Firefox Firefox "
+                     "Firefox Firefox Firefox Firefox "
+                     "Firefox Firefox Firefox Firefox"
+                     "</body></html>");
+
+  ConvertBufToPlainText(test, nsIDocumentEncoder::OutputFormatted |
+                              nsIDocumentEncoder::OutputCRLineBreak |
+                              nsIDocumentEncoder::OutputLFLineBreak |
+                              nsIDocumentEncoder::OutputFormatFlowed |
+                              nsIDocumentEncoder::OutputFormatDelSp);
+
+  // create result case
+  result.AssignLiteral("Firefox Firefox Firefox Firefox "
+                       "Firefox Firefox Firefox Firefox "
+                       "Firefox  \r\nFirefox Firefox Firefox\r\n");
+
+  if (!test.Equals(result)) {
+    fail("Wrong HTML to ASCII text serialization with format=flowed; delsp=yes");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("HTML to ASCII text serialization with format=flowed; delsp=yes");
+
+  return NS_OK;
+}
+
+// Test for CJK with format=flowed; delsp=yes
+nsresult
+TestCJKWithFlowedDelSp()
+{
+  nsString test;
+  nsString result;
+
+  test.AssignLiteral("<html><body>");
+  for (PRUint32 i = 0; i < 40; i++) {
+    // Insert Kanji (U+5341)
+    test.Append(0x5341);
+  }
+  test.AppendLiteral("</body></html>");
+
+  ConvertBufToPlainText(test, nsIDocumentEncoder::OutputFormatted |
+                              nsIDocumentEncoder::OutputCRLineBreak |
+                              nsIDocumentEncoder::OutputLFLineBreak |
+                              nsIDocumentEncoder::OutputFormatFlowed |
+                              nsIDocumentEncoder::OutputFormatDelSp);
+
+  // create result case
+  for (PRUint32 i = 0; i < 36; i++) {
+    result.Append(0x5341);
+  }
+  result.Append(NS_LITERAL_STRING(" \r\n"));
+  for (PRUint32 i = 0; i < 4; i++) {
+    result.Append(0x5341);
+  }
+  result.Append(NS_LITERAL_STRING("\r\n"));
+
+  if (!test.Equals(result)) {
+    fail("Wrong HTML to CJK text serialization with format=flowed; delsp=yes");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("HTML to CJK text serialization with format=flowed; delsp=yes");
+
+  return NS_OK;
+}
+
 nsresult
 TestPlainTextSerializer()
 {
   nsString test;
   test.AppendLiteral("<html><base>base</base><head><span>span</span></head>"
                      "<body>body</body></html>");
-  ConvertBufToPlainText(test);
+  ConvertBufToPlainText(test, 0);
   if (!test.EqualsLiteral("basespanbody")) {
     fail("Wrong html to text serialization");
     return NS_ERROR_FAILURE;
   }
 
   passed("HTML to text serialization test");
 
+  nsresult rv = TestASCIIWithFlowedDelSp();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = TestCJKWithFlowedDelSp();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Add new tests here...
   return NS_OK;
 }
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("PlainTextSerializer");
   if (xpcom.failed())