Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set foreground object rather than desktop object when initializing object cache #14301

Merged
merged 18 commits into from Oct 31, 2022

Conversation

seanbudd
Copy link
Member

@seanbudd seanbudd commented Oct 26, 2022

Link to issue number:

None

Summary of the issue:

This regression was introduced in 2022.3.1

If NVDA starts on the lock screen (such as when starting your device), NVDA fails to cache the desktop object as the focus object, mouse object, etc. as it is from below the lock screen.
This causes NVDA to not function correctly after sign-in, such as the inability to use nvda+q.

Description of user facing changes

Fixes severe regression with using NVDA after sign-in, if NVDA starts on the lock screen.

Description of development approach

Cache the foreground window when NVDA starts up, rather than the desktop object.
This window should be accessible from the lock screen when NVDA starts on the lock screen.
The setDesktopObject should continue to cached the desktop object as usual, as security code has not been implemented to prevent this from being set to insecure objects.

Creates a generic function blockUntilConditionMet, to wait for the foreground window to be fetched successfully.
Unit testing discovered a bug with the system test implementation of blockUntilConditionMet. This has now been fixed.

Testing strategy:

Using a try build tested the following steps:

  1. Restart device
  2. Wait for NVDA to start automatically on the lock screen
  3. Sign-in to Windows
  4. Continue to use NVDA normally after sign-in (e.g nvda+q opens the quit dialog)

Unit tests have been added for blockUntilConditionMet.

Known issues with pull request:

None

Change log entries:

- Fixes a regression from 2022.3.1 where certain functionality was disabled after sign-in, if NVDA started on the lock screen. (#14301)

Code Review Checklist:

  • Pull Request description:
    • description is up to date
    • change log entries
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • API is compatible with existing add-ons.
  • Documentation:
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • Security precautions taken.

@seanbudd seanbudd changed the title Fix startup regression Fix NVDA not functioning correctly when starting on the lock screen Oct 26, 2022
@seanbudd seanbudd changed the title Fix NVDA not functioning correctly when starting on the lock screen Set foreground object rather than desktop object when initializing object cache Oct 26, 2022
@seanbudd seanbudd marked this pull request as ready for review October 26, 2022 04:24
@seanbudd seanbudd requested a review from a team as a code owner October 26, 2022 04:24
@seanbudd seanbudd requested review from feerrenrut and removed request for a team October 26, 2022 04:24
@seanbudd seanbudd added this to the 2022.3.2 milestone Oct 26, 2022
@seanbudd seanbudd self-assigned this Oct 26, 2022
source/core.py Outdated Show resolved Hide resolved
feerrenrut
feerrenrut previously approved these changes Oct 26, 2022
Copy link
Contributor

@feerrenrut feerrenrut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, happy with the approach.

source/core.py Show resolved Hide resolved
Copy link
Member

@michaelDCurran michaelDCurran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetForegroundWindow will have to continuously polled to ensure it is not NULL. This polling should be allowed to take as long as it needs... E.g. 10, 20 seconds... the setting of these objects can not fail.
Though this situation is extremely rare, and most likely will correct itself within a second or two.

michaelDCurran
michaelDCurran previously approved these changes Oct 26, 2022
source/core.py Outdated Show resolved Hide resolved
@seanbudd seanbudd marked this pull request as ready for review October 26, 2022 08:42
@AppVeyorBot
Copy link

See test results for failed build of commit dbca3e5488

source/core.py Show resolved Hide resolved
source/core.py Outdated
Comment on lines 426 to 461
foregroundHWND = winUser.getForegroundWindow()
_hasWarned = False
WARN_AFTER_SECS = 5
MAX_WAIT_TIME_SECS = 20
POLL_INTERVAL_SECS = 0.1
waitForForegroundHWND_startTime = time.time()

while not foregroundHWND:
# The foreground window can be NULL in certain circumstances,
# such as when a window is losing activation.
# This should not remain the case for an extended period of time.

_timeElapsed = time.time() - waitForForegroundHWND_startTime
if (
not _hasWarned
and _timeElapsed > WARN_AFTER_SECS
):
_hasWarned = True
# Allow for braille / speech to be understood before exiting.
# Unfortunately we cannot block with a dialog as NVDA cannot read dialogs yet.
ui.message(_(
# Translators: Message when NVDA is having an issue starting up
"NVDA is failing to fetch the foreground window. "
"If this continues, NVDA will quit starting in %d seconds." % (MAX_WAIT_TIME_SECS - WARN_AFTER_SECS)
))

if _timeElapsed > MAX_WAIT_TIME_SECS:
log.critical("NVDA could not fetch the foreground window. Exiting NVDA.")
# Raising exception here causes core.main to exit and NVDA to fail to start
raise NVDANotInitializedError("Could not fetch foreground window")

log.debugWarning("Foreground window not found, fetching again")
time.sleep(POLL_INTERVAL_SECS)
foregroundHWND = winUser.getForegroundWindow()

return foregroundHWND
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I didn't hit submit on this comment.

I want to make sure all the paths here are tested. Doing so manually will be difficult. I think this could be easier to read and unit testable if the polling algorithm was decoupled from the condition and outcomes.

Suggestion:

	# The foreground window can be NULL in certain circumstances,
	# such as when a window is losing activation.
	# This should not remain the case for an extended period of time.
	WARN_AFTER_SECS = 5
	MAX_WAIT_TIME_SECS = 20
	POLL_INTERVAL_SECS = 0.1
	
	success, foregroundHWND = pollForCondition(
		condition=lambda: bool(winUser.getForegroundWindow()),
		maxWait=WARN_AFTER_SECS,
		intervalSeconds=POLL_INTERVAL_SECS
	)
	if not success:
		ui.message(_(
			# Translators: Message when NVDA is having an issue starting up
			"NVDA is failing to fetch the foreground window. "
			"If this continues, NVDA will quit starting in %d seconds." % (MAX_WAIT_TIME_SECS - WARN_AFTER_SECS)
		))
	success, foregroundHWND = pollForCondition(
		condition=lambda: bool(winUser.getForegroundWindow()),
		maxWait=MAX_WAIT_TIME_SECS - WARN_AFTER_SECS,
		intervalSeconds=POLL_INTERVAL_SECS
	)
	if not success:
		log.critical("NVDA could not fetch the foreground window. Exiting NVDA.")
		# Raising exception here causes core.main to exit and NVDA to fail to start
		raise NVDANotInitializedError("Could not fetch foreground window")
	return foregroundHWND

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have implemented something similar

@seanbudd seanbudd marked this pull request as draft October 27, 2022 04:43
@seanbudd seanbudd marked this pull request as ready for review October 28, 2022 01:07
@AppVeyorBot
Copy link

See test results for failed build of commit c7522f8c27

Copy link
Member Author

@seanbudd seanbudd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rewritten the testing suite

Copy link
Contributor

@feerrenrut feerrenrut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @seanbudd

@seanbudd seanbudd merged commit 49b66db into rc Oct 31, 2022
@seanbudd seanbudd deleted the fix-startup-regression branch October 31, 2022 06:21
seanbudd added a commit that referenced this pull request Nov 3, 2022
Summary of the issue:
#14301 moved an api import from core.main as it was otherwise unused
#14050 introduced a usage of api to core.main to beta
This means that api was used without being imported on beta and master.

Description of user facing changes
None

Description of development approach
Fix import error
seanbudd added a commit that referenced this pull request Nov 7, 2022
Summary of the issue:
During NVDA initialization, the api module is used to set initial object caches.
These api functions check if an object is behind the lock screen when Windows is locked before setting the cache.
Until #14301, the api module initialised the object caches to the desktop object.
When NVDA started on the lock screen, it failed to set the desktop object due to the added security checks.
Now the object caches are initialised to the foreground window, to avoid leaking insecure information.
Unfortunately, setting those object caches causes side-effects, which may rely on uninitialized dependencies.

Description of user facing changes
None

Description of development approach
Revert to setting initial object caches to the desktop object.
Ensure security failures are not raised when initialising caches by ignoring the windows lock state.
seanbudd added a commit that referenced this pull request Dec 7, 2022
Summary of the issue:
Due to intermittent system test failures, #14284 increased the polling interval used in blockUntilConditionMet.
When writing unit tests for blockUntilConditionMet in #14301, a bug was picked up. This bug caused blockUntilConditionMet to spin for longer than expected and potentially caused system tests to fail.

This bug was fixed with a new implementation of blockUntilConditionMet.

Description of user facing changes
For devs, system tests should be faster (todo: estimate from build?)

Description of development approach
Lower default polling interval
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants