Skip to content

Commit da1193e

Browse files
authored
Merge 1212759 into 3f6bf0c
2 parents 3f6bf0c + 1212759 commit da1193e

4 files changed

Lines changed: 84 additions & 87 deletions

File tree

nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp

Lines changed: 39 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,6 @@ static IAccessible2* IAccessible2FromIdentifier(int docHandle, int ID) {
105105
return pacc2;
106106
}
107107

108-
template<typename TableType> inline void fillTableCounts(VBufStorage_controlFieldNode_t* node, IAccessible2* pacc, TableType* paccTable) {
109-
wostringstream s;
110-
long count = 0;
111-
// Fetch row and column counts and add them as attributes on this vbuf node.
112-
if (paccTable->get_nRows(&count) == S_OK) {
113-
s << count;
114-
node->addAttribute(L"table-rowcount", s.str());
115-
s.str(L"");
116-
}
117-
if (paccTable->get_nColumns(&count) == S_OK) {
118-
s << count;
119-
node->addAttribute(L"table-columncount", s.str());
120-
}
121-
}
122-
123108
inline int getTableIDFromCell(IAccessibleTableCell* tableCell) {
124109
IUnknown* unk = NULL;
125110
if (tableCell->get_table(&unk) != S_OK || !unk)
@@ -136,31 +121,6 @@ inline int getTableIDFromCell(IAccessibleTableCell* tableCell) {
136121
return id;
137122
}
138123

139-
inline void fillTableCellInfo_IATable(VBufStorage_controlFieldNode_t* node, IAccessibleTable* paccTable, const wstring& cellIndexStr) {
140-
wostringstream s;
141-
long cellIndex = _wtoi(cellIndexStr.c_str());
142-
long row, column, rowExtents, columnExtents;
143-
boolean isSelected;
144-
// Fetch row and column extents and add them as attributes on this node.
145-
if (paccTable->get_rowColumnExtentsAtIndex(cellIndex, &row, &column, &rowExtents, &columnExtents, &isSelected) == S_OK) {
146-
s << row + 1;
147-
node->addAttribute(L"table-rownumber", s.str());
148-
s.str(L"");
149-
s << column + 1;
150-
node->addAttribute(L"table-columnnumber", s.str());
151-
if (columnExtents > 1) {
152-
s.str(L"");
153-
s << columnExtents;
154-
node->addAttribute(L"table-columnsspanned", s.str());
155-
}
156-
if (rowExtents > 1) {
157-
s.str(L"");
158-
s << rowExtents;
159-
node->addAttribute(L"table-rowsspanned", s.str());
160-
}
161-
}
162-
}
163-
164124
typedef HRESULT(STDMETHODCALLTYPE IAccessibleTableCell::*IATableCellGetHeaderCellsFunc)(IUnknown***, long*);
165125
inline void fillTableHeaders(VBufStorage_controlFieldNode_t* node, IAccessibleTableCell* paccTableCell, const IATableCellGetHeaderCellsFunc getHeaderCells, const wstring& attribName) {
166126
wostringstream s;
@@ -379,7 +339,6 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
379339
VBufStorage_buffer_t* buffer,
380340
VBufStorage_controlFieldNode_t* parentNode,
381341
VBufStorage_fieldNode_t* previousNode,
382-
IAccessibleTable* paccTable,
383342
IAccessibleTable2* paccTable2,
384343
long tableID,
385344
const wchar_t* parentPresentationalRowNumber,
@@ -505,7 +464,7 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
505464
LOG_DEBUG(L"pacc->get_states failed");
506465
IA2States=0;
507466
}
508-
// Remove state_editible from tables as Gecko exposes it for ARIA grids which is not in the ARIA spec.
467+
// Remove state_editable from tables as Gecko exposes it for ARIA grids which is not in the ARIA spec.
509468
if(IA2States&IA2_STATE_EDITABLE&&role==ROLE_SYSTEM_TABLE) {
510469
IA2States-=IA2_STATE_EDITABLE;
511470
}
@@ -732,7 +691,7 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
732691
}
733692

734693
// Handle table cell information.
735-
IAccessibleTableCell* paccTableCell = NULL;
694+
IAccessibleTableCell* paccTableCell = nullptr;
736695
if(pacc->QueryInterface(IID_IAccessibleTableCell, (void**)&paccTableCell)!=S_OK) {
737696
paccTableCell=nullptr;
738697
}
@@ -764,56 +723,58 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
764723
}
765724
}
766725

767-
// For IAccessibleTable, we must always be passed the table interface by the caller.
768726
// For IAccessibleTable2, we can always obtain the cell interface,
769727
// which allows us to handle updates to table cells.
770-
if (
771-
paccTableCell || // IAccessibleTable2
772-
(paccTable && (IA2AttribsMapIt = IA2AttribsMap.find(L"table-cell-index")) != IA2AttribsMap.end()) // IAccessibleTable
773-
) {
774-
if (paccTableCell) {
775-
// IAccessibleTable2
776-
this->fillTableCellInfo_IATable2(parentNode, paccTableCell);
777-
if (!paccTable2) {
778-
// This is an update; we're not rendering the entire table.
779-
tableID = getTableIDFromCell(paccTableCell);
780-
}
781-
paccTableCell->Release();
782-
paccTableCell = NULL;
783-
} else // IAccessibleTable
784-
fillTableCellInfo_IATable(parentNode, paccTable, IA2AttribsMapIt->second);
728+
if (paccTableCell) {
729+
this->fillTableCellInfo_IATable2(parentNode, paccTableCell);
730+
if (!paccTable2) {
731+
// This is an update; we're not rendering the entire table.
732+
tableID = getTableIDFromCell(paccTableCell);
733+
}
734+
paccTableCell->Release();
735+
paccTableCell = nullptr;
785736
// tableID is the IAccessible2::uniqueID for paccTable.
786737
s << tableID;
787738
parentNode->addAttribute(L"table-id", s.str());
788739
s.str(L"");
789740
// We're now within a cell, so descendant nodes shouldn't refer to this table anymore.
790-
paccTable = NULL;
791-
paccTable2 = NULL;
741+
paccTable2 = nullptr;
792742
tableID = 0;
793743
}
794744
// Handle table information.
795745
// Don't release the table unless it was created in this call.
796-
bool releaseTable = false;
797-
// If paccTable is not NULL, we're within a table but not yet within a cell, so don't bother to query for table info.
798-
if (!paccTable2 && !paccTable) {
746+
IAccessibleTable2* curNodePaccTable2 = nullptr;
747+
// If paccTable2 is not NULL, we're within a table but not yet within a cell, so don't bother to query for table info.
748+
if (!paccTable2) {
799749
// Try to get table information.
800-
pacc->QueryInterface(IID_IAccessibleTable2,(void**)&paccTable2);
801-
if(!paccTable2)
802-
pacc->QueryInterface(IID_IAccessibleTable,(void**)&paccTable);
803-
if (paccTable2||paccTable) {
804-
// We did the QueryInterface for paccTable, so we must release it after all calls that use it are done.
805-
releaseTable = true;
750+
pacc->QueryInterface(IID_IAccessibleTable2,(void**)&curNodePaccTable2);
751+
if (curNodePaccTable2) {
806752
// This is a table, so add its information as attributes.
807-
if((IA2AttribsMapIt = IA2AttribsMap.find(L"layout-guess")) != IA2AttribsMap.end())
753+
if((IA2AttribsMapIt = IA2AttribsMap.find(L"layout-guess")) != IA2AttribsMap.end()) {
808754
parentNode->addAttribute(L"table-layout",L"1");
755+
}
809756
tableID = ID;
810757
s << ID;
811758
parentNode->addAttribute(L"table-id", s.str());
812759
s.str(L"");
813-
if(paccTable2)
814-
fillTableCounts<IAccessibleTable2>(parentNode, pacc, paccTable2);
815-
else
816-
fillTableCounts<IAccessibleTable>(parentNode, pacc, paccTable);
760+
// Fetch row and column counts and add them as attributes on the vbuf node.
761+
long rowCount = 0;
762+
if (curNodePaccTable2->get_nRows(&rowCount) == S_OK) {
763+
s << rowCount;
764+
parentNode->addAttribute(L"table-rowcount", s.str());
765+
s.str(L"");
766+
}
767+
long colCount = 0;
768+
if (curNodePaccTable2->get_nColumns(&colCount) == S_OK) {
769+
s << colCount;
770+
parentNode->addAttribute(L"table-columncount", s.str());
771+
s.str(L"");
772+
}
773+
if (rowCount > 0 || colCount > 0) {
774+
// This table has rows and columns.
775+
// Maintain curNodePaccTable2 for child rendering until any table cells are found.
776+
paccTable2 = curNodePaccTable2;
777+
}
817778
// Add the table summary if one is present and the table is visible.
818779
if (isVisible &&
819780
(!description.empty() && (tempNode = buffer->addTextFieldNode(parentNode, previousNode, description))) ||
@@ -939,7 +900,6 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
939900
buffer,
940901
parentNode,
941902
previousNode,
942-
paccTable,
943903
paccTable2,
944904
tableID,
945905
presentationalRowNumber,
@@ -981,7 +941,6 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
981941
buffer,
982942
parentNode,
983943
previousNode,
984-
paccTable,
985944
paccTable2,
986945
tableID,
987946
presentationalRowNumber,
@@ -1005,7 +964,6 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
1005964
buffer,
1006965
parentNode,
1007966
previousNode,
1008-
paccTable,
1009967
paccTable2,
1010968
tableID,
1011969
presentationalRowNumber,
@@ -1032,7 +990,6 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
1032990
buffer,
1033991
parentNode,
1034992
previousNode,
1035-
paccTable,
1036993
paccTable2,
1037994
tableID,
1038995
presentationalRowNumber,
@@ -1133,13 +1090,10 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
11331090
SysFreeString(IA2Text);
11341091
if(paccText)
11351092
paccText->Release();
1136-
if (releaseTable) {
1137-
if(paccTable2)
1138-
paccTable2->Release();
1139-
else
1140-
paccTable->Release();
1093+
if (curNodePaccTable2) {
1094+
curNodePaccTable2->Release();
1095+
curNodePaccTable2 = nullptr;
11411096
}
1142-
11431097
return parentNode;
11441098
}
11451099

nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class GeckoVBufBackend_t: public VBufBackend_t {
2828
VBufStorage_buffer_t* buffer,
2929
VBufStorage_controlFieldNode_t* parentNode,
3030
VBufStorage_fieldNode_t* previousNode,
31-
IAccessibleTable* paccTable=NULL,
3231
IAccessibleTable2* paccTable2=NULL,
3332
long tableID=0, const wchar_t* parentPresentationalRowNumber=NULL,
3433
bool ignoreInteractiveUnlabelledGraphics=false

tests/system/robot/chromeTests.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# A part of NonVisual Desktop Access (NVDA)
2-
# Copyright (C) 2020 NV Access Limited
2+
# Copyright (C) 2020-2021 NV Access Limited, Leonard de Ruijter
33
# This file may be used under the terms of the GNU General Public License, version 2 or later.
44
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html
55

@@ -379,3 +379,44 @@ def test_i12147():
379379
actualSpeech,
380380
"target 0 heading level 4"
381381
)
382+
383+
384+
def test_tableInStyleDisplayTable():
385+
"""
386+
Chrome treats nodes with `style="display: table"` as tables.
387+
When a HTML style table is positioned in such a node, NVDA was previously unable to announce
388+
table row and column count as well as provide table navigation for the inner table.
389+
"""
390+
_chrome.prepareChrome(
391+
"""
392+
<p>Paragraph</p>
393+
<div style="display:table">
394+
<table>
395+
<thead>
396+
<tr>
397+
<th>First heading</th>
398+
<th>Second heading</th>
399+
</tr>
400+
</thead>
401+
<tbody>
402+
<tr>
403+
<td>First content cell</td>
404+
<td>Second content cell</td>
405+
</tr>
406+
</tbody>
407+
</table>
408+
</div>
409+
"""
410+
)
411+
# Jump to the table
412+
actualSpeech = _chrome.getSpeechAfterKey("t")
413+
_asserts.strings_match(
414+
actualSpeech,
415+
"table with 2 rows and 2 columns row 1 First heading column 1 First heading"
416+
)
417+
nextActualSpeech = _chrome.getSpeechAfterKey("control+alt+downArrow")
418+
_asserts.strings_match(
419+
nextActualSpeech,
420+
"row 2 First content cell"
421+
)
422+

tests/system/robot/chromeTests.robot

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,6 @@ ARIA checkbox
5454
i12147
5555
[Documentation] New focus target should be announced if the triggering element is removed when activated
5656
test_i12147
57+
Table in style display: table
58+
[Documentation] Properly announce table row/column count and working table navigation for a HTML table in a div with style display: table
59+
test_tableInStyleDisplayTable

0 commit comments

Comments
 (0)