#ifndef HTMLCollection_h
#define HTMLCollection_h
#include "core/dom/LiveNodeListBase.h"
#include "core/html/CollectionIndexCache.h"
#include "core/html/CollectionType.h"
#include "wtf/Forward.h"
#include "wtf/HashMap.h"
#include "wtf/Vector.h"
namespace WebCore {
class HTMLCollection : public ScriptWrappable, public RefCounted<HTMLCollection>, public LiveNodeListBase {
public:
enum ItemAfterOverrideType {
OverridesItemAfter,
DoesNotOverrideItemAfter,
};
static PassRefPtr<HTMLCollection> create(ContainerNode& base, CollectionType);
virtual ~HTMLCollection();
virtual void invalidateCache(Document* oldDocument = 0) const OVERRIDE;
unsigned length() const { return m_collectionIndexCache.nodeCount(*this); }
Element* item(unsigned offset) const { return m_collectionIndexCache.nodeAt(*this, offset); }
virtual Element* namedItem(const AtomicString& name) const;
bool namedPropertyQuery(const AtomicString&, ExceptionState&);
void namedPropertyEnumerator(Vector<String>& names, ExceptionState&);
void namedItems(const AtomicString& name, Vector<RefPtr<Element> >&) const;
bool isEmpty() const { return m_collectionIndexCache.isEmpty(*this); }
bool hasExactlyOneItem() const { return m_collectionIndexCache.hasExactlyOneNode(*this); }
bool canTraverseBackward() const { return !overridesItemAfter(); }
Element* itemBefore(const Element* previousItem) const;
Element* traverseToFirstElement() const;
Element* traverseForwardToOffset(unsigned offset, Element& currentElement, unsigned& currentOffset) const;
protected:
HTMLCollection(ContainerNode& base, CollectionType, ItemAfterOverrideType);
class NamedItemCache {
public:
Vector<Element*>* getElementsById(const AtomicString& id) const { return m_idCache.get(id.impl()); }
Vector<Element*>* getElementsByName(const AtomicString& name) const { return m_nameCache.get(name.impl()); }
void addElementWithId(const AtomicString& id, Element* element) { addElementToMap(m_idCache, id, element); }
void addElementWithName(const AtomicString& name, Element* element) { addElementToMap(m_nameCache, name, element); }
private:
typedef HashMap<StringImpl*, OwnPtr<Vector<Element*> > > StringToElementsMap;
static void addElementToMap(StringToElementsMap& map, const AtomicString& key, Element* element)
{
OwnPtr<Vector<Element*> >& vector = map.add(key.impl(), nullptr).storedValue->value;
if (!vector)
vector = adoptPtr(new Vector<Element*>);
vector->append(element);
}
StringToElementsMap m_idCache;
StringToElementsMap m_nameCache;
};
bool overridesItemAfter() const { return m_overridesItemAfter; }
virtual Element* virtualItemAfter(Element*) const;
bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
virtual void supportedPropertyNames(Vector<String>& names);
virtual void updateIdNameCache() const;
bool hasValidIdNameCache() const { return m_namedItemCache; }
NamedItemCache& createNamedItemCache() const
{
ASSERT(!m_namedItemCache);
document().incrementNodeListWithIdNameCacheCount();
m_namedItemCache = adoptPtr(new NamedItemCache);
return *m_namedItemCache;
}
NamedItemCache& namedItemCache() const
{
ASSERT(m_namedItemCache);
return *m_namedItemCache;
}
private:
Element* traverseNextElement(Element& previous) const;
void invalidateIdNameCacheMaps(Document* oldDocument = 0) const
{
if (!hasValidIdNameCache())
return;
unregisterIdNameCacheFromDocument(oldDocument ? *oldDocument : document());
m_namedItemCache.clear();
}
void unregisterIdNameCacheFromDocument(Document& document) const
{
ASSERT(hasValidIdNameCache());
document.decrementNodeListWithIdNameCacheCount();
}
const unsigned m_overridesItemAfter : 1;
const unsigned m_shouldOnlyIncludeDirectChildren : 1;
mutable OwnPtr<NamedItemCache> m_namedItemCache;
mutable CollectionIndexCache<HTMLCollection, Element> m_collectionIndexCache;
friend class LiveNodeListBase;
};
}
#endif