[Tizen][ATSPI] Accessibility initial implementation
[platform/core/uifw/dali-adaptor.git] / dali / dali-bridge / src / BridgeAccessible.cpp
1 #include "BridgeAccessible.hpp"
2 #include <iostream>
3
4 using namespace Dali::Accessibility;
5
6 #define GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH 10000
7
8 BridgeAccessible::BridgeAccessible()
9 {
10 }
11
12 void BridgeAccessible::RegisterInterfaces()
13 {
14   DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_ACCESSIBLE};
15   AddGetPropertyToInterface( desc, "ChildCount", &BridgeAccessible::GetChildCount );
16   AddGetPropertyToInterface( desc, "Name", &BridgeAccessible::GetName );
17   AddGetPropertyToInterface( desc, "Description", &BridgeAccessible::GetDescription );
18   AddGetPropertyToInterface( desc, "Parent", &BridgeAccessible::GetParent );
19   AddFunctionToInterface( desc, "GetRole", &BridgeAccessible::GetRole );
20   AddFunctionToInterface( desc, "GetRoleName", &BridgeAccessible::GetRoleName );
21   AddFunctionToInterface( desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName );
22   AddFunctionToInterface( desc, "GetState", &BridgeAccessible::GetStates );
23   AddFunctionToInterface( desc, "GetAttributes", &BridgeAccessible::GetAttributes );
24   AddFunctionToInterface( desc, "GetInterfaces", &BridgeAccessible::GetInterfaces );
25   AddFunctionToInterface( desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex );
26   AddFunctionToInterface( desc, "GetChildren", &BridgeAccessible::GetChildren );
27   AddFunctionToInterface( desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent );
28   AddFunctionToInterface( desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint );
29   AddFunctionToInterface( desc, "GetNeighbor", &BridgeAccessible::GetNeighbor );
30   AddFunctionToInterface( desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo );
31   AddFunctionToInterface( desc, "DoGesture", &BridgeAccessible::DoGesture );
32   AddFunctionToInterface( desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial );
33   dbusServer.addInterface( "/", desc, true );
34 }
35
36 static bool AcceptObjectCheckRole( Component* obj )
37 {
38   if( !obj )
39     return false;
40   switch( obj->GetRole() )
41   {
42     case Role::Application:
43     case Role::Filler:
44     case Role::ScrollPane:
45     case Role::SplitPane:
46     case Role::Window:
47     case Role::Image:
48     case Role::ImageMap:
49     case Role::List:
50     case Role::Icon:
51     case Role::ToolBar:
52     case Role::RedundantObject:
53     case Role::ColorChooser:
54     case Role::TreeTable:
55     case Role::PageTabList:
56     case Role::PageTab:
57     case Role::SpinButton:
58     case Role::InputMethodWindow:
59     case Role::Embedded:
60     case Role::Invalid:
61     case Role::Notification:
62     case Role::DateEditor:
63     {
64       return false;
65     }
66     default:
67     {
68       break;
69     }
70   }
71
72   return true;
73 }
74
75 static Component* GetScrollableParent( Accessible* obj )
76 {
77   while( obj )
78   {
79     obj = obj->GetParent();
80     auto comp = dynamic_cast< Component* >( obj );
81     if( comp && comp->IsScrollable() )
82       return comp;
83   }
84   return nullptr;
85 }
86
87 static bool ObjectIsItem( Component* obj )
88 {
89   if( !obj )
90     return false;
91   auto role = obj->GetRole();
92   return role == Role::ListItem || role == Role::MenuItem;
93 }
94
95 static bool ObjectIsCollapsed( Component* obj )
96 {
97   if( !obj )
98     return false;
99   const auto states = obj->GetStates();
100   return states[State::Expandable] && !states[State::Expanded];
101 }
102
103 static bool OobjectIsZeroSize( Component* obj )
104 {
105   if( !obj )
106     return false;
107   auto size = obj->GetExtents( CoordType::Window ).size;
108   return size.height == 0 || size.width == 0;
109 }
110
111 static bool AcceptObject( Component* obj )
112 {
113   if( !obj )
114     return false;
115   const auto states = obj->GetStates();
116   if( !states[State::Visible] )
117     return false;
118   if( !AcceptObjectCheckRole( obj ) )
119     return false;
120 /*
121   TODO: add relations
122   if (CALL(get_object_in_relation_by_type, obj, ATSPI_RELATION_CONTROLLED_BY) != NULL) return 0;
123 */
124   if( !states[State::Highlightable] )
125     return false;
126
127   if( GetScrollableParent( obj ) != nullptr )
128   {
129     auto parent = dynamic_cast< Component* >( obj->GetParent() );
130
131     if( parent )
132     {
133       return !ObjectIsItem( obj ) || !ObjectIsCollapsed( parent );
134     }
135   }
136   else
137   {
138     if( OobjectIsZeroSize( obj ) )
139     {
140       return false;
141     }
142     if( !states[State::Showing] )
143     {
144       return false;
145     }
146   }
147   return true;
148 }
149
150 static bool AcceptObject( Accessible* obj )
151 {
152   auto c = dynamic_cast< Component* >( obj );
153   return AcceptObject( c );
154 }
155
156 static bool AcceptObjectOrProxy( Component* obj )
157 {
158   return obj->IsProxy() || AcceptObject( obj );
159 }
160
161 static Component* CalculateNavigableAccessibleAtPoint( Accessible* root, Point p, CoordType cType, unsigned int maxRecursionDepth )
162 {
163   if( !root || maxRecursionDepth == 0 )
164     return nullptr;
165   auto root_component = dynamic_cast< Component* >( root );
166   if( root_component && !root_component->Contains( p, cType ) )
167   {
168     return nullptr;
169   }
170
171   auto children = root->GetChildren();
172   for( auto childIt = children.rbegin(); childIt != children.rend(); childIt++ )
173   {
174     auto result = CalculateNavigableAccessibleAtPoint( *childIt, p, cType, maxRecursionDepth - 1 );
175     if( result )
176       return result;
177   }
178   if( root_component && AcceptObjectOrProxy( root_component ) )
179     return root_component;
180   return nullptr;
181
182   /*
183   TODO: add relations
184   void *relation_obj = CALL(get_object_in_relation_by_type, root, ATSPI_RELATION_CONTROLLED_BY);
185   unsigned char contains = 0;
186   if (relation_obj)
187   {
188     contains = CALL(object_contains, relation_obj, x, y, coordinates_are_screen_based);
189     if (contains) root = relation_obj;
190   }
191   */
192 }
193
194 BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial()
195 {
196   auto self = FindSelf();
197   auto attributes = self->GetAttributes();
198   auto name = self->GetName();
199   std::string labeledByName = "";
200   std::string textIfceName = "";
201   auto role = static_cast< uint32_t >( self->GetRole() );
202   auto states = self->GetStates();
203   auto localizedName = self->GetLocalizedRoleName();
204   auto childCount = static_cast< int32_t >( self->GetChildCount() );
205
206   double currentValue = 0.0;
207   double minimumIncrement = 0.0;
208   double maximumValue = 0.0;
209   double minimumValue = 0.0;
210
211   auto description = self->GetDescription();
212   auto indexInParent = static_cast< int32_t >( self->GetIndexInParent() );
213   bool isSelectedInParent = false;
214   bool hasCheckBoxChild = false;
215   int32_t firstSelectedChildIndex = 0;
216   int32_t selectedChildCount = 0;
217
218   for( auto i = 0u; i < static_cast< size_t >( childCount ); ++i )
219   {
220     auto q = self->GetChildAtIndex( i );
221     auto s = q->GetStates();
222     if( s[State::Selectable] )
223     {
224       ++selectedChildCount;
225       if( s[State::Selected] )
226       {
227         if( firstSelectedChildIndex < 0 )
228           firstSelectedChildIndex = static_cast< int32_t >( i );
229       }
230     }
231     if( q->GetRole() == Role::CheckBox )
232       hasCheckBoxChild = true;
233   }
234
235   int32_t listChildrenCount = 0;
236   Accessible* parent = self->GetParent();
237   auto parentStateSet = parent ? parent->GetStates() : States{};
238   auto parentChildCount = parent ? static_cast< int32_t >( parent->GetChildCount() ) : 0;
239   auto parentRole = static_cast< uint32_t >( parent ? parent->GetRole() : Role{} );
240   Accessible* describedByObject = nullptr;
241
242   return {
243       attributes,
244       name,
245       labeledByName,
246       textIfceName,
247       role,
248       states,
249       localizedName,
250       childCount,
251       currentValue,
252       minimumIncrement,
253       maximumValue,
254       minimumValue,
255       description,
256       indexInParent,
257       isSelectedInParent,
258       hasCheckBoxChild,
259       listChildrenCount,
260       firstSelectedChildIndex,
261       parent,
262       parentStateSet,
263       parentChildCount,
264       parentRole,
265       selectedChildCount,
266       describedByObject};
267 }
268
269 DBus::ValueOrError< bool > BridgeAccessible::DoGesture( int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, uint32_t )
270 {
271   return false;
272 }
273
274 DBus::ValueOrError< Accessible*, uint8_t, Accessible* > BridgeAccessible::GetNavigableAtPoint( int32_t x, int32_t y, uint32_t coordType )
275 {
276   SCOPE();
277   Accessible* deputy = nullptr;
278   auto accessible = FindSelf();
279   auto cType = static_cast< CoordType >( coordType );
280   auto component = CalculateNavigableAccessibleAtPoint( accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH );
281   bool recurse = false;
282   if( component )
283   {
284     const auto states = component->GetStates();
285     if( states[State::Modal] )
286     {
287       component = nullptr;
288     }
289   }
290   if( component )
291   {
292     recurse = component->IsProxy();
293   }
294   //TODO: add deputy
295   return {component, recurse, deputy};
296 }
297
298 static bool CheckChainEndWithAttribute( Accessible* obj, unsigned char forward )
299 {
300   if( !obj )
301     return false;
302   auto attrs = obj->GetAttributes();
303   for( auto& attr : attrs )
304   {
305     if( attr.first == "relation_chain_end" )
306     {
307       if( ( attr.second == "prev,end" && forward == 0 ) || ( attr.second == "next,end" && forward == 1 ) || attr.second == "prev,next,end" )
308       {
309         return true;
310       }
311     }
312   }
313   return false;
314 }
315
316 static Accessible* DeputyOfProxyInParentGet( Accessible* obj )
317 {
318   return nullptr;
319 /*
320 if (!obj)
321   return nullptr;
322
323 Accessible *deputy = nullptr;
324 auto children = obj->GetChildren();
325 unsigned int index = 0;
326 for (auto child : children) {
327   if (child->IsProxy()) {
328     if (index == 0) {
329       //WRN("Proxy does not have deputy object");
330       break;
331     }
332     deputy = children[index - 1];
333     break;
334   }
335   index++;
336 }
337 return deputy;
338 */
339 }
340
341 Accessible* BridgeAccessible::GetCurrentlyHighlighted()
342 {
343   //TODO: add currently highlighted object
344   return nullptr;
345 }
346
347 std::vector< Accessible* > BridgeAccessible::ValidChildrenGet( const std::vector< Accessible* >& children, Accessible* start, Accessible* root )
348 {
349   /* condition to find first(last) object regardless of scrollable parent.
350       looping navigation does not care scrollable parent.
351       1. currently highlighted object exists
352       2. both start and root are same */
353
354   /* TODO: add code, we need a scrollable implementation first
355   Accessible *current = GetCurrentlyHighlighted();
356    if (current && start == root) return children;
357    if(children.size() == 0) return {};
358
359    Eo *child = children[0];
360
361    if (child)
362      {
363         Evas_Coord x = 0, y = 0, w = 0, h = 0;
364         Evas_Coord sx = 0, sy = 0, sw = 0, sh = 0;
365
366         if (_new_scrollable_parent_viewport_geometry_get(child, start,
367                                                    &sx, &sy, &sw, &sh))
368           {
369              Eina_List *l, *l_next;
370              EINA_LIST_FOREACH_SAFE(children, l, l_next, child)
371                {
372                   eo_do(child,
373                         elm_interface_atspi_component_extents_get(EINA_FALSE,
374                                                              &x, &y, &w, &h));
375                   if (w == 0 || h == 0 ||
376                       !ELM_RECTS_INTERSECT(x, y, w, h, sx, sy, sw, sh))
377                      children = eina_list_remove_list(children, l);
378                }
379           }
380      }
381      */
382   return children;
383 }
384
385 static bool DeputyIs( Accessible* obj )
386 {
387   //TODO: add deputy
388   return false;
389 }
390
391 static Accessible* GetObjectInRelationFlow( Accessible* ptr, RelationType type )
392 {
393   //TODO: add relations
394   return nullptr;
395 }
396 static Accessible* ProxyInParentGet( Accessible* obj )
397 {
398   if( !obj )
399     return nullptr;
400   auto children = obj->GetChildren();
401   for( auto& child : children )
402   {
403     if( child->IsProxy() )
404       return child;
405   }
406   return nullptr;
407 }
408
409 static bool ObjectRoleIsAcceptableWhenNavigatingNextPrev( Accessible* obj )
410 {
411   if( !obj )
412     return false;
413   auto role = obj->GetRole();
414   return role != Role::PopupMenu && role != Role::Dialog;
415 }
416
417 template < class T >
418 struct CycleDetection
419 {
420   CycleDetection( const T value ) : key( value ), currentSearchSize( 1 ), counter( 1 ) {}
421   bool check( const T value )
422   {
423     if( key == value )
424       return true;
425     if( --counter == 0 )
426     {
427       currentSearchSize <<= 1;
428       if( currentSearchSize == 0 )
429         return true; // UNDEFINED BEHAVIOR
430       counter = currentSearchSize;
431       key = value;
432     }
433     return false;
434   }
435   T key;
436   unsigned int currentSearchSize;
437   unsigned int counter;
438 };
439
440 static Accessible* FindNonDefunctChild( const std::vector< Accessible* >& children, unsigned int currentIndex, unsigned char forward )
441 {
442   unsigned int childrenCount = children.size();
443   for( ; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex )
444   {
445     Accessible* n = children[currentIndex];
446     if( n && !n->GetStates()[State::Defunct] )
447       return n;
448   }
449   return nullptr;
450 }
451
452 static Accessible* DirectionalDepthFirstSearchTryNonDefunctChild( Accessible* node, const std::vector< Accessible* >& children, unsigned char forward )
453 {
454   if( !node )
455     return nullptr;
456   auto childrenCount = children.size();
457   if( childrenCount > 0 )
458   {
459     const bool isShowing = GetScrollableParent( node ) == nullptr ? node->GetStates()[State::Showing] : true;
460     if( isShowing )
461     {
462       return FindNonDefunctChild( children, forward ? 0 : childrenCount - 1, forward );
463     }
464   }
465   return nullptr;
466 }
467
468 Accessible* BridgeAccessible::GetNextNonDefunctSibling( Accessible* obj, Accessible* start, Accessible* root, unsigned char forward )
469 {
470   if( !obj )
471     return nullptr;
472   auto parent = obj->GetParent();
473   if( !parent )
474     return nullptr;
475
476   auto children = ValidChildrenGet( parent->GetChildren(), start, root );
477
478   unsigned int children_count = children.size();
479   if( children_count == 0 )
480   {
481     return nullptr;
482   }
483   unsigned int current = 0;
484   for( ; current < children_count && children[current] != obj; ++current )
485     ;
486   if( current >= children_count )
487   {
488     return nullptr;
489   }
490   forward ? ++current : --current;
491   auto ret = FindNonDefunctChild( children, current, forward );
492   return ret;
493 }
494
495 Accessible* BridgeAccessible::DirectionalDepthFirstSearchTryNonDefunctSibling( bool& all_children_visited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward )
496 {
497   while( true )
498   {
499     Accessible* sibling = GetNextNonDefunctSibling( node, start, root, forward );
500     if( sibling )
501     {
502       node = sibling;
503       all_children_visited = false;
504       break;
505     }
506     // walk up...
507     node = node->GetParent();
508     if( node == nullptr || node == root )
509       return nullptr;
510
511     // in backward traversing stop the walk up on parent
512     if( !forward )
513       break;
514   }
515   return node;
516 }
517
518 Accessible* BridgeAccessible::CalculateNeighbor( Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::GetNeighborSearchMode search_mode )
519 {
520   if( start && CheckChainEndWithAttribute( start, forward ) )
521     return start;
522   if( root && root->GetStates()[State::Defunct] )
523     return NULL;
524   if( start && start->GetStates()[State::Defunct] )
525   {
526     start = NULL;
527     forward = 1;
528   }
529
530   if( search_mode == BridgeAccessible::GetNeighborSearchMode::recurseToOutside )
531   {
532     /* This only works if we navigate backward, and it is not possible to
533      find in embedded process. In this case the deputy should be used */
534     return DeputyOfProxyInParentGet( start );
535   }
536
537   Accessible* node = start ? start : root;
538   if( !node )
539     return nullptr;
540
541   // initialization of all-children-visited flag for start node - we assume
542   // that when we begin at start node and we navigate backward, then all children
543   // are visited, so navigation will ignore start's children and go to
544   // previous sibling available.
545   /* Regarding condtion (start != root):
546    The last object can be found only if all_children_visited is false.
547    The start is same with root, when looking for the last object. */
548   bool all_children_visited = ( start != root ) && ( search_mode != BridgeAccessible::GetNeighborSearchMode::recurseFromRoot && !forward );
549   // true, if starting element should be ignored. this is only used in rare case of
550   // recursive search failing to find an object.
551   // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from
552   // element A algorithm has to descend into BUS_B and search element B and its children. this is done
553   // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B).
554   // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out
555   // and will call us again with object A and flag search_mode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING.
556   // this flag means, that object A was already checked previously and we should skip it and its children.
557   bool force_next = ( search_mode == BridgeAccessible::GetNeighborSearchMode::continueAfterFailedRecursion );
558
559   CycleDetection< Accessible* > cycleDetection( node );
560   while( node )
561   {
562     if( node->GetStates()[State::Defunct] )
563       return nullptr;
564
565     // always accept proxy object from different world
566     if( !force_next && node->IsProxy() )
567       return node;
568
569     auto children = node->GetChildren();
570     children = ValidChildrenGet( children, start, root );
571
572     // do accept:
573     // 1. not start node
574     // 2. parent after all children in backward traversing
575     // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element.
576     //    Objects with those roles shouldnt be reachable, when navigating next / prev.
577     bool all_children_visited_or_moving_forward = ( children.size() == 0 || forward || all_children_visited );
578     if( !force_next && node != start && all_children_visited_or_moving_forward && AcceptObject( node ) )
579     {
580       if( start == NULL || ObjectRoleIsAcceptableWhenNavigatingNextPrev( node ) )
581         return node;
582     }
583
584     Accessible* next_related_in_direction = !force_next ? GetObjectInRelationFlow( node, forward ? RelationType::FlowsTo : RelationType::FlowsFrom ) : nullptr;
585     /* force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING
586       in this case the node is elm_layout which is parent of proxy object.
587       There is an access object working for the proxy object, and the access
588       object could have relation information. This relation information should
589       be checked first before using the elm_layout as a node. */
590     if( force_next && forward )
591     {
592       auto deputy = DeputyOfProxyInParentGet( node );
593       next_related_in_direction =
594           GetObjectInRelationFlow( deputy, forward ? RelationType::FlowsTo : RelationType::FlowsFrom );
595     }
596
597     if( next_related_in_direction && start->GetStates()[State::Defunct] )
598       next_related_in_direction = NULL;
599     unsigned char want_cycle_detection = 0;
600     if( next_related_in_direction )
601     {
602       /* Check next_related_in_direction is deputy object */
603       Accessible* parent;
604       if( !forward )
605       {
606         /* If the prev object is deputy, then go to inside of its proxy first */
607         if( DeputyIs( next_related_in_direction ) )
608         {
609           parent = next_related_in_direction->GetParent();
610           next_related_in_direction = ProxyInParentGet( parent );
611         }
612       }
613       else
614       {
615         /* If current object is deputy, and it has relation next object,
616                    then do not use the relation next object, and use proxy first */
617         if( DeputyIs( node ) )
618         {
619           parent = node->GetParent();
620           next_related_in_direction = ProxyInParentGet( parent );
621         }
622       }
623       node = next_related_in_direction;
624       want_cycle_detection = 1;
625     }
626     else
627     {
628       auto child = !force_next && !all_children_visited ? DirectionalDepthFirstSearchTryNonDefunctChild( node, children, forward ) : nullptr;
629       if( child )
630       {
631         want_cycle_detection = 1;
632       }
633       else
634       {
635         if( !force_next && node == root )
636           return NULL;
637         all_children_visited = true;
638         child = DirectionalDepthFirstSearchTryNonDefunctSibling( all_children_visited, node, start, root, forward );
639       }
640       node = child;
641     }
642     force_next = 0;
643     if( want_cycle_detection && cycleDetection.check( node ) )
644     {
645       return NULL;
646     }
647   }
648   return NULL;
649 }
650
651 DBus::ValueOrError< Accessible*, uint8_t > BridgeAccessible::GetNeighbor( std::string rootPath, int32_t direction, int32_t search_mode )
652 {
653   auto start = FindSelf();
654   rootPath = StripPrefix( rootPath );
655   auto root = !rootPath.empty() ? Find( rootPath ) : nullptr;
656   auto accessible = CalculateNeighbor( root, start, direction == 1, static_cast< GetNeighborSearchMode >( search_mode ) );
657   unsigned char recurse = 0;
658   if( accessible )
659   {
660     recurse = accessible->IsProxy();
661   }
662   return {accessible, recurse};
663 }
664
665 Accessible* BridgeAccessible::GetParent()
666 {
667   // NOTE: currently bridge supports single application root element.
668   // only element set as application root might return nullptr from GetParent
669   // if you want more, then you need to change setApplicationRoot to
670   // add/remove ApplicationRoot and make roots a vector.
671   auto z = FindSelf();
672   auto p = z->GetParent();
673   assert( p );
674   return p;
675 }
676 DBus::ValueOrError< std::vector< Accessible* > > BridgeAccessible::GetChildren()
677 {
678   return FindSelf()->GetChildren();
679 }
680 std::string BridgeAccessible::GetDescription()
681 {
682   return FindSelf()->GetDescription();
683 }
684 DBus::ValueOrError< uint32_t > BridgeAccessible::GetRole()
685 {
686   return static_cast< unsigned int >( FindSelf()->GetRole() );
687 }
688 DBus::ValueOrError< std::string > BridgeAccessible::GetRoleName()
689 {
690   return FindSelf()->GetRoleName();
691 }
692 DBus::ValueOrError< std::string > BridgeAccessible::GetLocalizedRoleName()
693 {
694   return FindSelf()->GetLocalizedRoleName();
695 }
696 DBus::ValueOrError< int32_t > BridgeAccessible::GetIndexInParent()
697 {
698   return FindSelf()->GetIndexInParent();
699 }
700 DBus::ValueOrError< std::array< uint32_t, 2 > > BridgeAccessible::GetStates()
701 {
702   return FindSelf()->GetStates().GetRawData();
703 }
704 DBus::ValueOrError< std::unordered_map< std::string, std::string > > BridgeAccessible::GetAttributes()
705 {
706   return FindSelf()->GetAttributes();
707 }
708 DBus::ValueOrError< std::vector< std::string > > BridgeAccessible::GetInterfaces()
709 {
710   return FindSelf()->GetInterfaces();
711 }
712 int BridgeAccessible::GetChildCount()
713 {
714   return FindSelf()->GetChildCount();
715 }
716 DBus::ValueOrError< Accessible* > BridgeAccessible::GetChildAtIndex( int index )
717 {
718   if( index < 0 )
719     throw AccessibleError{"negative index (" + std::to_string( index ) + ")"};
720   return FindSelf()->GetChildAtIndex( static_cast< size_t >( index ) );
721 }
722
723 std::string BridgeAccessible::GetName()
724 {
725   return FindSelf()->GetName();
726 }
727
728 DBus::ValueOrError< Accessible*, uint32_t > BridgeAccessible::GetDefaultLabelInfo()
729 {
730   auto p = FindSelf();
731   return {p, static_cast< uint32_t >( p->GetRole() )};
732 }