309be51d118468259390e5e92c891da62c9bdceb
[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(CoordType::WINDOW);
38   auto rightRect = rhs->GetExtents(CoordType::WINDOW);
39
40   return leftRect.y < rightRect.y;
41 }
42
43 bool SortHorizontally(Component* lhs, Component* rhs)
44 {
45   auto leftRect  = lhs->GetExtents(CoordType::WINDOW);
46   auto rightRect = rhs->GetExtents(CoordType::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(CoordType::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(CoordType::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(CoordType::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 OobjectIsZeroSize(Component* obj)
168 {
169   if(!obj)
170     return false;
171   auto extents = obj->GetExtents(CoordType::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(OobjectIsZeroSize(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(CoordType::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, CoordType cType, unsigned int maxRecursionDepth)
252 {
253   if(!root || maxRecursionDepth == 0)
254     return nullptr;
255   auto root_component = dynamic_cast<Component*>(root);
256   LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << makeIndent(maxRecursionDepth) << objDump(root_component);
257
258   if(root_component && !root_component->Contains(p, cType))
259     return nullptr;
260
261   auto children = root->GetChildren();
262   for(auto childIt = children.rbegin(); childIt != children.rend(); childIt++)
263   {
264     //check recursively all children first
265     auto result = CalculateNavigableAccessibleAtPoint(*childIt, p, cType, maxRecursionDepth - 1);
266     if(result)
267       return result;
268   }
269   if(root_component)
270   {
271     //Found a candidate, all its children are already checked
272     auto controledBy = GetObjectInRelation(root_component, RelationType::CONTROLLED_BY);
273     if(!controledBy)
274       controledBy = root_component;
275
276     if(controledBy->IsProxy() || AcceptObject(controledBy))
277     {
278       LOG() << "CalculateNavigableAccessibleAtPoint: found:    " << makeIndent(maxRecursionDepth) << objDump(root_component);
279       return controledBy;
280     }
281   }
282   return nullptr;
283 }
284
285 BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
286 {
287   auto        self          = FindSelf();
288   auto        attributes    = self->GetAttributes();
289   auto        name          = self->GetName();
290   std::string labeledByName = "";
291   std::string textIfceName  = "";
292   auto        role          = static_cast<uint32_t>(self->GetRole());
293   auto        states        = self->GetStates();
294   auto        localizedName = self->GetLocalizedRoleName();
295   auto        childCount    = static_cast<int32_t>(self->GetChildCount());
296
297   double currentValue     = 0.0;
298   double minimumIncrement = 0.0;
299   double maximumValue     = 0.0;
300   double minimumValue     = 0.0;
301
302   auto* value = dynamic_cast<Dali::Accessibility::Value*>(self);
303   if(value)
304   {
305     currentValue     = value->GetCurrent();
306     minimumIncrement = value->GetMinimumIncrement();
307     maximumValue     = value->GetMaximum();
308     minimumValue     = value->GetMinimum();
309   }
310
311   auto    description             = self->GetDescription();
312   auto    indexInParent           = static_cast<int32_t>(self->GetIndexInParent());
313   bool    isSelectedInParent      = false;
314   bool    hasCheckBoxChild        = false;
315   int32_t firstSelectedChildIndex = -1;
316   int32_t selectedChildCount      = 0;
317
318   for(auto i = 0u; i < static_cast<size_t>(childCount); ++i)
319   {
320     auto q = self->GetChildAtIndex(i);
321     auto s = q->GetStates();
322     if(s[State::SELECTABLE])
323     {
324       if(s[State::SELECTED])
325       {
326         ++selectedChildCount;
327         if(firstSelectedChildIndex < 0)
328           firstSelectedChildIndex = static_cast<int32_t>(i);
329       }
330     }
331     if(q->GetRole() == Role::CHECK_BOX)
332       hasCheckBoxChild = true;
333   }
334
335   int32_t     listChildrenCount = 0;
336   Accessible* parent            = self->GetParent();
337   auto        parentStateSet    = parent ? parent->GetStates() : States{};
338   auto        parentChildCount  = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
339   auto        parentRole        = static_cast<uint32_t>(parent ? parent->GetRole() : Role{});
340   Accessible* describedByObject = nullptr;
341
342   return {
343     attributes,
344     name,
345     labeledByName,
346     textIfceName,
347     role,
348     states,
349     localizedName,
350     childCount,
351     currentValue,
352     minimumIncrement,
353     maximumValue,
354     minimumValue,
355     description,
356     indexInParent,
357     isSelectedInParent,
358     hasCheckBoxChild,
359     listChildrenCount,
360     firstSelectedChildIndex,
361     parent,
362     parentStateSet,
363     parentChildCount,
364     parentRole,
365     selectedChildCount,
366     describedByObject};
367 }
368
369 void BridgeAccessible::SuppressScreenReader(bool suppress)
370 {
371   suppressScreenReader = suppress;
372 }
373
374 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)
375 {
376   return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, xBeg, xEnd, yBeg, yEnd, state, eventTime});
377 }
378
379 DBus::ValueOrError<Accessible*, uint8_t, Accessible*> BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordType)
380 {
381   Accessible* deputy     = nullptr;
382   auto        accessible = FindSelf();
383   auto        cType      = static_cast<CoordType>(coordType);
384   LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordType;
385   auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH);
386   bool recurse   = false;
387   if(component)
388   {
389     recurse = component->IsProxy();
390   }
391   //TODO: add deputy
392   return {component, recurse, deputy};
393 }
394
395 static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward)
396 {
397   if(!obj)
398     return false;
399   auto attrs = obj->GetAttributes();
400   for(auto& attr : attrs)
401   {
402     if(attr.first == "relation_chain_end")
403     {
404       if((attr.second == "prev,end" && forward == 0) || (attr.second == "next,end" && forward == 1) || attr.second == "prev,next,end")
405       {
406         return true;
407       }
408     }
409   }
410   return false;
411 }
412
413 static Accessible* DeputyOfProxyInParentGet(Accessible* obj)
414 {
415   return nullptr;
416 }
417
418 Accessible* BridgeAccessible::GetCurrentlyHighlighted()
419 {
420   //TODO: add currently highlighted object
421   return nullptr;
422 }
423
424 std::vector<Accessible*> BridgeAccessible::ValidChildrenGet(const std::vector<Accessible*>& children, Accessible* start, Accessible* root)
425 {
426   std::vector<Component*> vec;
427   std::vector<Accessible*> ret;
428
429   for(auto child : children)
430   {
431     if(auto* component = dynamic_cast<Component*>(child); component)
432     {
433       vec.push_back(component);
434     }
435   }
436
437   std::sort(vec.begin(), vec.end(), &SortVertically);
438
439   for(auto& line : SplitLines(vec))
440   {
441     std::sort(line.begin(), line.end(), &SortHorizontally);
442     ret.insert(ret.end(), line.begin(), line.end());
443   }
444
445   return ret;
446 }
447
448 static bool DeputyIs(Accessible* obj)
449 {
450   //TODO: add deputy
451   return false;
452 }
453
454 static Accessible* ProxyInParentGet(Accessible* obj)
455 {
456   if(!obj)
457     return nullptr;
458   auto children = obj->GetChildren();
459   for(auto& child : children)
460   {
461     if(child->IsProxy())
462       return child;
463   }
464   return nullptr;
465 }
466
467 static bool ObjectRoleIsAcceptableWhenNavigatingNextPrev(Accessible* obj)
468 {
469   if(!obj)
470     return false;
471   auto role = obj->GetRole();
472   return role != Role::POPUP_MENU && role != Role::DIALOG;
473 }
474
475 template<class T>
476 struct CycleDetection
477 {
478   CycleDetection(const T value)
479   : key(value),
480     currentSearchSize(1),
481     counter(1)
482   {
483   }
484   bool check(const T value)
485   {
486     if(key == value)
487       return true;
488     if(--counter == 0)
489     {
490       currentSearchSize <<= 1;
491       if(currentSearchSize == 0)
492         return true; // UNDEFINED BEHAVIOR
493       counter = currentSearchSize;
494       key     = value;
495     }
496     return false;
497   }
498   T            key;
499   unsigned int currentSearchSize;
500   unsigned int counter;
501 };
502
503 static Accessible* FindNonDefunctChild(const std::vector<Accessible*>& children, unsigned int currentIndex, unsigned char forward)
504 {
505   unsigned int childrenCount = children.size();
506   for(; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex)
507   {
508     Accessible* n = children[currentIndex];
509     if(n && !n->GetStates()[State::DEFUNCT])
510       return n;
511   }
512   return nullptr;
513 }
514
515 static Accessible* DirectionalDepthFirstSearchTryNonDefunctChild(Accessible* node, const std::vector<Accessible*>& children, unsigned char forward)
516 {
517   if(!node)
518     return nullptr;
519   auto childrenCount = children.size();
520   if(childrenCount > 0)
521   {
522     const bool isShowing = GetScrollableParent(node) == nullptr ? node->GetStates()[State::SHOWING] : true;
523     if(isShowing)
524     {
525       return FindNonDefunctChild(children, forward ? 0 : childrenCount - 1, forward);
526     }
527   }
528   return nullptr;
529 }
530
531 Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, Accessible* root, unsigned char forward)
532 {
533   if(!obj)
534     return nullptr;
535   auto parent = obj->GetParent();
536   if(!parent)
537     return nullptr;
538
539   auto children = ValidChildrenGet(parent->GetChildren(), start, root);
540
541   unsigned int children_count = children.size();
542   if(children_count == 0)
543   {
544     return nullptr;
545   }
546   unsigned int current = 0;
547   for(; current < children_count && children[current] != obj; ++current)
548     ;
549   if(current >= children_count)
550   {
551     return nullptr;
552   }
553   forward ? ++current : --current;
554   auto ret = FindNonDefunctChild(children, current, forward);
555   return ret;
556 }
557
558 Accessible* BridgeAccessible::DirectionalDepthFirstSearchTryNonDefunctSibling(bool& all_children_visited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward)
559 {
560   while(true)
561   {
562     Accessible* sibling = GetNextNonDefunctSibling(node, start, root, forward);
563     if(sibling)
564     {
565       node                 = sibling;
566       all_children_visited = false;
567       break;
568     }
569     // walk up...
570     node = node->GetParent();
571     if(node == nullptr || node == root)
572       return nullptr;
573
574     // in backward traversing stop the walk up on parent
575     if(!forward)
576       break;
577   }
578   return node;
579 }
580
581 Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::GetNeighborSearchMode search_mode)
582 {
583   if(start && CheckChainEndWithAttribute(start, forward))
584     return start;
585   if(root && root->GetStates()[State::DEFUNCT])
586     return NULL;
587   if(start && start->GetStates()[State::DEFUNCT])
588   {
589     start   = NULL;
590     forward = 1;
591   }
592
593   if(search_mode == BridgeAccessible::GetNeighborSearchMode::recurseToOutside)
594   {
595     // This only works if we navigate backward, and it is not possible to
596     // find in embedded process. In this case the deputy should be used */
597     return DeputyOfProxyInParentGet(start);
598   }
599
600   Accessible* node = start ? start : root;
601   if(!node)
602     return nullptr;
603
604   // initialization of all-children-visited flag for start node - we assume
605   // that when we begin at start node and we navigate backward, then all children
606   // are visited, so navigation will ignore start's children and go to
607   // previous sibling available.
608   // Regarding condtion (start != root):
609   // The last object can be found only if all_children_visited is false.
610   // The start is same with root, when looking for the last object.
611   bool all_children_visited = (start != root) && (search_mode != BridgeAccessible::GetNeighborSearchMode::recurseFromRoot && !forward);
612   // true, if starting element should be ignored. this is only used in rare case of
613   // recursive search failing to find an object.
614   // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from
615   // element A algorithm has to descend into BUS_B and search element B and its children. this is done
616   // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B).
617   // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out
618   // and will call us again with object A and flag search_mode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING.
619   // this flag means, that object A was already checked previously and we should skip it and its children.
620   bool force_next = (search_mode == BridgeAccessible::GetNeighborSearchMode::continueAfterFailedRecursion);
621
622   CycleDetection<Accessible*> cycleDetection(node);
623   while(node)
624   {
625     if(node->GetStates()[State::DEFUNCT])
626       return nullptr;
627
628     // always accept proxy object from different world
629     if(!force_next && node->IsProxy())
630       return node;
631
632     auto children = node->GetChildren();
633     children      = ValidChildrenGet(children, start, root);
634
635     // do accept:
636     // 1. not start node
637     // 2. parent after all children in backward traversing
638     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
639     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
640     bool all_children_visited_or_moving_forward = (children.size() == 0 || forward || all_children_visited);
641     if(!force_next && node != start && all_children_visited_or_moving_forward && AcceptObject(node))
642     {
643       if(start == NULL || ObjectRoleIsAcceptableWhenNavigatingNextPrev(node))
644         return node;
645     }
646
647     Accessible* next_related_in_direction = !force_next ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr;
648     // force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING
649     // in this case the node is elm_layout which is parent of proxy object.
650     // There is an access object working for the proxy object, and the access
651     // object could have relation information. This relation information should
652     // be checked first before using the elm_layout as a node.
653     if(force_next && forward)
654     {
655       auto deputy = DeputyOfProxyInParentGet(node);
656       next_related_in_direction =
657         GetObjectInRelation(deputy, RelationType::FLOWS_TO);
658     }
659
660     if(next_related_in_direction && start && start->GetStates()[State::DEFUNCT])
661     {
662       next_related_in_direction = NULL;
663     }
664
665     unsigned char want_cycle_detection = 0;
666     if(next_related_in_direction)
667     {
668       // Check next_related_in_direction is deputy object
669       Accessible* parent;
670       if(!forward)
671       {
672         // If the prev object is deputy, then go to inside of its proxy first
673         if(DeputyIs(next_related_in_direction))
674         {
675           parent                    = next_related_in_direction->GetParent();
676           next_related_in_direction = ProxyInParentGet(parent);
677         }
678       }
679       else
680       {
681         // If current object is deputy, and it has relation next object,
682         // then do not use the relation next object, and use proxy first
683         if(DeputyIs(node))
684         {
685           parent                    = node->GetParent();
686           next_related_in_direction = ProxyInParentGet(parent);
687         }
688       }
689       node                 = next_related_in_direction;
690       want_cycle_detection = 1;
691     }
692     else
693     {
694       auto child = !force_next && !all_children_visited ? DirectionalDepthFirstSearchTryNonDefunctChild(node, children, forward) : nullptr;
695       if(child)
696       {
697         want_cycle_detection = 1;
698       }
699       else
700       {
701         if(!force_next && node == root)
702           return NULL;
703         all_children_visited = true;
704         child                = DirectionalDepthFirstSearchTryNonDefunctSibling(all_children_visited, node, start, root, forward);
705       }
706       node = child;
707     }
708     force_next = 0;
709     if(want_cycle_detection && cycleDetection.check(node))
710     {
711       return NULL;
712     }
713   }
714   return NULL;
715 }
716
717 DBus::ValueOrError<Accessible*, uint8_t> BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t search_mode)
718 {
719   auto start               = FindSelf();
720   rootPath                 = StripPrefix(rootPath);
721   auto          root       = !rootPath.empty() ? Find(rootPath) : nullptr;
722   auto          accessible = CalculateNeighbor(root, start, direction == 1, static_cast<GetNeighborSearchMode>(search_mode));
723   unsigned char recurse    = 0;
724   if(accessible)
725   {
726     recurse = accessible->IsProxy();
727   }
728   return {accessible, recurse};
729 }
730
731 Accessible* BridgeAccessible::GetParent()
732 {
733   // NOTE: currently bridge supports single application root element.
734   // only element set as application root might return nullptr from GetParent
735   // if you want more, then you need to change setApplicationRoot to
736   // add/remove ApplicationRoot and make roots a vector.
737   auto p = FindSelf()->GetParent();
738
739   return p;
740 }
741 DBus::ValueOrError<std::vector<Accessible*>> BridgeAccessible::GetChildren()
742 {
743   return FindSelf()->GetChildren();
744 }
745 std::string BridgeAccessible::GetDescription()
746 {
747   return FindSelf()->GetDescription();
748 }
749 DBus::ValueOrError<uint32_t> BridgeAccessible::GetRole()
750 {
751   return static_cast<unsigned int>(FindSelf()->GetRole());
752 }
753 DBus::ValueOrError<std::string> BridgeAccessible::GetRoleName()
754 {
755   return FindSelf()->GetRoleName();
756 }
757 DBus::ValueOrError<std::string> BridgeAccessible::GetLocalizedRoleName()
758 {
759   return FindSelf()->GetLocalizedRoleName();
760 }
761 DBus::ValueOrError<int32_t> BridgeAccessible::GetIndexInParent()
762 {
763   return FindSelf()->GetIndexInParent();
764 }
765 DBus::ValueOrError<std::array<uint32_t, 2>> BridgeAccessible::GetStates()
766 {
767   return FindSelf()->GetStates().GetRawData();
768 }
769 DBus::ValueOrError<std::unordered_map<std::string, std::string>> BridgeAccessible::GetAttributes()
770 {
771   std::unordered_map<std::string, std::string> attributes = FindSelf()->GetAttributes();
772   if(suppressScreenReader)
773   {
774     attributes.insert({"suppress-screen-reader", "true"});
775   }
776
777   return attributes;
778 }
779 DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfaces()
780 {
781   return FindSelf()->GetInterfaces();
782 }
783 int BridgeAccessible::GetChildCount()
784 {
785   return FindSelf()->GetChildCount();
786 }
787 DBus::ValueOrError<Accessible*> BridgeAccessible::GetChildAtIndex(int index)
788 {
789   if(index < 0)
790     throw std::domain_error{"negative index (" + std::to_string(index) + ")"};
791   return FindSelf()->GetChildAtIndex(static_cast<size_t>(index));
792 }
793
794 std::string BridgeAccessible::GetName()
795 {
796   return FindSelf()->GetName();
797 }
798
799 DBus::ValueOrError<Accessible*, uint32_t, std::unordered_map<std::string, std::string>> BridgeAccessible::GetDefaultLabelInfo()
800 {
801   auto defaultLabel = FindSelf()->GetDefaultLabel();
802   return {defaultLabel, static_cast<uint32_t>(defaultLabel->GetRole()), defaultLabel->GetAttributes()};
803 }
804
805 DBus::ValueOrError<std::vector<BridgeAccessible::Relation>> BridgeAccessible::GetRelationSet()
806 {
807   auto                                    relations = FindSelf()->GetRelationSet();
808   std::vector<BridgeAccessible::Relation> ret;
809
810   for(auto& it : relations)
811     ret.emplace_back(Relation{static_cast<uint32_t>(it.relationType), it.targets});
812
813   return ret;
814 }