blob: b7d3a1884308f90bd5e52e4306232eb0eaa9cdc4 [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
Sharon Yangfbb9ba4a2019-11-18 23:59:5676} // namespace
77
78class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
79 public:
Wez7a5f5ca2020-08-17 07:41:0880 AccessibilityBridgeTest() {
Sharon Yangfbb9ba4a2019-11-18 23:59:5681 cr_fuchsia::WebEngineBrowserTest::set_test_server_root(
82 base::FilePath(cr_fuchsia::kTestServerRoot));
83 }
84
85 ~AccessibilityBridgeTest() override = default;
86
Sharon Yangd0d86f42020-07-11 15:38:5387 AccessibilityBridgeTest(const AccessibilityBridgeTest&) = delete;
88 AccessibilityBridgeTest& operator=(const AccessibilityBridgeTest&) = delete;
89
Sharon Yang8b548ab92019-12-20 01:52:5490 void SetUp() override {
91 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
92 command_line->AppendSwitchNative(switches::kOzonePlatform,
93 switches::kHeadless);
94 command_line->AppendSwitch(switches::kHeadless);
95 cr_fuchsia::WebEngineBrowserTest::SetUp();
96 }
97
Sharon Yangfbb9ba4a2019-11-18 23:59:5698 void SetUpOnMainThread() override {
Sharon Yangfbb9ba4a2019-11-18 23:59:5699 frame_ptr_ =
100 cr_fuchsia::WebEngineBrowserTest::CreateFrame(&navigation_listener_);
101 frame_impl_ = context_impl()->GetFrameImplForTest(&frame_ptr_);
Wez7a5f5ca2020-08-17 07:41:08102 frame_impl_->set_semantics_manager_for_test(&semantics_manager_);
Sharon Yang8b548ab92019-12-20 01:52:54103 frame_ptr_->EnableHeadlessRendering();
Sharon Yangfbb9ba4a2019-11-18 23:59:56104
Wez19de8b522020-01-28 20:50:04105 semantics_manager_.WaitUntilViewRegistered();
106 ASSERT_TRUE(semantics_manager_.is_view_registered());
107 ASSERT_TRUE(semantics_manager_.is_listener_valid());
Sharon Yang1dd8b6db32020-04-10 23:10:18108
109 frame_ptr_->GetNavigationController(navigation_controller_.NewRequest());
110 ASSERT_TRUE(embedded_test_server()->Start());
Lucas Radaelli0b226d232020-10-12 22:00:32111
112 // Change the accessibility mode on the Fuchsia side and check that it is
113 // propagated correctly.
114 ASSERT_FALSE(frame_impl_->web_contents_for_test()
115 ->IsWebContentsOnlyAccessibilityModeForTesting());
116
Sharon Yang1dd8b6db32020-04-10 23:10:18117 semantics_manager_.SetSemanticsModeEnabled(true);
Lucas Radaelli0b226d232020-10-12 22:00:32118 base::RunLoop().RunUntilIdle();
119
120 ASSERT_TRUE(frame_impl_->web_contents_for_test()
121 ->IsWebContentsOnlyAccessibilityModeForTesting());
Sharon Yangfbb9ba4a2019-11-18 23:59:56122 }
123
Lucas Radaelli8c500542020-10-12 21:31:02124 void LoadPage(base::StringPiece url, base::StringPiece page_title) {
125 GURL page_url(embedded_test_server()->GetURL(std::string(url)));
126 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
127 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
128 page_url.spec()));
129 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, page_title);
130 }
131
Lucas Radaelli0b226d232020-10-12 22:00:32132 // Helper function that checks if |num_deletes|, |num_updates| and
133 // |num_commits| match the ones in the FakeSemanticTree.
134 void CheckCallsToFakeSemanticTree(size_t num_deletes,
135 size_t num_updates,
136 size_t num_commits) {
137 auto* tree = semantics_manager_.semantic_tree();
138 EXPECT_EQ(tree->num_delete_calls(), num_deletes);
139 EXPECT_EQ(tree->num_update_calls(), num_updates);
140 EXPECT_EQ(tree->num_commit_calls(), num_commits);
141 }
142
Sharon Yangfbb9ba4a2019-11-18 23:59:56143 protected:
144 fuchsia::web::FramePtr frame_ptr_;
145 FrameImpl* frame_impl_;
146 FakeSemanticsManager semantics_manager_;
Sharon Yangfbb9ba4a2019-11-18 23:59:56147 cr_fuchsia::TestNavigationListener navigation_listener_;
Sharon Yang1dd8b6db32020-04-10 23:10:18148 fuchsia::web::NavigationControllerPtr navigation_controller_;
Sharon Yangfbb9ba4a2019-11-18 23:59:56149};
150
Sharon Yangfbb9ba4a2019-11-18 23:59:56151IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
Lucas Radaelli8c500542020-10-12 21:31:02152 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56153
154 // Check that the data values are correct in the FakeSemanticTree.
155 // TODO(fxb/18796): Test more fields once Chrome to Fuchsia conversions are
156 // available.
157 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
158 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15159 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56160 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15161 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56162 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15163 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56164}
165
166// Batching is performed when the number of nodes to send or delete exceeds the
167// maximum, as set on the Fuchsia side. Check that all nodes are received by the
168// Semantic Tree when batching is performed.
169IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
Lucas Radaelli8c500542020-10-12 21:31:02170 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56171
172 // Run until we expect more than a batch's worth of nodes to be present.
173 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangfb25b4a2020-02-10 23:50:15174 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Lucas Radaelli0b226d232020-10-12 22:00:32175 EXPECT_EQ(semantics_manager_.semantic_tree()->num_delete_calls(), 0u);
176
177 // Checks if the actual batching happened.
178 EXPECT_GE(semantics_manager_.semantic_tree()->num_update_calls(), 18u);
179 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
Sharon Yangfbb9ba4a2019-11-18 23:59:56180}
181
182// Check that semantics information is correctly sent when navigating from page
183// to page.
184IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TestNavigation) {
Lucas Radaelli8c500542020-10-12 21:31:02185 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56186
187 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32188 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
189
Sharon Yangfbb9ba4a2019-11-18 23:59:56190 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15191 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56192 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15193 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56194 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15195 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56196
Lucas Radaelli8c500542020-10-12 21:31:02197 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56198
199 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32200 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u);
201
Sharon Yangca616422019-12-18 23:52:39202 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15203 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title));
204 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangca616422019-12-18 23:52:39205
206 // Check that data from the first page has been deleted successfully.
207 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15208 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangca616422019-12-18 23:52:39209 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15210 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56211}
Sharon Yang8b548ab92019-12-20 01:52:54212
213// Checks that the correct node ID is returned when performing hit testing.
Wez99fac70b2020-02-07 15:26:10214// TODO(https://crbug.com/1050049): Re-enable once flake is fixed.
215IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_HitTest) {
Lucas Radaelli8c500542020-10-12 21:31:02216 LoadPage(kPage1Path, kPage1Title);
Sharon Yang8b548ab92019-12-20 01:52:54217
Sharon Yangd0d86f42020-07-11 15:38:53218 fuchsia::accessibility::semantics::Node* hit_test_node =
Sharon Yang8b548ab92019-12-20 01:52:54219 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName);
Sharon Yangfb25b4a2020-02-10 23:50:15220 EXPECT_TRUE(hit_test_node);
Sharon Yang8b548ab92019-12-20 01:52:54221
222 fuchsia::math::PointF target_point =
223 GetCenterOfBox(hit_test_node->location());
224
225 EXPECT_EQ(hit_test_node->node_id(),
226 semantics_manager_.HitTestAtPointSync(std::move(target_point)));
227
228 // Expect hit testing to return the root when the point given is out of
229 // bounds or there is no semantic node at that position.
230 target_point.x = -1;
231 target_point.y = -1;
232 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
233 target_point.x = 1;
234 target_point.y = 1;
235 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
236}
Sharon Yang5fb4ce22020-02-08 00:45:21237
238IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformDefaultAction) {
Lucas Radaelli8c500542020-10-12 21:31:02239 LoadPage(kPage1Path, kPage1Title);
240
Sharon Yang5fb4ce22020-02-08 00:45:21241 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
242
Sharon Yangd0d86f42020-07-11 15:38:53243 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang5fb4ce22020-02-08 00:45:21244 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
245 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53246 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang5fb4ce22020-02-08 00:45:21247 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
248 EXPECT_TRUE(button2);
Sharon Yangd0d86f42020-07-11 15:38:53249 fuchsia::accessibility::semantics::Node* button3 =
Sharon Yang5fb4ce22020-02-08 00:45:21250 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
251 EXPECT_TRUE(button3);
252
253 // Perform the default action (click) on multiple buttons.
Sharon Yangd0d86f42020-07-11 15:38:53254 semantics_manager_.RequestAccessibilityAction(
255 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
256 semantics_manager_.RequestAccessibilityAction(
257 button2->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
Sharon Yang5fb4ce22020-02-08 00:45:21258 semantics_manager_.RunUntilNumActionsHandledEquals(2);
Sharon Yang5fb4ce22020-02-08 00:45:21259}
Sharon Yang1dd8b6db32020-04-10 23:10:18260
261IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformUnsupportedAction) {
Lucas Radaelli8c500542020-10-12 21:31:02262 LoadPage(kPage1Path, kPage1Title);
263
Sharon Yang1dd8b6db32020-04-10 23:10:18264 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
265
Sharon Yangd0d86f42020-07-11 15:38:53266 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang1dd8b6db32020-04-10 23:10:18267 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
268 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53269 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang1dd8b6db32020-04-10 23:10:18270 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
271 EXPECT_TRUE(button2);
272
273 // Perform one supported action (DEFAULT) and one non-supported action
274 // (SET_VALUE);
Sharon Yangd0d86f42020-07-11 15:38:53275 semantics_manager_.RequestAccessibilityAction(
276 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
277 semantics_manager_.RequestAccessibilityAction(
278 button2->node_id(), fuchsia::accessibility::semantics::Action::SET_VALUE);
Sharon Yang1dd8b6db32020-04-10 23:10:18279 semantics_manager_.RunUntilNumActionsHandledEquals(2);
280
281 EXPECT_EQ(1, semantics_manager_.num_actions_handled());
282 EXPECT_EQ(1, semantics_manager_.num_actions_unhandled());
283}
Sharon Yang7919fab2020-07-28 16:46:20284
285IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Disconnect) {
286 base::RunLoop run_loop;
287 frame_ptr_.set_error_handler([&run_loop](zx_status_t status) {
288 EXPECT_EQ(ZX_ERR_INTERNAL, status);
289 run_loop.Quit();
290 });
291
292 semantics_manager_.semantic_tree()->Disconnect();
293 run_loop.Run();
294}
Sharon Yang3231ad92020-08-12 02:22:08295
Adam Ettenberger9c15a3a2020-09-01 22:10:27296IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
Sharon Yang3231ad92020-08-12 02:22:08297 constexpr int kScreenWidth = 720;
298 constexpr int kScreenHeight = 640;
299 gfx::Rect screen_bounds(kScreenWidth, kScreenHeight);
300
Lucas Radaelli8c500542020-10-12 21:31:02301 LoadPage(kPage1Path, kPage1Title);
302
Sharon Yang3231ad92020-08-12 02:22:08303 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
304
305 auto* content_view =
306 frame_impl_->web_contents_for_test()->GetContentNativeView();
307 content_view->SetBounds(screen_bounds);
308
309 // Get a node that is off the screen.
310 fuchsia::accessibility::semantics::Node* node =
311 semantics_manager_.semantic_tree()->GetNodeFromLabel(kOffscreenNodeName);
312 ASSERT_TRUE(node);
313 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
314 ui::AXNode* ax_node = bridge->ax_tree_for_test()->GetFromId(node->node_id());
315 ASSERT_TRUE(ax_node);
316 bool is_offscreen = false;
317 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
318 EXPECT_TRUE(is_offscreen);
319
320 // Perform SHOW_ON_SCREEN on that node and check that it is on the screen.
321 base::RunLoop run_loop;
322 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
323 semantics_manager_.RequestAccessibilityAction(
324 node->node_id(),
325 fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
326 semantics_manager_.RunUntilNumActionsHandledEquals(1);
327 run_loop.Run();
328
329 // Initialize |is_offscreen| to false before calling GetTreeBounds as
330 // specified by the API.
331 is_offscreen = false;
332 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
333
334 EXPECT_FALSE(is_offscreen);
335}
Sharon Yang2caf82e2020-10-12 18:00:35336
337IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Slider) {
Lucas Radaelli8c500542020-10-12 21:31:02338 LoadPage(kPage1Path, kPage1Title);
339
Sharon Yang2caf82e2020-10-12 18:00:35340 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
341
342 fuchsia::accessibility::semantics::Node* node =
343 semantics_manager_.semantic_tree()->GetNodeFromRole(
344 fuchsia::accessibility::semantics::Role::SLIDER);
345 EXPECT_TRUE(node);
346 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
347 EXPECT_EQ(node->states().range_value(), kInitialRangeValue);
348
349 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
350 base::RunLoop run_loop;
351 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
352 semantics_manager_.RequestAccessibilityAction(
353 node->node_id(), fuchsia::accessibility::semantics::Action::INCREMENT);
354 semantics_manager_.RunUntilNumActionsHandledEquals(1);
355 run_loop.Run();
356
357 // Wait for the slider node to be updated, then check the value.
358 base::RunLoop run_loop2;
359 semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
360 node->node_id(), run_loop2.QuitClosure());
361 run_loop2.Run();
362
363 node = semantics_manager_.semantic_tree()->GetNodeWithId(node->node_id());
364 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
365 EXPECT_EQ(node->states().range_value(), kInitialRangeValue + kStepSize);
366}
Lucas Radaelli8c500542020-10-12 21:31:02367
368// This test makes sure that when semantic updates toggle on / off / on, the
369// full semantic tree is sent in the first update when back on.
370IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TogglesSemanticsUpdates) {
371 LoadPage(kPage1Path, kPage1Title);
372
373 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32374 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
Lucas Radaelli8c500542020-10-12 21:31:02375
376 semantics_manager_.SetSemanticsModeEnabled(false);
377 base::RunLoop().RunUntilIdle();
378
379 ASSERT_FALSE(frame_impl_->web_contents_for_test()
380 ->IsWebContentsOnlyAccessibilityModeForTesting());
381
382 // The tree gets cleared when semantic updates are off.
383 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 0u);
384 semantics_manager_.SetSemanticsModeEnabled(true);
385 base::RunLoop().RunUntilIdle();
386 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
Lucas Radaelli0b226d232020-10-12 22:00:32387 EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u);
Lucas Radaelli8c500542020-10-12 21:31:02388
389 ASSERT_TRUE(frame_impl_->web_contents_for_test()
390 ->IsWebContentsOnlyAccessibilityModeForTesting());
391}
Lucas Radaelli0b226d232020-10-12 22:00:32392
393// This test performs several tree modifications (insertions, changes, removals
394// and reparentings). All operations must leave the tree in a valid state and
395// also forward the nodes in a way that leaves the tree in the Fuchsia side in a
396// valid state. Note that every time that a new tree is sent to Fuchsia, the
397// FakeSemantiTree checks if the tree is valid.
398IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TreeModificationsAreForwarded) {
399 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
400 size_t tree_size = 5;
401
402 // The tree has the following form: (1 (2 (3 (4 (5)))))
403 auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
404 bridge->AccessibilityEventReceived(tree_accessibility_event);
405 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(tree_size);
406 CheckCallsToFakeSemanticTree(/*num_deletes=*/0, /*num_updates=*/1,
407 /*num_commits=*/1);
408
409 // Adds a new node with ID 6.
410 // (1 (2 (3 (4 (5 6)))))
411 {
412 ui::AXTreeUpdate update;
413 update.root_id = 1;
414 update.nodes.resize(2);
415 update.nodes[0].id = 4;
416 update.nodes[0].child_ids.push_back(5);
417 update.nodes[0].child_ids.push_back(6);
418 update.nodes[1].id = 6;
419
420 bridge->AccessibilityEventReceived(
421 CreateAccessibilityEventWithUpdate(std::move(update)));
422 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(tree_size + 1);
423 CheckCallsToFakeSemanticTree(/*num_deletes=*/0, /*num_updates=*/2,
424 /*num_commits=*/2);
425 }
426
427 // Removes the added node 6.
428 // (1 (2 (3 (4 (5)))))
429 {
430 ui::AXTreeUpdate update;
431 update.root_id = 1;
432 update.node_id_to_clear = 4;
433 update.nodes.resize(2);
434 update.nodes[0].id = 4;
435 update.nodes[0].child_ids.push_back(5);
436
437 update.nodes[1].id = 5;
438
439 bridge->AccessibilityEventReceived(
440 CreateAccessibilityEventWithUpdate(std::move(update)));
441
442 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(3);
443 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/3,
444 /*num_commits=*/3);
445 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
446 }
447
448 // Reparents node 5 to be a child of node 3.
449 // (1 (2 (3 (4 5))))
450 {
451 ui::AXTreeUpdate update;
452 update.root_id = 1;
453 update.node_id_to_clear = 3;
454 update.nodes.resize(3);
455 update.nodes[0].id = 3;
456 update.nodes[0].child_ids.push_back(4);
457 update.nodes[0].child_ids.push_back(5);
458
459 update.nodes[1].id = 4;
460 update.nodes[2].id = 5;
461
462 bridge->AccessibilityEventReceived(
463 CreateAccessibilityEventWithUpdate(std::move(update)));
464
465 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(4);
466 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/4,
467 /*num_commits=*/4);
468 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
469 }
470
471 // Reparents the subtree rooted at node 3 to be a child of node 1.
472 // (1 (2 3 (4 5)))
473 {
474 ui::AXTreeUpdate update;
475 update.root_id = 1;
476 update.node_id_to_clear = 2;
477 update.nodes.resize(5);
478 update.nodes[0].id = 1;
479 update.nodes[0].child_ids.push_back(2);
480 update.nodes[0].child_ids.push_back(3);
481
482 update.nodes[1].id = 2;
483 update.nodes[2].id = 3;
484 update.nodes[2].child_ids.push_back(4);
485 update.nodes[2].child_ids.push_back(5);
486
487 update.nodes[3].id = 4;
488 update.nodes[4].id = 5;
489
490 bridge->AccessibilityEventReceived(
491 CreateAccessibilityEventWithUpdate(std::move(update)));
492
493 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(5);
494 CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/5,
495 /*num_commits=*/5);
496 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
497 }
498
499 // Deletes the subtree rooted at node 3.
500 // (1 (2))
501 {
502 ui::AXTreeUpdate update;
503 update.root_id = 1;
504 update.node_id_to_clear = 1;
505 update.nodes.resize(2);
506 update.nodes[0].id = 1;
507 update.nodes[0].child_ids.push_back(2);
508
509 update.nodes[1].id = 2;
510
511 bridge->AccessibilityEventReceived(
512 CreateAccessibilityEventWithUpdate(std::move(update)));
513
514 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(6);
515 CheckCallsToFakeSemanticTree(/*num_deletes=*/2, /*num_updates=*/6,
516 /*num_commits=*/6);
517 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
518 }
519
520 // Give this tree a new root.
521 // (7 (2))
522 {
523 ui::AXTreeUpdate update;
524 update.root_id = 7;
525 update.node_id_to_clear = 1;
526 update.nodes.resize(2);
527 update.nodes[0].id = 7;
528 update.nodes[0].child_ids.push_back(2);
529
530 update.nodes[1].id = 2;
531
532 bridge->AccessibilityEventReceived(
533 CreateAccessibilityEventWithUpdate(std::move(update)));
534
535 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(7);
536 CheckCallsToFakeSemanticTree(/*num_deletes=*/3, /*num_updates=*/7,
537 /*num_commits=*/7);
538 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
539 }
540
541 // Delete child and change root ID.
542 // (1)
543 {
544 ui::AXTreeUpdate update;
545 update.root_id = 1;
546 update.node_id_to_clear = 7;
547 update.nodes.resize(1);
548 update.nodes[0].id = 1;
549
550 bridge->AccessibilityEventReceived(
551 CreateAccessibilityEventWithUpdate(std::move(update)));
552
553 semantics_manager_.semantic_tree()->RunUntilCommitCountIs(8);
554 CheckCallsToFakeSemanticTree(/*num_deletes=*/4, /*num_updates=*/8,
555 /*num_commits=*/8);
556 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 1u);
557 }
558}