910e62b5创建于 1月15日历史提交
// 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