AAPT2: Add support to strip namespaces from XML
The --no-xml-namespaces flag will strip namespace information from
compiled binary XML files in res/* (excluding res/raw/*) and
AndroidManifest.xml. It will also strip URI information from compiled
binary XML files in res/* (excluding res/raw/* and AndroidManifest.xml).
AndroidManifest.xml URI information is retained due to PackageParser, which
requires the Android URI for intent filters.
Bug: 29115919
Change-Id: I90cad6ed39ce02a69776f55314c1d4f38ad1aabe
diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp
new file mode 100644
index 0000000..9f95177
--- /dev/null
+++ b/tools/aapt2/link/XmlNamespaceRemover.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResourceTable.h"
+#include "link/Linkers.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+namespace {
+
+/**
+ * Visits each xml Node, removing URI references and nested namespaces.
+ */
+class XmlVisitor : public xml::Visitor {
+public:
+ XmlVisitor(bool keepUris) : mKeepUris(keepUris) {
+ }
+
+ void visit(xml::Element* el) override {
+ // Strip namespaces
+ for (auto& child : el->children) {
+ while (child && xml::nodeCast<xml::Namespace>(child.get())) {
+ if (child->children.empty()) {
+ child = {};
+ } else {
+ child = std::move(child->children.front());
+ child->parent = el;
+ }
+ }
+ }
+ el->children.erase(std::remove_if(el->children.begin(), el->children.end(),
+ [](const std::unique_ptr<xml::Node>& child) -> bool {
+ return child == nullptr;
+ }), el->children.end());
+
+ if (!mKeepUris) {
+ for (xml::Attribute& attr : el->attributes) {
+ attr.namespaceUri = std::string();
+ }
+ el->namespaceUri = std::string();
+ }
+ xml::Visitor::visit(el);
+ }
+
+private:
+ bool mKeepUris;
+};
+
+} // namespace
+
+bool XmlNamespaceRemover::consume(IAaptContext* context, xml::XmlResource* resource) {
+ if (!resource->root) {
+ return false;
+ }
+ // Replace any root namespaces until the root is a non-namespace node
+ while (xml::nodeCast<xml::Namespace>(resource->root.get())) {
+ if (resource->root->children.empty()) {
+ break;
+ }
+ resource->root = std::move(resource->root->children.front());
+ resource->root->parent = nullptr;
+ }
+ XmlVisitor visitor(mKeepUris);
+ resource->root->accept(&visitor);
+ return true;
+}
+
+} // namespace aapt