Skip to content

Commit ae4ea8f

Browse files
Derek RiemerLeonarddeRmichaelDCurranfeerrenrut
authored
Refactor NVDAHelper32remote Live region handler (PR #9079)
Updates to ARIA live regions are now suppressed when reporting of dynamic content changes is disabled. ### Summary of the issue: The same logic was used for sending external applications speech to core and to send live region messages to core. ### Description of the Fix NVDAControllerInternal_speakMessage used to be used to send live regions at NVDA, which made it impossible to differentiate them from messages sent from external applications through the controller client. This change separates the passage of live regions into another function, nvdaControllerInternal_reportLiveRegion(wchar_t* text, wchar_t* level); which is part of the NVDAControllerInternal interface instead of the NVDAController interface. This is because I don't see a reason for non NVDA specific DLL's to call into this. This function is then used in place of speakText in ia2LiveRegions.cpp. Fixes #9077, Unblocks #7756, Closes #7743 Co-authored-by: Leonard de Ruijter <alderuijter@gmail.com> Co-authored-by: Michael Curran <mick@nvaccess.org> Co-authored-by: Reef Turner <reef@nvaccess.org>
1 parent 0312687 commit ae4ea8f

15 files changed

Lines changed: 134 additions & 48 deletions

File tree

nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
1717
]
1818
interface NvdaControllerInternal {
1919
[fault_status,comm_status] requestRegistration();
20+
[fault_status,comm_status] reportLiveRegion();
2021
[fault_status,comm_status] inputLangChangeNotify();
2122
[fault_status,comm_status] typedCharacterNotify();
2223
[fault_status,comm_status] displayModelTextChangeNotify();

nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2018 NV Access Limited, rui Batista, Google LLC.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -15,7 +15,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
1515
cpp_quote("/*")
1616
cpp_quote("This file is a part of the NVDA project.")
1717
cpp_quote("URL: http://www.nvda-project.org/")
18-
cpp_quote("Copyright 2006-2010 NVDA contributers.")
18+
cpp_quote("Copyright 2006-2018 NV Access Limited, rui Batista, Google LLC.")
1919
cpp_quote("This program is free software: you can redistribute it and/or modify")
2020
cpp_quote("it under the terms of the GNU General Public License version 2.0, as published by")
2121
cpp_quote("the Free Software Foundation.")
@@ -37,6 +37,13 @@ interface NvdaControllerInternal {
3737

3838
error_status_t __stdcall requestRegistration([in,string] const wchar_t* uuidString);
3939

40+
/**
41+
* Notifies NVDA that a live region was updated.
42+
* @param text the text to report for the live region.
43+
* @param level The level of live region, I.E. "polite"
44+
*/
45+
error_status_t __stdcall reportLiveRegion([in,string] const wchar_t* text, [in,string] const wchar_t* level);
46+
4047
/**
4148
* Notifies NVDA that the keyboard layout has changed for this thread.
4249
* @param threadID the thread the layout change occured in

nvdaHelper/local/nvdaControllerInternal.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2018 NV Access Limited, rui Batista, Google LLC.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -19,6 +19,11 @@ error_status_t __stdcall nvdaControllerInternal_requestRegistration(const wchar_
1919
return _nvdaControllerInternal_requestRegistration(uuidString);
2020
}
2121

22+
error_status_t(__stdcall *_nvdaControllerInternal_reportLiveRegion)(const wchar_t*, const wchar_t*);
23+
error_status_t __stdcall nvdaControllerInternal_reportLiveRegion(const wchar_t* text, const wchar_t* politeness) {
24+
return _nvdaControllerInternal_reportLiveRegion(text, politeness);
25+
}
26+
2227
error_status_t(__stdcall *_nvdaControllerInternal_inputLangChangeNotify)(const long, const unsigned long, const wchar_t*);
2328
error_status_t __stdcall nvdaControllerInternal_inputLangChangeNotify(const long threadID, const unsigned long hkl, const wchar_t* layoutString) {
2429
return _nvdaControllerInternal_inputLangChangeNotify(threadID,hkl,layoutString);

nvdaHelper/local/nvdaHelperLocal.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ EXPORTS
3333
VBuf_locateTextFieldNodeAtOffset
3434
VBuf_setSelectionOffsets
3535
_nvdaControllerInternal_requestRegistration
36+
_nvdaControllerInternal_reportLiveRegion
3637
_nvdaControllerInternal_displayModelTextChangeNotify
3738
_nvdaControllerInternal_inputLangChangeNotify
3839
_nvdaControllerInternal_inputCompositionUpdate

nvdaHelper/remote/ia2LiveRegions.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2020 NV Access Limited, Google LLC, Leonard de Ruijter
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -18,7 +18,7 @@ This license can be found at:
1818
#include <windows.h>
1919
#include <atlcomcli.h>
2020
#include <ia2.h>
21-
#include "nvdaController.h"
21+
#include "nvdaControllerInternal.h"
2222
#include <common/ia2utils.h>
2323
#include "nvdaHelperRemote.h"
2424

@@ -308,6 +308,7 @@ void CALLBACK winEventProcHook(HWINEVENTHOOK hookID, DWORD eventID, HWND hwnd, l
308308
pacc2->Release();
309309
return;
310310
}
311+
wstring politeness = i->second;
311312
i=attribsMap.find(L"container-busy");
312313
bool busy=(i!=attribsMap.end()&&i->second.compare(L"true")==0);
313314
if(busy) {
@@ -410,7 +411,9 @@ void CALLBACK winEventProcHook(HWINEVENTHOOK hookID, DWORD eventID, HWND hwnd, l
410411
gotText=getTextFromIAccessible(textBuf,pacc2,true,allowAdditions,allowText);
411412
}
412413
pacc2->Release();
413-
if(gotText&&!textBuf.empty()) nvdaController_speakText(textBuf.c_str());
414+
if (gotText && !textBuf.empty()) {
415+
nvdaControllerInternal_reportLiveRegion(textBuf.c_str(), politeness.c_str());
416+
}
414417
}
415418

416419
void ia2LiveRegions_inProcess_initialize() {

nvdaHelper/remote/nvdaHelperRemote.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ EXPORTS
1212
logMessage
1313
NVDALogCrtReportHook
1414
nvdaInProcUtils_winword_expandToLine
15+
nvdaControllerInternal_reportLiveRegion
1516
nvdaControllerInternal_logMessage
1617
nvdaControllerInternal_vbufChangeNotify
1718
nvdaControllerInternal_installAddonPackageFromPath

nvdaHelper/vbufBackends/mshtml/node.cpp

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2015 NVDA contributers.
4+
Copyright 2006-2020 NV Access Limited, Google LLC, Leonard de Ruijter
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -20,7 +20,7 @@ This license can be found at:
2020
#include <mshtmdid.h>
2121
#include <common/log.h>
2222
#include "mshtml.h"
23-
#include <remote/nvdaController.h>
23+
#include <remote/nvdaControllerInternal.h>
2424
#include <common/xml.h>
2525
#include "node.h"
2626

@@ -124,7 +124,15 @@ class CDispatchChangeSink : public IDispatch {
124124
if(dispIdMember==DISPID_EVMETH_ONPROPERTYCHANGE||dispIdMember==DISPID_EVMETH_ONLOAD) {
125125
this->storageNode->backend->invalidateSubtree(this->storageNode);
126126
// Force the update to happen with no delay if we happen to be in a live region
127-
if(this->storageNode->ariaLiveNode&&this->storageNode->ariaLiveNode!=this->storageNode&&!this->storageNode->ariaLiveIsBusy&&(this->storageNode->ariaLiveIsTextRelevant||this->storageNode->ariaLiveIsAdditionsRelevant)) {
127+
if (
128+
this->storageNode->ariaLiveNode
129+
&& this->storageNode->ariaLiveNode != this->storageNode
130+
&& !this->storageNode->ariaLiveIsBusy
131+
&& (
132+
this->storageNode->ariaLiveIsTextRelevant
133+
|| this->storageNode->ariaLiveIsAdditionsRelevant
134+
)
135+
) {
128136
this->storageNode->backend->forceUpdate();
129137
}
130138
return S_OK;
@@ -266,7 +274,15 @@ class CHTMLChangeSink : public IHTMLChangeSink {
266274
this->storageNode->backend->invalidateSubtree(invalidNode);
267275
MshtmlVBufStorage_controlFieldNode_t* invalidMshtmlNode=(MshtmlVBufStorage_controlFieldNode_t*)invalidNode;
268276
// Force the update to happen with no delay if we happen to be in a live region
269-
if(invalidMshtmlNode->ariaLiveNode&&invalidMshtmlNode->ariaLiveNode!=invalidMshtmlNode&&!invalidMshtmlNode->ariaLiveIsBusy&&(invalidMshtmlNode->ariaLiveIsTextRelevant||invalidMshtmlNode->ariaLiveIsAdditionsRelevant)) {
277+
if (
278+
invalidMshtmlNode->ariaLiveNode
279+
&& invalidMshtmlNode->ariaLiveNode != invalidMshtmlNode
280+
&& !invalidMshtmlNode->ariaLiveIsBusy
281+
&& (
282+
invalidMshtmlNode->ariaLiveIsTextRelevant
283+
|| invalidMshtmlNode->ariaLiveIsAdditionsRelevant
284+
)
285+
) {
270286
this->storageNode->backend->forceUpdate();
271287
}
272288
}
@@ -353,11 +369,18 @@ MshtmlVBufStorage_controlFieldNode_t::~MshtmlVBufStorage_controlFieldNode_t() {
353369
}
354370

355371
void MshtmlVBufStorage_controlFieldNode_t::preProcessLiveRegion(const MshtmlVBufStorage_controlFieldNode_t* parent, const std::map<std::wstring,std::wstring>& attribsMap) {
356-
auto i=attribsMap.find(L"HTMLAttrib::aria-live");
372+
auto i=attribsMap.find(L"HTMLAttrib::aria-live");
357373
if(i!=attribsMap.end()&&!i->second.empty()) {
358-
this->ariaLiveNode=((i->second.compare(L"polite")==0)||(i->second.compare(L"assertive")==0))?this:NULL;
374+
bool isAriaLiveEnabled = i->second == L"polite" || i->second == L"assertive";
375+
this->ariaLiveNode = isAriaLiveEnabled ? this : nullptr;
376+
this->ariaLivePoliteness = i->second;
359377
} else {
360-
this->ariaLiveNode=parent?parent->ariaLiveNode:NULL;
378+
this->ariaLiveNode = parent? parent->ariaLiveNode : nullptr;
379+
if (this->ariaLiveNode) {
380+
this->ariaLivePoliteness = this->ariaLiveNode->ariaLivePoliteness;
381+
} else {
382+
this->ariaLivePoliteness = nullptr;
383+
}
361384
}
362385
i=attribsMap.find(L"HTMLAttrib::aria-relevant");
363386
if(i!=attribsMap.end()&&!i->second.empty()) {
@@ -384,13 +407,13 @@ void MshtmlVBufStorage_controlFieldNode_t::preProcessLiveRegion(const MshtmlVBuf
384407
} else {
385408
this->ariaLiveAtomicNode=parent?parent->ariaLiveAtomicNode:NULL;
386409
}
387-
//LOG_INFO(L"preProcessLiveRegion: ariaLiveNode "<<ariaLiveNode<<L", ariaLiveIsTextRelevant "<<ariaLiveIsTextRelevant<<L", ariaLiveIsAdditionsRelevant "<<ariaLiveIsAdditionsRelevant<<L", ariaLiveIsBusy "<<ariaLiveIsBusy<<L", ariaLiveAtomicNode "<<ariaLiveAtomicNode);
410+
LOG_DEBUG(L"preProcessLiveRegion: ariaLiveNode "<<ariaLiveNode<<L", ariaLiveIsTextRelevant "<<ariaLiveIsTextRelevant<<L", ariaLiveIsAdditionsRelevant "<<ariaLiveIsAdditionsRelevant<<L", ariaLiveIsBusy "<<ariaLiveIsBusy<<L", ariaLiveAtomicNode "<<ariaLiveAtomicNode);
388411
}
389412

390-
void MshtmlVBufStorage_controlFieldNode_t::reportLiveText(wstring& text) {
413+
void MshtmlVBufStorage_controlFieldNode_t::reportLiveText(wstring& text, wstring& politeness) {
391414
for(auto c: text) {
392415
if(!iswspace(c)) {
393-
nvdaController_speakText(text.c_str());
416+
nvdaControllerInternal_reportLiveRegion(text.c_str(), politeness.c_str());
394417
break;
395418
}
396419
}
@@ -399,21 +422,23 @@ void MshtmlVBufStorage_controlFieldNode_t::reportLiveText(wstring& text) {
399422
bool isNodeInLiveRegion(VBufStorage_fieldNode_t* node) {
400423
if(!node) return false;
401424
if(node->getFirstChild()) {
402-
return ((MshtmlVBufStorage_controlFieldNode_t*)node)->ariaLiveNode!=NULL;
425+
return ((MshtmlVBufStorage_controlFieldNode_t*)node)->ariaLiveNode != nullptr;
403426
}
404427
return true;
405428
}
406429

407430
void MshtmlVBufStorage_controlFieldNode_t::reportLiveAddition() {
408431
wstring text; //=(this->ariaLiveAtomicNode==this)?L"atomic: ":L"additions: ";
409432
this->getTextInRange(0,this->getLength(),text,false,isNodeInLiveRegion);
410-
this->reportLiveText(text);
433+
this->reportLiveText(text, this->ariaLivePoliteness);
411434
}
412435

413436
void MshtmlVBufStorage_controlFieldNode_t::postProcessLiveRegion(VBufStorage_controlFieldNode_t* oldNode, set<VBufStorage_controlFieldNode_t*>& atomicNodes) {
414-
//LOG_INFO(L"preProcessLiveRegion: ariaLiveNode "<<ariaLiveNode<<L", ariaLiveIsTextRelevant "<<ariaLiveIsTextRelevant<<L", ariaLiveIsAdditionsRelevant "<<ariaLiveIsAdditionsRelevant<<L", ariaLiveIsBusy "<<ariaLiveIsBusy<<L", ariaLiveAtomicNode "<<ariaLiveAtomicNode);
415-
if(!this->ariaLiveNode||this->ariaLiveIsBusy) return;
416-
bool reportNode=!oldNode&&this->ariaLiveIsAdditionsRelevant&&this->ariaLiveNode!=this;
437+
LOG_DEBUG(L"postProcessLiveRegion: ariaLiveNode "<<ariaLiveNode<<L", ariaLiveIsTextRelevant "<<ariaLiveIsTextRelevant<<L", ariaLiveIsAdditionsRelevant "<<ariaLiveIsAdditionsRelevant<<L", ariaLiveIsBusy "<<ariaLiveIsBusy<<L", ariaLiveAtomicNode "<<ariaLiveAtomicNode);
438+
if (!this->ariaLiveNode || this->ariaLiveIsBusy) {
439+
return;
440+
}
441+
bool reportNode=!oldNode && this->ariaLiveIsAdditionsRelevant && this->ariaLiveNode != this;
417442
wstring newChildrenText;
418443
if(!reportNode&&oldNode&&ariaLiveIsTextRelevant) {
419444
// Find the first new text child
@@ -469,7 +494,7 @@ void MshtmlVBufStorage_controlFieldNode_t::postProcessLiveRegion(VBufStorage_con
469494
} else if(reportNode) {
470495
this->reportLiveAddition();
471496
} else if(!newChildrenText.empty()) {
472-
this->reportLiveText(newChildrenText);
497+
this->reportLiveText(newChildrenText, this->ariaLivePoliteness);
473498
}
474499
}
475500

nvdaHelper/vbufBackends/mshtml/node.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2015 NVDA contributers.
4+
Copyright 2006-2020 NV Access Limited, Google LLC, Leonard de Ruijter
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -31,13 +31,14 @@ class MshtmlVBufStorage_controlFieldNode_t : public VBufStorage_controlFieldNode
3131
IHTMLChangeSink* pHTMLChangeSink;
3232
DWORD HTMLChangeSinkCookey;
3333
std::wstring language;
34-
VBufStorage_controlFieldNode_t* ariaLiveNode;
34+
MshtmlVBufStorage_controlFieldNode_t* ariaLiveNode;
35+
std::wstring ariaLivePoliteness;
3536
unsigned int formatState;
3637
bool ariaLiveIsTextRelevant;
3738
bool ariaLiveIsAdditionsRelevant;
3839
bool ariaLiveIsBusy;
3940
VBufStorage_controlFieldNode_t* ariaLiveAtomicNode;
40-
void reportLiveText(std::wstring& text);
41+
void reportLiveText(std::wstring& text, std::wstring& politeness);
4142
void reportLiveAddition();
4243
void preProcessLiveRegion(const MshtmlVBufStorage_controlFieldNode_t* parent, const std::map<std::wstring,std::wstring>& attribsMap);
4344
void postProcessLiveRegion(VBufStorage_controlFieldNode_t* oldNode, std::set<VBufStorage_controlFieldNode_t*>& atomicNodes);

source/NVDAHelper.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#NVDAHelper.py
2-
#A part of NonVisual Desktop Access (NVDA)
3-
#Copyright (C) 2008-2019 NV Access Limited, Peter Vagner, Davy Kager, Mozilla Corporation
4-
#This file is covered by the GNU General Public License.
5-
#See the file COPYING for more details.
1+
# A part of NonVisual Desktop Access (NVDA)
2+
# Copyright (C) 2008-2020 NV Access Limited, Peter Vagner, Davy Kager, Mozilla Corporation, Google LLC,
3+
# Leonard de Ruijter
4+
# This file is covered by the GNU General Public License.
5+
# See the file COPYING for more details.
66

77
import os
88
import sys
@@ -16,6 +16,7 @@
1616
from ctypes import (
1717
WINFUNCTYPE,
1818
c_long,
19+
c_wchar_p,
1920
c_wchar,
2021
windll,
2122
)
@@ -120,6 +121,39 @@ def nvdaControllerInternal_requestRegistration(uuidString):
120121
queueHandler.queueFunction(queueHandler.eventQueue,appModuleHandler.update,pid,helperLocalBindingHandle=bindingHandle,inprocRegistrationHandle=registrationHandle)
121122
return 0
122123

124+
125+
@WINFUNCTYPE(c_long, c_wchar_p, c_wchar_p)
126+
def nvdaControllerInternal_reportLiveRegion(text: str, politeness: str):
127+
assert isinstance(text, str), "Text isn't a string"
128+
assert isinstance(politeness, str), "Politeness isn't a string"
129+
if not config.conf["presentation"]["reportDynamicContentChanges"]:
130+
return -1
131+
focus = api.getFocusObject()
132+
if focus.sleepMode == focus.SLEEP_FULL:
133+
return -1
134+
import queueHandler
135+
import speech
136+
from aria import AriaLivePoliteness
137+
from speech.priorities import Spri
138+
try:
139+
politenessValue = AriaLivePoliteness(politeness.lower())
140+
except ValueError:
141+
log.error(f"nvdaControllerInternal_reportLiveRegion got unknown politeness of {politeness}", exc_info=True)
142+
return -1
143+
if politenessValue == AriaLivePoliteness.OFF:
144+
log.error(f"nvdaControllerInternal_reportLiveRegion got unexpected politeness of {politeness}")
145+
queueHandler.queueFunction(
146+
queueHandler.eventQueue,
147+
speech.speakText,
148+
text,
149+
priority=(
150+
Spri.NEXT
151+
if politenessValue == AriaLivePoliteness.ASSERTIVE
152+
else Spri.NORMAL
153+
)
154+
)
155+
return 0
156+
123157
@WINFUNCTYPE(c_long,c_long,c_long,c_long,c_long,c_long)
124158
def nvdaControllerInternal_displayModelTextChangeNotify(hwnd, left, top, right, bottom):
125159
import displayModel
@@ -482,6 +516,7 @@ def initialize():
482516
("nvdaController_cancelSpeech",nvdaController_cancelSpeech),
483517
("nvdaController_brailleMessage",nvdaController_brailleMessage),
484518
("nvdaControllerInternal_requestRegistration",nvdaControllerInternal_requestRegistration),
519+
("nvdaControllerInternal_reportLiveRegion", nvdaControllerInternal_reportLiveRegion),
485520
("nvdaControllerInternal_inputLangChangeNotify",nvdaControllerInternal_inputLangChangeNotify),
486521
("nvdaControllerInternal_typedCharacterNotify",nvdaControllerInternal_typedCharacterNotify),
487522
("nvdaControllerInternal_displayModelTextChangeNotify",nvdaControllerInternal_displayModelTextChangeNotify),

source/NVDAObjects/IAccessible/MSHTML.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,10 +1039,11 @@ def _get_language(self):
10391039

10401040
def _get_liveRegionPoliteness(self) -> aria.AriaLivePoliteness:
10411041
politeness = self.HTMLAttributes["aria-live"] or "off"
1042-
return next(
1043-
(v for v in aria.AriaLivePoliteness if v._name_.lower() == politeness.lower()),
1044-
aria.AriaLivePoliteness.OFF
1045-
)
1042+
try:
1043+
return aria.AriaLivePoliteness(politeness.lower())
1044+
except ValueError:
1045+
log.error(f"Unknown live politeness of {politeness}", exc_info=True)
1046+
super().liveRegionPoliteness
10461047

10471048
def event_liveRegionChange(self):
10481049
# MSHTML live regions are currently handled with custom code in-process

0 commit comments

Comments
 (0)