accessibility_bridge_browsertest.cc 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. // 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. #include <fuchsia/accessibility/semantics/cpp/fidl.h>
  5. #include <lib/ui/scenic/cpp/view_ref_pair.h>
  6. #include <zircon/types.h>
  7. #include "base/command_line.h"
  8. #include "base/fuchsia/mem_buffer_util.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/test/bind.h"
  11. #include "content/public/test/browser_test.h"
  12. #include "fuchsia_web/common/test/frame_test_util.h"
  13. #include "fuchsia_web/common/test/test_navigation_listener.h"
  14. #include "fuchsia_web/webengine/browser/accessibility_bridge.h"
  15. #include "fuchsia_web/webengine/browser/context_impl.h"
  16. #include "fuchsia_web/webengine/browser/fake_semantics_manager.h"
  17. #include "fuchsia_web/webengine/browser/frame_impl.h"
  18. #include "fuchsia_web/webengine/test/frame_for_test.h"
  19. #include "fuchsia_web/webengine/test/test_data.h"
  20. #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
  21. #include "net/test/embedded_test_server/embedded_test_server.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. #include "ui/accessibility/ax_action_data.h"
  24. #include "ui/accessibility/ax_tree_observer.h"
  25. #include "ui/gfx/switches.h"
  26. #include "ui/ozone/public/ozone_switches.h"
  27. namespace {
  28. const char kPage1Path[] = "/ax1.html";
  29. const char kPage2Path[] = "/batching.html";
  30. const char kPageIframePath[] = "/iframe.html";
  31. const char kPage1Title[] = "accessibility 1";
  32. const char kPage2Title[] = "lots of nodes!";
  33. const char kPageIframeTitle[] = "iframe title";
  34. const char kButtonName1[] = "a button";
  35. const char kButtonName2[] = "another button";
  36. const char kButtonName3[] = "button 3";
  37. const char kNodeName[] = "last node";
  38. const char kParagraphName[] = "a third paragraph";
  39. const char kOffscreenNodeName[] = "offscreen node";
  40. const char kUpdate1Name[] = "update1";
  41. const char kUpdate2Name[] = "update2";
  42. const char kUpdate3Name[] = "update3";
  43. const char kUpdate4Name[] = "update4";
  44. const char kUpdate5Name[] = "update5";
  45. const char kUpdate6Name[] = "update6";
  46. const char kUpdate7Name[] = "update7";
  47. const char kUpdate8Name[] = "update8";
  48. const size_t kPage1NodeCount = 29;
  49. const size_t kPage2NodeCount = 190;
  50. const size_t kInitialRangeValue = 51;
  51. const size_t kStepSize = 3;
  52. // Simulated screen bounds to use when testing the SemanticsManager.
  53. constexpr gfx::Size kTestWindowSize = {720, 640};
  54. fuchsia::math::PointF GetCenterOfBox(fuchsia::ui::gfx::BoundingBox box) {
  55. fuchsia::math::PointF center;
  56. center.x = (box.min.x + box.max.x) / 2;
  57. center.y = (box.min.y + box.max.y) / 2;
  58. return center;
  59. }
  60. // Creates an AXEventNotificationDetails that contains an AxTreeUpdate that
  61. // builds a tree from scratch of the form: (1 (2 ... (|tree_size|))).
  62. content::AXEventNotificationDetails CreateTreeAccessibilityEvent(
  63. size_t tree_size) {
  64. content::AXEventNotificationDetails event;
  65. event.ax_tree_id = ui::AXTreeID ::CreateNewAXTreeID();
  66. ui::AXTreeUpdate update;
  67. update.root_id = 1;
  68. update.nodes.resize(tree_size);
  69. for (int i = 1; i <= static_cast<int>(tree_size); ++i) {
  70. auto& node = update.nodes[i - 1]; // vector 0-indexed, IDs 1-indexed.
  71. node.id = i;
  72. node.child_ids.push_back(i + 1);
  73. }
  74. // The deepest node does not have any child.
  75. update.nodes.back().child_ids.clear();
  76. event.updates.push_back(std::move(update));
  77. return event;
  78. }
  79. // Creates an AXEventNotificationDetails that contains |update| for the tree
  80. // referenced by |tree_id|.
  81. content::AXEventNotificationDetails CreateAccessibilityEventWithUpdate(
  82. ui::AXTreeUpdate update,
  83. ui::AXTreeID tree_id) {
  84. content::AXEventNotificationDetails event;
  85. event.ax_tree_id = tree_id;
  86. event.updates.push_back(std::move(update));
  87. return event;
  88. }
  89. // Returns whether or not the given node supports the given action.
  90. bool HasAction(const fuchsia::accessibility::semantics::Node& node,
  91. fuchsia::accessibility::semantics::Action action) {
  92. for (const auto& node_action : node.actions()) {
  93. if (node_action == action)
  94. return true;
  95. }
  96. return false;
  97. }
  98. } // namespace
  99. class AccessibilityBridgeTest : public WebEngineBrowserTest {
  100. public:
  101. AccessibilityBridgeTest() {
  102. WebEngineBrowserTest::set_test_server_root(base::FilePath(kTestServerRoot));
  103. }
  104. ~AccessibilityBridgeTest() override = default;
  105. AccessibilityBridgeTest(const AccessibilityBridgeTest&) = delete;
  106. AccessibilityBridgeTest& operator=(const AccessibilityBridgeTest&) = delete;
  107. void SetUp() override {
  108. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  109. command_line->AppendSwitchNative(switches::kOzonePlatform,
  110. switches::kHeadless);
  111. command_line->AppendSwitch(switches::kHeadless);
  112. WebEngineBrowserTest::SetUp();
  113. }
  114. void SetUpOnMainThread() override {
  115. frame_ = FrameForTest::Create(context(), {});
  116. base::RunLoop().RunUntilIdle();
  117. frame_impl_ = context_impl()->GetFrameImplForTest(&frame_.ptr());
  118. frame_impl_->set_semantics_manager_for_test(&semantics_manager_);
  119. frame_impl_->set_window_size_for_test(kTestWindowSize);
  120. // TODO(crbug.com/1291330): Remove uses of
  121. // set_use_v2_accessibility_bridge().
  122. frame_impl_->set_use_v2_accessibility_bridge(false);
  123. frame_->EnableHeadlessRendering();
  124. semantics_manager_.WaitUntilViewRegistered();
  125. ASSERT_TRUE(semantics_manager_.is_view_registered());
  126. ASSERT_TRUE(semantics_manager_.is_listener_valid());
  127. ASSERT_TRUE(embedded_test_server()->Start());
  128. // Change the accessibility mode on the Fuchsia side and check that it is
  129. // propagated correctly.
  130. ASSERT_FALSE(frame_impl_->web_contents_for_test()
  131. ->IsWebContentsOnlyAccessibilityModeForTesting());
  132. semantics_manager_.SetSemanticsModeEnabled(true);
  133. base::RunLoop().RunUntilIdle();
  134. ASSERT_TRUE(frame_impl_->web_contents_for_test()
  135. ->IsWebContentsOnlyAccessibilityModeForTesting());
  136. }
  137. void LoadPage(base::StringPiece url, base::StringPiece page_title) {
  138. GURL page_url(embedded_test_server()->GetURL(std::string(url)));
  139. ASSERT_TRUE(LoadUrlAndExpectResponse(frame_.GetNavigationController(),
  140. fuchsia::web::LoadUrlParams(),
  141. page_url.spec()));
  142. frame_.navigation_listener().RunUntilUrlAndTitleEquals(page_url,
  143. page_title);
  144. }
  145. protected:
  146. FrameForTest frame_;
  147. FrameImpl* frame_impl_;
  148. FakeSemanticsManager semantics_manager_;
  149. };
  150. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) {
  151. LoadPage(kPage1Path, kPage1Title);
  152. // Check that the data values are correct in the FakeSemanticTree.
  153. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  154. EXPECT_TRUE(
  155. semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
  156. EXPECT_TRUE(
  157. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
  158. EXPECT_TRUE(
  159. semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
  160. }
  161. // Batching is performed when the number of nodes to send or delete exceeds the
  162. // maximum, as set on the Fuchsia side. Check that all nodes are received by the
  163. // Semantic Tree when batching is performed.
  164. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) {
  165. LoadPage(kPage2Path, kPage2Title);
  166. // Run until we expect more than a batch's worth of nodes to be present.
  167. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount);
  168. EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
  169. EXPECT_EQ(semantics_manager_.semantic_tree()->num_delete_calls(), 0u);
  170. // Checks if the actual batching happened.
  171. EXPECT_GE(semantics_manager_.semantic_tree()->num_update_calls(), 18u);
  172. // Checks if one or more commit calls were made to send the data.
  173. EXPECT_GE(semantics_manager_.semantic_tree()->num_commit_calls(), 1u);
  174. }
  175. // Check that semantics information is correctly sent when navigating from page
  176. // to page.
  177. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, NavigateFromPageToPage) {
  178. LoadPage(kPage1Path, kPage1Title);
  179. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  180. EXPECT_TRUE(
  181. semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title));
  182. EXPECT_TRUE(
  183. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
  184. EXPECT_TRUE(
  185. semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
  186. LoadPage(kPage2Path, kPage2Title);
  187. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  188. kPage2Title);
  189. EXPECT_TRUE(
  190. semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title));
  191. EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
  192. // Check that data from the first page has been deleted successfully.
  193. EXPECT_FALSE(
  194. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
  195. EXPECT_FALSE(
  196. semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName));
  197. }
  198. // Checks that the correct node ID is returned when performing hit testing.
  199. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, HitTest) {
  200. LoadPage(kPage1Path, kPage1Title);
  201. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  202. fuchsia::accessibility::semantics::Node* target_node =
  203. semantics_manager_.semantic_tree()->GetNodeFromLabel(kParagraphName);
  204. EXPECT_TRUE(target_node);
  205. fuchsia::math::PointF target_point = GetCenterOfBox(target_node->location());
  206. float scale_factor = 20.f;
  207. // Make the bridge use scaling in hit test calculations.
  208. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  209. bridge->set_device_scale_factor_for_test(scale_factor);
  210. // Downscale the target point, since the hit test calculation will scale it
  211. // back up.
  212. target_point.x /= scale_factor;
  213. target_point.y /= scale_factor;
  214. uint32_t hit_node_id =
  215. semantics_manager_.HitTestAtPointSync(std::move(target_point));
  216. fuchsia::accessibility::semantics::Node* hit_node =
  217. semantics_manager_.semantic_tree()->GetNodeWithId(hit_node_id);
  218. EXPECT_EQ(hit_node->attributes().label(), kParagraphName);
  219. // Expect hit testing to return the root when the point given is out of
  220. // bounds or there is no semantic node at that position.
  221. target_point.x = -1;
  222. target_point.y = -1;
  223. EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
  224. target_point.x = 1. / scale_factor;
  225. target_point.y = 1. / scale_factor;
  226. EXPECT_EQ(0u, semantics_manager_.HitTestAtPointSync(std::move(target_point)));
  227. }
  228. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformDefaultAction) {
  229. LoadPage(kPage1Path, kPage1Title);
  230. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  231. fuchsia::accessibility::semantics::Node* button1 =
  232. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
  233. EXPECT_TRUE(button1);
  234. fuchsia::accessibility::semantics::Node* button2 =
  235. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
  236. EXPECT_TRUE(button2);
  237. fuchsia::accessibility::semantics::Node* button3 =
  238. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
  239. EXPECT_TRUE(button3);
  240. EXPECT_TRUE(
  241. HasAction(*button1, fuchsia::accessibility::semantics::Action::DEFAULT));
  242. EXPECT_TRUE(
  243. HasAction(*button2, fuchsia::accessibility::semantics::Action::DEFAULT));
  244. EXPECT_TRUE(
  245. HasAction(*button3, fuchsia::accessibility::semantics::Action::DEFAULT));
  246. // Perform the default action (click) on multiple buttons.
  247. semantics_manager_.RequestAccessibilityAction(
  248. button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
  249. semantics_manager_.RequestAccessibilityAction(
  250. button2->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
  251. semantics_manager_.RunUntilNumActionsHandledEquals(2);
  252. }
  253. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformUnsupportedAction) {
  254. LoadPage(kPage1Path, kPage1Title);
  255. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  256. fuchsia::accessibility::semantics::Node* button1 =
  257. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1);
  258. EXPECT_TRUE(button1);
  259. fuchsia::accessibility::semantics::Node* button2 =
  260. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName2);
  261. EXPECT_TRUE(button2);
  262. // Perform one supported action (DEFAULT) and one non-supported action
  263. // (SET_VALUE);
  264. semantics_manager_.RequestAccessibilityAction(
  265. button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
  266. semantics_manager_.RequestAccessibilityAction(
  267. button2->node_id(), fuchsia::accessibility::semantics::Action::SET_VALUE);
  268. semantics_manager_.RunUntilNumActionsHandledEquals(2);
  269. EXPECT_EQ(1, semantics_manager_.num_actions_handled());
  270. EXPECT_EQ(1, semantics_manager_.num_actions_unhandled());
  271. }
  272. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, Disconnect) {
  273. base::RunLoop run_loop;
  274. frame_.ptr().set_error_handler([&run_loop](zx_status_t status) {
  275. EXPECT_EQ(ZX_ERR_INTERNAL, status);
  276. run_loop.Quit();
  277. });
  278. semantics_manager_.semantic_tree()->Disconnect();
  279. run_loop.Run();
  280. }
  281. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, PerformScrollToMakeVisible) {
  282. // Set the screen height to be small so that we can detect if we've
  283. // scrolled past our target, even if the max scroll is bounded.
  284. constexpr int kScreenWidth = 720;
  285. constexpr int kScreenHeight = 20;
  286. gfx::Rect screen_bounds(kScreenWidth, kScreenHeight);
  287. LoadPage(kPage1Path, kPage1Title);
  288. auto* semantic_tree = semantics_manager_.semantic_tree();
  289. ASSERT_TRUE(semantic_tree);
  290. semantic_tree->RunUntilNodeCountAtLeast(kPage1NodeCount);
  291. auto* content_view =
  292. frame_impl_->web_contents_for_test()->GetContentNativeView();
  293. content_view->SetBounds(screen_bounds);
  294. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  295. // Get a node that is off the screen, and verify that it is off the screen.
  296. fuchsia::accessibility::semantics::Node* fuchsia_node =
  297. semantic_tree->GetNodeFromLabel(kOffscreenNodeName);
  298. ASSERT_TRUE(fuchsia_node);
  299. // Get the corresponding AXNode.
  300. auto ax_node_id = bridge->node_id_mapper_for_test()
  301. ->ToAXNodeID(fuchsia_node->node_id())
  302. ->second;
  303. ui::AXNode* ax_node = bridge->ax_tree_for_test()->GetFromId(ax_node_id);
  304. ASSERT_TRUE(ax_node);
  305. bool is_offscreen = false;
  306. bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
  307. EXPECT_TRUE(is_offscreen);
  308. // Perform SHOW_ON_SCREEN on that node.
  309. semantics_manager_.RequestAccessibilityAction(
  310. fuchsia_node->node_id(),
  311. fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
  312. semantics_manager_.RunUntilNumActionsHandledEquals(1);
  313. semantic_tree->RunUntilConditionIsTrue(
  314. base::BindLambdaForTesting([semantic_tree]() {
  315. auto* root = semantic_tree->GetNodeWithId(0u);
  316. if (!root)
  317. return false;
  318. // Once the scroll action has been handled, the root should have a
  319. // non-zero y-scroll offset.
  320. return root->has_states() && root->states().has_viewport_offset() &&
  321. root->states().viewport_offset().y > 0;
  322. }));
  323. // Verify that the AXNode we tried to make visible is now onscreen.
  324. // Initialize |is_offscreen| to false before calling GetTreeBounds as
  325. // specified by the API.
  326. is_offscreen = false;
  327. bridge->ax_tree_for_test()->GetTreeBounds(ax_node, &is_offscreen);
  328. EXPECT_FALSE(is_offscreen);
  329. }
  330. // TODO(1168167): Test sets node-updated callback only after triggering node
  331. // update, making it flaky.
  332. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_Slider) {
  333. LoadPage(kPage1Path, kPage1Title);
  334. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  335. fuchsia::accessibility::semantics::Node* node =
  336. semantics_manager_.semantic_tree()->GetNodeFromRole(
  337. fuchsia::accessibility::semantics::Role::SLIDER);
  338. EXPECT_TRUE(node);
  339. EXPECT_TRUE(node->has_states() && node->states().has_range_value());
  340. EXPECT_EQ(node->states().range_value(), kInitialRangeValue);
  341. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  342. base::RunLoop run_loop;
  343. bridge->set_event_received_callback_for_test(run_loop.QuitClosure());
  344. semantics_manager_.RequestAccessibilityAction(
  345. node->node_id(), fuchsia::accessibility::semantics::Action::INCREMENT);
  346. semantics_manager_.RunUntilNumActionsHandledEquals(1);
  347. run_loop.Run();
  348. // Wait for the slider node to be updated, then check the value.
  349. base::RunLoop run_loop2;
  350. semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
  351. node->node_id(), run_loop2.QuitClosure());
  352. run_loop2.Run();
  353. node = semantics_manager_.semantic_tree()->GetNodeWithId(node->node_id());
  354. EXPECT_TRUE(node->has_states() && node->states().has_range_value());
  355. EXPECT_EQ(node->states().range_value(), kInitialRangeValue + kStepSize);
  356. }
  357. // This test makes sure that when semantic updates toggle on / off / on, the
  358. // full semantic tree is sent in the first update when back on.
  359. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TogglesSemanticsUpdates) {
  360. LoadPage(kPage1Path, kPage1Title);
  361. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  362. semantics_manager_.SetSemanticsModeEnabled(false);
  363. base::RunLoop().RunUntilIdle();
  364. ASSERT_FALSE(frame_impl_->web_contents_for_test()
  365. ->IsWebContentsOnlyAccessibilityModeForTesting());
  366. // The tree gets cleared when semantic updates are off.
  367. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 0u);
  368. semantics_manager_.SetSemanticsModeEnabled(true);
  369. base::RunLoop().RunUntilIdle();
  370. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  371. ASSERT_TRUE(frame_impl_->web_contents_for_test()
  372. ->IsWebContentsOnlyAccessibilityModeForTesting());
  373. }
  374. // This test performs several tree modifications (insertions, changes, removals
  375. // and reparentings). All operations must leave the tree in a valid state and
  376. // also forward the nodes in a way that leaves the tree in the Fuchsia side in a
  377. // valid state. Note that every time that a new tree is sent to Fuchsia, the
  378. // FakeSemantiTree checks if the tree is valid.
  379. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TreeModificationsAreForwarded) {
  380. // Loads a page, so a real frame is created for this test. Then, several tree
  381. // operations are applied on top of it, using the AXTreeID that corresponds to
  382. // that frame.
  383. LoadPage(kPage1Path, kPage1Title);
  384. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  385. // Fetch the AXTreeID of the main frame (the page just loaded). This ID will
  386. // be used in the operations that follow to simulate new data coming in.
  387. auto tree_id = frame_impl_->web_contents_for_test()
  388. ->GetPrimaryMainFrame()
  389. ->GetAXTreeID();
  390. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  391. size_t tree_size = 5;
  392. // The tree has the following form: (1 (2 (3 (4 (5)))))
  393. auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
  394. tree_accessibility_event.ax_tree_id = tree_id;
  395. // The root of this tree needs to be cleared (because it holds the page just
  396. // loaded, and we are loading something completely new).
  397. tree_accessibility_event.updates[0].node_id_to_clear =
  398. bridge->ax_tree_for_test()->root()->id();
  399. // Set a name in a node so we can wait for this node to appear. This pattern
  400. // is used throughout this test to ensure that the new data we are waiting for
  401. // arrived.
  402. tree_accessibility_event.updates[0].nodes[0].role =
  403. ax::mojom::Role::kStaticText;
  404. tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name);
  405. bridge->AccessibilityEventReceived(tree_accessibility_event);
  406. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  407. kUpdate1Name);
  408. // Adds a new node with ID 6.
  409. // (1 (2 (3 (4 (5 6)))))
  410. {
  411. ui::AXTreeUpdate update;
  412. update.root_id = 1;
  413. update.nodes.resize(2);
  414. update.nodes[0].id = 4;
  415. update.nodes[0].child_ids.push_back(5);
  416. update.nodes[0].child_ids.push_back(6);
  417. update.nodes[1].id = 6;
  418. update.nodes[0].role = ax::mojom::Role::kStaticText;
  419. update.nodes[0].SetName(kUpdate2Name);
  420. bridge->AccessibilityEventReceived(
  421. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  422. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  423. kUpdate2Name);
  424. }
  425. // Removes the added node 6.
  426. // (1 (2 (3 (4 (5)))))
  427. {
  428. ui::AXTreeUpdate update;
  429. update.root_id = 1;
  430. update.node_id_to_clear = 4;
  431. update.nodes.resize(2);
  432. update.nodes[0].id = 4;
  433. update.nodes[0].child_ids.push_back(5);
  434. update.nodes[1].id = 5;
  435. update.nodes[0].role = ax::mojom::Role::kStaticText;
  436. update.nodes[0].SetName(kUpdate3Name);
  437. bridge->AccessibilityEventReceived(
  438. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  439. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  440. kUpdate3Name);
  441. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
  442. }
  443. // Reparents node 5 to be a child of node 3.
  444. // (1 (2 (3 (4 5))))
  445. {
  446. ui::AXTreeUpdate update;
  447. update.root_id = 1;
  448. update.node_id_to_clear = 3;
  449. update.nodes.resize(3);
  450. update.nodes[0].id = 3;
  451. update.nodes[0].child_ids.push_back(4);
  452. update.nodes[0].child_ids.push_back(5);
  453. update.nodes[1].id = 4;
  454. update.nodes[2].id = 5;
  455. update.nodes[0].role = ax::mojom::Role::kStaticText;
  456. update.nodes[0].SetName(kUpdate4Name);
  457. bridge->AccessibilityEventReceived(
  458. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  459. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  460. kUpdate4Name);
  461. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
  462. }
  463. // Reparents the subtree rooted at node 3 to be a child of node 1.
  464. // (1 (2 3 (4 5)))
  465. {
  466. ui::AXTreeUpdate update;
  467. update.root_id = 1;
  468. update.node_id_to_clear = 2;
  469. update.nodes.resize(5);
  470. update.nodes[0].id = 1;
  471. update.nodes[0].child_ids.push_back(2);
  472. update.nodes[0].child_ids.push_back(3);
  473. update.nodes[1].id = 2;
  474. update.nodes[2].id = 3;
  475. update.nodes[2].child_ids.push_back(4);
  476. update.nodes[2].child_ids.push_back(5);
  477. update.nodes[3].id = 4;
  478. update.nodes[4].id = 5;
  479. update.nodes[0].role = ax::mojom::Role::kStaticText;
  480. update.nodes[0].SetName(kUpdate5Name);
  481. bridge->AccessibilityEventReceived(
  482. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  483. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  484. kUpdate5Name);
  485. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size);
  486. }
  487. // Deletes the subtree rooted at node 3.
  488. // (1 (2))
  489. {
  490. ui::AXTreeUpdate update;
  491. update.root_id = 1;
  492. update.node_id_to_clear = 1;
  493. update.nodes.resize(2);
  494. update.nodes[0].id = 1;
  495. update.nodes[0].child_ids.push_back(2);
  496. update.nodes[1].id = 2;
  497. update.nodes[0].role = ax::mojom::Role::kStaticText;
  498. update.nodes[0].SetName(kUpdate6Name);
  499. bridge->AccessibilityEventReceived(
  500. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  501. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  502. kUpdate6Name);
  503. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
  504. }
  505. // Give this tree a new root.
  506. // (7 (2))
  507. {
  508. ui::AXTreeUpdate update;
  509. update.root_id = 7;
  510. update.node_id_to_clear = 1;
  511. update.nodes.resize(2);
  512. update.nodes[0].id = 7;
  513. update.nodes[0].child_ids.push_back(2);
  514. update.nodes[1].id = 2;
  515. update.nodes[0].role = ax::mojom::Role::kStaticText;
  516. update.nodes[0].SetName(kUpdate7Name);
  517. bridge->AccessibilityEventReceived(
  518. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  519. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  520. kUpdate7Name);
  521. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u);
  522. }
  523. // Delete child and change root ID.
  524. // (1)
  525. {
  526. ui::AXTreeUpdate update;
  527. update.root_id = 1;
  528. update.node_id_to_clear = 7;
  529. update.nodes.resize(1);
  530. update.nodes[0].id = 1;
  531. update.nodes[0].role = ax::mojom::Role::kStaticText;
  532. update.nodes[0].SetName(kUpdate8Name);
  533. bridge->AccessibilityEventReceived(
  534. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  535. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  536. kUpdate8Name);
  537. EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 1u);
  538. }
  539. }
  540. // Verifies that offset container bookkeeping is updated correctly.
  541. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest,
  542. OffsetContainerBookkeepingIsUpdated) {
  543. // Loads a page, so a real frame is created for this test. Then, several tree
  544. // operations are applied on top of it, using the AXTreeID that corresponds to
  545. // that frame.
  546. LoadPage(kPage1Path, kPage1Title);
  547. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  548. // Fetch the AXTreeID of the main frame (the page just loaded). This ID will
  549. // be used in the operations that follow to simulate new data coming in.
  550. auto tree_id = frame_impl_->web_contents_for_test()
  551. ->GetPrimaryMainFrame()
  552. ->GetAXTreeID();
  553. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  554. size_t tree_size = 5;
  555. // The tree has the following form: (1 (2 (3 (4 (5)))))
  556. auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
  557. tree_accessibility_event.ax_tree_id = tree_id;
  558. // The root of this tree needs to be cleared (because it holds the page just
  559. // loaded, and we are loading something completely new).
  560. tree_accessibility_event.updates[0].node_id_to_clear =
  561. bridge->ax_tree_for_test()->root()->id();
  562. // Set a name in a node so we can wait for this node to appear. This pattern
  563. // is used throughout this test to ensure that the new data we are waiting for
  564. // arrived.
  565. tree_accessibility_event.updates[0].nodes[0].role =
  566. ax::mojom::Role::kStaticText;
  567. tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name);
  568. bridge->AccessibilityEventReceived(tree_accessibility_event);
  569. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  570. kUpdate1Name);
  571. // Adds a new node with ID 6.
  572. // (1 (2 (3 (4 (5 6)))))
  573. {
  574. ui::AXTreeUpdate update;
  575. update.root_id = 1;
  576. update.nodes.resize(2);
  577. update.nodes[0].id = 4;
  578. update.nodes[0].child_ids.push_back(5);
  579. update.nodes[0].child_ids.push_back(6);
  580. update.nodes[1].id = 6;
  581. update.nodes[1].relative_bounds.offset_container_id = 3;
  582. update.nodes[1].role = ax::mojom::Role::kStaticText;
  583. update.nodes[1].SetName(kUpdate2Name);
  584. bridge->AccessibilityEventReceived(
  585. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  586. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  587. kUpdate2Name);
  588. }
  589. // Then, verify that offset container bookkeeping was updated.
  590. {
  591. auto* tree = bridge->ax_tree_for_test();
  592. auto offset_container_children_it = bridge->offset_container_children_.find(
  593. std::make_pair(tree->GetAXTreeID(), 3));
  594. EXPECT_NE(offset_container_children_it,
  595. bridge->offset_container_children_.end());
  596. const auto& offset_children = offset_container_children_it->second;
  597. EXPECT_EQ(offset_children.size(), 1u);
  598. EXPECT_TRUE(offset_children.count(std::make_pair(tree->GetAXTreeID(), 6)));
  599. }
  600. // Now, change node 6's offset container to be node 4.
  601. {
  602. ui::AXTreeUpdate update;
  603. update.root_id = 1;
  604. update.nodes.resize(1);
  605. update.nodes[0].id = 6;
  606. update.nodes[0].relative_bounds.offset_container_id = 4;
  607. update.nodes[0].role = ax::mojom::Role::kStaticText;
  608. update.nodes[0].SetName(kUpdate3Name);
  609. bridge->AccessibilityEventReceived(
  610. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  611. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  612. kUpdate3Name);
  613. }
  614. // Then, verify that offset container bookkeeping was updated.
  615. {
  616. // Check that node 6 was deleted from node 3's offset children.
  617. auto* tree = bridge->ax_tree_for_test();
  618. auto offset_container_children_it = bridge->offset_container_children_.find(
  619. std::make_pair(tree->GetAXTreeID(), 3));
  620. EXPECT_NE(offset_container_children_it,
  621. bridge->offset_container_children_.end());
  622. const auto& offset_children = offset_container_children_it->second;
  623. EXPECT_TRUE(offset_children.empty());
  624. // Check that node 6 was added to node 4's offset children.
  625. auto new_offset_container_children_it =
  626. bridge->offset_container_children_.find(
  627. std::make_pair(tree->GetAXTreeID(), 4));
  628. EXPECT_NE(offset_container_children_it,
  629. bridge->offset_container_children_.end());
  630. const auto& new_offset_children = new_offset_container_children_it->second;
  631. EXPECT_EQ(new_offset_children.size(), 1u);
  632. EXPECT_TRUE(
  633. new_offset_children.count(std::make_pair(tree->GetAXTreeID(), 6)));
  634. }
  635. // Removes the added node 6.
  636. // (1 (2 (3 (4 (5)))))
  637. {
  638. ui::AXTreeUpdate update;
  639. update.root_id = 1;
  640. update.node_id_to_clear = 4;
  641. update.nodes.resize(2);
  642. update.nodes[0].id = 4;
  643. update.nodes[0].child_ids.push_back(5);
  644. update.nodes[1].id = 5;
  645. update.nodes[1].role = ax::mojom::Role::kStaticText;
  646. update.nodes[1].SetName(kUpdate4Name);
  647. bridge->AccessibilityEventReceived(
  648. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  649. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  650. kUpdate4Name);
  651. }
  652. // Verify that node 6 was removed as an offset child of node 4.
  653. {
  654. auto* tree = bridge->ax_tree_for_test();
  655. auto offset_container_children_it = bridge->offset_container_children_.find(
  656. std::make_pair(tree->GetAXTreeID(), 4));
  657. EXPECT_NE(offset_container_children_it,
  658. bridge->offset_container_children_.end());
  659. const auto& offset_children = offset_container_children_it->second;
  660. EXPECT_TRUE(offset_children.empty());
  661. }
  662. // Removes node 4.
  663. // (1 (2 (3 )))
  664. {
  665. ui::AXTreeUpdate update;
  666. update.root_id = 1;
  667. update.node_id_to_clear = 3;
  668. update.nodes.resize(1);
  669. update.nodes[0].id = 3;
  670. update.nodes[0].role = ax::mojom::Role::kStaticText;
  671. update.nodes[0].SetName(kUpdate5Name);
  672. bridge->AccessibilityEventReceived(
  673. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  674. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  675. kUpdate5Name);
  676. }
  677. // Verify that node 4 was cleared from the offset children map.
  678. {
  679. auto* tree = bridge->ax_tree_for_test();
  680. EXPECT_FALSE(bridge->offset_container_children_.count(
  681. std::make_pair(tree->GetAXTreeID(), 4)));
  682. }
  683. }
  684. // This test verifies that a node's transform includes a translation for its
  685. // offset container's bounds.
  686. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest,
  687. TransformAccountsForOffsetContainerBounds) {
  688. // Loads a page, so a real frame is created for this test. Then, several tree
  689. // operations are applied on top of it, using the AXTreeID that corresponds to
  690. // that frame.
  691. LoadPage(kPage1Path, kPage1Title);
  692. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  693. // Fetch the AXTreeID of the main frame (the page just loaded). This ID will
  694. // be used in the operations that follow to simulate new data coming in.
  695. auto tree_id = frame_impl_->web_contents_for_test()
  696. ->GetPrimaryMainFrame()
  697. ->GetAXTreeID();
  698. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  699. size_t tree_size = 5;
  700. // The tree has the following form: (1 (2 (3 (4 (5)))))
  701. auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
  702. tree_accessibility_event.ax_tree_id = tree_id;
  703. // The root of this tree needs to be cleared (because it holds the page just
  704. // loaded, and we are loading something completely new).
  705. tree_accessibility_event.updates[0].node_id_to_clear =
  706. bridge->ax_tree_for_test()->root()->id();
  707. // Set a name in a node so we can wait for this node to appear. This pattern
  708. // is used throughout this test to ensure that the new data we are waiting for
  709. // arrived.
  710. tree_accessibility_event.updates[0].nodes[0].role =
  711. ax::mojom::Role::kStaticText;
  712. tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name);
  713. bridge->AccessibilityEventReceived(tree_accessibility_event);
  714. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  715. kUpdate1Name);
  716. const char kNodeName[] = "transfrom should update";
  717. // Changes the bounds of node 1.
  718. // (1 (2 (3 (4 (5)))))
  719. ui::AXTreeUpdate update;
  720. update.root_id = 1;
  721. update.nodes.resize(2);
  722. update.nodes[0].id = 1;
  723. // Update the relative bounds of node 1, which is node 2's offset container.
  724. update.nodes[0].relative_bounds.bounds = gfx::RectF(2, 3, 4, 5);
  725. update.nodes[0].child_ids = {2};
  726. update.nodes[0].role = ax::mojom::Role::kStaticText;
  727. update.nodes[0].SetName(kUpdate2Name);
  728. update.nodes[1].id = 2;
  729. update.nodes[1].role = ax::mojom::Role::kStaticText;
  730. update.nodes[1].SetName(kNodeName);
  731. update.nodes[1].relative_bounds.offset_container_id = 1;
  732. // Node 2 should have non-trivial relative bounds to ensure that the
  733. // accessibility bridge correctly composes node 2's transform and the
  734. // translation for node 1's bounds.
  735. update.nodes[1].relative_bounds.bounds = gfx::RectF(10, 11, 10, 11);
  736. update.nodes[1].relative_bounds.transform = std::make_unique<gfx::Transform>(
  737. 5, 0, 0, 100, 0, 5, 0, 200, 0, 0, 5, 0, 0, 0, 0, 1);
  738. bridge->AccessibilityEventReceived(
  739. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  740. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  741. kUpdate2Name);
  742. auto* tree = bridge->ax_tree_for_test();
  743. auto* updated_node = tree->GetFromId(2);
  744. ASSERT_TRUE(updated_node);
  745. // Verify that the transform for the Fuchsia semantic node corresponding to
  746. // node 2 reflects the new bounds of node 1.
  747. fuchsia::accessibility::semantics::Node* fuchsia_node =
  748. semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName);
  749. ASSERT_TRUE(fuchsia_node);
  750. // A Fuchsia node's semantic transform should include an offset for its parent
  751. // node as a post-translation on top of its existing transform. Therefore, the
  752. // x, y, and z scale (indices 0, 5, and 10, respectively) should remain
  753. // unchanged, and the x and y bounds of the offset container should be added
  754. // to the node's existing translation entries (indices 12 and 13).
  755. EXPECT_EQ(fuchsia_node->transform().matrix[0], 5);
  756. EXPECT_EQ(fuchsia_node->transform().matrix[5], 5);
  757. EXPECT_EQ(fuchsia_node->transform().matrix[10], 5);
  758. EXPECT_EQ(fuchsia_node->transform().matrix[12], 102);
  759. EXPECT_EQ(fuchsia_node->transform().matrix[13], 203);
  760. }
  761. // This test verifies that a node's transform is updated correctly when its
  762. // container's relative bounds change.
  763. // NOTE: This test is distinct from the above test case.
  764. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest,
  765. UpdateTransformWhenContainerBoundsChange) {
  766. // Loads a page, so a real frame is created for this test. Then, several tree
  767. // operations are applied on top of it, using the AXTreeID that corresponds to
  768. // that frame.
  769. LoadPage(kPage1Path, kPage1Title);
  770. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  771. // Fetch the AXTreeID of the main frame (the page just loaded). This ID will
  772. // be used in the operations that follow to simulate new data coming in.
  773. auto tree_id = frame_impl_->web_contents_for_test()
  774. ->GetPrimaryMainFrame()
  775. ->GetAXTreeID();
  776. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  777. size_t tree_size = 5;
  778. // The tree has the following form: (1 (2 (3 (4 (5)))))
  779. auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
  780. tree_accessibility_event.ax_tree_id = tree_id;
  781. // The root of this tree needs to be cleared (because it holds the page just
  782. // loaded, and we are loading something completely new).
  783. tree_accessibility_event.updates[0].node_id_to_clear =
  784. bridge->ax_tree_for_test()->root()->id();
  785. // Set a name in a node so we can wait for this node to appear. This pattern
  786. // is used throughout this test to ensure that the new data we are waiting for
  787. // arrived.
  788. tree_accessibility_event.updates[0].nodes[0].role =
  789. ax::mojom::Role::kStaticText;
  790. tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name);
  791. bridge->AccessibilityEventReceived(tree_accessibility_event);
  792. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  793. kUpdate1Name);
  794. // Ensure that the accessibility bridge's offset container bookkeeping is up
  795. // to date.
  796. bridge->offset_container_children_[std::make_pair(tree_id, 1)].insert(
  797. std::make_pair(tree_id, 2));
  798. const char kNodeName[] = "transfrom should update";
  799. // Changes the bounds of node 1.
  800. // (1 (2 (3 (4 (5)))))
  801. ui::AXTreeUpdate update;
  802. update.root_id = 1;
  803. update.nodes.resize(2);
  804. update.nodes[0].id = 1;
  805. // Update the relative bounds of node 1, which is node 2's offset container.
  806. update.nodes[0].relative_bounds.bounds = gfx::RectF(2, 3, 4, 5);
  807. update.nodes[0].child_ids = {2};
  808. update.nodes[0].role = ax::mojom::Role::kStaticText;
  809. update.nodes[0].SetName(kUpdate2Name);
  810. update.nodes[1].id = 2;
  811. update.nodes[1].role = ax::mojom::Role::kStaticText;
  812. update.nodes[1].SetName(kNodeName);
  813. bridge->AccessibilityEventReceived(
  814. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  815. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  816. kUpdate2Name);
  817. // Verify that the transform for the Fuchsia semantic node corresponding to
  818. // node 2 reflects the new bounds of node 1.
  819. fuchsia::accessibility::semantics::Node* fuchsia_node =
  820. semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName);
  821. // A Fuchsia node's semantic transform should include an offset for its parent
  822. // node as a post-translation on top of its existing transform. Therefore, the
  823. // x, y, and z scale (indices 0, 5, and 10, respectively) should remain
  824. // unchanged, and the x and y bounds of the offset container should be added
  825. // to the node's existing translation entries (indices 12 and 13).
  826. EXPECT_EQ(fuchsia_node->transform().matrix[12], 2);
  827. EXPECT_EQ(fuchsia_node->transform().matrix[13], 3);
  828. }
  829. // This test ensures that fuchsia only receives one update per node.
  830. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, OneUpdatePerNode) {
  831. // Loads a page, so a real frame is created for this test. Then, several tree
  832. // operations are applied on top of it, using the AXTreeID that corresponds to
  833. // that frame.
  834. LoadPage(kPage1Path, kPage1Title);
  835. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  836. // Fetch the AXTreeID of the main frame (the page just loaded). This ID will
  837. // be used in the operations that follow to simulate new data coming in.
  838. auto tree_id = frame_impl_->web_contents_for_test()
  839. ->GetPrimaryMainFrame()
  840. ->GetAXTreeID();
  841. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  842. size_t tree_size = 5;
  843. // The tree has the following form: (1 (2 (3 (4 (5)))))
  844. auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size);
  845. tree_accessibility_event.ax_tree_id = tree_id;
  846. // The root of this tree needs to be cleared (because it holds the page just
  847. // loaded, and we are loading something completely new).
  848. tree_accessibility_event.updates[0].node_id_to_clear =
  849. bridge->ax_tree_for_test()->root()->id();
  850. // Set a name in a node so we can wait for this node to appear. This pattern
  851. // is used throughout this test to ensure that the new data we are waiting for
  852. // arrived.
  853. tree_accessibility_event.updates[0].nodes[0].role =
  854. ax::mojom::Role::kStaticText;
  855. tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name);
  856. bridge->AccessibilityEventReceived(tree_accessibility_event);
  857. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  858. kUpdate1Name);
  859. // Mark node 2 as node 3's offset container. Below, we will send an update to
  860. // change node 3's offset container to node 1, so that we can verify that the
  861. // fuchsia node produced reflects that update.
  862. bridge->offset_container_children_[std::make_pair(tree_id, 2)].insert(
  863. std::make_pair(tree_id, 3));
  864. // Send three updates:
  865. // 1. Change bounds for node 1.
  866. // 2. Change bounds for node 2. Since node 3 is marked as node 2's offset
  867. // child (above), OnNodeDataChanged() should produce an update for node 3 that
  868. // includes a transform accounting for node 2's new bounds.
  869. // 3. Change offset container for node 3 from node 2 to node 1.
  870. // OnAtomicUpdateFinished() should replace the now-incorrect update from step
  871. // (2) with a new update that includes a transform accounting for node 1's
  872. // bounds.
  873. const char kNodeName[] = "transform should update";
  874. // Changes the bounds of node 1.
  875. // (1 (2 (3 (4 (5)))))
  876. ui::AXTreeUpdate update;
  877. update.root_id = 1;
  878. update.nodes.resize(3);
  879. update.nodes[0].id = 1;
  880. // Update the relative bounds of node 1, which is node 2's offset container.
  881. auto new_root_bounds = gfx::RectF(2, 3, 4, 5);
  882. update.nodes[0].relative_bounds.bounds = new_root_bounds;
  883. update.nodes[0].child_ids = {2};
  884. update.nodes[0].role = ax::mojom::Role::kStaticText;
  885. update.nodes[0].SetName(kUpdate2Name);
  886. update.nodes[1].id = 2;
  887. update.nodes[1].relative_bounds.bounds = gfx::RectF(20, 30, 40, 50);
  888. update.nodes[1].child_ids = {3};
  889. update.nodes[2].id = 3;
  890. update.nodes[2].relative_bounds.offset_container_id = 1u;
  891. update.nodes[2].role = ax::mojom::Role::kStaticText;
  892. update.nodes[2].SetName(kNodeName);
  893. bridge->AccessibilityEventReceived(
  894. CreateAccessibilityEventWithUpdate(std::move(update), tree_id));
  895. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(kNodeName);
  896. // Verify that the transform for the Fuchsia semantic node corresponding to
  897. // node 3 reflects the new bounds of node 1.
  898. fuchsia::accessibility::semantics::Node* fuchsia_node =
  899. semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName);
  900. // A Fuchsia node's semantic transform should include an offset for its parent
  901. // node as a post-translation on top of its existing transform. Therefore, the
  902. // x, y, and z scale (indices 0, 5, and 10, respectively) should remain
  903. // unchanged, and the x and y bounds of the offset container should be added
  904. // to the node's existing translation entries (indices 12 and 13).
  905. EXPECT_EQ(fuchsia_node->transform().matrix[12], new_root_bounds.x());
  906. EXPECT_EQ(fuchsia_node->transform().matrix[13], new_root_bounds.y());
  907. }
  908. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, OutOfProcessIframe) {
  909. constexpr int64_t kBindingsId = 1234;
  910. // Start a different embedded test server, and load a page on it. The URL for
  911. // this page will have a different port and be considered out of process when
  912. // used as the src for an iframe.
  913. net::EmbeddedTestServer second_test_server;
  914. second_test_server.ServeFilesFromSourceDirectory(
  915. base::FilePath(kTestServerRoot));
  916. ASSERT_TRUE(second_test_server.Start());
  917. GURL out_of_process_url = second_test_server.GetURL(kPage1Path);
  918. // Before loading a page on the default embedded test server, set the iframe
  919. // src to be |out_of_process_url|.
  920. frame_->AddBeforeLoadJavaScript(
  921. kBindingsId, {"*"},
  922. base::MemBufferFromString(
  923. base::StringPrintf("iframeSrc = '%s'",
  924. out_of_process_url.spec().c_str()),
  925. "test"),
  926. [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
  927. EXPECT_TRUE(result.is_response());
  928. });
  929. LoadPage(kPageIframePath, "iframe loaded");
  930. // Run until the title of the iframe page is in the semantic tree. Because
  931. // the iframe's semantic tree is only sent when it is connected to the parent
  932. // tree, it is guaranteed that both trees will be present.
  933. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  934. kPage1Title);
  935. // Two frames should be present.
  936. int num_frames = CollectAllRenderFrameHosts(
  937. frame_impl_->web_contents_for_test()->GetPrimaryPage())
  938. .size();
  939. EXPECT_EQ(num_frames, 2);
  940. // Check that the iframe node has been loaded.
  941. EXPECT_TRUE(
  942. semantics_manager_.semantic_tree()->GetNodeFromLabel(kPageIframeTitle));
  943. // Data that is part of the iframe should be in the semantic tree.
  944. EXPECT_TRUE(
  945. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
  946. // Makes the iframe navigate to a different page.
  947. GURL out_of_process_url_2 = second_test_server.GetURL(kPage2Path);
  948. const auto script =
  949. base::StringPrintf("document.getElementById(\"iframeId\").src = '%s'",
  950. out_of_process_url_2.spec().c_str());
  951. frame_->ExecuteJavaScript(
  952. {"*"}, base::MemBufferFromString(script, "test2"),
  953. [](fuchsia::web::Frame_ExecuteJavaScript_Result result) {
  954. EXPECT_TRUE(result.is_response());
  955. });
  956. semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree(
  957. kPage2Title);
  958. // check that the iframe navigated to a different page.
  959. EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName));
  960. // Old iframe data should be gone.
  961. EXPECT_FALSE(
  962. semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName1));
  963. // Makes the main page navigate to a different page, causing the iframe to go
  964. // away.
  965. LoadPage(kPage2Path, kPage2Title);
  966. // Wait for the root to be updated, which means that we navigated to a new
  967. // page.
  968. base::RunLoop run_loop;
  969. semantics_manager_.semantic_tree()->SetNodeUpdatedCallback(
  970. 0u, run_loop.QuitClosure());
  971. run_loop.Run();
  972. // We've navigated to a different page that has no iframes. Only one frame
  973. // should be present.
  974. num_frames = CollectAllRenderFrameHosts(
  975. frame_impl_->web_contents_for_test()->GetPrimaryPage())
  976. .size();
  977. EXPECT_EQ(num_frames, 1);
  978. }
  979. IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, UpdatesFocusInformation) {
  980. LoadPage(kPage1Path, kPage1Title);
  981. semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount);
  982. ASSERT_FALSE(semantics_manager_.semantic_tree()
  983. ->GetNodeWithId(0u)
  984. ->states()
  985. .has_has_input_focus());
  986. // Focus the root node.
  987. ui::AXActionData action_data;
  988. action_data.action = ax::mojom::Action::kFocus;
  989. AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test();
  990. action_data.target_tree_id = bridge->ax_tree_for_test()->GetAXTreeID();
  991. action_data.target_node_id = bridge->ax_tree_for_test()->root()->id();
  992. frame_impl_->web_contents_for_test()
  993. ->GetPrimaryMainFrame()
  994. ->AccessibilityPerformAction(action_data);
  995. FakeSemanticTree* semantic_tree = semantics_manager_.semantic_tree();
  996. semantic_tree->RunUntilConditionIsTrue(
  997. base::BindLambdaForTesting([semantic_tree]() {
  998. auto* node = semantic_tree->GetNodeWithId(0u);
  999. if (!node)
  1000. return false;
  1001. return node->has_states() && node->states().has_has_input_focus() &&
  1002. node->states().has_input_focus();
  1003. }));
  1004. ASSERT_TRUE(semantics_manager_.semantic_tree()
  1005. ->GetNodeWithId(0u)
  1006. ->states()
  1007. .has_input_focus());
  1008. // Changes the focus to a different node and checks that the old value is
  1009. // cleared.
  1010. auto new_focus_id = semantics_manager_.semantic_tree()
  1011. ->GetNodeFromLabel(kButtonName1)
  1012. ->node_id();
  1013. action_data.target_node_id =
  1014. bridge->node_id_mapper_for_test()->ToAXNodeID(new_focus_id)->second;
  1015. frame_impl_->web_contents_for_test()
  1016. ->GetPrimaryMainFrame()
  1017. ->AccessibilityPerformAction(action_data);
  1018. semantic_tree->RunUntilConditionIsTrue(
  1019. base::BindLambdaForTesting([semantic_tree, new_focus_id]() {
  1020. auto* root = semantic_tree->GetNodeWithId(0u);
  1021. auto* node = semantic_tree->GetNodeWithId(new_focus_id);
  1022. if (!node || !root)
  1023. return false;
  1024. // Node has the focus, root does not.
  1025. return (node->has_states() && node->states().has_has_input_focus() &&
  1026. node->states().has_input_focus()) &&
  1027. (root->has_states() && root->states().has_has_input_focus() &&
  1028. !root->states().has_input_focus());
  1029. }));
  1030. ASSERT_FALSE(semantics_manager_.semantic_tree()
  1031. ->GetNodeWithId(0u)
  1032. ->states()
  1033. .has_input_focus());
  1034. ASSERT_TRUE(semantics_manager_.semantic_tree()
  1035. ->GetNodeWithId(new_focus_id)
  1036. ->states()
  1037. .has_input_focus());
  1038. }