// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. #include "include/wrapper/cef_xml_object.h" #include #include "include/base/cef_logging.h" #include "include/base/cef_macros.h" #include "include/cef_stream.h" namespace { class CefXmlObjectLoader { public: explicit CefXmlObjectLoader(CefRefPtr root_object) : root_object_(root_object) {} bool Load(CefRefPtr stream, CefXmlReader::EncodingType encodingType, const CefString& URI) { CefRefPtr reader( CefXmlReader::Create(stream, encodingType, URI)); if (!reader.get()) return false; bool ret = reader->MoveToNextNode(); if (ret) { CefRefPtr cur_object(root_object_), new_object; CefXmlObject::ObjectVector queue; int cur_depth, value_depth = -1; CefXmlReader::NodeType cur_type; std::stringstream cur_value; bool last_has_ns = false; queue.push_back(root_object_); do { cur_depth = reader->GetDepth(); if (value_depth >= 0 && cur_depth > value_depth) { // The current node has already been parsed as part of a value. continue; } cur_type = reader->GetType(); if (cur_type == XML_NODE_ELEMENT_START) { if (cur_depth == value_depth) { // Add to the current value. cur_value << std::string(reader->GetOuterXml()); continue; } else if (last_has_ns && reader->GetPrefix().empty()) { if (!cur_object->HasChildren()) { // Start a new value because the last element has a namespace and // this element does not. value_depth = cur_depth; cur_value << std::string(reader->GetOuterXml()); } else { // Value following a child element is not allowed. std::stringstream ss; ss << "Value following child element, line " << reader->GetLineNumber(); load_error_ = ss.str(); ret = false; break; } } else { // Start a new element. new_object = new CefXmlObject(reader->GetQualifiedName()); cur_object->AddChild(new_object); last_has_ns = !reader->GetPrefix().empty(); if (!reader->IsEmptyElement()) { // The new element potentially has a value and/or children, so // set the current object and add the object to the queue. cur_object = new_object; queue.push_back(cur_object); } if (reader->HasAttributes() && reader->MoveToFirstAttribute()) { // Read all object attributes. do { new_object->SetAttributeValue(reader->GetQualifiedName(), reader->GetValue()); } while (reader->MoveToNextAttribute()); reader->MoveToCarryingElement(); } } } else if (cur_type == XML_NODE_ELEMENT_END) { if (cur_depth == value_depth) { // Ending an element that is already in the value. continue; } else if (cur_depth < value_depth) { // Done with parsing the value portion of the current element. cur_object->SetValue(cur_value.str()); cur_value.str(""); value_depth = -1; } // Pop the current element from the queue. queue.pop_back(); if (queue.empty() || cur_object->GetName() != reader->GetQualifiedName()) { // Open tag without close tag or close tag without open tag should // never occur (the parser catches this error). NOTREACHED(); std::stringstream ss; ss << "Mismatched end tag for " << std::string(cur_object->GetName()) << ", line " << reader->GetLineNumber(); load_error_ = ss.str(); ret = false; break; } // Set the current object to the previous object in the queue. cur_object = queue.back().get(); } else if (cur_type == XML_NODE_TEXT || cur_type == XML_NODE_CDATA || cur_type == XML_NODE_ENTITY_REFERENCE) { if (cur_depth == value_depth) { // Add to the current value. cur_value << std::string(reader->GetValue()); } else if (!cur_object->HasChildren()) { // Start a new value. value_depth = cur_depth; cur_value << std::string(reader->GetValue()); } else { // Value following a child element is not allowed. std::stringstream ss; ss << "Value following child element, line " << reader->GetLineNumber(); load_error_ = ss.str(); ret = false; break; } } } while (reader->MoveToNextNode()); } if (reader->HasError()) { load_error_ = reader->GetError(); return false; } return ret; } CefString GetLoadError() { return load_error_; } private: CefString load_error_; CefRefPtr root_object_; DISALLOW_COPY_AND_ASSIGN(CefXmlObjectLoader); }; } // namespace CefXmlObject::CefXmlObject(const CefString& name) : name_(name), parent_(nullptr) {} CefXmlObject::~CefXmlObject() {} bool CefXmlObject::Load(CefRefPtr stream, CefXmlReader::EncodingType encodingType, const CefString& URI, CefString* loadError) { Clear(); CefXmlObjectLoader loader(this); if (!loader.Load(stream, encodingType, URI)) { if (loadError) *loadError = loader.GetLoadError(); return false; } return true; } void CefXmlObject::Set(CefRefPtr object) { DCHECK(object.get()); Clear(); name_ = object->GetName(); Append(object, true); } void CefXmlObject::Append(CefRefPtr object, bool overwriteAttributes) { DCHECK(object.get()); if (object->HasChildren()) { ObjectVector children; object->GetChildren(children); ObjectVector::const_iterator it = children.begin(); for (; it != children.end(); ++it) AddChild((*it)->Duplicate()); } if (object->HasAttributes()) { AttributeMap attributes; object->GetAttributes(attributes); AttributeMap::const_iterator it = attributes.begin(); for (; it != attributes.end(); ++it) { if (overwriteAttributes || !HasAttribute(it->first)) SetAttributeValue(it->first, it->second); } } } CefRefPtr CefXmlObject::Duplicate() { CefRefPtr new_obj; { base::AutoLock lock_scope(lock_); new_obj = new CefXmlObject(name_); new_obj->Append(this, true); } return new_obj; } void CefXmlObject::Clear() { ClearChildren(); ClearAttributes(); } CefString CefXmlObject::GetName() { CefString name; { base::AutoLock lock_scope(lock_); name = name_; } return name; } bool CefXmlObject::SetName(const CefString& name) { DCHECK(!name.empty()); if (name.empty()) return false; base::AutoLock lock_scope(lock_); name_ = name; return true; } bool CefXmlObject::HasParent() { base::AutoLock lock_scope(lock_); return (parent_ != nullptr); } CefRefPtr CefXmlObject::GetParent() { CefRefPtr parent; { base::AutoLock lock_scope(lock_); parent = parent_; } return parent; } bool CefXmlObject::HasValue() { base::AutoLock lock_scope(lock_); return !value_.empty(); } CefString CefXmlObject::GetValue() { CefString value; { base::AutoLock lock_scope(lock_); value = value_; } return value; } bool CefXmlObject::SetValue(const CefString& value) { base::AutoLock lock_scope(lock_); DCHECK(children_.empty()); if (!children_.empty()) return false; value_ = value; return true; } bool CefXmlObject::HasAttributes() { base::AutoLock lock_scope(lock_); return !attributes_.empty(); } size_t CefXmlObject::GetAttributeCount() { base::AutoLock lock_scope(lock_); return attributes_.size(); } bool CefXmlObject::HasAttribute(const CefString& name) { if (name.empty()) return false; base::AutoLock lock_scope(lock_); AttributeMap::const_iterator it = attributes_.find(name); return (it != attributes_.end()); } CefString CefXmlObject::GetAttributeValue(const CefString& name) { DCHECK(!name.empty()); CefString value; if (!name.empty()) { base::AutoLock lock_scope(lock_); AttributeMap::const_iterator it = attributes_.find(name); if (it != attributes_.end()) value = it->second; } return value; } bool CefXmlObject::SetAttributeValue(const CefString& name, const CefString& value) { DCHECK(!name.empty()); if (name.empty()) return false; base::AutoLock lock_scope(lock_); AttributeMap::iterator it = attributes_.find(name); if (it != attributes_.end()) { it->second = value; } else { attributes_.insert(std::make_pair(name, value)); } return true; } size_t CefXmlObject::GetAttributes(AttributeMap& attributes) { base::AutoLock lock_scope(lock_); attributes = attributes_; return attributes_.size(); } void CefXmlObject::ClearAttributes() { base::AutoLock lock_scope(lock_); attributes_.clear(); } bool CefXmlObject::HasChildren() { base::AutoLock lock_scope(lock_); return !children_.empty(); } size_t CefXmlObject::GetChildCount() { base::AutoLock lock_scope(lock_); return children_.size(); } bool CefXmlObject::HasChild(CefRefPtr child) { DCHECK(child.get()); base::AutoLock lock_scope(lock_); ObjectVector::const_iterator it = children_.begin(); for (; it != children_.end(); ++it) { if ((*it).get() == child.get()) return true; } return false; } bool CefXmlObject::AddChild(CefRefPtr child) { DCHECK(child.get()); if (!child.get()) return false; CefRefPtr parent = child->GetParent(); DCHECK(!parent); if (parent) return false; base::AutoLock lock_scope(lock_); children_.push_back(child); child->SetParent(this); return true; } bool CefXmlObject::RemoveChild(CefRefPtr child) { DCHECK(child.get()); base::AutoLock lock_scope(lock_); ObjectVector::iterator it = children_.begin(); for (; it != children_.end(); ++it) { if ((*it).get() == child.get()) { children_.erase(it); child->SetParent(nullptr); return true; } } return false; } size_t CefXmlObject::GetChildren(ObjectVector& children) { base::AutoLock lock_scope(lock_); children = children_; return children_.size(); } void CefXmlObject::ClearChildren() { base::AutoLock lock_scope(lock_); ObjectVector::iterator it = children_.begin(); for (; it != children_.end(); ++it) (*it)->SetParent(nullptr); children_.clear(); } CefRefPtr CefXmlObject::FindChild(const CefString& name) { DCHECK(!name.empty()); if (name.empty()) return nullptr; base::AutoLock lock_scope(lock_); ObjectVector::const_iterator it = children_.begin(); for (; it != children_.end(); ++it) { if ((*it)->GetName() == name) return (*it); } return nullptr; } size_t CefXmlObject::FindChildren(const CefString& name, ObjectVector& children) { DCHECK(!name.empty()); if (name.empty()) return 0; size_t ct = 0; base::AutoLock lock_scope(lock_); ObjectVector::const_iterator it = children_.begin(); for (; it != children_.end(); ++it) { if ((*it)->GetName() == name) { children.push_back(*it); ct++; } } return ct; } void CefXmlObject::SetParent(CefXmlObject* parent) { base::AutoLock lock_scope(lock_); if (parent) { DCHECK(parent_ == nullptr); parent_ = parent; } else { DCHECK(parent_ != nullptr); parent_ = nullptr; } }