Adding a SafeXMLParser to the data-decoder service.

Introducing a new XML parser service that parses XML into a
base::DictionaryValue. It does so by JSONifying the XML content.

Also factoring out TestServiceManagerListener to its own file, now that
it is used in 2 places (safe_json_parser_browsertest.cc and
safe_xml_parser_browsertest.cc).

Bug: 784667
Change-Id: I1f41f44d49910da43837aac8bca52e24533460ef
Reviewed-on: https://chromium-review.googlesource.com/767890
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: Scott Graham <[email protected]>
Reviewed-by: Robert Sesek <[email protected]>
Reviewed-by: Lei Zhang <[email protected]>
Reviewed-by: Ken Rockot <[email protected]>
Commit-Queue: Jay Civelli <[email protected]>
Cr-Commit-Position: refs/heads/master@{#519832}
diff --git a/chrome/browser/safe_xml_parser_browsertest.cc b/chrome/browser/safe_xml_parser_browsertest.cc
new file mode 100644
index 0000000..f6062ce
--- /dev/null
+++ b/chrome/browser/safe_xml_parser_browsertest.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_service_manager_listener.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/test/test_utils.h"
+#include "services/data_decoder/public/cpp/safe_xml_parser.h"
+#include "services/data_decoder/public/interfaces/constants.mojom.h"
+#include "services/data_decoder/public/interfaces/xml_parser.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace {
+
+class SafeXmlParserTest : public InProcessBrowserTest {
+ public:
+  SafeXmlParserTest() = default;
+  ~SafeXmlParserTest() override = default;
+
+ protected:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    listener_.Init();
+
+    // The data_decoder service will stop if no connection is bound to it after
+    // 5 seconds. We bind a connection to it for the duration of the test so it
+    // is guaranteed the service is always running.
+    content::ServiceManagerConnection::GetForProcess()
+        ->GetConnector()
+        ->BindInterface(data_decoder::mojom::kServiceName, &xml_parser_ptr_);
+    listener_.WaitUntilServiceStarted(data_decoder::mojom::kServiceName);
+    EXPECT_EQ(
+        1U, listener_.GetServiceStartCount(data_decoder::mojom::kServiceName));
+  }
+
+  uint32_t GetServiceStartCount(const std::string& service_name) const {
+    return listener_.GetServiceStartCount(service_name);
+  }
+
+  // Parses |xml| and compares its parsed representation with |expected_json|.
+  // If |expected_json| is empty, the XML parsing is expected to fail.
+  void TestParse(base::StringPiece xml, base::StringPiece expected_json) {
+    SCOPED_TRACE(xml);
+
+    base::RunLoop run_loop;
+    std::unique_ptr<base::Value> expected_value;
+    if (!expected_json.empty()) {
+      expected_value = base::JSONReader::Read(expected_json);
+      DCHECK(expected_value) << "Bad test, incorrect JSON: " << expected_json;
+    }
+
+    data_decoder::ParseXml(
+        content::ServiceManagerConnection::GetForProcess()->GetConnector(),
+        xml.as_string(),
+        base::BindOnce(&SafeXmlParserTest::XmlParsingDone,
+                       base::Unretained(this), run_loop.QuitClosure(),
+                       std::move(expected_value)));
+    run_loop.Run();
+  }
+
+ private:
+  void XmlParsingDone(base::Closure quit_loop_closure,
+                      std::unique_ptr<base::Value> expected_value,
+                      std::unique_ptr<base::Value> actual_value,
+                      const base::Optional<std::string>& error) {
+    base::ScopedClosureRunner(std::move(quit_loop_closure));
+    if (!expected_value) {
+      EXPECT_FALSE(actual_value);
+      EXPECT_TRUE(error);
+      return;
+    }
+    EXPECT_FALSE(error);
+    ASSERT_TRUE(actual_value);
+    EXPECT_EQ(*expected_value, *actual_value);
+  }
+
+  data_decoder::mojom::XmlParserPtr xml_parser_ptr_;
+  TestServiceManagerListener listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeXmlParserTest);
+};
+
+}  // namespace
+
+// Tests that SafeXmlParser does parse. (actual XML parsing is tested in the
+// service unit-tests).
+IN_PROC_BROWSER_TEST_F(SafeXmlParserTest, Parse) {
+  TestParse("[\"this is JSON not XML\"]", "");
+  TestParse("<hello>bonjour</hello>",
+            R"(
+              {"type": "element",
+               "tag": "hello",
+               "children": [{"type": "text", "text": "bonjour"}]
+               } )");
+}
+
+// Tests that when calling SafeXmlParser::Parse() a new service is started
+// every time.
+IN_PROC_BROWSER_TEST_F(SafeXmlParserTest, Isolation) {
+  for (int i = 0; i < 5; i++) {
+    base::RunLoop run_loop;
+    bool parsing_success = false;
+    data_decoder::ParseXml(
+        content::ServiceManagerConnection::GetForProcess()->GetConnector(),
+        "<hello>bonjour</hello>",
+        base::BindOnce(
+            [](bool* success, base::Closure quit_loop_closure,
+               std::unique_ptr<base::Value> actual_value,
+               const base::Optional<std::string>& error) {
+              *success = !error;
+              std::move(quit_loop_closure).Run();
+            },
+            &parsing_success, run_loop.QuitClosure()));
+    run_loop.Run();
+    EXPECT_TRUE(parsing_success);
+    // 2 + i below because the data_decoder is already running and the index
+    // starts at 0.
+    EXPECT_EQ(2U + i, GetServiceStartCount(data_decoder::mojom::kServiceName));
+  }
+}