blob: e4b8e6ba0206acd2b9a604d27ea62c6ca0c008e5 [file] [log] [blame]
// Copyright 2015 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 "garnet/bin/ui/view_manager/view_registry.h"
#include <algorithm>
#include <cmath>
#include <utility>
#include "garnet/bin/ui/view_manager/view_impl.h"
#include "garnet/bin/ui/view_manager/view_tree_impl.h"
#include "lib/app/cpp/connect.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "lib/fxl/strings/string_printf.h"
#include "lib/ui/input/cpp/formatting.h"
#include "lib/ui/input/fidl/ime_service.fidl.h"
#include "lib/ui/scenic/client/resources.h"
#include "lib/ui/views/cpp/formatting.h"
namespace view_manager {
namespace {
// The height at which hit tests originate.
// TODO(MZ-163): This shouldn't be hardcoded here.
constexpr float kHitTestOriginZ = 10000.f;
bool Validate(const mozart::DisplayMetrics& value) {
return std::isnormal(value.device_pixel_ratio) &&
value.device_pixel_ratio > 0.f;
}
bool Validate(const mozart::ViewLayout& value) {
return value.size && value.size->width >= 0 && value.size->height >= 0;
}
bool Validate(const mozart::ViewProperties& value) {
if (value.display_metrics && !Validate(*value.display_metrics))
return false;
if (value.view_layout && !Validate(*value.view_layout))
return false;
return true;
}
// Returns true if the properties are valid and are sufficient for
// operating the view tree.
bool IsComplete(const mozart::ViewProperties& value) {
return Validate(value) && value.view_layout && value.display_metrics;
}
void ApplyOverrides(mozart::ViewProperties* value,
const mozart::ViewProperties* overrides) {
if (!overrides)
return;
if (overrides->display_metrics)
value->display_metrics = overrides->display_metrics.Clone();
if (overrides->view_layout)
value->view_layout = overrides->view_layout.Clone();
}
std::string SanitizeLabel(const fidl::String& label) {
return label.get().substr(0, mozart::ViewManager::kLabelMaxLength);
}
std::unique_ptr<FocusChain> CopyFocusChain(const FocusChain* chain) {
std::unique_ptr<FocusChain> new_chain = nullptr;
if (chain) {
new_chain = std::make_unique<FocusChain>();
new_chain->version = chain->version;
new_chain->chain.resize(chain->chain.size());
for (size_t index = 0; index < chain->chain.size(); ++index) {
new_chain->chain[index] = chain->chain[index].Clone();
}
}
return new_chain;
}
mozart::TransformPtr ToTransform(scenic::mat4Ptr matrix) {
FXL_DCHECK(matrix);
// Note: mat4 is column-major but transform is row-major
auto transform = mozart::Transform::New();
const auto& in = matrix->matrix;
auto& out = transform->matrix;
out.resize(16u);
out[0] = in[0];
out[1] = in[4];
out[2] = in[8];
out[3] = in[12];
out[4] = in[1];
out[5] = in[5];
out[6] = in[9];
out[7] = in[13];
out[8] = in[2];
out[9] = in[6];
out[10] = in[10];
out[11] = in[14];
out[12] = in[3];
out[13] = in[7];
out[14] = in[11];
out[15] = in[15];
return transform;
}
} // namespace
ViewRegistry::ViewRegistry(app::ApplicationContext* application_context)
: application_context_(application_context),
scene_manager_(application_context_
->ConnectToEnvironmentService<scenic::SceneManager>()),
session_(scene_manager_.get()),
weak_factory_(this) {
// TODO(MZ-128): Register session listener and destroy views if their
// content nodes become unavailable.
scene_manager_.set_connection_error_handler([] {
FXL_LOG(ERROR) << "Exiting due to scene manager connection error.";
exit(1);
});
session_.set_connection_error_handler([] {
FXL_LOG(ERROR) << "Exiting due to session connection error.";
exit(1);
});
}
ViewRegistry::~ViewRegistry() {}
void ViewRegistry::GetSceneManager(
fidl::InterfaceRequest<scenic::SceneManager> scene_manager_request) {
// TODO(jeffbrown): We should have a better way to duplicate the
// SceneManager connection without going back out through the environment.
application_context_->ConnectToEnvironmentService(
std::move(scene_manager_request));
}
// CREATE / DESTROY VIEWS
void ViewRegistry::CreateView(
fidl::InterfaceRequest<mozart::View> view_request,
fidl::InterfaceRequest<mozart::ViewOwner> view_owner_request,
mozart::ViewListenerPtr view_listener,
zx::eventpair parent_export_token,
const fidl::String& label) {
FXL_DCHECK(view_request.is_pending());
FXL_DCHECK(view_owner_request.is_pending());
FXL_DCHECK(view_listener);
FXL_DCHECK(parent_export_token);
auto view_token = mozart::ViewToken::New();
view_token->value = next_view_token_value_++;
FXL_CHECK(view_token->value);
FXL_CHECK(!FindView(view_token->value));
// Create the state and bind the interfaces to it.
ViewState* view_state =
new ViewState(this, std::move(view_token), std::move(view_request),
std::move(view_listener), &session_, SanitizeLabel(label));
view_state->BindOwner(std::move(view_owner_request));
// Export a node which represents the view's attachment point.
view_state->top_node().Export(std::move(parent_export_token));
view_state->top_node().SetTag(view_state->view_token()->value);
view_state->top_node().SetLabel(view_state->FormattedLabel());
SchedulePresentSession();
// Add to registry and return token.
views_by_token_.emplace(view_state->view_token()->value, view_state);
FXL_VLOG(1) << "CreateView: view=" << view_state;
}
void ViewRegistry::OnViewDied(ViewState* view_state,
const std::string& reason) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
FXL_VLOG(1) << "OnViewDied: view=" << view_state << ", reason=" << reason;
UnregisterView(view_state);
}
void ViewRegistry::UnregisterView(ViewState* view_state) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
FXL_VLOG(1) << "UnregisterView: view=" << view_state;
HijackView(view_state);
UnregisterChildren(view_state);
// Remove the view's content node from the session.
view_state->top_node().Detach();
SchedulePresentSession();
// Remove from registry.
views_by_token_.erase(view_state->view_token()->value);
delete view_state;
}
// CREATE / DESTROY VIEW TREES
void ViewRegistry::CreateViewTree(
fidl::InterfaceRequest<mozart::ViewTree> view_tree_request,
mozart::ViewTreeListenerPtr view_tree_listener,
const fidl::String& label) {
FXL_DCHECK(view_tree_request.is_pending());
FXL_DCHECK(view_tree_listener);
auto view_tree_token = mozart::ViewTreeToken::New();
view_tree_token->value = next_view_tree_token_value_++;
FXL_CHECK(view_tree_token->value);
FXL_CHECK(!FindViewTree(view_tree_token->value));
// Create the state and bind the interfaces to it.
ViewTreeState* tree_state = new ViewTreeState(
this, std::move(view_tree_token), std::move(view_tree_request),
std::move(view_tree_listener), SanitizeLabel(label));
// Add to registry.
view_trees_by_token_.emplace(tree_state->view_tree_token()->value,
tree_state);
FXL_VLOG(1) << "CreateViewTree: tree=" << tree_state;
}
void ViewRegistry::OnViewTreeDied(ViewTreeState* tree_state,
const std::string& reason) {
FXL_DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
FXL_VLOG(1) << "OnViewTreeDied: tree=" << tree_state << ", reason=" << reason;
UnregisterViewTree(tree_state);
}
void ViewRegistry::UnregisterViewTree(ViewTreeState* tree_state) {
FXL_DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
FXL_VLOG(1) << "UnregisterViewTree: tree=" << tree_state;
UnregisterChildren(tree_state);
// Remove from registry.
view_trees_by_token_.erase(tree_state->view_tree_token()->value);
delete tree_state;
}
// LIFETIME
void ViewRegistry::UnregisterViewContainer(
ViewContainerState* container_state) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
ViewState* view_state = container_state->AsViewState();
if (view_state)
UnregisterView(view_state);
else
UnregisterViewTree(container_state->AsViewTreeState());
}
void ViewRegistry::UnregisterViewStub(std::unique_ptr<ViewStub> view_stub) {
FXL_DCHECK(view_stub);
ViewState* view_state = view_stub->ReleaseView();
if (view_state)
UnregisterView(view_state);
ReleaseViewStubChildHost(view_stub.get());
}
void ViewRegistry::UnregisterChildren(ViewContainerState* container_state) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
// Recursively unregister all children since they will become unowned
// at this point taking care to unlink each one before its unregistration.
for (auto& child : container_state->UnlinkAllChildren())
UnregisterViewStub(std::move(child));
}
void ViewRegistry::ReleaseViewStubChildHost(ViewStub* view_stub) {
view_stub->ReleaseHost();
SchedulePresentSession();
}
// TREE MANIPULATION
void ViewRegistry::AddChild(
ViewContainerState* container_state,
uint32_t child_key,
fidl::InterfaceHandle<mozart::ViewOwner> child_view_owner,
zx::eventpair host_import_token) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
FXL_DCHECK(child_view_owner);
FXL_DCHECK(host_import_token);
FXL_VLOG(1) << "AddChild: container=" << container_state
<< ", child_key=" << child_key;
// Ensure there are no other children with the same key.
if (container_state->children().find(child_key) !=
container_state->children().end()) {
FXL_LOG(ERROR) << "Attempted to add a child with a duplicate key: "
<< "container=" << container_state
<< ", child_key=" << child_key;
UnregisterViewContainer(container_state);
return;
}
// If this is a view tree, ensure it only has one root.
ViewTreeState* view_tree_state = container_state->AsViewTreeState();
if (view_tree_state && !container_state->children().empty()) {
FXL_LOG(ERROR) << "Attempted to add a second child to a view tree: "
<< "container=" << container_state
<< ", child_key=" << child_key;
UnregisterViewContainer(container_state);
return;
}
// Add a stub, pending resolution of the view owner.
// Assuming the stub isn't removed prematurely, |OnViewResolved| will be
// called asynchronously with the result of the resolution.
container_state->LinkChild(child_key, std::unique_ptr<ViewStub>(new ViewStub(
this, std::move(child_view_owner),
std::move(host_import_token))));
}
void ViewRegistry::RemoveChild(
ViewContainerState* container_state,
uint32_t child_key,
fidl::InterfaceRequest<mozart::ViewOwner> transferred_view_owner_request) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
FXL_VLOG(1) << "RemoveChild: container=" << container_state
<< ", child_key=" << child_key;
// Ensure the child key exists in the container.
auto child_it = container_state->children().find(child_key);
if (child_it == container_state->children().end()) {
FXL_LOG(ERROR) << "Attempted to remove a child with an invalid key: "
<< "container=" << container_state
<< ", child_key=" << child_key;
UnregisterViewContainer(container_state);
return;
}
// Unlink the child from its container.
TransferOrUnregisterViewStub(container_state->UnlinkChild(child_key),
std::move(transferred_view_owner_request));
}
void ViewRegistry::SetChildProperties(
ViewContainerState* container_state,
uint32_t child_key,
mozart::ViewPropertiesPtr child_properties) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
FXL_VLOG(1) << "SetChildProperties: container=" << container_state
<< ", child_key=" << child_key
<< ", child_properties=" << child_properties;
// Check whether the properties are well-formed.
if (child_properties && !Validate(*child_properties)) {
FXL_LOG(ERROR) << "Attempted to set invalid child view properties: "
<< "container=" << container_state
<< ", child_key=" << child_key
<< ", child_properties=" << child_properties;
UnregisterViewContainer(container_state);
return;
}
// Check whether the child key exists in the container.
auto child_it = container_state->children().find(child_key);
if (child_it == container_state->children().end()) {
FXL_LOG(ERROR) << "Attempted to modify child with an invalid key: "
<< "container=" << container_state
<< ", child_key=" << child_key
<< ", child_properties=" << child_properties;
UnregisterViewContainer(container_state);
return;
}
// Immediately discard requests on unavailable views.
ViewStub* child_stub = child_it->second.get();
if (child_stub->is_unavailable())
return;
// Store the updated properties specified by the container if changed.
if (child_properties.Equals(child_stub->properties()))
return;
// Apply the change.
child_stub->SetProperties(std::move(child_properties));
if (child_stub->state()) {
InvalidateView(child_stub->state(),
ViewState::INVALIDATION_PROPERTIES_CHANGED);
}
}
void ViewRegistry::RequestFocus(ViewContainerState* container_state,
uint32_t child_key) {
FXL_DCHECK(IsViewContainerStateRegisteredDebug(container_state));
FXL_VLOG(1) << "RequestFocus: container=" << container_state
<< ", child_key=" << child_key;
// Check whether the child key exists in the container.
auto child_it = container_state->children().find(child_key);
if (child_it == container_state->children().end()) {
FXL_LOG(ERROR) << "Attempted to modify child with an invalid key: "
<< "container=" << container_state
<< ", child_key=" << child_key;
UnregisterViewContainer(container_state);
return;
}
// Immediately discard requests on unavailable views.
ViewStub* child_stub = child_it->second.get();
if (child_stub->is_unavailable())
return;
// Set active focus chain for this view tree
ViewTreeState* tree_state = child_stub->tree();
tree_state->RequestFocus(child_stub);
}
void ViewRegistry::OnViewResolved(ViewStub* view_stub,
mozart::ViewTokenPtr view_token) {
FXL_DCHECK(view_stub);
ViewState* view_state = view_token ? FindView(view_token->value) : nullptr;
if (view_state)
AttachResolvedViewAndNotify(view_stub, view_state);
else
ReleaseUnavailableViewAndNotify(view_stub);
}
void ViewRegistry::TransferViewOwner(
mozart::ViewTokenPtr view_token,
fidl::InterfaceRequest<mozart::ViewOwner> transferred_view_owner_request) {
FXL_DCHECK(view_token);
FXL_DCHECK(transferred_view_owner_request.is_pending());
ViewState* view_state = view_token ? FindView(view_token->value) : nullptr;
if (view_state) {
view_state->ReleaseOwner(); // don't need the ViewOwner pipe anymore
view_state->BindOwner(std::move(transferred_view_owner_request));
}
}
void ViewRegistry::AttachResolvedViewAndNotify(ViewStub* view_stub,
ViewState* view_state) {
FXL_DCHECK(view_stub);
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
FXL_VLOG(2) << "AttachViewStubAndNotify: view=" << view_state;
// Hijack the view from its current container, if needed.
HijackView(view_state);
// Attach the view's content.
if (view_stub->container()) {
view_stub->ImportHostNode(&session_);
view_stub->host_node()->AddChild(view_state->top_node());
SchedulePresentSession();
auto view_info = mozart::ViewInfo::New();
SendChildAttached(view_stub->container(), view_stub->key(),
std::move(view_info));
}
// Attach the view.
view_state->ReleaseOwner(); // don't need the ViewOwner pipe anymore
view_stub->AttachView(view_state);
InvalidateView(view_state, ViewState::INVALIDATION_PARENT_CHANGED);
}
void ViewRegistry::ReleaseUnavailableViewAndNotify(ViewStub* view_stub) {
FXL_DCHECK(view_stub);
FXL_VLOG(2) << "ReleaseUnavailableViewAndNotify: key=" << view_stub->key();
ViewState* view_state = view_stub->ReleaseView();
FXL_DCHECK(!view_state);
if (view_stub->container())
SendChildUnavailable(view_stub->container(), view_stub->key());
}
void ViewRegistry::HijackView(ViewState* view_state) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
ViewStub* view_stub = view_state->view_stub();
if (view_stub) {
view_stub->ReleaseView();
if (view_stub->container())
SendChildUnavailable(view_stub->container(), view_stub->key());
}
}
void ViewRegistry::TransferOrUnregisterViewStub(
std::unique_ptr<ViewStub> view_stub,
fidl::InterfaceRequest<mozart::ViewOwner> transferred_view_owner_request) {
FXL_DCHECK(view_stub);
if (transferred_view_owner_request.is_pending()) {
ReleaseViewStubChildHost(view_stub.get());
if (view_stub->state()) {
ViewState* view_state = view_stub->ReleaseView();
InvalidateView(view_state, ViewState::INVALIDATION_PARENT_CHANGED);
view_state->BindOwner(std::move(transferred_view_owner_request));
return;
}
if (view_stub->is_pending()) {
FXL_DCHECK(!view_stub->state());
// Handle transfer of pending view.
view_stub->TransferViewOwnerWhenViewResolved(
std::move(view_stub), std::move(transferred_view_owner_request));
return;
}
}
UnregisterViewStub(std::move(view_stub));
}
// INVALIDATION
void ViewRegistry::InvalidateView(ViewState* view_state, uint32_t flags) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
FXL_VLOG(2) << "InvalidateView: view=" << view_state << ", flags=" << flags;
view_state->set_invalidation_flags(view_state->invalidation_flags() | flags);
if (view_state->view_stub() && view_state->view_stub()->tree()) {
InvalidateViewTree(view_state->view_stub()->tree(),
ViewTreeState::INVALIDATION_VIEWS_INVALIDATED);
}
}
void ViewRegistry::InvalidateViewTree(ViewTreeState* tree_state,
uint32_t flags) {
FXL_DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
FXL_VLOG(2) << "InvalidateViewTree: tree=" << tree_state
<< ", flags=" << flags;
tree_state->set_invalidation_flags(tree_state->invalidation_flags() | flags);
ScheduleTraversal();
}
void ViewRegistry::ScheduleTraversal() {
if (!traversal_scheduled_) {
traversal_scheduled_ = true;
fsl::MessageLoop::GetCurrent()->task_runner()->PostTask(
[weak = weak_factory_.GetWeakPtr()] {
if (weak)
weak->Traverse();
});
}
}
void ViewRegistry::Traverse() {
FXL_DCHECK(traversal_scheduled_);
traversal_scheduled_ = false;
for (const auto& pair : view_trees_by_token_)
TraverseViewTree(pair.second);
}
void ViewRegistry::TraverseViewTree(ViewTreeState* tree_state) {
FXL_DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
FXL_VLOG(2) << "TraverseViewTree: tree=" << tree_state
<< ", invalidation_flags=" << tree_state->invalidation_flags();
uint32_t flags = tree_state->invalidation_flags();
if (flags & ViewTreeState::INVALIDATION_VIEWS_INVALIDATED) {
ViewStub* root_stub = tree_state->GetRoot();
if (root_stub && root_stub->state())
TraverseView(root_stub->state(), false);
}
tree_state->set_invalidation_flags(0u);
}
void ViewRegistry::TraverseView(ViewState* view_state,
bool parent_properties_changed) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
FXL_VLOG(2) << "TraverseView: view=" << view_state
<< ", parent_properties_changed=" << parent_properties_changed
<< ", invalidation_flags=" << view_state->invalidation_flags();
uint32_t flags = view_state->invalidation_flags();
// Update view properties.
bool view_properties_changed = false;
if (parent_properties_changed ||
(flags & (ViewState::INVALIDATION_PROPERTIES_CHANGED |
ViewState::INVALIDATION_PARENT_CHANGED))) {
mozart::ViewPropertiesPtr properties = ResolveViewProperties(view_state);
if (properties) {
if (!view_state->issued_properties() ||
!properties->Equals(*view_state->issued_properties())) {
view_state->IssueProperties(std::move(properties));
view_properties_changed = true;
}
}
flags &= ~(ViewState::INVALIDATION_PROPERTIES_CHANGED |
ViewState::INVALIDATION_PARENT_CHANGED);
}
// If we don't have view properties yet then we cannot pursue traversals
// any further.
if (!view_state->issued_properties()) {
FXL_VLOG(2) << "View has no valid properties: view=" << view_state;
view_state->set_invalidation_flags(flags);
return;
}
// Deliver property change event if needed.
bool send_properties = view_properties_changed ||
(flags & ViewState::INVALIDATION_RESEND_PROPERTIES);
if (send_properties) {
if (!(flags & ViewState::INVALIDATION_IN_PROGRESS)) {
SendPropertiesChanged(view_state,
view_state->issued_properties().Clone());
flags = ViewState::INVALIDATION_IN_PROGRESS;
} else {
FXL_VLOG(2) << "View invalidation stalled awaiting response: view="
<< view_state;
if (send_properties)
flags |= ViewState::INVALIDATION_RESEND_PROPERTIES;
flags |= ViewState::INVALIDATION_STALLED;
}
}
view_state->set_invalidation_flags(flags);
// TODO(jeffbrown): Optimize propagation.
// This should defer traversal of the rest of the subtree until the view
// flushes its container or a timeout expires. We will need to be careful
// to ensure that we completely process one traversal before starting the
// next one and we'll have to retain some state. The same behavior should
// be applied when the parent's own properties change (assuming that it is
// likely to want to resize its children, unless it says otherwise somehow).
// Traverse all children.
for (const auto& pair : view_state->children()) {
ViewState* child_state = pair.second->state();
if (child_state)
TraverseView(pair.second->state(), view_properties_changed);
}
}
mozart::ViewPropertiesPtr ViewRegistry::ResolveViewProperties(
ViewState* view_state) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
ViewStub* view_stub = view_state->view_stub();
if (!view_stub || !view_stub->properties())
return nullptr;
if (view_stub->parent()) {
if (!view_stub->parent()->issued_properties())
return nullptr;
mozart::ViewPropertiesPtr properties =
view_stub->parent()->issued_properties().Clone();
ApplyOverrides(properties.get(), view_stub->properties().get());
return properties;
} else if (view_stub->is_root_of_tree()) {
if (!view_stub->properties() || !IsComplete(*view_stub->properties())) {
FXL_VLOG(2) << "View tree properties are incomplete: root=" << view_state
<< ", properties=" << view_stub->properties();
return nullptr;
}
return view_stub->properties().Clone();
} else {
return nullptr;
}
}
void ViewRegistry::SchedulePresentSession() {
if (!present_session_scheduled_) {
present_session_scheduled_ = true;
fsl::MessageLoop::GetCurrent()->task_runner()->PostTask(
[weak = weak_factory_.GetWeakPtr()] {
if (weak)
weak->PresentSession();
});
}
}
void ViewRegistry::PresentSession() {
FXL_DCHECK(present_session_scheduled_);
present_session_scheduled_ = false;
session_.Present(0, [this](scenic::PresentationInfoPtr info) {});
}
// VIEW AND VIEW TREE SERVICE PROVIDERS
void ViewRegistry::ConnectToViewService(ViewState* view_state,
const fidl::String& service_name,
zx::channel client_handle) {
FXL_DCHECK(IsViewStateRegisteredDebug(view_state));
if (service_name == mozart::InputConnection::Name_) {
CreateInputConnection(view_state->view_token()->Clone(),
fidl::InterfaceRequest<mozart::InputConnection>(
std::move(client_handle)));
}
}
void ViewRegistry::ConnectToViewTreeService(ViewTreeState* tree_state,
const fidl::String& service_name,
zx::channel client_handle) {
FXL_DCHECK(IsViewTreeStateRegisteredDebug(tree_state));
if (service_name == mozart::InputDispatcher::Name_) {
CreateInputDispatcher(tree_state->view_tree_token()->Clone(),
fidl::InterfaceRequest<mozart::InputDispatcher>(
std::move(client_handle)));
}
}
// VIEW INSPECTOR
void ViewRegistry::HitTest(const mozart::ViewTreeToken& view_tree_token,
const mozart::PointF& point,
HitTestCallback callback) {
FXL_VLOG(1) << "HitTest: tree=" << view_tree_token;
ViewTreeState* view_tree = FindViewTree(view_tree_token.value);
if (!view_tree || !view_tree->GetRoot() ||
!view_tree->GetRoot()->host_node()) {
callback(std::vector<ViewHit>());
return;
}
// TODO(MZ-163): We're making 2D assumptions all over view manager.
// We should redesign the relevant input related APIs to handle 3D content
// and revisit this.
session_.HitTest(
view_tree->GetRoot()->host_node()->id(),
(float[3]){point.x, point.y, kHitTestOriginZ}, (float[3]){0.f, 0.f, -1.f},
[ this,
callback = std::move(callback) ](fidl::Array<scenic::HitPtr> hits) {
std::vector<ViewHit> view_hits;
view_hits.reserve(hits.size());
for (auto& hit : hits) {
auto it = views_by_token_.find(hit->tag_value);
if (it != views_by_token_.end()) {
ViewState* view_state = it->second;
view_hits.emplace_back(
ViewHit{*view_state->view_token(),
ToTransform(std::move(hit->inverse_transform))});
}
}
callback(std::move(view_hits));
});
}
void ViewRegistry::ResolveFocusChain(
mozart::ViewTreeTokenPtr view_tree_token,
const ResolveFocusChainCallback& callback) {
FXL_DCHECK(view_tree_token);
FXL_VLOG(1) << "ResolveFocusChain: view_tree_token=" << view_tree_token;
auto it = view_trees_by_token_.find(view_tree_token->value);
if (it != view_trees_by_token_.end()) {
callback(CopyFocusChain(it->second->focus_chain()));
} else {
callback(nullptr);
}
}
void ViewRegistry::ActivateFocusChain(
mozart::ViewTokenPtr view_token,
const ActivateFocusChainCallback& callback) {
FXL_DCHECK(view_token);
FXL_VLOG(1) << "ActivateFocusChain: view_token=" << view_token;
ViewState* view = FindView(view_token->value);
if (!view) {
callback(nullptr);
return;
}
RequestFocus(view->view_stub()->container(), view->view_stub()->key());
auto tree_state = view->view_stub()->tree();
std::unique_ptr<FocusChain> new_chain =
CopyFocusChain(tree_state->focus_chain());
callback(std::move(new_chain));
}
void ViewRegistry::HasFocus(mozart::ViewTokenPtr view_token,
const HasFocusCallback& callback) {
FXL_DCHECK(view_token);
FXL_VLOG(1) << "HasFocus: view_token=" << view_token;
ViewState* view = FindView(view_token->value);
if (!view) {
callback(false);
return;
}
auto tree_state = view->view_stub()->tree();
auto chain = tree_state->focus_chain();
if (chain) {
for (size_t index = 0; index < chain->chain.size(); ++index) {
if (chain->chain[index]->value == view_token->value) {
callback(true);
return;
}
}
}
callback(false);
}
app::ServiceProvider* ViewRegistry::FindViewServiceProvider(
uint32_t view_token,
std::string service_name) {
ViewState* view_state = FindView(view_token);
if (!view_state) {
return nullptr;
}
auto provider = view_state->GetServiceProviderIfSupports(service_name);
while (!provider && view_state) {
view_state = view_state->view_stub()->parent();
provider = view_state
? view_state->GetServiceProviderIfSupports(service_name)
: nullptr;
}
return provider;
}
void ViewRegistry::GetSoftKeyboardContainer(
mozart::ViewTokenPtr view_token,
fidl::InterfaceRequest<mozart::SoftKeyboardContainer> container) {
FXL_DCHECK(view_token);
FXL_DCHECK(container.is_pending());
FXL_VLOG(1) << "GetSoftKeyboardContainer: view_token=" << view_token;
auto provider = FindViewServiceProvider(view_token->value,
mozart::SoftKeyboardContainer::Name_);
if (provider) {
app::ConnectToService(provider, std::move(container));
}
}
void ViewRegistry::GetImeService(
mozart::ViewTokenPtr view_token,
fidl::InterfaceRequest<mozart::ImeService> ime_service) {
FXL_DCHECK(view_token);
FXL_DCHECK(ime_service.is_pending());
FXL_VLOG(1) << "GetImeService: view_token=" << view_token;
auto provider =
FindViewServiceProvider(view_token->value, mozart::ImeService::Name_);
if (provider) {
app::ConnectToService(provider, std::move(ime_service));
} else {
application_context_->ConnectToEnvironmentService(std::move(ime_service));
}
}
// EXTERNAL SIGNALING
void ViewRegistry::SendPropertiesChanged(ViewState* view_state,
mozart::ViewPropertiesPtr properties) {
FXL_DCHECK(view_state);
FXL_DCHECK(view_state->view_listener());
FXL_VLOG(1) << "SendPropertiesChanged: view_state=" << view_state
<< ", properties=" << properties;
// It's safe to capture the view state because the ViewListener is closed
// before the view state is destroyed so we will only receive the callback
// if the view state is still alive.
view_state->view_listener()->OnPropertiesChanged(
std::move(properties), [this, view_state] {
uint32_t old_flags = view_state->invalidation_flags();
FXL_DCHECK(old_flags & ViewState::INVALIDATION_IN_PROGRESS);
view_state->set_invalidation_flags(
old_flags & ~(ViewState::INVALIDATION_IN_PROGRESS |
ViewState::INVALIDATION_STALLED));
if (old_flags & ViewState::INVALIDATION_STALLED) {
FXL_VLOG(2) << "View recovered from stalled invalidation: view_state="
<< view_state;
InvalidateView(view_state, 0u);
}
});
}
void ViewRegistry::SendChildAttached(ViewContainerState* container_state,
uint32_t child_key,
mozart::ViewInfoPtr child_view_info) {
FXL_DCHECK(container_state);
FXL_DCHECK(child_view_info);
if (!container_state->view_container_listener())
return;
// TODO: Detect ANRs
FXL_VLOG(1) << "SendChildAttached: container_state=" << container_state
<< ", child_key=" << child_key
<< ", child_view_info=" << child_view_info;
container_state->view_container_listener()->OnChildAttached(
child_key, std::move(child_view_info), [] {});
}
void ViewRegistry::SendChildUnavailable(ViewContainerState* container_state,
uint32_t child_key) {
FXL_DCHECK(container_state);
if (!container_state->view_container_listener())
return;
// TODO: Detect ANRs
FXL_VLOG(1) << "SendChildUnavailable: container=" << container_state
<< ", child_key=" << child_key;
container_state->view_container_listener()->OnChildUnavailable(child_key,
[] {});
}
void ViewRegistry::DeliverEvent(const mozart::ViewToken* view_token,
mozart::InputEventPtr event,
ViewInspector::OnEventDelivered callback) {
FXL_DCHECK(view_token);
FXL_DCHECK(event);
FXL_VLOG(1) << "DeliverEvent: view_token=" << *view_token
<< ", event=" << *event;
auto it = input_connections_by_view_token_.find(view_token->value);
if (it == input_connections_by_view_token_.end()) {
FXL_VLOG(1)
<< "DeliverEvent: dropped because there was no input connection";
if (callback)
callback(false);
return;
}
it->second->DeliverEvent(std::move(event), [callback](bool handled) {
if (callback)
callback(handled);
});
}
void ViewRegistry::CreateInputConnection(
mozart::ViewTokenPtr view_token,
fidl::InterfaceRequest<mozart::InputConnection> request) {
FXL_DCHECK(view_token);
FXL_DCHECK(request.is_pending());
FXL_VLOG(1) << "CreateInputConnection: view_token=" << view_token;
const uint32_t view_token_value = view_token->value;
input_connections_by_view_token_.emplace(
view_token_value,
std::make_unique<InputConnectionImpl>(this, this, std::move(view_token),
std::move(request)));
}
void ViewRegistry::OnInputConnectionDied(InputConnectionImpl* connection) {
FXL_DCHECK(connection);
auto it =
input_connections_by_view_token_.find(connection->view_token()->value);
FXL_DCHECK(it != input_connections_by_view_token_.end());
FXL_DCHECK(it->second.get() == connection);
FXL_VLOG(1) << "OnInputConnectionDied: view_token="
<< connection->view_token();
input_connections_by_view_token_.erase(it);
}
void ViewRegistry::CreateInputDispatcher(
mozart::ViewTreeTokenPtr view_tree_token,
fidl::InterfaceRequest<mozart::InputDispatcher> request) {
FXL_DCHECK(view_tree_token);
FXL_DCHECK(request.is_pending());
FXL_VLOG(1) << "CreateInputDispatcher: view_tree_token=" << view_tree_token;
const uint32_t view_tree_token_value = view_tree_token->value;
input_dispatchers_by_view_tree_token_.emplace(
view_tree_token_value,
std::unique_ptr<InputDispatcherImpl>(new InputDispatcherImpl(
this, this, std::move(view_tree_token), std::move(request))));
}
void ViewRegistry::OnInputDispatcherDied(InputDispatcherImpl* dispatcher) {
FXL_DCHECK(dispatcher);
FXL_VLOG(1) << "OnInputDispatcherDied: view_tree_token="
<< dispatcher->view_tree_token();
auto it = input_dispatchers_by_view_tree_token_.find(
dispatcher->view_tree_token()->value);
FXL_DCHECK(it != input_dispatchers_by_view_tree_token_.end());
FXL_DCHECK(it->second.get() == dispatcher);
input_dispatchers_by_view_tree_token_.erase(it);
}
// LOOKUP
ViewState* ViewRegistry::FindView(uint32_t view_token_value) {
auto it = views_by_token_.find(view_token_value);
return it != views_by_token_.end() ? it->second : nullptr;
}
ViewTreeState* ViewRegistry::FindViewTree(uint32_t view_tree_token_value) {
auto it = view_trees_by_token_.find(view_tree_token_value);
return it != view_trees_by_token_.end() ? it->second : nullptr;
}
} // namespace view_manager