Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac6edc50 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Fix escaping sequence processing in XML parsing

Bug: 35483813
Test: make aapt2_tests
Change-Id: I68baba17ab3639c220b734a2a68d86aad0dedf8c
parent 555bf410
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
            <View xmlns:test="http://com.test"
                  attr="hey">
              <Layout test:hello="hi" />
              <Layout>Some text</Layout>
              <Layout>Some text\\</Layout>
            </View>)EOF");

  android::ResXMLTree tree;
@@ -128,7 +128,7 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {

  ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
  const char16_t* text = tree.getText(&len);
  EXPECT_EQ(StringPiece16(text, len), u"Some text");
  EXPECT_EQ(StringPiece16(text, len), u"Some text\\");

  ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
  ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+3 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ class StringBuilder {
  StringBuilder& Append(const android::StringPiece& str);
  const std::string& ToString() const;
  const std::string& Error() const;
  bool IsEmpty() const;

  // When building StyledStrings, we need UTF-16 indices into the string,
  // which is what the Java layer expects when dealing with java
@@ -185,6 +186,8 @@ inline const std::string& StringBuilder::ToString() const { return str_; }

inline const std::string& StringBuilder::Error() const { return error_; }

inline bool StringBuilder::IsEmpty() const { return str_.empty(); }

inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }

inline StringBuilder::operator bool() const { return error_.empty(); }
+33 −17
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@

#include <expat.h>

#include <cassert>
#include <memory>
#include <stack>
#include <string>
@@ -41,6 +40,8 @@ struct Stack {
  std::unique_ptr<xml::Node> root;
  std::stack<xml::Node*> node_stack;
  std::string pending_comment;
  std::unique_ptr<xml::Text> last_text_node;
  util::StringBuilder pending_text;
};

/**
@@ -62,6 +63,19 @@ static void SplitName(const char* name, std::string* out_ns,
  }
}

static void FinishPendingText(Stack* stack) {
  if (stack->last_text_node != nullptr) {
    if (!stack->pending_text.IsEmpty()) {
      stack->last_text_node->text = stack->pending_text.ToString();
      stack->pending_text = {};
      stack->node_stack.top()->AppendChild(std::move(stack->last_text_node));
    } else {
      // Drop an empty text node.
      stack->last_text_node = nullptr;
    }
  }
}

static void AddToStack(Stack* stack, XML_Parser parser,
                       std::unique_ptr<Node> node) {
  node->line_number = XML_GetCurrentLineNumber(parser);
@@ -83,6 +97,7 @@ static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
                                          const char* uri) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
  FinishPendingText(stack);

  std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
  if (prefix) {
@@ -99,6 +114,7 @@ static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
  FinishPendingText(stack);

  CHECK(!stack->node_stack.empty());
  stack->node_stack.pop();
@@ -113,6 +129,7 @@ static void XMLCALL StartElementHandler(void* user_data, const char* name,
                                        const char** attrs) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
  FinishPendingText(stack);

  std::unique_ptr<Element> el = util::make_unique<Element>();
  SplitName(name, &el->namespace_uri, &el->name);
@@ -120,7 +137,9 @@ static void XMLCALL StartElementHandler(void* user_data, const char* name,
  while (*attrs) {
    Attribute attribute;
    SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
    attribute.value = *attrs++;
    util::StringBuilder builder;
    builder.Append(*attrs++);
    attribute.value = builder.ToString();

    // Insert in sorted order.
    auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
@@ -135,41 +154,38 @@ static void XMLCALL StartElementHandler(void* user_data, const char* name,
static void XMLCALL EndElementHandler(void* user_data, const char* name) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
  FinishPendingText(stack);

  CHECK(!stack->node_stack.empty());
  // stack->nodeStack.top()->comment = std::move(stack->pendingComment);
  stack->node_stack.pop();
}

static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
                                         int len) {
static void XMLCALL CharacterDataHandler(void* user_data, const char* s, int len) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));

  if (!s || len <= 0) {
  const StringPiece str(s, len);
  if (str.empty()) {
    return;
  }

  // See if we can just append the text to a previous text node.
  if (!stack->node_stack.empty()) {
    Node* currentParent = stack->node_stack.top();
    if (!currentParent->children.empty()) {
      Node* last_child = currentParent->children.back().get();
      if (Text* text = NodeCast<Text>(last_child)) {
        text->text.append(s, len);
  if (stack->last_text_node != nullptr) {
    stack->pending_text.Append(str);
    return;
  }
    }
  }

  std::unique_ptr<Text> text = util::make_unique<Text>();
  text->text.assign(s, len);
  AddToStack(stack, parser, std::move(text));
  stack->last_text_node = util::make_unique<Text>();
  stack->last_text_node->line_number = XML_GetCurrentLineNumber(parser);
  stack->last_text_node->column_number = XML_GetCurrentColumnNumber(parser);
  stack->pending_text.Append(str);
}

static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {
  XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
  Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
  FinishPendingText(stack);

  if (!stack->pending_comment.empty()) {
    stack->pending_comment += '\n';
+19 −0
Original line number Diff line number Diff line
@@ -49,4 +49,23 @@ TEST(XmlDomTest, Inflate) {
  EXPECT_EQ(ns->namespace_prefix, "android");
}

TEST(XmlDomTest, HandleEscapes) {
  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
      R"EOF(<shortcode pattern="\\d{5}">\\d{5}</shortcode>)EOF");

  xml::Element* el = xml::FindRootElement(doc->root.get());
  ASSERT_NE(nullptr, el);

  xml::Attribute* attr = el->FindAttribute({}, "pattern");
  ASSERT_NE(nullptr, attr);

  EXPECT_EQ("\\d{5}", attr->value);

  ASSERT_EQ(1u, el->children.size());

  xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
  ASSERT_NE(nullptr, text);
  EXPECT_EQ("\\d{5}", text->text);
}

}  // namespace aapt