Merge "AAPT2: Fix string escaping" into oc-dev

This commit is contained in:
TreeHugger Robot
2017-04-29 21:04:34 +00:00
committed by Android (Google) Code Review
5 changed files with 129 additions and 85 deletions

View File

@@ -99,7 +99,11 @@ class XmlFlattenerVisitor : public xml::Visitor {
flat_node->comment.index = util::HostToDevice32(-1);
ResXMLTree_cdataExt* flat_text = writer.NextBlock<ResXMLTree_cdataExt>();
AddString(node->text, kLowPriority, &flat_text->data);
// Process plain strings to make sure they get properly escaped.
util::StringBuilder builder;
builder.Append(node->text);
AddString(builder.ToString(), kLowPriority, &flat_text->data);
writer.Finish();
}
@@ -184,17 +188,14 @@ class XmlFlattenerVisitor : public xml::Visitor {
writer.Finish();
}
void WriteAttributes(xml::Element* node, ResXMLTree_attrExt* flat_elem,
ChunkWriter* writer) {
void WriteAttributes(xml::Element* node, ResXMLTree_attrExt* flat_elem, ChunkWriter* writer) {
filtered_attrs_.clear();
filtered_attrs_.reserve(node->attributes.size());
// Filter the attributes.
for (xml::Attribute& attr : node->attributes) {
if (options_.max_sdk_level && attr.compiled_attribute &&
attr.compiled_attribute.value().id) {
size_t sdk_level =
FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
if (options_.max_sdk_level && attr.compiled_attribute && attr.compiled_attribute.value().id) {
size_t sdk_level = FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
if (sdk_level > options_.max_sdk_level.value()) {
continue;
}
@@ -211,8 +212,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
const ResourceId kIdAttr(0x010100d0);
std::sort(filtered_attrs_.begin(), filtered_attrs_.end(),
cmp_xml_attribute_by_id);
std::sort(filtered_attrs_.begin(), filtered_attrs_.end(), cmp_xml_attribute_by_id);
flat_elem->attributeCount = util::HostToDevice16(filtered_attrs_.size());
@@ -234,18 +234,15 @@ class XmlFlattenerVisitor : public xml::Visitor {
}
attribute_index++;
// Add the namespaceUri to the list of StringRefs to encode. Use null if
// the namespace
// Add the namespaceUri to the list of StringRefs to encode. Use null if the namespace
// is empty (doesn't exist).
AddString(xml_attr->namespace_uri, kLowPriority, &flat_attr->ns,
true /* treat_empty_string_as_null */);
flat_attr->rawValue.index = util::HostToDevice32(-1);
if (!xml_attr->compiled_attribute ||
!xml_attr->compiled_attribute.value().id) {
// The attribute has no associated ResourceID, so the string order
// doesn't matter.
if (!xml_attr->compiled_attribute || !xml_attr->compiled_attribute.value().id) {
// The attribute has no associated ResourceID, so the string order doesn't matter.
AddString(xml_attr->name, kLowPriority, &flat_attr->name);
} else {
// Attribute names are stored without packages, but we use
@@ -255,8 +252,7 @@ class XmlFlattenerVisitor : public xml::Visitor {
// pools that we later combine.
//
// Lookup the StringPool for this package and make the reference there.
const xml::AaptAttribute& aapt_attr =
xml_attr->compiled_attribute.value();
const xml::AaptAttribute& aapt_attr = xml_attr->compiled_attribute.value();
StringPool::Ref name_ref =
package_pools[aapt_attr.id.value().package_id()].MakeRef(
@@ -266,10 +262,18 @@ class XmlFlattenerVisitor : public xml::Visitor {
AddString(name_ref, &flat_attr->name);
}
// Process plain strings to make sure they get properly escaped.
StringPiece raw_value = xml_attr->value;
util::StringBuilder str_builder;
if (!options_.keep_raw_values) {
str_builder.Append(xml_attr->value);
raw_value = str_builder.ToString();
}
if (options_.keep_raw_values || !xml_attr->compiled_value) {
// Keep raw values if the value is not compiled or
// if we're building a static library (need symbols).
AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
AddString(raw_value, kLowPriority, &flat_attr->rawValue);
}
if (xml_attr->compiled_value) {
@@ -277,12 +281,12 @@ class XmlFlattenerVisitor : public xml::Visitor {
} else {
// Flatten as a regular string type.
flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
AddString(xml_attr->value, kLowPriority,
(ResStringPool_ref*)&flat_attr->typedValue.data);
AddString(str_builder.ToString(), kLowPriority,
(ResStringPool_ref*) &flat_attr->typedValue.data);
}
flat_attr->typedValue.size =
util::HostToDevice16(sizeof(flat_attr->typedValue));
flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
flat_attr++;
}
}

View File

@@ -82,72 +82,71 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
ASSERT_EQ(android::ResXMLTree::START_NAMESPACE, tree.next());
size_t len;
const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len));
const char16_t* namespace_uri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
const char16_t* tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"View");
EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len));
ASSERT_EQ(1u, tree.getAttributeCount());
ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
ASSERT_EQ(nullptr, tree.getAttributeNamespace(0, &len));
const char16_t* attr_name = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attr_name, len), u"attr");
EXPECT_EQ(StringPiece16(u"attr"), StringPiece16(attr_name, len));
EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr",
StringPiece16(u"attr").size()));
EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size()));
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
ASSERT_EQ(1u, tree.getAttributeCount());
const char16_t* attr_namespace = tree.getAttributeNamespace(0, &len);
EXPECT_EQ(StringPiece16(attr_namespace, len), u"http://com.test");
EXPECT_EQ(StringPiece16(u"http://com.test"), StringPiece16(attr_namespace, len));
attr_name = tree.getAttributeName(0, &len);
EXPECT_EQ(StringPiece16(attr_name, len), u"hello");
EXPECT_EQ(StringPiece16(u"hello"), StringPiece16(attr_name, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
ASSERT_EQ(0u, tree.getAttributeCount());
ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
ASSERT_EQ(android::ResXMLTree::TEXT, tree.next());
const char16_t* text = tree.getText(&len);
EXPECT_EQ(StringPiece16(text, len), u"Some text\\");
EXPECT_EQ(StringPiece16(u"Some text\\"), StringPiece16(text, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"Layout");
EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
tag_name = tree.getElementName(&len);
EXPECT_EQ(StringPiece16(tag_name, len), u"View");
EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
ASSERT_EQ(android::ResXMLTree::END_NAMESPACE, tree.next());
namespace_prefix = tree.getNamespacePrefix(&len);
EXPECT_EQ(StringPiece16(namespace_prefix, len), u"test");
EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len));
namespace_uri = tree.getNamespaceUri(&len);
ASSERT_EQ(StringPiece16(namespace_uri, len), u"http://com.test");
ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len));
ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
ASSERT_EQ(android::ResXMLTree::END_DOCUMENT, tree.next());
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
@@ -300,4 +299,41 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
EXPECT_EQ(ResourceId(0x80020000), tree.getAttributeData(idx));
}
TEST_F(XmlFlattenerTest, ProcessEscapedStrings) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
R"EOF(<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
}
const StringPiece16 kValue = u"value";
const StringPiece16 kPattern = u"pattern";
size_t len;
ssize_t idx;
const char16_t* str16;
idx = tree.indexOfAttribute(nullptr, 0, kValue.data(), kValue.size());
ASSERT_GE(idx, 0);
str16 = tree.getAttributeStringValue(idx, &len);
ASSERT_NE(nullptr, str16);
EXPECT_EQ(StringPiece16(u"?hello"), StringPiece16(str16, len));
idx = tree.indexOfAttribute(nullptr, 0, kPattern.data(), kPattern.size());
ASSERT_GE(idx, 0);
str16 = tree.getAttributeStringValue(idx, &len);
ASSERT_NE(nullptr, str16);
EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len));
ASSERT_EQ(android::ResXMLTree::TEXT, tree.next());
str16 = tree.getText(&len);
ASSERT_NE(nullptr, str16);
EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len));
}
} // namespace aapt

View File

@@ -81,6 +81,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
android:layout_width="match_parent"
android:background="@color/green"
android:text="hello"
android:attr="\?hello"
nonAaptAttr="1"
nonAaptAttrRef="@id/id"
class="hello" />)EOF");
@@ -89,35 +90,40 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = xml::FindRootElement(doc.get());
ASSERT_NE(view_el, nullptr);
ASSERT_NE(nullptr, view_el);
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
ASSERT_NE(xml_attr, nullptr);
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x01010000));
ASSERT_NE(xml_attr->compiled_value, nullptr);
ASSERT_NE(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), nullptr);
EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id.value());
ASSERT_NE(nullptr, xml_attr->compiled_value);
ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()));
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
ASSERT_NE(xml_attr, nullptr);
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute.value().id);
EXPECT_EQ(xml_attr->compiled_attribute.value().id.value(), ResourceId(0x01010001));
ASSERT_NE(xml_attr->compiled_value, nullptr);
EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id.value());
ASSERT_NE(nullptr, xml_attr->compiled_value);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_NE(ref, nullptr);
ASSERT_NE(nullptr, ref);
AAPT_ASSERT_TRUE(ref->name);
EXPECT_EQ(ref->name.value(), test::ParseNameOrDie("color/green")); // Make sure the name
EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name.value()); // Make sure the name
// didn't change.
AAPT_ASSERT_TRUE(ref->id);
EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000));
EXPECT_EQ(ResourceId(0x7f020000), ref->id.value());
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
ASSERT_NE(xml_attr, nullptr);
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
ASSERT_FALSE(xml_attr->compiled_value); // Strings don't get compiled for memory sake.
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_TRUE(xml_attr->compiled_attribute);
ASSERT_FALSE(xml_attr->compiled_value); // Should be a plain string.
xml_attr = view_el->FindAttribute("", "nonAaptAttr");
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
@@ -131,9 +137,9 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
ASSERT_NE(nullptr, ValueCast<Reference>(xml_attr->compiled_value.get()));
xml_attr = view_el->FindAttribute("", "class");
ASSERT_NE(xml_attr, nullptr);
ASSERT_NE(nullptr, xml_attr);
AAPT_ASSERT_FALSE(xml_attr->compiled_attribute);
ASSERT_EQ(xml_attr->compiled_value, nullptr);
ASSERT_EQ(nullptr, xml_attr->compiled_value);
}
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {

View File

@@ -42,7 +42,6 @@ struct Stack {
std::stack<xml::Node*> node_stack;
std::string pending_comment;
std::unique_ptr<xml::Text> last_text_node;
util::StringBuilder pending_text;
};
/**
@@ -66,14 +65,12 @@ 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 = {};
if (!stack->last_text_node->text.empty()) {
stack->node_stack.top()->AppendChild(std::move(stack->last_text_node));
} else {
// Drop an empty text node.
stack->last_text_node = nullptr;
}
stack->last_text_node = nullptr;
}
}
@@ -138,13 +135,11 @@ static void XMLCALL StartElementHandler(void* user_data, const char* name,
while (*attrs) {
Attribute attribute;
SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
util::StringBuilder builder;
builder.Append(*attrs++);
attribute.value = builder.ToString();
attribute.value = *attrs++;
// Insert in sorted order.
auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
attribute, less_attribute);
auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
less_attribute);
el->attributes.insert(iter, std::move(attribute));
}
@@ -173,14 +168,14 @@ static void XMLCALL CharacterDataHandler(void* user_data, const char* s, int len
// See if we can just append the text to a previous text node.
if (stack->last_text_node != nullptr) {
stack->pending_text.Append(str);
stack->last_text_node->text.append(str.data(), str.size());
return;
}
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);
stack->last_text_node->text = str.to_string();
}
static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {

View File

@@ -49,23 +49,26 @@ 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");
// Escaping is handled after parsing of the values for resource-specific values.
TEST(XmlDomTest, ForwardEscapes) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)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);
EXPECT_EQ("\\d{5}", attr->value);
attr = el->FindAttribute({}, "value");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("\\?hello", 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);
EXPECT_EQ("\\\\d{5}", text->text);
}
} // namespace aapt