1 /* 2 * Copyright (C) 2010 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 "core/dom/DatasetDOMStringMap.h" 28 29 #include "bindings/v8/ExceptionState.h" 30 #include "core/dom/Attribute.h" 31 #include "core/dom/Element.h" 32 #include "core/dom/ExceptionCode.h" 33 #include "wtf/ASCIICType.h" 34 #include "wtf/text/StringBuilder.h" 35 36 namespace WebCore { 37 38 static bool isValidAttributeName(const String& name) 39 { 40 if (!name.startsWith("data-")) 41 return false; 42 43 unsigned length = name.length(); 44 for (unsigned i = 5; i < length; ++i) { 45 if (isASCIIUpper(name[i])) 46 return false; 47 } 48 49 return true; 50 } 51 52 static String convertAttributeNameToPropertyName(const String& name) 53 { 54 StringBuilder stringBuilder; 55 56 unsigned length = name.length(); 57 for (unsigned i = 5; i < length; ++i) { 58 UChar character = name[i]; 59 if (character != '-') 60 stringBuilder.append(character); 61 else { 62 if ((i + 1 < length) && isASCIILower(name[i + 1])) { 63 stringBuilder.append(toASCIIUpper(name[i + 1])); 64 ++i; 65 } else 66 stringBuilder.append(character); 67 } 68 } 69 70 return stringBuilder.toString(); 71 } 72 73 template<typename CharType1, typename CharType2> 74 static bool propertyNameMatchesAttributeName(const CharType1* propertyName, const CharType2* attributeName, unsigned propertyLength, unsigned attributeLength) 75 { 76 unsigned a = 5; 77 unsigned p = 0; 78 bool wordBoundary = false; 79 while (a < attributeLength && p < propertyLength) { 80 if (attributeName[a] == '-' && a + 1 < attributeLength && isASCIILower(attributeName[a + 1])) 81 wordBoundary = true; 82 else { 83 if ((wordBoundary ? toASCIIUpper(attributeName[a]) : attributeName[a]) != propertyName[p]) 84 return false; 85 p++; 86 wordBoundary = false; 87 } 88 a++; 89 } 90 91 return (a == attributeLength && p == propertyLength); 92 } 93 94 static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) 95 { 96 if (!attributeName.startsWith("data-")) 97 return false; 98 99 unsigned propertyLength = propertyName.length(); 100 unsigned attributeLength = attributeName.length(); 101 102 if (propertyName.is8Bit()) { 103 if (attributeName.is8Bit()) 104 return propertyNameMatchesAttributeName(propertyName.characters8(), attributeName.characters8(), propertyLength, attributeLength); 105 return propertyNameMatchesAttributeName(propertyName.characters8(), attributeName.characters16(), propertyLength, attributeLength); 106 } 107 108 if (attributeName.is8Bit()) 109 return propertyNameMatchesAttributeName(propertyName.characters16(), attributeName.characters8(), propertyLength, attributeLength); 110 return propertyNameMatchesAttributeName(propertyName.characters16(), attributeName.characters16(), propertyLength, attributeLength); 111 } 112 113 static bool isValidPropertyName(const String& name) 114 { 115 unsigned length = name.length(); 116 for (unsigned i = 0; i < length; ++i) { 117 if (name[i] == '-' && (i + 1 < length) && isASCIILower(name[i + 1])) 118 return false; 119 } 120 return true; 121 } 122 123 // This returns an AtomicString because attribute names are always stored 124 // as AtomicString types in Element (see setAttribute()). 125 static AtomicString convertPropertyNameToAttributeName(const String& name) 126 { 127 StringBuilder builder; 128 builder.append("data-"); 129 130 unsigned length = name.length(); 131 for (unsigned i = 0; i < length; ++i) { 132 UChar character = name[i]; 133 if (isASCIIUpper(character)) { 134 builder.append('-'); 135 builder.append(toASCIILower(character)); 136 } else 137 builder.append(character); 138 } 139 140 return builder.toAtomicString(); 141 } 142 143 #if !ENABLE(OILPAN) 144 void DatasetDOMStringMap::ref() 145 { 146 m_element->ref(); 147 } 148 149 void DatasetDOMStringMap::deref() 150 { 151 m_element->deref(); 152 } 153 #endif 154 155 void DatasetDOMStringMap::getNames(Vector<String>& names) 156 { 157 if (!m_element->hasAttributes()) 158 return; 159 160 AttributeCollection attributes = m_element->attributes(); 161 AttributeCollection::const_iterator end = attributes.end(); 162 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { 163 if (isValidAttributeName(it->localName())) 164 names.append(convertAttributeNameToPropertyName(it->localName())); 165 } 166 } 167 168 String DatasetDOMStringMap::item(const String& name) 169 { 170 if (!m_element->hasAttributes()) 171 return String(); 172 173 AttributeCollection attributes = m_element->attributes(); 174 AttributeCollection::const_iterator end = attributes.end(); 175 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { 176 if (propertyNameMatchesAttributeName(name, it->localName())) 177 return it->value(); 178 } 179 180 return String(); 181 } 182 183 bool DatasetDOMStringMap::contains(const String& name) 184 { 185 if (!m_element->hasAttributes()) 186 return false; 187 188 AttributeCollection attributes = m_element->attributes(); 189 AttributeCollection::const_iterator end = attributes.end(); 190 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { 191 if (propertyNameMatchesAttributeName(name, it->localName())) 192 return true; 193 } 194 return false; 195 } 196 197 void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionState& exceptionState) 198 { 199 if (!isValidPropertyName(name)) { 200 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid property name."); 201 return; 202 } 203 204 m_element->setAttribute(convertPropertyNameToAttributeName(name), AtomicString(value), exceptionState); 205 } 206 207 bool DatasetDOMStringMap::deleteItem(const String& name) 208 { 209 if (isValidPropertyName(name)) { 210 AtomicString attributeName = convertPropertyNameToAttributeName(name); 211 if (m_element->hasAttribute(attributeName)) { 212 m_element->removeAttribute(attributeName); 213 return true; 214 } 215 } 216 return false; 217 } 218 219 void DatasetDOMStringMap::trace(Visitor* visitor) 220 { 221 visitor->trace(m_element); 222 DOMStringMap::trace(visitor); 223 } 224 225 } // namespace WebCore 226