Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / dom / DatasetDOMStringMap.cpp
1 /*
2  * Copyright (C) 2010, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DatasetDOMStringMap.h"
28
29 #include "Element.h"
30 #include <wtf/ASCIICType.h>
31 #include <wtf/IsoMallocInlines.h>
32 #include <wtf/text/AtomString.h>
33 #include <wtf/text/StringBuilder.h>
34
35 namespace WebCore {
36
37 WTF_MAKE_ISO_ALLOCATED_IMPL(DatasetDOMStringMap);
38
39 static bool isValidAttributeName(const String& name)
40 {
41     if (!name.startsWith("data-"))
42         return false;
43
44     unsigned length = name.length();
45     for (unsigned i = 5; i < length; ++i) {
46         if (isASCIIUpper(name[i]))
47             return false;
48     }
49
50     return true;
51 }
52
53 static String convertAttributeNameToPropertyName(const String& name)
54 {
55     StringBuilder stringBuilder;
56
57     unsigned length = name.length();
58     for (unsigned i = 5; i < length; ++i) {
59         UChar character = name[i];
60         if (character != '-')
61             stringBuilder.append(character);
62         else {
63             if ((i + 1 < length) && isASCIILower(name[i + 1])) {
64                 stringBuilder.append(toASCIIUpper(name[i + 1]));
65                 ++i;
66             } else
67                 stringBuilder.append(character);
68         }
69     }
70
71     return stringBuilder.toString();
72 }
73
74 static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName)
75 {
76     if (!attributeName.startsWith("data-"))
77         return false;
78
79     unsigned propertyLength = propertyName.length();
80     unsigned attributeLength = attributeName.length();
81
82     unsigned a = 5;
83     unsigned p = 0;
84     bool wordBoundary = false;
85     while (a < attributeLength && p < propertyLength) {
86         const UChar currentAttributeNameChar = attributeName[a];
87         if (currentAttributeNameChar == '-' && a + 1 < attributeLength && attributeName[a + 1] != '-')
88             wordBoundary = true;
89         else {
90             if ((wordBoundary ? toASCIIUpper(currentAttributeNameChar) : currentAttributeNameChar) != propertyName[p])
91                 return false;
92             p++;
93             wordBoundary = false;
94         }
95         a++;
96     }
97
98     return (a == attributeLength && p == propertyLength);
99 }
100
101 static bool isValidPropertyName(const String& name)
102 {
103     unsigned length = name.length();
104     for (unsigned i = 0; i < length; ++i) {
105         if (name[i] == '-' && (i + 1 < length) && isASCIILower(name[i + 1]))
106             return false;
107     }
108     return true;
109 }
110
111 template<typename CharacterType>
112 static inline AtomString convertPropertyNameToAttributeName(const StringImpl& name)
113 {
114     const CharacterType dataPrefix[] = { 'd', 'a', 't', 'a', '-' };
115
116     Vector<CharacterType, 32> buffer;
117
118     unsigned length = name.length();
119     buffer.reserveInitialCapacity(WTF_ARRAY_LENGTH(dataPrefix) + length);
120
121     buffer.append(dataPrefix, WTF_ARRAY_LENGTH(dataPrefix));
122
123     const CharacterType* characters = name.characters<CharacterType>();
124     for (unsigned i = 0; i < length; ++i) {
125         CharacterType character = characters[i];
126         if (isASCIIUpper(character)) {
127             buffer.append('-');
128             buffer.append(toASCIILower(character));
129         } else
130             buffer.append(character);
131     }
132     return AtomString(buffer.data(), buffer.size());
133 }
134
135 static AtomString convertPropertyNameToAttributeName(const String& name)
136 {
137     if (name.isNull())
138         return nullAtom();
139
140     StringImpl* nameImpl = name.impl();
141     if (nameImpl->is8Bit())
142         return convertPropertyNameToAttributeName<LChar>(*nameImpl);
143     return convertPropertyNameToAttributeName<UChar>(*nameImpl);
144 }
145
146 void DatasetDOMStringMap::ref()
147 {
148     m_element.ref();
149 }
150
151 void DatasetDOMStringMap::deref()
152 {
153     m_element.deref();
154 }
155
156 bool DatasetDOMStringMap::isSupportedPropertyName(const String& propertyName) const
157 {
158     if (!m_element.hasAttributes())
159         return false;
160
161     auto attributeIteratorAccessor = m_element.attributesIterator();
162     if (attributeIteratorAccessor.attributeCount() == 1) {
163         // If the node has a single attribute, it is the dataset member accessed in most cases.
164         // Building a new AtomString in that case is overkill so we do a direct character comparison.
165         const auto& attribute = *attributeIteratorAccessor.begin();
166         if (propertyNameMatchesAttributeName(propertyName, attribute.localName()))
167             return true;
168     } else {
169         auto attributeName = convertPropertyNameToAttributeName(propertyName);
170         for (const Attribute& attribute : attributeIteratorAccessor) {
171             if (attribute.localName() == attributeName)
172                 return true;
173         }
174     }
175     
176     return false;
177 }
178
179 Vector<String> DatasetDOMStringMap::supportedPropertyNames() const
180 {
181     Vector<String> names;
182
183     if (!m_element.hasAttributes())
184         return names;
185
186     for (auto& attribute : m_element.attributesIterator()) {
187         if (isValidAttributeName(attribute.localName()))
188             names.append(convertAttributeNameToPropertyName(attribute.localName()));
189     }
190
191     return names;
192 }
193
194 const AtomString* DatasetDOMStringMap::item(const String& propertyName) const
195 {
196     if (m_element.hasAttributes()) {
197         AttributeIteratorAccessor attributeIteratorAccessor = m_element.attributesIterator();
198
199         if (attributeIteratorAccessor.attributeCount() == 1) {
200             // If the node has a single attribute, it is the dataset member accessed in most cases.
201             // Building a new AtomString in that case is overkill so we do a direct character comparison.
202             const Attribute& attribute = *attributeIteratorAccessor.begin();
203             if (propertyNameMatchesAttributeName(propertyName, attribute.localName()))
204                 return &attribute.value();
205         } else {
206             AtomString attributeName = convertPropertyNameToAttributeName(propertyName);
207             for (const Attribute& attribute : attributeIteratorAccessor) {
208                 if (attribute.localName() == attributeName)
209                     return &attribute.value();
210             }
211         }
212     }
213
214     return nullptr;
215 }
216
217 String DatasetDOMStringMap::namedItem(const AtomString& name) const
218 {
219     if (const auto* value = item(name))
220         return *value;
221     return String { };
222 }
223
224 ExceptionOr<void> DatasetDOMStringMap::setNamedItem(const String& name, const String& value)
225 {
226     if (!isValidPropertyName(name))
227         return Exception { SyntaxError };
228     return m_element.setAttribute(convertPropertyNameToAttributeName(name), value);
229 }
230
231 bool DatasetDOMStringMap::deleteNamedProperty(const String& name)
232 {
233     return m_element.removeAttribute(convertPropertyNameToAttributeName(name));
234 }
235
236 } // namespace WebCore