Bug 959150 part 5 - Avoid reallocating the attribute holder when parsing with nsHtml5StringParser. r=smaug.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Wed, 05 Mar 2014 21:38:50 +0200
changeset 172305 aeef9a68e4a5042a2654cf823ef349bda7f2b1c2
parent 172304 5acef36769eb72f297bbea048916101894a7641b
child 172306 5182bb4118cc1bb809d0211c660f1bba0a693abc
push id26358
push usercbook@mozilla.com
push dateFri, 07 Mar 2014 11:48:31 +0000
treeherdermozilla-central@b9fc2eb18bd1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs959150
milestone30.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 959150 part 5 - Avoid reallocating the attribute holder when parsing with nsHtml5StringParser. r=smaug.
parser/html/javasrc/Tokenizer.java
parser/html/javasrc/TreeBuilder.java
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5Tokenizer.h
parser/html/nsHtml5TokenizerHSupplement.h
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeBuilderHSupplement.h
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -492,22 +492,22 @@ public class Tokenizer implements Locato
     private XmlViolationPolicy commentPolicy = XmlViolationPolicy.ALTER_INFOSET;
 
     private XmlViolationPolicy xmlnsPolicy = XmlViolationPolicy.ALTER_INFOSET;
 
     private XmlViolationPolicy namePolicy = XmlViolationPolicy.ALTER_INFOSET;
 
     private boolean html4ModeCompatibleWithXhtml1Schemata;
 
-    private final boolean newAttributesEachTime;
-
     private int mappingLangToXmlLang;
 
     // ]NOCPP]
 
+    private final boolean newAttributesEachTime;
+
     private boolean shouldSuspend;
 
     protected boolean confident;
 
     private int line;
 
     private Interner interner;
 
@@ -549,18 +549,22 @@ public class Tokenizer implements Locato
         // ]NOCPP]
         this.bmpChar = new char[1];
         this.astralChar = new char[2];
         this.tagName = null;
         this.attributeName = null;
         this.doctypeName = null;
         this.publicIdentifier = null;
         this.systemIdentifier = null;
+        // [NOCPP[
         this.attributes = null;
-    // CPPONLY: this.viewingXmlSource = viewingXmlSource;
+        // ]NOCPP]
+        // CPPONLY: this.attributes = tokenHandler.HasBuilder() ? new HtmlAttributes(mappingLangToXmlLang) : null;
+        // CPPONLY: this.newAttributesEachTime = !tokenHandler.HasBuilder();
+        // CPPONLY: this.viewingXmlSource = viewingXmlSource;
     }
 
     public void setInterner(Interner interner) {
         this.interner = interner;
     }
 
     public void initLocation(String newPublicId, String newSystemId) {
         this.systemId = newSystemId;
@@ -1091,31 +1095,16 @@ public class Tokenizer implements Locato
     public void warn(String message) throws SAXException {
         if (errorHandler == null) {
             return;
         }
         SAXParseException spe = new SAXParseException(message, this);
         errorHandler.warning(spe);
     }
 
-    /**
-     * 
-     */
-    private void resetAttributes() {
-        // [NOCPP[
-        if (newAttributesEachTime) {
-            // ]NOCPP]
-            attributes = null;
-            // [NOCPP[
-        } else {
-            attributes.clear(mappingLangToXmlLang);
-        }
-        // ]NOCPP]
-    }
-
     private void strBufToElementNameString() {
         // if (strBufOffset != -1) {
         // return ElementName.elementNameByBuffer(buf, strBufOffset, strBufLen);
         // } else {
         tagName = ElementName.elementNameByBuffer(strBuf, 0, strBufLen,
                 interner);
         // }
     }
@@ -1131,27 +1120,36 @@ public class Tokenizer implements Locato
             /*
              * When an end tag token is emitted, the content model flag must be
              * switched to the PCDATA state.
              */
             maybeErrAttributesOnEndTag(attrs);
             // CPPONLY: if (!viewingXmlSource) {
             tokenHandler.endTag(tagName);
             // CPPONLY: }
-            Portability.delete(attributes);
+            // CPPONLY: if (newAttributesEachTime) {
+            // CPPONLY:   Portability.delete(attributes);
+            // CPPONLY:   attributes = null;
+            // CPPONLY: }
         } else {
             // CPPONLY: if (viewingXmlSource) {
-            // CPPONLY: Portability.delete(attributes);
+            // CPPONLY:   assert newAttributesEachTime;
+            // CPPONLY:   Portability.delete(attributes);
+            // CPPONLY:   attributes = null;
             // CPPONLY: } else {
             tokenHandler.startTag(tagName, attrs, selfClosing);
             // CPPONLY: }
         }
         tagName.release();
         tagName = null;
-        resetAttributes();
+        if (newAttributesEachTime) {
+            attributes = null;
+        } else {
+            attributes.clear(mappingLangToXmlLang);
+        }
         /*
          * The token handler may have called setStateAndEndTagExpectation
          * and changed stateSave since the start of this method.
          */
         return stateSave;
     }
 
     private void attributeNameComplete() throws SAXException {
@@ -6592,21 +6590,21 @@ public class Tokenizer implements Locato
             tagName.release();
             tagName = null;
         }
         if (attributeName != null) {
             attributeName.release();
             attributeName = null;
         }
         tokenHandler.endTokenization();
+        // [NOCPP[
         if (attributes != null) {
-            attributes.clear(mappingLangToXmlLang);
-            Portability.delete(attributes);
             attributes = null;
         }
+        // ]NOCPP]
     }
 
     public void requestSuspension() {
         shouldSuspend = true;
     }
 
     // [NOCPP[
     
@@ -6675,26 +6673,22 @@ public class Tokenizer implements Locato
         if (tagName != null) {
             tagName.release();
             tagName = null;
         }
         if (attributeName != null) {
             attributeName.release();
             attributeName = null;
         }
-        // [NOCPP[
         if (newAttributesEachTime) {
-            // ]NOCPP]
             if (attributes != null) {
                 Portability.delete(attributes);
                 attributes = null;
             }
-            // [NOCPP[
         }
-        // ]NOCPP]
     }
 
     public void loadState(Tokenizer other) throws SAXException {
         strBufLen = other.strBufLen;
         if (strBufLen > strBuf.length) {
             strBuf = new char[strBufLen];
         }
         System.arraycopy(other.strBuf, 0, strBuf, 0, strBufLen);
@@ -7000,16 +6994,18 @@ public class Tokenizer implements Locato
      */
     public void setEncodingDeclarationHandler(
             EncodingDeclarationHandler encodingDeclarationHandler) {
         this.encodingDeclarationHandler = encodingDeclarationHandler;
     }
     
     void destructor() {
         // The translator will write refcount tracing stuff here
+        Portability.delete(attributes);
+        attributes = null;
     }
     
     // [NOCPP[
     
     /**
      * Sets an offset to be added to the position reported to 
      * <code>TransitionHandler</code>.
      * 
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -3022,19 +3022,19 @@ public abstract class TreeBuilder<T> imp
                     assert false;
                     break starttagloop; // Avoid infinite loop if the assertion
                                         // fails
             }
         }
         if (selfClosing) {
             errSelfClosing();
         }
-        if (attributes != HtmlAttributes.EMPTY_ATTRIBUTES) {
-            Portability.delete(attributes);
-        }
+        // CPPONLY: if (mBuilder == null && attributes != HtmlAttributes.EMPTY_ATTRIBUTES) {
+        // CPPONLY:    Portability.delete(attributes);
+        // CPPONLY: }
     }
 
     private void startTagTitleInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
         appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
         originalMode = mode;
         mode = TEXT;
         tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, elementName);
     }
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -93,17 +93,18 @@ nsHtml5Tokenizer::nsHtml5Tokenizer(nsHtm
     encodingDeclarationHandler(nullptr),
     bmpChar(jArray<char16_t,int32_t>::newJArray(1)),
     astralChar(jArray<char16_t,int32_t>::newJArray(2)),
     tagName(nullptr),
     attributeName(nullptr),
     doctypeName(nullptr),
     publicIdentifier(nullptr),
     systemIdentifier(nullptr),
-    attributes(nullptr),
+    attributes(tokenHandler->HasBuilder() ? new nsHtml5HtmlAttributes(0) : nullptr),
+    newAttributesEachTime(!tokenHandler->HasBuilder()),
     viewingXmlSource(viewingXmlSource)
 {
   MOZ_COUNT_CTOR(nsHtml5Tokenizer);
 }
 
 void 
 nsHtml5Tokenizer::setInterner(nsHtml5AtomTable* interner)
 {
@@ -279,22 +280,16 @@ nsHtml5Tokenizer::flushChars(char16_t* b
 {
   if (pos > cstart) {
     tokenHandler->characters(buf, cstart, pos - cstart);
   }
   cstart = INT32_MAX;
 }
 
 void 
-nsHtml5Tokenizer::resetAttributes()
-{
-  attributes = nullptr;
-}
-
-void 
 nsHtml5Tokenizer::strBufToElementNameString()
 {
   tagName = nsHtml5ElementName::elementNameByBuffer(strBuf, 0, strBufLen, interner);
 }
 
 int32_t 
 nsHtml5Tokenizer::emitCurrentTagToken(bool selfClosing, int32_t pos)
 {
@@ -302,27 +297,36 @@ nsHtml5Tokenizer::emitCurrentTagToken(bo
   maybeErrSlashInEndTag(selfClosing);
   stateSave = NS_HTML5TOKENIZER_DATA;
   nsHtml5HtmlAttributes* attrs = (!attributes ? nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES : attributes);
   if (endTag) {
     maybeErrAttributesOnEndTag(attrs);
     if (!viewingXmlSource) {
       tokenHandler->endTag(tagName);
     }
-    delete attributes;
+    if (newAttributesEachTime) {
+      delete attributes;
+      attributes = nullptr;
+    }
   } else {
     if (viewingXmlSource) {
+      MOZ_ASSERT(newAttributesEachTime);
       delete attributes;
+      attributes = nullptr;
     } else {
       tokenHandler->startTag(tagName, attrs, selfClosing);
     }
   }
   tagName->release();
   tagName = nullptr;
-  resetAttributes();
+  if (newAttributesEachTime) {
+    attributes = nullptr;
+  } else {
+    attributes->clear(0);
+  }
   return stateSave;
 }
 
 void 
 nsHtml5Tokenizer::attributeNameComplete()
 {
   attributeName = nsHtml5AttributeName::nameByBuffer(strBuf, 0, strBufLen, interner);
   if (!attributes) {
@@ -3927,21 +3931,16 @@ nsHtml5Tokenizer::end()
     tagName->release();
     tagName = nullptr;
   }
   if (attributeName) {
     attributeName->release();
     attributeName = nullptr;
   }
   tokenHandler->endTokenization();
-  if (attributes) {
-    attributes->clear(0);
-    delete attributes;
-    attributes = nullptr;
-  }
 }
 
 void 
 nsHtml5Tokenizer::requestSuspension()
 {
   shouldSuspend = true;
 }
 
@@ -3976,19 +3975,21 @@ nsHtml5Tokenizer::resetToDataState()
   if (tagName) {
     tagName->release();
     tagName = nullptr;
   }
   if (attributeName) {
     attributeName->release();
     attributeName = nullptr;
   }
-  if (attributes) {
-    delete attributes;
-    attributes = nullptr;
+  if (newAttributesEachTime) {
+    if (attributes) {
+      delete attributes;
+      attributes = nullptr;
+    }
   }
 }
 
 void 
 nsHtml5Tokenizer::loadState(nsHtml5Tokenizer* other)
 {
   strBufLen = other->strBufLen;
   if (strBufLen > strBuf.length) {
@@ -4075,16 +4076,18 @@ nsHtml5Tokenizer::setEncodingDeclaration
 {
   this->encodingDeclarationHandler = encodingDeclarationHandler;
 }
 
 
 nsHtml5Tokenizer::~nsHtml5Tokenizer()
 {
   MOZ_COUNT_DTOR(nsHtml5Tokenizer);
+  delete attributes;
+  attributes = nullptr;
 }
 
 void
 nsHtml5Tokenizer::initializeStatics()
 {
 }
 
 void
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -125,16 +125,17 @@ class nsHtml5Tokenizer
     nsHtml5ElementName* tagName;
   protected:
     nsHtml5AttributeName* attributeName;
   private:
     nsIAtom* doctypeName;
     nsString* publicIdentifier;
     nsString* systemIdentifier;
     nsHtml5HtmlAttributes* attributes;
+    bool newAttributesEachTime;
     bool shouldSuspend;
   protected:
     bool confident;
   private:
     int32_t line;
     nsHtml5AtomTable* interner;
     bool viewingXmlSource;
   public:
@@ -201,17 +202,16 @@ class nsHtml5Tokenizer
       appendLongStrBuf(strBuf, 0, strBufLen);
     }
 
     nsString* longStrBufToString();
     void emitComment(int32_t provisionalHyphens, int32_t pos);
   protected:
     void flushChars(char16_t* buf, int32_t pos);
   private:
-    void resetAttributes();
     void strBufToElementNameString();
     int32_t emitCurrentTagToken(bool selfClosing, int32_t pos);
     void attributeNameComplete();
     void addAttributeWithoutValue();
     void addAttributeWithValue();
   public:
     void start();
     bool tokenizeBuffer(nsHtml5UTF16Buffer* buffer);
--- a/parser/html/nsHtml5TokenizerHSupplement.h
+++ b/parser/html/nsHtml5TokenizerHSupplement.h
@@ -1,12 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+inline nsHtml5HtmlAttributes* GetAttributes()
+{
+  return attributes;
+}
+
 nsAutoPtr<nsHtml5Highlighter> mViewSource;
 
 /**
  * Starts handling text/plain. This is a one-way initialization. There is
  * no corresponding EndPlainText() call.
  */
 void StartPlainText();
 
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -1886,17 +1886,17 @@ nsHtml5TreeBuilder::startTag(nsHtml5Elem
         NS_HTML5_BREAK(starttagloop);
       }
     }
   }
   starttagloop_end: ;
   if (selfClosing) {
     errSelfClosing();
   }
-  if (attributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
+  if (!mBuilder && attributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
     delete attributes;
   }
 }
 
 void 
 nsHtml5TreeBuilder::startTagTitleInHead(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -76,17 +76,18 @@ nsHtml5TreeBuilder::createElement(int32_
   if (mBuilder) {
     nsCOMPtr<nsIAtom> name = nsHtml5TreeOperation::Reget(aName);
     nsIContent* elem =
       nsHtml5TreeOperation::CreateElement(aNamespace,
                                           name,
                                           aAttributes,
                                           mozilla::dom::FROM_PARSER_FRAGMENT,
                                           mBuilder);
-    if (aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
+    if (MOZ_UNLIKELY(aAttributes != tokenizer->GetAttributes() &&
+                     aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES)) {
       delete aAttributes;
     }
     return elem;
   }
 
   nsIContentHandle* content = AllocateContentHandle();
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
@@ -506,16 +507,18 @@ nsHtml5TreeBuilder::addAttributesToEleme
   NS_PRECONDITION(aElement, "Null element");
   NS_PRECONDITION(aAttributes, "Null attributes");
 
   if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
     return;
   }
 
   if (mBuilder) {
+    MOZ_ASSERT(aAttributes == tokenizer->GetAttributes(),
+      "Using attribute other than the tokenizer's to add to body or html.");
     nsresult rv = nsHtml5TreeOperation::AddAttributes(
       static_cast<nsIContent*>(aElement),
       aAttributes,
       mBuilder);
     if (NS_FAILED(rv)) {
       MarkAsBrokenAndRequestSuspension(rv);
     }
     return;
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -114,16 +114,20 @@
     void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine);
 
     void DropHandles();
 
     void SetPreventScriptExecution(bool aPrevent) {
       mPreventScriptExecution = aPrevent;
     }
 
+    bool HasBuilder() {
+      return mBuilder;
+    }
+
     void EnableViewSource(nsHtml5Highlighter* aHighlighter);
 
     void errStrayStartTag(nsIAtom* aName);
 
     void errStrayEndTag(nsIAtom* aName);
 
     void errUnclosedElements(int32_t aIndex, nsIAtom* aName);