/* * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "DatasetDOMStringMap.h" #include "Element.h" #include #include #include #include namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(DatasetDOMStringMap); static bool isValidAttributeName(const String& name) { if (!name.startsWith("data-")) return false; unsigned length = name.length(); for (unsigned i = 5; i < length; ++i) { if (isASCIIUpper(name[i])) return false; } return true; } static String convertAttributeNameToPropertyName(const String& name) { StringBuilder stringBuilder; unsigned length = name.length(); for (unsigned i = 5; i < length; ++i) { UChar character = name[i]; if (character != '-') stringBuilder.append(character); else { if ((i + 1 < length) && isASCIILower(name[i + 1])) { stringBuilder.append(toASCIIUpper(name[i + 1])); ++i; } else stringBuilder.append(character); } } return stringBuilder.toString(); } static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) { if (!attributeName.startsWith("data-")) return false; unsigned propertyLength = propertyName.length(); unsigned attributeLength = attributeName.length(); unsigned a = 5; unsigned p = 0; bool wordBoundary = false; while (a < attributeLength && p < propertyLength) { const UChar currentAttributeNameChar = attributeName[a]; if (currentAttributeNameChar == '-' && a + 1 < attributeLength && attributeName[a + 1] != '-') wordBoundary = true; else { if ((wordBoundary ? toASCIIUpper(currentAttributeNameChar) : currentAttributeNameChar) != propertyName[p]) return false; p++; wordBoundary = false; } a++; } return (a == attributeLength && p == propertyLength); } static bool isValidPropertyName(const String& name) { unsigned length = name.length(); for (unsigned i = 0; i < length; ++i) { if (name[i] == '-' && (i + 1 < length) && isASCIILower(name[i + 1])) return false; } return true; } template static inline AtomString convertPropertyNameToAttributeName(const StringImpl& name) { const CharacterType dataPrefix[] = { 'd', 'a', 't', 'a', '-' }; Vector buffer; unsigned length = name.length(); buffer.reserveInitialCapacity(WTF_ARRAY_LENGTH(dataPrefix) + length); buffer.append(dataPrefix, WTF_ARRAY_LENGTH(dataPrefix)); const CharacterType* characters = name.characters(); for (unsigned i = 0; i < length; ++i) { CharacterType character = characters[i]; if (isASCIIUpper(character)) { buffer.append('-'); buffer.append(toASCIILower(character)); } else buffer.append(character); } return AtomString(buffer.data(), buffer.size()); } static AtomString convertPropertyNameToAttributeName(const String& name) { if (name.isNull()) return nullAtom(); StringImpl* nameImpl = name.impl(); if (nameImpl->is8Bit()) return convertPropertyNameToAttributeName(*nameImpl); return convertPropertyNameToAttributeName(*nameImpl); } void DatasetDOMStringMap::ref() { m_element.ref(); } void DatasetDOMStringMap::deref() { m_element.deref(); } bool DatasetDOMStringMap::isSupportedPropertyName(const String& propertyName) const { if (!m_element.hasAttributes()) return false; auto attributeIteratorAccessor = m_element.attributesIterator(); if (attributeIteratorAccessor.attributeCount() == 1) { // If the node has a single attribute, it is the dataset member accessed in most cases. // Building a new AtomString in that case is overkill so we do a direct character comparison. const auto& attribute = *attributeIteratorAccessor.begin(); if (propertyNameMatchesAttributeName(propertyName, attribute.localName())) return true; } else { auto attributeName = convertPropertyNameToAttributeName(propertyName); for (const Attribute& attribute : attributeIteratorAccessor) { if (attribute.localName() == attributeName) return true; } } return false; } Vector DatasetDOMStringMap::supportedPropertyNames() const { Vector names; if (!m_element.hasAttributes()) return names; for (auto& attribute : m_element.attributesIterator()) { if (isValidAttributeName(attribute.localName())) names.append(convertAttributeNameToPropertyName(attribute.localName())); } return names; } const AtomString* DatasetDOMStringMap::item(const String& propertyName) const { if (m_element.hasAttributes()) { AttributeIteratorAccessor attributeIteratorAccessor = m_element.attributesIterator(); if (attributeIteratorAccessor.attributeCount() == 1) { // If the node has a single attribute, it is the dataset member accessed in most cases. // Building a new AtomString in that case is overkill so we do a direct character comparison. const Attribute& attribute = *attributeIteratorAccessor.begin(); if (propertyNameMatchesAttributeName(propertyName, attribute.localName())) return &attribute.value(); } else { AtomString attributeName = convertPropertyNameToAttributeName(propertyName); for (const Attribute& attribute : attributeIteratorAccessor) { if (attribute.localName() == attributeName) return &attribute.value(); } } } return nullptr; } String DatasetDOMStringMap::namedItem(const AtomString& name) const { if (const auto* value = item(name)) return *value; return String { }; } ExceptionOr DatasetDOMStringMap::setNamedItem(const String& name, const String& value) { if (!isValidPropertyName(name)) return Exception { SyntaxError }; return m_element.setAttribute(convertPropertyNameToAttributeName(name), value); } bool DatasetDOMStringMap::deleteNamedProperty(const String& name) { return m_element.removeAttribute(convertPropertyNameToAttributeName(name)); } } // namespace WebCore