/* -*- Mode: C++; tab-width: 8; 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 "mozilla/dom/HTMLAllCollection.h" #include "mozilla/dom/HTMLAllCollectionBinding.h" #include "mozilla/dom/Nullable.h" #include "mozilla/dom/Element.h" #include "nsHTMLDocument.h" namespace mozilla { namespace dom { HTMLAllCollection::HTMLAllCollection(nsHTMLDocument* aDocument) : mDocument(aDocument) { MOZ_ASSERT(mDocument); } HTMLAllCollection::~HTMLAllCollection() { } NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection, mDocument, mCollection, mNamedMap) NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection) NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END nsINode* HTMLAllCollection::GetParentObject() const { return mDocument; } uint32_t HTMLAllCollection::Length() { return Collection()->Length(true); } nsIContent* HTMLAllCollection::Item(uint32_t aIndex) { return Collection()->Item(aIndex); } nsContentList* HTMLAllCollection::Collection() { if (!mCollection) { nsIDocument* document = mDocument; mCollection = document->GetElementsByTagName(NS_LITERAL_STRING("*")); MOZ_ASSERT(mCollection); } return mCollection; } static bool IsAllNamedElement(nsIContent* aContent) { return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::applet, nsGkAtoms::button, nsGkAtoms::embed, nsGkAtoms::form, nsGkAtoms::iframe, nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::map, nsGkAtoms::meta, nsGkAtoms::object, nsGkAtoms::select, nsGkAtoms::textarea, nsGkAtoms::frame, nsGkAtoms::frameset); } static bool DocAllResultMatch(Element* aElement, int32_t aNamespaceID, nsIAtom* aAtom, void* aData) { if (aElement->GetID() == aAtom) { return true; } nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aElement); if (!elm) { return false; } if (!IsAllNamedElement(elm)) { return false; } const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name); return val && val->Type() == nsAttrValue::eAtom && val->GetAtomValue() == aAtom; } nsContentList* HTMLAllCollection::GetDocumentAllList(const nsAString& aID) { if (nsContentList* docAllList = mNamedMap.GetWeak(aID)) { return docAllList; } nsCOMPtr id = NS_Atomize(aID); RefPtr docAllList = new nsContentList(mDocument, DocAllResultMatch, nullptr, nullptr, true, id); mNamedMap.Put(aID, docAllList); return docAllList; } void HTMLAllCollection::NamedGetter(const nsAString& aID, bool& aFound, Nullable& aResult) { if (aID.IsEmpty()) { aFound = false; aResult.SetNull(); return; } nsContentList* docAllList = GetDocumentAllList(aID); if (!docAllList) { aFound = false; aResult.SetNull(); return; } // Check if there are more than 1 entries. Do this by getting the second one // rather than the length since getting the length always requires walking // the entire document. if (docAllList->Item(1, true)) { aFound = true; aResult.SetValue().SetAsHTMLCollection() = docAllList; return; } // There's only 0 or 1 items. Return the first one or null. if (nsIContent* node = docAllList->Item(0, true)) { aFound = true; aResult.SetValue().SetAsNode() = node; return; } aFound = false; aResult.SetNull(); } void HTMLAllCollection::GetSupportedNames(nsTArray& aNames) { Collection()->GetSupportedNames(aNames, IsAllNamedElement); } JSObject* HTMLAllCollection::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return HTMLAllCollectionBinding::Wrap(aCx, this, aGivenProto); } } // namespace dom } // namespace mozilla