blob: 5375922c96a970d0d2ee913ef8d336b358e70d15 [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";
Sharon Yangaa2c9bd2019-11-21 17:59:2234const size_t kPage1NodeCount = 9;
35const size_t kPage2NodeCount = 190;
Sharon Yangfbb9ba4a2019-11-18 23:59:5636
Sharon Yang8b548ab92019-12-20 01:52:5437fuchsia::math::PointF GetCenterOfBox(fuchsia::ui::gfx::BoundingBox box) {
38 fuchsia::math::PointF center;
39 center.x = (box.min.x + box.max.x) / 2;
40 center.y = (box.min.y + box.max.y) / 2;
41 return center;
42}
43
Sharon Yangfbb9ba4a2019-11-18 23:59:5644} // namespace
45
46class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
47 public:
Wez7a5f5ca2020-08-17 07:41:0848 AccessibilityBridgeTest() {
Sharon Yangfbb9ba4a2019-11-18 23:59:5649 cr_fuchsia::WebEngineBrowserTest::set_test_server_root(
50 base::FilePath(cr_fuchsia::kTestServerRoot));
51 }
52
53 ~AccessibilityBridgeTest() override = default;
54
Sharon Yangd0d86f42020-07-11 15:38:5355 AccessibilityBridgeTest(const AccessibilityBridgeTest&) = delete;
56 AccessibilityBridgeTest& operator=(const AccessibilityBridgeTest&) = delete;
57
Sharon Yang8b548ab92019-12-20 01:52:5458 void SetUp() override {
59 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
60 command_line->AppendSwitchNative(switches::kOzonePlatform,
61 switches::kHeadless);
62 command_line->AppendSwitch(switches::kHeadless);
63 cr_fuchsia::WebEngineBrowserTest::SetUp();
64 }
65
Sharon Yangfbb9ba4a2019-11-18 23:59:5666 void SetUpOnMainThread() override {
Sharon Yangfbb9ba4a2019-11-18 23:59:5667 frame_ptr_ =
68 cr_fuchsia::WebEngineBrowserTest::CreateFrame(&navigation_listener_);
69 frame_impl_ = context_impl()->GetFrameImplForTest(&frame_ptr_);
Wez7a5f5ca2020-08-17 07:41:0870 frame_impl_->set_semantics_manager_for_test(&semantics_manager_);
Sharon Yang8b548ab92019-12-20 01:52:5471 frame_ptr_->EnableHeadlessRendering();
Sharon Yangfbb9ba4a2019-11-18 23:59:5672
Wez19de8b522020-01-28 20:50:0473 semantics_manager_.WaitUntilViewRegistered();
74 ASSERT_TRUE(semantics_manager_.is_view_registered());
75 ASSERT_TRUE(semantics_manager_.is_listener_valid());
Sharon Yang1dd8b6db32020-04-10 23:10:1876
77 frame_ptr_->GetNavigationController(navigation_controller_.NewRequest());
78 ASSERT_TRUE(embedded_test_server()->Start());
79 semantics_manager_.SetSemanticsModeEnabled(true);
Sharon Yangfbb9ba4a2019-11-18 23:59:5680 }
81
82 protected:
83 fuchsia::web::FramePtr frame_ptr_;
84 FrameImpl* frame_impl_;
85 FakeSemanticsManager semantics_manager_;
Sharon Yangfbb9ba4a2019-11-18 23:59:5686 cr_fuchsia::TestNavigationListener navigation_listener_;
Sharon Yang1dd8b6db32020-04-10 23:10:1887 fuchsia::web::NavigationControllerPtr navigation_controller_;
Sharon Yangfbb9ba4a2019-11-18 23:59:5688};
89
90// Test registration to the SemanticsManager and accessibility mode on
91// WebContents is set correctly.
92IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, RegisterViewRef) {
Sharon Yangfbb9ba4a2019-11-18 23:59:5693 // Change the accessibility mode on the Fuchsia side and check that it is
94 // propagated correctly.
Wez19de8b522020-01-28 20:50:0495 ASSERT_FALSE(frame_impl_->web_contents_for_test()
Sharon Yangfbb9ba4a2019-11-18 23:59:5696 ->IsWebContentsOnlyAccessibilityModeForTesting());
97 semantics_manager_.SetSemanticsModeEnabled(true);
Wez19de8b522020-01-28 20:50:0498
99 // Spin the loop to let the FrameImpl receive the mode-change.
Sharon Yangfbb9ba4a2019-11-18 23:59:56100 base::RunLoop().RunUntilIdle();
Wez19de8b522020-01-28 20:50:04101
Sharon Yangfbb9ba4a2019-11-18 23:59:56102 EXPECT_TRUE(frame_impl_->web_contents_for_test()
103 ->IsWebContentsOnlyAccessibilityModeForTesting());
104}
105
106IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
Sharon Yang1dd8b6db32020-04-10 23:10:18107 GURL page_url(embedded_test_server()->GetURL(kPage1Path));
Wez19de8b522020-01-28 20:50:04108 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18109 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
110 page_url.spec()));
111 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56112
113 // Check that the data values are correct in the FakeSemanticTree.
114 // TODO(fxb/18796): Test more fields once Chrome to Fuchsia conversions are
115 // available.
116 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
117 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15118 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56119 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15120 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56121 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15122 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56123}
124
125// Batching is performed when the number of nodes to send or delete exceeds the
126// maximum, as set on the Fuchsia side. Check that all nodes are received by the
127// Semantic Tree when batching is performed.
128IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
Sharon Yang1dd8b6db32020-04-10 23:10:18129 GURL page_url(embedded_test_server()->GetURL(kPage2Path));
Wez19de8b522020-01-28 20:50:04130 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18131 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
132 page_url.spec()));
133 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage2Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56134
135 // Run until we expect more than a batch's worth of nodes to be present.
136 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangfb25b4a2020-02-10 23:50:15137 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56138}
139
140// Check that semantics information is correctly sent when navigating from page
141// to page.
142IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TestNavigation) {
Sharon Yang8b548ab92019-12-20 01:52:54143 GURL page_url1(embedded_test_server()->GetURL(kPage1Path));
Wez19de8b522020-01-28 20:50:04144 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18145 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
146 page_url1.spec()));
Sharon Yang8b548ab92019-12-20 01:52:54147 navigation_listener_.RunUntilUrlAndTitleEquals(page_url1, kPage1Title);
Sharon Yangfbb9ba4a2019-11-18 23:59:56148
149 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
150 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15151 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
Sharon Yangfbb9ba4a2019-11-18 23:59:56152 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15153 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangfbb9ba4a2019-11-18 23:59:56154 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15155 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56156
Sharon Yang8b548ab92019-12-20 01:52:54157 GURL page_url2(embedded_test_server()->GetURL(kPage2Path));
Wez19de8b522020-01-28 20:50:04158 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18159 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
160 page_url2.spec()));
Sharon Yangfbb9ba4a2019-11-18 23:59:56161
162 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
Sharon Yangca616422019-12-18 23:52:39163 EXPECT_TRUE(
Sharon Yangfb25b4a2020-02-10 23:50:15164 semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title));
165 EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
Sharon Yangca616422019-12-18 23:52:39166
167 // Check that data from the first page has been deleted successfully.
168 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15169 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
Sharon Yangca616422019-12-18 23:52:39170 EXPECT_FALSE(
Sharon Yangfb25b4a2020-02-10 23:50:15171 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
Sharon Yangfbb9ba4a2019-11-18 23:59:56172}
Sharon Yang8b548ab92019-12-20 01:52:54173
174// Checks that the correct node ID is returned when performing hit testing.
Wez99fac70b2020-02-07 15:26:10175// TODO(https://crbug.com/1050049): Re-enable once flake is fixed.
176IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_HitTest) {
Sharon Yang1dd8b6db32020-04-10 23:10:18177 GURL page_url(embedded_test_server()->GetURL(kPage1Path));
Wez19de8b522020-01-28 20:50:04178 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18179 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
180 page_url.spec()));
181 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
Sharon Yang8b548ab92019-12-20 01:52:54182
Sharon Yangd0d86f42020-07-11 15:38:53183 fuchsia::accessibility::semantics::Node* hit_test_node =
Sharon Yang8b548ab92019-12-20 01:52:54184 semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName);
Sharon Yangfb25b4a2020-02-10 23:50:15185 EXPECT_TRUE(hit_test_node);
Sharon Yang8b548ab92019-12-20 01:52:54186
187 fuchsia::math::PointF target_point =
188 GetCenterOfBox(hit_test_node->location());
189
190 EXPECT_EQ(hit_test_node->node_id(),
191 semantics_manager_.HitTestAtPointSync(std::move(target_point)));
192
193 // Expect hit testing to return the root when the point given is out of
194 // bounds or there is no semantic node at that position.
195 target_point.x = -1;
196 target_point.y = -1;
197 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
198 target_point.x = 1;
199 target_point.y = 1;
200 EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
201}
Sharon Yang5fb4ce22020-02-08 00:45:21202
203IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformDefaultAction) {
Sharon Yang1dd8b6db32020-04-10 23:10:18204 GURL page_url(embedded_test_server()->GetURL(kPage1Path));
Sharon Yang5fb4ce22020-02-08 00:45:21205 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
Sharon Yang1dd8b6db32020-04-10 23:10:18206 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
207 page_url.spec()));
208 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
Sharon Yang5fb4ce22020-02-08 00:45:21209 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
210
Sharon Yangd0d86f42020-07-11 15:38:53211 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang5fb4ce22020-02-08 00:45:21212 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
213 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53214 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang5fb4ce22020-02-08 00:45:21215 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
216 EXPECT_TRUE(button2);
Sharon Yangd0d86f42020-07-11 15:38:53217 fuchsia::accessibility::semantics::Node* button3 =
Sharon Yang5fb4ce22020-02-08 00:45:21218 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
219 EXPECT_TRUE(button3);
220
221 // Perform the default action (click) on multiple buttons.
Sharon Yangd0d86f42020-07-11 15:38:53222 semantics_manager_.RequestAccessibilityAction(
223 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
224 semantics_manager_.RequestAccessibilityAction(
225 button2->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
Sharon Yang5fb4ce22020-02-08 00:45:21226 semantics_manager_.RunUntilNumActionsHandledEquals(2);
Sharon Yang5fb4ce22020-02-08 00:45:21227}
Sharon Yang1dd8b6db32020-04-10 23:10:18228
229IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformUnsupportedAction) {
230 GURL page_url(embedded_test_server()->GetURL(kPage1Path));
231 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
232 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
233 page_url.spec()));
234 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
235 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
236
Sharon Yangd0d86f42020-07-11 15:38:53237 fuchsia::accessibility::semantics::Node* button1 =
Sharon Yang1dd8b6db32020-04-10 23:10:18238 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
239 EXPECT_TRUE(button1);
Sharon Yangd0d86f42020-07-11 15:38:53240 fuchsia::accessibility::semantics::Node* button2 =
Sharon Yang1dd8b6db32020-04-10 23:10:18241 semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
242 EXPECT_TRUE(button2);
243
244 // Perform one supported action (DEFAULT) and one non-supported action
245 // (SET_VALUE);
Sharon Yangd0d86f42020-07-11 15:38:53246 semantics_manager_.RequestAccessibilityAction(
247 button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
248 semantics_manager_.RequestAccessibilityAction(
249 button2->node_id(), fuchsia::accessibility::semantics::Action::SET_VALUE);
Sharon Yang1dd8b6db32020-04-10 23:10:18250 semantics_manager_.RunUntilNumActionsHandledEquals(2);
251
252 EXPECT_EQ(1, semantics_manager_.num_actions_handled());
253 EXPECT_EQ(1, semantics_manager_.num_actions_unhandled());
254}
Sharon Yang7919fab2020-07-28 16:46:20255
256IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Disconnect) {
257 base::RunLoop run_loop;
258 frame_ptr_.set_error_handler([&run_loop](zx_status_t status) {
259 EXPECT_EQ(ZX_ERR_INTERNAL, status);
260 run_loop.Quit();
261 });
262
263 semantics_manager_.semantic_tree()->Disconnect();
264 run_loop.Run();
265}
Sharon Yang3231ad92020-08-12 02:22:08266
Adam Ettenberger9c15a3a2020-09-01 22:10:27267IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
Sharon Yang3231ad92020-08-12 02:22:08268 constexpr int kScreenWidth = 720;
269 constexpr int kScreenHeight = 640;
270 gfx::Rect screen_bounds(kScreenWidth, kScreenHeight);
271
272 GURL page_url(embedded_test_server()->GetURL(kPage1Path));
273 ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
274 navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
275 page_url.spec()));
276 navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
277 semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
278
279 auto* content_view =
280 frame_impl_->web_contents_for_test()->GetContentNativeView();
281 content_view->SetBounds(screen_bounds);
282
283 // Get a node that is off the screen.
284 fuchsia::accessibility::semantics::Node* node =
285 semantics_manager_.semantic_tree()->GetNodeFromLabel(kOffscreenNodeName);
286 ASSERT_TRUE(node);
287 AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
288 ui::AXNode* ax_node = bridge->ax_tree_for_test()->GetFromId(node->node_id());
289 ASSERT_TRUE(ax_node);
290 bool is_offscreen = false;
291 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
292 EXPECT_TRUE(is_offscreen);
293
294 // Perform SHOW_ON_SCREEN on that node and check that it is on the screen.
295 base::RunLoop run_loop;
296 bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
297 semantics_manager_.RequestAccessibilityAction(
298 node->node_id(),
299 fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
300 semantics_manager_.RunUntilNumActionsHandledEquals(1);
301 run_loop.Run();
302
303 // Initialize |is_offscreen| to false before calling GetTreeBounds as
304 // specified by the API.
305 is_offscreen = false;
306 bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
307
308 EXPECT_FALSE(is_offscreen);
309}