#include "fuchsia_web/runners/cast/test/cast_runner_launcher.h"
#include <chromium/cast/cpp/fidl.h>
#include <fuchsia/buildinfo/cpp/fidl.h>
#include <fuchsia/camera3/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>
#include <fuchsia/fonts/cpp/fidl.h>
#include <fuchsia/intl/cpp/fidl.h>
#include <fuchsia/kernel/cpp/fidl.h>
#include <fuchsia/legacymetrics/cpp/fidl.h>
#include <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/memorypressure/cpp/fidl.h>
#include <fuchsia/net/interfaces/cpp/fidl.h>
#include <fuchsia/settings/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/tracing/provider/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <lib/vfs/cpp/remote_dir.h>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "base/run_loop.h"
#include "fuchsia_web/common/test/test_realm_support.h"
#include "media/fuchsia/audio/fake_audio_device_enumerator_local_component.h"
using ::component_testing::ChildRef;
using ::component_testing::Dictionary;
using ::component_testing::Directory;
using ::component_testing::DirectoryContents;
using ::component_testing::FrameworkRef;
using ::component_testing::ParentRef;
using ::component_testing::Protocol;
using ::component_testing::RealmBuilder;
using ::component_testing::Route;
using ::component_testing::Storage;
namespace test {
namespace {
constexpr char kDynamicComponentCapabilitiesName[] =
"for_dynamic_component_host";
constexpr char kDynamicComponentCapabilitiesPath[] =
"/for_dynamic_component_host";
class TestProxyLocalComponent : public component_testing::LocalComponentImpl {
public:
TestProxyLocalComponent() = default;
void OnStart() override {
vfs::PseudoDir* capability_dir =
base::ComponentContextForProcess()->outgoing()->GetOrCreateDirectory(
kDynamicComponentCapabilitiesName);
fidl::InterfaceHandle<fuchsia::io::Directory> services;
zx_status_t status =
capability_dir->Serve(fuchsia_io::wire::kPermReadable,
fidl::ServerEnd<fuchsia_io::Directory>(
services.NewRequest().TakeChannel()));
ZX_CHECK(status == ZX_OK, status) << "Serve()";
status = outgoing()->root_dir()->AddEntry(
kDynamicComponentCapabilitiesName,
std::make_unique<vfs::RemoteDir>(services.TakeChannel()));
ZX_CHECK(status == ZX_OK, status) << "AddEntry";
}
};
}
CastRunnerLauncher::CastRunnerLauncher(CastRunnerFeatures runner_features) {
auto realm_builder = RealmBuilder::Create();
static constexpr char kCastRunnerComponentName[] = "cast_runner";
realm_builder.AddChild(kCastRunnerComponentName, "#meta/cast_runner.cm");
base::CommandLine command_line = CommandLineFromFeatures(runner_features);
static constexpr char const* kSwitchesToCopy[] = {"ozone-platform"};
command_line.CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kSwitchesToCopy);
AppendCommandLineArguments(realm_builder, kCastRunnerComponentName,
command_line);
FakeFeedbackService::RouteToChild(realm_builder, kCastRunnerComponentName);
AddSyslogRoutesFromParent(realm_builder, kCastRunnerComponentName);
AddFontService(realm_builder, kCastRunnerComponentName);
AddTestUiStack(realm_builder, kCastRunnerComponentName);
realm_builder.AddRoute(Route{
.capabilities =
{
Directory{.name = "config-data", .subdir = "cast_runner"},
Directory{.name = "config-data",
.as = "config-data-for-web-instance",
.subdir = "web_engine"},
Directory{.name = "root-ssl-certificates"},
Directory{.name = "tzdata-icu"},
Protocol{fuchsia::buildinfo::Provider::Name_},
Protocol{"fuchsia.device.NameProvider"},
Protocol{"fuchsia.hwinfo.Product"},
Protocol{fuchsia::intl::PropertyProvider::Name_},
Protocol{fuchsia::kernel::VmexResource::Name_},
Dictionary{"diagnostics"},
Protocol{fuchsia::media::ProfileProvider::Name_},
Protocol{"fuchsia.scheduler.RoleManager"},
Protocol{fuchsia::memorypressure::Provider::Name_},
Protocol{"fuchsia.process.Launcher"},
Protocol{"fuchsia.sysmem.Allocator"},
Protocol{"fuchsia.sysmem2.Allocator"},
Protocol{fuchsia::net::interfaces::State::Name_},
Protocol{"fuchsia.net.name.Lookup"},
Protocol{"fuchsia.posix.socket.Provider"},
Storage{.name = "cache", .path = "/cache"},
},
.source = ParentRef(),
.targets = {ChildRef{kCastRunnerComponentName}}});
static constexpr char kFakeCastAgentName[] = "fake-cast-agent";
auto fake_cast_agent = std::make_unique<FakeCastAgent>();
fake_cast_agent_ = fake_cast_agent.get();
realm_builder.AddLocalChild(
kFakeCastAgentName,
[fake_cast_agent = std::move(fake_cast_agent)]() mutable {
return std::move(fake_cast_agent);
});
realm_builder.AddRoute(
Route{.capabilities =
{
Protocol{chromium::cast::ApplicationConfigManager::Name_},
Protocol{chromium::cast::CorsExemptHeaderProvider::Name_},
Protocol{fuchsia::camera3::DeviceWatcher::Name_},
Protocol{fuchsia::legacymetrics::MetricsRecorder::Name_},
Protocol{fuchsia::media::Audio::Name_},
},
.source = ChildRef{kFakeCastAgentName},
.targets = {ChildRef{kCastRunnerComponentName}}});
if (!(runner_features & kCastRunnerFeaturesHeadless)) {
AddRouteFromParent(realm_builder, kCastRunnerComponentName,
fuchsia::settings::Display::Name_);
}
if (runner_features & kCastRunnerFeaturesVulkan) {
AddVulkanRoutesFromParent(realm_builder, kCastRunnerComponentName);
}
if (runner_features & kCastRunnerFeaturesFakeAudioDeviceEnumerator) {
static constexpr char kAudioDeviceEnumerator[] =
"fake_audio_device_enumerator";
realm_builder.AddLocalChild(kAudioDeviceEnumerator, []() {
return std::make_unique<media::FakeAudioDeviceEnumeratorLocalComponent>();
});
realm_builder.AddRoute(
Route{.capabilities = {Protocol{
fuchsia::media::AudioDeviceEnumerator::Name_}},
.source = ChildRef{kAudioDeviceEnumerator},
.targets = {ChildRef{kCastRunnerComponentName}}});
} else {
AddRouteFromParent(realm_builder, kCastRunnerComponentName,
fuchsia::media::AudioDeviceEnumerator::Name_);
}
AddRouteFromParent(realm_builder, kCastRunnerComponentName,
"fuchsia.tracing.provider.Registry");
AddRouteFromParent(realm_builder, kCastRunnerComponentName,
"fuchsia.tracing.perfetto.ProducerConnector");
realm_builder.AddRoute(
Route{.capabilities = {Protocol{chromium::cast::DataReset::Name_},
Protocol{fuchsia::web::FrameHost::Name_},
Protocol{fuchsia::web::Debug::Name_}},
.source = ChildRef{kCastRunnerComponentName},
.targets = {ParentRef()}});
static constexpr char kTestProxyName[] = "test_proxy";
realm_builder.AddLocalChild(kTestProxyName,
std::make_unique<TestProxyLocalComponent>);
static constexpr char kCastResolverName[] = "cast-resolver";
static constexpr char kCastRunnerName[] = "cast-runner";
{
auto test_proxy_decl = realm_builder.GetComponentDecl(kTestProxyName);
static constexpr char kEnvironmentName[] = "cast-test-environment";
static constexpr char kCastUrlScheme[] = "cast";
fuchsia::component::decl::ResolverRegistration resolver_decl;
resolver_decl.set_resolver(kCastResolverName);
resolver_decl.set_source(fuchsia::component::decl::Ref::WithParent({}));
resolver_decl.set_scheme(kCastUrlScheme);
fuchsia::component::decl::RunnerRegistration runner_decl;
runner_decl.set_source_name(kCastRunnerName);
runner_decl.set_source(fuchsia::component::decl::Ref::WithParent({}));
runner_decl.set_target_name(kCastRunnerName);
fuchsia::component::decl::Environment environment_decl;
environment_decl.set_name(kEnvironmentName);
environment_decl.set_extends(
fuchsia::component::decl::EnvironmentExtends::NONE);
environment_decl.set_stop_timeout_ms(1000);
environment_decl.mutable_resolvers()->emplace_back(
std::move(resolver_decl));
environment_decl.mutable_runners()->emplace_back(std::move(runner_decl));
test_proxy_decl.mutable_environments()->emplace_back(
std::move(environment_decl));
fuchsia::component::decl::Collection collection_decl;
collection_decl.set_name(kTestCollectionName);
collection_decl.set_environment(kEnvironmentName);
collection_decl.set_durability(
fuchsia::component::decl::Durability::TRANSIENT);
collection_decl.set_allowed_offers(
fuchsia::component::decl::AllowedOffers::STATIC_AND_DYNAMIC);
test_proxy_decl.mutable_collections()->emplace_back(
std::move(collection_decl));
test_proxy_decl.mutable_capabilities()->emplace_back(
fuchsia::component::decl::Capability::WithDirectory(std::move(
fuchsia::component::decl::Directory()
.set_name(kDynamicComponentCapabilitiesName)
.set_rights(fuchsia::io::R_STAR_DIR)
.set_source_path(kDynamicComponentCapabilitiesPath))));
test_proxy_decl.mutable_exposes()->emplace_back(
fuchsia::component::decl::Expose::WithProtocol(std::move(
fuchsia::component::decl::ExposeProtocol()
.set_source(fuchsia::component::decl::Ref::WithFramework({}))
.set_source_name(fuchsia::component::Realm::Name_)
.set_target(fuchsia::component::decl::Ref::WithParent({}))
.set_target_name(fuchsia::component::Realm::Name_))));
realm_builder.ReplaceComponentDecl(kTestProxyName,
std::move(test_proxy_decl));
}
{
auto runner_decl = realm_builder.GetComponentDecl(kCastRunnerComponentName);
runner_decl.mutable_exposes()->emplace_back(
fuchsia::component::decl::Expose::WithProtocol(std::move(
fuchsia::component::decl::ExposeProtocol()
.set_source(fuchsia::component::decl::Ref::WithFramework({}))
.set_source_name(fuchsia::component::Realm::Name_)
.set_target(fuchsia::component::decl::Ref::WithParent({}))
.set_target_name(fuchsia::component::Realm::Name_))));
realm_builder.ReplaceComponentDecl(kCastRunnerComponentName,
std::move(runner_decl));
}
{
auto realm_decl = realm_builder.GetRealmDecl();
realm_decl.mutable_exposes()->emplace_back(
fuchsia::component::decl::Expose::WithProtocol(std::move(
fuchsia::component::decl::ExposeProtocol()
.set_source(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{.name = kTestProxyName}))
.set_source_name(fuchsia::component::Realm::Name_)
.set_target(fuchsia::component::decl::Ref::WithParent({}))
.set_target_name(fuchsia::component::Realm::Name_))));
realm_decl.mutable_offers()->emplace_back(
fuchsia::component::decl::Offer::WithResolver(std::move(
fuchsia::component::decl::OfferResolver()
.set_source(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{
.name = kCastRunnerComponentName}))
.set_source_name(kCastResolverName)
.set_target(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{.name = kTestProxyName}))
.set_target_name(kCastResolverName))));
realm_decl.mutable_offers()->emplace_back(
fuchsia::component::decl::Offer::WithRunner(std::move(
fuchsia::component::decl::OfferRunner()
.set_source(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{
.name = kCastRunnerComponentName}))
.set_source_name(kCastRunnerName)
.set_target(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{.name = kTestProxyName}))
.set_target_name(kCastRunnerName))));
realm_decl.mutable_exposes()->emplace_back(
fuchsia::component::decl::Expose::WithProtocol(std::move(
fuchsia::component::decl::ExposeProtocol()
.set_source(fuchsia::component::decl::Ref::WithChild(
fuchsia::component::decl::ChildRef{
.name = kCastRunnerComponentName}))
.set_source_name(fuchsia::component::Realm::Name_)
.set_target(fuchsia::component::decl::Ref::WithParent({}))
.set_target_name(kCastRunnerRealmProtocol))));
realm_builder.ReplaceRealmDecl(std::move(realm_decl));
}
realm_root_ = realm_builder.Build();
exposed_services_ = std::make_unique<sys::ServiceDirectory>(
realm_root_->component().CloneExposedDir());
}
CastRunnerLauncher::~CastRunnerLauncher() {
if (realm_root_.has_value()) {
base::RunLoop run_loop;
realm_root_.value().Teardown(
[quit = run_loop.QuitClosure()](auto result) { quit.Run(); });
run_loop.Run();
}
}
}