MozillaCompoundTextInfo: Add special handling for the insertion point at the end of a line.#16745
Conversation
… at the end of a line.
WalkthroughThe recent changes in Changes
Assessment against linked issues
These changes enhance text navigation in Firefox by accurately identifying the caret's position at the end of lines, leading to better reporting and consistency, addressing the issues mentioned in #3156. Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
| if ( | ||
| self._isEndOfLineInsertionPoint and unit != textInfos.UNIT_CHARACTER | ||
| and obj is self._startObj and expandTi._startOffset > 0 | ||
| ): | ||
| # #3156: This is the insertion point at the end of a line. If we use | ||
| # this offset, we'll get the unit at the start of the next line. | ||
| # Subtract 1 so that we get the unit on the current line. | ||
| expandTi._startOffset -= 1 | ||
| expandTi._endOffset -= 1 |
There was a problem hiding this comment.
Optimize the handling of end-of-line insertion points in _findUnitEndpoints.
The logic to adjust the offsets when _isEndOfLineInsertionPoint is True is sound, but this block of code might benefit from a refactor for clarity and potential performance improvements. Consider encapsulating this logic into a method to reduce complexity in _findUnitEndpoints and improve readability.
- if (
- self._isEndOfLineInsertionPoint and unit != textInfos.UNIT_CHARACTER
- and obj is self._startObj and expandTi._startOffset > 0
- ):
- expandTi._startOffset -= 1
- expandTi._endOffset -= 1
+ self.adjustOffsetsForEndOfLine(expandTi, unit, obj)And then define a new method:
def adjustOffsetsForEndOfLine(self, expandTi, unit, obj):
if (
self._isEndOfLineInsertionPoint and unit != textInfos.UNIT_CHARACTER
and obj is self._startObj and expandTi._startOffset > 0
):
expandTi._startOffset -= 1
expandTi._endOffset -= 1
seanbudd
left a comment
There was a problem hiding this comment.
Looks good to me other than the coderabbit comments
…evious character is a line feed. (#16763) Fixup of #16745. Summary of the issue: In my work on #16745, I neglected to consider an empty line after a line feed. In that case, adjusting for the line end caused NVDA to read from the previous line instead of the empty one. Description of user facing changes In Firefox, NVDA no longer speaks the previous line when the caret is on the last line and the last line is empty. However, this does not need a change log entry because this bug never shipped in release. Description of development approach Don't set the _isInsertionPointAtEndOfLine flag to True if the previous character is a line feed. This prevents the line end adjustment. Because this additional check makes the code a little more complex, I refactored this code into a helper method.
…d of an object. (#16914) Fixup of #16745. Summary of the issue: In my work on #16745, I apparently neglected to consider the end of an inline object such as a link. In that case, adjusting for the line end caused NVDA to report blank when moving to the character after the link instead of reporting the actual character. Description of user facing changes When editing text in Firefox, NVDA now reports the correct character instead of blank when pressing right arrow to move out of a link. This doesn't need a change log entry if we can get this into beta because the bug will not have shipped in release. Description of development approach Return False in _isCaretAtEndOfLine if we're at the end of an object. While the end of an object could indeed be the end of a line, we don't need the special adjustment in this case and the adjustment causes problems if it isn't the end of a line. This also means we can remove the change in #16763 because that was only ever a problem for a line feed at the end of an object anyway. This new fix covers both cases
…rge files (#17051) Fixes #17039 Summary of the issue: With the introduction of pr #16745 which added better reporting of the caret at when at the end of a line, VS Code became quite slow when arrowing up and down through particular large files. IAccessibleText::textAtOffset with IA2_OFFSET_CARET is very costly in VS Code, and Chromium does not actually implement this correctly yet anyway. Description of user facing changes NVDA is less sluggish when arrowing up and down through large files in VS Code. Description of development approach VS code appModule: provide a new VsCodeEditorTextInfo which disables end-of-line caret detection by returning False in _IsCaretAtEndOfLine.
… yet implement what we need, and it is costly for no gain. (#17067) More broadly fixes #17039 Summary of the issue: In PR #16745, code was added to detect the caret at end of lines in Mozilla-compatible edit areas, so as to not report the next line when on the final insertion point of the line before. Although this works great in Firefox and other Gecko-based apps, it does not work at all in Chromium due to Chromium not correctly implementing IAccessibleText::textAtOffset with IA2_OFFSET_CARET. More importantly, this is a very costly check in VS code, which has a noticeable performance hit when arrowing up and down large files (issue #17039), for no gain. PR #17051 successfully fixed the performance hit in VS code by disabling caret at end of line detection just for VS Code. But it was pointed out that there are several VS Code variants including VS Code.dev which require this also. Description of user facing changes NVDA is no longer as sluggish when arrowing up and down large fles in VS code and otherChromium-based applications. Description of development approach Remoe the VSCodeditor and VSCodeEditorTextInfo classes from the VS ode appModule introduced in pr VS Code is no longer as sluggish when arrowing up and down through large files #17051. Add Editor and EditorTextInfo classes to NVDAObjects.IAccessible.chromium which are used in Chromium where ever NVDAObject.IAccessible.ia2Web.Editor is used. These classes disable caret at end of line detection for any chromium edit area.
Link to issue number:
Fixes #3156. Strictly speaking, that issue covers other problems as well, but this addresses the issue for which it was originally opened. Other issues should be (re)opened for any remaining problems.
Summary of the issue:
When editing text, there is an insertion point at the end of a line. This is where you are positioned when you press the end key. Firefox also allows you to reach this position with the left/right arrow keys. When text wraps across multiple lines, lines other than the last have this insertion point, but it does not have its own IAccessible2 offset, since it doesn't actually exist in the text. Instead, the caret offset reported is the same offset as the start of the next line.
This is problematic for NVDA because when the user is at this position, NVDA reports the character at the start of the next line. Worse, it reports the next word and line instead of the current word and line.
Description of user facing changes
In Mozilla Firefox, NVDA now correctly reports the current character, word and line when the cursor is at the insertion point at the end of a line.
Description of development approach
IAccessible2 provides IA2_TEXT_OFFSET_CARET to deal with this. While somewhat awkward, the idea is that you pass this special offset to IAccessibleText functions so that they know you care about the caret and can handle it specially if the caret is at the end of line insertion point. For example, asking for the character at the caret might report (3, 3, None), where 3 is the caret offset. In contrast, if the caret was at the start of the next line, it might report (3, 4, 'c'). Similarly, asking for the line at the caret when at the end of the line might report (0, 3, 'ab ') instead of (3, 6, 'cd ') at the start of the next.
Unfortunately, making NVDA use IA2_TEXT_OFFSET_CARET when fetching units is tricky. IA2TextTextInfo would need to keep track of the fact that it was constructed for the caret and pass IA2_TEXT_OFFSET_CARET if so. But this means that when the caret moves, the TextInfo moves too, which breaks detaching the review cursor from the caret, etc.
Instead, a flag
_isEndOfLineInsertionPointhas been added to MozillaCompoundTextInfo. This is set by querying the character at IA2_TEXT_OFFSET_CARET when constructing with POSITION_CARET. This allows us to implement special behaviour when this is True: we expand to no character, we expand to the current word/line instead of the next word/line, etc. If the TextInfo is copied, this flag is copied too. If the TextInfo is mutated in any way, the flag is set to False. This allows us to represent this position (even across copies) without being attached to the current position of the caret.Some of this could theoretically be done in IA2TextTextInfo, but that would have meant plumbing (and coupling) this special casing through both classes. I'm not aware of any other implementation which supports IA2_TEXT_OFFSET_CARET that doesn't use MozillaCompoundTextInfo.
Note that although Chromium supports IA2_TEXT_OFFSET_CARET, it doesn't special case the insertion point at the end of a line. Thus, although this works fine in Chromium, there is no benefit there, though Chromium could choose to implement this to gain that benefit.
Testing strategy:
Test case:
data:text/html,<textarea cols="2">ab cd</textarea>In Firefox:
In Google Chrome:
Known issues with pull request:
None.
Code Review Checklist:
Summary by CodeRabbit
New Features
Documentation