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