blob: 8bb5df9c0d2e30078c70d3dc7e451612cf59aa39 [file] [log] [blame]
// Copyright 2016 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_stub.h"
#include <utility>
#include "garnet/bin/ui/view_manager/view_registry.h"
#include "garnet/bin/ui/view_manager/view_state.h"
#include "garnet/bin/ui/view_manager/view_tree_state.h"
#include "lib/fxl/logging.h"
namespace view_manager {
class PendingViewOwnerTransferState {
public:
PendingViewOwnerTransferState(
std::unique_ptr<ViewStub> view_stub,
fidl::InterfaceRequest<::fuchsia::ui::viewsv1token::ViewOwner>
transferred_view_owner_request)
: view_stub_(std::move(view_stub)),
transferred_view_owner_request_(
std::move(transferred_view_owner_request)) {}
~PendingViewOwnerTransferState() {}
// A reference to keep the |ViewStub| alive until |OnViewResolved| is called.
std::unique_ptr<ViewStub> view_stub_;
// The |ViewOwner| we want to transfer ownership to.
fidl::InterfaceRequest<::fuchsia::ui::viewsv1token::ViewOwner>
transferred_view_owner_request_;
};
ViewStub::ViewStub(
ViewRegistry* registry,
fidl::InterfaceHandle<::fuchsia::ui::viewsv1token::ViewOwner> owner,
zx::eventpair host_import_token)
: registry_(registry),
owner_(owner.Bind()),
host_import_token_(std::move(host_import_token)),
weak_factory_(this) {
FXL_DCHECK(registry_);
FXL_DCHECK(owner_);
FXL_DCHECK(host_import_token_);
owner_.set_error_handler([this] {
OnViewResolved(::fuchsia::ui::viewsv1token::ViewToken(), false);
});
owner_->GetToken([this](::fuchsia::ui::viewsv1token::ViewToken view_token) {
OnViewResolved(std::move(view_token), true);
});
}
ViewStub::~ViewStub() {
// Ensure that everything was properly released before this object was
// destroyed. The |ViewRegistry| is responsible for maintaining the
// invariant that all |ViewState| objects are owned so by the time we
// get here, the view should have found a new owner or been unregistered.
FXL_DCHECK(is_unavailable());
}
ViewContainerState* ViewStub::container() const {
return parent_ ? static_cast<ViewContainerState*>(parent_) : tree_;
}
void ViewStub::AttachView(ViewState* state) {
FXL_DCHECK(state);
FXL_DCHECK(!state->view_stub());
FXL_DCHECK(is_pending());
state_ = state;
state_->set_view_stub(this);
SetTreeForChildrenOfView(state_, tree_);
}
void ViewStub::SetProperties(
::fuchsia::ui::viewsv1::ViewPropertiesPtr properties) {
FXL_DCHECK(!is_unavailable());
properties_ = std::move(properties);
}
ViewState* ViewStub::ReleaseView() {
if (is_unavailable())
return nullptr;
ViewState* state = state_;
if (state) {
FXL_DCHECK(state->view_stub() == this);
state->set_view_stub(nullptr);
state_ = nullptr;
SetTreeForChildrenOfView(state, nullptr);
}
properties_.reset();
unavailable_ = true;
return state;
}
void ViewStub::SetContainer(ViewContainerState* container, uint32_t key) {
FXL_DCHECK(container);
FXL_DCHECK(!tree_ && !parent_);
key_ = key;
parent_ = container->AsViewState();
if (parent_) {
if (parent_->view_stub())
SetTreeRecursively(parent_->view_stub()->tree());
} else {
ViewTreeState* tree = container->AsViewTreeState();
FXL_DCHECK(tree);
SetTreeRecursively(tree);
}
}
void ViewStub::Unlink() {
parent_ = nullptr;
key_ = 0;
SetTreeRecursively(nullptr);
}
void ViewStub::SetTreeRecursively(ViewTreeState* tree) {
if (tree_ == tree)
return;
tree_ = tree;
if (state_)
SetTreeForChildrenOfView(state_, tree);
}
void ViewStub::SetTreeForChildrenOfView(ViewState* view, ViewTreeState* tree) {
for (const auto& pair : view->children()) {
pair.second->SetTreeRecursively(tree);
}
}
// Called when the ViewOwner returns a token (using GetToken), or when the
// ViewOwner is disconnected.
void ViewStub::OnViewResolved(::fuchsia::ui::viewsv1token::ViewToken view_token,
bool success) {
if (success && transfer_view_owner_when_view_resolved()) {
// While we were waiting for GetToken(), the view was transferred to a new
// ViewOwner). Now that we got the GetToken() call, transfer the ownership
// correctly internally.
FXL_DCHECK(!container()); // Make sure we're removed from the view tree
FXL_DCHECK(pending_view_owner_transfer_->view_stub_ != nullptr);
FXL_DCHECK(pending_view_owner_transfer_->transferred_view_owner_request_
.is_valid());
FXL_DCHECK(owner_);
owner_.Unbind();
registry_->TransferViewOwner(
std::move(view_token),
std::move(
pending_view_owner_transfer_->transferred_view_owner_request_));
// We don't have any |view_state| resolved to us now, but |ReleaseView| will
// still mark us as unavailable and clear properties
ReleaseView();
// |pending_view_owner_transfer_| holds a reference to ourselves. Don't hold
// that reference anymore, which should release us immediately.
pending_view_owner_transfer_.reset();
} else {
// 1. We got the ViewOwner GetToken() callback as expected.
// 2. Or, the ViewOwner was closed before the GetToken() callback (in
// which case view_token is null).
owner_.Unbind();
registry_->OnViewResolved(this, std::move(view_token), success);
}
}
void ViewStub::TransferViewOwnerWhenViewResolved(
std::unique_ptr<ViewStub> view_stub,
fidl::InterfaceRequest<::fuchsia::ui::viewsv1token::ViewOwner>
transferred_view_owner_request) {
FXL_DCHECK(!container()); // Make sure we've been removed from the view tree
FXL_DCHECK(!pending_view_owner_transfer_);
// When |OnViewResolved| gets called, we'll just transfer ownership
// of the view instead of calling |ViewRegistry.OnViewResolved|.
// Save the necessary state in |pending_view_owner_transfer_|
pending_view_owner_transfer_.reset(new PendingViewOwnerTransferState(
std::move(view_stub), std::move(transferred_view_owner_request)));
// TODO(mikejurka): should we have an error handler on
// transferred_view_owner_request_?
}
void ViewStub::ReleaseHost() {
host_import_token_.reset();
host_node_.reset();
}
void ViewStub::ImportHostNode(scenic::Session* session) {
FXL_DCHECK(host_import_token_);
FXL_DCHECK(!host_node_);
host_node_.reset(new scenic::ImportNode(session));
host_node_->Bind(std::move(host_import_token_));
}
} // namespace view_manager