blob: fb483fe8fe10226816c966fd757d49ec958e2a9f [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/data_decoder/public/cpp/safe_xml_parser.h"
#include <string_view>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/threading/thread_checker.h"
#include "base/values.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/data_decoder/public/mojom/xml_parser.mojom.h"
namespace data_decoder {
const base::Value::List* GetXmlElementChildren(const base::Value& element) {
if (!element.is_dict())
return nullptr;
return element.GetDict().FindList(mojom::XmlParser::kChildrenKey);
}
std::string GetXmlQualifiedName(const std::string& name_space,
const std::string& name) {
return name_space.empty() ? name : name_space + ":" + name;
}
bool IsXmlElementNamed(const base::Value& element, const std::string& name) {
if (!element.is_dict())
return false;
const std::string* tag_text =
element.GetDict().FindString(mojom::XmlParser::kTagKey);
return tag_text && *tag_text == name;
}
bool IsXmlElementOfType(const base::Value& element, const std::string& type) {
if (!element.is_dict())
return false;
const std::string* type_text =
element.GetDict().FindString(mojom::XmlParser::kTypeKey);
return type_text && *type_text == type;
}
bool GetXmlElementTagName(const base::Value& element, std::string* tag_name) {
DCHECK(tag_name);
if (!element.is_dict())
return false;
const std::string* tag_text =
element.GetDict().FindString(mojom::XmlParser::kTagKey);
if (!tag_text)
return false;
*tag_name = *tag_text;
return true;
}
bool GetXmlElementText(const base::Value& element, std::string* text) {
DCHECK(text);
const base::Value::List* children = GetXmlElementChildren(element);
if (!children)
return false;
const base::Value* text_node = nullptr;
for (const base::Value& value : *children) {
if (IsXmlElementOfType(value, mojom::XmlParser::kTextNodeType) ||
IsXmlElementOfType(value, mojom::XmlParser::kCDataNodeType)) {
text_node = &value;
break;
}
}
if (!text_node || !text_node->is_dict()) {
return false;
}
const std::string* text_value =
text_node->GetDict().FindString(mojom::XmlParser::kTextKey);
*text = text_value ? *text_value : "";
return true;
}
bool GetXmlElementNamespacePrefix(const base::Value& element,
const std::string& namespace_uri,
std::string* prefix) {
if (!element.is_dict()) {
return false;
}
prefix->clear();
const base::Value::Dict* namespaces =
element.GetDict().FindDict(mojom::XmlParser::kNamespacesKey);
if (!namespaces)
return false;
// The namespaces dictionary is prefix -> URI, so we have to do a reverse
// lookup.
for (auto item : *namespaces) {
if (item.second.GetString() == namespace_uri) {
*prefix = item.first;
return true;
}
}
return false;
}
int GetXmlElementChildrenCount(const base::Value& element,
const std::string& name) {
const base::Value::List* children = GetXmlElementChildren(element);
if (!children)
return 0;
int child_count = 0;
for (const base::Value& value : *children) {
DCHECK(value.is_dict());
std::string tag_name;
bool success = GetXmlElementTagName(value, &tag_name);
if (success && tag_name == name)
child_count++;
}
return child_count;
}
const base::Value* GetXmlElementChildWithType(const base::Value& element,
const std::string& type) {
const base::Value::List* children = GetXmlElementChildren(element);
if (!children)
return nullptr;
for (const base::Value& value : *children) {
DCHECK(value.is_dict());
if (IsXmlElementOfType(value, type)) {
return &value;
}
}
return nullptr;
}
const base::Value* GetXmlElementChildWithTag(const base::Value& element,
const std::string& tag) {
const base::Value::List* children = GetXmlElementChildren(element);
if (!children)
return nullptr;
for (const base::Value& value : *children) {
DCHECK(value.is_dict());
if (IsXmlElementNamed(value, tag))
return &value;
}
return nullptr;
}
bool GetAllXmlElementChildrenWithTag(
const base::Value& element,
const std::string& tag,
std::vector<const base::Value*>* children_out) {
const base::Value::List* children = GetXmlElementChildren(element);
if (!children)
return false;
bool found = false;
for (const base::Value& child : *children) {
DCHECK(child.is_dict());
if (IsXmlElementNamed(child, tag)) {
found = true;
children_out->push_back(&child);
}
}
return found;
}
const base::Value* FindXmlElementPath(
const base::Value& element,
std::initializer_list<std::string_view> path,
bool* unique_path) {
const base::Value* cur = nullptr;
if (unique_path)
*unique_path = true;
for (const std::string_view component_piece : path) {
std::string component(component_piece);
if (!cur) {
// First element has to match the current node.
if (!IsXmlElementNamed(element, component))
return nullptr;
cur = &element;
continue;
}
const base::Value* new_cur = GetXmlElementChildWithTag(*cur, component);
if (!new_cur)
return nullptr;
if (unique_path && *unique_path &&
GetXmlElementChildrenCount(*cur, component) > 1)
*unique_path = false;
cur = new_cur;
}
return cur;
}
std::string GetXmlElementAttribute(const base::Value& element,
const std::string& element_name) {
if (!element.is_dict())
return "";
const base::Value::Dict* attributes =
element.GetDict().FindDict(mojom::XmlParser::kAttributesKey);
if (!attributes)
return "";
const std::string* value = attributes->FindString(element_name);
return value ? *value : "";
}
} // namespace data_decoder