8fa36d2ae9b84d26846940d44b20e3c243c2cf5a
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-accessible.cpp
1 /*
2  * Copyright (c) 2023 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 <dali/public-api/math/math-utils.h>
23
24 #include <algorithm>
25 #include <iostream>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/atspi-interfaces/accessible.h>
29 #include <dali/devel-api/atspi-interfaces/component.h>
30 #include <dali/devel-api/atspi-interfaces/selection.h>
31 #include <dali/devel-api/atspi-interfaces/text.h>
32 #include <dali/devel-api/atspi-interfaces/value.h>
33
34 //comment out 2 lines below to get more logs
35 #undef LOG
36 #define LOG() _LoggerEmpty()
37
38 using namespace Dali::Accessibility;
39
40 #define GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH 10000
41
42 namespace
43 {
44 bool SortVertically(Component* lhs, Component* rhs)
45 {
46   auto leftRect  = lhs->GetExtents(CoordinateType::WINDOW);
47   auto rightRect = rhs->GetExtents(CoordinateType::WINDOW);
48
49   return leftRect.y < rightRect.y;
50 }
51
52 bool SortHorizontally(Component* lhs, Component* rhs)
53 {
54   auto leftRect  = lhs->GetExtents(CoordinateType::WINDOW);
55   auto rightRect = rhs->GetExtents(CoordinateType::WINDOW);
56
57   return leftRect.x < rightRect.x;
58 }
59
60 std::vector<std::vector<Component*>> SplitLines(const std::vector<Component*>& children)
61 {
62   // Find first with non-zero area
63   auto first = std::find_if(children.begin(), children.end(), [](Component* child) -> bool {
64     auto extents = child->GetExtents(CoordinateType::WINDOW);
65     return !Dali::EqualsZero(extents.height) && !Dali::EqualsZero(extents.width);
66   });
67
68   if(first == children.end())
69   {
70     return {};
71   }
72
73   std::vector<std::vector<Component*>> lines(1);
74   Dali::Rect<>                         lineRect = (*first)->GetExtents(CoordinateType::WINDOW);
75   Dali::Rect<>                         rect;
76
77   // Split into lines
78   for(auto it = first; it != children.end(); ++it)
79   {
80     auto child = *it;
81
82     rect = child->GetExtents(CoordinateType::WINDOW);
83     if(Dali::EqualsZero(rect.height) || Dali::EqualsZero(rect.width))
84     {
85       // Zero area, ignore
86       continue;
87     }
88
89     if(lineRect.y + (0.5 * lineRect.height) >= rect.y + (0.5 * rect.height))
90     {
91       // Same line
92       lines.back().push_back(child);
93     }
94     else
95     {
96       // Start a new line
97       lineRect = rect;
98       lines.emplace_back();
99       lines.back().push_back(child);
100     }
101   }
102
103   return lines;
104 }
105
106 static bool AcceptObjectCheckRelations(Component* obj)
107 {
108   auto relations = obj->GetRelationSet();
109
110   for(const auto& it : relations)
111   {
112     if(it.mRelationType == RelationType::CONTROLLED_BY)
113     {
114       return false;
115     }
116   }
117   return true;
118 }
119
120 static Component* GetScrollableParent(Accessible* obj)
121 {
122   while(obj)
123   {
124     obj       = obj->GetParent();
125     auto comp = dynamic_cast<Component*>(obj);
126     if(comp && comp->IsScrollable())
127     {
128       return comp;
129     }
130   }
131   return nullptr;
132 }
133
134 static bool IsObjectItem(Component* obj)
135 {
136   if(!obj)
137   {
138     return false;
139   }
140   auto role = obj->GetRole();
141   return role == Role::LIST_ITEM || role == Role::MENU_ITEM;
142 }
143
144 static bool IsObjectCollapsed(Component* obj)
145 {
146   if(!obj)
147   {
148     return false;
149   }
150   const auto states = obj->GetStates();
151   return states[State::EXPANDABLE] && !states[State::EXPANDED];
152 }
153
154 static bool IsObjectZeroSize(Component* obj)
155 {
156   if(!obj)
157   {
158     return false;
159   }
160   auto extents = obj->GetExtents(CoordinateType::WINDOW);
161   return Dali::EqualsZero(extents.height) || Dali::EqualsZero(extents.width);
162 }
163
164 static bool IsObjectAcceptable(Component* obj)
165 {
166   if(!obj)
167   {
168     return false;
169   }
170
171   const auto states = obj->GetStates();
172   if(!states[State::VISIBLE])
173   {
174     return false;
175   }
176   if(!AcceptObjectCheckRelations(obj))
177   {
178     return false;
179   }
180   if(!states[State::HIGHLIGHTABLE])
181   {
182     return false;
183   }
184
185   if(GetScrollableParent(obj) != nullptr)
186   {
187     auto parent = dynamic_cast<Component*>(obj->GetParent());
188
189     if(parent)
190     {
191       return !IsObjectItem(obj) || !IsObjectCollapsed(parent);
192     }
193   }
194   else
195   {
196     if(IsObjectZeroSize(obj))
197     {
198       return false;
199     }
200     if(!states[State::SHOWING])
201     {
202       return false;
203     }
204   }
205   return true;
206 }
207
208 static bool IsObjectAcceptable(Accessible* obj)
209 {
210   auto component = dynamic_cast<Component*>(obj);
211   return IsObjectAcceptable(component);
212 }
213
214 static int32_t GetItemCountOfContainer(Accessible* obj, Dali::Accessibility::Role containerRole, Dali::Accessibility::Role itemRole, bool isDirectChild)
215 {
216   int32_t itemCount = 0;
217   if(obj && (!isDirectChild || obj->GetRole() == containerRole))
218   {
219     for(auto i = 0u; i < static_cast<size_t>(obj->GetChildCount()); ++i)
220     {
221       auto child = obj->GetChildAtIndex(i);
222       if(child && child->GetRole() == itemRole)
223       {
224         itemCount++;
225       }
226     }
227   }
228   return itemCount;
229 }
230
231 static int32_t GetItemCountOfFirstDescendantContainer(Accessible* obj, Dali::Accessibility::Role containerRole, Dali::Accessibility::Role itemRole, bool isDirectChild)
232 {
233   int32_t itemCount = 0;
234   itemCount         = GetItemCountOfContainer(obj, containerRole, itemRole, isDirectChild);
235   if(itemCount > 0 || !obj)
236   {
237     return itemCount;
238   }
239
240   for(auto i = 0u; i < static_cast<size_t>(obj->GetChildCount()); ++i)
241   {
242     auto child = obj->GetChildAtIndex(i);
243     itemCount  = GetItemCountOfFirstDescendantContainer(child, containerRole, itemRole, isDirectChild);
244     if(itemCount > 0)
245     {
246       return itemCount;
247     }
248   }
249   return itemCount;
250 }
251
252 static std::string GetComponentInfo(Component* obj)
253 {
254   if(!obj)
255   {
256     return "nullptr";
257   }
258
259   std::ostringstream object;
260   auto               extent = obj->GetExtents(CoordinateType::SCREEN);
261   object << "name: " << obj->GetName() << " extent: (" << extent.x << ", "
262          << extent.y << "), [" << extent.width << ", " << extent.height << "]";
263   return object.str();
264 }
265
266 static std::string MakeIndent(unsigned int maxRecursionDepth)
267 {
268   return std::string(GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH - maxRecursionDepth, ' ');
269 }
270
271 static bool IsRoleAcceptableWhenNavigatingNextPrev(Accessible* obj)
272 {
273   if(!obj)
274   {
275     return false;
276   }
277   auto role = obj->GetRole();
278   return role != Role::POPUP_MENU && role != Role::DIALOG;
279 }
280
281 static Accessible* FindNonDefunctChild(const std::vector<Component*>& children, unsigned int currentIndex, unsigned char forward)
282 {
283   unsigned int childrenCount = children.size();
284   for(; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex)
285   {
286     Accessible* object = children[currentIndex];
287     if(object && !object->GetStates()[State::DEFUNCT])
288     {
289       return object;
290     }
291   }
292   return nullptr;
293 }
294
295 // The auxiliary method for Depth-First Search (DFS) algorithm to find non defunct child directionally
296 static Accessible* FindNonDefunctChildWithDepthFirstSearch(Accessible* node, const std::vector<Component*>& children, unsigned char forward)
297 {
298   if(!node)
299   {
300     return nullptr;
301   }
302
303   auto childrenCount = children.size();
304   if(childrenCount > 0)
305   {
306     const bool isShowing = GetScrollableParent(node) == nullptr ? node->GetStates()[State::SHOWING] : true;
307     if(isShowing)
308     {
309       return FindNonDefunctChild(children, forward ? 0 : childrenCount - 1, forward);
310     }
311   }
312   return nullptr;
313 }
314
315 static bool CheckChainEndWithAttribute(Accessible* obj, unsigned char forward)
316 {
317   if(!obj)
318   {
319     return false;
320   }
321
322   auto attrs = obj->GetAttributes();
323   for(auto& attr : attrs)
324   {
325     if(attr.first == "relation_chain_end")
326     {
327       if((attr.second == "prev,end" && forward == 0) || (attr.second == "next,end" && forward == 1) || attr.second == "prev,next,end")
328       {
329         return true;
330       }
331     }
332   }
333   return false;
334 }
335
336 static std::vector<Component*> GetScrollableParents(Accessible* accessible)
337 {
338   std::vector<Component*> scrollableParents;
339
340   while(accessible)
341   {
342     accessible     = accessible->GetParent();
343     auto component = dynamic_cast<Component*>(accessible);
344     if(component && component->IsScrollable())
345     {
346       scrollableParents.push_back(component);
347     }
348   }
349   return scrollableParents;
350 }
351
352 static std::vector<Component*> GetNonDuplicatedScrollableParents(Accessible* child, Accessible* start)
353 {
354   auto scrollableParentsOfChild = GetScrollableParents(child);
355   auto scrollableParentsOfStart = GetScrollableParents(start);
356
357   // find the first different scrollable parent by comparing from top to bottom.
358   // since it can not be the same after that, there is no need to compare.
359   while(!scrollableParentsOfChild.empty() && !scrollableParentsOfStart.empty() && scrollableParentsOfChild.back() == scrollableParentsOfStart.back())
360   {
361     scrollableParentsOfChild.pop_back();
362     scrollableParentsOfStart.pop_back();
363   }
364
365   return scrollableParentsOfChild;
366 }
367
368 } // anonymous namespace
369
370 BridgeAccessible::BridgeAccessible()
371 {
372 }
373
374 void BridgeAccessible::RegisterInterfaces()
375 {
376   DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::ACCESSIBLE)};
377   AddGetPropertyToInterface(desc, "ChildCount", &BridgeAccessible::GetChildCount);
378   AddGetPropertyToInterface(desc, "Name", &BridgeAccessible::GetName);
379   AddGetPropertyToInterface(desc, "Description", &BridgeAccessible::GetDescription);
380   AddGetPropertyToInterface(desc, "Parent", &BridgeAccessible::GetParent);
381   AddFunctionToInterface(desc, "GetRole", &BridgeAccessible::GetRole);
382   AddFunctionToInterface(desc, "GetRoleName", &BridgeAccessible::GetRoleName);
383   AddFunctionToInterface(desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName);
384   AddFunctionToInterface(desc, "GetState", &BridgeAccessible::GetStates);
385   AddFunctionToInterface(desc, "GetAttributes", &BridgeAccessible::GetAttributes);
386   AddFunctionToInterface(desc, "GetInterfaces", &BridgeAccessible::GetInterfacesAsStrings);
387   AddFunctionToInterface(desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex);
388   AddFunctionToInterface(desc, "GetChildren", &BridgeAccessible::GetChildren);
389   AddFunctionToInterface(desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent);
390   AddFunctionToInterface(desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint);
391   AddFunctionToInterface(desc, "GetNeighbor", &BridgeAccessible::GetNeighbor);
392   AddFunctionToInterface(desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo);
393   AddFunctionToInterface(desc, "DoGesture", &BridgeAccessible::DoGesture);
394   AddFunctionToInterface(desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial);
395   AddFunctionToInterface(desc, "GetRelationSet", &BridgeAccessible::GetRelationSet);
396   AddFunctionToInterface(desc, "SetListenPostRender", &BridgeAccessible::SetListenPostRender);
397   mDbusServer.addInterface("/", desc, true);
398 }
399
400 Accessible* BridgeAccessible::FindSelf() const
401 {
402   return FindCurrentObject();
403 }
404
405 Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType relationType)
406 {
407   if(!obj)
408   {
409     return nullptr;
410   }
411
412   for(auto& relation : obj->GetRelationSet())
413   {
414     if(relation.mRelationType == relationType)
415     {
416       for(auto& target : relation.mTargets)
417       {
418         auto component = dynamic_cast<Component*>(target);
419         if(component)
420         {
421           return component;
422         }
423       }
424     }
425   }
426   return nullptr;
427 }
428
429 Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* root, Point point, CoordinateType type, unsigned int maxRecursionDepth)
430 {
431   if(!root || maxRecursionDepth == 0)
432   {
433     return nullptr;
434   }
435
436   auto rootComponent = dynamic_cast<Component*>(root);
437   LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent);
438
439   if(rootComponent && !rootComponent->IsAccessibleContainingPoint(point, type))
440   {
441     return nullptr;
442   }
443
444   auto children = root->GetChildren();
445   for(auto childIt = children.rbegin(); childIt != children.rend(); childIt++)
446   {
447     //check recursively all children first
448     auto result = CalculateNavigableAccessibleAtPoint(*childIt, point, type, maxRecursionDepth - 1);
449     if(result)
450     {
451       return result;
452     }
453   }
454
455   if(rootComponent)
456   {
457     //Found a candidate, all its children are already checked
458     auto controledBy = GetObjectInRelation(rootComponent, RelationType::CONTROLLED_BY);
459     if(!controledBy)
460     {
461       controledBy = rootComponent;
462     }
463
464     if(controledBy->IsProxy() || IsObjectAcceptable(controledBy))
465     {
466       LOG() << "CalculateNavigableAccessibleAtPoint: found:    " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent);
467       return controledBy;
468     }
469   }
470   return nullptr;
471 }
472
473 BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
474 {
475   auto self                     = FindSelf();
476   auto findObjectByRelationType = [this, &self](RelationType relationType) {
477     auto relations = self->GetRelationSet();
478     auto relation  = std::find_if(relations.begin(),
479                                  relations.end(),
480                                  [relationType](const Dali::Accessibility::Relation& relation) -> bool {
481                                    return relation.mRelationType == relationType;
482                                  });
483     return relations.end() != relation && !relation->mTargets.empty() ? relation->mTargets.back() : nullptr;
484   };
485
486   auto        labellingObject = findObjectByRelationType(RelationType::LABELLED_BY);
487   std::string labeledByName   = labellingObject ? labellingObject->GetName() : "";
488
489   auto describedByObject = findObjectByRelationType(RelationType::DESCRIBED_BY);
490
491   double      currentValue     = 0.0;
492   std::string currentValueText;
493   double      minimumIncrement = 0.0;
494   double      maximumValue     = 0.0;
495   double      minimumValue     = 0.0;
496   auto*       valueInterface   = Value::DownCast(self);
497   if(valueInterface)
498   {
499     currentValue     = valueInterface->GetCurrent();
500     currentValueText = valueInterface->GetValueText();
501     minimumIncrement = valueInterface->GetMinimumIncrement();
502     maximumValue     = valueInterface->GetMaximum();
503     minimumValue     = valueInterface->GetMinimum();
504   }
505
506   int32_t firstSelectedChildIndex = -1;
507   int32_t selectedChildCount      = 0;
508   auto*   selfSelectionInterface  = Selection::DownCast(self);
509   if(selfSelectionInterface)
510   {
511     selectedChildCount      = selfSelectionInterface->GetSelectedChildrenCount();
512     auto firstSelectedChild = selfSelectionInterface->GetSelectedChild(0);
513     if(firstSelectedChild)
514     {
515       firstSelectedChildIndex = firstSelectedChild->GetIndexInParent();
516     }
517   }
518
519   auto childCount       = static_cast<int32_t>(self->GetChildCount());
520   bool hasCheckBoxChild = false;
521   for(auto i = 0u; i < static_cast<size_t>(childCount); ++i)
522   {
523     auto child = self->GetChildAtIndex(i);
524     if(child->GetRole() == Role::CHECK_BOX)
525     {
526       hasCheckBoxChild = true;
527       break;
528     }
529   }
530
531   auto    attributes        = self->GetAttributes();
532   auto    itemCount         = attributes.find("item_count");
533   auto    atspiRole         = self->GetRole();
534   int32_t listChildrenCount = 0;
535   if(itemCount != attributes.end())
536   {
537     // "item_count" gives manual control to the application, so it has priority
538     listChildrenCount = std::atoi(itemCount->second.c_str());
539   }
540   else if(atspiRole == Role::DIALOG)
541   {
542     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::LIST, Role::LIST_ITEM, true);
543   }
544   else if(atspiRole == Role::POPUP_MENU)
545   {
546     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::POPUP_MENU, Role::MENU_ITEM, false);
547   }
548
549   auto*       textInterface         = Text::DownCast(self);
550   std::string nameFromTextInterface = "";
551   if(textInterface)
552   {
553     nameFromTextInterface = textInterface->GetText(0, textInterface->GetCharacterCount());
554   }
555
556   auto name              = self->GetName();
557   auto role              = static_cast<uint32_t>(atspiRole);
558   auto states            = self->GetStates();
559   auto localizedRoleName = self->GetLocalizedRoleName();
560   auto description       = self->GetDescription();
561   auto indexInParent     = static_cast<int32_t>(self->GetIndexInParent());
562
563   auto  parent                   = self->GetParent();
564   auto  parentRole               = static_cast<uint32_t>(parent ? parent->GetRole() : Role{});
565   auto  parentChildCount         = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
566   auto  parentStateSet           = parent ? parent->GetStates() : States{};
567   bool  isSelectedInParent       = false;
568   auto* parentSelectionInterface = Selection::DownCast(parent);
569   if(parentSelectionInterface)
570   {
571     isSelectedInParent = parentSelectionInterface->IsChildSelected(indexInParent);
572   }
573
574   return {
575     attributes,
576     name,
577     labeledByName,
578     nameFromTextInterface,
579     role,
580     states,
581     localizedRoleName,
582     childCount,
583     currentValue,
584     currentValueText,
585     minimumIncrement,
586     maximumValue,
587     minimumValue,
588     description,
589     indexInParent,
590     isSelectedInParent,
591     hasCheckBoxChild,
592     listChildrenCount,
593     firstSelectedChildIndex,
594     parent,
595     parentStateSet,
596     parentChildCount,
597     parentRole,
598     selectedChildCount,
599     describedByObject};
600 }
601
602 DBus::ValueOrError<bool> BridgeAccessible::DoGesture(Dali::Accessibility::Gesture type, int32_t startPositionX, int32_t startPositionY, int32_t endPositionX, int32_t endPositionY, Dali::Accessibility::GestureState state, uint32_t eventTime)
603 {
604   // Please be aware of sending GestureInfo point in the different order with parameters
605   return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, startPositionX, endPositionX, startPositionY, endPositionY, state, eventTime});
606 }
607
608 DBus::ValueOrError<Accessible*, uint8_t, Accessible*> BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordinateType)
609 {
610   Accessible* deputy     = nullptr;
611   auto        accessible = FindSelf();
612   auto        cType      = static_cast<CoordinateType>(coordinateType);
613
614   x -= mData->mExtentsOffset.first;
615   y -= mData->mExtentsOffset.second;
616
617   LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordinateType;
618   auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH);
619   bool recurse   = false;
620   if(component)
621   {
622     recurse = component->IsProxy();
623   }
624   //TODO: add deputy
625   return {component, recurse, deputy};
626 }
627
628 Accessible* BridgeAccessible::GetCurrentlyHighlighted()
629 {
630   //TODO: add currently highlighted object
631   return nullptr;
632 }
633
634 std::vector<Component*> BridgeAccessible::GetValidChildren(const std::vector<Accessible*>& children, Accessible* start)
635 {
636   if(children.empty())
637   {
638     return {};
639   }
640
641   std::vector<Component*> vec;
642
643   Dali::Rect<> scrollableParentExtents;
644   auto         nonDuplicatedScrollableParents = GetNonDuplicatedScrollableParents(children.front(), start);
645   if(!nonDuplicatedScrollableParents.empty())
646   {
647     scrollableParentExtents = nonDuplicatedScrollableParents.front()->GetExtents(CoordinateType::WINDOW);
648   }
649
650   for(auto child : children)
651   {
652     auto* component = dynamic_cast<Component*>(child);
653     if(component)
654     {
655       if(nonDuplicatedScrollableParents.empty() || scrollableParentExtents.Intersects(component->GetExtents(CoordinateType::WINDOW)))
656       {
657         vec.push_back(component);
658       }
659     }
660   }
661
662   return vec;
663 }
664
665 void BridgeAccessible::SortChildrenFromTopLeft(std::vector<Dali::Accessibility::Component*>& children)
666 {
667   if(children.empty())
668   {
669     return;
670   }
671
672   std::vector<Component*> sortedChildren;
673
674   std::sort(children.begin(), children.end(), &SortVertically);
675
676   for(auto& line : SplitLines(children))
677   {
678     std::sort(line.begin(), line.end(), &SortHorizontally);
679     sortedChildren.insert(sortedChildren.end(), line.begin(), line.end());
680   }
681
682   children = sortedChildren;
683 }
684
685 template<class T>
686 struct CycleDetection
687 {
688   CycleDetection(const T value)
689   : mKey(value),
690     mCurrentSearchSize(1),
691     mCounter(1)
692   {
693   }
694
695   bool Check(const T value)
696   {
697     if(mKey == value)
698     {
699       return true;
700     }
701
702     if(--mCounter == 0)
703     {
704       mCurrentSearchSize <<= 1;
705       if(mCurrentSearchSize == 0)
706       {
707         return true; // UNDEFINED BEHAVIOR
708       }
709       mCounter = mCurrentSearchSize;
710       mKey     = value;
711     }
712     return false;
713   }
714
715   T            mKey;
716   unsigned int mCurrentSearchSize;
717   unsigned int mCounter;
718 };
719
720 Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, unsigned char forward)
721 {
722   if(!obj)
723   {
724     return nullptr;
725   }
726
727   auto parent = obj->GetParent();
728   if(!parent)
729   {
730     return nullptr;
731   }
732   else if(parent->IsProxy())
733   {
734     return parent;
735   }
736
737   auto children = GetValidChildren(parent->GetChildren(), start);
738   SortChildrenFromTopLeft(children);
739
740   unsigned int childrenCount = children.size();
741   if(childrenCount == 0)
742   {
743     return nullptr;
744   }
745
746   unsigned int current = 0;
747   for(; current < childrenCount && children[current] != obj; ++current)
748   {
749   }
750
751   if(current >= childrenCount)
752   {
753     return nullptr;
754   }
755
756   forward ? ++current : --current;
757   auto ret = FindNonDefunctChild(children, current, forward);
758   return ret;
759 }
760
761 Accessible* BridgeAccessible::FindNonDefunctSibling(bool& areAllChildrenVisited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward)
762 {
763   while(true)
764   {
765     Accessible* sibling = GetNextNonDefunctSibling(node, start, forward);
766     if(sibling)
767     {
768       node                  = sibling;
769       areAllChildrenVisited = false; // Note that this is passed by non-const reference, so it is the caller that can determine whether this search exhausted all children.
770       break;
771     }
772     // walk up...
773     node = node->GetParent();
774     if(node == nullptr || node == root)
775     {
776       return nullptr;
777     }
778
779     // in backward traversing stop the walk up on parent
780     if(!forward)
781     {
782       break;
783     }
784   }
785
786   return node;
787 }
788
789 Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::NeighborSearchMode searchMode)
790 {
791   if(start && CheckChainEndWithAttribute(start, forward))
792   {
793     return start;
794   }
795   if(root && root->GetStates()[State::DEFUNCT])
796   {
797     return NULL;
798   }
799   if(start && start->GetStates()[State::DEFUNCT])
800   {
801     start   = NULL;
802     forward = 1;
803   }
804
805   if(searchMode == BridgeAccessible::NeighborSearchMode::RECURSE_TO_OUTSIDE)
806   {
807     searchMode = BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION;
808   }
809
810   Accessible* node = start ? start : root;
811   if(!node)
812   {
813     return nullptr;
814   }
815
816   // initialization of all-children-visited flag for start node - we assume
817   // that when we begin at start node and we navigate backward, then all children
818   // are visited, so navigation will ignore start's children and go to
819   // previous sibling available.
820   // Regarding condtion (start != root):
821   // The last object can be found only if areAllChildrenVisited is false.
822   // The start is same with root, when looking for the last object.
823   bool areAllChildrenVisited = (start != root) && (searchMode != BridgeAccessible::NeighborSearchMode::RECURSE_FROM_ROOT && !forward);
824
825   // true, if starting element should be ignored. this is only used in rare case of
826   // recursive search failing to find an object.
827   // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from
828   // element A algorithm has to descend into BUS_B and search element B and its children. this is done
829   // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B).
830   // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out
831   // and will call us again with object A and flag searchMode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING.
832   // this flag means, that object A was already checked previously and we should skip it and its children.
833   bool forceNext = (searchMode == BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION);
834
835   CycleDetection<Accessible*> cycleDetection(node);
836   while(node)
837   {
838     if(node->GetStates()[State::DEFUNCT])
839     {
840       return nullptr;
841     }
842
843     // always accept proxy object from different world
844     if(!forceNext && node->IsProxy())
845     {
846       return node;
847     }
848
849     auto children = GetValidChildren(node->GetChildren(), start);
850     SortChildrenFromTopLeft(children);
851
852     // do accept:
853     // 1. not start node
854     // 2. parent after all children in backward traversing
855     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
856     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
857     bool areAllChildrenVisitedOrMovingForward = (children.size() == 0 || forward || areAllChildrenVisited);
858
859     if(!forceNext && node != start && areAllChildrenVisitedOrMovingForward && IsObjectAcceptable(node))
860     {
861       if(start == NULL || IsRoleAcceptableWhenNavigatingNextPrev(node))
862       {
863         return node;
864       }
865     }
866
867     Accessible* nextRelatedInDirection = !forceNext ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr;
868     if(nextRelatedInDirection && start && start->GetStates()[State::DEFUNCT])
869     {
870       nextRelatedInDirection = NULL;
871     }
872
873     unsigned char wantCycleDetection = 0;
874     if(nextRelatedInDirection)
875     {
876       node               = nextRelatedInDirection;
877       wantCycleDetection = 1;
878     }
879     else
880     {
881       auto child = !forceNext && !areAllChildrenVisited ? FindNonDefunctChildWithDepthFirstSearch(node, children, forward) : nullptr;
882       if(child)
883       {
884         wantCycleDetection = 1;
885       }
886       else
887       {
888         if(!forceNext && node == root)
889         {
890           return NULL;
891         }
892         areAllChildrenVisited = true;
893         child                 = FindNonDefunctSibling(areAllChildrenVisited, node, start, root, forward);
894       }
895       node = child;
896     }
897
898     forceNext = 0;
899     if(wantCycleDetection && cycleDetection.Check(node))
900     {
901       return NULL;
902     }
903   }
904   return NULL;
905 }
906
907 DBus::ValueOrError<Accessible*, uint8_t> BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t searchMode)
908 {
909   auto          start      = FindSelf();
910   auto          root       = !rootPath.empty() ? Find(StripPrefix(rootPath)) : nullptr;
911   auto          accessible = CalculateNeighbor(root, start, direction == 1, static_cast<NeighborSearchMode>(searchMode));
912   unsigned char recurse    = 0;
913   if(accessible)
914   {
915     recurse = accessible->IsProxy();
916   }
917   return {accessible, recurse};
918 }
919
920 Accessible* BridgeAccessible::GetParent()
921 {
922   // NOTE: currently bridge supports single application root element.
923   // only element set as application root might return nullptr from GetParent
924   // if you want more, then you need to change setApplicationRoot to
925   // add/remove ApplicationRoot and make roots a vector.
926   auto parent = FindSelf()->GetParent();
927
928   return parent;
929 }
930
931 DBus::ValueOrError<std::vector<Accessible*>> BridgeAccessible::GetChildren()
932 {
933   return FindSelf()->GetChildren();
934 }
935
936 std::string BridgeAccessible::GetDescription()
937 {
938   return FindSelf()->GetDescription();
939 }
940
941 DBus::ValueOrError<uint32_t> BridgeAccessible::GetRole()
942 {
943   return static_cast<unsigned int>(FindSelf()->GetRole());
944 }
945
946 DBus::ValueOrError<std::string> BridgeAccessible::GetRoleName()
947 {
948   return FindSelf()->GetRoleName();
949 }
950
951 DBus::ValueOrError<std::string> BridgeAccessible::GetLocalizedRoleName()
952 {
953   return FindSelf()->GetLocalizedRoleName();
954 }
955
956 DBus::ValueOrError<int32_t> BridgeAccessible::GetIndexInParent()
957 {
958   return FindSelf()->GetIndexInParent();
959 }
960
961 DBus::ValueOrError<std::array<uint32_t, 2>> BridgeAccessible::GetStates()
962 {
963   return FindSelf()->GetStates().GetRawData();
964 }
965
966 DBus::ValueOrError<std::unordered_map<std::string, std::string>> BridgeAccessible::GetAttributes()
967 {
968   std::unordered_map<std::string, std::string> attributes = FindSelf()->GetAttributes();
969
970   if(mIsScreenReaderSuppressed)
971   {
972     attributes.insert({"suppress-screen-reader", "true"});
973   }
974
975   return attributes;
976 }
977
978 DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfacesAsStrings()
979 {
980   return FindSelf()->GetInterfacesAsStrings();
981 }
982
983 int BridgeAccessible::GetChildCount()
984 {
985   return FindSelf()->GetChildCount();
986 }
987
988 DBus::ValueOrError<Accessible*> BridgeAccessible::GetChildAtIndex(int index)
989 {
990   if(index < 0)
991   {
992     throw std::domain_error{"negative index (" + std::to_string(index) + ")"};
993   }
994   return FindSelf()->GetChildAtIndex(static_cast<size_t>(index));
995 }
996
997 std::string BridgeAccessible::GetName()
998 {
999   return FindSelf()->GetName();
1000 }
1001
1002 DBus::ValueOrError<Accessible*, uint32_t, std::unordered_map<std::string, std::string>> BridgeAccessible::GetDefaultLabelInfo()
1003 {
1004   auto* defaultLabel = GetDefaultLabel(FindSelf());
1005   DALI_ASSERT_DEBUG(defaultLabel);
1006
1007   // By default, the text is taken from navigation context root's accessibility properties name and description.
1008   return {defaultLabel, static_cast<uint32_t>(defaultLabel->GetRole()), defaultLabel->GetAttributes()};
1009 }
1010
1011 DBus::ValueOrError<std::vector<BridgeAccessible::Relation>> BridgeAccessible::GetRelationSet()
1012 {
1013   auto                                    relations = FindSelf()->GetRelationSet();
1014   std::vector<BridgeAccessible::Relation> ret;
1015
1016   for(auto& it : relations)
1017   {
1018     ret.emplace_back(Relation{static_cast<uint32_t>(it.mRelationType), it.mTargets});
1019   }
1020
1021   return ret;
1022 }
1023
1024 DBus::ValueOrError<void> BridgeAccessible::SetListenPostRender(bool enabled)
1025 {
1026   FindSelf()->SetListenPostRender(enabled);
1027   return {};
1028 }