blob: ff7bb11a2a1725e8bd55d01410816bf677cff968 [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
Sharon Yangfbb9ba4a2019-11-18 23:59:5646} // namespace
47
48class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
49 public:
Wez7a5f5ca2020-08-17 07:41:0850 AccessibilityBridgeTest() {
Sharon Yangfbb9ba4a2019-11-18 23:59:5651 cr_fuchsia::WebEngineBrowserTest::set_test_server_root(
52 base::FilePath(cr_fuchsia::kTestServerRoot));
53 }
54
55 ~AccessibilityBridgeTest() override = default;
56
Sharon Yangd0d86f42020-07-11 15:38:5357 AccessibilityBridgeTest(const AccessibilityBridgeTest&) = delete;
58 AccessibilityBridgeTest& operator=(const AccessibilityBridgeTest&) = delete;
59
Sharon Yang8b548ab92019-12-20 01:52:5460 void SetUp() override {
61 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
62 command_line->AppendSwitchNative(switches::kOzonePlatform,
63 switches::kHeadless);
64 command_line->AppendSwitch(switches::kHeadless);
65 cr_fuchsia::WebEngineBrowserTest::SetUp();
66 }
67
Sharon Yangfbb9ba4a2019-11-18 23:59:5668 void SetUpOnMainThread() override {
Sharon Yangfbb9ba4a2019-11-18 23:59:5669 frame_ptr_ =
70 cr_fuchsia::WebEngineBrowserTest::CreateFrame(&navigation_listener_);
71 frame_impl_ = context_impl()->GetFrameImplForTest(&frame_ptr_);
Wez7a5f5ca2020-08-17 07:41:0872 frame_impl_->set_semantics_manager_for_test(&semantics_manager_);
Sharon Yang8b548ab92019-12-20 01:52:5473 frame_ptr_->EnableHeadlessRendering();
Sharon Yangfbb9ba4a2019-11-18 23:59:5674
Wez19de8b522020-01-28 20:50:0475 semantics_manager_.WaitUntilViewRegistered();
76 ASSERT_TRUE(semantics_manager_.is_view_registered());
77 ASSERT_TRUE(semantics_manager_.is_listener_valid());
Sharon Yang1dd8b6db32020-04-10 23:10:1878
79 frame_ptr_->GetNavigationController(navigation_controller_.NewRequest());
80 ASSERT_TRUE(embedded_test_server()->Start());
81 semantics_manager_.SetSemanticsModeEnabled(true);
Sharon Yangfbb9ba4a2019-11-18 23:59:5682 }
83
Lucas Radaelli8c500542020-10-12 21:31:0284 void LoadPage(base::StringPiece url, base::StringPiece page_title) {
85 GURL page_url(embedded_test_server()->GetURL(std::string(url)));
86 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
87 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
88 page_url.spec()));
89 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, page_title);
90 }
91
Sharon Yangfbb9ba4a2019-11-18 23:59:5692 protected:
93 fuchsia::web::FramePtr frame_ptr_;
94 FrameImpl* frame_impl_;
95 FakeSemanticsManager semantics_manager_;
Sharon Yangfbb9ba4a2019-11-18 23:59:5696 cr_fuchsia::TestNavigationListener navigation_listener_;
Sharon Yang1dd8b6db32020-04-10 23:10:1897 fuchsia::web::NavigationControllerPtr navigation_controller_;
Sharon Yangfbb9ba4a2019-11-18 23:59:5698};
99
100// Test registration to the SemanticsManager and accessibility mode on
101// WebContents is set correctly.
102IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, RegisterViewRef) {
Sharon Yangfbb9ba4a2019-11-18 23:59:56103 // Change the accessibility mode on the Fuchsia side and check that it is
104 // propagated correctly.
Wez19de8b522020-01-28 20:50:04105 ASSERT_FALSE(frame_impl_->web_contents_for_test()
Sharon Yangfbb9ba4a2019-11-18 23:59:56106 ->IsWebContentsOnlyAccessibilityModeForTesting());
107 semantics_manager_.SetSemanticsModeEnabled(true);
Wez19de8b522020-01-28 20:50:04108
109 // Spin the loop to let the FrameImpl receive the mode-change.
Sharon Yangfbb9ba4a2019-11-18 23:59:56110 base::RunLoop().RunUntilIdle();
Wez19de8b522020-01-28 20:50:04111
Sharon Yangfbb9ba4a2019-11-18 23:59:56112 EXPECT_TRUE(frame_impl_->web_contents_for_test()
113 ->IsWebContentsOnlyAccessibilityModeForTesting());
114}
115
116IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
Lucas Radaelli8c500542020-10-12 21:31:02117 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56118
119 // Check that the data values are correct in the FakeSemanticTree.
120 // TODO(fxb/18796): Test more fields once Chrome to Fuchsia conversions are
121 // available.
122 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
123 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15124 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56125 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15126 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56127 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15128 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56129}
130
131// Batching is performed when the number of nodes to send or delete exceeds the
132// maximum, as set on the Fuchsia side. Check that all nodes are received by the
133// Semantic Tree when batching is performed.
134IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
Lucas Radaelli8c500542020-10-12 21:31:02135 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56136
137 // Run until we expect more than a batch's worth of nodes to be present.
138 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangfb25b4a2020-02-10 23:50:15139 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56140}
141
142// Check that semantics information is correctly sent when navigating from page
143// to page.
144IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TestNavigation) {
Lucas Radaelli8c500542020-10-12 21:31:02145 LoadPage(kPage1Path, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56146
147 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
148 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15149 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56150 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15151 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56152 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15153 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56154
Lucas Radaelli8c500542020-10-12 21:31:02155 LoadPage(kPage2Path, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56156
157 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangca616422019-12-18 23:52:39158 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15159 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title));
160 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangca616422019-12-18 23:52:39161
162 // Check that data from the first page has been deleted successfully.
163 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15164 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangca616422019-12-18 23:52:39165 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15166 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56167}
Sharon Yang8b548ab92019-12-20 01:52:54168
169// Checks that the correct node ID is returned when performing hit testing.
Wez99fac70b2020-02-07 15:26:10170// TODO(https://crbug.com/1050049): Re-enable once flake is fixed.
171IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_HitTest) {
Lucas Radaelli8c500542020-10-12 21:31:02172 LoadPage(kPage1Path, kPage1Title);
Sharon Yang8b548ab92019-12-20 01:52:54173
Sharon Yangd0d86f42020-07-11 15:38:53174 fuchsia::accessibility::semantics::Node* hit_test_node =
Sharon Yang8b548ab92019-12-20 01:52:54175 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName);
Sharon Yangfb25b4a2020-02-10 23:50:15176 EXPECT_TRUE(hit_test_node);
Sharon Yang8b548ab92019-12-20 01:52:54177
178 fuchsia::math::PointF target_point =
179 GetCenterOfBox(hit_test_node->location());
180
181 EXPECT_EQ(hit_test_node->node_id(),
182 semantics_manager_.HitTestAtPointSync(std::move(target_point)));
183
184 // Expect hit testing to return the root when the point given is out of
185 // bounds or there is no semantic node at that position.
186 target_point.x = -1;
187 target_point.y = -1;
188 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
189 target_point.x = 1;
190 target_point.y = 1;
191 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
192}
Sharon Yang5fb4ce22020-02-08 00:45:21193
194IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformDefaultAction) {
Lucas Radaelli8c500542020-10-12 21:31:02195 LoadPage(kPage1Path, kPage1Title);
196
Sharon Yang5fb4ce22020-02-08 00:45:21197 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
198
Sharon Yangd0d86f42020-07-11 15:38:53199 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang5fb4ce22020-02-08 00:45:21200 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
201 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53202 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang5fb4ce22020-02-08 00:45:21203 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
204 EXPECT_TRUE(button2);
Sharon Yangd0d86f42020-07-11 15:38:53205 fuchsia::accessibility::semantics::Node* button3 =
Sharon Yang5fb4ce22020-02-08 00:45:21206 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
207 EXPECT_TRUE(button3);
208
209 // Perform the default action (click) on multiple buttons.
Sharon Yangd0d86f42020-07-11 15:38:53210 semantics_manager_.RequestAccessibilityAction(
211 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
212 semantics_manager_.RequestAccessibilityAction(
213 button2->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
Sharon Yang5fb4ce22020-02-08 00:45:21214 semantics_manager_.RunUntilNumActionsHandledEquals(2);
Sharon Yang5fb4ce22020-02-08 00:45:21215}
Sharon Yang1dd8b6db32020-04-10 23:10:18216
217IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformUnsupportedAction) {
Lucas Radaelli8c500542020-10-12 21:31:02218 LoadPage(kPage1Path, kPage1Title);
219
Sharon Yang1dd8b6db32020-04-10 23:10:18220 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
221
Sharon Yangd0d86f42020-07-11 15:38:53222 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang1dd8b6db32020-04-10 23:10:18223 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
224 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53225 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang1dd8b6db32020-04-10 23:10:18226 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
227 EXPECT_TRUE(button2);
228
229 // Perform one supported action (DEFAULT) and one non-supported action
230 // (SET_VALUE);
Sharon Yangd0d86f42020-07-11 15:38:53231 semantics_manager_.RequestAccessibilityAction(
232 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
233 semantics_manager_.RequestAccessibilityAction(
234 button2->node_id(), fuchsia::accessibility::semantics::Action::SET_VALUE);
Sharon Yang1dd8b6db32020-04-10 23:10:18235 semantics_manager_.RunUntilNumActionsHandledEquals(2);
236
237 EXPECT_EQ(1, semantics_manager_.num_actions_handled());
238 EXPECT_EQ(1, semantics_manager_.num_actions_unhandled());
239}
Sharon Yang7919fab2020-07-28 16:46:20240
241IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Disconnect) {
242 base::RunLoop run_loop;
243 frame_ptr_.set_error_handler([&run_loop](zx_status_t status) {
244 EXPECT_EQ(ZX_ERR_INTERNAL, status);
245 run_loop.Quit();
246 });
247
248 semantics_manager_.semantic_tree()->Disconnect();
249 run_loop.Run();
250}
Sharon Yang3231ad92020-08-12 02:22:08251
Adam Ettenberger9c15a3a2020-09-01 22:10:27252IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
Sharon Yang3231ad92020-08-12 02:22:08253 constexpr int kScreenWidth = 720;
254 constexpr int kScreenHeight = 640;
255 gfx::Rect screen_bounds(kScreenWidth, kScreenHeight);
256
Lucas Radaelli8c500542020-10-12 21:31:02257 LoadPage(kPage1Path, kPage1Title);
258
Sharon Yang3231ad92020-08-12 02:22:08259 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
260
261 auto* content_view =
262 frame_impl_->web_contents_for_test()->GetContentNativeView();
263 content_view->SetBounds(screen_bounds);
264
265 // Get a node that is off the screen.
266 fuchsia::accessibility::semantics::Node* node =
267 semantics_manager_.semantic_tree()->GetNodeFromLabel(kOffscreenNodeName);
268 ASSERT_TRUE(node);
269 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
270 ui::AXNode* ax_node = bridge->ax_tree_for_test()->GetFromId(node->node_id());
271 ASSERT_TRUE(ax_node);
272 bool is_offscreen = false;
273 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
274 EXPECT_TRUE(is_offscreen);
275
276 // Perform SHOW_ON_SCREEN on that node and check that it is on the screen.
277 base::RunLoop run_loop;
278 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
279 semantics_manager_.RequestAccessibilityAction(
280 node->node_id(),
281 fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
282 semantics_manager_.RunUntilNumActionsHandledEquals(1);
283 run_loop.Run();
284
285 // Initialize |is_offscreen| to false before calling GetTreeBounds as
286 // specified by the API.
287 is_offscreen = false;
288 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
289
290 EXPECT_FALSE(is_offscreen);
291}
Sharon Yang2caf82e2020-10-12 18:00:35292
293IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Slider) {
Lucas Radaelli8c500542020-10-12 21:31:02294 LoadPage(kPage1Path, kPage1Title);
295
Sharon Yang2caf82e2020-10-12 18:00:35296 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
297
298 fuchsia::accessibility::semantics::Node* node =
299 semantics_manager_.semantic_tree()->GetNodeFromRole(
300 fuchsia::accessibility::semantics::Role::SLIDER);
301 EXPECT_TRUE(node);
302 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
303 EXPECT_EQ(node->states().range_value(), kInitialRangeValue);
304
305 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
306 base::RunLoop run_loop;
307 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
308 semantics_manager_.RequestAccessibilityAction(
309 node->node_id(), fuchsia::accessibility::semantics::Action::INCREMENT);
310 semantics_manager_.RunUntilNumActionsHandledEquals(1);
311 run_loop.Run();
312
313 // Wait for the slider node to be updated, then check the value.
314 base::RunLoop run_loop2;
315 semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
316 node->node_id(), run_loop2.QuitClosure());
317 run_loop2.Run();
318
319 node = semantics_manager_.semantic_tree()->GetNodeWithId(node->node_id());
320 EXPECT_TRUE(node->has_states() && node->states().has_range_value());
321 EXPECT_EQ(node->states().range_value(), kInitialRangeValue + kStepSize);
322}
Lucas Radaelli8c500542020-10-12 21:31:02323
324// This test makes sure that when semantic updates toggle on / off / on, the
325// full semantic tree is sent in the first update when back on.
326IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TogglesSemanticsUpdates) {
327 LoadPage(kPage1Path, kPage1Title);
328
329 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
330
331 semantics_manager_.SetSemanticsModeEnabled(false);
332 base::RunLoop().RunUntilIdle();
333
334 ASSERT_FALSE(frame_impl_->web_contents_for_test()
335 ->IsWebContentsOnlyAccessibilityModeForTesting());
336
337 // The tree gets cleared when semantic updates are off.
338 EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 0u);
339 semantics_manager_.SetSemanticsModeEnabled(true);
340 base::RunLoop().RunUntilIdle();
341 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
342
343 ASSERT_TRUE(frame_impl_->web_contents_for_test()
344 ->IsWebContentsOnlyAccessibilityModeForTesting());
345}