[AT-SPI] Remove Role check in AcceptObject
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-accessible.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
20
21 // EXTERNAL INCLUDES
22 #include <iostream>
23
24 //comment out 2 lines below to get more logs
25 #undef LOG
26 #define LOG() _LoggerEmpty()
27
28 using namespace Dali::Accessibility;
29
30 #define GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH 10000
31
32 BridgeAccessible::BridgeAccessible()
33 {
34 }
35
36 void BridgeAccessible::RegisterInterfaces()
37 {
38   DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceAccessible};
39   AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount);
40   AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName);
41   AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription);
42   AddGetPropertyToInterface(desc, "Parent", &BridgeAccessible::GetParent);
43   AddFunctionToInterface(desc, "GetRole", &BridgeAccessible::GetRole);
44   AddFunctionToInterface(desc, "GetRoleName", &BridgeAccessible::GetRoleName);
45   AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName);
46   AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates);
47   AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes);
48   AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfaces);
49   AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex);
50   AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren);
51   AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent);
52   AddFunctionToInterface(desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint);
53   AddFunctionToInterface(desc, "GetNeighbor", &BridgeAccessible::GetNeighbor);
54   AddFunctionToInterface(desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo);
55   AddFunctionToInterface(desc, "DoGesture", &BridgeAccessible::DoGesture);
56   AddFunctionToInterface(desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial);
57   AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet);
58   dbusServer.addInterface("/", desc, true);
59 }
60
61 static bool AcceptObjectCheckRelations(Component* obj)
62 {
63   auto r = obj->GetRelationSet();
64
65   for(const auto& it : r)
66     if(it.relationType == RelationType::CONTROLLED_BY)
67       return false;
68
69   return true;
70 }
71
72 static Component* GetScrollableParent(Accessible* obj)
73 {
74   while(obj)
75   {
76     obj       = obj->GetParent();
77     auto comp = dynamic_cast<Component*>(obj);
78     if(comp && comp->IsScrollable())
79       return comp;
80   }
81   return nullptr;
82 }
83
84 static bool ObjectIsItem(Component* obj)
85 {
86   if(!obj)
87     return false;
88   auto role = obj->GetRole();
89   return role == Role::LIST_ITEM || role == Role::MENU_ITEM;
90 }
91
92 static bool ObjectIsCollapsed(Component* obj)
93 {
94   if(!obj)
95     return false;
96   const auto states = obj->GetStates();
97   return states[State::EXPANDABLE] && !states[State::EXPANDED];
98 }
99
100 static bool OobjectIsZeroSize(Component* obj)
101 {
102   if(!obj)
103     return false;
104   auto extents = obj->GetExtents(CoordType::WINDOW);
105   return extents.height == 0 || extents.width == 0;
106 }
107
108 static bool AcceptObject(Component* obj)
109 {
110   if(!obj)
111     return false;
112   const auto states = obj->GetStates();
113   if(!states[State::VISIBLE])
114     return false;
115   if(!AcceptObjectCheckRelations(obj))
116     return false;
117   if(!states[State::HIGHLIGHTABLE])
118     return false;
119
120   if(GetScrollableParent(obj) != nullptr)
121   {
122     auto parent = dynamic_cast<Component*>(obj->GetParent());
123
124     if(parent)
125     {
126       return !ObjectIsItem(obj) || !ObjectIsCollapsed(parent);
127     }
128   }
129   else
130   {
131     if(OobjectIsZeroSize(obj))
132     {
133       return false;
134     }
135     if(!states[State::SHOWING])
136     {
137       return false;
138     }
139   }
140   return true;
141 }
142
143 static bool AcceptObject(Accessible* obj)
144 {
145   auto c = dynamic_cast<Component*>(obj);
146   return AcceptObject(c);
147 }
148
149 static std::string objDump(Component* obj)
150 {
151   if(!obj)
152     return "nullptr";
153   std::ostringstream o;
154   auto               e = obj->GetExtents(CoordType::SCREEN);
155   o << "name: " << obj->GetName() << " extent: (" << e.x << ", "
156     << e.y << "), [" << e.width << ", " << e.height << "]";
157   return o.str();
158 }
159
160 Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType ralationType)
161 {
162   if(!obj)
163     return nullptr;
164   for(auto& relation : obj->GetRelationSet())
165   {
166     if(relation.relationType == ralationType)
167     {
168       for(auto& address : relation.targets)
169       {
170         auto component = dynamic_cast<Component*>(Find(address));
171         if(component)
172           return component;
173       }
174     }
175   }
176   return nullptr;
177 }
178
179 static std::string makeIndent(unsigned int maxRecursionDepth)
180 {
181   return std::string(GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH - maxRecursionDepth, ' ');
182 }
183
184 Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* root, Point p, CoordType cType, unsigned int maxRecursionDepth)
185 {
186   if(!root || maxRecursionDepth == 0)
187     return nullptr;
188   auto root_component = dynamic_cast<Component*>(root);
189   LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << makeIndent(maxRecursionDepth) << objDump(root_component);
190
191   if(root_component && !root_component->Contains(p, cType))
192     return nullptr;
193
194   auto children = root->GetChildren();
195   for(auto childIt = children.rbegin(); childIt != children.rend(); childIt++)
196   {
197     //check recursively all children first
198     auto result = CalculateNavigableAccessibleAtPoint(*childIt, p, cType, maxRecursionDepth - 1);
199     if(result)
200       return result;
201   }
202   if(root_component)
203   {
204     //Found a candidate, all its children are already checked
205     auto controledBy = GetObjectInRelation(root_component, RelationType::CONTROLLED_BY);
206     if(!controledBy)
207       controledBy = root_component;
208
209     if(controledBy->IsProxy() || AcceptObject(controledBy))
210     {
211       LOG() << "CalculateNavigableAccessibleAtPoint: found:    " << makeIndent(maxRecursionDepth) << objDump(root_component);
212       return controledBy;
213     }
214   }
215   return nullptr;
216 }
217
218 BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
219 {
220   auto        self          = FindSelf();
221   auto        attributes    = self->GetAttributes();
222   auto        name          = self->GetName();
223   std::string labeledByName = "";
224   std::string textIfceName  = "";
225   auto        role          = static_cast<uint32_t>(self->GetRole());
226   auto        states        = self->GetStates();
227   auto        localizedName = self->GetLocalizedRoleName();
228   auto        childCount    = static_cast<int32_t>(self->GetChildCount());
229
230   double currentValue     = 0.0;
231   double minimumIncrement = 0.0;
232   double maximumValue     = 0.0;
233   double minimumValue     = 0.0;
234
235   auto* value = dynamic_cast<Dali::Accessibility::Value*>(self);
236   if(value)
237   {
238     currentValue     = value->GetCurrent();
239     minimumIncrement = value->GetMinimumIncrement();
240     maximumValue     = value->GetMaximum();
241     minimumValue     = value->GetMinimum();
242   }
243
244   auto    description             = self->GetDescription();
245   auto    indexInParent           = static_cast<int32_t>(self->GetIndexInParent());
246   bool    isSelectedInParent      = false;
247   bool    hasCheckBoxChild        = false;
248   int32_t firstSelectedChildIndex = -1;
249   int32_t selectedChildCount      = 0;
250
251   for(auto i = 0u; i < static_cast<size_t>(childCount); ++i)
252   {
253     auto q = self->GetChildAtIndex(i);
254     auto s = q->GetStates();
255     if(s[State::SELECTABLE])
256     {
257       if(s[State::SELECTED])
258       {
259         ++selectedChildCount;
260         if(firstSelectedChildIndex < 0)
261           firstSelectedChildIndex = static_cast<int32_t>(i);
262       }
263     }
264     if(q->GetRole() == Role::CHECK_BOX)
265       hasCheckBoxChild = true;
266   }
267
268   int32_t     listChildrenCount = 0;
269   Accessible* parent            = self->GetParent();
270   auto        parentStateSet    = parent ? parent->GetStates() : States{};
271   auto        parentChildCount  = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
272   auto        parentRole        = static_cast<uint32_t>(parent ? parent->GetRole() : Role{});
273   Accessible* describedByObject = nullptr;
274
275   return {
276     attributes,
277     name,
278     labeledByName,
279     textIfceName,
280     role,
281     states,
282     localizedName,
283     childCount,
284     currentValue,
285     minimumIncrement,
286     maximumValue,
287     minimumValue,
288     description,
289     indexInParent,
290     isSelectedInParent,
291     hasCheckBoxChild,
292     listChildrenCount,
293     firstSelectedChildIndex,
294     parent,
295     parentStateSet,
296     parentChildCount,
297     parentRole,
298     selectedChildCount,
299     describedByObject};
300 }
301
302 void BridgeAccessible::SuppressScreenReader(bool suppress)
303 {
304   suppressScreenReader = suppress;
305 }
306
307 DBus::ValueOrError<bool> BridgeAccessible::DoGesture(Dali::Accessibility::Gesture type, int32_t xBeg, int32_t yBeg, int32_t xEnd, int32_t yEnd, Dali::Accessibility::GestureState state, uint32_t eventTime)
308 {
309   return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, xBeg, xEnd, yBeg, yEnd, state, eventTime});
310 }
311
312 DBus::ValueOrError<Accessible*, uint8_t, Accessible*> BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordType)
313 {
314   Accessible* deputy     = nullptr;
315   auto        accessible = FindSelf();
316   auto        cType      = static_cast<CoordType>(coordType);
317   LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordType;
318   auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH);
319   bool recurse   = false;
320   if(component)
321   {
322     recurse = component->IsProxy();
323   }
324   //TODO: add deputy
325   return {component, recurse, deputy};
326 }
327
328 static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward)
329 {
330   if(!obj)
331     return false;
332   auto attrs = obj->GetAttributes();
333   for(auto& attr : attrs)
334   {
335     if(attr.first == "relation_chain_end")
336     {
337       if((attr.second == "prev,end" && forward == 0) || (attr.second == "next,end" && forward == 1) || attr.second == "prev,next,end")
338       {
339         return true;
340       }
341     }
342   }
343   return false;
344 }
345
346 static Accessible* DeputyOfProxyInParentGet(Accessible* obj)
347 {
348   return nullptr;
349 }
350
351 Accessible* BridgeAccessible::GetCurrentlyHighlighted()
352 {
353   //TODO: add currently highlighted object
354   return nullptr;
355 }
356
357 std::vector<Accessible*> BridgeAccessible::ValidChildrenGet(const std::vector<Accessible*>& children, Accessible* start, Accessible* root)
358 {
359   return children;
360 }
361
362 static bool DeputyIs(Accessible* obj)
363 {
364   //TODO: add deputy
365   return false;
366 }
367
368 static Accessible* ProxyInParentGet(Accessible* obj)
369 {
370   if(!obj)
371     return nullptr;
372   auto children = obj->GetChildren();
373   for(auto& child : children)
374   {
375     if(child->IsProxy())
376       return child;
377   }
378   return nullptr;
379 }
380
381 static bool ObjectRoleIsAcceptableWhenNavigatingNextPrev(Accessible* obj)
382 {
383   if(!obj)
384     return false;
385   auto role = obj->GetRole();
386   return role != Role::POPUP_MENU && role != Role::DIALOG;
387 }
388
389 template<class T>
390 struct CycleDetection
391 {
392   CycleDetection(const T value)
393   : key(value),
394     currentSearchSize(1),
395     counter(1)
396   {
397   }
398   bool check(const T value)
399   {
400     if(key == value)
401       return true;
402     if(--counter == 0)
403     {
404       currentSearchSize <<= 1;
405       if(currentSearchSize == 0)
406         return true; // UNDEFINED BEHAVIOR
407       counter = currentSearchSize;
408       key     = value;
409     }
410     return false;
411   }
412   T            key;
413   unsigned int currentSearchSize;
414   unsigned int counter;
415 };
416
417 static Accessible* FindNonDefunctChild(const std::vector<Accessible*>& children, unsigned int currentIndex, unsigned char forward)
418 {
419   unsigned int childrenCount = children.size();
420   for(; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex)
421   {
422     Accessible* n = children[currentIndex];
423     if(n && !n->GetStates()[State::DEFUNCT])
424       return n;
425   }
426   return nullptr;
427 }
428
429 static Accessible* DirectionalDepthFirstSearchTryNonDefunctChild(Accessible* node, const std::vector<Accessible*>& children, unsigned char forward)
430 {
431   if(!node)
432     return nullptr;
433   auto childrenCount = children.size();
434   if(childrenCount > 0)
435   {
436     const bool isShowing = GetScrollableParent(node) == nullptr ? node->GetStates()[State::SHOWING] : true;
437     if(isShowing)
438     {
439       return FindNonDefunctChild(children, forward ? 0 : childrenCount - 1, forward);
440     }
441   }
442   return nullptr;
443 }
444
445 Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, Accessible* root, unsigned char forward)
446 {
447   if(!obj)
448     return nullptr;
449   auto parent = obj->GetParent();
450   if(!parent)
451     return nullptr;
452
453   auto children = ValidChildrenGet(parent->GetChildren(), start, root);
454
455   unsigned int children_count = children.size();
456   if(children_count == 0)
457   {
458     return nullptr;
459   }
460   unsigned int current = 0;
461   for(; current < children_count && children[current] != obj; ++current)
462     ;
463   if(current >= children_count)
464   {
465     return nullptr;
466   }
467   forward ? ++current : --current;
468   auto ret = FindNonDefunctChild(children, current, forward);
469   return ret;
470 }
471
472 Accessible* BridgeAccessible::DirectionalDepthFirstSearchTryNonDefunctSibling(bool& all_children_visited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward)
473 {
474   while(true)
475   {
476     Accessible* sibling = GetNextNonDefunctSibling(node, start, root, forward);
477     if(sibling)
478     {
479       node                 = sibling;
480       all_children_visited = false;
481       break;
482     }
483     // walk up...
484     node = node->GetParent();
485     if(node == nullptr || node == root)
486       return nullptr;
487
488     // in backward traversing stop the walk up on parent
489     if(!forward)
490       break;
491   }
492   return node;
493 }
494
495 Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::GetNeighborSearchMode search_mode)
496 {
497   if(start && CheckChainEndWithAttribute(start, forward))
498     return start;
499   if(root && root->GetStates()[State::DEFUNCT])
500     return NULL;
501   if(start && start->GetStates()[State::DEFUNCT])
502   {
503     start   = NULL;
504     forward = 1;
505   }
506
507   if(search_mode == BridgeAccessible::GetNeighborSearchMode::recurseToOutside)
508   {
509     // This only works if we navigate backward, and it is not possible to
510     // find in embedded process. In this case the deputy should be used */
511     return DeputyOfProxyInParentGet(start);
512   }
513
514   Accessible* node = start ? start : root;
515   if(!node)
516     return nullptr;
517
518   // initialization of all-children-visited flag for start node - we assume
519   // that when we begin at start node and we navigate backward, then all children
520   // are visited, so navigation will ignore start's children and go to
521   // previous sibling available.
522   // Regarding condtion (start != root):
523   // The last object can be found only if all_children_visited is false.
524   // The start is same with root, when looking for the last object.
525   bool all_children_visited = (start != root) && (search_mode != BridgeAccessible::GetNeighborSearchMode::recurseFromRoot && !forward);
526   // true, if starting element should be ignored. this is only used in rare case of
527   // recursive search failing to find an object.
528   // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from
529   // element A algorithm has to descend into BUS_B and search element B and its children. this is done
530   // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B).
531   // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out
532   // and will call us again with object A and flag search_mode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING.
533   // this flag means, that object A was already checked previously and we should skip it and its children.
534   bool force_next = (search_mode == BridgeAccessible::GetNeighborSearchMode::continueAfterFailedRecursion);
535
536   CycleDetection<Accessible*> cycleDetection(node);
537   while(node)
538   {
539     if(node->GetStates()[State::DEFUNCT])
540       return nullptr;
541
542     // always accept proxy object from different world
543     if(!force_next && node->IsProxy())
544       return node;
545
546     auto children = node->GetChildren();
547     children      = ValidChildrenGet(children, start, root);
548
549     // do accept:
550     // 1. not start node
551     // 2. parent after all children in backward traversing
552     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
553     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
554     bool all_children_visited_or_moving_forward = (children.size() == 0 || forward || all_children_visited);
555     if(!force_next && node != start && all_children_visited_or_moving_forward && AcceptObject(node))
556     {
557       if(start == NULL || ObjectRoleIsAcceptableWhenNavigatingNextPrev(node))
558         return node;
559     }
560
561     Accessible* next_related_in_direction = !force_next ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr;
562     // force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING
563     // in this case the node is elm_layout which is parent of proxy object.
564     // There is an access object working for the proxy object, and the access
565     // object could have relation information. This relation information should
566     // be checked first before using the elm_layout as a node.
567     if(force_next && forward)
568     {
569       auto deputy = DeputyOfProxyInParentGet(node);
570       next_related_in_direction =
571         GetObjectInRelation(deputy, RelationType::FLOWS_TO);
572     }
573
574     if(next_related_in_direction && start && start->GetStates()[State::DEFUNCT])
575     {
576       next_related_in_direction = NULL;
577     }
578
579     unsigned char want_cycle_detection = 0;
580     if(next_related_in_direction)
581     {
582       // Check next_related_in_direction is deputy object
583       Accessible* parent;
584       if(!forward)
585       {
586         // If the prev object is deputy, then go to inside of its proxy first
587         if(DeputyIs(next_related_in_direction))
588         {
589           parent                    = next_related_in_direction->GetParent();
590           next_related_in_direction = ProxyInParentGet(parent);
591         }
592       }
593       else
594       {
595         // If current object is deputy, and it has relation next object,
596         // then do not use the relation next object, and use proxy first
597         if(DeputyIs(node))
598         {
599           parent                    = node->GetParent();
600           next_related_in_direction = ProxyInParentGet(parent);
601         }
602       }
603       node                 = next_related_in_direction;
604       want_cycle_detection = 1;
605     }
606     else
607     {
608       auto child = !force_next && !all_children_visited ? DirectionalDepthFirstSearchTryNonDefunctChild(node, children, forward) : nullptr;
609       if(child)
610       {
611         want_cycle_detection = 1;
612       }
613       else
614       {
615         if(!force_next && node == root)
616           return NULL;
617         all_children_visited = true;
618         child                = DirectionalDepthFirstSearchTryNonDefunctSibling(all_children_visited, node, start, root, forward);
619       }
620       node = child;
621     }
622     force_next = 0;
623     if(want_cycle_detection && cycleDetection.check(node))
624     {
625       return NULL;
626     }
627   }
628   return NULL;
629 }
630
631 DBus::ValueOrError<Accessible*, uint8_t> BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t search_mode)
632 {
633   auto start               = FindSelf();
634   rootPath                 = StripPrefix(rootPath);
635   auto          root       = !rootPath.empty() ? Find(rootPath) : nullptr;
636   auto          accessible = CalculateNeighbor(root, start, direction == 1, static_cast<GetNeighborSearchMode>(search_mode));
637   unsigned char recurse    = 0;
638   if(accessible)
639   {
640     recurse = accessible->IsProxy();
641   }
642   return {accessible, recurse};
643 }
644
645 Accessible* BridgeAccessible::GetParent()
646 {
647   // NOTE: currently bridge supports single application root element.
648   // only element set as application root might return nullptr from GetParent
649   // if you want more, then you need to change setApplicationRoot to
650   // add/remove ApplicationRoot and make roots a vector.
651   auto p = FindSelf()->GetParent();
652
653   return p;
654 }
655 DBus::ValueOrError<std::vector<Accessible*>> BridgeAccessible::GetChildren()
656 {
657   return FindSelf()->GetChildren();
658 }
659 std::string BridgeAccessible::GetDescription()
660 {
661   return FindSelf()->GetDescription();
662 }
663 DBus::ValueOrError<uint32_t> BridgeAccessible::GetRole()
664 {
665   return static_cast<unsigned int>(FindSelf()->GetRole());
666 }
667 DBus::ValueOrError<std::string> BridgeAccessible::GetRoleName()
668 {
669   return FindSelf()->GetRoleName();
670 }
671 DBus::ValueOrError<std::string> BridgeAccessible::GetLocalizedRoleName()
672 {
673   return FindSelf()->GetLocalizedRoleName();
674 }
675 DBus::ValueOrError<int32_t> BridgeAccessible::GetIndexInParent()
676 {
677   return FindSelf()->GetIndexInParent();
678 }
679 DBus::ValueOrError<std::array<uint32_t, 2>> BridgeAccessible::GetStates()
680 {
681   return FindSelf()->GetStates().GetRawData();
682 }
683 DBus::ValueOrError<std::unordered_map<std::string, std::string>> BridgeAccessible::GetAttributes()
684 {
685   std::unordered_map<std::string, std::string> attributes = FindSelf()->GetAttributes();
686   if(suppressScreenReader)
687   {
688     attributes.insert({"suppress-screen-reader", "true"});
689   }
690
691   return attributes;
692 }
693 DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfaces()
694 {
695   return FindSelf()->GetInterfaces();
696 }
697 int BridgeAccessible::GetChildCount()
698 {
699   return FindSelf()->GetChildCount();
700 }
701 DBus::ValueOrError<Accessible*> BridgeAccessible::GetChildAtIndex(int index)
702 {
703   if(index < 0)
704     throw std::domain_error{"negative index (" + std::to_string(index) + ")"};
705   return FindSelf()->GetChildAtIndex(static_cast<size_t>(index));
706 }
707
708 std::string BridgeAccessible::GetName()
709 {
710   return FindSelf()->GetName();
711 }
712
713 DBus::ValueOrError<Accessible*, uint32_t, std::unordered_map<std::string, std::string>> BridgeAccessible::GetDefaultLabelInfo()
714 {
715   auto defaultLabel = FindSelf()->GetDefaultLabel();
716   return {defaultLabel, static_cast<uint32_t>(defaultLabel->GetRole()), defaultLabel->GetAttributes()};
717 }
718
719 DBus::ValueOrError<std::vector<BridgeAccessible::Relation>> BridgeAccessible::GetRelationSet()
720 {
721   auto                                    relations = FindSelf()->GetRelationSet();
722   std::vector<BridgeAccessible::Relation> ret;
723
724   for(auto& it : relations)
725     ret.emplace_back(Relation{static_cast<uint32_t>(it.relationType), it.targets});
726
727   return ret;
728 }