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

Commit 6de29ed5 authored by Alexandria Cornwall's avatar Alexandria Cornwall Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Add support to strip namespaces from XML"

parents 7791cc9b a7cc3f1d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ sources := \
	link/ReferenceLinker.cpp \
	link/TableMerger.cpp \
	link/VersionCollapser.cpp \
	link/XmlNamespaceRemover.cpp \
	link/XmlReferenceLinker.cpp \
	process/SymbolTable.cpp \
	proto/ProtoHelpers.cpp \
@@ -89,6 +90,7 @@ testSources := \
	link/ReferenceLinker_test.cpp \
	link/TableMerger_test.cpp \
	link/VersionCollapser_test.cpp \
	link/XmlNamespaceRemover_test.cpp \
	link/XmlReferenceLinker_test.cpp \
	process/SymbolTable_test.cpp \
	proto/TableProtoSerializer_test.cpp \
+21 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ struct LinkOptions {
    bool generateNonFinalIds = false;
    std::vector<std::string> javadocAnnotations;
    bool outputToDirectory = false;
    bool noXmlNamespaces = false;
    bool autoAddOverlay = false;
    bool doNotCompressAnything = false;
    std::unordered_set<std::string> extensionsToNotCompress;
@@ -293,6 +294,7 @@ static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
struct ResourceFileFlattenerOptions {
    bool noAutoVersion = false;
    bool noVersionVectors = false;
    bool noXmlNamespaces = false;
    bool keepRawValues = false;
    bool doNotCompressAnything = false;
    bool updateProguardSpec = false;
@@ -382,6 +384,13 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry,
        return false;
    }

    if (mOptions.noXmlNamespaces) {
        XmlNamespaceRemover namespaceRemover;
        if (!namespaceRemover.consume(mContext, outFileOp->xmlToFlatten.get())) {
            return false;
        }
    }

    if (!mOptions.noAutoVersion) {
        if (mOptions.noVersionVectors) {
            // Skip this if it is a vector or animated-vector.
@@ -1296,6 +1305,7 @@ public:
        fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
        fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
        fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
        fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
        fileFlattenerOptions.updateProguardSpec =
                static_cast<bool>(mOptions.generateProguardRulesPath);

@@ -1594,6 +1604,14 @@ public:
                        error = true;
                    }
                }

                if (mOptions.noXmlNamespaces) {
                    // PackageParser will fail if URIs are removed from AndroidManifest.xml.
                    XmlNamespaceRemover namespaceRemover(true /* keepUris */);
                    if (!namespaceRemover.consume(mContext, manifestXml.get())) {
                        error = true;
                    }
                }
            } else {
                error = true;
            }
@@ -1732,6 +1750,9 @@ int link(const std::vector<StringPiece>& args) {
            .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
                            "by -o",
                            &options.outputToDirectory)
            .optionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI "
                            "information from AndroidManifest.xml\nand XML binaries in res/*.",
                            &options.noXmlNamespaces)
            .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
                          "AndroidManifest.xml",
                          &options.manifestFixerOptions.minSdkVersionDefault)
+17 −0
Original line number Diff line number Diff line
@@ -85,6 +85,23 @@ struct PrivateAttributeMover : public IResourceTableConsumer {
    bool consume(IAaptContext* context, ResourceTable* table) override;
};

/**
 * Removes namespace nodes and URI information from the XmlResource.
 *
 * Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
 * parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
 */
class XmlNamespaceRemover : public IXmlResourceConsumer {
private:
    bool mKeepUris;

public:
    XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) {
    };

    bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};

/**
 * Resolves attributes in the XmlResource and compiles string values to resource values.
 * Once an XmlResource is processed by this linker, it is ready to be flattened.
+83 −0
Original line number Diff line number Diff line
/*
 * 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
+109 −0
Original line number Diff line number Diff line
/*
 * 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 "link/Linkers.h"
#include "test/Test.h"

namespace aapt {

class XmlUriTestVisitor : public xml::Visitor {
public:
    void visit(xml::Element* el) override {
        for (const auto& attr : el->attributes) {
            EXPECT_EQ(std::string(), attr.namespaceUri);
        }
        EXPECT_EQ(std::string(), el->namespaceUri);
        xml::Visitor::visit(el);
    }

    void visit(xml::Namespace* ns) override {
        EXPECT_EQ(std::string(), ns->namespaceUri);
        xml::Visitor::visit(ns);
    }
};

class XmlNamespaceTestVisitor : public xml::Visitor {
public:
    void visit(xml::Namespace* ns) override {
        ADD_FAILURE() << "Detected namespace: "
                << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\"";
        xml::Visitor::visit(ns);
    }
};

class XmlNamespaceRemoverTest : public ::testing::Test {
public:
    void SetUp() override {
        mContext = test::ContextBuilder()
                .setCompilationPackage("com.app.test")
                .build();
    }

protected:
    std::unique_ptr<IAaptContext> mContext;
};

TEST_F(XmlNamespaceRemoverTest, RemoveUris) {
    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
            <View xmlns:android="http://schemas.android.com/apk/res/android"
                  android:text="hello" />)EOF");

    XmlNamespaceRemover remover;
    ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));

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

    XmlUriTestVisitor visitor;
    root->accept(&visitor);
}

TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) {
    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
            <View xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:foo="http://schemas.android.com/apk/res/foo"
                  foo:bar="foobar"
                  android:text="hello" />)EOF");

    XmlNamespaceRemover remover;
    ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));

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

    XmlNamespaceTestVisitor visitor;
    root->accept(&visitor);
}

TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) {
    std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
            <View xmlns:android="http://schemas.android.com/apk/res/android"
                  android:text="hello">
              <View xmlns:foo="http://schemas.example.com/foo"
                    android:text="foo"/>
            </View>)EOF");

    XmlNamespaceRemover remover;
    ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));

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

    XmlNamespaceTestVisitor visitor;
    root->accept(&visitor);
}

} // namespace aapt