Repository: nvaccess/nvda

Issue:
NVDA freezes iTunes in web views
OS: Windows 10 64 bit
iTunes version: 12.5.1.21 64 bit

STR:
1. Search for a song in Apple Music. The iTunes Store will probably do it too.
2. Press f6 to move into the web view.
3. Keep pressing (or even hold) downArrow.
   - Expected: NVDA should navigate through the document.
   - Actual: Eventually, iTunes freezes, taking NVDA with it.



Discussion:
Debugging this reveals a deadlock in our vbuf backend:
1. `getNativeHandleForNode` gets called in an RPC thread. It acquires the backend lock and sends a window message to the main thread.
2. The main thread runs the update timer proc. It calls update, which eventually tries to acquire the lock.
3. Unfortunately, it hasn't yet processed the message sent by `getNativeHandleForNode` yet, but `getNativeHandleForNode` is still holding the lock waiting for a response to that message. The main thread can't process the message because it's waiting on the lock. Deadlock!

Relevant stack from main thread:

> ```
>  # Child-SP          RetAddr           Call Site
> 00 00000065`715fee88 00007ffe`58d7acb7 ntdll!NtWaitForAlertByThreadId+0x14
> 01 00000065`715fee90 00007ffe`58d7abce ntdll!RtlpWaitOnAddressWithTimeout+0x43
> 02 00000065`715feec0 00007ffe`58d7aa63 ntdll!RtlpWaitOnAddress+0xb2
> 03 00000065`715fef30 00007ffe`58d7760c ntdll!RtlpWaitOnCriticalSection+0xdb
> 04 00000065`715ff000 00007ffe`58d77360 ntdll!RtlpEnterCriticalSectionContended+0x160
> 05 00000065`715ff030 00007ffe`2a53acdb ntdll!RtlEnterCriticalSection+0x40
> 06 00000065`715ff060 00007ffe`2a5482df VBufBackend_webKit!LockableObject::acquire(void)+0x1b [c:\projects\nvda\build\x86_64\common\lock.h @ 44]
> 07 00000065`715ff090 00007ffe`2a547ea1 VBufBackend_webKit!VBufBackend_t::update(void)+0x1df [c:\projects\nvda\build\x86_64\vbufbase\backend.cpp @ 206]
> 08 00000065`715ff1c0 00007ffe`577105c1 VBufBackend_webKit!VBufBackend_t::renderThread_timerProc(struct HWND__ * hwnd = 0x00000000`00000000, unsigned int msg = 0x113, unsigned int64 timerID = 0x3c30, unsigned long time = 0x13989d0d)+0xd1 [c:\projects\nvda\build\x86_64\vbufbase\backend.cpp @ 118]
> 09 00000065`715ff210 00007ffe`57711ade USER32!UserCallWinProc+0x231
> ...
> ```

Relevant stack from RPC thread:

> ```
> 00 00000065`751fe718 00007ffe`57711631 win32u!NtUserMessageCall+0x14
> 01 00000065`751fe720 00007ffe`577114d2 USER32!SendMessageWorker+0xf1
> 02 00000065`751fe7b0 00007ffe`2a54ee41 USER32!SendMessageW+0x112
> 03 00000065`751fe810 00007ffe`40799d14 VBufBackend_webKit!WebKitVBufBackend_t::getNodeForNativeHandle(int nativeHandle = 0n2356)+0x51 [c:\projects\nvda\build\x86_64\vbufbackends\webkit\webkit.cpp @ 314]
> 04 00000065`751fe870 00007ffe`57f5a3b3 nvdaHelperRemote!VBufRemote_getNodeForNativeHandle(void * buffer = 0x0000023c`e43dd560, int nativeHandle = 0n2356, unsigned int64 * node = 0x00000065`751feec0)+0x44 [c:\projects\nvda\build\x86_64\remote\vbufremote.cpp @ 177]
> 05 00000065`751fe8b0 00007ffe`57f402f7 RPCRT4!Invoke+0x73
> ```


Resolving patch:
diff --git a/source/virtualBuffers/webKit.py b/source/virtualBuffers/webKit.py
--- a/source/virtualBuffers/webKit.py
+++ b/source/virtualBuffers/webKit.py
@@ -1,6 +1,6 @@
 #virtualBuffers/webKit.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2011 NV Access Inc
+#Copyright (C) 2011-2016 NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -60,29 +60,16 @@ def _get_isAlive(self):
 		return True
 
 	def getNVDAObjectFromIdentifier(self, docHandle, ID):
-		node=VBufRemote_nodeHandle_t()
-		NVDAHelper.localLib.VBuf_getControlFieldNodeWithIdentifier(self.VBufHandle, docHandle, ID,ctypes.byref(node))
-		if not node:
-			return None
-		lresult = NVDAHelper.localLib.VBuf_getNativeHandleForNode(self.VBufHandle, node)
-		if not lresult:
-			return None
-		return NVDAObjects.IAccessible.IAccessible(
-			IAccessibleObject=oleacc.ObjectFromLresult(lresult, 0, oleacc.IAccessible),
-			IAccessibleChildID=0, windowHandle=self.rootDocHandle)
-
-	def getIdentifierFromNVDAObject(self,obj):
-		if obj == self.rootNVDAObject:
-			return obj.windowHandle, 0
-		if not self.isReady or not obj.event_childID:
-			# We can only retrieve the node for objects obtained from events.
-			raise LookupError
-		node=VBufRemote_nodeHandle_t()
-		NVDAHelper.localLib.VBuf_getNodeForNativeHandle(self.VBufHandle, obj.event_childID,ctypes.byref(node))
-		docHandle=ctypes.c_int()
-		ID=ctypes.c_int()
-		NVDAHelper.localLib.VBuf_getIdentifierFromControlFieldNode(self.VBufHandle, node, ctypes.byref(docHandle), ctypes.byref(ID))
-		return docHandle.value, ID.value
+		if ID > 0:
+			# WebKit returns a positive value for uniqueID,
+			# but we need to pass a negative value when retrieving objects.
+			ID = -ID
+		return NVDAObjects.IAccessible.getNVDAObjectFromEvent(docHandle, winUser.OBJID_CLIENT, ID)
+
+	def getIdentifierFromNVDAObject(self, obj):
+		docHandle = obj.windowHandle
+		ID = obj.IA2UniqueID
+		return docHandle, ID
 
 	def _searchableAttribsForNodeType(self,nodeType):
 		if nodeType=="formField":


Test patch: