Repository: nvaccess/nvda

Issue:
NVDA stops working after using Windows Media Player
**Reported by a11cf0.vk on 2015-11-06 16:40**
Hello.
NVDA often stops working after using Windows Media Player 12 on Windows 7.

STR:
1. Open any media file with WMP
2. Tab to any playback control and interact with it
3. Close the player.

After closing the player NVDA starts to give errors on every movement of the focus and completely stops speaking. After this, the only way to continue working is to restart NVDA.
This happens almost always, but sometimes it doesn't. Also, this generally doesn't happen when there was no interaction with the player's controls.

Here's an extract from the log.

ERROR - eventHandler.executeEvent (18:26:08):
error executing event: gainFocus on <NVDAObjects.IAccessible.IAccessible object at 0x05DE6790> with extra args of {}
Traceback (most recent call last):
  File "eventHandler.pyc", line 138, in executeEvent
  File "eventHandler.pyc", line 151, in doPreGainFocus
  File "api.pyc", line 134, in setFocusObject
  File "appModuleHandler.pyc", line 240, in handleAppSwitch
AttributeError: 'AppModule' object has no attribute '_configProfileTrigger'

I'm using the latest next snapshot of NVDA on Windows 7 x64. I also tried both 32 and 64-bit versions of WMP with same results.
Thanks.



Discussion:
Comment 1 by jteh on 2015-11-06 21:35
For which applications, if any, do you have application specific configuration profiles?

Comment 2 by a11cf0.vk (in reply to comment 1) on 2015-11-07 08:08
Replying to [jteh](comment:1):

> For which applications, if any, do you have application specific configuration profiles?

I don't have any application-specific  profiles.

I have figured out how to reproduce this bug.
1. Open a media file in windows media player.
2. Go to a slider, such as the seek or volume slider.
3. press alt+f4, and NVDA will constantly play the error sound and be dead.

P1 because this completely breaks NVDA once it happens and it seems like a pretty serious bug in the config profile trigger code.
Technical:
This is caused by the following chain of events:
1. As media Player exits, there is a foreground event on the main window. However, this window dies somewhere between the winEvent being filtered, and the appModule being created for the resulting NVDAObject. This causes focus to be set on this NVDAObject representing a dead window, and an appModule with a processID of 0.
2. When setting the focus object, ancestors for both the new and old focus objects are walked and compared to see where they differ. It is found that this new focus object (for the dead media player main window) matches one of the old ancestors, thus the new ancestor list generated contains all common ancestors, including the old ancestor that matches this focus object. But as the old ancestor was created a long time before (when the media player main window was alive) the appModule it got was made from a valid processID. Therefore, although these two objects correctly match, the old copy has a valid appModule, but the new copy has an invalid appModule.
3. The newAppModule list (passed to handleAppSwitch) is made from the ancestors before the new focus (final ancestor) is stripped off. Thus newAppModules list contains the valid appModule, not the invalid appModule.
4. later on, for the next focus event,  an oldAppModules list (for passing to handleAppSwitch) is generated from the old focus object and its ancestors. Therefore, oldAppModules list contains the invalid appModule, not the valid one. As this appModule never appeared in newAppModules list, it was never initialized for config profile switching.

The most important fix here is to ensure that newAppModules list contains the correct appModule for the focus object. I.e. generate newAppModules after  the last ancestor is stripped, but then append the focus object's appModule to newAppModules.

As to why we see this bug on this particular slider... I have no darn idea.
We may also consider raising clear errors if an appModule is ever created with a processID of 0. Interestingly, this is actually happening quite a lot for foreground events after task switching in Windows 10. Again, because a window dies after winEvent filtering but before the appModule is created.
 

Resolving patch:
diff --git a/source/api.py b/source/api.py
--- a/source/api.py
+++ b/source/api.py
@@ -107,9 +107,13 @@ def setFocusObject(obj):
 		container=tempObj.container
 		tempObj.container=container # Cache the parent.
 		tempObj=container
-	newAppModules=[o.appModule for o in ancestors if o and o.appModule]
 	#Remove the final new ancestor as this will be the new focus object
 	del ancestors[-1]
+	# #5467: Ensure that the appModule of the real focus is included in the newAppModule list for profile switching
+	# Rather than an original focus ancestor which happened to match the new focus.
+	newAppModules=[o.appModule for o in ancestors if o and o.appModule]
+	if obj.appModule:
+		newAppModules.append(obj.appModule)
 	try:
 		treeInterceptorHandler.cleanup()
 	except watchdog.CallCancelled:


Test patch: