Bug 629200 part 1 - Make MaybeCheckSameAttrVal work on parsed attributes instead of strings; r=bz

This commit is contained in:
Brian Birtles 2012-02-14 11:00:56 +09:00
Родитель 614777e8a0
Коммит c949bd39d5
8 изменённых файлов: 222 добавлений и 31 удалений

Просмотреть файл

@ -81,6 +81,7 @@ CPPSRCS = \
nsAtomListUtils.cpp \
nsAttrAndChildArray.cpp \
nsAttrValue.cpp \
nsAttrValueOrString.cpp \
nsCCUncollectableMarker.cpp \
nsChannelPolicy.cpp \
nsCommentNode.cpp \

Просмотреть файл

@ -314,6 +314,19 @@ nsAttrValue::SetTo(const nsIntMargin& aValue)
}
}
void
nsAttrValue::SetToSerialized(const nsAttrValue& aOther)
{
if (aOther.Type() != nsAttrValue::eString &&
aOther.Type() != nsAttrValue::eAtom) {
nsAutoString val;
aOther.ToString(val);
SetTo(val);
} else {
SetTo(aOther);
}
}
void
nsAttrValue::SwapValueWith(nsAttrValue& aOther)
{
@ -419,6 +432,29 @@ nsAttrValue::ToString(nsAString& aResult) const
}
}
already_AddRefed<nsIAtom>
nsAttrValue::GetAsAtom() const
{
switch (Type()) {
case eString:
return do_GetAtom(GetStringValue());
case eAtom:
{
nsIAtom* atom = GetAtomValue();
NS_ADDREF(atom);
return atom;
}
default:
{
nsAutoString val;
ToString(val);
return do_GetAtom(val);
}
}
}
const nsCheapString
nsAttrValue::GetStringValue() const
{
@ -751,6 +787,36 @@ nsAttrValue::Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
return aValue->Equals(val);
}
bool
nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const
{
if (Type() == aOther.Type()) {
return Equals(aOther);
}
// We need to serialize at least one nsAttrValue before passing to
// Equals(const nsAString&), but we can avoid unnecessarily serializing both
// by checking if one is already of a string type.
bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
const nsAttrValue& lhs = thisIsString ? *this : aOther;
const nsAttrValue& rhs = thisIsString ? aOther : *this;
switch (rhs.BaseType()) {
case eAtomBase:
return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
case eStringBase:
return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
default:
{
nsAutoString val;
rhs.ToString(val);
return lhs.Equals(val, eCaseMatters);
}
}
}
bool
nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
{

Просмотреть файл

@ -136,9 +136,23 @@ public:
void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
void SetTo(const nsIntMargin& aValue);
/**
* Sets this object with the string or atom representation of aValue.
*
* After calling this method, this object will have type eString unless the
* type of aValue is eAtom, in which case this object will also have type
* eAtom.
*/
void SetToSerialized(const nsAttrValue& aValue);
void SwapValueWith(nsAttrValue& aOther);
void ToString(nsAString& aResult) const;
/**
* Returns the value of this object as an atom. If necessary, the value will
* first be serialised using ToString before converting to an atom.
*/
already_AddRefed<nsIAtom> GetAsAtom() const;
// Methods to get value. These methods do not convert so only use them
// to retrieve the datatype that this nsAttrValue has.
@ -175,6 +189,15 @@ public:
bool Equals(const nsAString& aValue, nsCaseTreatment aCaseSensitive) const;
bool Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const;
/**
* Compares this object with aOther according to their string representation.
*
* For example, when called on an object with type eInteger and value 4, and
* given aOther of type eString and value "4", EqualsAsStrings will return
* true (while Equals will return false).
*/
bool EqualsAsStrings(const nsAttrValue& aOther) const;
/**
* Returns true if this AttrValue is equal to the given atom, or is an
* array which contains the given atom.

Просмотреть файл

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAttrValueOrString.h"
const nsAString&
nsAttrValueOrString::String() const
{
if (mStringPtr) {
return *mStringPtr;
}
if (mAttrValue->Type() == nsAttrValue::eString) {
mCheapString = mAttrValue->GetStringValue();
mStringPtr = &mCheapString;
return *mStringPtr;
}
mAttrValue->ToString(mCheapString);
mStringPtr = &mCheapString;
return *mStringPtr;
}

Просмотреть файл

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* A wrapper to contain either an nsAttrValue or an nsAString. This is useful
* because constructing an nsAttrValue from an nsAString can be expensive when
* the buffer of the string is not shared.
*
* Since a raw pointer to the passed-in string is kept, this class should only
* be used on the stack.
*/
#ifndef nsAttrValueOrString_h___
#define nsAttrValueOrString_h___
#include "nsString.h"
#include "nsAttrValue.h"
class NS_STACK_CLASS nsAttrValueOrString
{
public:
nsAttrValueOrString(const nsAString& aValue)
: mAttrValue(nsnull)
, mStringPtr(&aValue)
, mCheapString(nsnull)
{ }
nsAttrValueOrString(const nsAttrValue& aValue)
: mAttrValue(&aValue)
, mStringPtr(nsnull)
, mCheapString(nsnull)
{ }
/**
* Returns a reference to the string value of the contents of this object.
*
* When this object points to a string or an nsAttrValue of string or atom
* type this should be fairly cheap. Other nsAttrValue types will be
* serialized the first time this is called and cached from thereon.
*/
const nsAString& String() const;
/**
* Compares the string representation of this object with the string
* representation of an nsAttrValue.
*/
bool EqualsAsStrings(const nsAttrValue& aOther) const
{
if (mStringPtr) {
return aOther.Equals(*mStringPtr, eCaseMatters);
}
return aOther.EqualsAsStrings(*mAttrValue);
}
protected:
const nsAttrValue* mAttrValue;
mutable const nsAString* mStringPtr;
mutable nsCheapString mCheapString;
};
#endif // nsAttrValueOrString_h___

Просмотреть файл

@ -86,6 +86,7 @@
#include "nsMutationEvent.h"
#include "nsNodeUtils.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
#ifdef MOZ_XUL
#include "nsXULElement.h"
#endif /* MOZ_XUL */
@ -4979,10 +4980,14 @@ nsGenericElement::CopyInnerTo(nsGenericElement* aDst) const
}
bool
nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify, nsAutoString* aOldValue,
PRUint8* aModType, bool* aHasListeners)
nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify,
nsAttrValue& aOldValue,
PRUint8* aModType,
bool* aHasListeners)
{
bool modification = false;
*aHasListeners = aNotify &&
@ -5000,16 +5005,19 @@ nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID, nsIAtom* aName,
if (info.mValue) {
// Check whether the old value is the same as the new one. Note that we
// only need to actually _get_ the old value if we have listeners.
bool valueMatches;
if (*aHasListeners) {
// Need to store the old value
info.mValue->ToString(*aOldValue);
valueMatches = aValue.Equals(*aOldValue);
} else {
NS_ABORT_IF_FALSE(aNotify,
"Either hasListeners or aNotify should be true.");
valueMatches = info.mValue->Equals(aValue, eCaseMatters);
// Need to store the old value.
//
// If the current attribute value contains a pointer to some other data
// structure that gets updated in the process of setting the attribute
// we'll no longer have the old value of the attribute. Therefore, we
// should serialize the attribute value now to keep a snapshot.
//
// We have to serialize the value anyway in order to create the
// mutation event so there's no cost in doing it now.
aOldValue.SetToSerialized(*info.mValue);
}
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
return true;
}
@ -5039,10 +5047,11 @@ nsGenericElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
PRUint8 modType;
bool hasListeners;
nsAutoString oldValue;
nsAttrValueOrString value(aValue);
nsAttrValue oldValue;
if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
&oldValue, &modType, &hasListeners)) {
if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners)) {
return NS_OK;
}
@ -5082,19 +5091,18 @@ nsGenericElement::SetParsedAttr(PRInt32 aNamespaceID, nsIAtom* aName,
return NS_ERROR_FAILURE;
}
nsAutoString value;
aParsedValue.ToString(value);
PRUint8 modType;
bool hasListeners;
nsAutoString oldValue;
nsAttrValueOrString value(aParsedValue);
nsAttrValue oldValue;
if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, value, aNotify,
&oldValue, &modType, &hasListeners)) {
oldValue, &modType, &hasListeners)) {
return NS_OK;
}
nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value.String(), aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNotify) {
@ -5103,14 +5111,14 @@ nsGenericElement::SetParsedAttr(PRInt32 aNamespaceID, nsIAtom* aName,
return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
aParsedValue, modType, hasListeners, aNotify,
&value);
&value.String());
}
nsresult
nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
const nsAString& aOldValue,
const nsAttrValue& aOldValue,
nsAttrValue& aParsedValue,
PRUint8 aModType,
bool aFireMutation,
@ -5182,8 +5190,8 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
if (!newValue.IsEmpty()) {
mutation.mNewAttrValue = do_GetAtom(newValue);
}
if (!aOldValue.IsEmpty()) {
mutation.mPrevAttrValue = do_GetAtom(aOldValue);
if (!aOldValue.IsEmptyString()) {
mutation.mPrevAttrValue = aOldValue.GetAsAtom();
}
mutation.mAttrChange = aModType;

Просмотреть файл

@ -79,6 +79,7 @@ class nsINodeInfo;
class nsIControllers;
class nsEventListenerManager;
class nsIScrollableFrame;
class nsAttrValueOrString;
class nsContentList;
class nsDOMTokenList;
struct nsRect;
@ -289,16 +290,18 @@ public:
* have mutation listeners (in which case it's cheap to just return false
* and let the caller go ahead and set the value).
* @param aOldValue Set to the old value of the attribute, but only if there
* are event listeners
* are event listeners. If set, the type of aOldValue will be either
* nsAttrValue::eString or nsAttrValue::eAtom.
* @param aModType Set to nsIDOMMutationEvent::MODIFICATION or to
* nsIDOMMutationEvent::ADDITION, but only if this helper returns true
* @param aHasListeners Set to true if there are mutation event listeners
* listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED
*/
bool MaybeCheckSameAttrVal(PRInt32 aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify, nsAutoString* aOldValue,
PRUint8* aModType, bool* aHasListeners);
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify, nsAttrValue& aOldValue,
PRUint8* aModType, bool* aHasListeners);
virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
const nsAString& aValue, bool aNotify);
virtual nsresult SetParsedAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
@ -656,7 +659,7 @@ protected:
nsresult SetAttrAndNotify(PRInt32 aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
const nsAString& aOldValue,
const nsAttrValue& aOldValue,
nsAttrValue& aParsedValue,
PRUint8 aModType,
bool aFireMutation,

Просмотреть файл

@ -167,7 +167,7 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aSty
{
SetMayHaveStyle();
bool modification = false;
nsAutoString oldValueStr;
nsAttrValue oldValue;
bool hasListeners = aNotify &&
nsContentUtils::HasMutationListeners(this,
@ -182,8 +182,12 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aSty
// save the old attribute so we can set up the mutation event properly
// XXXbz if the old rule points to the same declaration as the new one,
// this is getting the new attr value, not the old one....
nsAutoString oldValueStr;
modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style,
oldValueStr);
if (modification) {
oldValue.SetTo(oldValueStr);
}
}
else if (aNotify && IsInDoc()) {
modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style);
@ -197,7 +201,7 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aSty
static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nsnull,
oldValueStr, attrValue, modType, hasListeners,
oldValue, attrValue, modType, hasListeners,
aNotify, nsnull);
}