1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_accessibility_accessbridge.hxx"
26 
27 //------------------------------------------------------------------------
28 // includes
29 //------------------------------------------------------------------------
30 
31 #include <tools/prewin.h>
32 #include <wtypes.h>
33 #include <tools/postwin.h>
34 #include <rtl/process.h>
35 #include <tools/link.hxx>
36 
37 #ifndef _SVAPP_HXX
38 #include <vcl/svapp.hxx>
39 #endif
40 #include <vcl/window.hxx>
41 #include <vcl/sysdata.hxx>
42 #include <uno/current_context.hxx>
43 #include <uno/environment.h>
44 #include <uno/mapping.hxx>
45 #include <com/sun/star/accessibility/AccessibleRole.hpp>
46 #include <com/sun/star/accessibility/XAccessible.hpp>
47 
48 #ifndef _JVMACCESS_UNOVIRTUALMACHINE_HXX_
49 #include "jvmaccess/unovirtualmachine.hxx"
50 #endif
51 
52 #ifndef _JVMACCESS_VIRTUALMACHINE_HXX_
53 #include "jvmaccess/virtualmachine.hxx"
54 #endif
55 
56 #include <osl/diagnose.h>
57 
58 using ::rtl::OUString;
59 using ::com::sun::star::uno::Mapping;
60 using ::com::sun::star::uno::Reference;
61 using ::com::sun::star::uno::RuntimeException;
62 using namespace ::com::sun::star::accessibility;
63 
64 long VCLEventListenerLinkFunc(void * pInst, void * pData);
65 
66 //------------------------------------------------------------------------
67 // global vatiables
68 //------------------------------------------------------------------------
69 
70 Link g_aEventListenerLink(NULL, VCLEventListenerLinkFunc);
71 
72 rtl::Reference< jvmaccess::UnoVirtualMachine > g_xUnoVirtualMachine;
73 typelib_InterfaceTypeDescription * g_pTypeDescription = NULL;
74 Mapping g_unoMapping;
75 
76 jclass g_jcWindowsAccessBridgeAdapter = NULL;
77 jmethodID g_jmRegisterTopWindow = 0;
78 jmethodID g_jmRevokeTopWindow = 0;
79 
80 //------------------------------------------------------------------------
81 // functions
82 //------------------------------------------------------------------------
83 
JNI_OnLoad(JavaVM *,void *)84 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *)
85 {
86     return JNI_VERSION_1_2;
87 }
88 
89 JNIEXPORT jbyteArray JNICALL
Java_org_openoffice_accessibility_WindowsAccessBridgeAdapter_getProcessID(JNIEnv * pJNIEnv,jclass clazz)90 Java_org_openoffice_accessibility_WindowsAccessBridgeAdapter_getProcessID(JNIEnv *pJNIEnv, jclass clazz)
91 {
92     // Initialize global class and method references
93     g_jcWindowsAccessBridgeAdapter =
94         static_cast< jclass > (pJNIEnv->NewGlobalRef(clazz));
95     if (NULL == g_jcWindowsAccessBridgeAdapter) {
96         return 0; /* jni error occurred */
97     }
98     g_jmRegisterTopWindow =
99         pJNIEnv->GetStaticMethodID(clazz, "registerTopWindow", "(ILcom/sun/star/accessibility/XAccessible;)V");
100     if (0 == g_jmRegisterTopWindow) {
101         return 0; /* jni error occurred */
102     }
103     g_jmRevokeTopWindow =
104         pJNIEnv->GetStaticMethodID(clazz, "revokeTopWindow", "(ILcom/sun/star/accessibility/XAccessible;)V");
105     if (0 == g_jmRevokeTopWindow) {
106         return 0; /* jni error occurred */
107     }
108 
109     // Use the special protocol of XJavaVM.getJavaVM:  If the passed in
110     // process ID has an extra 17th byte of value one, the returned any
111     // contains a pointer to a jvmaccess::UnoVirtualMachine, instead of
112     // the underlying JavaVM pointer:
113     jbyte processID[17];
114     rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8 *> (processID));
115     // #i51265# we need a jvmaccess::UnoVirtualMachine pointer for the
116     // uno_getEnvironment() call later.
117     processID[16] = 1;
118 
119     // Copy the result into a java byte[] and return.
120     jbyteArray jbaProcessID = pJNIEnv->NewByteArray(17);
121     pJNIEnv->SetByteArrayRegion(jbaProcessID, 0, 17, processID);
122     return jbaProcessID;
123 }
124 
125 JNIEXPORT jboolean JNICALL
Java_org_openoffice_accessibility_WindowsAccessBridgeAdapter_createMapping(JNIEnv *,jclass,jlong pointer)126 Java_org_openoffice_accessibility_WindowsAccessBridgeAdapter_createMapping(JNIEnv *, jclass, jlong pointer)
127 {
128 	uno_Environment * pJava_environment = NULL;
129 	uno_Environment * pUno_environment = NULL;
130 
131     try {
132         // We get a non-refcounted pointer to a jvmaccess::VirtualMachine
133         // from the XJavaVM service (the pointer is guaranteed to be valid
134         // as long as our reference to the XJavaVM service lasts), and
135         // convert the non-refcounted pointer into a refcounted one
136         // immediately:
137         g_xUnoVirtualMachine = reinterpret_cast< jvmaccess::UnoVirtualMachine * >(pointer);
138 
139         if ( g_xUnoVirtualMachine.is() )
140         {
141             OUString sJava(RTL_CONSTASCII_USTRINGPARAM("java"));
142             uno_getEnvironment(&pJava_environment, sJava.pData, g_xUnoVirtualMachine.get());
143 
144             OUString sCppu_current_lb_name(RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME));
145             uno_getEnvironment(&pUno_environment, sCppu_current_lb_name.pData, NULL);
146 
147             if ( pJava_environment && pUno_environment )
148             {
149                 g_unoMapping = Mapping(pUno_environment, pJava_environment);
150                 getCppuType((::com::sun::star::uno::Reference< XAccessible > *) 0).getDescription((typelib_TypeDescription **) & g_pTypeDescription);
151             }
152 
153             if ( pJava_environment )
154             {
155                 // release java environment
156                 pJava_environment->release(pJava_environment);
157                 pJava_environment = NULL;
158             }
159 
160             if ( pUno_environment )
161             {
162                 // release uno environment
163                 pUno_environment->release(pUno_environment);
164                 pUno_environment = NULL;
165             }
166         }
167     }
168 
169     catch ( RuntimeException e)
170     {
171         OSL_TRACE("RuntimeException caught while initializing the mapping");
172     }
173 
174     if ( (0 != g_jmRegisterTopWindow) && (0 != g_jmRevokeTopWindow) )
175     {
176         ::Application::AddEventListener(g_aEventListenerLink);
177     }
178     return JNI_TRUE;
179 }
180 
JNI_OnUnload(JavaVM * jvm,void *)181 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *)
182 {
183     ::Application::RemoveEventListener(g_aEventListenerLink);
184 
185     if ( NULL != g_jcWindowsAccessBridgeAdapter )
186     {
187         JNIEnv * pJNIEnv;
188         if ( ! jvm->GetEnv((void **) &pJNIEnv, JNI_VERSION_1_2) )
189         {
190             pJNIEnv->DeleteGlobalRef(g_jcWindowsAccessBridgeAdapter);
191             g_jcWindowsAccessBridgeAdapter = NULL;
192         }
193     }
194 
195     if ( NULL != g_pTypeDescription )
196     {
197         typelib_typedescription_release( reinterpret_cast< typelib_TypeDescription * > (g_pTypeDescription) );
198         g_pTypeDescription = NULL;
199     }
200 
201     g_unoMapping.clear();
202     g_xUnoVirtualMachine.clear();
203 }
204 
GetHWND(Window * pWindow)205 HWND GetHWND(Window * pWindow)
206 {
207     const SystemEnvData * pEnvData = pWindow->GetSystemData();
208     if (pEnvData != NULL)
209     {
210         return pEnvData->hWnd;
211     }
212     return (HWND) -1;
213 }
214 
handleWindowEvent(Window * pWindow,bool bShow)215 void handleWindowEvent(Window * pWindow, bool bShow)
216 {
217     if ( pWindow && pWindow->IsTopWindow() )
218     {
219         ::com::sun::star::uno::Reference< XAccessible > xAccessible;
220 
221         // Test for combo box - drop down floating windows first
222         Window * pParentWindow = pWindow->GetParent();
223 
224         if ( pParentWindow )
225         {
226             try
227             {
228                 // The parent window of a combo box floating window should have the role COMBO_BOX
229                 ::com::sun::star::uno::Reference< XAccessible > xParentAccessible(pParentWindow->GetAccessible());
230                 if ( xParentAccessible.is() )
231                 {
232                     ::com::sun::star::uno::Reference< XAccessibleContext > xParentAC(xParentAccessible->getAccessibleContext());
233                     if ( xParentAC.is() && (AccessibleRole::COMBO_BOX == xParentAC->getAccessibleRole()) )
234                     {
235                         // O.k. - this is a combo box floating window corresponding to the child of role LIST of the parent.
236                         // Let's not rely on a specific child order, just search for the child with the role LIST
237                         sal_Int32 nCount = xParentAC->getAccessibleChildCount();
238                         for ( sal_Int32 n = 0; (n < nCount) && !xAccessible.is(); n++)
239                         {
240                             ::com::sun::star::uno::Reference< XAccessible > xChild = xParentAC->getAccessibleChild(n);
241                             if ( xChild.is() )
242                             {
243                                 ::com::sun::star::uno::Reference< XAccessibleContext > xChildAC = xChild->getAccessibleContext();
244                                 if ( xChildAC.is() && (AccessibleRole::LIST == xChildAC->getAccessibleRole()) )
245                                 {
246                                     xAccessible = xChild;
247                                 }
248                             }
249                         }
250                     }
251                 }
252             }
253             catch (::com::sun::star::uno::RuntimeException e)
254             {
255                 // Ignore show events that throw DisposedExceptions in getAccessibleContext(),
256                 // but keep revoking these windows in hide(s).
257                 if (bShow)
258                     return;
259             }
260         }
261 
262         // We have to rely on the fact that Window::GetAccessible()->getAccessibleContext() returns a valid XAccessibleContext
263         // also for other menus than menubar or toplevel popup window. Otherwise we had to traverse the hierarchy to find the
264         // context object to this menu floater. This makes the call to Window->IsMenuFloatingWindow() obsolete.
265         if ( ! xAccessible.is() )
266             xAccessible = pWindow->GetAccessible();
267 
268         if ( xAccessible.is() && g_unoMapping.is() )
269         {
270             jobject * joXAccessible = reinterpret_cast < jobject * > (g_unoMapping.mapInterface(
271                 xAccessible.get(), g_pTypeDescription));
272 
273             if ( NULL != joXAccessible )
274             {
275                 jvmaccess::VirtualMachine::AttachGuard aGuard(g_xUnoVirtualMachine->getVirtualMachine());
276                 JNIEnv * pJNIEnv = aGuard.getEnvironment();
277 
278                 if ( NULL != pJNIEnv )
279                 {
280                     // g_jmRegisterTopWindow and g_jmRevokeTopWindow are ensured to be != 0 - otherwise
281                     // the event listener would not have been attached.
282                     pJNIEnv->CallStaticVoidMethod(g_jcWindowsAccessBridgeAdapter,
283                         (bShow) ? g_jmRegisterTopWindow : g_jmRevokeTopWindow,
284                         (jint) GetHWND(pWindow), joXAccessible );
285 
286                     // Clear any exception that might have been occurred.
287                     if (pJNIEnv->ExceptionCheck()) {
288                         pJNIEnv->ExceptionClear();
289                     }
290                 }
291             }
292         }
293     }
294 }
295 
VCLEventListenerLinkFunc(void *,void * pData)296 long VCLEventListenerLinkFunc(void *, void * pData)
297 {
298     ::VclSimpleEvent const * pEvent = (::VclSimpleEvent const *) pData;
299 
300     switch (pEvent->GetId())
301     {
302     case VCLEVENT_WINDOW_SHOW:
303         handleWindowEvent(((::VclWindowEvent const *) pEvent)->GetWindow(), true);
304 	    break;
305     case VCLEVENT_WINDOW_HIDE:
306         handleWindowEvent(((::VclWindowEvent const *) pEvent)->GetWindow(), false);
307         break;
308     }
309 
310     return 0;
311 }
312