Skip to content

Commit

Permalink
Merge 0b305bc into 9823556
Browse files Browse the repository at this point in the history
  • Loading branch information
seanbudd authored Dec 15, 2022
2 parents 9823556 + 0b305bc commit 267bb3f
Show file tree
Hide file tree
Showing 20 changed files with 901 additions and 357 deletions.
6 changes: 3 additions & 3 deletions source/NVDAHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import api
import globalVars
from logHandler import log
from utils.security import isWindowsLocked
from utils.security import _isLockScreenModeActive

versionedLibPath = os.path.join(globalVars.appDir, 'lib')
if os.environ.get('PROCESSOR_ARCHITEW6432') == 'ARM64':
Expand Down Expand Up @@ -455,7 +455,7 @@ def nvdaControllerInternal_installAddonPackageFromPath(addonPath):
if globalVars.appArgs.secure:
log.debugWarning("Unable to install add-on into secure copy of NVDA.")
return
if isWindowsLocked():
if _isLockScreenModeActive():
log.debugWarning("Unable to install add-on while Windows is locked.")
return
import wx
Expand All @@ -470,7 +470,7 @@ def nvdaControllerInternal_openConfigDirectory():
if globalVars.appArgs.secure:
log.debugWarning("Unable to open user config directory for secure copy of NVDA.")
return
if isWindowsLocked():
if _isLockScreenModeActive():
log.debugWarning("Unable to open user config directory while Windows is locked.")
return
import systemUtils
Expand Down
13 changes: 11 additions & 2 deletions source/NVDAObjects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
TreeInterceptor,
)
import braille
from utils.security import _isObjectBelowLockScreen
import vision
import globalPluginHandler
import brailleInput
import locationHelper
import aria
from winAPI.sessionTracking import isWindowsLocked
from winAPI.sessionTracking import _isLockScreenModeActive


class NVDAObjectTextInfo(textInfos.offsets.OffsetsTextInfo):
Expand Down Expand Up @@ -181,7 +182,7 @@ def _insertLockScreenObject(self, clsList: typing.List["NVDAObject"]) -> None:
Inserts LockScreenObject to the start of the clsList if Windows is locked.
"""
from .lockscreen import LockScreenObject
if isWindowsLocked():
if _isLockScreenModeActive():
# This must be resolved first to prevent object navigation outside of the lockscreen.
clsList.insert(0, LockScreenObject)

Expand Down Expand Up @@ -1433,3 +1434,11 @@ def getSelectedItemsCount(self,maxCount=2):
For performance, this method will only count up to the given maxCount number, and if there is one more above that, then sys.maxint is returned stating that many items are selected.
"""
return 0

#: Type definition for auto prop '_get_isBelowLockScreen'
isBelowLockScreen: bool

def _get_isBelowLockScreen(self) -> bool:
if not _isLockScreenModeActive():
return False
return _isObjectBelowLockScreen(self)
19 changes: 17 additions & 2 deletions source/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,23 @@ def setReviewPosition(
@param isCaret: Whether the review position is changed due to caret following.
@param isMouse: Whether the review position is changed due to mouse following.
"""
if _isSecureObjectWhileLockScreenActivated(reviewPosition.obj):
return False
reviewObj = reviewPosition.obj

if isinstance(reviewObj, treeInterceptorHandler.DocumentTreeInterceptor):
# reviewPosition.obj can be a number of classes, e.g.
# CursorManager, DocumentWithTableNavigation, EditableText.
# We can only handle the NVDAObject case.
reviewObj = reviewObj.rootNVDAObject

if isinstance(reviewObj, NVDAObjects.NVDAObject):
# reviewPosition.obj can be a number of classes, e.g.
# CursorManager, DocumentWithTableNavigation, EditableText.
# We can only handle the NVDAObject case.
if _isSecureObjectWhileLockScreenActivated(reviewObj):
return False
else:
log.debug(f"Unhandled reviewObj type {type(reviewObj)} when checking security of reviewObj")

globalVars.reviewPosition=reviewPosition.copy()
globalVars.reviewPositionObj=reviewPosition.obj
if clearNavigatorObject: globalVars.navigatorObject=None
Expand Down
25 changes: 25 additions & 0 deletions source/appModuleHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,3 +832,28 @@ def getWmiProcessInfo(processId):
except:
raise LookupError("Couldn't get process information using WMI")
raise LookupError("No such process")


def _checkWindowsForAppModules():
"""
Updates the appModuleHandler with the process from all top level windows.
Adds any missing processes.
"""
import winUser

# BOOL CALLBACK EnumWindowsProc _In_ HWND,_In_ LPARAM
# HWND as a pointer creates confusion, treat as an int
# http://makble.com/the-story-of-lpclong

@ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
def _appModuleHandlerUpdate(
hwnd: winUser.HWNDVal,
_lParam: ctypes.wintypes.LPARAM
) -> bool:
processID, _threadID = winUser.getWindowThreadProcessID(hwnd)
if processID not in runningTable:
update(processID)
return True

if not ctypes.windll.user32.EnumWindows(_appModuleHandlerUpdate, 0):
log.error("Failed to refresh app modules")
10 changes: 5 additions & 5 deletions source/appModules/lockapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
from NVDAObjects.lockscreen import LockScreenObject
from NVDAObjects.UIA import UIA
from utils.security import getSafeScripts
from winAPI.sessionTracking import isWindowsLocked
from winAPI.sessionTracking import _isLockScreenModeActive

"""App module for the Windows 10 and 11 lock screen.
The lock screen allows other windows to be opened, so security related functions
are done at a higher level than the lockapp app module.
Refer to usages of `winAPI.sessionTracking.isWindowsLocked`.
Refer to usages of `winAPI.sessionTracking._isLockScreenModeActive`.
"""


Expand Down Expand Up @@ -60,11 +60,11 @@ def chooseNVDAObjectOverlayClasses(
if isinstance(obj,UIA) and obj.role==controlTypes.Role.PANE and obj.UIAElement.cachedClassName=="LockAppContainer":
clsList.insert(0,LockAppContainer)

if not isWindowsLocked():
if not _isLockScreenModeActive():
log.debugWarning(
"LockApp is being initialized but NVDA does not expect Windows to be locked. "
"DynamicNVDAObjectType may have failed to apply LockScreenObject. "
"This means Windows session tracking has failed or NVDA is yet to receive lock event. "
"This means session lock state tracking has failed. "
)
clsList.insert(0, LockScreenObject)

Expand All @@ -83,7 +83,7 @@ def _inputCaptor(self, gesture: inputCore.InputGesture) -> bool:
if not scriptShouldRun:
log.error(
"scriptHandler failed to block script when Windows is locked. "
"This means Windows session tracking has failed. "
"This means session lock state tracking has failed. "
)
return scriptShouldRun

Expand Down
30 changes: 26 additions & 4 deletions source/baseObject.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2007-2020 NV Access Limited, Christopher Toth, Babbage B.V., Julien Cochuyt
# Copyright (C) 2007-2022 NV Access Limited, Christopher Toth, Babbage B.V., Julien Cochuyt
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

"""Contains the base classes that many of NVDA's classes such as NVDAObjects, virtualBuffers, appModules, synthDrivers inherit from. These base classes provide such things as auto properties, and methods and properties for scripting and key binding.
"""

from typing import (
Any,
Callable,
Optional,
Set,
Union,
)
import weakref
import garbageHandler
from logHandler import log
from abc import ABCMeta, abstractproperty

GetterReturnT = Any
GetterMethodT = Callable[["AutoPropertyObject"], GetterReturnT]


class Getter(object):

def __init__(self,fget, abstract=False):
self.fget=fget
if abstract:
self._abstract = self.__isabstractmethod__ = abstract

def __get__(self,instance,owner):
def __get__(
self,
instance: Union[Any, None, "AutoPropertyObject"],
owner,
) -> Union[GetterReturnT, "Getter"]:
if isinstance(self.fget, classmethod):
return self.fget.__get__(instance, owner)()
elif instance is None:
Expand All @@ -31,16 +46,22 @@ def setter(self,func):
def deleter(self,func):
return (abstractproperty if self._abstract else property)(fget=self.fget,fdel=func)


class CachingGetter(Getter):

def __get__(self, instance, owner):
def __get__(
self,
instance: Union[Any, None, "AutoPropertyObject"],
owner,
) -> Union[GetterReturnT, "CachingGetter"]:
if isinstance(self.fget, classmethod):
log.warning("Class properties do not support caching")
return self.fget.__get__(instance, owner)()
elif instance is None:
return self
return instance._getPropertyViaCache(self.fget)


class AutoPropertyType(ABCMeta):

def __init__(self,name,bases,dict):
Expand Down Expand Up @@ -125,6 +146,7 @@ class AutoPropertyObject(garbageHandler.TrackedObject, metaclass=AutoPropertyTyp
#: @type: bool
cachePropertiesByDefault = False

_propertyCache: Set[GetterMethodT]

def __new__(cls, *args, **kwargs):
self = super(AutoPropertyObject, cls).__new__(cls)
Expand All @@ -134,7 +156,7 @@ def __new__(cls, *args, **kwargs):
self.__instances[self]=None
return self

def _getPropertyViaCache(self,getterMethod=None):
def _getPropertyViaCache(self, getterMethod: Optional[GetterMethodT] = None) -> GetterReturnT:
if not getterMethod:
raise ValueError("getterMethod is None")
missing=False
Expand Down
8 changes: 4 additions & 4 deletions source/brailleViewer/brailleViewerGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import fonts
import inputCore
import gui.contextHelp
from utils.security import isWindowsLocked, postSessionLockStateChanged
from utils.security import _isLockScreenModeActive, postSessionLockStateChanged

BRAILLE_UNICODE_PATTERNS_START = 0x2800
BRAILLE_SPACE_CHARACTER = chr(BRAILLE_UNICODE_PATTERNS_START)
Expand Down Expand Up @@ -398,7 +398,7 @@ def _createControls(self, sizer: wx.Sizer, parent: wx.Control) -> None:
self._shouldShowOnStartupCheckBox.SetValue(config.conf["brailleViewer"]["showBrailleViewerAtStartup"])
self._shouldShowOnStartupCheckBox.Bind(wx.EVT_CHECKBOX, self._onShouldShowOnStartupChanged)
optionsSizer.Add(self._shouldShowOnStartupCheckBox)
if isWindowsLocked():
if _isLockScreenModeActive():
self._shouldShowOnStartupCheckBox.Disable()

# Translators: The label for a setting in the braille viewer that controls
Expand All @@ -415,11 +415,11 @@ def _createControls(self, sizer: wx.Sizer, parent: wx.Control) -> None:
sizer.Add(optionsSizer, flag=wx.EXPAND | wx.TOP, border=5)

def _onShouldShowOnStartupChanged(self, evt: wx.CommandEvent):
if not isWindowsLocked():
if not _isLockScreenModeActive():
config.conf["brailleViewer"]["showBrailleViewerAtStartup"] = self._shouldShowOnStartupCheckBox.IsChecked()

def _onShouldHoverRouteToCellCheckBoxChanged(self, evt: wx.CommandEvent):
if not isWindowsLocked():
if not _isLockScreenModeActive():
config.conf["brailleViewer"]["shouldHoverRouteToCell"] = self._shouldHoverRouteToCellCheckBox.IsChecked()
self._updateMouseOverBinding(self._shouldHoverRouteToCellCheckBox.IsChecked())

Expand Down
26 changes: 7 additions & 19 deletions source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,13 +451,13 @@ class _TrackNVDAInitialization:
regardless of lock state.
Security checks may cause the desktop object to not be set if NVDA starts on the lock screen.
As such, during initialization, NVDA should behave as if Windows is unlocked,
i.e. winAPI.sessionTracking.isWindowsLocked should return False.
i.e. winAPI.sessionTracking._isLockScreenModeActive should return False.
TODO: move to NVDAState module
"""

_isNVDAInitialized = False
"""When False, isWindowsLocked is forced to return False.
"""When False, _isLockScreenModeActive is forced to return False.
"""

@staticmethod
Expand Down Expand Up @@ -601,7 +601,6 @@ def onEndSession(evt):
wx.CallAfter(audioDucking.initialize)

from winAPI.messageWindow import WindowMessage
from winAPI import sessionTracking
import winUser
# #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR.
# NVDA uses the main frame to check for and quit another instance of NVDA.
Expand Down Expand Up @@ -630,27 +629,12 @@ def __init__(self, windowName=None):
self.orientationCoordsCache = (0,0)
self.handlePowerStatusChange()

# Call must be paired with a call to sessionTracking.unregister
if not sessionTracking.register(self.handle):
import utils.security
wx.CallAfter(utils.security.warnSessionLockStateUnknown)

def destroy(self):
"""
NVDA must unregister session tracking before destroying the message window.
"""
# Requires an active message window and a handle to unregister.
sessionTracking.unregister(self.handle)
super().destroy()

def windowProc(self, hwnd, msg, wParam, lParam):
post_windowMessageReceipt.notify(msg=msg, wParam=wParam, lParam=lParam)
if msg == WindowMessage.POWER_BROADCAST and wParam == self.PBT_APMPOWERSTATUSCHANGE:
self.handlePowerStatusChange()
elif msg == winUser.WM_DISPLAYCHANGE:
self.handleScreenOrientationChange(lParam)
elif msg == WindowMessage.WTS_SESSION_CHANGE:
sessionTracking.handleSessionChange(sessionTracking.WindowsTrackedSession(wParam), lParam)

def handleScreenOrientationChange(self, lParam):
# TODO: move to winAPI
Expand Down Expand Up @@ -809,7 +793,8 @@ def run(self):
mouseHandler.pumpAll()
braille.pumpAll()
vision.pumpAll()
except:
sessionTracking.pumpAll()
except Exception:
log.exception("errors in this core pump cycle")
baseObject.AutoPropertyObject.invalidateCaches()
watchdog.asleep()
Expand All @@ -832,6 +817,9 @@ def run(self):
log.debug("initializing updateCheck")
updateCheck.initialize()

from winAPI import sessionTracking
sessionTracking.initialize()

_TrackNVDAInitialization.markInitializationComplete()

log.info("NVDA initialized")
Expand Down
3 changes: 2 additions & 1 deletion source/globalVars.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import typing

if typing.TYPE_CHECKING:
import documentBase # noqa: F401 used for type checking only
import NVDAObjects # noqa: F401 used for type checking only


Expand Down Expand Up @@ -70,7 +71,7 @@ class DefaultAppArgs(argparse.Namespace):
mouseOldY=None
navigatorObject: typing.Optional['NVDAObjects.NVDAObject'] = None
reviewPosition=None
reviewPositionObj=None
reviewPositionObj: typing.Optional["documentBase.TextContainerObject"] = None
lastProgressValue=0
appArgs = DefaultAppArgs()
unknownAppArgs: typing.List[str] = []
Expand Down
13 changes: 10 additions & 3 deletions source/nvda.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,20 @@ if mutex is None:
sys.exit(1)


if _isSecureDesktop():
def _serviceDebugEnabled() -> bool:
import winreg
try:
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\NVDA")
if not winreg.QueryValueEx(k, u"serviceDebug")[0]:
globalVars.appArgs.secure = True
if winreg.QueryValueEx(k, "serviceDebug")[0]:
return True
except WindowsError:
# Expected state by default, serviceDebug parameter not set
pass
return False


if _isSecureDesktop():
if not _serviceDebugEnabled():
globalVars.appArgs.secure = True
globalVars.appArgs.changeScreenReaderFlag = False
globalVars.appArgs.minimal = True
Expand Down
4 changes: 2 additions & 2 deletions source/scriptHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ def getGlobalMapScripts(gesture: "inputCore.InputGesture") -> List["inputCore.In

def findScript(gesture: "inputCore.InputGesture") -> Optional[_ScriptFunctionT]:
from utils.security import getSafeScripts
from winAPI.sessionTracking import isWindowsLocked
from winAPI.sessionTracking import _isLockScreenModeActive
foundScript = _findScript(gesture)
if (
foundScript is not None
and isWindowsLocked()
and _isLockScreenModeActive()
and foundScript not in getSafeScripts()
):
return None
Expand Down
Loading

0 comments on commit 267bb3f

Please sign in to comment.