|  | // Copyright 2020 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/a11y/lib/focus_chain/focus_chain_manager.h" | 
|  |  | 
|  | #include <lib/async/default.h> | 
|  | #include <lib/syslog/cpp/macros.h> | 
|  | #include <zircon/status.h> | 
|  | #include <zircon/types.h> | 
|  |  | 
|  | #include "src/ui/a11y/lib/util/util.h" | 
|  |  | 
|  | namespace a11y { | 
|  |  | 
|  | FocusChainManager::FocusChainManager(fuchsia::ui::views::FocuserPtr focuser, | 
|  | SemanticsSource* semantics_source) | 
|  | : focuser_(std::move(focuser)), semantics_source_(semantics_source) { | 
|  | FX_DCHECK(semantics_source_); | 
|  | } | 
|  |  | 
|  | FocusChainManager::ViewRefWatcher::ViewRefWatcher(fuchsia::ui::views::ViewRef view_ref, | 
|  | OnViewRefInvalidatedCallback callback) | 
|  | : view_ref_(std::move(view_ref)), | 
|  | callback_(std::move(callback)), | 
|  | wait_(this, view_ref_.reference.get(), ZX_EVENTPAIR_PEER_CLOSED) { | 
|  | FX_CHECK(wait_.Begin(async_get_default_dispatcher()) == ZX_OK); | 
|  | } | 
|  |  | 
|  | FocusChainManager::ViewRefWatcher::~ViewRefWatcher() = default; | 
|  |  | 
|  | void FocusChainManager::OnFocusChange(fuchsia::ui::focus::FocusChain focus_chain, | 
|  | OnFocusChangeCallback callback) { | 
|  | focus_chain_.clear(); | 
|  | if (focus_chain.has_focus_chain()) { | 
|  | auto& new_focus_chain = *(focus_chain.mutable_focus_chain()); | 
|  | for (auto& view_ref : new_focus_chain) { | 
|  | auto view_ref_watcher = std::make_unique<ViewRefWatcher>( | 
|  | std::move(view_ref), [this]() { InvalidateFocusChain(); }); | 
|  | focus_chain_.push_back(std::move(view_ref_watcher)); | 
|  | } | 
|  | } | 
|  | Notify(); | 
|  | callback(); | 
|  | } | 
|  |  | 
|  | void FocusChainManager::Register(fxl::WeakPtr<AccessibilityFocusChainListener> listener) { | 
|  | // On registration, sends the listener the current focus. | 
|  | if (listener) { | 
|  | listener->OnViewFocus(GetFocusedView()); | 
|  | listeners_.push_back(std::move(listener)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FocusChainManager::InvalidateFocusChain() { | 
|  | focus_chain_.clear(); | 
|  | Notify(); | 
|  | } | 
|  |  | 
|  | zx_koid_t FocusChainManager::GetFocusedView() const { | 
|  | if (focus_chain_.empty()) { | 
|  | return ZX_KOID_INVALID; | 
|  | } | 
|  | const auto& focused_view = focus_chain_.back(); | 
|  | return focused_view->koid(); | 
|  | } | 
|  |  | 
|  | void FocusChainManager::Notify() { | 
|  | zx_koid_t focused_view = GetFocusedView(); | 
|  | auto it = listeners_.begin(); | 
|  | while (it != listeners_.end()) { | 
|  | if (*it) { | 
|  | (*it)->OnViewFocus(focused_view); | 
|  | ++it; | 
|  | } else { | 
|  | // This listener is no longer listening for updates. | 
|  | it = listeners_.erase(it); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | zx_koid_t FocusChainManager::ViewRefWatcher::koid() const { return GetKoid(view_ref_); } | 
|  |  | 
|  | void FocusChainManager::ViewRefWatcher::OnViewRefSignaled(async_dispatcher_t* dispatcher, | 
|  | async::WaitBase* wait, zx_status_t status, | 
|  | const zx_packet_signal* signal) { | 
|  | // The only signal we are waiting for is ZX_EVENTPAIR_PEER_CLOSED. | 
|  | callback_(); | 
|  | } | 
|  |  | 
|  | void FocusChainManager::ChangeFocusToView(zx_koid_t view_ref_koid, | 
|  | ChangeFocusToViewCallback callback) { | 
|  | if (!semantics_source_->ViewHasSemantics(view_ref_koid)) { | 
|  | callback(false); | 
|  | return; | 
|  | } | 
|  | auto view_ref = semantics_source_->ViewRefClone(view_ref_koid); | 
|  | focuser_->RequestFocus(std::move(*view_ref), [callback = std::move(callback)](auto result) { | 
|  | if (result.is_err()) { | 
|  | callback(false); | 
|  | } else { | 
|  | callback(true); | 
|  | } | 
|  | }); | 
|  | return; | 
|  | } | 
|  |  | 
|  | }  // namespace a11y |