[Tizen][ATSPI] Introduce GetNodeInfo interface
[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   AddFunctionToInterface(desc, "GetNodeInfo", &BridgeAccessible::GetNodeInfo);
398   mDbusServer.addInterface("/", desc, true);
399 }
400
401 Accessible* BridgeAccessible::FindSelf() const
402 {
403   return FindCurrentObject();
404 }
405
406 Component* BridgeAccessible::GetObjectInRelation(Accessible* obj, RelationType relationType)
407 {
408   if(!obj)
409   {
410     return nullptr;
411   }
412
413   for(auto& relation : obj->GetRelationSet())
414   {
415     if(relation.mRelationType == relationType)
416     {
417       for(auto& target : relation.mTargets)
418       {
419         auto component = dynamic_cast<Component*>(target);
420         if(component)
421         {
422           return component;
423         }
424       }
425     }
426   }
427   return nullptr;
428 }
429
430 Component* BridgeAccessible::CalculateNavigableAccessibleAtPoint(Accessible* root, Point point, CoordinateType type, unsigned int maxRecursionDepth)
431 {
432   if(!root || maxRecursionDepth == 0)
433   {
434     return nullptr;
435   }
436
437   auto rootComponent = dynamic_cast<Component*>(root);
438   LOG() << "CalculateNavigableAccessibleAtPoint: checking: " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent);
439
440   if(rootComponent && !rootComponent->IsAccessibleContainingPoint(point, type))
441   {
442     return nullptr;
443   }
444
445   auto children = root->GetChildren();
446   for(auto childIt = children.rbegin(); childIt != children.rend(); childIt++)
447   {
448     //check recursively all children first
449     auto result = CalculateNavigableAccessibleAtPoint(*childIt, point, type, maxRecursionDepth - 1);
450     if(result)
451     {
452       return result;
453     }
454   }
455
456   if(rootComponent)
457   {
458     //Found a candidate, all its children are already checked
459     auto controledBy = GetObjectInRelation(rootComponent, RelationType::CONTROLLED_BY);
460     if(!controledBy)
461     {
462       controledBy = rootComponent;
463     }
464
465     if(controledBy->IsProxy() || IsObjectAcceptable(controledBy))
466     {
467       LOG() << "CalculateNavigableAccessibleAtPoint: found:    " << MakeIndent(maxRecursionDepth) << GetComponentInfo(rootComponent);
468       return controledBy;
469     }
470   }
471   return nullptr;
472 }
473
474 BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
475 {
476   auto self                     = FindSelf();
477   auto findObjectByRelationType = [this, &self](RelationType relationType) {
478     auto relations = self->GetRelationSet();
479     auto relation  = std::find_if(relations.begin(),
480                                  relations.end(),
481                                  [relationType](const Dali::Accessibility::Relation& relation) -> bool {
482                                    return relation.mRelationType == relationType;
483                                  });
484     return relations.end() != relation && !relation->mTargets.empty() ? relation->mTargets.back() : nullptr;
485   };
486
487   auto        labellingObject = findObjectByRelationType(RelationType::LABELLED_BY);
488   std::string labeledByName   = labellingObject ? labellingObject->GetName() : "";
489
490   auto describedByObject = findObjectByRelationType(RelationType::DESCRIBED_BY);
491
492   double      currentValue     = 0.0;
493   std::string currentValueText;
494   double      minimumIncrement = 0.0;
495   double      maximumValue     = 0.0;
496   double      minimumValue     = 0.0;
497   auto*       valueInterface   = Value::DownCast(self);
498   if(valueInterface)
499   {
500     currentValue     = valueInterface->GetCurrent();
501     currentValueText = valueInterface->GetValueText();
502     minimumIncrement = valueInterface->GetMinimumIncrement();
503     maximumValue     = valueInterface->GetMaximum();
504     minimumValue     = valueInterface->GetMinimum();
505   }
506
507   int32_t firstSelectedChildIndex = -1;
508   int32_t selectedChildCount      = 0;
509   auto*   selfSelectionInterface  = Selection::DownCast(self);
510   if(selfSelectionInterface)
511   {
512     selectedChildCount      = selfSelectionInterface->GetSelectedChildrenCount();
513     auto firstSelectedChild = selfSelectionInterface->GetSelectedChild(0);
514     if(firstSelectedChild)
515     {
516       firstSelectedChildIndex = firstSelectedChild->GetIndexInParent();
517     }
518   }
519
520   auto childCount       = static_cast<int32_t>(self->GetChildCount());
521   bool hasCheckBoxChild = false;
522   for(auto i = 0u; i < static_cast<size_t>(childCount); ++i)
523   {
524     auto child = self->GetChildAtIndex(i);
525     if(child->GetRole() == Role::CHECK_BOX)
526     {
527       hasCheckBoxChild = true;
528       break;
529     }
530   }
531
532   auto    attributes        = self->GetAttributes();
533   auto    itemCount         = attributes.find("item_count");
534   auto    atspiRole         = self->GetRole();
535   int32_t listChildrenCount = 0;
536   if(itemCount != attributes.end())
537   {
538     // "item_count" gives manual control to the application, so it has priority
539     listChildrenCount = std::atoi(itemCount->second.c_str());
540   }
541   else if(atspiRole == Role::DIALOG)
542   {
543     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::LIST, Role::LIST_ITEM, true);
544   }
545   else if(atspiRole == Role::POPUP_MENU)
546   {
547     listChildrenCount = GetItemCountOfFirstDescendantContainer(self, Role::POPUP_MENU, Role::MENU_ITEM, false);
548   }
549
550   auto*       textInterface         = Text::DownCast(self);
551   std::string nameFromTextInterface = "";
552   if(textInterface)
553   {
554     nameFromTextInterface = textInterface->GetText(0, textInterface->GetCharacterCount());
555   }
556
557   auto name              = self->GetName();
558   auto role              = static_cast<uint32_t>(atspiRole);
559   auto states            = self->GetStates();
560   auto localizedRoleName = self->GetLocalizedRoleName();
561   auto description       = self->GetDescription();
562   auto indexInParent     = static_cast<int32_t>(self->GetIndexInParent());
563
564   auto  parent                   = self->GetParent();
565   auto  parentRole               = static_cast<uint32_t>(parent ? parent->GetRole() : Role{});
566   auto  parentChildCount         = parent ? static_cast<int32_t>(parent->GetChildCount()) : 0;
567   auto  parentStateSet           = parent ? parent->GetStates() : States{};
568   bool  isSelectedInParent       = false;
569   auto* parentSelectionInterface = Selection::DownCast(parent);
570   if(parentSelectionInterface)
571   {
572     isSelectedInParent = parentSelectionInterface->IsChildSelected(indexInParent);
573   }
574
575   return {
576     attributes,
577     name,
578     labeledByName,
579     nameFromTextInterface,
580     role,
581     states,
582     localizedRoleName,
583     childCount,
584     currentValue,
585     currentValueText,
586     minimumIncrement,
587     maximumValue,
588     minimumValue,
589     description,
590     indexInParent,
591     isSelectedInParent,
592     hasCheckBoxChild,
593     listChildrenCount,
594     firstSelectedChildIndex,
595     parent,
596     parentStateSet,
597     parentChildCount,
598     parentRole,
599     selectedChildCount,
600     describedByObject};
601 }
602
603 BridgeAccessible::NodeInfoType BridgeAccessible::GetNodeInfo()
604 {
605   auto self         = FindSelf();
606   auto roleName     = self->GetRoleName();
607   auto name         = self->GetName();
608   auto toolkitName  = "dali";
609   auto attributes   = self->GetAttributes();
610   auto states       = self->GetStates();
611
612   auto* component   = Component::DownCast(self);
613   Dali::Rect<> screenExtents = {0, 0, 0, 0};
614   Dali::Rect<> windowExtents = {0, 0, 0, 0};
615   if (component)
616   {
617     screenExtents = component->GetExtents(CoordinateType::SCREEN);
618     windowExtents = component->GetExtents(CoordinateType::WINDOW);
619   }
620
621   auto* valueInterface    = Value::DownCast(self);
622   double currentValue     = 0.0;
623   double minimumIncrement = 0.0;
624   double maximumValue     = 0.0;
625   double minimumValue     = 0.0;
626   if(valueInterface)
627   {
628     currentValue     = valueInterface->GetCurrent();
629     minimumIncrement = valueInterface->GetMinimumIncrement();
630     maximumValue     = valueInterface->GetMaximum();
631     minimumValue     = valueInterface->GetMinimum();
632   }
633
634   return {
635     roleName,
636     name,
637     toolkitName,
638     attributes,
639     states,
640     {screenExtents.x, screenExtents.y, screenExtents.width, screenExtents.height},
641     {windowExtents.x, windowExtents.y, windowExtents.width, windowExtents.height},
642     currentValue,
643     minimumIncrement,
644     maximumValue,
645     minimumValue};
646 }
647
648 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)
649 {
650   // Please be aware of sending GestureInfo point in the different order with parameters
651   return FindSelf()->DoGesture(Dali::Accessibility::GestureInfo{type, startPositionX, endPositionX, startPositionY, endPositionY, state, eventTime});
652 }
653
654 DBus::ValueOrError<Accessible*, uint8_t, Accessible*> BridgeAccessible::GetNavigableAtPoint(int32_t x, int32_t y, uint32_t coordinateType)
655 {
656   Accessible* deputy     = nullptr;
657   auto        accessible = FindSelf();
658   auto        cType      = static_cast<CoordinateType>(coordinateType);
659
660   x -= mData->mExtentsOffset.first;
661   y -= mData->mExtentsOffset.second;
662
663   LOG() << "GetNavigableAtPoint: " << x << ", " << y << " type: " << coordinateType;
664   auto component = CalculateNavigableAccessibleAtPoint(accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH);
665   bool recurse   = false;
666   if(component)
667   {
668     recurse = component->IsProxy();
669   }
670   //TODO: add deputy
671   return {component, recurse, deputy};
672 }
673
674 Accessible* BridgeAccessible::GetCurrentlyHighlighted()
675 {
676   //TODO: add currently highlighted object
677   return nullptr;
678 }
679
680 std::vector<Component*> BridgeAccessible::GetValidChildren(const std::vector<Accessible*>& children, Accessible* start)
681 {
682   if(children.empty())
683   {
684     return {};
685   }
686
687   std::vector<Component*> vec;
688
689   Dali::Rect<> scrollableParentExtents;
690   auto         nonDuplicatedScrollableParents = GetNonDuplicatedScrollableParents(children.front(), start);
691   if(!nonDuplicatedScrollableParents.empty())
692   {
693     scrollableParentExtents = nonDuplicatedScrollableParents.front()->GetExtents(CoordinateType::WINDOW);
694   }
695
696   for(auto child : children)
697   {
698     auto* component = dynamic_cast<Component*>(child);
699     if(component)
700     {
701       if(nonDuplicatedScrollableParents.empty() || scrollableParentExtents.Intersects(component->GetExtents(CoordinateType::WINDOW)))
702       {
703         vec.push_back(component);
704       }
705     }
706   }
707
708   return vec;
709 }
710
711 void BridgeAccessible::SortChildrenFromTopLeft(std::vector<Dali::Accessibility::Component*>& children)
712 {
713   if(children.empty())
714   {
715     return;
716   }
717
718   std::vector<Component*> sortedChildren;
719
720   std::sort(children.begin(), children.end(), &SortVertically);
721
722   for(auto& line : SplitLines(children))
723   {
724     std::sort(line.begin(), line.end(), &SortHorizontally);
725     sortedChildren.insert(sortedChildren.end(), line.begin(), line.end());
726   }
727
728   children = sortedChildren;
729 }
730
731 template<class T>
732 struct CycleDetection
733 {
734   CycleDetection(const T value)
735   : mKey(value),
736     mCurrentSearchSize(1),
737     mCounter(1)
738   {
739   }
740
741   bool Check(const T value)
742   {
743     if(mKey == value)
744     {
745       return true;
746     }
747
748     if(--mCounter == 0)
749     {
750       mCurrentSearchSize <<= 1;
751       if(mCurrentSearchSize == 0)
752       {
753         return true; // UNDEFINED BEHAVIOR
754       }
755       mCounter = mCurrentSearchSize;
756       mKey     = value;
757     }
758     return false;
759   }
760
761   T            mKey;
762   unsigned int mCurrentSearchSize;
763   unsigned int mCounter;
764 };
765
766 Accessible* BridgeAccessible::GetNextNonDefunctSibling(Accessible* obj, Accessible* start, unsigned char forward)
767 {
768   if(!obj)
769   {
770     return nullptr;
771   }
772
773   auto parent = obj->GetParent();
774   if(!parent)
775   {
776     return nullptr;
777   }
778   else if(parent->IsProxy())
779   {
780     return parent;
781   }
782
783   auto children = GetValidChildren(parent->GetChildren(), start);
784   SortChildrenFromTopLeft(children);
785
786   unsigned int childrenCount = children.size();
787   if(childrenCount == 0)
788   {
789     return nullptr;
790   }
791
792   unsigned int current = 0;
793   for(; current < childrenCount && children[current] != obj; ++current)
794   {
795   }
796
797   if(current >= childrenCount)
798   {
799     return nullptr;
800   }
801
802   forward ? ++current : --current;
803   auto ret = FindNonDefunctChild(children, current, forward);
804   return ret;
805 }
806
807 Accessible* BridgeAccessible::FindNonDefunctSibling(bool& areAllChildrenVisited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward)
808 {
809   while(true)
810   {
811     Accessible* sibling = GetNextNonDefunctSibling(node, start, forward);
812     if(sibling)
813     {
814       node                  = sibling;
815       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.
816       break;
817     }
818     // walk up...
819     node = node->GetParent();
820     if(node == nullptr || node == root)
821     {
822       return nullptr;
823     }
824
825     // in backward traversing stop the walk up on parent
826     if(!forward)
827     {
828       break;
829     }
830   }
831
832   return node;
833 }
834
835 Accessible* BridgeAccessible::CalculateNeighbor(Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::NeighborSearchMode searchMode)
836 {
837   if(start && CheckChainEndWithAttribute(start, forward))
838   {
839     return start;
840   }
841   if(root && root->GetStates()[State::DEFUNCT])
842   {
843     return NULL;
844   }
845   if(start && start->GetStates()[State::DEFUNCT])
846   {
847     start   = NULL;
848     forward = 1;
849   }
850
851   if(searchMode == BridgeAccessible::NeighborSearchMode::RECURSE_TO_OUTSIDE)
852   {
853     searchMode = BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION;
854   }
855
856   Accessible* node = start ? start : root;
857   if(!node)
858   {
859     return nullptr;
860   }
861
862   // initialization of all-children-visited flag for start node - we assume
863   // that when we begin at start node and we navigate backward, then all children
864   // are visited, so navigation will ignore start's children and go to
865   // previous sibling available.
866   // Regarding condtion (start != root):
867   // The last object can be found only if areAllChildrenVisited is false.
868   // The start is same with root, when looking for the last object.
869   bool areAllChildrenVisited = (start != root) && (searchMode != BridgeAccessible::NeighborSearchMode::RECURSE_FROM_ROOT && !forward);
870
871   // true, if starting element should be ignored. this is only used in rare case of
872   // recursive search failing to find an object.
873   // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from
874   // element A algorithm has to descend into BUS_B and search element B and its children. this is done
875   // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B).
876   // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out
877   // and will call us again with object A and flag searchMode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING.
878   // this flag means, that object A was already checked previously and we should skip it and its children.
879   bool forceNext = (searchMode == BridgeAccessible::NeighborSearchMode::CONTINUE_AFTER_FAILED_RECURSION);
880
881   CycleDetection<Accessible*> cycleDetection(node);
882   while(node)
883   {
884     if(node->GetStates()[State::DEFUNCT])
885     {
886       return nullptr;
887     }
888
889     // always accept proxy object from different world
890     if(!forceNext && node->IsProxy())
891     {
892       return node;
893     }
894
895     auto children = GetValidChildren(node->GetChildren(), start);
896     SortChildrenFromTopLeft(children);
897
898     // do accept:
899     // 1. not start node
900     // 2. parent after all children in backward traversing
901     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
902     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
903     bool areAllChildrenVisitedOrMovingForward = (children.size() == 0 || forward || areAllChildrenVisited);
904
905     if(!forceNext && node != start && areAllChildrenVisitedOrMovingForward && IsObjectAcceptable(node))
906     {
907       if(start == NULL || IsRoleAcceptableWhenNavigatingNextPrev(node))
908       {
909         return node;
910       }
911     }
912
913     Accessible* nextRelatedInDirection = !forceNext ? GetObjectInRelation(node, forward ? RelationType::FLOWS_TO : RelationType::FLOWS_FROM) : nullptr;
914     if(nextRelatedInDirection && start && start->GetStates()[State::DEFUNCT])
915     {
916       nextRelatedInDirection = NULL;
917     }
918
919     unsigned char wantCycleDetection = 0;
920     if(nextRelatedInDirection)
921     {
922       node               = nextRelatedInDirection;
923       wantCycleDetection = 1;
924     }
925     else
926     {
927       auto child = !forceNext && !areAllChildrenVisited ? FindNonDefunctChildWithDepthFirstSearch(node, children, forward) : nullptr;
928       if(child)
929       {
930         wantCycleDetection = 1;
931       }
932       else
933       {
934         if(!forceNext && node == root)
935         {
936           return NULL;
937         }
938         areAllChildrenVisited = true;
939         child                 = FindNonDefunctSibling(areAllChildrenVisited, node, start, root, forward);
940       }
941       node = child;
942     }
943
944     forceNext = 0;
945     if(wantCycleDetection && cycleDetection.Check(node))
946     {
947       return NULL;
948     }
949   }
950   return NULL;
951 }
952
953 DBus::ValueOrError<Accessible*, uint8_t> BridgeAccessible::GetNeighbor(std::string rootPath, int32_t direction, int32_t searchMode)
954 {
955   auto          start      = FindSelf();
956   auto          root       = !rootPath.empty() ? Find(StripPrefix(rootPath)) : nullptr;
957   auto          accessible = CalculateNeighbor(root, start, direction == 1, static_cast<NeighborSearchMode>(searchMode));
958   unsigned char recurse    = 0;
959   if(accessible)
960   {
961     recurse = accessible->IsProxy();
962   }
963   return {accessible, recurse};
964 }
965
966 Accessible* BridgeAccessible::GetParent()
967 {
968   // NOTE: currently bridge supports single application root element.
969   // only element set as application root might return nullptr from GetParent
970   // if you want more, then you need to change setApplicationRoot to
971   // add/remove ApplicationRoot and make roots a vector.
972   auto parent = FindSelf()->GetParent();
973
974   return parent;
975 }
976
977 DBus::ValueOrError<std::vector<Accessible*>> BridgeAccessible::GetChildren()
978 {
979   return FindSelf()->GetChildren();
980 }
981
982 std::string BridgeAccessible::GetDescription()
983 {
984   return FindSelf()->GetDescription();
985 }
986
987 DBus::ValueOrError<uint32_t> BridgeAccessible::GetRole()
988 {
989   return static_cast<unsigned int>(FindSelf()->GetRole());
990 }
991
992 DBus::ValueOrError<std::string> BridgeAccessible::GetRoleName()
993 {
994   return FindSelf()->GetRoleName();
995 }
996
997 DBus::ValueOrError<std::string> BridgeAccessible::GetLocalizedRoleName()
998 {
999   return FindSelf()->GetLocalizedRoleName();
1000 }
1001
1002 DBus::ValueOrError<int32_t> BridgeAccessible::GetIndexInParent()
1003 {
1004   return FindSelf()->GetIndexInParent();
1005 }
1006
1007 DBus::ValueOrError<std::array<uint32_t, 2>> BridgeAccessible::GetStates()
1008 {
1009   return FindSelf()->GetStates().GetRawData();
1010 }
1011
1012 DBus::ValueOrError<std::unordered_map<std::string, std::string>> BridgeAccessible::GetAttributes()
1013 {
1014   std::unordered_map<std::string, std::string> attributes = FindSelf()->GetAttributes();
1015
1016   if(mIsScreenReaderSuppressed)
1017   {
1018     attributes.insert({"suppress-screen-reader", "true"});
1019   }
1020
1021   return attributes;
1022 }
1023
1024 DBus::ValueOrError<std::vector<std::string>> BridgeAccessible::GetInterfacesAsStrings()
1025 {
1026   return FindSelf()->GetInterfacesAsStrings();
1027 }
1028
1029 int BridgeAccessible::GetChildCount()
1030 {
1031   return FindSelf()->GetChildCount();
1032 }
1033
1034 DBus::ValueOrError<Accessible*> BridgeAccessible::GetChildAtIndex(int index)
1035 {
1036   if(index < 0)
1037   {
1038     throw std::domain_error{"negative index (" + std::to_string(index) + ")"};
1039   }
1040   return FindSelf()->GetChildAtIndex(static_cast<size_t>(index));
1041 }
1042
1043 std::string BridgeAccessible::GetName()
1044 {
1045   return FindSelf()->GetName();
1046 }
1047
1048 DBus::ValueOrError<Accessible*, uint32_t, std::unordered_map<std::string, std::string>> BridgeAccessible::GetDefaultLabelInfo()
1049 {
1050   auto* defaultLabel = GetDefaultLabel(FindSelf());
1051   DALI_ASSERT_DEBUG(defaultLabel);
1052
1053   // By default, the text is taken from navigation context root's accessibility properties name and description.
1054   return {defaultLabel, static_cast<uint32_t>(defaultLabel->GetRole()), defaultLabel->GetAttributes()};
1055 }
1056
1057 DBus::ValueOrError<std::vector<BridgeAccessible::Relation>> BridgeAccessible::GetRelationSet()
1058 {
1059   auto                                    relations = FindSelf()->GetRelationSet();
1060   std::vector<BridgeAccessible::Relation> ret;
1061
1062   for(auto& it : relations)
1063   {
1064     ret.emplace_back(Relation{static_cast<uint32_t>(it.mRelationType), it.mTargets});
1065   }
1066
1067   return ret;
1068 }
1069
1070 DBus::ValueOrError<void> BridgeAccessible::SetListenPostRender(bool enabled)
1071 {
1072   FindSelf()->SetListenPostRender(enabled);
1073   return {};
1074 }