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/core/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 blink { 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.appendLiteral("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 AttributeCollection attributes = m_element->attributes(); 158 AttributeCollection::iterator end = attributes.end(); 159 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { 160 if (isValidAttributeName(it->localName())) 161 names.append(convertAttributeNameToPropertyName(it->localName())); 162 } 163 } 164 165 String DatasetDOMStringMap::item(const String& name) 166 { 167 AttributeCollection attributes = m_element->attributes(); 168 AttributeCollection::iterator end = attributes.end(); 169 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { 170 if (propertyNameMatchesAttributeName(name, it->localName())) 171 return it->value(); 172 } 173 174 return String(); 175 } 176 177 bool DatasetDOMStringMap::contains(const String& name) 178 { 179 AttributeCollection attributes = m_element->attributes(); 180 AttributeCollection::iterator end = attributes.end(); 181 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { 182 if (propertyNameMatchesAttributeName(name, it->localName())) 183 return true; 184 } 185 return false; 186 } 187 188 void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionState& exceptionState) 189 { 190 if (!isValidPropertyName(name)) { 191 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid property name."); 192 return; 193 } 194 195 m_element->setAttribute(convertPropertyNameToAttributeName(name), AtomicString(value), exceptionState); 196 } 197 198 bool DatasetDOMStringMap::deleteItem(const String& name) 199 { 200 if (isValidPropertyName(name)) { 201 AtomicString attributeName = convertPropertyNameToAttributeName(name); 202 if (m_element->hasAttribute(attributeName)) { 203 m_element->removeAttribute(attributeName); 204 return true; 205 } 206 } 207 return false; 208 } 209 210 void DatasetDOMStringMap::trace(Visitor* visitor) 211 { 212 visitor->trace(m_element); 213 DOMStringMap::trace(visitor); 214 } 215 216 } // namespace blink 217