From cd67e85c8dda13c76f1ec5b412b32958c0964e2e Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 19 Dec 2014 12:45:50 -0500 Subject: [PATCH] Bug 1113238 - Part 1: Make our plaintext and HTML serializers aware of CSS preformatted styles; r=bzbarsky This code is super-hairy, but I think this is the minimum amount of changes that we need. nsPlainTextSerializer::IsInPre() before this patch is completely broken, and I changed it to maintain a stack of bools representing whether the elements that we saw as we were traversing the tree are preformatted or not. nsXHTMLContentSerializer maintains this information using a counter, which is broken in case pre and non-preformatted elements are stacked underneath each other, but I'm not sure why this code is using a counter and I didn't want to change it drastically, so for now I'm just making it look at the element's style first as opposed to its tag name. Follow-up work may include exploring whether nsXHTMLContentSerializer should use a stack similar to nsPlainTextSerializer, and also audit this code for more places where things are hardcoded based on tag names where we should be really looking at the style. --- dom/base/nsPlainTextSerializer.cpp | 35 +++++++++---------- dom/base/nsPlainTextSerializer.h | 10 ++++++ dom/base/nsXHTMLContentSerializer.cpp | 22 ++++++++++-- dom/base/nsXHTMLContentSerializer.h | 4 +++ dom/base/test/copypaste.js | 6 ++-- .../test/file_htmlserializer_1_bodyonly.html | 5 ++- .../test/file_htmlserializer_1_format.html | 13 ++++--- .../test/file_htmlserializer_1_linebreak.html | 5 ++- .../test/file_htmlserializer_1_links.html | 5 ++- .../file_htmlserializer_1_nested_body.html | 5 ++- .../test/file_htmlserializer_1_noflag.html | 5 ++- .../file_htmlserializer_1_noformatpre.html | 15 ++++---- .../file_htmlserializer_1_sibling_body.html | 5 ++- ...mlserializer_1_sibling_body_only_body.html | 5 ++- dom/base/test/file_htmlserializer_1_wrap.html | 5 ++- .../file_xhtmlserializer_1_bodyonly.xhtml | 5 ++- .../test/file_xhtmlserializer_1_format.xhtml | 13 ++++--- .../file_xhtmlserializer_1_linebreak.xhtml | 5 ++- .../test/file_xhtmlserializer_1_links.xhtml | 5 ++- .../file_xhtmlserializer_1_nested_body.xhtml | 5 ++- .../test/file_xhtmlserializer_1_noflag.xhtml | 5 ++- .../file_xhtmlserializer_1_noformatpre.xhtml | 15 ++++---- .../file_xhtmlserializer_1_sibling_body.xhtml | 5 ++- ...lserializer_1_sibling_body_only_body.xhtml | 5 ++- .../test/file_xhtmlserializer_1_wrap.xhtml | 5 ++- dom/base/test/test_bug116083.html | 4 +++ 26 files changed, 151 insertions(+), 66 deletions(-) diff --git a/dom/base/nsPlainTextSerializer.cpp b/dom/base/nsPlainTextSerializer.cpp index 86c70b0f3667..c299a428e400 100644 --- a/dom/base/nsPlainTextSerializer.cpp +++ b/dom/base/nsPlainTextSerializer.cpp @@ -22,6 +22,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" #include "mozilla/BinarySearch.h" +#include "nsComputedDOMStyle.h" using namespace mozilla; using namespace mozilla::dom; @@ -356,6 +357,7 @@ nsPlainTextSerializer::AppendElementStart(Element* aElement, if (isContainer) { rv = DoOpenContainer(id); + mPreformatStack.push(IsElementPreformatted(mElement)); } else { rv = DoAddLeaf(id); @@ -389,6 +391,7 @@ nsPlainTextSerializer::AppendElementEnd(Element* aElement, rv = NS_OK; if (isContainer) { rv = DoCloseContainer(id); + mPreformatStack.pop(); } mElement = nullptr; @@ -1537,7 +1540,7 @@ nsPlainTextSerializer::Write(const nsAString& aStr) // This mustn't be mixed with intelligent wrapping without clearing // the mCurrentLine buffer before!!! - NS_ASSERTION(mCurrentLine.IsEmpty(), + NS_ASSERTION(mCurrentLine.IsEmpty() || IsInPre(), "Mixed wrapping data and nonwrapping data on the same line"); if (!mCurrentLine.IsEmpty()) { FlushLine(); @@ -1755,28 +1758,22 @@ nsPlainTextSerializer::GetIdForContent(nsIContent* aContent) return localName->IsStaticAtom() ? localName : nullptr; } -/** - * Returns true if we currently are inside a
. The check is done
- * by traversing the tag stack looking for 
 until we hit a block
- * level tag which is assumed to override any 
:s below it in
- * the stack. To do this correctly to a 100% would require access
- * to style which we don't support in this converter.
- */  
 bool
 nsPlainTextSerializer::IsInPre()
 {
-  int32_t i = mTagStackIndex;
-  while(i > 0) {
-    if (mTagStack[i - 1] == nsGkAtoms::pre)
-      return true;
-    if (nsContentUtils::IsHTMLBlock(mTagStack[i - 1])) {
-      // We assume that every other block overrides a 
-      return false;
-    }
-    --i;
-  }
+  return !mPreformatStack.empty() && mPreformatStack.top();
+}
 
-  // Not a 
 in the whole stack
+bool
+nsPlainTextSerializer::IsElementPreformatted(Element* aElement)
+{
+  nsRefPtr styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr,
+                                                         nullptr);
+  if (styleContext) {
+    const nsStyleText* textStyle = styleContext->StyleText();
+    return textStyle->WhiteSpaceOrNewlineIsSignificant();
+  }
   return false;
 }
 
diff --git a/dom/base/nsPlainTextSerializer.h b/dom/base/nsPlainTextSerializer.h
index 65ce863e4e25..5688fed5d99c 100644
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -22,6 +22,8 @@
 #include "nsString.h"
 #include "nsTArray.h"
 
+#include 
+
 class nsIContent;
 
 namespace mozilla {
@@ -112,6 +114,9 @@ protected:
 
   bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag);
 
+private:
+  bool IsElementPreformatted(mozilla::dom::Element* aElement);
+
 protected:
   nsString         mCurrentLine;
   uint32_t         mHeadLevel;
@@ -196,6 +201,11 @@ protected:
   nsIAtom**        mTagStack;
   uint32_t         mTagStackIndex;
 
+  // The stack indicating whether the elements we've been operating on are
+  // CSS preformatted elements, so that we can tell if the text inside them
+  // should be formatted.
+  std::stack mPreformatStack;
+
   // Content in the stack above this index should be ignored:
   uint32_t          mIgnoreAboveIndex;
 
diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp
index 5974c254bb72..63dbbba43e72 100644
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -33,6 +33,8 @@
 #include "nsIScriptElement.h"
 #include "nsAttrName.h"
 #include "nsParserConstants.h"
+#include "nsComputedDOMStyle.h"
+#include "mozilla/dom/Element.h"
 
 static const int32_t kLongLineLen = 128;
 
@@ -850,7 +852,7 @@ nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
 
   nsIAtom *name = aNode->Tag();
 
-  if (name == nsGkAtoms::pre ||
+  if (IsElementPreformatted(aNode) ||
       name == nsGkAtoms::script ||
       name == nsGkAtoms::style ||
       name == nsGkAtoms::noscript ||
@@ -868,7 +870,7 @@ nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
   }
 
   nsIAtom *name = aNode->Tag();
-  if (name == nsGkAtoms::pre ||
+  if (IsElementPreformatted(aNode) ||
       name == nsGkAtoms::script ||
       name == nsGkAtoms::style ||
       name == nsGkAtoms::noscript ||
@@ -878,6 +880,22 @@ nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
   }
 }
 
+bool
+nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode)
+{
+  if (!aNode->IsElement()) {
+    return false;
+  }
+  nsRefPtr styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aNode->AsElement(),
+                                                         nullptr, nullptr);
+  if (styleContext) {
+    const nsStyleText* textStyle = styleContext->StyleText();
+    return textStyle->WhiteSpaceOrNewlineIsSignificant();
+  }
+  return false;
+}
+
 void 
 nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
                                                     nsAString& aStr)
diff --git a/dom/base/nsXHTMLContentSerializer.h b/dom/base/nsXHTMLContentSerializer.h
index d3e9d162ad8a..d885ee58b292 100644
--- a/dom/base/nsXHTMLContentSerializer.h
+++ b/dom/base/nsXHTMLContentSerializer.h
@@ -93,6 +93,10 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer {
                      const nsAString& aURI,
                      nsAString& aEscapedURI);
 
+private:
+  bool IsElementPreformatted(nsIContent* aNode);
+
+protected:
   nsCOMPtr mEntityConverter;
 
   /*
diff --git a/dom/base/test/copypaste.js b/dom/base/test/copypaste.js
index 8a018a34987b..d28664b29bca 100644
--- a/dom/base/test/copypaste.js
+++ b/dom/base/test/copypaste.js
@@ -160,8 +160,8 @@ function testCopyPaste (isXHTML) {
   testPasteText(" Tt t t ");
 
   copyChildrenToClipboard("div5");
-  testSelectionToString(" T ");
-  testClipboardValue("text/unicode", " T ");
+  testSelectionToString(" T     ");
+  testClipboardValue("text/unicode", " T     ");
   if (isXHTML) {
     testClipboardValue("text/html", "
\n T\n
"); testInnerHTML("div5", "\n T\n"); @@ -170,7 +170,7 @@ function testCopyPaste (isXHTML) { testClipboardValue("text/html", "
\n T\n
"); testInnerHTML("div5", "\n T\n"); } - testPasteText(" T "); + testPasteText(" T "); copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden); testSelectionToString(""); diff --git a/dom/base/test/file_htmlserializer_1_bodyonly.html b/dom/base/test/file_htmlserializer_1_bodyonly.html index 848167c62a11..d4e3582dabbb 100644 --- a/dom/base/test/file_htmlserializer_1_bodyonly.html +++ b/dom/base/test/file_htmlserializer_1_bodyonly.html @@ -33,7 +33,10 @@ comments -->
lacinia libero ullamcorper laoreet.
Cras quis
nisi at odio
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum,
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non +urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci +luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at +pharetra rutrum,
lacus risus pulvinar ante.
ut gravida eros leo ut libero diff --git a/dom/base/test/file_htmlserializer_1_format.html b/dom/base/test/file_htmlserializer_1_format.html index 09f80467e46e..111d7a257b56 100644 --- a/dom/base/test/file_htmlserializer_1_format.html +++ b/dom/base/test/file_htmlserializer_1_format.html @@ -42,11 +42,14 @@ var d = a < b && a > c;
lacinia libero ullamcorper laoreet.
- Cras quis
- nisi at odio
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum,
-lacus risus pulvinar ante. -
+ Cras quis
+ nisi at odio
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non + urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci + luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla + at pharetra rutrum,
+ lacus risus pulvinar ante. +
ut gravida eros leo ut libero