| // 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 |