AccessibilityObject* AccessibilityTableCell::titleUIElement() const { // Try to find if the first cell in this row is a <th>. If it is, // then it can act as the title ui element. (This is only in the // case when the table is not appearing as an AXTable.) if (!m_renderer || isTableCell()) return 0; RenderTableCell* renderCell = static_cast<RenderTableCell*>(m_renderer); // If this cell is in the first column, there is no need to continue. int col = renderCell->col(); if (!col) return 0; int row = renderCell->row(); RenderTableSection* section = renderCell->section(); if (!section) return 0; RenderTableCell* headerCell = section->cellAt(row, 0).cell; if (!headerCell || headerCell == renderCell) return 0; Node* cellElement = headerCell->element(); if (!cellElement || !cellElement->hasTagName(thTag)) return 0; return axObjectCache()->get(headerCell); }
void AccessibilityTableCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange) { if (!m_renderer || !m_renderer->isTableCell()) return; RenderTableCell* renderCell = toRenderTableCell(m_renderer); columnRange.first = renderCell->col(); columnRange.second = renderCell->colSpan(); }
void AccessibilityTableCell::columnIndexRange(pair<int, int>& columnRange) { if (!m_renderer) return; RenderTableCell* renderCell = static_cast<RenderTableCell*>(m_renderer); columnRange.first = renderCell->col(); columnRange.second = renderCell->colSpan(); }
AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired) { if (!section) return nullptr; unsigned numCols = section->numColumns(); if (m_columnIndex >= numCols) return nullptr; if (!section->numRows()) return nullptr; RenderTableCell* cell = nullptr; // also account for cells that have a span for (int testCol = m_columnIndex; testCol >= 0; --testCol) { // Run down the rows in case initial rows are invalid (like when a <caption> is used). unsigned rowCount = section->numRows(); for (unsigned testRow = 0; testRow < rowCount; testRow++) { RenderTableCell* testCell = section->primaryCellAt(testRow, testCol); // No cell at this index, keep checking more rows and columns. if (!testCell) continue; // If we've reached a cell that doesn't even overlap our column it can't be the header. if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex) break; // If this does not have an element (like a <caption>) then check the next row if (!testCell->element()) continue; // If th is required, but we found an element that doesn't have a th tag, we can stop looking. if (thTagRequired && !testCell->element()->hasTagName(thTag)) break; cell = testCell; break; } } if (!cell) return nullptr; return axObjectCache()->getOrCreate(cell); }
AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired) { if (!section) return 0; int numCols = section->numColumns(); if (m_columnIndex >= numCols) return 0; if (!section->numRows()) return 0; RenderTableCell* cell = 0; // also account for cells that have a span for (int testCol = m_columnIndex; testCol >= 0; --testCol) { RenderTableCell* testCell = section->primaryCellAt(0, testCol); if (!testCell) continue; // we've reached a cell that doesn't even overlap our column // it can't be our header if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex) break; Node* node = testCell->node(); if (!node) continue; if (thTagRequired && !node->hasTagName(thTag)) continue; cell = testCell; } if (!cell) return 0; return m_parentTable->axObjectCache()->getOrCreate(cell); }
AccessibilityObject* AccessibilityTableCell::titleUIElement() const { // Try to find if the first cell in this row is a <th>. If it is, // then it can act as the title ui element. (This is only in the // case when the table is not appearing as an AXTable.) if (isTableCell() || !m_renderer || !m_renderer->isTableCell()) return 0; // Table cells that are th cannot have title ui elements, since by definition // they are title ui elements Node* node = m_renderer->node(); if (node && node->hasTagName(thTag)) return 0; RenderTableCell* renderCell = toRenderTableCell(m_renderer); // If this cell is in the first column, there is no need to continue. int col = renderCell->col(); if (!col) return 0; int row = renderCell->rowIndex(); RenderTableSection* section = renderCell->section(); if (!section) return 0; RenderTableCell* headerCell = section->primaryCellAt(row, 0); if (!headerCell || headerCell == renderCell) return 0; Node* cellElement = headerCell->node(); if (!cellElement || !cellElement->hasTagName(thTag)) return 0; return axObjectCache()->getOrCreate(headerCell); }
/* This method takes care of colspans. effWidth is the same as width for cells without colspans. If we have colspans, they get modified. */ int AutoTableLayout::calcEffectiveWidth() { int tMaxWidth = 0; unsigned int nEffCols = layoutStruct.size(); int hspacing = table->borderHSpacing(); #ifdef DEBUG_LAYOUT qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols ); #endif for ( unsigned int i = 0; i < nEffCols; i++ ) { layoutStruct[i].effWidth = layoutStruct[i].width; layoutStruct[i].effMinWidth = layoutStruct[i].minWidth; layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth; } for ( unsigned int i = 0; i < spanCells.size(); i++ ) { RenderTableCell *cell = spanCells[i]; if ( !cell || cell == (RenderTableCell *)-1 ) break; int span = cell->colSpan(); Length w = cell->styleOrColWidth(); if ( !w.isRelative() && w.value() == 0 ) w = Length(); // make it Variable int col = table->colToEffCol( cell->col() ); unsigned int lastCol = col; int cMinWidth = cell->minWidth() + hspacing; int cMaxWidth = cell->maxWidth() + hspacing; int totalPercent = 0; int minWidth = 0; int maxWidth = 0; bool allColsArePercent = true; bool allColsAreFixed = true; bool haveVariable = false; int fixedWidth = 0; #ifdef DEBUG_LAYOUT int cSpan = span; #endif while ( lastCol < nEffCols && span > 0 ) { switch( layoutStruct[lastCol].width.type() ) { case Percent: totalPercent += layoutStruct[lastCol].width.value(); allColsAreFixed = false; break; case Fixed: if (layoutStruct[lastCol].width.value() > 0) { fixedWidth += layoutStruct[lastCol].width.value(); allColsArePercent = false; // IE resets effWidth to Variable here, but this breaks the konqueror about page and seems to be some bad // legacy behavior anyway. mozilla doesn't do this so I decided we don't either. break; } // fall through case Variable: haveVariable = true; // fall through default: // If the column is a percentage width, do not let the spanning cell overwrite the // width value. This caused a mis-rendering on amazon.com. // Sample snippet: // <table border=2 width=100%>< // <tr><td>1</td><td colspan=2>2-3</tr> // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr> // </table> if (!layoutStruct[lastCol].effWidth.isPercent()) { layoutStruct[lastCol].effWidth = Length(); allColsArePercent = false; } else totalPercent += layoutStruct[lastCol].effWidth.value(); allColsAreFixed = false; } span -= table->spanOfEffCol( lastCol ); minWidth += layoutStruct[lastCol].effMinWidth; maxWidth += layoutStruct[lastCol].effMaxWidth; lastCol++; cMinWidth -= hspacing; cMaxWidth -= hspacing; } #ifdef DEBUG_LAYOUT qDebug(" colspan cell %p at effCol %d, span %d, type %d, value %d cmin=%d min=%d fixedwidth=%d", cell, col, cSpan, w.type(), w.value(), cMinWidth, minWidth, fixedWidth ); #endif // adjust table max width if needed if ( w.isPercent() ) { if ( totalPercent > w.value() || allColsArePercent ) { // can't satify this condition, treat as variable w = Length(); } else { int spanMax = kMax( maxWidth, cMaxWidth ); #ifdef DEBUG_LAYOUT qDebug(" adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.value(), totalPercent ); #endif tMaxWidth = kMax( tMaxWidth, spanMax * 100 / w.value() ); // all non percent columns in the span get percent values to sum up correctly. int percentMissing = w.value() - totalPercent; int totalWidth = 0; for ( unsigned int pos = col; pos < lastCol; pos++ ) { if ( !(layoutStruct[pos].width.isPercent() ) ) totalWidth += layoutStruct[pos].effMaxWidth; } for ( unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++ ) { if ( !(layoutStruct[pos].width.isPercent() ) ) { int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth; #ifdef DEBUG_LAYOUT qDebug(" col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth ); #endif totalWidth -= layoutStruct[pos].effMaxWidth; percentMissing -= percent; if ( percent > 0 ) layoutStruct[pos].effWidth = Length( percent, Percent ); else layoutStruct[pos].effWidth = Length(); } } } } // make sure minWidth and maxWidth of the spanning cell are honoured if ( cMinWidth > minWidth ) { if ( allColsAreFixed ) { #ifdef DEBUG_LAYOUT qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol-1, cMinWidth, minWidth, fixedWidth ); #endif for ( unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++ ) { int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].width.value() / fixedWidth ); #ifdef DEBUG_LAYOUT qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w ); #endif fixedWidth -= layoutStruct[pos].width.value(); cMinWidth -= w; layoutStruct[pos].effMinWidth = w; } } else if ( allColsArePercent ) { int maxw = maxWidth; int minw = minWidth; int cminw = cMinWidth; for ( unsigned int pos = col; maxw > 0 && pos < lastCol; pos++ ) { if ( layoutStruct[pos].effWidth.isPercent() && layoutStruct[pos].effWidth.value()>0 && fixedWidth <= cMinWidth) { int w = layoutStruct[pos].effMinWidth; w = kMax( w, cminw*layoutStruct[pos].effWidth.value()/totalPercent ); w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w); #ifdef DEBUG_LAYOUT qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w ); #endif maxw -= layoutStruct[pos].effMaxWidth; minw -= layoutStruct[pos].effMinWidth; cMinWidth -= w; layoutStruct[pos].effMinWidth = w; } } } else { #ifdef DEBUG_LAYOUT qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol-1, cMinWidth, minWidth ); #endif int maxw = maxWidth; int minw = minWidth; // Give min to variable first, to fixed second, and to others third. for ( unsigned int pos = col; maxw > 0 && pos < lastCol; pos++ ) { if ( layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth ) { int w = kMax( int( layoutStruct[pos].effMinWidth ), layoutStruct[pos].width.value() ); fixedWidth -= layoutStruct[pos].width.value(); minw -= layoutStruct[pos].effMinWidth; #ifdef DEBUG_LAYOUT qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w ); #endif maxw -= layoutStruct[pos].effMaxWidth; cMinWidth -= w; layoutStruct[pos].effMinWidth = w; } } for ( unsigned int pos = col; maxw > 0 && pos < lastCol && minw < cMinWidth; pos++ ) { if ( !(layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth) ) { int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].effMaxWidth / maxw ); w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w); #ifdef DEBUG_LAYOUT qDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w ); #endif maxw -= layoutStruct[pos].effMaxWidth; minw -= layoutStruct[pos].effMinWidth; cMinWidth -= w; layoutStruct[pos].effMinWidth = w; } } } } if ( !w.isPercent() ) { if ( cMaxWidth > maxWidth ) { #ifdef DEBUG_LAYOUT qDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol-1, cMaxWidth ); #endif for ( unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++ ) { int w = kMax( int( layoutStruct[pos].effMaxWidth ), cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth ); #ifdef DEBUG_LAYOUT qDebug(" col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w ); #endif maxWidth -= layoutStruct[pos].effMaxWidth; cMaxWidth -= w; layoutStruct[pos].effMaxWidth = w; } } } else { for ( unsigned int pos = col; pos < lastCol; pos++ ) layoutStruct[pos].maxWidth = kMax(layoutStruct[pos].maxWidth, int(layoutStruct[pos].minWidth) ); } } effWidthDirty = false; // qDebug("calcEffectiveWidth: tMaxWidth=%d", tMaxWidth ); return tMaxWidth; }
AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row) { if (!m_renderer) return 0; if (!hasChildren()) addChildren(); RenderTable* table = static_cast<RenderTable*>(m_renderer); RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); RenderTableCell* cell = 0; unsigned rowCount = 0; unsigned rowOffset = 0; while (tableSection) { rowCount += tableSection->numRows(); unsigned numCols = tableSection->numColumns(); if (row < rowCount && column < numCols) { int sectionSpecificRow = row - rowOffset; cell = tableSection->cellAt(sectionSpecificRow, column).cell; // we didn't find the cell, which means there's spanning happening // search backwards to find the spanning cell if (!cell) { // first try rows for (int testRow = sectionSpecificRow-1; testRow >= 0; --testRow) { cell = tableSection->cellAt(testRow, column).cell; // cell overlapped. use this one if (cell && ((cell->row() + (cell->rowSpan()-1)) >= (int)sectionSpecificRow)) break; cell = 0; } if (!cell) { // try cols for (int testCol = column-1; testCol >= 0; --testCol) { cell = tableSection->cellAt(sectionSpecificRow, testCol).cell; // cell overlapped. use this one if (cell && ((cell->col() + (cell->colSpan()-1)) >= (int)column)) break; cell = 0; } } } } if (cell) break; rowOffset += rowCount; // we didn't find anything between the rows we should have if (row < rowOffset) break; tableSection = table->sectionBelow(tableSection, true); } if (!cell) return 0; AccessibilityObject* cellObject = axObjectCache()->get(cell); ASSERT(cellObject->isTableCell()); return static_cast<AccessibilityTableCell*>(cellObject); }
AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row) { if (!m_renderer || !m_renderer->isTable()) return 0; updateChildrenIfNecessary(); RenderTable* table = toRenderTable(m_renderer); // FIXME: This will skip a table with just a tfoot. Should fix by using RenderTable::topSection. RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); RenderTableCell* cell = 0; unsigned rowCount = 0; unsigned rowOffset = 0; while (tableSection) { unsigned numRows = tableSection->numRows(); unsigned numCols = tableSection->numColumns(); rowCount += numRows; unsigned sectionSpecificRow = row - rowOffset; if (row < rowCount && column < numCols && sectionSpecificRow < numRows) { cell = tableSection->primaryCellAt(sectionSpecificRow, column); // we didn't find the cell, which means there's spanning happening // search backwards to find the spanning cell if (!cell) { // first try rows for (int testRow = sectionSpecificRow - 1; testRow >= 0; --testRow) { cell = tableSection->primaryCellAt(testRow, column); // cell overlapped. use this one ASSERT(cell->rowSpan() >= 1); if (cell && ((cell->rowIndex() + (cell->rowSpan() - 1)) >= sectionSpecificRow)) break; cell = 0; } if (!cell) { // try cols for (int testCol = column - 1; testCol >= 0; --testCol) { cell = tableSection->primaryCellAt(sectionSpecificRow, testCol); // cell overlapped. use this one ASSERT(cell->rowSpan() >= 1); if (cell && ((cell->col() + (cell->colSpan() - 1)) >= column)) break; cell = 0; } } } } if (cell) break; rowOffset += numRows; // we didn't find anything between the rows we should have if (row < rowCount) break; tableSection = table->sectionBelow(tableSection, SkipEmptySections); } if (!cell) return 0; AccessibilityObject* cellObject = axObjectCache()->getOrCreate(cell); ASSERT(cellObject->isTableCell()); return static_cast<AccessibilityTableCell*>(cellObject); }