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