| Flutter iOS Embedder |

SemanticsObjectTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
7
8 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
9 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
10 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSemanticsScrollView.h"
11 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h"
12 #import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
13 #import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h"
14 #import "flutter/shell/platform/darwin/ios/framework/Source/TextInputSemanticsObject.h"
15
16 FLUTTER_ASSERT_ARC
17
18 const float kFloatCompareEpsilon = 0.001;
19
20 @interface SemanticsObject (UIFocusSystem) <UIFocusItem, UIFocusItemContainer>
21 @end
22
23 @interface FlutterScrollableSemanticsObject (UIFocusItemScrollableContainer) <
24 UIFocusItemScrollableContainer>
25 @end
26
27 @interface TextInputSemanticsObject (Test)
28 - (UIView<UITextInput>*)textInputSurrogate;
29 @end
30
31 @interface SemanticsObjectTest : XCTestCase
32 @end
33
34 @implementation SemanticsObjectTest
35
36 - (void)testCreate {
37 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
38 new flutter::testing::MockAccessibilityBridge());
39 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
40 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
41 XCTAssertNotNil(object);
42 }
43
44 - (void)testSetChildren {
45 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
46 new flutter::testing::MockAccessibilityBridge());
47 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
48 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
49 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
50 parent.children = @[ child ];
51 XCTAssertEqual(parent, child.parent);
52 parent.children = @[];
53 XCTAssertNil(child.parent);
54 }
55
56 - (void)testAccessibilityHitTestFocusAtLeaf {
57 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
58 new flutter::testing::MockAccessibilityBridge());
59 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
60 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
61 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
62 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
63 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
64 object0.children = @[ object1 ];
65 object0.childrenInHitTestOrder = @[ object1 ];
66 object1.children = @[ object2, object3 ];
67 object1.childrenInHitTestOrder = @[ object2, object3 ];
68
69 flutter::SemanticsNode node0;
70 node0.id = 0;
71 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
72 node0.label = "0";
73 [object0 setSemanticsNode:&node0];
74
75 flutter::SemanticsNode node1;
76 node1.id = 1;
77 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
78 node1.label = "1";
79 [object1 setSemanticsNode:&node1];
80
81 flutter::SemanticsNode node2;
82 node2.id = 2;
83 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
84 node2.label = "2";
85 [object2 setSemanticsNode:&node2];
86
87 flutter::SemanticsNode node3;
88 node3.id = 3;
89 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
90 node3.label = "3";
91 [object3 setSemanticsNode:&node3];
92
93 CGPoint point = CGPointMake(10, 10);
94 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
95
96 // Focus to object2 because it's the first object in hit test order
97 XCTAssertEqual(hitTestResult, object2);
98 }
99
100 - (void)testAccessibilityHitTestNoFocusableItem {
101 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
102 new flutter::testing::MockAccessibilityBridge());
103 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
104 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
105 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
106 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
107 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
108 object0.children = @[ object1 ];
109 object0.childrenInHitTestOrder = @[ object1 ];
110 object1.children = @[ object2, object3 ];
111 object1.childrenInHitTestOrder = @[ object2, object3 ];
112
113 flutter::SemanticsNode node0;
114 node0.id = 0;
115 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
116 [object0 setSemanticsNode:&node0];
117
118 flutter::SemanticsNode node1;
119 node1.id = 1;
120 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
121 [object1 setSemanticsNode:&node1];
122
123 flutter::SemanticsNode node2;
124 node2.id = 2;
125 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
126 [object2 setSemanticsNode:&node2];
127
128 flutter::SemanticsNode node3;
129 node3.id = 3;
130 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
131 [object3 setSemanticsNode:&node3];
132
133 CGPoint point = CGPointMake(10, 10);
134 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
135
136 XCTAssertNil(hitTestResult);
137 }
138
139 - (void)testAccessibilityScrollToVisible {
140 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
141 new flutter::testing::MockAccessibilityBridge());
142 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
143 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
144
145 flutter::SemanticsNode node3;
146 node3.id = 3;
147 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
148 [object3 setSemanticsNode:&node3];
149
150 [object3 accessibilityScrollToVisible];
151
152 XCTAssertTrue(bridge->observations.size() == 1);
153 XCTAssertTrue(bridge->observations[0].id == 3);
154 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
155 }
156
157 - (void)testAccessibilityScrollToVisibleWithChild {
158 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
159 new flutter::testing::MockAccessibilityBridge());
160 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
161 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
162
163 flutter::SemanticsNode node3;
164 node3.id = 3;
165 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
166 [object3 setSemanticsNode:&node3];
167
168 [object3 accessibilityScrollToVisibleWithChild:object3];
169
170 XCTAssertTrue(bridge->observations.size() == 1);
171 XCTAssertTrue(bridge->observations[0].id == 3);
172 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
173 }
174
175 - (void)testAccessibilityHitTestOutOfRect {
176 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
177 new flutter::testing::MockAccessibilityBridge());
178 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
179 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
180 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
181 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
182 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
183 object0.children = @[ object1 ];
184 object0.childrenInHitTestOrder = @[ object1 ];
185 object1.children = @[ object2, object3 ];
186 object1.childrenInHitTestOrder = @[ object2, object3 ];
187
188 flutter::SemanticsNode node0;
189 node0.id = 0;
190 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
191 node0.label = "0";
192 [object0 setSemanticsNode:&node0];
193
194 flutter::SemanticsNode node1;
195 node1.id = 1;
196 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
197 node1.label = "1";
198 [object1 setSemanticsNode:&node1];
199
200 flutter::SemanticsNode node2;
201 node2.id = 2;
202 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
203 node2.label = "2";
204 [object2 setSemanticsNode:&node2];
205
206 flutter::SemanticsNode node3;
207 node3.id = 3;
208 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
209 node3.label = "3";
210 [object3 setSemanticsNode:&node3];
211
212 CGPoint point = CGPointMake(300, 300);
213 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
214
215 XCTAssertNil(hitTestResult);
216 }
217
218 - (void)testReplaceChildAtIndex {
219 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
220 new flutter::testing::MockAccessibilityBridge());
221 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
222 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
223 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
224 SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
225 parent.children = @[ child1 ];
226 [parent replaceChildAtIndex:0 withChild:child2];
227 XCTAssertNil(child1.parent);
228 XCTAssertEqual(parent, child2.parent);
229 XCTAssertEqualObjects(parent.children, @[ child2 ]);
230 }
231
232 - (void)testPlainSemanticsObjectWithLabelHasStaticTextTrait {
233 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
234 new flutter::testing::MockAccessibilityBridge());
235 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
236 flutter::SemanticsNode node;
237 node.label = "foo";
238 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
239 [object setSemanticsNode:&node];
240 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
241 }
242
243 - (void)testNodeWithImplicitScrollIsAnAccessibilityElementWhenItisHidden {
244 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
245 new flutter::testing::MockAccessibilityBridge());
246 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
247 flutter::SemanticsNode node;
248
249 node.flags.hasImplicitScrolling = true;
250 node.flags.isHidden = true;
251 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
252 [object setSemanticsNode:&node];
253 XCTAssertEqual(object.isAccessibilityElement, YES);
254 }
255
256 - (void)testNodeWithImplicitScrollIsNotAnAccessibilityElementWhenItisNotHidden {
257 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
258 new flutter::testing::MockAccessibilityBridge());
259 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
260 flutter::SemanticsNode node;
261 node.flags.hasImplicitScrolling = true;
262 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
263 [object setSemanticsNode:&node];
264 XCTAssertEqual(object.isAccessibilityElement, NO);
265 }
266
267 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait {
268 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
269 new flutter::testing::MockAccessibilityBridge());
270 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
271 flutter::SemanticsNode node;
272 node.label = "foo";
273 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
274 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
275 object.children = @[ child1 ];
276 [object setSemanticsNode:&node];
277 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
278 }
279
280 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1 {
281 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
282 new flutter::testing::MockAccessibilityBridge());
283 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
284 flutter::SemanticsNode node;
285 node.label = "foo";
286 node.flags.isTextField = true;
287 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
288 [object setSemanticsNode:&node];
289 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
290 }
291
292 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2 {
293 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
294 new flutter::testing::MockAccessibilityBridge());
295 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
296 flutter::SemanticsNode node;
297 node.label = "foo";
298
299 node.flags.isButton = true;
300 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
301 [object setSemanticsNode:&node];
302 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
303 }
304
305 - (void)testVerticalFlutterScrollableSemanticsObject {
306 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
307 new flutter::testing::MockAccessibilityBridge());
308 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
309
310 float transformScale = 0.5f;
311 float screenScale = [[bridge->view() window] screen].scale;
312 float effectivelyScale = transformScale / screenScale;
313 float x = 10;
314 float y = 10;
315 float w = 100;
316 float h = 200;
317 float scrollExtentMax = 500.0;
318 float scrollPosition = 150.0;
319
320 flutter::SemanticsNode node;
321 node.flags.hasImplicitScrolling = true;
322 node.actions = flutter::kVerticalScrollSemanticsActions;
323 node.rect = SkRect::MakeXYWH(x, y, w, h);
324 node.scrollExtentMax = scrollExtentMax;
325 node.scrollPosition = scrollPosition;
326 node.transform = {
327 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
328 FlutterScrollableSemanticsObject* scrollable =
329 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
330 [scrollable setSemanticsNode:&node];
331 [scrollable accessibilityBridgeDidFinishUpdate];
332 UIScrollView* scrollView = [scrollable nativeAccessibility];
333
334 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
335 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
336 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
337 kFloatCompareEpsilon);
338 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
339 kFloatCompareEpsilon);
340
341 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
342 kFloatCompareEpsilon);
343 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
344 (h + scrollExtentMax) * effectivelyScale, kFloatCompareEpsilon);
345
346 XCTAssertEqual(scrollView.contentOffset.x, 0);
347 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
348 kFloatCompareEpsilon);
349 }
350
351 - (void)testVerticalFlutterScrollableSemanticsObjectNoWindowDoesNotCrash {
352 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
353 new flutter::testing::MockAccessibilityBridgeNoWindow());
354 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
355
356 float transformScale = 0.5f;
357 float x = 10;
358 float y = 10;
359 float w = 100;
360 float h = 200;
361 float scrollExtentMax = 500.0;
362 float scrollPosition = 150.0;
363
364 flutter::SemanticsNode node;
365 node.flags.hasImplicitScrolling = true;
366 node.actions = flutter::kVerticalScrollSemanticsActions;
367 node.rect = SkRect::MakeXYWH(x, y, w, h);
368 node.scrollExtentMax = scrollExtentMax;
369 node.scrollPosition = scrollPosition;
370 node.transform = {
371 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
372 FlutterScrollableSemanticsObject* scrollable =
373 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
374 [scrollable setSemanticsNode:&node];
375 [scrollable accessibilityBridgeDidFinishUpdate];
376 XCTAssertNoThrow([scrollable accessibilityBridgeDidFinishUpdate]);
377 }
378
379 - (void)testHorizontalFlutterScrollableSemanticsObject {
380 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
381 new flutter::testing::MockAccessibilityBridge());
382 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
383
384 float transformScale = 0.5f;
385 float screenScale = [[bridge->view() window] screen].scale;
386 float effectivelyScale = transformScale / screenScale;
387 float x = 10;
388 float y = 10;
389 float w = 100;
390 float h = 200;
391 float scrollExtentMax = 500.0;
392 float scrollPosition = 150.0;
393
394 flutter::SemanticsNode node;
395 node.flags.hasImplicitScrolling = true;
396 node.actions = flutter::kHorizontalScrollSemanticsActions;
397 node.rect = SkRect::MakeXYWH(x, y, w, h);
398 node.scrollExtentMax = scrollExtentMax;
399 node.scrollPosition = scrollPosition;
400 node.transform = {
401 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
402 FlutterScrollableSemanticsObject* scrollable =
403 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
404 [scrollable setSemanticsNode:&node];
405 [scrollable accessibilityBridgeDidFinishUpdate];
406 UIScrollView* scrollView = [scrollable nativeAccessibility];
407
408 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
409 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
410 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
411 kFloatCompareEpsilon);
412 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
413 kFloatCompareEpsilon);
414
415 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, (w + scrollExtentMax) * effectivelyScale,
416 kFloatCompareEpsilon);
417 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
418 kFloatCompareEpsilon);
419
420 XCTAssertEqualWithAccuracy(scrollView.contentOffset.x, scrollPosition * effectivelyScale,
421 kFloatCompareEpsilon);
422 XCTAssertEqual(scrollView.contentOffset.y, 0);
423 }
424
425 - (void)testCanHandleInfiniteScrollExtent {
426 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
427 new flutter::testing::MockAccessibilityBridge());
428 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
429
430 float transformScale = 0.5f;
431 float screenScale = [[bridge->view() window] screen].scale;
432 float effectivelyScale = transformScale / screenScale;
433 float x = 10;
434 float y = 10;
435 float w = 100;
436 float h = 200;
437 float scrollExtentMax = INFINITY;
438 float scrollPosition = 150.0;
439
440 flutter::SemanticsNode node;
441 node.flags.hasImplicitScrolling = true;
442 node.actions = flutter::kVerticalScrollSemanticsActions;
443 node.rect = SkRect::MakeXYWH(x, y, w, h);
444 node.scrollExtentMax = scrollExtentMax;
445 node.scrollPosition = scrollPosition;
446 node.transform = {
447 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
448 FlutterScrollableSemanticsObject* scrollable =
449 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
450 [scrollable setSemanticsNode:&node];
451 [scrollable accessibilityBridgeDidFinishUpdate];
452 UIScrollView* scrollView = [scrollable nativeAccessibility];
453 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
454 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
455 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
456 kFloatCompareEpsilon);
457 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
458 kFloatCompareEpsilon);
459
460 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
461 kFloatCompareEpsilon);
462 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
463 (h + kScrollExtentMaxForInf + scrollPosition) * effectivelyScale,
464 kFloatCompareEpsilon);
465
466 XCTAssertEqual(scrollView.contentOffset.x, 0);
467 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
468 kFloatCompareEpsilon);
469 }
470
471 - (void)testCanHandleNaNScrollExtentAndScrollPoisition {
472 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
473 new flutter::testing::MockAccessibilityBridge());
474 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
475
476 float transformScale = 0.5f;
477 float screenScale = [[bridge->view() window] screen].scale;
478 float effectivelyScale = transformScale / screenScale;
479 float x = 10;
480 float y = 10;
481 float w = 100;
482 float h = 200;
483 float scrollExtentMax = std::nan("");
484 float scrollPosition = std::nan("");
485
486 flutter::SemanticsNode node;
487 node.flags.hasImplicitScrolling = true;
488 node.actions = flutter::kVerticalScrollSemanticsActions;
489 node.rect = SkRect::MakeXYWH(x, y, w, h);
490 node.scrollExtentMax = scrollExtentMax;
491 node.scrollPosition = scrollPosition;
492 node.transform = {
493 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
494 FlutterScrollableSemanticsObject* scrollable =
495 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
496 [scrollable setSemanticsNode:&node];
497 [scrollable accessibilityBridgeDidFinishUpdate];
498 UIScrollView* scrollView = [scrollable nativeAccessibility];
499
500 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
501 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
502 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
503 kFloatCompareEpsilon);
504 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
505 kFloatCompareEpsilon);
506
507 // Content size equal to the scrollable size.
508 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
509 kFloatCompareEpsilon);
510 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
511 kFloatCompareEpsilon);
512
513 XCTAssertEqual(scrollView.contentOffset.x, 0);
514 XCTAssertEqual(scrollView.contentOffset.y, 0);
515 }
516
517 - (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
518 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
519 new flutter::testing::MockAccessibilityBridge());
520 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
521
522 flutter::SemanticsNode node;
523 node.flags.hasImplicitScrolling = true;
524 node.actions = flutter::kHorizontalScrollSemanticsActions;
525 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
526 node.scrollExtentMax = 100.0;
527 node.scrollPosition = 0.0;
528
529 FlutterScrollableSemanticsObject* scrollable =
530 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
531 [scrollable setSemanticsNode:&node];
532 [scrollable accessibilityBridgeDidFinishUpdate];
533 UIScrollView* scrollView = [scrollable nativeAccessibility];
534 XCTAssertEqual([scrollView hitTest:CGPointMake(10, 10) withEvent:nil], nil);
535 }
536
537 - (void)testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning {
538 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
539 mock->isVoiceOverRunningValue = false;
540 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
541 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
542
543 flutter::SemanticsNode node;
544 node.flags.hasImplicitScrolling = true;
545 node.actions = flutter::kHorizontalScrollSemanticsActions;
546 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
547 node.scrollExtentMax = 100.0;
548 node.scrollPosition = 0.0;
549
550 FlutterScrollableSemanticsObject* scrollable =
551 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
552 [scrollable setSemanticsNode:&node];
553 [scrollable accessibilityBridgeDidFinishUpdate];
554 UIScrollView* scrollView = [scrollable nativeAccessibility];
555 XCTAssertTrue(scrollView.isAccessibilityElement);
556 mock->isVoiceOverRunningValue = true;
557 XCTAssertFalse(scrollView.isAccessibilityElement);
558 }
559
560 - (void)testFlutterSemanticsObjectHasIdentifier {
561 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
562 mock->isVoiceOverRunningValue = true;
563 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
564 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
565
566 flutter::SemanticsNode node;
567 node.identifier = "identifier";
568
569 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
570 [object setSemanticsNode:&node];
571 XCTAssertTrue([object.accessibilityIdentifier isEqualToString:@"identifier"]);
572 }
573
574 - (void)testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning {
575 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
576 mock->isVoiceOverRunningValue = true;
577 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
578 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
579
580 flutter::SemanticsNode node;
581 node.flags.hasImplicitScrolling = true;
582 node.actions = flutter::kHorizontalScrollSemanticsActions;
583 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
584 node.label = "label";
585 node.value = "value";
586 node.hint = "hint";
587 node.scrollExtentMax = 100.0;
588 node.scrollPosition = 0.0;
589
590 FlutterScrollableSemanticsObject* scrollable =
591 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
592 [scrollable setSemanticsNode:&node];
593 [scrollable accessibilityBridgeDidFinishUpdate];
594 UIScrollView* scrollView = [scrollable nativeAccessibility];
595 XCTAssertTrue(scrollView.isAccessibilityElement);
596 XCTAssertTrue(
597 [scrollView.accessibilityLabel isEqualToString:NSLocalizedString(@"label", @"test")]);
598 XCTAssertTrue(
599 [scrollView.accessibilityValue isEqualToString:NSLocalizedString(@"value", @"test")]);
600 XCTAssertTrue([scrollView.accessibilityHint isEqualToString:NSLocalizedString(@"hint", @"test")]);
601 }
602
603 - (void)testFlutterSemanticsObjectMergeTooltipToLabel {
604 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
605 mock->isVoiceOverRunningValue = true;
606 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
607 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
608
609 flutter::SemanticsNode node;
610 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
611 node.label = "label";
612 node.tooltip = "tooltip";
613 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
614 [object setSemanticsNode:&node];
615 XCTAssertTrue(object.isAccessibilityElement);
616 XCTAssertTrue([object.accessibilityLabel isEqualToString:@"label\ntooltip"]);
617 }
618
619 - (void)testSemanticsObjectContainerAccessibilityFrameCoversEntireScreen {
620 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
621 mock->isVoiceOverRunningValue = true;
622 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
623 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
624
625 flutter::SemanticsNode parent;
626 parent.id = 0;
627 parent.actions = static_cast<int32_t>(flutter::SemanticsAction::kTap);
628
629 flutter::SemanticsNode child;
630 child.id = 1;
631 child.actions = static_cast<int32_t>(flutter::SemanticsAction::kTap);
632 child.rect = SkRect::MakeXYWH(0, 0, 100, 100);
633 parent.childrenInTraversalOrder.push_back(1);
634
635 FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
636 uid:0];
637 [parentObject setSemanticsNode:&parent];
638
639 FlutterSemanticsObject* childObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
640 uid:1];
641 [childObject setSemanticsNode:&child];
642
643 parentObject.children = @[ childObject ];
644 [parentObject accessibilityBridgeDidFinishUpdate];
645 [childObject accessibilityBridgeDidFinishUpdate];
646
647 SemanticsObjectContainer* container =
648 static_cast<SemanticsObjectContainer*>(parentObject.accessibilityContainer);
649
650 XCTAssertTrue(childObject.accessibilityRespondsToUserInteraction);
651 XCTAssertTrue(CGRectEqualToRect(container.accessibilityFrame, UIScreen.mainScreen.bounds));
652 }
653
654 - (void)testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty {
655 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
656 mock->isVoiceOverRunningValue = true;
657 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
658 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
659
660 flutter::SemanticsNode node;
661 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
662 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
663 [object setSemanticsNode:&node];
664 XCTAssertTrue(object.accessibilityAttributedLabel == nil);
665 }
666
667 - (void)testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren {
668 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
669 mock->isVoiceOverRunningValue = true;
670 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
671 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
672
673 flutter::SemanticsNode parent;
674 parent.id = 0;
675 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
676 parent.label = "label";
677 parent.value = "value";
678 parent.hint = "hint";
679
680 flutter::SemanticsNode node;
681 node.id = 1;
682 node.flags.hasImplicitScrolling = true;
683 node.actions = flutter::kHorizontalScrollSemanticsActions;
684 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
685 node.label = "label";
686 node.value = "value";
687 node.hint = "hint";
688 node.scrollExtentMax = 100.0;
689 node.scrollPosition = 0.0;
690 parent.childrenInTraversalOrder.push_back(1);
691
692 FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
693 uid:0];
694 [parentObject setSemanticsNode:&parent];
695
696 FlutterScrollableSemanticsObject* scrollable =
697 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
698 [scrollable setSemanticsNode:&node];
699 UIScrollView* scrollView = [scrollable nativeAccessibility];
700
701 parentObject.children = @[ scrollable ];
702 [parentObject accessibilityBridgeDidFinishUpdate];
703 [scrollable accessibilityBridgeDidFinishUpdate];
704 XCTAssertTrue(scrollView.isAccessibilityElement);
705 SemanticsObjectContainer* container =
706 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
707 XCTAssertEqual(container.semanticsObject, parentObject);
708 }
709
710 - (void)testFlutterScrollableSemanticsObjectNoScrollBarOrContentInsets {
711 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
712 new flutter::testing::MockAccessibilityBridge());
713 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
714
715 flutter::SemanticsNode node;
716 node.flags.hasImplicitScrolling = true;
717 node.actions = flutter::kHorizontalScrollSemanticsActions;
718 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
719 node.scrollExtentMax = 100.0;
720 node.scrollPosition = 0.0;
721
722 FlutterScrollableSemanticsObject* scrollable =
723 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
724 [scrollable setSemanticsNode:&node];
725 [scrollable accessibilityBridgeDidFinishUpdate];
726 UIScrollView* scrollView = [scrollable nativeAccessibility];
727
728 XCTAssertFalse(scrollView.showsHorizontalScrollIndicator);
729 XCTAssertFalse(scrollView.showsVerticalScrollIndicator);
730 XCTAssertEqual(scrollView.contentInsetAdjustmentBehavior,
731 UIScrollViewContentInsetAdjustmentNever);
732 XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, UIEdgeInsetsZero));
733 }
734
735 - (void)testSemanticsObjectBuildsAttributedString {
736 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
737 new flutter::testing::MockAccessibilityBridge());
738 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
739 flutter::SemanticsNode node;
740 node.label = "label";
741 std::shared_ptr<flutter::SpellOutStringAttribute> attribute =
742 std::make_shared<flutter::SpellOutStringAttribute>();
743 attribute->start = 1;
744 attribute->end = 2;
745 attribute->type = flutter::StringAttributeType::kSpellOut;
746 node.labelAttributes.push_back(attribute);
747 node.value = "value";
748 attribute = std::make_shared<flutter::SpellOutStringAttribute>();
749 attribute->start = 2;
750 attribute->end = 3;
751 attribute->type = flutter::StringAttributeType::kSpellOut;
752 node.valueAttributes.push_back(attribute);
753 node.hint = "hint";
754 std::shared_ptr<flutter::LocaleStringAttribute> local_attribute =
755 std::make_shared<flutter::LocaleStringAttribute>();
756 local_attribute->start = 3;
757 local_attribute->end = 4;
758 local_attribute->type = flutter::StringAttributeType::kLocale;
759 local_attribute->locale = "en-MX";
760 node.hintAttributes.push_back(local_attribute);
761 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
762 [object setSemanticsNode:&node];
763 NSMutableAttributedString* expectedAttributedLabel =
764 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"label", @"test")];
765 NSDictionary* attributeDict = @{
766 UIAccessibilitySpeechAttributeSpellOut : @YES,
767 };
768 [expectedAttributedLabel setAttributes:attributeDict range:NSMakeRange(1, 1)];
769 XCTAssertTrue(
770 [object.accessibilityAttributedLabel isEqualToAttributedString:expectedAttributedLabel]);
771
772 NSMutableAttributedString* expectedAttributedValue =
773 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"value", @"test")];
774 attributeDict = @{
775 UIAccessibilitySpeechAttributeSpellOut : @YES,
776 };
777 [expectedAttributedValue setAttributes:attributeDict range:NSMakeRange(2, 1)];
778 XCTAssertTrue(
779 [object.accessibilityAttributedValue isEqualToAttributedString:expectedAttributedValue]);
780
781 NSMutableAttributedString* expectedAttributedHint =
782 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"hint", @"test")];
783 attributeDict = @{
784 UIAccessibilitySpeechAttributeLanguage : @"en-MX",
785 };
786 [expectedAttributedHint setAttributes:attributeDict range:NSMakeRange(3, 1)];
787 XCTAssertTrue(
788 [object.accessibilityAttributedHint isEqualToAttributedString:expectedAttributedHint]);
789 }
790
791 - (void)testShouldTriggerAnnouncement {
792 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
793 new flutter::testing::MockAccessibilityBridge());
794 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
795 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
796
797 // Handle nil with no node set.
798 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
799
800 // Handle initial setting of node with liveRegion
801 flutter::SemanticsNode node;
802 node.flags.isLiveRegion = true;
803 node.label = "foo";
804 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
805
806 // Handle nil with node set.
807 [object setSemanticsNode:&node];
808 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
809
810 // Handle new node, still has live region, same label.
811 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&node]);
812
813 // Handle update node with new label, still has live region.
814 flutter::SemanticsNode updatedNode;
815 updatedNode.flags.isLiveRegion = true;
816 updatedNode.label = "bar";
817 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&updatedNode]);
818
819 // Handle dropping the live region flag.
820 updatedNode.flags = flutter::SemanticsFlags{};
821 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&updatedNode]);
822
823 // Handle adding the flag when the label has not changed.
824 updatedNode.label = "foo";
825 [object setSemanticsNode:&updatedNode];
826 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
827 }
828
829 - (void)testShouldDispatchShowOnScreenActionForHeader {
830 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
831 new flutter::testing::MockAccessibilityBridge());
832 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
833 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
834
835 // Handle initial setting of node with header.
836 flutter::SemanticsNode node;
837 node.flags.isHeader = true;
838 node.label = "foo";
839
840 [object setSemanticsNode:&node];
841
842 // Simulate accessibility focus.
843 [object accessibilityElementDidBecomeFocused];
844
845 XCTAssertTrue(bridge->observations.size() == 1);
846 XCTAssertTrue(bridge->observations[0].id == 1);
847 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
848 }
849
850 - (void)testShouldDispatchShowOnScreenActionForHidden {
851 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
852 new flutter::testing::MockAccessibilityBridge());
853 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
854 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
855
856 // Handle initial setting of node with hidden.
857 flutter::SemanticsNode node;
858 node.flags.isHidden = true;
859 node.label = "foo";
860
861 [object setSemanticsNode:&node];
862
863 // Simulate accessibility focus.
864 [object accessibilityElementDidBecomeFocused];
865
866 XCTAssertTrue(bridge->observations.size() == 1);
867 XCTAssertTrue(bridge->observations[0].id == 1);
868 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
869 }
870
871 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch {
872 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
873 new flutter::testing::MockAccessibilityBridge());
874 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
875 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
876 uid:1];
877
878 // Handle initial setting of node with header.
879 flutter::SemanticsNode node;
880 node.flags.hasToggledState = true;
881 node.flags.isToggled = true;
882 node.flags.isEnabled = true;
883 node.label = "foo";
884 [object setSemanticsNode:&node];
885 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
886 UISwitch* nativeSwitch = [[UISwitch alloc] init];
887 nativeSwitch.on = YES;
888
889 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
890 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
891
892 // Set the toggled to false;
893 flutter::SemanticsNode update;
894 update.flags.hasToggledState = true;
895 update.flags.isToggled = false;
896 update.flags.isEnabled = true;
897
898 update.label = "foo";
899 [object setSemanticsNode:&update];
900
901 nativeSwitch.on = NO;
902
903 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
904 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
905 }
906
907 - (void)testFlutterSemanticsObjectOfRadioButton {
908 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
909 new flutter::testing::MockAccessibilityBridge());
910 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
911 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
912
913 // Handle initial setting of node with header.
914 flutter::SemanticsNode node;
915 node.flags.isInMutuallyExclusiveGroup = true;
916 node.flags.hasCheckedState = true;
917 node.flags.hasEnabledState = true;
918 node.flags.isEnabled = true;
919 node.label = "foo";
920 [object setSemanticsNode:&node];
921 XCTAssertTrue((object.accessibilityTraits & UIAccessibilityTraitButton) > 0);
922 XCTAssertNil(object.accessibilityValue);
923 }
924
925 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitchDisabled {
926 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
927 new flutter::testing::MockAccessibilityBridge());
928 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
929 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
930 uid:1];
931
932 // Handle initial setting of node with header.
933 flutter::SemanticsNode node;
934 node.flags.hasToggledState = true;
935 node.flags.isToggled = true;
936 node.label = "foo";
937 [object setSemanticsNode:&node];
938 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
939 UISwitch* nativeSwitch = [[UISwitch alloc] init];
940 nativeSwitch.on = YES;
941 nativeSwitch.enabled = NO;
942
943 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
944 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
945 }
946
947 - (void)testSemanticsObjectDeallocated {
948 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
949 new flutter::testing::MockAccessibilityBridge());
950 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
951 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
952 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
953 parent.children = @[ child ];
954 // Validate SemanticsObject deallocation does not crash.
955 // https://github.com/flutter/flutter/issues/66032
956 __weak SemanticsObject* weakObject = parent;
957 parent = nil;
958 XCTAssertNil(weakObject);
959 }
960
961 - (void)testFlutterSemanticsObjectReturnsNilContainerWhenBridgeIsNotAlive {
962 FlutterSemanticsObject* parentObject;
963 FlutterScrollableSemanticsObject* scrollable;
964 FlutterSemanticsObject* object2;
965
966 flutter::SemanticsNode parent;
967 parent.id = 0;
968 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
969 parent.label = "label";
970 parent.value = "value";
971 parent.hint = "hint";
972
973 flutter::SemanticsNode node;
974 node.id = 1;
975 node.flags.hasImplicitScrolling = true;
976 node.actions = flutter::kHorizontalScrollSemanticsActions;
977 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
978 node.label = "label";
979 node.value = "value";
980 node.hint = "hint";
981 node.scrollExtentMax = 100.0;
982 node.scrollPosition = 0.0;
983 parent.childrenInTraversalOrder.push_back(1);
984
985 flutter::SemanticsNode node2;
986 node2.id = 2;
987 node2.rect = SkRect::MakeXYWH(0, 0, 100, 200);
988 node2.label = "label";
989 node2.value = "value";
990 node2.hint = "hint";
991 node2.scrollExtentMax = 100.0;
992 node2.scrollPosition = 0.0;
993 parent.childrenInTraversalOrder.push_back(2);
994
995 {
996 flutter::testing::MockAccessibilityBridge* mock =
997 new flutter::testing::MockAccessibilityBridge();
998 mock->isVoiceOverRunningValue = true;
999 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
1000 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1001
1002 parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
1003 [parentObject setSemanticsNode:&parent];
1004
1005 scrollable = [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
1006 [scrollable setSemanticsNode:&node];
1007 [scrollable accessibilityBridgeDidFinishUpdate];
1008
1009 object2 = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:2];
1010 [object2 setSemanticsNode:&node2];
1011
1012 parentObject.children = @[ scrollable, object2 ];
1013 [parentObject accessibilityBridgeDidFinishUpdate];
1014 [scrollable accessibilityBridgeDidFinishUpdate];
1015 [object2 accessibilityBridgeDidFinishUpdate];
1016
1017 // Returns the correct container if the bridge is alive.
1018 SemanticsObjectContainer* container =
1019 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
1020 XCTAssertEqual(container.semanticsObject, parentObject);
1021 SemanticsObjectContainer* container2 =
1022 static_cast<SemanticsObjectContainer*>(object2.accessibilityContainer);
1023 XCTAssertEqual(container2.semanticsObject, parentObject);
1024 }
1025 // The bridge pointer went out of scope and was deallocated.
1026
1027 XCTAssertNil(scrollable.accessibilityContainer);
1028 XCTAssertNil(object2.accessibilityContainer);
1029 }
1030
1031 - (void)testAccessibilityHitTestSearchCanReturnPlatformView {
1032 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1033 new flutter::testing::MockAccessibilityBridge());
1034 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1035 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
1036 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
1037 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
1038 FlutterTouchInterceptingView* platformView =
1039 [[FlutterTouchInterceptingView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
1040 FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer =
1041 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
1042 uid:1
1043 platformView:platformView];
1044
1045 object0.children = @[ object1 ];
1046 object0.childrenInHitTestOrder = @[ object1 ];
1047 object1.children = @[ platformViewSemanticsContainer, object3 ];
1048 object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ];
1049
1050 flutter::SemanticsNode node0;
1051 node0.id = 0;
1052 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1053 node0.label = "0";
1054 [object0 setSemanticsNode:&node0];
1055
1056 flutter::SemanticsNode node1;
1057 node1.id = 1;
1058 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1059 node1.label = "1";
1060 [object1 setSemanticsNode:&node1];
1061
1062 flutter::SemanticsNode node2;
1063 node2.id = 2;
1064 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
1065 node2.label = "2";
1066 [platformViewSemanticsContainer setSemanticsNode:&node2];
1067
1068 flutter::SemanticsNode node3;
1069 node3.id = 3;
1070 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1071 node3.label = "3";
1072 [object3 setSemanticsNode:&node3];
1073
1074 CGPoint point = CGPointMake(10, 10);
1075 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
1076
1077 XCTAssertEqual(hitTestResult, platformView);
1078 }
1079
1080 - (void)testFlutterPlatformViewSemanticsContainer {
1081 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
1082 new flutter::testing::MockAccessibilityBridge());
1083 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1084 __weak FlutterTouchInterceptingView* weakPlatformView;
1085 __weak FlutterPlatformViewSemanticsContainer* weakContainer;
1086 @autoreleasepool {
1087 FlutterTouchInterceptingView* platformView = [[FlutterTouchInterceptingView alloc] init];
1088 weakPlatformView = platformView;
1089
1090 @autoreleasepool {
1091 FlutterPlatformViewSemanticsContainer* container =
1092 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
1093 uid:1
1094 platformView:platformView];
1095 weakContainer = container;
1096 XCTAssertEqualObjects(platformView.accessibilityContainer, container);
1097 XCTAssertNotNil(weakPlatformView);
1098 XCTAssertNotNil(weakContainer);
1099 }
1100 // Check the variables are still lived.
1101 // `container` is `retain` in `platformView`, so it will not be nil here.
1102 XCTAssertNotNil(weakPlatformView);
1103 XCTAssertNotNil(weakContainer);
1104 }
1105 // Check if there's no more strong references to `platformView` after container and platformView
1106 // are released.
1107 XCTAssertNil(weakPlatformView);
1108 XCTAssertNil(weakContainer);
1109 }
1110
1111 - (void)testTextInputSemanticsObject {
1112 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1113 new flutter::testing::MockAccessibilityBridge());
1114 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1115
1116 flutter::SemanticsNode node;
1117 node.label = "foo";
1118 node.flags.isTextField = true;
1119 node.flags.isReadOnly = true;
1120 TextInputSemanticsObject* object = [[TextInputSemanticsObject alloc] initWithBridge:bridge uid:0];
1121 [object setSemanticsNode:&node];
1122 [object accessibilityBridgeDidFinishUpdate];
1123 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
1124 }
1125
1126 - (void)testTextInputSemanticsObject_canPerformAction {
1127 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1128 new flutter::testing::MockAccessibilityBridge());
1129 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1130
1131 flutter::SemanticsNode node;
1132 node.label = "foo";
1133 node.flags.isTextField = true;
1134 node.flags.isReadOnly = true;
1135 TextInputSemanticsObject* object = [[TextInputSemanticsObject alloc] initWithBridge:bridge uid:0];
1136 [object setSemanticsNode:&node];
1137 [object accessibilityBridgeDidFinishUpdate];
1138
1139 id textInputSurrogate = OCMClassMock([UIResponder class]);
1140 id partialSemanticsObject = OCMPartialMock(object);
1141 OCMStub([partialSemanticsObject textInputSurrogate]).andReturn(textInputSurrogate);
1142
1143 OCMExpect([textInputSurrogate canPerformAction:[OCMArg anySelector] withSender:OCMOCK_ANY])
1144 .andReturn(YES);
1145 XCTAssertTrue([partialSemanticsObject canPerformAction:@selector(copy:) withSender:nil]);
1146
1147 OCMExpect([textInputSurrogate canPerformAction:[OCMArg anySelector] withSender:OCMOCK_ANY])
1148 .andReturn(NO);
1149 XCTAssertFalse([partialSemanticsObject canPerformAction:@selector(copy:) withSender:nil]);
1150 }
1151
1152 - (void)testTextInputSemanticsObject_editActions {
1153 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1154 new flutter::testing::MockAccessibilityBridge());
1155 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1156
1157 flutter::SemanticsNode node;
1158 node.label = "foo";
1159
1160 node.flags.isTextField = true;
1161 node.flags.isReadOnly = true;
1162 TextInputSemanticsObject* object = [[TextInputSemanticsObject alloc] initWithBridge:bridge uid:0];
1163 [object setSemanticsNode:&node];
1164 [object accessibilityBridgeDidFinishUpdate];
1165
1166 id textInputSurrogate = OCMClassMock([UIResponder class]);
1167 id partialSemanticsObject = OCMPartialMock(object);
1168 OCMStub([partialSemanticsObject textInputSurrogate]).andReturn(textInputSurrogate);
1169
1170 XCTestExpectation* copyExpectation =
1171 [self expectationWithDescription:@"Surrogate's copy method is called."];
1172 XCTestExpectation* cutExpectation =
1173 [self expectationWithDescription:@"Surrogate's cut method is called."];
1174 XCTestExpectation* pasteExpectation =
1175 [self expectationWithDescription:@"Surrogate's paste method is called."];
1176 XCTestExpectation* selectAllExpectation =
1177 [self expectationWithDescription:@"Surrogate's selectAll method is called."];
1178 XCTestExpectation* deleteExpectation =
1179 [self expectationWithDescription:@"Surrogate's delete method is called."];
1180
1181 OCMStub([textInputSurrogate copy:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1182 [copyExpectation fulfill];
1183 });
1184 OCMStub([textInputSurrogate cut:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1185 [cutExpectation fulfill];
1186 });
1187 OCMStub([textInputSurrogate paste:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1188 [pasteExpectation fulfill];
1189 });
1190 OCMStub([textInputSurrogate selectAll:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1191 [selectAllExpectation fulfill];
1192 });
1193 OCMStub([textInputSurrogate delete:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1194 [deleteExpectation fulfill];
1195 });
1196
1197 [partialSemanticsObject copy:nil];
1198 [partialSemanticsObject cut:nil];
1199 [partialSemanticsObject paste:nil];
1200 [partialSemanticsObject selectAll:nil];
1201 [partialSemanticsObject delete:nil];
1202
1203 [self waitForExpectationsWithTimeout:1 handler:nil];
1204 }
1205
1206 - (void)testSliderSemanticsObject {
1207 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1208 new flutter::testing::MockAccessibilityBridge());
1209 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1210
1211 flutter::SemanticsNode node;
1212 node.flags.isSlider = true;
1213 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
1214 [object setSemanticsNode:&node];
1215 [object accessibilityBridgeDidFinishUpdate];
1216 XCTAssertEqual([object accessibilityActivate], YES);
1217 }
1218
1219 - (void)testUIFocusItemConformance {
1220 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1221 new flutter::testing::MockAccessibilityBridge());
1222 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1223 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
1224 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
1225 parent.children = @[ child ];
1226
1227 // parentFocusEnvironment
1228 XCTAssertTrue([parent.parentFocusEnvironment isKindOfClass:[UIView class]]);
1229 XCTAssertEqual(child.parentFocusEnvironment, child.parent);
1230
1231 // canBecomeFocused
1232 flutter::SemanticsNode childNode;
1233 childNode.flags.isHidden = true;
1234 childNode.actions = static_cast<int32_t>(flutter::SemanticsAction::kTap);
1235 [child setSemanticsNode:&childNode];
1236 XCTAssertFalse(child.canBecomeFocused);
1237 childNode.flags = flutter::SemanticsFlags{};
1238 [child setSemanticsNode:&childNode];
1239 XCTAssertTrue(child.canBecomeFocused);
1240 childNode.actions = 0;
1241 [child setSemanticsNode:&childNode];
1242 XCTAssertFalse(child.canBecomeFocused);
1243
1244 CGFloat scale = ((bridge->view().window.screen ?: UIScreen.mainScreen)).scale;
1245
1246 childNode.rect = SkRect::MakeXYWH(0, 0, 100 * scale, 100 * scale);
1247 [child setSemanticsNode:&childNode];
1248 flutter::SemanticsNode parentNode;
1249 parentNode.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1250 [parent setSemanticsNode:&parentNode];
1251
1252 XCTAssertTrue(CGRectEqualToRect(child.frame, CGRectMake(0, 0, 100, 100)));
1253 }
1254
1255 - (void)testUIFocusItemContainerConformance {
1256 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1257 new flutter::testing::MockAccessibilityBridge());
1258 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1259 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
1260 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
1261 SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
1262 parent.childrenInHitTestOrder = @[ child1, child2 ];
1263
1264 // focusItemsInRect
1265 NSArray<id<UIFocusItem>>* itemsInRect = [parent focusItemsInRect:CGRectMake(0, 0, 100, 100)];
1266 XCTAssertEqual(itemsInRect.count, (unsigned long)2);
1267 XCTAssertTrue([itemsInRect containsObject:child1]);
1268 XCTAssertTrue([itemsInRect containsObject:child2]);
1269 }
1270
1271 - (void)testUIFocusItemScrollableContainerConformance {
1272 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1273 new flutter::testing::MockAccessibilityBridge());
1274 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1275 FlutterScrollableSemanticsObject* scrollable =
1276 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:5];
1277
1278 // setContentOffset
1279 CGPoint p = CGPointMake(123.0, 456.0);
1280 [scrollable.scrollView scrollViewWillEndDragging:scrollable.scrollView
1281 withVelocity:CGPointZero
1282 targetContentOffset:&p];
1283 scrollable.scrollView.contentOffset = p;
1284 [scrollable.scrollView scrollViewDidEndDecelerating:scrollable.scrollView];
1285 XCTAssertEqual(bridge->observations.size(), (size_t)1);
1286 XCTAssertEqual(bridge->observations[0].id, 5);
1287 XCTAssertEqual(bridge->observations[0].action, flutter::SemanticsAction::kScrollToOffset);
1288
1289 std::vector<uint8_t> args = bridge->observations[0].args;
1290 XCTAssertEqual(args.size(), 3 * sizeof(CGFloat));
1291
1292 NSData* encoded = [NSData dataWithBytes:args.data() length:args.size()];
1293 FlutterStandardTypedData* decoded = [[FlutterStandardMessageCodec sharedInstance] decode:encoded];
1294 CGPoint point = CGPointZero;
1295 memcpy(&point, decoded.data.bytes, decoded.data.length);
1296 XCTAssertTrue(CGPointEqualToPoint(point, p));
1297 }
1298
1299 - (void)testUIFocusItemScrollableContainerNoFeedbackLoops {
1300 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1301 new flutter::testing::MockAccessibilityBridge());
1302 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1303 FlutterScrollableSemanticsObject* scrollable =
1304 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:5];
1305
1306 // setContentOffset
1307 const CGPoint p = CGPointMake(0.0, 456.0);
1308 scrollable.scrollView.contentOffset = p;
1309 bridge->observations.clear();
1310
1311 const SkScalar scrollPosition = p.y + 0.0000000000000001;
1312 flutter::SemanticsNode node;
1313 node.flags.hasImplicitScrolling = true;
1314 node.actions = flutter::kVerticalScrollSemanticsActions;
1315 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
1316 node.scrollExtentMax = 10000;
1317 node.scrollPosition = scrollPosition;
1318 node.transform = {1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0, 0, 0, scrollPosition, 0, 1.0};
1319 [scrollable setSemanticsNode:&node];
1320 [scrollable accessibilityBridgeDidFinishUpdate];
1321
1322 XCTAssertEqual(bridge->observations.size(), (size_t)0);
1323 }
1324 @end
FlutterMacros.h
FlutterPlatformViews_Internal.h
FlutterSemanticsScrollView.h
FlutterTouchInterceptingView_Test.h
SemanticsObject.h
kScrollExtentMaxForInf
constexpr float kScrollExtentMaxForInf
Definition: SemanticsObject.h:18
kFloatCompareEpsilon
FLUTTER_ASSERT_ARC const float kFloatCompareEpsilon
Definition: SemanticsObjectTest.mm:18
SemanticsObjectTestMocks.h
TextInputSemanticsObject.h
FlutterScrollableSemanticsObject(UIFocusItemScrollableContainer)
Definition: SemanticsObjectTest.mm:24
SemanticsObject(UIFocusSystem)
Definition: SemanticsObject+UIFocusSystem.mm:32
TextInputSemanticsObject(Test)
Definition: SemanticsObjectTest.mm:27
-[TextInputSemanticsObject(Test) textInputSurrogate]
UIView< UITextInput > * textInputSurrogate()
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:513
-[FlutterTouchInterceptingView accessibilityContainer]
id accessibilityContainer()
flutter::testing::MockAccessibilityBridge
Definition: SemanticsObjectTestMocks.h:32
flutter::testing::MockAccessibilityBridge::isVoiceOverRunningValue
bool isVoiceOverRunningValue
Definition: SemanticsObjectTestMocks.h:56
flutter::testing::MockAccessibilityBridgeNoWindow
Definition: SemanticsObjectTestMocks.h:63
FlutterPlatformViewSemanticsContainer
Definition: SemanticsObject.h:170
FlutterScrollableSemanticsObject
Definition: SemanticsObject.h:190
FlutterScrollableSemanticsObject::scrollView
FlutterSemanticsScrollView * scrollView
Definition: SemanticsObject.h:190
FlutterSemanticsObject
Definition: SemanticsObject.h:156
FlutterStandardMessageCodec
Definition: FlutterCodecs.h:209
FlutterStandardTypedData
Definition: FlutterCodecs.h:301
FlutterStandardTypedData::data
NSData * data
Definition: FlutterCodecs.h:344
FlutterSwitchSemanticsObject
Definition: SemanticsObject.h:184
SemanticsObjectContainer
Definition: SemanticsObject.h:228
SemanticsObjectContainer::semanticsObject
SemanticsObject * semanticsObject
Definition: SemanticsObject.h:235
SemanticsObject
Definition: SemanticsObject.h:32
-[SemanticsObject _accessibilityHitTest:withEvent:]
id _accessibilityHitTest:withEvent:(CGPoint point,[withEvent] UIEvent *event)
SemanticsObject::parent
SemanticsObject * parent
Definition: SemanticsObject.h:42
SemanticsObject::childrenInHitTestOrder
NSArray< SemanticsObject * > * childrenInHitTestOrder
Definition: SemanticsObject.h:74
SemanticsObject::nativeAccessibility
id nativeAccessibility
Definition: SemanticsObject.h:83
-[SemanticsObject accessibilityScrollToVisibleWithChild:]
BOOL accessibilityScrollToVisibleWithChild:(id child)
-[SemanticsObject accessibilityBridgeDidFinishUpdate]
void accessibilityBridgeDidFinishUpdate()
Definition: SemanticsObject.mm:336
-[SemanticsObject accessibilityScrollToVisible]
BOOL accessibilityScrollToVisible()
SemanticsObject::children
NSArray< SemanticsObject * > * children
Definition: SemanticsObject.h:68
-[SemanticsObject replaceChildAtIndex:withChild:]
void replaceChildAtIndex:withChild:(NSInteger index,[withChild] SemanticsObject *child)
Definition: SemanticsObject.mm:373
-[SemanticsObject setSemanticsNode:]
void setSemanticsNode:(const flutter::SemanticsNode *NS_REQUIRES_SUPER)
Definition: SemanticsObject.mm:332
SemanticsObjectTest
Definition: SemanticsObjectTest.mm:32
TextInputSemanticsObject
Definition: TextInputSemanticsObject.h:20
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()