closes #519: Element.toggleAttribute() + backbugs M1258205 M1276938

This commit is contained in:
Cameron Kaiser 2018-09-11 19:23:49 -07:00
parent 2c14dc434a
commit e5c93de89f
5 changed files with 76 additions and 36 deletions

View File

@ -1196,28 +1196,56 @@ Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
} }
} }
bool
Element::ToggleAttribute(const nsAString& aName,
const Optional<bool>& aForce,
ErrorResult& aError)
{
aError = nsContentUtils::CheckQName(aName, false);
if (aError.Failed()) {
return false;
}
nsAutoString nameToUse;
const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
if (!name) {
if (aForce.WasPassed() && !aForce.Value()) {
return false;
}
nsCOMPtr<nsIAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
if (!nameAtom) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
}
aError = SetAttr(kNameSpaceID_None, nameAtom, EmptyString(), true);
return true;
}
if (aForce.WasPassed() && aForce.Value()) {
return true;
}
// Hold a strong reference here so that the atom or nodeinfo doesn't go
// away during UnsetAttr. If it did UnsetAttr would be left with a
// dangling pointer as argument without knowing it.
nsAttrName tmp(*name);
aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
return false;
}
void void
Element::SetAttribute(const nsAString& aName, Element::SetAttribute(const nsAString& aName,
const nsAString& aValue, const nsAString& aValue,
ErrorResult& aError) ErrorResult& aError)
{ {
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); aError = nsContentUtils::CheckQName(aName, false);
if (aError.Failed()) {
return;
}
nsAutoString nameToUse;
const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
if (!name) { if (!name) {
aError = nsContentUtils::CheckQName(aName, false); nsCOMPtr<nsIAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
if (aError.Failed()) {
return;
}
nsCOMPtr<nsIAtom> nameAtom;
if (IsHTMLElement() && IsInHTMLDocument()) {
nsAutoString lower;
nsContentUtils::ASCIIToLower(aName, lower);
nameAtom = NS_AtomizeMainThread(lower);
}
else {
nameAtom = NS_AtomizeMainThread(aName);
}
if (!nameAtom) { if (!nameAtom) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY); aError.Throw(NS_ERROR_OUT_OF_MEMORY);
return; return;
@ -1234,7 +1262,7 @@ Element::SetAttribute(const nsAString& aName,
void void
Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError)
{ {
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); const nsAttrName* name = InternalGetAttrNameFromQName(aName);
if (!name) { if (!name) {
// If there is no canonical nsAttrName for this attribute name, then the // If there is no canonical nsAttrName for this attribute name, then the
@ -2017,7 +2045,7 @@ Element::FindAttributeDependence(const nsIAtom* aAttribute,
already_AddRefed<mozilla::dom::NodeInfo> already_AddRefed<mozilla::dom::NodeInfo>
Element::GetExistingAttrNameFromQName(const nsAString& aStr) const Element::GetExistingAttrNameFromQName(const nsAString& aStr) const
{ {
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr); const nsAttrName* name = InternalGetAttrNameFromQName(aStr);
if (!name) { if (!name) {
return nullptr; return nullptr;
} }
@ -2193,9 +2221,27 @@ Element::SetEventHandler(nsIAtom* aEventName,
//---------------------------------------------------------------------- //----------------------------------------------------------------------
const nsAttrName* const nsAttrName*
Element::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const Element::InternalGetAttrNameFromQName(const nsAString& aStr,
nsAutoString* aNameToUse) const
{ {
return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr); MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty());
const nsAttrName* val = nullptr;
if (IsHTMLElement() && IsInHTMLDocument()) {
nsAutoString lower;
nsAutoString& outStr = aNameToUse ? *aNameToUse : lower;
nsContentUtils::ASCIIToLower(aStr, outStr);
val = mAttrsAndChildren.GetExistingAttrNameFromQName(outStr);
if (val) {
outStr.Truncate();
}
} else {
val = mAttrsAndChildren.GetExistingAttrNameFromQName(aStr);
if (!val && aNameToUse) {
*aNameToUse = aStr;
}
}
return val;
} }
bool bool

View File

@ -675,6 +675,8 @@ public:
void GetAttributeNS(const nsAString& aNamespaceURI, void GetAttributeNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName, const nsAString& aLocalName,
nsAString& aReturn); nsAString& aReturn);
bool ToggleAttribute(const nsAString& aName, const Optional<bool>& aForce,
ErrorResult& aError);
void SetAttribute(const nsAString& aName, const nsAString& aValue, void SetAttribute(const nsAString& aName, const nsAString& aValue,
ErrorResult& aError); ErrorResult& aError);
void SetAttributeNS(const nsAString& aNamespaceURI, void SetAttributeNS(const nsAString& aNamespaceURI,
@ -688,7 +690,7 @@ public:
ErrorResult& aError); ErrorResult& aError);
bool HasAttribute(const nsAString& aName) const bool HasAttribute(const nsAString& aName) const
{ {
return InternalGetExistingAttrNameFromQName(aName) != nullptr; return InternalGetAttrNameFromQName(aName) != nullptr;
} }
bool HasAttributeNS(const nsAString& aNamespaceURI, bool HasAttributeNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName) const; const nsAString& aLocalName) const;
@ -1281,9 +1283,13 @@ protected:
GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer); GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer);
/** /**
* Internal hook for converting an attribute name-string to an atomized name * Internal hook for converting an attribute name-string to nsAttrName in
* case there is such existing attribute. aNameToUse can be passed to get
* name which was used for looking for the attribute (lowercase in HTML).
*/ */
virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const; const nsAttrName*
InternalGetAttrNameFromQName(const nsAString& aStr,
nsAutoString* aNameToUse = nullptr) const;
nsIFrame* GetStyledFrame(); nsIFrame* GetStyledFrame();

View File

@ -2857,18 +2857,6 @@ nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation,
return focused; return focused;
} }
const nsAttrName*
nsGenericHTMLElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
{
if (IsInHTMLDocument()) {
nsAutoString lower;
nsContentUtils::ASCIIToLower(aStr, lower);
return mAttrsAndChildren.GetExistingAttrNameFromQName(lower);
}
return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr);
}
nsresult nsresult
nsGenericHTMLElement::GetEditor(nsIEditor** aEditor) nsGenericHTMLElement::GetEditor(nsIEditor** aEditor)
{ {

View File

@ -1025,8 +1025,6 @@ protected:
GetEventListenerManagerForAttr(nsIAtom* aAttrName, GetEventListenerManagerForAttr(nsIAtom* aAttrName,
bool* aDefer) override; bool* aDefer) override;
virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const override;
/** /**
* Create a URI for the given aURISpec string. * Create a URI for the given aURISpec string.
* Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty * Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty

View File

@ -42,6 +42,8 @@ interface Element : Node {
[Pure] [Pure]
DOMString? getAttributeNS(DOMString? namespace, DOMString localName); DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
[Throws] [Throws]
boolean toggleAttribute(DOMString name, optional boolean force);
[Throws]
void setAttribute(DOMString name, DOMString value); void setAttribute(DOMString name, DOMString value);
[Throws] [Throws]
void setAttributeNS(DOMString? namespace, DOMString name, DOMString value); void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);