Bug 677050 - Return NS_ERROR_DOM_INVALID_STATE_ERR if fragment parsing is invoked re-entrantly. r=bzbarsky.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 14 Oct 2011 13:39:19 +0300
changeset 78734 2244089600ffab5d1db0ea72ed68ca06c92a9a0c
parent 78733 d50bd6d5b097d2790c9125c3a060ae2773f98e53
child 78735 fa65bb9a09092e9cffa8cf352ddd4eaf8d3bef00
push id21329
push usereakhgari@mozilla.com
push dateFri, 14 Oct 2011 14:37:50 +0000
treeherdermozilla-central@349f3d4b2d87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs677050
milestone10.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 677050 - Return NS_ERROR_DOM_INVALID_STATE_ERR if fragment parsing is invoked re-entrantly. r=bzbarsky.
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/html/content/src/nsGenericHTMLElement.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -184,16 +184,17 @@ struct nsShortcutCandidate {
   }
   PRUint32 mCharCode;
   bool     mIgnoreShift;
 };
 
 class nsContentUtils
 {
   friend class nsAutoScriptBlockerSuppressNodeRemoved;
+  friend class mozilla::AutoRestore<bool>;
   typedef mozilla::dom::Element Element;
   typedef mozilla::TimeDuration TimeDuration;
 
 public:
   static nsresult Init();
 
   /**
    * Get a JSContext from the document's scope object.
@@ -1056,32 +1057,36 @@ public:
    * @param aSourceBuffer the string being set as innerHTML
    * @param aTargetNode the target container
    * @param aContextLocalName local name of context node
    * @param aContextNamespace namespace of context node
    * @param aQuirks true to make <table> not close <p>
    * @param aPreventScriptExecution true to prevent scripts from executing;
    *        don't set to false when parsing into a target node that has been
    *        bound to tree.
+   * @return NS_ERROR_DOM_INVALID_STATE_ERR if a re-entrant attempt to parse
+   *         fragments is made and NS_OK otherwise.
    */
-  static void ParseFragmentHTML(const nsAString& aSourceBuffer,
-                                nsIContent* aTargetNode,
-                                nsIAtom* aContextLocalName,
-                                PRInt32 aContextNamespace,
-                                bool aQuirks,
-                                bool aPreventScriptExecution);
+  static nsresult ParseFragmentHTML(const nsAString& aSourceBuffer,
+                                    nsIContent* aTargetNode,
+                                    nsIAtom* aContextLocalName,
+                                    PRInt32 aContextNamespace,
+                                    bool aQuirks,
+                                    bool aPreventScriptExecution);
 
   /**
    * Invoke the fragment parsing algorithm (innerHTML) using the XML parser.
    *
    * @param aSourceBuffer the string being set as innerHTML
    * @param aTargetNode the target container
    * @param aTagStack the namespace mapping context
    * @param aPreventExecution whether to mark scripts as already started
    * @param aReturn the result fragment
+   * @return NS_ERROR_DOM_INVALID_STATE_ERR if a re-entrant attempt to parse
+   *         fragments is made, a return code from the XML parser.
    */
   static nsresult ParseFragmentXML(const nsAString& aSourceBuffer,
                                    nsIDocument* aDocument,
                                    nsTArray<nsString>& aTagStack,
                                    bool aPreventScriptExecution,
                                    nsIDOMDocumentFragment** aReturn);
 
   /**
@@ -1906,16 +1911,21 @@ private:
   static bool sTrustedFullScreenOnly;
   static bool sFullScreenKeyInputRestricted;
   static PRUint32 sHandlingInputTimeout;
 
   static nsHtml5Parser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
+  /**
+   * True if there's a fragment parser activation on the stack.
+   */
+  static bool sFragmentParsingActive;
+
   static nsString* sShiftText;
   static nsString* sControlText;
   static nsString* sMetaText;
   static nsString* sAltText;
   static nsString* sModifierSeparator;
 };
 
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -267,16 +267,17 @@ bool nsContentUtils::sIsFullScreenApiEna
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sFullScreenKeyInputRestricted = true;
 
 PRUint32 nsContentUtils::sHandlingInputTimeout = 1000;
 
 nsHtml5Parser* nsContentUtils::sHTMLFragmentParser = nsnull;
 nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
+bool nsContentUtils::sFragmentParsingActive = false;
 
 static PLDHashTable sEventListenerManagersHash;
 
 class EventListenerManagerMapEntry : public PLDHashEntryHdr
 {
 public:
   EventListenerManagerMapEntry(const void *aKey)
     : mKey(aKey)
@@ -3504,37 +3505,38 @@ nsContentUtils::CreateContextualFragment
     if (contextAsContent && !contextAsContent->IsElement()) {
       contextAsContent = contextAsContent->GetParent();
       if (contextAsContent && !contextAsContent->IsElement()) {
         // can this even happen?
         contextAsContent = nsnull;
       }
     }
     
+    nsresult rv;
     nsCOMPtr<nsIContent> fragment = do_QueryInterface(frag);
     if (contextAsContent && !contextAsContent->IsHTML(nsGkAtoms::html)) {
-      ParseFragmentHTML(aFragment,
-                        fragment,
-                        contextAsContent->Tag(),
-                        contextAsContent->GetNameSpaceID(),
-                        (document->GetCompatibilityMode() ==
-                            eCompatibility_NavQuirks),
-                        aPreventScriptExecution);
+      rv = ParseFragmentHTML(aFragment,
+                             fragment,
+                             contextAsContent->Tag(),
+                             contextAsContent->GetNameSpaceID(),
+                             (document->GetCompatibilityMode() ==
+                               eCompatibility_NavQuirks),
+                             aPreventScriptExecution);
     } else {
-      ParseFragmentHTML(aFragment,
-                        fragment,
-                        nsGkAtoms::body,
-                        kNameSpaceID_XHTML,
-                        (document->GetCompatibilityMode() ==
-                            eCompatibility_NavQuirks),
-                        aPreventScriptExecution);
+      rv = ParseFragmentHTML(aFragment,
+                             fragment,
+                             nsGkAtoms::body,
+                             kNameSpaceID_XHTML,
+                             (document->GetCompatibilityMode() ==
+                               eCompatibility_NavQuirks),
+                             aPreventScriptExecution);
     }
 
     frag.forget(aReturn);
-    return NS_OK;
+    return rv;
   }
 
   nsAutoTArray<nsString, 32> tagStack;
   nsAutoString uriStr, nameStr;
   nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
   // just in case we have a text node
   if (content && !content->IsElement())
     content = content->GetParent();
@@ -3606,46 +3608,59 @@ nsContentUtils::DropFragmentParsers()
 /* static */
 void
 nsContentUtils::XPCOMShutdown()
 {
   nsContentUtils::DropFragmentParsers();
 }
 
 /* static */
-void
+nsresult
 nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer,
                                   nsIContent* aTargetNode,
                                   nsIAtom* aContextLocalName,
                                   PRInt32 aContextNamespace,
                                   bool aQuirks,
                                   bool aPreventScriptExecution)
 {
+  if (nsContentUtils::sFragmentParsingActive) {
+    NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+  nsContentUtils::sFragmentParsingActive = true;
   if (!sHTMLFragmentParser) {
     sHTMLFragmentParser =
       static_cast<nsHtml5Parser*>(nsHtml5Module::NewHtml5Parser().get());
     // Now sHTMLFragmentParser owns the object
   }
   sHTMLFragmentParser->ParseHtml5Fragment(aSourceBuffer,
                                           aTargetNode,
                                           aContextLocalName,
                                           aContextNamespace,
                                           aQuirks,
                                           aPreventScriptExecution);
   sHTMLFragmentParser->Reset();
+  return NS_OK;
 }
 
 /* static */
 nsresult
 nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
                                  nsIDocument* aDocument,
                                  nsTArray<nsString>& aTagStack,
                                  bool aPreventScriptExecution,
                                  nsIDOMDocumentFragment** aReturn)
 {
+  if (nsContentUtils::sFragmentParsingActive) {
+    NS_NOTREACHED("Re-entrant fragment parsing attempted.");
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
+  nsContentUtils::sFragmentParsingActive = true;
   if (!sXMLFragmentParser) {
     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID);
     parser.forget(&sXMLFragmentParser);
     // sXMLFragmentParser now owns the parser
   }
   if (!sXMLFragmentSink) {
     NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
     // sXMLFragmentSink now owns the sink
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -765,23 +765,23 @@ nsGenericHTMLElement::SetInnerHTML(const
   }
 
   nsAutoScriptLoaderDisabler sld(doc);
   
   nsCOMPtr<nsIDOMDocumentFragment> df;
 
   if (doc->IsHTML()) {
     PRInt32 oldChildCount = GetChildCount();
-    nsContentUtils::ParseFragmentHTML(aInnerHTML,
-                                      this,
-                                      Tag(),
-                                      GetNameSpaceID(),
-                                      doc->GetCompatibilityMode() ==
-                                          eCompatibility_NavQuirks,
-                                      PR_TRUE);
+    rv = nsContentUtils::ParseFragmentHTML(aInnerHTML,
+                                           this,
+                                           Tag(),
+                                           GetNameSpaceID(),
+                                           doc->GetCompatibilityMode() ==
+                                             eCompatibility_NavQuirks,
+                                           PR_TRUE);
     // HTML5 parser has notified, but not fired mutation events.
     FireMutationEventsForDirectParsing(doc, this, oldChildCount);
   } else {
     rv = nsContentUtils::CreateContextualFragment(this, aInnerHTML,
                                                   PR_TRUE,
                                                   getter_AddRefs(df));
     nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
     if (NS_SUCCEEDED(rv)) {
@@ -836,48 +836,49 @@ nsGenericHTMLElement::InsertAdjacentHTML
 
   // Needed when insertAdjacentHTML is used in combination with contenteditable
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
   nsAutoScriptLoaderDisabler sld(doc);
   
   // Batch possible DOMSubtreeModified events.
   mozAutoSubtreeModified subtree(doc, nsnull);
 
+  nsresult rv;
   // Parse directly into destination if possible
   if (doc->IsHTML() &&
       (position == eBeforeEnd ||
        (position == eAfterEnd && !GetNextSibling()) ||
        (position == eAfterBegin && !GetFirstChild()))) {
     PRInt32 oldChildCount = destination->GetChildCount();
     PRInt32 contextNs = destination->GetNameSpaceID();
     nsIAtom* contextLocal = destination->Tag();
     if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
       // For compat with IE6 through IE9. Willful violation of HTML5 as of
       // 2011-04-06. CreateContextualFragment does the same already.
       // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
       contextLocal = nsGkAtoms::body;
     }
-    nsContentUtils::ParseFragmentHTML(aText,
-                                      destination,
-                                      contextLocal,
-                                      contextNs,
-                                      doc->GetCompatibilityMode() ==
-                                          eCompatibility_NavQuirks,
-                                      PR_TRUE);
+    rv = nsContentUtils::ParseFragmentHTML(aText,
+                                           destination,
+                                           contextLocal,
+                                           contextNs,
+                                           doc->GetCompatibilityMode() ==
+                                             eCompatibility_NavQuirks,
+                                           PR_TRUE);
     // HTML5 parser has notified, but not fired mutation events.
     FireMutationEventsForDirectParsing(doc, destination, oldChildCount);
-    return NS_OK;
+    return rv;
   }
 
   // couldn't parse directly
   nsCOMPtr<nsIDOMDocumentFragment> df;
-  nsresult rv = nsContentUtils::CreateContextualFragment(destination,
-                                                         aText,
-                                                         PR_TRUE,
-                                                         getter_AddRefs(df));
+  rv = nsContentUtils::CreateContextualFragment(destination,
+                                                aText,
+                                                PR_TRUE,
+                                                getter_AddRefs(df));
   nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Suppress assertion about node removal mutation events that can't have
   // listeners anyway, because no one has had the chance to register mutation
   // listeners on the fragment that comes from the parser.
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -2702,33 +2702,34 @@ nsresult nsHTMLEditor::CreateDOMFragment
 
 
 nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr,
                                      nsIAtom* aContextLocalName,
                                      nsIDocument* aTargetDocument,
                                      nsCOMPtr<nsIDOMNode> *outNode,
                                      bool aTrustedInput)
 {
+  nsresult rv;
   nsCOMPtr<nsIDOMDocumentFragment> frag;
   NS_NewDocumentFragment(getter_AddRefs(frag),
                          aTargetDocument->NodeInfoManager());
   nsCOMPtr<nsIContent> fragment = do_QueryInterface(frag);
-  nsContentUtils::ParseFragmentHTML(aFragStr,
-                                    fragment,
-                                    aContextLocalName ?
-                                        aContextLocalName : nsGkAtoms::body,
-                                    kNameSpaceID_XHTML,
-                                    PR_FALSE,
-                                    PR_TRUE);
+  rv = nsContentUtils::ParseFragmentHTML(aFragStr,
+                                         fragment,
+                                         aContextLocalName ?
+                                           aContextLocalName : nsGkAtoms::body,
+                                        kNameSpaceID_XHTML,
+                                        PR_FALSE,
+                                        PR_TRUE);
   if (!aTrustedInput) {
     nsTreeSanitizer sanitizer(!!aContextLocalName, !aContextLocalName);
     sanitizer.Sanitize(fragment);
   }
   *outNode = do_QueryInterface(frag);
-  return NS_OK;
+  return rv;
 }
 
 nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode  *aFragmentAsNode,
                                                 nsCOMArray<nsIDOMNode>& outNodeList,
                                                 nsIDOMNode *aStartNode,
                                                 PRInt32 aStartOffset,
                                                 nsIDOMNode *aEndNode,
                                                 PRInt32 aEndOffset)
--- a/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
+++ b/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
@@ -175,22 +175,22 @@ nsScriptableUnescapeHTML::ParseFragment(
                                             tagStack,
                                             PR_TRUE,
                                             aReturn);
       fragment = do_QueryInterface(*aReturn);
     } else {
       NS_NewDocumentFragment(aReturn,
                              document->NodeInfoManager());
       fragment = do_QueryInterface(*aReturn);
-      nsContentUtils::ParseFragmentHTML(aFragment,
-                                        fragment,
-                                        nsGkAtoms::body,
-                                        kNameSpaceID_XHTML,
-                                        PR_FALSE,
-                                        PR_TRUE);
+      rv = nsContentUtils::ParseFragmentHTML(aFragment,
+                                             fragment,
+                                             nsGkAtoms::body,
+                                             kNameSpaceID_XHTML,
+                                             PR_FALSE,
+                                             PR_TRUE);
       // Now, set the base URI on all subtree roots.
       if (aBaseURI) {
         aBaseURI->GetSpec(spec);
         nsAutoString spec16;
         CopyUTF8toUTF16(spec, spec16);
         nsIContent* node = fragment->GetFirstChild();
         while (node) {
           if (node->IsElement()) {