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