Bug 1355479 - Flatten attribute storage in the HTML parser to AutoTArray to avoid malloc. r=wchen
authorHenri Sivonen <hsivonen@hsivonen.fi>
Thu, 13 Apr 2017 11:14:48 +0300
changeset 355848 5fc028d97c7565dcb057d3ad27bafa0b6f886255
parent 355847 f5fbea7e93d41fa6b77ef084d5d33989c91d9c8f
child 355849 83113056a17715711e8544248bd82ce30cbd4da2
push id31747
push userkwierso@gmail.com
push dateMon, 01 May 2017 22:40:00 +0000
treeherdermozilla-central@4168f0fec834 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen
bugs1355479
milestone55.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 1355479 - Flatten attribute storage in the HTML parser to AutoTArray to avoid malloc. r=wchen MozReview-Commit-ID: 77Hqo24F2nB
parser/html/javasrc/AttributeName.java
parser/html/javasrc/ElementName.java
parser/html/javasrc/HtmlAttributes.java
parser/html/javasrc/Tokenizer.java
parser/html/javasrc/TreeBuilder.java
parser/html/moz.build
parser/html/nsHtml5AtomList.h
parser/html/nsHtml5AttributeEntry.h
parser/html/nsHtml5AttributeName.cpp
parser/html/nsHtml5AttributeName.h
parser/html/nsHtml5ElementName.cpp
parser/html/nsHtml5ElementName.h
parser/html/nsHtml5HtmlAttributes.cpp
parser/html/nsHtml5HtmlAttributes.h
parser/html/nsHtml5MetaScanner.cpp
parser/html/nsHtml5MetaScanner.h
parser/html/nsHtml5Portability.h
parser/html/nsHtml5ReleasableAttributeName.cpp
parser/html/nsHtml5ReleasableAttributeName.h
parser/html/nsHtml5StackNode.cpp
parser/html/nsHtml5StackNode.h
parser/html/nsHtml5StateSnapshot.cpp
parser/html/nsHtml5StateSnapshot.h
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5Tokenizer.h
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilder.h
parser/html/nsHtml5UTF16Buffer.cpp
parser/html/nsHtml5UTF16Buffer.h
--- a/parser/html/javasrc/AttributeName.java
+++ b/parser/html/javasrc/AttributeName.java
@@ -26,17 +26,16 @@ import java.util.Arrays;
 
 import nu.validator.htmlparser.annotation.Inline;
 import nu.validator.htmlparser.annotation.Local;
 import nu.validator.htmlparser.annotation.NoLength;
 import nu.validator.htmlparser.annotation.NsUri;
 import nu.validator.htmlparser.annotation.Prefix;
 import nu.validator.htmlparser.annotation.QName;
 import nu.validator.htmlparser.annotation.Unsigned;
-import nu.validator.htmlparser.annotation.Virtual;
 import nu.validator.htmlparser.common.Interner;
 
 public final class AttributeName
 // Uncomment to regenerate
 // implements Comparable<AttributeName>
 {
     // [NOCPP[
 
@@ -268,45 +267,29 @@ public final class AttributeName
      * @param length
      *            length of data
      * @param checkNcName
      *            whether to check ncnameness
      * @return an <code>AttributeName</code> corresponding to the argument data
      */
     static AttributeName nameByBuffer(@NoLength char[] buf, int offset,
             int length
-            // [NOCPP[
-            , boolean checkNcName
-            // ]NOCPP]
             , Interner interner) {
         // XXX deal with offset
         @Unsigned int hash = AttributeName.bufToHash(buf, length);
         int index = Arrays.binarySearch(AttributeName.ATTRIBUTE_HASHES, hash);
         if (index < 0) {
-            return AttributeName.createAttributeName(
-                    Portability.newLocalNameFromBuffer(buf, offset, length,
-                            interner)
-                    // [NOCPP[
-                    , checkNcName
-            // ]NOCPP]
-            );
-        } else {
-            AttributeName attributeName = AttributeName.ATTRIBUTE_NAMES[index];
-            @Local String name = attributeName.getLocal(AttributeName.HTML);
-            if (!Portability.localEqualsBuffer(name, buf, offset, length)) {
-                return AttributeName.createAttributeName(
-                        Portability.newLocalNameFromBuffer(buf, offset, length,
-                                interner)
-                        // [NOCPP[
-                        , checkNcName
-                // ]NOCPP]
-                );
-            }
-            return attributeName;
+            return null;
         }
+        AttributeName attributeName = AttributeName.ATTRIBUTE_NAMES[index];
+        @Local String name = attributeName.getLocal(AttributeName.HTML);
+        if (!Portability.localEqualsBuffer(name, buf, offset, length)) {
+            return null;
+        }
+        return attributeName;
     }
 
     /**
      * This method has to return a unique positive integer for each well-known
      * lower-cased attribute name.
      *
      * @param buf
      * @param len
@@ -378,16 +361,18 @@ public final class AttributeName
      */
     private final @Local @NoLength String[] local;
 
     /**
      * The prefixes indexably by mode.
      */
     private final @Prefix @NoLength String[] prefix;
 
+    // CPPONLY: private final boolean custom;
+
     // [NOCPP[
 
     private final int flags;
 
     /**
      * The qnames indexable by mode.
      */
     private final @QName @NoLength String[] qName;
@@ -403,41 +388,60 @@ public final class AttributeName
      *            the local name
      * @param prefix
      *            the prefix
      * @param ncname
      *            the ncnameness
      * @param xmlns
      *            whether this is an xmlns attribute
      */
-    protected AttributeName(@NsUri @NoLength String[] uri,
+    private AttributeName(@NsUri @NoLength String[] uri,
             @Local @NoLength String[] local, @Prefix @NoLength String[] prefix
             // [NOCPP[
             , int flags
     // ]NOCPP]
     ) {
         this.uri = uri;
         this.local = local;
         this.prefix = prefix;
         // [NOCPP[
         this.qName = COMPUTE_QNAME(local, prefix);
         this.flags = flags;
         // ]NOCPP]
+        // CPPONLY: this.custom = false;
     }
 
+    // CPPONLY: public AttributeName() {
+    // CPPONLY:     this.uri = AttributeName.ALL_NO_NS;
+    // CPPONLY:     this.local = AttributeName.SAME_LOCAL(null);
+    // CPPONLY:     this.prefix = ALL_NO_PREFIX;
+    // CPPONLY:     this.custom = true;
+    // CPPONLY: }
+    // CPPONLY:
+    // CPPONLY: public boolean isInterned() {
+    // CPPONLY:     return !custom;
+    // CPPONLY: }
+    // CPPONLY:
+    // CPPONLY: public void setNameForNonInterned(@Local String name) {
+    // CPPONLY:     assert custom;
+    // CPPONLY:     local[0] = name;
+    // CPPONLY:     local[1] = name;
+    // CPPONLY:     local[2] = name;
+    // CPPONLY: }
+
     /**
      * Creates an <code>AttributeName</code> for a local name.
      *
      * @param name
      *            the name
      * @param checkNcName
      *            whether to check ncnameness
      * @return an <code>AttributeName</code>
      */
-    private static AttributeName createAttributeName(@Local String name
+    static AttributeName createAttributeName(@Local String name
     // [NOCPP[
             , boolean checkNcName
     // ]NOCPP]
     ) {
         // [NOCPP[
         int flags = NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG;
         if (name.startsWith("xmlns:")) {
             flags = IS_XMLNS;
@@ -445,42 +449,22 @@ public final class AttributeName
             flags = 0;
         }
         // ]NOCPP]
         return new AttributeName(AttributeName.ALL_NO_NS,
                 AttributeName.SAME_LOCAL(name), ALL_NO_PREFIX, flags);
     }
 
     /**
-     * Deletes runtime-allocated instances in C++.
-     */
-    @Virtual void release() {
-        // No-op in Java.
-        // Implement as |delete this;| in subclass.
-    }
-
-    /**
      * The C++ destructor.
      */
-    @SuppressWarnings("unused") @Virtual private void destructor() {
+    @SuppressWarnings("unused") private void destructor() {
         Portability.deleteArray(local);
     }
 
-    /**
-     * Clones the attribute using an interner. Returns <code>this</code> in Java
-     * and for non-dynamic instances in C++.
-     *
-     * @param interner
-     *            an interner
-     * @return a clone
-     */
-    @Virtual public AttributeName cloneAttributeName(Interner interner) {
-        return this;
-    }
-
     // [NOCPP[
     /**
      * Creator for use when the XML violation policy requires an attribute name
      * to be changed.
      *
      * @param name
      *            the name of the attribute to create
      */
--- a/parser/html/javasrc/ElementName.java
+++ b/parser/html/javasrc/ElementName.java
@@ -187,16 +187,21 @@ public final class ElementName
         // C++ case the scoped atom table remembers its own atoms.
         this.name = name;
         this.camelCaseName = name;
         assert this.flags == (TreeBuilder.OTHER | NOT_INTERNED);
     }
 
     public static final ElementName ANNOTATION_XML = new ElementName("annotation-xml", "annotation-xml", TreeBuilder.ANNOTATION_XML | SCOPING_AS_MATHML);
 
+    // CPPONLY: public static final ElementName ISINDEX = new ElementName("isindex", "isindex", TreeBuilder.ISINDEX | SPECIAL);
+    // [NOCPP[
+    public static final ElementName ISINDEX = new ElementName("isindex", "isindex", TreeBuilder.OTHER);
+    // ]NOCPP]
+
     // START CODE ONLY USED FOR GENERATING CODE uncomment and run to regenerate
 
 //    /**
 //     * @see java.lang.Object#toString()
 //     */
 //    @Override public String toString() {
 //        return "(\"" + name + "\", \"" + camelCaseName + "\", " + decomposedFlags() + ")";
 //    }
@@ -419,16 +424,19 @@ public final class ElementName
 //                    System.err.println("Hash collision: " + ELEMENT_NAMES[i].name
 //                            + ", " + ELEMENT_NAMES[j].name);
 //                    return;
 //                }
 //            }
 //        }
 //        for (int i = 0; i < ELEMENT_NAMES.length; i++) {
 //            ElementName el = ELEMENT_NAMES[i];
+//            if ("isindex".equals(el.name)) {
+//                continue;
+//            }
 //            System.out.println("public static final ElementName "
 //                    + el.constName() + " = new ElementName" + el.toString()
 //                    + ";");
 //        }
 //        System.out.println("private final static @NoLength ElementName[] ELEMENT_NAMES = {");
 //        for (int i = 0; i < ELEMENT_NAMES.length; i++) {
 //            ElementName el = ELEMENT_NAMES[i];
 //            System.out.println(el.constName() + ",");
@@ -636,17 +644,16 @@ public final class ElementName
     public static final ElementName SCRIPT = new ElementName("script", "script", TreeBuilder.SCRIPT | SPECIAL);
     public static final ElementName TFOOT = new ElementName("tfoot", "tfoot", TreeBuilder.TBODY_OR_THEAD_OR_TFOOT | SPECIAL | FOSTER_PARENTING | OPTIONAL_END_TAG);
     public static final ElementName TEXT = new ElementName("text", "text", TreeBuilder.OTHER);
     public static final ElementName MENU = new ElementName("menu", "menu", TreeBuilder.DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU | SPECIAL);
     public static final ElementName FEDROPSHADOW = new ElementName("fedropshadow", "feDropShadow", TreeBuilder.OTHER);
     public static final ElementName VIEW = new ElementName("view", "view", TreeBuilder.OTHER);
     public static final ElementName FECOLORMATRIX = new ElementName("fecolormatrix", "feColorMatrix", TreeBuilder.OTHER);
     public static final ElementName FECONVOLVEMATRIX = new ElementName("feconvolvematrix", "feConvolveMatrix", TreeBuilder.OTHER);
-    public static final ElementName ISINDEX = new ElementName("isindex", "isindex", TreeBuilder.ISINDEX | SPECIAL);
     public static final ElementName BODY = new ElementName("body", "body", TreeBuilder.BODY | SPECIAL | OPTIONAL_END_TAG);
     public static final ElementName FEMORPHOLOGY = new ElementName("femorphology", "feMorphology", TreeBuilder.OTHER);
     public static final ElementName RUBY = new ElementName("ruby", "ruby", TreeBuilder.RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR);
     public static final ElementName SUMMARY = new ElementName("summary", "summary", TreeBuilder.ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY | SPECIAL);
     public static final ElementName TBODY = new ElementName("tbody", "tbody", TreeBuilder.TBODY_OR_THEAD_OR_TFOOT | SPECIAL | FOSTER_PARENTING | OPTIONAL_END_TAG);
     private final static @NoLength ElementName[] ELEMENT_NAMES = {
     BIG,
     BDO,
deleted file mode 100644
--- a/parser/html/javasrc/HtmlAttributes.java
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
- * Copyright (c) 2007 Henri Sivonen
- * Copyright (c) 2008-2011 Mozilla Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in 
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
- * DEALINGS IN THE SOFTWARE.
- */
-
-package nu.validator.htmlparser.impl;
-
-import nu.validator.htmlparser.annotation.Auto;
-import nu.validator.htmlparser.annotation.IdType;
-import nu.validator.htmlparser.annotation.Local;
-import nu.validator.htmlparser.annotation.NsUri;
-import nu.validator.htmlparser.annotation.Prefix;
-import nu.validator.htmlparser.annotation.QName;
-import nu.validator.htmlparser.common.Interner;
-import nu.validator.htmlparser.common.XmlViolationPolicy;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-
-/**
- * Be careful with this class. QName is the name in from HTML tokenization.
- * Otherwise, please refer to the interface doc.
- * 
- * @version $Id: AttributesImpl.java 206 2008-03-20 14:09:29Z hsivonen $
- * @author hsivonen
- */
-public final class HtmlAttributes implements Attributes {
-
-    // [NOCPP[
-
-    private static final AttributeName[] EMPTY_ATTRIBUTENAMES = new AttributeName[0];
-
-    private static final String[] EMPTY_STRINGS = new String[0];
-
-    // ]NOCPP]
-
-    public static final HtmlAttributes EMPTY_ATTRIBUTES = new HtmlAttributes(
-            AttributeName.HTML);
-
-    private int mode;
-
-    private int length;
-
-    private @Auto AttributeName[] names;
-
-    private @Auto String[] values; // XXX perhaps make this @NoLength?
-    
-    // CPPONLY: private @Auto int[] lines; // XXX perhaps make this @NoLength?
-
-    // [NOCPP[
-
-    private String idValue;
-
-    private int xmlnsLength;
-
-    private AttributeName[] xmlnsNames;
-
-    private String[] xmlnsValues;
-
-    // ]NOCPP]
-
-    public HtmlAttributes(int mode) {
-        this.mode = mode;
-        this.length = 0;
-        /*
-         * The length of 5 covers covers 98.3% of elements
-         * according to Hixie, but lets round to the next power of two for
-         * jemalloc.
-         */
-        this.names = new AttributeName[8];
-        this.values = new String[8];
-        // CPPONLY: this.lines = new int[8];
-
-        // [NOCPP[
-
-        this.idValue = null;
-
-        this.xmlnsLength = 0;
-
-        this.xmlnsNames = HtmlAttributes.EMPTY_ATTRIBUTENAMES;
-
-        this.xmlnsValues = HtmlAttributes.EMPTY_STRINGS;
-
-        // ]NOCPP]
-    }
-    /*
-    public HtmlAttributes(HtmlAttributes other) {
-        this.mode = other.mode;
-        this.length = other.length;
-        this.names = new AttributeName[other.length];
-        this.values = new String[other.length];
-        // [NOCPP[
-        this.idValue = other.idValue;
-        this.xmlnsLength = other.xmlnsLength;
-        this.xmlnsNames = new AttributeName[other.xmlnsLength];
-        this.xmlnsValues = new String[other.xmlnsLength];
-        // ]NOCPP]
-    }
-    */
-
-    void destructor() {
-        clear(0);
-    }
-    
-    /**
-     * Only use with a static argument
-     * 
-     * @param name
-     * @return
-     */
-    public int getIndex(AttributeName name) {
-        for (int i = 0; i < length; i++) {
-            if (names[i] == name) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Only use with static argument.
-     * 
-     * @see org.xml.sax.Attributes#getValue(java.lang.String)
-     */
-    public String getValue(AttributeName name) {
-        int index = getIndex(name);
-        if (index == -1) {
-            return null;
-        } else {
-            return getValueNoBoundsCheck(index);
-        }
-    }
-
-    public int getLength() {
-        return length;
-    }
-
-    /**
-     * Variant of <code>getLocalName(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the local name at index
-     */
-    public @Local String getLocalNameNoBoundsCheck(int index) {
-        // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-        return names[index].getLocal(mode);
-    }
-
-    /**
-     * Variant of <code>getURI(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the namespace URI at index
-     */
-    public @NsUri String getURINoBoundsCheck(int index) {
-        // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-        return names[index].getUri(mode);
-    }
-
-    /**
-     * Variant of <code>getPrefix(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the namespace prefix at index
-     */
-    public @Prefix String getPrefixNoBoundsCheck(int index) {
-        // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-        return names[index].getPrefix(mode);
-    }
-
-    /**
-     * Variant of <code>getValue(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the attribute value at index
-     */
-    public String getValueNoBoundsCheck(int index) {
-        // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-        return values[index];
-    }
-
-    /**
-     * Variant of <code>getAttributeName(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the attribute name at index
-     */
-    public AttributeName getAttributeNameNoBoundsCheck(int index) {
-        // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-        return names[index];
-    }
-
-    // CPPONLY: /**
-    // CPPONLY: * Obtains a line number without bounds check.
-    // CPPONLY: * @param index a valid attribute index
-    // CPPONLY: * @return the line number at index or -1 if unknown
-    // CPPONLY: */
-    // CPPONLY: public int getLineNoBoundsCheck(int index) {
-    // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
-    // CPPONLY: return lines[index];
-    // CPPONLY: }
-
-    // [NOCPP[
-    
-    /**
-     * Variant of <code>getQName(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the QName at index
-     */
-    public @QName String getQNameNoBoundsCheck(int index) {
-        return names[index].getQName(mode);
-    }
-
-    /**
-     * Variant of <code>getType(int index)</code> without bounds check.
-     * @param index a valid attribute index
-     * @return the attribute type at index
-     */
-    public @IdType String getTypeNoBoundsCheck(int index) {
-        return (names[index] == AttributeName.ID) ? "ID" : "CDATA";
-    }
-
-    public int getIndex(String qName) {
-        for (int i = 0; i < length; i++) {
-            if (names[i].getQName(mode).equals(qName)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-    
-    public int getIndex(String uri, String localName) {
-        for (int i = 0; i < length; i++) {
-            if (names[i].getLocal(mode).equals(localName)
-                    && names[i].getUri(mode).equals(uri)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    public @IdType String getType(String qName) {
-        int index = getIndex(qName);
-        if (index == -1) {
-            return null;
-        } else {
-            return getType(index);
-        }
-    }
-
-    public @IdType String getType(String uri, String localName) {
-        int index = getIndex(uri, localName);
-        if (index == -1) {
-            return null;
-        } else {
-            return getType(index);
-        }
-    }
-    
-    public String getValue(String qName) {
-        int index = getIndex(qName);
-        if (index == -1) {
-            return null;
-        } else {
-            return getValue(index);
-        }
-    }
-
-    public String getValue(String uri, String localName) {
-        int index = getIndex(uri, localName);
-        if (index == -1) {
-            return null;
-        } else {
-            return getValue(index);
-        }
-    }
-    
-    public @Local String getLocalName(int index) {
-        if (index < length && index >= 0) {
-            return names[index].getLocal(mode);
-        } else {
-            return null;
-        }
-    }
-    
-    public @QName String getQName(int index) {
-        if (index < length && index >= 0) {
-            return names[index].getQName(mode);
-        } else {
-            return null;
-        }
-    }
-
-    public @IdType String getType(int index) {
-        if (index < length && index >= 0) {
-            return (names[index] == AttributeName.ID) ? "ID" : "CDATA";
-        } else {
-            return null;
-        }
-    }
-
-    public AttributeName getAttributeName(int index) {
-        if (index < length && index >= 0) {
-            return names[index];
-        } else {
-            return null;
-        }
-    }
-
-    public @NsUri String getURI(int index) {
-        if (index < length && index >= 0) {
-            return names[index].getUri(mode);
-        } else {
-            return null;
-        }
-    }
-
-    public @Prefix String getPrefix(int index) {
-        if (index < length && index >= 0) {
-            return names[index].getPrefix(mode);
-        } else {
-            return null;
-        }
-    }
-
-    public String getValue(int index) {
-        if (index < length && index >= 0) {
-            return values[index];
-        } else {
-            return null;
-        }
-    }
-
-    public String getId() {
-        return idValue;
-    }
-
-    public int getXmlnsLength() {
-        return xmlnsLength;
-    }
-
-    public @Local String getXmlnsLocalName(int index) {
-        if (index < xmlnsLength && index >= 0) {
-            return xmlnsNames[index].getLocal(mode);
-        } else {
-            return null;
-        }
-    }
-
-    public @NsUri String getXmlnsURI(int index) {
-        if (index < xmlnsLength && index >= 0) {
-            return xmlnsNames[index].getUri(mode);
-        } else {
-            return null;
-        }
-    }
-
-    public String getXmlnsValue(int index) {
-        if (index < xmlnsLength && index >= 0) {
-            return xmlnsValues[index];
-        } else {
-            return null;
-        }
-    }
-    
-    public int getXmlnsIndex(AttributeName name) {
-        for (int i = 0; i < xmlnsLength; i++) {
-            if (xmlnsNames[i] == name) {
-                return i;
-            }
-        }
-        return -1;
-    }
-    
-    public String getXmlnsValue(AttributeName name) {
-        int index = getXmlnsIndex(name);
-        if (index == -1) {
-            return null;
-        } else {
-            return getXmlnsValue(index);
-        }
-    }
-    
-    public AttributeName getXmlnsAttributeName(int index) {
-        if (index < xmlnsLength && index >= 0) {
-            return xmlnsNames[index];
-        } else {
-            return null;
-        }
-    }
-
-    // ]NOCPP]
-
-    void addAttribute(AttributeName name, String value
-            // [NOCPP[
-            , XmlViolationPolicy xmlnsPolicy
-            // ]NOCPP]
-            // CPPONLY: , int line
-    ) throws SAXException {
-        // [NOCPP[
-        if (name == AttributeName.ID) {
-            idValue = value;
-        }
-
-        if (name.isXmlns()) {
-            if (xmlnsNames.length == xmlnsLength) {
-                int newLen = xmlnsLength == 0 ? 2 : xmlnsLength << 1;
-                AttributeName[] newNames = new AttributeName[newLen];
-                System.arraycopy(xmlnsNames, 0, newNames, 0, xmlnsNames.length);
-                xmlnsNames = newNames;
-                String[] newValues = new String[newLen];
-                System.arraycopy(xmlnsValues, 0, newValues, 0, xmlnsValues.length);
-                xmlnsValues = newValues;
-            }
-            xmlnsNames[xmlnsLength] = name;
-            xmlnsValues[xmlnsLength] = value;
-            xmlnsLength++;
-            switch (xmlnsPolicy) {
-                case FATAL:
-                    // this is ugly
-                    throw new SAXException("Saw an xmlns attribute.");
-                case ALTER_INFOSET:
-                    return;
-                case ALLOW:
-                    // fall through
-            }
-        }
-
-        // ]NOCPP]
-
-        if (names.length == length) {
-            int newLen = length << 1; // The first growth covers virtually
-            // 100% of elements according to
-            // Hixie
-            AttributeName[] newNames = new AttributeName[newLen];
-            System.arraycopy(names, 0, newNames, 0, names.length);
-            names = newNames;
-            String[] newValues = new String[newLen];
-            System.arraycopy(values, 0, newValues, 0, values.length);
-            values = newValues;
-            // CPPONLY: int[] newLines = new int[newLen];
-            // CPPONLY: System.arraycopy(lines, 0, newLines, 0, lines.length);
-            // CPPONLY: lines = newLines;
-        }
-        names[length] = name;
-        values[length] = value;
-        // CPPONLY: lines[length] = line;
-        length++;
-    }
-
-    void clear(int m) {
-        for (int i = 0; i < length; i++) {
-            names[i].release();
-            names[i] = null;
-            Portability.releaseString(values[i]);
-            values[i] = null;
-        }
-        length = 0;
-        mode = m;
-        // [NOCPP[
-        idValue = null;
-        for (int i = 0; i < xmlnsLength; i++) {
-            xmlnsNames[i] = null;
-            xmlnsValues[i] = null;
-        }
-        xmlnsLength = 0;
-        // ]NOCPP]
-    }
-    
-    /**
-     * This is used in C++ to release special <code>isindex</code>
-     * attribute values whose ownership is not transferred.
-     */
-    void releaseValue(int i) {
-        Portability.releaseString(values[i]);        
-    }
-    
-    /**
-     * This is only used for <code>AttributeName</code> ownership transfer
-     * in the isindex case to avoid freeing custom names twice in C++.
-     */
-    void clearWithoutReleasingContents() {
-        for (int i = 0; i < length; i++) {
-            names[i] = null;
-            values[i] = null;
-        }
-        length = 0;
-    }
-
-    boolean contains(AttributeName name) {
-        for (int i = 0; i < length; i++) {
-            if (name.equalsAnother(names[i])) {
-                return true;
-            }
-        }
-        // [NOCPP[
-        for (int i = 0; i < xmlnsLength; i++) {
-            if (name.equalsAnother(xmlnsNames[i])) {
-                return true;
-            }
-        }
-        // ]NOCPP]
-        return false;
-    }
-
-    public void adjustForMath() {
-        mode = AttributeName.MATHML;
-    }
-
-    public void adjustForSvg() {
-        mode = AttributeName.SVG;
-    }
-
-    public HtmlAttributes cloneAttributes(Interner interner)
-            throws SAXException {
-        assert (length == 0
-                // [NOCPP[
-                && xmlnsLength == 0
-                // ]NOCPP]
-                )
-                || mode == 0 || mode == 3;
-        HtmlAttributes clone = new HtmlAttributes(0);
-        for (int i = 0; i < length; i++) {
-            clone.addAttribute(names[i].cloneAttributeName(interner),
-                    Portability.newStringFromString(values[i])
-                    // [NOCPP[
-                    , XmlViolationPolicy.ALLOW
-                    // ]NOCPP]
-                    // CPPONLY: , lines[i]
-            );
-        }
-        // [NOCPP[
-        for (int i = 0; i < xmlnsLength; i++) {
-            clone.addAttribute(xmlnsNames[i], xmlnsValues[i],
-                    XmlViolationPolicy.ALLOW);
-        }
-        // ]NOCPP]
-        return clone; // XXX!!!
-    }
-
-    public boolean equalsAnother(HtmlAttributes other) {
-        assert mode == 0 || mode == 3 : "Trying to compare attributes in foreign content.";
-        int otherLength = other.getLength();
-        if (length != otherLength) {
-            return false;
-        }
-        for (int i = 0; i < length; i++) {
-            // Work around the limitations of C++
-            boolean found = false;
-            // The comparing just the local names is OK, since these attribute
-            // holders are both supposed to belong to HTML formatting elements
-            @Local String ownLocal = names[i].getLocal(AttributeName.HTML);
-            for (int j = 0; j < otherLength; j++) {
-                if (ownLocal == other.names[j].getLocal(AttributeName.HTML)) {
-                    found = true;
-                    if (!Portability.stringEqualsString(values[i], other.values[j])) {
-                        return false;
-                    }
-                }
-            }
-            if (!found) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    // [NOCPP[
-    
-    void processNonNcNames(TreeBuilder<?> treeBuilder, XmlViolationPolicy namePolicy) throws SAXException {
-        for (int i = 0; i < length; i++) {
-            AttributeName attName = names[i];
-            if (!attName.isNcName(mode)) {
-                String name = attName.getLocal(mode);
-                switch (namePolicy) {
-                    case ALTER_INFOSET:
-                        names[i] = AttributeName.create(NCName.escapeName(name));
-                        // fall through
-                    case ALLOW:
-                        if (attName != AttributeName.XML_LANG) {
-                            treeBuilder.warn("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0.");
-                        }
-                        break;
-                    case FATAL:
-                        treeBuilder.fatal("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0.");
-                        break;
-                }
-            }
-        }
-    }
-    
-    public void merge(HtmlAttributes attributes) throws SAXException {
-        int len = attributes.getLength();
-        for (int i = 0; i < len; i++) {
-            AttributeName name = attributes.getAttributeNameNoBoundsCheck(i);
-            if (!contains(name)) {
-                addAttribute(name, attributes.getValueNoBoundsCheck(i), XmlViolationPolicy.ALLOW);
-            }
-        }
-    }
-
-
-    // ]NOCPP]
-    
-}
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -433,16 +433,18 @@ public class Tokenizer implements Locato
      */
     private ElementName nonInternedTagName = null;
 
     /**
      * The current attribute name.
      */
     protected AttributeName attributeName = null;
 
+    // CPPONLY: private AttributeName nonInternedAttributeName = null;
+
     // [NOCPP[
 
     /**
      * Whether comment tokens are emitted.
      */
     private boolean wantsComments = false;
 
     /**
@@ -530,16 +532,17 @@ public class Tokenizer implements Locato
         // the semicolon never gets appended to the buffer.
         this.charRefBuf = new char[32];
         this.bmpChar = new char[1];
         this.astralChar = new char[2];
         this.containsHyphen = false;
         this.tagName = null;
         this.nonInternedTagName = new ElementName();
         this.attributeName = null;
+        // CPPONLY: this.nonInternedAttributeName = new AttributeName();
         this.doctypeName = null;
         this.publicIdentifier = null;
         this.systemIdentifier = null;
         this.attributes = null;
     }
 
     // ]NOCPP]
 
@@ -561,16 +564,17 @@ public class Tokenizer implements Locato
         // the semicolon never gets appended to the buffer.
         this.charRefBuf = new char[32];
         this.bmpChar = new char[1];
         this.astralChar = new char[2];
         this.containsHyphen = false;
         this.tagName = null;
         this.nonInternedTagName = new ElementName();
         this.attributeName = null;
+        // CPPONLY: this.nonInternedAttributeName = new AttributeName();
         this.doctypeName = null;
         this.publicIdentifier = null;
         this.systemIdentifier = null;
         // [NOCPP[
         this.attributes = null;
         // ]NOCPP]
         // CPPONLY: this.attributes = tokenHandler.HasBuilder() ? new HtmlAttributes(mappingLangToXmlLang) : null;
         // CPPONLY: this.newAttributesEachTime = !tokenHandler.HasBuilder();
@@ -1170,38 +1174,43 @@ public class Tokenizer implements Locato
         /*
          * The token handler may have called setStateAndEndTagExpectation
          * and changed stateSave since the start of this method.
          */
         return stateSave;
     }
 
     private void attributeNameComplete() throws SAXException {
-        attributeName = AttributeName.nameByBuffer(strBuf, 0, strBufLen
-        // [NOCPP[
-                , namePolicy != XmlViolationPolicy.ALLOW
-                // ]NOCPP]
-                , interner);
+        attributeName = AttributeName.nameByBuffer(strBuf, 0, strBufLen, interner);
+        if (attributeName == null) {
+            // [NOCPP[
+            attributeName = AttributeName.createAttributeName(
+                    Portability.newLocalNameFromBuffer(strBuf, 0, strBufLen,
+                            interner),
+                    namePolicy != XmlViolationPolicy.ALLOW);
+            // ]NOCPP]
+            // CPPONLY:     nonInternedAttributeName.setNameForNonInterned(Portability.newLocalNameFromBuffer(strBuf, 0, strBufLen, interner));
+            // CPPONLY:     attributeName = nonInternedAttributeName;
+        }
         clearStrBufAfterUse();
 
         if (attributes == null) {
             attributes = new HtmlAttributes(mappingLangToXmlLang);
         }
 
         /*
          * When the user agent leaves the attribute name state (and before
          * emitting the tag token, if appropriate), the complete attribute's
          * name must be compared to the other attributes on the same token; if
          * there is already an attribute on the token with the exact same name,
          * then this is a parse error and the new attribute must be dropped,
          * along with the value that gets associated with it (if any).
          */
         if (attributes.contains(attributeName)) {
             errDuplicateAttribute();
-            attributeName.release();
             attributeName = null;
         }
     }
 
     private void addAttributeWithoutValue() throws SAXException {
         noteAttributeWithoutValue();
 
         // [NOCPP[
@@ -1240,18 +1249,17 @@ public class Tokenizer implements Locato
                         // [NOCPP[
                         , xmlnsPolicy
                 // ]NOCPP]
                 // CPPONLY: , attributeLine
                 );
                 // [NOCPP[
             }
             // ]NOCPP]
-            attributeName = null; // attributeName has been adopted by the
-            // |attributes| object
+            attributeName = null;
         } else {
             clearStrBufAfterUse();
         }
     }
 
     private void addAttributeWithValue() throws SAXException {
         // [NOCPP[
         if (metaBoundaryPassed && ElementName.META == tagName
@@ -1272,18 +1280,17 @@ public class Tokenizer implements Locato
             }
             // ]NOCPP]
             attributes.addAttribute(attributeName, val
             // [NOCPP[
                     , xmlnsPolicy
             // ]NOCPP]
             // CPPONLY: , attributeLine
             );
-            attributeName = null; // attributeName has been adopted by the
-            // |attributes| object
+            attributeName = null;
         } else {
             // We have a duplicate attribute. Explicitly discard its value.
             clearStrBufAfterUse();
         }
     }
 
     // [NOCPP[
 
@@ -6675,20 +6682,18 @@ public class Tokenizer implements Locato
             systemIdentifier = null;
         }
         if (publicIdentifier != null) {
             Portability.releaseString(publicIdentifier);
             publicIdentifier = null;
         }
         tagName = null;
         nonInternedTagName.setNameForNonInterned(null);
-        if (attributeName != null) {
-            attributeName.release();
-            attributeName = null;
-        }
+        attributeName = null;
+        // CPPONLY: nonInternedAttributeName.setNameForNonInterned(null);
         tokenHandler.endTokenization();
         if (attributes != null) {
             // [NOCPP[
             attributes = null;
             // ]NOCPP]
             // CPPONLY: attributes.clear(mappingLangToXmlLang);
         }
     }
@@ -6756,23 +6761,18 @@ public class Tokenizer implements Locato
         candidate = -1;
         charRefBufMark = 0;
         value = 0;
         seenDigits = false;
         endTag = false;
         shouldSuspend = false;
         initDoctypeFields();
         containsHyphen = false;
-        if (tagName != null) {
-            tagName = null;
-        }
-        if (attributeName != null) {
-            attributeName.release();
-            attributeName = null;
-        }
+        tagName = null;
+        attributeName = null;
         if (newAttributesEachTime) {
             if (attributes != null) {
                 Portability.delete(attributes);
                 attributes = null;
             }
         }
     }
 
@@ -6828,32 +6828,37 @@ public class Tokenizer implements Locato
         }
 
         containsHyphen = other.containsHyphen;
         if (other.tagName == null) {
             tagName = null;
         } else if (other.tagName.isInterned()) {
             tagName = other.tagName;
         } else {
-            // In the C++ case, We might be loading state from another
-            // tokenizer that has atoms from a different tokenizer-scoped
-            // atom table. Therefore, we have to obtain the correspoding
-            // atom from our own atom table.
+            // In the C++ case, the atoms in the other tokenizer are from a
+            // different tokenizer-scoped atom table. Therefore, we have to
+            // obtain the correspoding atom from our own atom table.
             nonInternedTagName.setNameForNonInterned(Portability.newLocalFromLocal(other.tagName.getName(), interner));
             tagName = nonInternedTagName;
         }
 
-        if (attributeName != null) {
-            attributeName.release();
-        }
-        if (other.attributeName == null) {
-            attributeName = null;
-        } else {
-            attributeName = other.attributeName.cloneAttributeName(interner);
-        }
+        // [NOCPP[
+        attributeName = other.attributeName;
+        // ]NOCPP]
+        // CPPONLY: if (other.attributeName == null) {
+        // CPPONLY:     attributeName = null;
+        // CPPONLY: } else if (other.attributeName.isInterned()) {
+        // CPPONLY:     attributeName = other.attributeName;
+        // CPPONLY: } else {
+        // CPPONLY:     // In the C++ case, the atoms in the other tokenizer are from a
+        // CPPONLY:     // different tokenizer-scoped atom table. Therefore, we have to
+        // CPPONLY:     // obtain the correspoding atom from our own atom table.
+        // CPPONLY:     nonInternedAttributeName.setNameForNonInterned(Portability.newLocalFromLocal(other.attributeName.getLocal(AttributeName.HTML), interner));
+        // CPPONLY:     attributeName = nonInternedAttributeName;
+        // CPPONLY: }
 
         Portability.delete(attributes);
         if (other.attributes == null) {
             attributes = null;
         } else {
             attributes = other.attributes.cloneAttributes(interner);
         }
     }
@@ -7085,16 +7090,17 @@ public class Tokenizer implements Locato
      */
     public void setEncodingDeclarationHandler(
             EncodingDeclarationHandler encodingDeclarationHandler) {
         this.encodingDeclarationHandler = encodingDeclarationHandler;
     }
 
     void destructor() {
         Portability.delete(nonInternedTagName);
+        // CPPONLY: Portability.delete(nonInternedAttributeName);
         nonInternedTagName = null;
         // The translator will write refcount tracing stuff here
         Portability.delete(attributes);
         attributes = null;
     }
 
     // [NOCPP[
 
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -2326,95 +2326,84 @@ public abstract class TreeBuilder<T> imp
                             case INPUT:
                                 reconstructTheActiveFormattingElements();
                                 appendVoidElementToCurrentMayFoster(
                                         name, attributes,
                                         formPointer);
                                 selfClosing = false;
                                 attributes = null; // CPP
                                 break starttagloop;
-                            case ISINDEX:
-                                errIsindex();
-                                if (formPointer != null && !isTemplateContents()) {
-                                    break starttagloop;
-                                }
-                                implicitlyCloseP();
-                                HtmlAttributes formAttrs = new HtmlAttributes(0);
-                                int actionIndex = attributes.getIndex(AttributeName.ACTION);
-                                if (actionIndex > -1) {
-                                    formAttrs.addAttribute(
-                                            AttributeName.ACTION,
-                                            attributes.getValueNoBoundsCheck(actionIndex)
-                                            // [NOCPP[
-                                            , XmlViolationPolicy.ALLOW
-                                    // ]NOCPP]
-                                            // CPPONLY: , attributes.getLineNoBoundsCheck(actionIndex)
-                                    );
-                                }
-                                appendToCurrentNodeAndPushFormElementMayFoster(formAttrs);
-                                appendVoidElementToCurrentMayFoster(
-                                        ElementName.HR,
-                                        HtmlAttributes.EMPTY_ATTRIBUTES);
-                                appendToCurrentNodeAndPushElementMayFoster(
-                                        ElementName.LABEL,
-                                        HtmlAttributes.EMPTY_ATTRIBUTES);
-                                int promptIndex = attributes.getIndex(AttributeName.PROMPT);
-                                if (promptIndex > -1) {
-                                    @Auto char[] prompt = Portability.newCharArrayFromString(attributes.getValueNoBoundsCheck(promptIndex));
-                                    appendCharacters(stack[currentPtr].node,
-                                            prompt, 0, prompt.length);
-                                } else {
-                                    appendIsindexPrompt(stack[currentPtr].node);
-                                }
-                                HtmlAttributes inputAttributes = new HtmlAttributes(
-                                        0);
-                                inputAttributes.addAttribute(
-                                        AttributeName.NAME,
-                                        Portability.newStringFromLiteral("isindex")
-                                        // [NOCPP[
-                                        , XmlViolationPolicy.ALLOW
-                                // ]NOCPP]
-                                // CPPONLY: , tokenizer.getLineNumber()
-                                );
-                                for (int i = 0; i < attributes.getLength(); i++) {
-                                    AttributeName attributeQName = attributes.getAttributeNameNoBoundsCheck(i);
-                                    if (AttributeName.NAME == attributeQName
-                                            || AttributeName.PROMPT == attributeQName) {
-                                        attributes.releaseValue(i);
-                                    } else if (AttributeName.ACTION != attributeQName) {
-                                        inputAttributes.addAttribute(
-                                                attributeQName,
-                                                attributes.getValueNoBoundsCheck(i)
-                                                // [NOCPP[
-                                                , XmlViolationPolicy.ALLOW
-                                        // ]NOCPP]
-                                        // CPPONLY: , attributes.getLineNoBoundsCheck(i)
-                                        );
-                                    }
-                                }
-                                attributes.clearWithoutReleasingContents();
-                                appendVoidElementToCurrentMayFoster(
-                                        "input",
-                                        inputAttributes, formPointer);
-                                pop(); // label
-                                appendVoidElementToCurrentMayFoster(
-                                        ElementName.HR,
-                                        HtmlAttributes.EMPTY_ATTRIBUTES);
-                                pop(); // form
-
-                                if (!isTemplateContents()) {
-                                    formPointer = null;
-                                }
-
-                                selfClosing = false;
-                                // Portability.delete(formAttrs);
-                                // Portability.delete(inputAttributes);
-                                // Don't delete attributes, they are deleted
-                                // later
-                                break starttagloop;
+                            // CPPONLY:case ISINDEX:
+                            // CPPONLY:    errIsindex();
+                            // CPPONLY:    if (formPointer != null && !isTemplateContents()) {
+                            // CPPONLY:        break starttagloop;
+                            // CPPONLY:    }
+                            // CPPONLY:    implicitlyCloseP();
+                            // CPPONLY:    HtmlAttributes formAttrs = new HtmlAttributes(0);
+                            // CPPONLY:    int actionIndex = attributes.getIndex(AttributeName.ACTION);
+                            // CPPONLY:    if (actionIndex > -1) {
+                            // CPPONLY:        formAttrs.addAttribute(
+                            // CPPONLY:                AttributeName.ACTION,
+                            // CPPONLY:                attributes.getValueNoBoundsCheck(actionIndex),
+                            // CPPONLY:                attributes.getLineNoBoundsCheck(actionIndex)
+                            // CPPONLY:        );
+                            // CPPONLY:    }
+                            // CPPONLY:    appendToCurrentNodeAndPushFormElementMayFoster(formAttrs);
+                            // CPPONLY:    appendVoidElementToCurrentMayFoster(
+                            // CPPONLY:            ElementName.HR,
+                            // CPPONLY:            HtmlAttributes.EMPTY_ATTRIBUTES);
+                            // CPPONLY:    appendToCurrentNodeAndPushElementMayFoster(
+                            // CPPONLY:            ElementName.LABEL,
+                            // CPPONLY:            HtmlAttributes.EMPTY_ATTRIBUTES);
+                            // CPPONLY:    int promptIndex = attributes.getIndex(AttributeName.PROMPT);
+                            // CPPONLY:    if (promptIndex > -1) {
+                            // CPPONLY:        @Auto char[] prompt = Portability.newCharArrayFromString(attributes.getValueNoBoundsCheck(promptIndex));
+                            // CPPONLY:        appendCharacters(stack[currentPtr].node,
+                            // CPPONLY:                prompt, 0, prompt.length);
+                            // CPPONLY:    } else {
+                            // CPPONLY:        appendIsindexPrompt(stack[currentPtr].node);
+                            // CPPONLY:    }
+                            // CPPONLY:    HtmlAttributes inputAttributes = new HtmlAttributes(
+                            // CPPONLY:            0);
+                            // CPPONLY:    inputAttributes.addAttribute(
+                            // CPPONLY:            AttributeName.NAME,
+                            // CPPONLY:            Portability.newStringFromLiteral("isindex"),
+                            // CPPONLY:            tokenizer.getLineNumber()
+                            // CPPONLY:    );
+                            // CPPONLY:    for (int i = 0; i < attributes.getLength(); i++) {
+                            // CPPONLY:        @Local String attributeQName = attributes.getLocalNameNoBoundsCheck(i);
+                            // CPPONLY:        if ("name" == attributeQName
+                            // CPPONLY:                || "prompt" == attributeQName) {
+                            // CPPONLY:            attributes.releaseValue(i);
+                            // CPPONLY:        } else if ("action" != attributeQName) {
+                            // CPPONLY:            inputAttributes.AddAttributeWithLocal(
+                            // CPPONLY:                    attributeQName,
+                            // CPPONLY:                    attributes.getValueNoBoundsCheck(i),
+                            // CPPONLY:                    attributes.getLineNoBoundsCheck(i)
+                            // CPPONLY:            );
+                            // CPPONLY:        }
+                            // CPPONLY:    }
+                            // CPPONLY:    attributes.clearWithoutReleasingContents();
+                            // CPPONLY:    appendVoidElementToCurrentMayFoster(
+                            // CPPONLY:            "input",
+                            // CPPONLY:            inputAttributes, formPointer);
+                            // CPPONLY:    pop(); // label
+                            // CPPONLY:    appendVoidElementToCurrentMayFoster(
+                            // CPPONLY:            ElementName.HR,
+                            // CPPONLY:            HtmlAttributes.EMPTY_ATTRIBUTES);
+                            // CPPONLY:    pop(); // form
+                            // CPPONLY:
+                            // CPPONLY:    if (!isTemplateContents()) {
+                            // CPPONLY:        formPointer = null;
+                            // CPPONLY:    }
+                            // CPPONLY:
+                            // CPPONLY:    selfClosing = false;
+                            // CPPONLY:    // Don't delete attributes, they are deleted
+                            // CPPONLY:    // later
+                            // CPPONLY:    break starttagloop;
                             case TEXTAREA:
                                 appendToCurrentNodeAndPushElementMayFoster(
                                         elementName,
                                         attributes, formPointer);
                                 tokenizer.setStateAndEndTagExpectation(
                                         Tokenizer.RCDATA, elementName);
                                 originalMode = mode;
                                 mode = TEXT;
@@ -3837,17 +3826,17 @@ public abstract class TreeBuilder<T> imp
                         // CPPONLY: case MENUITEM:
                         case PARAM_OR_SOURCE_OR_TRACK:
                         case EMBED:
                         case IMG:
                         case IMAGE:
                         case INPUT:
                         case KEYGEN: // XXX??
                         case HR:
-                        case ISINDEX:
+                        // CPPONLY: case ISINDEX:
                         case IFRAME:
                         case NOEMBED: // XXX???
                         case NOFRAMES: // XXX??
                         case SELECT:
                         case TABLE:
                         case TEXTAREA: // XXX??
                             errStrayEndTag(name);
                             break endtagloop;
@@ -5681,17 +5670,17 @@ public abstract class TreeBuilder<T> imp
 
     protected abstract void insertFosterParentedCharacters(
             @NoLength char[] buf, int start, int length, T table, T stackParent)
             throws SAXException;
 
     protected abstract void appendCharacters(T parent, @NoLength char[] buf,
             int start, int length) throws SAXException;
 
-    protected abstract void appendIsindexPrompt(T parent) throws SAXException;
+    // CPPONLY: protected abstract void appendIsindexPrompt(T parent) throws SAXException;
 
     protected abstract void appendComment(T parent, @NoLength char[] buf,
             int start, int length) throws SAXException;
 
     protected abstract void appendCommentToDocument(@NoLength char[] buf,
             int start, int length) throws SAXException;
 
     protected abstract void addAttributesToElement(T element,
@@ -6377,19 +6366,19 @@ public abstract class TreeBuilder<T> imp
         errNoCheck("Bad start tag in \u201C" + name
                 + "\u201D in \u201Chead\u201D.");
     }
 
     private void errImage() throws SAXException {
         err("Saw a start tag \u201Cimage\u201D.");
     }
 
-    private void errIsindex() throws SAXException {
-        err("\u201Cisindex\u201D seen.");
-    }
+    // CPPONLY: private void errIsindex() throws SAXException {
+    // CPPONLY:     err("\u201Cisindex\u201D seen.");
+    // CPPONLY: }
 
     private void errFooSeenWhenFooOpen(@Local String name) throws SAXException {
         if (errorHandler == null) {
             return;
         }
         errNoCheck("An \u201C" + name + "\u201D start tag seen but an element of the same type was already open.");
     }
 
--- a/parser/html/moz.build
+++ b/parser/html/moz.build
@@ -14,16 +14,18 @@ XPIDL_MODULE = 'html5'
 EXPORTS += [
     'jArray.h',
     'nsAHtml5TreeBuilderState.h',
     'nsAHtml5TreeOpSink.h',
     'nsHtml5ArrayCopy.h',
     'nsHtml5AtomList.h',
     'nsHtml5Atoms.h',
     'nsHtml5AtomTable.h',
+    'nsHtml5AttributeEntry.h',
+    'nsHtml5AttributeName.h',
     'nsHtml5ByteReadable.h',
     'nsHtml5DependentUTF16Buffer.h',
     'nsHtml5DocumentBuilder.h',
     'nsHtml5DocumentMode.h',
     'nsHtml5HtmlAttributes.h',
     'nsHtml5Macros.h',
     'nsHtml5MetaScanner.h',
     'nsHtml5MetaScannerHSupplement.h',
@@ -66,17 +68,16 @@ UNIFIED_SOURCES += [
     'nsHtml5Module.cpp',
     'nsHtml5NamedCharacters.cpp',
     'nsHtml5NamedCharactersAccel.cpp',
     'nsHtml5OplessBuilder.cpp',
     'nsHtml5OwningUTF16Buffer.cpp',
     'nsHtml5Parser.cpp',
     'nsHtml5PlainTextUtils.cpp',
     'nsHtml5Portability.cpp',
-    'nsHtml5ReleasableAttributeName.cpp',
     'nsHtml5Speculation.cpp',
     'nsHtml5SpeculativeLoad.cpp',
     'nsHtml5StackNode.cpp',
     'nsHtml5StateSnapshot.cpp',
     'nsHtml5StreamListener.cpp',
     'nsHtml5StreamParser.cpp',
     'nsHtml5String.cpp',
     'nsHtml5StringParser.cpp',
--- a/parser/html/nsHtml5AtomList.h
+++ b/parser/html/nsHtml5AtomList.h
@@ -43,16 +43,19 @@ HTML5_ATOM(script, "script")
 HTML5_ATOM(svg, "svg")
 HTML5_ATOM(table, "table")
 HTML5_ATOM(caption, "caption")
 HTML5_ATOM(p, "p")
 HTML5_ATOM(address, "address")
 HTML5_ATOM(div, "div")
 HTML5_ATOM(a, "a")
 HTML5_ATOM(nobr, "nobr")
+HTML5_ATOM(name, "name")
+HTML5_ATOM(prompt, "prompt")
+HTML5_ATOM(action, "action")
 HTML5_ATOM(input, "input")
 HTML5_ATOM(option, "option")
 HTML5_ATOM(ruby, "ruby")
 HTML5_ATOM(rtc, "rtc")
 HTML5_ATOM(select, "select")
 HTML5_ATOM(optgroup, "optgroup")
 HTML5_ATOM(tbody, "tbody")
 HTML5_ATOM(tfoot, "tfoot")
@@ -244,17 +247,16 @@ HTML5_ATOM(hidefocus, "hidefocus")
 HTML5_ATOM(index, "index")
 HTML5_ATOM(irrelevant, "irrelevant")
 HTML5_ATOM(intercept, "intercept")
 HTML5_ATOM(integrity, "integrity")
 HTML5_ATOM(linebreak, "linebreak")
 HTML5_ATOM(label, "label")
 HTML5_ATOM(linethickness, "linethickness")
 HTML5_ATOM(mode, "mode")
-HTML5_ATOM(name, "name")
 HTML5_ATOM(noresize, "noresize")
 HTML5_ATOM(onbeforeunload, "onbeforeunload")
 HTML5_ATOM(onrepeat, "onrepeat")
 HTML5_ATOM(object, "object")
 HTML5_ATOM(onselect, "onselect")
 HTML5_ATOM(order, "order")
 HTML5_ATOM(other, "other")
 HTML5_ATOM(onreset, "onreset")
@@ -350,17 +352,16 @@ HTML5_ATOM(mathcolor, "mathcolor")
 HTML5_ATOM(mathsize, "mathsize")
 HTML5_ATOM(noshade, "noshade")
 HTML5_ATOM(onchange, "onchange")
 HTML5_ATOM(pathlength, "pathlength")
 HTML5_ATOM(pathLength, "pathLength")
 HTML5_ATOM(path, "path")
 HTML5_ATOM(altimg, "altimg")
 HTML5_ATOM(actiontype, "actiontype")
-HTML5_ATOM(action, "action")
 HTML5_ATOM(active, "active")
 HTML5_ATOM(additive, "additive")
 HTML5_ATOM(begin, "begin")
 HTML5_ATOM(dominant_baseline, "dominant-baseline")
 HTML5_ATOM(divisor, "divisor")
 HTML5_ATOM(definitionurl, "definitionurl")
 HTML5_ATOM(definitionURL, "definitionURL")
 HTML5_ATOM(horiz_adv_x, "horiz-adv-x")
@@ -418,17 +419,16 @@ HTML5_ATOM(tablevalues, "tablevalues")
 HTML5_ATOM(tableValues, "tableValues")
 HTML5_ATOM(v_alphabetic, "v-alphabetic")
 HTML5_ATOM(azimuth, "azimuth")
 HTML5_ATOM(format, "format")
 HTML5_ATOM(frameborder, "frameborder")
 HTML5_ATOM(frame, "frame")
 HTML5_ATOM(framespacing, "framespacing")
 HTML5_ATOM(from, "from")
-HTML5_ATOM(prompt, "prompt")
 HTML5_ATOM(primitiveunits, "primitiveunits")
 HTML5_ATOM(primitiveUnits, "primitiveUnits")
 HTML5_ATOM(symmetric, "symmetric")
 HTML5_ATOM(stemh, "stemh")
 HTML5_ATOM(stemv, "stemv")
 HTML5_ATOM(seamless, "seamless")
 HTML5_ATOM(summary, "summary")
 HTML5_ATOM(usemap, "usemap")
@@ -708,16 +708,17 @@ HTML5_ATOM(dy, "dy")
 HTML5_ATOM(fy, "fy")
 HTML5_ATOM(ry, "ry")
 HTML5_ATOM(refy, "refy")
 HTML5_ATOM(refY, "refY")
 HTML5_ATOM(verythinmathspace, "verythinmathspace")
 HTML5_ATOM(verythickmathspace, "verythickmathspace")
 HTML5_ATOM(veryverythinmathspace, "veryverythinmathspace")
 HTML5_ATOM(veryverythickmathspace, "veryverythickmathspace")
+HTML5_ATOM(isindex, "isindex")
 HTML5_ATOM(big, "big")
 HTML5_ATOM(bdo, "bdo")
 HTML5_ATOM(col, "col")
 HTML5_ATOM(del, "del")
 HTML5_ATOM(dfn, "dfn")
 HTML5_ATOM(img, "img")
 HTML5_ATOM(ins, "ins")
 HTML5_ATOM(kbd, "kbd")
@@ -880,11 +881,10 @@ HTML5_ATOM(radialGradient, "radialGradie
 HTML5_ATOM(menu, "menu")
 HTML5_ATOM(fedropshadow, "fedropshadow")
 HTML5_ATOM(feDropShadow, "feDropShadow")
 HTML5_ATOM(view, "view")
 HTML5_ATOM(fecolormatrix, "fecolormatrix")
 HTML5_ATOM(feColorMatrix, "feColorMatrix")
 HTML5_ATOM(feconvolvematrix, "feconvolvematrix")
 HTML5_ATOM(feConvolveMatrix, "feConvolveMatrix")
-HTML5_ATOM(isindex, "isindex")
 HTML5_ATOM(femorphology, "femorphology")
 HTML5_ATOM(feMorphology, "feMorphology")
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5AttributeEntry.h
@@ -0,0 +1,91 @@
+/* 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/. */
+
+#ifndef nsHtml5AttributeEntry_h
+#define nsHtml5AttributeEntry_h
+
+#include "nsHtml5AttributeName.h"
+
+class nsHtml5AttributeEntry final
+{
+public:
+  nsHtml5AttributeEntry(nsHtml5AttributeName* aName,
+                        nsHtml5String aValue,
+                        int32_t aLine)
+    : mLine(aLine)
+    , mValue(aValue)
+  {
+    // Let's hope the compiler coalesces the following as appropriate.
+    mLocals[0] = aName->getLocal(0);
+    mLocals[1] = aName->getLocal(1);
+    mLocals[2] = aName->getLocal(2);
+    mPrefixes[0] = aName->getPrefix(0);
+    mPrefixes[1] = aName->getPrefix(1);
+    mPrefixes[2] = aName->getPrefix(2);
+    mUris[0] = aName->getUri(0);
+    mUris[1] = aName->getUri(1);
+    mUris[2] = aName->getUri(2);
+  }
+
+  // Isindex-only so doesn't need to deal with SVG and MathML
+  nsHtml5AttributeEntry(nsIAtom* aName, nsHtml5String aValue, int32_t aLine)
+    : mLine(aLine)
+    , mValue(aValue)
+  {
+    // Let's hope the compiler coalesces the following as appropriate.
+    mLocals[0] = aName;
+    mLocals[1] = aName;
+    mLocals[2] = aName;
+    mPrefixes[0] = nullptr;
+    mPrefixes[1] = nullptr;
+    mPrefixes[2] = nullptr;
+    mUris[0] = kNameSpaceID_None;
+    mUris[1] = kNameSpaceID_None;
+    mUris[2] = kNameSpaceID_None;
+  }
+
+  inline nsIAtom* GetLocal(int32_t aMode) { return mLocals[aMode]; }
+
+  inline nsIAtom* GetPrefix(int32_t aMode) { return mPrefixes[aMode]; }
+
+  inline int32_t GetUri(int32_t aMode) { return mUris[aMode]; }
+
+  inline nsHtml5String GetValue() { return mValue; }
+
+  inline int32_t GetLine() { return mLine; }
+
+  inline void ReleaseValue() { mValue.Release(); }
+
+  inline nsHtml5AttributeEntry Clone(nsHtml5AtomTable* aInterner)
+  {
+    // Copy the memory
+    nsHtml5AttributeEntry clone(*this);
+    // Increment refcount for value
+    clone.mValue = this->mValue.Clone();
+    if (aInterner) {
+      // Now if we have an interner, we'll need to rewrite non-static atoms.
+      // Only the local names may be non-static, in which case all three
+      // are the same.
+      nsIAtom* local = GetLocal(0);
+      if (!local->IsStaticAtom()) {
+        nsAutoString str;
+        local->ToString(str);
+        local = aInterner->GetAtom(str);
+        clone.mLocals[0] = local;
+        clone.mLocals[1] = local;
+        clone.mLocals[2] = local;
+      }
+    }
+    return clone;
+  }
+
+private:
+  nsIAtom* mLocals[3];
+  nsIAtom* mPrefixes[3];
+  int32_t mUris[3];
+  int32_t mLine;
+  nsHtml5String mValue;
+};
+
+#endif // nsHtml5AttributeEntry_h
--- a/parser/html/nsHtml5AttributeName.cpp
+++ b/parser/html/nsHtml5AttributeName.cpp
@@ -41,24 +41,22 @@
 #include "nsIUnicodeDecoder.h"
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5AttributeName.h"
-#include "nsHtml5ReleasableAttributeName.h"
 
 int32_t* nsHtml5AttributeName::ALL_NO_NS = 0;
 int32_t* nsHtml5AttributeName::XMLNS_NS = 0;
 int32_t* nsHtml5AttributeName::XML_NS = 0;
 int32_t* nsHtml5AttributeName::XLINK_NS = 0;
 nsIAtom** nsHtml5AttributeName::ALL_NO_PREFIX = 0;
 nsIAtom** nsHtml5AttributeName::XMLNS_PREFIX = 0;
 nsIAtom** nsHtml5AttributeName::XLINK_PREFIX = 0;
@@ -104,60 +102,77 @@ nsHtml5AttributeName::SAME_LOCAL(nsIAtom
 }
 
 nsHtml5AttributeName* 
 nsHtml5AttributeName::nameByBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner)
 {
   uint32_t hash = nsHtml5AttributeName::bufToHash(buf, length);
   int32_t index = nsHtml5AttributeName::ATTRIBUTE_HASHES.binarySearch(hash);
   if (index < 0) {
-    return nsHtml5AttributeName::createAttributeName(nsHtml5Portability::newLocalNameFromBuffer(buf, offset, length, interner));
-  } else {
-    nsHtml5AttributeName* attributeName = nsHtml5AttributeName::ATTRIBUTE_NAMES[index];
-    nsIAtom* name = attributeName->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML);
-    if (!nsHtml5Portability::localEqualsBuffer(name, buf, offset, length)) {
-      return nsHtml5AttributeName::createAttributeName(nsHtml5Portability::newLocalNameFromBuffer(buf, offset, length, interner));
-    }
-    return attributeName;
+    return nullptr;
   }
+  nsHtml5AttributeName* attributeName =
+    nsHtml5AttributeName::ATTRIBUTE_NAMES[index];
+  nsIAtom* name = attributeName->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML);
+  if (!nsHtml5Portability::localEqualsBuffer(name, buf, offset, length)) {
+    return nullptr;
+  }
+  return attributeName;
 }
 
-
-nsHtml5AttributeName::nsHtml5AttributeName(int32_t* uri, nsIAtom** local, nsIAtom** prefix)
-  : uri(uri),
-    local(local),
-    prefix(prefix)
+nsHtml5AttributeName::nsHtml5AttributeName(int32_t* uri,
+                                           nsIAtom** local,
+                                           nsIAtom** prefix)
+  : uri(uri)
+  , local(local)
+  , prefix(prefix)
+  , custom(false)
 {
   MOZ_COUNT_CTOR(nsHtml5AttributeName);
 }
 
-nsHtml5AttributeName* 
+nsHtml5AttributeName::nsHtml5AttributeName()
+  : uri(nsHtml5AttributeName::ALL_NO_NS)
+  , local(nsHtml5AttributeName::SAME_LOCAL(nullptr))
+  , prefix(ALL_NO_PREFIX)
+  , custom(true)
+{
+  MOZ_COUNT_CTOR(nsHtml5AttributeName);
+}
+
+bool
+nsHtml5AttributeName::isInterned()
+{
+  return !custom;
+}
+
+void
+nsHtml5AttributeName::setNameForNonInterned(nsIAtom* name)
+{
+  MOZ_ASSERT(custom);
+  local[0] = name;
+  local[1] = name;
+  local[2] = name;
+}
+
+nsHtml5AttributeName*
 nsHtml5AttributeName::createAttributeName(nsIAtom* name)
 {
-  return new nsHtml5ReleasableAttributeName(nsHtml5AttributeName::ALL_NO_NS, nsHtml5AttributeName::SAME_LOCAL(name), ALL_NO_PREFIX);
-}
-
-void 
-nsHtml5AttributeName::release()
-{
+  return new nsHtml5AttributeName(nsHtml5AttributeName::ALL_NO_NS,
+                                  nsHtml5AttributeName::SAME_LOCAL(name),
+                                  ALL_NO_PREFIX);
 }
 
 
 nsHtml5AttributeName::~nsHtml5AttributeName()
 {
   MOZ_COUNT_DTOR(nsHtml5AttributeName);
   delete[] local;
 }
 
-nsHtml5AttributeName* 
-nsHtml5AttributeName::cloneAttributeName(nsHtml5AtomTable* interner)
-{
-  return this;
-}
-
 int32_t 
 nsHtml5AttributeName::getUri(int32_t mode)
 {
   return uri[mode];
 }
 
 nsIAtom* 
 nsHtml5AttributeName::getLocal(int32_t mode)
--- a/parser/html/nsHtml5AttributeName.h
+++ b/parser/html/nsHtml5AttributeName.h
@@ -44,17 +44,16 @@
 #include "nsIContentHandle.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5AttributeName
 {
   public:
@@ -107,24 +106,24 @@ class nsHtml5AttributeName
         second <<= 24;
       }
       return len + first + second + third + fourth + fifth + sixth;
     }
 
     int32_t* uri;
     nsIAtom** local;
     nsIAtom** prefix;
-  protected:
+    bool custom;
     nsHtml5AttributeName(int32_t* uri, nsIAtom** local, nsIAtom** prefix);
-  private:
+  public:
+    nsHtml5AttributeName();
+    bool isInterned();
+    void setNameForNonInterned(nsIAtom* name);
     static nsHtml5AttributeName* createAttributeName(nsIAtom* name);
-  public:
-    virtual void release();
-    virtual ~nsHtml5AttributeName();
-    virtual nsHtml5AttributeName* cloneAttributeName(nsHtml5AtomTable* interner);
+    ~nsHtml5AttributeName();
     int32_t getUri(int32_t mode);
     nsIAtom* getLocal(int32_t mode);
     nsIAtom* getPrefix(int32_t mode);
     bool equalsAnother(nsHtml5AttributeName* another);
     static nsHtml5AttributeName* ATTR_ALT;
     static nsHtml5AttributeName* ATTR_DIR;
     static nsHtml5AttributeName* ATTR_DUR;
     static nsHtml5AttributeName* ATTR_END;
--- a/parser/html/nsHtml5ElementName.cpp
+++ b/parser/html/nsHtml5ElementName.cpp
@@ -41,17 +41,16 @@
 #include "nsIUnicodeDecoder.h"
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5ElementName.h"
 
 int32_t 
@@ -111,16 +110,17 @@ nsHtml5ElementName::setNameForNonInterne
 {
   this->name = name;
   this->camelCaseName = name;
   MOZ_ASSERT(this->flags ==
              (NS_HTML5TREE_BUILDER_OTHER | NS_HTML5ELEMENT_NAME_NOT_INTERNED));
 }
 
 nsHtml5ElementName* nsHtml5ElementName::ELT_ANNOTATION_XML = nullptr;
+nsHtml5ElementName* nsHtml5ElementName::ELT_ISINDEX = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_BIG = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_BDO = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_COL = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_DEL = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_DFN = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_DIR = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_DIV = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_IMG = nullptr;
@@ -309,17 +309,16 @@ nsHtml5ElementName* nsHtml5ElementName::
 nsHtml5ElementName* nsHtml5ElementName::ELT_SCRIPT = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_TFOOT = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_TEXT = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_MENU = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_FEDROPSHADOW = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_VIEW = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_FECOLORMATRIX = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_FECONVOLVEMATRIX = nullptr;
-nsHtml5ElementName* nsHtml5ElementName::ELT_ISINDEX = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_BODY = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_FEMORPHOLOGY = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_RUBY = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_SUMMARY = nullptr;
 nsHtml5ElementName* nsHtml5ElementName::ELT_TBODY = nullptr;
 nsHtml5ElementName** nsHtml5ElementName::ELEMENT_NAMES = 0;
 static int32_t const ELEMENT_HASHES_DATA[] = {
   51434643,   51438659,   51961587,   52485715,   52486755,   52488851,
@@ -361,16 +360,20 @@ staticJArray<int32_t,int32_t> nsHtml5Ele
 void
 nsHtml5ElementName::initializeStatics()
 {
   ELT_ANNOTATION_XML =
     new nsHtml5ElementName(nsHtml5Atoms::annotation_xml,
                            nsHtml5Atoms::annotation_xml,
                            NS_HTML5TREE_BUILDER_ANNOTATION_XML |
                              NS_HTML5ELEMENT_NAME_SCOPING_AS_MATHML);
+  ELT_ISINDEX = new nsHtml5ElementName(nsHtml5Atoms::isindex,
+                                       nsHtml5Atoms::isindex,
+                                       NS_HTML5TREE_BUILDER_ISINDEX |
+                                         NS_HTML5ELEMENT_NAME_SPECIAL);
   ELT_BIG = new nsHtml5ElementName(nsHtml5Atoms::big, nsHtml5Atoms::big, NS_HTML5TREE_BUILDER_B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U);
   ELT_BDO = new nsHtml5ElementName(nsHtml5Atoms::bdo, nsHtml5Atoms::bdo, NS_HTML5TREE_BUILDER_OTHER);
   ELT_COL = new nsHtml5ElementName(nsHtml5Atoms::col, nsHtml5Atoms::col, NS_HTML5TREE_BUILDER_COL | NS_HTML5ELEMENT_NAME_SPECIAL);
   ELT_DEL = new nsHtml5ElementName(nsHtml5Atoms::del, nsHtml5Atoms::del, NS_HTML5TREE_BUILDER_OTHER);
   ELT_DFN = new nsHtml5ElementName(nsHtml5Atoms::dfn, nsHtml5Atoms::dfn, NS_HTML5TREE_BUILDER_OTHER);
   ELT_DIR = new nsHtml5ElementName(nsHtml5Atoms::dir, nsHtml5Atoms::dir, NS_HTML5TREE_BUILDER_ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY | NS_HTML5ELEMENT_NAME_SPECIAL);
   ELT_DIV = new nsHtml5ElementName(nsHtml5Atoms::div, nsHtml5Atoms::div, NS_HTML5TREE_BUILDER_DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU | NS_HTML5ELEMENT_NAME_SPECIAL);
   ELT_IMG = new nsHtml5ElementName(nsHtml5Atoms::img, nsHtml5Atoms::img, NS_HTML5TREE_BUILDER_IMG | NS_HTML5ELEMENT_NAME_SPECIAL);
@@ -934,20 +937,16 @@ nsHtml5ElementName::initializeStatics()
                                             nsHtml5Atoms::feDropShadow,
                                             NS_HTML5TREE_BUILDER_OTHER);
   ELT_VIEW = new nsHtml5ElementName(
     nsHtml5Atoms::view, nsHtml5Atoms::view, NS_HTML5TREE_BUILDER_OTHER);
   ELT_FECOLORMATRIX = new nsHtml5ElementName(nsHtml5Atoms::fecolormatrix,
                                              nsHtml5Atoms::feColorMatrix,
                                              NS_HTML5TREE_BUILDER_OTHER);
   ELT_FECONVOLVEMATRIX = new nsHtml5ElementName(nsHtml5Atoms::feconvolvematrix, nsHtml5Atoms::feConvolveMatrix, NS_HTML5TREE_BUILDER_OTHER);
-  ELT_ISINDEX = new nsHtml5ElementName(nsHtml5Atoms::isindex,
-                                       nsHtml5Atoms::isindex,
-                                       NS_HTML5TREE_BUILDER_ISINDEX |
-                                         NS_HTML5ELEMENT_NAME_SPECIAL);
   ELT_BODY = new nsHtml5ElementName(nsHtml5Atoms::body,
                                     nsHtml5Atoms::body,
                                     NS_HTML5TREE_BUILDER_BODY |
                                       NS_HTML5ELEMENT_NAME_SPECIAL |
                                       NS_HTML5ELEMENT_NAME_OPTIONAL_END_TAG);
   ELT_FEMORPHOLOGY = new nsHtml5ElementName(nsHtml5Atoms::femorphology,
                                             nsHtml5Atoms::feMorphology,
                                             NS_HTML5TREE_BUILDER_OTHER);
@@ -1172,16 +1171,17 @@ nsHtml5ElementName::initializeStatics()
   ELEMENT_NAMES[202] = ELT_SUMMARY;
   ELEMENT_NAMES[203] = ELT_TBODY;
 }
 
 void
 nsHtml5ElementName::releaseStatics()
 {
   delete ELT_ANNOTATION_XML;
+  delete ELT_ISINDEX;
   delete ELT_BIG;
   delete ELT_BDO;
   delete ELT_COL;
   delete ELT_DEL;
   delete ELT_DFN;
   delete ELT_DIR;
   delete ELT_DIV;
   delete ELT_IMG;
@@ -1370,17 +1370,16 @@ nsHtml5ElementName::releaseStatics()
   delete ELT_SCRIPT;
   delete ELT_TFOOT;
   delete ELT_TEXT;
   delete ELT_MENU;
   delete ELT_FEDROPSHADOW;
   delete ELT_VIEW;
   delete ELT_FECOLORMATRIX;
   delete ELT_FECONVOLVEMATRIX;
-  delete ELT_ISINDEX;
   delete ELT_BODY;
   delete ELT_FEMORPHOLOGY;
   delete ELT_RUBY;
   delete ELT_SUMMARY;
   delete ELT_TBODY;
   delete[] ELEMENT_NAMES;
 }
 
--- a/parser/html/nsHtml5ElementName.h
+++ b/parser/html/nsHtml5ElementName.h
@@ -44,17 +44,16 @@
 #include "nsIContentHandle.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5ElementName
 {
 private:
@@ -108,16 +107,17 @@ private:
     }
 
     nsHtml5ElementName(nsIAtom* name, nsIAtom* camelCaseName, int32_t flags);
   public:
     nsHtml5ElementName();
     ~nsHtml5ElementName();
     void setNameForNonInterned(nsIAtom* name);
     static nsHtml5ElementName* ELT_ANNOTATION_XML;
+    static nsHtml5ElementName* ELT_ISINDEX;
     static nsHtml5ElementName* ELT_BIG;
     static nsHtml5ElementName* ELT_BDO;
     static nsHtml5ElementName* ELT_COL;
     static nsHtml5ElementName* ELT_DEL;
     static nsHtml5ElementName* ELT_DFN;
     static nsHtml5ElementName* ELT_DIR;
     static nsHtml5ElementName* ELT_DIV;
     static nsHtml5ElementName* ELT_IMG;
@@ -306,17 +306,16 @@ private:
     static nsHtml5ElementName* ELT_SCRIPT;
     static nsHtml5ElementName* ELT_TFOOT;
     static nsHtml5ElementName* ELT_TEXT;
     static nsHtml5ElementName* ELT_MENU;
     static nsHtml5ElementName* ELT_FEDROPSHADOW;
     static nsHtml5ElementName* ELT_VIEW;
     static nsHtml5ElementName* ELT_FECOLORMATRIX;
     static nsHtml5ElementName* ELT_FECONVOLVEMATRIX;
-    static nsHtml5ElementName* ELT_ISINDEX;
     static nsHtml5ElementName* ELT_BODY;
     static nsHtml5ElementName* ELT_FEMORPHOLOGY;
     static nsHtml5ElementName* ELT_RUBY;
     static nsHtml5ElementName* ELT_SUMMARY;
     static nsHtml5ElementName* ELT_TBODY;
   private:
     static nsHtml5ElementName** ELEMENT_NAMES;
     static staticJArray<int32_t,int32_t> ELEMENT_HASHES;
--- a/parser/html/nsHtml5HtmlAttributes.cpp
+++ b/parser/html/nsHtml5HtmlAttributes.cpp
@@ -1,11 +1,11 @@
 /*
  * Copyright (c) 2007 Henri Sivonen
- * Copyright (c) 2008-2011 Mozilla Foundation
+ * Copyright (c) 2008-2017 Mozilla Foundation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a 
  * copy of this software and associated documentation files (the "Software"), 
  * to deal in the Software without restriction, including without limitation 
  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
  * and/or sell copies of the Software, and to permit persons to whom the 
  * Software is furnished to do so, subject to the following conditions:
  *
@@ -16,21 +16,16 @@
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
  * DEALINGS IN THE SOFTWARE.
  */
 
-/*
- * THIS IS A GENERATED FILE. PLEASE DO NOT EDIT.
- * Please edit HtmlAttributes.java instead and regenerate.
- */
-
 #define nsHtml5HtmlAttributes_cpp__
 
 #include "nsIAtom.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5String.h"
 #include "nsNameSpaceManager.h"
 #include "nsIContent.h"
 #include "nsTraceRefcnt.h"
@@ -52,217 +47,211 @@
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5HtmlAttributes.h"
 
 nsHtml5HtmlAttributes* nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES = nullptr;
 
-nsHtml5HtmlAttributes::nsHtml5HtmlAttributes(int32_t mode)
-  : mode(mode)
-  , length(0)
-  , names(jArray<nsHtml5AttributeName*, int32_t>::newJArray(8))
-  , values(jArray<nsHtml5String, int32_t>::newJArray(8))
-  , lines(jArray<int32_t, int32_t>::newJArray(8))
+nsHtml5HtmlAttributes::nsHtml5HtmlAttributes(int32_t aMode)
+  : mMode(aMode)
 {
   MOZ_COUNT_CTOR(nsHtml5HtmlAttributes);
 }
 
 
 nsHtml5HtmlAttributes::~nsHtml5HtmlAttributes()
 {
   MOZ_COUNT_DTOR(nsHtml5HtmlAttributes);
   clear(0);
 }
 
-int32_t 
-nsHtml5HtmlAttributes::getIndex(nsHtml5AttributeName* name)
+int32_t
+nsHtml5HtmlAttributes::getIndex(nsHtml5AttributeName* aName)
 {
-  for (int32_t i = 0; i < length; i++) {
-    if (names[i] == name) {
+  for (size_t i = 0; i < mStorage.Length(); i++) {
+    if (mStorage[i].GetLocal(NS_HTML5ATTRIBUTE_NAME_HTML) ==
+        aName->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML)) {
+      // It's release asserted elsewhere that i can't be too large.
       return i;
     }
   }
   return -1;
 }
 
 nsHtml5String
-nsHtml5HtmlAttributes::getValue(nsHtml5AttributeName* name)
+nsHtml5HtmlAttributes::getValue(nsHtml5AttributeName* aName)
 {
-  int32_t index = getIndex(name);
+  int32_t index = getIndex(aName);
   if (index == -1) {
     return nullptr;
   } else {
     return getValueNoBoundsCheck(index);
   }
 }
 
 int32_t 
 nsHtml5HtmlAttributes::getLength()
 {
-  return length;
+  return mStorage.Length();
 }
 
-nsIAtom* 
-nsHtml5HtmlAttributes::getLocalNameNoBoundsCheck(int32_t index)
+nsIAtom*
+nsHtml5HtmlAttributes::getLocalNameNoBoundsCheck(int32_t aIndex)
 {
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return names[index]->getLocal(mode);
+  MOZ_ASSERT(aIndex < int32_t(mStorage.Length()) && aIndex >= 0,
+             "Index out of bounds");
+  return mStorage[aIndex].GetLocal(mMode);
 }
 
-int32_t 
-nsHtml5HtmlAttributes::getURINoBoundsCheck(int32_t index)
+int32_t
+nsHtml5HtmlAttributes::getURINoBoundsCheck(int32_t aIndex)
 {
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return names[index]->getUri(mode);
+  MOZ_ASSERT(aIndex < int32_t(mStorage.Length()) && aIndex >= 0,
+             "Index out of bounds");
+  return mStorage[aIndex].GetUri(mMode);
 }
 
-nsIAtom* 
-nsHtml5HtmlAttributes::getPrefixNoBoundsCheck(int32_t index)
+nsIAtom*
+nsHtml5HtmlAttributes::getPrefixNoBoundsCheck(int32_t aIndex)
 {
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return names[index]->getPrefix(mode);
+  MOZ_ASSERT(aIndex < int32_t(mStorage.Length()) && aIndex >= 0,
+             "Index out of bounds");
+  return mStorage[aIndex].GetPrefix(mMode);
 }
 
 nsHtml5String
-nsHtml5HtmlAttributes::getValueNoBoundsCheck(int32_t index)
+nsHtml5HtmlAttributes::getValueNoBoundsCheck(int32_t aIndex)
 {
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return values[index];
+  MOZ_ASSERT(aIndex < int32_t(mStorage.Length()) && aIndex >= 0,
+             "Index out of bounds");
+  return mStorage[aIndex].GetValue();
 }
 
-nsHtml5AttributeName* 
-nsHtml5HtmlAttributes::getAttributeNameNoBoundsCheck(int32_t index)
+int32_t
+nsHtml5HtmlAttributes::getLineNoBoundsCheck(int32_t aIndex)
 {
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return names[index];
-}
-
-int32_t 
-nsHtml5HtmlAttributes::getLineNoBoundsCheck(int32_t index)
-{
-  MOZ_ASSERT(index < length && index >= 0, "Index out of bounds");
-  return lines[index];
+  MOZ_ASSERT(aIndex < int32_t(mStorage.Length()) && aIndex >= 0,
+             "Index out of bounds");
+  return mStorage[aIndex].GetLine();
 }
 
 void
-nsHtml5HtmlAttributes::addAttribute(nsHtml5AttributeName* name,
-                                    nsHtml5String value,
-                                    int32_t line)
+nsHtml5HtmlAttributes::addAttribute(nsHtml5AttributeName* aName,
+                                    nsHtml5String aValue,
+                                    int32_t aLine)
 {
-  if (names.length == length) {
-    int32_t newLen = length << 1;
-    jArray<nsHtml5AttributeName*,int32_t> newNames = jArray<nsHtml5AttributeName*,int32_t>::newJArray(newLen);
-    nsHtml5ArrayCopy::arraycopy(names, newNames, names.length);
-    names = newNames;
-    jArray<nsHtml5String, int32_t> newValues =
-      jArray<nsHtml5String, int32_t>::newJArray(newLen);
-    nsHtml5ArrayCopy::arraycopy(values, newValues, values.length);
-    values = newValues;
-    jArray<int32_t,int32_t> newLines = jArray<int32_t,int32_t>::newJArray(newLen);
-    nsHtml5ArrayCopy::arraycopy(lines, newLines, lines.length);
-    lines = newLines;
-  }
-  names[length] = name;
-  values[length] = value;
-  lines[length] = line;
-  length++;
+  mStorage.AppendElement(nsHtml5AttributeEntry(aName, aValue, aLine));
+  MOZ_RELEASE_ASSERT(mStorage.Length() <= INT32_MAX,
+                     "Can't handle this many attributes.");
 }
 
-void 
-nsHtml5HtmlAttributes::clear(int32_t m)
+// Isindex-only, so doesn't need to deal with SVG and MathML
+void
+nsHtml5HtmlAttributes::AddAttributeWithLocal(nsIAtom* aName,
+                                             nsHtml5String aValue,
+                                             int32_t aLine)
 {
-  for (int32_t i = 0; i < length; i++) {
-    names[i]->release();
-    names[i] = nullptr;
-    values[i].Release();
-    values[i] = nullptr;
-  }
-  length = 0;
-  mode = m;
+  mStorage.AppendElement(nsHtml5AttributeEntry(aName, aValue, aLine));
+  MOZ_RELEASE_ASSERT(mStorage.Length() <= INT32_MAX,
+                     "Can't handle this many attributes.");
 }
 
-void 
-nsHtml5HtmlAttributes::releaseValue(int32_t i)
+void
+nsHtml5HtmlAttributes::clear(int32_t aMode)
 {
-  values[i].Release();
+  for (nsHtml5AttributeEntry& entry : mStorage) {
+    entry.ReleaseValue();
+  }
+  mStorage.TruncateLength(0);
+  mMode = aMode;
+}
+
+void
+nsHtml5HtmlAttributes::releaseValue(int32_t aIndex)
+{
+  mStorage[aIndex].ReleaseValue();
 }
 
 void 
 nsHtml5HtmlAttributes::clearWithoutReleasingContents()
 {
-  for (int32_t i = 0; i < length; i++) {
-    names[i] = nullptr;
-    values[i] = nullptr;
-  }
-  length = 0;
+  mStorage.TruncateLength(0);
 }
 
-bool 
-nsHtml5HtmlAttributes::contains(nsHtml5AttributeName* name)
+bool
+nsHtml5HtmlAttributes::contains(nsHtml5AttributeName* aName)
 {
-  for (int32_t i = 0; i < length; i++) {
-    if (name->equalsAnother(names[i])) {
+  for (size_t i = 0; i < mStorage.Length(); i++) {
+    if (mStorage[i].GetLocal(NS_HTML5ATTRIBUTE_NAME_HTML) ==
+        aName->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML)) {
       return true;
     }
   }
   return false;
 }
 
 void 
 nsHtml5HtmlAttributes::adjustForMath()
 {
-  mode = NS_HTML5ATTRIBUTE_NAME_MATHML;
+  mMode = NS_HTML5ATTRIBUTE_NAME_MATHML;
 }
 
 void 
 nsHtml5HtmlAttributes::adjustForSvg()
 {
-  mode = NS_HTML5ATTRIBUTE_NAME_SVG;
+  mMode = NS_HTML5ATTRIBUTE_NAME_SVG;
 }
 
-nsHtml5HtmlAttributes* 
-nsHtml5HtmlAttributes::cloneAttributes(nsHtml5AtomTable* interner)
+nsHtml5HtmlAttributes*
+nsHtml5HtmlAttributes::cloneAttributes(nsHtml5AtomTable* aInterner)
 {
-  MOZ_ASSERT((!length) || !mode || mode == 3);
-  nsHtml5HtmlAttributes* clone = new nsHtml5HtmlAttributes(0);
-  for (int32_t i = 0; i < length; i++) {
-    clone->addAttribute(names[i]->cloneAttributeName(interner), nsHtml5Portability::newStringFromString(values[i]), lines[i]);
+  MOZ_ASSERT(mStorage.IsEmpty() || !mMode);
+  nsHtml5HtmlAttributes* clone =
+    new nsHtml5HtmlAttributes(NS_HTML5ATTRIBUTE_NAME_HTML);
+  for (nsHtml5AttributeEntry& entry : mStorage) {
+    clone->AddEntry(entry.Clone(aInterner));
   }
   return clone;
 }
 
-bool 
-nsHtml5HtmlAttributes::equalsAnother(nsHtml5HtmlAttributes* other)
+bool
+nsHtml5HtmlAttributes::equalsAnother(nsHtml5HtmlAttributes* aOther)
 {
-  MOZ_ASSERT(!mode || mode == 3, "Trying to compare attributes in foreign content.");
-  int32_t otherLength = other->getLength();
-  if (length != otherLength) {
+  MOZ_ASSERT(!mMode, "Trying to compare attributes in foreign content.");
+  if (mStorage.Length() != aOther->mStorage.Length()) {
     return false;
   }
-  for (int32_t i = 0; i < length; i++) {
+  for (nsHtml5AttributeEntry& entry : mStorage) {
     bool found = false;
-    nsIAtom* ownLocal = names[i]->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML);
-    for (int32_t j = 0; j < otherLength; j++) {
-      if (ownLocal == other->names[j]->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML)) {
+    nsIAtom* ownLocal = entry.GetLocal(NS_HTML5ATTRIBUTE_NAME_HTML);
+    for (nsHtml5AttributeEntry& otherEntry : aOther->mStorage) {
+      if (ownLocal == otherEntry.GetLocal(NS_HTML5ATTRIBUTE_NAME_HTML)) {
         found = true;
-        if (!nsHtml5Portability::stringEqualsString(values[i], other->values[j])) {
+        if (!entry.GetValue().Equals(otherEntry.GetValue())) {
           return false;
         }
+        break;
       }
     }
     if (!found) {
       return false;
     }
   }
   return true;
 }
 
 void
+nsHtml5HtmlAttributes::AddEntry(nsHtml5AttributeEntry&& aEntry)
+{
+  mStorage.AppendElement(aEntry);
+}
+
+void
 nsHtml5HtmlAttributes::initializeStatics()
 {
   EMPTY_ATTRIBUTES = new nsHtml5HtmlAttributes(NS_HTML5ATTRIBUTE_NAME_HTML);
 }
 
 void
 nsHtml5HtmlAttributes::releaseStatics()
 {
--- a/parser/html/nsHtml5HtmlAttributes.h
+++ b/parser/html/nsHtml5HtmlAttributes.h
@@ -1,11 +1,11 @@
 /*
  * Copyright (c) 2007 Henri Sivonen
- * Copyright (c) 2008-2011 Mozilla Foundation
+ * Copyright (c) 2008-2017 Mozilla Foundation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a 
  * copy of this software and associated documentation files (the "Software"), 
  * to deal in the Software without restriction, including without limitation 
  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
  * and/or sell copies of the Software, and to permit persons to whom the 
  * Software is furnished to do so, subject to the following conditions:
  *
@@ -16,21 +16,16 @@
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
  * DEALINGS IN THE SOFTWARE.
  */
 
-/*
- * THIS IS A GENERATED FILE. PLEASE DO NOT EDIT.
- * Please edit HtmlAttributes.java instead and regenerate.
- */
-
 #ifndef nsHtml5HtmlAttributes_h
 #define nsHtml5HtmlAttributes_h
 
 #include "nsIAtom.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5String.h"
 #include "nsNameSpaceManager.h"
 #include "nsIContent.h"
@@ -38,16 +33,18 @@
 #include "jArray.h"
 #include "nsHtml5ArrayCopy.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
+#include "nsTArray.h"
+#include "nsHtml5AttributeEntry.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
@@ -56,44 +53,49 @@ class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5HtmlAttributes
 {
   public:
     static nsHtml5HtmlAttributes* EMPTY_ATTRIBUTES;
   private:
-    int32_t mode;
-    int32_t length;
-    autoJArray<nsHtml5AttributeName*,int32_t> names;
-    autoJArray<nsHtml5String, int32_t> values;
-    autoJArray<int32_t,int32_t> lines;
+    AutoTArray<nsHtml5AttributeEntry, 5> mStorage;
+    int32_t mMode;
+    void AddEntry(nsHtml5AttributeEntry&& aEntry);
+
   public:
-    explicit nsHtml5HtmlAttributes(int32_t mode);
+    explicit nsHtml5HtmlAttributes(int32_t aMode);
     ~nsHtml5HtmlAttributes();
-    int32_t getIndex(nsHtml5AttributeName* name);
-    nsHtml5String getValue(nsHtml5AttributeName* name);
+
+    // Remove getIndex when removing isindex support
+    int32_t getIndex(nsHtml5AttributeName* aName);
+
+    nsHtml5String getValue(nsHtml5AttributeName* aName);
     int32_t getLength();
-    nsIAtom* getLocalNameNoBoundsCheck(int32_t index);
-    int32_t getURINoBoundsCheck(int32_t index);
-    nsIAtom* getPrefixNoBoundsCheck(int32_t index);
-    nsHtml5String getValueNoBoundsCheck(int32_t index);
-    nsHtml5AttributeName* getAttributeNameNoBoundsCheck(int32_t index);
-    int32_t getLineNoBoundsCheck(int32_t index);
-    void addAttribute(nsHtml5AttributeName* name,
-                      nsHtml5String value,
-                      int32_t line);
-    void clear(int32_t m);
-    void releaseValue(int32_t i);
+    nsIAtom* getLocalNameNoBoundsCheck(int32_t aIndex);
+    int32_t getURINoBoundsCheck(int32_t aIndex);
+    nsIAtom* getPrefixNoBoundsCheck(int32_t aIndex);
+    nsHtml5String getValueNoBoundsCheck(int32_t aIndex);
+    nsHtml5AttributeName* getAttributeNameNoBoundsCheck(int32_t aIndex);
+    int32_t getLineNoBoundsCheck(int32_t aIndex);
+    void addAttribute(nsHtml5AttributeName* aName,
+                      nsHtml5String aValue,
+                      int32_t aLine);
+    void AddAttributeWithLocal(nsIAtom* aName,
+                               nsHtml5String aValue,
+                               int32_t aLine);
+    void clear(int32_t aMode);
+    void releaseValue(int32_t aIndex);
     void clearWithoutReleasingContents();
-    bool contains(nsHtml5AttributeName* name);
+    bool contains(nsHtml5AttributeName* aName);
     void adjustForMath();
     void adjustForSvg();
-    nsHtml5HtmlAttributes* cloneAttributes(nsHtml5AtomTable* interner);
-    bool equalsAnother(nsHtml5HtmlAttributes* other);
+    nsHtml5HtmlAttributes* cloneAttributes(nsHtml5AtomTable* aInterner);
+    bool equalsAnother(nsHtml5HtmlAttributes* aOther);
     static void initializeStatics();
     static void releaseStatics();
 };
 
 
 
 #endif
 
--- a/parser/html/nsHtml5MetaScanner.cpp
+++ b/parser/html/nsHtml5MetaScanner.cpp
@@ -42,17 +42,16 @@
 #include "nsIUnicodeDecoder.h"
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5MetaScanner.h"
 
 static char16_t const CHARSET_DATA[] = { 'h', 'a', 'r', 's', 'e', 't' };
--- a/parser/html/nsHtml5MetaScanner.h
+++ b/parser/html/nsHtml5MetaScanner.h
@@ -45,17 +45,16 @@
 #include "nsIContentHandle.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5MetaScanner
 {
   private:
--- a/parser/html/nsHtml5Portability.h
+++ b/parser/html/nsHtml5Portability.h
@@ -45,17 +45,16 @@
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 
 
 class nsHtml5Portability
 {
   public:
     static nsIAtom* newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner);
deleted file mode 100644
--- a/parser/html/nsHtml5ReleasableAttributeName.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/* 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/. */
-
-#include "nsHtml5ReleasableAttributeName.h"
-#include "nsHtml5Portability.h"
-#include "nsHtml5AtomTable.h"
-
-nsHtml5ReleasableAttributeName::nsHtml5ReleasableAttributeName(int32_t* uri, nsIAtom** local, nsIAtom** prefix)
-  : nsHtml5AttributeName(uri, local, prefix)
-{
-}
-
-nsHtml5AttributeName*
-nsHtml5ReleasableAttributeName::cloneAttributeName(nsHtml5AtomTable* aInterner)
-{
-  nsIAtom* l = getLocal(0);
-  if (aInterner) {
-    if (!l->IsStaticAtom()) {
-      nsAutoString str;
-      l->ToString(str);
-      l = aInterner->GetAtom(str);
-    }
-  }
-  return new nsHtml5ReleasableAttributeName(nsHtml5AttributeName::ALL_NO_NS, 
-                                            nsHtml5AttributeName::SAME_LOCAL(l), 
-                                            nsHtml5AttributeName::ALL_NO_PREFIX);
-}
-
-void
-nsHtml5ReleasableAttributeName::release()
-{
-  delete this;
-}
deleted file mode 100644
--- a/parser/html/nsHtml5ReleasableAttributeName.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* 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/. */
-
-#ifndef nsHtml5ReleasableAttributeName_h
-#define nsHtml5ReleasableAttributeName_h
-
-#include "nsHtml5AttributeName.h"
-#include "mozilla/Attributes.h"
-
-class nsHtml5AtomTable;
-
-class nsHtml5ReleasableAttributeName final : public nsHtml5AttributeName
-{
-  public:
-    nsHtml5ReleasableAttributeName(int32_t* uri, nsIAtom** local, nsIAtom** prefix);
-    virtual nsHtml5AttributeName* cloneAttributeName(nsHtml5AtomTable* aInterner);
-    virtual void release();
-};
-
-#endif // nsHtml5ReleasableAttributeName_h
--- a/parser/html/nsHtml5StackNode.cpp
+++ b/parser/html/nsHtml5StackNode.cpp
@@ -43,17 +43,16 @@
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5StackNode.h"
 
 int32_t 
 nsHtml5StackNode::getGroup()
--- a/parser/html/nsHtml5StackNode.h
+++ b/parser/html/nsHtml5StackNode.h
@@ -46,17 +46,16 @@
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5StackNode
 {
   public:
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -42,17 +42,16 @@
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5StateSnapshot.h"
 
 
 nsHtml5StateSnapshot::nsHtml5StateSnapshot(jArray<nsHtml5StackNode*,int32_t> stack, jArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements, jArray<int32_t,int32_t> templateModeStack, nsIContentHandle* formPointer, nsIContentHandle* headPointer, nsIContentHandle* deepTreeSurrogateParent, int32_t mode, int32_t originalMode, bool framesetOk, bool needToDropLF, bool quirks)
--- a/parser/html/nsHtml5StateSnapshot.h
+++ b/parser/html/nsHtml5StateSnapshot.h
@@ -45,17 +45,16 @@
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5Portability;
 
 
 class nsHtml5StateSnapshot : public nsAHtml5TreeBuilderState
 {
   private:
     autoJArray<nsHtml5StackNode*,int32_t> stack;
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -45,17 +45,16 @@
 #include "nsHtml5Macros.h"
 #include "nsHtml5Highlighter.h"
 #include "nsHtml5TokenizerLoopPolicies.h"
 
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5Tokenizer.h"
 
 char16_t nsHtml5Tokenizer::LT_GT[] = { '<', '>' };
@@ -94,16 +93,17 @@ nsHtml5Tokenizer::nsHtml5Tokenizer(nsHtm
   , encodingDeclarationHandler(nullptr)
   , charRefBuf(jArray<char16_t, int32_t>::newJArray(32))
   , bmpChar(jArray<char16_t, int32_t>::newJArray(1))
   , astralChar(jArray<char16_t, int32_t>::newJArray(2))
   , containsHyphen(false)
   , tagName(nullptr)
   , nonInternedTagName(new nsHtml5ElementName())
   , attributeName(nullptr)
+  , nonInternedAttributeName(new nsHtml5AttributeName())
   , doctypeName(nullptr)
   , publicIdentifier(nullptr)
   , systemIdentifier(nullptr)
   , attributes(tokenHandler->HasBuilder() ? new nsHtml5HtmlAttributes(0)
                                           : nullptr)
   , newAttributesEachTime(!tokenHandler->HasBuilder())
   , viewingXmlSource(viewingXmlSource)
 {
@@ -345,23 +345,28 @@ nsHtml5Tokenizer::emitCurrentTagToken(bo
   }
   return stateSave;
 }
 
 void 
 nsHtml5Tokenizer::attributeNameComplete()
 {
   attributeName = nsHtml5AttributeName::nameByBuffer(strBuf, 0, strBufLen, interner);
+  if (!attributeName) {
+    nonInternedAttributeName->setNameForNonInterned(
+      nsHtml5Portability::newLocalNameFromBuffer(
+        strBuf, 0, strBufLen, interner));
+    attributeName = nonInternedAttributeName;
+  }
   clearStrBufAfterUse();
   if (!attributes) {
     attributes = new nsHtml5HtmlAttributes(0);
   }
   if (attributes->contains(attributeName)) {
     errDuplicateAttribute();
-    attributeName->release();
     attributeName = nullptr;
   }
 }
 
 void 
 nsHtml5Tokenizer::addAttributeWithoutValue()
 {
 
@@ -3974,20 +3979,18 @@ nsHtml5Tokenizer::end()
     systemIdentifier = nullptr;
   }
   if (publicIdentifier) {
     publicIdentifier.Release();
     publicIdentifier = nullptr;
   }
   tagName = nullptr;
   nonInternedTagName->setNameForNonInterned(nullptr);
-  if (attributeName) {
-    attributeName->release();
-    attributeName = nullptr;
-  }
+  attributeName = nullptr;
+  nonInternedAttributeName->setNameForNonInterned(nullptr);
   tokenHandler->endTokenization();
   if (attributes) {
     attributes->clear(0);
   }
 }
 
 void 
 nsHtml5Tokenizer::requestSuspension()
@@ -4018,23 +4021,18 @@ nsHtml5Tokenizer::resetToDataState()
   candidate = -1;
   charRefBufMark = 0;
   value = 0;
   seenDigits = false;
   endTag = false;
   shouldSuspend = false;
   initDoctypeFields();
   containsHyphen = false;
-  if (tagName) {
-    tagName = nullptr;
-  }
-  if (attributeName) {
-    attributeName->release();
-    attributeName = nullptr;
-  }
+  tagName = nullptr;
+  attributeName = nullptr;
   if (newAttributesEachTime) {
     if (attributes) {
       delete attributes;
       attributes = nullptr;
     }
   }
 }
 
@@ -4089,23 +4087,25 @@ nsHtml5Tokenizer::loadState(nsHtml5Token
   } else if (other->tagName->isInterned()) {
     tagName = other->tagName;
   } else {
     nonInternedTagName->setNameForNonInterned(
       nsHtml5Portability::newLocalFromLocal(other->tagName->getName(),
                                             interner));
     tagName = nonInternedTagName;
   }
-  if (attributeName) {
-    attributeName->release();
-  }
   if (!other->attributeName) {
     attributeName = nullptr;
+  } else if (other->attributeName->isInterned()) {
+    attributeName = other->attributeName;
   } else {
-    attributeName = other->attributeName->cloneAttributeName(interner);
+    nonInternedAttributeName->setNameForNonInterned(
+      nsHtml5Portability::newLocalFromLocal(
+        other->attributeName->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML), interner));
+    attributeName = nonInternedAttributeName;
   }
   delete attributes;
   if (!other->attributes) {
     attributes = nullptr;
   } else {
     attributes = other->attributes->cloneAttributes(interner);
   }
 }
@@ -4126,16 +4126,17 @@ nsHtml5Tokenizer::setEncodingDeclaration
   this->encodingDeclarationHandler = encodingDeclarationHandler;
 }
 
 
 nsHtml5Tokenizer::~nsHtml5Tokenizer()
 {
   MOZ_COUNT_DTOR(nsHtml5Tokenizer);
   delete nonInternedTagName;
+  delete nonInternedAttributeName;
   nonInternedTagName = nullptr;
   delete attributes;
   attributes = nullptr;
 }
 
 void
 nsHtml5Tokenizer::initializeStatics()
 {
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -48,17 +48,16 @@
 #include "nsHtml5TokenizerLoopPolicies.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5Tokenizer
 {
   private:
@@ -122,16 +121,17 @@ class nsHtml5Tokenizer
     bool endTag;
   private:
     bool containsHyphen;
     nsHtml5ElementName* tagName;
     nsHtml5ElementName* nonInternedTagName;
   protected:
     nsHtml5AttributeName* attributeName;
   private:
+    nsHtml5AttributeName* nonInternedAttributeName;
     nsIAtom* doctypeName;
     nsHtml5String publicIdentifier;
     nsHtml5String systemIdentifier;
     nsHtml5HtmlAttributes* attributes;
     bool newAttributesEachTime;
     bool shouldSuspend;
   protected:
     bool confident;
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -55,17 +55,16 @@
 #include "mozilla/Likely.h"
 #include "nsIContentHandle.h"
 #include "nsHtml5OplessBuilder.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5TreeBuilder.h"
 
 char16_t nsHtml5TreeBuilder::REPLACEMENT_CHARACTER[] = { 0xfffd };
@@ -1259,21 +1258,26 @@ nsHtml5TreeBuilder::startTag(nsHtml5Elem
                 autoJArray<char16_t,int32_t> prompt = nsHtml5Portability::newCharArrayFromString(attributes->getValueNoBoundsCheck(promptIndex));
                 appendCharacters(stack[currentPtr]->node, prompt, 0, prompt.length);
               } else {
                 appendIsindexPrompt(stack[currentPtr]->node);
               }
               nsHtml5HtmlAttributes* inputAttributes = new nsHtml5HtmlAttributes(0);
               inputAttributes->addAttribute(nsHtml5AttributeName::ATTR_NAME, nsHtml5Portability::newStringFromLiteral("isindex"), tokenizer->getLineNumber());
               for (int32_t i = 0; i < attributes->getLength(); i++) {
-                nsHtml5AttributeName* attributeQName = attributes->getAttributeNameNoBoundsCheck(i);
-                if (nsHtml5AttributeName::ATTR_NAME == attributeQName || nsHtml5AttributeName::ATTR_PROMPT == attributeQName) {
+                nsIAtom* attributeQName =
+                  attributes->getLocalNameNoBoundsCheck(i);
+                if (nsHtml5Atoms::name == attributeQName ||
+                    nsHtml5Atoms::prompt == attributeQName) {
                   attributes->releaseValue(i);
-                } else if (nsHtml5AttributeName::ATTR_ACTION != attributeQName) {
-                  inputAttributes->addAttribute(attributeQName, attributes->getValueNoBoundsCheck(i), attributes->getLineNoBoundsCheck(i));
+                } else if (nsHtml5Atoms::action != attributeQName) {
+                  inputAttributes->AddAttributeWithLocal(
+                    attributeQName,
+                    attributes->getValueNoBoundsCheck(i),
+                    attributes->getLineNoBoundsCheck(i));
                 }
               }
               attributes->clearWithoutReleasingContents();
               appendVoidElementToCurrentMayFoster(nsHtml5Atoms::input, inputAttributes, formPointer);
               pop();
               appendVoidElementToCurrentMayFoster(nsHtml5ElementName::ELT_HR, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES);
               pop();
               if (!isTemplateContents()) {
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -58,17 +58,16 @@
 #include "nsHtml5OplessBuilder.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState
 {
   private:
--- a/parser/html/nsHtml5UTF16Buffer.cpp
+++ b/parser/html/nsHtml5UTF16Buffer.cpp
@@ -42,17 +42,16 @@
 #include "nsHtml5Macros.h"
 #include "nsIContentHandle.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
-#include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5UTF16Buffer.h"
 
 int32_t 
 nsHtml5UTF16Buffer::getStart()
--- a/parser/html/nsHtml5UTF16Buffer.h
+++ b/parser/html/nsHtml5UTF16Buffer.h
@@ -45,17 +45,16 @@
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
-class nsHtml5HtmlAttributes;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5UTF16Buffer
 {
   private:
     char16_t* buffer;