From f4a1d0123c41647f2f05aeaa2ae14bd1806fbb5c Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 17 Apr 2020 06:12:55 -0400 Subject: Bug 1375701 - Atomize class attribute value in the parser in the innerHTML case Tag #1375 --- dom/base/Element.cpp | 41 +++++- dom/base/Element.h | 7 ++ .../nu/validator/htmlparser/impl/MetaScanner.java | 4 +- .../nu/validator/htmlparser/impl/Portability.java | 2 +- .../nu/validator/htmlparser/impl/Tokenizer.java | 2 +- .../nu/validator/htmlparser/impl/TreeBuilder.java | 2 +- parser/html/nsHtml5MetaScanner.cpp | 4 +- parser/html/nsHtml5Portability.cpp | 20 ++- parser/html/nsHtml5Portability.h | 2 +- parser/html/nsHtml5String.cpp | 140 +++++++++------------ parser/html/nsHtml5String.h | 112 +++++++++++++---- parser/html/nsHtml5Tokenizer.cpp | 2 +- parser/html/nsHtml5TreeBuilder.cpp | 2 +- parser/html/nsHtml5TreeOperation.cpp | 88 +++++++------ 14 files changed, 275 insertions(+), 153 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 96ccfd52a..31af0c1db 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2450,12 +2450,45 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* 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(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 + // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser + // above. NS_ENSURE_ARG_POINTER(aName); NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, @@ -2520,7 +2553,7 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix, nsAttrValue& aParsedValue, bool aNotify) { - // Keep this in sync with SetAttr above + // Keep this in sync with SetAttr and SetSingleClassFromParser above NS_ENSURE_ARG_POINTER(aName); NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, @@ -2764,6 +2797,10 @@ Element::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, // 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(); } } diff --git a/dom/base/Element.h b/dom/base/Element.h index 76f0767e6..88f416be7 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -601,6 +601,13 @@ public: 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 diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java index 9a3dc16b2..be7576ff0 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/MetaScanner.java @@ -798,13 +798,13 @@ public abstract class MetaScanner { } if (contentIndex == CONTENT.length && content == null) { content = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , treeBuilder + // CPPONLY: , treeBuilder, false ); return; } if (charsetIndex == CHARSET.length && charset == null) { charset = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , treeBuilder + // CPPONLY: , treeBuilder, false ); return; } diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java index 2b3f96625..8f941ce01 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Portability.java @@ -42,7 +42,7 @@ public final class Portability { } public static String newStringFromBuffer(@NoLength char[] buf, int offset, int length - // CPPONLY: , TreeBuilder treeBuilder + // CPPONLY: , TreeBuilder treeBuilder, boolean maybeAtomize ) { return new String(buf, offset, length); } diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java index 996bd9ceb..3d617fd01 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/Tokenizer.java @@ -917,7 +917,7 @@ public class Tokenizer implements Locator { */ protected String strBufToString() { String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen - // CPPONLY: , tokenHandler + // CPPONLY: , tokenHandler, !newAttributesEachTime && attributeName == AttributeName.CLASS ); clearStrBufAfterUse(); return str; diff --git a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java index cc60f4c4b..ef9576ee3 100644 --- a/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java +++ b/parser/html/java/htmlparser/src/nu/validator/htmlparser/impl/TreeBuilder.java @@ -3337,7 +3337,7 @@ public abstract class TreeBuilder implements TokenHandler, } charset = Portability.newStringFromBuffer(buffer, start, end - start - // CPPONLY: , tb + // CPPONLY: , tb, false ); } return charset; diff --git a/parser/html/nsHtml5MetaScanner.cpp b/parser/html/nsHtml5MetaScanner.cpp index 07d81c1a1..9fa3d3a70 100644 --- a/parser/html/nsHtml5MetaScanner.cpp +++ b/parser/html/nsHtml5MetaScanner.cpp @@ -757,11 +757,11 @@ nsHtml5MetaScanner::handleAttributeValue() return; } if (contentIndex == CONTENT.length && !content) { - content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder); + content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false); return; } if (charsetIndex == CHARSET.length && !charset) { - charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder); + charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false); return; } if (httpEquivIndex == HTTP_EQUIV.length && httpEquivState == HTTP_EQUIV_NOT_SEEN) { diff --git a/parser/html/nsHtml5Portability.cpp b/parser/html/nsHtml5Portability.cpp index 5a76b3c56..400f06204 100644 --- a/parser/html/nsHtml5Portability.cpp +++ b/parser/html/nsHtml5Portability.cpp @@ -16,12 +16,30 @@ nsHtml5Portability::newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_ return interner->GetAtom(nsDependentSubstring(buf, buf + length)); } +static bool +ContainsWhiteSpace(mozilla::Span 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) + nsHtml5TreeBuilder* treeBuilder, + bool maybeAtomize) { + 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); } diff --git a/parser/html/nsHtml5Portability.h b/parser/html/nsHtml5Portability.h index 20d9e08e8..2a9d9422b 100644 --- a/parser/html/nsHtml5Portability.h +++ b/parser/html/nsHtml5Portability.h @@ -61,7 +61,7 @@ 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); + static nsHtml5String newStringFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5TreeBuilder* treeBuilder, bool maybeAtomize); static nsHtml5String newEmptyString(); static nsHtml5String newStringFromLiteral(const char* literal); static nsHtml5String newStringFromString(nsHtml5String string); diff --git a/parser/html/nsHtml5String.cpp b/parser/html/nsHtml5String.cpp index d26eeaede..e72798016 100644 --- a/parser/html/nsHtml5String.cpp +++ b/parser/html/nsHtml5String.cpp @@ -7,82 +7,49 @@ #include "nsUTF8Utils.h" #include "nsHtml5TreeBuilder.h" -nsHtml5String::nsHtml5String(already_AddRefed 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) { - if (mBuffer) { - mBuffer->ToString(mLength, aString); - } else { - aString.Truncate(); - if (mLength) { + switch (GetKind()) { + case eStringBuffer: + return AsStringBuffer()->ToString(Length(), aString); + case eAtom: + return AsAtom()->ToString(aString); + case eEmpty: + aString.Truncate(); + return; + default: + aString.Truncate(); aString.SetIsVoid(true); - } + return; } } void -nsHtml5String::CopyToBuffer(char16_t* aBuffer) +nsHtml5String::CopyToBuffer(char16_t* aBuffer) const { - if (mBuffer) { - memcpy(aBuffer, mBuffer->Data(), mLength * sizeof(char16_t)); - } + memcpy(aBuffer, AsPtr(), Length() * sizeof(char16_t)); } bool -nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral) +nsHtml5String::LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const { - if (!mBuffer) { - if (mLength) { - // This string is null - return false; - } - // this string is empty - return !(*aLowerCaseLiteral); - } return !nsCharTraits::compareLowerCaseToASCIINullTerminated( - reinterpret_cast(mBuffer->Data()), Length(), aLowerCaseLiteral); + AsPtr(), Length(), aLowerCaseLiteral); } bool -nsHtml5String::EqualsASCII(const char* aLiteral) +nsHtml5String::EqualsASCII(const char* aLiteral) const { - if (!mBuffer) { - if (mLength) { - // This string is null - return false; - } - // this string is empty - return !(*aLiteral); - } return !nsCharTraits::compareASCIINullTerminated( - reinterpret_cast(mBuffer->Data()), Length(), aLiteral); + AsPtr(), Length(), aLiteral); } bool -nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) +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 = reinterpret_cast(mBuffer->Data()); + const char16_t* strPtr = AsPtr(); const char16_t* end = strPtr + Length(); char16_t litChar; while ((litChar = *litPtr) && (strPtr != end)) { @@ -102,37 +69,47 @@ nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) } bool -nsHtml5String::Equals(nsHtml5String aOther) +nsHtml5String::Equals(nsHtml5String aOther) const { MOZ_ASSERT(operator bool()); MOZ_ASSERT(aOther); - if (mLength != aOther.mLength) { + if (Length() != aOther.Length()) { return false; } - if (!mBuffer) { - return true; - } - MOZ_ASSERT(aOther.mBuffer); return !memcmp( - mBuffer->Data(), aOther.mBuffer->Data(), Length() * sizeof(char16_t)); + AsPtr(), aOther.AsPtr(), Length() * sizeof(char16_t)); } nsHtml5String nsHtml5String::Clone() { - MOZ_ASSERT(operator bool()); - RefPtr ref(mBuffer); - return nsHtml5String(ref.forget(), mLength); + switch (GetKind()) { + case eStringBuffer: + AsStringBuffer()->AddRef(); + break; + case eAtom: + AsAtom()->AddRef(); + break; + default: + break; + } + return nsHtml5String(mBits); } void nsHtml5String::Release() { - if (mBuffer) { - mBuffer->Release(); - mBuffer = nullptr; - } - mLength = UINT32_MAX; + switch (GetKind()) { + case eStringBuffer: + AsStringBuffer()->Release(); + break; + case eAtom: + AsAtom()->Release(); + break; + default: + break; + } + mBits = eNull; } // static @@ -142,7 +119,7 @@ nsHtml5String::FromBuffer(char16_t* aBuffer, nsHtml5TreeBuilder* aTreeBuilder) { if (!aLength) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } // Work with nsStringBuffer directly to make sure that storage is actually // nsStringBuffer and to make sure the allocation strategy matches @@ -163,12 +140,12 @@ nsHtml5String::FromBuffer(char16_t* aBuffer, char16_t* data = reinterpret_cast(buffer->Data()); data[0] = 0xFFFD; data[1] = 0; - return nsHtml5String(buffer.forget(), 1); + return nsHtml5String(reinterpret_cast(buffer.forget().take()) | eStringBuffer); } char16_t* data = reinterpret_cast(buffer->Data()); memcpy(data, aBuffer, aLength * sizeof(char16_t)); data[aLength] = 0; - return nsHtml5String(buffer.forget(), aLength); + return nsHtml5String(reinterpret_cast(buffer.forget().take()) | eStringBuffer); } // static @@ -177,7 +154,7 @@ nsHtml5String::FromLiteral(const char* aLiteral) { size_t length = std::strlen(aLiteral); if (!length) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } // Work with nsStringBuffer directly to make sure that storage is actually // nsStringBuffer and to make sure the allocation strategy matches @@ -192,7 +169,7 @@ nsHtml5String::FromLiteral(const char* aLiteral) LossyConvertEncoding8to16 converter(data); converter.write(aLiteral, length); data[length] = 0; - return nsHtml5String(buffer.forget(), length); + return nsHtml5String(reinterpret_cast(buffer.forget().take()) | eStringBuffer); } // static @@ -201,11 +178,11 @@ nsHtml5String::FromString(const nsAString& aString) { auto length = aString.Length(); if (!length) { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); } RefPtr buffer = nsStringBuffer::FromString(aString); - if (buffer) { - return nsHtml5String(buffer.forget(), length); + if (buffer && (length == buffer->StorageSize()/sizeof(char16_t) - 1)) { + return nsHtml5String(reinterpret_cast(buffer.forget().take()) | eStringBuffer); } buffer = nsStringBuffer::Alloc((length + 1) * sizeof(char16_t)); if (!buffer) { @@ -214,13 +191,20 @@ nsHtml5String::FromString(const nsAString& aString) char16_t* data = reinterpret_cast(buffer->Data()); memcpy(data, aString.BeginReading(), length * sizeof(char16_t)); data[length] = 0; - return nsHtml5String(buffer.forget(), length); + return nsHtml5String(reinterpret_cast(buffer.forget().take()) | eStringBuffer); +} + +// static +nsHtml5String +nsHtml5String::FromAtom(already_AddRefed aAtom) +{ + return nsHtml5String(reinterpret_cast(aAtom.take()) | eAtom); } // static nsHtml5String nsHtml5String::EmptyString() { - return nsHtml5String(nullptr, 0U); + return nsHtml5String(eEmpty); -} \ No newline at end of file +} diff --git a/parser/html/nsHtml5String.h b/parser/html/nsHtml5String.h index 191bf6be8..54954fe1a 100644 --- a/parser/html/nsHtml5String.h +++ b/parser/html/nsHtml5String.h @@ -6,24 +6,61 @@ #define nsHtml5String_h #include "nsString.h" +#include "nsIAtom.h" class nsHtml5TreeBuilder; /** - * 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.) + * 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* * * Holding or passing this type is as unsafe as holding or passing - * `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. + * `nsStringBuffer*`/`nsIAtom*`. */ 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(mBits & kPtrMask); + } + + inline nsIAtom* AsAtom() const + { + MOZ_ASSERT(GetKind() == eAtom); + return reinterpret_cast(mBits & kPtrMask); + } + + inline const char16_t* AsPtr() const + { + switch (GetKind()) { + case eStringBuffer: + return reinterpret_cast(AsStringBuffer()->Data()); + case eAtom: + return AsAtom()->GetUTF16String(); + default: + return nullptr; + } + } + public: /** * Default constructor. @@ -37,29 +74,50 @@ public: * Constructor from nullptr. */ inline MOZ_IMPLICIT nsHtml5String(decltype(nullptr)) - : mBuffer(nullptr) - , mLength(UINT32_MAX) + : mBits(eNull) { } - inline uint32_t Length() const { return mBuffer ? mLength : 0; } + inline uint32_t Length() const + { + switch (GetKind()) { + case eStringBuffer: + return (AsStringBuffer()->StorageSize()/sizeof(char16_t) - 1); + case eAtom: + return AsAtom()->GetLength(); + default: + return 0; + } + } /** * False iff the string is logically null */ - inline MOZ_IMPLICIT operator bool() const { return !(!mBuffer && mLength); } + 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; + } void ToString(nsAString& aString); - void CopyToBuffer(char16_t* aBuffer); + void CopyToBuffer(char16_t* aBuffer) const; - bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral); + bool LowerCaseEqualsASCII(const char* aLowerCaseLiteral) const; - bool EqualsASCII(const char* aLiteral); + bool EqualsASCII(const char* aLiteral) const; - bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral); + bool LowerCaseStartsWithASCII(const char* aLowerCaseLiteral) const; - bool Equals(nsHtml5String aOther); + bool Equals(nsHtml5String aOther) const; nsHtml5String Clone(); @@ -73,23 +131,23 @@ public: static nsHtml5String FromString(const nsAString& aString); + static nsHtml5String FromAtom(already_AddRefed aAtom); + static nsHtml5String EmptyString(); private: - /** - * Constructor from raw parts. - */ - nsHtml5String(already_AddRefed aBuffer, uint32_t aLength); /** - * nullptr if the string is logically null or logically empty + * Constructor from raw bits. */ - nsStringBuffer* mBuffer; + explicit nsHtml5String(uintptr_t aBits) : mBits(aBits) {}; /** - * The length of the string. non-zero if the string is logically null. + * Zero if null, one if empty, otherwise tagged pointer + * to either nsIAtom or nsStringBuffer. The two least-significant + * bits are tag bits. */ - uint32_t mLength; + uintptr_t mBits; }; -#endif // nsHtml5String_h \ No newline at end of file +#endif // nsHtml5String_h diff --git a/parser/html/nsHtml5Tokenizer.cpp b/parser/html/nsHtml5Tokenizer.cpp index 8fea32eb8..60285ce8e 100644 --- a/parser/html/nsHtml5Tokenizer.cpp +++ b/parser/html/nsHtml5Tokenizer.cpp @@ -225,7 +225,7 @@ nsHtml5Tokenizer::emitOrAppendCharRefBuf(int32_t returnState) nsHtml5String nsHtml5Tokenizer::strBufToString() { - nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler); + nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler, !newAttributesEachTime && attributeName == nsHtml5AttributeName::ATTR_CLASS); clearStrBufAfterUse(); return str; } diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp index efbc33967..76a2398f5 100644 --- a/parser/html/nsHtml5TreeBuilder.cpp +++ b/parser/html/nsHtml5TreeBuilder.cpp @@ -2181,7 +2181,7 @@ nsHtml5TreeBuilder::extractCharsetFromContent(nsHtml5String attributeValue, nsHt if (end == -1) { end = buffer.length; } - charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb); + charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb, false); } return charset; } diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 22c805859..46907d3a4 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -361,29 +361,34 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes) { - int32_t len = aAttributes->getLength(); + int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(i); + nsHtml5String val = aAttributes->getValueNoBoundsCheck(i); + nsIAtom* klass = val.MaybeAsAtom(); + if (klass) { + aElement->SetSingleClassFromParser(klass); + } else { + // prefix doesn't need regetting. it is always null or a static atom + // local name is never null + RefPtr localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + RefPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) { - // This is an HTML5-incompliant Geckoism. - // Remove when fixing bug 582361 - NS_ConvertUTF16toUTF8 cname(value); - NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting())); - aElement->SetAttr(nsuri, + 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())); + aElement->SetAttr(nsuri, localName, prefix, uv, false); - } else { - aElement->SetAttr(nsuri, + } else { + aElement->SetAttr(nsuri, localName, prefix, value, @@ -391,6 +396,7 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement, } } } +} nsIContent* nsHtml5TreeOperation::CreateHTMLElement( @@ -580,16 +586,22 @@ nsHtml5TreeOperation::CreateSVGElement( int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(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 localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - newContent->SetAttr(nsuri, localName, prefix, value, false); + nsString value; // Not Auto, because using it to hold nsStringBuffer* + val.ToString(value); + newContent->SetAttr(nsuri, localName, prefix, value, false); + } } return newContent; } @@ -618,16 +630,22 @@ nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName, int32_t len = aAttributes->getLength(); for (int32_t i = 0; i < len; i++) { - // prefix doesn't need regetting. it is always null or a static atom - // local name is never null - nsCOMPtr localName = - Reget(aAttributes->getLocalNameNoBoundsCheck(i)); - nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); - int32_t nsuri = aAttributes->getURINoBoundsCheck(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 localName = + Reget(aAttributes->getLocalNameNoBoundsCheck(i)); + nsCOMPtr prefix = aAttributes->getPrefixNoBoundsCheck(i); + int32_t nsuri = aAttributes->getURINoBoundsCheck(i); - nsString value; // Not Auto, because using it to hold nsStringBuffer* - aAttributes->getValueNoBoundsCheck(i).ToString(value); - newContent->SetAttr(nsuri, localName, prefix, value, false); + nsString value; // Not Auto, because using it to hold nsStringBuffer* + val.ToString(value); + newContent->SetAttr(nsuri, localName, prefix, value, false); + } } return newContent; } -- cgit v1.2.3