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