Skip to content

Commit 55cdef2

Browse files
authored
Merge 1e336c7 into 8220d6c
2 parents 8220d6c + 1e336c7 commit 55cdef2

57 files changed

Lines changed: 9065 additions & 4309 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

devDocs/technicalDesignOverview.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ Aside from accessibility and native APIs, Windows provides many functions which
6767
Information that can be obtained includes the class name of a window, the current foreground window and system battery status.
6868
Tasks that can be performed include moving/clicking the mouse and sending key presses.
6969

70+
### Logging
71+
72+
#### Logging in secure mode
73+
`logHandler.initialize` prevents logging in secure mode.
74+
This is because it is a security concern to log during secure mode (e.g. passwords are logged).
75+
To change this for testing, patch the source build.
76+
`nvda.log` files are then generated in the System profile's `%TEMP%` directory.
77+
7078
## NVDA Components
7179
NVDA is built with an extensible, modular, object oriented, abstract design.
7280
It is divided into several distinct components.

nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ typedef struct {
3434
BSTR address;
3535
BSTR inputTitle;
3636
BSTR inputMessage;
37-
hyper states;
37+
hyper nvCellStates; // bitwise OR of the NvCellState enum values that apply to this cell.
3838
long rowNumber;
3939
long rowSpan;
4040
long columnNumber;

nvdaHelper/remote/excel.cpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ long getCellTextWidth(HWND hwnd, IDispatch* pDispatchRange) {
109109
return sizl.cx;
110110
}
111111

112-
__int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
113-
__int64 states=0;
112+
std::uint64_t getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
113+
std::uint64_t nvCellStates = 0;
114114
// If the current row is a summary row, expose the collapsed or expanded states depending on wither the inner rows are showing or not.
115115
CComPtr<IDispatch> pDispatchRow=nullptr;
116116
HRESULT res=_com_dispatch_raw_propget(pDispatchRange,XLDISPID_RANGE_ENTIREROW,VT_DISPATCH,&pDispatchRow);
@@ -129,11 +129,13 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
129129
if(FAILED(res)) {
130130
LOG_DEBUGWARNING(L"row.showDetail failed with code "<<res);
131131
}
132-
states|=(showDetail?NVSTATE_EXPANDED:NVSTATE_COLLAPSED);
132+
nvCellStates |= (showDetail ? NvCellState::EXPANDED : NvCellState::COLLAPSED);
133133
}
134134
}
135-
// If this row was neither collapsed or expanded, then try the same for columns instead.
136-
if(!(states&NVSTATE_EXPANDED)&&!(states&NVSTATE_COLLAPSED)) {
135+
if( // Row not collapsed or expanded, try the same for columns instead.
136+
!(nvCellStates& NvCellState::EXPANDED)
137+
&&!(nvCellStates & NvCellState::COLLAPSED)
138+
) {
137139
CComPtr<IDispatch> pDispatchColumn=nullptr;
138140
res=_com_dispatch_raw_propget(pDispatchRange,XLDISPID_RANGE_ENTIRECOLUMN,VT_DISPATCH,&pDispatchColumn);
139141
if(FAILED(res)) {
@@ -151,7 +153,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
151153
if(FAILED(res)) {
152154
LOG_DEBUGWARNING(L"column.showDetail failed with code "<<res);
153155
}
154-
states|=(showDetail?NVSTATE_EXPANDED:NVSTATE_COLLAPSED);
156+
nvCellStates |= (showDetail ? NvCellState::EXPANDED : NvCellState::COLLAPSED);
155157
}
156158
}
157159
}
@@ -162,7 +164,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
162164
LOG_DEBUGWARNING(L"range.hasFormula failed with code "<<res);
163165
}
164166
if(hasFormula) {
165-
states|=NVSTATE_HASFORMULA;
167+
nvCellStates |= NvCellState::HASFORMULA;
166168
}
167169
// Expose whether this cell has a dropdown menu for choosing valid values
168170
CComPtr<IDispatch> pDispatchValidation=nullptr;
@@ -179,7 +181,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
179181
LOG_DEBUGWARNING(L"validation.type failed with code "<<res);
180182
}
181183
if(validationType==xlValidateList) {
182-
states|=NVSTATE_HASPOPUP;
184+
nvCellStates |= NvCellState::HASPOPUP;
183185
}
184186
}
185187
// Expose whether this cell has comments
@@ -189,7 +191,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
189191
LOG_DEBUGWARNING(L"range.comment failed with code "<<res);
190192
}
191193
if(pDispatchComment) {
192-
states|=NVSTATE_HASCOMMENT;
194+
nvCellStates |= NvCellState::HASCOMMENT;
193195
}
194196
// Expose whether this cell is unlocked for editing
195197
BOOL locked=false;
@@ -210,7 +212,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
210212
LOG_DEBUGWARNING(L"worksheet.protectcontents failed with code "<<res);
211213
}
212214
if(protectContents) {
213-
states|=NVSTATE_UNLOCKED;
215+
nvCellStates |= NvCellState::UNLOCKED;
214216
}
215217
}
216218
}
@@ -227,7 +229,7 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
227229
LOG_DEBUGWARNING(L"hyperlinks.count failed with code "<<res);
228230
}
229231
if(count>0) {
230-
states|=NVSTATE_LINKED;
232+
nvCellStates |= NvCellState::LINKED;
231233
}
232234
}
233235
// Expose whether this cell's content flows outside the cell,
@@ -304,17 +306,17 @@ __int64 getCellStates(HWND hwnd, IDispatch* pDispatchRange) {
304306
LOG_DEBUGWARNING(L"range.text failed with code "<<res);
305307
}
306308
if(text&&text.Length()>0) {
307-
states|=NVSTATE_CROPPED;
309+
nvCellStates |= NvCellState::CROPPED;
308310
}
309311
}
310-
if(!(states&NVSTATE_CROPPED)) {
311-
states|=NVSTATE_OVERFLOWING;
312+
if(!(nvCellStates & NvCellState::CROPPED)) {
313+
nvCellStates |= NvCellState::OVERFLOWING;
312314
}
313315
}
314316
}
315317
}
316318
}
317-
return states;
319+
return nvCellStates;
318320
}
319321

320322
HRESULT getCellInfo(HWND hwnd, IDispatch* pDispatchRange, long cellInfoFlags, EXCEL_CELLINFO* cellInfo) {
@@ -364,7 +366,7 @@ HRESULT getCellInfo(HWND hwnd, IDispatch* pDispatchRange, long cellInfoFlags, EX
364366
}
365367
}
366368
if(cellInfoFlags&NVCELLINFOFLAG_STATES) {
367-
cellInfo->states=getCellStates(hwnd,pDispatchRange);
369+
cellInfo->nvCellStates = getCellStates(hwnd, pDispatchRange);
368370
}
369371
CComPtr<IDispatch> pDispatchMergeArea=nullptr;
370372
if(cellInfoFlags&NVCELLINFOFLAG_COORDS||cellInfoFlags&NVCELLINFOFLAG_OUTLINELEVEL) {

nvdaHelper/remote/excel/Constants.h

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/*
22
This file is a part of the NVDA project.
3-
Copyright 2019-2020 NV Access Limited, Accessolutions, Julien Cochuyt
4-
This program is free software: you can redistribute it and/or modify
5-
it under the terms of the GNU General Public License version 2.0, as published by
6-
the Free Software Foundation.
7-
This program is distributed in the hope that it will be useful,
8-
but WITHOUT ANY WARRANTY; without even the implied warranty of
9-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
3+
Copyright 2019-2022 NV Access Limited, Accessolutions, Julien Cochuyt
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License version 2.0, as published by
6+
the Free Software Foundation.
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1010
This license can be found at:
1111
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
1212
*/
@@ -78,17 +78,25 @@ const long NVCELLINFOFLAG_COMMENTS=0x40;
7878
const long NVCELLINFOFLAG_FORMULA=0x80;
7979
const long NVCELLINFOFLAG_ALL=0xffff;
8080

81-
// NVDA states
82-
const __int64 NVSTATE_EXPANDED=0x100;
83-
const __int64 NVSTATE_COLLAPSED=0x200;
84-
const __int64 NVSTATE_LINKED=0x1000;
85-
const __int64 NVSTATE_HASPOPUP=0x2000;
86-
const __int64 NVSTATE_PROTECTED=0x4000;
87-
const __int64 NVSTATE_HASFORMULA=0x1000000000;
88-
const __int64 NVSTATE_HASCOMMENT=0x2000000000;
89-
const __int64 NVSTATE_CROPPED=0x8000000000;
90-
const __int64 NVSTATE_OVERFLOWING=0x10000000000;
91-
const __int64 NVSTATE_UNLOCKED=0x20000000000;
81+
constexpr std::uint64_t setBit(const unsigned int bitPos) {
82+
return std::uint64_t(1) << bitPos;
83+
}
84+
85+
/*NVDA sell specific states.
86+
These values must match NvCellState enum in source/nvdaObjects/excel.py
87+
*/
88+
enum NvCellState : std::uint64_t {
89+
EXPANDED = setBit(1),
90+
COLLAPSED = setBit(2),
91+
LINKED = setBit(3),
92+
HASPOPUP = setBit(4),
93+
PROTECTED = setBit(5),
94+
HASFORMULA = setBit(6),
95+
HASCOMMENT = setBit(7),
96+
CROPPED = setBit(8),
97+
OVERFLOWING = setBit(9),
98+
UNLOCKED = setBit(10)
99+
};
92100

93101
// an HRESULT error code randomly given by Excel such as for validation.type when there is no validation on the cell
94102
const HRESULT XLGeneralError=0x800a03ec;

source/IAccessibleHandler/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,10 @@ def NVDARoleFromAttr(accRole: Optional[str]) -> Role:
319319
return IAccessibleRolesToNVDARoles.get(accRole, controlTypes.Role.UNKNOWN)
320320

321321

322-
def normalizeIAccessible(pacc, childID=0):
322+
def normalizeIAccessible(
323+
pacc: Union[IUnknown, IA.IAccessible, IA2.IAccessible2],
324+
childID: int = 0
325+
) -> Union[IA.IAccessible, IA2.IAccessible2]:
323326
if not isinstance(pacc, IA.IAccessible):
324327
try:
325328
pacc = pacc.QueryInterface(IA.IAccessible)

source/NVDAObjects/IAccessible/__init__.py

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
# This file is covered by the GNU General Public License.
44
# See the file COPYING for more details.
55
import typing
6+
from typing import (
7+
Optional,
8+
Union,
9+
)
610

711
from comtypes.automation import IEnumVARIANT, VARIANT
812
from comtypes import (
@@ -21,6 +25,7 @@
2125
import itertools
2226
import importlib
2327
from comInterfaces.tom import ITextDocument
28+
from comInterfaces import Accessibility as IA
2429
from comInterfaces import IAccessible2Lib as IA2
2530
import tones
2631
import languageHandler
@@ -609,17 +614,20 @@ def findOverlayClasses(self,clsList):
609614
if clsList[0]==IAccessible and len(clsList)==3 and self.IAccessibleRole==oleacc.ROLE_SYSTEM_CLIENT and self.childCount==0:
610615
clsList.insert(0,ContentGenericClient)
611616

612-
def __init__(self,windowHandle=None,IAccessibleObject=None,IAccessibleChildID=None,event_windowHandle=None,event_objectID=None,event_childID=None):
617+
# C901: 'IAccessible.__init__' is too complex
618+
def __init__( # noqa: C901
619+
self,
620+
windowHandle: Optional[int] = None,
621+
IAccessibleObject: Optional[Union[IUnknown, IA.IAccessible, IA2.IAccessible2]] = None,
622+
IAccessibleChildID: Optional[int] = None,
623+
event_windowHandle: Optional = None,
624+
event_objectID: Optional = None,
625+
event_childID: Optional = None
626+
):
627+
"""
628+
@param windowHandle: the window handle, if known
629+
@param IAccessibleChildID: A child ID that will be used on all methods of the IAccessible pointer
613630
"""
614-
@param pacc: a pointer to an IAccessible object
615-
@type pacc: ctypes.POINTER(IAccessible)
616-
@param child: A child ID that will be used on all methods of the IAccessible pointer
617-
@type child: int
618-
@param hwnd: the window handle, if known
619-
@type hwnd: int
620-
@param objectID: the objectID for the IAccessible Object, if known
621-
@type objectID: int
622-
"""
623631
self.IAccessibleObject=IAccessibleObject
624632
self.IAccessibleChildID=IAccessibleChildID
625633

@@ -1484,12 +1492,14 @@ def _get_indexInParent(self):
14841492

14851493
def _get__IA2Relations(self) -> typing.List[IA2.IAccessibleRelation]:
14861494
if not isinstance(self.IAccessibleObject, IA2.IAccessible2):
1495+
log.debug("Not an IA2.IAccessible2")
14871496
raise NotImplementedError
14881497
import ctypes
14891498
import comtypes.hresult
14901499
try:
14911500
size = self.IAccessibleObject.nRelations
14921501
except COMError:
1502+
log.debug("Unable to get nRelations")
14931503
raise NotImplementedError
14941504
if size <= 0:
14951505
return list()
@@ -1498,16 +1508,21 @@ def _get__IA2Relations(self) -> typing.List[IA2.IAccessibleRelation]:
14981508
# The client allocated relations array is an [out] parameter instead of [in, out], so we need to use the raw COM method.
14991509
res = self.IAccessibleObject._IAccessible2__com__get_relations(size, relations, ctypes.byref(count))
15001510
if res != comtypes.hresult.S_OK:
1511+
log.debug("Unable to get relations")
15011512
raise NotImplementedError
15021513
return list(relations)
15031514

15041515
def _getIA2TargetsForRelationsOfType(
15051516
self,
15061517
relationType: "IAccessibleHandler.RelationType",
15071518
maxRelations: int = 1,
1508-
) -> typing.List[ctypes.POINTER(IUnknown)]:
1519+
) -> typing.List[IUnknown]:
15091520
"""Gets the target IAccessible (actually IUnknown; use QueryInterface or
1510-
normalizeIAccessible to resolve) for the relations with given type."""
1521+
normalizeIAccessible to resolve) for the relations with given type.
1522+
Allows escape of exception: COMError(-2147417836, 'Requested object does not exist.'),
1523+
callers should handle this, for this reason consider using _getIA2RelationFirstTarget
1524+
if only the first target is required, and you wish the target to be converted to an IAccessible
1525+
"""
15111526
acc = self.IAccessibleObject
15121527
if not isinstance(acc, IA2.IAccessible2):
15131528
raise NotImplementedError
@@ -1548,13 +1563,17 @@ def _getIA2RelationFirstTarget(
15481563
# rather than fetch all the relations and querying the type, do that in process for performance reasons
15491564
targets = self._getIA2TargetsForRelationsOfType(relationType, maxRelations=1)
15501565
if targets:
1551-
return targets[0]
1566+
ia2Object = IAccessibleHandler.normalizeIAccessible(targets[0])
1567+
return IAccessible(
1568+
IAccessibleObject=ia2Object,
1569+
IAccessibleChildID=0
1570+
)
15521571
except (NotImplementedError, COMError):
15531572
log.debugWarning("Unable to use _getIA2TargetsForRelationsOfType, fallback to _IA2Relations.")
15541573

15551574
# eg IA2_2 is not available, fall back to old approach
1556-
for relation in self._IA2Relations:
1557-
try:
1575+
try:
1576+
for relation in self._IA2Relations:
15581577
if relation.relationType == relationType:
15591578
# Take the first of 'relation.nTargets' see IAccessibleRelation._methods_
15601579
target = relation.target(0)
@@ -1563,24 +1582,18 @@ def _getIA2RelationFirstTarget(
15631582
IAccessibleObject=ia2Object,
15641583
IAccessibleChildID=0
15651584
)
1566-
except COMError:
1567-
pass
1585+
except (NotImplementedError, COMError):
1586+
log.debug("Unable to fetch _IA2Relations", exc_info=True)
1587+
pass
15681588
return None
15691589

15701590
#: Type definition for auto prop '_get_detailsRelations'
15711591
detailsRelations: typing.Iterable["IAccessible"]
15721592

15731593
def _get_detailsRelations(self) -> typing.Iterable["IAccessible"]:
1574-
relations = self._getIA2TargetsForRelationsOfType(
1575-
IAccessibleHandler.RelationType.DETAILS,
1576-
maxRelations=1
1577-
)
1578-
if not relations:
1594+
relationTarget = self._getIA2RelationFirstTarget(IAccessibleHandler.RelationType.DETAILS)
1595+
if not relationTarget:
15791596
return ()
1580-
relationTarget = IAccessible(
1581-
IAccessibleObject=IAccessibleHandler.normalizeIAccessible(relations[0]),
1582-
IAccessibleChildID=0
1583-
)
15841597
return (relationTarget, )
15851598

15861599
#: Type definition for auto prop '_get_flowsTo'

source/NVDAObjects/IAccessible/mozilla.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ def _get_detailsSummary(self) -> Optional[str]:
6868
# Although slower, we have to fetch the details relations instead.
6969
detailsRelations = self.detailsRelations
7070
if not detailsRelations:
71-
log.error("should be able to fetch detailsRelations")
7271
return None
7372
for target in detailsRelations:
7473
# just take the first for now.

source/NVDAObjects/UIA/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ def _getTextWithFields_text(self,textRange,formatConfig,UIAFormatUnits=None):
592592
rangeIter=iterUIARangeByUnit(textRange,unit) if unit is not None else [textRange]
593593
for tempRange in rangeIter:
594594
text=self._getTextFromUIARange(tempRange) or ""
595-
if text:
595+
if text is not None:
596596
if debug:
597597
log.debug("Chunk has text. Fetching formatting")
598598
try:
@@ -607,7 +607,9 @@ def _getTextWithFields_text(self,textRange,formatConfig,UIAFormatUnits=None):
607607
continue
608608
if debug:
609609
log.debug("Yielding formatting and text")
610-
yield field
610+
log.debug(f"field: {field}, text: {text}")
611+
if field:
612+
yield field
611613
yield text
612614
if debug:
613615
log.debug("Done _getTextWithFields_text")

0 commit comments

Comments
 (0)