Skip to content

Commit af3213b

Browse files
authored
Merge 445be6c into 0b0bfb0
2 parents 0b0bfb0 + 445be6c commit af3213b

4 files changed

Lines changed: 51 additions & 7 deletions

File tree

nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,10 +1000,16 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
10001000
}
10011001

10021002
BSTR value=NULL;
1003-
if(pacc->get_accValue(varChild,&value)==S_OK) {
1004-
if(value&&SysStringLen(value)==0) {
1005-
SysFreeString(value);
1006-
value=NULL;
1003+
if (pacc->get_accValue(varChild, &value) == S_OK) {
1004+
if (value) {
1005+
if (role == ROLE_SYSTEM_LINK) {
1006+
// For links, store the IAccessible value to handle same page link detection.
1007+
parentNode->addAttribute(L"IAccessible::value", value);
1008+
}
1009+
if (SysStringLen(value)==0) {
1010+
SysFreeString(value);
1011+
value=NULL;
1012+
}
10071013
}
10081014
}
10091015

source/NVDAObjects/IAccessible/ia2Web.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,30 @@ def _get_states(self):
213213
if popupState:
214214
states.discard(controlTypes.State.HASPOPUP)
215215
states.add(popupState)
216+
if self.isInternalLink:
217+
states.add(controlTypes.State.INTERNAL_LINK)
216218
return states
217219

220+
@property
221+
def isInternalLink(self) -> bool:
222+
if self.role != controlTypes.Role.LINK:
223+
return False
224+
if (
225+
not hasattr(self, "treeInterceptor")
226+
or self.treeInterceptor is None
227+
or not self.treeInterceptor.documentConstantIdentifier
228+
):
229+
return False
230+
documentConstantIdentifier = self.treeInterceptor.documentConstantIdentifier
231+
if documentConstantIdentifier.endswith("/"):
232+
documentConstantIdentifier = documentConstantIdentifier[:-1]
233+
queryParamCharPos = documentConstantIdentifier.find("?")
234+
if queryParamCharPos > 0:
235+
documentConstantIdentifier = documentConstantIdentifier[:queryParamCharPos]
236+
if self.value.startswith(f"{documentConstantIdentifier}#"):
237+
return True
238+
return False
239+
218240
def _get_landmark(self):
219241
xmlRoles = self.IA2Attributes.get("xml-roles", "").split(" ")
220242
landmark = next((xr for xr in xmlRoles if xr in aria.landmarkRoles), None)

source/controlTypes/state.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def negativeDisplayString(self) -> str:
102102
HASPOPUP_GRID = setBit(48)
103103
HASPOPUP_LIST = setBit(49)
104104
HASPOPUP_TREE = setBit(50)
105+
INTERNAL_LINK = setBit(51)
105106

106107

107108
STATES_SORTED = frozenset([State.SORTED, State.SORTED_ASCENDING, State.SORTED_DESCENDING])
@@ -204,6 +205,9 @@ def negativeDisplayString(self) -> str:
204205
State.HASPOPUP_LIST: _("opens list"),
205206
# Translators: Presented when a control has a pop-up tree.
206207
State.HASPOPUP_TREE: _("opens tree"),
208+
# Translators: Presented when a link destination points to the page containing the link.
209+
# For example, links of a table of contents of a document with different sections.
210+
State.INTERNAL_LINK: _("same page"),
207211
}
208212

209213

source/virtualBuffers/gecko_ia2.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,20 @@ def _normalizeControlField(self, attrs): # noqa: C901
167167
attrs["roleTextBraille"] = roleTextBraille
168168
if attrs.get("IAccessible2::attribute_dropeffect", "none") != "none":
169169
states.add(controlTypes.State.DROPTARGET)
170-
if role == controlTypes.Role.LINK and controlTypes.State.LINKED not in states:
171-
# This is a named link destination, not a link which can be activated. The user doesn't care about these.
172-
role = controlTypes.Role.TEXTFRAME
170+
if role == controlTypes.Role.LINK:
171+
if controlTypes.State.LINKED not in states:
172+
# This is a named link destination, not a link which can be activated. The user doesn't care about these.
173+
role = controlTypes.Role.TEXTFRAME
174+
else:
175+
attrs["value"] = self.NVDAObjectAtStart.value
176+
documentConstantIdentifier = self.obj.documentConstantIdentifier
177+
value = attrs.get("value", "")
178+
if (
179+
value
180+
and documentConstantIdentifier
181+
and value.startswith(f"{documentConstantIdentifier}#")
182+
):
183+
states.add(controlTypes.State.INTERNAL_LINK)
173184
level = attrs.get("IAccessible2::attribute_level", "")
174185
xmlRoles = attrs.get("IAccessible2::attribute_xml-roles", "").split(" ")
175186
landmark = next((xr for xr in xmlRoles if xr in aria.landmarkRoles), None)
@@ -210,6 +221,7 @@ def _normalizeControlField(self, attrs): # noqa: C901
210221
attrs["detailsRoles"] = set(self._normalizeDetailsRole(detailsRoles))
211222
if config.conf["debugLog"]["annotations"]:
212223
log.debug(f"detailsRoles: {attrs['detailsRoles']}")
224+
log.info(attrs)
213225
return super()._normalizeControlField(attrs)
214226

215227
def _normalizeDetailsRole(self, detailsRoles: str) -> Iterable[Optional[controlTypes.Role]]:

0 commit comments

Comments
 (0)