Bug 1475342 - Move document.getElementsByAttribute[NS] to ParentNode so it'll work for HTML document and elements;r=bz draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Thu, 26 Jul 2018 13:40:13 -0700
changeset 823218 75d823c688cba8d84dc19705e83284be383962f2
parent 822981 4e6486b672b32aba075b704c6b1e41e8ccf7a135
push id117613
push userbgrinstead@mozilla.com
push dateThu, 26 Jul 2018 20:40:30 +0000
reviewersbz
bugs1475342
milestone63.0a1
Bug 1475342 - Move document.getElementsByAttribute[NS] to ParentNode so it'll work for HTML document and elements;r=bz It's currently only accessible on XULDocument and XULElement, but that makes porting existing JS to run in an HTML document inconvenient. We could alternatively change calling JS, but this can be easily moved and exposed in chrome contexts. MozReview-Commit-ID: JitYET20NSE
accessible/generic/Accessible.cpp
devtools/shared/webconsole/test/test_bug819670_getter_throws.html
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/webidl/ParentNode.webidl
dom/webidl/XULDocument.webidl
dom/webidl/XULElement.webidl
dom/xul/XULDocument.cpp
dom/xul/XULDocument.h
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1783,17 +1783,17 @@ Accessible::RelationByType(RelationType 
           }
         }
       } else {
         // In XUL, use first <button default="true" .../> in the document
         nsIDocument* doc = mContent->OwnerDoc();
         nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
         if (doc->IsXULDocument()) {
           dom::XULDocument* xulDoc = doc->AsXULDocument();
-          nsCOMPtr<nsINodeList> possibleDefaultButtons =
+          nsCOMPtr<nsIHTMLCollection> possibleDefaultButtons =
             xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
                                            NS_LITERAL_STRING("true"));
           if (possibleDefaultButtons) {
             uint32_t length = possibleDefaultButtons->Length();
             // Check for button in list of default="true" elements
             for (uint32_t count = 0; count < length && !buttonEl; count ++) {
               buttonEl = do_QueryInterface(possibleDefaultButtons->Item(count));
             }
--- a/devtools/shared/webconsole/test/test_bug819670_getter_throws.html
+++ b/devtools/shared/webconsole/test/test_bug819670_getter_throws.html
@@ -48,17 +48,16 @@ function onEvaluate(aState, aResponse)
 function onInspect(aState, aResponse)
 {
   ok(!aResponse.error, "no response error");
 
   let expectedProps = {
     "addBroadcastListenerFor": { value: { type: "object" } },
     "commandDispatcher": { get: { type: "object" } },
     "getBoxObjectFor": { value: { type: "object" } },
-    "getElementsByAttribute": { value: { type: "object" } },
   };
 
   let props = aResponse.ownProperties;
   ok(props, "response properties available");
 
   if (props) {
     ok(Object.keys(props).length > Object.keys(expectedProps).length,
        "number of enumerable properties");
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1654,16 +1654,103 @@ nsINode::GetLastElementChild() const
     if (child->IsElement()) {
       return child->AsElement();
     }
   }
 
   return nullptr;
 }
 
+static
+bool MatchAttribute(Element* aElement,
+                    int32_t aNamespaceID,
+                    nsAtom* aAttrName,
+                    void* aData)
+{
+  MOZ_ASSERT(aElement, "Must have content node to work with!");
+  nsString* attrValue = static_cast<nsString*>(aData);
+  if (aNamespaceID != kNameSpaceID_Unknown &&
+      aNamespaceID != kNameSpaceID_Wildcard) {
+    return attrValue->EqualsLiteral("*") ?
+      aElement->HasAttr(aNamespaceID, aAttrName) :
+      aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
+                            eCaseMatters);
+  }
+
+  // Qualified name match. This takes more work.
+  uint32_t count = aElement->GetAttrCount();
+  for (uint32_t i = 0; i < count; ++i) {
+    const nsAttrName* name = aElement->GetAttrNameAt(i);
+    bool nameMatch;
+    if (name->IsAtom()) {
+      nameMatch = name->Atom() == aAttrName;
+    } else if (aNamespaceID == kNameSpaceID_Wildcard) {
+      nameMatch = name->NodeInfo()->Equals(aAttrName);
+    } else {
+      nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
+    }
+
+    if (nameMatch) {
+      return attrValue->EqualsLiteral("*") ||
+        aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
+                              *attrValue, eCaseMatters);
+    }
+  }
+
+  return false;
+}
+
+already_AddRefed<nsIHTMLCollection>
+nsINode::GetElementsByAttribute(const nsAString& aAttribute,
+                                const nsAString& aValue)
+{
+  RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
+  nsAutoPtr<nsString> attrValue(new nsString(aValue));
+  RefPtr<nsContentList> list = new nsContentList(this,
+                                          MatchAttribute,
+                                          nsContentUtils::DestroyMatchString,
+                                          attrValue.forget(),
+                                          true,
+                                          attrAtom,
+                                          kNameSpaceID_Unknown);
+
+  return list.forget();
+}
+
+already_AddRefed<nsIHTMLCollection>
+nsINode::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
+                                  const nsAString& aAttribute,
+                                  const nsAString& aValue,
+                                  ErrorResult& aRv)
+{
+  RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
+  nsAutoPtr<nsString> attrValue(new nsString(aValue));
+
+  int32_t nameSpaceId = kNameSpaceID_Wildcard;
+  if (!aNamespaceURI.EqualsLiteral("*")) {
+    nsresult rv =
+      nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
+                                                            nameSpaceId);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return nullptr;
+    }
+  }
+
+  RefPtr<nsContentList> list = new nsContentList(this,
+                                          MatchAttribute,
+                                          nsContentUtils::DestroyMatchString,
+                                          attrValue.forget(),
+                                          true,
+                                          attrAtom,
+                                          nameSpaceId);
+  return list.forget();
+}
+
+
 void
 nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes,
                  ErrorResult& aRv)
 {
   nsCOMPtr<nsIDocument> doc = OwnerDoc();
   nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   if (aRv.Failed()) {
     return;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -37,16 +37,17 @@
 
 class nsAttrAndChildArray;
 class nsAttrChildContentList;
 class nsDOMAttributeMap;
 class nsIAnimationObserver;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
+class nsIHTMLCollection;
 class nsIMutationObserver;
 class nsINode;
 class nsINodeList;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
 class nsDOMMutationObserver;
@@ -1848,16 +1849,26 @@ public:
    * Remove this node from its parent, if any.
    */
   void Remove();
 
   // ParentNode methods
   mozilla::dom::Element* GetFirstElementChild() const;
   mozilla::dom::Element* GetLastElementChild() const;
 
+  already_AddRefed<nsIHTMLCollection>
+    GetElementsByAttribute(const nsAString& aAttribute,
+                           const nsAString& aValue);
+  already_AddRefed<nsIHTMLCollection>
+    GetElementsByAttributeNS(const nsAString& aNamespaceURI,
+                             const nsAString& aAttribute,
+                             const nsAString& aValue,
+                             ErrorResult& aRv);
+
+
   MOZ_CAN_RUN_SCRIPT void Prepend(const Sequence<OwningNodeOrString>& aNodes,
                                   ErrorResult& aRv);
   MOZ_CAN_RUN_SCRIPT void Append(const Sequence<OwningNodeOrString>& aNodes,
                                  ErrorResult& aRv);
 
   void GetBoxQuads(const BoxQuadOptions& aOptions,
                    nsTArray<RefPtr<DOMQuad> >& aResult,
                    CallerType aCallerType,
--- a/dom/webidl/ParentNode.webidl
+++ b/dom/webidl/ParentNode.webidl
@@ -13,13 +13,20 @@ interface ParentNode {
   readonly attribute HTMLCollection children;
   [Pure]
   readonly attribute Element? firstElementChild;
   [Pure]
   readonly attribute Element? lastElementChild;
   [Pure]
   readonly attribute unsigned long childElementCount;
 
+  [Func="IsChromeOrXBL"]
+  HTMLCollection getElementsByAttribute(DOMString name,
+                                        [TreatNullAs=EmptyString] DOMString value);
+  [Throws, Func="IsChromeOrXBL"]
+  HTMLCollection getElementsByAttributeNS(DOMString? namespaceURI, DOMString name,
+                                          [TreatNullAs=EmptyString] DOMString value);
+
   [CEReactions, Throws, Unscopable]
   void prepend((Node or DOMString)... nodes);
   [CEReactions, Throws, Unscopable]
   void append((Node or DOMString)... nodes);
 };
--- a/dom/webidl/XULDocument.webidl
+++ b/dom/webidl/XULDocument.webidl
@@ -21,22 +21,16 @@ interface XULDocument : Document {
   readonly attribute Node? popupRangeParent;
   [Throws, ChromeOnly]
   readonly attribute long  popupRangeOffset;
 
            attribute Node? tooltipNode;
 
   readonly attribute XULCommandDispatcher? commandDispatcher;
 
-  NodeList getElementsByAttribute(DOMString name,
-                                  [TreatNullAs=EmptyString] DOMString value);
-  [Throws]
-  NodeList getElementsByAttributeNS(DOMString? namespaceURI, DOMString name,
-                                    [TreatNullAs=EmptyString] DOMString value);
-
   [Throws]
   void addBroadcastListenerFor(Element broadcaster, Element observer,
                                DOMString attr);
   void removeBroadcastListenerFor(Element broadcaster, Element observer,
                                   DOMString attr);
 
   [Throws]
   BoxObject? getBoxObjectFor(Element? element);
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -78,23 +78,15 @@ interface XULElement : Element {
   [Throws]
   void                      focus();
   [Throws]
   void                      blur();
   [NeedsCallerType]
   void                      click();
   void                      doCommand();
 
-  // XXXbz this isn't really a nodelist!  See bug 818548
-  NodeList            getElementsByAttribute(DOMString name,
-                                             DOMString value);
-  // XXXbz this isn't really a nodelist!  See bug 818548
-  [Throws]
-  NodeList            getElementsByAttributeNS(DOMString namespaceURI,
-                                               DOMString name,
-                                               DOMString value);
   [Constant]
   readonly attribute CSSStyleDeclaration style;
 };
 
 XULElement implements GlobalEventHandlers;
 XULElement implements TouchEventHandlers;
 XULElement implements OnErrorEventHandlerForNodes;
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -946,62 +946,16 @@ XULDocument::ResolveForwardReferences()
     return NS_OK;
 }
 
 //----------------------------------------------------------------------
 //
 // nsIDocument interface
 //
 
-already_AddRefed<nsINodeList>
-XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
-                                    const nsAString& aValue)
-{
-    RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-    nsAutoPtr<nsString> attrValue(new nsString(aValue));
-    RefPtr<nsContentList> list = new nsContentList(this,
-                                            MatchAttribute,
-                                            nsContentUtils::DestroyMatchString,
-                                            attrValue.forget(),
-                                            true,
-                                            attrAtom,
-                                            kNameSpaceID_Unknown);
-
-    return list.forget();
-}
-
-already_AddRefed<nsINodeList>
-XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
-                                      const nsAString& aAttribute,
-                                      const nsAString& aValue,
-                                      ErrorResult& aRv)
-{
-    RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-    nsAutoPtr<nsString> attrValue(new nsString(aValue));
-
-    int32_t nameSpaceId = kNameSpaceID_Wildcard;
-    if (!aNamespaceURI.EqualsLiteral("*")) {
-      nsresult rv =
-        nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
-                                                              nameSpaceId);
-      if (NS_FAILED(rv)) {
-          aRv.Throw(rv);
-          return nullptr;
-      }
-    }
-
-    RefPtr<nsContentList> list = new nsContentList(this,
-                                            MatchAttribute,
-                                            nsContentUtils::DestroyMatchString,
-                                            attrValue.forget(),
-                                            true,
-                                            attrAtom,
-                                            nameSpaceId);
-    return list.forget();
-}
 
 void
 XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
                      nsAtom* aAttribute)
 {
     // For non-chrome documents, persistance is simply broken
     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
         return;
@@ -1365,57 +1319,16 @@ XULDocument::StartLayout(void)
 
         nsresult rv = shell->Initialize();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
 }
 
-/* static */
-bool
-XULDocument::MatchAttribute(Element* aElement,
-                            int32_t aNamespaceID,
-                            nsAtom* aAttrName,
-                            void* aData)
-{
-    MOZ_ASSERT(aElement, "Must have content node to work with!");
-    nsString* attrValue = static_cast<nsString*>(aData);
-    if (aNamespaceID != kNameSpaceID_Unknown &&
-        aNamespaceID != kNameSpaceID_Wildcard) {
-        return attrValue->EqualsLiteral("*") ?
-            aElement->HasAttr(aNamespaceID, aAttrName) :
-            aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
-                                  eCaseMatters);
-    }
-
-    // Qualified name match. This takes more work.
-
-    uint32_t count = aElement->GetAttrCount();
-    for (uint32_t i = 0; i < count; ++i) {
-        const nsAttrName* name = aElement->GetAttrNameAt(i);
-        bool nameMatch;
-        if (name->IsAtom()) {
-            nameMatch = name->Atom() == aAttrName;
-        } else if (aNamespaceID == kNameSpaceID_Wildcard) {
-            nameMatch = name->NodeInfo()->Equals(aAttrName);
-        } else {
-            nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
-        }
-
-        if (nameMatch) {
-            return attrValue->EqualsLiteral("*") ||
-                aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
-                                      *attrValue, eCaseMatters);
-        }
-    }
-
-    return false;
-}
-
 nsresult
 XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
                                     nsIPrincipal* aDocumentPrincipal,
                                     nsIParser** aResult)
 {
     nsresult rv;
 
     // Create a new prototype document.
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -127,45 +127,31 @@ public:
 
     virtual nsIDocument::DocumentTheme GetDocumentLWTheme() override;
     virtual nsIDocument::DocumentTheme ThreadSafeGetDocumentLWTheme() const override;
 
     void ResetDocumentLWTheme() { mDocLWTheme = Doc_Theme_Uninitialized; }
 
     NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override;
 
-    static bool
-    MatchAttribute(Element* aContent,
-                   int32_t aNameSpaceID,
-                   nsAtom* aAttrName,
-                   void* aData);
-
     NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument)
 
     void TraceProtos(JSTracer* aTrc);
 
     // WebIDL API
     already_AddRefed<nsINode> GetPopupNode();
     void SetPopupNode(nsINode* aNode);
     nsINode* GetPopupRangeParent(ErrorResult& aRv);
     int32_t GetPopupRangeOffset(ErrorResult& aRv);
     already_AddRefed<nsINode> GetTooltipNode();
     void SetTooltipNode(nsINode* aNode) { /* do nothing */ }
     nsIDOMXULCommandDispatcher* GetCommandDispatcher() const
     {
         return mCommandDispatcher;
     }
-    already_AddRefed<nsINodeList>
-      GetElementsByAttribute(const nsAString& aAttribute,
-                             const nsAString& aValue);
-    already_AddRefed<nsINodeList>
-      GetElementsByAttributeNS(const nsAString& aNamespaceURI,
-                               const nsAString& aAttribute,
-                               const nsAString& aValue,
-                               ErrorResult& aRv);
     void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
                                  const nsAString& aAttr, ErrorResult& aRv);
     void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
                                     const nsAString& aAttr);
     using nsDocument::GetBoxObjectFor;
 
 protected:
     virtual ~XULDocument();
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -368,64 +368,16 @@ nsXULElement::Clone(mozilla::dom::NodeIn
     }
 
     element.forget(aResult);
     return rv;
 }
 
 //----------------------------------------------------------------------
 
-already_AddRefed<nsINodeList>
-nsXULElement::GetElementsByAttribute(const nsAString& aAttribute,
-                                     const nsAString& aValue)
-{
-    RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-    void* attrValue = new nsString(aValue);
-    RefPtr<nsContentList> list =
-        new nsContentList(this,
-                          XULDocument::MatchAttribute,
-                          nsContentUtils::DestroyMatchString,
-                          attrValue,
-                          true,
-                          attrAtom,
-                          kNameSpaceID_Unknown);
-    return list.forget();
-}
-
-already_AddRefed<nsINodeList>
-nsXULElement::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
-                                       const nsAString& aAttribute,
-                                       const nsAString& aValue,
-                                       ErrorResult& rv)
-{
-    RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-
-    int32_t nameSpaceId = kNameSpaceID_Wildcard;
-    if (!aNamespaceURI.EqualsLiteral("*")) {
-      rv =
-        nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
-                                                              nameSpaceId);
-      if (rv.Failed()) {
-          return nullptr;
-      }
-    }
-
-    void* attrValue = new nsString(aValue);
-    RefPtr<nsContentList> list =
-        new nsContentList(this,
-                          XULDocument::MatchAttribute,
-                          nsContentUtils::DestroyMatchString,
-                          attrValue,
-                          true,
-                          attrAtom,
-                          nameSpaceId);
-
-    return list.forget();
-}
-
 EventListenerManager*
 nsXULElement::GetEventListenerManagerForAttr(nsAtom* aAttrName, bool* aDefer)
 {
     // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
     // here, override BindToTree for those classes and munge event
     // listeners there?
     nsIDocument* doc = OwnerDoc();
 
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -612,24 +612,16 @@ public:
         SetXULBoolAttr(nsGkAtoms::allowevents, aAllowEvents);
     }
     nsIControllers* GetControllers(mozilla::ErrorResult& rv);
     // Note: this can only fail if the do_CreateInstance for the boxobject
     // contact fails for some reason.
     already_AddRefed<mozilla::dom::BoxObject> GetBoxObject(mozilla::ErrorResult& rv);
     void Click(mozilla::dom::CallerType aCallerType);
     void DoCommand();
-    already_AddRefed<nsINodeList>
-      GetElementsByAttribute(const nsAString& aAttribute,
-                             const nsAString& aValue);
-    already_AddRefed<nsINodeList>
-      GetElementsByAttributeNS(const nsAString& aNamespaceURI,
-                               const nsAString& aAttribute,
-                               const nsAString& aValue,
-                               mozilla::ErrorResult& rv);
     // Style() inherited from nsStyledElement
 
     nsINode* GetScopeChainParent() const override
     {
         // For XUL, the parent is the parent element, if any
         Element* parent = GetParentElement();
         return parent ? parent : nsStyledElement::GetScopeChainParent();
     }