Example #1
0
 def IUIAutomationEventHandler_HandleAutomationEvent(self, sender, eventID):
     if not self.MTAThreadInitEvent.isSet():
         # UIAHandler hasn't finished initialising yet, so just ignore this event.
         return
     if eventID == UIA_MenuOpenedEventId and eventHandler.isPendingEvents(
             "gainFocus"):
         # We don't need the menuOpened event if focus has been fired,
         # as focus should be more correct.
         return
     NVDAEventName = UIAEventIdsToNVDAEventNames.get(eventID, None)
     if not NVDAEventName:
         return
     if not self.isNativeUIAElement(sender):
         return
     window = self.getNearestWindowHandle(sender)
     if window and not eventHandler.shouldAcceptEvent(NVDAEventName,
                                                      windowHandle=window):
         return
     import NVDAObjects.UIA
     obj = NVDAObjects.UIA.UIA(UIAElement=sender)
     if (not obj or
         (NVDAEventName == "gainFocus" and not obj.shouldAllowUIAFocusEvent)
             or (NVDAEventName == "liveRegionChange"
                 and not obj._shouldAllowUIALiveRegionChangeEvent)):
         return
     focus = api.getFocusObject()
     if obj == focus:
         obj = focus
     eventHandler.queueEvent(NVDAEventName, obj)
Example #2
0
 def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(
         self, sender, propertyId, newValue):
     # #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes.
     # We also don't use the value in this callback.
     newValue.vt = VT_EMPTY
     if not self.MTAThreadInitEvent.isSet():
         # UIAHandler hasn't finished initialising yet, so just ignore this event.
         return
     NVDAEventName = UIAPropertyIdsToNVDAEventNames.get(propertyId, None)
     if not NVDAEventName:
         return
     if not self.isNativeUIAElement(sender):
         return
     window = self.getNearestWindowHandle(sender)
     if window and not eventHandler.shouldAcceptEvent(NVDAEventName,
                                                      windowHandle=window):
         return
     import NVDAObjects.UIA
     obj = NVDAObjects.UIA.UIA(UIAElement=sender)
     if not obj:
         return
     focus = api.getFocusObject()
     if obj == focus:
         obj = focus
     eventHandler.queueEvent(NVDAEventName, obj)
Example #3
0
	def IUIAutomationEventHandler_HandleAutomationEvent(self,sender,eventID):
		if not self.MTAThreadInitEvent.isSet():
			# UIAHandler hasn't finished initialising yet, so just ignore this event.
			if _isDebug():
				log.debug("HandleAutomationEvent: event received while not fully initialized")
			return
		if eventID==UIA_MenuOpenedEventId and eventHandler.isPendingEvents("gainFocus"):
			# We don't need the menuOpened event if focus has been fired,
			# as focus should be more correct.
			if _isDebug():
				log.debug("HandleAutomationEvent: Ignored MenuOpenedEvent while focus event pending")
			return
		NVDAEventName=UIAEventIdsToNVDAEventNames.get(eventID,None)
		if not NVDAEventName:
			if _isDebug():
				log.debugWarning(f"HandleAutomationEvent: Don't know how to handle event {eventID}")
			return
		focus = api.getFocusObject()
		import NVDAObjects.UIA
		if (
			isinstance(focus, NVDAObjects.UIA.UIA)
			and self.clientObject.compareElements(focus.UIAElement, sender)
		):
			pass
		elif not self.isNativeUIAElement(sender):
			if _isDebug():
				log.debug(
					f"HandleAutomationEvent: Ignoring event {NVDAEventName} for non native element"
				)
			return
		window = self.getNearestWindowHandle(sender)
		if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window):
			if _isDebug():
				log.debug(
					f"HandleAutomationEvent: Ignoring event {NVDAEventName} for shouldAcceptEvent=False"
				)
			return
		try:
			obj = NVDAObjects.UIA.UIA(UIAElement=sender)
		except Exception:
			if _isDebug():
				log.debugWarning(
					f"HandleAutomationEvent: Exception while creating object for event {NVDAEventName}",
					exc_info=True
				)
			return
		if (
			not obj
			or (NVDAEventName=="gainFocus" and not obj.shouldAllowUIAFocusEvent)
			or (NVDAEventName=="liveRegionChange" and not obj._shouldAllowUIALiveRegionChangeEvent)
		):
			if _isDebug():
				log.debug(
					"HandleAutomationEvent: "
					f"Ignoring event {NVDAEventName} because no object or ignored by object itself"
				)
			return
		if obj==focus:
			obj=focus
		eventHandler.queueEvent(NVDAEventName,obj)
Example #4
0
	def IUIAutomationEventHandler_HandleAutomationEvent(self,sender,eventID):
		if not self.MTAThreadInitEvent.isSet():
			# UIAHandler hasn't finished initialising yet, so just ignore this event.
			return
		if eventID==UIA_MenuOpenedEventId and eventHandler.isPendingEvents("gainFocus"):
			# We don't need the menuOpened event if focus has been fired,
			# as focus should be more correct.
			return
		NVDAEventName=UIAEventIdsToNVDAEventNames.get(eventID,None)
		if not NVDAEventName:
			return
		if not self.isNativeUIAElement(sender):
			return
		window=self.getNearestWindowHandle(sender)
		if window and not eventHandler.shouldAcceptEvent(NVDAEventName,windowHandle=window):
			return
		import NVDAObjects.UIA
		obj=NVDAObjects.UIA.UIA(UIAElement=sender)
		if (
			not obj
			or (NVDAEventName=="gainFocus" and not obj.shouldAllowUIAFocusEvent)
			or (NVDAEventName=="liveRegionChange" and not obj._shouldAllowUIALiveRegionChangeEvent)
		):
			return
		focus=api.getFocusObject()
		if obj==focus:
			obj=focus
		eventHandler.queueEvent(NVDAEventName,obj)
Example #5
0
 def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(
         self, sender):
     if not self.MTAThreadInitEvent.isSet():
         # UIAHandler hasn't finished initialising yet, so just ignore this event.
         return
     self.lastFocusedUIAElement = sender
     if not self.isNativeUIAElement(sender):
         return
     import NVDAObjects.UIA
     if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA):
         lastFocus = eventHandler.lastQueuedFocusObject.UIAElement
         # Ignore duplicate focus events.
         # It seems that it is possible for compareElements to return True, even though the objects are different.
         # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state.
         if self.clientObject.compareElements(
                 sender, lastFocus) and lastFocus.currentHasKeyboardFocus:
             return
     window = self.getNearestWindowHandle(sender)
     if window and not eventHandler.shouldAcceptEvent("gainFocus",
                                                      windowHandle=window):
         return
     obj = NVDAObjects.UIA.UIA(UIAElement=sender)
     if not obj or not obj.shouldAllowUIAFocusEvent:
         return
     eventHandler.queueEvent("gainFocus", obj)
	def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(self,sender,propertyId,newValue):
		# #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes.
		# We also don't use the value in this callback.
		newValue.vt=VT_EMPTY
		if not self.MTAThreadInitEvent.isSet():
			# UIAHandler hasn't finished initialising yet, so just ignore this event.
			return
		NVDAEventName=UIAPropertyIdsToNVDAEventNames.get(propertyId,None)
		if not NVDAEventName:
			return
		if not self.isNativeUIAElement(sender):
			return
		try:
			window=sender.cachedNativeWindowHandle
		except COMError:
			window=None
		if window and not eventHandler.shouldAcceptEvent(NVDAEventName,windowHandle=window):
			return
		import NVDAObjects.UIA
		obj=NVDAObjects.UIA.UIA(UIAElement=sender)
		if not obj:
			return
		focus=api.getFocusObject()
		if obj==focus:
			obj=focus
		eventHandler.queueEvent(NVDAEventName,obj)
Example #7
0
def pumpAll():
	global _deferUntilForegroundWindow,_foregroundDefers
	if _deferUntilForegroundWindow:
		# #3831: Sometimes, a foreground event is fired,
		# but GetForegroundWindow() takes a short while to return this new foreground.
		if _foregroundDefers<MAX_FOREGROUND_DEFERS and winUser.getForegroundWindow()!=_deferUntilForegroundWindow:
			# Wait a core cycle before handling events to give the foreground window time to update.
			core.requestPump()
			_foregroundDefers+=1
			return
		else:
			# Either the foreground window is now correct
			# or we've already had the maximum number of defers.
			# (Sometimes, foreground events are fired even when the foreground hasn't actually changed.)
			_deferUntilForegroundWindow=None

	#Receive all the winEvents from the limiter for this cycle
	winEvents=winEventLimiter.flushEvents()
	focusWinEvents=[]
	validFocus=False
	fakeFocusEvent=None
	for winEvent in winEvents[0-MAX_WINEVENTS:]:
		# #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback,
		# but this causes focus issues when starting applications.
		if not eventHandler.shouldAcceptEvent(winEventIDsToNVDAEventNames[winEvent[0]], windowHandle=winEvent[1]):
			continue
		#We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one 
		if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS,winUser.EVENT_SYSTEM_FOREGROUND):
			focusWinEvents.append(winEvent)
			continue
		else:
			for focusWinEvent in reversed(focusWinEvents):
				procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent
				if procFunc(*(focusWinEvent[1:])):
					validFocus=True
					break
			focusWinEvents=[]
			if winEvent[0]==winUser.EVENT_SYSTEM_DESKTOPSWITCH:
				processDesktopSwitchWinEvent(*winEvent[1:])
			elif winEvent[0]==winUser.EVENT_OBJECT_SHOW:
				processShowWinEvent(*winEvent[1:])
			elif winEvent[0] in MENU_EVENTIDS+(winUser.EVENT_SYSTEM_SWITCHEND,):
				# If there is no valid focus event, we may need to use this to fake the focus later.
				fakeFocusEvent=winEvent
			else:
				processGenericWinEvent(*winEvent)
	for focusWinEvent in reversed(focusWinEvents):
		procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent
		if procFunc(*(focusWinEvent[1:])):
			validFocus=True
			break
	if fakeFocusEvent:
		# Try this as a last resort.
		if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART):
			# menuStart needs to be handled specially and might act even if there was a valid focus event.
			processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus)
		elif not validFocus:
			# Other fake focus events only need to be handled if there was no valid focus event.
			processFakeFocusWinEvent(*fakeFocusEvent)
Example #8
0
 def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(
         self, sender):
     if not self.MTAThreadInitEvent.isSet():
         # UIAHandler hasn't finished initialising yet, so just ignore this event.
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: event received while not fully initialized"
             )
         return
     self.lastFocusedUIAElement = sender
     if not self.isNativeUIAElement(sender):
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring for non native element")
         return
     import NVDAObjects.UIA
     if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA):
         lastFocusObj = eventHandler.lastQueuedFocusObject
         # Ignore duplicate focus events.
         # It seems that it is possible for compareElements to return True, even though the objects are different.
         # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state.
         try:
             if (not lastFocusObj.shouldAllowDuplicateUIAFocusEvent
                     and self.clientObject.compareElements(
                         sender, lastFocusObj.UIAElement)
                     and lastFocusObj.UIAElement.currentHasKeyboardFocus):
                 if _isDebug():
                     log.debugWarning(
                         "HandleFocusChangedEvent: Ignoring duplicate focus event"
                     )
                 return
         except COMError:
             if _isDebug():
                 log.debugWarning(
                     "HandleFocusChangedEvent: Couldn't check for duplicate focus event",
                     exc_info=True)
     window = self.getNearestWindowHandle(sender)
     if window and not eventHandler.shouldAcceptEvent("gainFocus",
                                                      windowHandle=window):
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring for shouldAcceptEvent=False"
             )
         return
     try:
         obj = NVDAObjects.UIA.UIA(UIAElement=sender)
     except Exception:
         if _isDebug():
             log.debugWarning(
                 "HandleFocusChangedEvent: Exception while creating object",
                 exc_info=True)
         return
     if not obj or not obj.shouldAllowUIAFocusEvent:
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring because no object or ignored by object itself"
             )
         return
     eventHandler.queueEvent("gainFocus", obj)
def pumpAll():
	global _deferUntilForegroundWindow,_foregroundDefers
	if _deferUntilForegroundWindow:
		# #3831: Sometimes, a foreground event is fired,
		# but GetForegroundWindow() takes a short while to return this new foreground.
		if _foregroundDefers<MAX_FOREGROUND_DEFERS and winUser.getForegroundWindow()!=_deferUntilForegroundWindow:
			# Wait a core cycle before handling events to give the foreground window time to update.
			core.requestPump()
			_foregroundDefers+=1
			return
		else:
			# Either the foreground window is now correct
			# or we've already had the maximum number of defers.
			# (Sometimes, foreground events are fired even when the foreground hasn't actually changed.)
			_deferUntilForegroundWindow=None

	#Receive all the winEvents from the limiter for this cycle
	winEvents=winEventLimiter.flushEvents()
	focusWinEvents=[]
	validFocus=False
	fakeFocusEvent=None
	for winEvent in winEvents[0-MAX_WINEVENTS:]:
		# #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback,
		# but this causes focus issues when starting applications.
		if not eventHandler.shouldAcceptEvent(winEventIDsToNVDAEventNames[winEvent[0]], windowHandle=winEvent[1]):
			continue
		#We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one 
		if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS,winUser.EVENT_SYSTEM_FOREGROUND):
			focusWinEvents.append(winEvent)
			continue
		else:
			for focusWinEvent in reversed(focusWinEvents):
				procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent
				if procFunc(*(focusWinEvent[1:])):
					validFocus=True
					break
			focusWinEvents=[]
			if winEvent[0]==winUser.EVENT_SYSTEM_DESKTOPSWITCH:
				processDesktopSwitchWinEvent(*winEvent[1:])
			elif winEvent[0]==winUser.EVENT_OBJECT_SHOW:
				processShowWinEvent(*winEvent[1:])
			elif winEvent[0] in MENU_EVENTIDS+(winUser.EVENT_SYSTEM_SWITCHEND,):
				# If there is no valid focus event, we may need to use this to fake the focus later.
				fakeFocusEvent=winEvent
			else:
				processGenericWinEvent(*winEvent)
	for focusWinEvent in reversed(focusWinEvents):
		procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent
		if procFunc(*(focusWinEvent[1:])):
			validFocus=True
			break
	if fakeFocusEvent:
		# Try this as a last resort.
		if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART):
			# menuStart needs to be handled specially and might act even if there was a valid focus event.
			processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus)
		elif not validFocus:
			# Other fake focus events only need to be handled if there was no valid focus event.
			processFakeFocusWinEvent(*fakeFocusEvent)
Example #10
0
	def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(self,sender,propertyId,newValue):
		# #3867: For now manually force this VARIANT type to empty to get around a nasty double free in comtypes/ctypes.
		# We also don't use the value in this callback.
		newValue.vt=VT_EMPTY
		if not self.MTAThreadInitEvent.isSet():
			# UIAHandler hasn't finished initialising yet, so just ignore this event.
			if _isDebug():
				log.debug("HandlePropertyChangedEvent: event received while not fully initialized")
			return
		try:
			processId = sender.CachedProcessID
		except COMError:
			pass
		else:
			appMod = appModuleHandler.getAppModuleFromProcessID(processId)
			if not appMod.shouldProcessUIAPropertyChangedEvent(sender, propertyId):
				return
		NVDAEventName=UIAPropertyIdsToNVDAEventNames.get(propertyId,None)
		if not NVDAEventName:
			if _isDebug():
				log.debugWarning(f"HandlePropertyChangedEvent: Don't know how to handle property {propertyId}")
			return
		focus = api.getFocusObject()
		import NVDAObjects.UIA
		if (
			isinstance(focus, NVDAObjects.UIA.UIA)
			and self.clientObject.compareElements(focus.UIAElement, sender)
		):
			pass
		elif not self.isNativeUIAElement(sender):
			if _isDebug():
				log.debug(
					f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} for non native element"
				)
			return
		window = self.getNearestWindowHandle(sender)
		if window and not eventHandler.shouldAcceptEvent(NVDAEventName, windowHandle=window):
			if _isDebug():
				log.debug(
					f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} for shouldAcceptEvent=False"
				)
			return
		try:
			obj = NVDAObjects.UIA.UIA(UIAElement=sender)
		except Exception:
			if _isDebug():
				log.debugWarning(
					f"HandlePropertyChangedEvent: Exception while creating object for event {NVDAEventName}",
					exc_info=True
				)
			return
		if not obj:
			if _isDebug():
				log.debug(f"HandlePropertyChangedEvent: Ignoring event {NVDAEventName} because no object")
			return
		if obj==focus:
			obj=focus
		eventHandler.queueEvent(NVDAEventName,obj)
Example #11
0
	def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(self,sender):
		if not self.MTAThreadInitEvent.isSet():
			# UIAHandler hasn't finished initialising yet, so just ignore this event.
			return
		if not self.isNativeUIAElement(sender):
			return
		import NVDAObjects.UIA
		if isinstance(eventHandler.lastQueuedFocusObject,NVDAObjects.UIA.UIA):
			lastFocus=eventHandler.lastQueuedFocusObject.UIAElement
			# Ignore duplicate focus events.
			# It seems that it is possible for compareElements to return True, even though the objects are different.
			# Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state.
			if self.clientObject.compareElements(sender,lastFocus) and lastFocus.currentHasKeyboardFocus:
				return
		window=self.getNearestWindowHandle(sender)
		if window and not eventHandler.shouldAcceptEvent("gainFocus",windowHandle=window):
			return
		obj=NVDAObjects.UIA.UIA(UIAElement=sender)
		if not obj or not obj.shouldAllowUIAFocusEvent:
			return
		eventHandler.queueEvent("gainFocus",obj)
Example #12
0
def pumpAll():  # noqa: C901
    if not _shouldGetEvents():
        return
    focusWinEvents = []
    validFocus = False
    fakeFocusEvent = None
    focus = eventHandler.lastQueuedFocusObject

    # Receive all the winEvents from the limiter for this cycle
    winEvents = winEventLimiter.flushEvents()
    winEvents = winEvents[0 - MAX_WINEVENTS:]

    for winEvent in winEvents:
        isEventOnCaret = winEvent[2] == winUser.OBJID_CARET
        showHideCaretEvent = focus and isEventOnCaret and winEvent[0] in [
            winUser.EVENT_OBJECT_SHOW, winUser.EVENT_OBJECT_HIDE
        ]
        # #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback, but this causes focus issues when
        # starting applications. #7332: If this is a show event, which would normally be dropped by
        # `shouldAcceptEvent` and this event is for the caret, later it will be mapped to a caret event,
        # so skip `shouldAcceptEvent`
        if showHideCaretEvent:
            if not focus.shouldAcceptShowHideCaretEvent:
                continue
        elif not eventHandler.shouldAcceptEvent(
                winEventIDsToNVDAEventNames[winEvent[0]],
                windowHandle=winEvent[1]):
            continue
        # We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one
        if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS,
                           winUser.EVENT_SYSTEM_FOREGROUND):
            focusWinEvents.append(winEvent)
            continue
        else:
            for focusWinEvent in reversed(focusWinEvents):
                isForeground = focusWinEvent[
                    0] == winUser.EVENT_SYSTEM_FOREGROUND
                procFunc = processForegroundWinEvent if isForeground else processFocusWinEvent
                if procFunc(*(focusWinEvent[1:])):
                    validFocus = True
                    break
            focusWinEvents = []
            if winEvent[0] == winUser.EVENT_SYSTEM_DESKTOPSWITCH:
                processDesktopSwitchWinEvent(*winEvent[1:])
            # we dont want show caret events to be processed by `processShowWinEvent`, instead they should be
            # handled by `processGenericWinEvent`
            elif winEvent[
                    0] == winUser.EVENT_OBJECT_SHOW and not isEventOnCaret:
                processShowWinEvent(*winEvent[1:])
            elif winEvent[0] in MENU_EVENTIDS + (
                    winUser.EVENT_SYSTEM_SWITCHEND, ):
                # If there is no valid focus event, we may need to use this to fake the focus later.
                fakeFocusEvent = winEvent
            else:
                processGenericWinEvent(*winEvent)
    for focusWinEvent in reversed(focusWinEvents):
        isForeground = focusWinEvent[0] == winUser.EVENT_SYSTEM_FOREGROUND
        procFunc = processForegroundWinEvent if isForeground else processFocusWinEvent
        if procFunc(*(focusWinEvent[1:])):
            validFocus = True
            break
    if fakeFocusEvent:
        # Try this as a last resort.
        if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART,
                                 winUser.EVENT_SYSTEM_MENUPOPUPSTART):
            # menuStart needs to be handled specially and might act even if there was a valid focus event.
            processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus)
        elif not validFocus:
            # Other fake focus events only need to be handled if there was no valid focus event.
            processFakeFocusWinEvent(*fakeFocusEvent)
Example #13
0
 def IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(
         self, sender):
     if not self.MTAThreadInitEvent.isSet():
         # UIAHandler hasn't finished initialising yet, so just ignore this event.
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: event received while not fully initialized"
             )
         return
     self.lastFocusedUIAElement = sender
     if not self.isNativeUIAElement(sender):
         # #12982: This element may be the root of an MS Word document
         # for which we may be refusing to use UIA as its implementation may be incomplete.
         # However, there are some controls embedded in the MS Word document window
         # such as the Modern comments side track pane
         # for which we do have to use UIA.
         # But, if focus jumps from one of these controls back to the document (E.g. the user presses escape),
         # we receive no MSAA focus event, only a UIA focus event.
         # As we are not treating the Word doc as UIA, we need to manually fire an MSAA focus event on the document.
         self._emitMSAAFocusForWordDocIfNecessary(sender)
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring for non native element")
         return
     import NVDAObjects.UIA
     if isinstance(eventHandler.lastQueuedFocusObject, NVDAObjects.UIA.UIA):
         lastFocusObj = eventHandler.lastQueuedFocusObject
         # Ignore duplicate focus events.
         # It seems that it is possible for compareElements to return True, even though the objects are different.
         # Therefore, don't ignore the event if the last focus object has lost its hasKeyboardFocus state.
         try:
             if (not lastFocusObj.shouldAllowDuplicateUIAFocusEvent
                     and self.clientObject.compareElements(
                         sender, lastFocusObj.UIAElement)
                     and lastFocusObj.UIAElement.currentHasKeyboardFocus):
                 if _isDebug():
                     log.debugWarning(
                         "HandleFocusChangedEvent: Ignoring duplicate focus event"
                     )
                 return
         except COMError:
             if _isDebug():
                 log.debugWarning(
                     "HandleFocusChangedEvent: Couldn't check for duplicate focus event",
                     exc_info=True)
     window = self.getNearestWindowHandle(sender)
     if window and not eventHandler.shouldAcceptEvent("gainFocus",
                                                      windowHandle=window):
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring for shouldAcceptEvent=False"
             )
         return
     try:
         obj = NVDAObjects.UIA.UIA(UIAElement=sender)
     except Exception:
         if _isDebug():
             log.debugWarning(
                 "HandleFocusChangedEvent: Exception while creating object",
                 exc_info=True)
         return
     if not obj or not obj.shouldAllowUIAFocusEvent:
         if _isDebug():
             log.debug(
                 "HandleFocusChangedEvent: Ignoring because no object or ignored by object itself"
             )
         return
     eventHandler.queueEvent("gainFocus", obj)