blob: 24a837a4aaaab26ab8da3458bd0f2edf76ccde3e [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "src/ui/tools/present_view/present_view.h"
#include <fuchsia/intl/cpp/fidl.h>
#include <lib/fidl/cpp/vector.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
#include <zircon/status.h>
#include "src/lib/fxl/strings/substitute.h"
namespace present_view {
namespace {
// The component manifest of the component that provides an implementation of
// fuchsia.intl.PropertyProvider. present_view will start it up if the flag
// --locale=... is specified.
constexpr char kIntlPropertyProviderUri[] =
"fuchsia-pkg://fuchsia.com/intl_property_manager#meta/"
"intl_property_manager_without_flags.cmx";
} // anonymous namespace
PresentView::PresentView(std::unique_ptr<sys::ComponentContext> context,
ViewErrorCallback on_view_error)
: context_(std::move(context)), view_error_callback_(std::move(on_view_error)) {}
PresentView::~PresentView() = default;
bool PresentView::Present(ViewInfo view_info) {
FX_CHECK(!intl_controller_);
FX_CHECK(!presenter_);
FX_CHECK(!view_controller_);
// Skip doing any work for an empty URL.
if (view_info.url.empty()) {
return false;
}
fuchsia::sys::LauncherPtr launcher = context_->svc()->Connect<fuchsia::sys::Launcher>();
launcher.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::sys::Launcher error", status);
});
// Configure the information to launch the component with.
fuchsia::sys::LaunchInfo launch_info;
fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_services_dir;
launch_info.directory_request = outgoing_services_dir.NewRequest().TakeChannel();
launch_info.url = std::move(view_info.url);
launch_info.arguments = fidl::VectorPtr(std::move(view_info.arguments));
if (!view_info.locale.empty()) {
fidl::InterfaceHandle<fuchsia::io::Directory> additional_services_dir;
// If we wanted present_view to serve |fuchsia.intl.ProfileProvider|, then start the
// |intl_property_provider| and make it available to the component under test.
RunIntlService(view_info.locale, additional_services_dir.NewRequest(), launcher.get());
fuchsia::sys::ServiceListPtr injected_services(new fuchsia::sys::ServiceList);
injected_services->names.push_back(fuchsia::intl::PropertyProvider::Name_);
injected_services->host_directory = additional_services_dir.TakeChannel();
launch_info.additional_services = std::move(injected_services);
}
// Launch the component.
launcher->CreateComponent(std::move(launch_info), view_controller_.NewRequest());
view_controller_.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::sys::ComponentController (for View) error", status);
});
// Instruct the component to create a Scenic View using the |fuchsia.ui.app.ViewProvider|
// service that it exposes.
auto services = std::make_shared<sys::ServiceDirectory>(std::move(outgoing_services_dir));
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
if (!view_info.locale.empty()) {
// For now, use the presence of a locale option as an indication to use the
// |fuchsia.ui.views.View| interface.
view_ = services->Connect<fuchsia::ui::views::View>();
view_.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::ui::views::View error", status);
});
view_->Present(std::move(view_token));
} else {
legacy_view_provider_ = services->Connect<fuchsia::ui::app::ViewProvider>();
legacy_view_provider_.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::ui::app::ViewProvider error", status);
});
legacy_view_provider_->CreateView(std::move(view_token.value), nullptr, nullptr);
}
// Ask the presenter to display it.
presenter_ = context_->svc()->Connect<fuchsia::ui::policy::Presenter>();
presenter_.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::ui::policy::Presenter error", status);
});
presenter_->PresentView(std::move(view_holder_token), nullptr);
return true;
}
void PresentView::Kill() {
presenter_.Unbind();
legacy_view_provider_.Unbind();
view_.Unbind();
view_controller_.Unbind();
intl_controller_.Unbind();
}
void PresentView::RunIntlService(const std::string& locale,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
fuchsia::sys::Launcher* launcher) {
FX_CHECK(!intl_controller_);
fuchsia::sys::LaunchInfo launch_info;
launch_info.url = kIntlPropertyProviderUri;
launch_info.arguments = std::vector{std::string{"--set_initial_profile"},
std::string{fxl::Substitute("--locale_ids=$0", locale)}};
launch_info.directory_request = directory_request.TakeChannel();
launcher->CreateComponent(std::move(launch_info), intl_controller_.NewRequest());
intl_controller_.set_error_handler([this](zx_status_t status) {
Kill();
view_error_callback_("fuchsia::sys::ComponentController (for intl_manager) error", status);
});
}
} // namespace present_view