1/* 2 * Copyright (C) 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "AccessibilityTableCell.h" 31 32#include "AXObjectCache.h" 33#include "AccessibilityTable.h" 34#include "AccessibilityTableRow.h" 35#include "HTMLElement.h" 36#include "HTMLNames.h" 37#include "RenderObject.h" 38#include "RenderTableCell.h" 39 40namespace WebCore { 41 42using namespace HTMLNames; 43 44AccessibilityTableCell::AccessibilityTableCell(RenderObject* renderer) 45 : AccessibilityRenderObject(renderer) 46{ 47} 48 49AccessibilityTableCell::~AccessibilityTableCell() 50{ 51} 52 53PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer) 54{ 55 return adoptRef(new AccessibilityTableCell(renderer)); 56} 57 58bool AccessibilityTableCell::computeAccessibilityIsIgnored() const 59{ 60 AccessibilityObjectInclusion decision = defaultObjectInclusion(); 61 if (decision == IncludeObject) 62 return false; 63 if (decision == IgnoreObject) 64 return true; 65 66 // Ignore anonymous table cells. 67 if (!node()) 68 return true; 69 70 if (!isTableCell()) 71 return AccessibilityRenderObject::computeAccessibilityIsIgnored(); 72 73 return false; 74} 75 76AccessibilityTable* AccessibilityTableCell::parentTable() const 77{ 78 if (!m_renderer || !m_renderer->isTableCell()) 79 return 0; 80 81 // If the document no longer exists, we might not have an axObjectCache. 82 if (!axObjectCache()) 83 return 0; 84 85 // Do not use getOrCreate. parentTable() can be called while the render tree is being modified 86 // by javascript, and creating a table element may try to access the render tree while in a bad state. 87 // By using only get() implies that the AXTable must be created before AXTableCells. This should 88 // always be the case when AT clients access a table. 89 // https://bugs.webkit.org/show_bug.cgi?id=42652 90 return toAccessibilityTable(axObjectCache()->get(toRenderTableCell(m_renderer)->table())); 91} 92 93bool AccessibilityTableCell::isTableCell() const 94{ 95 AccessibilityObject* parent = parentObjectUnignored(); 96 if (!parent || !parent->isTableRow()) 97 return false; 98 99 return true; 100} 101 102AccessibilityRole AccessibilityTableCell::determineAccessibilityRole() 103{ 104 // Always call determineAccessibleRole so that the ARIA role is set. 105 // Even though this object reports a Cell role, the ARIA role will be used 106 // to determine if it's a column header. 107 AccessibilityRole defaultRole = AccessibilityRenderObject::determineAccessibilityRole(); 108 if (!isTableCell()) 109 return defaultRole; 110 111 return CellRole; 112} 113 114bool AccessibilityTableCell::isTableHeaderCell() const 115{ 116 return node() && node()->hasTagName(thTag); 117} 118 119bool AccessibilityTableCell::isTableCellInSameRowGroup(AccessibilityTableCell* otherTableCell) 120{ 121 Node* parentNode = node(); 122 for ( ; parentNode; parentNode = parentNode->parentNode()) { 123 if (parentNode->hasTagName(theadTag) || parentNode->hasTagName(tbodyTag) || parentNode->hasTagName(tfootTag)) 124 break; 125 } 126 127 Node* otherParentNode = otherTableCell->node(); 128 for ( ; otherParentNode; otherParentNode = otherParentNode->parentNode()) { 129 if (otherParentNode->hasTagName(theadTag) || otherParentNode->hasTagName(tbodyTag) || otherParentNode->hasTagName(tfootTag)) 130 break; 131 } 132 133 return otherParentNode == parentNode; 134} 135 136 137bool AccessibilityTableCell::isTableCellInSameColGroup(AccessibilityTableCell* tableCell) 138{ 139 std::pair<unsigned, unsigned> colRange; 140 columnIndexRange(colRange); 141 142 std::pair<unsigned, unsigned> otherColRange; 143 tableCell->columnIndexRange(otherColRange); 144 145 if (colRange.first <= (otherColRange.first + otherColRange.second)) 146 return true; 147 return false; 148} 149 150String AccessibilityTableCell::expandedTextValue() const 151{ 152 return getAttribute(abbrAttr); 153} 154 155bool AccessibilityTableCell::supportsExpandedTextValue() const 156{ 157 return isTableHeaderCell() && hasAttribute(abbrAttr); 158} 159 160void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers) 161{ 162 AccessibilityTable* parent = parentTable(); 163 if (!parent) 164 return; 165 166 // Choose columnHeaders as the place where the "headers" attribute is reported. 167 ariaElementsFromAttribute(headers, headersAttr); 168 // If the headers attribute returned valid values, then do not further search for column headers. 169 if (!headers.isEmpty()) 170 return; 171 172 std::pair<unsigned, unsigned> rowRange; 173 rowIndexRange(rowRange); 174 175 std::pair<unsigned, unsigned> colRange; 176 columnIndexRange(colRange); 177 178 for (unsigned row = 0; row < rowRange.first; row++) { 179 AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row); 180 if (!tableCell || tableCell == this || headers.contains(tableCell)) 181 continue; 182 183 std::pair<unsigned, unsigned> childRowRange; 184 tableCell->rowIndexRange(childRowRange); 185 186 const AtomicString& scope = tableCell->getAttribute(scopeAttr); 187 if (scope == "col" || tableCell->isTableHeaderCell()) 188 headers.append(tableCell); 189 else if (scope == "colgroup" && isTableCellInSameColGroup(tableCell)) 190 headers.append(tableCell); 191 } 192} 193 194void AccessibilityTableCell::rowHeaders(AccessibilityChildrenVector& headers) 195{ 196 AccessibilityTable* parent = parentTable(); 197 if (!parent) 198 return; 199 200 std::pair<unsigned, unsigned> rowRange; 201 rowIndexRange(rowRange); 202 203 std::pair<unsigned, unsigned> colRange; 204 columnIndexRange(colRange); 205 206 for (unsigned column = 0; column < colRange.first; column++) { 207 AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(column, rowRange.first); 208 if (!tableCell || tableCell == this || headers.contains(tableCell)) 209 continue; 210 211 const AtomicString& scope = tableCell->getAttribute(scopeAttr); 212 if (scope == "row") 213 headers.append(tableCell); 214 else if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell)) 215 headers.append(tableCell); 216 } 217} 218 219void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange) 220{ 221 if (!m_renderer || !m_renderer->isTableCell()) 222 return; 223 224 RenderTableCell* renderCell = toRenderTableCell(m_renderer); 225 rowRange.first = renderCell->rowIndex(); 226 rowRange.second = renderCell->rowSpan(); 227 228 // since our table might have multiple sections, we have to offset our row appropriately 229 RenderTableSection* section = renderCell->section(); 230 RenderTable* table = renderCell->table(); 231 if (!table || !section) 232 return; 233 234 RenderTableSection* footerSection = table->footer(); 235 unsigned rowOffset = 0; 236 for (RenderTableSection* tableSection = table->topSection(); tableSection; tableSection = table->sectionBelow(tableSection, SkipEmptySections)) { 237 // Don't add row offsets for bottom sections that are placed in before the body section. 238 if (tableSection == footerSection) 239 continue; 240 if (tableSection == section) 241 break; 242 rowOffset += tableSection->numRows(); 243 } 244 245 rowRange.first += rowOffset; 246} 247 248void AccessibilityTableCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange) 249{ 250 if (!m_renderer || !m_renderer->isTableCell()) 251 return; 252 253 const RenderTableCell& cell = *toRenderTableCell(m_renderer); 254 columnRange.first = cell.table()->colToEffCol(cell.col()); 255 columnRange.second = cell.table()->colToEffCol(cell.col() + cell.colSpan()) - columnRange.first; 256} 257 258AccessibilityObject* AccessibilityTableCell::titleUIElement() const 259{ 260 // Try to find if the first cell in this row is a <th>. If it is, 261 // then it can act as the title ui element. (This is only in the 262 // case when the table is not appearing as an AXTable.) 263 if (isTableCell() || !m_renderer || !m_renderer->isTableCell()) 264 return 0; 265 266 // Table cells that are th cannot have title ui elements, since by definition 267 // they are title ui elements 268 Node* node = m_renderer->node(); 269 if (node && node->hasTagName(thTag)) 270 return 0; 271 272 RenderTableCell* renderCell = toRenderTableCell(m_renderer); 273 274 // If this cell is in the first column, there is no need to continue. 275 int col = renderCell->col(); 276 if (!col) 277 return 0; 278 279 int row = renderCell->rowIndex(); 280 281 RenderTableSection* section = renderCell->section(); 282 if (!section) 283 return 0; 284 285 RenderTableCell* headerCell = section->primaryCellAt(row, 0); 286 if (!headerCell || headerCell == renderCell) 287 return 0; 288 289 if (!headerCell->element() || !headerCell->element()->hasTagName(thTag)) 290 return 0; 291 292 return axObjectCache()->getOrCreate(headerCell); 293} 294 295} // namespace WebCore 296