blob: e2d71c20d324be5ab8624c6f6dc57efecbe307f9 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/dom_string_map.h"
#include "base/string_util.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/global_stats.h"
namespace cobalt {
namespace dom {
namespace {
const char kDataPrefix[] = "data-";
// Subtract one for nul terminator.
const size_t kDataPrefixLength = sizeof(kDataPrefix) - 1;
// See "The algorithm for getting the list of name-value pairs" at
// https://www.w3.org/TR/html5/dom.html#dom-dataset.
base::optional<std::string> TryConvertAttributeNameToPropertyName(
const std::string& attribute_name) {
// First five characters of attribute name should be "data-".
if (attribute_name.compare(0, kDataPrefixLength, kDataPrefix) != 0) {
return base::nullopt;
}
// For each "-" (U+002D) character in the name that is followed by
// a lowercase ASCII letter, remove the "-" (U+002D) character and replace
// the character that followed it by the same character converted to ASCII
// uppercase.
std::string property_name;
bool preceded_by_hyphen = false;
for (std::string::const_iterator attribute_name_iterator =
attribute_name.begin() + kDataPrefixLength;
attribute_name_iterator != attribute_name.end();
++attribute_name_iterator) {
char attribute_name_character = *attribute_name_iterator;
if (attribute_name_character == '-') {
// Double hyphen in attribute name, preserve it.
if (preceded_by_hyphen) {
property_name += '-';
} else {
preceded_by_hyphen = true;
}
continue;
}
// Attribute name should not contain uppercase ASCII characters.
if (base::ToLowerASCII(attribute_name_character) !=
attribute_name_character) {
return base::nullopt;
}
// Convert to uppercase character if preceded by hyphen.
char property_name_character = attribute_name_character;
if (preceded_by_hyphen) {
preceded_by_hyphen = false;
property_name_character = base::ToUpperASCII(property_name_character);
// Non-letter character after hyphen, preserve the hyphen.
if (property_name_character == attribute_name_character) {
property_name += '-';
}
}
property_name += property_name_character;
}
if (preceded_by_hyphen) {
property_name += '-';
}
return property_name;
}
// See "The algorithm for setting names to certain values" at
// https://www.w3.org/TR/html5/dom.html#dom-dataset.
base::optional<std::string> TryConvertPropertyNameToAttributeName(
const std::string& property_name) {
// Insert the string "data-" at the front of attribute name.
std::string attribute_name = kDataPrefix;
bool preceded_by_hyphen = false;
for (std::string::const_iterator property_name_iterator =
property_name.begin();
property_name_iterator != property_name.end();
++property_name_iterator) {
char property_name_character = *property_name_iterator;
// If property name contains a "-" (U+002D) character followed by
// a lowercase ASCII letter, abort these steps.
if (preceded_by_hyphen &&
base::ToUpperASCII(property_name_character) !=
property_name_character) {
return base::nullopt;
}
// For each uppercase ASCII letter in name, insert a "-" (U+002D) character
// before the character and replace the character with the same character
// converted to ASCII lowercase.
if (base::ToLowerASCII(property_name_character) !=
property_name_character) {
attribute_name += '-';
attribute_name += base::ToLowerASCII(property_name_character);
} else {
attribute_name += property_name_character;
}
preceded_by_hyphen = property_name_character == '-';
}
return attribute_name;
}
} // namespace
DOMStringMap::DOMStringMap(const scoped_refptr<Element>& element)
: element_(element) {
GlobalStats::GetInstance()->Add(this);
}
base::optional<std::string> DOMStringMap::AnonymousNamedGetter(
const std::string& property_name, script::ExceptionState* exception_state) {
base::optional<std::string> attribute_name =
TryConvertPropertyNameToAttributeName(property_name);
if (attribute_name) {
return element_->GetAttribute(*attribute_name);
} else {
exception_state->SetSimpleException(script::kSyntaxError,
property_name.c_str());
return base::nullopt;
}
}
void DOMStringMap::AnonymousNamedSetter(
const std::string& property_name, const std::string& value,
script::ExceptionState* exception_state) {
base::optional<std::string> attribute_name =
TryConvertPropertyNameToAttributeName(property_name);
if (attribute_name) {
element_->SetAttribute(*attribute_name, value);
} else {
exception_state->SetSimpleException(script::kSyntaxError,
property_name.c_str());
}
}
bool DOMStringMap::CanQueryNamedProperty(
const std::string& property_name) const {
base::optional<std::string> attribute_name =
TryConvertPropertyNameToAttributeName(property_name);
// TODO: Throw a SyntaxError if attribute name is invalid once getters and
// setters support throwing exceptions.
return attribute_name && element_->HasAttribute(*attribute_name);
}
void DOMStringMap::EnumerateNamedProperties(
script::PropertyEnumerator* enumerator) {
for (Element::AttributeMap::const_iterator
attribute_iterator = element_->attribute_map().begin(),
attribute_end_iterator = element_->attribute_map().end();
attribute_iterator != attribute_end_iterator; ++attribute_iterator) {
base::optional<std::string> property_name =
TryConvertAttributeNameToPropertyName(attribute_iterator->first);
if (property_name) {
enumerator->AddProperty(*property_name);
}
}
}
DOMStringMap::~DOMStringMap() { GlobalStats::GetInstance()->Remove(this); }
} // namespace dom
} // namespace cobalt