mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 23:07:31 +09:00
Bug 1375701 - Atomize class attribute value in the parser in the innerHTML case
Tag #1375
This commit is contained in:
parent
a25be6bc13
commit
fe13c5bffc
14 changed files with 275 additions and 153 deletions
|
|
@ -2450,12 +2450,45 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
|
||||||
return true;
|
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
|
nsresult
|
||||||
Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
||||||
nsIAtom* aPrefix, const nsAString& aValue,
|
nsIAtom* aPrefix, const nsAString& aValue,
|
||||||
bool aNotify)
|
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_ENSURE_ARG_POINTER(aName);
|
||||||
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
|
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
|
||||||
|
|
@ -2520,7 +2553,7 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
|
||||||
nsIAtom* aPrefix, nsAttrValue& aParsedValue,
|
nsIAtom* aPrefix, nsAttrValue& aParsedValue,
|
||||||
bool aNotify)
|
bool aNotify)
|
||||||
{
|
{
|
||||||
// Keep this in sync with SetAttr above
|
// Keep this in sync with SetAttr and SetSingleClassFromParser above
|
||||||
|
|
||||||
NS_ENSURE_ARG_POINTER(aName);
|
NS_ENSURE_ARG_POINTER(aName);
|
||||||
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
|
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
|
// If it is ever made to be exact, we probably need to handle this
|
||||||
// similarly to how ids are handled in PreIdMaybeChange and
|
// similarly to how ids are handled in PreIdMaybeChange and
|
||||||
// PostIdMaybeChange.
|
// 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();
|
SetMayHaveClass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,13 @@ public:
|
||||||
uint8_t* aModType, bool* aHasListeners,
|
uint8_t* aModType, bool* aHasListeners,
|
||||||
bool* aOldValueSet);
|
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,
|
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
|
||||||
const nsAString& aValue, bool aNotify) override;
|
const nsAString& aValue, bool aNotify) override;
|
||||||
// aParsedValue receives the old value of the attribute. That's useful if
|
// aParsedValue receives the old value of the attribute. That's useful if
|
||||||
|
|
|
||||||
|
|
@ -798,13 +798,13 @@ public abstract class MetaScanner {
|
||||||
}
|
}
|
||||||
if (contentIndex == CONTENT.length && content == null) {
|
if (contentIndex == CONTENT.length && content == null) {
|
||||||
content = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
content = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
||||||
// CPPONLY: , treeBuilder
|
// CPPONLY: , treeBuilder, false
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (charsetIndex == CHARSET.length && charset == null) {
|
if (charsetIndex == CHARSET.length && charset == null) {
|
||||||
charset = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
charset = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
||||||
// CPPONLY: , treeBuilder
|
// CPPONLY: , treeBuilder, false
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public final class Portability {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String newStringFromBuffer(@NoLength char[] buf, int offset, int length
|
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);
|
return new String(buf, offset, length);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -917,7 +917,7 @@ public class Tokenizer implements Locator {
|
||||||
*/
|
*/
|
||||||
protected String strBufToString() {
|
protected String strBufToString() {
|
||||||
String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen
|
||||||
// CPPONLY: , tokenHandler
|
// CPPONLY: , tokenHandler, !newAttributesEachTime && attributeName == AttributeName.CLASS
|
||||||
);
|
);
|
||||||
clearStrBufAfterUse();
|
clearStrBufAfterUse();
|
||||||
return str;
|
return str;
|
||||||
|
|
|
||||||
|
|
@ -3337,7 +3337,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
||||||
}
|
}
|
||||||
charset = Portability.newStringFromBuffer(buffer, start, end
|
charset = Portability.newStringFromBuffer(buffer, start, end
|
||||||
- start
|
- start
|
||||||
// CPPONLY: , tb
|
// CPPONLY: , tb, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return charset;
|
return charset;
|
||||||
|
|
|
||||||
|
|
@ -757,11 +757,11 @@ nsHtml5MetaScanner::handleAttributeValue()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (contentIndex == CONTENT.length && !content) {
|
if (contentIndex == CONTENT.length && !content) {
|
||||||
content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder);
|
content = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (charsetIndex == CHARSET.length && !charset) {
|
if (charsetIndex == CHARSET.length && !charset) {
|
||||||
charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder);
|
charset = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, treeBuilder, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (httpEquivIndex == HTTP_EQUIV.length && httpEquivState == HTTP_EQUIV_NOT_SEEN) {
|
if (httpEquivIndex == HTTP_EQUIV.length && httpEquivState == HTTP_EQUIV_NOT_SEEN) {
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,30 @@ nsHtml5Portability::newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_
|
||||||
return interner->GetAtom(nsDependentSubstring(buf, buf + length));
|
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
|
nsHtml5String
|
||||||
nsHtml5Portability::newStringFromBuffer(char16_t* buf,
|
nsHtml5Portability::newStringFromBuffer(char16_t* buf,
|
||||||
int32_t offset,
|
int32_t offset,
|
||||||
int32_t length,
|
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);
|
return nsHtml5String::FromBuffer(buf + offset, length, treeBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class nsHtml5Portability
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static nsIAtom* newLocalNameFromBuffer(char16_t* buf, int32_t offset, int32_t length, nsHtml5AtomTable* interner);
|
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 newEmptyString();
|
||||||
static nsHtml5String newStringFromLiteral(const char* literal);
|
static nsHtml5String newStringFromLiteral(const char* literal);
|
||||||
static nsHtml5String newStringFromString(nsHtml5String string);
|
static nsHtml5String newStringFromString(nsHtml5String string);
|
||||||
|
|
|
||||||
|
|
@ -7,82 +7,49 @@
|
||||||
#include "nsUTF8Utils.h"
|
#include "nsUTF8Utils.h"
|
||||||
#include "nsHtml5TreeBuilder.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
|
void
|
||||||
nsHtml5String::ToString(nsAString& aString)
|
nsHtml5String::ToString(nsAString& aString)
|
||||||
{
|
{
|
||||||
if (mBuffer) {
|
switch (GetKind()) {
|
||||||
mBuffer->ToString(mLength, aString);
|
case eStringBuffer:
|
||||||
} else {
|
return AsStringBuffer()->ToString(Length(), aString);
|
||||||
|
case eAtom:
|
||||||
|
return AsAtom()->ToString(aString);
|
||||||
|
case eEmpty:
|
||||||
|
aString.Truncate();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
aString.Truncate();
|
aString.Truncate();
|
||||||
if (mLength) {
|
|
||||||
aString.SetIsVoid(true);
|
aString.SetIsVoid(true);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHtml5String::CopyToBuffer(char16_t* aBuffer)
|
nsHtml5String::CopyToBuffer(char16_t* aBuffer) const
|
||||||
{
|
{
|
||||||
if (mBuffer) {
|
memcpy(aBuffer, AsPtr(), Length() * sizeof(char16_t));
|
||||||
memcpy(aBuffer, mBuffer->Data(), mLength * sizeof(char16_t));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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<char16_t>::compareLowerCaseToASCIINullTerminated(
|
return !nsCharTraits<char16_t>::compareLowerCaseToASCIINullTerminated(
|
||||||
reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLowerCaseLiteral);
|
AsPtr(), Length(), aLowerCaseLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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<char16_t>::compareASCIINullTerminated(
|
return !nsCharTraits<char16_t>::compareASCIINullTerminated(
|
||||||
reinterpret_cast<char16_t*>(mBuffer->Data()), Length(), aLiteral);
|
AsPtr(), Length(), aLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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 char* litPtr = aLowerCaseLiteral;
|
||||||
const char16_t* strPtr = reinterpret_cast<char16_t*>(mBuffer->Data());
|
const char16_t* strPtr = AsPtr();
|
||||||
const char16_t* end = strPtr + Length();
|
const char16_t* end = strPtr + Length();
|
||||||
char16_t litChar;
|
char16_t litChar;
|
||||||
while ((litChar = *litPtr) && (strPtr != end)) {
|
while ((litChar = *litPtr) && (strPtr != end)) {
|
||||||
|
|
@ -102,37 +69,47 @@ nsHtml5String::LowerCaseStartsWithASCII(const char* aLowerCaseLiteral)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsHtml5String::Equals(nsHtml5String aOther)
|
nsHtml5String::Equals(nsHtml5String aOther) const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(operator bool());
|
MOZ_ASSERT(operator bool());
|
||||||
MOZ_ASSERT(aOther);
|
MOZ_ASSERT(aOther);
|
||||||
if (mLength != aOther.mLength) {
|
if (Length() != aOther.Length()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!mBuffer) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(aOther.mBuffer);
|
|
||||||
return !memcmp(
|
return !memcmp(
|
||||||
mBuffer->Data(), aOther.mBuffer->Data(), Length() * sizeof(char16_t));
|
AsPtr(), aOther.AsPtr(), Length() * sizeof(char16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsHtml5String
|
nsHtml5String
|
||||||
nsHtml5String::Clone()
|
nsHtml5String::Clone()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(operator bool());
|
switch (GetKind()) {
|
||||||
RefPtr<nsStringBuffer> ref(mBuffer);
|
case eStringBuffer:
|
||||||
return nsHtml5String(ref.forget(), mLength);
|
AsStringBuffer()->AddRef();
|
||||||
|
break;
|
||||||
|
case eAtom:
|
||||||
|
AsAtom()->AddRef();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nsHtml5String(mBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsHtml5String::Release()
|
nsHtml5String::Release()
|
||||||
{
|
{
|
||||||
if (mBuffer) {
|
switch (GetKind()) {
|
||||||
mBuffer->Release();
|
case eStringBuffer:
|
||||||
mBuffer = nullptr;
|
AsStringBuffer()->Release();
|
||||||
|
break;
|
||||||
|
case eAtom:
|
||||||
|
AsAtom()->Release();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
mLength = UINT32_MAX;
|
mBits = eNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -142,7 +119,7 @@ nsHtml5String::FromBuffer(char16_t* aBuffer,
|
||||||
nsHtml5TreeBuilder* aTreeBuilder)
|
nsHtml5TreeBuilder* aTreeBuilder)
|
||||||
{
|
{
|
||||||
if (!aLength) {
|
if (!aLength) {
|
||||||
return nsHtml5String(nullptr, 0U);
|
return nsHtml5String(eEmpty);
|
||||||
}
|
}
|
||||||
// Work with nsStringBuffer directly to make sure that storage is actually
|
// Work with nsStringBuffer directly to make sure that storage is actually
|
||||||
// nsStringBuffer and to make sure the allocation strategy matches
|
// nsStringBuffer and to make sure the allocation strategy matches
|
||||||
|
|
@ -163,12 +140,12 @@ nsHtml5String::FromBuffer(char16_t* aBuffer,
|
||||||
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
||||||
data[0] = 0xFFFD;
|
data[0] = 0xFFFD;
|
||||||
data[1] = 0;
|
data[1] = 0;
|
||||||
return nsHtml5String(buffer.forget(), 1);
|
return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
|
||||||
}
|
}
|
||||||
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
||||||
memcpy(data, aBuffer, aLength * sizeof(char16_t));
|
memcpy(data, aBuffer, aLength * sizeof(char16_t));
|
||||||
data[aLength] = 0;
|
data[aLength] = 0;
|
||||||
return nsHtml5String(buffer.forget(), aLength);
|
return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -177,7 +154,7 @@ nsHtml5String::FromLiteral(const char* aLiteral)
|
||||||
{
|
{
|
||||||
size_t length = std::strlen(aLiteral);
|
size_t length = std::strlen(aLiteral);
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return nsHtml5String(nullptr, 0U);
|
return nsHtml5String(eEmpty);
|
||||||
}
|
}
|
||||||
// Work with nsStringBuffer directly to make sure that storage is actually
|
// Work with nsStringBuffer directly to make sure that storage is actually
|
||||||
// nsStringBuffer and to make sure the allocation strategy matches
|
// nsStringBuffer and to make sure the allocation strategy matches
|
||||||
|
|
@ -192,7 +169,7 @@ nsHtml5String::FromLiteral(const char* aLiteral)
|
||||||
LossyConvertEncoding8to16 converter(data);
|
LossyConvertEncoding8to16 converter(data);
|
||||||
converter.write(aLiteral, length);
|
converter.write(aLiteral, length);
|
||||||
data[length] = 0;
|
data[length] = 0;
|
||||||
return nsHtml5String(buffer.forget(), length);
|
return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -201,11 +178,11 @@ nsHtml5String::FromString(const nsAString& aString)
|
||||||
{
|
{
|
||||||
auto length = aString.Length();
|
auto length = aString.Length();
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return nsHtml5String(nullptr, 0U);
|
return nsHtml5String(eEmpty);
|
||||||
}
|
}
|
||||||
RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aString);
|
RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aString);
|
||||||
if (buffer) {
|
if (buffer && (length == buffer->StorageSize()/sizeof(char16_t) - 1)) {
|
||||||
return nsHtml5String(buffer.forget(), length);
|
return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) | eStringBuffer);
|
||||||
}
|
}
|
||||||
buffer = nsStringBuffer::Alloc((length + 1) * sizeof(char16_t));
|
buffer = nsStringBuffer::Alloc((length + 1) * sizeof(char16_t));
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
|
|
@ -214,13 +191,20 @@ nsHtml5String::FromString(const nsAString& aString)
|
||||||
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
|
||||||
memcpy(data, aString.BeginReading(), length * sizeof(char16_t));
|
memcpy(data, aString.BeginReading(), length * sizeof(char16_t));
|
||||||
data[length] = 0;
|
data[length] = 0;
|
||||||
return nsHtml5String(buffer.forget(), length);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsHtml5String
|
nsHtml5String
|
||||||
nsHtml5String::EmptyString()
|
nsHtml5String::EmptyString()
|
||||||
{
|
{
|
||||||
return nsHtml5String(nullptr, 0U);
|
return nsHtml5String(eEmpty);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -6,24 +6,61 @@
|
||||||
#define nsHtml5String_h
|
#define nsHtml5String_h
|
||||||
|
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
|
#include "nsIAtom.h"
|
||||||
|
|
||||||
class nsHtml5TreeBuilder;
|
class nsHtml5TreeBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pass-by-value type that combines an unsafe `nsStringBuffer*` with its
|
* A pass-by-value type that can represent
|
||||||
* logical length (`uint32_t`). (`nsStringBuffer` knows its capacity but not
|
* * nullptr
|
||||||
* its logical length, i.e. how much of the capacity is in use.)
|
* * 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
|
* Holding or passing this type is as unsafe as holding or passing
|
||||||
* `nsStringBuffer*`.
|
* `nsStringBuffer*`/`nsIAtom*`.
|
||||||
*
|
|
||||||
* 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
|
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:
|
public:
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
|
|
@ -37,29 +74,50 @@ public:
|
||||||
* Constructor from nullptr.
|
* Constructor from nullptr.
|
||||||
*/
|
*/
|
||||||
inline MOZ_IMPLICIT nsHtml5String(decltype(nullptr))
|
inline MOZ_IMPLICIT nsHtml5String(decltype(nullptr))
|
||||||
: mBuffer(nullptr)
|
: mBits(eNull)
|
||||||
, mLength(UINT32_MAX)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* 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 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();
|
nsHtml5String Clone();
|
||||||
|
|
||||||
|
|
@ -73,23 +131,23 @@ public:
|
||||||
|
|
||||||
static nsHtml5String FromString(const nsAString& aString);
|
static nsHtml5String FromString(const nsAString& aString);
|
||||||
|
|
||||||
|
static nsHtml5String FromAtom(already_AddRefed<nsIAtom> aAtom);
|
||||||
|
|
||||||
static nsHtml5String EmptyString();
|
static nsHtml5String EmptyString();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* Constructor from raw parts.
|
|
||||||
*/
|
|
||||||
nsHtml5String(already_AddRefed<nsStringBuffer> 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
|
#endif // nsHtml5String_h
|
||||||
|
|
@ -225,7 +225,7 @@ nsHtml5Tokenizer::emitOrAppendCharRefBuf(int32_t returnState)
|
||||||
nsHtml5String
|
nsHtml5String
|
||||||
nsHtml5Tokenizer::strBufToString()
|
nsHtml5Tokenizer::strBufToString()
|
||||||
{
|
{
|
||||||
nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler);
|
nsHtml5String str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler, !newAttributesEachTime && attributeName == nsHtml5AttributeName::ATTR_CLASS);
|
||||||
clearStrBufAfterUse();
|
clearStrBufAfterUse();
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2181,7 +2181,7 @@ nsHtml5TreeBuilder::extractCharsetFromContent(nsHtml5String attributeValue, nsHt
|
||||||
if (end == -1) {
|
if (end == -1) {
|
||||||
end = buffer.length;
|
end = buffer.length;
|
||||||
}
|
}
|
||||||
charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb);
|
charset = nsHtml5Portability::newStringFromBuffer(buffer, start, end - start, tb, false);
|
||||||
}
|
}
|
||||||
return charset;
|
return charset;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -363,16 +363,21 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement,
|
||||||
{
|
{
|
||||||
int32_t len = aAttributes->getLength();
|
int32_t len = aAttributes->getLength();
|
||||||
for (int32_t i = 0; i < len; i++) {
|
for (int32_t i = 0; i < len; 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
|
// prefix doesn't need regetting. it is always null or a static atom
|
||||||
// local name is never null
|
// local name is never null
|
||||||
nsCOMPtr<nsIAtom> localName =
|
RefPtr<nsIAtom> localName =
|
||||||
Reget(aAttributes->getLocalNameNoBoundsCheck(i));
|
Reget(aAttributes->getLocalNameNoBoundsCheck(i));
|
||||||
nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
|
RefPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
|
||||||
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
||||||
|
|
||||||
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
||||||
aAttributes->getValueNoBoundsCheck(i).ToString(value);
|
val.ToString(value);
|
||||||
if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) {
|
if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) {
|
||||||
// This is an HTML5-incompliant Geckoism.
|
// This is an HTML5-incompliant Geckoism.
|
||||||
// Remove when fixing bug 582361
|
// Remove when fixing bug 582361
|
||||||
NS_ConvertUTF16toUTF8 cname(value);
|
NS_ConvertUTF16toUTF8 cname(value);
|
||||||
|
|
@ -391,6 +396,7 @@ nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsIContent*
|
nsIContent*
|
||||||
nsHtml5TreeOperation::CreateHTMLElement(
|
nsHtml5TreeOperation::CreateHTMLElement(
|
||||||
|
|
@ -580,6 +586,11 @@ nsHtml5TreeOperation::CreateSVGElement(
|
||||||
|
|
||||||
int32_t len = aAttributes->getLength();
|
int32_t len = aAttributes->getLength();
|
||||||
for (int32_t i = 0; i < len; i++) {
|
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
|
// prefix doesn't need regetting. it is always null or a static atom
|
||||||
// local name is never null
|
// local name is never null
|
||||||
nsCOMPtr<nsIAtom> localName =
|
nsCOMPtr<nsIAtom> localName =
|
||||||
|
|
@ -588,9 +599,10 @@ nsHtml5TreeOperation::CreateSVGElement(
|
||||||
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
||||||
|
|
||||||
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
||||||
aAttributes->getValueNoBoundsCheck(i).ToString(value);
|
val.ToString(value);
|
||||||
newContent->SetAttr(nsuri, localName, prefix, value, false);
|
newContent->SetAttr(nsuri, localName, prefix, value, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return newContent;
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,6 +630,11 @@ nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName,
|
||||||
|
|
||||||
int32_t len = aAttributes->getLength();
|
int32_t len = aAttributes->getLength();
|
||||||
for (int32_t i = 0; i < len; i++) {
|
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
|
// prefix doesn't need regetting. it is always null or a static atom
|
||||||
// local name is never null
|
// local name is never null
|
||||||
nsCOMPtr<nsIAtom> localName =
|
nsCOMPtr<nsIAtom> localName =
|
||||||
|
|
@ -626,9 +643,10 @@ nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName,
|
||||||
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
||||||
|
|
||||||
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
||||||
aAttributes->getValueNoBoundsCheck(i).ToString(value);
|
val.ToString(value);
|
||||||
newContent->SetAttr(nsuri, localName, prefix, value, false);
|
newContent->SetAttr(nsuri, localName, prefix, value, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return newContent;
|
return newContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue