Backed out changeset fabf345eec6e (bug 1375701) for bustage at parser/html/nsHtml5String.h:143:3: bad implicit conversion constructor for 'nsHtml5String'. r=backout on a CLOSED TREE
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 15 Aug 2017 16:28:10 +0200
changeset 424298 76eecfca4bc68248176e48a63efd147e16ec135d
parent 424297 fabf345eec6e49c8616b30459f1c292ac77e92a6
child 424299 661723c4cd4567ecaca7e1035cf78e3d29985f90
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1375701
milestone57.0a1
backs outfabf345eec6e49c8616b30459f1c292ac77e92a6
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
Backed out changeset fabf345eec6e (bug 1375701) for bustage at parser/html/nsHtml5String.h:143:3: bad implicit conversion constructor for 'nsHtml5String'. r=backout on a CLOSED TREE
dom/base/Element.cpp
dom/base/Element.h
parser/html/javasrc/MetaScanner.java
parser/html/javasrc/Portability.java
parser/html/javasrc/Tokenizer.java
parser/html/javasrc/TreeBuilder.java
parser/html/nsHtml5MetaScanner.cpp
parser/html/nsHtml5Portability.cpp
parser/html/nsHtml5Portability.h
parser/html/nsHtml5String.cpp
parser/html/nsHtml5String.h
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeOperation.cpp
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2401,54 +2401,21 @@ Element::OnlyNotifySameValueSet(int32_t 
   }
 
   nsAutoScriptBlocker scriptBlocker;
   nsNodeUtils::AttributeSetToCurrentValue(this, aNamespaceID, aName);
   return true;
 }
 
 nsresult
-Element::SetSingleClassFromParser(nsIAtom* aSingleClassName)
-{
-  // Keep this in sync with SetAttr and SetParsedAttr below.
-
-  if (!mAttrsAndChildren.CanFitMoreAttrs()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAttrValue value(aSingleClassName);
-
-  nsIDocument* document = GetComposedDoc();
-  mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, false);
-
-  // In principle, BeforeSetAttr should be called here if a node type
-  // existed that wanted to do something special for class, but there
-  // is no such node type, so calling SetMayHaveClass() directly.
-  SetMayHaveClass();
-
-  return SetAttrAndNotify(kNameSpaceID_None,
-                          nsGkAtoms::_class,
-                          nullptr, // prefix
-                          nullptr, // old value
-                          value,
-                          static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION),
-                          false, // hasListeners
-                          false, // notify
-                          kCallAfterSetAttr,
-                          document,
-                          updateBatch);
-}
-
-nsresult
 Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
                  nsIAtom* aPrefix, const nsAString& aValue,
                  bool aNotify)
 {
-  // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
-  // above.
+  // Keep this in sync with SetParsedAttr below
 
   NS_ENSURE_ARG_POINTER(aName);
   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
                "Don't call SetAttr with unknown namespace");
 
   if (!mAttrsAndChildren.CanFitMoreAttrs()) {
     return NS_ERROR_FAILURE;
   }
@@ -2503,17 +2470,17 @@ Element::SetAttr(int32_t aNamespaceID, n
                           kCallAfterSetAttr, document, updateBatch);
 }
 
 nsresult
 Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
                        nsIAtom* aPrefix, nsAttrValue& aParsedValue,
                        bool aNotify)
 {
-  // Keep this in sync with SetAttr and SetSingleClassFromParser above
+  // Keep this in sync with SetAttr above
 
   NS_ENSURE_ARG_POINTER(aName);
   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
                "Don't call SetAttr with unknown namespace");
 
   if (!mAttrsAndChildren.CanFitMoreAttrs()) {
     return NS_ERROR_FAILURE;
   }
@@ -2737,20 +2704,16 @@ Element::BeforeSetAttr(int32_t aNamespac
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::_class) {
       if (aValue) {
         // Note: This flag is asymmetrical. It is never unset and isn't exact.
         // If it is ever made to be exact, we probably need to handle this
         // similarly to how ids are handled in PreIdMaybeChange and
         // PostIdMaybeChange.
-        // Note that SetSingleClassFromParser inlines BeforeSetAttr and
-        // calls SetMayHaveClass directly. Making a subclass take action
-        // on the class attribute in a BeforeSetAttr override would
-        // require revising SetSingleClassFromParser.
         SetMayHaveClass();
       }
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -701,23 +701,16 @@ public:
    */
   bool OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
                               nsIAtom* aPrefix,
                               const nsAttrValueOrString& aValue,
                               bool aNotify, nsAttrValue& aOldValue,
                               uint8_t* aModType, bool* aHasListeners,
                               bool* aOldValueSet);
 
-  /**
-   * Sets the class attribute to a value that contains no whitespace.
-   * Assumes that we are not notifying and that the attribute hasn't been
-   * set previously.
-   */
-  nsresult SetSingleClassFromParser(nsIAtom* aSingleClassName);
-
   virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
                            const nsAString& aValue, bool aNotify) override;
   // aParsedValue receives the old value of the attribute. That's useful if
   // either the input or output value of aParsedValue is StoresOwnData.
   nsresult SetParsedAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
                          nsAttrValue& aParsedValue, bool aNotify);
   // GetAttr is not inlined on purpose, to keep down codesize from all
   // the inlined nsAttrValue bits for C++ callers.
--- a/parser/html/javasrc/MetaScanner.java
+++ b/parser/html/javasrc/MetaScanner.java
@@ -792,23 +792,23 @@ public abstract class MetaScanner {
      * @throws SAXException
      */
     private void handleAttributeValue() throws SAXException {
         if (metaState != A) {
             return;
         }
         if (contentIndex == CONTENT.length && content == null) {
             content = Portability.newStringFromBuffer(strBuf, 0, strBufLen
-                 // CPPONLY: , treeBuilder, false
+                 // CPPONLY: , treeBuilder
             );
             return;
         }
         if (charsetIndex == CHARSET.length && charset == null) {
             charset = Portability.newStringFromBuffer(strBuf, 0, strBufLen
-                 // CPPONLY: , treeBuilder, false
+                 // CPPONLY: , treeBuilder
             );
             return;
         }
         if (httpEquivIndex == HTTP_EQUIV.length
                 && httpEquivState == HTTP_EQUIV_NOT_SEEN) {
             httpEquivState = (contentTypeIndex == CONTENT_TYPE.length) ? HTTP_EQUIV_CONTENT_TYPE
                     : HTTP_EQUIV_OTHER;
             return;
--- a/parser/html/javasrc/Portability.java
+++ b/parser/html/javasrc/Portability.java
@@ -35,17 +35,17 @@ public final class Portability {
      * Allocates a new local name object. In C++, the refcount must be set up in such a way that
      * calling <code>releaseLocal</code> on the return value balances the refcount set by this method.
      */
     public static @Local String newLocalNameFromBuffer(@NoLength char[] buf, int offset, int length, Interner interner) {
         return new String(buf, offset, length).intern();
     }
 
     public static String newStringFromBuffer(@NoLength char[] buf, int offset, int length
-        // CPPONLY: , TreeBuilder treeBuilder, boolean maybeAtomize
+        // CPPONLY: , TreeBuilder treeBuilder
     ) {
         return new String(buf, offset, length);
     }
 
     public static String newEmptyString() {
         return "";
     }
 
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -892,17 +892,17 @@ public class Tokenizer implements Locato
      *
      * <p>
      * C++ memory note: The return value must be released.
      *
      * @return the buffer as a string
      */
     protected String strBufToString() {
         String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen
-            // CPPONLY: , tokenHandler, !newAttributesEachTime && attributeName == AttributeName.CLASS
+            // CPPONLY: , tokenHandler
         );
         clearStrBufAfterUse();
         return str;
     }
 
     /**
      * Returns the buffer as a local name. The return value is released in
      * emitDoctypeToken().
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -3288,17 +3288,17 @@ public abstract class TreeBuilder<T> imp
         }
         String charset = null;
         if (start != -1) {
             if (end == -1) {
                 end = buffer.length;
             }
             charset = Portability.newStringFromBuffer(buffer, start, end
                     - start
-                // CPPONLY: , tb, false
+                // CPPONLY: , tb
             );
         }
         return charset;
     }
 
     private void checkMetaCharset(HtmlAttributes attributes)
             throws SAXException {
         String charset = attributes.getValue(AttributeName.CHARSET);
--- a/parser/html/nsHtml5MetaScanner.cpp
+++ b/parser/html/nsHtml5MetaScanner.cpp
@@ -750,23 +750,21 @@ nsHtml5MetaScanner::addToBuffer(int32_t 
 
 void 
 nsHtml5MetaScanner::handleAttributeValue()
 {
   if (metaState != A) {
     return;
   }
   if (contentIndex == CONTENT.length && !content) {
-    content = nsHtml5Portability::newStringFromBuffer(
-      strBuf, 0, strBufLen, treeBuilder, false);
+    content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder);
     return;
   }
   if (charsetIndex == CHARSET.length && !charset) {
-    charset = nsHtml5Portability::newStringFromBuffer(
-      strBuf, 0, strBufLen, treeBuilder, false);
+    charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder);
     return;
   }
   if (httpEquivIndex == HTTP_EQUIV.length &&
       httpEquivState == HTTP_EQUIV_NOT_SEEN) {
     httpEquivState = (contentTypeIndex == CONTENT_TYPE.length)
                        ? HTTP_EQUIV_CONTENT_TYPE
                        : HTTP_EQUIV_OTHER;
     return;
--- a/parser/html/nsHtml5Portability.cpp
+++ b/parser/html/nsHtml5Portability.cpp
@@ -11,40 +11,22 @@
 nsIAtom*
 nsHtml5Portability::newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner)
 {
   NS_ASSERTION(!offset, "The offset should always be zero here.");
   NS_ASSERTION(interner, "Didn't get an atom service.");
   return interner->GetAtom(nsDependentSubstring(buf, buf + length));
 }
 
-static bool
-ContainsWhiteSpace(mozilla::Span<char16_t> aSpan)
-{
-  for (char16_t c : aSpan) {
-    if (nsContentUtils::IsHTMLWhitespace(c)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 nsHtml5String
 nsHtml5Portability::newStringFromBuffer(char16_t* buf,
                                         int32_t offset,
                                         int32_t length,
-                                        nsHtml5TreeBuilder* treeBuilder,
-                                        bool maybeAtomize)
+                                        nsHtml5TreeBuilder* treeBuilder)
 {
-  if (!length) {
-    return nsHtml5String::EmptyString();
-  }
-  if (maybeAtomize && !ContainsWhiteSpace(mozilla::MakeSpan(buf + offset, length))) {
-    return nsHtml5String::FromAtom(NS_AtomizeMainThread(nsDependentSubstring(buf + offset, length)));
-  }
   return nsHtml5String::FromBuffer(buf + offset, length, treeBuilder);
 }
 
 nsHtml5String
 nsHtml5Portability::newEmptyString()
 {
   return nsHtml5String::EmptyString();
 }
--- a/parser/html/nsHtml5Portability.h
+++ b/parser/html/nsHtml5Portability.h
@@ -56,18 +56,17 @@ class nsHtml5StateSnapshot;
 
 class nsHtml5Portability
 {
   public:
     static nsIAtom* newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner);
     static nsHtml5String newStringFromBuffer(char16_t* buf,
                                              int32_t offset,
                                              int32_t length,
-                                             nsHtml5TreeBuilder* treeBuilder,
-                                             bool maybeAtomize);
+                                             nsHtml5TreeBuilder* treeBuilder);
     static nsHtml5String newEmptyString();
     static nsHtml5String newStringFromLiteral(const char* literal);
     static nsHtml5String newStringFromString(nsHtml5String string);
     static jArray<char16_t,int32_t> newCharArrayFromLocal(nsIAtom* local);
     static jArray<char16_t, int32_t> newCharArrayFromString(
       nsHtml5String string);
     static nsIAtom* newLocalFromLocal(nsIAtom* local, nsHtml5AtomTable* interner);
     static bool localEqualsBuffer(nsIAtom* local, char16_t* buf, int32_t offset, int32_t length);
--- a/parser/html/nsHtml5String.cpp
+++ b/parser/html/nsHtml5String.cpp
@@ -2,59 +2,92 @@
  * 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 "nsHtml5String.h"
 #include "nsCharTraits.h"
 #include "nsUTF8Utils.h"
 #include "nsHtml5TreeBuilder.h"
 
+nsHtml5String::nsHtml5String(already_AddRefed<nsStringBuffer> aBuffer,
+                             uint32_t aLength)
+  : mBuffer(aBuffer.take())
+  , mLength(aLength)
+{
+  if (mBuffer) {
+    MOZ_ASSERT(aLength);
+  } else {
+    MOZ_ASSERT(!aLength || aLength == UINT32_MAX);
+  }
+}
+
 void
 nsHtml5String::ToString(nsAString& aString)
 {
-  switch (GetKind()) {
-    case eStringBuffer:
-      return AsStringBuffer()->ToString(Length(), aString);
-    case eAtom:
-      return AsAtom()->ToString(aString);
-    case eEmpty:
-      aString.Truncate();
-      return;
-    default:
-      aString.Truncate();
+  if (mBuffer) {
+    mBuffer->ToString(mLength, aString);
+  } else {
+    aString.Truncate();
+    if (mLength) {
       aString.SetIsVoid(true);
-      return;
+    }
   }
 }
 
 void
-nsHtml5String::CopyToBuffer(char16_t* aBuffer) const
+nsHtml5String::CopyToBuffer(char16_t* aBuffer)
+{
+  if (mBuffer) {
+    memcpy(aBuffer, mBuffer->Data(), mLength * sizeof(char16_t));
+  }
+}
+
+bool
+nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral)
 {
-  memcpy(aBuffer, AsPtr(), Length() * sizeof(char16_t));
+  if (!mBuffer) {
+    if (mLength) {
+      // This string is null
+      return false;
+    }
+    // this string is empty
+    return !(*aLowerCaseLiteral);
+  }
+  return !nsCharTraits<char16_t>::compareLowerCaseToASCIINullTerminated(
+    reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLowerCaseLiteral);
 }
 
 bool
-nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const
+nsHtml5String::EqualsASCII(const char* aLiteral)
 {
-  return !nsCharTraits<char16_t>::compareLowerCaseToASCIINullTerminated(
-    AsPtr(), Length(), aLowerCaseLiteral);
+  if (!mBuffer) {
+    if (mLength) {
+      // This string is null
+      return false;
+    }
+    // this string is empty
+    return !(*aLiteral);
+  }
+  return !nsCharTraits<char16_t>::compareASCIINullTerminated(
+    reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLiteral);
 }
 
 bool
-nsHtml5String::EqualsASCII(const char* aLiteral) const
+nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral)
 {
-  return !nsCharTraits<char16_t>::compareASCIINullTerminated(
-    AsPtr(), Length(), aLiteral);
-}
-
-bool
-nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) const
-{
+  if (!mBuffer) {
+    if (mLength) {
+      // This string is null
+      return false;
+    }
+    // this string is empty
+    return !(*aLowerCaseLiteral);
+  }
   const char* litPtr = aLowerCaseLiteral;
-  const char16_t* strPtr = AsPtr();
+  const char16_t* strPtr = reinterpret_cast<char16_t*>(mBuffer->Data());
   const char16_t* end = strPtr + Length();
   char16_t litChar;
   while ((litChar = *litPtr) && (strPtr != end)) {
     MOZ_ASSERT(!(litChar >= 'A' && litChar <= 'Z'),
                "Literal isn't in lower case.");
     char16_t strChar = *strPtr;
     if (strChar >= 'A' && strChar <= 'Z') {
       strChar += 0x20;
@@ -64,67 +97,57 @@ nsHtml5String::LowerCaseStartsWithASCII(
     }
     ++litPtr;
     ++strPtr;
   }
   return true;
 }
 
 bool
-nsHtml5String::Equals(nsHtml5String aOther) const
+nsHtml5String::Equals(nsHtml5String aOther)
 {
   MOZ_ASSERT(operator bool());
   MOZ_ASSERT(aOther);
-  if (Length() != aOther.Length()) {
+  if (mLength != aOther.mLength) {
     return false;
   }
+  if (!mBuffer) {
+    return true;
+  }
+  MOZ_ASSERT(aOther.mBuffer);
   return !memcmp(
-    AsPtr(), aOther.AsPtr(), Length() * sizeof(char16_t));
+    mBuffer->Data(), aOther.mBuffer->Data(), Length() * sizeof(char16_t));
 }
 
 nsHtml5String
 nsHtml5String::Clone()
 {
-  switch (GetKind()) {
-    case eStringBuffer:
-      AsStringBuffer()->AddRef();
-      break;
-    case eAtom:
-      AsAtom()->AddRef();
-      break;
-    default:
-      break;
-  }
-  return nsHtml5String(mBits);
+  MOZ_ASSERT(operator bool());
+  RefPtr<nsStringBuffer> ref(mBuffer);
+  return nsHtml5String(ref.forget(), mLength);
 }
 
 void
 nsHtml5String::Release()
 {
-  switch (GetKind()) {
-    case eStringBuffer:
-      AsStringBuffer()->Release();
-      break;
-    case eAtom:
-      AsAtom()->Release();
-      break;
-    default:
-      break;
+  if (mBuffer) {
+    mBuffer->Release();
+    mBuffer = nullptr;
   }
-  mBits = eNull;
+  mLength = UINT32_MAX;
 }
 
 // static
 nsHtml5String
 nsHtml5String::FromBuffer(char16_t* aBuffer,
                           int32_t aLength,
                           nsHtml5TreeBuilder* aTreeBuilder)
 {
   if (!aLength) {
-    return nsHtml5String(eEmpty);
+    return nsHtml5String(nullptr, 0U);
   }
   // Work with nsStringBuffer directly to make sure that storage is actually
   // nsStringBuffer and to make sure the allocation strategy matches
   // nsAttrValue::GetStringBuffer, so that it doesn't need to reallocate and
   // copy.
   RefPtr<nsStringBuffer> buffer(
     nsStringBuffer::Alloc((aLength + 1) * sizeof(char16_t)));
   if (!buffer) {
@@ -135,75 +158,68 @@ nsHtml5String::FromBuffer(char16_t* aBuf
     buffer = nsStringBuffer::Alloc(2 * sizeof(char16_t));
     if (!buffer) {
       MOZ_CRASH(
         "Out of memory so badly that couldn't even allocate placeholder.");
     }
     char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
     data[0] = 0xFFFD;
     data[1] = 0;
-    return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
+    return nsHtml5String(buffer.forget(), 1);
   }
   char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
   memcpy(data, aBuffer, aLength * sizeof(char16_t));
   data[aLength] = 0;
-  return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
+  return nsHtml5String(buffer.forget(), aLength);
 }
 
 // static
 nsHtml5String
 nsHtml5String::FromLiteral(const char* aLiteral)
 {
   size_t length = std::strlen(aLiteral);
   if (!length) {
-    return nsHtml5String(eEmpty);
+    return nsHtml5String(nullptr, 0U);
   }
   // Work with nsStringBuffer directly to make sure that storage is actually
   // nsStringBuffer and to make sure the allocation strategy matches
   // nsAttrValue::GetStringBuffer, so that it doesn't need to reallocate and
   // copy.
   RefPtr<nsStringBuffer> buffer(
     nsStringBuffer::Alloc((length + 1) * sizeof(char16_t)));
   if (!buffer) {
     MOZ_CRASH("Out of memory.");
   }
   char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
   LossyConvertEncoding8to16 converter(data);
   converter.write(aLiteral, length);
   data[length] = 0;
-  return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
+  return nsHtml5String(buffer.forget(), length);
 }
 
 // static
 nsHtml5String
 nsHtml5String::FromString(const nsAString& aString)
 {
   auto length = aString.Length();
   if (!length) {
-    return nsHtml5String(eEmpty);
+    return nsHtml5String(nullptr, 0U);
   }
   RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aString);
-  if (buffer && (length == buffer->StorageSize()/sizeof(char16_t) - 1)) {
-    return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
+  if (buffer) {
+    return nsHtml5String(buffer.forget(), length);
   }
   buffer = nsStringBuffer::Alloc((length + 1) * sizeof(char16_t));
   if (!buffer) {
     MOZ_CRASH("Out of memory.");
   }
   char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
   memcpy(data, aString.BeginReading(), length * sizeof(char16_t));
   data[length] = 0;
-  return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
-}
-
-// static
-nsHtml5String
-nsHtml5String::FromAtom(already_AddRefed<nsIAtom> aAtom)
-{
-  return nsHtml5String(reinterpret_cast<uintptr_t>(aAtom.take()) | eAtom);
+  return nsHtml5String(buffer.forget(), length);
 }
 
 // static
 nsHtml5String
 nsHtml5String::EmptyString()
 {
-  return nsHtml5String(eEmpty);
+  return nsHtml5String(nullptr, 0U);
 }
--- a/parser/html/nsHtml5String.h
+++ b/parser/html/nsHtml5String.h
@@ -1,153 +1,95 @@
 /* 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 nsHtml5String_h
 #define nsHtml5String_h
 
 #include "nsString.h"
-#include "nsIAtom.h"
 
 class nsHtml5TreeBuilder;
 
 /**
- * A pass-by-value type that can represent 
- *  * nullptr
- *  * empty string
- *  * Non-empty string as exactly-sized (capacity is length) `nsStringBuffer*`
- *  * Non-empty string as an nsIAtom*
+ * A pass-by-value type that combines an unsafe `nsStringBuffer*` with its
+ * logical length (`uint32_t`). (`nsStringBuffer` knows its capacity but not
+ * its logical length, i.e. how much of the capacity is in use.)
  *
  * Holding or passing this type is as unsafe as holding or passing
- * `nsStringBuffer*`/`nsIAtom*`.
+ * `nsStringBuffer*`.
+ *
+ * Empty strings and null strings are distinct. Since an empty nsString does
+ * not have a an `nsStringBuffer`, both empty and null `nsHtml5String` have
+ * `nullptr` as `mBuffer`. If `mBuffer` is `nullptr`, the empty case is marked
+ * with `mLength` being zero and the null case with `mLength` being non-zero.
  */
 class nsHtml5String final
 {
-private:
-
-  static const uintptr_t kKindMask = uintptr_t(3);
-
-  static const uintptr_t kPtrMask = ~kKindMask;
-
-  enum Kind : uintptr_t {
-    eNull = 0,
-    eEmpty = 1,
-    eStringBuffer = 2,
-    eAtom = 3,
-  };
-
-  inline Kind GetKind() const { return (Kind)(mBits & kKindMask); }
-
-  inline nsStringBuffer* AsStringBuffer() const
-  {
-    MOZ_ASSERT(GetKind() == eStringBuffer);
-    return reinterpret_cast<nsStringBuffer*>(mBits & kPtrMask);
-  }
-
-  inline nsIAtom* AsAtom() const
-  {
-    MOZ_ASSERT(GetKind() == eAtom);
-    return reinterpret_cast<nsIAtom*>(mBits & kPtrMask);
-  }
-
-  inline const char16_t* AsPtr() const
-  {
-    switch (GetKind()) {
-      case eStringBuffer:
-        return reinterpret_cast<char16_t*>(AsStringBuffer()->Data());
-      case eAtom:
-        return AsAtom()->GetUTF16String();
-      default:
-        return nullptr;
-    }
-  }
-
 public:
   /**
    * Default constructor.
    */
   inline nsHtml5String()
     : nsHtml5String(nullptr)
   {
   }
 
   /**
    * Constructor from nullptr.
    */
   inline MOZ_IMPLICIT nsHtml5String(decltype(nullptr))
-    : mBits(eNull)
+    : mBuffer(nullptr)
+    , mLength(UINT32_MAX)
   {
   }
 
-  inline uint32_t Length() const
-  {
-    switch (GetKind()) {
-      case eStringBuffer:
-        return (AsStringBuffer()->StorageSize()/sizeof(char16_t) - 1);
-      case eAtom:
-        return AsAtom()->GetLength();
-      default:
-        return 0;
-    }
-  }
+  inline uint32_t Length() const { return mBuffer ? mLength : 0; }
 
   /**
    * False iff the string is logically null
    */
-  inline MOZ_IMPLICIT operator bool() const { return mBits; }
-
-  /**
-   * Get the underlying nsIAtom* or nullptr if this nsHtml5String
-   * does not hold an atom.
-   */
-  inline nsIAtom* MaybeAsAtom()
-  {
-    if (GetKind() == eAtom) {
-      return AsAtom();
-    }
-    return nullptr;
-  }
+  inline MOZ_IMPLICIT operator bool() const { return !(!mBuffer && mLength); }
 
   void ToString(nsAString& aString);
 
-  void CopyToBuffer(char16_t* aBuffer) const;
+  void CopyToBuffer(char16_t* aBuffer);
 
-  bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const;
+  bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral);
 
-  bool EqualsASCII(const char* aLiteral) const;
+  bool EqualsASCII(const char* aLiteral);
 
-  bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) const;
+  bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral);
 
-  bool Equals(nsHtml5String aOther) const;
+  bool Equals(nsHtml5String aOther);
 
   nsHtml5String Clone();
 
   void Release();
 
   static nsHtml5String FromBuffer(char16_t* aBuffer,
                                   int32_t aLength,
                                   nsHtml5TreeBuilder* aTreeBuilder);
 
   static nsHtml5String FromLiteral(const char* aLiteral);
 
   static nsHtml5String FromString(const nsAString& aString);
 
-  static nsHtml5String FromAtom(already_AddRefed<nsIAtom> aAtom);
-
   static nsHtml5String EmptyString();
 
 private:
+  /**
+   * Constructor from raw parts.
+   */
+  nsHtml5String(already_AddRefed<nsStringBuffer> aBuffer, uint32_t aLength);
 
   /**
-   * Constructor from raw bits.
+   * nullptr if the string is logically null or logically empty
    */
-  nsHtml5String(uintptr_t aBits) : mBits(aBits) {};
+  nsStringBuffer* mBuffer;
 
   /**
-   * Zero if null, one if empty, otherwise tagged pointer
-   * to either nsIAtom or nsStringBuffer. The two least-significant
-   * bits are tag bits.
+   * The length of the string. non-zero if the string is logically null.
    */
-  uintptr_t mBits;
+  uint32_t mLength;
 };
 
 #endif // nsHtml5String_h
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -226,23 +226,18 @@ nsHtml5Tokenizer::emitOrAppendCharRefBuf
       charRefBufLen = 0;
     }
   }
 }
 
 nsHtml5String
 nsHtml5Tokenizer::strBufToString()
 {
-  nsHtml5String str = nsHtml5Portability::newStringFromBuffer(
-    strBuf,
-    0,
-    strBufLen,
-    tokenHandler,
-    !newAttributesEachTime &&
-      attributeName == nsHtml5AttributeName::ATTR_CLASS);
+  nsHtml5String str =
+    nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler);
   clearStrBufAfterUse();
   return str;
 }
 
 void 
 nsHtml5Tokenizer::strBufToDoctypeName()
 {
   doctypeName = nsHtml5Portability::newLocalNameFromBuffer(strBuf, 0, strBufLen, interner);
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -2215,18 +2215,18 @@ nsHtml5TreeBuilder::extractCharsetFromCo
     }
   }
   charsetloop_end: ;
     nsHtml5String charset = nullptr;
     if (start != -1) {
       if (end == -1) {
         end = buffer.length;
       }
-      charset = nsHtml5Portability::newStringFromBuffer(
-        buffer, start, end - start, tb, false);
+      charset =
+        nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb);
   }
   return charset;
 }
 
 void 
 nsHtml5TreeBuilder::checkMetaCharset(nsHtml5HtmlAttributes* attributes)
 {
   nsHtml5String charset =
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -408,51 +408,45 @@ nsHtml5TreeOperation::CreateHTMLElement(
   }
 
   if (!aAttributes) {
     return newContent;
   }
 
   int32_t len = aAttributes->getLength();
   for (int32_t i = 0; i < len; i++) {
-    nsHtml5String val = aAttributes->getValueNoBoundsCheck(i);
-    nsIAtom* klass = val.MaybeAsAtom();
-    if (klass) {
-      newContent->SetSingleClassFromParser(klass);
-    } else {
-      // prefix doesn't need regetting. it is always null or a static atom
-      // local name is never null
-      nsCOMPtr<nsIAtom> localName =
-        Reget(aAttributes->getLocalNameNoBoundsCheck(i));
-      nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
-      int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+    // prefix doesn't need regetting. it is always null or a static atom
+    // local name is never null
+    nsCOMPtr<nsIAtom> localName =
+      Reget(aAttributes->getLocalNameNoBoundsCheck(i));
+    nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
+    int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
 
-      nsString value; // Not Auto, because using it to hold nsStringBuffer*
-      val.ToString(value);
-      if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) {
-        // This is an HTML5-incompliant Geckoism.
-        // Remove when fixing bug 582361
-        NS_ConvertUTF16toUTF8 cname(value);
-        NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
-        newContent->SetAttr(nsuri,
-                            localName,
-                            prefix,
-                            uv,
-                            false);
-      } else {
-        newContent->SetAttr(nsuri,
-                            localName,
-                            prefix,
-                            value,
-                            false);
+    nsString value; // Not Auto, because using it to hold nsStringBuffer*
+    aAttributes->getValueNoBoundsCheck(i).ToString(value);
+    if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) {
+      // This is an HTML5-incompliant Geckoism.
+      // Remove when fixing bug 582361
+      NS_ConvertUTF16toUTF8 cname(value);
+      NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
+      newContent->SetAttr(nsuri,
+                          localName,
+                          prefix,
+                          uv,
+                          false);
+    } else {
+      newContent->SetAttr(nsuri,
+                          localName,
+                          prefix,
+                          value,
+                          false);
 
-        // Custom element setup may be needed if there is an "is" attribute.
-        if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
-          nsContentUtils::SetupCustomElement(newContent, &value);
-        }
+      // Custom element setup may be needed if there is an "is" attribute.
+      if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
+        nsContentUtils::SetupCustomElement(newContent, &value);
       }
     }
   }
   return newContent;
 }
 
 nsIContent*
 nsHtml5TreeOperation::CreateSVGElement(
@@ -498,32 +492,26 @@ nsHtml5TreeOperation::CreateSVGElement(
   }
 
   if (!aAttributes) {
     return newContent;
   }
 
   int32_t len = aAttributes->getLength();
   for (int32_t i = 0; i < len; i++) {
-    nsHtml5String val = aAttributes->getValueNoBoundsCheck(i);
-    nsIAtom* klass = val.MaybeAsAtom();
-    if (klass) {
-      newContent->SetSingleClassFromParser(klass);
-    } else {
-      // prefix doesn't need regetting. it is always null or a static atom
-      // local name is never null
-      nsCOMPtr<nsIAtom> localName =
-        Reget(aAttributes->getLocalNameNoBoundsCheck(i));
-      nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
-      int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+    // prefix doesn't need regetting. it is always null or a static atom
+    // local name is never null
+    nsCOMPtr<nsIAtom> localName =
+      Reget(aAttributes->getLocalNameNoBoundsCheck(i));
+    nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
+    int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
 
-      nsString value; // Not Auto, because using it to hold nsStringBuffer*
-      val.ToString(value);
-      newContent->SetAttr(nsuri, localName, prefix, value, false);
-    }
+    nsString value; // Not Auto, because using it to hold nsStringBuffer*
+    aAttributes->getValueNoBoundsCheck(i).ToString(value);
+    newContent->SetAttr(nsuri, localName, prefix, value, false);
   }
   return newContent;
 }
 
 nsIContent*
 nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName,
                                           nsHtml5HtmlAttributes* aAttributes,
                                           nsNodeInfoManager* aNodeInfoManager,
@@ -552,32 +540,26 @@ nsHtml5TreeOperation::CreateMathMLElemen
   aBuilder->HoldElement(newElement.forget());
 
   if (!aAttributes) {
     return newContent;
   }
 
   int32_t len = aAttributes->getLength();
   for (int32_t i = 0; i < len; i++) {
-    nsHtml5String val = aAttributes->getValueNoBoundsCheck(i);
-    nsIAtom* klass = val.MaybeAsAtom();
-    if (klass) {
-      newContent->SetSingleClassFromParser(klass);
-    } else {
-      // prefix doesn't need regetting. it is always null or a static atom
-      // local name is never null
-      nsCOMPtr<nsIAtom> localName =
-        Reget(aAttributes->getLocalNameNoBoundsCheck(i));
-      nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
-      int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+    // prefix doesn't need regetting. it is always null or a static atom
+    // local name is never null
+    nsCOMPtr<nsIAtom> localName =
+      Reget(aAttributes->getLocalNameNoBoundsCheck(i));
+    nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
+    int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
 
-      nsString value; // Not Auto, because using it to hold nsStringBuffer*
-      val.ToString(value);
-      newContent->SetAttr(nsuri, localName, prefix, value, false);
-    }
+    nsString value; // Not Auto, because using it to hold nsStringBuffer*
+    aAttributes->getValueNoBoundsCheck(i).ToString(value);
+    newContent->SetAttr(nsuri, localName, prefix, value, false);
   }
   return newContent;
 }
 
 void
 nsHtml5TreeOperation::SetFormElement(nsIContent* aNode, nsIContent* aParent)
 {
   nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aNode));