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 "DatasetDOMStringMap.h" 28 29 #include "Attribute.h" 30 #include "Element.h" 31 #include "ExceptionCode.h" 32 #include "NamedNodeMap.h" 33 #include <wtf/ASCIICType.h> 34 35 namespace WebCore { 36 37 static bool isValidAttributeName(const String& name) 38 { 39 if (!name.startsWith("data-")) 40 return false; 41 42 const UChar* characters = name.characters(); 43 unsigned length = name.length(); 44 for (unsigned i = 5; i < length; ++i) { 45 if (isASCIIUpper(characters[i])) 46 return false; 47 } 48 49 return true; 50 } 51 52 static String convertAttributeNameToPropertyName(const String& name) 53 { 54 Vector<UChar> newStringBuffer; 55 56 const UChar* characters = name.characters(); 57 unsigned length = name.length(); 58 for (unsigned i = 5; i < length; ++i) { 59 if (characters[i] != '-') 60 newStringBuffer.append(characters[i]); 61 else { 62 if ((i + 1 < length) && isASCIILower(characters[i + 1])) { 63 newStringBuffer.append(toASCIIUpper(characters[i + 1])); 64 ++i; 65 } else 66 newStringBuffer.append(characters[i]); 67 } 68 } 69 70 return String::adopt(newStringBuffer); 71 } 72 73 static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) 74 { 75 if (!attributeName.startsWith("data-")) 76 return false; 77 78 const UChar* property = propertyName.characters(); 79 const UChar* attribute = attributeName.characters(); 80 unsigned propertyLength = propertyName.length(); 81 unsigned attributeLength = attributeName.length(); 82 83 unsigned a = 5; 84 unsigned p = 0; 85 bool wordBoundary = false; 86 while (a < attributeLength && p < propertyLength) { 87 if (attribute[a] == '-' && a + 1 < attributeLength && attribute[a + 1] != '-') 88 wordBoundary = true; 89 else { 90 if ((wordBoundary ? toASCIIUpper(attribute[a]) : attribute[a]) != property[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 const UChar* characters = name.characters(); 104 unsigned length = name.length(); 105 for (unsigned i = 0; i < length; ++i) { 106 if (characters[i] == '-' && (i + 1 < length) && isASCIILower(characters[i + 1])) 107 return false; 108 } 109 return true; 110 } 111 112 static String convertPropertyNameToAttributeName(const String& name) 113 { 114 Vector<UChar> newStringBuffer; 115 116 newStringBuffer.append('d'); 117 newStringBuffer.append('a'); 118 newStringBuffer.append('t'); 119 newStringBuffer.append('a'); 120 newStringBuffer.append('-'); 121 122 const UChar* characters = name.characters(); 123 unsigned length = name.length(); 124 for (unsigned i = 0; i < length; ++i) { 125 if (isASCIIUpper(characters[i])) { 126 newStringBuffer.append('-'); 127 newStringBuffer.append(toASCIILower(characters[i])); 128 } else 129 newStringBuffer.append(characters[i]); 130 } 131 132 return String::adopt(newStringBuffer); 133 } 134 135 136 void DatasetDOMStringMap::ref() 137 { 138 m_element->ref(); 139 } 140 141 void DatasetDOMStringMap::deref() 142 { 143 m_element->deref(); 144 } 145 146 void DatasetDOMStringMap::getNames(Vector<String>& names) 147 { 148 NamedNodeMap* attributeMap = m_element->attributes(true); 149 if (attributeMap) { 150 unsigned length = attributeMap->length(); 151 for (unsigned i = 0; i < length; i++) { 152 Attribute* attribute = attributeMap->attributeItem(i); 153 if (isValidAttributeName(attribute->localName())) 154 names.append(convertAttributeNameToPropertyName(attribute->localName())); 155 } 156 } 157 } 158 159 String DatasetDOMStringMap::item(const String& name) 160 { 161 NamedNodeMap* attributeMap = m_element->attributes(true); 162 if (attributeMap) { 163 unsigned length = attributeMap->length(); 164 for (unsigned i = 0; i < length; i++) { 165 Attribute* attribute = attributeMap->attributeItem(i); 166 if (propertyNameMatchesAttributeName(name, attribute->localName())) 167 return attribute->value(); 168 } 169 } 170 171 return String(); 172 } 173 174 bool DatasetDOMStringMap::contains(const String& name) 175 { 176 NamedNodeMap* attributeMap = m_element->attributes(true); 177 if (attributeMap) { 178 unsigned length = attributeMap->length(); 179 for (unsigned i = 0; i < length; i++) { 180 Attribute* attribute = attributeMap->attributeItem(i); 181 if (propertyNameMatchesAttributeName(name, attribute->localName())) 182 return true; 183 } 184 } 185 return false; 186 } 187 188 void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionCode& ec) 189 { 190 if (!isValidPropertyName(name)) { 191 ec = SYNTAX_ERR; 192 return; 193 } 194 195 m_element->setAttribute(convertPropertyNameToAttributeName(name), value, ec); 196 } 197 198 void DatasetDOMStringMap::deleteItem(const String& name, ExceptionCode& ec) 199 { 200 if (!isValidPropertyName(name)) { 201 ec = SYNTAX_ERR; 202 return; 203 } 204 205 ExceptionCode dummy; 206 m_element->removeAttribute(convertPropertyNameToAttributeName(name), dummy); 207 } 208 209 } // namespace WebCore 210