Skip to content

Commit ae331b7

Browse files
authored
Merge 82d34be into 52559a9
2 parents 52559a9 + 82d34be commit ae331b7

4 files changed

Lines changed: 52 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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,40 @@ 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+
value = self.value
225+
if not value:
226+
return False
227+
if not hasattr(self, "treeInterceptor"):
228+
return False
229+
ti = self.treeInterceptor
230+
if ti is None or not hasattr(ti, "documentConstantIdentifier"):
231+
return False
232+
documentConstantIdentifier = ti.documentConstantIdentifier
233+
if self.valueToSamePage(value, documentConstantIdentifier):
234+
return True
235+
return False
236+
237+
def valueToSamePage(self, value: str, constantIdentifier: str) -> bool:
238+
"""Function used to check if link destination points to the same page"""
239+
if not value or not constantIdentifier:
240+
return False
241+
if constantIdentifier.endswith("/"):
242+
constantIdentifier = constantIdentifier[:-1]
243+
queryParamCharPos = constantIdentifier.find("?")
244+
if queryParamCharPos > 0:
245+
constantIdentifier = constantIdentifier[:queryParamCharPos]
246+
if value.startswith(f"{constantIdentifier}#"):
247+
return True
248+
return False
249+
218250
def _get_landmark(self):
219251
xmlRoles = self.IA2Attributes.get("xml-roles", "").split(" ")
220252
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: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,12 @@ 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+
elif self.NVDAObjectAtStart.isInternalLink:
175+
states.add(controlTypes.State.INTERNAL_LINK)
173176
level = attrs.get("IAccessible2::attribute_level", "")
174177
xmlRoles = attrs.get("IAccessible2::attribute_xml-roles", "").split(" ")
175178
landmark = next((xr for xr in xmlRoles if xr in aria.landmarkRoles), None)

0 commit comments

Comments
 (0)