1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #ifndef AttributeCollection_h
34 #define AttributeCollection_h
35
36 #include "core/dom/Attr.h"
37 #include "core/dom/Attribute.h"
38 #include "wtf/Vector.h"
39
40 namespace blink {
41
42 template <typename Container, typename ContainerMemberType = Container>
43 class AttributeCollectionGeneric {
44 public:
45 typedef typename Container::ValueType ValueType;
46 typedef ValueType* iterator;
47
AttributeCollectionGeneric(Container & attributes)48 AttributeCollectionGeneric(Container& attributes)
49 : m_attributes(attributes)
50 { }
51
52 ValueType& operator[](unsigned index) const { return at(index); }
at(unsigned index)53 ValueType& at(unsigned index) const
54 {
55 RELEASE_ASSERT(index < size());
56 return begin()[index];
57 }
58
begin()59 iterator begin() const { return m_attributes.data(); }
end()60 iterator end() const { return begin() + size(); }
61
size()62 unsigned size() const { return m_attributes.size(); }
isEmpty()63 bool isEmpty() const { return !size(); }
64
65 iterator find(const QualifiedName&) const;
66 iterator find(const AtomicString& name, bool shouldIgnoreCase) const;
67 size_t findIndex(const QualifiedName&, bool shouldIgnoreCase = false) const;
68 size_t findIndex(const AtomicString& name, bool shouldIgnoreCase) const;
69 size_t findIndex(Attr*) const;
70
71 protected:
72 size_t findSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
73
74 ContainerMemberType m_attributes;
75 };
76
77 class AttributeArray {
78 public:
79 typedef const Attribute ValueType;
80
AttributeArray(const Attribute * array,unsigned size)81 AttributeArray(const Attribute* array, unsigned size)
82 : m_array(array)
83 , m_size(size)
84 { }
85
data()86 const Attribute* data() const { return m_array; }
size()87 unsigned size() const { return m_size; }
88
89 private:
90 const Attribute* m_array;
91 unsigned m_size;
92 };
93
94 class AttributeCollection : public AttributeCollectionGeneric<const AttributeArray> {
95 public:
AttributeCollection()96 AttributeCollection()
97 : AttributeCollectionGeneric<const AttributeArray>(AttributeArray(nullptr, 0))
98 { }
99
AttributeCollection(const Attribute * array,unsigned size)100 AttributeCollection(const Attribute* array, unsigned size)
101 : AttributeCollectionGeneric<const AttributeArray>(AttributeArray(array, size))
102 { }
103 };
104
105 typedef Vector<Attribute, 4> AttributeVector;
106 class MutableAttributeCollection : public AttributeCollectionGeneric<AttributeVector, AttributeVector&> {
107 public:
MutableAttributeCollection(AttributeVector & attributes)108 explicit MutableAttributeCollection(AttributeVector& attributes)
109 : AttributeCollectionGeneric<AttributeVector, AttributeVector&>(attributes)
110 { }
111
112 // These functions do no error/duplicate checking.
113 void append(const QualifiedName&, const AtomicString& value);
114 void remove(unsigned index);
115 };
116
append(const QualifiedName & name,const AtomicString & value)117 inline void MutableAttributeCollection::append(const QualifiedName& name, const AtomicString& value)
118 {
119 m_attributes.append(Attribute(name, value));
120 }
121
remove(unsigned index)122 inline void MutableAttributeCollection::remove(unsigned index)
123 {
124 m_attributes.remove(index);
125 }
126
127 template <typename Container, typename ContainerMemberType>
find(const AtomicString & name,bool shouldIgnoreCase)128 inline typename AttributeCollectionGeneric<Container, ContainerMemberType>::iterator AttributeCollectionGeneric<Container, ContainerMemberType>::find(const AtomicString& name, bool shouldIgnoreCase) const
129 {
130 size_t index = findIndex(name, shouldIgnoreCase);
131 return index != kNotFound ? &at(index) : 0;
132 }
133
134 template <typename Container, typename ContainerMemberType>
findIndex(const QualifiedName & name,bool shouldIgnoreCase)135 inline size_t AttributeCollectionGeneric<Container, ContainerMemberType>::findIndex(const QualifiedName& name, bool shouldIgnoreCase) const
136 {
137 iterator end = this->end();
138 unsigned index = 0;
139 for (iterator it = begin(); it != end; ++it, ++index) {
140 if (it->name().matchesPossiblyIgnoringCase(name, shouldIgnoreCase))
141 return index;
142 }
143 return kNotFound;
144 }
145
146 // We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
147 // can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
148 template <typename Container, typename ContainerMemberType>
findIndex(const AtomicString & name,bool shouldIgnoreCase)149 inline size_t AttributeCollectionGeneric<Container, ContainerMemberType>::findIndex(const AtomicString& name, bool shouldIgnoreCase) const
150 {
151 bool doSlowCheck = shouldIgnoreCase;
152
153 // Optimize for the case where the attribute exists and its name exactly matches.
154 iterator end = this->end();
155 unsigned index = 0;
156 for (iterator it = begin(); it != end; ++it, ++index) {
157 // FIXME: Why check the prefix? Namespaces should be all that matter.
158 // Most attributes (all of HTML and CSS) have no namespace.
159 if (!it->name().hasPrefix()) {
160 if (name == it->localName())
161 return index;
162 } else {
163 doSlowCheck = true;
164 }
165 }
166
167 if (doSlowCheck)
168 return findSlowCase(name, shouldIgnoreCase);
169 return kNotFound;
170 }
171
172 template <typename Container, typename ContainerMemberType>
find(const QualifiedName & name)173 inline typename AttributeCollectionGeneric<Container, ContainerMemberType>::iterator AttributeCollectionGeneric<Container, ContainerMemberType>::find(const QualifiedName& name) const
174 {
175 iterator end = this->end();
176 for (iterator it = begin(); it != end; ++it) {
177 if (it->name().matches(name))
178 return it;
179 }
180 return 0;
181 }
182
183 template <typename Container, typename ContainerMemberType>
findIndex(Attr * attr)184 size_t AttributeCollectionGeneric<Container, ContainerMemberType>::findIndex(Attr* attr) const
185 {
186 // This relies on the fact that Attr's QualifiedName == the Attribute's name.
187 iterator end = this->end();
188 unsigned index = 0;
189 for (iterator it = begin(); it != end; ++it, ++index) {
190 if (it->name() == attr->qualifiedName())
191 return index;
192 }
193 return kNotFound;
194 }
195
196 template <typename Container, typename ContainerMemberType>
findSlowCase(const AtomicString & name,bool shouldIgnoreAttributeCase)197 size_t AttributeCollectionGeneric<Container, ContainerMemberType>::findSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const
198 {
199 // Continue to checking case-insensitively and/or full namespaced names if necessary:
200 iterator end = this->end();
201 unsigned index = 0;
202 for (iterator it = begin(); it != end; ++it, ++index) {
203 // FIXME: Why check the prefix? Namespace is all that should matter
204 // and all HTML/SVG attributes have a null namespace!
205 if (!it->name().hasPrefix()) {
206 if (shouldIgnoreAttributeCase && equalIgnoringCase(name, it->localName()))
207 return index;
208 } else {
209 // FIXME: Would be faster to do this comparison without calling toString, which
210 // generates a temporary string by concatenation. But this branch is only reached
211 // if the attribute name has a prefix, which is rare in HTML.
212 if (equalPossiblyIgnoringCase(name, it->name().toString(), shouldIgnoreAttributeCase))
213 return index;
214 }
215 }
216 return kNotFound;
217 }
218
219 } // namespace blink
220
221 #endif // AttributeCollection_h
222