1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
5 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#ifndef HTMLCollection_h
25#define HTMLCollection_h
26
27#include "core/dom/LiveNodeListBase.h"
28#include "core/html/CollectionItemsCache.h"
29#include "core/html/CollectionType.h"
30#include "wtf/Forward.h"
31
32namespace blink {
33
34class HTMLCollection : public RefCountedWillBeGarbageCollectedFinalized<HTMLCollection>, public ScriptWrappable, public LiveNodeListBase {
35    DEFINE_WRAPPERTYPEINFO();
36    WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(HTMLCollection);
37public:
38    enum ItemAfterOverrideType {
39        OverridesItemAfter,
40        DoesNotOverrideItemAfter,
41    };
42
43    static PassRefPtrWillBeRawPtr<HTMLCollection> create(ContainerNode& base, CollectionType);
44    virtual ~HTMLCollection();
45    virtual void invalidateCache(Document* oldDocument = 0) const OVERRIDE;
46    void invalidateCacheForAttribute(const QualifiedName*) const;
47
48    // DOM API
49    unsigned length() const;
50    Element* item(unsigned offset) const;
51    virtual Element* namedItem(const AtomicString& name) const;
52    bool namedPropertyQuery(const AtomicString&, ExceptionState&);
53    void namedPropertyEnumerator(Vector<String>& names, ExceptionState&);
54
55    // Non-DOM API
56    void namedItems(const AtomicString& name, WillBeHeapVector<RefPtrWillBeMember<Element> >&) const;
57    bool isEmpty() const { return m_collectionItemsCache.isEmpty(*this); }
58    bool hasExactlyOneItem() const { return m_collectionItemsCache.hasExactlyOneNode(*this); }
59    bool elementMatches(const Element&) const;
60
61    // CollectionIndexCache API.
62    bool canTraverseBackward() const { return !overridesItemAfter(); }
63    Element* traverseToFirst() const;
64    Element* traverseToLast() const;
65    Element* traverseForwardToOffset(unsigned offset, Element& currentElement, unsigned& currentOffset) const;
66    Element* traverseBackwardToOffset(unsigned offset, Element& currentElement, unsigned& currentOffset) const;
67
68    virtual void trace(Visitor*);
69
70protected:
71    HTMLCollection(ContainerNode& base, CollectionType, ItemAfterOverrideType);
72
73    class NamedItemCache FINAL : public NoBaseWillBeGarbageCollected<NamedItemCache> {
74    public:
75        static PassOwnPtrWillBeRawPtr<NamedItemCache> create()
76        {
77            return adoptPtrWillBeNoop(new NamedItemCache);
78        }
79
80        WillBeHeapVector<RawPtrWillBeMember<Element> >* getElementsById(const AtomicString& id) const { return m_idCache.get(id.impl()); }
81        WillBeHeapVector<RawPtrWillBeMember<Element> >* getElementsByName(const AtomicString& name) const { return m_nameCache.get(name.impl()); }
82        void addElementWithId(const AtomicString& id, Element* element) { addElementToMap(m_idCache, id, element); }
83        void addElementWithName(const AtomicString& name, Element* element) { addElementToMap(m_nameCache, name, element); }
84
85        void trace(Visitor* visitor)
86        {
87#if ENABLE(OILPAN)
88            visitor->trace(m_idCache);
89            visitor->trace(m_nameCache);
90#endif
91        }
92
93    private:
94        NamedItemCache();
95        typedef WillBeHeapHashMap<StringImpl*, OwnPtrWillBeMember<WillBeHeapVector<RawPtrWillBeMember<Element> > > > StringToElementsMap;
96        static void addElementToMap(StringToElementsMap& map, const AtomicString& key, Element* element)
97        {
98            OwnPtrWillBeMember<WillBeHeapVector<RawPtrWillBeMember<Element> > >& vector = map.add(key.impl(), nullptr).storedValue->value;
99            if (!vector)
100                vector = adoptPtrWillBeNoop(new WillBeHeapVector<RawPtrWillBeMember<Element> >);
101            vector->append(element);
102        }
103
104        StringToElementsMap m_idCache;
105        StringToElementsMap m_nameCache;
106    };
107
108    bool overridesItemAfter() const { return m_overridesItemAfter; }
109    virtual Element* virtualItemAfter(Element*) const;
110    bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
111    virtual void supportedPropertyNames(Vector<String>& names);
112
113    virtual void updateIdNameCache() const;
114    bool hasValidIdNameCache() const { return m_namedItemCache; }
115
116    void setNamedItemCache(PassOwnPtrWillBeRawPtr<NamedItemCache> cache) const
117    {
118        ASSERT(!m_namedItemCache);
119        document().registerNodeListWithIdNameCache(this);
120        m_namedItemCache = cache;
121    }
122
123    NamedItemCache& namedItemCache() const
124    {
125        ASSERT(m_namedItemCache);
126        return *m_namedItemCache;
127    }
128
129private:
130    void invalidateIdNameCacheMaps(Document* oldDocument = 0) const
131    {
132        if (!hasValidIdNameCache())
133            return;
134
135        // Make sure we decrement the NodeListWithIdNameCache count from
136        // the old document instead of the new one in the case the collection
137        // is moved to a new document.
138        unregisterIdNameCacheFromDocument(oldDocument ? *oldDocument : document());
139
140        m_namedItemCache.clear();
141    }
142
143    void unregisterIdNameCacheFromDocument(Document& document) const
144    {
145        ASSERT(hasValidIdNameCache());
146        document.unregisterNodeListWithIdNameCache(this);
147    }
148
149    const unsigned m_overridesItemAfter : 1;
150    const unsigned m_shouldOnlyIncludeDirectChildren : 1;
151    mutable OwnPtrWillBeMember<NamedItemCache> m_namedItemCache;
152    mutable CollectionItemsCache<HTMLCollection, Element> m_collectionItemsCache;
153};
154
155DEFINE_TYPE_CASTS(HTMLCollection, LiveNodeListBase, collection, isHTMLCollectionType(collection->type()), isHTMLCollectionType(collection.type()));
156
157inline void HTMLCollection::invalidateCacheForAttribute(const QualifiedName* attrName) const
158{
159    if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
160        invalidateCache();
161    else if (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr)
162        invalidateIdNameCacheMaps();
163}
164
165} // namespace blink
166
167#endif // HTMLCollection_h
168