1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/uno/Any.hxx>
21 #include <com/sun/star/uno/Type.hxx>
22 #include <com/sun/star/uno/Sequence.hxx>
23 #include <com/sun/star/accessibility/AccessibleRole.hpp>
24 #include <com/sun/star/accessibility/AccessibleRelation.hpp>
25 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
26 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
27 #include <com/sun/star/accessibility/XAccessible.hpp>
28 #include <com/sun/star/accessibility/XAccessibleText.hpp>
29 #include <com/sun/star/accessibility/XAccessibleValue.hpp>
30 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
31 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
32 #include <com/sun/star/accessibility/XAccessibleContext2.hpp>
33 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
34 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
35 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
36 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
37 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
38 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
39 #include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
40 #include <com/sun/star/accessibility/XAccessibleImage.hpp>
41 #include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
42 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
43 #include <com/sun/star/awt/XWindow.hpp>
44 
45 #include <rtl/strbuf.hxx>
46 #include <osl/diagnose.h>
47 #include <tools/diagnose_ex.h>
48 #include <vcl/syschild.hxx>
49 #include <vcl/sysdata.hxx>
50 #include <vcl/toolkit/unowrap.hxx>
51 
52 #include "atkwrapper.hxx"
53 #include "atkregistry.hxx"
54 #include "atklistener.hxx"
55 #include "atktextattributes.hxx"
56 
57 #include <vector>
58 #include <dlfcn.h>
59 
60 using namespace ::com::sun::star;
61 
62 static GObjectClass *parent_class = nullptr;
63 
mapRelationType(sal_Int16 nRelation)64 static AtkRelationType mapRelationType( sal_Int16 nRelation )
65 {
66     AtkRelationType type = ATK_RELATION_NULL;
67 
68     switch( nRelation )
69     {
70         case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM:
71             type = ATK_RELATION_FLOWS_FROM;
72             break;
73 
74         case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO:
75             type = ATK_RELATION_FLOWS_TO;
76             break;
77 
78         case accessibility::AccessibleRelationType::CONTROLLED_BY:
79             type = ATK_RELATION_CONTROLLED_BY;
80             break;
81 
82         case accessibility::AccessibleRelationType::CONTROLLER_FOR:
83             type = ATK_RELATION_CONTROLLER_FOR;
84             break;
85 
86         case accessibility::AccessibleRelationType::LABEL_FOR:
87             type = ATK_RELATION_LABEL_FOR;
88             break;
89 
90         case accessibility::AccessibleRelationType::LABELED_BY:
91             type = ATK_RELATION_LABELLED_BY;
92             break;
93 
94         case accessibility::AccessibleRelationType::MEMBER_OF:
95             type = ATK_RELATION_MEMBER_OF;
96             break;
97 
98         case accessibility::AccessibleRelationType::SUB_WINDOW_OF:
99             type = ATK_RELATION_SUBWINDOW_OF;
100             break;
101 
102         case accessibility::AccessibleRelationType::NODE_CHILD_OF:
103             type = ATK_RELATION_NODE_CHILD_OF;
104             break;
105 
106         default:
107             break;
108     }
109 
110     return type;
111 }
112 
mapAtkState(sal_Int16 nState)113 AtkStateType mapAtkState( sal_Int16 nState )
114 {
115     AtkStateType type = ATK_STATE_INVALID;
116 
117     // A perfect / complete mapping ...
118     switch( nState )
119     {
120 #define MAP_DIRECT( a ) \
121         case accessibility::AccessibleStateType::a: \
122             type = ATK_STATE_##a; break
123 
124         MAP_DIRECT( INVALID );
125         MAP_DIRECT( ACTIVE );
126         MAP_DIRECT( ARMED );
127         MAP_DIRECT( BUSY );
128         MAP_DIRECT( CHECKED );
129         MAP_DIRECT( EDITABLE );
130         MAP_DIRECT( ENABLED );
131         MAP_DIRECT( EXPANDABLE );
132         MAP_DIRECT( EXPANDED );
133         MAP_DIRECT( FOCUSABLE );
134         MAP_DIRECT( FOCUSED );
135         MAP_DIRECT( HORIZONTAL );
136         MAP_DIRECT( ICONIFIED );
137         MAP_DIRECT( INDETERMINATE );
138         MAP_DIRECT( MANAGES_DESCENDANTS );
139         MAP_DIRECT( MODAL );
140         MAP_DIRECT( MULTI_LINE );
141         MAP_DIRECT( OPAQUE );
142         MAP_DIRECT( PRESSED );
143         MAP_DIRECT( RESIZABLE );
144         MAP_DIRECT( SELECTABLE );
145         MAP_DIRECT( SELECTED );
146         MAP_DIRECT( SENSITIVE );
147         MAP_DIRECT( SHOWING );
148         MAP_DIRECT( SINGLE_LINE );
149         MAP_DIRECT( STALE );
150         MAP_DIRECT( TRANSIENT );
151         MAP_DIRECT( VERTICAL );
152         MAP_DIRECT( VISIBLE );
153         MAP_DIRECT( DEFAULT );
154         // a spelling error ...
155         case accessibility::AccessibleStateType::DEFUNC:
156             type = ATK_STATE_DEFUNCT; break;
157         case accessibility::AccessibleStateType::MULTI_SELECTABLE:
158             type = ATK_STATE_MULTISELECTABLE; break;
159     default:
160         //Mis-use ATK_STATE_LAST_DEFINED to check if a state is unmapped
161         //NOTE! Do not report it
162         type = ATK_STATE_LAST_DEFINED;
163         break;
164     }
165 
166     return type;
167 }
168 
getRoleForName(const gchar * name)169 static AtkRole getRoleForName( const gchar * name )
170 {
171     AtkRole ret = atk_role_for_name( name );
172     if( ATK_ROLE_INVALID == ret )
173     {
174         // this should only happen in old ATK versions
175         SAL_WNODEPRECATED_DECLARATIONS_PUSH
176         ret = atk_role_register( name );
177         SAL_WNODEPRECATED_DECLARATIONS_POP
178     }
179 
180     return ret;
181 }
182 
mapToAtkRole(sal_Int16 nRole)183 static AtkRole mapToAtkRole( sal_Int16 nRole )
184 {
185     AtkRole role = ATK_ROLE_UNKNOWN;
186 
187     static AtkRole roleMap[] = {
188         ATK_ROLE_UNKNOWN,
189         ATK_ROLE_ALERT,
190         ATK_ROLE_COLUMN_HEADER,
191         ATK_ROLE_CANVAS,
192         ATK_ROLE_CHECK_BOX,
193         ATK_ROLE_CHECK_MENU_ITEM,
194         ATK_ROLE_COLOR_CHOOSER,
195         ATK_ROLE_COMBO_BOX,
196         ATK_ROLE_DATE_EDITOR,
197         ATK_ROLE_DESKTOP_ICON,
198         ATK_ROLE_DESKTOP_FRAME,   // ? pane
199         ATK_ROLE_DIRECTORY_PANE,
200         ATK_ROLE_DIALOG,
201         ATK_ROLE_UNKNOWN,         // DOCUMENT - registered below
202         ATK_ROLE_UNKNOWN,         // EMBEDDED_OBJECT - registered below
203         ATK_ROLE_UNKNOWN,         // END_NOTE - registered below
204         ATK_ROLE_FILE_CHOOSER,
205         ATK_ROLE_FILLER,
206         ATK_ROLE_FONT_CHOOSER,
207         ATK_ROLE_FOOTER,
208         ATK_ROLE_UNKNOWN,         // FOOTNOTE - registered below
209         ATK_ROLE_FRAME,
210         ATK_ROLE_GLASS_PANE,
211         ATK_ROLE_IMAGE,           // GRAPHIC
212         ATK_ROLE_UNKNOWN,         // GROUP_BOX - registered below
213         ATK_ROLE_HEADER,
214         ATK_ROLE_HEADING,
215         ATK_ROLE_TEXT,            // HYPER_LINK - registered below
216         ATK_ROLE_ICON,
217         ATK_ROLE_INTERNAL_FRAME,
218         ATK_ROLE_LABEL,
219         ATK_ROLE_LAYERED_PANE,
220         ATK_ROLE_LIST,
221         ATK_ROLE_LIST_ITEM,
222         ATK_ROLE_MENU,
223         ATK_ROLE_MENU_BAR,
224         ATK_ROLE_MENU_ITEM,
225         ATK_ROLE_OPTION_PANE,
226         ATK_ROLE_PAGE_TAB,
227         ATK_ROLE_PAGE_TAB_LIST,
228         ATK_ROLE_PANEL,
229         ATK_ROLE_PARAGRAPH,
230         ATK_ROLE_PASSWORD_TEXT,
231         ATK_ROLE_POPUP_MENU,
232         ATK_ROLE_PUSH_BUTTON,
233         ATK_ROLE_PROGRESS_BAR,
234         ATK_ROLE_RADIO_BUTTON,
235         ATK_ROLE_RADIO_MENU_ITEM,
236         ATK_ROLE_ROW_HEADER,
237         ATK_ROLE_ROOT_PANE,
238         ATK_ROLE_SCROLL_BAR,
239         ATK_ROLE_SCROLL_PANE,
240         ATK_ROLE_PANEL,         // SHAPE
241         ATK_ROLE_SEPARATOR,
242         ATK_ROLE_SLIDER,
243         ATK_ROLE_SPIN_BUTTON,   // SPIN_BOX ?
244         ATK_ROLE_SPLIT_PANE,
245         ATK_ROLE_STATUSBAR,
246         ATK_ROLE_TABLE,
247         ATK_ROLE_TABLE_CELL,
248         ATK_ROLE_TEXT,
249         ATK_ROLE_PANEL,         // TEXT_FRAME
250         ATK_ROLE_TOGGLE_BUTTON,
251         ATK_ROLE_TOOL_BAR,
252         ATK_ROLE_TOOL_TIP,
253         ATK_ROLE_TREE,
254         ATK_ROLE_VIEWPORT,
255         ATK_ROLE_WINDOW,
256         ATK_ROLE_PUSH_BUTTON,   // BUTTON_DROPDOWN
257         ATK_ROLE_PUSH_BUTTON,   // BUTTON_MENU
258         ATK_ROLE_UNKNOWN,       // CAPTION - registered below
259         ATK_ROLE_UNKNOWN,       // CHART - registered below
260         ATK_ROLE_UNKNOWN,       // EDIT_BAR - registered below
261         ATK_ROLE_UNKNOWN,       // FORM - registered below
262         ATK_ROLE_UNKNOWN,       // IMAGE_MAP - registered below
263         ATK_ROLE_UNKNOWN,       // NOTE - registered below
264         ATK_ROLE_UNKNOWN,       // PAGE - registered below
265         ATK_ROLE_RULER,
266         ATK_ROLE_UNKNOWN,       // SECTION - registered below
267         ATK_ROLE_UNKNOWN,       // TREE_ITEM - registered below
268         ATK_ROLE_TREE_TABLE,
269         ATK_ROLE_SCROLL_PANE,   // COMMENT - mapped to atk_role_scroll_pane
270         ATK_ROLE_UNKNOWN        // COMMENT_END - mapped to atk_role_unknown
271 #if defined(ATK_CHECK_VERSION)
272         //older ver that doesn't define ATK_CHECK_VERSION doesn't have the following
273         , ATK_ROLE_DOCUMENT_PRESENTATION
274         , ATK_ROLE_DOCUMENT_SPREADSHEET
275         , ATK_ROLE_DOCUMENT_TEXT
276 #if ATK_CHECK_VERSION(2,15,2)
277         , ATK_ROLE_STATIC
278 #else
279         , ATK_ROLE_LABEL
280 #endif
281 #else
282         //older version should fallback to DOCUMENT_FRAME role
283         , ATK_ROLE_DOCUMENT_FRAME
284         , ATK_ROLE_DOCUMENT_FRAME
285         , ATK_ROLE_DOCUMENT_FRAME
286         , ATK_ROLE_LABEL
287 #endif
288     };
289 
290     static bool initialized = false;
291 
292     if( ! initialized )
293     {
294         // the accessible roles below were added to ATK in later versions,
295         // with role_for_name we will know if they exist in runtime.
296         roleMap[accessibility::AccessibleRole::EDIT_BAR] = getRoleForName("edit bar");
297         roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = getRoleForName("embedded");
298         roleMap[accessibility::AccessibleRole::CHART] = getRoleForName("chart");
299         roleMap[accessibility::AccessibleRole::CAPTION] = getRoleForName("caption");
300         roleMap[accessibility::AccessibleRole::DOCUMENT] = getRoleForName("document frame");
301         roleMap[accessibility::AccessibleRole::PAGE] = getRoleForName("page");
302         roleMap[accessibility::AccessibleRole::SECTION] = getRoleForName("section");
303         roleMap[accessibility::AccessibleRole::FORM] = getRoleForName("form");
304         roleMap[accessibility::AccessibleRole::GROUP_BOX] = getRoleForName("grouping");
305         roleMap[accessibility::AccessibleRole::COMMENT] = getRoleForName("comment");
306         roleMap[accessibility::AccessibleRole::IMAGE_MAP] = getRoleForName("image map");
307         roleMap[accessibility::AccessibleRole::TREE_ITEM] = getRoleForName("tree item");
308         roleMap[accessibility::AccessibleRole::HYPER_LINK] = getRoleForName("link");
309         roleMap[accessibility::AccessibleRole::END_NOTE] = getRoleForName("footnote");
310         roleMap[accessibility::AccessibleRole::FOOTNOTE] = getRoleForName("footnote");
311         roleMap[accessibility::AccessibleRole::NOTE] = getRoleForName("comment");
312 
313         initialized = true;
314     }
315 
316     static const sal_Int32 nMapSize = SAL_N_ELEMENTS(roleMap);
317     if( 0 <= nRole &&  nMapSize > nRole )
318         role = roleMap[nRole];
319 
320     return role;
321 }
322 
323 /*****************************************************************************/
324 
325 extern "C" {
326 
327 /*****************************************************************************/
328 
329 static const gchar*
wrapper_get_name(AtkObject * atk_obj)330 wrapper_get_name( AtkObject *atk_obj )
331 {
332     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
333 
334     if( obj->mpContext.is() )
335     {
336         try {
337             OString aName =
338                 OUStringToOString(
339                     obj->mpContext->getAccessibleName(),
340                     RTL_TEXTENCODING_UTF8);
341 
342             int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1;
343             if( nCmp != 0 )
344             {
345                 if( atk_obj->name )
346                     g_free(atk_obj->name);
347                 atk_obj->name = g_strdup(aName.getStr());
348             }
349         }
350         catch(const uno::Exception&) {
351             g_warning( "Exception in getAccessibleName()" );
352         }
353     }
354 
355     return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj);
356 }
357 
358 /*****************************************************************************/
359 
360 static const gchar*
wrapper_get_description(AtkObject * atk_obj)361 wrapper_get_description( AtkObject *atk_obj )
362 {
363     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
364 
365     if( obj->mpContext.is() )
366     {
367         try {
368             OString aDescription =
369                 OUStringToOString(
370                     obj->mpContext->getAccessibleDescription(),
371                     RTL_TEXTENCODING_UTF8);
372 
373             g_free(atk_obj->description);
374             atk_obj->description = g_strdup(aDescription.getStr());
375         }
376         catch(const uno::Exception&) {
377             g_warning( "Exception in getAccessibleDescription()" );
378         }
379     }
380 
381     return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj);
382 
383 }
384 
385 /*****************************************************************************/
386 
387 static AtkAttributeSet *
wrapper_get_attributes(AtkObject * atk_obj)388 wrapper_get_attributes( AtkObject *atk_obj )
389 {
390     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER( atk_obj );
391     AtkAttributeSet *pSet = nullptr;
392 
393     try
394     {
395         uno::Reference< accessibility::XAccessibleExtendedAttributes >
396             xExtendedAttrs( obj->mpContext, uno::UNO_QUERY );
397         if( xExtendedAttrs.is() )
398             pSet = attribute_set_new_from_extended_attributes( xExtendedAttrs );
399     }
400     catch(const uno::Exception&)
401     {
402         g_warning( "Exception in getAccessibleAttributes()" );
403     }
404 
405     return pSet;
406 }
407 
408 /*****************************************************************************/
409 
410 static gint
wrapper_get_n_children(AtkObject * atk_obj)411 wrapper_get_n_children( AtkObject *atk_obj )
412 {
413     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
414 
415     if (obj->mpSysObjChild)
416         return 1;
417 
418     gint n = 0;
419 
420     if( obj->mpContext.is() )
421     {
422         try {
423             n = obj->mpContext->getAccessibleChildCount();
424         }
425         catch(const uno::Exception&) {
426             TOOLS_WARN_EXCEPTION( "vcl", "Exception" );
427         }
428     }
429 
430     return n;
431 }
432 
433 /*****************************************************************************/
434 
435 static AtkObject *
wrapper_ref_child(AtkObject * atk_obj,gint i)436 wrapper_ref_child( AtkObject *atk_obj,
437                    gint       i )
438 {
439     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
440 
441     if (obj->mpSysObjChild)
442     {
443         g_object_ref(obj->mpSysObjChild);
444         return obj->mpSysObjChild;
445     }
446 
447     AtkObject* child = nullptr;
448 
449     // see comments above atk_object_wrapper_remove_child
450     if( -1 < i && obj->index_of_child_about_to_be_removed == i )
451     {
452         g_object_ref( obj->child_about_to_be_removed );
453         return obj->child_about_to_be_removed;
454     }
455 
456     if( obj->mpContext.is() )
457     {
458         try {
459             uno::Reference< accessibility::XAccessible > xAccessible =
460                 obj->mpContext->getAccessibleChild( i );
461 
462             child = atk_object_wrapper_ref( xAccessible );
463         }
464         catch(const uno::Exception&) {
465             TOOLS_WARN_EXCEPTION( "vcl", "getAccessibleChild");
466         }
467     }
468 
469     return child;
470 }
471 
472 /*****************************************************************************/
473 
474 static gint
wrapper_get_index_in_parent(AtkObject * atk_obj)475 wrapper_get_index_in_parent( AtkObject *atk_obj )
476 {
477     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
478 
479     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
480     if (obj->mpOrig)
481         return atk_object_get_index_in_parent(obj->mpOrig);
482 
483     gint i = -1;
484 
485     if( obj->mpContext.is() )
486     {
487         try {
488             i = obj->mpContext->getAccessibleIndexInParent();
489         }
490         catch(const uno::Exception&) {
491             g_warning( "Exception in getAccessibleIndexInParent()" );
492         }
493     }
494     return i;
495 }
496 
497 /*****************************************************************************/
498 
499 AtkRelation*
atk_object_wrapper_relation_new(const accessibility::AccessibleRelation & rRelation)500 atk_object_wrapper_relation_new(const accessibility::AccessibleRelation& rRelation)
501 {
502     sal_uInt32 nTargetCount = rRelation.TargetSet.getLength();
503 
504     std::vector<AtkObject*> aTargets;
505 
506     for (const auto& rTarget : rRelation.TargetSet)
507     {
508         uno::Reference< accessibility::XAccessible > xAccessible( rTarget, uno::UNO_QUERY );
509         aTargets.push_back(atk_object_wrapper_ref(xAccessible));
510     }
511 
512     AtkRelation *pRel =
513         atk_relation_new(
514             aTargets.data(), nTargetCount,
515             mapRelationType( rRelation.RelationType )
516         );
517 
518     return pRel;
519 }
520 
521 static AtkRelationSet *
wrapper_ref_relation_set(AtkObject * atk_obj)522 wrapper_ref_relation_set( AtkObject *atk_obj )
523 {
524     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
525 
526     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit relation set impl
527     if (obj->mpOrig)
528         return atk_object_ref_relation_set(obj->mpOrig);
529 
530     AtkRelationSet *pSet = atk_relation_set_new();
531 
532     if( obj->mpContext.is() )
533     {
534         try {
535             uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet(
536                     obj->mpContext->getAccessibleRelationSet()
537             );
538 
539             sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0;
540             for( sal_Int32 n = 0; n < nRelations; n++ )
541             {
542                 AtkRelation *pRel = atk_object_wrapper_relation_new(xRelationSet->getRelation(n));
543                 atk_relation_set_add(pSet, pRel);
544                 g_object_unref(pRel);
545             }
546         }
547         catch(const uno::Exception &) {
548             g_object_unref( G_OBJECT( pSet ) );
549             pSet = nullptr;
550         }
551     }
552 
553     return pSet;
554 }
555 
556 static AtkStateSet *
wrapper_ref_state_set(AtkObject * atk_obj)557 wrapper_ref_state_set( AtkObject *atk_obj )
558 {
559     AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
560     AtkStateSet *pSet = atk_state_set_new();
561 
562     if( obj->mpContext.is() )
563     {
564         try {
565             uno::Reference< accessibility::XAccessibleStateSet > xStateSet(
566                 obj->mpContext->getAccessibleStateSet());
567 
568             if( xStateSet.is() )
569             {
570                 uno::Sequence< sal_Int16 > aStates = xStateSet->getStates();
571 
572                 for( const auto nState : aStates )
573                 {
574                     // ATK_STATE_LAST_DEFINED is used to check if the state
575                     // is unmapped, do not report it to Atk
576                     if ( mapAtkState( nState ) != ATK_STATE_LAST_DEFINED )
577                         atk_state_set_add_state( pSet, mapAtkState( nState ) );
578                 }
579 
580                 // We need to emulate FOCUS state for menus, menu-items etc.
581                 if( atk_obj == atk_get_focus_object() )
582                     atk_state_set_add_state( pSet, ATK_STATE_FOCUSED );
583 /* FIXME - should we do this ?
584                 else
585                     atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED );
586 */
587             }
588         }
589 
590         catch(const uno::Exception &) {
591             g_warning( "Exception in wrapper_ref_state_set" );
592             atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT );
593         }
594     }
595     else
596         atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT );
597 
598     return pSet;
599 }
600 
601 /*****************************************************************************/
602 
603 static void
atk_object_wrapper_finalize(GObject * obj)604 atk_object_wrapper_finalize (GObject *obj)
605 {
606     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj);
607 
608     if( pWrap->mpAccessible.is() )
609     {
610         ooo_wrapper_registry_remove( pWrap->mpAccessible );
611         pWrap->mpAccessible.clear();
612     }
613 
614     atk_object_wrapper_dispose( pWrap );
615 
616     parent_class->finalize( obj );
617 }
618 
619 static void
atk_object_wrapper_class_init(AtkObjectWrapperClass * klass)620 atk_object_wrapper_class_init (AtkObjectWrapperClass *klass)
621 {
622   GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
623   AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass );
624 
625   parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass));
626 
627   // GObject methods
628   gobject_class->finalize = atk_object_wrapper_finalize;
629 
630   // AtkObject methods
631   atk_class->get_name = wrapper_get_name;
632   atk_class->get_description = wrapper_get_description;
633   atk_class->get_attributes = wrapper_get_attributes;
634   atk_class->get_n_children = wrapper_get_n_children;
635   atk_class->ref_child = wrapper_ref_child;
636   atk_class->get_index_in_parent = wrapper_get_index_in_parent;
637   atk_class->ref_relation_set = wrapper_ref_relation_set;
638   atk_class->ref_state_set = wrapper_ref_state_set;
639 }
640 
641 static void
atk_object_wrapper_init(AtkObjectWrapper * wrapper,AtkObjectWrapperClass *)642 atk_object_wrapper_init (AtkObjectWrapper      *wrapper,
643                          AtkObjectWrapperClass*)
644 {
645    wrapper->mpAction = nullptr;
646    wrapper->mpComponent = nullptr;
647    wrapper->mpEditableText = nullptr;
648    wrapper->mpHypertext = nullptr;
649    wrapper->mpImage = nullptr;
650    wrapper->mpSelection = nullptr;
651    wrapper->mpTable = nullptr;
652    wrapper->mpText = nullptr;
653    wrapper->mpValue = nullptr;
654 }
655 
656 } // extern "C"
657 
658 GType
atk_object_wrapper_get_type()659 atk_object_wrapper_get_type()
660 {
661   static GType type = 0;
662 
663   if (!type)
664     {
665       static const GTypeInfo typeInfo =
666       {
667         sizeof (AtkObjectWrapperClass),
668         nullptr,
669         nullptr,
670         reinterpret_cast<GClassInitFunc>(atk_object_wrapper_class_init),
671         nullptr,
672         nullptr,
673         sizeof (AtkObjectWrapper),
674         0,
675         reinterpret_cast<GInstanceInitFunc>(atk_object_wrapper_init),
676         nullptr
677       } ;
678       type = g_type_register_static (ATK_TYPE_OBJECT,
679                                      "OOoAtkObj",
680                                      &typeInfo, GTypeFlags(0)) ;
681     }
682   return type;
683 }
684 
685 static bool
isOfType(uno::XInterface * pInterface,const uno::Type & rType)686 isOfType( uno::XInterface *pInterface, const uno::Type & rType )
687 {
688     g_return_val_if_fail( pInterface != nullptr, false );
689 
690     bool bIs = false;
691     try {
692         uno::Any aRet = pInterface->queryInterface( rType );
693 
694         bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) &&
695                 ( aRet.pReserved != nullptr ) );
696     } catch( const uno::Exception &) { }
697 
698     return bIs;
699 }
700 
701 extern "C" {
702 typedef  GType (* GetGIfaceType ) ();
703 }
704 const struct {
705         const char          *name;
706         GInterfaceInitFunc const   aInit;
707         GetGIfaceType const        aGetGIfaceType;
708         const uno::Type &  (*aGetUnoType) ();
709 } aTypeTable[] = {
710 // re-location heaven:
711     {
712         "Comp", reinterpret_cast<GInterfaceInitFunc>(componentIfaceInit),
713         atk_component_get_type,
714         cppu::UnoType<accessibility::XAccessibleComponent>::get
715     },
716     {
717         "Act",  reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit),
718         atk_action_get_type,
719         cppu::UnoType<accessibility::XAccessibleAction>::get
720     },
721     {
722         "Txt",  reinterpret_cast<GInterfaceInitFunc>(textIfaceInit),
723         atk_text_get_type,
724         cppu::UnoType<accessibility::XAccessibleText>::get
725     },
726     {
727         "Val",  reinterpret_cast<GInterfaceInitFunc>(valueIfaceInit),
728         atk_value_get_type,
729         cppu::UnoType<accessibility::XAccessibleValue>::get
730     },
731     {
732         "Tab",  reinterpret_cast<GInterfaceInitFunc>(tableIfaceInit),
733         atk_table_get_type,
734         cppu::UnoType<accessibility::XAccessibleTable>::get
735     },
736     {
737         "Edt",  reinterpret_cast<GInterfaceInitFunc>(editableTextIfaceInit),
738         atk_editable_text_get_type,
739         cppu::UnoType<accessibility::XAccessibleEditableText>::get
740     },
741     {
742         "Img",  reinterpret_cast<GInterfaceInitFunc>(imageIfaceInit),
743         atk_image_get_type,
744         cppu::UnoType<accessibility::XAccessibleImage>::get
745     },
746     {
747         "Hyp",  reinterpret_cast<GInterfaceInitFunc>(hypertextIfaceInit),
748         atk_hypertext_get_type,
749         cppu::UnoType<accessibility::XAccessibleHypertext>::get
750     },
751     {
752         "Sel",  reinterpret_cast<GInterfaceInitFunc>(selectionIfaceInit),
753         atk_selection_get_type,
754         cppu::UnoType<accessibility::XAccessibleSelection>::get
755     }
756     // AtkDocument is a nastily broken interface (so far)
757     //  we could impl. get_document_type perhaps though.
758 };
759 
760 const int aTypeTableSize = G_N_ELEMENTS( aTypeTable );
761 
762 static GType
ensureTypeFor(uno::XInterface * pAccessible)763 ensureTypeFor( uno::XInterface *pAccessible )
764 {
765     int i;
766     bool bTypes[ aTypeTableSize ] = { false, };
767     OStringBuffer aTypeNameBuf( "OOoAtkObj" );
768 
769     for( i = 0; i < aTypeTableSize; i++ )
770     {
771         if( isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) )
772         {
773             aTypeNameBuf.append(aTypeTable[i].name);
774             bTypes[i] = true;
775         }
776     }
777 
778     OString aTypeName = aTypeNameBuf.makeStringAndClear();
779     GType nType = g_type_from_name( aTypeName.getStr() );
780     if( nType == G_TYPE_INVALID )
781     {
782         GTypeInfo aTypeInfo = {
783             sizeof( AtkObjectWrapperClass ),
784             nullptr, nullptr, nullptr, nullptr, nullptr,
785             sizeof( AtkObjectWrapper ),
786             0, nullptr, nullptr
787         } ;
788         nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER,
789                                         aTypeName.getStr(), &aTypeInfo,
790                                         GTypeFlags(0) ) ;
791 
792         for( int j = 0; j < aTypeTableSize; j++ )
793             if( bTypes[j] )
794             {
795                 GInterfaceInfo aIfaceInfo = { nullptr, nullptr, nullptr };
796                 aIfaceInfo.interface_init = aTypeTable[j].aInit;
797                 g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(),
798                                              &aIfaceInfo);
799             }
800     }
801     return nType;
802 }
803 
804 AtkObject *
atk_object_wrapper_ref(const uno::Reference<accessibility::XAccessible> & rxAccessible,bool create)805 atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create )
806 {
807     g_return_val_if_fail( bool(rxAccessible), nullptr );
808 
809     AtkObject *obj = ooo_wrapper_registry_get(rxAccessible);
810     if( obj )
811     {
812         g_object_ref( obj );
813         return obj;
814     }
815 
816     if( create )
817         return atk_object_wrapper_new( rxAccessible );
818 
819     return nullptr;
820 }
821 
822 AtkObject *
atk_object_wrapper_new(const css::uno::Reference<css::accessibility::XAccessible> & rxAccessible,AtkObject * parent,AtkObject * orig)823 atk_object_wrapper_new( const css::uno::Reference< css::accessibility::XAccessible >& rxAccessible,
824                         AtkObject* parent, AtkObject* orig )
825 {
826     g_return_val_if_fail( bool(rxAccessible), nullptr );
827 
828     AtkObjectWrapper *pWrap = nullptr;
829 
830     try {
831         uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext());
832 
833         g_return_val_if_fail( bool(xContext), nullptr );
834 
835         GType nType = ensureTypeFor( xContext.get() );
836         gpointer obj = g_object_new( nType, nullptr);
837 
838         pWrap = ATK_OBJECT_WRAPPER( obj );
839         pWrap->mpAccessible = rxAccessible;
840 
841         pWrap->index_of_child_about_to_be_removed = -1;
842         pWrap->child_about_to_be_removed = nullptr;
843 
844         pWrap->mpContext = xContext;
845         pWrap->mpOrig = orig;
846 
847         AtkObject* atk_obj = ATK_OBJECT(pWrap);
848         atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() );
849         atk_obj->accessible_parent = parent;
850 
851         ooo_wrapper_registry_add( rxAccessible, atk_obj );
852 
853         if( parent )
854             g_object_ref( atk_obj->accessible_parent );
855         else
856         {
857             /* gail_focus_tracker remembers the focused object at the first
858              * parent in the hierarchy that is a Gtk+ widget, but at the time the
859              * event gets processed (at idle), it may be too late to create the
860              * hierarchy, so doing it now ..
861              */
862             uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() );
863 
864             if( xParent.is() )
865                 atk_obj->accessible_parent = atk_object_wrapper_ref( xParent );
866         }
867 
868         // Attach a listener to the UNO object if it's not TRANSIENT
869         uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() );
870         if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) )
871         {
872             uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
873             if( xBroadcaster.is() )
874             {
875                 uno::Reference<accessibility::XAccessibleEventListener> xListener(new AtkListener(pWrap));
876                 xBroadcaster->addAccessibleEventListener(xListener);
877             }
878             else
879                 OSL_ASSERT( false );
880         }
881 
882         static auto func = reinterpret_cast<void(*)(AtkObject*, const gchar*)>(dlsym(nullptr, "atk_object_set_accessible_id"));
883         if (func)
884         {
885             css::uno::Reference<css::accessibility::XAccessibleContext2> xContext2(xContext, css::uno::UNO_QUERY);
886             if( xContext2.is() )
887             {
888                 OString aId = OUStringToOString( xContext2->getAccessibleId(), RTL_TEXTENCODING_UTF8);
889                 (*func)(atk_obj, aId.getStr());
890             }
891         }
892 
893         // tdf#141197 if we have a sysobj child then include that in the hierarchy
894         if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper())
895         {
896             css::uno::Reference<css::awt::XWindow> xAWTWindow(rxAccessible, css::uno::UNO_QUERY);
897             VclPtr<vcl::Window> xWindow = pWrapper->GetWindow(xAWTWindow);
898             if (xWindow && xWindow->GetType() == WindowType::SYSTEMCHILDWINDOW)
899             {
900                 const SystemEnvData* pEnvData = static_cast<SystemChildWindow*>(xWindow.get())->GetSystemData();
901                 if (GtkWidget *pSysObj = pEnvData ? static_cast<GtkWidget*>(pEnvData->pWidget) : nullptr)
902                     pWrap->mpSysObjChild = gtk_widget_get_accessible(pSysObj);
903             }
904         }
905 
906         return ATK_OBJECT( pWrap );
907     }
908     catch (const uno::Exception &)
909     {
910         if( pWrap )
911             g_object_unref( pWrap );
912 
913         return nullptr;
914     }
915 }
916 
917 /*****************************************************************************/
918 
atk_object_wrapper_add_child(AtkObjectWrapper * wrapper,AtkObject * child,gint index)919 void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index)
920 {
921     AtkObject *atk_obj = ATK_OBJECT( wrapper );
922 
923     atk_object_set_parent( child, atk_obj );
924     g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, nullptr );
925 }
926 
927 /*****************************************************************************/
928 
atk_object_wrapper_remove_child(AtkObjectWrapper * wrapper,AtkObject * child,gint index)929 void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index)
930 {
931     /*
932      * the atk-bridge GTK+ module gets back to the event source to ref the child just
933      * vanishing, so we keep this reference because the semantic on OOo side is different.
934      */
935     wrapper->child_about_to_be_removed = child;
936     wrapper->index_of_child_about_to_be_removed = index;
937 
938     g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, nullptr );
939 
940     wrapper->index_of_child_about_to_be_removed = -1;
941     wrapper->child_about_to_be_removed = nullptr;
942 }
943 
944 /*****************************************************************************/
945 
atk_object_wrapper_set_role(AtkObjectWrapper * wrapper,sal_Int16 role)946 void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role)
947 {
948     AtkObject *atk_obj = ATK_OBJECT( wrapper );
949     atk_object_set_role( atk_obj, mapToAtkRole( role ) );
950 }
951 
952 /*****************************************************************************/
953 
atk_object_wrapper_dispose(AtkObjectWrapper * wrapper)954 void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper)
955 {
956     wrapper->mpContext.clear();
957     wrapper->mpAction.clear();
958     wrapper->mpComponent.clear();
959     wrapper->mpEditableText.clear();
960     wrapper->mpHypertext.clear();
961     wrapper->mpImage.clear();
962     wrapper->mpSelection.clear();
963     wrapper->mpMultiLineText.clear();
964     wrapper->mpTable.clear();
965     wrapper->mpText.clear();
966     wrapper->mpTextMarkup.clear();
967     wrapper->mpTextAttributes.clear();
968     wrapper->mpValue.clear();
969 }
970 
971 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
972