From e5c93de89fac03f8db286cfc91b02c0bcafd1007 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Tue, 11 Sep 2018 19:23:49 -0700 Subject: [PATCH] closes #519: Element.toggleAttribute() + backbugs M1258205 M1276938 --- dom/base/Element.cpp | 84 ++++++++++++++++++++++++------- dom/base/Element.h | 12 +++-- dom/html/nsGenericHTMLElement.cpp | 12 ----- dom/html/nsGenericHTMLElement.h | 2 - dom/webidl/Element.webidl | 2 + 5 files changed, 76 insertions(+), 36 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 134952cec..2056fe7e9 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1196,28 +1196,56 @@ Element::GetAttribute(const nsAString& aName, DOMString& aReturn) } } +bool +Element::ToggleAttribute(const nsAString& aName, + const Optional& 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 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 Element::SetAttribute(const nsAString& aName, const nsAString& aValue, 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) { - aError = nsContentUtils::CheckQName(aName, false); - if (aError.Failed()) { - return; - } - - nsCOMPtr nameAtom; - if (IsHTMLElement() && IsInHTMLDocument()) { - nsAutoString lower; - nsContentUtils::ASCIIToLower(aName, lower); - nameAtom = NS_AtomizeMainThread(lower); - } - else { - nameAtom = NS_AtomizeMainThread(aName); - } + nsCOMPtr nameAtom = NS_AtomizeMainThread(nameToUse); if (!nameAtom) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return; @@ -1234,7 +1262,7 @@ Element::SetAttribute(const nsAString& aName, void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) { - const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); + const nsAttrName* name = InternalGetAttrNameFromQName(aName); if (!name) { // If there is no canonical nsAttrName for this attribute name, then the @@ -2017,7 +2045,7 @@ Element::FindAttributeDependence(const nsIAtom* aAttribute, already_AddRefed Element::GetExistingAttrNameFromQName(const nsAString& aStr) const { - const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr); + const nsAttrName* name = InternalGetAttrNameFromQName(aStr); if (!name) { return nullptr; } @@ -2193,9 +2221,27 @@ Element::SetEventHandler(nsIAtom* aEventName, //---------------------------------------------------------------------- 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 diff --git a/dom/base/Element.h b/dom/base/Element.h index 1433c3b85..03a29ec7c 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -675,6 +675,8 @@ public: void GetAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, nsAString& aReturn); + bool ToggleAttribute(const nsAString& aName, const Optional& aForce, + ErrorResult& aError); void SetAttribute(const nsAString& aName, const nsAString& aValue, ErrorResult& aError); void SetAttributeNS(const nsAString& aNamespaceURI, @@ -688,7 +690,7 @@ public: ErrorResult& aError); bool HasAttribute(const nsAString& aName) const { - return InternalGetExistingAttrNameFromQName(aName) != nullptr; + return InternalGetAttrNameFromQName(aName) != nullptr; } bool HasAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) const; @@ -1281,9 +1283,13 @@ protected: 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(); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index b2243723b..5a58c7324 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2857,18 +2857,6 @@ nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation, 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 nsGenericHTMLElement::GetEditor(nsIEditor** aEditor) { diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 7440b825b..875e88aa5 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -1025,8 +1025,6 @@ protected: GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer) override; - virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const override; - /** * Create a URI for the given aURISpec string. * Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index a456aeaaa..7ee627dc0 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -42,6 +42,8 @@ interface Element : Node { [Pure] DOMString? getAttributeNS(DOMString? namespace, DOMString localName); [Throws] + boolean toggleAttribute(DOMString name, optional boolean force); + [Throws] void setAttribute(DOMString name, DOMString value); [Throws] void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);