blob: 9bada58f666e7dbbe3f6be27590f99e2b757c318 [file] [log] [blame]
Sharon Yangfbb9ba4a2019-11-18 23:59:561// Copyright 2019 The Chromium 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 <fuchsia/accessibility/semantics/cpp/fidl.h>
Sharon Yang8b548ab92019-12-20 01:52:546#include <lib/ui/scenic/cpp/view_ref_pair.h>
Sharon Yangfbb9ba4a2019-11-18 23:59:567#include <zircon/types.h>
8
Peter Kasting919ce652020-05-07 10:22:369#include "content/public/test/browser_test.h"
Sharon Yangfbb9ba4a2019-11-18 23:59:5610#include "fuchsia/base/frame_test_util.h"
Sharon Yangfbb9ba4a2019-11-18 23:59:5611#include "fuchsia/base/test_navigation_listener.h"
12#include "fuchsia/engine/browser/accessibility_bridge.h"
Sharon Yangd0d86f42020-07-11 15:38:5313#include "fuchsia/engine/browser/fake_semantics_manager.h"
Sharon Yangfbb9ba4a2019-11-18 23:59:5614#include "fuchsia/engine/browser/frame_impl.h"
15#include "fuchsia/engine/test/test_data.h"
16#include "fuchsia/engine/test/web_engine_browser_test.h"
17#include "net/test/embedded_test_server/embedded_test_server.h"
18#include "testing/gtest/include/gtest/gtest.h"
Sharon Yang8b548ab92019-12-20 01:52:5419#include "ui/gfx/switches.h"
20#include "ui/ozone/public/ozone_switches.h"
Sharon Yangfbb9ba4a2019-11-18 23:59:5621
Sharon Yangfbb9ba4a2019-11-18 23:59:5622namespace {
23
24const char kPage1Path[] = "/ax1.html";
25const char kPage2Path[] = "/batching.html";
26const char kPage1Title[] = "accessibility 1";
27const char kPage2Title[] = "lots of nodes!";
Sharon Yang5fb4ce22020-02-08 00:45:2128const char kButtonName1[] = "a button";
29const char kButtonName2[] = "another button";
30const char kButtonName3[] = "button 3";
Sharon Yangfbb9ba4a2019-11-18 23:59:5631const char kNodeName[] = "last node";
32const char kParagraphName[] = "a third paragraph";
Sharon Yang3231ad92020-08-12 02:22:0833const char kOffscreenNodeName[] = "offscreen node";
Lucas Radaelli8c500542020-10-12 21:31:0234const size_t kPage1NodeCount = 29;
Sharon Yangaa2c9bd2019-11-21 17:59:2235const size_t kPage2NodeCount = 190;
Sharon Yang2caf82e2020-10-12 18:00:3536const size_t kInitialRangeValue = 51;
37const size_t kStepSize = 3;
Sharon Yangfbb9ba4a2019-11-18 23:59:5638
Sharon Yang8b548ab92019-12-20 01:52:5439fuchsia::math::PointF GetCenterOfBox(fuchsia::ui::gfx::BoundingBox box) {
40 fuchsia::math::PointF center;
41 center.x = (box.min.x + box.max.x) / 2;
42 center.y = (box.min.y + box.max.y) / 2;
43 return center;
44}
45
Lucas Radaelli0b226d232020-10-12 22:00:3246// Creates an AXEventNotificationDetails that contains an AxTreeUpdate that
47// builds a tree from scratch of the form: (1 (2 ... (|tree_size|))).
48content::AXEventNotificationDetails CreateTreeAccessibilityEvent(
49 size_t tree_size) {
50 content::AXEventNotificationDetails event;
51 event.ax_tree_id = ui::AXTreeID ::CreateNewAXTreeID();
52 ui::AXTreeUpdate update;
53 update.root_id = 1;
54 update.nodes.resize(tree_size);
55
56 for (int i = 1; i <= static_cast<int>(tree_size); ++i) {
57 auto& node = update.nodes[i - 1]; // vector 0-indexed, IDs 1-indexed.
58 node.id = i;
59 node.child_ids.push_back(i + 1);
60 }
61
62 // The deepest node does not have any child.
63 update.nodes.back().child_ids.clear();
64 event.updates.push_back(std::move(update));
65 return event;
66}
67
68// Creates an AXEventNotificationDetails that contains |update|.
69content::AXEventNotificationDetails CreateAccessibilityEventWithUpdate(
70 ui::AXTreeUpdate update) {
71 content::AXEventNotificationDetails event;
72 event.updates.push_back(std::move(update));
73 return event;
74}
75
Tess Eisenberger77314d52020-10-12 23:30:2176// Returns whether or not the given node supports the given action.
77bool HasAction(const fuchsia::accessibility::semantics::Node& node,
78 fuchsia::accessibility::semantics::Action action) {
79 for (const auto& node_action : node.actions()) {
80 if (node_action == action)
81 return true;
82 }
83 return false;
84}
85
Sharon Yangfbb9ba4a2019-11-18 23:59:5686} // namespace
87
88class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
89 public:
Wez7a5f5ca2020-08-17 07:41:0890 AccessibilityBridgeTest() {
Sharon Yangfbb9ba4a2019-11-18 23:59:5691 cr_fuchsia::WebEngineBrowserTest::set_test_server_root(
92 base::FilePath(cr_fuchsia::kTestServerRoot));
93 }
94
95 ~AccessibilityBridgeTest() override = default;
96
Sharon Yangd0d86f42020-07-11 15:38:5397 AccessibilityBridgeTest(const AccessibilityBridgeTest&) = delete;
98 AccessibilityBridgeTest& operator=(const AccessibilityBridgeTest&) = delete;
99
Sharon Yang8b548ab92019-12-20 01:52:54100 void SetUp() override {
101 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
102 command_line->AppendSwitchNative(switches::kOzonePlatform,
103 switches::kHeadless);
104 command_line->AppendSwitch(switches::kHeadless);
105 cr_fuchsia::WebEngineBrowserTest::SetUp();
106 }
107
Sharon Yangfbb9ba4a2019-11-18 23:59:56108 void SetUpOnMainThread() override {
Sharon Yangfbb9ba4a2019-11-18 23:59:56109 frame_ptr_ =
110 cr_fuchsia::WebEngineBrowserTest::CreateFrame(&navigation_listener_);
111 frame_impl_ = context_impl()->GetFrameImplForTest(&frame_ptr_);
Wez7a5f5ca2020-08-17 07:41:08112 frame_impl_->set_semantics_manager_for_test(&semantics_manager_);
Sharon Yang8b548ab92019-12-20 01:52:54113 frame_ptr_->EnableHeadlessRendering();
Sharon Yangfbb9ba4a2019-11-18 23:59:56114
Wez19de8b522020-01-28 20:50:04115 semantics_manager_.WaitUntilViewRegistered();
116 ASSERT_TRUE(semantics_manager_.is_view_registered());
117 ASSERT_TRUE(semantics_manager_.is_listener_valid());
Sharon Yang1dd8b6db32020-04-10 23:10:18118
119 frame_ptr_->GetNavigationController(navigation_controller_.NewRequest());
120 ASSERT_TRUE(embedded_test_server()->Start());
Lucas Radaelli0b226d232020-10-12 22:00:32121
122 // Change the accessibility mode on the Fuchsia side and check that it is
123 // propagated correctly.
124 ASSERT_FALSE(frame_impl_->web_contents_for_test()
125 ->IsWebContentsOnlyAccessibilityModeForTesting());
126
Sharon Yang1dd8b6db32020-04-10 23:10:18127 semantics_manager_.SetSemanticsModeEnabled(true);
Lucas Radaelli0b226d232020-10-12 22:00:32128 base::RunLoop().RunUntilIdle();
129
130 ASSERT_TRUE(frame_impl_->web_contents_for_test()
131 ->IsWebContentsOnlyAccessibilityModeForTesting());
Sharon Yangfbb9ba4a2019-11-18 23:59:56132 }
133
Lucas Radaelli8c500542020-10-12 21:31:02134 void LoadPage(base::StringPiece url, base::StringPiece page_title) {
135 GURL page_url(embedded_test_server()->GetURL(std::string(url)));
136 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
137 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
138 page_url.spec()));
139 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, page_title);
140 }
141
Lucas Radaelli0b226d232020-10-12 22:00:32142 // Helper function that checks if |num_deletes|, |num_updates| and
143 // |num_commits| match the ones in the FakeSemanticTree.
144 void CheckCallsToFakeSemanticTree(size_t num_deletes,
145 size_t num_updates,
146 size_t num_commits) {
147 auto* tree = semantics_manager_.semantic_tree();
148 EXPECT_EQ(tree->num_delete_calls(), num_deletes);
149 EXPECT_EQ(tree->num_update_calls(), num_updates);
150 EXPECT_EQ(tree->num_commit_calls(), num_commits);
151 }
152
Sharon Yangfbb9ba4a2019-11-18 23:59:56153 protected:
154 fuchsia::web::FramePtr frame_ptr_;
155 FrameImpl* frame_impl_;
156 FakeSemanticsManager semantics_manager_;
Sharon Yangfbb9ba4a2019-11-18 23:59:56157 cr_fuchsia::TestNavigationListener navigation_listener_;
Sharon Yang1dd8b6db32020-04-10 23:10:18158 fuchsia::web::NavigationControllerPtr navigation_controller_;
Sharon Yangfbb9ba4a2019-11-18 23:59:56159};
160
Sharon Yangfbb9ba4a2019-11-18 23:59:56161IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
Lucas Radaelli8c500542020-10-12 21:31:02162 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56163
164 // Check that the data values are correct in the FakeSemanticTree.
165 // TODO(fxb/18796): Test more fields once Chrome to Fuchsia conversions are
166 // available.
167 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
168 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15169 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56170 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15171 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56172 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15173 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56174}
175
176// Batching is performed when the number of nodes to send or delete exceeds the
177// maximum, as set on the Fuchsia side. Check that all nodes are received by the
178// Semantic Tree when batching is performed.
179IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
Lucas Radaelli8c500542020-10-12 21:31:02180 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56181
182 // Run until we expect more than a batch's worth of nodes to be present.
183 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangfb25b4a2020-02-10 23:50:15184 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Lucas Radaelli0b226d232020-10-12 22:00:32185 EXPECT_EQ(semantics_manager_.semantic_tree()->num_delete_calls(), 0u);
186
187 // Checks if the actual batching happened.
188 EXPECT_GE(semantics_manager_.semantic_tree()->num_update_calls(), 18u);
189 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
Sharon Yangfbb9ba4a2019-11-18 23:59:56190}
191
192// Check that semantics information is correctly sent when navigating from page
193// to page.
194IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TestNavigation) {
Lucas Radaelli8c500542020-10-12 21:31:02195 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56196
197 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32198 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
199
Sharon Yangfbb9ba4a2019-11-18 23:59:56200 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15201 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56202 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15203 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56204 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15205 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56206
Lucas Radaelli8c500542020-10-12 21:31:02207 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56208
209 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32210 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u);
211
Sharon Yangca616422019-12-18 23:52:39212 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15213 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title));
214 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangca616422019-12-18 23:52:39215
216 // Check that data from the first page has been deleted successfully.
217 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15218 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangca616422019-12-18 23:52:39219 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15220 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56221}
Sharon Yang8b548ab92019-12-20 01:52:54222
223// Checks that the correct node ID is returned when performing hit testing.
Wez99fac70b2020-02-07 15:26:10224// TODO(https://crbug.com/1050049): Re-enable once flake is fixed.
225IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_HitTest) {
Lucas Radaelli8c500542020-10-12 21:31:02226 LoadPage(kPage1Path, kPage1Title);
Sharon Yang8b548ab92019-12-20 01:52:54227
Sharon Yangd0d86f42020-07-11 15:38:53228 fuchsia::accessibility::semantics::Node* hit_test_node =
Sharon Yang8b548ab92019-12-20 01:52:54229 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName);
Sharon Yangfb25b4a2020-02-10 23:50:15230 EXPECT_TRUE(hit_test_node);
Sharon Yang8b548ab92019-12-20 01:52:54231
232 fuchsia::math::PointF target_point =
233 GetCenterOfBox(hit_test_node->location());
234
235 EXPECT_EQ(hit_test_node->node_id(),
236 semantics_manager_.HitTestAtPointSync(std::move(target_point)));
237
238 // Expect hit testing to return the root when the point given is out of
239 // bounds or there is no semantic node at that position.
240 target_point.x = -1;
241 target_point.y = -1;
242 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
243 target_point.x = 1;
244 target_point.y = 1;
245 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
246}
Sharon Yang5fb4ce22020-02-08 00:45:21247
248IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformDefaultAction) {
Lucas Radaelli8c500542020-10-12 21:31:02249 LoadPage(kPage1Path, kPage1Title);
250
Sharon Yang5fb4ce22020-02-08 00:45:21251 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
252
Sharon Yangd0d86f42020-07-11 15:38:53253 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang5fb4ce22020-02-08 00:45:21254 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
255 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53256 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang5fb4ce22020-02-08 00:45:21257 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
258 EXPECT_TRUE(button2);
Sharon Yangd0d86f42020-07-11 15:38:53259 fuchsia::accessibility::semantics::Node* button3 =
Sharon Yang5fb4ce22020-02-08 00:45:21260 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
261 EXPECT_TRUE(button3);
262
Tess Eisenberger77314d52020-10-12 23:30:21263 EXPECT_TRUE(
264 HasAction(*button1, fuchsia::accessibility::semantics::Action::DEFAULT));
265 EXPECT_TRUE(
266 HasAction(*button2, fuchsia::accessibility::semantics::Action::DEFAULT));
267 EXPECT_TRUE(
268 HasAction(*button3, fuchsia::accessibility::semantics::Action::DEFAULT));
269
Sharon Yang5fb4ce22020-02-08 00:45:21270 // Perform the default action (click) on multiple buttons.
Sharon Yangd0d86f42020-07-11 15:38:53271 semantics_manager_.RequestAccessibilityAction(
272 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
273 semantics_manager_.RequestAccessibilityAction(
274 button2->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
Sharon Yang5fb4ce22020-02-08 00:45:21275 semantics_manager_.RunUntilNumActionsHandledEquals(2);
Sharon Yang5fb4ce22020-02-08 00:45:21276}
Sharon Yang1dd8b6db32020-04-10 23:10:18277
278IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformUnsupportedAction) {
Lucas Radaelli8c500542020-10-12 21:31:02279 LoadPage(kPage1Path, kPage1Title);
280
Sharon Yang1dd8b6db32020-04-10 23:10:18281 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
282
Sharon Yangd0d86f42020-07-11 15:38:53283 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang1dd8b6db32020-04-10 23:10:18284 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
285 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53286 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang1dd8b6db32020-04-10 23:10:18287 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
288 EXPECT_TRUE(button2);
289
290 // Perform one supported action (DEFAULT) and one non-supported action
291 // (SET_VALUE);
Sharon Yangd0d86f42020-07-11 15:38:53292 semantics_manager_.RequestAccessibilityAction(
293 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
294 semantics_manager_.RequestAccessibilityAction(
295 button2->node_id(), fuchsia::accessibility::semantics::Action::SET_VALUE);
Sharon Yang1dd8b6db32020-04-10 23:10:18296 semantics_manager_.RunUntilNumActionsHandledEquals(2);
297
298 EXPECT_EQ(1, semantics_manager_.num_actions_handled());
299 EXPECT_EQ(1, semantics_manager_.num_actions_unhandled());
300}
Sharon Yang7919fab2020-07-28 16:46:20301
302IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Disconnect) {
303 base::RunLoop run_loop;
304 frame_ptr_.set_error_handler([&run_loop](zx_status_t status) {
305 EXPECT_EQ(ZX_ERR_INTERNAL, status);
306 run_loop.Quit();
307 });
308
309 semantics_manager_.semantic_tree()->Disconnect();
310 run_loop.Run();
311}
Sharon Yang3231ad92020-08-12 02:22:08312
Adam Ettenberger9c15a3a2020-09-01 22:10:27313IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
Sharon Yang3231ad92020-08-12 02:22:08314 constexpr int kScreenWidth = 720;
315 constexpr int kScreenHeight = 640;
316 gfx::Rect screen_bounds(kScreenWidth, kScreenHeight);
317
Lucas Radaelli8c500542020-10-12 21:31:02318 LoadPage(kPage1Path, kPage1Title);
319
Sharon Yang3231ad92020-08-12 02:22:08320 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
321
322 auto* content_view =
323 frame_impl_->web_contents_for_test()->GetContentNativeView();
324 content_view->SetBounds(screen_bounds);
325
326 // Get a node that is off the screen.
327 fuchsia::accessibility::semantics::Node* node =
328 semantics_manager_.semantic_tree()->GetNodeFromLabel(kOffscreenNodeName);
329 ASSERT_TRUE(node);
330 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
331 ui::AXNode* ax_node = bridge->ax_tree_for_test()->GetFromId(node->node_id());
332 ASSERT_TRUE(ax_node);
333 bool is_offscreen = false;
334 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
335 EXPECT_TRUE(is_offscreen);
336
337 // Perform SHOW_ON_SCREEN on that node and check that it is on the screen.
338 base::RunLoop run_loop;
339 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
340 semantics_manager_.RequestAccessibilityAction(
341 node->node_id(),
342 fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
343 semantics_manager_.RunUntilNumActionsHandledEquals(1);
344 run_loop.Run();
345
346 // Initialize |is_offscreen| to false before calling GetTreeBounds as
347 // specified by the API.
348 is_offscreen = false;
349 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
350
351 EXPECT_FALSE(is_offscreen);
352}
Sharon Yang2caf82e2020-10-12 18:00:35353
354IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Slider) {
Lucas Radaelli8c500542020-10-12 21:31:02355 LoadPage(kPage1Path, kPage1Title);
356
Sharon Yang2caf82e2020-10-12 18:00:35357 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
358
359 fuchsia::accessibility::semantics::Node* node =
360 semantics_manager_.semantic_tree()->GetNodeFromRole(
361 fuchsia::accessibility::semantics::Role::SLIDER);
362 EXPECT_TRUE(node);
363 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
364 EXPECT_EQ(node->states().range_value(), kInitialRangeValue);
365
366 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
367 base::RunLoop run_loop;
368 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
369 semantics_manager_.RequestAccessibilityAction(
370 node->node_id(), fuchsia::accessibility::semantics::Action::INCREMENT);
371 semantics_manager_.RunUntilNumActionsHandledEquals(1);
372 run_loop.Run();
373
374 // Wait for the slider node to be updated, then check the value.
375 base::RunLoop run_loop2;
376 semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
377 node->node_id(), run_loop2.QuitClosure());
378 run_loop2.Run();
379
380 node = semantics_manager_.semantic_tree()->GetNodeWithId(node->node_id());
381 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
382 EXPECT_EQ(node->states().range_value(), kInitialRangeValue + kStepSize);
383}
Lucas Radaelli8c500542020-10-12 21:31:02384
385// This test makes sure that when semantic updates toggle on / off / on, the
386// full semantic tree is sent in the first update when back on.
387IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TogglesSemanticsUpdates) {
388 LoadPage(kPage1Path, kPage1Title);
389
390 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32391 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
Lucas Radaelli8c500542020-10-12 21:31:02392
393 semantics_manager_.SetSemanticsModeEnabled(false);
394 base::RunLoop().RunUntilIdle();
395
396 ASSERT_FALSE(frame_impl_->web_contents_for_test()
397 ->IsWebContentsOnlyAccessibilityModeForTesting());
398
399 // The tree gets cleared when semantic updates are off.
400 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 0u);
401 semantics_manager_.SetSemanticsModeEnabled(true);
402 base::RunLoop().RunUntilIdle();
403 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32404 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u);
Lucas Radaelli8c500542020-10-12 21:31:02405
406 ASSERT_TRUE(frame_impl_->web_contents_for_test()
407 ->IsWebContentsOnlyAccessibilityModeForTesting());
408}
Lucas Radaelli0b226d232020-10-12 22:00:32409
410// This test performs several tree modifications (insertions, changes, removals
411// and reparentings). All operations must leave the tree in a valid state and
412// also forward the nodes in a way that leaves the tree in the Fuchsia side in a
413// valid state. Note that every time that a new tree is sent to Fuchsia, the
414// FakeSemantiTree checks if the tree is valid.
415IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TreeModificationsAreForwarded) {
416 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
417 size_t tree_size = 5;
418
419 // The tree has the following form: (1 (2 (3 (4 (5)))))
420 auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
421 bridge->AccessibilityEventReceived(tree_accessibility_event);
422 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(tree_size);
423 CheckCallsToFakeSemanticTree(/*num_deletes=*/0, /*num_updates=*/1,
424 /*num_commits=*/1);
425
426 // Adds a new node with ID 6.
427 // (1 (2 (3 (4 (5 6)))))
428 {
429 ui::AXTreeUpdate update;
430 update.root_id = 1;
431 update.nodes.resize(2);
432 update.nodes[0].id = 4;
433 update.nodes[0].child_ids.push_back(5);
434 update.nodes[0].child_ids.push_back(6);
435 update.nodes[1].id = 6;
436
437 bridge->AccessibilityEventReceived(
438 CreateAccessibilityEventWithUpdate(std::move(update)));
439 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(tree_size + 1);
440 CheckCallsToFakeSemanticTree(/*num_deletes=*/0, /*num_updates=*/2,
441 /*num_commits=*/2);
442 }
443
444 // Removes the added node 6.
445 // (1 (2 (3 (4 (5)))))
446 {
447 ui::AXTreeUpdate update;
448 update.root_id = 1;
449 update.node_id_to_clear = 4;
450 update.nodes.resize(2);
451 update.nodes[0].id = 4;
452 update.nodes[0].child_ids.push_back(5);
453
454 update.nodes[1].id = 5;
455
456 bridge->AccessibilityEventReceived(
457 CreateAccessibilityEventWithUpdate(std::move(update)));
458
459 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(3);
460 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/3,
461 /*num_commits=*/3);
462 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
463 }
464
465 // Reparents node 5 to be a child of node 3.
466 // (1 (2 (3 (4 5))))
467 {
468 ui::AXTreeUpdate update;
469 update.root_id = 1;
470 update.node_id_to_clear = 3;
471 update.nodes.resize(3);
472 update.nodes[0].id = 3;
473 update.nodes[0].child_ids.push_back(4);
474 update.nodes[0].child_ids.push_back(5);
475
476 update.nodes[1].id = 4;
477 update.nodes[2].id = 5;
478
479 bridge->AccessibilityEventReceived(
480 CreateAccessibilityEventWithUpdate(std::move(update)));
481
482 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(4);
483 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/4,
484 /*num_commits=*/4);
485 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
486 }
487
488 // Reparents the subtree rooted at node 3 to be a child of node 1.
489 // (1 (2 3 (4 5)))
490 {
491 ui::AXTreeUpdate update;
492 update.root_id = 1;
493 update.node_id_to_clear = 2;
494 update.nodes.resize(5);
495 update.nodes[0].id = 1;
496 update.nodes[0].child_ids.push_back(2);
497 update.nodes[0].child_ids.push_back(3);
498
499 update.nodes[1].id = 2;
500 update.nodes[2].id = 3;
501 update.nodes[2].child_ids.push_back(4);
502 update.nodes[2].child_ids.push_back(5);
503
504 update.nodes[3].id = 4;
505 update.nodes[4].id = 5;
506
507 bridge->AccessibilityEventReceived(
508 CreateAccessibilityEventWithUpdate(std::move(update)));
509
510 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(5);
511 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/5,
512 /*num_commits=*/5);
513 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
514 }
515
516 // Deletes the subtree rooted at node 3.
517 // (1 (2))
518 {
519 ui::AXTreeUpdate update;
520 update.root_id = 1;
521 update.node_id_to_clear = 1;
522 update.nodes.resize(2);
523 update.nodes[0].id = 1;
524 update.nodes[0].child_ids.push_back(2);
525
526 update.nodes[1].id = 2;
527
528 bridge->AccessibilityEventReceived(
529 CreateAccessibilityEventWithUpdate(std::move(update)));
530
531 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(6);
532 CheckCallsToFakeSemanticTree(/*num_deletes=*/2, /*num_updates=*/6,
533 /*num_commits=*/6);
534 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
535 }
536
537 // Give this tree a new root.
538 // (7 (2))
539 {
540 ui::AXTreeUpdate update;
541 update.root_id = 7;
542 update.node_id_to_clear = 1;
543 update.nodes.resize(2);
544 update.nodes[0].id = 7;
545 update.nodes[0].child_ids.push_back(2);
546
547 update.nodes[1].id = 2;
548
549 bridge->AccessibilityEventReceived(
550 CreateAccessibilityEventWithUpdate(std::move(update)));
551
552 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(7);
553 CheckCallsToFakeSemanticTree(/*num_deletes=*/3, /*num_updates=*/7,
554 /*num_commits=*/7);
555 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
556 }
557
558 // Delete child and change root ID.
559 // (1)
560 {
561 ui::AXTreeUpdate update;
562 update.root_id = 1;
563 update.node_id_to_clear = 7;
564 update.nodes.resize(1);
565 update.nodes[0].id = 1;
566
567 bridge->AccessibilityEventReceived(
568 CreateAccessibilityEventWithUpdate(std::move(update)));
569
570 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(8);
571 CheckCallsToFakeSemanticTree(/*num_deletes=*/4, /*num_updates=*/8,
572 /*num_commits=*/8);
573 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 1u);
574 }
575}