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