blob: 954d05980dea52d27edc1c2bf136f6ff46b54260 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#include <fstream>
#include <memory>
#include "base/base_paths.h"
#include "base/containers/span.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "content/browser/webui/url_data_source_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_controller_interface_binder.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/scoped_web_ui_controller_factory_registration.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_ui_browsertest_util.h"
#include "content/shell/browser/shell.h"
#include "content/test/grit/web_ui_mojo_test_resources.h"
#include "content/test/grit/web_ui_mojo_test_resources_map.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
#include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.test-mojom.h"
#include "ui/base/webui/resource_path.h"
namespace content {
namespace {
std::vector<base::FilePath> EnumerateFilesInDirectory(
const base::FilePath& dir,
const base::FilePath::StringType& pattern) {
std::vector<base::FilePath> test_files;
base::FileEnumerator e(dir, false, base::FileEnumerator::FILES, pattern);
e.ForEach([&test_files](const base::FilePath& file_path) -> void {
test_files.push_back(file_path);
});
return test_files;
}
// Reads the content of a file to a string. Crashes if reading failed.
std::string ReadFile(const base::FilePath& file_path) {
std::string content;
CHECK(base::ReadFileToString(file_path, &content));
return std::string(
base::TrimWhitespaceASCII(content, base::TrimPositions::TRIM_TRAILING));
}
std::vector<mojo::test::TestCasePtr> ParseTestCases() {
base::FilePath base_dir;
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &base_dir);
const auto test_dir_relative_path = base::FilePath::FromASCII(
"mojo/public/interfaces/bindings/tests/data/"
"validation/");
base::FilePath test_cases_dir = base_dir.Append(test_dir_relative_path);
std::vector<base::FilePath> test_cases = EnumerateFilesInDirectory(
test_cases_dir, FILE_PATH_LITERAL("conformance_*.data"));
std::vector<mojo::test::TestCasePtr> parsed_test_cases;
for (const base::FilePath& test_file : test_cases) {
std::string test_data_raw = ReadFile(test_file);
std::vector<uint8_t> bytes;
size_t num_handles;
std::string error_msg;
CHECK(mojo::test::ParseValidationTestInput(test_data_raw, &bytes,
&num_handles, &error_msg));
std::string expectation = ReadFile(
test_file.RemoveFinalExtension().AddExtensionASCII("expected"));
parsed_test_cases.push_back(mojo::test::TestCase::New(
test_file.BaseName().RemoveFinalExtension().MaybeAsASCII(), bytes,
expectation));
}
CHECK(parsed_test_cases.size() > 0)
<< "No test cases were found, there might be an issue with pathing, "
"looked at: "
<< test_cases_dir;
return parsed_test_cases;
}
// WebUIController that sets up mojo bindings.
class ConformanceTestWebUIController : public WebUIController,
public mojo::test::PageHandlerFactory {
public:
explicit ConformanceTestWebUIController(WebUI* web_ui)
: WebUIController(web_ui) {
web_ui->SetBindings(
{BindingsPolicyValue::kMojoWebUi, BindingsPolicyValue::kWebUi});
WebUIDataSource* data_source = WebUIDataSource::CreateAndAdd(
web_ui->GetWebContents()->GetBrowserContext(),
"mojo-web-ui-conformance");
data_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources 'self';");
data_source->AddResourcePaths(kWebUiMojoTestResources);
data_source->AddResourcePath("", IDR_WEB_UI_TS_TEST_CONFORMANCE_HTML);
}
ConformanceTestWebUIController(const ConformanceTestWebUIController&) =
delete;
ConformanceTestWebUIController& operator=(
const ConformanceTestWebUIController&) = delete;
void BindInterface(
mojo::PendingReceiver<mojo::test::PageHandlerFactory> receiver) {
page_factory_receiver_.reset();
page_factory_receiver_.Bind(std::move(receiver));
}
private:
void GetTestCases(GetTestCasesCallback cb) override {
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
[](GetTestCasesCallback cb,
const scoped_refptr<base::SequencedTaskRunner>& runner) {
auto test_cases = ParseTestCases();
runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), std::move(test_cases)));
},
std::move(cb), base::SequencedTaskRunner::GetCurrentDefault()));
}
mojo::Receiver<mojo::test::PageHandlerFactory> page_factory_receiver_{this};
WEB_UI_CONTROLLER_TYPE_DECL();
};
WEB_UI_CONTROLLER_TYPE_IMPL(ConformanceTestWebUIController)
class ConformanceTestWebUIControllerFactory : public WebUIControllerFactory {
public:
ConformanceTestWebUIControllerFactory() = default;
ConformanceTestWebUIControllerFactory(const TestWebUIControllerFactory&) =
delete;
ConformanceTestWebUIControllerFactory& operator=(
const TestWebUIControllerFactory&) = delete;
std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
WebUI* web_ui,
const GURL& url) override {
return std::make_unique<ConformanceTestWebUIController>(web_ui);
}
WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
const GURL& url) override {
if (!url.SchemeIs(kChromeUIScheme)) {
return WebUI::kNoWebUI;
}
return reinterpret_cast<WebUI::TypeID>(1);
}
bool UseWebUIForURL(BrowserContext* browser_context,
const GURL& url) override {
return true;
}
};
class ConformanceTestWebUIClient
: public ContentBrowserTestContentBrowserClient {
public:
ConformanceTestWebUIClient() = default;
ConformanceTestWebUIClient(const ConformanceTestWebUIClient&) = delete;
ConformanceTestWebUIClient& operator=(const ConformanceTestWebUIClient&) =
delete;
void RegisterBrowserInterfaceBindersForFrame(
RenderFrameHost* render_frame_host,
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override {
RegisterWebUIControllerInterfaceBinder<mojo::test::PageHandlerFactory,
ConformanceTestWebUIController>(map);
}
};
class WebUIMojoConformanceTest : public ContentBrowserTest {
public:
WebUIMojoConformanceTest() = default;
WebUIMojoConformanceTest(const WebUIMojoConformanceTest&) = delete;
WebUIMojoConformanceTest& operator=(const WebUIMojoConformanceTest&) = delete;
protected:
void SetUpOnMainThread() override {
client_ = std::make_unique<ConformanceTestWebUIClient>();
}
void TearDownOnMainThread() override {}
ConformanceTestWebUIControllerFactory factory_;
ScopedWebUIControllerFactoryRegistration factory_registration_{&factory_};
std::unique_ptr<ConformanceTestWebUIClient> client_;
};
IN_PROC_BROWSER_TEST_F(WebUIMojoConformanceTest, ConformanceTest) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("data:,foo")));
GURL kTestUrl(GetWebUIURL("mojo-web-ui-conformance/"));
const std::string kTestScript = "runTest();";
EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));
EXPECT_EQ(true, EvalJs(shell()->web_contents(), kTestScript));
}
} // namespace
} // namespace content