blob: 0a18a0a6e08246b077feefb0aa7efa5fae5b2c36 [file] [log] [blame]
/*
* Copyright (C) 2010 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 "third_party/blink/renderer/core/dom/dataset_dom_string_map.h"
#include "third_party/blink/renderer/core/dom/attribute.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
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 string_builder;
unsigned length = name.length();
for (unsigned i = 5; i < length; ++i) {
UChar character = name[i];
if (character != '-') {
string_builder.Append(character);
} else {
if ((i + 1 < length) && IsASCIILower(name[i + 1])) {
string_builder.Append(ToASCIIUpper(name[i + 1]));
++i;
} else {
string_builder.Append(character);
}
}
}
return string_builder.ToString();
}
template <typename CharType1, typename CharType2>
static bool PropertyNameMatchesAttributeName(const CharType1* property_name,
const CharType2* attribute_name,
unsigned property_length,
unsigned attribute_length) {
unsigned a = 5;
unsigned p = 0;
bool word_boundary = false;
while (a < attribute_length && p < property_length) {
if (attribute_name[a] == '-' && a + 1 < attribute_length &&
IsASCIILower(attribute_name[a + 1])) {
word_boundary = true;
} else {
if ((word_boundary ? ToASCIIUpper(attribute_name[a])
: attribute_name[a]) != property_name[p])
return false;
p++;
word_boundary = false;
}
a++;
}
return (a == attribute_length && p == property_length);
}
static bool PropertyNameMatchesAttributeName(const String& property_name,
const String& attribute_name) {
if (!attribute_name.StartsWith("data-"))
return false;
unsigned property_length = property_name.length();
unsigned attribute_length = attribute_name.length();
if (property_name.Is8Bit()) {
if (attribute_name.Is8Bit())
return PropertyNameMatchesAttributeName(
property_name.Characters8(), attribute_name.Characters8(),
property_length, attribute_length);
return PropertyNameMatchesAttributeName(property_name.Characters8(),
attribute_name.Characters16(),
property_length, attribute_length);
}
if (attribute_name.Is8Bit())
return PropertyNameMatchesAttributeName(property_name.Characters16(),
attribute_name.Characters8(),
property_length, attribute_length);
return PropertyNameMatchesAttributeName(property_name.Characters16(),
attribute_name.Characters16(),
property_length, attribute_length);
}
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;
}
// This returns an AtomicString because attribute names are always stored
// as AtomicString types in Element (see setAttribute()).
static AtomicString ConvertPropertyNameToAttributeName(const String& name) {
StringBuilder builder;
builder.Append("data-");
unsigned length = name.length();
for (unsigned i = 0; i < length; ++i) {
UChar character = name[i];
if (IsASCIIUpper(character)) {
builder.Append('-');
builder.Append(ToASCIILower(character));
} else {
builder.Append(character);
}
}
return builder.ToAtomicString();
}
void DatasetDOMStringMap::GetNames(Vector<String>& names) {
AttributeCollection attributes = element_->Attributes();
for (const Attribute& attr : attributes) {
if (IsValidAttributeName(attr.LocalName()))
names.push_back(ConvertAttributeNameToPropertyName(attr.LocalName()));
}
}
String DatasetDOMStringMap::item(const String& name) {
AttributeCollection attributes = element_->Attributes();
for (const Attribute& attr : attributes) {
if (PropertyNameMatchesAttributeName(name, attr.LocalName()))
return attr.Value();
}
return String();
}
bool DatasetDOMStringMap::Contains(const String& name) {
AttributeCollection attributes = element_->Attributes();
for (const Attribute& attr : attributes) {
if (PropertyNameMatchesAttributeName(name, attr.LocalName()))
return true;
}
return false;
}
void DatasetDOMStringMap::SetItem(const String& name,
const String& value,
ExceptionState& exception_state) {
if (!IsValidPropertyName(name)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"'" + name + "' is not a valid property name.");
return;
}
element_->setAttribute(ConvertPropertyNameToAttributeName(name),
AtomicString(value), exception_state);
}
bool DatasetDOMStringMap::DeleteItem(const String& name) {
if (IsValidPropertyName(name)) {
AtomicString attribute_name = ConvertPropertyNameToAttributeName(name);
if (element_->hasAttribute(attribute_name)) {
element_->removeAttribute(attribute_name);
return true;
}
}
return false;
}
void DatasetDOMStringMap::Trace(blink::Visitor* visitor) {
visitor->Trace(element_);
DOMStringMap::Trace(visitor);
}
} // namespace blink