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

Commit cb76d734 authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

AAPT2: Fix unrecognized CDATA

This change adds support for resources that have CDATA blocks within
their values. The blocks should allow any character to occur without
being escaped. It also should not effect the current state of quote
processing.

Bug: 80326349
Test: Created tests in aapt2_tests
Change-Id: Ie1a00e50cffc877e2eb5f788f8d7a1bda839c0cf
parent e4286897
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -208,6 +208,15 @@ class SegmentNode : public Node {
  }
};

// A chunk of text in the XML string within a CDATA tags.
class CdataSegmentNode : public SegmentNode {
 public:

  void Build(StringBuilder* builder) const override {
    builder->AppendText(data, /* preserve_spaces */ true);
  }
};

// A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
class SpanNode : public Node {
 public:
@@ -244,6 +253,7 @@ bool ResourceParser::FlattenXmlSubtree(
  std::vector<Node*> node_stack;
  node_stack.push_back(&root);

  bool cdata_block = false;
  bool saw_span_node = false;
  SegmentNode* first_segment = nullptr;
  SegmentNode* last_segment = nullptr;
@@ -253,11 +263,15 @@ bool ResourceParser::FlattenXmlSubtree(
    const xml::XmlPullParser::Event event = parser->event();

    // First take care of any SegmentNodes that should be created.
    if (event == xml::XmlPullParser::Event::kStartElement ||
        event == xml::XmlPullParser::Event::kEndElement) {
    if (event == xml::XmlPullParser::Event::kStartElement
        || event == xml::XmlPullParser::Event::kEndElement
        || event == xml::XmlPullParser::Event::kCdataStart
        || event == xml::XmlPullParser::Event::kCdataEnd) {
      if (!current_text.empty()) {
        std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
        std::unique_ptr<SegmentNode> segment_node = (cdata_block)
            ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>();
        segment_node->data = std::move(current_text);

        last_segment = node_stack.back()->AddChild(std::move(segment_node));
        if (first_segment == nullptr) {
          first_segment = last_segment;
@@ -333,6 +347,16 @@ bool ResourceParser::FlattenXmlSubtree(
        }
      } break;

      case xml::XmlPullParser::Event::kCdataStart: {
        cdata_block = true;
        break;
      }

      case xml::XmlPullParser::Event::kCdataEnd: {
        cdata_block = false;
        break;
      }

      default:
        // ignore.
        break;
+36 −0
Original line number Diff line number Diff line
@@ -971,4 +971,40 @@ TEST_F(ResourceParserTest, ParseIdItem) {
  ASSERT_FALSE(TestParse(input));
}

TEST_F(ResourceParserTest, ParseCData) {
  std::string input = R"(
      <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)";

  ASSERT_TRUE(TestParse(input));
  String* output = test::GetValue<String>(&table_, "string/foo");
  ASSERT_THAT(output, NotNull());
  EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe"));

  // Double quotes should not change the state of whitespace processing
  input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]>      World</string>)";
  ASSERT_TRUE(TestParse(input));
  output = test::GetValue<String>(&table_, "string/foo2");
  ASSERT_THAT(output, NotNull());
  EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>'  World").data()));

  // Cdata blocks should not have their whitespace trimmed
  input = R"(<string name="foo3">     <![CDATA[ text ]]>     </string>)";
  ASSERT_TRUE(TestParse(input));
  output = test::GetValue<String>(&table_, "string/foo3");
  ASSERT_THAT(output, NotNull());
  EXPECT_THAT(*output, StrValueEq(std::string(" text ").data()));

  input = R"(<string name="foo4">     <![CDATA[]]>     </string>)";
  ASSERT_TRUE(TestParse(input));
  output = test::GetValue<String>(&table_, "string/foo4");
  ASSERT_THAT(output, NotNull());
  EXPECT_THAT(*output, StrValueEq(std::string("").data()));

  input = R"(<string name="foo5">     <![CDATA[    ]]>     </string>)";
  ASSERT_TRUE(TestParse(input));
  output = test::GetValue<String>(&table_, "string/foo5");
  ASSERT_THAT(output, NotNull());
  EXPECT_THAT(*output, StrValueEq(std::string("    ").data()));
}

}  // namespace aapt
+9 −6
Original line number Diff line number Diff line
@@ -797,16 +797,20 @@ StringBuilder::StringBuilder(bool preserve_spaces)
    : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
}

StringBuilder& StringBuilder::AppendText(const std::string& text) {
StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
  if (!error_.empty()) {
    return *this;
  }

  // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
  // to preserve spaces
  preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;

  const size_t previous_len = xml_string_.text.size();
  Utf8Iterator iter(text);
  while (iter.HasNext()) {
    char32_t codepoint = iter.Next();
    if (!quote_ && iswspace(codepoint)) {
    if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
      if (!last_codepoint_was_space_) {
        // Emit a space if it's the first.
        xml_string_.text += ' ';
@@ -827,7 +831,6 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) {
          case U't':
            xml_string_.text += '\t';
            break;

          case U'n':
            xml_string_.text += '\n';
            break;
@@ -855,12 +858,12 @@ StringBuilder& StringBuilder::AppendText(const std::string& text) {
            break;
        }
      }
    } else if (!preserve_spaces_ && codepoint == U'"') {
    } else if (!preserve_spaces && codepoint == U'"') {
      // Only toggle the quote state when we are not preserving spaces.
      quote_ = !quote_;

    } else if (!quote_ && codepoint == U'\'') {
      // This should be escaped.
    } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
      // This should be escaped when we are not preserving spaces
      error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
      return *this;

+4 −2
Original line number Diff line number Diff line
@@ -267,8 +267,10 @@ class StringBuilder {
  // single quotations can be used without escaping them.
  explicit StringBuilder(bool preserve_spaces = false);

  // Appends a chunk of text.
  StringBuilder& AppendText(const std::string& text);
  // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and
  // single quotations can be used without escaping them for this append. Otherwise, the
  // StringBuilder will behave as it was constructed.
  StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false);

  // Starts a Span (tag) with the given name. The name is expected to be of the form:
  //  "tag_name;attr1=value;attr2=value;"
+23 −0
Original line number Diff line number Diff line
@@ -254,6 +254,29 @@ TEST(ResourceUtilsTest, StringBuilderUnicodeCodes) {
TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
  EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
              Eq("\""));

  // Single quotes should be able to be used without escaping them when preserving spaces and the
  // spaces should not be trimmed
  EXPECT_THAT(ResourceUtils::StringBuilder()
                  .AppendText("    hey guys ")
                  .AppendText(" 'this is so cool' ", /* preserve_spaces */ true)
                  .AppendText(" wow    ")
                  .to_string(),
              Eq(" hey guys  'this is so cool'  wow "));

  // Reading a double quote while preserving spaces should not change the quote state
  EXPECT_THAT(ResourceUtils::StringBuilder()
                  .AppendText("    hey guys ")
                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
                  .AppendText(" wow    ")
                  .to_string(),
              Eq(" hey guys  \"this is so cool'  wow "));
  EXPECT_THAT(ResourceUtils::StringBuilder()
                  .AppendText("    hey guys\"  ")
                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
                  .AppendText(" wow  \"  ")
                  .to_string(),
              Eq(" hey guys   \"this is so cool'  wow   "));
}

}  // namespace aapt
Loading