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 static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) 74 { 75 if (!attributeName.startsWith("data-")) 76 return false; 77 78 unsigned propertyLength = propertyName.length(); 79 unsigned attributeLength = attributeName.length(); 80 81 unsigned a = 5; 82 unsigned p = 0; 83 bool wordBoundary = false; 84 while (a < attributeLength && p < propertyLength) { 85 if (attributeName[a] == '-' && a + 1 < attributeLength && attributeName[a + 1] != '-') 86 wordBoundary = true; 87 else { 88 if ((wordBoundary ? toASCIIUpper(attributeName[a]) : attributeName[a]) != propertyName[p]) 89 return false; 90 p++; 91 wordBoundary = false; 92 } 93 a++; 94 } 95 96 return (a == attributeLength && p == propertyLength); 97 } 98 99 static bool isValidPropertyName(const String& name) 100 { 101 unsigned length = name.length(); 102 for (unsigned i = 0; i < length; ++i) { 103 if (name[i] == '-' && (i + 1 < length) && isASCIILower(name[i + 1])) 104 return false; 105 } 106 return true; 107 } 108 109 static String convertPropertyNameToAttributeName(const String& name) 110 { 111 StringBuilder builder; 112 builder.append("data-"); 113 114 unsigned length = name.length(); 115 for (unsigned i = 0; i < length; ++i) { 116 UChar character = name[i]; 117 if (isASCIIUpper(character)) { 118 builder.append('-'); 119 builder.append(toASCIILower(character)); 120 } else 121 builder.append(character); 122 } 123 124 return builder.toString(); 125 } 126 127 void DatasetDOMStringMap::ref() 128 { 129 m_element->ref(); 130 } 131 132 void DatasetDOMStringMap::deref() 133 { 134 m_element->deref(); 135 } 136 137 void DatasetDOMStringMap::getNames(Vector<String>& names) 138 { 139 if (!m_element->hasAttributes()) 140 return; 141 142 unsigned length = m_element->attributeCount(); 143 for (unsigned i = 0; i < length; i++) { 144 const Attribute* attribute = m_element->attributeItem(i); 145 if (isValidAttributeName(attribute->localName())) 146 names.append(convertAttributeNameToPropertyName(attribute->localName())); 147 } 148 } 149 150 String DatasetDOMStringMap::item(const String& name) 151 { 152 if (!m_element->hasAttributes()) 153 return String(); 154 155 unsigned length = m_element->attributeCount(); 156 for (unsigned i = 0; i < length; i++) { 157 const Attribute* attribute = m_element->attributeItem(i); 158 if (propertyNameMatchesAttributeName(name, attribute->localName())) 159 return attribute->value(); 160 } 161 162 return String(); 163 } 164 165 bool DatasetDOMStringMap::contains(const String& name) 166 { 167 if (!m_element->hasAttributes()) 168 return false; 169 170 unsigned length = m_element->attributeCount(); 171 for (unsigned i = 0; i < length; i++) { 172 const Attribute* attribute = m_element->attributeItem(i); 173 if (propertyNameMatchesAttributeName(name, attribute->localName())) 174 return true; 175 } 176 177 return false; 178 } 179 180 void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionState& es) 181 { 182 if (!isValidPropertyName(name)) { 183 es.throwDOMException(SyntaxError); 184 return; 185 } 186 187 m_element->setAttribute(convertPropertyNameToAttributeName(name), value, es); 188 } 189 190 void DatasetDOMStringMap::deleteItem(const String& name, ExceptionState& es) 191 { 192 if (!isValidPropertyName(name)) { 193 es.throwDOMException(SyntaxError); 194 return; 195 } 196 197 m_element->removeAttribute(convertPropertyNameToAttributeName(name)); 198 } 199 200 } // namespace WebCore 201