Repository: nvaccess/nvda

Issue:
NVDA crashes Java Swing application when transferring focus between JTable cells.
In a java Swing application that uses a JTable component with two columns large enough to activate the HorizontalScrollBar, transferring the focus between these cells causes a crash.

To reproduce the bug:
1.	Given that there’s a Java Swing application with a JTable component with at least two columns and cells with a width large enough to active the HorizontalScrollBar (get reference application in https://www.dropbox.com/s/xc8oy5m4e0m476a/NVDAJTableProblem.jar?dl=1).
2.	When transferring the focus between the cells there’s a crash in the application and NVDA stays freezed for a few seconds.

Additionally, notice that there’s NO BUG if the cells are not large enough to activate the HorizontalScrollBar.

Here’s the configurations:
NVDA 2016.2 or higher;
Windows 10 X64  (Noticed the same problem in Windows 7);
Language: PT-BR;
Java: JDK 1.8.0_121;
Accessbridge: 2.0.1;

Java and NVDA logs at [Logs.zip](https://github.com/nvaccess/nvda/files/856147/Logs.zip)


Discussion:
I've seen similar issues with IBM SPSS STATISTICS. I was able to stop the crashes by disabling table row and column header reporting.

N.B. Sent on behalf of @BabbageCom

> Op 20 mrt. 2017 om 20:45 heeft legle <notifications@github.com> het volgende geschreven:
> 
> In a java Swing application that uses a JTable component with two columns large enough to activate the HorizontalScrollBar, transferring the focus between these cells causes a crash.
> 
> To reproduce the bug:
> 
> Given that there’s a Java Swing application with a JTable component with at least two columns and cells with a width large enough to active the HorizontalScrollBar (get reference application in https://www.dropbox.com/s/xc8oy5m4e0m476a/NVDAJTableProblem.jar?dl=1).
> When transferring the focus between the cells there’s a crash in the application and NVDA stays freezed for a few seconds.
> Additionally, notice that there’s NO BUG if the cells are not large enough to activate the HorizontalScrollBar.
> 
> Here’s the configurations:
> NVDA 2016.2 or higher;
> Windows 10 X64 (Noticed the same problem in Windows 7);
> Language: PT-BR;
> Java: JDK 1.8.0_121;
> Accessbridge: 2.0.1;
> 
> Java and NVDA logs at Logs.zip
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub, or mute the thread.
> 

@josephsl are you working on this issue? or is the reference in the commit (https://github.com/josephsl/nvda/commit/f55a3f56f9329688260382c26ab375ac650ef05a) a mistake? If you are I will assign it to you.

Either way. This issue seems serious enough that we should investigate the cause of it. Assigning priority 2.
I suspect this is a bug in Java/the Java Access Bridge, in which case there's probably nothing we can do. We access Java apps via the Java Access Bridge out-of-process. Because our code isn't running in the Java process, it cannot be responsible for the crash. Either way, the Java process needs to be debugged so we know for sure.

It'd also be good to know exactly what JAB call from NVDA triggers the crash. If it is indeed related to headers as https://github.com/nvaccess/nvda/issues/6992#issuecomment-287891813 suggests, I'd suggest starting with `_get_columnHeaderText` and `_get_rowHeaderText` in `NVDAObjects.JAB.TableCell`.
Also reproduced with SQuirreL SQL Client
[Here](https://github.com/nvaccess/nvda/files/916359/JavaFreeze.log.txt) is an additional log file which contains information about the freezes I'm having with the SQuirreL SQL Client. Also, these freezes don't occur when table row/column header reporting is off.
These crashes/freezes occur at two separate places as far as I can see, though both related to table column header fetching. They may or may not be related; I'm not sure.

In @legle's log, NVDA tries to fetch the cell for a column header. When NVDA calls JAB's `getAccessibleTableCellInfo` function, it returns false. However, I suspect that by this time, the app has already crashed.

In @leonardder's log, NVDA tries to fetch the table of column header cells. JAB's `getAccessibleTableColumnHeader` function returns the header table info. NVDA needs an HWND for any object it creates, so it tries to get this for the header table. It calls `getTopLevelObject` on the header table and then tries to call `getHWNDFromAccessibleContext` on that top level object. That is where it gets stuck; JAB blocks forever. No idea why yet.

I'm trying to run @legle's test case, but I just get an exception at the moment. I'm upgrading Java at the moment to see if this helps.
You might have more luck with [SQuirrel](http://squirrel-sql.sourceforge.net/). You need to download and install it, than throw the [MariaDB connector](https://downloads.mariadb.org/connector-java/) in its lib directory to have access to MariaDB or MySQL databases, in the hope that you can contact any from your local pc. You will also have to change the layout of the application from the tabbed to the multi document interface to increase accessibility. If you connect to a database and do a select statement, the output will be in one of those crashing tables.
I Just tested @legle's application with both JAWS and SuperNova, and with those screen readers, all works great. This makes me think NVDA is responsible for the problem here.
NVDA accesses the Java Access Bridge out-of-process. It's literally impossible for us to directly crash the Java process, since our code doesn't run in that process. We might be accessing something in a different way, but either way, it suggests a bug in the JAB. Now, if we can figure out what that bug is and find a work around, great. At the end of the day, though, without knowledge of where it's crashing in the Java process, there's not a great deal we can do.

I just tested the application on a Windows 7 x86 vm with the newest JVM, which installs the legacy access bridge. IN this vm, all worked fine, no crashes at all.

Resolving patch:
diff --git a/source/JABHandler.py b/source/JABHandler.py
--- a/source/JABHandler.py
+++ b/source/JABHandler.py
@@ -1,6 +1,7 @@
+# -*- coding: UTF-8 -*-
 #javaAccessBridgeHandler.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/>
+#Copyright (C) 2007-2017 NV Access Limited, Peter Vágner, Renaud Paquay, Babbage B.V.
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -494,16 +495,22 @@ def getAccessibleRelationSet(self):
 	def getAccessibleTableInfo(self):
 		info=AccessibleTableInfo()
 		if bridgeDll.getAccessibleTableInfo(self.vmID,self.accContext,byref(info)):
-			info.jabCaption=JABContext(vmID=self.vmID,accContext=info.caption) if info.caption else None
-			info.jabSummary=JABContext(vmID=self.vmID,accContext=info.summary) if info.summary else None
-			info.jabContext=JABContext(vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
-			info.jabTable=JABContext(vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None
+			info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None
+			info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
+			info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
 			return info
 
 	def getAccessibleTableCellInfo(self,row,col):
 		info=AccessibleTableCellInfo()
 		if bridgeDll.getAccessibleTableCellInfo(self.vmID,self.accContext,row,col,byref(info)):
-			info.jabContext=JABContext(vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
 			return info
 
 	def getAccessibleTableRow(self,index):
@@ -515,30 +522,42 @@ def getAccessibleTableColumn(self,index):
 	def getAccessibleTableRowHeader(self):
 		info=AccessibleTableInfo()
 		if bridgeDll.getAccessibleTableRowHeader(self.vmID,self.accContext,byref(info)):
-			info.jabCaption=JABContext(vmID=self.vmID,accContext=info.caption) if info.caption else None
-			info.jabSummary=JABContext(vmID=self.vmID,accContext=info.summary) if info.summary else None
-			info.jabContext=JABContext(vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
-			info.jabTable=JABContext(vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None
+			info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None
+			info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
+			info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
 			return info
 
 	def getAccessibleTableRowDescription(self,row):
 		accContext=bridgeDll.getAccessibleTableRowDescription(self.vmID,self.accContext,row)
 		if accContext:
-			return JabContext(vmID=self.vmID,accContext=accContext)
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			return JabContext(hwnd=self.hwnd,vmID=self.vmID,accContext=accContext)
 
 	def getAccessibleTableColumnHeader(self):
 		info=AccessibleTableInfo()
 		if bridgeDll.getAccessibleTableColumnHeader(self.vmID,self.accContext,byref(info)):
-			info.jabCaption=JABContext(vmID=self.vmID,accContext=info.caption) if info.caption else None
-			info.jabSummary=JABContext(vmID=self.vmID,accContext=info.summary) if info.summary else None
-			info.jabContext=JABContext(vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
-			info.jabTable=JABContext(vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None
+			info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None
+			info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None
+			info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None
 			return info
 
 	def getAccessibleTableColumnDescription(self,column):
 		accContext=bridgeDll.getAccessibleTableColumnDescription(self.vmID,self.accContext,column)
 		if accContext:
-			return JabContext(vmID=self.vmID,accContext=accContext)
+			# #6992: Querying the hwnd for table related objects can cause the app to crash.
+			# A table is almost certainly contained within a single hwnd,
+			# so just pass the hwnd for the querying object.
+			return JabContext(hwnd=self.hwnd,vmID=self.vmID,accContext=accContext)
 
 	def getAccessibleKeyBindings(self):
 		bindings=AccessibleKeyBindings()


Test patch: