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/CollectionIndexCache.h"
29 #include "core/html/CollectionType.h"
30 #include "wtf/Forward.h"
31 #include "wtf/HashMap.h"
32 #include "wtf/Vector.h"
33
34 namespace WebCore {
35
36 class HTMLCollection : public RefCountedWillBeGarbageCollectedFinalized<HTMLCollection>, public ScriptWrappable, public LiveNodeListBase {
37 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(HTMLCollection);
38 public:
39 enum ItemAfterOverrideType {
40 OverridesItemAfter,
41 DoesNotOverrideItemAfter,
42 };
43
44 static PassRefPtrWillBeRawPtr<HTMLCollection> create(ContainerNode& base, CollectionType);
45 virtual ~HTMLCollection();
46 virtual void invalidateCache(Document* oldDocument = 0) const OVERRIDE;
47 void invalidateCacheForAttribute(const QualifiedName*) const;
48
49 // DOM API
length()50 unsigned length() const { return m_collectionIndexCache.nodeCount(*this); }
item(unsigned offset)51 Element* item(unsigned offset) const { return m_collectionIndexCache.nodeAt(*this, offset); }
52 virtual Element* namedItem(const AtomicString& name) const;
53 bool namedPropertyQuery(const AtomicString&, ExceptionState&);
54 void namedPropertyEnumerator(Vector<String>& names, ExceptionState&);
55
56 // Non-DOM API
57 void namedItems(const AtomicString& name, WillBeHeapVector<RefPtrWillBeMember<Element> >&) const;
isEmpty()58 bool isEmpty() const { return m_collectionIndexCache.isEmpty(*this); }
hasExactlyOneItem()59 bool hasExactlyOneItem() const { return m_collectionIndexCache.hasExactlyOneNode(*this); }
60
61 // CollectionIndexCache API.
canTraverseBackward()62 bool canTraverseBackward() const { return !overridesItemAfter(); }
63 Element* traverseToFirstElement() const;
64 Element* traverseToLastElement() 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
70 protected:
71 HTMLCollection(ContainerNode& base, CollectionType, ItemAfterOverrideType);
72
73 class NamedItemCache FINAL : public NoBaseWillBeGarbageCollected<NamedItemCache> {
74 public:
create()75 static PassOwnPtrWillBeRawPtr<NamedItemCache> create()
76 {
77 return adoptPtrWillBeNoop(new NamedItemCache);
78 }
79
getElementsById(const AtomicString & id)80 WillBeHeapVector<RawPtrWillBeMember<Element> >* getElementsById(const AtomicString& id) const { return m_idCache.get(id.impl()); }
getElementsByName(const AtomicString & name)81 WillBeHeapVector<RawPtrWillBeMember<Element> >* getElementsByName(const AtomicString& name) const { return m_nameCache.get(name.impl()); }
addElementWithId(const AtomicString & id,Element * element)82 void addElementWithId(const AtomicString& id, Element* element) { addElementToMap(m_idCache, id, element); }
addElementWithName(const AtomicString & name,Element * element)83 void addElementWithName(const AtomicString& name, Element* element) { addElementToMap(m_nameCache, name, element); }
84
trace(Visitor * visitor)85 void trace(Visitor* visitor)
86 {
87 visitor->trace(m_idCache);
88 visitor->trace(m_nameCache);
89 }
90
91 private:
92 NamedItemCache();
93 typedef WillBeHeapHashMap<StringImpl*, OwnPtrWillBeMember<WillBeHeapVector<RawPtrWillBeMember<Element> > > > StringToElementsMap;
addElementToMap(StringToElementsMap & map,const AtomicString & key,Element * element)94 static void addElementToMap(StringToElementsMap& map, const AtomicString& key, Element* element)
95 {
96 OwnPtrWillBeMember<WillBeHeapVector<RawPtrWillBeMember<Element> > >& vector = map.add(key.impl(), nullptr).storedValue->value;
97 if (!vector)
98 vector = adoptPtrWillBeNoop(new WillBeHeapVector<RawPtrWillBeMember<Element> >);
99 vector->append(element);
100 }
101
102 StringToElementsMap m_idCache;
103 StringToElementsMap m_nameCache;
104 };
105
overridesItemAfter()106 bool overridesItemAfter() const { return m_overridesItemAfter; }
107 virtual Element* virtualItemAfter(Element*) const;
shouldOnlyIncludeDirectChildren()108 bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
109 virtual void supportedPropertyNames(Vector<String>& names);
110
111 virtual void updateIdNameCache() const;
hasValidIdNameCache()112 bool hasValidIdNameCache() const { return m_namedItemCache; }
113
setNamedItemCache(PassOwnPtrWillBeRawPtr<NamedItemCache> cache)114 void setNamedItemCache(PassOwnPtrWillBeRawPtr<NamedItemCache> cache) const
115 {
116 ASSERT(!m_namedItemCache);
117 document().registerNodeListWithIdNameCache(this);
118 m_namedItemCache = cache;
119 }
120
namedItemCache()121 NamedItemCache& namedItemCache() const
122 {
123 ASSERT(m_namedItemCache);
124 return *m_namedItemCache;
125 }
126
127 private:
128 void invalidateIdNameCacheMaps(Document* oldDocument = 0) const
129 {
130 if (!hasValidIdNameCache())
131 return;
132
133 // Make sure we decrement the NodeListWithIdNameCache count from
134 // the old document instead of the new one in the case the collection
135 // is moved to a new document.
136 unregisterIdNameCacheFromDocument(oldDocument ? *oldDocument : document());
137
138 m_namedItemCache.clear();
139 }
140
unregisterIdNameCacheFromDocument(Document & document)141 void unregisterIdNameCacheFromDocument(Document& document) const
142 {
143 ASSERT(hasValidIdNameCache());
144 document.unregisterNodeListWithIdNameCache(this);
145 }
146
147 const unsigned m_overridesItemAfter : 1;
148 const unsigned m_shouldOnlyIncludeDirectChildren : 1;
149 mutable OwnPtrWillBeMember<NamedItemCache> m_namedItemCache;
150 mutable CollectionIndexCache<HTMLCollection, Element> m_collectionIndexCache;
151 };
152
153 DEFINE_TYPE_CASTS(HTMLCollection, LiveNodeListBase, collection, isHTMLCollectionType(collection->type()), isHTMLCollectionType(collection.type()));
154
invalidateCacheForAttribute(const QualifiedName * attrName)155 inline void HTMLCollection::invalidateCacheForAttribute(const QualifiedName* attrName) const
156 {
157 if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
158 invalidateCache();
159 else if (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr)
160 invalidateIdNameCacheMaps();
161 }
162
163 } // namespace
164
165 #endif
166