blob: efb28fb04712890e413d4bd7f647104fad9f699a [file] [log] [blame]
// Copyright 2021 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 "chrome/browser/chrome_browser_main_parts_fuchsia.h"
#include <fuchsia/ui/app/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/ui/scenic/cpp/commands.h>
#include <lib/ui/scenic/cpp/resources.h>
#include <lib/ui/scenic/cpp/session.h>
#include "base/bind.h"
#include "base/check.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "base/fuchsia/scoped_service_binding.h"
#include "base/notreached.h"
#include "ui/platform_window/fuchsia/initialize_presenter_api_view.h"
namespace {
fuchsia::ui::views::ViewRef CloneViewRef(
const fuchsia::ui::views::ViewRef& view_ref) {
fuchsia::ui::views::ViewRef dup;
zx_status_t status =
view_ref.reference.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup.reference);
ZX_CHECK(status == ZX_OK, status) << "zx_object_duplicate";
return dup;
}
struct SubViewData {
scenic::ViewHolder view_holder;
fuchsia::ui::views::ViewRef view_ref;
};
// ViewProvider implementation that provides a single view and exposes all
// requested view from PlatformOzoneScenic inside it.
class ViewProviderScenic : public fuchsia::ui::app::ViewProvider {
public:
ViewProviderScenic()
: binding_(base::ComponentContextForProcess()->outgoing().get(), this),
scenic_(base::ComponentContextForProcess()
->svc()
->Connect<fuchsia::ui::scenic::Scenic>()),
scenic_session_(scenic_.get(), focuser_.NewRequest()) {
scenic_.set_error_handler([](zx_status_t status) {
ZX_LOG(ERROR, status) << " Scenic lost.";
// Terminate here so that e.g. a Scenic crash results in the browser
// immediately terminating, without generating a cascading crash report.
base::Process::TerminateCurrentProcessImmediately(1);
});
scenic_session_.set_event_handler(
fit::bind_member(this, &ViewProviderScenic::OnScenicEvents));
}
~ViewProviderScenic() override = default;
// fuchsia::ui::app::ViewProvider overrides.
void CreateView(
zx::eventpair token,
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
override {
CreateViewWithViewRef(std::move(token), {}, {});
}
void CreateViewWithViewRef(zx::eventpair token,
fuchsia::ui::views::ViewRefControl control_ref,
fuchsia::ui::views::ViewRef view_ref) override {
if (view_) {
LOG(WARNING) << "Unexpected spurious call to |CreateViewWithViewRef|. "
"Deleting previously created view.";
subviews_.clear();
is_node_attached_ = false;
view_has_focus_ = false;
node_.reset();
view_.reset();
view_properties_ = absl::nullopt;
}
view_ = std::make_unique<scenic::View>(
&scenic_session_, fuchsia::ui::views::ViewToken({std::move(token)}),
std::move(control_ref), std::move(view_ref), "root-view");
node_ = std::make_unique<scenic::EntityNode>(&scenic_session_);
for (auto& subview : subviews_) {
node_->AddChild(subview.view_holder);
}
Present();
}
void PresentView(fuchsia::ui::views::ViewHolderToken view_holder_token,
fuchsia::ui::views::ViewRef view_ref) {
SubViewData subview = {
.view_holder = scenic::ViewHolder(&scenic_session_,
std::move(view_holder_token).value,
"subview-holder"),
.view_ref = std::move(view_ref)};
if (view_) {
if (view_properties_) {
subview.view_holder.SetViewProperties(*view_properties_);
}
node_->AddChild(subview.view_holder);
Present();
}
subviews_.push_back(std::move(subview));
}
private:
void OnScenicEvents(std::vector<fuchsia::ui::scenic::Event> events) {
for (const auto& event : events) {
if (event.is_gfx() && event.gfx().is_view_properties_changed()) {
if (event.gfx().view_properties_changed().view_id != view_->id()) {
LOG(WARNING) << "Received event for unknown view.";
return;
}
UpdateViewProperties(event.gfx().view_properties_changed().properties);
} else if (event.is_input() && event.input().is_focus()) {
view_has_focus_ = event.input().focus().focused;
FocusView();
}
}
}
void UpdateViewProperties(
const fuchsia::ui::gfx::ViewProperties& view_properties) {
const float width =
view_properties.bounding_box.max.x - view_properties.bounding_box.min.x;
const float height =
view_properties.bounding_box.max.y - view_properties.bounding_box.min.y;
if (width == 0 || height == 0) {
if (is_node_attached_) {
node_->Detach();
is_node_attached_ = false;
}
} else {
if (!is_node_attached_) {
view_->AddChild(*node_);
is_node_attached_ = true;
}
}
view_properties_ = view_properties;
for (auto& subview : subviews_) {
subview.view_holder.SetViewProperties(*view_properties_);
}
Present();
}
void FocusView(int tries = 2) {
if (tries == 0) {
LOG(ERROR) << "Unable to pass focus to chrome window.";
return;
}
if (!subviews_.empty() && view_has_focus_) {
focuser_->RequestFocus({CloneViewRef(subviews_.front().view_ref)},
[this, tries](auto result) {
if (result.is_err()) {
FocusView(tries - 1);
}
});
}
}
void Present() {
scenic_session_.Present(
zx_clock_get_monotonic(),
[this](fuchsia::images::PresentationInfo info) { FocusView(); });
}
const base::ScopedServiceBinding<fuchsia::ui::app::ViewProvider> binding_;
fuchsia::ui::scenic::ScenicPtr scenic_;
fuchsia::ui::views::FocuserPtr focuser_;
scenic::Session scenic_session_;
// The view created by this ViewProvider. The view is created lazily when a
// request is received.
std::unique_ptr<scenic::View> view_;
// Entity node for the |view_|.
std::unique_ptr<scenic::EntityNode> node_;
// True if the root EntityNode has been added to the View.
bool is_node_attached_ = false;
// True is the root view has focus.
bool view_has_focus_ = false;
// The holders for all the views that are presented.
std::vector<SubViewData> subviews_;
// The properties of the top level view. They are forwarded to the embedded
// views.
absl::optional<fuchsia::ui::gfx::ViewProperties> view_properties_;
};
} // namespace
ChromeBrowserMainPartsFuchsia::ChromeBrowserMainPartsFuchsia(
const content::MainFunctionParams& parameters,
StartupData* startup_data)
: ChromeBrowserMainParts(parameters, startup_data) {}
void ChromeBrowserMainPartsFuchsia::ShowMissingLocaleMessageBox() {
// Locale data should be bundled for all possible platform locales,
// so crash here to make missing-locale states more visible.
CHECK(false);
}
int ChromeBrowserMainPartsFuchsia::PreMainMessageLoopRun() {
// Register the ViewPresenter API.
ui::fuchsia::SetScenicViewPresenter(
base::BindRepeating(&ViewProviderScenic::PresentView,
std::make_unique<ViewProviderScenic>()));
zx_status_t status =
base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo();
ZX_CHECK(status == ZX_OK, status);
return ChromeBrowserMainParts::PreMainMessageLoopRun();
}