Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2d6eea1

Browse files
committed
[flutter_runner] Port the accessibility bridge from Topaz
1 parent a353f93 commit 2d6eea1

9 files changed

Lines changed: 474 additions & 6 deletions

File tree

ci/licenses_golden/licenses_flutter

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1392,4 +1392,40 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13921392
See the License for the specific language governing permissions and
13931393
limitations under the License.
13941394
====================================================================================================
1395-
Total license count: 2
1395+
1396+
====================================================================================================
1397+
LIBRARY: engine
1398+
ORIGIN: ../../../fuchsia/sdk/linux/LICENSE
1399+
TYPE: LicenseType.bsd
1400+
FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.cc
1401+
FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h
1402+
----------------------------------------------------------------------------------------------------
1403+
Copyright 2019 The Fuchsia Authors. All rights reserved.
1404+
1405+
Redistribution and use in source and binary forms, with or without
1406+
modification, are permitted provided that the following conditions are
1407+
met:
1408+
1409+
* Redistributions of source code must retain the above copyright
1410+
notice, this list of conditions and the following disclaimer.
1411+
* Redistributions in binary form must reproduce the above
1412+
copyright notice, this list of conditions and the following disclaimer
1413+
in the documentation and/or other materials provided with the
1414+
distribution.
1415+
* Neither the name of Google Inc. nor the names of its
1416+
contributors may be used to endorse or promote products derived from
1417+
this software without specific prior written permission.
1418+
1419+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1420+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1421+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1422+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1423+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1424+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1425+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1426+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1427+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1428+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1429+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1430+
====================================================================================================
1431+
Total license count: 3
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright 2019 The Fuchsia Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h"
6+
7+
#include <zircon/status.h>
8+
#include <zircon/types.h>
9+
10+
#include <deque>
11+
12+
#include "flutter/fml/logging.h"
13+
#include "flutter/lib/ui/semantics/semantics_node.h"
14+
15+
namespace flutter_runner {
16+
AccessibilityBridge::AccessibilityBridge(
17+
const std::shared_ptr<sys::ServiceDirectory> services,
18+
fuchsia::ui::views::ViewRef view_ref)
19+
: binding_(this) {
20+
services->Connect(fuchsia::accessibility::semantics::SemanticsManager::Name_,
21+
fuchsia_semantics_manager_.NewRequest().TakeChannel());
22+
fuchsia_semantics_manager_.set_error_handler([](zx_status_t status) {
23+
FML_LOG(ERROR) << "Flutter cannot connect to SemanticsManager with status: "
24+
<< zx_status_get_string(status) << ".";
25+
});
26+
fidl::InterfaceHandle<
27+
fuchsia::accessibility::semantics::SemanticActionListener>
28+
listener_handle;
29+
binding_.Bind(listener_handle.NewRequest());
30+
fuchsia_semantics_manager_->RegisterView(
31+
std::move(view_ref), std::move(listener_handle), tree_ptr_.NewRequest());
32+
}
33+
34+
bool AccessibilityBridge::GetSemanticsEnabled() const {
35+
return semantics_enabled_;
36+
}
37+
38+
void AccessibilityBridge::SetSemanticsEnabled(bool enabled) {
39+
semantics_enabled_ = enabled;
40+
if (!enabled) {
41+
nodes_.clear();
42+
}
43+
}
44+
45+
fuchsia::ui::gfx::BoundingBox AccessibilityBridge::GetNodeLocation(
46+
const flutter::SemanticsNode& node) const {
47+
fuchsia::ui::gfx::BoundingBox box;
48+
box.min.x = node.rect.fLeft;
49+
box.min.y = node.rect.fTop;
50+
box.min.z = static_cast<float>(node.elevation);
51+
box.max.x = node.rect.fRight;
52+
box.max.y = node.rect.fBottom;
53+
box.max.z = static_cast<float>(node.thickness);
54+
return box;
55+
}
56+
57+
fuchsia::ui::gfx::mat4 AccessibilityBridge::GetNodeTransform(
58+
const flutter::SemanticsNode& node) const {
59+
fuchsia::ui::gfx::mat4 value;
60+
float* m = value.matrix.data();
61+
node.transform.asColMajorf(m);
62+
return value;
63+
}
64+
65+
fuchsia::accessibility::semantics::Attributes
66+
AccessibilityBridge::GetNodeAttributes(const flutter::SemanticsNode& node,
67+
size_t* added_size) const {
68+
fuchsia::accessibility::semantics::Attributes attributes;
69+
// TODO(MI4-2531): Don't truncate.
70+
if (node.label.size() > fuchsia::accessibility::semantics::MAX_LABEL_SIZE) {
71+
attributes.set_label(node.label.substr(
72+
0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE));
73+
*added_size += fuchsia::accessibility::semantics::MAX_LABEL_SIZE;
74+
} else {
75+
attributes.set_label(node.label);
76+
*added_size += node.label.size();
77+
}
78+
79+
return attributes;
80+
}
81+
82+
fuchsia::accessibility::semantics::States AccessibilityBridge::GetNodeStates(
83+
const flutter::SemanticsNode& node) const {
84+
fuchsia::accessibility::semantics::States states;
85+
if (node.HasFlag(flutter::SemanticsFlags::kHasCheckedState)) {
86+
states.set_checked(node.HasFlag(flutter::SemanticsFlags::kIsChecked));
87+
}
88+
return states;
89+
}
90+
91+
std::unordered_set<int32_t> AccessibilityBridge::GetDescendants(
92+
int32_t node_id) const {
93+
std::unordered_set<int32_t> descendents;
94+
std::deque<int32_t> to_process = {node_id};
95+
while (!to_process.empty()) {
96+
int32_t id = to_process.front();
97+
to_process.pop_front();
98+
descendents.emplace(id);
99+
100+
auto it = nodes_.find(id);
101+
if (it != nodes_.end()) {
102+
auto const& children = it->second;
103+
for (const auto& child : children) {
104+
if (descendents.find(child) == descendents.end()) {
105+
to_process.push_back(child);
106+
} else {
107+
// This indicates either a cycle or a child with multiple parents.
108+
// Flutter should never let this happen, but the engine API does not
109+
// explicitly forbid it right now.
110+
FML_LOG(ERROR) << "Semantics Node " << child
111+
<< " has already been listed as a child of another "
112+
"node, ignoring for parent "
113+
<< id << ".";
114+
}
115+
}
116+
}
117+
}
118+
return descendents;
119+
}
120+
121+
// The only known usage of a negative number for a node ID is in the embedder
122+
// API as a sentinel value, which is not expected here. No valid producer of
123+
// nodes should give us a negative ID.
124+
static uint32_t FlutterIdToFuchsiaId(int32_t flutter_node_id) {
125+
FML_DCHECK(flutter_node_id >= 0)
126+
<< "Unexpectedly recieved a negative semantics node ID.";
127+
return static_cast<uint32_t>(flutter_node_id);
128+
}
129+
130+
void AccessibilityBridge::PruneUnreachableNodes() {
131+
const auto& reachable_nodes = GetDescendants(kRootNodeId);
132+
std::vector<uint32_t> nodes_to_remove;
133+
auto iter = nodes_.begin();
134+
while (iter != nodes_.end()) {
135+
int32_t id = iter->first;
136+
if (reachable_nodes.find(id) == reachable_nodes.end()) {
137+
// TODO(MI4-2531): This shouldn't be strictly necessary at this level.
138+
if (sizeof(nodes_to_remove) + (nodes_to_remove.size() * kNodeIdSize) >=
139+
kMaxMessageSize) {
140+
tree_ptr_->DeleteSemanticNodes(std::move(nodes_to_remove));
141+
nodes_to_remove.clear();
142+
}
143+
nodes_to_remove.push_back(FlutterIdToFuchsiaId(id));
144+
iter = nodes_.erase(iter);
145+
} else {
146+
iter++;
147+
}
148+
}
149+
if (!nodes_to_remove.empty()) {
150+
tree_ptr_->DeleteSemanticNodes(std::move(nodes_to_remove));
151+
}
152+
}
153+
154+
// TODO(FIDL-718) - remove this, handle the error instead in something like
155+
// set_error_handler.
156+
static void PrintNodeSizeError(uint32_t node_id) {
157+
FML_LOG(ERROR) << "Semantics node with ID " << node_id
158+
<< " exceeded the maximum FIDL message size and may not "
159+
"be delivered to the accessibility manager service.";
160+
}
161+
162+
void AccessibilityBridge::AddSemanticsNodeUpdate(
163+
const flutter::SemanticsNodeUpdates update) {
164+
if (update.empty()) {
165+
return;
166+
}
167+
FML_DCHECK(nodes_.find(kRootNodeId) != nodes_.end() ||
168+
update.find(kRootNodeId) != update.end())
169+
<< "AccessibilityBridge received an update with out ever getting a root "
170+
"node.";
171+
172+
std::vector<fuchsia::accessibility::semantics::Node> nodes;
173+
size_t current_size = 0;
174+
175+
// TODO(MI4-2498): Actions, Roles, hit test children, additional
176+
// flags/states/attr
177+
178+
// TODO(MI4-1478): Support for partial updates for nodes > 64kb
179+
// e.g. if a node has a long label or more than 64k children.
180+
for (const auto& value : update) {
181+
size_t this_node_size = sizeof(fuchsia::accessibility::semantics::Node);
182+
const auto& flutter_node = value.second;
183+
nodes_[flutter_node.id] =
184+
std::vector<int32_t>(flutter_node.childrenInTraversalOrder);
185+
fuchsia::accessibility::semantics::Node fuchsia_node;
186+
std::vector<uint32_t> child_ids;
187+
for (int32_t flutter_child_id : flutter_node.childrenInTraversalOrder) {
188+
child_ids.push_back(FlutterIdToFuchsiaId(flutter_child_id));
189+
}
190+
fuchsia_node.set_node_id(flutter_node.id)
191+
.set_location(GetNodeLocation(flutter_node))
192+
.set_transform(GetNodeTransform(flutter_node))
193+
.set_attributes(GetNodeAttributes(flutter_node, &this_node_size))
194+
.set_states(GetNodeStates(flutter_node))
195+
.set_child_ids(child_ids);
196+
this_node_size +=
197+
kNodeIdSize * flutter_node.childrenInTraversalOrder.size();
198+
199+
// TODO(MI4-2531, FIDL-718): Remove this
200+
// This is defensive. If, despite our best efforts, we ended up with a node
201+
// that is larger than the max fidl size, we send no updates.
202+
if (this_node_size >= kMaxMessageSize) {
203+
PrintNodeSizeError(flutter_node.id);
204+
return;
205+
}
206+
207+
current_size += this_node_size;
208+
209+
// If we would exceed the max FIDL message size by appending this node,
210+
// we should delete/update/commit now.
211+
if (current_size >= kMaxMessageSize) {
212+
tree_ptr_->UpdateSemanticNodes(std::move(nodes));
213+
nodes.clear();
214+
current_size = this_node_size;
215+
}
216+
nodes.push_back(std::move(fuchsia_node));
217+
}
218+
219+
if (current_size > kMaxMessageSize) {
220+
PrintNodeSizeError(nodes.back().node_id());
221+
}
222+
223+
PruneUnreachableNodes();
224+
225+
tree_ptr_->UpdateSemanticNodes(std::move(nodes));
226+
tree_ptr_->Commit();
227+
}
228+
229+
// |fuchsia::accessibility::semantics::SemanticActionListener|
230+
void AccessibilityBridge::OnAccessibilityActionRequested(
231+
uint32_t node_id,
232+
fuchsia::accessibility::semantics::Action action,
233+
fuchsia::accessibility::semantics::SemanticActionListener::
234+
OnAccessibilityActionRequestedCallback callback) {}
235+
236+
// |fuchsia::accessibility::semantics::SemanticActionListener|
237+
void AccessibilityBridge::HitTest(
238+
fuchsia::math::PointF local_point,
239+
fuchsia::accessibility::semantics::SemanticActionListener::HitTestCallback
240+
callback) {}
241+
242+
} // namespace flutter_runner

0 commit comments

Comments
 (0)